Show HN: Tilth – 에이전트의 토큰 낭비를 막기 위해 직접 만든 도구 (~4k Rust)
요약
Tilth는 AI 에이전트가 코드를 읽을 때 발생하는 토큰 낭비를 줄이기 위해 설계된 Rust 기반의 스마트 코드 읽기 도구입니다. Tree-sitter를 활용하여 코드의 구조적 정의와 호출 체인을 파악함으로써, 에이전트가 불필요한 전체 코드를 읽지 않고도 핵심 정보를 효율적으로 탐색할 수 있게 돕습니다.
핵심 포인트
- Claude Sonnet 모델 기준 정답당 비용을 약 44% 절감하는 효과를 입증함
- Tree-sitter를 사용하여 단순 텍스트 검색이 아닌 심볼의 정의(definition)와 호출 지점(call sites)을 구조적으로 탐색
- 호출자 푸터(callee footer) 기능을 통해 별도의 검색 없이도 호출 체인을 즉시 파악 가능
- MCP(Model Context Protocol) 모드를 지원하여 AI 에이전트와의 연동 최적화
- 다중 심볼 검색 및 의존성(Blast-radius) 확인 기능을 통해 복잡한 코드베이스 탐색 지원
tilth
인간과 AI 에이전트를 위한 스마트한 코드 읽기 도구. 160회의 벤치마크(benchmark) 실행 결과, Sonnet에서는 44%, Opus에서는 39%, Haiku에서는 **38%**의 정답당 비용을 절감했습니다. (benchmarks)
tilth는 ripgrep, tree-sitter, 그리고 cat에게 공유된 두뇌를 부여했을 때 나타나는 결과물입니다.
$ tilth src/auth.ts
# src/auth.ts (258 lines, ~3.4k tokens) [outline]
...
작은 파일은 전체 내용이 반환됩니다. 큰 파일은 개요(outline)가 제공됩니다. --section 옵션을 사용하여 상세 내용을 파고들 수 있습니다:
$ tilth src/auth.ts --section 44-89
$ tilth docs/guide.md --section "## Installation"
검색 시 정의(definitions)를 우선적으로 탐색
$ tilth handleAuth --scope src/
# Search: "handleAuth" in src/ — 6 matches (2 definitions, 4 usages)
...
Tree-sitter는 단순히 문자열이 나타나는 위치가 아니라, 심볼(symbol)이 **정의(defined)**된 위치를 찾아냅니다. 각 매치(match) 결과는 주변 파일 구조를 함께 보여주므로, 다시 읽지 않고도 무엇을 보고 있는지 즉시 알 수 있습니다.
확장된 정의에는 파일, 라인 범위, 시그니처(signature)와 함께 해결된 호출 대상(callees)을 보여주는 호출자 푸터(callee footer)(── calls ──)가 포함됩니다. 이를 통해 에이전트는 각 호출 대상에 대해 별도의 검색을 수행하지 않고도 호출 체인(call chains)을 따라갈 수 있습니다.
확장된 검색 (Expanded search)
CLI 검색은 기본적으로 압축된 결과를 반환합니다. --expand를 사용하면 상위 매치 결과에 소스 코드를 인라인(inline)으로 포함할 수 있습니다:
$ tilth handleAuth --scope src/ --expand # 상위 2개 (플래그만 사용할 경우 기본값)
$ tilth handleAuth --scope src/ --expand=5 # 상위 5개
MCP 모드에서는 expand의 기본값이 2로 설정되어 플래그가 필요하지 않습니다.
다중 심볼 검색 (Multi-symbol search)
한 번의 호출로 여러 파일에 걸친 추적을 수행합니다:
$ tilth "ServeHTTP, HandlersChain, Next" --scope .
각 심볼은 정의와 확장이 포함된 개별 결과 블록을 가집니다. 확장 예산(expand budget)은 공유되며, 파일 간 중복을 제거하여 심볼당 최소 하나 이상의 확장이 제공됩니다.
호출자 쿼리 (Callers query)
텍스트 검색이 아닌 구조적 Tree-sitter 매칭을 사용하여 심볼의 모든 호출 지점(call sites)을 찾습니다:
$ tilth isTrustedProxy --callers --scope .
# Callers of "isTrustedProxy" — 5 call sites
...
MCP 모드에서는 tilth_search에 kind: "callers"를 대신 사용하세요.
Blast-radius 의존성 (Blast-radius deps)
파일이 무엇을 임포트(import)하는지, 그리고 무엇이 해당 파일을 의존(depend)하고 있는지 확인하세요. 파일 이름을 변경하거나 내보내기(exports)를 수정하기 전에 유용합니다.
$ tilth src/auth.ts --deps
# deps: src/auth.ts
...
MCP 모드에서는 tilth_deps 도구를 사용하세요.
세션 중복 제거 (Session dedup)
MCP 모드에서는 이전에 확장된 정의가 이후 검색 시 전체 본문 대신 [shown earlier]로 표시됩니다. 에이전트가 이미 확인한 심볼(symbols)을 다시 방문할 때 토큰(tokens)을 절약할 수 있습니다.
구조적 차이점 (Structural diff)
$ tilth diff HEAD~1
# Diff: HEAD~1 — 3 files, 2 modified, 1 added (~350 tokens)
...
함수 수준의 변경 사항 탐지(Function-level change detection)를 수행합니다. --scope로 상세히 파고들거나, --log로 이력을 요약하고, 머지 충돌(merge conflicts)을 자동으로 감지할 수 있습니다. AI 에이전트를 위한 git diff 대체제입니다.
벤치마크 (Benchmarks)
4개의 실제 리포지토리(Express, FastAPI, Gin, ripgrep)를 대상으로 코드 탐색 작업을 수행했습니다. 기준점(Baseline)은 Claude Code의 내장 도구이며, tilth는 내장 도구에 tilth MCP 서버를 추가한 것입니다. 저희는 재시도 시 예상 비용인 정답당 비용 (total_spend / correct_answers)을 보고합니다. 전체 방법론은 benchmark/를 참조하세요.
| 모델 | 작업 수 | 실행 횟수 | 기준 정답당 $ | tilth 정답당 $ | 변화량 | 기준 정확도 | tilth 정확도 |
|---|---|---|---|---|---|---|---|
| Sonnet 4.6 | 26 | 86 | $0.26 | $0.15 | -44% | 84% | 94% |
| ... |
v0.5.0은 가중치가 높은 상위 MCP 지침(instructions)과 스코프 폴백(scope fallback)을 도입하여, 세 모델 모두에서 평균 40%의 비용 절감을 달성했습니다. Sonnet의 정확도는 84%에서 94%로, Haiku는 54%에서 73%로 향상되었습니다. 모든 모델에서 유의미한 턴(turn) 감소(평균 25% 적은 턴 수)를 보였습니다.
스코프 혼동(모델이 잘못된 디렉토리 경로를 전달하는 현상)은 이제 경고와 함께 현재 작업 디렉토리(cwd)로 자동 폴백되도록 처리됩니다. MCP 지침 상단의 DO NOT 규칙은 모든 모델에서 중복된 내장 도구(Grep, Read, Glob) 사용을 거의 제로(zero) 수준으로 줄였습니다.
작업별 결과, 언어별 세부 분석 및 모델 비교는 benchmark/를 참조하세요.
이유 (Why)
저는 AI 에이전트가 단 하나의 함수를 찾기 위해 6번의 도구 호출 (tool calls)을 수행하는 것을 보고 이 도구를 만들었습니다. glob → read → "너무 큼" → grep → 다시 read → 다른 파일 read. 매 라운드 트립 (round-trip)마다 토큰과 추론 시간 (inference time)이 낭비됩니다.
tilth는 단 한 번의 호출로 구조적 인식 (structural awareness)을 제공합니다. 개요 (outline)는 파일에 무엇이 들어있는지 알려줍니다. 검색 (search)은 무엇이 어디에 정의되어 있는지 알려줍니다. --section은 *정확히 필요한 줄 (lines)*만 가져옵니다.
설치 (Install)
cargo install tilth
# 또는
npx tilth
릴리스 페이지에서 빌드된 바이너리 (Prebuilt binaries)를 받을 수 있습니다.
MCP 서버 (MCP server)
tilth install claude-code # ~/.claude.json
tilth install cursor # ~/.cursor/mcp.json
tilth install windsurf # ~/.codeium/windsurf/mcp_config.json
...
해시 앵커 기반의 파일 편집 (hash-anchored file editing)을 활성화하려면 --edit을 추가하세요 (편집 모드 참조):
tilth install claude-code --edit
또는 bash에서 직접 호출할 수 있습니다. MCP 에이전트 프롬프트 (agent prompt)는 AGENTS.md를, Claude Code 스킬 프롬프트 (skill prompt)는 skills/SKILL.md를 참조하세요.
더 작은 모델들 (Smaller models)
더 작은 모델들(예: Haiku)은 내장된 Bash/Grep 대신 tilth 도구를 무시할 수 있습니다. tilth 사용을 강제하려면 중복되는 내장 도구들을 비활성화하세요:
claude --disallowedTools "Bash,Grep,Glob"
벤치마크 결과에 따르면 Haiku는 tilth를 통해 상당한 이점(정확도 54% → 73%)을 얻지만, 여전히 내장 도구로 회귀할 수 있습니다. 강제 모드 (Forced mode)는 일관된 도구 채택을 보장합니다.
무엇을 보여줄지 결정하는 방식
| 입력 (Input) | 동작 (Behaviour) |
|---|---|
| 0 bytes | [empty] |
| ... |
줄 (line) 기준이 아닌 토큰 (Token) 기준입니다. 1줄짜리 압축된 번들 (minified bundle)은 개요가 생성되지만, 120줄짜리 집중된 모듈은 전체가 출력됩니다.
편집 모드 (Edit mode)
--edit과 함께 설치하면 tilth_edit이 추가되고 tilth_read가 해시 줄 (hashline) 출력 방식으로 전환됩니다:
42:a3f| let x = compute();
43:f1b| return x;
tilth_edit은 이 해시들을 앵커 (anchors)로 사용합니다. 마지막 읽기 이후 파일이 변경되었다면 해시가 일치하지 않으며, 현재 내용이 표시되면서 편집이 거부됩니다:
{
"path": "src/auth.ts",
"edits": [
...
대용량 파일은 여전히 개요(outline)를 먼저 보여줍니다 — 필요한 부분의 해시 라인(hashlined) 콘텐츠를 얻으려면 section을 사용하세요.
The Harness Problem에서 영감을 받았습니다.
사용법 (Usage)
tilth <path> # 파일 읽기 (대용량인 경우 개요 표시)
tilth <path> --section 45-89 # 정확한 라인 범위
tilth <path> --section "## Foo" # 마크다운 헤딩 (markdown heading)
...
--map은 CLI에서 사용할 수 있지만 MCP 도구로는 노출되지 않습니다. 벤치마크 결과 AI 에이전트가 이를 과도하게 사용하여 정확도를 떨어뜨리는 것으로 나타났습니다.
속도 (Speed)
x86_64 Mac에서의 CLI 실행 시간, 26~1060개 파일의 코드베이스 기준입니다. 약 17ms의 프로세스 시작 시간이 포함되어 있습니다 (MCP 모드에서는 이 비용이 한 번만 발생합니다).
| 작업 (Operation) | ~30개 파일 | ~1000개 파일 |
|---|---|---|
| 파일 읽기 + 타입 감지 (File read + type detect) | ~18ms | ~18ms |
| ... |
검색(Search), 콘텐츠 검색(content search), 그리고 글로브(glob)는 조기 종료(early termination)를 사용하므로, 코드베이스 크기에 관계없이 시간은 대략 일정합니다.
내부 구성 (What's inside)
Rust로 작성되었습니다. 약 20,000 라인. 런타임 의존성(runtime dependencies) 없음.
- tree-sitter — 14개 언어(Rust, TypeScript, TSX, JavaScript, Python, Go, Java, Scala, C, C++, Ruby, PHP, C#, Swift)에 대한 AST 파싱(AST parsing). 정의 감지(definition detection), 피호출자 추출(callee extraction), 호출자 쿼리(callers query), 그리고 구조적 개요(structural outlines)를 위해 사용됩니다.
- ripgrep 내부 로직 (
grep-regex,grep-searcher) — 빠른 콘텐츠 검색 - ignore 크레이트 (crate) — 병렬 디렉토리 순회(parallel directory walking), gitignored 파일을 포함한 모든 파일 검색
- memmap2 — 메모리 맵 파일 읽기 (memory-mapped file reads, 버퍼 미사용)
- DashMap — 동시성 개요 캐시(concurrent outline cache), mtime에 의해 무효화됨
검색은 rayon::join을 통해 정의(definitions)와 사용처(usages)를 병렬로 실행합니다. 피호출자 확인(Callee resolution)은 확장(expand) 시점에 실행됩니다 — tree-sitter 쿼리를 통해 피호출자 이름을 추출하고, 소스 파일의 개요 및 임포트된 파일들을 대상으로 확인합니다. 호출자 쿼리(Callers query)는 동일한 tree-sitter 패턴을 역방향으로 사용하며, 빠른 제거를 위해 memchr SIMD 사전 필터링(pre-filtering)을 사용하여 코드베이스를 순회합니다.
검색 출력 형식은 웨이브릿 다중 해상도(wavelet multi-resolution, 개요 헤더가 상세 조회를 위한 라인 범위를 표시)와 1-홉 피호출자 확장(1-hop callee expansion, 확장된 정의가 피호출자를 인라인으로 확인) 방식을 따릅니다.
이름
tilth — 파종을 위해 준비된 토양의 상태를 의미합니다. 당신의 코드베이스는 토양이며, tilth는 당신이 어디를 파헤쳐야 할지 찾을 수 있도록 구조를 제공합니다.
후원
라이선스
MIT
AI 자동 생성 콘텐츠
본 콘텐츠는 HN Claude Code Search의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기