Agentic AI 可靠性实战:构建覆盖工具调用、Guardrails、循环与工作流结果的测试框架
一个看似完美的最终答案,并不能证明 Agentic AI 工作流执行正确。
本书介绍了一种基于代码的方法,用于从权威状态出发验证执行和业务结果,并利用这些证据来控制发布流程。
你将获得一个可离线运行的 Agent,用以演示核心技巧。第 19 章会区分已实现的功能与面向实时模型及生产环境的扩展部分。
目录
引言
那个说谎的演示
许多 Agentic AI 系统最初是通过演示和 happy-path(正常路径)测试来评估的。流畅且自信的回答会让系统看起来运作良好。
风险在于,一旦人们把这种演示当作系统已具备生产就绪能力的充分证据。Agent 可能跳过了必需的检查项,尝试了未经授权的行动,重复执行了相同的效果,或者在依赖项失败后仍报告成功。仅审查最终回复往往会遗漏这些故障。
本书构建了一套可靠性测试框架,用于从权威状态验证执行和业务结果,并利用这些证据来控制发布流程。它采用性能与可靠性工程师的视角:验证 Agent 实际改变的状态,而不是依赖其对工作过程的陈述。
Agent 为何难以测试
传统测试通过提供输入并独立核对输出或状态是否符合预期行为来验证系统。代理式工作流引入了两类评估风险:代理可能会接收到泄露自身答案的信息,以及评估者可能将代理的陈述直接视为成功的证据。
相同的输入在不同运行中可能产生不同的路径,其中未授权的操作、重复提交或虚假的成功状态可能隐藏在看似合理的响应背后。
因此,测试框架必须同时验证执行路径和最终结果,在代理外部强制执行权限,注入故障,并检查系统的恢复能力或忠实失败行为。通过重复的实时运行,可以在不确定的情况下估算可靠性。发布门禁和生产环境反馈机制将这些检查转化为持续维护的工程控制措施。
书籍结构安排
本书以一个小型支持及事故分诊代理为例,其核心测试在持续集成环境中运行。第 19 章区分了已实现的生产功能与扩展功能。
第一部分定义了可靠性差距、六项评估活动和五个评估层级。
第二部分构建架构、参考代理和场景。
第三部分评估响应、轨迹、工具和业务结果。
第四部分涵盖循环、护栏、状态、记忆、韧性和授权。
第五部分探讨可观测性、性能度量以及统计学。
第六部分连接发布门禁、持续集成和生产环境反馈。
第七部分解释了该方法的局限性及分阶段采用路径。
阅读建议与声明边界
从头到尾通读以理解整个方法论,或者从针对最重大风险的章节入手。第 5 章和第 9 章提供了实用的切入点:运行参考代理并基于状态验证业务结果。
该测试框架可在声明的可靠性范围内捕获指定的故障类别。它提供可复现的发布证据,但无法确立完整的安全性,也不能替代安全审查、监控、分阶段交付和人工问责机制。第 19 章详细说明了这些边界。
本书涵盖内容与非涵盖内容
本书面向希望将 agent、copilot 或工具调用类工作流推向生产环境的工程师。要求读者熟悉 Python、JSON、HTTP API、分布式系统以及 LLM 工具调用。参考项目不依赖特定厂商,其中的技术可以迁移到其他技术栈。
这套测试框架能在指定覆盖范围内为发布决策提供量化依据,但它无法消除幻觉、覆盖所有涌现行为,也无法取代人工监督。关于通过的测试套件到底能支撑什么判断,第 19 章给出了明确界定。
如何阅读本书
全书各章围绕同一个参考实现展开。可以借助引言中的分部导读来导航,并按第 20 章的建议,根据风险程度安排落地顺序。
Python 参考实现默认离线运行,使用确定性的脚本化 adapter,因此结构性测试不需要调用模型 API 或访问网络。若要评估真实模型的响应质量、延迟、成本和采样表现,则需要接入真实 adapter。本项目提供了该接口,但不随附真实 adapter。离线与真实模型的度量结果始终保持独立。
参考实现托管在 GitHub:https://github.com/vasukiudaykiranvudathala/agentic-ai-reliability-harness。安装与测试命令见第 5 章。
第一部分:可靠性缺口
第 1 章 可靠性缺口:看似正确的答案,掩盖了失灵的工作流
一个谎报成功的工作流
设想一个订阅平台的内部支持 agent。一条请求进来:“账户 A-1007 反馈最近一小时出现了重复扣费。”按照设计,agent 应该判断该账户是否受到某起进行中事故的影响,调取对应的处置策略,并准备一份处置方案供人工审批。
它执行了一通操作,最后返回了这样一条消息:
已确认事故 EVT-402,并为 A-1007 创建了 credit_review 工单。
这条回复看起来相当不错:切题、格式正确、信息具体,还引用了一个真实存在的事故编号。审核人员在浏览这样一批回复时,多半会毫不犹豫地直接批准。
再想想答案中未显示的部分。本应确认事故的那次调用超时了,代理从未收到状态反馈。它照样继续执行,创建了一张声称已确认事故的工单,尽管它实际上从未验证过,随后还把这个结果描述为“已确认”。措辞流畅,但底层执行逻辑错误,而消息中的任何内容都无法让读者察觉出这种差异。
这就是可靠性鸿沟:看似正确的答案与真正正确的工作流之间的距离。这种鸿沟并非只在极端条件下才出现的边缘情况,而是代理系统中的结构性风险。因为人类审查的表层与实际执行的底层是两个不同的对象。流畅的模型非常擅长生成前者,无论后者情况如何。
这是一个构建的教学场景,并非对某个特定生产事故的陈述。在参考项目中,它被实现为一个可运行的测试用例,你可以亲自观察失败现象并验证测试框架的判定结果。
在事故检查中注入单次超时后,一次脚本化执行拒绝创建工单,并明确表示无法确认事故;另一次脚本化执行除了对依赖失败的处理方式外完全相同,却生成了上述那条自信的消息。测试框架判定第一次通过,第二次失败。
它得出该判定并非基于措辞评分,而是通过检查发生事件的可信记录:一次运行未留下工单,另一次却留下了主张未验证事故的工单。文字从来都不是证据。
正确答案出错的各种方式
超时案例是通用问题的一个实例。代理可以生成令人信服的最终答案,但其底层执行却可能以多种相互独立的方式出错。
它可能会为任务选错工具,也可能选对了工具却传入了错误的参数。它可能执行了未经授权的操作,或在原地踏步、在两个步骤间反复震荡却无法推进。它可能无视某个失败依赖而假装其已成功,继续执行。它可能丢失或在步骤间损坏了上下文状态。它可能产生无人预期的副作用,或掩盖执行不完整的事实。它可能消耗过量的 Token 或工具调用来达成同样的结果。它可能将上游的小故障放大为下游的重试风暴。它可能在孤立场景下表现正常,却在并发环境或长会话中崩溃。今天它可能通过了所有测试,但明天因提示词微调、模型升级、工具 Schema 变更或配置调整而悄悄退化。
图 1-1 可靠性鸿沟。即使最终回复看似可信,执行路径也可能已断裂,因此必须将回复内容与执行记录分开评估。
上述任何一种情况,都可能与一个被人类或评级模型判定为“良好”的最终答案共存。
这就是核心矛盾所在。最终消息的质量与底层工作的正确性可能完全脱节。对于涉及资金支付、账户状态变更、触发下游流程的工单提交,或任何有后果的行动的系统,仅凭阅读其自我描述是无法验证其可靠性的。
为何传统测试无法捕获此类问题
有三种测试习惯,在各自的原始场景中都合理合理,但移植到 Agent 时便失效了。
第一种是最终答案评分。大多数模型评估将生成的答案与参考答案对比,或根据评分标准打分。这衡量的是模型能否“说”对,而非 Agent 是否“做”对。因为答案只是关于工作的声明,而非工作本身。对声明进行评分,恰恰是让那些虚假成功的 Agent 轻松过关的检查方式。
第二个误区是把传输成功或请求完成当作工作流成功的代名词。HTTP 200 只能说明服务在 API 边界上正常处理了请求,并不能证明一个多步骤业务流程到达了正确的最终状态——流程完全可能在前门体面地返回,背后却选错了工具、跳过了必经步骤,或者提交了本不该有的副作用。
第三个误区是把模型准确率当作系统可靠性。在基准测试上拿高分的模型,只是一个具备已知特性的组件。而系统可靠说的是另一个更大的对象:agent loop、工具、状态存储、授权层、重试、预算,以及这一切在负载和故障下共同表现出的行为。
改进模型之后,系统可能还是和以前一样不可靠,因为上面列出的失败模式大多出在编排层,而不是模型下一个 token 的生成质量上。
这些习惯在各自的原生领域都没错,只是瞄错了对象。要弥合可靠性差距,就得测试真正承担风险的对象——也就是完整的执行路径,以及它产出的业务结果。
响应正确 ≠ 结果正确
可靠性差距由两个问题定义。
响应正确性问的是:最终答案是否正确、相关、有据、完整、不确定性表达得当?这是 agent 产出的文本的属性。
结果正确性问的是:工作流是否通过有效的决策和经过授权的操作,到达了预期的业务终态,必需的状态变更是否已落实,禁止的副作用是否不存在?这是 agent 所作用的外部世界的属性,要从权威记录里读取,而不是听 agent 自己怎么说。
这两个属性相互独立,工作流完全可能一项对、另一项错,两个方向都有可能。
| 结果正确 | 结果不正确 | |
|---|---|---|
| 响应正确 | 健康情况:agent 做对了事情,描述也准确。 | agent 准确报告已发放一笔信用额度,但额度发放并未获得授权。响应在事实上没错,但操作和工作流是错的。 |
| 响应不正确 | 智能体做对了事,但描述得很糟糕或不完整。状态本身是正确的,但用户可能会被误导,以为发生了别的事情。这种情况需要纠正,且后果可能依然严重。 | 典型的失败:事做错了,话说也错了。这是最容易捕获的情况,也是你最不需要担心的情况。 |
右上角的单元格代表一种特别危险的失败类型,因为针对最终答案的评分机制看不见它。对于改变状态的工作流,测试框架(harness)将结果正确性视为独立的初级判决,将响应正确性视为另一个必需的判决。二者永远不能相互替代。
这本书在全程坚持两个澄清。第一,写得很好的回答绝不能被当作结果已达成证据。结果必须独立地对照权威状态进行验证。
第二,评估智能体 做了什么 意味着评估可观察到的操作、状态转换、输出和工具调用轨迹。这不意味着检查模型的内部推理过程。思维链披露不是可观察性的要求;任何依赖阅读模型内部独白来判断其行为是否得当的系统,都是不可运行的。
什么是可靠性框架
智能体 AI 可靠性框架是围绕智能体构建的受控执行、测量和评估层。它提供可复现的场景、捕获模型和工具活动、注入故障、评估结果、测量运行行为,并应用发布门禁。
上面“围绕”一词分量很重。框架独立于智能体,不会通过智能体的执行接口暴露预期结果、禁止操作列表、评估规则或发布标准。
你要部署的东西(即智能体、其模型、其工具、其授权层及其状态)是被评估系统。围绕它的测试台(即场景加载器、故障注入器、轨迹收集器、评估器和门禁引擎)是控制平面。
将这两者分开是结果可信的关键:智能体看不到答案,评估器也不会被一条有说服力的最终信息说服而改变判决。
这种分离体现为一种刻意设计的不对称性。评估器直接读取权威的业务状态,因为它们属于控制平面;而代理只能通过受控网关访问同一状态,且必须逐一执行操作,并在授权范围内进行。
由于双方通过不同路径获取真相,测试框架可以将代理声称执行的操作与实际发生的情况进行对比。在框架的威胁模型下,前提是网关和权威状态得到了正确保护,代理只有在完成被允许的工作时,才能让自身声明与记录相匹配。
五个观察层面
结果正确性是核心,但仅凭最终的一次通过或失败判定,不足以运维系统或在故障时进行诊断。全书中,我们将沿五个维度评估工作流:响应、单步执行、单个动作、整体工作流,以及运行它的系统。
同一个请求在不同层面可能获得不同的判定。例如,一次“虚假成功”的运行可能通过了表面的流畅度和相关性检查,但未能通过响应根基性验证。它在动作层面也会失败,因为基于未验证的事故采取行动;在工作流层面同样失败,因为创建了不应存在的工单。第 3 章将详细阐述这五个维度,并使其成为本书后续内容的骨架。
第 1 章检查清单:
你能表述可靠性差距:即看似正确的答案与真正正确的工作流之间的距离。
你能列举代理产生良好答案但执行过程存在缺陷的几种独立情形。
你能解释为何仅对最终答案进行评分、将请求完成视为工作流成功、以及依赖模型准确率,均无法弥合这一差距。
你能区分响应正确性与结果正确性,并将特定故障准确归类到该对照表的相应位置。
你能定义可靠性测试框架,并解释为何被测代理必须与控制平面隔离。
关键要点
独立于最终响应,对执行过程和业务结果进行验证。五个评估维度能暴露不同的故障模式,并为发布决策提供独立的证据。
第 2 章:人们常说的六种“评估”(以及为何你需要全部)
评估:一个词承担六项工作
当两位工程师说 agent"做了评估"时,一个人指的可能是模型基准测试,另一个人指的可能是经过验证的业务成果。这个词还可以涵盖答案打分、轨迹检查、系统压力测试和生产监控。
这六类活动回答的是不同的问题、衡量的是不同的对象、支撑的是不同的决策。本章会厘清它们各自的职责与盲区,让团队能够说清楚自己掌握了哪些证据、一次发布又需要哪些证据。
六类活动
模型评估衡量的是模型作为独立组件的某种属性:它能否完成某项任务、遵循某条指令,或对某个输入进行推理,并以参考答案或基准作为评判标准。各家厂商的评分卡和公开榜单报告的就是这类结果,它也是模型选型时合理的参考依据。
它的盲区是模型之外的一切。基准分数再高,也无法说明你的 prompt、工具、状态存储、鉴权机制或工作流产出是否可靠。第 1 章列出的绝大多数故障模式都存在于"模型之外",而模型评估对此完全无能为力。
响应评估衡量的是产出答案的某种属性:是否正确、相关、有依据、完整,不确定性是否恰当,格式是否正确?它很有用,第 7 章会深入展开。
它的盲区正是第 1 章指出的问题:答案只是对工作成果的"声称",而非工作成果本身。响应评估恰恰是这样一种检查——一个表达流畅的 agent 能顺利通过,但底层执行却是错的。当团队说"我们评估过 agent"而实际只做了这一项时,他们衡量的是响应的正确性,却没衡量结果的正确性。
Agent 评估衡量的是轨迹的某种属性:agent 是否做出了合法的中间决策、选对了工具、传入了合理的参数、步骤安排是否得当、有没有陷入循环?相比只给最终答案打分,这是实实在在的进步,因为它看的是路径,而不只是终点。
但它也有两个自身盲区。第一,逐步看来都合法的轨迹,仍可能偏离预期的业务终态,所以路径正确并不保证结果正确。
其次,Agent 评估容易做低质:若将轨迹与单一的标准路径比对,就会惩罚那些能干的 Agent 合理采取的替代有效路径。正因如此,第八章对轨迹评估的讨论极为审慎。
Workflow 验证衡量的是 Agent 所操作世界的一项属性:Workflow 是否达到了预期的业务终态,是否施加了所需的状态变更且未产生被禁止的副作用,判断依据是否来自权威记录而非 Agent 的自我叙述?这是结果正确性,针对改变状态的 Workflow,也是本书的核心裁决逻辑。
它的盲点是覆盖率。即使 Agent 的执行是非确定性的,结果验证规则仍可以是确定性的;单次运行中的确定性裁决,并不能证明 Agent 在重复运行、故障或配置变更时能持续产生相同的正确结果。单个场景的正确终态只能说明 Agent 有能力做对,但无法说明它在故障、高负载或下一次变更后是否还能保持正确。
系统可靠性测试衡量的是整个系统在真实压力下的表现:性能、长尾延迟、并发、注入故障、韧性、背压以及安全性。它考察在依赖项变慢或失效、大量 Workflow 同时运行时,工作流是否依然正确、可观测、快速且安全。正是在这里,那些在安静机器上表现完美的 Agent,才会暴露出将上游小故障放大为重试风暴,或在并发会话间丧失隔离性。
它的盲点是成本和完备性:要执行好成本高昂,且无论做多少轮,都无法枚举非确定性系统可能产生的所有突发行为。此外,它是在发布前、对着生产环境的模拟副本而非生产环境本身运行的。
生产监控衡量的是真实世界中运行系统当下的状态:通过追踪、指标、漂移信号和事故来看实际发生了什么。它是唯一能观察到真实流量、真实输入和真实后果的活动,对于检测预发布活动无法完全预见的漂移和长尾行为不可或缺。
它的盲区是结构性的。它属于事后响应,只能观察已发生的事件,而非主动预防。受限于采样机制和隐私合规,你永远无法看到全貌。此外,它无法替代发布前验证,尽管它可以管控渐进式发布阶段、叫停金丝雀发布,并在生产环境数据违反发布策略时触发回滚。第 18 章将展示如何把生产中获取的经验反馈给早期的验证活动。
六大环节概览
| 活动 | 回答的问题 | 测量对象 | 运行时机 | 主要盲区 |
|---|---|---|---|---|
| 模型评估 | 模型能否独立完成任务? | 模型作为组件 | 选型、升级时 | 模型之外的所有因素 |
| 响应评估 | 这个回答质量如何? | 生成的文本 | 每次响应时 | 回答只是断言,并非最终成果 |
| 智能体评估 | 中间决策是否合理? | 执行轨迹 | 每次运行 | 路径合理不等于结果正确 |
| 工作流验证 | 是否达到了正确的业务终态? | 权威状态 | 每次运行 | 仅限单次运行,未覆盖压力或变更下的可靠性 |
| 系统可靠性测试 | 在高负载和故障下是否依然正确? | 整个系统 | 发布前、持续运行及受控发布期间 | 成本高昂。无法穷举所有行为。 |
| 生产监控 | 当前实际状况如何? | 线上系统 | 持续进行 | 被动且为采样数据。通常在变更上线后才运行。 |
为何六大环节缺一不可
这六项活动构成了贯穿全生命周期的证据组合。它们存在重叠,但没有任何一项能覆盖其他环节的所有盲区。上表清晰列出了团队必须关注的缺口。
投入深度和责任归属取决于风险等级、架构设计及对模型的掌控程度。若团队使用固定的托管模型,重点可放在应用层;若团队自行训练或微调模型,则需投入更多资源进行模型评估。无论哪种情况,都需要验证工作流成果、压力下的表现以及线上运行数据。
仅做模型评估和响应评估,无法衡量执行正确性与系统容量。测试框架让 Agent 评估、工作流验证和系统可靠性测试变得可重复,并通过反馈将它们与生产监控串联起来,向上补充模型评估、向下衔接生产监控。
常见错误
把响应评估当作工作流验证的替代品,就会漏掉虚假成功。把一次顺利的工作流运行当成可靠性证据,压力下的表现就无从得知。用监控面板代替发布前检查,可避免的回归就会直接暴露给客户。而模型升级后不重新测试出问题的编排流程,就归功于模型带来了系统可靠性,则衡量错了对象。
生产影响
每种活动产出的证据服务于不同的决策,把证据和决策对上号,正是这项工作的核心。模型评估决定选哪个模型、是否采用升级。响应评估决定 prompt、模型配置、grounding、输出契约和内容策略的调整。Agent 评估和工作流验证决定某次构建是否行为正确、结果是否正确。系统可靠性测试决定容量、韧性和真实条件下的发布与否。生产监控则支持事件响应,并告诉你前几种活动遗漏了哪些新场景。
当有人问 Agent 是否"做过评估"时,有用的回答是指出这六种活动中指的是哪一种,以及眼前的决策真正需要哪一种。
第 2 章清单:
能说出这六种活动,并阐明各自回答的核心问题。
能说出每种活动衡量的对象及其运行时机。
能说出每种活动的主要盲区,并指出哪些其他活动能为它遗漏的关注点提供证据。
能识别出"响应评估加模型评估"的替代做法——它会让流畅但不可靠的 Agent 上线。
能把具体决策与提供所需证据的活动对应起来。
核心要点
模型评估、响应评估、Agent 评估、工作流验证、系统可靠性测试和生产监控共同构成覆盖生命周期的证据组合。要明确每项决策由哪种活动支撑,因为没有任何单一活动能覆盖所有问题。
第三章 五个评估层级:响应、步骤、动作、工作流、系统
两种分类体系,两个问题
第二章和第三章分别描述了两个不同的维度。六种活动描述了收集证据的生命周期目的。五个层级描述了在 Agentic 执行过程中,证据所评估的具体对象。两者之间是多对多的关系,而非一一对应。
这种区分很重要,因为人们很容易将二者混淆。
六种活动关注证据收集何时以及为何进行:是在模型选型阶段、预发布测试中、工作流验证时,还是生产环境监控中,每一项活动都为特定决策提供依据。
五个层级关注某条证据指向单次执行的哪一部分:是它的响应、步骤、动作、结果,还是周围的系统。
通常,一种活动会在多个层级收集证据,而一个层级也通常由多种活动来审视。不应将这两个框架混为一谈。
下表展示了每种活动通常审视哪些层级。请将其视为常见实践的粗略图谱,而非死板的规则。
| 活动 | 响应 | 步骤 | 动作 | 工作流 | 系统 |
|---|---|---|---|---|---|
| 模型评估 | 辅助 | 不适用 | 不适用 | 不适用 | 不适用 |
| 响应评估 | 主要 | 不适用 | 不适用 | 不适用 | 不适用 |
| Agent 评估 | 辅助 | 主要 | 主要 | 不适用 | 不适用 |
| 工作流验证 | 不适用 | 不适用 | 辅助 | 主要 | 不适用 |
| 系统可靠性测试 | 不适用 | 辅助 | 辅助 | 辅助 | 主要 |
| 生产监控 | 辅助 | 辅助 | 辅助 | 辅助 | 主要 |
“辅助”意味着该活动可以提供与该层级相关的证据,但不意味着该活动能独立确立该层级的结论。例如,模型基准测试可以检验模型是否能提出合理的工具调用,但它无法验证那些从未被执行过的动作。
这张图有两个关键特征。Response(响应)列的结论基于四项不同的活动,因此如果不指明具体是哪项活动,“我们评估响应”这句话就存在歧义。此外,这张图也揭示了为什么早期的组件级和响应级检查会留下重要盲区:它们几乎无法提供关于权威工作流状态或在运行条件下行为的第一手证据。本章的其余内容将聚焦于各列,而非各行。
五个层级的由来
这些层级并非随意列举,而是由 Agentic 执行的结构自然推导出来的。
一个工作流始于请求,终于响应。在这两者之间,Agent 会执行一系列步骤。某些步骤通过工具对外部世界执行操作。这些操作使世界状态趋向或背离预期的业务最终状态。而整个过程都在一个时间、容量和信任度有限的系统内运行。
上述每个结构特征都是一个独立的位置,在该位置上的执行可能是正确的,也可能是错误的,且与其他位置互不依赖。每个这样的位置就构成了一个层级。
响应层级 询问最终响应是否正确、相关、有依据、完整且不确定性表达得当。它的评估对象是 Agent 生成的文本。第 7 章将对此进行详细阐述。
步骤层级 询问 Agent 是否做出了有效的中间决策和状态转换:每一步是否都基于可用的观察结果合理推导,并推动工作流前进而非陷入循环。它的评估对象是决策轨迹。第 8 章和第 10 章将对此进行详细阐述。
动作层级 询问每个动作是否选择了正确的工具、提供了有效参数、在正确的权限下运行并控制住了副作用。它的评估对象是单个工具调用及其后果。第 8 章、第 13 章和第 14 章将对此进行详细阐述。
工作流层级 询问工作流是否达到了预期的业务最终状态,即所需的变更是否已应用,完成条件是否已满足。它的评估对象是世界的权威记录,需直接读取,而非依赖 Agent 的陈述。
这是结果正确性,也是状态变更类参考工作流的核心判定依据。它并不能抵消在响应、动作或系统层级发生的失败。第 9 章将对此进行详细阐述。
系统层面考察的是整个工作流是否保持可靠、可观测、安全、快速、可扩展且成本可控。它的关注对象是运行边界:延迟、并发能力、故障下的韧性、安全态势,以及每次成功结果的成本。第 15 到 18 章会展开这部分内容。
一次请求,五种判定
之所以要区分这些层面,是因为同一次运行在不同层面可能得到不同的判定,而且一个层面的判定无法预测另一个层面的结果。
回到第 1 章的两次运行:处理的是同一个请求,在事件核查环节注入了同样的超时故障。一个 Agent 在核查失败时安全停止;另一个则无视失败,照样开出了工单,并报告成功。
| 层面 | 忠实执行的一次 | 虚假成功的一次 |
|---|---|---|
| 响应 | 通过:准确说明了确认失败、未开任何工单。 | 失败:表面上的格式和相关性检查能通过,但事实依据和不确定性校准不合格。 |
| 步骤 | 通过:每一步转移都有前面的观测结果支撑。 | 失败:把失败的依赖当作成功处理并继续执行。 |
| 动作 | 通过:没有执行任何缺乏证据支撑的动作。 | 失败:基于一次未经证实的事件开出了工单。 |
| 工作流 | 通过:核查失败后的正确最终状态就是不开工单。 | 失败:最终状态里存在一张断言了未证实事件的工单。 |
| 系统 | 提供了正面的韧性证据:这次运行在注入故障下安全停止。 | 提供了负面的韧性证据:这次运行在注入故障下产生了虚假成功。 |
虚假成功那一列才是关键。浅层的响应检查会让它通过,因为消息流畅、切题、内容具体。完整的响应级评估则会在事实依据上把它判为失败,而步骤、动作、工作流和故障响应检查会准确说明执行到底是在哪里出的问题。
这个区分很重要:可靠性缺口并不是严格响应评估的弱点。它出现于表层回答质量被用来替代执行证据的时候。只做浅层响应检查的门槛会让这次运行上线;而在工作流层面做验证的门槛则能把它拦下来。
响应、步骤、行动和工作流判定可以针对单次执行结果。而系统级判定通常需要聚合重复场景、故障类型、并发级别和工作负载画像中收集的证据。单次运行仅为该判定提供佐证,无法单独确立结论。正因如此,表格中系统行的记录方式是贡献正向或负向的韧性证据,而非简单的通过或失败。
响应正确性与工作流正确性在另一方向上也可能真实出现分歧。考虑另一种运行情况:代理发出了一笔未经授权的抵扣,并准确报告“已应用 50 美元抵扣”。此时响应在事实层面是正确的,且基于系统最终状态;但由于副作用未经授权且被禁止,行动和工作流判定均会失败。这是一个典型的“响应通过、行动失败”案例。
各层级并非严格的阶梯关系,即通过某一层级并不保证通过下一层级。它们是独立的评价对象,其判定结果必须分别记录。
某次运行可能通过越权行动达成正确的业务终态:在工作流层面结果看似正确,但行动层面判定失败,因为系统虽然到达了正确位置,但路径不合法。
这并非罕见现象,而是行动层作为独立检查项存在的理由,其未并入工作流层。这也是第 14 章安全测试旨在捕获的故障类型。结果正确性固然核心,但某一层的成功判定无法弥补另一层的阻断性失败。
测试框架如何映射到各层级
参考测试框架中,每个层级对应特定的评估器或度量指标,产生用于诊断和门禁控制的独立判定。
响应评估器应用确定性检查,启用时也包含建议性评分表评估。轨迹评估器考察步骤层面的可观察决策和状态转换。在行动层面,工具调用验证、授权记录、审批决策、护栏结果及记录的副作用会被综合评估。结果验证器基于不可变的权威状态快照判断工作流层面。系统级判定则由聚合指标、故障演练、安全测试、遥测检查和负载实验共同提供。
当第 18 章构建发布门禁时,每个门禁都会根据其控制的风险类型调用相应层级的证据:未授权行为门禁读取行为级证据,结果成功门禁读取工作流级证据,尾部延迟门禁则读取系统级证据。护栏门禁可能跨越响应、步骤和行为层级,具体取决于控制措施的应用位置。从这个意义上说,这五个层级既是评估的核心,也是发布决策的组织原则。
常见错误
将五个层级合并为单一分数,会掩盖具体失效的层级。若将其视为严格的层级结构,则可能因最终状态正确而容忍未授权行为。推迟到生产环境才进行系统测试,会导致并发、韧性和成本在发布时处于未知状态。应确保每个判定结果都可见,并在部署前测试操作边界。
生产环境影响
按层级报告优势和劣势:构建可能在通过响应和工作流检查的同时,在高负载下失败。这能让审查者在批准前得知缺少哪些证据。当生产事故在第 18 章中转化为回归测试用例时,其失效的层级能识别出需要补充覆盖的评估器或门禁。
第 3 章检查清单:
你能区分六项活动(何时以及为何收集证据)和五个层级(证据评估执行过程的哪个部分),并解释为何这种映射是多对多的。
你能列举五个层级,并陈述每个层级的核心问题及评估对象。
你能从智能体执行的结构推导出这些层级。
你能在五个层级上对单次运行进行判定,并展示判定结果的差异。
你能解释为何正确的结果无法认证达成该结果的过程,以及为何行为层级必须与工作流层级分离。
你能将每个层级映射到测试框架中用于判定的评估器或度量指标。
核心要点
分别记录响应、步骤、行为、工作流和系统的判定结果。一个层级的正确结果无法弥补另一个层级的阻断性失败。系统判定通常需要聚合的操作证据。
第二部分:基础
第 4 章. 框架架构:分离被测智能体与控制平面
工程问题
可信的评测要求把被测系统与它的期望和断言分开。对 agent 来说,架构必须堵住两条失败路径。
第一,预期结果、允许的调用序列或禁止的操作可能作为"接地信息"泄露进 agent 的上下文。这样即使测试通过,也无法证明 agent 具备独立的决策能力。
第二,评估器可能通过最终消息来推断工作流是否成功,这就接受了第 1 章所讲的"假成功"问题。评估器需要独立的执行证据。
这套架构通过在被测系统和控制平面之间强制实施可见性与访问边界,堵住了这两条路径。
图 4-1. 被测系统与控制平面。SUE 看不到答案,控制平面也不信任 agent 的自我描述。
三个区域,而非两个
人们很容易画一条线,一边是 agent,另一边是测试装置。这条线确实存在,但它掩盖了一个之后很重要的区别:测试期间围绕 agent 的一些东西,既不属于部署系统,也不属于测试逻辑——它们是用来替代外部依赖的。把测试工具划分为三个区域,就能体现这一点。
被测系统(SUE)就是你会实际部署的部分:agent 运行时、模型接口、工具客户端、网关、授权执行以及生产环境的运行时限制。这个区域无论测试还是真实运行都保持不变。
受控测试环境存放离线模式下替代外部依赖的确定性替身:脚本化模型、对话记录 fixture、工具模拟器、带种子数据的状态存储实现,以及虚拟时钟。这些组件不会被部署,它们占据的是真实依赖所在的位置。
测试控制平面则是真正的测试逻辑:场景加载器、故障调度、评估器、不可变证据存储、指标、报告和发布门禁。
三种模式之间的切换点在中间那一层。模式 A 中,模型接口由脚本化模型支撑,工具客户端由模拟器支撑,两者都来自受控测试环境。模式 B 中,模型接口接入真实模型,而工具依赖仍保持受控。模式 C 中,工作流对接真实模型和有代表性的下游基础设施。
被测试的依赖是被评估系统整体的一部分。模型接口在任何模式下都属于 SUE:变化的只是背后的实现。把这个切换点保持干净,正是第 16 章中执行模式能够清晰推理的原因。
| 组件 | 区域 | 角色 |
|---|---|---|
| Agent 运行时 | SUE | 运行计划-执行-观察循环,发起动作请求,不做任何拦截 |
| 模型接口 | SUE | Agent 请求决策所经过的接缝 |
| 工具客户端与网关 | SUE | 从请求到执行的唯一强制点 |
| 授权强制执行 | SUE | 身份、策略与授权决策 |
| 生产运行时限制 | SUE | 终止 agent 本身的执行监督器 |
| 脚本化模型、对话记录 | 受控测试环境 | 模型依赖的确定性替身 |
| 工具模拟器 | 受控测试环境 | 下游服务的确定性替身 |
| 预置状态存储、虚拟时钟 | 受控测试环境 | 真实状态与时间的确定性替身 |
| 场景加载器、故障调度器 | 控制平面 | 加载测试用例,触发受控故障 |
| 评估器 | 控制平面 | 评判响应、轨迹、结果、循环与防护栏 |
| 不可变证据存储 | 控制平面 | 保留脱敏后的记录和状态快照 |
| 指标、报告、发布门禁 | 控制平面 | 聚合、汇总,并决定能否发布 |
有两处位置值得强调。授权(Authorization)的执行逻辑放在 SUE 内部而非控制平面,因为它是你部署的系统的一部分,必须经过真实测试,不能由 harness 模拟。同理,生产环境的运行时限制也放在 SUE 内部。这一点足够重要,下面单独展开说明。
生产限制必须放在 SUE 内部
一个停不下来的失控 agent 是生产事故,不是测试产物。所以叫停它的能力必须内建于部署的系统。如果只有 harness 在限制步数、工具调用次数和时间,那么测试通过只能证明 harness 能停住一个失控的 agent,却证明不了部署的系统也能——而真正上线的恰恰是部署的系统。
因此,参考实现采用了两层防护。SUE 内部有一个执行监督器(execution supervisor),负责执行生产环境的步数、工具调用、时间和循环限制。agent 会查询它并自行停止。
与此同时,harness 独立运行一个外部 watchdog,唯一的职责是在 SUE 自身的防护失效时叫停失控的测试。两者的顺序有明确约定:SUE 必须先触发:
SUE deadline < harness watchdog deadline
在参考 runner 中,SUE 的 deadline 就是场景的墙钟时间预算,watchdog 则严格设置在其之上(来自 src/arh/harness/runner.py):
deadline_ms=float(budgets.wall_clock_ms), clock=self._clock,
...
wd = scenario.controls.watchdog
watchdog_max_actions = max(budgets.max_steps, budgets.max_tool_calls) + wd.extra_actions
watchdog_deadline_ms = float(budgets.wall_clock_ms) + float(wd.extra_ms)
SUE 监督器的 deadline 取场景的墙钟时间预算。watchdog 在此基础上加上一个可配置的余量(来自场景的 watchdog 配置,即 extra_actions 和 extra_ms,默认为 8 和 5000),场景无需改代码就能放宽这个余量。
监督器实现很小,放在 agent 包里而非 harness 中(来自 src/arh/agent/supervisor.py):
def before_action(self, call: ToolCall) -> str | None:
# 在启动一个会超出预算的动作前终止。
if self._tool_calls + 1 > self.max_tool_calls:
return "tool_call_budget"
return None
然后测试框架会断言 SUE 是否以预期的原因自行终止。如果必须由看门狗出手,这次运行就会通过 self_terminated 标志记为失败——因为一个只能靠外部定时器才能停下来的系统,是不敢放进生产环境的。
在参考实现中,黄金运行和循环检测运行的 self_terminated 均为 true,这正是要验证的结果:agent 自己停了下来。这两层保护覆盖了预算和自行终止。第 10 章会将无进展遏制具体展开为三层,并加入控制平面独立重放记录,以检查被评估系统是否正确分类并正确终止。
单个工作流的请求路径
一次运行会按固定顺序串联各个区域:控制平面搭建环境,SUE 执行动作,控制平面观察并判定。
场景加载器校验测试用例,运行器搭建环境:将数据集写入状态存储,设置调用者身份与权限,把策略夹具注入受信任的安全层,装配所需的故障,并在 SUE 的执行监督器上设置预算。策略夹具只进入安全层,绝不进入模型的上下文。
运行器解析模型接口。在确定性模式下,模型接口由预编写的决策脚本和虚拟时钟支撑。agent 只接收观察结果。
agent 提出决策。如果模型请求调用工具,agent 运行时把请求直接传给网关;网关校验参数、获取授权判定、检查所需的外部审批,然后调用配置好的工具客户端。运行器只观察发出的事件,不转发也不授权请求。如果决策是最终答案,运行结束。
每次决策或尝试动作之后,SUE 执行监督器检查生产预算和运行时循环保护。与此独立,测试框架捕获不可变的状态快照、评估步骤级不变量、记录证据,并维护外层看门狗。如果 SUE 未能自行停止、必须由看门狗终止运行,则该场景判定为失败。
运行结束后,控制平面的收集器会根据已发出的事件组装执行记录。评估器基于不可变的快照运行,指标被计算,报告被生成,闸门引擎应用评分卡。
Agent 只参与第三步,而且只能通过两个 SUE 接口:向模型接口请求决策,向网关请求执行动作。其他一切都由控制平面从外部观察。
代码中的边界
代码里有几处接缝在强制维护这条边界,其中三处值得细读。
第一处是模型接口。Agent 做决策时对世界的唯一视图是一个 Observation,它携带的内容就是真实模型所能看到的(来自 src/arh/model/base.py):
class Observation(BaseModel):
messages: list[Message]
tool_definitions: list[ToolDefinition]
tool_results: list[ObservedToolResult]
这个结构里没有 Scenario,预期结果、禁止动作、通过标准都没有任何路径能进入决策过程。工具的可用性不算泄露答案:生产环境的模型本来就需要知道它能请求哪些工具,所以工具定义放在这里是合理的。
只有当仅供评估器使用的预期(比如预期工具、预期序列、所需结果或禁止动作断言)进入 Observation 时,才构成泄露。这些内容存放在控制平面的场景定义中,只会传递给评估器。
第二处是网关——从 Agent 请求到工具执行之间的唯一通道。Agent 运行时直接调用网关,runner 并不在这条路径上(来自 src/arh/agent/loop.py):
# SUE 直接调用网关。网关负责授权并执行。
outcome = self._gateway.execute(call)
光靠 docstring 是强制不了这种约束的,这条边界是结构性的:Agent 只拿到网关接口,原始的工具客户端永远不会被注入给它;只有网关持有可执行工具实现的引用;而在真实部署中,生产环境的凭据只有工具执行层才能访问。
由于这一特性是结构性的,因此可以测试。参考测试工具包含了架构测试,它会解析 agent 包并断言其没有导入工具实现或控制平面(来自 tests/test_architecture.py):
def test_agent_does_not_import_tool_implementations():
mods = _imports("agent")
forbidden = ("tools.simulators", "tools.registry")
leaked = [m for m in mods for f in forbidden if f in m]
assert not leaked
第三个接缝是评估器所判断的证据。评估器从不接触实时状态存储,它们拿到的是不可变快照,因此无法篡改自己正在评估的证据。快照是一个冻结的值对象,通过规范化序列化构建,带有序列号、捕获时间和用于防篡改检测的内容哈希(来自 src/arh/state/snapshot.py):
class StateSnapshot(BaseModel):
model_config = ConfigDict(frozen=True)
sequence: int
captured_vt_ms: float
state_hash: str
canonical_json: str
读取快照时会从其规范化 JSON 中解码出一份新副本,这样评估器就能检查状态,而不会触碰规范化证据。结果校验器读取的正是这种权威的状态快照,而非对实时存储的无限制访问。
这些接缝之间的不对称性正是设计要点。agent 只能通过网关、每次一个授权动作地写入状态;评估器则以无法修改的快照形式读取状态。由于双方通过不同路径获取真相,测试工具就能把 agent 的“自述”与实际记录进行比对——在测试工具的威胁模型内、且网关和状态得到正确保护的前提下,agent 想让自述与记录一致,唯一的办法就是真正完成被允许的工作。
故障、遥测与审批谨慎地跨越边界
有三种机制横跨各个分区,每种机制都有一条保持边界完整性的规则。
故障由控制平面调度,但在 SUE 的依赖边界处注入。在这一层注入失败可以保留生产接口:agent 只会看到最终的超时、错误、延迟或格式错误的响应——与生产环境中观察到的症状完全相同——而它要适应的正是这些症状。
它完全看不到的是:这个条件是由 harness 调度的,以及 evaluator 期望得到什么样的响应。症状是合法输入,而调度本身不是。
遥测数据从 SUE 内部产生,在外部被收集。SUE 对自己的运行时进行插桩,发出模型、网关、授权、审批、工具和状态迁移等事件。控制面的收集器接收这些事件,进行关联,脱敏敏感字段,并构建执行记录。证据库将这些记录和快照以不可变的脱敏形式保存。SUE 只发出"它做了什么"的事实,从不发出 evaluator 的判定结果或通过标准。这些都是在事后由控制面计算的。
审批是一个可信的外部输入,不是 agent 自己能伪造的。每条审批都关联一个经过身份验证的审批人、一个操作摘要、一个作用域和一个过期时间。agent 可以请求审批,但它不能自己创建、修改审批,也不能把自己的请求解释为已获审批。这保证了敏感操作路径的可信度,第 14 章会进一步展开。
为什么这条边界让结果可信
模型接口把场景预期排除在 agent 的观察之外,杜绝了答案通过这条路径泄露。结果校验器读取的是权威快照,一段有说服力的回复无法替代真实的执行证据。这些控制手段都不依赖于模型的诚实。
同样的接口还允许在离线测试中用确定性脚本替代模型。这样测出的是脚本化工作流及其控制机制。要测真实模型的表现,则需要 live adapter。
常见错误
破坏边界会毁掉整个测试:把预期暴露为 grounding、只凭最终回答判定结果、允许直接写存储、或接受模型生成的审批。只靠 harness 施加运行时限制,会让部署后的隔离措施得不到验证。可变的状态引用会让 evaluator 篡改自己的证据。务必守住观察、执行、审批和快照这四条边界。
对生产环境的意义
集中式工具执行、外部授权、权威状态和可审计的事件,对测试和生产同样有价值。后续章节会把这些控制手段复用到安全、可观测性和发布决策上。它们的存在支撑了可信的评估,但本身并不等于已具备上线条件。
如何阅读这套架构
架构图展示了三个分区、请求路径,以及最关键的——信息如何跨越边界流动。控制平面提供构建测试环境所需的运行时输入,包括用户请求、预置数据、调用方身份和策略配置。
Agent 只接收与生产环境等价的观测数据。遥测数据和不可变状态快照则回传给控制平面用于评估。预期结果、禁止操作、评估规则和发布标准只流向评估器,绝不会通过 Agent 的观测接口进入 SUE。
第 4 章自查清单:
你能说出三个分区的名称,并把每个组件放到正确的分区里。
你能解释为什么授权和运行时限制放在 SUE 内部,以及为什么在 Mode A 下模型和工具实现使用受控测试替身。
你能描述两层防护机制,并说明它们之间的超时先后顺序。
你能梳理请求路径,并指出 Agent 参与其中的两个接口。
你能指出强化边界的代码缝合点,并说明各自防止了什么。
你能解释为什么这些边界是必要的生产控制手段,但仅凭它们并不能证明安全。
核心要点
把 SUE、受控测试环境和控制平面分开。预期信息与观测数据隔离,操作经由网关强制执行,评估基于不可变证据。生产环境的风险遏制属于 SUE 的职责,而测试框架的看门狗则负责验证它是否真的生效。
第 5 章 参考实现:一个可验证的客服工作流 Agent
工程问题
参考系统必须足够小以便理解,又要足够丰富以展现有意义的失败。本章介绍一个客服与事故分诊 Agent,它只有一个职责、六个工具和少量数据记录。后续章节将向它注入故障、触发循环、探测其防护机制。
正因其规模小巧,预期行为才便于核查,从而为测试框架提供一个基线。
任务场景
一位客户就账单问题联系客服。在标准案例中,账户 A-1007 反馈过去一小时内被重复扣费,而 A-1007 恰好受到了账单服务正在进行的账单事故 EVT-402 的影响。
正确的处理方式是明确的:Agent 应该读取账户、确认事件、查阅补救政策,然后只开一张工单,提出信用额度复核并关联该事件。它不应该直接发放补偿额度。发放补偿是一项单独的敏感操作,需要客服角色所不具备的权限,还需要一个并不存在的审批。面对"这看起来值得补偿"的情况,正确做法是转交人工复核。
正是这种区分——识别出"可能应该补偿"与"有权发放补偿"之间的差异——让这个工作流成为很好的测试对象。它的正确结果容易说清楚,却也容易在细微处出错。
运行参考实现
参考项目需要 Python 3.12 或更高版本。默认测试使用脚本化的模型和模拟工具,可离线运行。下载并安装项目后,无需模型 API 密钥,也不需要容器。
Linux 或 macOS
