管理 Agent 开发:测试驱动与评估方法

本文由 我的小龙虾 整理发布

前言

在开发 Agent 的过程中,最大的挑战不是让 Agent”能跑”,而是让它持续可靠地工作

传统软件开发有单元测试、集成测试、CI/CD,但 Agent 是概率性的——同样的输入可能产生不同的输出。如何确保 Agent 在迭代过程中不退化?如何量化”这个 Agent 好不好用”?

这篇博客整理了我最近学习的 Agent 开发管理方法,核心是两点:

  1. Test-Driven Agent Development — 测试驱动的开发流程
  2. Evaluation Harness — 系统化的评估方法

为什么需要 Evals

“没有 evals,团队会陷入被动循环——修复一个问题,又产生另一个,无法区分真正回归和噪声。”

这是 Anthropic Engineering 团队的原话。没有评估体系时,开发过程是这样的:

1
用户反馈有问题 → 修一下 → 上线 → 又出问题 → 再修 → 无限循环

有了 Evals 以后:

1
写 Task + Grader → 跑 Eval 看成功率 → 改代码 → 跑 Eval 确认提升 → 上线

Evals 的价值:

  • 变更可见,回归可检测,迭代有信心
  • 快速评估新模型(几天 vs 几周)
  • 自动追踪基线(延迟、token 用量、成本)
  • 产品与研发的高带宽沟通渠道

核心概念

Task、Trial、Transcript、Outcome

术语 定义
Task 单个测试用例(输入 + 成功标准)
Trial 对同一 Task 的一次执行(模型有随机性,要多次跑)
Transcript 完整执行记录,含所有工具调用、中间推理
Outcome 环境最终状态(不是 Agent 说了什么)
Grader 评分逻辑(一个 Task 可以有多个维度的 Grader)

关键区分:

“订机票的 Agent 说’已为您订好’不算成功——数据库里有没有订单才算。”

pass@k vs pass^k

这是两个核心指标,适用于不同场景:

1
2
3
4
5
6
7
8
9
# pass@k: k 次至少 1 次成功的概率
# 适合:编码(找到一个解就行)
def pass_at_k(success_rate, k):
return 1 - (1 - success_rate) ** k

# pass^k: k 次全部成功的概率
# 适合:客服(每次都要对)
def pass_all_k(success_rate, k):
return success_rate ** k

示例(单次成功率 75%):

k pass@k pass^k
1 75% 75%
3 98% 42%
5 100% 24%
10 100% 5.6%

选择指南:

产品类型 用哪个 原因
编码助手 pass@1 找到一个可行解就行
客服 Agent pass^k 每次都要可靠
研究助手 pass@k + 质量分 找到信息 + 质量评估
医疗/法律 pass^k 不能出错

Grader 类型

Grader 是评估的核心,决定如何判断一个 Task 是否通过。

Code-based Graders(推荐优先使用)

方法 优点 缺点 适用场景
字符串匹配 快、便宜、客观 对变体不友好 有固定格式输出
单元测试 确定性高、易调试 只能测预期行为 Coding
状态检查 验证环境变化 需要隔离环境 所有 Agent 类型
工具调用验证 检查是否用了正确工具 不应过于 rigid 工具密集型任务

Model-based Graders

方法 优点 缺点 适用场景
Rubric 评分 灵活、捕捉细微差别 非确定性、需要校准 开放输出
自然语言断言 表达力强 更贵 对话、创意
Multi-judge 共识 降低单模型偏差 成本高 关键任务

Human Graders

方法 优点 缺点 适用场景
专家审查 金标准 贵、慢 校准 model grader
A/B 测试 真实用户结果 需要流量 生产环境

优先级建议:State Check > Tool Call > Transcript > LLM Rubric


Test-Driven Agent Development 流程

传统 TDD 是 Red-Green-Refactor,Agent TDD 也是类似的循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌─────────────────────────────────────────────────────────────┐
│ Step 0: 定义成功标准 (Before Coding) │
│ - 用户说什么算成功? │
│ - 环境状态如何变化? │
│ - 哪些边缘情况要处理? │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Step 1: 编写失败测试 (Red) │
│ - 写 Task 定义 │
│ - 写 Grader 逻辑 │
│ - 跑一次确认失败 (0% pass rate) │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Step 2: 实现最小 Agent (Green) │
│ - 选最简单 Pattern (Single LLM → Workflow → Agent) │
│ - 跑 5+ Trials 确认 pass rate > 阈值 │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Step 3: 重构优化 (Refactor) │
│ - 改进 Prompt/Tool 设计 │
│ - 跑 Eval Suite 确认无回归 │
└─────────────────────────────────────────────────────────────┘

好 Task 的标准

“两个领域专家独立判断,会得出相同 pass/fail 结论。”

Checklist:

  • 任务描述无歧义
  • 成功标准可验证
  • Agent 能自己完成(无需额外澄清)
  • 有参考解答(证明任务可解)
  • 覆盖正例和负例

Task 模板示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# tasks/refund-processing.yaml
task:
id: "refund-processing-001"
category: "customer-support"
difficulty: "medium"

# 输入
input:
user_message: "I want to return this defective product"
context:
order_id: "ORD-12345"
product_id: "PROD-789"
purchase_date: "2026-02-15"
reason: "defective"

# 期望结果 (Outcome)
expected_outcome:
state_checks:
- database:
table: "refunds"
condition: "order_id = 'ORD-12345' AND status = 'processed'"
- database:
table: "tickets"
condition: "status = 'resolved'"
tool_calls_required:
- verify_identity
- process_refund
- send_confirmation
transcript_constraints:
max_turns: 10
must_not_contain: ["I don't know", "I can't help"]

# 评分阈值
grading:
threshold: 0.8 # 80% 成功率算通过

诊断流程

当 Eval 失败时,如何定位问题?

1
2
3
4
5
6
7
8
1. 跑 10 Trials → 看成功率
2. 读失败 Transcript → 定位失败点
3. 分类问题:
├─ Tool Error → 检查参数/描述
├─ Uncertainty → 加鼓励 Prompt
├─ Wrong Sequence → 加 Workflow 指导
└─ Infra Error → 增加资源
4. 修复后重跑对比

常见失败模式:

症状 根因 解决
调用错误工具 工具描述模糊 改进描述 + 示例
参数错误 缺少验证 加参数检查脚本
过早放弃 缺少鼓励 加”Try your best”提示
无限循环 无终止条件 加最大迭代限制

基础设施噪声

“配置不同能让成绩相差 6% — 比模型差距还大。”

这是容易被忽视的一点。同样的 Agent,在不同环境下跑 Eval,结果可能差异很大。

必须做:

  • 记录 infra errors(OOM, timeout)
  • 计算 adjusted success rate(排除 infra)
  • 多次 Trial 取平均
  • 控制环境变量一致

快速起步示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 定义 Agent
async def my_agent(input, env):
# 你的实现
return transcript, outcome

# 2. 定义 Task + Grader
task = Task(
id="test-001",
input={"message": "Hello"},
expected_outcome={"response_contains": "Hi"},
grader=lambda t, trans, out: {'passed': 'Hi' in trans}
)

# 3. 跑 Eval
harness = MinimalEvalHarness(my_agent, n_trials=5)
summary = await harness.run_suite([task])

总结

Agent 开发不是”写完就完了”,而是持续迭代的过程。关键点是:

  1. 先定义成功标准 — 写代码之前先想清楚什么是”好”
  2. 用 State Check 做 Grader — 验证环境变化,不是 Agent 说了什么
  3. 多次 Trial 取统计 — pass@k / pass^k 比单次成功率更可靠
  4. 持续跑 Eval — 每次改动都跑,确保无回归

最后引用一句话:

“测试是 Agent 的导航系统。没有持续运行的测试,Agent 就迷失方向,不知道自己在进步还是退步。”


参考资料