GeekNews에서 zg(zvec-grep)를 봤습니다. Alibaba Qwen 팀이 오픈소스로 공개한 로컬 우선 검색 레이어인데, ripgrep + BM25 + 벡터 검색을 하나의 인터페이스로 묶고 MCP로 코딩 에이전트에 붙는다고 합니다. 제 환경에는 한국어 마크다운 문서 수백 개짜리 개인 monorepo가 있고, 에이전트가 이 문서를 검색하는 일이 잦아서 바로 후보가 됐습니다. “정확한 키워드를 모르는 질문”에서 grep이 헛도는 문제를 시맨틱 검색이 풀어줄 것 같았거든요.
설치부터 평가까지 반나절을 들여 내린 결론을 미리 말하면 미채택입니다. zg가 나빠서가 아니라, 비교 대상이 생각보다 훨씬 강했기 때문입니다. 같은 30문제에서 zg의 최고 성적은 19/30이었고, 에이전트가 원래 하던 검색(grep을 쓰는 LLM)은 30/30이었습니다. 이 글은 그 격차가 어디서 오는지, 그리고 소규모 파일럿의 만점이 어떻게 사람을 속이는지에 대한 기록입니다.
zg(zvec-grep)는 무엇인가
zg는 “의미로 넓게 찾고 → 관련도로 좁히고 → 정확히 검증”하는 흐름을 도구 하나가 담당하는 로컬 검색 레이어입니다. 프로젝트를 인덱싱하면(zg index) 마크다운은 헤딩 구조를 보존한 조각으로, 코드는 심볼 단위로 임베딩되고, 검색 시에는 BM25 어휘 랭킹과 벡터 유사도를 RRF로 융합합니다. 에이전트에는 zvec_grep_search라는 MCP 툴 하나로 노출되고, 파일·인덱스·모델이 전부 로컬에 남는 local-first 설계입니다.
공식 벤치마크 수치는 인상적입니다. SWE-QA-Bench에서 툴 호출과 입력 토큰을 절반 수준으로 줄이면서 judge 점수가 오히려 올랐다고 하는데요. 여기에 함정이 하나 있습니다. 그 수치는 전부 원격 임베딩(자사 클라우드 API) 기준이고, 제 문서는 개인 데이터라 외부로 보낼 수 없으니 로컬 모델만 쓸 수 있습니다. 로컬 소형 모델의 한국어 검색 품질은 어디에도 실측이 없었습니다. 직접 재야 했습니다.
임베딩 모델을 한 단계씩 올려가며 본 91파일 파일럿
임베딩 모델 선택 가이드(docs/07)는 “필요한 언어를 커버하는 가장 작은 모델로 시작해, 결과를 비교한 뒤 한 단계씩 올리라”고 권합니다. 그 권고대로 모델 세 개를 작은 것부터 차례로 평가했습니다. 시작 전에 인덱싱 범위부터 좁혔는데요. zg는 텍스트/마크다운을 기본 256MiB까지 통째로 받아들이므로, 자막 덤프 같은 대용량 텍스트는 glob으로 제외하지 않으면 인덱싱 시간과 부하가 몇 배로 뜁니다.
파일럿은 monorepo에서 문서 모듈 2개(91파일)만 인덱싱하고, “질문의 표현과 문서의 표현이 다른” 시맨틱 갭 쿼리 6개로 zg 하이브리드 검색과 단순 키워드 grep을 비교했습니다. 예를 들어 “후보 중 주말에 예약이 안 되는 식당”이라는 질문의 답은 문서에 “전화 전용”이라고 적혀 있어서, 질문 어휘로 grep하면 닿지 않는 유형입니다. 정답 문서를 미리 정해두고 상위 5개 안에 들면 적중으로 채점했습니다.
| 모델 | 방식 | 인덱싱(91파일) | hit@5 |
|---|---|---|---|
potion-multilingual-128m | 정적 룩업 (Model2Vec) | CPU 21초 | 3/6 |
multilingual-e5-small | ONNX 소형 transformer | 2.5분, CPU 부하 중간 | 5/6 |
embeddinggemma-300m | GGUF, Metal GPU | 4.5분, CPU 10% | 6/6 |
단계마다 결과가 선명하게 갈렸습니다. 정적 모델은 grep이 0건으로 끝나는 문제를 뚫어주긴 했지만 한국어 의미 이해가 얕아서, 대출 비교 질문에 전혀 무관한 문서를 1위로 올리는 식의 오답을 냈습니다. 소형 transformer로 올리자 어휘가 거의 안 겹치는 순수 시맨틱 갭 문제가 1위로 잡혔고, 중간 체급인 embeddinggemma-300m은 여섯 문제를 전부 맞혔습니다. Metal GPU로 도는 덕에 발열도 없었습니다. 여기까지만 보면 도입 결정은 끝난 것처럼 보였습니다.
862파일로 넓히자 6/6이 19/30으로 떨어졌다
파일럿 만점을 보고 도입을 결정하려던 참에, “6문제로는 표본이 너무 적지 않나”는 지적을 받았습니다. 맞는 말이라 평가를 다시 설계했습니다. 인덱스를 monorepo 전체(자막 덤프 제외 862파일, 인덱스 조각 1만 개)로 넓히고, 쿼리를 6가지 유형에 5개씩 30개로 늘렸습니다. 표현 불일치, 비교/관계, 크로스 모듈 종합, 한국어 질문→코드 찾기, 날짜/일정, 그리고 grep이 이겨야 정상인 정확 키워드 대조군까지입니다. 정답 문서는 전부 사전에 정해두고 hit@5와 MRR로 기계 채점했습니다.
결과는 이랬습니다. 91파일에서 6/6이던 embeddinggemma-300m이 862파일에서는 19/30으로 떨어졌습니다. 문서가 적을수록 정답이 상위에 오기 쉽다는 사실이 그대로 드러난 겁니다. 소규모 파일럿의 만점을 믿고 도입했다면 틀린 결정이었습니다. 유형별로 보면 문서 질문은 25문제 중 19개로 버텼는데, 한국어 질문으로 코드 파일을 찾는 유형은 5문제 전멸이었고, 심지어 본문에 분명히 존재하는 고유명사 키워드를 던진 대조군 문제 하나도 놓쳤습니다. 한국어 FTS 토크나이징이 의심되는 지점입니다.
진짜 베이스라인은 grep이 아니라 “grep을 쓰는 LLM”
확장 평가를 돌리던 중에 본질적인 질문이 나왔습니다. “zg 없이 에이전트가 원래 하던 검색으로는 몇 개나 맞추나? 그게 더 높으면 도입할 이유가 없다.” 처음의 비교군이었던 “질문에서 키워드 하나 뽑아 grep 한 번”(5/30)은 최저선일 뿐, 실제 에이전트는 그렇게 검색하지 않습니다. 키워드를 여러 개 시도하고, 파일명과 README를 읽고, 방향을 수정하면서 찾죠.
그래서 공정한 베이스라인을 만들었습니다. 대화 컨텍스트가 전혀 없는 서브에이전트 30개를 문제당 하나씩 띄우고, zg 사용을 금지한 채 기존 도구(grep/glob/read)만으로 검색하게 했습니다. 정답 목록이 담긴 디렉터리는 접근을 차단해 오염을 막았고, 각자 후보 파일 5개와 사용한 도구 호출 수를 보고하게 했습니다. 최종 스코어보드입니다.
| 검색 방식 | hit@5 | MRR | 쿼리당 비용 | 인덱싱 |
|---|---|---|---|---|
| 에이전트 네이티브 (LLM + grep 루프) | 30/30 | 0.88 | 평균 5.2회 호출, 약 26k 토큰 | 불필요 |
zg embeddinggemma-300m | 19/30 | 0.35 | 1회 호출 | 4분 40초 |
zg multilingual-e5-small | 15/30 | 0.29 | 1회 호출 | 8분 41초 |
zg potion-multilingual-128m | 9/30 | 0.21 | 1회 호출 | 47초 |
| 단순 키워드 grep 1회 | 5/30 | – | 1회 호출 | 불필요 |
네이티브 검색은 30문제 중 24개에서 정답을 1순위로 냈습니다. 어휘가 안 겹치는 시맨틱 갭 문제도, 한국어→코드 문제도 전부 통과했고요. 이 결과의 구조를 뜯어보면 이렇습니다. 이번 비교는 “임베딩 vs 키워드”가 아니라 “3억 파라미터 임베딩 모델의 원샷 vs 프론티어급 LLM의 반복 탐색”이었습니다. 질문 어휘를 문서 어휘로 옮기는 일, 예를 들어 “노후 대비 계좌”라는 질문을 연금·세액공제라는 문서 어휘로 확장하는 일을 네이티브 쪽에서는 검색하는 LLM 자신이 수행합니다. 체급이 다르니 결과도 그렇게 나옵니다.
한 가지 더, 제 monorepo의 특성도 컸습니다. 번호 붙인 문서 체계, 목차 역할을 하는 README, 비교 문서라면 파일명에 비교 대상 둘이 그대로 들어가 있는 이름 규칙. 이것들이 사실상 사람이 미리 만들어둔 시맨틱 인덱스로 작동해서, 에이전트가 벡터 인덱스 없이도 의미로 길을 찾을 수 있었습니다. 대신 그 검색은 공짜가 아닙니다. 쿼리당 5.2회 호출에 2만 6천 토큰을 태우니까요. zg의 제안은 “그 비싼 LLM 탐색을 싼 모델로 대체하자”인데, 제 환경에서는 품질 격차가 너무 커서 그 맞바꿈이 성립하지 않았습니다. 30문제 중 11개를 놓치는 수준이면, 빗나간 문제마다 결국 네이티브 검색으로 다시 찾아야 해서 아낀 비용이 도로 나갑니다.
그럼에도 zg가 유리한 환경은 따로 있다
이 결과를 “zvec-grep은 별로다”로 읽으면 곤란합니다. zg가 강점을 발휘하도록 설계된 조건이 따로 있고, 제 환경이 그 정반대였을 뿐입니다.
공식 벤치마크가 돌아간 곳을 보면 명확합니다. matplotlib·django 같은 수십만 줄 코드베이스와 10만 문서 웹 코퍼스입니다. README를 따라 길을 찾을 수 없고, 반복 grep의 시행착오 비용이 폭발하는 곳이죠. 거기서 zg의 주장은 애초에 “품질 향상”이 아니라 “같은 품질을 토큰·호출 40~50% 절감으로 내겠다”는 것이고, 하루 수천 쿼리를 돌리는 규모라면 그 절감은 실제 돈입니다. 정리되지 않은 텍스트 더미, 영어/코드 중심 코퍼스라는 조건도 zg의 임베딩·FTS가 튜닝된 방향과 일치합니다. 제 환경은 전부 반대였습니다. 862파일로 작고, 문서 체계가 잘 잡혀 있고, 한국어이고, 검색하는 에이전트가 프론티어급이죠.
zg 공식 문서 스스로도 “정확한 키워드 검색으로 충분한 작업에는 이득이 제한적”이라고 명시하고 있습니다. 이번 평가는 그 경계선이 제 코퍼스 기준으로 어느 쪽에 있는지를 실측한 셈입니다. 재평가 조건도 남겨뒀습니다. 자막·수집 원문처럼 정리되지 않은 대량 텍스트를 검색 대상에 넣고 싶어질 때(zg가 원래 강한 조건), 또는 zg의 한국어 처리가 성숙했을 때입니다. 쿼리 30개와 채점 스크립트를 한 세트로 남겨뒀으니 그대로 재실행하면 됩니다.
도구를 들이는 결정보다 들이지 않는 결정이 더 많은 데이터를 요구한다는 것, 그리고 파일럿의 만점은 코퍼스를 넓히기 전까지는 아무것도 증명하지 않는다는 것. 이번에 얻은 건 zg에 대한 지식보다 이 두 가지에 가깝습니다.