这是进阶内容,面向想理解 eval 如何打分、指标怎么算、以及自动多轮评测怎么跑的用户。日常评估看快速上手即可。
Comet 的 eval 不只给出”通过/失败”,而是指标驱动的评测:用 rubric 多维评分量化 Skill 在各个质量维度的表现,用 pass@k / pass^k 区分”能力上限”和”可靠性下限”。对于多阶段工作流类 Skill,评测由两个 Agent 自动交互完成——一个跑被测 Skill,另一个模拟用户在决策点回复。
读报告时先看这些
如果你不是在调试评分器,读报告时按这个顺序看即可:
| 顺序 | 报告信号 | 判断什么 |
|---|
| 1 | checks_failed | 这次运行是否真的失败;为空才算通过 |
| 2 | failure attribution | 失败该归到环境、workflow、task 还是模型 |
| 3 | pass@k / pass^k | Skill 是偶尔能成功,还是重复运行稳定 |
| 4 | weighted_score | 质量维度的加权诊断分 |
| 5 | 最低 rubric 维度 | 下一轮优化优先投到哪里 |
日常发布判断不要只看 weighted_score。真正的通过/失败先看 checks_failed;rubric、pass@k 和 pass^k 用来判断质量、稳定性和下一步优化方向。
指标体系一览
| 指标 | 回答的问题 | 类型 | 是否门禁 |
|---|
| rubric 各维度分(0.0–1.0) | 这个维度做得怎么样 | 诊断性 | 否(信息性) |
| weighted_score(加权总分) | 综合质量如何 | 诊断性 | 否 |
| RubricAvg(报告列) | 该次运行的维度均分 | 诊断性 | 否 |
| pass@k | k 次里至少成功一次的概率(能力上限) | 分布性 | 否 |
| pass^k | k 次全部成功的概率(可靠性下限) | 分布性 | 否 |
| 工作流契约检查 | 被测 Skill 是否真的按契约触发 | 硬判定 | 是(缺必需 Skill 会失败) |
| 任务校验器通过/失败 | 这次实现到底对不对 | 硬判定 | 是(决定 pass/fail) |
关键区分:rubric 的维度分和 pass@k/pass^k 都是信息性指标,用于诊断和对比;真正的通过/失败看 checks_failed == []。大多数失败来自任务校验器(target_artifacts 存在性 + test_scripts),但 profile 也会把必需 Skill 未触发这类工作流契约问题写入 checks_failed。
证据从哪里来
Comet eval 的运行证据来自 Claude CLI 自己输出的 stream-json,不是从终端文本里猜,也不是让另一个模型回忆总结。
单轮评测会在 Docker 里的任务目录直接运行被测 Agent:
claude -p "$prompt" \
--dangerously-skip-permissions \
--output-format stream-json \
--verbose
eval harness 保存这次运行的原始 stdout / stderr,再逐行解析 stdout 里的 JSONL 事件。解析器会提取:
result 事件里的 duration_ms、num_turns、token 和 cost
tool_use 事件里的工具调用
Bash 命令,作为 commands_run
Write / Edit 文件路径,作为 files_created / files_modified
Skill 调用,作为 skills_invoked
- 对应的
tool_result 输出,挂回原工具调用
所以报告里的 Skills invoked、commands_run、tool_calls、files_created、token/cost 等,都是从 Claude CLI 的结构化事件流整理出来的可观测行为日志。
stream-json 仍然只是可观测行为日志。它适合统计工具调用、命令、Skill 调用、文件变化和成本;它不包含模型内部推理,也不能证明某个内部决策的因果链。schema 变化、坏 JSON 行、CLI 版本变化都可能影响解析,因此 raw stdout/stderr 会保留下来供审计。
轨迹和事件流不是一回事
Comet Classic runtime 自己也会写 .comet/trajectory*.jsonl,记录状态推进,例如 state_transitioned。这类 trajectory 是恢复能力的证据之一,rubric 会在 recovery_resilience 里检查它是否存在。
但 pass@k / pass^k 不从 trajectory 计算,rubric 的大部分维度也不是只看 trajectory。主证据来源是 Claude CLI stream-json、任务目录里的产物、任务校验器结果,以及 Comet runtime 留下的状态文件。
rubric 评分
rubric 把 Skill 的质量拆成多个维度,每个维度由若干二元(pass/fail)检查项组成,维度分 = 通过项数 / 总项数(0.0–1.0)。每个维度输出一行 [RUBRIC] <维度>: <分> - <原因>。
评分方法论(对齐 Galileo、Hebbia、τ-bench 等业界实践):
- 每个维度含 N 个二元检查项
- 维度分 = passed / total(0.0–1.0)
- 加权总分 = Σ(维度分 × 权重) / Σ(权重)
- 权重反映维度对工作流质量的重要性
三套 rubric
eval 内置三套 rubric,对应三种 profile:
| Profile | 维度数 | 权重和 | 交互模式 | 适合 |
|---|
generic | 7 | 7.7 | none(单轮) | 通用 Skill 冒烟 |
comet-workflow | 9 | 11.0 | auto_user(多轮) | 经典 /comet 五阶段 |
authoring-skill | 11 | 13.8 | auto_user(多轮) | /comet-any 生成物 |
comet-workflow rubric(9 维)
评估经典五阶段工作流。九个 [RUBRIC] 维度分本身是诊断性分数;但 profile 仍会检查工作流契约:如果没有真实触发 comet、嵌套 Comet 阶段 Skill、OpenSpec 依赖 Skill 或 Superpowers 依赖 Skill,会产生硬失败。
| 维度 | 权重 | 检查什么 |
|---|
completion | 2.0(最高) | 基线校验器通过比例(任务到底做对没) |
main_flow | 1.5 | 五阶段(open→design→build→verify→archive)有多少阶段留下证据 |
gate_guard | 1.5 | 是否用了 comet-guard / transition / --apply |
artifact_quality | 1.2 | proposal/design/tasks/test 是否有实质内容(proposal≥10 行、design 含权衡关键词、tasks≥3 个勾选项、有断言测试) |
skill_invocation | 1.0 | 是否真实观测到 comet 入口、嵌套 Comet 阶段 Skill、OpenSpec 依赖 Skill、Superpowers 依赖 Skill,且入口先于阶段 Skill |
spec_drift | 1.0 | build 期改的 delta spec 是否在归档前 reconcile(没动 spec 则中性 1.0) |
decision_point_compliance | 1.0 | 是否在决策点暂停问用户而非自动决策(ask 调用数 ≥ mutation 数的一半) |
recovery_resilience | 1.0 | 中断后是否正确保留和恢复状态(checkpoint/trajectory/context/pending/.comet.yaml 五项) |
efficiency | 0.8(最低) | 成本归一化(turns≤80、tools≤150、duration≤600s 三项) |
generic rubric(7 维)
评估通用 Skill。和 comet-workflow 不同,它对”必需 Skill 未被调用”会产生硬失败(当 require_skill_invocation: true 时),其余维度信息性。
| 维度 | 权重 | 检查什么 | |
|---|
completion | 2.0 | 基线校验器通过比例 | |
safety_boundary | 1.2 | 是否有危险命令(rm -rf、git reset --hard、`curl … | sh`) |
skill_invocation | 1.0 | required skills 是否都被调用(未配置则中性 1.0) | |
artifact_presence | 1.0 | expected_artifacts 是否存在(支持 glob) | |
instruction_following | 1.0 | 是否有失败检查提到 “constraint” | |
interaction_compliance | 0.8 | auto_user 模式下外层往返是否超 max_turns | |
efficiency | 0.7 | 同 comet 的 efficiency 阈值 | |
authoring-skill rubric(11 维)
评估 /comet-any 生成的 Skill 包。它先继承 generic 的四个共享维度,再加七个包专属检查。缺 SKILL.md、缺 resolved-skills.json、缺 workflow-protocol.json、缺 Engine 文件、缺 authoring-lanes.json、缺 skill-review.md 都会产生硬失败。
| 维度 | 权重 | 检查什么 |
|---|
workflow_route_conformance | 1.5(与 completion 并列最高) | 协议路由顺序是否与期望节点序一致、内部 Node Skill 文件是否齐全、comet/eval.yaml 是否列出路由 |
completion | 1.5 | 继承自 generic |
generated_package | 1.4 | 包存在 + SKILL.md 含路由/停顿点/workflow 与 resolved-skill 引用/恢复指引 |
resolved_skill_evidence | 1.2 | reference/resolved-skills.json 存在且 sourceSummaries 非空 |
engine_contract | 1.2 | comet/ 下有 skill.yaml/guardrails.yaml/checks.yaml/eval.yaml(轻量包无 Engine 则中性 1.0) |
authoring_lanes | 1.2 | reference/authoring-lanes.json 含 6 条必需 lane(skill-core/script-contract/reference/pause-points/eval/skill-review)且 review 通过 |
safety_boundary | 1.2 | 继承自 generic |
skill_invocation | 1.0 | 继承自 generic |
artifact_presence | 1.0 | 继承自 generic |
review_gate | 0.8 | reference/skill-review.md 报告通过、authoring-lanes review 通过、无阻塞 finding |
review_readiness | 0.8(最低) | generic 层无硬失败则为 1.0 |
加权总分公式
weighted_score = Σ(维度分 × 权重) / Σ(权重)
每个维度输出一行 [RUBRIC] <dim>: <score> - <reason>,最后输出一行 [RUBRIC] weighted_score: <分>。
RubricAvg(报告列)
报告里的 RubricAvg 列是该次运行所有维度分(含 weighted_score 行)的简单均分(sum/len)。跨多次运行时是各运行均分的均值。它是快速横向对比的汇总,和单 rubric 的 weighted_score(用各自权重)算法不同。
LLM-as-judge 覆盖(可选)
设 BENCH_LLM_JUDGE=1 启用。默认情况下,Comet 的最终 weighted_score 是规则分,不是 LLM-as-judge。规则型 rubric 能抓结构信号(文件存在、命令执行),但抓不到产物的实质深度——agent 是真做了有意义的输出,还是只生成了一个 stub?LLM judge 让一个裁判模型读取 workspace artifacts 后重新打分,输出 [RUBRIC-JUDGE] 行(和规则分的 [RUBRIC] 区分)。best-effort:失败则回退到规则分,不影响运行。
judge 在宿主机(不在 Docker 内)通过复用 claude CLI 运行,不引入新依赖。但裁判配置和被测 Agent 配置是故意隔离的:启用 BENCH_LLM_JUDGE=1 时,必须显式设置 BENCH_JUDGE_MODEL,不能回退复用主模型的 ANTHROPIC_MODEL。
如果配置了 BENCH_JUDGE_BASE_URL 和 BENCH_JUDGE_AUTH_TOKEN / BENCH_JUDGE_API_KEY,judge 会优先直接调用 Anthropic Messages HTTP(/v1/messages)。没有专用 judge endpoint 时,才回退到宿主机 claude CLI,并在子进程里清除继承来的主 ANTHROPIC_* provider 设置,再把独立裁判配置映射给自己的 CLI 调用:
| 环境变量 | 用途 |
|---|
BENCH_JUDGE_MODEL | 必填。裁判模型;direct HTTP 时作为 request model,CLI fallback 时传给 claude --model 和子进程 ANTHROPIC_MODEL |
BENCH_JUDGE_AUTH_TOKEN | 可选。裁判专用 Anthropic 兼容代理 token,优先于 BENCH_JUDGE_API_KEY |
BENCH_JUDGE_BASE_URL | 可选。裁判专用 Anthropic 兼容代理地址 |
BENCH_JUDGE_API_KEY | 可选。裁判专用 Anthropic API key;设置 auth token 时不会传入子进程 |
这样设计是为了避免”同一个模型既当选手又当裁判”:主 Agent 可以继续使用自己的 ANTHROPIC_MODEL、ANTHROPIC_BASE_URL 和 token,judge 必须单独声明模型和 provider。direct HTTP 路径也能避开某些严格 Anthropic 兼容代理不接受 Claude CLI 额外请求参数的问题。若开启了 BENCH_LLM_JUDGE=1 但缺少 BENCH_JUDGE_MODEL,报告会写入:
[RUBRIC-JUDGE] status: skipped - BENCH_JUDGE_MODEL is required when BENCH_LLM_JUDGE=1
这种 skipped 状态不会被标记为 enabled_and_successful,也不会调用裁判模型。
不同 profile 覆盖的维度不同:
| Profile | 覆盖维度 | 说明 |
|---|
comet-workflow | artifact_quality、spec_drift、main_flow | 重新打分规则较弱的三个定性维度(llm_judge.py) |
generic / authoring-skill | task_completion、output_quality、instruction_adherence | 通用三维度,外加任务在 task.toml 里声明的 rubric_criteria 作为 custom_N 维度(generic_llm_judge.py) |
generic/authoring 的三维度评分标准:
| 维度 | 1.0(满分) | 0.5 | 0.0 |
|---|
task_completion | 全部需求满足、产物存在且正确、基线检查通过 | 部分完成 | 关键产物缺失或为空 |
output_quality | 结构良好、完整、非平凡、有思考痕迹 | 存在但浅薄、勉强达标 | 缺失、空、明显 stub |
instruction_adherence | 完全遵守指令和约束、无违禁模式、工具使用得当 | 轻微偏离但核心意图保留 | 严重违反、危险命令、约束失败 |
judge 收集 workspace 文件时跳过 .git、node_modules、.comet 等目录,单文件上限 3000 字符、总预算 20000 字符,大文件(>50KB)和二进制文件会被跳过,保证 prompt 不失控。裁判必须按 [RUBRIC-JUDGE] <dim>: <score> - <reason> 格式逐行输出,每个 reason ≤25 词并引用具体内容。
pass@k 与 pass^k
这两个指标衡量能力 vs 可靠性,是评估 Skill 能否反复稳定运行的关键。它们基于多次重复运行(由 --count N 产生)的通过/失败序列计算。
| 指标 | 公式 | 含义 |
|---|
| pass@k(能力上限) | 1 - C(n-c, k) / C(n, k) | k 次独立尝试里至少一次成功的概率(HumanEval 无偏估计) |
| pass^k(可靠性下限) | 1.0 当且仅当 c == n,否则 0.0 | k 次尝试全部成功的概率 |
其中 n = 总运行数,c = 成功运行数。“成功”= 该次运行任务校验器零失败。
为什么需要两个

pass@k 看能力上限,pass^k 看可靠性下限,两者差距越大越不稳定
- pass@k 高、pass^k 低:能力够,但不稳定——“能做,但不能保证每次都做对”。对一个用户反复运行的 Skill,这是危险信号。
- 两者都高:既能做,又每次都做对——可信赖。
gap(pass@k − pass^k)量化不稳定性。报告里会显式提示这个 gap。
怎么得到多次运行
--count N(pytest 选项)把每个 (task, treatment) 组合重复 N 次,产生 N 个独立的通过/失败结果。对比报告先过滤出 analysis set:明确的环境或运行器噪声会被排除,flagged run 仍进入主统计但会被标出。analysis set 里的这 N 个布尔值就是 pass@k/pass^k 的输入。
# 重复 5 次,让 pass@k/pass^k 有意义
# (实际通过 comet eval 间接驱动,底层等价于 --count 5)
报告在运行数不足时会警告:“Only N run per treatment — pass@k/pass^k for k>1 need ≥2 runs to be meaningful. Use —count 5.” 单次运行只能算 pass@1/pass^1,k>1 的指标需要多次重复。
k 怎么选
报告从 {1, 2, 5} 里取不超过实际运行数 n 的 k 值(k 被 clamp 到 n),不足时回退到 [1]。报告列:pass@1 [pass@2 pass@5] 和 pass^1 [pass^2 pass^5]。
出现在哪
pass@k/pass^k 不在 summary.md,而在对比报告(compare_baselines.py 产出的 comparison_report.md)的 ## pass@k / pass^k — capability vs reliability 章节。它们是信息性的,不是门禁。
双 Agent 自动交互评测
对于多阶段工作流类 Skill(comet-workflow 和 authoring-skill profile),评测由两个 Agent 自动交互完成,不需要真人介入。
两个角色
| 角色 | 做什么 | 会话 |
|---|
| 被测 Agent(subject) | 跑被测 Skill(如 /comet 五阶段) | 单一连续会话,每轮 --resume 续接 |
| 用户模拟 Agent(simulator) | 在决策点模拟用户回复(批准合理方案、选合理默认、只在真有歧义时澄清) | 每个决策点一次性的 claude -p 调用,无状态 |
交互循环
循环最多 max_turns 次外层往返(comet-workflow 通常 12 次,authoring-skill 通常 8 次),命中”完成”(archive complete / workflow complete / all 5 phases 等)则提前结束。
每一轮被测 Agent 都用 --output-format stream-json --verbose 运行。loop 驱动只把每轮被测 Agent 的 stream-json stdout 拼接起来给 harness 解析;用户模拟 Agent 的一次性回复不会进入主事件流。因此事件统计反映的是 subject Agent 的可观测行为,而不是 simulator 的行为。
max_turns 不是被测 Agent 内部真实工作轮数,也不是工具调用次数。一次外层往返指:被测 Agent 跑到决策点,用户模拟 Agent 回复,然后被测 Agent 用 —resume 继续。同一次往返内部仍可能包含多条 assistant 消息、工具调用和文件操作。
决策点检测
被测 Agent 的输出文本匹配这些信号就判定为决策点(不区分大小写):
? | confirm | choose | proceed | continue | approve | select an option |
which (option|approach|name) | enter the | provide | would you | shall we |
do you want | preferred
也支持自定义 --decision-pattern。
用户模拟 Agent 的指令
模拟 Agent 收到的是 simulator prompt + 被测 Agent 的最后一条消息,被要求:
- 被要求确认时,批准提出的方案/名字/计划
- 被要求选择时,选最合理的默认
- 只在问题对”做什么”真正有歧义时才要求澄清
- 永不拒绝,永远让工作流前进
- 不写代码或文件
comet-workflow 用 COMET_SIMULATOR_PROMPT,generic/authoring 用 GENERIC_SIMULATOR_PROMPT(措辞略简)。模拟回复为空时回退到 "Yes, please proceed with the recommended option."。
自定义模拟器提示词
auto_user 评测的模拟器指令可被覆盖。默认从 eval/simulator-instruction.md 读取,其内容就是上面这几条原则的标准模板。两种覆盖方式(优先级从高到低):
| 方式 | 怎么设 | 作用范围 |
|---|
--simulator-prompt "..." | pytest 命令行参数 | 单次运行 |
BENCH_SIMULATOR_PROMPT_FILE=<path> | 环境变量(可写进 eval/.env) | 所有 auto_user 运行 |
BENCH_SIMULATOR_PROMPT_FILE 的相对路径从 eval/ 解析,默认值 simulator-instruction.md,文件存在才会被读取。这让你能换一套用户行为画像(比如更挑剔、会要求澄清更多)来压力测试工作流在”难缠用户”下的表现,而不必改 harness 代码。
为什么这样设计
工作流类 Skill 会在决策点暂停等用户确认。如果评测只跑单轮,Skill 会卡在第一个决策点。双 Agent 循环让评测自动跑完整条工作流(open→…→archive),同时保证决策点的用户输入是”合理的、推动前进的”,而不是硬编码回复。这样测出来的 rubric 分和 pass/fail 才反映真实使用场景。
单轮 vs 多轮
generic profile(interaction.mode: none):单轮,被测 Agent 一次性跑完(适合不需交互的冒烟任务)。
comet-workflow / authoring-skill profile(interaction.mode: auto_user):多轮,启用双 Agent 循环。
comet-* 开头或 category: comet 的任务会自动推断为 comet-workflow profile 并启用 auto_user。
评测的三个轴:treatment × task × reps
一次评测是三个轴的笛卡尔积:
| 轴 | 含义 | 例子 |
|---|
| task | 一个评测任务(目录,含 instruction/environment/validation) | comet-fix-median(修 bug 维度) |
| treatment | 实验条件(注入哪些 Skill / CLAUDE.md) | CONTROL(无 Skill 基线)、COMET_FULL(注入 comet) |
| reps | 重复次数(--count N) | --count 5 每个 (task,treatment) 跑 5 次 |
treatment 实现 A/B 对比
同一任务跑多个 treatment,就能测出 Comet Skill 的边际效果:
| Treatment | 注入 | 用途 |
|---|
CONTROL | 无 Skill 基线 | 对照 |
COMET_FULL | comet 主 + 7 个阶段子 Skill + brainstorming(v3,.mjs 运行时) | 当前版本 |
COMET_FULL_039 | 0.3.9 冻结基线(.sh 运行时) | 回归对比 |
对比报告(compare_baselines.py)把 COMET_FULL(WORKFLOW)和 COMET_FULL_039(BASELINE)跨 rubric 维度对比,CONTROL 仅作上下文。
指标怎么进报告
| 报告位置 | 内容 |
|---|
summary.md 的 Results 表 | 每个 treatment 一行:Checks + 各 rubric 维度列 + RubricAvg + Turns/Duration/Tools/Tokens/Cost |
summary.md 的 Aggregated by Treatment(reps>1 时) | Reps Passed、Checks、Avg Turns/Duration、Tokens、Cost、Skills、Scripts |
每次 report.json | passed、checks_passed[]、checks_failed[]、events_summary(含 failure_attribution)、rubric 分 |
comparison_report.md | pass@k/pass^k 表(能力 vs 可靠性)+ rubric 维度跨 treatment 对比 |
详见读取评估报告。
下一步