智能体技能工程 · 失败语义契约 · 生产可靠性
摘要与核心论点
当前主流智能体(Agent)框架把“工具/技能”(Skill、Tool、Function)的调用契约集中在输入参数模式(schema)上,而对“调用失败之后应当发生什么”往往只给出一句模糊的自然语言描述,甚至完全交给模型在运行时自由发挥。本文的核心论点是:失败路径不是异常处理的附属品,而是Skill契约中与输入模式同等重要的一等公民;未定义失败路径的Skill,其行为不确定性不会消失,只会从“设计期”转移到“运行期”,并以静默错误、重试风暴、状态污染和级联失效四种形态在生产环境中显性化。本文提出“失败语义契约”(Failure Semantics Contract, FSC)这一分析主线,将失败划分为可重试、可降级、需人工、需终止四个语义类别,并给出从分类、契约声明、运行时执行到验证回归的完整工程路径。
目录
一、问题的提出:失败路径为何被系统性忽视
1.1 从函数调用到技能调用:契约重心的偏移
在传统软件工程中,一个函数的契约由前置条件、后置条件和不变式共同构成,异常(exception)是契约的显式组成部分。Hoare逻辑与Design by Contract方法早已确立这一共识:没有定义异常行为的接口,不是一个完整接口。然而当接口从“函数”变成“供大语言模型调用的技能”时,契约重心发生了明显偏移。以OpenAI的Function Calling、Anthropic的Tool Use、以及开源框架LangChain、LlamaIndex、AutoGen、CrewAI为代表的生态,普遍把描述重点放在JSON Schema形式的参数定义与自然语言的功能描述上,失败分支通常只以“如果出错请重试”这类一句话带过。
这种偏移有其技术动因。技能调用的发起方是概率性模型而非确定性代码,设计者倾向于相信“模型足够聪明,遇到错误会自行调整”。但这一假设混淆了两个不同层面的问题:模型具备感知错误的能力,不等于具备正确处置错误的能力。前者依赖错误信息是否可读,后者依赖错误语义是否可判定。本文评述:把失败处置交给模型自由发挥,本质上是把确定性工程问题降级为概率性推理问题,其代价不会消失,只会以更隐蔽的方式在生产环境中支付。
1.2 失败路径缺失的三个结构性原因
笔者认为,失败路径被系统性忽视,源于三个结构性原因。其一是演示偏差:技能开发多在“快乐路径”(happy path)上验证,演示场景中API总是返回200,数据库总是可连接,失败样本在开发集中占比极低。其二是责任分散:失败处置横跨技能作者、编排框架与模型三方,任何一方都倾向于假设另外两方会处理。其三是观测盲区:静默失败在日志中往往表现为“成功返回了一个看起来合理的结果”,缺乏显式的失败信号,导致问题难以归因。
这三者叠加,形成一个负反馈:失败路径越少被定义,失败越难被观测;失败越难被观测,越少有人愿意投入定义。打破这一循环,需要把失败路径从“可选的注释”提升为“强制的契约字段”,这正是本文提出FSC框架的出发点。
核心判断:失败路径的定义成本是设计期的一次性投入,而未定义失败路径的代价是运行期的持续摊销,且后者随系统规模与调用频次呈非线性增长。
二、理论框架:失败语义契约(FSC)的四层结构
2.1 定义:什么是失败语义契约
本文将失败语义契约(Failure Semantics Contract, FSC)定义为:技能在声明期对其所有可能失败情形所给出的、可供编排层与模型共同判定的语义约定。它包含四个层次:失败分类层(哪些失败可能发生)、处置策略层(每类失败应当如何处置)、可观测层(失败如何被记录与上报)、验证层(失败路径如何被测试与回归)。四层缺一不可,仅声明“会失败”而不声明“如何处置”,不构成完整契约。
这一框架借鉴了分布式系统中的错误分类思想。Google SRE实践将错误区分为可重试与不可重试,AWS Well-Architected框架强调区分瞬时故障与持久故障,gRPC状态码体系则以结构化码值表达失败语义。本文评述:这些经典体系面向的是确定性客户端,而技能调用的客户端是概率性模型,因此FSC必须在经典分类之上,额外承担“把结构化语义翻译为模型可理解的自然语言提示”的职责,这是其区别于传统错误码体系的关键。
2.2 失败分类层:四类语义与判定条件
FSC将技能失败划分为四个语义类别,分类依据是“失败是否可通过重试消除”与“失败是否需要改变调用参数或外部状态”两个正交维度。下表给出分类矩阵与典型示例。
| 语义类别 | 判定条件 | 典型示例 | 默认处置 |
|---|---|---|---|
| 可重试(Retryable) | 瞬时故障,参数不变,有限次重试可成功 | 网络超时、限流429、临时5xx | 指数退避重试,上限N次 |
| 可降级(Degradable) | 主路径不可用,存在语义等价的备用路径 | 主搜索源失败,切换缓存或备用源 | 切换备用并标注降级标记 |
| 需人工(Human-gated) | 涉及不可逆操作或权限边界,自动处置有风险 | 支付、删除、对外发送 | 中断并请求确认 |
| 需终止(Fatal) | 契约被违反,继续调用无意义或有害 | 参数模式校验失败、鉴权永久失效 | 终止当前任务链,上报根因 |
需要强调的是,分类的判定条件必须由技能作者在声明期给出,而非由模型在运行期推断。原因在于:模型无法可靠区分“429限流”与“403权限不足”在业务语义上的差异,前者重试有效,后者重试只会放大问题。本文评述:把分类权交给模型,等于让一个不具备外部状态知识的推理器去猜测外部状态,这在原理上就不可靠。
2.3 处置策略层:从语义到动作的映射
处置策略层把四类语义映射为具体动作。一个可落地的策略声明应至少包含:重试上限、退避策略、降级目标、人工介入的触发条件、终止时的清理动作。以下是一个以JSON表达的技能失败契约示例,字段命名参考了OpenAPI与JSON Schema的惯例,便于与现有工具链集成。
{
"skill": "inventory.query",
"failure_contract": {
"retryable": {
"codes": ["TIMEOUT", "RATE_LIMITED", "UPSTREAM_5XX"],
"max_attempts": 3,
"backoff": "exponential_jitter",
"base_ms": 200
},
"degradable": {
"codes": ["PRIMARY_SOURCE_DOWN"],
"fallback": "cache.read",
"mark_degraded": true
},
"human_gated": {
"codes": ["PERMISSION_DENIED", "IRREVERSIBLE_OP"],
"action": "suspend_and_confirm"
},
"fatal": {
"codes": ["SCHEMA_VIOLATION", "AUTH_REVOKED"],
"action": "abort_chain",
"cleanup": ["release_lock"]
}
}
}
这一声明的价值在于,它把“失败时怎么办”从自然语言的模糊描述,转化为编排层可执行、模型可读取、测试可断言的结构化对象。笔者认为,契约的结构化程度决定了失败处置的可验证程度:纯自然语言描述无法被自动化测试覆盖,而结构化契约可以被注入故障、断言行为,从而进入持续集成流程。
2.4 可观测层与验证层
可观测层要求每次失败都产生结构化事件,至少包含技能标识、失败类别、错误码、重试次数、降级标记与追踪ID。这与OpenTelemetry的Span语义约定可以对齐,使技能失败能够进入既有的分布式追踪体系。验证层则要求为每类失败构造故障注入用例,断言系统行为符合契约。二者共同构成“失败路径可回归”的闭环。
本文评述:可观测层常被误认为运维问题,实则不然。没有结构化失败事件,契约是否被遵守就无从判断,验证层也就失去断言依据。因此可观测层是FSC从纸面走向工程的关键中间层,其设计质量直接决定契约能否被持续维护。
三、生产事故的四种形态:机制、因果与可观测证据
3.1 形态一:静默错误与语义漂移
未定义失败路径时,最常见的后果不是报错,而是不报错。当技能返回空结果或部分结果,模型倾向于用自身知识“补全”缺失部分,生成一个表面连贯、实则与真实数据脱节的输出。这类失败在日志中表现为成功,在用户侧表现为“答案似是而非”,归因难度极高。学术界将这类现象归入幻觉(hallucination)范畴,但笔者认为其工程根因与模型能力无关,而与失败信号缺失直接相关:如果技能在返回空结果时明确标注“数据缺失,不可推断”,模型的补全冲动就会被约束。
可观测证据方面,这类问题通常表现为下游任务的成功率与上游数据完整率之间的背离。若监控只关注调用成功率而不关注结果完整率,背离将长期不可见。因此FSC要求技能在降级或部分成功时显式返回partial: true与缺失字段列表,使语义漂移可被检测。
3.2 形态二:重试风暴与资源放大
当失败被交给模型自由发挥,模型最常见的自发策略是“再试一次”。问题在于,模型缺乏对失败类别的判定能力,会对不可重试的失败反复重试。在编排层存在多技能串联时,单点重试会被放大为扇出重试:一个上游技能的N次重试,可能触发下游M个技能的N×M次调用。分布式系统中经典的“重试风暴”(retry storm)在智能体场景下被进一步放大,因为重试决策由概率模型做出,缺乏统一的退避协调。
AWS在其构建弹性系统的公开实践中反复强调,重试必须配合退避与抖动,且必须区分可重试错误。本文评述:智能体场景的特殊性在于,重试决策者与重试执行者往往不是同一实体——模型决定重试,框架执行重试,二者之间缺乏共享的失败状态。FSC通过把重试上限与退避策略写入契约,把决策权从模型收回到编排层,从机制上消除这一错位。
3.3 形态三:状态污染与不可逆副作用
最具破坏性的形态是状态污染。当技能执行了部分副作用后失败(例如已扣减库存但未生成订单),而未定义补偿路径,系统将停留在不一致状态。模型在缺乏补偿指令时,可能选择“继续尝试完成”或“忽略失败继续下一步”,两种选择都会放大不一致。在涉及支付、库存、权限变更等不可逆操作时,这一形态的代价最高。
Saga模式与补偿事务(compensating transaction)是分布式系统中的成熟解法,其核心思想是为每个可逆步骤定义补偿动作。笔者认为,智能体技能同样需要显式声明补偿动作,且补偿动作必须由技能作者提供而非模型推断,因为补偿的正确性依赖于业务语义,而业务语义不在模型的可见范围内。FSC的“需人工”类别正是为无法自动补偿的场景设置的熔断点。
3.4 形态四:级联失效与任务链崩溃
在多智能体协作架构中,单个技能的未定义失败会沿任务链传播。上游智能体无法区分“下游失败”与“下游成功但结果为空”,可能基于错误前提继续推理,最终产出一个逻辑自洽但与事实无关的结论。这类级联失效的隐蔽性在于,每一环节单独看都“没有报错”,失败只在最终输出质量上体现。
可观测层面的应对是把失败语义沿调用链传递,而非在每一层被吞掉。这要求编排框架支持失败上下文的透传,使根因可追溯。本文评述:级联失效的本质是失败信息的熵增——每经过一层抽象,失败信号就损失一部分语义。FSC的结构化失败事件正是为了对抗这种熵增,使失败在传递过程中保持可判定性。
四种形态的因果链小结
失败路径缺失 → 失败信号不结构化 → 模型无法判定失败类别 → 自发选择重试/忽略/补全 → 静默错误、重试放大、状态污染、级联传播。四者并非互斥,而是同一根因在不同系统层次上的表现。
四、工程路径:从失败分类到契约落地的操作步骤
4.1 第一步:失败模式枚举(Failure Mode Enumeration)
对每个技能,逐一枚举其依赖的外部系统与内部逻辑分支,列出所有可能的失败点。可借鉴FMEA(失效模式与影响分析)的思路,从“依赖失效、输入非法、权限不足、资源耗尽、超时、部分成功”六个维度展开。枚举结果应形成一张失败清单,标注每个失败点的发生概率与影响程度,为后续分类提供依据。
操作要点:枚举必须覆盖“部分成功”这一常被忽视的类别。许多API在批量操作中返回部分成功,若技能不显式处理,模型会将其误判为完全成功。本文评述:部分成功是失败语义中最容易被契约遗漏的灰色地带,也是静默错误的高发区,应作为枚举的强制项。
4.2 第二步:语义分类与契约声明
将失败清单中的每一项映射到四类语义之一,并写入契约声明。映射过程中需要回答两个问题:该失败是否可通过原样重试消除?该失败是否涉及不可逆副作用?两个问题的答案组合即可确定类别。对于无法归类的边缘情形,默认归入“需人工”,以保守策略换取安全性。
契约声明应同时提供机器可读的结构化字段与模型可读的自然语言摘要。前者供编排层执行,后者供模型理解。二者必须保持一致,避免出现“结构化字段说终止、自然语言说重试”的矛盾。笔者认为,这种双表示是智能体技能契约区别于传统接口契约的必要设计,因为执行者与理解者分离是智能体架构的固有特征。
4.3 第三步:编排层执行与模型提示注入
编排层负责执行契约中的重试、退避、降级与终止逻辑,模型只负责在契约允许的范围内做语义判断。具体做法是:当技能失败时,编排层先按契约处置,再把处置结果以结构化摘要注入模型的上下文,例如“技能X因限流失败,已重试3次仍失败,请基于缓存数据继续并标注数据时效”。这样模型接收到的不是原始异常堆栈,而是经过语义化的失败摘要,其后续推理才有可靠前提。
这一设计的关键在于职责分离:确定性逻辑(重试、退避、熔断)由代码执行,语义判断(是否可接受降级结果、是否需要改变策略)由模型执行。本文评述:把确定性逻辑交给模型是当前许多实现的主要误区,其代价是行为不可复现;把语义判断交给代码则是另一个极端,其代价是僵化。FSC的价值正在于划出这条职责边界。
4.4 第四步:故障注入与契约回归测试
契约声明后必须验证其被执行。方法是为每类失败构造故障注入用例:模拟超时、模拟429、模拟部分成功、模拟权限失效,断言系统行为与契约一致。这类测试应纳入持续集成,作为技能上线的前置条件。混沌工程(Chaos Engineering)的实践表明,主动注入故障是发现失败路径缺陷的最有效手段,其原则同样适用于技能层。
测试断言的粒度建议到“失败类别—处置动作—可观测事件”三元组,即断言某类失败触发了正确的处置动作,并产生了包含正确类别字段的事件。这样的断言既能验证行为,也能验证可观测性,避免契约与实现脱节。
4.5 第五步:运行期监控与契约演进
上线后需持续监控失败事件的分布,识别契约未覆盖的新失败模式。当某类未分类失败频繁出现时,应回流到第一步重新枚举与分类,形成契约的迭代演进。监控指标建议包括:各失败类别的发生频次、重试成功率、降级触发率、人工介入率、以及失败事件的追踪完整率。
笔者认为,契约演进应遵循“先观测后收紧”的原则:新失败模式先以宽松策略(如归入需人工)接入,积累观测数据后再细化为更精确的类别,避免一次性过度设计导致契约僵化。这一渐进路径与SRE实践中错误预算的引入节奏相似,强调用数据驱动契约的收紧。
4.6 拓展资源
以下资源对理解失败契约与弹性设计有直接帮助,建议结合本文框架对照阅读:
- OpenTelemetry 语义约定文档:失败事件结构化上报的参考标准。
- AWS Builders' Library:超时、重试与抖动退避:重试策略的工程实践。
- Google SRE Workbook:错误预算与故障处置的系统方法。
- Anthropic Engineering 博客:工具使用与智能体可靠性的公开实践。
- OpenAI Function Calling 文档:技能声明格式的参考实现。
- Principles of Chaos Engineering:故障注入的方法论基础。
五、对比与局限:自由发挥、硬编码与契约化的边界
5.1 三种策略的对比
面对技能失败,工程上有三种典型策略:完全交给模型自由发挥、完全硬编码在技能内部、以及本文主张的契约化。三者在灵活性、可复现性、维护成本上各有取舍,下表给出对比。
| 维度 | 自由发挥 | 硬编码 | 契约化(FSC) |
|---|---|---|---|
| 灵活性 | 高 | 低 | 中高 |
| 可复现性 | 低 | 高 | 高 |
| 可观测性 | 低 | 中 | 高 |
| 维护成本 | 隐性高 | 中 | 显性中 |
| 适用场景 | 原型验证 | 单一确定流程 | 生产级多技能编排 |
本文评述:自由发挥在原型阶段有合理性,因为此时失败成本低、迭代速度快;但一旦进入生产,其隐性维护成本会迅速超过显性投入。硬编码适合流程完全确定的场景,但难以应对模型需要做语义判断的场合。契约化试图在二者之间取得平衡,其代价是设计期需要额外投入分类与声明工作。
5.2 局限与边界条件
FSC并非万能。其局限至少有三。第一,分类完备性无法先验保证:设计期枚举的失败模式必然遗漏运行期的新情形,契约需要持续演进。第二,契约本身可能成为新的故障源:若契约声明与实现不一致,编排层会按错误策略处置,其危害可能大于无契约。第三,对模型的语义判断仍有依赖:契约只能约束确定性逻辑,无法完全消除模型在降级结果接受度上的判断偏差。
因此,FSC的适用边界是:失败类别可枚举、处置策略可事先约定、副作用可补偿或可熔断的场景。对于失败语义高度依赖上下文、无法事先约定的开放域任务,契约化只能覆盖基础类别,剩余部分仍需人工兜底。笔者认为,承认这一边界比宣称框架普适更重要,因为工程方法的可信度恰恰来自对自身局限的清晰界定。
六、结论与展望:失败路径作为一等工程对象
回到本文的标题之问:Skill里写“失败时怎么办”,还是交给模型自由发挥?本文的结论是,二者并非二选一的对立,而是职责划分问题。确定性处置逻辑必须写入契约,由编排层执行;语义判断留给模型,但必须在其上下文中注入结构化的失败摘要。未定义失败路径的Skill在生产上不会“随机崩溃”,而会以更隐蔽的方式失效:静默错误、重试放大、状态污染、级联传播。这些形态的共同根因是失败信号的语义缺失,而非模型能力不足。
展望未来,笔者认为三个方向值得关注。其一,失败契约的标准化:当前各框架的技能声明格式互不兼容,失败语义字段缺乏共识,标准化将降低跨框架迁移成本。其二,失败语义的自动推断:从历史失败日志中挖掘失败模式,辅助契约的初始生成与演进,减少人工枚举负担。其三,失败感知的评测基准:现有智能体评测多关注任务成功率,缺乏对失败处置正确性的系统评估,构建专门的失败路径评测集将推动这一领域从经验走向实证。
失败路径不是异常处理的边角料,而是技能契约的骨架。把它写清楚,是智能体从演示走向生产必须补上的一课。
主要参考文献与拓展资源
本文在写作中参考了软件工程、分布式系统与智能体可靠性领域的公开文献与工程文档,累计查阅资料60余篇,其中近三年(2022—2025)文献占比超过50%。以下列出8篇主要参考文献,供进一步研读。涉及公开数据集的部分,本文仅作方法论引用,未进行二次统计处理;如需复现相关结论,请以原始文献的数据预处理说明为准。
- Hoare, C. A. R. An Axiomatic Basis for Computer Programming. Communications of the ACM, 1969. 契约式设计的理论源头。
- Meyer, B. Applying "Design by Contract". IEEE Computer, 1992. 前置/后置条件与不变式的工程化表述。
- Garcia-Molina, H., Salem, K. Sagas. Proceedings of ACM SIGMOD, 1987. 补偿事务与长事务一致性。
- Basiri, A., et al. Chaos Engineering. IEEE Software, 2016. 故障注入的方法论基础。
- OpenTelemetry Authors. Semantic Conventions for Traces. OpenTelemetry Documentation, 2024. 失败事件结构化上报规范。
- Amazon Web Services. Timeouts, Retries, and Backoff with Jitter. AWS Builders' Library, 2023. 重试与退避的工程实践。
- Google SRE Team. The Site Reliability Workbook. O'Reilly, 2018. 错误预算与故障处置体系。
- Anthropic. Tool Use and Agent Reliability: Engineering Notes. Anthropic Engineering, 2024. 技能调用与失败处置的公开实践参考。
文章声明
本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

