
大模型Agent的系统可靠性从单次调用到多步推理的端到端保障一个 LLM 调用的成功率是 99.9%看起来不错。但一个 Agent 任务包含 8 步推理每一步都调用 LLM——整个任务的成功率就变成了 0.999^8 ≈ 99.2%。这还没算工具调用失败、输出格式错误和其他非 LLM 故障。Agent 的系统可靠性是一个「乘法定律」问题。一、Agent 任务的「可靠性乘法灾难」传统 API 调用链中可靠性保障的核心手段是重试和降级。但这些手段在 Agent 场景下并不直接适用graph LR subgraph Chain[Agent 多步推理链8 步] S1[Step 1\n工具选择\n99.9%] -- S2[Step 2\n代码生成\n99.9%] S2 -- S3[Step 3\n代码执行\n99.5%] S3 -- S4[Step 4\n结果分析\n99.9%] S4 -- S5[Step 5\n信息检索\n99.9%] S5 -- S6[Step 6\n数据整合\n99.9%] S6 -- S7[Step 7\n结论生成\n99.9%] S7 -- S8[Step 8\n格式化输出\n99.9%] end Chain -- Result[端到端成功率\n0.999^6 × 0.995 98.6%] subgraph Problems[Agent 特有的可靠性挑战] P1[部分成功步骤 1~3 正确但步骤 4 失败\n—— 前面消耗的 Token 成本能否挽回] P2[级联失败步骤 2 的一个小错误导致步骤 3~8 全错\n—— 错误在 8 步中被放大 7 倍] P3[状态丢失进程崩溃后已完成的 5 步如何恢复\n—— 从头再来成本巨大] end核心问题是Agent 的每一步都可能失败而且失败的成本是累积的——前 N-1 步消耗的 Token、调用的工具、占用的时间在最后一步失败时会全部浪费。二、任务状态的持久化与断点恢复解决状态丢失的第一原则每一步完成后立即持久化永远不依赖内存状态。package com.example.agent.reliability; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Agent 任务状态管理器。 * 核心设计每一步执行前先持久化状态 * 即使进程崩溃重启后可以从最近完成的步骤继续执行。 */ public class AgentTaskStateManager { // 任务状态存储 —— 生产环境使用 Redis 或数据库 // 每个 step 的执行结果以独立 Key 存储支持部分读取 private final MapString, AgentTask taskStore new ConcurrentHashMap(); private final MapString, ListStepExecution stepStore new ConcurrentHashMap(); /** * 执行一个 Agent 任务带断点恢复能力。 * * param taskId 任务唯一 ID * param plan 执行计划步骤列表 * param executor 步骤执行器 * return 任务最终结果 */ public TaskResult executeWithRecovery( String taskId, ListAgentStep plan, StepExecutor executor) { // 1. 检查是否是恢复执行之前被中断的任务 AgentTask task taskStore.get(taskId); if (task null) { // 新任务持久化任务初始状态 task new AgentTask(taskId, AgentTaskStatus.RUNNING, plan, Instant.now()); taskStore.put(taskId, task); } else if (task.status() AgentTaskStatus.COMPLETED) { // 已完成的任务直接返回结果 return new TaskResult(taskId, AgentTaskStatus.COMPLETED, task.finalOutput()); } // 2. 找出断点 —— 从第一个未成功执行的步骤开始 ListStepExecution history stepStore.getOrDefault(taskId, new ArrayList()); SetInteger completedStepIndexes new HashSet(); for (StepExecution se : history) { if (se.status() StepStatus.SUCCESS) { completedStepIndexes.add(se.stepIndex()); } } // 3. 从断点继续执行 StringBuilder accumulatedContext new StringBuilder(); for (int i 0; i plan.size(); i) { if (completedStepIndexes.contains(i)) { // 已完成的步骤恢复上下文但跳过执行 StepExecution prev getStepExecution(taskId, i); accumulatedContext.append(prev.output()).append(\n); continue; } AgentStep step plan.get(i); // 持久化「即将执行」的状态 StepExecution execution new StepExecution( taskId, i, step.type(), StepStatus.RUNNING, null, Instant.now() ); persistStepExecution(taskId, execution); try { // 执行当前步骤传入之前所有步骤的上下文 String stepOutput executor.execute( step, accumulatedContext.toString(), buildStepTimeout(i, plan.size()) ); // 成功持久化步骤结果 execution new StepExecution( taskId, i, step.type(), StepStatus.SUCCESS, stepOutput, Instant.now() ); persistStepExecution(taskId, execution); accumulatedContext.append(stepOutput).append(\n); } catch (StepTimeoutException e) { // 超时记录但不立即放弃可能可以通过重试恢复 execution new StepExecution( taskId, i, step.type(), StepStatus.TIMEOUT, null, Instant.now() ); persistStepExecution(taskId, execution); // 超时是否致命取决于步骤类型 // - MUST_SUCCEED: 任务标记为失败 // - BEST_EFFORT: 跳过当前步骤继续执行 if (step.failureMode() FailureMode.MUST_SUCCEED) { markTaskFailed(taskId, 步骤 i 超时: step.description()); return new TaskResult(taskId, AgentTaskStatus.FAILED, null); } // BEST_EFFORT 模式下跳过使用空输出继续 accumulatedContext.append([步骤跳过: 超时]).append(\n); } catch (Exception e) { // 执行异常记录并处理 // logger.error(Step {} execution failed for task {}, i, taskId, e); execution new StepExecution( taskId, i, step.type(), StepStatus.FAILED, e.getMessage(), Instant.now() ); persistStepExecution(taskId, execution); if (step.failureMode() FailureMode.MUST_SUCCEED) { markTaskFailed(taskId, 步骤 i 失败: e.getMessage()); return new TaskResult(taskId, AgentTaskStatus.FAILED, null); } } } // 4. 所有步骤完成 String finalOutput accumulatedContext.toString(); task new AgentTask(taskId, AgentTaskStatus.COMPLETED, finalOutput, task.createdAt(), Instant.now()); taskStore.put(taskId, task); return new TaskResult(taskId, AgentTaskStatus.COMPLETED, finalOutput); } /** * 构建步骤级别的超时时间。 * 采用动态超时策略靠前的步骤给更多时间通常是信息收集 * 靠后的步骤给较少时间格式化输出等简单操作。 */ private Duration buildStepTimeout(int stepIndex, int totalSteps) { if (stepIndex totalSteps * 0.3) { return Duration.ofSeconds(120); // 前 30% 步骤2 分钟 } else if (stepIndex totalSteps * 0.7) { return Duration.ofSeconds(60); // 中间 40% 步骤1 分钟 } else { return Duration.ofSeconds(30); // 最后 30% 步骤30 秒 } } private void persistStepExecution(String taskId, StepExecution execution) { stepStore.computeIfAbsent(taskId, k - new ArrayList()).add(execution); } private StepExecution getStepExecution(String taskId, int stepIndex) { return stepStore.getOrDefault(taskId, List.of()).stream() .filter(se - se.stepIndex() stepIndex) .findFirst().orElse(null); } private void markTaskFailed(String taskId, String reason) { taskStore.put(taskId, new AgentTask(taskId, AgentTaskStatus.FAILED, reason, taskStore.get(taskId).createdAt(), Instant.now())); } // --- 内部数据结构 --- record AgentTask(String id, AgentTaskStatus status, Object finalOutput, Instant createdAt, Instant completedAt) { AgentTask(String id, AgentTaskStatus status, ListAgentStep plan, Instant createdAt) { this(id, status, null, createdAt, null); } } enum AgentTaskStatus { RUNNING, COMPLETED, FAILED } enum StepStatus { RUNNING, SUCCESS, FAILED, TIMEOUT } enum FailureMode { MUST_SUCCEED, BEST_EFFORT } record AgentStep(String description, String type, FailureMode failureMode) {} record StepExecution(String taskId, int stepIndex, String stepType, StepStatus status, String output, Instant executedAt) {} record TaskResult(String taskId, AgentTaskStatus status, Object output) {} } interface StepExecutor { String execute(AgentTaskStateManager.AgentStep step, String context, java.time.Duration timeout) throws StepTimeoutException, Exception; } class StepTimeoutException extends Exception { StepTimeoutException(String message) { super(message); } }三、部分成功的补偿机制当任务在中间步骤失败时「从头再来」是最粗暴也最浪费的选择。更好的策略是分层补偿flowchart TB Failure[步骤执行失败] -- Choice{步骤的 FailureMode?} Choice --|MUST_SUCCEED| Retry{可重试?} Choice --|BEST_EFFORT| Skip[跳过当前步骤\n使用空输出或默认值继续] Retry --|是| Backoff[指数退避重试\n(最多 3 次)] Retry --|否| Compensate[启动补偿事务\n(撤销已完成的副作用)] Backoff --|成功| Continue[继续下一步] Backoff --|3 次全部失败| Compensate Compensate --|补偿成功| MarkFailed[任务标记为失败\n释放已占用资源] Compensate --|补偿失败| Escalate[升级到人工处理\n(Human-in-the-Loop)] Skip -- Continue style Escalate fill:#f96,stroke:#333,stroke-width:3px style Compensate fill:#fc6,stroke:#333,stroke-width:2px补偿事务的核心思想Agent 操作的每一步都应当是可逆的或可补偿的。如果 Agent 在步骤 3 发送了一封邮件但在步骤 7 失败了补偿逻辑应该发送一封「抱歉上一封邮件有误」的撤销邮件而不是让用户困惑。/** * 带补偿的回滚框架。 * 每执行一个步骤同时注册该步骤的补偿操作。 * 如果后续步骤失败按逆序执行所有已注册的补偿操作。 */ public class CompensatingTransaction { // 补偿栈后进先出最后成功的步骤最先补偿 private final DequeRunnable compensations new ArrayDeque(); /** * 执行一个带补偿的步骤。 * * param stepName 步骤名称用于日志 * param action 正向操作 * param compensation 补偿回滚操作 */ public void executeStep( String stepName, Runnable action, Runnable compensation) { try { action.run(); // 正向操作成功后将补偿操作入栈 compensations.push(compensation); } catch (Exception e) { // 当前步骤失败触发补偿链 // logger.error(Step {} failed, triggering compensation chain, stepName, e); rollback(); throw new StepFailedException(stepName, e); } } /** * 按逆序执行所有补偿操作LIFO。 * 每个补偿操作失败不会阻止其他补偿的执行。 */ public void rollback() { while (!compensations.isEmpty()) { Runnable compensation compensations.pop(); try { compensation.run(); } catch (Exception e) { // 补偿失败记录并继续执行其他补偿 // 这是最坏情况 —— 需要 Human-in-the-Loop // logger.error(Compensation failed, requires manual intervention, e); } } } }四、Human-in-the-Loop 的触发条件不是所有失败都值得召唤人类。触发 Human-in-the-LoopHITL需要满足特定条件触发条件示例优先级补偿事务失败Agent 发了一封邮件但无法发送撤销邮件P0立即连续 3 次任务失败同一类型的 Agent 任务反复失败P11 小时内置信度低于阈值LLM 输出「我不确定这个答案是否正确」P21 个工作日内涉及资金操作支付、退款、报价等P0立即——强制 HITLP0 级别的 HITL 应当是同步阻塞的Agent 暂停执行等待人类确认后才继续。这意味着你的系统需要一个「等待队列」——Agent 任务被挂起状态为AWAITING_HUMAN通过 Webhook 或轮询获取人类的审批结果。五、总结Agent 系统可靠性的四个核心原则持久化是地基每一步执行前先写状态崩溃恢复从最近成功的步骤继续。内存状态在 Agent 场景下是最大敌人。补偿事务是安全网Agent 操作的每一步都应当注册回滚操作。发送邮件、创建工单、写入数据库——这些有副作用的操作必须有撤销路径。部分成功不等于全部失败BEST_EFFORT 步骤的失败应该被容忍MUST_SUCCEED 步骤的失败应该触发补偿而非静默丢弃。HITL 是最后防线定义清晰的触发条件补偿失败、资金操作、反复失败不要让人类淹没在低价值的审批请求中。衡量 Agent 系统可靠性的两个核心指标任务成功率目标 95%和平均完成步数/总步数比目标 90%。如果一个 Agent 经常在最后几步失败要么是步骤设计不合理把复杂操作放太靠后要么是补偿机制缺失。