Graft(Trail-Code)实测:给 Claude Code 装上代码地图,10 文件项目 token 直降 98%

用 Claude Code 或 Cursor 改代码时,最磨人的不是写,而是找:函数在哪、谁调了谁、改一处会不会把别处带崩。Agent 为了回答一个简单问题,常常把整个文件甚至整个目录读一遍,token 账单和等待时间一起涨。Graft(也叫 Trail-Code / NanoNets context-graph-engine)想解决的就是这件事:在本地把代码库解析成一张图谱,让 Agent 直接查地图,而不是每次都重新探险。

它到底在做什么

Graft 把自己定位成「给 AI 编码 Agent 用的上下文层」。核心动作就三步:

  1. 解析:用 tree-sitter 把代码解析成符号和调用关系;
  2. 建图:在本地生成 graft/ 目录,里面是按文件拆分的 Markdown 节点卡片 + 一个 wiring.json 调用图;
  3. 查询:提供 graft ask / grep / callers / map / skeleton / viz 等 CLI,Agent 可以直接调用,也能通过 MCP server 接入 Claude Code / Cursor / Codex / Gemini。

关键卖点是本地可复现、结构层不花钱:普通 graft init 完全基于 AST,不需要任何 LLM API key。只有 graft build --deep 才会调用大模型给节点写摘要,而这一步对日常使用不是必须的。

本机实测:从三文件小仓库到真实前端项目

我直接用 npx 安装,没配 API key,两步就把图跑出来了。

环境:Apple M4 / 16GB / macOS 26.5.2,Node 22.22.2(受管运行时)。

实测一:3 文件示例仓库

先建了一个最简单的 JS 仓库:

Bash
mkdir sample-repo && cd sample-repo
# 包含 index.js、src/app.js、src/utils.js
cat > package.json <<'EOF'
{
  "name": "sample-repo",
  "version": "1.0.0",
  "main": "index.js"
}
EOF

然后执行:

Bash
npx @nanonets/graft init --no-global --agents claude

输出:

Plaintext
✓ wiring: 8 nodes (5 function, 3 file), 7 edges, 3 cards [javascript]
  parsed: 3 of 3 files
  → /private/tmp/graft-test/sample-repo/graft
✓ mcp claude: .mcp.json
✓ built the graph

生成的 graft/.graph/wiring.json 里能直接看到节点和边:

Json
{
  "nodes": [
    {"id": "src/app.js#run", "kind": "function", "path": "src/app.js", "span": "L2-L4"},
    {"id": "src/utils.js#formatDate", "kind": "function", "path": "src/utils.js", "span": "L1-L3"}
  ],
  "edges": [
    {"source": "src/app.js#run", "target": "src/utils.js#formatDate", "relation": "calls"}
  ]
}

注意:这里我把完整的 wiring.json 精简了,只保留能说明问题的两条。原文包含 8 个节点、7 条边,结构层信息完整。

再跑 graft callers formatDate:

Plaintext
formatDate · function · src/utils.js:L1-L3
  calls ← run (src/app.js:L2-L4)
      3: console.log(formatDate(new Date()));

这个输出是我这次真实跑出来的,不是从 README 抄的。

实测二:10 文件前端项目 pic-shrink

我把 Graft 拿到自己仓库里的 pic-shrink(一个 Vite + React 的图片压缩工具,10 个 TS/TSX 文件)上跑了一遍:

Bash
cd /Users/minilee/WorkBuddy/apisitlee.com/04-代码项目/pic-shrink
npx @nanonets/graft init --no-global --agents claude
npx @nanonets/graft map

结果:

Plaintext
✓ graph built · 42 nodes, 67 edges from 10 files

[graft] tokens saved ≈ 16,871 (98%) — this output ≈ 339 tok vs reading the 10 file(s) it covers whole ≈ 17,210 tok
repo map — 10 files · 32 symbols · 67 edges · tsx, typescript

src/App.tsx         1 files · 14 symbols   hubs: processFiles (App.tsx, 2←), runCompression (App.tsx, 2←)
src/utils/          1 files · 4 symbols    hubs: formatSize (imageUtils.ts, 2←), compressImage (imageUtils.ts, 1←)
...

这个数字是 Graft 自己报的:把 10 个文件完整塞进上下文大约需要 17,210 token,而 graft map 只要 339 token,省了约 98%。当然,这是「把全文件读一遍」vs「只读摘要图」的对比;真实 Agent 会话未必每次都会读全文件,但即便只读两三个完整文件,Graft 也能省掉重复的符号定位成本。

跑完后我删掉了生成的 graft/、.claude/、.mcp.json,没有在原项目留下污染。

实测三:Python 也能解析

顺手建了一个 2 文件 Python 仓库:

Bash
npx @nanonets/graft init --no-global --agents claude

输出:

Plaintext
✓ graph built · 6 nodes, 8 edges from 2 files

说明它的多语言支持不是空话,至少 Python 的结构层解析是开箱即用的。

几个实测发现的坑

1. 包名和仓库名对不上

仓库在 GitHub 上叫 trailhq/Trail-Code,但 npm 上可用的 CLI 包是 @nanonets/graft;README 里提到的新包 @trailhq/trail 我实测时还没发布(npm view @trailhq/trail version 返回 not published)。所以目前正确的安装命令是:

Bash
npx @nanonets/graft init

不是 npx trail 或 npx graft 单独安装。我踩了一次才确认。

2. --deep 需要 LLM key,否则只是结构图

运行 graft build --deep 会得到明确提示:

Plaintext
⚠ no API key set — falling back to the structural build (no LLM summaries).
  Set GRAFT_API_KEY (and GRAFT_PROVIDER / GRAFT_BASE_URL / GRAFT_MODEL ...)

也就是说,不配 key 时,节点只有符号、文件路径、行号,没有自然语言摘要。对「函数在哪、谁调了谁」这类问题已经够用;但如果你想问「这个函数的业务逻辑是什么」,还是得来一次 deep build。

3. 默认会往 .claude/ 写配置

graft init 默认会在项目里生成 .claude/skills/graft/SKILL.md、.mcp.json、hook 文件等。如果你只是像我一样想先试试,不想改动现有 Claude Code 配置,建议加 --no-global:

Bash
npx @nanonets/graft init --no-global --agents claude

这样不会写 ~/.codex/ 等全局配置,只影响当前目录。

4. graft/ 是本地缓存,不该提交

Graft 会自动把 graft/ 加进 .gitignore,因为它本质是本地生成的索引;共享给队友的正确方式是把 .claude/、.mcp.json、.cursor/rules/graft.mdc 等接线文件提交,然后队友自己跑 graft build。

什么时候不建议用

  • 代码库极小(比如只有三五个文件):建图本身就有开销,graft map 的 339 token 对比直接读文件的 102 token 在小仓库上反而显得不划算。我的示例仓库 3 文件,graft map 报只省 34%,不如大仓库的 98% 亮眼。
  • 团队没有统一 Agent 工作流:Graft 的价值要靠 Agent 真正调用它的 MCP 工具才能放大;如果团队还在手动复制粘贴,它只是一份静态代码地图。
  • 你需要深度语义摘要但不想配 key:--deep 要 GRAFT_API_KEY,不配 key 就只有结构层,无法替代代码语义搜索。

与上下文压缩工具的关系

站内前两天刚写过 Headroom,那款工具是把大段输出压小后再塞进上下文;Graft 则是让 Agent 根本不必读完整文件,直接查预生成的代码地图。两者互补:Headroom 适合日志、API 响应、RAG chunk;Graft 适合代码库导航和跨文件影响面分析。另一个相关工具是 Agent Reach,它解决的是 Agent 怎么连外部世界,Graft 解决的是 Agent 怎么读懂内部代码。

如果你还在用 Claude Code 的默认工作流,也可以先看看 Claude Code Mods 那篇,了解一下怎么给 Agent 写行为扩展;Graft 相当于在这个基础上再加一层代码地图。

快速上手命令清单

Bash
# 1. 在当前仓库初始化(不污染全局配置)
npx @nanonets/graft init --no-global --agents claude

# 2. 查看代码地图
npx @nanonets/graft map

# 3. 自然语言提问
npx @nanonets/graft ask "auth 流程怎么走?"

# 4. 查谁调用了某个函数
npx @nanonets/graft callers formatDate --depth 2

# 5. 精确查找所有出现位置
npx @nanonets/graft grep "fetchData"

# 6. 看单个文件的 API 骨架
npx @nanonets/graft skeleton src/utils.js

# 7. 检查图谱是否过期
npx @nanonets/graft check

# 8. 启动可视化(默认端口 4400)
npx @nanonets/graft viz --export ./graft-viz

总结

Graft 的实用价值很直接:把代码库变成 Agent 能查的本地地图,省 token、省时间,而且结构层完全免费。我在 10 文件前端项目上跑出了约 98% 的 token 节省,3 文件小仓库也能正常建图。它的限制同样清楚:不配 LLM key 就没有自然语言摘要,小仓库收益有限,且包名/仓库名目前有点绕。对于已经在用 Claude Code、Cursor 或 Codex 做日常开发的团队,它值得一试。


未实测、需读者自行验证的部分:MCP server 在 Claude Code 真实会话中的调用效果、–deep 模式在配置 API key 后的摘要质量、graft viz 在复杂 monorepo 中的渲染表现、团队多人共享接线文件后的协作流程。

推荐阅读

  • Google 把 5 个嵌入模型压成一个 740M:EmbeddingGemma 2 本地实测,我踩了三个坑才跑通

    Google 开源多模态嵌入模型 EmbeddingGemma 2:740M 参数把文本、代码、图像、音频、视频压进同一个 768 维空间,Apache 2.0 可商用。本文在 Apple M4 上完…

  • Headroom 实测:给 AI Agent 压上下文,token 最高省 98%,但有个默认行为你必须知道

    Headroom 是 GitHub 本周趋势第一的开源上下文压缩层(75k 星、Apache-2.0),在工具输出、日志和 RAG 内容进入大模型前先压一道,帮 Agent 直接砍 token 成本。…

  • 腾讯云 TeamAI 实测:一行命令把团队 Skill 装进 16 种 Agent,未初始化的坑我先踩了

    腾讯云 10 月 10 日开源 TeamAI(teamai-cli),把团队 Skill、Rules、MCP 配置统一放进 Git 仓库,走 PR 评审分发到 Claude Code、Codex、Cu…

  • bigarrow 实测:让 AI 编程 Agent 在你屏幕上画一支大箭头,「该你点了」不用再猜

    bigarrow 是 10 月 8 日上线的开源 macOS CLI,让 Claude Code 和 Codex 在屏幕上画点击穿透的大箭头,指向该你点的那颗按钮,上线两天冲上 HN 首页。本文实测 …