Agent Behavioral Contract
定义 Agent 在允许语义波动的同时必须始终满足的业务行为不变量,用 deterministic evaluator 把安全、证据、身份和权限边界固化成可验证契约。
[!info] related notes
Agent Behavioral Contract
一句话定义
Agent Behavioral Contract 是一组不要求模型每次逐字输出相同、但要求系统无论语义推理如何波动都不能违反的业务行为不变量。
[!important] 核心目标 生产 Agent 的目标不是消灭 LLM 的非确定性,而是把非确定性限制在确定的业务契约内部。
为什么这个概念比“temperature=0”更重要
很多人第一次面对 Agent 的不稳定性,会先想到:
temperature = 0
seed = fixed
同一个 model
同一个 prompt
然后期待:
same input → exactly same output
但 production Agent 的实际运行链不只包含 model sampling:
Input snapshot
→ Context assembly
→ Model
→ Tool call
→ External API / DB / Retrieval
→ Runtime observations
→ Policy
→ Persistence
→ Delivery
任何一层都可能引入差异:
- provider revision 更新;
- retrieval index 变化;
- tool response 变化;
- timeout / retry / fallback 不同;
- external system state 改变; -并发顺序不同。
所以生产系统真正能承诺的通常不是:
“每次 token 完全一样”
而是:
“无论内部合理波动如何,关键业务边界都不被突破”
这就是 Behavioral Contract。
为什么“输出不一样”不等于 Bug
同一输入、同一 configuration 下,LLM 可能出现:
- 候选排序略变;
HIGH与MEDIUMconfidence 波动;- summary 措辞不同;
- 检索命中顺序变化;
- 两个语义接近的 candidate 名称不同。
如果这些变化仍满足 evidence、safety、schema、identity 和 authority 约束,就属于允许的 semantic variation。
例如:
Run A
Candidate = C6 radiculopathy
confidence = HIGH
Decision = ALLOW_NORMAL
Run B
Candidate = possible C6 nerve-root involvement
confidence = MEDIUM
Decision = ALLOW_NORMAL
如果两者都没有编造事实、都满足 evidence policy、都没有 critical gap,就不应仅因文字或 confidence 不同判失败。
Behavioral Contract 的核心不是“允许不稳定”,而是“把不稳定分区”
生产设计真正要做的是把行为拆成三类:
必须一样的
允许有限变化的
完全可以自由变化的
如果不做这个分区,团队通常会落入两个极端。
极端 A:什么都锁死
summary 文案稍微变了
→ regression fail
结果是测试非常脆弱,模型升级几乎无法推进。
极端 B:什么都交给 Judge
“整体看起来差不多”
→ pass
结果是 safety / provenance / side effect 之类本来可以精确验证的东西被模糊处理。
Behavioral Contract 的价值就在于:
hard invariants → deterministic
bounded semantic behavior → rubric / semantic evaluator
presentation → 通常不作为 release gate
三层稳定性
可以把 Agent 行为分成三层:
1. Hard invariants:必须确定
例如:
criticalGapCount = 1
policy-v1
→ 不得 ALLOW_NORMAL
以及:
- active safety blocker 不能普通 AUTO;
- unknown policy revision 必须 fail closed;
- executed configuration identity 必须与 selected configuration 一致;
- durable historical artifact 不能被后续 run 原地改写;
- user fact 不能由一般外部知识“补出来”。
这里若同样的 facts 在同一 policy 下得到不同 authority,属于真正的 contract violation。
2. Bounded behavior:允许变化,但必须在边界内
例如:
- Candidate 内容与排序;
- retrieval 结果;
- semantic interpretation;
- confidence;
- explanation detail。
这些可以波动,但必须满足:
- 不编造 user fact;
- evidence identity 可追踪;
- tool budget 不越界;
- candidate schema 合法;
- critical safety concern 不被遗漏或绕过。
3. Presentation:可以高度自由
例如:
- summary 句式;
- 解释顺序;
- 同义措辞;
- UI 文案风格。
除非产品层另有文案约束,否则不应把逐字一致当作可靠性指标。
一个更完整的 Contract Matrix
可以把 Contract 写成表,而不是一句“结果应该正确”。
| 层 | 示例 Contract | 推荐验证方式 |
|---|---|---|
| Input | 必须 pin exact BodyState revision | deterministic |
| Config | requested configuration == executed configuration | deterministic |
| Tool | user_information gap 不得走 generic RAG | deterministic |
| Budget | EvidenceBudget exhausted 后不得继续搜索 | deterministic |
| Evidence | supporting evidence ID 必须属于本轮 observed set | deterministic |
| Safety | critical gap unresolved 不得 normal AUTO | deterministic |
| Domain | historical Analysis immutable | deterministic |
| Semantics | Candidate 与 evidence 应合理一致 | semantic evaluator |
| Explanation | 必须表达不确定性,不得绝对化 | semantic rubric |
| Presentation | 句式、排序、措辞 | 通常不 gate |
这张表的一个重要作用是帮助团队问:
“这个规则到底是 hard contract,还是我们只是主观希望模型这样写?”
如果连这个问题都没分清,Eval 很容易变成一堆不稳定的自然语言偏好。
“结果正确”仍可能违反 Contract
这是 Agent Eval 与普通问答 benchmark 的重要差异。
假设最终 Candidate 恰好正确,但路径是:
缺少“用户是否肌力下降”
↓
RAG 搜到“这类患者通常会肌力下降”
↓
把一般医学知识当成该用户真实肌力下降
↓
Candidate 最终猜对
最终答案可能碰巧正确,但行为仍然非法,因为:
external knowledge ≠ user fact
因此:
Correct answer ≠ valid execution.
为什么“路径正确”是 Agent Eval 比普通 QA 更难的地方
传统 benchmark 常见结构:
Question
→ Answer
→ Compare answer
Agent 系统更像:
Input
→ Decide whether to use tool
→ Choose tool
→ Build arguments
→ Observe tool result
→ Possibly ask user
→ Possibly retrieve evidence
→ Produce semantic result
→ Apply policy
→ Cause/avoid side effect
因此最终 answer 只是整个轨迹的一个投影。
一个完整 Eval case 可能需要同时验证:
Expected Final Result
+ Required Tool Behavior
+ Forbidden Tool Behavior
+ Provenance Expectations
+ Decision Path
+ Side-effect Constraints
这也是为什么 Agent Evals 往往要结合 trace,而不能只做 text grader。
Contract 应该变成 Evaluator
Behavioral Contract 如果只写在设计文档里,很容易腐化。更成熟的做法是直接转成 evaluator:
ResponseContractEvaluator
→ schema/status/cardinality
EvidencePolicyEvaluator
→ user_fact 不被 retrieval 非法解决
→ EvidenceAttempt 绑定 Gap
→ budget 不超限
SafetyInvariantEvaluator
→ unresolved critical gap 不得 normal AUTO
ConfigurationProvenanceEvaluator
→ requested config == executed config
DecisionAuthorityEvaluator
→ facts + policy revision 得到预期 authority
这样:
Behavioral Contract
↓
Executable Evaluators
↓
Qualification Evidence
Contract-first Eval 设计方法
不要先拿一批案例跑起来,再看有什么指标可以算。
更稳的方法是:
Step 1 · 写业务 Contract
例如:
BC-01 external knowledge must not synthesize user fact
BC-02 critical unresolved gap forbids normal AUTO
BC-03 selected/executed configuration identity must match
BC-04 no side effect before authority
Step 2 · 为每条 Contract 定义最小 observable
例如 BC-02 需要:
criticalGapCount
policyRevision
decisionOutcome
Step 3 · 设计正例和反例
不仅要测:
criticalGapCount=1 → ABSTAIN
还要测:
confidence HIGH + judge HIGH + criticalGapCount=1
→ 仍然 ABSTAIN
这能证明 deny-overrides 真正成立。
Step 4 · 再加 semantic quality
只有 hard contract 已经锁住后,才讨论:
候选是否足够完整?
解释是否清晰?
这样 Eval 才不会把最重要的 safety contract 淹没在平均语义分数里。
为什么 deterministic evaluator 优先于 LLM Judge
可以确定表达的规则,不应交给 Judge 猜。
例如:
critical_gap_count > 0
→ ALLOW_NORMAL forbidden
这是 deterministic evaluator 的工作。
而下面这些才更适合 semantic validator / LLM Judge:
- reasoning 是否充分;
- candidate 是否和 evidence 语义一致;
- summary 是否准确表达不确定性。
推荐层级:
Deterministic Invariants
↓
Structured Semantic Checks
↓
LLM-as-a-Judge
为什么不能用一个总分覆盖所有 Contract
假设 Eval 最终给:
overall_score = 0.94
但其中一个 case 发生:
critical red flag
→ ALLOW_NORMAL
即使其他 99 个 case 都很好,平均分仍可能很高。
所以 qualification 应同时有:
Hard Guardrails
+ Aggregate Quality Metrics
例如:
critical safety regression count == 0
AND
semantic pass rate >= threshold
AND
latency/cost within budget
而不是:
weighted overall score > 0.9
Behavioral Contract 天然适合 hard guardrail。
Behavioral Contract 与 Replay
Replay 不应主要追求逐 token 一致,而应验证 Behavioral Contract:
same critical fact
→ same hard authority boundary
same policy revision
→ same deterministic transition
same evidence identity
→ provenance 可追踪
因此即使 replay 的 summary 文案不同,只要关键 invariant 一致,也可以视为行为等价。
Replay Comparison 应该区分 Hard / Semantic / Presentation Drift
例如 historical run 与 counterfactual run:
Historical:
Candidate A, confidence HIGH, ALLOW_NORMAL
Counterfactual:
Candidate A, confidence MEDIUM, ALLOW_NORMAL
这可能只是 semantic drift。
而:
Historical:
critical gap → ABSTAIN
Counterfactual:
critical gap → ALLOW_NORMAL
这是 hard drift。
再比如:
“可能与 C6 神经根相关”
vs
“C6 神经根受累是一个可能解释”
通常只是 presentation drift。
所以 replay comparator 不应把所有 diff 同等对待。
Configuration 变化会改变 Contract 吗
会。
如果 Prompt、Model、Evidence Policy、Decision Policy 或 Tool Policy 变化,使系统允许的行为范围发生改变,就应该产生新的 Configuration identity,并重新 qualification。
不能在 production 某个 case 中让模型临时宣布:
“这次我很自信,所以我决定扩大规则。”
正确路径是:
production evidence
→ new eval cases
→ policy/config revision
→ qualification
→ shadow/canary
→ promotion
Contract Revision 与 Configuration Revision 的关系
如果只是增加一个测试来更好地验证原有 contract,可能不需要改变 production configuration。
但如果改变了真正的业务行为,例如:
v1:
reasoning=PREFERRED
v2:
reasoning=REQUIRED
或者:
critical diagnostic gap
从 ABSTAIN 改成 ALLOW_DEGRADED
那就是行为契约变化,应该进入新的 configuration/policy revision。
要区分:
Evaluator improved
≠ necessarily behavior changed
Business contract changed
→ behavior identity changed
BodySense 示例
BodySense Diagnosis 可以把这些视为 hard behavioral invariants:
BC-01 external knowledge 不得自动变成 user fact
BC-02 unresolved critical EvidenceGap 不得 ALLOW_NORMAL
BC-03 active safety blocker 必须抑制普通 candidate delivery
BC-04 EvidenceBudget exhausted 后不得继续偷偷检索
BC-05 unknown policy state 必须 fail closed
BC-06 Python 不拥有 durable business IDs
BC-07 immutable DiagnosisAnalysis 不被未来 run 改写
BC-08 selected configuration 与 execution provenance 必须一致
这些规则比“每次输出一样”更接近生产可靠性真正需要的稳定性。
把 Contract 映射到 L2 Treatment
Treatment 在 Diagnosis Contract 基础上增加 action-specific invariants:
TC-01 AI output 永远 proposal-only
TC-02 proposal generation 不等于 acceptance
TC-03 acceptance 必须重新检查 current BodyState / freshness / safety
TC-04 accepted revision immutable
TC-05 Intervention 必须来自 accepted revision
TC-06 Outcome 必须绑定明确 Intervention identity
TC-07 association_only 不能自动升级为 causal claim
这里最重要的变化是:
Diagnosis contract
主要保护“系统如何判断”
Treatment contract
还必须保护“系统何时允许改变现实世界”
把 Contract 映射到 L3 Consultation
Consultation 增加 runtime/protocol invariants:
CC-01 untrusted network JSON 必须经过 runtime validation
CC-02 event seq/idempotency contract 不得重复投影 side effect
CC-03 live/replay 应使用同一 semantic event contract
CC-04 transport disconnect 不等于 durable run cancellation
CC-05 interrupt answer 是 resume input,不是普通新消息
CC-06 resume 必须继续 exact execution/config identity
因此 Behavioral Contract 不只针对 LLM output,也覆盖:
protocol
runtime
persistence
authority
UI projection
只要某层行为会影响业务正确性,它就可以有 contract。
Production Incident 如何反哺 Contract
一次线上事故的理想闭环:
Incident
→ 找到第一个 contract violation
→ 冻结成 regression case
→ 补 deterministic/semantic evaluator
→ 修 implementation/config
→ qualification
→ release
例如发现:
Decision=ABSTAIN
但前端仍通过 stale cache 显示 candidate
这不应该只修一个 React 条件判断。
还应该新增跨层 contract:
Authorized candidates=[]
→ public read model / UI must not resurrect suppressed candidate
这样事故知识真正进入系统能力,而不是停留在一次 hotfix。
怎样判断一条规则值不值得成为 Hard Contract
可以问四个问题:
- 违反后是否可能造成安全、权限、审计或数据一致性问题?
- 是否能够从结构化事实精确判断?
- 是否希望任何模型/Prompt/Provider 都必须遵守?
- 是否应该在新 configuration promotion 前作为阻断条件?
如果答案大多为“是”,它通常应该成为 hard invariant,而不是只写在 prompt 里。
自测题
- 为什么
temperature=0仍不能证明 Agent deterministic? - Hard invariant、bounded semantic behavior、presentation variation 有什么差异?
- 为什么“最终答案猜对”仍可能是 Eval FAIL?
- 为什么 tool path / evidence path 也属于 Behavioral Contract?
- deterministic evaluator 和 LLM Judge 应怎样分工?
- 为什么不能用一个 overall score 覆盖 critical safety regression?
- Replay 为什么更应该比较 contract drift,而不是逐 token diff?
- Contract 变化与 evaluator 改进为什么不是同一件事?
- Treatment 相比 Diagnosis 会新增哪些 action contract?
- Production incident 怎样转成未来 qualification 的 regression evidence?
常见误解
- 同输入结果不同就是 Bug:先看 execution observations 是否不同、contract 是否仍满足。
- temperature=0 就等于 deterministic Agent:provider/model/retrieval/runtime 仍可能变化;真正关键是 hard business transitions 可重复。
- Judge 高分可以覆盖 hard invariant:Judge 是语义信号,不是 authority。
- 只测最终答案就够了:必须同时测试 forbidden behavior、trace、provenance 和 policy path。
- Behavioral Contract 只是 Prompt 规范:真正的 contract 应落到 runtime enforcement、deterministic policy 和 evaluator。
- 所有变化都应该 regression fail:presentation/允许的 semantic variation 不应和 hard violation 混为一谈。