grep이 LSP를 이겼다: 코딩 에이전트가 '더 좋은 도구'를 외면하는 이유

한눈에 보는 요약
코딩 에이전트에게 코드 구조를 이해하는 LSP(Language Server Protocol) 기반 탐색 도구를 쥐여줬습니다. 참조 찾기, 정의 이동, 심벌 목록까지 — 사람이라면 IDE에서 매일 쓰는 그 기능들이죠. 그런데 결과는 예상 밖이었습니다. 에이전트는 단순 코드 위치 찾기에서 거의 언제나 grep을 골랐고, LSP를 먼저 쓰라고 강제하자 오히려 성공률이 떨어졌습니다.
이 실험이 던지는 진짜 질문은 "LSP가 나쁜가?"가 아닙니다. "우리는 새 도구를 어떻게 평가해야 하는가?" 입니다.
⚠️ 먼저 짚고 갈 점: 이 실험은 작업 몇 개, 저장소 소수, Claude 모델 3종, 조건별 2~3회 실행으로 구성된 예비 연구입니다. LSP 전반의 우열을 가리는 결론이 아닙니다.
실험은 무엇을 비교했나
grep: 텍스트가 일치하는 곳을 찾습니다. 주석이든 문자열이든 가리지 않습니다.- LSP 기반 도구: 참조(references)·정의(definition)·문서 심벌(document symbols)을 통한 의미 기반 탐색을 합니다. 실제 함수 호출과 주석 속 같은 단어를 구분할 수 있죠.
대상은 Opus 4.8, Sonnet 4.6, Haiku 4.5 그리고 여러 Python·TypeScript 저장소. 작업은 세 가지였습니다 — 코드 위치 찾기, 모든 호출자 찾기, 편집하기.
한 가지 실험 설계가 인상적입니다. 토큰 사용량은 두 방식이 모두 성공한 경우에만 비교했습니다. 실패한 실행이 일찍 종료됐다는 이유만으로 "효율적"으로 보이는 착시를 막기 위해서죠. 벤치마크를 설계하는 분이라면 기억해 둘 만한 디테일입니다.
발견 1: 도구 선택은 '작업에 따라' 완전히 갈렸다
단순 위치 찾기 → 압도적으로 grep
| 모델 | 의미 기반 도구 호출 비중 | |---|---| | Opus 4.8 | 0% | | Sonnet 4.6 | 4% | | Haiku 4.5 | 6% |
Opus 4.8은 단 한 번도 LSP를 부르지 않았습니다. 그리고 "의미 기반 탐색을 먼저 써라"고 강제한 실험군에서는 성공률이 100% → 89%로 하락했습니다. 더 정교한 도구를 쓰라고 시켰더니 결과가 나빠진 겁니다.
다중 파일 이름 변경에서도 Opus 4.8의 LSP 호출 비중은 3%에 그쳤습니다.
모든 호출자 찾기 → 이야기가 달라진다
반면 "이 함수를 부르는 곳을 빠짐없이 찾아라" 같은 참조 완전성 작업에서는 비중이 각각 45%, 50%, 57%로 뛰었습니다. 에이전트가 맹목적으로 grep만 좋아한 게 아니라는 뜻입니다.
여기서 지표를 뜯어보면 흥미롭습니다.
- 정밀도(precision): LSP 1.00 vs
grep0.76 → LSP가 거짓 일치를 완벽히 걸러냄 - 재현율(recall): 둘 다 약 0.66 → LSP도 실제 호출을 더 많이 찾아내지는 못함
즉 병목은 "검색이 부정확해서"가 아니라 "에이전트가 끝까지 꼼꼼히 훑지 않아서"였습니다. 도구를 바꿔도 해결되지 않는 문제였던 거죠. 게다가 더 강력한 모델에서는 정밀도가 오르는 만큼 토큰 사용량도 함께 늘었습니다.
발견 2: 갈림길은 '정적 타입'이 아니라 '검색 잡음'이었다
흔한 예상은 "TypeScript처럼 타입이 강한 언어에서 LSP가 빛나겠지"입니다. 실제 데이터는 다른 이야기를 합니다.
| 저장소 | 언어 | grep 정밀도 | LSP 적용 시 |
|---|---|---|---|
| remeda | TypeScript | 1.00 | F1 개선 없음, 토큰 +16% |
| hono | TypeScript | 0.51 | F1 +0.246, 토큰 -12% |
| requests | Python | 0.76 | F1 +0.072, 토큰 +19% |
같은 TypeScript인데 remeda와 hono의 결과가 정반대입니다. 결정 요인은 언어가 아니라 grep이 얼마나 시끄러운가였습니다.
grep이 이미 깨끗하게 찾는 저장소(remeda) → LSP는 순수 비용grep이 거짓 일치를 쏟아내는 저장소(hono) → LSP가 정확도와 토큰을 동시에 개선
실무 시사점: "우리 팀에 LSP 도구를 붙일까?"의 답은 언어가 아니라 여러분의 코드베이스에서 grep을 직접 돌려보고 결정할 문제입니다.
발견 3: 같은 결과라도 '출력 형식'이 성능을 바꾼다
이 실험에서 가장 실용적인 대목입니다.
초기 LSP 도구는 파일 경로·행·열만 반환했습니다. 에이전트는 코드를 확인하려면 파일을 또 열어야 했죠. 반면 grep은 이렇게 돌려줍니다.
src/auth.ts:42: return validateToken(token)
위치와 내용이 한 줄에 다 있습니다. 그래서 연구진은 백엔드도 참조 집합도 그대로 둔 채, 응답에 소스 텍스트만 추가해봤습니다. (Opus 4.8 + 인덱스를 미리 준비한 pyright, 다중 파일 이름 변경 작업)
| 방식 | 첫 시도 성공률 | 위치 재현율 | 토큰 | 후속 읽기 |
|---|---|---|---|---|
| grep | 1.00 | 1.000 | 2,451 | 4.3회 |
| LSP (위치만) | 0.67 | 0.930 | 4,131 | 15.2회 |
| LSP (코드 맥락 포함) | 0.83 | 0.958 | 3,336 | 3.2회 |
검색 로직은 한 글자도 바꾸지 않았는데 성공률이 67% → 83%로 올랐고, 후속 파일 읽기는 15.2회에서 3.2회로 급감했습니다.
교훈은 명확합니다. 정확한 위치를 아는 것만으로는 좋은 작업 흐름이 보장되지 않습니다. 결과를 해석할 때마다 추가 행동이 필요하다면, 그 도구는 정확해도 비효율적입니다. Anthropic의 Writing effective tools for agents가 "반환 맥락"을 도구 설계의 일부로 보는 이유이기도 하죠.
발견 4: 텍스트 검색에는 구조적 이점이 있다
조금 더 근본적인 이야기입니다.
의미 기반 참조는 텍스트 출현의 부분집합이다.
이름 변경 작업을 떠올려 보세요. 바꿔야 할 건 실제 코드 참조만이 아닙니다. 주석, 문서 문자열(docstring), 설정 파일, 문자열 리터럴까지 손봐야 하죠. find_references는 설계상 이런 항목을 반환하지 않습니다. grep은 찾아냅니다.
그래서 관찰된 행동에는 두 가지 설명이 가능합니다.
- 구조적 원인: 어떤 작업은 의미 기반 탐색이 원리적으로 제공하지 않는 텍스트 전반의 포괄성을 요구한다.
- 학습 분포 원인: 모델이
read/grep/edit/bash같은 익숙한 도구와 결과 형식을 훨씬 많이 연습했다.
1번은 도구의 검색 대상만 봐도 확인됩니다. 2번은 학습 데이터를 조작하지 않았으므로 입증할 수 없는 가설입니다. 출력 형식 개선 효과 역시 "응답마다 유용한 정보가 늘어났다"는 것만으로 충분히 설명되므로, 사후 학습 데이터 탓이라고 단정할 수 없습니다.
그래서 '하네스'가 중요하다
하네스(harness) 는 모델을 둘러싼 실행 환경 전체를 말합니다.
- 문맥에 넣는 지시
- 사용 가능한 도구와 입력 스키마
- 결과·오류의 형식
- 모델이 다음에 무엇을 볼지 결정하는 루프
여기에 환경 구성, 진행 기록, 검증 절차까지 더해지면 에이전트가 실제로 달성할 수 있는 일이 달라집니다. Anthropic의 Effective harnesses for long-running agents도 여러 세션에 걸쳐 안정적으로 진전하려면 모델 외의 이런 요소가 필요하다고 짚습니다.
그리고 사후 학습에 에이전트의 행동 궤적이 포함된다면, 하네스는 곧 학습 예시의 프롬프트·호출·결과·복구 경로를 규정하는 존재가 됩니다. read, grep, edit, bash를 반복하며 학습한 모델은 그 인터페이스에 의존하는 행동 정책을 배웁니다. 같은 모델이라도 도구 계층을 갈아끼우면 실효 성능이 달라지는 겁니다.
에이전트 역량 = 모델 × 하네스
이 관계 때문에 모델 벤치마크 점수는 다른 실행 환경으로 그대로 이전되지 않습니다. 같은 모델을 지원한다고 같은 에이전트가 재현되는 것은 아닙니다. 도구 선택, 시그니처, 출력 형식, 오류 동작 하나하나가 모델의 행동 정책을 바꿉니다.
새 도구를 도입할 때의 6가지 원칙
- 기본 도구 인터페이스에서 출발해 같은 정확도 조건으로 비교하라
- 성공률이 떨어졌다면 토큰이 줄었다는 이유로 개선이라 불러선 안 됩니다.
- 제공했는지가 아니라 실제로 호출했는지를 측정하라
- 쓸 수 있다는 사실이 채택을 뜻하지는 않습니다. (Opus 4.8의 0%를 떠올려 보세요.)
- 결과에 다음 결정을 내릴 만큼의 맥락을 담아라
- 위치만 든 객체보다
path:line:content형식이 더 잘 작동할 수 있습니다.
- 위치만 든 객체보다
- 기존 도구를 대체 경로로 남겨둬라
- 의미 기반 검색과 텍스트 검색은 애초에 다른 문제를 풉니다.
- 작업과 코드베이스에 맞춰 선택하라
- 검색 잡음이 많은 저장소는 의미 기반 탐색의 덕을 보지만, 텍스트 전반을 훑어야 하면 여전히
grep이 필요합니다.
- 검색 잡음이 많은 저장소는 의미 기반 탐색의 덕을 보지만, 텍스트 전반을 훑어야 하면 여전히
- 새 도구 사용이 정말 중요하다면 그 행동 경로를 강화하라
- 프롬프트로 소개할 수는 있어도, 그것만으로 신뢰할 만한 사용 정책이 생기지는 않습니다.
LSP·MCP·새로운 에이전트 스킬을 피하라는 이야기가 아닙니다. 전체 에이전트 루프 안에서 평가하라는 이야기입니다. Anthropic의 Building effective agents가 단순하고 조합 가능한 패턴을 강조하는 것과 같은 맥락이죠. 도구가 많다고 에이전트가 자동으로 유능해지지 않습니다. 각 도구의 역할이 구별되고, 이해하기 쉽고, 작업 흐름 안에서 유용해야 합니다.
마무리: 실험의 한계를 분명히
에이전트 플랫폼을 만드는 입장이라면 세 가지로 정리됩니다. 모델과 하네스를 함께 평가하고, 이미 안정적으로 작동하는 인터페이스를 보존하고, 더 정교한 추상화를 도입하기 전에 실제 작업으로 검증하는 것. 더 정확한 검색도 토큰을 더 쓸 수 있고, 올바른 위치도 불필요한 파일 읽기를 부를 수 있으니까요.
다만 이 연구의 경계선은 명확히 그어둘 필요가 있습니다.
- 작은 작업 집합, 소수 저장소, Claude 모델 3종, 조건별 2~3회 실행의 예비 연구입니다.
- 참조·정의·문서 심벌 기반 탐색만 시험했습니다.
textDocument/rename, 진단(diagnostics), 코드 액션은 제외됐습니다. - 따라서 이름 변경 기능을 갖춘 LSP라면
grep이 우세했던 리팩터링 작업에서 다른 결과가 나올 수 있습니다. - 편집 작업은 자체 로컬 작업이며 표준 SWE-bench 점수가 아닙니다.
- "모델이 학습 과정에서 익숙해진 도구를 선호한다"는 설명은 여전히 검증되지 않은 가설입니다.
모든 모델·도구·코드베이스에 대한 최종 판정으로 일반화할 수 없다는 점, 이것이 이 실험이 스스로에게 가장 엄격하게 적용한 원칙입니다.
전체 실험 구성과 작업 정의, 원본 결과는 *Does a Language Server Save Tokens for Coding Agents?*에서 확인할 수 있습니다.