
화상 기반 AI 법률 상담 플랫폼을 제작하면서 가장 먼저 부딪힌 문제는 바로 검색이었다.
사용자가 남긴 사건 설명을 어떻게 수만 건의 판례와 연결할 것인가?
처음에는 PostgreSQL의 ILIKE에 기대어 시작했지만 결과는 처참했다.
Recall@10 수치는 0%, 사실상 “검색 불능” 상태였다.
그래서 LawAId 프로젝트에서는 검색 성능을 끌어올리기 위해 BM25(키워드 검색), 벡터 검색(의미 검색), 그리고 두 가지를 결합한 하이브리드 검색을 실험했다.
최종적으로 Recall@10을 55%까지 끌어올렸고, 이 글은 그 과정을 정리한 글이다.
1. ILIKE: 단순 매칭의 한계
PostgreSQL의 ILIKE는 텍스트 안에 문자열이 있는지 단순히 확인한다.
예를 들어 "횡령"이라는 단어가 있어야만 검색된다.
- 장점: 구현이 매우 간단하다.
- 단점:
- 동의어나 활용형 구분 불가 (예: “절도” vs “절도죄”)
- 띄어쓰기에 민감
- 실제 결과: Recall@10 = 0% → 실무에서 전혀 쓸 수 없었다.
2. BM25: 키워드 기반 확률적 검색
BM25는 정보 검색에서 가장 널리 쓰이는 문서 랭킹 함수다.
사용자 질의(Query)와 문서(Document)의 관련도를 수치 화해서 점수가 높은 문서를 위에 올린다.
핵심 원리
BM25는 TF-IDF의 발전형이다.
- TF (Term Frequency): 단어가 문서에 얼마나 자주 나오나
- IDF (Inverse Document Frequency): 흔하지 않은 단어일수록 가중치 ↑
- 문서 길이 보정: 긴 문서가 불리하지 않도록 평균 길이에 맞춰 조정
즉, 문서 안에서 단어가 얼마나 “의미 있게” 등장하는지를 수치화한다.
예시
질의: "회사 돈을 개인적으로 사용"
"업무상 횡령 사건"→ 핵심 단어 “횡령” 매칭, 점수 상승"회사 자금을 개인적으로 사용"→ 질의 단어 대부분 매칭, 최고 점수"교통사고 과실 판례"→ 관련 단어 없음, 점수 0
장단점
- 장점:
- 키워드 기반 정확도 높음
- 속도 빠름
- 점수 계산 방식이 직관적
- 단점:
- 문맥 이해 불가
- 예:
"횡령"과"재산 편취"를 같은 의미로 보지 못한다
3. 벡터 검색: 의미 기반 검색
BM25가 키워드 위주라면, 벡터 검색은 문맥 전체 의미를 본다.
문장이나 문서를 고차원 벡터로 바꾼 뒤, 질의 벡터와의 유사도를 계산하는 방식이다.
보통 코사인 유사도를 많이 쓴다.
특징
- 동의어 대응:
"횡령"과"배임"같은 비슷한 표현도 매칭 가능 - 문맥 이해: 단순 단어 매칭이 아니라 문장의 의미 반영
- 자연어 질의 강점: “내가 회사 돈을 써버린 경우” 같은 표현도 검색 가능
한계
- 리소스 부담: 고차원 연산으로 CPU/GPU 자원 많이 소모
- 속도 느림: BM25보다 훨씬 무거움
- 의미 손실: 하나의 벡터로 문서를 표현하다 보니 세부 정보가 사라질 수 있음
실제 적용
- Sentence-BERT 기반 한국어 임베딩 모델 사용
- PostgreSQL + pgvector 확장으로 벡터 검색 구현
- FP16 양자화로 메모리 절감 (2.5GB → 1.3GB, 약 49% 감소)
- 배치 처리로 OOM(Out Of Memory) 방지
4. 하이브리드 검색: 두 방식을 합치다
BM25와 벡터 검색은 성격이 다르다.
- BM25는 법률 용어 같은 정확한 키워드 매칭에서 강하고,
- 벡터 검색은 “말을 바꿔도 같은 의미”인 표현을 잘 잡아낸다.
두 방식 모두 강점이 뚜렷했기 때문에, 우리는 이들을 하나의 파이프라인으로 엮었다. 단순히 합치는 게 아니라, 후보군을 모으고 최종적으로 재정렬(re-ranking) 하는 구조였다.
Cross-Encoder의 역할
BM25와 벡터 검색만 합치면 여전히 순서가 뒤죽박죽이다.
여기서 Cross-Encoder가 최종 심판 역할을 한다.
- Bi-Encoder(임베딩)는 쿼리와 문서를 각각 임베딩한 뒤 유사도를 계산하는 반면,
- Cross-Encoder는 쿼리와 문서를 한 쌍으로 입력해 “이 둘이 얼마나 관련 있는지”를 직접 학습된 모델이 판단한다.
예를 들어 질의가 "회사 돈을 개인적으로 사용"일 때:
- BM25는
"회사 자금을 개인적으로 사용"같은 문서를 잘 올려주지만,"업무상 횡령 사건"은 점수가 낮을 수 있다. - 벡터 검색은
"업무상 횡령 사건"도 잘 잡아내지만,"개인적 비용 처리 사례"처럼 덜 중요한 문서도 끌어올린다. - Cross-Encoder는 이 둘을 비교해 실제 사용자의 의도와 더 가까운 문서를 위에 올린다.
최종 파이프라인 구조
- BM25 – 빠른 키워드 기반 후보 30개 확보
- 벡터 검색 – 의미 기반 후보 20개 확보
- 후보 합치기 – 두 결과를 합쳐 약 50개 후보군 생성
- Cross-Encoder 재정렬 – 쿼리와 문서 쌍별로 점수를 계산해 최종 Top-K 선정
# Simplified Example
bm25_results = bm25_service.search(query, top_n=30)
vector_results = vector_service.search(query, top_n=20)
# 후보군 합치기 (중복 제거 포함)
candidates = bm25_results + vector_results
# Cross-Encoder로 최종 점수 산출 및 재정렬
final_results = cross_encoder.rerank(query, candidates)
도입 효과
- BM25의 정확성 + 벡터 검색의 의미 이해를 동시에 확보
- Cross-Encoder로 잡음(noise)을 줄이고 최종적으로 사용자에게 가장 타당한 결과를 제공
- Recall@10이 단일 BM25 대비 25%p 이상 향상
5. 성능 비교
직접 실험한 결과는 다음과 같다:
| 검색 방식 | Recall@1 | Recall@3 | Recall@10 | MRR |
|---|---|---|---|---|
| ILIKE | 0% | 0% | 0% | 0.000 |
| BM25 | 0% | 0% | 30.0% | 0.205 |
| 벡터 검색 | 0% | 15.0% | 15.0% | 0.180 |
| 하이브리드 | 15.0% | 30.0% | 55.0% | 0.250 |
- Recall@K: 상위 K개의 결과 안에 정답(관련 문서)이 포함될 확률
- MRR (Mean Reciprocal Rank): 정답이 몇 번째에 나왔는지를 역순위로 평균 낸 값 → 정답이 위에 있을수록 높음
➡️ 결론: 하이브리드 검색이 가장 높은 성능을 보였다.
6. 실무에서 배운 점과 주의사항
배운 점
- 메모리 최적화의 중요성
GPU에는 FP16(반정밀도), CPU에는 INT8 동적 양자화를 적용해 리소스를 크게 줄일 수 있었다. 경량화는 단순한 최적화가 아니라 “서비스 가능성”을 좌우하는 핵심 요소였다.
- 한국어 특화 처리의 필요성
복합 명사(예: 업무상횡령, 사기죄)와 법률 동의어(예: 횡령 ↔ 자금 유용)를 처리하지 않으면 검색 품질이 떨어졌다. 한국어 NLP에서는 일반 모델만 쓰기보다 도메인 맞춤 처리가 필수라는 걸 깨달았다.
- 캐싱 전략의 효과
반복적으로 들어오는 쿼리를 캐싱하거나 BM25 인덱스를 직렬화해두니 응답 속도가 체감될 정도로 개선됐다. 작은 최적화가 전체 시스템의 사용자 경험에 크게 기여했다.
- 하이브리드 접근의 실효성
단순 키워드 매칭(Recall@10 = 0%)에서 출발해 BM25와 벡터 검색을 결합하자 Recall@10이 55%까지 올라갔다. 서로 다른 방법을 조합하는 것이 실제 서비스 환경에서 훨씬 안정적이라는 점을 배웠다.
주의사항
- 법률 용어의 의미적 차이 주의
예를 들어 횡령과 배임은 의미가 유사해 보이지만 법적으로는 전혀 다른 범죄다. 임베딩 모델이 의미적 유사성으로 연결할 수 있어도, 법률 도메인에서는 반드시 추가 필터링이 필요하다.
- 실험 결과 해석 시 주의
Recall@10이나 MRR 같은 지표는 상대적인 성능 비교에는 좋지만, “사용자 만족도”와 1:1 대응되지 않는다. 지표만 보고 성능을 단정하기보다는 실제 사용 시나리오 테스트를 병행해야 한다.
7. 한계와 앞으로의 방향
- 현재 한계: Recall@1 낮음(15%), 응답시간 길음(20~30초), 형사 사건 중심 데이터
- 앞으로 개선:
- 법률 특화 임베딩 모델 파인튜닝 → Recall@10 90% 목표
- Cross-Encoder 고도화로 랭킹 정밀화
- 고가용성 아키텍처 → Redis 세션 관리, Kubernetes 확장
8. 결론
법률 검색은 단순히 키워드 매칭만으로는 부족하다.
BM25는 키워드를 잘 잡고, 벡터 검색은 의미를 잡는다.
두 방식을 합친 하이브리드 접근법은 실제 서비스에서도 쓸 수 있을 만큼 안정적인 성능을 보여줬다.
검색에 정답은 없다. 하지만 도메인 특성을 고려한 하이브리드 전략이 현실적인 해답이었다.
참고 자료
- Robertson & Zaragoza (2009). The Probabilistic Relevance Framework
- Reimers & Gurevych (2019). Sentence Embeddings using Siamese BERT-Networks*
- pgvector GitHub