已发布资料基础官方资料 + 个人实践
·15 分钟AI Agent 工程进阶
文章/AI 与智能体

Agent 系统契约:先定义任务、边界与非 Agent 基线

AI Agent 工程进阶第 1 篇:用一个可运行的知识库工程,把模糊的 Agent 需求拆成 Job、Input、Done、Boundary、Failure、Evidence 与 Baseline,并通过坏契约和阈值实验验证它是否真的需要 Agent。

文章目录
  1. 一分钟概览
  2. 1. 为什么“做一个 Agent”不是可执行需求
  3. 2. 系统契约位于哪一层
  4. 产品与模型错位
  5. Prompt 与权限错位
  6. Task 与 Grader 错位
  7. Demo 与生产错位
  8. 3. Agent System Card 的八个部分
  9. 3.1 Job:替谁完成什么现实工作
  10. 3.2 Input:输入和信任从哪里来
  11. 3.3 Done:什么状态才算完成
  12. 3.4 Boundary:允许、禁止和需审批
  13. 3.5 Failure:失败怎样结束和移交
  14. 3.6 Evidence:用什么证据判断版本
  15. 3.7 Baseline:不用 Agent 能做到什么
  16. 3.8 Version:让约定变化可追踪
  17. 4. 进入真实工程:Agent Reliability Lab
  18. 4.1 获取固定版本
  19. 5. 契约校验器到底检查什么
  20. 6. 把需求写成可执行任务
  21. 6.1 为什么一定要有拒答任务
  22. 6.2 Grader 不能暗中改变合同
  23. 7. 为什么控制组故意不用模型
  24. 8. 三个实验:让失败成为可见证据
  25. 实验 A:边界自相矛盾
  26. 实验 B:阈值过低,系统过度回答
  27. 实验 C:阈值过高,系统过度拒答
  28. 8.1 为什么两个指标都要看
  29. 9. 5/5 到底证明了什么
  30. 10. System Contract 不等于 Prompt、Schema 或 Agent Card
  31. 11. 什么时候不值得写一张复杂卡片
  32. 12. 给自己项目的可复制模板
  33. 13. 45-60 分钟跟做练习
  34. 第一步:写 Job 与非 Agent 基线,10 分钟
  35. 第二步:写完成、失败和边界,15 分钟
  36. 第三步:建立五条控制任务,15 分钟
  37. 第四步:运行最简单基线,10 分钟
  38. 第五步:做一次故意失败,10 分钟
  39. 14. 发布前检查清单
  40. 结语
  41. 参考资料
  42. 官方资料
  43. 论文与开源工程
阅读提要

AI Agent 工程进阶第 1 篇:用一个可运行的知识库工程,把模糊的 Agent 需求拆成 Job、Input、Done、Boundary、Failure、Evidence 与 Baseline,并通过坏契约和阈值实验验证它是否真的需要 Agent。

#Agent#Agent Engineering#系统契约#评估#可靠性

这是《AI Agent 工程进阶:从 Demo 到可靠系统》的第 1 篇。

系列从一个看起来不够“AI”的问题开始:

在接入模型、工具、Memory 或 Graph 之前,这个系统到底替谁完成什么工作?什么算完成?什么不能做?我们又凭什么判断它值得被做成 Agent?

很多 Agent 项目的第一条需求是:

text
做一个能回答内部问题的 Agent。

这句话能表达方向,却不能直接指导工程实现。它没有说明:

  • “内部问题”来自谁;
  • 哪些资料可以读取;
  • 资料不足时能不能猜;
  • 回答一段文字是否就算完成;
  • 是否允许联网、写文件或发布结果;
  • 什么时候必须拒答或交给人;
  • 一个普通检索脚本已经能做到多少。

如果这些问题没有答案,后面再精巧的 Prompt、Tool、Loop 和 Graph 都只是在放大一个模糊目标。

因此,本篇不会先接模型。我会在公开仓库 RalfNick/ai-agent-learn 的真实代码上建立一个 Agent Reliability Lab,先交付四样东西:

text
Agent System Card
  + 可执行任务集
  + 非 Agent 控制组
  + 可检查的失败报告

然后用三个实验回答:合同写错时会发生什么,拒答阈值过低和过高时会发生什么,以及当前证据是否真的支持升级 Agent。

项目说明
内容类型AI Agent 工程教程与可复现实验
适合读者已经能调用模型或运行 Agent,希望把 Demo 变成可评估系统的开发者
阅读时间速读约 10 分钟,完整阅读约 14-20 分钟
跟做时间45-60 分钟
实验环境Python 3.10+,零第三方依赖,不需要 API Key
代码检查点556bace
可带走产物Agent System Card、任务集、确定性基线、失败实验与检查清单
资料核对日期2026-07-26

术语边界

本文的 Agent System Card 是我为这个系列整理的工程模板,用于把产品、运行时、评测和运营约定放在同一份版本化文件里。它不是 OpenAI 官方字段,不是模型厂商发布的 Model System Card,也不是 A2A 协议中用于服务发现的 Agent Card

卡片里的声明也不是安全机制。prohibited_actions 写着“禁止联网”,并不会自动切断网络;真正的限制仍要由 Sandbox、权限、工具注册表、审批和运行时策略执行。

下文用 System Contract 指这套系统约定,用 System Card 指它在仓库中的结构化 JSON 载体。

一分钟概览

如果只保存这篇文章的结论,可以记住下面七句:

  1. Agent 需求的最小单位不是 Prompt,而是一个可判断完成或失败的现实任务。
  2. 系统契约连接产品、Runtime、Eval 与 Ops,但不会替代其中任何一层。
  3. Done 要描述可验证结果,Failure 要描述明确终态,不能只写“尽量完成”。
  4. “允许”“禁止”和“需要批准”是三种不同策略,不能互相矛盾。
  5. 任务集中的 Grader 只能检查任务描述已经承诺的内容,不能暗中增加要求。
  6. 先运行脚本或固定工作流;只有动态决策带来可测量净收益时才升级 Agent。
  7. 当前 Lab 的 5/5 只说明一个小型控制组通过,反而还没有证明必须使用 Agent。

三种阅读方式

  • 只想理解概念:读第 1-3、9-11 节。
  • 准备运行代码:读第 4-8 节。
  • 想直接用于自己的项目:读第 12-14 节,并下载文末模板。

图 1:Agent 系统契约的责任边界图 1:Agent 系统契约的责任边界

图 1:System Contract 是四类角色之间的共同接口。Contract 负责声明,Runtime 负责强制,Eval 负责验证,Ops 与人负责审批、接管和追责。移动端可点击查看原图。

1. 为什么“做一个 Agent”不是可执行需求

OpenAI 的 Agent 实践指南把 Agent 的基础组成概括为模型、工具与指令,并建议优先选择传统规则难以覆盖、依赖非结构化信息或需要复杂判断的工作;如果这些条件并不成立,确定性方案可能已经足够。

Anthropic 对 Workflow 与 Agent 的区分也很有帮助:

  • Workflow 由预先定义的代码路径编排模型和工具;
  • Agent 由模型动态决定过程与工具使用;
  • 两者都应该从能解决问题的最简单结构开始。

因此,“是否使用 Agent”不是产品名称,而是一个架构判断:

text
任务是否需要在运行过程中,
根据不完整信息动态选择行动、工具或恢复路径?

下面这些工作不一定需要 Agent:

任务更可能的起点原因
根据固定字段生成日报模板或脚本输入和输出结构稳定
从一份手册检索对应段落搜索或确定性检索路径固定,可直接验证
对一批格式统一的文件做转换批处理 Workflow步骤已知,不需要动态规划
在多个工具间调查异常并根据结果改计划Agent下一步依赖中间观察
跨系统处理例外并在风险动作前请求批准Agent + Runtime Policy需要动态决策与人工边界

如果一开始就默认“必须是 Agent”,团队很容易把所有失败归因于模型或 Prompt,却没有一个简单控制组回答:

这部分工作是不是原本就可以用更便宜、更稳定的程序完成?

2. 系统契约位于哪一层

OpenAI 的 Define agents 文档会要求开发者配置 Agent 的名称、指令、模型、工具、Handoff、结构化输出、Guardrail、Approval 和 MCP 能力。这些是运行实现的重要组成。

本文再向前走一步:在选择具体 SDK 和模型之前,先建立一份供应商中立的系统约定。

text
产品目标
   ↓
Agent System Contract
   ├── Runtime / Harness:怎样执行和限制
   ├── Eval / Grader:怎样判断和比较
   └── Ops / Human:怎样批准、接管和追踪

它主要解决四类错位。

产品与模型错位

产品要的是“减少工程手册查询时间”,模型输出的是“一段看起来合理的文字”。两者不是同一个完成条件。

Prompt 与权限错位

Prompt 说“不要联网”只是行为指令。网络是否真的不可用,要由运行环境决定。

Task 与 Grader 错位

任务只要求“修复登录跳转”,隐藏测试却要求一个没有在需求中出现的函数名。此时失败可能来自评测,而不是实现。

Demo 与生产错位

一次成功演示没有描述超时、证据不足、工具失败、审批拒绝和人工接管。

所以,System Contract 的价值不在于文档更完整,而在于让不同代码层共享同一个责任定义。

3. Agent System Card 的八个部分

本系列把卡片拆成八部分:

图 2:Agent System Card 的八个组成部分图 2:Agent System Card 的八个组成部分

图 2:八个部分分别回答工作、输入、完成、边界、失败、证据、控制组和版本问题。字段是本文的教学模板,不是行业标准。

3.1 Job:替谁完成什么现实工作

json
{
  "job": {
    "actor": "需要查询内部工程手册的开发者",
    "task": "根据允许读取的知识库回答问题,并在证据不足时明确拒答",
    "why_agent": "后续版本需要在检索、工具、验证和人工审批之间做受约束的动态决策"
  }
}

actor 防止需求退化为泛用聊天;task 指向现实工作;why_agent 则是一条待验证假设,不是预先成立的结论。

这里最关键的措辞是“后续版本需要”。当前控制组还没有证明它成立。

3.2 Input:输入和信任从哪里来

json
{
  "input": {
    "required": ["question"],
    "trusted_sources": ["fixtures/knowledge/*.md"],
    "untrusted_sources": ["user_question"]
  }
}

把用户问题标记为 untrusted,不是说用户一定恶意,而是提醒 Runtime:

  • 问题内容不能修改系统规则;
  • 问题中出现的路径或命令不能自动获得权限;
  • 用户声称的内部事实不能替代受控资料。

同样,trusted_sources 也只是合同中的允许列表。文件是否真实、是否过期、是否被篡改,还需要来源校验、版本和访问控制。

3.3 Done:什么状态才算完成

json
{
  "done": {
    "terminal_states": ["answered", "abstained"],
    "validators": [
      "回答包含任务要求的关键事实",
      "回答只使用允许的资料",
      "资料不足时状态必须为 abstained"
    ]
  }
}

abstained 被放进合法终态,是因为对知识库系统来说:

没有证据时明确拒答,可能比生成一段流畅文字更接近完成。

这与 failed 不同。拒答是系统按设计工作;失败则可能是文件不可读、解析错误、超时或预算耗尽。

3.4 Boundary:允许、禁止和需审批

json
{
  "boundaries": {
    "allowed_actions": ["read_knowledge", "return_answer", "abstain"],
    "prohibited_actions": ["write_source", "use_external_web"],
    "approval_required": ["change_policy", "publish_result"]
  }
}

三组动作必须语义一致:

text
allowed       当前策略下可以执行
prohibited    当前系统无论如何都不能执行
approval      暂停运行,得到授权后才可执行

早期版本曾把 publish_result 同时放进“禁止”和“需要批准”。这会让 Runtime 无法回答:批准之后到底能不能发布?

契约校验器现在会拒绝这种冲突。但仍要注意,校验器只能检查声明是否自洽,真正执行 publish_result 的工具仍需要身份、权限和 Approval。

OpenAI 的 Guardrails 与 Approvals 文档特别强调检查位置:涉及副作用的验证应该靠近产生副作用的工具。不能只在最终输出端检查一句“我没有发布”。

3.5 Failure:失败怎样结束和移交

json
{
  "failure": {
    "terminal_states": ["failed", "handed_off"],
    "handoff_when": [
      "资料互相冲突",
      "任务请求越过允许的数据边界",
      "预算或重试次数耗尽"
    ]
  }
}

一个系统如果只有 success,失败时就容易变成:

text
继续试
  → 换一种说法再试
  → 忽略风险继续试
  → 最后把不确定结果包装成完成

明确 failedhanded_off 后,Runtime 才能设计超时、重试上限、暂停状态和人工接管。

3.6 Evidence:用什么证据判断版本

json
{
  "evidence": {
    "dataset": "datasets/tasks.jsonl",
    "primary_metrics": [
      "task_pass_rate",
      "correct_abstention_rate"
    ],
    "secondary_metrics": [
      "retrieved_chunk_count",
      "latency_ms"
    ]
  }
}

这部分不要求第一天就有完整评测平台,但至少要把“以后凭什么判断”写出来。

没有 Evidence 时,系统改动通常会被描述成:

text
新版回答更自然。
新版看起来更聪明。
新版用了更强模型。

这些都不是可回归的结论。

3.7 Baseline:不用 Agent 能做到什么

json
{
  "baseline": {
    "strategy": "deterministic_paragraph_retrieval",
    "command": "python run_lab.py baseline",
    "agent_required_if": "动态工具选择或跨步骤恢复在同一任务集上带来可测量收益"
  }
}

agent_required_if 是整张卡片里最值得保留的一行。

它把“我们想做 Agent”改成了一个可以被证伪的判断:

如果确定性检索已经稳定完成任务,后续 Agent 必须在更复杂的任务上证明收益,而不是只增加 Token、延迟和故障面。

3.8 Version:让约定变化可追踪

当前卡片有独立的 idversion,代码也固定到 Git commit。

版本化至少要回答:

  • 任务定义何时改变;
  • 哪些边界被放宽或收紧;
  • Grader 是否增加了条件;
  • 哪个 Runtime 版本执行了这份合同;
  • 历史分数是否仍然可以比较。

如果任务和 Grader 已经变了,却继续沿用同一个“成功率”,指标就失去了含义。

4. 进入真实工程:Agent Reliability Lab

本篇代码位于:

phase-7-agent-engineering/agent-reliability-lab

它建立在仓库 Phase 6 的企业知识库 Agent 之上,但第一篇刻意把模型调用拿掉,只保留任务与控制组。这不是重新写一个无关 Demo,而是先从既有系统中提取责任和证据:

Phase 6 已有实现本篇怎样接住暂时没有伪装成已完成的部分
workflow.py 中的检索、证据检查、修复与拒答节点提炼为 JobDoneFailure 和允许动作暂未把 Card 自动转换成 Runtime Policy
evaluator.py 中的状态、关键词、来源与 Trace 检查收缩成五条可重复任务和最小 Grader暂未加入重复 Trial、模型评分和人工复核
Phase 6 的 Agentic QA 工作图与 Trace保留为后续版本要比较的候选系统本篇不声称 Agent 已经优于控制组
text
agent-reliability-lab/
├── contracts/
│   └── agent-system-card.json
├── datasets/
│   └── tasks.jsonl
├── fixtures/knowledge/
│   └── product-handbook.md
├── agent_lab/
│   ├── contracts.py
│   ├── baseline.py
│   └── reporting.py
├── examples/
│   └── invalid-card.json
├── reports/
│   ├── baseline.json
│   ├── baseline.md
│   └── local/              # 本地生成,Git 忽略
├── tests/
└── run_lab.py

图 3:Agent Reliability Lab 的代码与证据流图 3:Agent Reliability Lab 的代码与证据流

图 3:合同、任务和允许资料分别进入校验器与控制组,输出结构化报告。底部三个失败实验用于证明校验和阈值确实会改变行为。

4.1 获取固定版本

bash
git clone --branch agent-engineering-series https://github.com/RalfNick/ai-agent-learn.git
cd ai-agent-learn
git checkout 556bace
cd phase-7-agent-engineering/agent-reliability-lab

如果你已经在仓库根目录:

bash
cd phase-7-agent-engineering/agent-reliability-lab

实验只使用 Python 标准库。先运行三条命令:

bash
python run_lab.py check-contract
python run_lab.py baseline
python -m unittest discover -s tests -v

把三次输出合起来,应该能核对到:

text
contract status: valid
tasks: 5
passed: 5
task_pass_rate: 1.0
correct_abstention_rate: 1.0
tests: 9 passed

不同机器的 latency_ms 会变化,不应该拿来逐字比较。

默认命令把本地结果写入 reports/local/,该目录被 Git 忽略;仓库根部的 reports/baseline.jsonreports/baseline.md 是随代码检查点提交的参考报告。这样,读者运行实验不会因为机器延迟不同而得到一份无意义的 Git diff。

5. 契约校验器到底检查什么

agent_lab/contracts.py 的第一层检查是完整性:

python
REQUIRED_SECTIONS = {
    "id",
    "version",
    "job",
    "input",
    "done",
    "boundaries",
    "failure",
    "evidence",
    "baseline",
}

第二层检查 idversion、对象结构、必填字段和空值,并要求几组策略数组由非空字符串组成且没有重复项。第三层再检查语义冲突:

python
_reject_overlap(
    allowed_actions,
    prohibited_actions,
    "actions cannot be both allowed and prohibited",
)

_reject_overlap(
    prohibited_actions,
    approval_required,
    "actions cannot be both prohibited and approval-gated",
)

_reject_overlap(
    success_states,
    failure_states,
    "terminal states cannot be both successful and failed",
)

现在运行坏契约:

bash
python run_lab.py check-contract --contract examples/invalid-card.json

预期退出码是 1,输出为:

json
{
  "status": "invalid",
  "path": "examples\\invalid-card.json",
  "error": "actions cannot be both allowed and prohibited: read_knowledge"
}

这说明结构化契约比一段散落在文档里的描述更容易进入 CI。

但校验器不能证明:

  • fixtures/knowledge/*.md 真的可信;
  • 运行时真的断开了外网;
  • 工具不会绕过 prohibited_actions
  • 回答符合真实业务;
  • Grader 没有遗漏重要风险。

当前 run_lab.py 也没有根据卡片自动配置权限或推导文件路径:它先验证 Contract,再读取 CLI 指定的 --tasks--knowledge。这是 0.1.0 有意保留的实现边界,后续 Harness 才会把声明映射为真正的运行策略。

所以正确关系是:

text
Contract Validator:检查约定是否完整、自洽
Runtime Policy:强制权限、工具和状态边界
Eval:检查行为和现实结果
Human / Ops:处理高风险判断与责任

不要把“配置文件通过校验”误写成“系统已经安全”。

6. 把需求写成可执行任务

当前 datasets/tasks.jsonl 有五条任务:

json
{"id":"qa-001","question":"工具服务持续限流时,系统应该怎样降级?","expected_status":"answered","expected_terms":["只读模式","人工处理"]}
{"id":"qa-005","question":"公司差旅报销的每日额度是多少?","expected_status":"abstained","expected_terms":[]}

一条最小任务包含:

字段作用是否给被测系统
id稳定追踪失败样本可以
question系统实际收到的输入
expected_statusGrader 判断回答或拒答
expected_terms最小事实检查

控制组只使用 question 检索资料,expected_statusexpected_terms 只在结果产生后评分。

当前 Grader 对关键词使用的是最简单的包含判断:

python
for term in task.expected_terms:
    if term not in answer:
        failures.append(f"missing_term:{term}")

这只适合作为 Smoke Test。例如下面这句包含“最小权限、脱敏、审计”三个词,却明显是错误答案:

text
系统不需要最小权限,也不用脱敏,只要保留审计即可。

它仍可能通过关键词检查。因此,本篇的 passed 只能表示“状态与最小词项条件通过”,不能代表语义和业务事实已经完整正确。第 2 篇会加入更强的确定性检查、重复 Trial、必要的模型评分和人工抽查。

6.1 为什么一定要有拒答任务

如果任务集只有“资料中存在答案”的问题,一个最差策略也可能得高分:

text
无论证据多少都生成答案。

加入未知的差旅额度问题,才能观察系统是否会在没有证据时停下来。

更完整的任务集还需要:

  • 可回答与不可回答;
  • 正常输入与边界输入;
  • 单一证据与冲突证据;
  • 只读任务与需审批动作;
  • 能力任务与回归任务。

Anthropic 在 Demystifying evals for AI agents 中建议,早期可以从真实失败中收集约 20-50 个简单任务,并明确区分 Task、Trial、Grader 和 Transcript。

因此,本篇的 5 条任务只是教学控制组,不是生产规模 Eval。第 2 篇会扩充任务、加入重复 Trial 和更完整的 Grader。

6.2 Grader 不能暗中改变合同

SWE-bench 提供了一个很值得借鉴的任务结构:

text
代码仓库 + Issue 描述
  → 生成 Patch
  → fail-to-pass 与 regression tests 判断结果

它把环境、任务、执行和评测分开,是很好的工程思想。

但评测本身也可能出错。OpenAI 在 2026 年 2 月发布的 SWE-bench Verified 审计 中指出,部分问题的测试会要求任务描述没有说明的行为,或者过度限定实现细节;环境差异也可能造成伪失败。OpenAI 因此不再用 SWE-bench Verified 衡量前沿模型进展。

这给普通 Agent 项目的提醒是:

Grader 检查的每个关键条件,都应该能回到任务描述、业务规则或明确的安全策略。

隐藏答案可以,隐藏需求不行。

7. 为什么控制组故意不用模型

agent_lab/baseline.py 实现了一个很朴素的段落检索器:

  1. 把中文连续文本切成 bigram;
  2. 提取英文和数字词项;
  3. 计算问题词项与每个段落的重合率;
  4. 低于阈值时拒答;
  5. 否则直接返回得分最高的段落。

核心评分只有:

python
overlap = query_tokens & chunk_tokens
score = len(overlap) / len(query_tokens)

拒答逻辑是:

python
if best_chunk is None or best_score < threshold:
    status = "abstained"
    answer = "根据当前允许读取的资料,我无法可靠回答这个问题。"
else:
    status = "answered"
    answer = _content_without_heading(best_chunk)

它显然不是一个强检索系统:

  • 不理解同义词和语义;
  • 对问题措辞敏感;
  • 不能合并多个段落;
  • 不能处理冲突资料;
  • 不能选择外部工具;
  • 不能根据中间结果恢复步骤。

这些限制不是缺陷清单,而是控制组的意义。它便宜、确定、容易解释,后续复杂版本必须在同一任务上证明自己解决了哪些限制。

图 4:从非 Agent 基线到 Agent 的升级门槛图 4:从非 Agent 基线到 Agent 的升级门槛

图 4:真实任务先进入确定性基线。只有动态决策在相同任务、边界和预算下产生可测量收益,才进入 Agent 候选版本。

8. 三个实验:让失败成为可见证据

只展示 5/5 很容易让教程看起来正确,却不能证明系统的边界真的存在。

所以 Lab 提供三个故意失败的实验。

实验 A:边界自相矛盾

bash
python run_lab.py check-contract --contract examples/invalid-card.json

坏卡片同时允许和禁止 read_knowledge。命令返回结构化错误并以状态码 1 结束。

它验证的是合同的语义一致性,不是运行时权限。

实验 B:阈值过低,系统过度回答

bash
python run_lab.py baseline --threshold 0.0 --output reports/threshold-zero

结果:

text
task_pass_rate: 0.8
correct_abstention_rate: 0.0
qa-005: answered != abstained

差旅问题与任何资料都没有词项重合,但阈值为 0 时,系统仍然允许返回一个段落。它把“不知道”伪装成了回答。

实验 C:阈值过高,系统过度拒答

bash
python run_lab.py baseline --threshold 1.0 --output reports/threshold-one

结果:

text
task_pass_rate: 0.2
correct_abstention_rate: 1.0
qa-001..qa-004: abstained != answered

系统成功避开了未知问题,却把四个能够回答的问题也拒绝了。

8.1 为什么两个指标都要看

如果只看 correct_abstention_rate,阈值 1.0 看起来非常安全;如果只看“回答数量”,阈值 0.0 看起来覆盖率最高。

真实系统需要同时处理两类错误:

text
False Answer       没有证据却回答
False Abstention   有足够证据却拒答

这也是为什么 System Contract 不能只写“避免幻觉”。“一律不回答”确实很少幻觉,但也没有完成工作。

9. 5/5 到底证明了什么

默认阈值 0.28 的结果是:

TaskScoreStatusPassed
qa-001 限流降级0.60answeredyes
qa-002 发布门禁0.46answeredyes
qa-003 敏感数据0.40answeredyes
qa-004 资料不足0.55answeredyes
qa-005 差旅额度0.00abstainedyes

它只证明:

  • 当前五条问题可以被这份知识文件和算法区分;
  • 默认阈值能回答四条已知问题;
  • 未知差旅问题会拒答;
  • 报告和测试可以重复运行。

它没有证明:

  • 对其他表达方式仍然有效;
  • 能处理多段证据和冲突;
  • 能安全接入真实内部资料;
  • 能抵抗 Prompt Injection;
  • 比语义检索或单次模型调用更好;
  • 需要动态 Agent;
  • 可以上线。

更重要的是,当前结果支持一个保守结论:

对这五条小型任务,确定性检索已经够用;现在还没有证据证明 Agent 能带来净收益。

下一步不是为了让项目“更像 AI”而马上接模型,而是扩充真实任务,暴露控制组确实无法处理的决策:

  • 需要组合多个来源;
  • 资料冲突时需要比较新鲜度和权限;
  • 工具失败后需要切换或恢复;
  • 高风险动作需要暂停和审批;
  • 不同任务需要不同检索与验证路径。

只有这些任务出现,并且 Agent 在等预算比较中改善结果,升级才有依据。

10. System Contract 不等于 Prompt、Schema 或 Agent Card

几个概念容易混在一起。

概念主要作用是否执行权限是否定义完整系统责任
Prompt / Instructions指导当前模型行为通常否
Tool Schema描述一次工具调用的输入输出工具实现决定
JSON Schema校验数据结构只覆盖声明结构
A2A Agent Card服务发现与能力描述协议与实现决定
Model System Card说明模型能力、风险和评测面向模型而非单一业务系统
本文 Agent System Contract约定任务、边界、失败和证据否,需 Runtime 强制是,面向具体应用

Google 对 A2A 协议的介绍说明,A2A Agent Card 发布在约定 URL,用于描述 Agent 名称、能力和端点。它更像服务的可发现接口。

本文的卡片则是项目内部的责任接口。两者以后可以互相映射,但不能因为名字相似就当成同一标准。

11. 什么时候不值得写一张复杂卡片

System Contract 也不应该变成形式主义。

下面这些任务可以使用更轻的定义:

  • 一次性、只读、低风险的文本转换;
  • 输入和输出完全固定的本地脚本;
  • 已有成熟 API 合同和测试,只增加一层自然语言入口;
  • 不保存状态、不调用外部工具、不产生副作用的实验。

这时最小版本可能只有:

markdown
Job:
Input:
Done:
Boundary:
Failure:
Baseline:

当系统开始具备下面任一条件,再增加结构化和版本化:

  • 会访问多个数据源;
  • 会写入外部系统;
  • 有拒答、接管或审批;
  • 需要比较多个版本;
  • 有多人或多服务共同维护;
  • 失败会造成业务、隐私或资金风险。

卡片的目标是减少解释成本和责任歧义,不是追求字段数量。

12. 给自己项目的可复制模板

可以直接下载 agent-system-card.template.json,也可以查看本文实验使用的真实 Contract

下面这份模板可以先放在仓库的 contracts/agent-system-card.json

json
{
  "id": "your-agent-system",
  "version": "0.1.0",
  "job": {
    "actor": "谁在使用",
    "task": "替他完成什么现实工作",
    "why_agent": "为什么固定脚本或工作流可能不足"
  },
  "input": {
    "required": ["最小输入"],
    "trusted_sources": ["允许的数据来源"],
    "untrusted_sources": ["必须按不可信处理的输入"]
  },
  "done": {
    "terminal_states": ["completed", "abstained"],
    "validators": ["可执行或可人工复核的完成条件"]
  },
  "boundaries": {
    "allowed_actions": ["当前可自动执行"],
    "prohibited_actions": ["无条件禁止"],
    "approval_required": ["批准后才可执行"]
  },
  "failure": {
    "terminal_states": ["failed", "handed_off"],
    "handoff_when": ["冲突、超时、越权或高风险条件"]
  },
  "evidence": {
    "dataset": "datasets/tasks.jsonl",
    "primary_metrics": ["现实任务指标"],
    "secondary_metrics": ["延迟、成本、步骤或接管率"]
  },
  "baseline": {
    "strategy": "当前最简单可行方案",
    "command": "可重复运行的命令",
    "agent_required_if": "升级 Agent 必须证明的收益"
  }
}

填完后做四次交叉检查:

  1. DoneFailure 是否出现相同状态;
  2. allowedprohibitedapproval 是否互相冲突;
  3. 每个 Grader 条件是否能回到任务描述或安全策略;
  4. why_agent 是否只是“因为 Agent 很强”,而没有可测量场景。

13. 45-60 分钟跟做练习

选择一个你真的想做成 Agent 的任务,例如:

text
根据公司知识库回答工程问题
审查 GitHub Issue 并生成修复建议
调研一个主题并输出带引用报告
整理客服请求并起草处理动作

第一步:写 Job 与非 Agent 基线,10 分钟

先不要写模型名。

markdown
Actor:
Task:
Why Agent:
Simplest Baseline:

如果 Why Agent 只能写“回答更智能”,继续缩小任务。

第二步:写完成、失败和边界,15 分钟

至少写出:

  • 一个成功终态;
  • 一个合法拒答或不适用终态;
  • 一个失败终态;
  • 一个需要人工接管的条件;
  • 一个允许动作;
  • 一个禁止动作;
  • 一个需审批动作。

第三步:建立五条控制任务,15 分钟

建议分配:

text
2 条正常任务
1 条边界任务
1 条资料不足任务
1 条高风险或需接管任务

把 Grader 条件写在任务里,但不要把预期答案喂给被测系统。

第四步:运行最简单基线,10 分钟

可以是:

  • 关键词检索;
  • SQL;
  • 正则与模板;
  • 固定 Workflow;
  • 单次模型调用,不给自主工具选择。

记录每条任务的状态和失败原因。

第五步:做一次故意失败,10 分钟

任选一个:

  • 删除必要合同字段;
  • 让允许和禁止动作冲突;
  • 把拒答阈值调到极端;
  • 给 Grader 加一个任务没有说明的隐藏条件;
  • 移除一份必要资料。

如果失败后只能看到“没通过”,却无法知道哪个任务、哪个条件、哪次运行出了问题,证据层还不够。

练习结束时,你应该能用一句话说明:

text
当前控制组在 ____ 条任务上通过 ____;
它失败在 ____;
只有 Agent 能在相同边界与预算下改善 ____ 时,我才会升级。

14. 发布前检查清单

任务

  • 描述的是现实工作,而不是“做一个 Agent”。
  • 输入、成功、拒答、失败和人工接管都能区分。
  • 每个 Grader 条件在任务或策略中有依据。

边界

  • 允许、禁止和需审批没有重叠。
  • 高风险动作由 Runtime 与人强制,不只写在 Prompt。
  • 可信来源有版本、访问控制或来源证据。

证据

  • 有一个不用 Agent 的控制组。
  • 每次运行能定位到任务、版本和失败原因。
  • 至少运行过一个故意失败的反例。
  • 没有把小型任务集的 100% 外推为生产可靠。

升级

  • 能说清固定脚本或 Workflow 的能力上限。
  • Agent 候选版本会在同一任务、边界和预算下比较。
  • 新增复杂度对应一个可测量收益。

结语

Agent 工程的第一步很容易被误认为“选择模型和框架”。

这篇实验得到的结论更朴素:

text
先定义它负责什么
  → 再定义什么算完成与失败
  → 把权限和人工权力写清楚
  → 建立一个最简单控制组
  → 用失败实验检查合同是否真的生效
  → 最后才决定是否需要 Agent

当前 Agent Reliability Lab 的确定性基线在五条教学任务上全部通过。

这个结果不意味着系统已经可靠,也不意味着 Agent 没有价值。它只让下一步问题变得准确:

我们需要加入哪些真实任务,才能证明动态决策、工具使用和恢复能力确实比控制组更好?

这正是第 2 篇《Agent Evals》要解决的问题:扩充任务集,区分 Task、Trial、Grader 与 Trace,并让“新版更聪明”变成可重复比较的证据。

参考资料

官方资料

论文与开源工程