第4章:模块职责划分:业务逻辑层、编排层、基础设施层的异常职责边界
4.1 边界定义
在Agent系统中,异常处理并非由单一模块完成,而是由三个核心层各司其职、协同闭环。边界划分的核心原则是:异常由产生的源头就近捕获,由拥有恢复信息最丰富的层处理,跨层传递时保持语义不丢失。
| 层次 | 核心职责 | 异常范围 | 处理模式 |
|---|---|---|---|
| 业务逻辑层 | 业务规则验证、状态转换、意图判定 | 输入校验失败、业务规则冲突、状态机非法跳转 | 本地重试 → 补偿事务 → 返回业务错误码 |
| 编排层 | 流程调度、任务编排、资源分配、链路追踪 | 子任务超时、依赖断裂、编排逻辑死锁 | 降级链路 → 超时熔断 → 回滚至编排检查点 |
| 基础设施层 | 网络通信、存储I/O、外部API调用、资源计量 | 连接池耗尽、磁盘满、第三方API限流、序列化失败 | 指数退避重试 → 快速失败 → 上报度量指标 |
关键契约: 业务逻辑层不得直接处理基础设施层的连接超时异常;编排层不得在编排脚本中写入硬编码的退避算法。层次间通过异常适配器进行错误码与上下文的转换,保持信任边界的完整性。
4.2 分层职责详述
4.2.1 业务逻辑层
- 输入校验: 对用户输入的Agent指令、参数、上下文进行业务规则校验。校验失败直接返回
BusinessValidationError,不向下传递。 - 状态机保护: 当Agent状态(如“待处理→执行中→完成”)发生非法跳转时,产生
StateTransitionException,并触发状态回滚。 - 补偿事务: 当业务逻辑内的部分步骤失败时,业务逻辑层自行执行补偿(如撤销已完成的子步骤),补偿失败再上报编排层。
4.2.2 编排层
- 流程健康度检测: 监控每个子任务的预期完成时间,超时后判断是等待还是降级。
- 熔断与降级: 当某个下游依赖连续失败超过阈值(如5次),编排层自动切断该依赖调用,执行预设的降级逻辑(如返回缓存结果或模拟响应)。
- 检查点回滚: 维护流程执行的历史快照,异常不可恢复时回滚至最近的检查点,保证状态一致性。
4.2.3 基础设施层
- 资源隔离: 连接池超时、线程池耗尽等异常应在该层内部完成隔离,不泄露资源句柄。
- 重试策略: 支持可配置的指数退避重试(如初始间隔1s,倍率2,最大重试3次),超过次数后直接向编排层返回
ResourceExhaustedError。 - 度量上报: 记录每次失败的类型、频率、流量,供监控系统用于自动弹性和扩缩容决策。
避坑提示: 基础设施层不要自行“消化”异常并返回成功。例如,不能把写入数据库失败后静默返回"OK",这会导致编排层和业务逻辑层产生幻读,引发级联数据不一致。
4.3 数据流/控制流/信任链路
下图描述了当一次Agent任务执行过程中,异常在三个层次间的典型传播路径与信任边界。
信任链路要点: 基础设施层信任编排层不会在短时间内发送太多请求导致过载(编排层负责入口限流);编排层信任业务逻辑层不会产生无限循环的子任务(业务逻辑层负责循环检测)。异常边界明确后,每层只需守住自己的契约,调试时能快速定位肇事层。
4.4 选型权衡
| 选型维度 | 方案A:向上抛原始异常 | 方案B:本层封装为业务异常 | 方案C:本层静默消化 |
|---|---|---|---|
| 调试成本 | 低,保留完整stack trace | 中,需解析封装后的上下文 | 高,异常丢失难以根因定位 |
| 层间耦合 | 高,上层需理解下层异常细节 | 低,通过错误码/合约解耦 | 极低,但隐藏了故障 |
| 恢复能力 | 强,上层可针对性制定恢复策略 | 中,基于错误码触发通用恢复 | 无,本层已放弃恢复 |
| 适用场景 | 开发环境或内部调用链路短的Agent | 生产环境、多团队协作的编排系统 | 非关键路径、可容忍偶发失败的日志/指标 |
选型决策建议: 生产系统推荐方案B,因为它结合了可恢复性与低耦合。方案A可用于开发阶段的断言验证;方案C仅限基础设施层内对“可自愈的瞬间失败”(如偶尔的网络抖动的第一次重试)使用。
4.5 风险点
- 责任链过长: 异常每经过一层都被添加一次包装上下文,最终错误消息膨胀,上层难以提取原始根因。应每层只追加一个关键字段,不要复制整个trace。
- 降级黑盒: 编排层执行降级后,业务逻辑层可能不知道实际响应是降级产生的,导致基于降级数据做出错误业务决策。降级响应中必须附带
x-agent-retryable: true头。 - 资源泄露闭环: 基础设施层发生连接池异常时,若编排层同时发起了降级操作,新的降级路径可能又试图建立新的连接,导致连接池无法恢复。需要引入“熔断后隔离窗口”机制。
4.6 落地步骤
- 步骤1:定义三层之间的异常合约 — 明确错误码前缀(如
BIZ_、ORCH_、INFRA_)和每个错误的payload结构。 - 步骤2:为每层添加异常适配器 — 将原始异常转换为合约定义的格式。
- 步骤3:配置链路超时和重试参数 — 在编排层设置每个子任务的超时阈值,在基础设施层设置连接池的最大等待时间。
- 步骤4:安装熔断器与度量探针 — 在编排层关键路径上插入熔断器,在基础设施层所有外部调用处插入失败计数器。
- 步骤5:集成测试异常场景 — 编写故障注入测试,验证每个异常类型是否在正确的层被捕获且不泄漏。
代码示例:异常适配器核心结构
// 编排层适配器示例
public class OrchestrationExceptionAdapter {
public static OrchestrationError adapt(InfraException ex) {
return new OrchestrationError(
ErrorCode.ORCH_INFRA_DOWN,
"基础设施资源不可用: " + ex.getResourceType(),
ex.getCause(),
ex.isRetryable()
);
}
}
4.7 验收标准
| 验收项 | 验收方法 | 指标 |
|---|---|---|
| 异常不跨层泄漏 | 故障注入后检查上层收到的异常类是否属于合约定义 | 接收到的外部异常类名不包含下层实现类 |
| 降级路径可识别 | 模拟下游故障,检查降级响应中是否包含 x-agent-retryable 头 |
100%的降级响应携带该头 |
| 熔断后恢复时间 | 连续注入10次失败后等待熔断窗口,再注入成功请求 | 熔断窗口内失败直接快速失败,窗口后正常恢复 |
| 检查点回滚完整性 | 在编排脚本中途注入异常,验证状态是否回滚至上一个检查点 | 回滚后状态与检查点完全一致 |
验收自动化: 建议将上述验收标准写成Pytest或JUnit的混沌测试用例,每次CI/CD构建时自动运行,确保边界契约未被破坏。任何对异常处理代码的修改都必须通过所有验收用例才能合并。