第三章 边界定义原则:职责分离、最小泄漏、容错与隔离
Agent系统在运行时面临大量不确定性:外部API超时、模型推理异常、数据格式错误、资源争抢。如果没有为异常处理划定清晰的边界,一个组件的故障会像病毒一样扩散到整个系统。本章围绕三条核心原则——职责分离、最小泄漏、容错与隔离——建立异常处理的边界模型,并给出每一条原则在边界定义、分层职责、数据流/控制流/信任链路中的具体映射。
原则速览:三条原则并非并列选项,而是从谁处理、暴露什么、怎么隔离三个维度构成边界设计的完整约束。
3.1 边界定义与原则对比
边界是异常处理的责任划分点,也是信息传播的截止点。下表对比三条原则在边界上的不同含义:
| 原则 | 边界定义 | 核心约束 | 典型违规 |
|---|---|---|---|
| 职责分离 | 每个组件只负责自己能力范围内的异常 | 异常类型与处理逻辑必须属于同一抽象层 | 业务层直接捕获数据库连接异常 |
| 最小泄漏 | 跨边界传递的异常信息仅包含必要上下文 | 屏蔽内部堆栈、凭证、数据结构细节 | 将Postgres驱动异常原文返回给前端 |
| 容错与隔离 | 边界必须能阻止故障向相邻组件传播 | 超时熔断、舱壁分隔、降级响应 | Agent A的OOM导致Agent B的线程池泄露 |
3.2 分层职责:边界在架构中的落地
典型的Agent系统分为通信层、编排层、能力层三层。每个层级对三条原则的权重不同:
- 通信层:强职责分离(区分协议异常与业务异常)+ 强最小泄漏(对外仅返回标准化错误码)
- 编排层:强容错与隔离(使用舱壁模式隔离不同Agent实例)+ 弱最小泄漏(内部传递可包含traceId)
- 能力层:强职责分离(每个工具只处理自身异常)+ 强容错与隔离(工具超时自动熔断)
实践提示:不要在所有层级强行应用全部三条原则。通信层优先最小泄漏,能力层优先容错隔离,编排层侧重职责分离——分层赋予原则不同的优先级。
3.3 数据流、控制流与信任链路
异常处理边界直接影响三种流的走向。下面用一张SVG图展示三层边界如何协同工作:
上图揭示了三个层次如何处理数据流、控制流与信任链路:
- 数据流:异常信息从能力层向上传递,每经过一层边界就剥离一层细节(最小泄漏);
- 控制流:编排层收到下层异常后,根据职责分离原则决定是重试、降级还是终止;
- 信任链路:上层信任下层已经完成故障隔离,不需要重新检查下层组件的健康状态。
3.4 选型权衡
在真实系统中,三条原则之间存在资源与复杂度的矛盾。以下是常见权衡点:
| 权衡对 | 倾向原则 | 牺牲方向 | 缓解策略 |
|---|---|---|---|
| 职责分离 vs 最少代码 | 职责分离 | 代码量增加20~30% | 使用通用异常处理框架(如Spring @ControllerAdvice) |
| 最小泄漏 vs 调试效率 | 最小泄漏 | 生产故障排查难度上升 | 在内部链路携带traceId,日志级别保留详细堆栈 |
| 容错隔离 vs 资源利用率 | 容错隔离 | 线程池/连接池资源预留,利用率下降 | 动态舱壁大小(根据历史负载调整) |
常见陷阱:不要为了追求“完全隔离”而让每个Agent单独部署进程。在容器化环境下,隔离粒度应控制在线程/协程池+熔断器级别,避免过度分割增加运维成本。
3.5 风险点
- 职责模糊:没有明确“谁负责处理”,导致异常被多级捕获或无处捕获,最终由根节点吞没。
- 信息爆炸:最小泄漏执行不到位,内部异常细节通过RPC泄露到外部客户端,造成安全漏洞和耦合。
- 隔离失败:容错隔离只做了“捕获”没做“熔断”,故障线程持续占满线程池,最后拖垮整个Agent。
- 信任链断裂:上层不信任下层已经隔离,自己又做一次重试/恢复,造成重复工作和状态混乱。
3.6 落地步骤
- 定义三层边界:按通信/编排/能力切割系统,明确每层所属的异常类型命名空间。
- 为每层选择主导原则:通信层选最小泄漏,编排层选职责分离,能力层选容错隔离。
- 编写边界适配器:在层间接口处插入异常转换/清洗中间件。例如业务层抛出的
AgentTimeoutException在通信层转为标准503错误体。 - 实现容错基元:为每个能力组件配置熔断器、舱壁和超时参数。
- 编写边界测试:验证异常是否在正确层级被捕获,错误信息是否满足最小泄漏。
- 灰度与观察:在线上开启增量异常的日志标注,评审边界是否被绕过。
推荐工具:使用
Resilience4j的CircuitBreaker和Bulkhead实现容错隔离;使用Jackson @JsonIgnore或protobuf field mask控制序列化泄露。
3.7 验收标准
| 检查项 | 通过条件 | 测试手段 |
|---|---|---|
| 职责分离 | 所有异常均在最接近产生点的层级被捕获,且不被更上层直接Catch | 代码审查 + 静态分析(如ArchUnit) |
| 最小泄漏 | 外部接口返回的错误体中不包含嵌套异常、堆栈或内部IP/端口 | Mock对外接口,检查返回JSON字段 |
| 容错隔离 | 单个Agent工具的超时或OOM不会导致其他工具的响应延迟超过10% | 混沌工程注入故障,测量p99延迟变化 |
| 信任链路 | 下层熔断后,上层不需要额外的降级逻辑(信任下层已返回标准错误) | 观察熔断期间流量的处理路径日志 |
// 职责分离示例:能力层明确抛出工具异常,编排层不捕获具体技术异常
// 能力层
public class WeatherTool {
public WeatherResult fetch(String city) {
try {
return httpClient.get(url + city);
} catch (IOException e) {
throw new ToolExecutionException("weather", "HTTP failure", e);
}
}
}
// 编排层只处理ToolExecutionException,不关心底层IOException
public class AgentOrchestrator {
public Response handle(Request req) {
try {
return weatherTool.fetch(req.city);
} catch (ToolExecutionException e) {
return Response.error(e.getCode(), "weather service unavailable");
}
}
}
本章核心:边界不是代码中的一道if-else,而是架构中的契约。职责分离、最小泄漏、容错与隔离这三条原则定义了契约的内容——谁处理、暴露什么、怎么隔离。下一阶段将进入具体实现模式(第4章:异常分类与标准化错误码)。