CodeGraph /agent-eval:用 A/B 评测框架在真实仓库上量化代码知识图谱的检索价值
本文围绕 CodeGraph 仓库中的 agent-eval 技能(/agent-eval)展开:它是一套"带 CodeGraph 的 Agent vs 只用 grep/read 的 Agent"对照实验流程,用于在选定的真实开源仓库上审计某个 codegraph 版本(本地开发构建或已发布的 npm 版本)的检索质量。读完本文,你将掌握完整的六步审计工作流、audit.sh 的底层四阶段管线、A/B 双臂的污染控制机制,以及如何解读每次运行产出的三项检索反馈指标与对照表。
这个 Skill 解决什么问题
CodeGraph 把代码库预索引成符号与调用关系图谱,供 Claude Code、Codex、Cursor 等 Agent 通过 MCP 工具查询。"它对 Agent 到底有没有帮助、帮助多少"不能靠感觉回答,必须做受控实验:同一个真实仓库、同一个问题、同一个模型,唯一变量是 Agent 能否使用 CodeGraph。
.claude/skills/agent-eval/SKILL.md 就是这个实验的操作手册(Skill 定义)。它的定位是:
Measures how much CodeGraph helps an agent versus plain grep/read, for a chosen codegraph version on a chosen real-world repo.
它驱动的全部执行逻辑在 scripts/agent-eval/ 目录的脚本簇中:audit.sh 是入口,run-all.sh 是 A/B 引擎,parse-run.mjs / parse-session.mjs 负责从运行日志里还原工具调用序列与指标。Skill 本身只负责"问对人、传对参数"。
前置条件
SKILL.md 明确要求的环境(macOS / Linux):
tmux3+(交互式 harness 需要);- 已登录的
claudeCLI(headless 与交互两种模式都用它); node、git;- 必须在 codegraph 仓库根目录下执行(
audit.sh依赖仓库内的scripts/local-install.sh来构建/恢复本地开发链接)。
六步审计工作流
SKILL.md 给出的标准检查清单是:
- [ ] 1. Pick version (local or npm)
- [ ] 2. Pick language
- [ ] 3. Pick repo by size
- [ ] 4. Pick harness (headless / tmux / both)
- [ ] 5. Run audit.sh in the background
- [ ] 6. Report results
下面逐步说明,并补充每一步在源码中的实际行为。
Step 1 — 选择被测版本(VERSION token)
询问用户测试哪个 codegraph 版本,并映射为三个取值之一:
| 用户选择 | VERSION token | 实际行为 |
|---|---|---|
| Local dev build | local |
在 codegraph 仓库内执行 local-install.sh,构建当前代码并 npm link 成全局 codegraph |
| Latest published | latest |
npm install -g @colbymchenry/codegraph@latest |
手输具体版本号(如 0.7.10) |
该字符串 | npm install -g @colbymchenry/codegraph@0.7.10 |
这个映射在 audit.sh 中落地:local 走本地构建脚本,其余一律通过 npm 全局安装对应版本,随后用 codegraph --version 打印实际生效版本用于日志留痕。
Step 2 — 选择语言
语言清单不是写死的,而是从语料库 .claude/skills/agent-eval/corpus.json 动态读取。该文件按语言组织条目,目前覆盖 TypeScript、JavaScript、Go、Python、Rust、Java、Kotlin、Swift、C#、Ruby、PHP、C、C++、Dart、Svelte、Lua、Luau、Objective-C、Mixed iOS(Swift+ObjC)、React Native(legacy bridge / Fabric / TurboModule)、Expo Modules、R、COBOL、VB.NET、Erlang、Solidity、CUDA、Terraform、ArkTS、Nix 等约 30 个语言/技术栈分类,每类通常按 Small / Medium / Large 三档各备一个仓库(如 TypeScript 的 excalidraw 约 600 文件、vscode 约 10000 文件)。
Step 3 — 按规模选择仓库
从所选语言的条目中挑仓库,SKILL.md 要求选项标签带上规模与文件数,例如 excalidraw — Medium (~600 files)。每条 corpus 条目包含五个字段:
name:corpus 目录名,也是audit.sh的第二个参数;repo:git 仓库 URL(缺失时由 harness 浅克隆);size/files:规模档位与文件数估计(Small < ~150、Medium ~150–1500、Large > ~1500,见 corpus.json 头部注释);question:一个能考察"跨文件理解"的代表性架构问题,例如 "How does Excalidraw render and update canvas elements?"。这个字符串会作为 Agent 收到的提示词原样传入。
Step 4 — 选择 harness(MODE token)
| 用户选择 | MODE token | 运行方式 | 特点 |
|---|---|---|---|
| Headless | headless |
claude -p + stream-json |
精确的 token/cost 统计与干净的工具调用序列;每臂 2 runs,快、无 TTY |
| Interactive (tmux) | tmux |
tmux 内驱动真实 Claude TUI | 复现用户实际可见的 Explore 子代理行为,指标从 session 日志解析;每臂 2 runs,较慢 |
| Both | all |
上面两种都跑 | 共 4 runs |
两种模式各自的实现值得看一眼:
- headless 臂由 run-all.sh 的
headless()函数驱动:在目标仓库目录下执行claude -p "<question>",带--output-format stream-json --verbose、--permission-mode bypassPermissions、--model sonnet --effort high、--max-budget-usd 4,以及关键的--strict-mcp-config --mcp-config <cfg>。模型策略在 agent-eval-feedback-metrics.md 中被标注为"不可谈判":所有臂统一 sonnet + high effort——在最低配模型上成立的能力增强,才能向更强模型泛化。 - 交互式臂由 itrun.sh 实现:
tmux new-session开一个 230×60 的宽面板(避免 TUI 硬折行),自动应答"是否信任此目录"对话框,用"输入并校验首 24 字符落屏"的循环绕过欢迎页与 MCP 初始化的按键竞争,最后以"面板内容稳定 16 次轮询(约 8 秒)且出现提示符"判定 Agent 完成——注释里解释了为什么不能依赖 spinner 字符串:扩展思考模型在流式输出最终答案阶段不显示忙碌指示,基于 spinner 的短超时会在答案中途杀掉运行。
run-all.sh 还支持多轮问题:用 || 分隔多个问题,第 2 轮起通过 --resume <session-id> 续接同一会话(run-all.sh)。这是有意设计——工具响应会一直占住上下文窗口,单轮 A/B 结构性地看不到这份"后续轮次要买单"的成本。
Step 5 — 后台运行 audit.sh
确认四个选择后,在 codegraph 仓库根目录后台启动:
scripts/agent-eval/audit.sh <VERSION> <repo-name> <repo-url> "<question>" <MODE>
参数即前四步的产物,例如 scripts/agent-eval/audit.sh local excalidraw <url> "How does Excalidraw render and update canvas elements?" headless。从源码看,audit.sh 内部是四阶段管线:
- 设置被测版本:
local走local-install.sh,其余npm install -g @colbymchenry/codegraph@<VERSION>,并打印 PATH 上实际解析到的codegraph及其版本; - 确保语料仓库就位:corpus 目录默认
/tmp/codegraph-corpus(可用环境变量CORPUS覆盖),目录缺失则git clone --depth 1,已存在则直接复用; - 清库重建索引:
rm -rf "$REPO/.codegraph"后在目标仓库执行codegraph init -i。SKILL.md 的 Notes 解释了这一步不可省略的原因——索引必须由提供查询的同一个二进制构建,因为不同版本的抽取行为不同; - 跑 A/B:委托给
run-all.sh <repo> "<question>" <MODE>。
结束后 audit.sh 会再次执行 local-install.sh 把全局 codegraph 恢复为开发构建(audit.sh);恢复失败会打印 WARN 提示手动执行。整个流程需要数分钟,所以 SKILL.md 明确要求后台运行。
Step 6 — 读取日志并汇报
任务结束后,按臂(arm)读取日志汇报:
- headless 臂(parse-run.mjs):总工具调用数、
Read文件次数、Grep/Bash 次数、codegraph 工具调用次数、耗时、总花费; - 交互式臂(parse-session.mjs):
VERDICT: codegraph_explore used Nx | Read N | Grep/Bash N与TOKENS:行; - 两条路径都会追加输出三项检索反馈指标——residual context occupancy(残留上下文占用)、explore sufficiency(explore 充分性)、allocation efficiency(分配效率)——headless A/B 收尾还会打印并排的
ARM COMPARISON对照表(由 compare-arms.mjs 生成)。
SKILL.md 对汇报方式有明确纪律:先看对照表的污染行。CLI calls that RETURNED output > 0 意味着该臂通过 Bash 绕过了 MCP 直连了 codegraph,这组数据整体作废。其余指标的解读入口是 docs/benchmarks/agent-eval-feedback-metrics.md,其中强调"以 cost + 工具/Read 次数为先导信号;原始 token in/out 被子代理委派与 prompt caching 混淆,不可单独引用"。最后要说明两点结论:codegraph 是否降低了工作量,以及两臂是否都给出了正确答案。
A/B 框架的核心:把"唯一变量"做到物理级
run-all.sh 的头部注释一句话概括了设计:codegraph 是唯一的变量——with 臂挂只含 codegraph 的 MCP 配置(指向本次安装的 CG_BIN),without 臂挂空 MCP 配置;两臂都保留内置 Read/Grep/Bash。围绕这个不变式,源码里有三道防线,值得逐一了解。
1. 中性化环境 prompt-hook。 ~/.claude 下可能存在的 CodeGraph prompt-hook 会向每个提示词注入代码上下文,这会同时污染 without 臂(白送结构信息)和 with 臂(重复计数)。run-all.sh 在两臂统一导出 CODEGRAPH_NO_PROMPT_HOOK=1 关闭它。
2. 两层 CLI 封锁。 without 臂的仓库里躺着 .codegraph/ 索引,codegraph 二进制也在 PATH 上——Agent 完全可以绕开 MCP 直接调 CLI,把"无 codegraph 臂"变成"CLI 版 codegraph 臂"。实测事故写在 no-cli-shim.sh 注释里:某次 7 仓库批量跑中 15 个 without 臂里有 14 个都这么干了;后来 Agent 被 PATH 拦截后又执行了 find / -iname "*codegraph*" 用绝对路径调用。于是封锁做成两层:
- PATH 层:把包含
codegraph的目录就地替换为一个镜像目录——符号链接指向原目录里除 codegraph 外的所有条目,保持 PATH 顺序与优先级不变(不能整个目录删掉,因为claude等工具与它同目录); - PreToolUse hook 层:生成一个
hook-settings.json,对 Bash 工具做正则审查(只匹配命令位置,grep codegraph src/、ls .codegraph、which codegraph这类"查看"行为放行),命中即输出permissionDecision: deny。脚本最后还有自检探针:必须能拦截绝对路径调用、同时不误伤纯提及,否则拒绝开跑。
3. 污染计数器(检测半边)。 预防措施会静默失效(比如二进制换位置后 PATH 层失配),所以 parse-run.mjs 用独立的正则 CG_CLI_RE 复核每条 Bash 命令,区分"被拦截的尝试"(无输出进入窗口,良性)与"实际返回了输出的调用"(污染,该臂数据作废),并在 ARM COMPARISON 表中作为 contamination 行展示。
此外,MCP 配置以文件形式生成到输出目录(mcp-codegraph.json / mcp-empty.json)而不是内联 JSON,避免穿过 tmux 的引号转义问题;输出目录默认 /tmp/agent-eval(AGENT_EVAL_OUT 可覆盖),并可用 CG_ARMS=with|without 只重跑单臂而不重做另一臂。
三项反馈指标:表回答"动没动",块回答"为什么"
parse-run.mjs 每次运行都输出三个指标块,compare-arms.mjs 再把两臂并排。三者各答一个问题,且一次检索改动可能只移动其中一个:
| 指标 | 回答的问题 |
|---|---|
| Residual context occupancy(CG-7) | 运行结束时,该臂的检索结果还占着窗口多少 token——即后续每一轮必须在多小的剩余空间里工作 |
| Explore sufficiency(CG-8) | 某次 explore 的响应"够不够":看 Agent 随后做了什么(再次 explore / Read 已返回文件 / Read 未返回文件 / Grep / 直接作答) |
| Allocation efficiency(CG-9) | 响应花掉的字节里,有多大比例流向最终答案真正引用的文件 |
实现上有几个从源码可以直接确认的细节:
- 占用是测出来的,不是 bytes/4 估的。每次 assistant 请求的
usage(input + cache_read + cache_creation)之和就是该请求完整 prompt 的精确 token 数,相邻请求的差值按追加内容的字符比例分摊到各工具结果上;再用"≥80% 由工具结果构成的 gap"集合标定 chars/token 比率,实测 explore 输出约 2.3 chars/token,bytes/4 会低估约 40%。 - 微压缩按 FIFO 驱逐。窗口中途缩水时,最旧的 tool_result 先被丢弃,解析器按此模拟驱逐;
compact_boundary事件则整体清空。 - sufficiency 桶与修复方向一一对应(见 parse-run.mjs):
explore again→ 响应没答上(分配或召回问题,需看后续 query 判别);Read a file we returned→ 分配 bug(文件对、字节错);Read a file we did not return/Grep/Glob→ 召回 bug(文件根本没浮出);moved on / answered→ 充分(但不等于正确)。分类只在同一"线程"内匹配(子代理工具调用带parent_tool_use_id被交错注入同一 stream),委派类动作按子代理的首个实际动作判分。 - allocation efficiency 是相对指标。归因靠答案中的引用(路径引用 + 代码 span 内的符号引用,出现于 ≥3 个返回文件的符号被丢弃以防偏乐观),因此只适用于同一问题下两个构建的横向比较,不能引用为绝对浪费率。
- 解析器自带
--selftest(合成转录 + 已知答案回归),文档记载当前为 68/68 全过;agent-eval-feedback-metrics.md 还说明了为什么它故意不拆成独立模块——新的scripts/agent-eval/*.mjs会落入自查询评测 fixture 自己的语料并污染数字。
一份真实的 ARM COMPARISON 示例(express 仓库,new vs baseline,各 3 runs)在反馈指标文档中给出了完整形态:behavior 区(duration、Read、codegraph calls)、CG-7 区(codegraph residual、file-access residual、retrieval residual、share of final context)、CG-8 桶分布、CG-9 的 pooled / per-run efficiency,以及收尾的 contamination 两行;所有数值都以 median [min–max] 呈现——因为单次运行只有 1–5 次 explore 调用,百分比天然粗粒度,必须报区间。
注意事项(SKILL.md Notes 的完整继承)
- 每次运行都重建索引:
audit.sh会清掉.codegraph再索引——不同版本抽取方式不同,索引必须与提供服务的是同一二进制; - 临时改动全局安装:
audit.sh会暂时替换全局codegraph,结束后用 local-install.sh 恢复开发链接;跑完请检查日志确认恢复成功; - corpus 复用:语料仓库克隆到
/tmp/codegraph-corpus,已存在则复用不重克隆(意味着不会自动跟踪上游更新); - 扩展语料:直接编辑 corpus.json,字段固定为
name、repo、size、files、question;新条目会自动进入 Step 2/3 的选项列表。
延伸:同一脚本簇的其它评测入口
audit.sh 面向"某版本 vs 裸 grep/read"的采用类问题;若要回答"某次检索改动到底改了什么",仓库还提供配套入口,全部复用同一套污染防护与指标解析:
- ab-new-vs-baseline.sh:新构建(HEAD)对基线构建(git ref),两臂都开着 codegraph,
RUNS=n控制每臂重复次数,每轮预热 codegraph daemon(否则 Agent 会在 codegraph 2–3 秒冷启动期间转投 Read/grep,测到的是附加延迟而非检索质量); - run-agent.sh:单臂 headless 快速运行,打印完整 stream-json 与 per-tool 拆解,适合临时探查;
- probe-explore.mjs:指标块定位到"哪次 query 掉队、Agent 转去读了哪个文件"后,用它复现单次 explore 的返回内容;
- bench-readme.sh + parse-bench-readme.mjs:7 个 README 仓库 × 3 轮 × N 次/臂的批量战役与聚合。
理解这三个指标的取舍、各桶指向的修复、以及"small-n 必须报区间"等读表纪律,以 docs/benchmarks/agent-eval-feedback-metrics.md 为准——它同时声明这三项指标是 harness-only 的:全部从已有转录解析,产品侧零新增输出,数据不出本机。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00