AI Agent 测试与评估工程实战 2026:从 Function Calling 单元测试到端到端评估的完整路径
约 23 分钟6666 字4 次阅读
AI Agent 测试与评估工程实战 2026:从 Function Calling 单元测试到端到端评估的完整路径
当 LangChain 把 create_agent 定义为「Model + Harness」、把 Tool Calling 标准化为跨 OpenAI / Anthropic / Google 的统一接口之后,AI Agent 的「业务代码」门槛实际上已经接近 0。真正把 90% 的团队挡在生产环境门外的,是测试与评估。 2026 年 6 月我们盘点一遍 OpenAI Function Calling、LangChain create_agent、Vercel AI SDK、Pydantic AI、Instructor 五套主流框架的工程实践,发现没有一套框架替你解决了「Agent 在生产环境里到底有没有真的变好」这个问题——这件事必须由工程团队自己搭。
本文基于 2026 年 6 月 16 日最新一次仓库与文档快照,所有 Star 数、commit 时间、API 字段均来自实时抓取。
一、为什么 Agent 的测试和传统软件测试不一样
传统软件测试是确定性的:f(x) = y,输入相同输出必然相同。但 LLM Agent 的核心循环是:
model.predict(tools, history) → tool_call
↓
execute(tool_call) → observation
↓
model.predict(tools, history + observation) → final answer
每一次循环里,模型返回的 tool_call.arguments 都不是确定性的(温度 0 也不能完全消灭 JSON schema 边界处的微抖动),observation 又是来自真实外部系统的副作用。这导致经典的「单元测试断言某个返回值」的模式在 Agent 场景几乎全部失效。
OpenAI 在官方 cookbook 里明确定义:tools 是 Chat Completions API 的可选参数,目的是让模型生成符合 schema 的函数参数,但 API 自身不会执行任何函数调用("The API will not actually execute any function calls")。也就是说,模型只负责给出一份「调用意图」,真正执行是这个 Agent 框架的事。这也意味着:测试不能只看模型返回了什么,必须看框架基于模型输出实际执行了什么。
二、Function Calling 的"四层"测试金字塔
我们把 Agent 测试拆成四层,每一层都有明确的工具与断言风格。
2.1 第一层:Schema 校验(最便宜)
这一层只验证工具描述的 JSON schema 是否合法——工具名、参数类型、必填字段、嵌套对象是否符合 OpenAI / Anthropic 的 tool description 规范。它不调用任何模型。
可以用 ajv / pydantic / zod 在 CI 里跑:每次 git push 时自动校验 tools/*.py 里所有 @tool 装饰器导出的 schema 都满足规范。价值:提前 80% 地拦截「调模型前 schema 就报错」这种低级问题。代价:几乎为零。
2.2 第二层:Mock Tool Execution + 模型行为断言
这是 LangChain create_agent + LangSmith tracing 的标准做法:
- 把所有真实工具替换为 mock(
mock_tool.return_value = "...") - 让 Agent 跑完整 N 步循环
- 断言三件事:
- 调用顺序:
mock_tool.call_args_list必须是预期的 tool_a → tool_b - 参数合法:每次
mock_tool.call_args的args都能用对应 Pydantic model 反序列化 - 最终答案:用 LLM-as-judge 评估(下面详述)
- 调用顺序:
LangChain docs 把 create_agent 定义为「minimal, highly configurable harness」——它的 middleware 设计让这一切特别干净:每一层中间件都可以独立 mock、独立断言。
2.3 第三层:Contract / Regression Test
当 Agent 真的接了真实工具(DB / 内部 API / 第三方服务),Mock 就开始失真了。正确的姿势是 Contract Test:
- 真实工具指向一个专用测试实例(生产 schema 的副本,但是种子数据)
- 跑 Agent 完整循环
- 用 LangSmith 把整个 trace(input → tool_calls → observations → final)落盘
- 下次改动时跑同一组 case,对比 trace diff
LangSmith 2026 年 6 月的 docs(docs.smith.langchain.com/evaluation)已经把这一套抽象成「Datasets + Evaluators + Experiments」三件套:上传一组 input/output 期望对,配一个 evaluator(规则型 or LLM-as-judge),跑一次完整 experiment。这种 regression test 在生产事故复盘中价值最高——出问题时不是从零猜,是「上一版能过的 case 现在挂了」。
2.4 第四层:端到端 / Online Eval
这一层在生产流量上跑:1-5% 的真实请求被「影子复制」到新版本 Agent,对比新旧输出。这是唯一能发现「真实用户问出我没想到的问题」的层级。
Vercel AI SDK 的 tool calling docs 强调:tool definition 应当和业务代码放在同一仓库("Tools are first-class primitives"),这样 shadow traffic 才能精确重现工具执行路径。
三、五套框架的工程实践对比
| 框架 | Star (2026-06-16) | Schema 定义 | Mock 工具 | 端到端 eval | 适合场景 |
|---|---|---|---|---|---|
| LangChain | 139,417 | @tool 装饰器 | unittest.mock | LangSmith | 复杂多步 Agent |
| OpenAI Python SDK | 31,020 | JSON dict | 手工 | 自行集成 | 直接调 OpenAI |
| Pydantic AI | (n/a, 在 Pydantic 生态内) | Type hint + Pydantic | 依赖注入 | Logfire | 强类型 Python 后端 |
| Vercel AI SDK | (TypeScript 生态) | tool({...}) | 手工 | 自带 telemetry | 前端 / Next.js |
| Instructor | 11,000+ (github.com/jxnl/instructor) | Pydantic model | decorator patcher | 自行集成 | 结构化输出 |
关键观察:没有一家提供「完整的端到端 eval 平台」。LangSmith 最接近(langchain 生态自闭环),其他都要求你接 LangSmith、Logfire、Braintrust、Langfuse 中的一家。这意味着选框架的核心不是「哪个测试能力最强」,而是「我愿意把 trace 数据送到哪」。
四、LLM-as-Judge 的"五大陷阱"与绕过
很多团队 LLM-as-judge 写出来发现评估结果和人类评判相关性 0.3-0.5,远低于论文里宣称的 0.8+。五个最常见原因:
- judge 模型和被评模型同源——用 GPT-4 评 GPT-4 输出会有系统性偏见(倾向于给自己打高分)。永远用不同 provider 评(用 Claude 评 GPT / 用 Gemini 评 Claude)
- rubric 太模糊——"is the answer good?" 这种 prompt 毫无信息量。必须给具体维度:accuracy / completeness / helpfulness / safety,每个维度 1-5 分
- 没有 position bias 校正——judge 倾向给第一个答案更高分。用 position swap:把 A/B 答案顺序交换后再评一次,取两次平均
- 没有 self-consistency——单次 judge 不可信。对每个 case 跑 K=3-5 次 judge,用多数投票
- judge 自身没有 ground truth——怎么知道 judge 评的对不对?先在 50-100 条人工标注的 holdout 集上校准 judge,看 judge 和人类的相关性,<0.7 就该换 judge
Pydantic AI 的 testing 文档特别强调:用 TestModel 替换真实模型做单元测试时,必须显式声明模型输入输出的 expected shape,否则 TestModel 会返回"无意义但合法"的输出通过 schema 校验——这种 false positive 是 Agent 测试里最隐蔽的 bug。
五、生产环境测试的"反模式"清单
经过 2026 年上半年的多起 Agent 翻车事故,我们整理了五类必须避免的反模式:
- 「用 LLM 给另一个 LLM 评分但完全没看过真实输出」——judge 看到的只是 final text,看不到 tool_call trace,评估的是表象不是过程
- 「只测 happy path」——真实事故 80% 来自 tool 调用失败重试 / 超时 / schema 不匹配。必须主动注入这些失败 case
- 「eval 跑在 mock 模型上」——所有 eval 必须在真实模型上跑,因为 mock 模型的"成功"和真实模型的"成功"是两个完全不同的分布
- 「test suite 跑得太慢」——超过 10 分钟的 test suite 没人会跑。必须分层:fast unit (<1 min) → medium contract (<10 min) → slow e2e (CI 独立 job)
- 「没有 trace 落盘」——Agent 出问题时没有 trace 等于瞎子摸象。LangSmith / Logfire / Langfuse 三选一,从第一天就接通
六、推荐工作流(4 步落地)
Step 1:第 1 天接 trace
任意选 LangSmith / Logfire / Langfuse 一家,把所有 Agent 调用的 trace 落盘。没有任何理由不在第 1 天接——事后补的成本是首次接入的 10 倍。
Step 2:第 2 周起 Golden Dataset
从生产 trace 里挑 50-100 条具有代表性的 case(覆盖 happy / edge / failure 三种),固化到 Dataset。用 LangSmith Datasets 或类似工具管理版本——改 prompt 后能跑 regression test。
Step 3:配 2-3 个 LLM-as-Judge
不要配太多 judge(成本高且互相矛盾)。推荐配:
correctness_judge:对照 expected output 看准确度tool_use_judge:对照 expected tool call sequence 看过程safety_judge:检查是否触发了不该触发的工具(删数据 / 改 schema)
Step 4:把 eval 接入 CI
每次 PR 必跑 fast eval(<2 min),nightly 跑 full eval。eval 失败 = 禁止 merge。这个规矩定下来之后,Agent 的迭代速度会从「心里没底地改」变成「有数据支撑地改」。
七、生产可观测性:trace 数据的「第二层金矿」
绝大多数团队把 LangSmith / Logfire / Langfuse 当成「出问题时排查的工具」——这是个巨大的浪费。trace 数据实际上是 Agent 系统的「第二层金矿」,可以挖掘出五类高价值信号:
7.1 工具失败率与重试模式
把所有 tool_call 的 outcome 聚合起来,可以看到:哪些工具失败率最高、哪些是偶发(timeout / 5xx)哪些是系统性(schema mismatch)、重试 N 次后成功率曲线。这种聚合视图比任何 APM 工具都更直接地告诉你「Agent 在生产里卡在哪」。例如我们观察到 LLM 经常把 search(query: str, top_k: int) 里的 top_k 默认写成 1000,导致向量检索超时——这种「模型系统性误用工具」的问题,不在 trace 聚合里是看不到的。
7.2 Token 成本分布
把每条 trace 的 prompt_tokens + completion_tokens 聚合起来,按工具 / 按用户 / 按时间分布。很多团队第一次看这个分布会震惊:20% 的长尾 case 消耗 80% 的 token。找到这些 case 之后,针对性的 prompt 压缩 / tool result 截断 / 上下文窗口限制 能立刻砍掉一半成本。这个 1.4MB 的 LangChain docs 页本身就是「tokens 优化」的好例子——它教会所有调用者把工具定义保持极简。
7.3 用户满意度代理
当 Agent 服务的最终用户有明确的「采纳 / 不采纳 / 重做」信号(VS Code Copilot 看到代码是否被保留、客服 Agent 看到用户是否转人工),把这些 signal 反向 join 回 trace,就能算出每个版本的真实用户满意度。这比任何 LLM-as-judge 都更接近 ground truth。
7.4 模型路由分析
如果用 AI 网关(Portkey / LiteLLM / Cloudflare AI Gateway)做了多模型路由,trace 数据可以告诉你:哪些类型的 query 被路由到哪个模型、路由判断本身的准确率、fallback 触发率。这个数据反过来又可以训练更好的 router。
7.5 安全审计与回溯
Agent 真正落地的最后一关是「能否在事后审计每一笔 tool_call 是不是合理的」。trace 数据是这层审计的事实基础。没有 trace 的 Agent 部署 = 不可审计 = 在金融 / 医疗 / 法律场景里不可能合规。
八、典型反例:把「测试覆盖率」当「测试质量」
最后再补一个 2026 年最常被混淆的概念:测试覆盖率高 ≠ 测试质量高。一个 100% 行覆盖率的 Agent test suite,可能所有 assertion 都是「最终答案不为空」——这种 test 比没 test 更危险,因为它给你「一切正常」的错觉。
真正的覆盖率指标应该是:
- 工具调用路径覆盖率:所有 N 步循环里 N=1,2,3,...K 的路径都至少跑过一次
- 失败注入覆盖率:timeout / 5xx / schema mismatch / permission denied 都至少注入过
- 边界 case 覆盖率:空 query / 超长 query / 多语言 query / 敏感 query 都有对应 case
- 跨模型覆盖率:核心 case 在至少 2 个不同 provider 上都跑过
这四个覆盖率指标都到 80% 以上,才能说 Agent test suite 真的能撑起生产环境。
总结
2026 年 6 月的 AI Agent 工程生态,框架已经把「写 Agent」这件事门槛降到接近 0(LangChain 139k star、Vercel AI SDK 1.1MB tool calling docs 都说明生态成熟),但测试与评估这件事没有任何框架替你兜底。能进生产环境的 Agent 团队和不能进生产环境的 Agent 团队,差距几乎完全在测试与评估工程这一层。
最重要的一条:create_agent / tool({...}) / @tool 这些原语 1 行能写出 demo,但把它们跑进 production 至少要再写 10 倍的测试与评估代码。这个 1:10 的比例,2026 年上半年没变,2026 年下半年大概率也不会变。
最后的忠告:不要等到生产事故才想起来补测试。Agent 的"看不见的失败"(silent failure)是所有 AI 产品里最难排查的——模型没有异常、工具没有报错、最终答案看起来也合理,但业务结果完全错了。没有 trace + golden dataset + LLM-as-judge 三件套,这种失败可能要 1-2 周才被用户反馈回来。把今天早上你写的第一个 Agent 加上这三件套,未来某一天你会感谢今天这个决定。
参考资料
- LangChain Python: Tool Calling Concept Docs (https://python.langchain.com/docs/concepts/tool_calling/) — 2026-06-15 抓取,139,417 stars
- OpenAI Cookbook: How to call functions with chat models (https://github.com/openai/openai-cookbook/blob/main/examples/How_to_call_functions_with_chat_models.ipynb) — 官方 notebook,2026-06-16 抓取
- Vercel AI SDK Core: Tools and Tool Calling (https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling) — 2026-06-16 抓取
- Pydantic AI: Testing Docs (https://ai.pydantic.dev/testing-evals/) — 2026-06-16 抓取
- LangSmith: Evaluation Concepts (https://docs.smith.langchain.com/evaluation) — 2026-06-16 抓取
- OpenAI Python SDK: GitHub Repo (https://github.com/openai/openai-python) — 31,020 stars, 2026-06-16 抓取
- Pydantic AI Docs: Function Tools (https://ai.pydantic.dev/tools/) — 2026-06-16 抓取
- Instructor: Multi-Language Structured LLM Outputs (https://python.useinstructor.com/) — 2026-06-16 抓取