다중 모달 융합 검색: 모든 쿼리에 맞는 검색기를 고르는 법
> "parseConfig는 어디서 정의되었나" 같은 쿼리는 "인증은 어떻게 동작하나"와는 다른 검색을 원합니다. Maguyva는 의도를 분류하고, 그에 맞춰 네 가지 검색 방식에 가중치를 부여한 뒤, 가중 상호 순위 융합(Reciprocal Rank Fusion)으로 결과를 결합합니다.
검색 쿼리는 하나의 단일한 것이 아닙니다.
“parseConfig는 어디서 정의되었나”는 정확한 심볼을 원합니다. 하나의 정밀한 위치를, 빠르게. “인증은 어떻게 동작하나”는 의미를 원합니다. 하나의 개념을 설명하는 관련 코드의 퍼짐을요. “이 함수를 바꾸면 무엇이 깨지나”는 의존성 그래프를 원합니다. “문자열 ECONNREFUSED을 찾아줘”는 리터럴 일치를 원합니다. 영리한 것은 필요 없이요.
grep은 리터럴 일치에 탁월하고 일부 참조 탐색에도 유용하지만, 의존성 그래프도 아니고 의미를 이해하지도 못합니다. 임베딩은 시맨틱 측면을 다루지만, 정확한 문자열이나 영향도 분석에는 잘못된 도구입니다. 대부분의 코드 검색 도구는 하나의 엔진을 고르고 모든 쿼리가 그 선택과 함께 살아가게 만듭니다. Maguyva는 고르지 않습니다. 어떤 종류의 질문을 받았는지 파악한 뒤, 그 질문에 걸맞은 비율로 네 가지 검색기를 섞습니다.
네 가지 방식
내부적으로는 코드를 찾는 네 가지 독립적인 방법이 있습니다.
- semantic (시맨틱) — Voyage 바이너리 임베딩을 이용한 벡터 검색. 의미로 코드를 찾습니다.
- text (텍스트) — 트라이그램 매칭. 리터럴, 오류 문자열, 정확한 식별자를 찾습니다.
- structural (구조적) — AST 쿼리. 정의, 시그니처, 언어 구성 요소를 찾습니다.
- graph (그래프) — 의존성 그래프. 호출자, 피호출자, 영향 범위를 찾습니다.
각각은 서로 다른 부류의 질문에 강합니다. 관건은 눈앞의 쿼리에 각각을 얼마나 신뢰할지 결정하는 것입니다.
의도 분류
어떤 검색이든 실행되기 전에, 가벼운 분류기가 쿼리를 신뢰도 점수와 함께 여섯 가지 의도 중 하나로 분류합니다. 이는 의도적으로 저렴합니다. 순서가 있는 휴리스틱으로, 첫 매치가 이기는 방식입니다. 핫 패스에서 실행되고 1~2밀리초만 추가되기 때문입니다.
def,class,func,import… 로 시작 → find_definition (신뢰도 0.95)- “누가 호출하나”, “사용처”, “참조” → find_references (0.90)
- “영향”, “영향 범위”, “무엇이 의존하나” → impact_analysis (0.90)
- 인용된
"string"이나traceback같은 오류 토큰 → exact_match (0.85~0.90) CamelCase이나snake_case형태의 식별자 → find_definition (0.60~0.80)- “어떻게”, “왜”, “설명해줘”, “아키텍처” → understand_code (0.75)
- 아무것도 일치하지 않음 → understand_code, 낮은 신뢰도 (0.40)
각 의도는 네 가지 방식에 걸친 가중치 프로필을 갖습니다. 실제 수치는 다음과 같습니다.
| 의도 | semantic | text | structural (AST) | graph |
|---|---|---|---|---|
| find_definition | 0.2 | 0.1 | 0.6 | 0.1 |
| find_references | 0.1 | 0.2 | 0.2 | 0.5 |
| understand_code | 0.5 | 0.2 | 0.2 | 0.1 |
| find_similar | 0.4 | 0.3 | 0.2 | 0.1 |
| impact_analysis | 0.1 | 0.1 | 0.1 | 0.7 |
| exact_match | 0.0 | 0.9 | 0.1 | 0.0 |
그래서 “parseConfig는 어디서 정의되었나”는 AST에 크게 의존합니다(0.6). “인증은 어떻게 동작하나”는 시맨틱 벡터에 의존합니다(0.5). “무엇이 이것에 의존하나”는 거의 전부 그래프입니다(0.7). “ECONNREFUSED을 찾아줘”는 거의 전부 트라이그램입니다(0.9). 임베딩 모델은 완전히 꺼집니다. 정확한 문자열에는 시맨틱 유사도가 정확히 잘못된 도구이기 때문입니다.
빠른 경로와 융합된 경로
분류기가 확신할 때 — 점수 ≥ 0.85 — 이고 쿼리가 일반적인 것이라면, Maguyva는 융합을 완전히 건너뛰고 단일한 지배적 방식으로 곧장 라우팅합니다. “X는 어디서 정의되었나”는 네 개의 검색기가 필요 없습니다. 지금 당장 AST 인덱스가 필요할 뿐입니다. 이 직접 경로는 fusion_strategy: "direct"로 다시 보고됩니다.
모호한 것은 모두 융합을 거칩니다. 네 가지(혹은 기본 프리셋에서는 세 가지) 방식이 병렬로 실행되어 각자 자신의 순위 목록을 반환하고, 우리는 이를 결합합니다.
가중 상호 순위 융합(Weighted Reciprocal Rank Fusion)
이질적인 검색기들을 융합하는 것은 말처럼 쉽지 않습니다. 코사인 유사도 0.82와 트라이그램 점수 137, 그래프 중심성 0.004는 같은 척도 위에 있지 않기 때문에 그냥 더할 수 없습니다. 상호 순위 융합(Reciprocal Rank Fusion)은 원본 점수를 버리고 각 엔진이 매긴 순위만 남김으로써 이 문제를 피해 갑니다. 하나의 방식에서 나오는 결과의 기여도는 다음과 같습니다.
contribution = weight × 1 / (k + rank + 1)
여기서 rank은 해당 방식의 목록에서의 위치이고, k는 스무딩 상수입니다. 둘 이상의 엔진이 찾아낸 모든 결과에 대해서는 방식들에 걸쳐 기여도가 합산됩니다. 검색기들 사이의 합의는 자연스럽게 위로 떠오릅니다. 우리는 기본 프리셋에서 k = 40을, thorough에서는 60을 사용합니다(quick은 시맨틱 전용으로 실행되므로 그곳에서는 융합이 전혀 작동하지 않습니다). 원조 RRF 연구는 범용 검색에 대해 k = 60을 채택했지만, 우리는 조금 더 날카로운 값을 기본값으로 삼아 방식들 사이의 상위권 합의에 조금 더 무게를 둡니다. 그리고 이를 수동으로 조정하는 것은 권장하지 않습니다.
여기에 더해, 결과는 그래프 중요도 부스트를 갖습니다. 허브 — 전체 코드베이스가 의존하는 함수 — 는 동일한 텍스트 관련성이라도 눈에 띄지 않는 말단 노드보다 순위가 높아야 하므로, 우리는 각 기여도에 다음을 곱합니다.
boost = min(1 + 0.3 × ln(1 + centrality), 1.5)
중심성은 파이프라인이 미리 계산한 PageRank/차수 지표에서 나오며, 부스트는 1.5배로 제한되어 인기 있는 함수가 더 관련성 높은 무명 함수를 완전히 파묻을 수 없게 합니다. 마지막으로 우리는 벤더, 빌드, 아카이브 경로의 결과 순위를 낮추고, 파일당 최선의 청크로 중복을 제거합니다.
아직 완벽하지 않은 것
의도 분류기는 학습된 모델이 아니라 정규식들의 스택입니다. 흔한 형태의 쿼리는 잘 다룹니다. 이를 도입한 결정 기록에는 무응답률이 약 15%에서 5% 미만으로 떨어졌다고 남아 있습니다. 하지만 이는 휴리스틱이며, 진짜로 모호한 쿼리는 understand_code과 시맨틱 쪽으로 기운 블렌드로 빠집니다. 이는 영리한 기본값이 아니라 안전한 기본값입니다. 저렴한 버전이 충분히 빠르고 좋기 때문에, 그리고 틀렸지만 확신에 찬 분류기가 정직한 폴백보다 나쁘기 때문에, 우리는 이를 학습된 분류기로 교체하지 않았습니다. 가중치 자체도 우리가 수집하지 않는 클릭 데이터로부터 학습된 것이 아니라 손으로 고른 사전 값(prior)입니다.
도구가 아니라 질문을 던지세요
에이전트는 grep을 써야 할지, 임베딩을 써야 할지, 호출 그래프를 써야 할지 알아야 할 필요가 없어야 합니다. 그저 평범한 말로 질문을 던지고 올바른 답을 받아야 합니다. 다중 모달 융합이야말로 find_symbol, 시맨틱 검색, 의존성 분석이 하나의 쿼리 표면 뒤에 자리할 수 있게 해주는 것입니다. 시스템이 질문의 형태를 읽고 그에 맞는 검색기를 조용히 조립합니다. 임베딩을 채점하는 모델도 중요하지만, 언제 그것을 쓰지 않을지 아는 것도 마찬가지로 중요합니다. 각 쿼리에 맞는 도구를 고르는 것은 그 자체로 하나의 품질이며, 우리는 이를 호출자에게 떠넘기기보다는 우리가 책임지고 싶습니다.
// you bring the question. it brings the tools.
관련 글
Maguyva 빌드 로그의 다른 글들
우리가 코드 검색을 voyage-4-large로 업그레이드한 이유_
우리는 코드 임베딩을 voyage-4-large로 옮겼습니다. 현재 공개된 RTEB 코드 검색 리더보드에서 1위인 모델입니다. 솔직한 버전을 말하자면, 우리가 감수하는 트레이드오프, 실제로 우리가 인덱싱하는 것, 그리고 프리미엄 임베딩에 비용을 지불하는 이유입니다.
언어 재귀적 자기 개선: 약 280개 언어에 걸친 코드 인텔리전스 갈아내기_
우리는 약 280개 언어에 대한 코드 인텔리전스를 지원합니다. 사람이 이를 일일이 검수할 수는 없습니다. 그래서 우리는 언어 재귀적 자기 개선 루프 — 무작위 점검, LLM 판정, 한 가지 수정, 재검증 — 를 만들었고, 추출이 단순히 초록불(green)이 아니라 실제로 옳아질 때까지 격리된 에이전트 플릿으로 이를 실행합니다.
에이전트 관찰가능성: 훅, Alloy, 그리고 Grafana_
우리는 OpenTelemetry와 Alloy를 이용해 Claude Code와 Codex를 하나의 Grafana 스택에 연결한 뒤, 트레이스와 로그를 활용해 에이전트 동작 문제를 근원에서 찾아내고 고쳤습니다.