16 - 评估与测试:如何知道 Agent 好不好用
一句话总结:传统软件可以断言输入输出,但 Agent 的回答天生不确定 – 你需要用断言检查、关键词匹配、工具调用验证、甚至用另一个 Agent 当裁判,才能系统性地衡量 Agent 的质量。
为什么 Agent 的测试不一样
写普通函数的单元测试,你习惯这样:
assert add(1, 1) == 2
干净利落,跑一万次结果都一样。
但 Agent 不是这样的。你问它”1+1等于多少”,它可能回答”2”,也可能回答”1+1的结果是2”,甚至来一句”答案是二”。内容是对的,但格式每次都不一样。这就是非确定性输出的麻烦之处。
所以评估 Agent 需要换一套思路:
| 策略 | 适用场景 | 核心思路 |
|---|---|---|
| 断言检查 | 有明确答案的任务 | 检查输出是否包含关键内容 |
| 关键词匹配 | 开放式回答 | 看回答是否覆盖了关键概念 |
| 工具调用验证 | 带工具的 Agent | 检查是否调用了正确的工具 |
| Agent-as-Judge | 复杂的质量评估 | 用更强的模型给回答打分 |
下面逐个来看。
断言检查:最朴素也最实用
对于有标准答案的场景,直接写 lambda 做断言就行。不用搞得太复杂,能跑通就是好测试:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
instructions="你是一个数学助手,只回答数字结果",
)
# 简单的断言测试
test_cases = [
("1+1等于多少?", lambda r: "2" in str(r.content)),
("100除以5?", lambda r: "20" in str(r.content)),
("7乘以8?", lambda r: "56" in str(r.content)),
("144的平方根?", lambda r: "12" in str(r.content)),
]
passed_count = 0
for question, check in test_cases:
response = agent.run(question)
passed = check(response)
if passed:
passed_count += 1
status = "PASS" if passed else "FAIL"
print(f"[{status}] {question} -> {response.content}")
print(f"\n通过率: {passed_count}/{len(test_cases)}")
这种方式的好处是快、直接、容易集成到 CI 里。缺点也明显 – 只能处理有确定答案的场景。对于”解释一下什么是递归”这种开放式问题,断言就不够用了。
关键词匹配:开放式回答的评估
没有标准答案怎么办?退而求其次,检查回答里有没有出现关键概念。如果问”Python是什么”,回答里至少应该提到”编程语言”吧?
from agno.agent import Agent
from agno.models.openai import OpenAIChat
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
instructions="你是一个编程知识助手,用中文回答",
)
test_suite = [
{
"input": "Python是什么?",
"expected_keywords": ["编程语言", "解释型"],
},
{
"input": "什么是API?",
"expected_keywords": ["接口", "应用程序"],
},
{
"input": "数据库索引有什么用?",
"expected_keywords": ["查询", "性能"],
},
]
results = []
for test in test_suite:
response = agent.run(test["input"])
content = str(response.content)
hits = [kw for kw in test["expected_keywords"] if kw in content]
score = len(hits) / len(test["expected_keywords"])
results.append({
"input": test["input"],
"keywords_found": hits,
"total_keywords": len(test["expected_keywords"]),
"score": score,
})
status = "PASS" if score >= 0.5 else "FAIL"
print(f"[{status}] {test['input']}")
print(f" 命中关键词: {hits} ({score:.0%})")
# 汇总
avg_score = sum(r["score"] for r in results) / len(results)
print(f"\n平均得分: {avg_score:.1%}")
阈值设多少看你的场景。0.5 算宽松,0.8 算严格。关键词列表也别太苛刻 – 模型可能用同义词表达,比如”编程语言”说成”程序设计语言”。
工具调用验证:Agent 有没有做对的事
带工具的 Agent,除了看最终回答,还要验证它有没有调用正确的工具。如果你问股价但 Agent 根本没调 YFinance,那回答八成是编的。
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools.yfinance import YFinanceTools
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
tools=[YFinanceTools()],
)
response = agent.run("NVIDIA的股价是多少?")
# 检查工具调用记录
tool_calls = [te.tool_name for te in (response.tool_executions or [])]
print(f"调用的工具: {tool_calls}")
# 验证调用了正确的工具
has_finance_tool = any(
"stock" in t.lower() or "price" in t.lower() or "yfinance" in t.lower()
for t in tool_calls
)
if has_finance_tool:
print("[PASS] 正确调用了金融数据工具")
else:
print(f"[FAIL] 期望调用 YFinance 工具,实际调用: {tool_calls}")
# 也可以检查回答里有没有数字(股价总得是个数字吧)
content = str(response.content)
has_number = any(c.isdigit() for c in content)
print(f"[{'PASS' if has_number else 'FAIL'}] 回答中包含数字")
这种测试抓的是”行为”而不是”内容”。股价每秒都在变,你没法断言具体数字,但你可以断言 Agent 的行为路径是对的。
Agent-as-Judge:让 AI 评估 AI
到了最有意思的部分。对于真正复杂的、开放式的回答,人类评估太慢,关键词匹配又太粗糙。怎么办?用一个更强的模型当裁判。
这个思路在学术界叫 LLM-as-Judge,在实际工程里非常好用:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
# 被评估的 Agent
target_agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
instructions="你是一个Python教学助手",
)
# 裁判 Agent -- 用更强的模型
judge_agent = Agent(
model=OpenAIChat(id="gpt-4o"),
instructions="""\
你是一个评估专家。评估AI助手的回答质量。
打分标准(1-10分):
- 准确性:信息是否正确
- 完整性:是否回答了问题的核心
- 清晰度:表达是否清楚易懂
输出格式:
准确性: X/10
完整性: X/10
清晰度: X/10
总分: X/10
评语: 一句话总结""",
)
# 准备测试问题
questions = [
"解释Python的列表推导式",
"什么时候应该用class而不是function?",
]
for question in questions:
print(f"--- 问题: {question} ---")
response = target_agent.run(question)
print(f"回答: {str(response.content)[:200]}...")
print()
evaluation = judge_agent.run(
f"问题: {question}\n\n回答: {response.content}\n\n请评估这个回答的质量。"
)
print(f"评估:\n{evaluation.content}")
print()
用 gpt-4o 评估 gpt-4o-mini 的回答,这是最常见的搭配。裁判模型要比被评估的模型强,不然就像让实习生评价高级工程师的代码一样。
多轮评估:应对非确定性
Agent 的输出不确定,那就多跑几次。如果一个测试跑 5 次有 4 次通过,说明 Agent 在这个问题上基本靠谱:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
instructions="你是一个数学助手,只回答数字",
)
def evaluate_with_retry(question, check_fn, runs=5):
"""多次运行取通过率"""
passes = 0
for i in range(runs):
response = agent.run(question)
if check_fn(response):
passes += 1
return passes / runs
# 测试
cases = [
("15乘以4?", lambda r: "60" in str(r.content)),
("256的平方根?", lambda r: "16" in str(r.content)),
]
for question, check in cases:
rate = evaluate_with_retry(question, check, runs=3)
status = "PASS" if rate >= 0.6 else "FAIL"
print(f"[{status}] {question} -- 通过率: {rate:.0%}")
三次里过两次就算 PASS,这是一个比较务实的标准。生产环境里可以根据业务要求调整阈值。
把评估变成习惯
上面这些技术手段都不难,难的是坚持做。几个建议:
跑评估要成为流程的一部分。改了 instructions,跑一遍。换了模型,跑一遍。加了工具,跑一遍。别等上线了才发现 Agent 开始说胡话。
用更强的模型当裁判,但别盲信。gpt-4o 当裁判很好用,但它也有偏见 – 它倾向于给长回答更高的分。了解裁判的偏好,在评估标准里对冲掉。
测边界情况。正常问题 Agent 都能答好,关键是那些刁钻的、模糊的、多义的问题。”介绍一下苹果” – 你期望它说水果还是公司?
追踪指标变化。单次评估说明不了什么,趋势才有意义。把每次评估的结果存下来,看通过率是在上升还是下降。
今天学了什么
回顾一下关键概念:
- 断言检查最朴素但最实用,适合有标准答案的场景 – 用 lambda 检查输出是否包含预期内容
- 关键词匹配适合开放式回答,通过检查关键概念的覆盖率来评分
- 工具调用验证关注 Agent 的行为路径,确保它调用了正确的工具而不是在编造结果
- Agent-as-Judge 用更强的模型当裁判,是评估复杂回答质量的最佳方案
- 多轮评估对抗非确定性,多跑几次看通过率比单次测试靠谱得多
- 评估不是一次性的事,要融入开发流程,持续追踪指标变化
下一篇预告
17 - 综合实战:从零构建一个完整的 AI 研究助手
最后一篇,我们把前面学的所有东西串起来 – 多 Agent 协作、工具调用、结构化输出、记忆、评估 – 从零搭一个能搜索资料、整理信息、生成报告的研究助手。算是整个系列的毕业设计。