Graft: Bản đồ codebase cho coding agent
Graft dùng tree-sitter tạo bản đồ codebase và cho coding agent truy vấn qua MCP, giúp tránh dùng grep để tìm lại cấu trúc repository trong mỗi session.
Bản đồ codebase cho coding agent là gì
Bản đồ codebase cho coding agent là một index lâu dài của repository. Agent tra cứu thông tin trong đó thay vì mỗi session mới lại dùng grep để dò từ đầu. Graft là một triển khai của ý tưởng này. Nó phân tích code bằng tree-sitter, ghi ra một thư mục gồm các node markdown được liên kết cùng một wiring graph theo từng symbol, rồi cung cấp các công cụ retrieval qua MCP (model context protocol, interface tiêu chuẩn để coding agent gọi các công cụ bên ngoài).
Graft không phải proxy và cũng không phải gateway. Không có thành phần nào nằm giữa agent của bạn và model API. Bản đồ là một thư mục trên disk mà agent đọc. Phân biệt này quyết định bạn đang giải quyết vấn đề nào: token gateway tự host đo lường và định tuyến các request bạn đã gửi, còn bản đồ thay đổi tổng số request mà bạn cần gửi.
Kỹ thuật này có từ trước tool này và sẽ vẫn được sử dụng sau khi tool này không còn tồn tại. Hãy học kỹ thuật trước, sau đó mới tìm hiểu cách triển khai.
Các coding agent đốt context để tìm lại cấu trúc
Hãy xem một agent bắt đầu làm việc trên một repository mà nó đã thấy 50 lần. Nó liệt kê các thư mục. Nó dùng grep để tìm một symbol. Nó mở 3 file để xác định file nào định nghĩa function, rồi mở file thứ 4 để tìm xem function đó được gọi ở đâu. Không việc nào trong số đó là task. Đó là bước định hướng, và mỗi session đều phải trả chi phí bằng input token cho bước này.
Nguyên nhân rất đơn giản. Model không có memory giữa các session. Mọi thứ agent đã học về layout của bạn nằm trong context window bị loại bỏ khi session kết thúc. Vì vậy, cùng một quá trình khám phá lại chạy từ đầu với đầy đủ chi phí. Trong một repository lớn, giai đoạn định hướng tốn nhiều hơn phần chỉnh sửa: 10 lần gọi tool để tìm code, 1 lần để thay đổi code. Định hướng chỉ là một nửa chi phí đó và phần chỉnh sửa là nửa còn lại. Vì vậy, một skill buộc agent chỉ thực hiện thay đổi nhỏ nhất có hiệu quả nên được dùng cùng một bản đồ, thay vì chọn một trong hai.
Bản đồ phá vỡ vòng lặp đó bằng cách chuyển việc khám phá khỏi model và lưu vào disk. Một parser duyệt repository một lần, ghi lại symbol nào được định nghĩa ở đâu và symbol nào gọi symbol nào, rồi cập nhật bản ghi đó khi code thay đổi. Agent chỉ cần đặt một câu hỏi và nhận được câu trả lời kèm file và line. Việc khám phá lặp lại trở thành một lần tra cứu rẻ hơn.
Bạn đã dùng một phiên bản đơn giản hơn của cách này. AGENTS.md nêu các quy ước của bạn giúp agent không phải suy ra lại các quy ước đó mỗi lần. Bản đồ được tạo tự động giúp agent không phải suy ra lại cấu trúc. Khác biệt nằm ở người tạo ra nó. Bạn tự viết file hướng dẫn nên file này vẫn nhỏ. Parser tạo bản đồ nên bản đồ có thể bao quát 10000 file. Để biết budget thực sự được sử dụng vào đâu trong một session, cách Claude Code sử dụng context window trình bày phần phân bổ chi phí.
Graft thực sự tạo ra gì
Hai artefact, cả hai đều nằm trong một thư mục graft/ tại thư mục gốc của repository.
Artefact thứ nhất là một node graph được viết dưới dạng markdown liên kết, mỗi node nằm trong một file riêng. Mỗi node chứa phần tóm tắt bằng tiếng Anh đơn giản, một “crux” gồm các dòng logic quan trọng được trích từ source, các file source chính xác kèm content hash, các wikilink có kiểu đến những node khác (depends_on, part_of, uses, implements), và một phần ghi chú được giữ lại qua mỗi lần tạo lại để bạn ghi lại ngữ cảnh mà parser không thể suy ra.
Artefact thứ hai là graft/.graph/wiring.json, structural graph theo từng symbol mà tree-sitter trích xuất: các definition, reference và call edge giữa chúng.
Việc tách đôi này quan trọng vì chỉ một nửa cần model. graft build chỉ dùng tree-sitter và không bao giờ gọi LLM (large language model), nên kết quả có tính deterministic và không tốn chi phí. graft build --deep bổ sung các phần tóm tắt bằng văn bản và crux theo từng symbol; những phần này cần gọi model và bạn phải trả phí.
Mức hỗ trợ ngôn ngữ được chia theo tier, và tier cho biết bạn có thể tin cậy call graph đến mức nào. TypeScript, JavaScript, Python, Go và Java được resolution xuyên file có nhận biết scope. Rust, C, C++, C#, Ruby, PHP, Kotlin, Scala, Swift, Elixir, Solidity, OCaml, Zig và Dart có symbol cùng call edge tổng quát. Điều này có nghĩa là một edge có thể chỉ dựa trên việc khớp tên thay vì reference đã được resolution. Các edge ở cấp compiler là tùy chọn với --lsp và một language server như rust-analyzer hoặc gopls.
Cài đặt Graft và ghim phiên bản
Graft cần Node.js 20 trở lên và được cấp phép theo MIT. Tính đến tháng 8/2026, bản phát hành hiện tại là 0.10.1, còn phiên bản đầu tiên được phát hành là 0.1.0 vào tháng 7/2026. Hãy xem đây là phần mềm còn mới.
npm install -g @nanonets/graft@0.10.1
npm ls -g @nanonets/graftnpm ls -g phải in ra @nanonets/graft@0.10.1. Hãy cố ý ghim phiên bản đó. Một npm install -g @nanonets/graft không chỉ định phiên bản sẽ phân giải thành tag latest tại thời điểm bạn chạy lệnh. Với một dự án phát hành nhiều bản minor mỗi tháng, điều này khiến công cụ bạn dùng vào thứ Ba khác với công cụ đồng nghiệp đã cài vào thứ Hai. Phiên bản được ghim giữ cho các CLI flag và định dạng graph giống nhau trên mọi máy, vì vậy bạn chỉ nâng cấp khi chủ động quyết định.
Sau đó, tích hợp nó vào một repository do bạn sở hữu:
cd /path/to/your/repo
graft init --dry-run
graft initgraft init hỏi bạn muốn tích hợp với coding agent nào, rồi xây dựng graph. Trước tiên hãy chạy --dry-run và đọc danh sách file mà lệnh dự định thay đổi, vì một số file nằm bên ngoài repository. graft init có tính idempotent và không ghi đè config hiện có, nên chạy lại lần thứ hai vẫn an toàn.
Tính đến tháng 8/2026, phần tích hợp hỗ trợ Claude Code, Cursor, Codex, GitHub Copilot, Google Gemini, Kiro, Windsurf và AdaL. Claude Code có mức tích hợp sâu nhất: một mục MCP server, statusline hiển thị kích thước và độ cũ của graph, các post-edit hook để xây dựng lại graph, cùng một skill file trong .claude/. Các agent còn lại nhận một instruction hoặc rule file cho biết các tool này tồn tại. Vì vậy, “được hỗ trợ” có nghĩa là Graft ghi phần tích hợp; nếu một agent bỏ qua rule file của chính nó thì nó cũng sẽ bỏ qua map. Đó là lý do thông thường khiến agent bỏ qua các instruction bạn viết cho chúng, và trường hợp này cũng không ngoại lệ.
Những gì được ghi vào repository và những gì không được đưa vào git
Sau graft init, bạn sẽ thấy các mục sau:
graft/: node graph dạng markdown vàgraft/.graph/wiring.json. Mục này được thêm vào.gitignorecho bạn..mcp.json: đăng ký MCP server graft để Claude Code tự khởi động server..claude/settings.json: được merge tại chỗ, bổ sung statusline và các post-edit hook.AGENTS.md,GEMINI.md,.github/copilot-instructions.md,.cursor/rules/graft.mdc,.kiro/steering/graft.md,.windsurf/rules/graft.mdvà.adal/skills/graft/SKILL.md: các section được bao quanh bằng marker, nối vào những file tương ứng với các agent bạn đã chọn.~/.codex/config.toml,~/.codex/hooks.jsonvà~/.codex/hooks/graft/graft-hooks.cjs: áp dụng trên toàn máy, chỉ được ghi khi bạn chọn Codex.graft init --no-globalbỏ qua các mục này, còngraft init --no-hookschỉ bỏ qua hook shim.
Graph là một cache, giống như node_modules. Không commit graph này. Nó được tạo lại từ code chỉ trong vài giây, thay đổi gần như sau mỗi lần chỉnh sửa, và việc commit nó sẽ biến một thay đổi một dòng thành diff gồm vài trăm file mà không reviewer nào đọc hết. Hãy commit phần wiring, trong đó có AGENTS.md và .mcp.json. Thành viên trong nhóm chỉ cần clone repository, chạy graft build, rồi sẽ có graph riêng trên máy của họ.
Kiểm tra xem ignore rule đã được ghi trước khi commit lần đầu:
grep -n graft .gitignore
git status --shortgrep phải in ra một dòng có chứa graft/, còn git status --short phải không liệt kê mục nào bên dưới graft/. Nếu output hiển thị các file bên dưới graft/, nghĩa là ignore entry bị thiếu hoặc bị ghi đè ở nơi khác. Hãy sửa trước khi commit, vì git tiếp tục theo dõi một file sau khi file đó đã được thêm vào, và một lần chỉnh sửa .gitignore sau đó sẽ không bỏ theo dõi file này.
Nếu muốn tự đăng ký MCP server hoặc cố định server ở đúng version bạn đã cài, entry này khá ngắn:
{
"mcpServers": {
"graft": {
"command": "npx",
"args": ["-y", "@nanonets/graft@0.10.1", "mcp"]
}
}
}Các công cụ truy xuất mà agent gọi thay cho grep
Graft cung cấp 6 tool qua MCP. graft_find_code trả về các node được xếp hạng cho mô tả tác vụ, kèm file và dòng. graft_file_api trả về mọi signature trong một file mà không có phần thân. graft_trace_calls lần theo các caller hoặc callee qua nhiều cấp. graft_find_all trả về các kết quả khớp regex được nhóm theo symbol. graft_repo_map cung cấp cái nhìn ban đầu về một repository chưa quen thuộc. graft_check_freshness cho biết graph còn khớp với code hay không.
Mỗi tool đều có một CLI tương ứng. Đây là cách kiểm tra chính xác những gì agent thực sự nhận được:
graft map .
graft ask "where do we validate the refresh token"
graft skeleton src/auth/session.ts
graft callers validateRefreshToken
graft callers validateRefreshToken --direction out
graft grep "refresh_token" --jsongraft ask phải in ra các node được xếp hạng cùng các tham chiếu file:line, thay vì nội dung file. Đó là toàn bộ cơ chế: agent nhận một pointer rồi mở một file, thay vì đọc 10 file để tìm đúng file. graft viz mở một trình xem tương tác trên localhost nếu bạn muốn tự xem graph. Nếu graft ask không trả về thông tin hữu ích cho một câu hỏi mà bạn có thể trả lời trong 30 giây, graph đã stale hoặc ngôn ngữ của bạn thuộc broad tier, và bản đồ cũng sẽ không giúp được agent.
Có một chi phí dễ bị bỏ qua. 6 định nghĩa tool được inject vào system prompt của mọi request trong toàn bộ session. Bạn vẫn phải trả chi phí này dù agent có dùng bản đồ hay không. Với một repository đủ nhỏ để chứa trong context, chi phí cố định này có thể lớn hơn phần khám phá mà nó tiết kiệm.
Điều gì xảy ra với graph khi code thay đổi
Cập nhật cấu trúc nhanh và tự động. Graft đọc working tree thay vì git, nên một thay đổi chưa commit và một thay đổi đã stage đều được Graft nhìn thấy như nhau. Mỗi query chỉ phân tích lại các file có stat thay đổi. Tài liệu của project ghi nhận phần overhead này vào khoảng 3 ms. Việc rebuild khi kết thúc một lượt chỉ xử lý các file có code được di chuyển. Đặt GRAFT_NO_REFRESH=1 hoặc truyền --no-refresh để trả lời từ graph trên disk mà không phân tích lại. Truyền --no-reuse để buộc phân tích lại toàn bộ từ đầu. Đây là tùy chọn cần dùng sau khi nâng cấp chính Graft.
Phần do model viết hoạt động khác và đây là phần dễ âm thầm trở nên sai. Summaries và cruxes được cache. Mỗi node ghi lại content hash của các source. Khi một source file thay đổi, node được đánh dấu là stale thay vì được xem là hiện hành. Cờ này chỉ có tác dụng nếu có thành phần xử lý nó. Refresh bằng graft build --deep. Thao tác này sẽ dùng lại token của model.
Hiển thị trạng thái stale:
graft check .
echo $?Exit status 0 nghĩa là graph khớp với code. Exit status 1 nghĩa là có drift. Chạy lệnh này từ pre-push hook hoặc trên branch trong CI để một map đã cũ sáu tháng không thể trả lời một cách chắc chắn về code đã được viết lại vào tháng 3.
Đọc kỹ các số liệu benchmark đã công bố
Tuyên bố chính của Graft là “rẻ hơn tới 4x và nhanh hơn 3x, với độ chính xác tốt hơn hoặc không suy giảm”. Các con số này đến từ benchmark do chính dự án thực hiện và công bố trong README. Dưới đây là đầy đủ kết quả của 2 lần chạy mà dự án báo cáo.
The data behind this chart
[
{
"label": "Controlled sweep",
"run_count": 162,
"token_saving_pct": 42,
"tool_call_saving_pct": 46,
"correctness_pct": 93,
"baseline_correctness_pct": 93
},
{
"label": "SWE-bench Verified",
"run_count": 50,
"token_saving_pct": 23,
"tool_call_saving_pct": 25,
"correctness_pct": 66,
"baseline_correctness_pct": 54
}
]Bài kiểm tra có kiểm soát gồm 162 lần chạy trên 2 repository, trong đó có chính Graft, với 3 trial cho mỗi task. Kết quả ghi nhận ít hơn 42% token và ít hơn 46% lần gọi tool. Lần chạy SWE-bench Verified gồm 50 instance, với cùng model được dùng cho cả 2 nhánh, và ghi nhận mức tiết kiệm thấp hơn: sử dụng ít hơn 23% token và ít hơn 25% lần gọi tool. Một lần chạy thứ ba đã tái hiện 5 pull request của PocketBase đã được merge, với chi phí 11.02 US dollars so với 13.91 của baseline.
Hãy xem tất cả các kết quả này là vendor benchmark. Có 2 yếu tố giới hạn những gì bạn có thể suy ra. Bài kiểm tra có kiểm soát bao gồm repository của Graft, tức codebase mà các tác giả đã tối ưu tool của họ dựa trên đó. SWE-bench Verified là một dataset công khai gồm các issue từ những dự án Python open-source phổ biến. Các tool thường được tối ưu cho dataset công khai, dù đó có phải chủ đích hay không. Không kết quả nào trong 2 kết quả này phản ánh private monorepo của bạn, vốn có quy ước đặt tên riêng và phần dead code riêng.
Độ chính xác cần được xem xét kỹ hơn. Trong bài kiểm tra có kiểm soát, độ chính xác không thay đổi: 93% khi dùng map so với 93% khi không dùng. Mức tăng lên 66% từ 54% chỉ xuất hiện trên SWE-bench Verified. Một tool giúp giảm chi phí token mà chất lượng không giảm vẫn là một lựa chọn đáng cân nhắc. Tuy nhiên, đừng lấy kết quả độ chính xác trên SWE-bench ghép với kết quả token của bài kiểm tra có kiểm soát rồi trích dẫn cả 2 như một tuyên bố duy nhất.
Tự đo delta token của bạn trước khi tin vào bất kỳ con số nào
Con số duy nhất đáng tin là con số lấy từ repository của bạn. Phương pháp này mất một buổi chiều.
Chọn một tác vụ có thể lặp lại chính xác. Câu hỏi phù hợp hơn chỉnh sửa, vì chỉnh sửa sẽ thay đổi repository và lần chạy thứ hai không còn là cùng một thử nghiệm. “Module nào áp dụng rate limit cho login route” là dạng câu hỏi phù hợp.
Bật telemetry và gửi dữ liệu đến terminal của bạn:
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=console
claudeConsole exporter in các bản ghi metric ngay khi chúng được thu thập. Trường bạn cần là claude_code.token.usage, chứa một thuộc tính type có giá trị input, output, cacheRead hoặc cacheCreation. Orientation xuất hiện trong input và cacheRead, vì nội dung file được ghi vào đó. Cộng hai giá trị này lại.
Chạy tác vụ 3 lần, mỗi lần trong một session mới, khi map đã được nối. Sau đó xóa entry graft khỏi .mcp.json rồi chạy thêm 3 lần. So sánh median thay vì từng lần chạy riêng lẻ, vì các lần chạy của agent dao động rất lớn và một lần chạy không thuận lợi có thể cho bạn kết luận ngược với thực tế. Đồng thời ghi lại số lần gọi tool: tool call là cơ chế, còn token là kết quả. Vì vậy, nếu token giảm nhưng số lần gọi tool không giảm thì đã có yếu tố khác thay đổi.
Sau đó trừ các chi phí mà benchmark không hiển thị. graft build --deep sử dụng token của model cho mỗi lần refresh đầy đủ. Sáu tool schema được gửi kèm trong mọi request. Nếu agent chạy trên server bạn thuê, đặt giới hạn cứng cho chi phí agent sẽ biến khoản phát sinh bất ngờ thành một ngân sách, còn telemetry của coding agent thực sự báo cáo gì giải thích những dữ liệu rời khỏi máy sau khi bạn bật exporter.
Khi nào codebase map không còn hữu ích?
- Repository đã vừa với context. Một service nhỏ không cần map, và bạn vẫn phải trả chi phí cho 6 tool schema trong mỗi request. Nếu hiện tại agent có thể tìm mọi file chỉ bằng 1 hoặc 2 tool call, hãy bỏ qua map.
- Ngôn ngữ của bạn thuộc nhóm hỗ trợ rộng. Các call edge tổng quát có thể khiến
graft callersbỏ sót caller hoặc tạo ra caller giả do trùng tên. Hãy xác nhận bằnggraft greptrước khi tin vào phạm vi ảnh hưởng. - Graph đã lỗi thời nhưng không ai nhận ra.
graft checkthoát với mã 1 khi phát hiện drift. Điều này chỉ hữu ích nếu có thứ chạy nó. Hãy dùng hook hoặc một bước trong CI, không dựa vào thói quen. - Monorepo cần giới hạn phạm vi. Một monorepo dùng một git repository sẽ được tự động tách theo workspace file,
go.mod,pyproject.tomlhoặcCargo.toml, còngraft ask "..." --in services/billing/giới hạn query vào một sub-project. Cách tư duy dẫn đến việc dùng các file AGENTS.md lồng nhau cho từng package cũng áp dụng cho map. - Agent bỏ qua phần wiring. Hãy theo dõi các tool call trong một session thực tế trước khi kết luận map đang được sử dụng. Nếu agent vẫn chạy
grep, điều đó cho thấy nó chưa từng đọc file rules.
FAQ
Tôi có nên commit thư mục graft/ vào git không?
Không. graft build tự động thêm graft/ vào .gitignore của bạn, vì graph là một cache có thể tạo lại giống như node_modules. Graph thay đổi sau gần như mọi lần chỉnh sửa, nên commit thư mục này sẽ che khuất các diff thực sự dưới hàng trăm file được tạo tự động. Hãy commit phần wiring cho agent biết map tồn tại, trong đó có AGENTS.md và .mcp.json, rồi để từng thành viên chạy graft build cục bộ. Xác minh bằng grep -n graft .gitignore và git status --short trước commit đầu tiên, vì git tiếp tục theo dõi một file sau khi file đó đã được thêm, còn chỉnh sửa .gitignore về sau không làm git ngừng theo dõi file.
Chạy Graft có mất phí không?
Phần cấu trúc thì không. graft build, graft ask, graft check và 6 công cụ MCP retrieval là các thao tác tree-sitter, không bao giờ gọi model. graft build --deep là phần có phí: nó dùng LLM để viết các bản tóm tắt bằng tiếng Anh đơn giản và crux cho từng symbol, được cấu hình bằng GRAFT_PROVIDER, GRAFT_API_KEY và GRAFT_MODEL, cùng với GRAFT_BASE_URL cho mọi endpoint tương thích với OpenAI. Bạn có thể chạy Graft chỉ với phần cấu trúc mà không tốn token nào cho chính graph.
Codebase map thực sự tiết kiệm được bao nhiêu trên repository của tôi?
Không ai có thể trả lời nếu chưa đo. Dự án báo cáo ít hơn 42% token trong đợt chạy 162 lần của chính dự án và ít hơn 23% trên SWE-bench Verified, cả hai đều so với baseline không có map. Đây đều là benchmark do nhà cung cấp công bố; một benchmark được chạy một phần trên chính repository của Graft, và không benchmark nào mô tả code riêng của bạn. Hãy chạy cùng một câu hỏi có thể lặp lại 3 lần với map và 3 lần không có map, đặt CLAUDE_CODE_ENABLE_TELEMETRY=1 và OTEL_METRICS_EXPORTER=console, rồi so sánh median của claude_code.token.usage cho các loại input và cacheRead.
Graph thay đổi thế nào khi tôi refactor?
Phần cấu trúc tự phân tích lại. Graft kiểm tra working tree và chỉ phân tích lại các file đã thay đổi, nên rename sẽ được nhận ra ở query tiếp theo với overhead khoảng 3 ms. Graft cũng thấy các thay đổi chưa commit vì nó đọc file thay vì lịch sử git. Các bản tóm tắt do model viết mới là phần bị stale: mỗi node lưu content hash của các source tương ứng, và khi source thay đổi, node được đánh dấu stale thay vì được viết lại. Chạy graft check . để xem độ lệch, rồi chạy graft build --deep để refresh phần nội dung đã viết.
Hiện nay những coding agent nào có thể dùng Graft?
Tính đến tháng 8 năm 2026, graft init tích hợp Claude Code, Cursor, Codex, GitHub Copilot, Google Gemini, Kiro, Windsurf và AdaL. Claude Code nhận được nhiều tích hợp nhất: một MCP server entry trong .mcp.json, statusline, post-edit hooks và skill file tại .claude/. Codex nhận một section AGENTS.md cùng các entry cấp toàn máy trong ~/.codex/, nhưng graft init --no-global bỏ qua các entry này. Các agent còn lại nhận rules file hoặc steering file. Mọi MCP client khác đều có thể dùng server trực tiếp bằng cách đăng ký command npx -y @nanonets/graft@0.10.1 mcp.