Chapter 7:生产级设计:错误处理与可观测性
7.1 生产级 Agent 系统要求
与开发环境的本质区别
┌─────────────────────────────────────────────────────────────┐ │ 生产环境挑战 │ ├─────────────────────────────────────────────────────────────┤ │ 1. 网络不稳定 → LLM API 调用可能超时/失败 │ │ 2. 流量峰值 → 需要限流和熔断 │ │ 3. 长时运行 → 需要状态持久化和恢复 │ │ 4. 问题定位 → 无日志无法排查 │ │ 5. 性能监控 → 需要指标和告警 │ │ 6. 合规审计 → 需要完整操作记录 │ └─────────────────────────────────────────────────────────────┘
生产级 checklist
错误处理:-超时控制-重试机制-熔断策略-降级方案-死信处理可观测性:-结构化日志-链路追踪-指标监控-告警机制韧性设计:-幂等性-状态持久化-优雅关闭-限流保护
7.2 错误处理策略
错误分类
| 错误类型 | 说明 | 处理策略 |
|---|
| Transient | 临时性错误(网络抖动) | 重试 |
| Permanent | 永久性错误(参数错误) | 记录并返回友好错误 |
| Resource | 资源不足(内存/连接池) | 排队/降级 |
| Timeout | 超时 | 重试或降级 |
| RateLimit | 限流 | 等待后重试 |
统一错误处理框架
@Service@Slf4jpublicclassAgentErrorHandler{publicAssistantMessagehandleAgentError(AgentExecutionExceptione,ErrorContextcontext){log.error("Agent error: type={}, agent={}, message={}",e.getErrorType(),context.getAgentName(),e.getMessage());switch(e.getErrorType()){caseTRANSIENT:returnhandleTransientError(e,context);caseTIMEOUT:returnhandleTimeoutError(e,context);caseRATE_LIMIT:returnhandleRateLimitError(e,context);caseRESOURCE_EXHAUSTED:returnhandleResourceError(e,context);casePERMANENT:default:returnhandlePermanentError(e,context);}}privateAssistantMessagehandleTransientError(AgentExecutionExceptione,ErrorContextcontext){// 检查重试次数if(context.getRetryCount()<MAX_RETRIES){log.info("Retrying transient error, attempt {}",context.getRetryCount()+1);thrownewRetryableException(e);}// 重试耗尽,降级处理log.warn("Max retries exhausted for transient error");returnfallbackResponse(context);}privateAssistantMessagehandleTimeoutError(AgentExecutionExceptione,ErrorContextcontext){log.error("Agent timeout: agent={}, elapsed={}ms",context.getAgentName(),context.getElapsedTime());returnnewAssistantMessage(""" 处理时间较长,请稍后重试。 如果问题持续存在,请联系技术支持。 """);}privateAssistantMessagehandleRateLimitError(AgentExecutionExceptione,ErrorContextcontext){// 获取重试时间DurationretryAfter=context.getRetryAfter();log.warn("Rate limit hit, retry after {} seconds",retryAfter.getSeconds());thrownewRateLimitException(retryAfter);}privateAssistantMessagehandleResourceError(AgentExecutionExceptione,ErrorContextcontext){log.error("Resource exhausted: {}",e.getMessage());returnnewAssistantMessage(""" 系统繁忙,请稍后再试。 感谢您的耐心等待。 """);}privateAssistantMessagehandlePermanentError(AgentExecutionExceptione,ErrorContextcontext){log.error("Permanent error, no retry: {}",e.getMessage());// 记录完整错误用于排查log.error("Error details",Map.of("agent",context.getAgentName(),"input",context.getInput(),"stack",ExceptionUtils.getStackTrace(e)));returnnewAssistantMessage(""" 处理您的请求时遇到问题。 错误已记录,技术团队将尽快处理。 错误参考码:{},请保存此码以便查询。 """.formatted(context.getErrorCode()));}privateAssistantMessagefallbackResponse(ErrorContextcontext){// 降级方案:根据上下文尝试简化处理if(context.hasSimpleFallback()){log.info("Attempting simple fallback");returncontext.getSimpleFallback().process();}returnnewAssistantMessage(""" 抱歉,暂时无法完成您的请求。 请稍后重试或联系客服。 """);}}
7.3 重试机制
重试策略配置
@ConfigurationpublicclassRetryConfig{@BeanpublicRetryTemplateagentRetryTemplate(){ExponentialBackOffPolicybackOff=newExponentialBackOffPolicy();backOff.setInitialInterval(1000);// 初始 1sbackOff.setMultiplier(2.0);// 指数 2xbackOff.setMaxInterval(30000);// 最大 30sbackOff.setMaxElapsedTime(120000);// 总共最多 2minSimpleRetryPolicyretryPolicy=newSimpleRetryPolicy();retryPolicy.setMaxAttempts(5);RetryTemplatetemplate=newRetryTemplate();template.setBackOffPolicy(backOff);template.setRetryPolicy(retryPolicy);returntemplate;}}
Agent 重试集成
@Service@Slf4jpublicclassRetryableAgentService{privatefinalRetryTemplateretryTemplate;privatefinalAgentErrorHandlererrorHandler;publicAssistantMessageexecuteWithRetry(ReactAgentagent,UserMessagemessage,RetryContextretryContext){returnretryTemplate.execute(context->{try{retryContext.setRetryCount(context.getRetryCount());log.info("Agent execution attempt {}",context.getRetryCount()+1);returnagent.call(message);}catch(Exceptione){log.warn("Agent call failed: {}",e.getMessage());thrownewAgentRetryException(e);}},context->{// 恢复策略log.error("All retries exhausted");returnerrorHandler.handleAgentError(context.getLastException(),buildErrorContext(retryContext));});}}
重试最佳实践
// 重试注意事项publicclassRetryBestPractices{// ✅ 应该重试:临时性错误publicbooleanshouldRetry(Exceptione){returneinstanceofTimeoutException||einstanceofSocketTimeoutException||einstanceofServiceUnavailableException||(e