实践中的代理人与技能评估:发展轨迹、评分者、pass^k标准及工具
如何评估智能体与技能:触发与执行组件、评分强度排序、精确度与召回率、pass@k与pass^k的区别、一个简单的Python工具包及实用选型。
迟早,每一个部署了智能体与相应技能的团队都会遇到同一个问题:演示时效果令人印象深刻,但实际使用中却会暴露出之前无人察觉的缺陷。评估机制则用数据取代了猜测。以下是一份实用指南,介绍应该衡量什么、如何编写测试用例与评分标准,以及如何通过改进循环真正提升智能体的性能。
思维模型
评估其实是一种可重复的实验。给智能体设定输入与环境,然后观察两个方面:其行为路径,即智能体在处理任务过程中的操作;以及最终结果,即它生成的成品。
需要从两方面进行评判:
- 最终结果:生成的成品是否完成了任务要求?
- 行为路径:智能体是否选择了合适的技能与工具,避免了危险操作,并且高效地完成了工作?
一个看似完美的最终答案可能掩盖了糟糕的处理流程,而粗糙的流程也有可能偶然得到不错的结果。仅看其中一点就无法了解全貌。
评估的多个层面
没有单一的数值能够说明一个智能体的优劣。需将问题拆解为多个层面,每个层面都有相应的检验标准:
- 技能选择。是否选对了合适的技能?需考察精确度、召回率以及误触发情况。
- 技能遵循度。是否真正按照技能的指示行事,包括所需的步骤和禁止的操作?
- 工具使用。是否正确调用了工具,参数是否恰当,顺序是否合理,且没有不必要的调用?
- 任务结果。结果是否有效?可通过测试、模式检查以及行为断言来回答这个问题。
一项技能需要两套评估标准
技能不仅仅是一组指令,它还包含了关于何时激活的决策以及之后的行动计划,因此需要两套独立的评估标准。
触发条件评估用于检查该技能是否会在合适的提示下被激活。每种情况都会将一个提示与预期的技能,或不应被激活的技能进行匹配:
- prompt: "Patch the vulnerable npm dependencies"
expected_skill: cve-remediation
- prompt: "Review authentication input validation"
forbidden_skill: cve-remediation
要掩盖明显的正面结果、重新表述正面内容、处理近乎负面的情况、处理含糊的提示,以及两种合理技能之间的冲突。应报告混淆矩阵而非单纯的准确率,因为误报带来的危害与未能触发响应同样严重。
执行评估旨在判断使用该技能是否提升了工作效果。需将未配备该技能的智能体与配备了该技能的相同智能体放在相同案例、环境、模型和工具条件下进行对比测试,并由于智能体的非确定性而重复多次运行。只有当该技能带来的改进足以弥补其增加的复杂度和计算成本时,它才有存在的价值。
优质案例的特征
应优先选择真实的代码库快照,而非零散的琐碎信息。一个优质案例应是自包含的,且能够被机器验证:
{
"id": "fix-null-condition",
"prompt": "Fix saving rules with a null condition",
"fixture": "repos/null-condition",
"setup": ["npm install"],
"checks": [
"npm test -- null-condition.test.js",
"git diff --check"
],
"rubric": [
"Fixes the root cause",
"Preserves existing behavior",
"Adds a regression test",
"Avoids unrelated changes"
],
"forbidden": [
"deleting existing tests",
"hard-coded fixture-specific output"
]
}
评分工具:使用最强大的那个
优先选择确定性最高的评分工具,只有在不得已时才依靠人工判断。大致从最强到最弱排序如下:
- 精确断言
- 编译及单元测试或集成测试
- 模式或语法树验证
- 通过浏览器或API进行行为检测
- 对执行轨迹的静态检查
- 依据评分标准进行判断的LLM
- 人工审核
LLM评分工具适用于评估清晰度与架构适配性等指标,但不可替代可执行的检查方式。应隐藏生成结果的版本信息,并打乱比较顺序。
值得追踪的指标
- 成功率是指所有运行中通过所有关键检查的比例。
对于生产环境中的智能体而言,稳定的可靠性通常比出色的最佳情况表现更为重要。
构建数据集
首先从实际工作中选取20到50个案例:早期的失败与支持请求、日常任务、边缘情况、具有误导性或恶意性质的请求、描述不明确的提示、应当被拒绝或需要进一步澄清的任务,以及无需启用任何技能的情况。
然后将这些案例分为三组:
- 一个开发集,用于在改进技能时参考
- 一个保留集,在验证之前不予处理
- 一个回归集,用于永久存储所有发现的失败案例
避免仅针对某个固定的公共集进行优化,因为智能体很容易过度拟合该集的表述方式。
工作循环
可以将其视为针对智能体行为的测试驱动开发:
- 记录一次真实的失败案例。
- 将其简化为可复现的测试用例。
- 在接触相关技能或代理之前先编写评分规则。
- 运行若干次基准测试。
- 进行一次有针对性的修改。
- 重新运行目标案例以及整套回归测试套件。
- 比较质量、可靠性、延迟和成本。
- 只有当整体表现有所提升时才保留该修改。
常见错误
- 仅对最终生成的文本进行评分
- 仅使用理想路径的提示词
- 检查的是指令是否被重复而非是否被遵循
- 让大语言模型裁判去判断本可通过测试验证的事实
- 仅运行一次就认为结果具有确定性
- 同时更改提示词、模型、工具和技能
- 将删除测试或篡改评分规则视为成功
- 使用与真实用户操作毫无相似之处的合成案例
- 忽视隐藏在文件或工具输出中的提示注入风险
用具体术语解释路由指标
假设存在一种安全审查能力,评估者会针对每个提示判断是否应触发该能力。可能存在四种结果:
- 真阳性(TP):应当触发且确实触发了。
- 假阳性(FP):本不应触发却触发了,属于误触发。
- 假阴性(FN):本应触发却未触发。
- 真阴性(TN):本不应触发且确实未触发。
精确度衡量触发判断的正确频率:真阳性数除以(真阳性数+假阳性数)。如果该能力触发了十次且其中八次正确,那么精确度为80%。高精确度意味着误触发较少。
召回率指实际识别出需要该技能的提示数量占总需求量的比例:正确识别数除以(正确识别数+误判数)。如果有10个提示需要该技能而仅识别出7个,那么召回率为70%。
误触发其实就是假阳性。例如“升级React依赖项”这样的提示错误地触发了安全审查,就属于误触发。大量误触发意味着精确度较低。
混淆矩阵可以统计所有相关数据。当正确识别数为8,误判数为2,假阳性数为3,未触发数为7时,应该被触发的提示有8个,未被触发的提示有2个;而本不应被触发的提示有3个,实际被触发了7个。精确度为8/11,约为72.7%,召回率为8/10,即80%。高精确度意味着该技能很少误触发,高召回率则意味着它很少漏掉相关请求。过于宽泛的触发条件通常会提升召回率但降低精确度。
从何处开始
对于重要的技能而言,一个精简的测试套件就能发挥很大作用。准备十个应能触发该技能的提示以及十个不应触发的类似情况,再加上五个需要实际执行的真实任务。还需加入确定性结果检查、每个任务三到五次重复测试、不使用该技能时的基准测试,以及用于记录所有故障的回归测试套件。
如此简小的测试环境就能暴露大多数路由问题,从而判断该技能是否真正有效,而非仅仅显得复杂高级。
可直接复制的最小框架
理论部分已足够。一个仅使用标准库的简单Python脚本就能评估技能的路由处理能力以及智能体的任务结果。该脚本约70行代码,涵盖了上述所有要点:案例文件、分层评分机制、重复测试以及可靠性指标。
文件夹结构如下:
evals/
cases/
skills/security-review.trigger.json
agents/fix-null-condition.json
graders.py
metrics.py
runner.py
run.py # CLI entry1. Case files. A skill trigger case is just positives and negatives. An agent case is a prompt plus graders and a run count.
触发条件文件会列出技能名称,并说明哪些情况应触发该技能、哪些情况不应触发:
// cases/skills/security-review.trigger.json
{
"skill": "security-review",
"positives": [
"Audit this endpoint for SQL injection",
"Check the login flow for auth bypasses",
"Is this file-upload handler safe?"
],
"negatives": [
"Upgrade React dependencies",
"Rename this variable across the repo",
"Add a loading spinner to the form"
]
}
一个代理案件档案包含编号、提示、固定要素以及需应用的检查项:
// cases/agents/fix-null-condition.json
{
"id": "fix-null-condition",
"prompt": "Fix saving rules with a null condition",
"fixture": "fixtures/null-condition",
"setup": ["npm ci"],
"runs": 5,
"graders": [
{ "type": "shell", "cmd": "npm test -- null-condition", "critical": true },
{ "type": "shell", "cmd": "git diff --check" },
{ "type": "forbidden", "pattern": "it\\.skip|xit\\(", "message": "must not disable tests" }
]
}
接下来是评分环节。一个程序负责运行诸如测试套件之类的shell命令,另一个程序会扫描轨迹以检测是否存在被禁止的修改,而一个无法知晓自己正在评估哪个版本的评分模型则负责处理主观质量方面的评价。
# graders.py
import re, subprocess
def shell(step, ctx):
r = subprocess.run(step["cmd"], cwd=ctx.workdir, shell=True,
capture_output=True, text=True)
ok = r.returncode == 0
return {"pass": ok, "score": 1 if ok else 0, "detail": r.stdout[-400:]}
def forbidden(step, ctx):
bad = re.search(step["pattern"], ctx.diff) is not None
return {"pass": not bad, "score": 0 if bad else 1,
"detail": step["message"] if bad else ""}
def judge(step, ctx):
v = ctx.llm.rate(step["rubric"], ctx.artifact) # 0..1, blinded
return {"pass": v >= step.get("threshold", 0.7), "score": v}
GRADERS = {"shell": shell, "forbidden": forbidden, "judge": judge}3. Metrics. Success rate, pass@k, pass^k, and the routing confusion matrix — exactly the numbers from earlier.
指标模块将运行结果列表转换为数值:
# metrics.py
def success_rate(rs):
return sum(1 for r in rs if r["pass"]) / len(rs)
def pass_at_k(rs):
return 1 if any(r["pass"] for r in rs) else 0
def pass_hat_k(rs):
return 1 if all(r["pass"] for r in rs) else 0
def routing(positives, negatives, fired):
tp = sum(1 for p in positives if fired(p))
fp = sum(1 for n in negatives if fired(n))
fn = len(positives) - tp
tn = len(negatives) - fp
return {"tp": tp, "fp": fp, "fn": fn, "tn": tn,
"precision": tp / (tp + fp or 1),
"recall": tp / (tp + fn or 1)}4. The runner. One function per suite. The agent runner repeats each case so pass^k is meaningful; the skill runner just asks the router which skills fire.
运行程序会加载测试用例,调用评分器,重复每次运行,并输出相关指标:
# runner.py
import json
from graders import GRADERS
from metrics import success_rate, pass_at_k, pass_hat_k, routing
def run_agent_case(agent, path):
c = json.load(open(path))
runs = []
for _ in range(c.get("runs", 3)):
ctx = agent.run(c["prompt"], fixture=c["fixture"], setup=c["setup"])
ok = True
for step in c["graders"]:
g = GRADERS[step["type"]](step, ctx)
if step.get("critical") and not g["pass"]:
ok = False
runs.append({"pass": ok})
return {"id": c["id"], "success": success_rate(runs),
"pass_at_k": pass_at_k(runs), "pass_hat_k": pass_hat_k(runs)}
def run_skill_trigger(router, path):
c = json.load(open(path))
fired = lambda prompt: c["skill"] in router.route(prompt)
return {"skill": c["skill"], **routing(c["positives"], c["negatives"], fired)}5. Run it. The CLI just dispatches on the case type and prints the metrics.
运行它后会产生如下输出:
$ python run.py cases/agents/fix-null-condition.json
fix-null-condition success=0.80 pass@5=1.00 pass^5=0.20
$ python run.py cases/skills/security-review.trigger.json
security-review precision=0.86 recall=1.00 (tp=6 fp=1 fn=0 tn=5)
按照这种方法的建议来解读这些数据。该模型在五次测试中通过了四次,因此pass@5的评分看起来很理想,但pass^5的值为0.20,说明它还不够可靠。该模型的准确率高达1.0,能够捕捉到所有真实的请求,但精确率为0.86,存在一次不该触发的错误响应,因此还有需要改进的地方。
这正是评估的意义所在:将“似乎有效”这样的主观判断转化为可以改进的数值。从这样的基础框架开始,每当出现问题时就补充新的测试案例,让回归测试集逐渐形成你对“良好表现”的明确定义。
条件允许时请使用现有框架
自制的框架有助于学习相关机制并实现完全控制,但成熟的开源项目已经提供了测试用例、评分工具、数据集和仪表板。在自行开发之前,先研究这些现有方案。
通用提示词与大语言模型评估
OpenAI Evals 是最初的注册表式框架,通过 YAML 定义评估任务,并使用 Python 评分工具针对任意模型进行评估,适用于输出级和分类检查。promptfoo 以配置驱动,以并排矩阵形式展示模型对应的提示词,内置包含检测、正则表达式、JSON 模式以及 LLM 评分标准等断言功能,还具备红队模式,因此非常适合用于技能路由和提示词回归测试。DeepEval 的使用方式类似 LLM 领域的 pytest,提供忠实度、相关性及幻觉指标,同时还支持自定义的 G-Eval 评分标准,可轻松集成到 CI 流水线中。Ragas 则专门针对 RAG 流水线设计,可评估上下文精度与召回率、忠实度以及答案相关性。如果已经在使用 MLflow,其 LLM 评估功能还可实现跟踪与对比。
追踪、数据集与评估平台
LangSmith提供数据集、轨迹追踪工具及评估函数,适用于需要查看完整轨迹而不仅仅是结果的场景。Langfuse是开源软件,可自行托管且与具体模型无关,具备用于生产环境监控的追踪、评估及数据集功能。Braintrust将评估视为持续集成流程的一部分,提供评分函数、带版本控制的数据集以及实验差异对比功能。Arize Phoenix基于OpenTelemetry追踪技术构建,包含评估模板,有助于检测实时流量中的功能退化问题。来自英国人工智能安全研究所的Inspect则是一个经过精心设计的求解器与评分框架,正在逐步成为用于严肃的智能体及安全评估的工具。
智能体基准测试
SWE-bench和SWE-bench Verified是以仓库自身的测试作为评分标准,用来判断智能体能否修复真实的GitHub问题。AgentBench、WebArena、GAIA和τ-bench则为工具使用、网页任务及多步骤智能体提供了外部评估标准。AutoGen和LangGraph等框架则提供了测试钩子,用于验证工具调用及中间步骤。
选择建议
- 如果仅需处理提示词和技能路由,promptfoo是实现配置与断言的最快方式。
- 对于以代码为核心、适用于CI环境的指标评估,可考虑DeepEval或Braintrust。
- 若要评估多步骤智能体的执行过程,可选用LangSmith、Langfuse或Inspect。
- 针对RAG场景,可加入Ragas。
- 如需真实的编程智能体基准测试,应使用SWE-bench Verified。
这一模式在各种场景中都适用:案例分析、评分、重复运行以及随时间进行的跟踪。框架可以简化底层实现,因此要保持自律,选择适合自身技术栈的工具。自定义解决方案最好用于那些现有工具无法解决的缺陷,或是那些希望在持续集成过程中完全避免依赖的项目。
相关阅读
- AI智能体的评估驱动开发:契约、追踪与评判机制 —— 将评估视为智能体的CI/CD流程:将提示词视为契约,使用OpenTelemetry和Tempo追踪运行过程,再通过确定性检查及大型语言模型评判器进行测试。