Skip to content

Commit a936e40

Browse files
committed
Simplify DBOS docs
1 parent 974f595 commit a936e40

File tree

1 file changed

+31
-47
lines changed

1 file changed

+31
-47
lines changed

docs/dbos.md

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,22 @@
11
# Durable Execution with DBOS
22

3-
!!! note
4-
Durable execution support is in beta and the public interface is subject to change based on user feedback. We expect it to be stable by the release of Pydantic AI v1 at the end of August. Questions and feedback are welcome in [GitHub issues](https://github.com/pydantic/pydantic-ai/issues) and the [`#pydantic-ai` Slack channel](https://logfire.pydantic.dev/docs/join-slack/).
5-
63
Pydantic AI allows you to build durable agents that can preserve their progress across transient API failures and application errors or restarts, and handle long-running, asynchronous, and human-in-the-loop workflows with production-grade reliability. Durable agents have full support for [streaming](agents.md#streaming-all-events) and [MCP](mcp/client.md), with the added benefit of fault tolerance.
74

8-
[DBOS](https://www.dbos.dev/) is a lightweight [durable execution](https://docs.dbos.dev/architecture) library that's natively supported by Pydantic AI.
9-
The integration only uses Pydantic AI's public interface, so it can also serve as a reference for how to integrate with other durable execution systems.
5+
[DBOS](https://www.dbos.dev/) is a lightweight [durable execution](https://docs.dbos.dev/architecture) library natively integrated with Pydantic AI. The integration only uses Pydantic AI's public interface, so it also serves as a reference for integrating with other durable systems.
106

117
### Durable Execution
128

13-
In DBOS's durable execution implementation, a program that crashes or encounters an exception while interacting with a model or API will retry until it can successfully complete.
14-
15-
DBOS relies primarily on a replay mechanism to recover from failures.
16-
As the program makes progress, DBOS saves key inputs and decisions, allowing a re-started program to pick up right where it left off.
17-
18-
The key to making this work is to separate the application's repeatable (deterministic) and non-repeatable (non-deterministic) parts:
19-
20-
1. Deterministic pieces, termed [**workflows**](https://docs.dbos.dev/python/tutorials/workflow-tutorial), execute the same way when re-run with the same inputs.
21-
2. Non-deterministic pieces, termed [**steps**](https://docs.dbos.dev/python/tutorials/step-tutorial), can run arbitrary code, performing I/O and any other operations.
22-
23-
Workflow code can run for extended periods and, if interrupted, resume exactly where it left off.
24-
Critically, workflow code generally _cannot_ include any kind of I/O, over the network, disk, etc.
25-
Step code faces no restrictions on I/O or external interactions, but if a step fails part-way through it is restarted from the beginning.
26-
27-
28-
!!! note
9+
DBOS workflows make your program **durable** by checkpointing its state in a database. If your program ever fails, when it restarts all your workflows will automatically resume from the last completed step.
2910

30-
If you are familiar with celery, it may be helpful to think of DBOS steps as similar to celery tasks, but where you wait for the task to complete and obtain its result before proceeding to the next step in the workflow.
31-
However, DBOS workflows and steps offer a great deal more flexibility and functionality than celery tasks.
11+
* **Workflows** must be deterministic and generally cannot include I/O.
12+
* **Steps** may perform I/O (network, disk, API calls). If a step fails, it restarts from the beginning.
3213

33-
See the [DBOS documentation](https://docs.dbos.dev/architecture) for more information.
14+
Every workflow input and step output is durably stored in the system database. When workflow execution fails, whether from crashes, network issues, or server restarts, DBOS leverages these checkpoints to recover workflows from their last completed step.
3415

35-
In the case of Pydantic AI agents, integration with DBOS means that [model requests](models/index.md), [tool calls](tools.md) that may require I/O, and [MCP server communication](mcp/client.md) all need to be offloaded to DBOS steps due to their I/O requirements, while the logic that coordinates them (i.e. the agent run) lives in the workflow. Code that handles a scheduled job or web request can then execute the workflow, which will in turn execute the steps as needed.
16+
DBOS **queues** provide durable, database-backed alternatives to systems like Celery or BullMQ, supporting features such as concurrency limits, rate limits, timeouts, and prioritization. See the [DBOS docs](https://docs.dbos.dev/architecture) for details.
3617

3718
The diagram below shows the overall architecture of an agentic application in DBOS.
38-
DBOS is lightweight because it runs entirely in-process as a library, so your workflows and steps remain normal functions within your application that you can call from other application code. DBOS instruments them to checkpoint their state into a database (i.e., possibly replicated across cloud regions).
19+
DBOS runs fully in-process as a library. Functions remain normal Python functions but are checkpointed into a database (Postgres or SQLite).
3920

4021
```text
4122
Clients
@@ -66,18 +47,20 @@ See the [DBOS documentation](https://docs.dbos.dev/architecture) for more inform
6647

6748
## Durable Agent
6849

69-
Any agent can be wrapped in a [`DBOSAgent`][pydantic_ai.durable_exec.dbos.DBOSAgent] to get a durable agent, by automatically wrapping the agent run loop as a deterministic DBOS workflow and offloading work that requires I/O (namely model requests and MCP server communication) to non-deterministic steps. To make it flexible, `DBOSAgent` doesn't automatically wrap other tool functions, so you can decorate them as either DBOS workflows or steps as needed.
50+
Any agent can be wrapped in a [`DBOSAgent`][pydantic_ai.durable_exec.dbos.DBOSAgent] to get durable execution. `DBOSAgent` automatically:,
7051

71-
At the time of wrapping, the agent's [model](models/index.md) and [MCP server communication](mcp/client.md) are wrapped as DBOS steps instead of directly invoking the original functions inside the workflow. The original agent can still be used as normal outside the DBOS workflow.
52+
* Wraps `Agent.run` and `Agent.run_sync` as DBOS workflows.
53+
* Wraps [model requests](models/index.md) and [MCP communication](mcp/client.md) as DBOS steps.
7254

73-
Here is a simple but complete example of wrapping an agent for durable execution. All it requires is to install the DBOS [open-source library](https://github.com/dbos-inc/dbos-transact-py):
55+
Custom tool functions are not wrapped automatically. You can decorate them with `@DBOS.workflow` or `@DBOS.step` as needed.
7456

75-
```sh
76-
uv add pydantic-ai[dbos]
77-
```
57+
The original agent, model, and MCP server can still be used as normal outside the DBOS workflow.
58+
59+
Here is a simple but complete example of wrapping an agent for durable execution. All it requires is to install Pydantic AI with the DBOS [open-source library](https://github.com/dbos-inc/dbos-transact-py):
7860

79-
or if you use pip:
8061
```sh
62+
uv add pydantic-ai[dbos]
63+
# or
8164
pip install pydantic-ai[dbos]
8265
```
8366

@@ -108,9 +91,9 @@ async def main():
10891
#> Mexico City (Ciudad de México, CDMX)
10992
```
11093

111-
1. The original `Agent` cannot be used inside a deterministic DBOS workflow, but the `DBOSAgent` can. Workflow function declarations and `DBOSAgent` creations need to happen before calling `DBOS.launch()` because DBOS requires all workflows to be registered before launch so that recovery can correctly find all workflows.
112-
2. [`DBOSAgent.run()`][pydantic_ai.durable_exec.dbos.DBOSAgent.run] works like [`Agent.run()`][pydantic_ai.Agent.run], but runs inside a DBOS workflow and wraps model requests, decorated tool calls, and MCP communication as DBOS steps.
113-
3. This assumes DBOS is using SQLite. To deploy your agent to production, we recommend using a Postgres server.
94+
1. Workflows and `DBOSAgent` must be defined before `DBOS.launch()` so that recovery can correctly find all workflows.
95+
2. [`DBOSAgent.run()`][pydantic_ai.durable_exec.dbos.DBOSAgent.run] works like [`Agent.run()`][pydantic_ai.Agent.run], but runs as a DBOS workflow and executes model requests, decorated tool calls, and MCP communication as DBOS steps.
96+
3. This example uses SQLite. Postgres is recommended for production.
11497
4. The agent's `name` is used to uniquely identify its workflows.
11598

11699
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_
@@ -121,34 +104,35 @@ For more information on how to use DBOS in Python applications, see their [Pytho
121104

122105
## DBOS Integration Considerations
123106

124-
There are a few considerations specific to agents and toolsets when using DBOS for durable execution. These are important to understand to ensure that your agents and toolsets work correctly with DBOS's workflow and step model.
107+
When using DBOS with Pydantic AI agents, there are a few important considerations to ensure workflows and toolsets behave correctly.
125108

126109
### Agent and Toolset Requirements
127110

128-
To ensure that DBOS knows what code to run when a workflow fails or is interrupted and then restarted, each agent instance needs to have a unique name.
111+
Each agent instance must have a unique `name` so DBOS can correctly resume workflows after a failure or restart. Tools that perform I/O or external interactions should be annotated as DBOS steps.
129112

130113
Other than that, any agent and toolset will just work!
131114

132115
### Agent Run Context and Dependencies
133116

134-
As DBOS checkpoints workflow and step execution into a database, workflow inputs and outputs, and step outputs need to be serializable (JSON pickleable). You may also want to keep the inputs and outputs small (the maximum size for a single field in PostgreSQL is 1 GB, but usually you want to keep the output size under 2 MB).
117+
DBOS checkpoints workflow inputs/outputs and step outputs into a database using `jsonpickle`. This means you need to make sure [dependencies](dependencies.md) object provided to [`DBOSAgent.run()`][pydantic_ai.durable_exec.dbos.DBOSAgent.run] or [`DBOSAgent.run_sync()`][pydantic_ai.durable_exec.dbos.DBOSAgent.run_sync], and tool outputs can be serialized using jsonpickle. You may also want to keep the inputs and outputs small (under \~2 MB). PostgreSQL and SQLite support up to 1 GB per field, but large objects may impact performance.
135118

136119
### Streaming
137120

138-
Because DBOS steps cannot stream output directly to the step call site, [`Agent.run_stream()`][pydantic_ai.Agent.run_stream] is not supported.
121+
Because DBOS cannot stream output directly to the workflow or step call site, [`Agent.run_stream()`][pydantic_ai.Agent.run_stream] is not supported when running inside of a DBOS workflow.
139122

140123
Instead, you can implement streaming by setting an [`event_stream_handler`][pydantic_ai.agent.EventStreamHandler] on the `Agent` or `DBOSAgent` instance and using [`DBOSAgent.run()`][pydantic_ai.durable_exec.dbos.DBOSAgent.run].
141124
The event stream handler function will receive the agent [run context][pydantic_ai.tools.RunContext] and an async iterable of events from the model's streaming response and the agent's execution of tools. For examples, see the [streaming docs](agents.md#streaming-all-events).
142125

143126

144127
## Step Configuration
145128

146-
DBOS step configuration, like retry policies, can be customized by passing [`StepConfig`][pydantic_ai.durable_exec.dbos.StepConfig] objects to the `DBOSAgent` constructor:
129+
You can customize DBOS step behavior, such as retries, by passing [`StepConfig`][pydantic_ai.durable_exec.dbos.StepConfig] objects to the `DBOSAgent` constructor:
130+
131+
- `mcp_step_config`: The DBOS step config to use for MCP server communication. No retries if omitted.
132+
- `model_step_config`: The DBOS step config to use for model request steps. No retries if omitted.
147133

148-
- `mcp_step_config`: The DBOS step config to use for MCP server communication. If no config is provided, it disables DBOS step retries.
149-
- `model_step_config`: The DBOS step config to use for model request steps. If no config is provided, it disables DBOS step retries.
134+
For custom tools, you can annotate them directly with [`@DBOS.step`](https://docs.dbos.dev/python/reference/decorators#step) or [`@DBOS.workflow`](https://docs.dbos.dev/python/reference/decorators#workflow) decorators as needed. These decorators have no effect outside DBOS workflows, so tools remain usable in non-DBOS agents.
150135

151-
For individual tools, you can annotate them with [`@DBOS.step`](https://docs.dbos.dev/python/reference/decorators#step) or [`@DBOS.workflow`](https://docs.dbos.dev/python/reference/decorators#workflow) decorators as needed. Decorated steps are just normal functions if called outside of DBOS workflows, which can be used in non-DBOS agents.
152136

153137
## Step Retries
154138

@@ -160,9 +144,9 @@ You can customize DBOS's retry policy using [step configuration](#step-configura
160144

161145
## Observability with Logfire
162146

163-
DBOS generates OpenTelemetry traces and events for each workflow and step execution, and Pydantic AI generates events for each agent run, model request and tool call. These can be sent to [Pydantic Logfire](logfire.md) to get a complete picture of what's happening in your application.
147+
DBOS emits OpenTelemetry traces and events for every workflow and step, and Pydantic AI generates events for each agent run, model request and tool call. These can be sent to [Pydantic Logfire](logfire.md) to get a complete picture of what's happening in your application.
164148

165-
To disable sending DBOS traces to Logfire, you can pass `disable_otlp=True` to the `DBOS` constructor. For example:
149+
To disable DBOS traces and logs, you can set `disable_otlp=True` in `DBOSConfig`. For example:
166150

167151

168152
```python {title="dbos_no_traces.py" test="skip"}
@@ -176,4 +160,4 @@ dbos_config: DBOSConfig = {
176160
DBOS(config=dbos_config)
177161
```
178162

179-
1. If `True`, disables OpenTelemetry tracing and logging for DBOS. Defaults to `False`.
163+
1. If `True`, disables OpenTelemetry tracing and logging for DBOS. Default is `False`.

0 commit comments

Comments
 (0)