首页
/ gemini-cli 行为评估(Behavioral Evals)实战指南:从创建、修复到测试晋升的完整工作流

gemini-cli 行为评估(Behavioral Evals)实战指南:从创建、修复到测试晋升的完整工作流

2026-09-05 21:01:54作者:滕妙奇

本文基于 gemini-cli 仓库中的 behavioral-evals 技能文档(.gemini/skills/behavioral-evals/SKILL.md)及其配套参考指南编写,系统讲解如何为 LLM Agent 编写"决策验证"类测试:选择正确的 Rig、设计真实工作区场景、断言工具调用轨迹、运行与调试评估,以及将孵化中的测试稳定晋升进 CI。读完本文,你可以独立完成一条从"prompt 变更 → 编写行为评估 → 修复失败 → 晋升为 ALWAYS_PASSES"的完整回归防护链路。

什么是行为评估

行为评估(behavioral evaluations,简称 evals)验证的是 Agent 的决策过程,而不是纯功能正确性。用 evals/README.md 的说法:传统集成测试验证"系统是否正确运作"(比如"文件写入器是否真的写入了磁盘"),而行为评估验证"模型是否选择了正确的动作"(比如"当被要求保存代码时,模型是否决定写盘?")。

它们同样区别于 SWE-bench 等宽泛的行业基准:行业基准衡量复杂挑战下的通用能力,而 gemini-cli 的行为评估聚焦于与产品特性强相关的具体、细粒度行为。其核心价值体现在三个方面:

  • 反馈回路:理解 prompt 或工具定义变更如何影响模型决策——"系统 prompt 的改动是否让模型更少使用工具 X?"、"新的工具定义是否让模型困惑了?";
  • 回归防护:防止模型 steering(引导)层面的回归;
  • 非确定性治理:与单元测试不同,LLM 行为是非确定性的。gemini-cli 通过策略(policy)区分"应当稳健"的行为(ALWAYS_PASSES)和"总体可靠但偶尔波动"的行为(USUALLY_PASSES)。

技能入口 SKILL.md 明确将 evals/README.md 定位为概念、策略与运行方式的"单一事实来源(Single Source of Truth)",技能文档本身则提供流程化操作指引。

工作流决策树:先判断,再动手

SKILL.md 给出的决策树是进入任何 eval 任务前的第一步判断,完整继承如下:

  1. prompt/工具变更是否需要验证?
    • → 走普通集成测试即可。
    • → 继续。
  2. 是否 UI/交互密集?
    • → 使用 appEvalTest(AppRig),参见 creating.md
    • → 使用 evalTest(TestRig),同上。
  3. 是否新测试?
    • → 策略设为 USUALLY_PASSES(进入孵化期)。
    • ALWAYS_PASSES(锁定回归,禁止 PR 合入失败)。
  4. 是在修复失败还是晋升测试?

快速检查清单(Quick Checklist)概括为三步:

  1. 布置工作区:用 files 对象向工作区注入必要文件,模拟真实场景(例如带 package.json 的 NodeJS 项目);
  2. 编写断言:用 rig.setBreakpoint()(仅 AppRig)或 rig.readToolLogs() 的索引校验来审计 Agent 的决策;
  3. 本地验证:用 Vitest 单独运行目标测试,确认本地稳定后再依赖 CI 工作流。

创建行为评估:Rig 选择、场景设计与断言模式

creating.md 是编写过程的核心指南。

Rig 选型表

Rig 类型 导入来源 架构 适用场景
evalTest ./test-helper.js 子进程。在独立进程中运行 CLI 并等待退出。 标准工作区测试。不要使用 setBreakpoint;审计历史(readToolLogs)更安全。
appEvalTest ./app-test-helper.js 进程内。直接跑在 runner 循环里。 UI/Ink 渲染。可安全触发 setBreakpoint

这一选型建议有源码支撑。evals/test-helper.tsinternalEvalTest 通过 rig.run() 在子进程中执行 CLI(默认 approvalMode: 'yolo'),进程退出后才执行 evalCase.assert;而 evals/app-test-helper.tsappEvalTest 则直接实例化 AppRig(来自 packages/cli/src/test-utils/AppRig.ts),按 initialize → 写入 files → setup(可设断点)→ render → waitForIdle → sendMessage → assert 的进程内流程执行,因此只有后者能在执行中途挂起并等待确认。

场景设计原则

评估必须模拟真实的 Agent 环境:

  • 工作区状态:测试通用能力时注入标准项目锚点文件——NodeJS 环境给 package.json,外加最少的配置文件(tsconfig.jsonGEMINI.md)。
  • 结构复杂度:提供足够多的文件,迫使 Agent 需要搜索导航,而不是直接把答案喂给它。除非在测试精确的 prompt steering,否则避免单文件平凡用例。

evals/README.md 对此进一步量化了"好"与"坏":好的用例是"提供一个小而可运行的 React 组件,要求 Agent 添加某个特性";坏的用例是"问一个冷知识问题或让它写一个没有任何本地上下文的通用脚本"。文件数建议 2-3 个,既隔离被评估行为,又不至于 setup 逻辑本身脆弱。

Fail First 原则

在断言新能力或锁定修复之前,先验证测试确实会失败:很容易写出断言"默认就满足的行为"的用例。正确流程是——用测试复现失败 → 应用修复(prompt/工具)→ 验证测试通过。README 强调"每个 eval 都应伴随一次 prompt 变更,每次 prompt 变更也尽量应伴随一个 eval"。

四种测试模式

1. 断点(Breakpoint):验证 Agent 在执行前就"打算"使用某个工具,适合交互式确认与安全校验。

// ⚠️ 仅适用于 appEvalTest(AppRig)
setup: async (rig) => {
  rig.setBreakpoint(['ask_user']);
},
assert: async (rig) => {
  const confirmation = await rig.waitForPendingConfirmation('ask_user');
  expect(confirmation).toBeDefined();
}

2. 工具确认竞态(Tool Confirmation Race):断言多个顺序触发(例如"先进入计划模式再提问")时,需要处理确认的先后竞态:

assert: async (rig) => {
  let confirmation = await rig.waitForPendingConfirmation([
    'enter_plan_mode',
    'ask_user',
  ]);

  if (confirmation?.name === 'enter_plan_mode') {
    rig.acceptConfirmation('enter_plan_mode');
    confirmation = await rig.waitForPendingConfirmation('ask_user');
  }
  expect(confirmation?.toolName).toBe('ask_user');
};

3. 审计工具日志:精确审计操作序列,验证效率(例如没有冗余读):

assert: async (rig, result) => {
  await rig.waitForTelemetryReady();
  const toolLogs = rig.readToolLogs();

  const writeCall = toolLogs.find(
    (log) => log.toolRequest.name === 'write_file',
  );
  expect(writeCall).toBeDefined();
};

4. Mock MCP 门面:评估经 MCP 接入的工具而不触碰真实端点,在 setup 钩子中加载 mock 服务器配置:

setup: async (rig) => {
  rig.addMockMcpServer('workspace-server', 'google-workspace');
},
assert: async (rig) => {
  await rig.waitForTelemetryReady();
  const toolLogs = rig.readToolLogs();
  const workspaceCall = toolLogs.find(
    (log) => log.toolRequest.name === 'mcp_workspace-server_docs.getText'
  );
  expect(workspaceCall).toBeDefined();
};

安全与效率护栏

  • 断点死锁setBreakpoint 会暂停执行。标准 evalTestrig.run() 在断言前就等待进程退出,断点会导致无限挂起。交互式模拟用 appEvalTest + 断点,标准轨迹测试用工具日志审计。
  • 失控超时:始终在 EvalCase 中设置预算边界,防止配额失控循环:
evalTest('USUALLY_PASSES', {
  name: '...',
  timeout: 60000, // 1 分钟安全上限
  // ...
});
  • 效率断言(轮次上限):用索引检查工具是否被尽早调用:
assert: async (rig) => {
  const toolLogs = rig.readToolLogs();
  const toolCallIndex = toolLogs.findIndex(
    (log) => log.toolRequest.name === 'cli_help',
  );

  expect(toolCallIndex).toBeGreaterThan(-1);
  expect(toolCallIndex).toBeLessThan(5); // 在前 5 轮内被调用
};

运行评估:构建、命令与环境变量

行为评估运行在编译产物上,因此每次代码变更后必须先构建打包(见 running.md):

npm run build && npm run bundle

评估还需要 API 密钥。若 .env 中有多把 key 或注释,使用精确提取:

export GEMINI_API_KEY=$(grep '^GEMINI_API_KEY=' .env | cut -d '=' -f2) && RUN_EVALS=1 npx vitest run --config evals/vitest.config.ts <file_name>

仓库根目录 package.json 中定义了两个 npm 脚本,与技能文档的命令表一一对应:

命令 范围 说明
npm run test:always_passing_evals ALWAYS_PASSES 快速反馈,CI 中运行(脚本实质是 vitest run --config evals/vitest.config.ts
npm run test:all_evals 全部 运行夜间孵化测试,设置 RUN_EVALS=1cross-env RUN_EVALS=1 vitest run ...

定向运行单个孵化测试文件时,RUN_EVALS=1 是必需的:

RUN_EVALS=1 npx vitest run --config evals/vitest.config.ts my_feature.eval.ts

这些命令背后有明确的源码机制。evals/vitest.config.tsinclude: ['**/*.eval.ts'] 说明评估文件统一以 .eval.ts 命名(这也解释了 evals/ 目录下大量 xxx.eval.ts 文件);testTimeout 为 300000ms(5 分钟),JSON reporter 输出到 evals/logs/report.json。而策略到 Vitest 用例的映射在 evals/test-helper.tsrunEval 函数中:

  • 未设置 RUN_EVALS 时,USUALLY_PASSESUSUALLY_FAILS 策略的用例被 it.skip 跳过——这就是"孵化测试不进 CI"的实现方式;
  • USUALLY_FAILS 策略则注册为 it.fails(期望失败的用例,源码中存在但 SKILL 文档未展开,可以推断用于反向验证);
  • 用例还可携带 suiteType/suiteName 元数据,通过 EVAL_SUITE_TYPEEVAL_SUITE_NAME 环境变量按套件过滤跳过;
  • 默认评估模型由 GEMINI_MODEL 环境变量覆盖,缺省为 PREVIEW_GEMINI_FLASH_MODEL(见 EVAL_MODEL 常量)。

此外源码还有两个值得注意的健壮性设计:withEvalRetries 对 500/503 API 错误最多重试 3 次并在持续失败时"跳过而非判失败",把事件写入 evals/logs/api-reliability.jsonl(仓库中确有 api-reliability 集成测试 配套);prepareWorkspace 在为测试写入 files 后会执行 git init、禁用交互式 editor/pager 并做一次初始提交,让 Agent 面对一个真实可操作的 git 工作区。

修复失败的评估

fixing.md 给出四阶段流程:

1. 调查(Investigate)

  1. gh CLI 拉取 evals-nightly.yml 工作流的最新运行结果;
  2. 隔离:不推送变更、不启动远端运行,调查限定在本地工作区;
  3. 读日志:评估日志位于 evals/logs/<test_name>.log(对应源码中 prepareLogDir 生成的 ${sanitizedName}.log,内容为 rig.readToolLogs() 的 JSON 序列化);导出 GEMINI_DEBUG_LOG_FILE="debug.log" 可开启详细调试;
  4. 诊断:审计工具日志与遥测,判断失败源于 setup 还是 assert;主动加自定义日志验证假设。

2. 修复策略(Fix Strategy)

  • 定位:找到测试用例与对应的 prompt/代码位置;
  • 迭代范围:先做一个极端变更验证作用域,再收敛为最小、精准修改;
  • 断言保真:修改测试 prompt 是最后手段(prompt 常常按设计写得模糊),不要让测试因 prompt 过于直接而失去保真度;主要修复点应落在工具描述、系统 prompt(packages/core/src/prompts/snippets.ts)或贡献于 prompt 模板的模块上;
  • 指令通用性:系统 prompt 修改应尽可能通用,只按需增加特异性。与其罗列"禁用清单"(如"不要用 Object.create()"),不如提炼覆盖底层问题的工程原则(如"显式组合优先于隐式原型操作")。文档给出了三级特异性参考:低——"遵循生态最佳实践";中——"视情况运用 OOP 与函数式最佳实践";高——把生态特定提示作为广义原则的示例而非直接指令;
  • prompt 简化:测试通过后,用 ask_user 确认是否希望简化 prompt;仅当存在可去重或可归并到单一标题下的相关子句时才尝试简化,并且必须找出并运行受影响的 eval 以防回归;
  • 禁止作弊:不得通过修改测试的 GEMINI.md 或改写 prompt 来"绕开"bug 复现;
  • 架构选项:若 prompt 调整无改善,分析循环构成。AgentLoop 由 context + toolset + prompt 定义;循环在直接 prompt、更少的无关工具、低目标密度、最小低价值上下文的组合下表现最佳。修改方向可以是编排 subagent 或隔离工具,且必须基于观察到的 trace。

3. 验证(Verify)

  1. 用非交互 Vitest 只跑目标文件;
  2. 优先通过日志对比诊断,再触发重量级测试;
  3. 在关键模型(Gemini 3.0、Gemini 3 Flash、Gemini 2.5 Pro)上本地跑 3 次,可用脚本并行提速;
  4. 抖动规则:若 3 次中通过 2 次,可能是内禀噪声,不做结构性拆分难以再提升。

4. 报告(Report)

总结每个模型的成功率(如 3/3 = 100%)、根因定位与修复说明;若未修复,给出高置信度的架构建议。

晋升评估:从 USUALLY_PASSES 到 ALWAYS_PASSES

promoting.md 定义了严格的两阶段晋升流程。

1. 调查候选

  1. gh CLI 拉取 evals-nightly.yml 的结果(提示:最近一次运行的聚合摘要会自动整合最近 7 次运行的历史);全程本地验证,不推送、不启动远端运行;
  2. 稳定性判定:找出在 main 分支最近 7 次夜间运行中、所有启用模型上都 100% 通过的测试——100% 意味着每个模型、每次运行都 3/3 通过(夜间工作流每轮把每个测试连跑 3 次,以 0%/33%/66%/100% 计分);
  3. 满足条件的测试即 USUALLY_PASSESALWAYS_PASSES 的晋升候选。

evals/README.md 补充了入库门槛:测试在主要模型(如 Gemini 3.1 Pro、Gemini 3.0 Pro、Gemini 3 Flash)上应至少达到 66%,晋升前必须 100% 通过。

2. 晋升步骤

  1. evals/ 目录定位评估文件;
  2. 将策略参数改为 ALWAYS_PASSES
evalTest('ALWAYS_PASSES', { ... })
  1. 目标文件组织遵循 evals/README.md 的 stable suite 规范;
  2. 约束:最终变更必须最小、且严格限于晋升测试状态本身,不得重构测试或 setup fixtures。

running.md 还提到可通过 Agent 命令 gemini /promote-behavioral-eval 自动化晋升(技能文档描述的工作流入口;在当前仓库中该命令仅见于技能文档本身),以及 gemini /fix-behavioral-eval [optional-run-uri] 用于修复夜间回归。技能文档明确"不要手动晋升"——命令会先核验轨迹日志再更新文件策略。

3. 验证与报告

本地用非交互 Vitest 跑被晋升的测试确认结构有效性;确认测试能被标准运行范围拾取。报告内容:哪些测试被晋升、成功率证据(如"7/7 次运行、所有模型通过");若无合格候选,列出最接近的候选及其当前通过率。

回归检查脚本与 PR 质量门槛

evals/README.md 描述了 PR 上高信号回归检查的三件套脚本,均可本地运行用于调试:

  • scripts/get_trustworthy_evals.js:分析夜间历史,识别稳定测试(综合通过率 80%+);
  • scripts/run_regression_check.js:用"Best-of-4"逻辑与"动态基线校验"运行指定测试集;
  • scripts/run_eval_regression.js:主编排器,循环各模型并生成最终 PR 报告。

本地模拟 PR 回归检查:

# 对指定模型跑完整回归循环
MODEL_LIST=gemini-3-flash-preview node scripts/run_eval_regression.js

# 调试某个失败测试(与 CI 相同逻辑)
OUTPUT=$(node scripts/get_trustworthy_evals.js "gemini-3-flash-preview")
node scripts/run_regression_check.js "gemini-3-flash-preview" "$OUTPUT"

由于 LLM 非确定性,PR 回归检查采用高信号概率化方案而非 100% 通过要求:

  1. 可信度过滤(60/80):只运行有履历的测试——每个夜晚单模型至少 60%(2/3),且最近 6 天综合通过率 80%;
  2. 50% 通过规则:PR 中某测试最多尝试 4 次,成功 2 次即判为通过;
  3. 动态基线校验:若测试在 PR 上失败(如 0/3),系统自动在 main 分支复查,同样失败则标记为 Pre-existing 并放行该 PR——只让你的变更本身造成的回归阻塞你。

报告解读方面:Pass Rate (%) 是该次工作流实例中单个测试的成功率;History 展示最近 7 次夜间运行的通过率,用于识别行为是否趋向不稳定;Total Pass Rate 为该批次所有评估的聚合指标。USUALLY_PASSES 测试通过率的显著下滑(哪怕未到 0%)通常意味着最近的系统 prompt 或工具定义变更降低了模型行为的可靠性。

设计最佳实践总览

结合 evals/README.md 与技能文档,创建行为评估时应遵循五条准则:

  • 真实复杂度:在真实文件与源目录上操作,模仿 Agent 与真实工作区的交互方式;
  • 可维护规模:小到可以推理和维护(2-3 个文件的隔离场景),避免 setup 本身脆弱;
  • 断言无歧义且可靠:检查修改后的文件包含特定 AST 节点/精确字符串,或验证工具以正确参数被调用;不要只检查"发生过某工具调用"(可能出于无关原因),更不要期望特定的 LLM 输出文本;
  • Fail First:先观察到失败、写出可靠复现的 eval、再改 prompt/工具并验证通过;
  • 少即是多:优先少量真实、覆盖主要路径的测试,而非大量单测化的细碎用例。

此外 README 还给出两条经验法则:prompt 修复优先正向表述("do X"),仅在正向无法达成目标时才用负向("do not do X");且人工(或让 Agent)对任何 prompt 变更做复核——即使它通过了全部 eval。值得注意的是,evals/test-helper.tsevals/app-test-helper.ts 均在类型层面把 excludeTools/coreTools/allowedTools 等配置标记为 never——eval 必须面对完整的默认工具集运行,以确保持续验证真实行为,这条约束在源码中是强制性的。

小结

gemini-cli 的行为评估体系由三部分组成:以 SKILL.md 为入口的技能(面向 Agent 自动化修复与晋升)、以 evals/README.md 为概念与运行规范的事实来源、以及 evals/ 目录下 evalTest/appEvalTest 两套 Rig 支撑的可执行测试。其工程精髓在于:用"策略 + 孵化 + 晋升"机制驯服 LLM 的非确定性,用工具日志审计而非输出文本匹配来断言决策,用可信度过滤与动态基线让 CI 只对你的变更负责。这套实践可以直接迁移到任何需要为 LLM Agent 建立行为回归防护的开源或商业项目。

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