---
title: "Dex vs. 其他引擎"
description: "Dex 与基于重放的 workflow 引擎、基于日志的 durable function 运行时、基于数据库的 workflow 库、step function 服务和 Agent 框架 checkpoint 之间的对应关系，以及何时其他工具更合适。"
canonical_url: "https://docs.superdurable.io/zh-Hans/intro/compared-to-durable-execution/"
source: "docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/intro/compared-to-durable-execution.mdx"
locale: "zh-Hans"
language: "python"
---

> 供 AI Agent 使用的全部页面索引：https://docs.superdurable.io/zh-Hans/llms.txt

# Dex vs. 其他引擎

本页代码为 Python。其他语言：[Go](https://docs.superdurable.io/zh-Hans/intro/compared-to-durable-execution.go.md) · [Java](https://docs.superdurable.io/zh-Hans/intro/compared-to-durable-execution.java.md) · [TypeScript](https://docs.superdurable.io/zh-Hans/intro/compared-to-durable-execution.typescript.md) · [Rust](https://docs.superdurable.io/zh-Hans/intro/compared-to-durable-execution.rust.md)

Dex 和其他 durable execution 工具的目标相同：让流程在崩溃、重启和长时间等待之后继续运行。本页把每类工具的术语对应到 Dex，说明设计上的不同之处，以及何时其他工具更合适。

**从 Temporal 过来？** 按这个顺序读：

1. 本页的[主要区别](https://docs.superdurable.io/zh-Hans/intro/compared-to-durable-execution.md#the-main-difference-an-explicit-step-graph-not-replayed-code)和[确定性要求转移到了哪里](https://docs.superdurable.io/zh-Hans/intro/compared-to-durable-execution.md#determinism-moved-it-did-not-vanish)。
2. [Step](https://docs.superdurable.io/zh-Hans/concepts/step.md)，了解保证和默认值。
3. [Step 的进阶保证](https://docs.superdurable.io/zh-Hans/concepts/step/advanced-guarantees.md)，了解重新投递、结果未知，以及 SYNC 和 ASYNC Step。
4. [故障转移和部署](https://docs.superdurable.io/zh-Hans/concepts/failover-and-deploys.md)，了解哪些会重新运行。
5. [修改 Flow 代码](https://docs.superdurable.io/zh-Hans/concepts/change-flow-code.md)，了解不依赖重放的版本管理。
6. [Attribute 锁](https://docs.superdurable.io/zh-Hans/concepts/primitives/attribute.md#locking)，了解并行 Step 共享的状态。
7. [Client](https://docs.superdurable.io/zh-Hans/concepts/client.md)，了解怎样启动 Flow 和发送命令。
8. [性能](https://docs.superdurable.io/zh-Hans/intro/performance.md)和[限制](https://docs.superdurable.io/zh-Hans/intro/limits.md)。

<a id="what-they-share"></a>

## 共同点

本页的每种工具都在流程之外记录流程的进度。崩溃之后，流程从这份记录继续。这些引擎、运行时和库还都提供持久化 timer、等待外部事件和重试。Agent 框架的 checkpointer 覆盖的范围更小；[它的小节](https://docs.superdurable.io/zh-Hans/intro/compared-to-durable-execution.md#agent-framework-checkpoints)说明它把哪些事留给你。

<a id="the-main-difference-an-explicit-step-graph-not-replayed-code"></a>

## 主要区别：显式的 Step 图取代重放代码

大多数 durable execution 工具让你把流程写成一个函数，再通过重放这个函数重建它的状态。Dex 则记录 Step 的结果。一个 Flow 是一组 Step。每个 Step 返回一个决定，指明接下来运行哪些 Step，或者完成 Flow，Dex 把结果和这个决定一起记录下来。

- **得到什么。** Step 代码就是普通代码。它可以读取时钟、使用随机数、调用任何库。修改代码永远不会让正在运行的 Flow 发生重放或非确定性错误。状态保存在 Attribute 里，而不是事件历史里。
- **代价是什么。** 你要把控制流建模成 Step 和决定。循环是一个转到自己的 Step，分支是一个有两个目标的决定。

<a id="determinism-moved-it-did-not-vanish"></a>

### 确定性要求转移到了哪里

一条规则取代了重放规则。Step 可能运行不止一次，所以每个外部效果只能依赖 Dex 传给这个 Step 的内容；见[幂等键](https://docs.superdurable.io/zh-Hans/concepts/step.md#idempotency-keys)。本页的每种工具都把同样的责任留给你，[故障证明](https://docs.superdurable.io/zh-Hans/correctness-and-expressiveness/failure-transparency-proof.md)也以此为前提。时钟和随机值可以用在 Step 的决定和保存的数据里，但不要用在幂等键里，也不要用在重新运行时必须完全一致的效果里。

<a id="concurrency-defaults"></a>

### 并发的默认行为

Dex 在不同的 Worker 上并行运行同一个 Flow 的多个 Step，RPC 也可以和它们同时运行。它们共享 Flow 的 Attribute，由你选择哪些 Attribute 需要[锁](https://docs.superdurable.io/zh-Hans/concepts/primitives/attribute.md#locking)。Temporal workflow 在单线程上运行，Restate virtual object 每个 key 只允许一个独占 handler，所以两者默认都只有一个写入方。

<a id="the-same-process-in-both-models"></a>

### 同一个流程的两种写法

一个提醒循环每隔一段时间发送一次提醒，直到对方选择退出。在 Temporal 里，循环是由引擎重放的普通代码。下面这段代码只是示意，不来自 Dex 示例：

```python
@workflow.defn
class ReminderWorkflow:
    def __init__(self) -> None:
        self.opted_out = False

    @workflow.signal
    def opt_out(self) -> None:
        self.opted_out = True

    @workflow.run
    async def run(self) -> None:
        while True:
            try:
                await workflow.wait_condition(
                    lambda: self.opted_out, timeout=REMINDER_INTERVAL
                )
                return
            except asyncio.TimeoutError:
                await workflow.execute_activity(
                    send_reminder, start_to_close_timeout=timedelta(seconds=30)
                )
```

在 Dex 里，循环是一个转到自己的 Step。它等待 Timer 或退出 Channel，再由它的决定选择接下来做什么。下面的摘录来自可运行的 [提醒](https://docs.superdurable.io/zh-Hans/design-patterns/durable-timer/reminder.md) 模式：

**Python**

```python
class ReminderStep(Step[None]):
    def __init__(
        self,
        service: ServiceDependency,
        opt_out: Channel[None],
    ) -> None:
        self.service = service
        self.opt_out = opt_out

    def wait_for(self, context: Context, input: None) -> Wait:
        return Wait.any_of(
            Timer.by_duration(REMINDER_INTERVAL),
            self.opt_out.for_one(),
        )

    def execute(self, context: Context, input: None) -> StepDecision:
        if not context.has_timer_fired():
            return graceful_complete()
        self.service.send_email("Reminder: please respond", "Hello, ...")
        return go_to(ReminderStep, None)
```

例子: [examples/python/dex_examples/patterns/reminders/reminder_flow.py](https://github.com/superdurable/dex/tree/main/examples/python/dex_examples/patterns/reminders/reminder_flow.py)

提醒不带幂等键，因为多发一次提醒没有坏处。在 Rust 里，`has_any_timer_fired` 询问这个 Wait 中是否有任意一个 Timer 已触发。Temporal 版本从上往下读，靠重放重建。Dex 版本给每个状态命名，由 Dex 保存等待和决定。

如果你了解 iWF：Dex 由它发展而来。Dex 的 Step 就是 iWF 的 State，`WaitFor` 就是 `WaitUntil`。

<a id="replay-based-workflow-engines"></a>

## 基于重放的 workflow 引擎

Temporal 和 Cadence 是最知名的例子。Worker 根据记录的事件历史重放 workflow 函数，以重建它的状态。副作用放在 activity 里，workflow 代码只能通过确定性的 API 读取时间或随机数。

| Temporal 或 Cadence 术语            | 最接近的 Dex 术语                                                                                                                                                   |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Workflow type、Workflow ID、Run ID | Flow type、Flow ID、Run ID                                                                                                                                      |
| Activity                         | Step `Execute` 里的工作，它是一个重试单元。每个持久化调用都放进自己的 Step。SYNC Step 要等两次对象存储提交；延迟见[性能](https://docs.superdurable.io/zh-Hans/intro/performance.md)。                      |
| activity 之间的 workflow 代码         | Step 的决定：`GoTo`、`GoToMany`、`GracefulComplete`、`DeadEnd`                                                                                                       |
| Signal                           | Channel 消息。FlowService `PublishToChannel` 在没有 Worker 在线时也能保存它；见[发布消息](https://docs.superdurable.io/zh-Hans/concepts/primitives/channel.md#publish-messages)。  |
| Query                            | 只读 RPC，或者从 Client 直接读取 Attribute                                                                                                                              |
| Update                           | 写入 Attribute 或发布到 Channel 的 RPC。它需要在 10 秒内有 Worker 接手，并且没有按 update ID 去重。                                                                                     |
| Timer 或 sleep                    | Step `WaitFor` 里的 Timer 条件                                                                                                                                    |
| Child workflow                   | SubFlow                                                                                                                                                       |
| Task queue 和 sticky queue        | Flow type 队列，带尽力而为的 sticky 路由。要把工作交给单独的 Worker 池，就给它单独的 Flow type。                                                                                            |
| Search attributes                | 建了索引的 Attribute                                                                                                                                               |
| Reset                            | Time Travel，保留同一个 Run ID                                                                                                                                      |
| Continue-As-New                  | 用不到。Dex 为每个 run 保留一个最新历史事件的窗口，并清理更早的事件；见 [History window](https://docs.superdurable.io/zh-Hans/reference/server/configuration.md#history-window)（英文）。         |
| Local activity                   | 最接近的是 **ASYNC** Step，它在结果保存之前就继续往下走，如果 server 节点先出故障，它会重新运行。                                                                                                  |
| Cancellation scope               | RPC 按 Step 类型取消正在运行的 Step，`StopFlow` 停止整个 Flow。取消是协作式的；见[取消正在运行的 Step](https://docs.superdurable.io/zh-Hans/concepts/primitives/rpc.md#cancel-running-steps)。 |
| Schedule                         | 一个在 Timer 上循环的 Flow；见 [Cron 定时调度](https://docs.superdurable.io/zh-Hans/design-patterns/durable-timer/cron.md)。                                                |

**相同之处。** 每个 Flow ID 只有一个活跃的 run，并有 ID 复用策略。两者都使用 pull 模式的 Worker；持久化 timer 和等待都不占用 Worker；每个工作单元有自己的重试策略。

**区别。**

- Worker 读取保存的状态，而不是重放历史，所以它用不到历史缓存。见 [Routing and availability](https://docs.superdurable.io/zh-Hans/reference/server/workers.md#routing-and-availability)（英文）。
- 版本管理靠保留旧的 Step 类型并做增量修改，不需要 patch 标记。[修改 Flow 代码](https://docs.superdurable.io/zh-Hans/concepts/change-flow-code.md#versioning-flow-code)说明规则，[部署前检查](https://docs.superdurable.io/zh-Hans/concepts/change-flow-code.md#check-before-you-deploy)给出手动兼容性检查。
- Dex 的 Step 在不同的 Worker 上并行运行。Temporal 的单线程仍然会在 await 点交错运行 handler，所以差距比“一个写入方”听起来要小，但差距确实存在。
- Step 方法的重试复用第一次尝试的状态快照。activity 的重试会读取最新数据。
- 重试的 RPC 或重复的 Channel 发布会再次运行；Temporal 会丢弃重复的 update，并尽力丢弃重复的 signal。见[限制](https://docs.superdurable.io/zh-Hans/intro/limits.md#requests-that-can-repeat)。
- Client 根据在自己语言里注册的 Flow 定义来启动 Flow。其他语言的服务通过 gRPC 调用 FlowService `StartFlow`；见[从 API 服务启动 Flow](https://docs.superdurable.io/zh-Hans/concepts/client.md#start-from-an-api-service)。
- server 故障转移会重新运行每个进行中的 Step 尝试。见[故障转移和部署](https://docs.superdurable.io/zh-Hans/concepts/failover-and-deploys.md)。
- Dex Server 是一个运行在对象存储上的二进制文件，有一个 active 节点和若干 standby。Temporal 由多个服务组成，运行在数据库上。

<a id="journal-based-durable-function-runtimes"></a>

## 基于日志的 durable function 运行时

Restate 是一个例子。handler 通过运行时 context 执行每个副作用，运行时把结果记录在日志（journal）里。失败之后，handler 再次运行，已记录的结果直接返回。带 key 的 virtual object 保存状态，每个 key 只有一个写入方。

| Restate 术语                  | 最接近的 Dex 术语                                                                      |
| --------------------------- | -------------------------------------------------------------------------------- |
| Workflow                    | Flow                                                                             |
| Service handler             | 一个没有 Attribute 的短 Flow。Service handler 是一次持久化、记录了日志的调用，有重试和幂等键，但没有 key-value 状态。 |
| 记录日志的操作（`ctx.run`）          | Step `Execute`                                                                   |
| Virtual object 及其 key       | 每个 key 一个长期运行的 Flow，用 key 作为 Flow ID。已关闭的 run 在保留期后被删除，而 virtual object 会一直存在。   |
| Object state                | Attribute                                                                        |
| Awakeable 或 durable promise | Step 等待的 Channel 消息                                                              |
| Durable sleep               | Timer                                                                            |
| Shared handler              | 不写入 Attribute 的 RPC                                                              |

**相同之处。** 崩溃后复用已记录的副作用结果。状态按你选择的 ID 分 key 保存。

**区别。**

- 日志调用之间的 handler 代码必须是确定性的，因为它会再次运行。Dex 每次都重新运行 Step 代码，所以修改代码不会破坏正在运行的 Flow。
- virtual object 每个 key 只允许一个写入方。同一个 Flow 的 Step 和 RPC 可以同时运行，Attribute 锁按需启用。
- Restate 按幂等键对调用去重。Dex 按 Flow ID 和 request ID 对 Flow 启动去重，目前重复的 Channel 发布和 RPC 会再次运行。
- Restate 的 UI 和 CLI 显示一次调用的日志。Dex 把每个 Step 的输入、输出和错误随 run 一起保存到保留期结束，Dex Web 在 Flow 的图和 Timeline 上按 Step 显示它们。

<a id="database-backed-workflow-libraries"></a>

## 基于数据库的 workflow 库

DBOS 是一个例子。workflow 和 step 是你应用里加了注解的函数，checkpoint 保存在你的 PostgreSQL 数据库里。崩溃之后，workflow 再次运行，已完成的 step 返回记录下来的输出。

| DBOS 术语              | 最接近的 Dex 术语                                           |
| -------------------- | ----------------------------------------------------- |
| Workflow、Workflow ID | Flow、Flow ID                                          |
| Step                 | Step `Execute`                                        |
| Send 和 receive       | Channel                                               |
| Events（set 和 get）    | Attribute，通过 RPC 或 Client 读取                          |
| Durable sleep        | Timer                                                 |
| Queue                | Flow type 队列。它没有按队列设置的并发或速率限制；每个 Worker 的任务槽位限制它的工作量。 |

**相同之处。** 普通的应用代码，加上记录下来的 step。Workflow ID 让启动具有幂等性。

**区别。**

- DBOS 的 workflow 函数在 step 之间必须是确定性的。
- DBOS 的 **send** 接受幂等键。目前 Dex 重复的 Channel 发布可能投递两次。
- DBOS 只用你自己运行的 PostgreSQL 数据库，不增加其他 server。Dex 在对象存储上运行自己的 server，并且可以把 Attribute 值同步到你的数据库。

<a id="step-function-services"></a>

## Step function 服务

AWS Step Functions 是一个例子：用 JSON 定义语言写的状态机，每个状态调用一个服务。两者都记录一张显式的状态图。Dex 的 Step 和决定是用你的语言写的代码，运行在你的 Worker 上，所以 Step 可以调用任何库。

<a id="agent-framework-checkpoints"></a>

## Agent 框架 checkpoint

LangGraph 是一个例子。checkpointer 在每一步之后按 thread ID 保存图的状态，interrupt 让图暂停，等待人来处理。它的托管平台负责恢复 run。

| Agent 框架术语        | 最接近的 Dex 术语                                 |
| ----------------- | ------------------------------------------- |
| Thread ID         | Session ID，也就是 Flow ID                      |
| Checkpoint        | 每次模型调用或 Tool Call 之后 Session 的 Attribute    |
| 等待人工输入的 interrupt | 在 `AwaitToolApproval` 中等待的 Tool Call，或者一个提问 |
| Node 或 Tool Call  | 一个 Step                                     |

checkpointer 负责保存状态；重启崩溃的 run，或者用 timer 唤醒它，需要托管平台或你自己的代码。Dex Server 为每个 Session 做这些事。使用 Dex Agents 时，你继续用自己的框架，由适配器把 Agent 包装进一个 Session Flow；见 [持久化 Agent](https://docs.superdurable.io/zh-Hans/agent-frameworks.md)。

<a id="agent-frameworks-on-a-durable-execution-engine"></a>

### 运行在 durable execution 引擎上的 Agent 框架

运行在 Temporal 上的 OpenAI Agents SDK、运行在 Temporal、DBOS 或 Prefect 上的 Pydantic AI，以及 LangGraph 平台，都把每次模型调用和包装过的 Tool Call 记录为引擎的一个单元，所以崩溃只会重复正在运行的那次调用。

- 在 Dex 中，每个 tool 都有一个 effect class。读取可以再次运行。非幂等写操作默认只尝试一次，如果被中断，会为模型记录 `outcome_unknown`；见 [Tool Call](https://docs.superdurable.io/zh-Hans/agent-frameworks/tool-calls.md)。
- effect class 也决定默认的审批方式。审批等待不占用 Worker，可以持续几分钟或几天；见 [审批](https://docs.superdurable.io/zh-Hans/agent-frameworks/approvals.md)。
- 一个适配器模型和一个 Session Flow 覆盖所有受支持的框架。
- Dex Server 的版本是 0.1，可以运行在 [Dex Cloud](https://docs.superdurable.io/zh-Hans/components/choose-how-to-run.md#dex-cloud) 上，也可以运行在你自己的基础设施上。上面这些引擎有更长的生产使用记录。

<a id="when-another-tool-is-the-better-fit"></a>

## 何时其他工具更合适

- **你想把流程写成一个从头到尾顺序执行的函数。** 基于重放的引擎、基于日志的运行时或基于数据库的库都支持这种写法。Dex 要求你把流程建模成 Step 和决定。
- **你现在就需要 1.0 版本的 server。** Dex Server 的版本是 0.1。其他几种工具有更长的生产使用记录。
- **你的流程是一个服务里的一个短函数，而你已经在运行 PostgreSQL。** 基于数据库的库不会增加新的 server。
- **你的团队已经在大规模运行另一个引擎。** 迁移的代价会超过本页这些差异带来的好处，除非重放或版本管理错误对你来说确实是个问题。
- **你希望每个 key 只有一个写入方，而且不用声明锁。** virtual object 或单线程的 workflow 默认就是这样。在 Dex 里，你要声明 Attribute 锁。
- **你需要一个集群提供超过一个节点的容量。** 一个集群由一个 active Dex Server 节点服务。超出这个容量时，你要运行多个独立的集群。见[超出一个节点的容量](https://docs.superdurable.io/zh-Hans/intro/limits.md#grow-past-one-node)。
- **你需要 OSI 认可的开源许可证。** Dex 以 Sustainable Use License 提供源代码，这个许可证限制把 Dex 作为平台托管或转售。Temporal 和 DBOS 使用 MIT 许可证。见[许可证](https://docs.superdurable.io/zh-Hans/intro/limits.md#license)。

<a id="next"></a>

## 下一步

- [性能](https://docs.superdurable.io/zh-Hans/intro/performance.md)：实测吞吐、延迟和成本。
- [Dex vs. 数据库](https://docs.superdurable.io/zh-Hans/intro/compared-to-database.md)：用数据库和队列搭建的同一个订单流程。
- [Step 的保证](https://docs.superdurable.io/zh-Hans/concepts/step.md#guarantees)：Step 失败或再次运行时 Dex 承诺什么。
- [正确性与表达能力](https://docs.superdurable.io/zh-Hans/correctness-and-expressiveness.md)：证明及其前提假设。
