Conductor 的 Token 效率:可持久化执行如何让 Agent 崩溃恢复时不再重复付费
【免费下载链接】conductorConductor is an event driven agentic workflow engine providing durable and highly resilient execution engine for applications and AI Agents项目地址: https://gitcode.com/GitHub_Trending/co/conductor
导读:LLM 调用按 token 计费,每一次非必要的重执行都会烧掉已经付过费的 token。Conductor 作为事件驱动的 Agent 工作流引擎,通过在每个步骤持久化工作流与任务状态,保证已完成的 LLM 调用永不重跑——本文基于官方文档 token-efficiency.md 并结合仓库源码,系统讲解崩溃恢复、失败重试、指定任务重跑、循环断点四种场景下的 token 节省原理、真实成本量化,以及背后的任务输出持久化机制,帮助你为 Agent 应用构建"零重复付费"的执行底座。
没有持久化时,一次崩溃的真实成本
假设一个自主 Agent 运行 20 步循环,每轮迭代调用一次 LLM(规划)+ 一个工具(执行)。当执行到第 18 次迭代时进程崩溃:
没有持久化执行(普通框架):Agent 从第 1 次迭代重新开始。第 1~17 次迭代必须全部重跑——17 次 LLM 调用产生与之前完全相同的输出,token 被再次烧掉;工具调用也会重复执行(可能产生重复副作用);用户还要为已完成的工作继续等待。
使用 Conductor:Agent 从第 18 次迭代恢复。第 1~17 次迭代已被持久化——它们的 LLM 输出、工具结果和状态全部在持久化存储中。零 token 浪费、零重复工具调用,Agent 精确地从上次中断的位置继续。
这一对比正是 Conductor 的 持久化执行语义 的核心价值:完成的工作永不丢失(completed work is never lost)。
Token 节省发生在哪里:四种核心场景
1. 崩溃恢复(Crash Recovery)
Conductor 工作流中的每次 LLM 调用在完成时都会被持久化。Prompt、响应、token 用量、模型全部被记录下来(在任务输出中对应tokenUsed、promptTokens、completionTokens三个字段,见 LLMResponse.java 中的常量定义)。当服务器、Worker 或网络发生故障时:
- 已完成的 LLM 调用绝不重新执行,其输出直接从存储中读取;
- 只有正在进行中的那一次调用会被重试——且仅重试这一单个调用;
- 工作流从最后持久化的状态继续执行。
Token 节省量:与崩溃前 Agent 的进度成正比。在第 20 步中的第 18 步崩溃的 Agent,节省 17 次 LLM 调用的 token。
2. 从失败任务重试(Retry from Failed Task)
当工作流失败时(例如 LLM 规划成功但工具调用返回错误),你可以从失败任务处重试(replay and recovery)。Conductor 会复用所有先前已完成任务的输出。
示例:一个 5 任务组成的 Agent 工作流在第 4 个任务(工具执行)失败。任务 1~3 包含两次 LLM 调用,共消耗 8,000 token。从任务 4 重试:
- 任务 1~3不重新执行,它们的输出(包括 LLM 响应)从存储中直接复用;
- 只有任务 4(及其后续任务)重新执行;
- 每次重试节省 8,000 token。
3. 从指定任务重跑(Rerun from a Specific Task)
当你修复了某个任务定义中的 bug,并从该任务重跑(rerun)时,它之前的所有任务都保留各自的持久化输出。上游的 LLM 调用不会重新执行——这非常适合"改一处、验证一处"的调试循环。
4. 循环断点与重试边界(Loop Checkpointing and the Retry Boundary)
Agent 循环(DO_WHILE)对每一次迭代都做断点(checkpoint)。如果基础设施在 Agent 运行到 50 次迭代中的第 48 次时恢复:
- 第 1~47 次迭代连同其全部 LLM 调用和工具结果都已持久化;
- 只有第 48 次迭代重新执行;
- 节省 47 次迭代的 LLM token。
需要特别区分:重试一个失败的DO_WHILE会从第 1 次迭代重新开始该循环的迭代历史。因此要保证工具幂等(idempotent)、为循环设定边界,并保留重启动所需的上下文,让重启是安全的。这正是 持久化执行语义 中"at-least-once 投递"所隐含的工程约束。
真实世界的成本影响
下面是基于典型 LLM 定价的具体估算(原文表格完整引用):
| 场景 | 无持久化 | 使用 Conductor | 节省 |
|---|---|---|---|
| 20 步 Agent,第 18 步崩溃 | 重跑全部 20 步:约 40K token | 从第 18 步恢复:约 4K token | 约 36K token($0.04-$0.40) |
| RAG 流水线在 PDF 生成步骤失败 | 重跑 embedding + LLM:约 12K token | 只重试 PDF 步骤:0 次 LLM 调用 | 约 12K token($0.01-$0.12) |
| 100 次迭代循环,第 95 次崩溃 | 重跑全部 100 次:约 200K token | 从第 95 次恢复:约 10K token | 约 190K token($0.19-$1.90) |
| 带人工审批的 Agent,审批人响应慢 | 进程可能超时并重启 | HUMAN 任务无限期持久化 | 全部上游 token 得以保留 |
以上是单次执行级别的节省。乘以每天数千次执行,成本差异就会非常显著。在规模化场景下——每天数千次 Agent 执行——即使只有 5% 的崩溃/重试率,在没有持久化的情况下也会造成可观的 token 浪费;而使用 Conductor 后,这类浪费趋近于零。
崩溃之外的 Token 节省
持久化执行在以下场景中同样避免 token 浪费:
带人工介入(human-in-the-loop)的长期 Agent。一个 HUMAN 任务可以让工作流暂停数小时甚至数天。没有持久化时,进程可能超时或被杀死,导致整体重启(并重跑所有上游 LLM 调用);而 Conductor 的暂停是持久的——工作流从停止处精确恢复,所有 LLM 输出都被保留。
部署与扩缩容。当你部署新版本的 Worker 或缩容实例时,进行中的工作流得以存活,没有 LLM 调用丢失。没有持久化时,扩缩容事件可能在执行中途杀死进程,浪费迄今消耗的所有 token。
调试与迭代。调试失败的 Agent 时,你可以直接检查每一条 LLM prompt 和响应,而无需重跑 Agent;也可以从某个指定任务重跑来验证修复,而不必重新执行(并重新付费)上游 LLM 调用。
机械原理:LLM 输出是如何被持久化的
Conductor 持久化 LLM 任务输出的方式与持久化任何任务输出完全一致,官方文档将其归纳为四步:
LLM_CHAT_COMPLETE任务被调度,由 Worker(或服务器自身)执行;- 收到 LLM 响应——prompt、补全内容、token 用量、模型、延迟全部被记录;
- 任务进入
COMPLETED,其输出在下一个任务被调度之前写入持久化存储; - 此后若发生任何故障,LLM 输出已经持久化,绝不重新执行。
仓库源码可以印证这一机制:
- 系统任务 Worker LLMWorkers.java 中,
@WorkerTask("LLM_CHAT_COMPLETE")将ChatCompletion请求交给LLMs.chatComplete(...)处理; - 在 LLMHelper.java 的
chatComplete方法中,响应被封装为LLMResponse,其中completionTokens、promptTokens、tokenUsed三个字段来自ChatResponseMetadata.getUsage()——即 provider 返回的真实计费数据; - 同一方法内还会构建一条
TokenUsageLog(包含taskId、api、integrationName、promptTokens、completionTokens、totalTokens),通过tokenUsageLogger输出(默认以 INFO 日志记录,见 LLMs.java 中的默认实现),方便你核算每次调用的成本; - 这些 token 统计最终作为任务输出的一部分(
tokenUsed/promptTokens/completionTokens,见 LLMResponse.java)随任务一起写入持久化存储——这正是"崩溃后读取输出、无需重跑"的数据基础。
这与 Conductor 对所有任务通用的持久化执行语义是同一套模型:每个任务的状态机(SCHEDULED → IN_PROGRESS → COMPLETED,以及FAILED/TIMED_OUT后的重试回环)中,每一次状态迁移都会在采取后续动作之前被持久化。任务执行记录包含状态、输入、输出、时间戳、重试次数和 Worker ID;工作流执行则持久化定义快照、工作流状态、任务队列状态,全部写入配置的持久化存储(Redis、PostgreSQL、MySQL 或 Cassandra),服务器重启后从最后持久化状态恢复。
持久化执行减少重复工作的边界
已完成任务的输出在基础设施恢复、暂停和任务级重试期间始终可用,因此当后续任务失败、Agent 等待审批、或操作员从指定任务重跑时,上游 LLM 调用都不必重复。
但这不是"任何调用都永不重跑"的保证。Conductor 采用 at-least-once 投递:Worker 崩溃但副作用已发生时,任务会被重新投递给其他 Worker 再次执行;重试失败的DO_WHILE会重新开始该循环的迭代历史。因此请让工具幂等,并有意识地选择重试边界。
与此相关的超时与重试参数,可在每个任务的任务定义中配置,它们是持久化语义的调优旋钮:
| 参数 | 控制内容 |
|---|---|
timeoutSeconds | 任务到达终态的最大墙钟时间 |
responseTimeoutSeconds | Worker 状态更新前的最长等待时间,超时则重新入队 |
pollTimeoutSeconds | 已调度任务等待被 poll 的最长时间 |
retryCount | 失败或超时时的重试次数 |
retryLogic | FIXED、EXPONENTIAL_BACKOFF或LINEAR_BACKOFF |
retryDelaySeconds | 重试之间的基础延迟 |
timeoutPolicy | RETRY、TIME_OUT_WF或ALERT_ONLY |
其中responseTimeoutSeconds直接决定了"Worker 轮询到任务但未响应"时任务多久回到SCHEDULED重新投递(对应持久化执行语义中的 redelivery 机制)。
实践建议与下一步
要让 token 节省真正落地,建议在工程上做到三点:一是 Worker 实现幂等,容忍 at-least-once 投递下的重复执行;二是依赖 Conductor 的重试、超时与重新入队机制,不在业务代码里自建重试逻辑;三是在设计DO_WHILE等循环时设置迭代边界并保留重启所需上下文。结合 LLM 编排 中 14+ 个原生 LLM Provider、向量数据库与内容生成任务,以及LLM_CHAT_COMPLETE的webSearch、codeInterpreter、thinkingTokenLimit等能力(配置字段定义见 ChatCompletion.java),你可以构建既强大又省钱的持久化 Agent。
继续深入阅读:
- 持久化执行语义(Durable Execution Semantics) —— 什么会被持久化、什么会被重试、恢复如何影响重复工作,以及完整的失败矩阵、任务状态机与 Replay/Recovery 操作(Restart / Rerun / Retry);
- 构建你的第一个 Agent 工作流图 —— 用 SDK 编写带内置持久化执行的 Agent;
- 持久化自适应图(Durable Adaptive Graphs) —— 构建有界扇出与显式恢复控制的受治理循环;
- LLM 编排 —— 原生 LLM Provider、向量数据库与内容生成。
【免费下载链接】conductorConductor is an event driven agentic workflow engine providing durable and highly resilient execution engine for applications and AI Agents项目地址: https://gitcode.com/GitHub_Trending/co/conductor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考