首页
/ CodeGraph Agent 评测三反馈指标实战:Residual Occupancy、Explore Sufficiency 与 Allocation Efficiency

CodeGraph Agent 评测三反馈指标实战:Residual Occupancy、Explore Sufficiency 与 Allocation Efficiency

2026-09-05 14:51:36作者:邬祺芯Juliet

CodeGraph 的 agent-eval 评测框架在每次运行时固定输出三个反馈指标:残差上下文占用(Residual Context Occupancy,CG-7)、探索充分性(Explore Sufficiency,CG-8)与分配效率(Allocation Efficiency,CG-9)。它们不是同一个数字的三种视图,而是分别回答"这次检索为后续回合留下了多少上下文负担""检索结果是否足以让 Agent 停止翻查""返回的字节有多少真正被答案引用"三个独立问题,一次检索改动可能只移动其中一个而不动其他两个。本文围绕 入口文档 讲解如何为不同评测目的选择 harness、如何读输出表、三个指标各自的坑,并结合 scripts/agent-eval/parse-run.mjs 的源码说明每个指标是如何从既有 stream-json 转录日志中解析出来的。读完你可以独立运行这三套评测、解读 ARM COMPARISON 表,并在数字异动时定位到具体该修哪一段检索逻辑。

三个指标:各答一个问题,互不替代

入口文档给出的核心分工如下表。三者都是 harness-only 指标:全部从评测框架自己写出的转录日志中解析而来,产品侧不输出任何遥测数据,数据也不离开本机。

指标 回答的问题 深入文档
Residual context occupancy(CG-7) 运行结束时,该臂的检索结果还占着窗口里的多少 token——即后续每一轮都要在多大的剩余空间里工作 residual-context-occupancy.md
Explore sufficiency(CG-8) 响应够不够?读 Agent 紧接着做了什么:再 explore、Read 文件,还是直接作答 explore-sufficiency.md
Allocation efficiency(CG-9) 响应花掉的字节里,有多大比例给了答案真正引用的文件 explore-allocation-efficiency.md

入口文档还强调了一个阅读纪律:"Efficiency is not value, and occupancy is not sufficiency." 一个响应可以 100% 高效却毫无用处(比如只有一个被顺带提到的小文件),残差小也只是在答案仍然正确的前提下才有意义。把三个指标接在同一次运行里输出,正是为了强制三者一起读。

选择 harness:按你要问的问题挑

三个指标在两套 A/B harness 中都会打印,按问题类型选择:

1. 隔离一次检索改动:ab-new-vs-baseline.sh

新构建(HEAD)对比基线构建(任意 git ref),两臂都挂载 codegraph,跑同一个实现任务。这是这三个指标的设计目标场景——两臂 codegraph 都在,所有数字度量的都是"改动本身",而不是"有没有用 codegraph"。

RUNS=3 scripts/agent-eval/ab-new-vs-baseline.sh /tmp/codegraph-corpus/express \
  "Add a charset option to res.send and wire it through" main

ab-new-vs-baseline.sh 的脚本实现看,它的运行机制有几个关键点:

  • 脚本先把目标仓库 rsync 出两份干净副本(t-new/t-base,排除 node_modules/.git/dist/.codegraph),分别用 HEAD 构建和基线 ref 构建各自索引并运行;基线臂通过逐文件 git checkout <ref> -- <file> 还原旧构建,src/ 无差异时会直接拒绝运行("nothing to A/B");
  • 每次运行前对目标副本 预温热一个常驻 codegraph daemon 并等待 daemon.sock 出现,同时用 CODEGRAPH_WASM_RELAUNCHED=1 跳过启动再执行。这一步"load-bearing,不可删除":没有它,Agent 会在 codegraph 完成约 2–3 秒启动前就扑向 Read/grep,整次运行测的是 attach 延迟而不是检索质量;
  • 两臂都设置 CODEGRAPH_NO_PROMPT_HOOK=1,禁用环境里 ambient 的 UserPromptSubmit 前置钩子——否则会经第二条不受控通道注入上下文,污染工具调用计数;
  • RUNS 环境变量控制每臂运行次数(默认 1)。由于两臂各自只构建/索引一次,提高 RUNS 远比重新调用脚本便宜,而运行间方差很大,文档明确要求 RUNS>=2 并报告区间。

2. 有无 codegraph 对比:run-all.sh

Codegraph-on 臂(仅 codegraph MCP)对比空 MCP 配置臂。它回答的是另一个问题:位移(displacement)与采用(adoption),而非改动效果。内置 Read/Grep/Bash 在两臂中均可用,所以"without"臂获取同样字节的方式是读文件和搜索。

scripts/agent-eval/run-all.sh /tmp/codegraph-corpus/gin \
  "How does gin route requests through its middleware chain?||\
Where is the 404 / no-route case handled in that same chain?"

多回合语法是 || 分隔:第一回合正常运行,后续每个回合用 --resume 续接同一 session,前一回合的工具输出仍在窗口里——这正是残差占用真正被"记账"的地方,单问题运行结构性地看不到这个成本。从 run-all.sh 源码看,每段落在 run-<label>.jsonlrun-<label>.t2.jsonl… 中,parse-run.mjs 会把它们拼回一个 session(--resume 不重放历史消息,所以各段可干净拼接)。CG_ARMS=with|without 可只重跑一臂而不重做另一臂,对比表仍会对着 $AGENT_EVAL_OUT(默认 /tmp/agent-eval)中已有的日志渲染。

3. 完整战役:bench-readme.sh

7 个 README 仓库(vscode、excalidraw、django、tokio、okhttp、gin、alamofire),每个 3 回合,每臂 RUNS 次,全部经由 run-all.sh 驱动——因此战役中每次运行都携带三个指标。从 bench-readme.sh 看,仓库需预先克隆并索引到 $CORPUS(默认 /tmp/codegraph-corpus),每行固定为"主问题 + 两个不离开同一流程的追问",CG_TURNS=1 可退回原始单问题 A/B。聚合用 parse-bench-readme.mjs

CORPUS=/tmp/codegraph-corpus RUNS=2 scripts/agent-eval/bench-readme.sh
node scripts/agent-eval/parse-bench-readme.mjs /tmp/ab-readme

已运行过一次战役:2026-08-05 基线(sonnet,3 回合,每臂 4 次)。文档提醒:比较任何东西之前先读它上面的 regime box,且注意它不是 README 表格发布时所用的 regime

4. 已有日志:随时可解析

  • parse-run.mjs <run.jsonl> [run.tN.jsonl …] 对任意 stream-json 日志打印三个指标块;--brief 去掉带编号的调用转录,保留其余;--envelope 额外报告 explore 响应在文件间的字节分配,--answer <glob>(可重复)标记真正答题的文件;
  • parse-session.mjs <project-dir>交互式 session 做 sufficiency 和 allocation;
  • compare-arms.mjs <out-dir> <label>… 随时从磁盘日志构建对比表,任意标签。

模型策略(两套 harness 通用,不可协商):每臂都用 --model sonnet --effort high,两臂同模型。Sonnet 是有意的下限——能在它上面落地的 affordance 可以上泛到任何宿主模型;只在更强模型上有效的改动,无法下泛到多数用户实际拥有的 Agent。

读懂输出:对比表回答"动没动",逐运行块回答"为什么"

每次运行先打印自己的三个指标块(块形状见各指标文档),然后一张表把两臂并排。这是真实的 CG-22 express 运行输出,也是三个指标一起读的完整示例:

====== ARM COMPARISON — /private/tmp/cg22/ab-express ======
                                                           new                baseline
  runs                                                       3                       3

  behavior
    duration (s)                                    24 [18–35]              26 [24–30]
    Read                                                     0                       1
    codegraph calls                                    2 [1–2]                       2

  residual context occupancy (CG-7) — tokens still resident at end of run
    codegraph residual (tok)             11,549 [7,193–12,591]  10,388 [10,373–10,447]
    file-access residual (tok)                     231 [0–242]     1,661 [1,306–1,663]
    → retrieval residual (tok)           11,780 [7,193–12,833]  12,034 [11,753–12,051]
    → share of final context               23.3% [15.8%–24.9%]     23.8% [23.4%–23.9%]

  explore sufficiency (CG-8) — pooled over every answered explore call
    answered explore calls                                   5                       6
    explore again                                       2  40%                  3  50%
    Read a file we returned                              0  0%                  3  50%
    Read a file we did not return                        0  0%                   0  0%
    Grep/Glob                                            0  0%                   0  0%
    moved on / answered                                 3  60%                  0  0%

  explore allocation efficiency (CG-9) — share of returned bytes the answer cited
    pooled efficiency                                    96.9%                   82.0%
    per-run efficiency                     100.0% [92.5%–100.0%]   81.9% [81.9%–82.0%]

  contamination — the CLI must never be how codegraph is reached
    CLI calls that RETURNED output                           0                       0
    CLI attempts blocked                                     0                       0

这个例子的读法:基线臂把 18% 的 envelope 花在了一个答案从未引用的文件上,因此 Agent 在 6 次调用中的 3 次里 Read 了我们已经返回过的文件,整次运行以 82% 效率收场;新构建送出了正确的字节——该桶 5 次中 0 次、96.9%——而残差大体相当。只看 occupancy 会判定两臂等价,这正是三指标并排的价值。

对比表是"did it move?";逐运行块才是"why?"。只有逐运行块会指出哪个 query 落了空、Agent 转而去读了哪个文件,通常足以用 scripts/agent-eval/probe-explore.mjs 复现一次 miss。compare-arms.mjs 的表格统一按 median [min–max] 渲染,原因见下文"小样本"一节。

哪个桶指向哪个修复

Sufficiency 的桶被刻意设计成每个桶对应一个不同的修复方向,其中两个直接挂钩其他指标:

  • Read a file we returned → allocation(分配)问题:文件对了,字节错了(被裁掉了)。同一批运行上预期 allocation efficiency 也偏低。注意不对称性——效率指标会给一个"被引用但裁剪掉了部分内容、Agent 不得不去读"的文件其 section 记 100% 分,这个桶就是为此而设的捕获器。
  • Read a file we did not return / Grep/Glob → recall(召回)问题:文件根本没浮出来。Allocation efficiency 对此完全失明——envelope 只是缺了一块。
  • explore again → 构造上就是模糊的。它说明响应没有回答,但不说明是分配还是召回问题;后续 query 通常能分辨。
  • moved on / answered → sufficient(充分),但充分不等于正确。

指标实现原理(源码级补充)

以下三点是读懂指标块里那些数字的前提,均在 parse-run.mjs 中实现。

Occupancy:测的是真 token,不是估算。 对每个 assistant 请求,ctx_k = input_tokens + cache_read_input_tokens + cache_creation_input_tokens 是该请求完整 prompt 的精确 token 数,相邻请求之差 gap_k 就是新追加内容的量,按字符比例把 gap 拆给其中的各 tool_result。chars/token 比值只在"字符 ≥80% 为工具结果"的 gap 上校准(避免 assistant 输出在转录中欠代表时把整个 delta 记到某个小结果上),实测 explore 输出约 2.2–2.3 chars/token,bytes/4 的经验值会低估约 40%。内容离开窗口有两条路,都被跟踪:compact_boundary 事件清空此前的驻留集;micro-compaction 下按 FIFO 逐出最老的 tool result(短缺超过 max(200 tok, 5%) 容差才算逐出,真实 shed 是数千 token 量级)。

Sufficiency:Agent 的"下一步动作"是免费的地面真值。 分类器把每次已应答的 codegraph_explore 按其后的第一个实质动作分桶,并有三条保真规则:同一条 assistant 消息里与 explore 并发发出的 Read 不构成裁决(响应还不存在,单独计为 concurrent);ToolSearch/TodoWrite 这类无信号工具被跳过;subagent 的调用是独立线程(转录里以 parent_tool_use_id 交错标记),父线程的裁决是对"委派本身"的判断——按子 Agent 第一个实质动作来评,否则会把子 Agent 的 grep 误记成父线程对某次 explore 的裁决。Bash 命令还会被意图解析:sed -n 100,200p file 读作 Read,grep/rg/find 读作搜索,带重定向/here-doc 的写文件命令不算读。

Allocation:按引用归因,两个通道。 computeAllocation 从渲染后的 markdown(而非诊断 sidecar——sidecar 只存在于较新构建,无法测基线臂)解析出 explore 的 per-file section,然后从 Agent 的最终答案中提取引用:PATH 通道(答案点出文件,含 lib/response.js:126-220 与裸 basename——裸名只在扩展名确实出现在 envelope 中时放行,避免 res.send 被误认作文件)和 SYMBOL 通道(代码 span 中的符号,且该符号只由目标文件的 section 头列为定义才计分;出现在 ≥3 个返回文件中的名字过于通用,弃用——否则 send/get 会把半个 envelope 标成"已用",而乐观偏差是这个指标唯一不能倾斜的方向)。被返回两次的文件计两次——因为它占用了窗口两次。

汇总视图下仍然成立的注意事项

各指标文档有完整清单,以下是会改变你对表格本身的读法的那几条:

  • Allocation efficiency 是相对量,不是绝对量。 归因靠引用,而 Agent 可以"用了但从未点名"一个文件(用它来排除,或据此建立模型)。误差单向,只能在同一问题上比较构建,绝不能把数字引用为"codegraph 浪费了 N% 的返回内容"。语料中位数落在 80% 区间,是因为这些 flow 类问题的答案要遍历整条链;区分度在 p25 及以下。
  • Occupancy 的占比不跨宿主迁移。 基准是 Claude Code、名义 200k 窗口(CG_WINDOW_TOKENS 可覆盖)。窗口大小、系统提示、压缩策略在别处都不同;能迁移的是两臂之间的比值,百分比不是对 Cursor 的主张。
  • 比对的必须是正确的一对。 在 with/without A/B 中,是 codegraph 残差对 without 臂的 file-access 残差(Read + Grep/Glob + Bash)——Agent 把同样字节读进脑子的两种方式。只数 Read 工具,会把经 Bash cat 取文件的运行记成"没读任何东西"。
  • 充分不等于正确,一次 Read 是"票"不是"证明"。桶仍是对的信号——Agent 去读,说明有东西缺失——但单次调用有噪声。
  • 小样本,永远。 一次运行只有 1–5 次 explore 调用,单运行的百分比很粗。表格打印 median [min–max] 正是为此:报告区间。RUNS>=2,下结论用战役。
  • Subagent 上下文不计入 occupancy——Task 子 Agent 有自己的窗口,只有摘要回流。而 sufficiency 跟随子 Agent 线程(委派按子 Agent 的第一个动作评判)。两个指标对委派的差异化处理是刻意的。
  • 延迟工具 schema 计入 occupancy 的 basecodegraph_explore 是延迟加载的,ToolSearch 稍后拉取 schema,那次注入不是工具结果;fixed-overhead 行只计价一开始就存在的部分。

Contamination:先看这一行

两套 harness 都让每个臂运行在一个屏蔽 codegraph CLI 的环境里:PATH 中把二进制符号链接摘除的净化目录,外加一个 PreToolUse 钩子拦截绝对路径调用(no-cli-shim.sh,两套 harness 共用)。两层都必要——曾有 Agent 在 PATH 上拿不到 codegraph 后执行 find / -iname "*codegraph*",找到二进制并用绝对路径调起。

Contamination 行是检测的一半,与预防的一半不冗余:预防在下一次二进制落到新位置时会静默失效,计数器不会。

  • with/without A/B 中,一次 CLI 调用意味着 without 臂并非"without"。某次 7 仓库遍历中 15 次 without 臂运行有 14 次通过 Bash 跑了 codegraph explore——shim 之前的所有旧结果都应假定已被污染
  • new/baseline A/B 中,两臂都是 codegraph-on,CLI 调用不是泄漏而是归因失败,会同时破坏三个指标:经 Bash 到达的输出在 occupancy 表里被记到 Bash 头上;经 CLI 发出的 explore 根本不是工具调用,永远到不了 sufficiency 分类器和 allocation 解析。运行会静默地把调用从上方每个数字里丢掉。

CLI attempts blocked 是良性的——Agent 试了,但没有任何内容进入窗口。CLI calls that RETURNED output 不是。parse-run.mjs 用只匹配命令位置(而非任意提及)的正则识别 CLI 调用:grep codegraph xls .codegraphwhich codegraph 都是"看"而不是"用",放行。

测试

指标数学本身的回归用内建 selftest 覆盖:

node scripts/agent-eval/parse-run.mjs --selftest     # 68/68

它对带已知答案的合成转录做断言:occupancy 的完整算术(校准、FIFO 逐出、compaction 清空)、sufficiency 的每个桶以及 same-message/thread/delegation 三条规则、allocation 的引用通道及其守卫(歧义上限、prose 不计引用、per-call 与 pooled 的口径)。selftest 特意放在 parse-run.mjs 内部而不是独立测试文件——新写的 scripts/agent-eval/*.mjs 会进入 self-query eval fixture 自己的语料,污染被评测对象。各指标的用例清单见对应指标文档。

小结

这套三指标体系的设计逻辑一句话概括:occupancy 管"留了多少",sufficiency 管"够不够",allocation 管"送得准不准",三者接在同一次运行上互相制衡——任一指标单独向好都可能被另外两个证伪。运行侧记住三件事即可:按问题选 harness(隔离改动用 ab-new-vs-baseline.sh,采用/位移用 run-all.sh,战役用 bench-readme.sh),RUNS>=2 并报区间,输出表先看 contamination 行再看其余数字。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384