
图 1:System Contract 是四类角色之间的共同接口。Contract 负责声明,Runtime 负责强制,Eval 负责验证,Ops 与人负责审批、接管和追责。移动端可点击查看原图。
1. 为什么“做一个 Agent”不是可执行需求
OpenAI 的 Agent 实践指南把 Agent 的基础组成概括为模型、工具与指令,并建议优先选择传统规则难以覆盖、依赖非结构化信息或需要复杂判断的工作;如果这些条件并不成立,确定性方案可能已经足够。
Anthropic 对 Workflow 与 Agent 的区分也很有帮助:
-
Workflow 由预先定义的代码路径编排模型和工具; -
Agent 由模型动态决定过程与工具使用; -
两者都应该从能解决问题的最简单结构开始。
因此,“是否使用 Agent”不是产品名称,而是一个架构判断:
下面这些工作不一定需要 Agent:
如果一开始就默认“必须是 Agent”,团队很容易把所有失败归因于模型或 Prompt,却没有一个简单控制组回答:
这部分工作是不是原本就可以用更便宜、更稳定的程序完成?
2. 系统契约位于哪一层
OpenAI 的 Define agents 文档会要求开发者配置 Agent 的名称、指令、模型、工具、Handoff、结构化输出、Guardrail、Approval 和 MCP 能力。这些是运行实现的重要组成。
本文再向前走一步:在选择具体 SDK 和模型之前,先建立一份供应商中立的系统约定。
它主要解决四类错位。
产品与模型错位
产品要的是“减少工程手册查询时间”,模型输出的是“一段看起来合理的文字”。两者不是同一个完成条件。
Prompt 与权限错位
Prompt 说“不要联网”只是行为指令。网络是否真的不可用,要由运行环境决定。
Task 与 Grader 错位
任务只要求“修复登录跳转”,隐藏测试却要求一个没有在需求中出现的函数名。此时失败可能来自评测,而不是实现。
Demo 与生产错位
一次成功演示没有描述超时、证据不足、工具失败、审批拒绝和人工接管。
所以,System Contract 的价值不在于文档更完整,而在于让不同代码层共享同一个责任定义。
3. Agent System Card 的八个部分
本系列把卡片拆成八部分:

图 2:八个部分分别回答工作、输入、完成、边界、失败、证据、控制组和版本问题。字段是本文的教学模板,不是行业标准。
3.1 Job:替谁完成什么现实工作
actor 防止需求退化为泛用聊天;task 指向现实工作;why_agent 则是一条待验证假设,不是预先成立的结论。
这里最关键的措辞是“后续版本需要”。当前控制组还没有证明它成立。
3.2 Input:输入和信任从哪里来
把用户问题标记为 untrusted,不是说用户一定恶意,而是提醒 Runtime:
-
问题内容不能修改系统规则; -
问题中出现的路径或命令不能自动获得权限; -
用户声称的内部事实不能替代受控资料。
同样,trusted_sources 也只是合同中的允许列表。文件是否真实、是否过期、是否被篡改,还需要来源校验、版本和访问控制。
3.3 Done:什么状态才算完成
abstained 被放进合法终态,是因为对知识库系统来说:
没有证据时明确拒答,可能比生成一段流畅文字更接近完成。
这与 failed 不同。拒答是系统按设计工作;失败则可能是文件不可读、解析错误、超时或预算耗尽。
3.4 Boundary:允许、禁止和需审批
三组动作必须语义一致:
早期版本曾把 publish_result 同时放进“禁止”和“需要批准”。这会让 Runtime 无法回答:批准之后到底能不能发布?
契约校验器现在会拒绝这种冲突。但仍要注意,校验器只能检查声明是否自洽,真正执行 publish_result 的工具仍需要身份、权限和 Approval。
OpenAI 的 Guardrails 与 Approvals 文档特别强调检查位置:涉及副作用的验证应该靠近产生副作用的工具。不能只在最终输出端检查一句“我没有发布”。
3.5 Failure:失败怎样结束和移交
一个系统如果只有 success,失败时就容易变成:
明确 failed 和 handed_off 后,Runtime 才能设计超时、重试上限、暂停状态和人工接管。
3.6 Evidence:用什么证据判断版本
这部分不要求第一天就有完整评测平台,但至少要把“以后凭什么判断”写出来。
没有 Evidence 时,系统改动通常会被描述成:
这些都不是可回归的结论。
3.7 Baseline:不用 Agent 能做到什么
agent_required_if 是整张卡片里最值得保留的一行。
它把“我们想做 Agent”改成了一个可以被证伪的判断:
如果确定性检索已经稳定完成任务,后续 Agent 必须在更复杂的任务上证明收益,而不是只增加 Token、延迟和故障面。
3.8 Version:让约定变化可追踪
当前卡片有独立的 id 和 version,代码也固定到 Git commit。
版本化至少要回答:
-
任务定义何时改变; -
哪些边界被放宽或收紧; -
Grader 是否增加了条件; -
哪个 Runtime 版本执行了这份合同; -
历史分数是否仍然可以比较。
如果任务和 Grader 已经变了,却继续沿用同一个“成功率”,指标就失去了含义。
4. 进入真实工程:Agent Reliability Lab
本篇代码位于:
phase-7-agent-engineering/agent-reliability-lab
它建立在仓库 Phase 6 的企业知识库 Agent 之上,但第一篇刻意把模型调用拿掉,只保留任务与控制组。这不是重新写一个无关 Demo,而是先从既有系统中提取责任和证据:

图 3:合同、任务和允许资料分别进入校验器与控制组,输出结构化报告。底部三个失败实验用于证明校验和阈值确实会改变行为。
4.1 获取固定版本
如果你已经在仓库根目录:
实验只使用 Python 标准库。先运行三条命令:
把三次输出合起来,应该能核对到:
不同机器的 latency_ms 会变化,不应该拿来逐字比较。
默认命令把本地结果写入 reports/local/,该目录被 Git 忽略;仓库根部的 reports/baseline.json 和 reports/baseline.md 是随代码检查点提交的参考报告。这样,读者运行实验不会因为机器延迟不同而得到一份无意义的 Git diff。
5. 契约校验器到底检查什么
agent_lab/contracts.py 的第一层检查是完整性:
第二层检查 id、version、对象结构、必填字段和空值,并要求几组策略数组由非空字符串组成且没有重复项。第三层再检查语义冲突:
现在运行坏契约:
预期退出码是 1,输出为:
这说明结构化契约比一段散落在文档里的描述更容易进入 CI。
但校验器不能证明:
fixtures/knowledge/*.md
真的可信; -
运行时真的断开了外网; -
工具不会绕过 prohibited_actions; -
回答符合真实业务; -
Grader 没有遗漏重要风险。
当前 run_lab.py 也没有根据卡片自动配置权限或推导文件路径:它先验证 Contract,再读取 CLI 指定的 --tasks 与 --knowledge。这是 0.1.0 有意保留的实现边界,后续 Harness 才会把声明映射为真正的运行策略。
所以正确关系是:
不要把“配置文件通过校验”误写成“系统已经安全”。
6. 把需求写成可执行任务
当前 datasets/tasks.jsonl 有五条任务:
一条最小任务包含:
控制组只使用 question 检索资料,expected_status 和 expected_terms 只在结果产生后评分。
当前 Grader 对关键词使用的是最简单的包含判断:
这只适合作为 Smoke Test。例如下面这句包含“最小权限、脱敏、审计”三个词,却明显是错误答案:
它仍可能通过关键词检查。因此,本篇的 passed 只能表示“状态与最小词项条件通过”,不能代表语义和业务事实已经完整正确。第 2 篇会加入更强的确定性检查、重复 Trial、必要的模型评分和人工抽查。
6.1 为什么一定要有拒答任务
如果任务集只有“资料中存在答案”的问题,一个最差策略也可能得高分:
加入未知的差旅额度问题,才能观察系统是否会在没有证据时停下来。
更完整的任务集还需要:
-
可回答与不可回答; -
正常输入与边界输入; -
单一证据与冲突证据; -
只读任务与需审批动作; -
能力任务与回归任务。
Anthropic 在 Demystifying evals for AI agents 中建议,早期可以从真实失败中收集约 20-50 个简单任务,并明确区分 Task、Trial、Grader 和 Transcript。
因此,本篇的 5 条任务只是教学控制组,不是生产规模 Eval。第 2 篇会扩充任务、加入重复 Trial 和更完整的 Grader。
6.2 Grader 不能暗中改变合同
SWE-bench 提供了一个很值得借鉴的任务结构:
它把环境、任务、执行和评测分开,是很好的工程思想。
但评测本身也可能出错。OpenAI 在 2026 年 2 月发布的 SWE-bench Verified 审计 中指出,部分问题的测试会要求任务描述没有说明的行为,或者过度限定实现细节;环境差异也可能造成伪失败。OpenAI 因此不再用 SWE-bench Verified 衡量前沿模型进展。
这给普通 Agent 项目的提醒是:
Grader 检查的每个关键条件,都应该能回到任务描述、业务规则或明确的安全策略。
隐藏答案可以,隐藏需求不行。
7. 为什么控制组故意不用模型
agent_lab/baseline.py 实现了一个很朴素的段落检索器:
-
把中文连续文本切成 bigram; -
提取英文和数字词项; -
计算问题词项与每个段落的重合率; -
低于阈值时拒答; -
否则直接返回得分最高的段落。
核心评分只有:
拒答逻辑是:
它显然不是一个强检索系统:
-
不理解同义词和语义; -
对问题措辞敏感; -
不能合并多个段落; -
不能处理冲突资料; -
不能选择外部工具; -
不能根据中间结果恢复步骤。
这些限制不是缺陷清单,而是控制组的意义。它便宜、确定、容易解释,后续复杂版本必须在同一任务上证明自己解决了哪些限制。

图 4:真实任务先进入确定性基线。只有动态决策在相同任务、边界和预算下产生可测量收益,才进入 Agent 候选版本。
8. 三个实验:让失败成为可见证据
只展示 5/5 很容易让教程看起来正确,却不能证明系统的边界真的存在。
所以 Lab 提供三个故意失败的实验。
实验 A:边界自相矛盾
坏卡片同时允许和禁止 read_knowledge。命令返回结构化错误并以状态码 1 结束。
它验证的是合同的语义一致性,不是运行时权限。
实验 B:阈值过低,系统过度回答
结果:
差旅问题与任何资料都没有词项重合,但阈值为 0 时,系统仍然允许返回一个段落。它把“不知道”伪装成了回答。
实验 C:阈值过高,系统过度拒答
结果:
系统成功避开了未知问题,却把四个能够回答的问题也拒绝了。
8.1 为什么两个指标都要看
如果只看 correct_abstention_rate,阈值 1.0 看起来非常安全;如果只看“回答数量”,阈值 0.0 看起来覆盖率最高。
真实系统需要同时处理两类错误:
这也是为什么 System Contract 不能只写“避免幻觉”。“一律不回答”确实很少幻觉,但也没有完成工作。
9. 5/5 到底证明了什么
默认阈值 0.28 的结果是:
它只证明:
-
当前五条问题可以被这份知识文件和算法区分; -
默认阈值能回答四条已知问题; -
未知差旅问题会拒答; -
报告和测试可以重复运行。
它没有证明:
-
对其他表达方式仍然有效; -
能处理多段证据和冲突; -
能安全接入真实内部资料; -
能抵抗 Prompt Injection; -
比语义检索或单次模型调用更好; -
需要动态 Agent; -
可以上线。
更重要的是,当前结果支持一个保守结论:
对这五条小型任务,确定性检索已经够用;现在还没有证据证明 Agent 能带来净收益。
下一步不是为了让项目“更像 AI”而马上接模型,而是扩充真实任务,暴露控制组确实无法处理的决策:
-
需要组合多个来源; -
资料冲突时需要比较新鲜度和权限; -
工具失败后需要切换或恢复; -
高风险动作需要暂停和审批; -
不同任务需要不同检索与验证路径。
只有这些任务出现,并且 Agent 在等预算比较中改善结果,升级才有依据。
10. System Contract 不等于 Prompt、Schema 或 Agent Card
几个概念容易混在一起。
Google 对 A2A 协议的介绍说明,A2A Agent Card 发布在约定 URL,用于描述 Agent 名称、能力和端点。它更像服务的可发现接口。
本文的卡片则是项目内部的责任接口。两者以后可以互相映射,但不能因为名字相似就当成同一标准。
11. 什么时候不值得写一张复杂卡片
System Contract 也不应该变成形式主义。
下面这些任务可以使用更轻的定义:
-
一次性、只读、低风险的文本转换; -
输入和输出完全固定的本地脚本; -
已有成熟 API 合同和测试,只增加一层自然语言入口; -
不保存状态、不调用外部工具、不产生副作用的实验。
这时最小版本可能只有:
当系统开始具备下面任一条件,再增加结构化和版本化:
-
会访问多个数据源; -
会写入外部系统; -
有拒答、接管或审批; -
需要比较多个版本; -
有多人或多服务共同维护; -
失败会造成业务、隐私或资金风险。
卡片的目标是减少解释成本和责任歧义,不是追求字段数量。
12. 给自己项目的可复制模板
可以直接下载 agent-system-card.template.json,也可以查看本文实验使用的真实 Contract。
下面这份模板可以先放在仓库的 contracts/agent-system-card.json:
填完后做四次交叉检查:
Done
与 Failure是否出现相同状态;allowed
、 prohibited与approval是否互相冲突;-
每个 Grader 条件是否能回到任务描述或安全策略; why_agent
是否只是“因为 Agent 很强”,而没有可测量场景。
13. 45-60 分钟跟做练习
选择一个你真的想做成 Agent 的任务,例如:
第一步:写 Job 与非 Agent 基线,10 分钟
先不要写模型名。
如果 Why Agent 只能写“回答更智能”,继续缩小任务。
第二步:写完成、失败和边界,15 分钟
至少写出:
-
一个成功终态; -
一个合法拒答或不适用终态; -
一个失败终态; -
一个需要人工接管的条件; -
一个允许动作; -
一个禁止动作; -
一个需审批动作。
第三步:建立五条控制任务,15 分钟
建议分配:
把 Grader 条件写在任务里,但不要把预期答案喂给被测系统。
第四步:运行最简单基线,10 分钟
可以是:
-
关键词检索; -
SQL; -
正则与模板; -
固定 Workflow; -
单次模型调用,不给自主工具选择。
记录每条任务的状态和失败原因。
第五步:做一次故意失败,10 分钟
任选一个:
-
删除必要合同字段; -
让允许和禁止动作冲突; -
把拒答阈值调到极端; -
给 Grader 加一个任务没有说明的隐藏条件; -
移除一份必要资料。
如果失败后只能看到“没通过”,却无法知道哪个任务、哪个条件、哪次运行出了问题,证据层还不够。
练习结束时,你应该能用一句话说明:
14. 发布前检查清单
任务
-
[ ] 描述的是现实工作,而不是“做一个 Agent”。 -
[ ] 输入、成功、拒答、失败和人工接管都能区分。 -
[ ] 每个 Grader 条件在任务或策略中有依据。
边界
-
[ ] 允许、禁止和需审批没有重叠。 -
[ ] 高风险动作由 Runtime 与人强制,不只写在 Prompt。 -
[ ] 可信来源有版本、访问控制或来源证据。
证据
-
[ ] 有一个不用 Agent 的控制组。 -
[ ] 每次运行能定位到任务、版本和失败原因。 -
[ ] 至少运行过一个故意失败的反例。 -
[ ] 没有把小型任务集的 100%外推为生产可靠。
升级
-
[ ] 能说清固定脚本或 Workflow 的能力上限。 -
[ ] Agent 候选版本会在同一任务、边界和预算下比较。 -
[ ] 新增复杂度对应一个可测量收益。
结语
Agent 工程的第一步很容易被误认为“选择模型和框架”。
这篇实验得到的结论更朴素:
当前 Agent Reliability Lab 的确定性基线在五条教学任务上全部通过。
这个结果不意味着系统已经可靠,也不意味着 Agent 没有价值。它只让下一步问题变得准确:
我们需要加入哪些真实任务,才能证明动态决策、工具使用和恢复能力确实比控制组更好?
这正是第 2 篇《Agent Evals》要解决的问题:扩充任务集,区分 Task、Trial、Grader 与 Trace,并让“新版更聪明”变成可重复比较的证据。
参考资料
官方资料
-
OpenAI:A practical guide to building agents -
OpenAI Developer Docs:Build agents -
OpenAI Developer Docs:Define agents -
OpenAI Developer Docs:Guardrails and approvals -
OpenAI Cookbook:Eval-driven system design -
OpenAI:Why SWE-bench Verified no longer measures frontier coding capabilities -
OpenAI:Separating signal from noise in coding evaluations -
Anthropic:Building effective agents -
Anthropic:Demystifying evals for AI agents -
Google Developers Blog:Developer’s Guide to AI Agent Protocols
论文与开源工程
-
SWE-bench:Can Language Models Resolve Real-World GitHub Issues? -
SWE-bench/SWE-bench -
OpenHands/benchmarks -
openai/openai-agents-python -
RalfNick/ai-agent-learn:Agent Reliability Lab
订阅智宅客
AI / 技术 / 数字生活方式,新文章第一时间送到邮箱。









