想象一个很具体的产品请求:用户输入一首歌和一句画面描述,系统先生成脚本,再把它拆成八个镜头; 八个镜头分发到不同的视频模型或 GPU worker,失败的镜头要重试,完成后还要统一调色、配字幕、合成、上传,最后通知用户。 整个过程可能跑二十分钟,也可能因为额度、排队和人工确认拖到几小时。
一、先看一个普通后端会怎样丢掉这支 MV
最直接的实现,是业务 API 收到 HTTP 请求后启动一个 goroutine:先调 LLM,再提交八个镜头,循环查询结果,最后合成。 只要请求和进程都活着,这段代码当然能工作。麻烦发生在它跑到一半的时候。
- 浏览器刷新后,生成任务也许还在跑,但用户已经不知道该去哪里找它。
- pod 重启后,当前镜头、重试次数和等待状态都随进程内存消失。
- 视频服务已经接单、回执却丢失时,后端只看见超时;再次提交可能多付一份钱。
这三个故障表面上分别像前端、进程和网络问题,底层却是同一个缺口:任务进度只存在于这次请求和这个进程里。
多写几个 if err != nil 只能处理函数看得见的错误,不能让一个已经消失的进程继续拥有状态。
二、先把 Temporal 说成人话:任务活着,不要求进程一直活着
一句话定义:Temporal 是一套把长任务的进度记录在进程之外,并让新的执行进程能够接着做的工作流平台。 它不负责生成视频,也不是另一种 Agent 框架;它负责记住“这是哪一个任务、已经完成什么、正在等什么、下一步该做什么”。
先只认识四块积木:
- 任务卡(Workflow Execution):某一次具体任务的持久身份。这里就是“为用户 42 生成这支 MV”,而不是一段抽象代码。
- 事实本(Event History):这张任务卡的事实记录。开始了什么、调度了什么、哪一步完成了,都按顺序写进去。
- 流程规则(Workflow):决定下一步,例如“八个镜头都完成后再合成”。它读历史并作决定,不直接调用外部世界。
- 真实动作(Activity):调用 LLM、视频服务、对象存储或通知接口,并把结果交回任务卡。
把四块积木拼到同一件事上,可以先写出下面这张教学用任务卡。它不是 Temporal SDK 的请求格式,而是帮助我们在后文始终追踪同一个对象:
任务身份:mv-20260710-42
原始输入:song://summer.wav + “霓虹公路,八个镜头”
流程规则:脚本 -> 镜头并发生成 -> 验收 -> 合成 -> 通知
已经确认:脚本存于 object://mv-42/treatment.json
正在等待:scene-03 的 provider job vid-8831
最终产物:尚未产生
用户可见状态:6 / 8 个镜头完成
原始音频、脚本和视频本体放在对象存储;Temporal 保存任务身份和推进事实;前端显示的“6 / 8”则由 Workflow 状态计算得出。 这三类数据不能混成一份“大状态”:大文件不适合塞进历史,前端显示也不能反过来决定流程是否完成。
Workflow 和 Activity 代码都由业务部署的 Worker 进程运行。Worker 不是第五份持久状态,而是随时可以被替换的执行者。 可以把它记成一句更朴素的话:Temporal Service 保存任务卡和事实本;Worker 只是临时来干活的人。 durable 不是“一个 goroutine 活得很久”,而是“goroutine 可以消失,任务身份和已完成事实仍然存在”。 这正是 Workflow Execution 官方概念 所描述的持久执行。
2.1 跟着同一支 MV 从开始跑到结束
- 创建任务:业务 API 用稳定的业务 ID 创建 Workflow Execution,然后就可以把 HTTP 响应还给前端。
- 记录起点:Temporal 先写下“这支 MV 已开始”,再把第一小段决策工作交给 Worker。
- 决定下一步:Worker 运行 Workflow;Workflow 看到还没有脚本,于是安排一个“生成脚本”的 Activity。
- 执行真实动作:一个 Activity Worker 运行 Activity,调用 LLM 或视频 provider;结果返回后被写入历史。
- 等待与接力:把提交与等待拆开后,远端镜头生成期间不必占住 Activity Worker。回调、计时器或下一次轮询到来时,轮询对应队列且代码兼容的 Worker 就能读取历史并接着判断。
- 完成与查询:合成结果写入对象存储,通知 Activity 发出消息;浏览器即使重开,也能用同一个业务 ID 查询这次执行。
这六步就是全文主干。后面的 Task Queue、replay、Signal、heartbeat 和幂等键,都是在 Worker 会消失、外部调用会超时之后, 为这条主干补上的保护机制。
阅读目标。 这篇文章始终跟随同一支 AI MV。读完后,应当能用自己的话解释 Temporal 是什么、四块积木分别做什么、 一次任务怎样按顺序推进、Worker 与事实本各能改变什么、代码升级为什么也要照顾 replay, 以及哪一种外部副作用仍然必须由业务做幂等。
资料范围。 Temporal 的平台行为以官方文档和固定源码快照为准;服务内部的调用过程来自 temporalio/temporal。 对 tRPC-Agent-Go 的判断来自 公开源码快照。 外部视频 provider 怎样去重、取消和回调无法从 Temporal 源码得知,本文只说明接入方必须自己处理的部分。
2.2 三个容易混淆的“前端”和“worker”
主干清楚以后,再消除名字冲突。浏览器前端是用户界面;Temporal 源码里的 Frontend Service 是接收 gRPC API 的集群服务; Temporal Worker 是运行 Workflow 和 Activity 代码的进程。它又不同于 Agent 系统里被称为 worker 的子 Agent。
| 词 | 本文含义 | 它拥有的东西 |
|---|---|---|
| Client / UI | 浏览器、App 或业务 API。 | 创建任务、保存业务 ID、展示状态,不拥有执行内存。 |
| Temporal Service | Frontend、History、Matching 等服务与持久层。 | 执行身份、事件历史、任务队列与计时器。 |
| Temporal Worker | 轮询 Task Queue、运行用户代码的进程。 | 一次短暂的 Workflow Task 或 Activity attempt。 |
| Agent worker | 父 Agent 委派出去的角色或后台 run。 | 某段 Agent 语义,不天然拥有 durable execution。 |
三、一次创建请求怎样变成可恢复的工作流
现在把刚才的任务卡落到实现。创建请求不会把整张教学卡原样发给 Temporal;它先回答三个普通问题:
这是哪个业务任务、它遵循哪套流程规则、哪组 Worker 可以处理它。
SDK 分别用 Workflow ID、Workflow Type、Task Queue 和输入表达这些答案,再调用 StartWorkflowExecution。
Temporal 仓库的 README 也明确指出:这个仓库是
Temporal Server,
Workflow、Activity 和 Worker 应通过语言 SDK 编写。它不是 import 后便自动接管 goroutine 的普通库。
3.1 Frontend 接受的是创建执行,不是占住连接
服务入口
WorkflowHandler.StartWorkflowExecution
会准备请求、解析 namespace,然后把创建请求交给 History Service。源码注释直接说明它会创建
WorkflowExecutionStarted 事件并调度第一个 Workflow Task。
所以 Start 成功返回后,浏览器继续连着并不是执行成立的条件。
这里还要区分业务身份和单次运行:Workflow ID 与 Run ID 分别标识业务流程和具体一次运行。同一 Namespace 内,一个 Workflow ID 同时只能有一个打开的执行; Continue-As-New 或 Workflow Retry 会产生新的 Run ID。重复点击“生成”时,仍在运行的任务由 Conflict Policy 处理, 已关闭任务能否重用 ID 则由 Reuse Policy 与历史保留期约束。稳定业务 ID 很重要,但不是永久去重表。
3.2 History 先落记录,再让 Worker 推进
History Service 为每个 Workflow Execution 维护 mutable state 和追加式 Event History。
仓库的架构说明把核心设计写得很直白:每次执行都有 append-only history,完整状态可通过 replay 重建;
Workflow 代码必须确定且无副作用,Activity 则需要幂等或明确不重试。
参见
docs/architecture/README.md。
下面把同一支 MV 多走几步。字段和不影响这段解释的中间事件经过简化,但参与者、Command 与已接受事件的顺序是关键:
Client -> Temporal Service
StartWorkflowExecution(workflow_id=mv-20260710-42, input_ref=object://mv-42/input.json)
Temporal Service -> Event History
WorkflowExecutionStarted
Workflow Worker -> Temporal Service
Command: ScheduleActivity(write-treatment)
Temporal Service -> Event History
ActivityTaskScheduled
Activity Worker -> Temporal Service
RespondActivityTaskCompleted(result_ref=object://mv-42/treatment.json)
Temporal Service -> Event History
ActivityTaskCompleted(result_ref=object://mv-42/treatment.json)
Workflow Worker -> Temporal Service
Command: ScheduleActivity(submit-scene-03)
Activity Worker -> Temporal Service
RespondActivityTaskCompleted(provider_job_id=vid-8831)
Temporal Service -> Event History
ActivityTaskCompleted(provider_job_id=vid-8831)
Webhook API -> Temporal Service
SignalWorkflowExecution(scene-03-ready, artifact_ref=object://mv-42/scene-03.mp4)
Temporal Service -> Event History
WorkflowExecutionSignaled(scene-03-ready, artifact_ref=object://mv-42/scene-03.mp4)
API 调用与历史事件分别由不同参与者产生:Activity Worker 发送完成回执,服务端的
RespondActivityTaskCompleted 处理逻辑
才追加完成事件并请求下一次 Workflow Task。提交镜头的 Activity 完成,只说明拿到了 provider job ID,不说明视频已经生成。
同样,Signal 被写入历史
只确认消息已送达;Workflow 处理它、校验产物并推进状态仍是后续工作。
Worker 从 Task Queue 取走 Workflow Task,重放 Workflow 函数,直到它遇到需要等待的 Activity、Timer、Signal 或 Child Workflow,
再把“接下来应该做什么”的 Commands 交回服务端。Matching 的
PollWorkflowTaskQueue
展示了 worker 怎样轮询并从 Matching 取得任务。
这里最容易漏掉的区别是:Worker 提议 Command,Temporal Service 接受状态转换并追加事实。 Worker 崩溃时,尚未被服务端接受的提议可以消失;已经进入 Event History 的事实则成为下一位 Worker 的输入。
| 参与者 | 它能读到什么 | 它能改变什么 | 它不能冒充什么 |
|---|---|---|---|
| Client / 业务 API | 业务输入、Workflow ID、查询结果。 | 请求创建、Signal、Update 或取消。 | 不能直接改写 Event History。 |
| Workflow Worker | 历史投影出的 Workflow 状态。 | 返回 Timer、Activity、Child Workflow 等 Commands。 | 不能把本地内存当权威状态。 |
| Activity Worker | 一次 Activity 的输入和 heartbeat details。 | 执行副作用,返回 result、failure 或 heartbeat。 | 不能宣称整个 Workflow 已完成。 |
| Temporal Service | 已持久化的执行状态、队列与计时器。 | 接受事件、调度 Task、驱动恢复。 | 不替业务生成视频或判断内容是否合格。 |
四、Workflow 管控制流,Activity 承担真实世界
为什么不能把“生成镜头、上传文件、发通知”全写在 Workflow 函数里?因为恢复依赖 replay。 同一段 Workflow 代码可能运行多次,它必须在相同历史下产生相同 Commands;读取当前时间、随机数、直接调用视频 API,都会让重放路径漂移。 Temporal SDK 因此把非确定性和副作用推出 Workflow,放进 Activity。
4.1 一支 MV 的每一步应该放在哪里
| 步骤 | 建议实现 | 为什么 |
|---|---|---|
| 拆脚本、决定镜头列表 | LLM Activity 返回结构化镜头表。 | LLM 非确定,结果记录后 replay 不再重复调用。 |
| 提交每个镜头 | 每个镜头一个 Activity 或 Child Workflow。 | 独立重试、限流、取消和观察。 |
| 等待外部生成 | Signal/webhook、异步完成,或短轮询 Activity 配合 Workflow Timer。 | 持久等待不占住 Activity Worker;每次轮询只执行一次查询。 |
| 合成与上传 | 长 Activity,产物写对象存储。 | 大视频不进入 Event History,只保存 URI 与元数据。 |
| 通知用户 | 独立幂等 Activity。 | 合成重试不应重复发送通知。 |
Temporal 官方同样把 media transcoding、LLM call、large download 列为 Activity 的代表场景,并建议把大功能拆成多个 Activity, 以获得更小的失败恢复范围、更清楚的 timeout 和更容易实现的幂等性。 参见 Temporal Activities。
4.2 等待不是让 goroutine 睡着
视频 provider 返回 job ID 后,Workflow 可以等待 Signal、Timer 或 Activity Future。等待状态已经在服务端可重建, 没有 worker 时它也不需要占住一个业务线程。外部 webhook 到达后,业务 API 把结果作为 Signal 写入 Workflow; 用户要求换风格或取消时,也可以通过 Signal、Update 或 cancellation request 改变后续路径。 Query、Signal、Update 分别怎样读取或改变 Workflow,可见 Workflow Message Passing。
五、恢复发生在“最后一个已记录事实”之后
Event History 不保存 worker 的堆栈,而是保存已经被服务端接受的状态转换。 新 worker 取得 Workflow Task 后,从函数开头 replay;SDK 把历史中已有的 Activity 结果、Timer 和 Signal 重新喂给代码, 并检查它产生的 Commands 是否与历史一致。已完成 Activity 的结果直接复用;仍在执行或等待重试的步骤继续按自己的状态推进。
回到八个并行镜头:如果六个已完成、scene-03 正在 provider 排队、scene-08 等待重试,新 Worker 不会把八个镜头全部重新提交, 也不会只恢复一个“下一个步骤”。它复用六份结果,继续等待 scene-03,让 scene-08 按重试策略推进;全部验收通过后才合成。 如果 Activity 已调度但尚未完成,仅 Workflow Worker 的丢失不会要求重新创建那个 Activity。
5.1 浏览器断开、Workflow Worker 崩溃、Activity Worker 崩溃并不相同
| 故障 | Temporal 看见什么 | 恢复方式 |
|---|---|---|
| 浏览器/SSE 断开 | Workflow 没有变化。 | 前端之后用业务 ID 重新查询或订阅。 |
| Workflow Worker 崩溃 | Workflow Task 未完成或超时。 | 另一 worker replay history,再生成 Commands。 |
| Activity Worker 崩溃 | Activity 没有完成回执,最终触发 timeout。 | 按 Retry Policy 重新调度 Activity attempt。 |
| Temporal Service 短暂不可用 | 已提交历史仍在持久层。 | 服务恢复后继续派发;实际保障取决于集群与存储部署。 |
对长 Activity,heartbeat 同时承担存活检测、取消传递和应用层进度 checkpoint。
例如合成 8000 帧时,可以把最近完成的分片写入 heartbeat details;worker 失联后,下一次 attempt 读取这份进度继续。
官方文档说明 Start-To-Close timeout 用于发现 worker crash,heartbeat payload 可交给下一次 attempt:
Detecting Activity Failures。
服务端对应入口也把 heartbeat 的两个用途写在注释里:报告 liveness 与 progress,见
RecordActivityTaskHeartbeat。
heartbeat 是恢复提示,不是每一帧都已经持久化的承诺。SDK 可能合并上报,下一次 attempt 只能读取服务端真正收到的 details; 因而合成器仍要能安全重做最后一个分片。前端所需的实时进度也应另行记录,不能把 heartbeat 当作 Workflow 的业务完成事件。
5.2 进程可以换,旧执行依赖的 Workflow 语义不能突然换
“任意 Worker 都能接手”还有一个前提:它运行的 Workflow 代码必须能解释已有历史。 假设旧历史记录的是“镜头完成后直接合成”,新版本却在同一位置无条件插入“先运行内容审核 Activity”; 旧执行 replay 时,新代码产生的 Command 就可能和历史不一致。进程替换解决的是机器故障,不自动解决不兼容的代码升级。
旧执行的历史:scene-ready -> compose
不安全的直接替换:scene-ready -> moderate -> compose
安全发布要回答:
1. 旧执行继续由兼容代码处理,还是通过版本路由固定在旧 Worker?
2. 新分支是否通过 SDK 的 patch / version 机制显式加入?
3. 发布前能否用真实 Event History 做 replay test?
4. 新版本失败时,旧执行和新执行分别怎样回滚?
Temporal 当前的 Worker Deployments 指南 把 Worker Versioning 作为安全发布新 Workflow 代码的推荐默认方式,并把 patching 作为无法使用版本化部署时的兼容手段。 对持续几十分钟或数小时的 AI 任务,这不是运维附注,而是 durable execution 能否在多次发布后仍可重放的一部分。
六、最危险的缝:外部服务已经成功,回执却丢了
到这里很容易产生一个误读:“用了 Temporal,每一步就 exactly once。”事实不是这样。 Workflow 控制逻辑能通过 history 获得 effectively-once 的执行效果;Activity 与外部系统之间仍然存在经典的不确定窗口。
submit_scene(scene-03)已让视频 provider 创建付费任务。- provider 返回 job ID 之前,worker 或网络断开。
- Temporal 只知道 Activity 没有完成,于是按策略重试。
- 第二次请求若没有同一幂等身份,provider 可能再创建一个任务。
6.1 幂等键必须和逻辑动作绑定
一个实用做法是用 workflowId + sceneId + operation 生成稳定幂等键,并让 provider 或自己的提交表以它去重。
provider job ID 返回后立即作为 Activity result 持久化;后续等待和查询围绕 job ID 进行。
如果 provider 没有幂等 API,就需要在自己的数据库里建立提交记录表,并接受纯客户端无法彻底消除“调用成功但本地未记录”的不确定窗口。
{
"workflow_id": "mv-20260710-42",
"scene_id": "scene-03",
"operation": "submit-video",
"idempotency_key": "mv-20260710-42:scene-03:submit-video"
}
6.2 取消也是协作协议,不是远程 kill
Workflow 收到取消请求后,可以取消还未开始的步骤,并向正在运行的 Activity 传递 cancellation。 本文这种远端长 Activity 通过 heartbeat 从服务端接收取消;收到后,业务代码还必须检查 context 并及时退出。仅轮询本地 context 不能替代 heartbeat,SDK 的 heartbeat 节流也可能推迟取消送达。外部 provider 还必须提供 cancel API,业务 Activity 才能真正终止远端 GPU 任务。 因此“Temporal 中显示 Canceled”与“所有外部算力立刻停止”不能画等号。
6.3 Provider 说完成、Workflow 结束、产品可交付是三个状态
另一个常见误读是把外部 provider 的 succeeded 直接当成产品完成。
回调只能证明 provider 声称任务结束;业务还要确认对象可读、格式与时长正确、镜头清单完整、取消没有抢先发生,并把通过验收的产物引用写回 Workflow。
Workflow 完成以后,产品层才应把这支 MV 标成可下载或可发布,并决定何时通知用户。
provider produced
-> Activity verifies artifact and cost
-> Temporal records artifact_ref
-> Workflow satisfies all completion conditions
-> product marks the MV deliverable
-> notification is sent idempotently
任何一道验收门失败
-> 不宣称可交付
-> 重试、补偿或进入人工处理
这样就把“外部产出了文件”“流程有了完成证据”“产品允许交付”分开了。 Temporal 能保存并恢复这组检查步骤,但视频质量标准、预算上限和最终发布权限仍由业务定义。
七、tRPC-Agent-Go 运行 Agent,Temporal 让整次任务活过 worker
对 Agent 框架而言,Temporal 不是另一套 GraphAgent。两者回答的问题不同:tRPC-Agent-Go 负责模型、工具、子 Agent、事件流和图节点怎样执行; Temporal 负责在进程重启后保留一次长任务的身份、状态、重试、计时与消息,并重新调度任务,由轮询对应队列的兼容 Worker 处理。
7.1 tRPC-Agent-Go 已经有哪些接近的积木
先界定固定源码快照里的真实状态:agent/taskrun 定义了可替换的 Controller 接口,
仓库随附并被示例采用的是 inprocess.Service;多节点 durable controller 需要产品另行实现,
不能因为接口已经存在就认为主路径已经分布式。Graph checkpoint 与 detached cancel 已实现,但各自只处理下面表中的特定情况。
| 能力 | 快照中的状态 | 当前能解决什么 | 还没有自动解决什么 |
|---|---|---|---|
WithDetachedCancel | 已实现的 RunOption。 | 父请求 cancel 后,当前进程里的 run 可以继续。 | 进程重启和多节点恢复。 |
| Dynamic Workflow | 已实现,仍为前台一次性执行;提供本地与 sandbox Runtime。 | 临时 Python 编排多个 Agent,可隔离 guest 进程。 | 不保存可跨进程恢复的执行状态。 |
| Graph checkpoint | 已实现的 checkpoint / resume 机制。 | 保存 state、frontier,支持显式 resume 和 time travel。 | 谁发现执行进程死亡、谁取得任务并恢复、外部 Activity 怎样重试。 |
taskrun.Controller | 接口已实现;随附 controller 是 in-process。 | run ID、status、wait、cancel、child session 的控制面。 | 分布式存储、队列、lease 与跨节点接管由产品实现。 |
这些结论都能从源码直接验证。WithDetachedCancel
只改变 parent context 的取消传播;Dynamic Workflow 文档仍将执行限定为
“前台、一次性执行”;
Graph 文档把 checkpoint 描述为
显式恢复与 time travel 的基础;
taskrun 文档要求多节点实现基于
外部存储、队列、lease 与跨节点取消;
内置 FileStore 加载到未结束 run 时,
normalizeLoadedRuns
会把它标记为被前一次 runtime restart 中断,而不是继续执行。
例如,当前源码的 SandboxRunner.ExecuteWorkflow
会为一次调用启动新的受隔离 Python guest,并把可选 timeout 的 context 传给 guest 与宿主回调。
这改善了生成代码的执行隔离,却没有把 Python 控制流变成可重放历史。Session 中保存了子 Agent 事件,也不代表进程重启后能接着执行 Python 的下一行;
已经发生的外部副作用也不会自动回滚。
把“脚本隔离”“会话持久化”和“执行恢复”分开,才能判断还需要补哪一层。
7.2 两种接法的取舍
粗粒度接法是把一次完整 runner.Run 作为 Activity。改造小,但 worker 在 Agent 运行中途崩溃时,整个 invocation 可能重试,
LLM 与工具副作用需要更强幂等。细粒度接法则把 LLM call、tool call、视频提交和人工等待分别变成 Activity 或 Child Workflow;
恢复点和可观测性更好,但会把 Agent loop 改造成 durable state machine,接入成本明显更高。
把同一个 scene-03 放进两种方案,才能看清 worker 崩溃后哪些调用会重做。下面是设计对比,不是当前 tRPC-Agent-Go 已内置的 Temporal adapter:
| 接法 | 代表性持久单位 | Temporal 记录什么 | Worker 中途崩溃后 |
|---|---|---|---|
| 粗粒度 | 整个 Agent run:run ID、输入引用、幂等作用域。 | 一次 Activity 的 scheduled / completed,以及最终产物引用。 | 最后结果未落历史时,整个 runner.Run attempt 可能重做。 |
| 细粒度 | 一个逻辑步骤:scene ID、tool/model 操作、稳定副作用键。 | 每个步骤的 result reference、等待点和完成事实。 | 复用已记录步骤的结果,继续尚未完成的分支;Agent loop 必须显式建模成可重放状态机。 |
因此更稳妥的职责划分不是让 Agent 框架重造 Temporal,而是保留一个可替换的 durable controller 接口: 简单部署使用进程内 taskrun;需要跨节点恢复时接 Temporal、云状态机或业务自己的调度平台。
八、业界没有唯一答案,先按恢复压力选层级
Temporal 是成熟的 durable execution 平台,源自 Uber Cadence;但“长任务”并不自动意味着必须使用 Temporal。 如果流程只有一个可幂等的异步视频 API,数据库任务表加消息队列可能已经足够;如果主要压力是 Kubernetes GPU DAG,Argo Workflows 更贴近算力调度; 如果团队完全运行在 AWS,Step Functions 能减少自建控制面的运维。
| 方案 | 更适合的压力 | 主要代价 |
|---|---|---|
| Temporal / Cadence | 代码式长流程、Signal/HITL、复杂恢复、跨服务编排。 | 确定性约束、worker 版本治理、集群或 Cloud 成本。 |
| Restate / DBOS / Inngest | 希望以更轻的服务或 Postgres/step 模型获得 durable execution。 | 生态、语言与部署模型各不相同,需要逐项验证所需能力。 |
| AWS Step Functions / Azure Durable Functions | 深度绑定云服务,优先托管运维。 | 云绑定、状态机与费用模型。 |
| Argo / Airflow / Prefect / Dagster | GPU、数据、媒体、ML 批处理 DAG 与调度。 | 交互式消息和应用级长事务不是它们共同的强项。 |
| Queue + DB | 流程短、状态少、团队能自己维护重试与补偿。 | 取消、超时、幂等、可观测和孤儿恢复都要自己补。 |
可直接对照的官方资料包括 Restate、 DBOS、 Inngest、 AWS Step Functions 和 Azure Durable Functions。 它们共同承认的问题是状态、checkpoint、retry 和 recovery;差别在编程模型、部署面与谁来托管执行引擎。
九、把这支 AI MV 压缩成几条可迁移规则
| 如果状态属于 | 应放在哪里 | 保护的 invariant |
|---|---|---|
| 浏览器展示 | 业务 API、可重连事件流、查询接口。 | 断线不改变执行状态。 |
| 流程控制 | Workflow state 与 Event History。 | 进程死亡后可以重建下一步。 |
| 外部副作用 | Activity + 幂等键 + provider job 记录。 | 重试不会静默制造重复成本。 |
| 长步骤进度 | Heartbeat checkpoint 或外部进度表。 | 失败恢复不必从零开始。 |
| 视频与音频产物 | 对象存储,只在 history 保存引用。 | 工作流历史保持小而可重放。 |
| Workflow 代码版本 | Worker Deployment / patch 路由与 replay tests。 | 发布后旧执行仍能解释自己的历史。 |
| 产品可交付状态 | 业务验收记录与发布权限。 | provider 成功不被误当成用户结果已经可用。 |
| Agent 节点内部状态 | tRPC-Agent-Go session/graph/checkpoint。 | Agent runtime 处理单次推理,Temporal 处理跨进程恢复。 |
所以,“在 worker 里面搭流程”和“让流程活过 worker”不是同一件事。 前者决定进程还在时怎样调用模型、工具和子 Agent;后者决定进程消失后,系统怎样知道已经做过什么、下一步该做什么。 Temporal 的价值正是在这里:它把流程从一段短命控制流,变成一个由持久历史驱动、可被新 worker 接手的执行身份。
但真正可靠的 AI 视频系统仍然要组合多种能力:durable orchestrator 保存任务身份并安排恢复,GPU/Kubernetes 平台调度算力, 对象存储保存大产物,Agent runtime 运行推理,provider 的去重 API 与业务提交记录避免重复付费。 只有每项工作由谁执行、记录写在哪里都说清楚,“前端挂了,后端还在跑”才会从一句模糊需求变成可以验证的系统行为。