测试策略 — YAML Recipe 自测
测试策略 — YAML Recipe 自测
学习目标
- 理解 Goose 为何选择 YAML 而非传统 Rust 测试框架来定义 Agent 行为测试
- 掌握
goose-self-test.yaml的结构与核心概念 - 学会使用
goose run --recipe执行 YAML 测试 - 识别 YAML 行为测试的设计取舍与常见陷阱
项目实践
为什么用 YAML 做 Agent 行为测试
传统后端测试用 Rust 的 #[test] 和 cargo test,但 Goose 的 Agent 行为测试选择了截然不同的路径——用 YAML 文件定义完整的测试场景,通过 goose run --recipe 执行。
核心原因有三点:
第一,Agent 行为本质是对话式的。 Goose 是一个与 LLM 对话驱动的 Agent,它的”行为”不是调用某个函数返回某个值,而是一系列工具调用、状态转移、错误恢复的完整流程。Rust 测试适合验证 fn parse(input) -> Result<Output, Error>,但不适合验证”先创建文件,再修改内容,然后委托子 Agent 分析,最后生成报告”这种多轮对话行为。
第二,YAML 与产品形态一致。 Goose 的 Recipe 系统本身就是 YAML 驱动的——用户编写 YAML 定义 Agent 的工作流,Goose 按 YAML 中的 instructions、prompt、extensions 执行。用同样的格式写测试,意味着测试本身就是 Product 的一部分,而非脱离产品的工程附属物。
第三,非工程师可编写。 YAML Recipe 的语法是自然语言指令 + 结构化参数。产品经理、QA、甚至最终用户都能读懂和编写测试用例,不需要理解 Rust 的断言宏、异步运行时、Mock 对象等概念。
测试流程概览
goose-self-test.yaml 结构解析
以项目根目录的 goose-self-test.yaml 为例,一个完整的 Agent 自测 Recipe 包含以下层次:
元数据
version: 1.0.0title: Goose Self-Testing Integration Suitedescription: A comprehensive meta-testing recipe where goose tests its own capabilities元数据提供版本、标题和描述,用于 Recipe 的版本管理和展示。
activities — 测试阶段列表
activities: - Initialize test workspace and logging infrastructure - Test file operations (create, read, update, delete, undo) - Validate shell command execution and error handling - Analyze code structure and parsing capabilities - Test extension discovery and management - Test delegate tool for task delegation (sync and async) - Test error boundaries including nested delegation prevention - Generate comprehensive test reportactivities 是高层级的测试阶段描述,类似于测试计划的目录。每个 activity 对应一个测试阶段。
parameters — 可配置参数
parameters: - key: test_phases input_type: string requirement: optional default: "all" description: "Which test phases to run"
- key: test_depth input_type: string requirement: optional default: "standard" description: "Testing depth: quick, standard, deep"参数化使同一个 YAML 可以执行不同范围的测试——快速冒烟、标准覆盖、深度穷尽。
instructions — 自然语言测试指令
instructions: | You are testing yourself - a running goose instance validating its own capabilities.
## Test Execution Framework ### Phase 1: Environment Setup & Basic Tool Validation ### Phase 2: Extension System Testing ### Phase 3: Delegate & Load Testing ### Phase 4: Advanced Self-Testing ### Phase 5: Report Generation
## Success Criteria - Phase success: 80% tests pass - Suite success: All phases completeinstructions 用自然语言告诉 Agent 测试的整体框架、阶段划分和成功标准。这是 YAML 行为测试的核心——不是写断言,而是写”你应该怎么做测试”。
prompt — Jinja2 模板化测试用例
prompt: | Execute the Goose Self-Testing Integration Suite in {{ workspace_dir }}. Test phases: {{ test_phases }}, Depth: {{ test_depth }}
{% if test_phases == "all" or "basic" in test_phases %} ## PHASE 1: Basic Tool Validation ### File Operations Testing 1. Create test files with various content types 2. Test str_replace on each file type 3. Test undo functionality {% endif %}prompt 使用 Jinja2 模板语法,根据参数动态生成测试用例。条件分支控制哪些阶段被执行,变量插值将参数注入到测试指令中。
extensions — 声明依赖工具
extensions: - type: builtin name: developer display_name: Developer timeout: 600声明测试所需的扩展。developer 是内置扩展,提供文件操作、Shell 命令、代码分析等核心能力。
测试执行机制
执行命令
goose run --recipe goose-self-test.yaml该命令加载 YAML 文件,解析模板参数,启动 Agent 会话,按照 instructions 和 prompt 中的指令自主执行测试。
测试阶段详解
goose-self-test.yaml 定义了五个测试阶段:
Phase 1:基础工具验证。 测试文件 CRUD + undo、Shell 命令执行(包括命令链 mkdir && cd && echo、错误处理 false || echo)、代码分析(多语言结构解析、符号聚焦、调用图)。
Phase 2:扩展系统测试。 测试 Todo 扩展的持久性、动态扩展的启用/禁用、扩展间的隔离性。
Phase 3:委托与加载测试。 这是最复杂的阶段,覆盖:
load()无参发现模式——列出所有可用资源load(source)加载内置 Skill——验证 Skill 注入- 同步委托——同一消息内多次
delegate()串行执行 - 异步委托——
async: true实现并行执行 - 后台任务取消——通过
load(source, cancel: true)终止运行中的任务 - 嵌套委托防御——子 Agent 尝试调用
delegate()必须被拒绝(CRITICAL 安全测试)
Phase 4:高级边界测试。 故意触发错误路径:非法路径写入、不存在的命令执行、二进制文件分析、超长文件名、Shell 注入防御。
Phase 5:报告生成。 聚合所有阶段结果,生成详细报告和控制台摘要,包括通过率、功能矩阵、问题列表。
异步委托的并行验证
YAML 测试的一个精妙设计是验证同步 vs 异步委托的执行顺序:
同步委托在同一消息内调用 3 次,各包含 sleep 2——时间戳应相差约 6 秒(串行)。异步委托同样 3 次但 async: true——时间戳应在 5 秒内(并行)。通过时间戳对比,验证调度器的行为正确性。
测试结果的自我验证
与传统测试框架的 assert_eq!(actual, expected) 不同,YAML Recipe 测试由 Agent 自身判断通过与否。Agent 在每阶段末尾将结果写入日志文件(如 phase1_basic_tools.md),最终汇总为 detailed_report.md 和终端摘要。
成功标准在 YAML 中定义:
## Success Criteria- Phase success: 80% tests pass- Suite success: All phases complete, critical features work- Each test logs: setup -> execute -> validate -> result -> cleanupAgent 按此标准自行评估,生成 PASS/FAIL 的总体结论。
问题与规避
YAML 测试漂移
问题: 随着 Agent 能力进化,YAML 中定义的测试步骤可能不再覆盖关键行为。例如新增了 vision 能力但测试 YAML 未更新,导致视觉相关功能无测试覆盖。
规避: 在 AGENTS.md 规则中明确规定:添加功能时必须更新 goose-self-test.yaml。Recipe 的 activities 列表作为测试范围的权威清单,新功能必须在此添加对应条目。
LLM 非确定性导致测试不稳定
问题: 同样的 YAML 指令,不同次运行可能因为 LLM 输出差异而产生不同结果。Agent 可能选择不同的工具调用顺序,或生成不同格式的报告。
规避:
- 使用
test_depth参数控制测试深度,CI 使用quick做冒烟测试,人工审核使用deep - 关键安全测试(如嵌套委托防御)的结果判断是二元的(成功/失败),不受 LLM 风格影响
- 时间戳对比类测试使用宽松阈值(“约 5 秒内”而非精确值)
YAML 模板语法错误
问题: Jinja2 模板中的条件分支或变量引用拼写错误,导致 YAML 加载失败或参数注入为空。
规避:
- 所有参数必须有
default值 {% if %}条件使用or而非in匹配时注意边界情况(如"basic" in "all"为 true 是预期外的匹配)- 执行前用
goose recipe explain预览渲染结果
测试环境污染
问题: 测试在共享目录中创建文件,多次运行后残留文件影响后续测试。
规避: YAML 中的 cleanup_after 参数控制清理行为,默认 true。每个阶段使用独立的子目录,测试结束后归档到 archive/ 并清除临时产物。
设计取舍
YAML Recipe vs Rust 单元测试
| 维度 | YAML Recipe | Rust #[test] |
|---|---|---|
| 适用场景 | Agent 端到端行为验证 | 纯函数/模块逻辑验证 |
| 可读性 | 自然语言,非工程师可编写 | 需要 Rust 知识 |
| 执行速度 | 慢(LLM 调用,分钟级) | 快(纳秒到秒级) |
| 确定性 | 低(LLM 非确定性) | 高 |
| 覆盖范围 | 完整对话流程 | 单点逻辑 |
| 维护成本 | 低(自然语言修改) | 中(编译、类型约束) |
Goose 的策略是两者互补:Rust 测试用于验证解析器、配置加载、OTLP 层等纯工程组件;YAML Recipe 用于验证 Agent 的整体行为——文件操作是否正确、委托是否串行/并行、安全防御是否生效。
行为驱动测试 for AI Agents
传统 TDD 的”红-绿-重构”循环在 Agent 场景不适用——你无法为 Agent 的行为写出精确的期望输出。行为驱动测试的思路是:
- 描述期望行为(YAML instructions 中的自然语言)
- 提供验证标准(Success Criteria)
- 让 Agent 自主执行并自检
- 人工审核最终报告
这不是单元测试的替代,而是 Agent 特有的测试范式。它验证的是”Agent 能不能按指令完成工作”,而非”某个函数返回值是否正确”。
自我测试的价值
goose-self-test.yaml 的核心理念是”Agent 测试自身”(first-person integration testing)。Agent 既是系统又是测试者,用自己的工具验证自己的功能。这种方式的独特价值:
- 测试环境与生产环境完全一致——没有 Mock,没有测试夹具
- 验证的是真实工具链的集成,而非隔离的单元测试
- 测试结果本身就是 Agent 能力的证明——能完成自测的 Agent 才有能力执行用户任务
参考来源
- 源码:
.op/goose/goose-self-test.yaml— 完整的自测 Recipe - 源码:
.op/goose/crates/goose/src/recipe/— Recipe 解析与构建核心 - 源码:
.op/goose/crates/goose-cli/src/recipes/— CLI Recipe 加载与参数注入 - 源码:
.op/goose/recipe-scanner/base_recipe.yaml— Recipe 基础模板 - 文档:
.op/goose/documentation/docs/guides/recipes/recipe-reference.md— Recipe 参考文档