mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-27 12:04:58 +02:00
feat: add docs translations (#5552)
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com> Co-authored-by: Tat Dat Duong <david@duong.cz>
This commit is contained in:
co-authored by
Eugene Yurtsev
Tat Dat Duong
parent
72e418e4d0
commit
d59091672f
@@ -5,7 +5,7 @@ search:
|
||||
|
||||
# Durable Execution
|
||||
|
||||
**Durable execution** is a technique in which a process or workflow saves its progress at key points, allowing it to pause and later resume exactly where it left off. This is particularly useful in scenarios that require [human-in-the-loop](./human_in_the_loop.md), where users can inspect, validate, or modify the process before continuing, and in long-running tasks that might encounter interruptions or errors (e.g., calls to an LLM timing out). By preserving completed work, durable execution enables a process to resume without reprocessing previous steps -- even after a significant delay (e.g., a week later).
|
||||
**Durable execution** is a technique in which a process or workflow saves its progress at key points, allowing it to pause and later resume exactly where it left off. This is particularly useful in scenarios that require [human-in-the-loop](./human_in_the_loop.md), where users can inspect, validate, or modify the process before continuing, and in long-running tasks that might encounter interruptions or errors (e.g., calls to an LLM timing out). By preserving completed work, durable execution enables a process to resume without reprocessing previous steps -- even after a significant delay (e.g., a week later).
|
||||
|
||||
LangGraph's built-in [persistence](./persistence.md) layer provides durable execution for workflows, ensuring that the state of each execution step is saved to a durable store. This capability guarantees that if a workflow is interrupted -- whether by a system failure or for [human-in-the-loop](./human_in_the_loop.md) interactions -- it can be resumed from its last recorded state.
|
||||
|
||||
@@ -20,7 +20,14 @@ To leverage durable execution in LangGraph, you need to:
|
||||
|
||||
1. Enable [persistence](./persistence.md) in your workflow by specifying a [checkpointer](./persistence.md#checkpointer-libraries) that will save workflow progress.
|
||||
2. Specify a [thread identifier](./persistence.md#threads) when executing a workflow. This will track the execution history for a particular instance of the workflow.
|
||||
3. Wrap any non-deterministic operations (e.g., random number generation) or operations with side effects (e.g., file writes, API calls) inside [tasks][langgraph.func.task] to ensure that when a workflow is resumed, these operations are not repeated for the particular run, and instead their results are retrieved from the persistence layer. For more information, see [Determinism and Consistent Replay](#determinism-and-consistent-replay).
|
||||
|
||||
:::python
|
||||
3. Wrap any non-deterministic operations (e.g., random number generation) or operations with side effects (e.g., file writes, API calls) inside @[tasks][task] to ensure that when a workflow is resumed, these operations are not repeated for the particular run, and instead their results are retrieved from the persistence layer. For more information, see [Determinism and Consistent Replay](#determinism-and-consistent-replay).
|
||||
:::
|
||||
|
||||
:::js
|
||||
3. Wrap any non-deterministic operations (e.g., random number generation) or operations with side effects (e.g., file writes, API calls) inside @[tasks][task] to ensure that when a workflow is resumed, these operations are not repeated for the particular run, and instead their results are retrieved from the persistence layer. For more information, see [Determinism and Consistent Replay](#determinism-and-consistent-replay).
|
||||
:::
|
||||
|
||||
## Determinism and Consistent Replay
|
||||
|
||||
@@ -30,17 +37,25 @@ As a result, when you are writing a workflow for durable execution, you must wra
|
||||
|
||||
To ensure that your workflow is deterministic and can be consistently replayed, follow these guidelines:
|
||||
|
||||
- **Avoid Repeating Work**: If a [node](./low_level.md#nodes) contains multiple operations with side effects (e.g., logging, file writes, or network calls), wrap each operation in a separate **task**. This ensures that when the workflow is resumed, the operations are not repeated, and their results are retrieved from the persistence layer.
|
||||
- **Encapsulate Non-Deterministic Operations:** Wrap any code that might yield non-deterministic results (e.g., random number generation) inside **tasks** or **nodes**. This ensures that, upon resumption, the workflow follows the exact recorded sequence of steps with the same outcomes.
|
||||
- **Avoid Repeating Work**: If a [node](./low_level.md#nodes) contains multiple operations with side effects (e.g., logging, file writes, or network calls), wrap each operation in a separate **task**. This ensures that when the workflow is resumed, the operations are not repeated, and their results are retrieved from the persistence layer.
|
||||
- **Encapsulate Non-Deterministic Operations:** Wrap any code that might yield non-deterministic results (e.g., random number generation) inside **tasks** or **nodes**. This ensures that, upon resumption, the workflow follows the exact recorded sequence of steps with the same outcomes.
|
||||
- **Use Idempotent Operations**: When possible ensure that side effects (e.g., API calls, file writes) are idempotent. This means that if an operation is retried after a failure in the workflow, it will have the same effect as the first time it was executed. This is particularly important for operations that result in data writes. In the event that a **task** starts but fails to complete successfully, the workflow's resumption will re-run the **task**, relying on recorded outcomes to maintain consistency. Use idempotency keys or verify existing results to avoid unintended duplication, ensuring a smooth and predictable workflow execution.
|
||||
|
||||
:::python
|
||||
For some examples of pitfalls to avoid, see the [Common Pitfalls](./functional_api.md#common-pitfalls) section in the functional API, which shows
|
||||
how to structure your code using **tasks** to avoid these issues. The same principles apply to the [StateGraph (Graph API)][langgraph.graph.state.StateGraph].
|
||||
how to structure your code using **tasks** to avoid these issues. The same principles apply to the @[StateGraph (Graph API)][StateGraph].
|
||||
:::
|
||||
|
||||
:::js
|
||||
For some examples of pitfalls to avoid, see the [Common Pitfalls](./functional_api.md#common-pitfalls) section in the functional API, which shows
|
||||
how to structure your code using **tasks** to avoid these issues. The same principles apply to the @[StateGraph (Graph API)][StateGraph].
|
||||
:::
|
||||
|
||||
## Using tasks in nodes
|
||||
|
||||
If a [node](./low_level.md#nodes) contains multiple operations, you may find it easier to convert each operation into a **task** rather than refactor the operations into individual nodes.
|
||||
|
||||
:::python
|
||||
=== "Original"
|
||||
|
||||
```python
|
||||
@@ -142,16 +157,136 @@ If a [node](./low_level.md#nodes) contains multiple operations, you may find it
|
||||
graph.invoke({"urls": ["https://www.example.com"]}, config)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::js
|
||||
=== "Original"
|
||||
|
||||
```typescript
|
||||
import { StateGraph, START, END } from "@langchain/langgraph";
|
||||
import { MemorySaver } from "@langchain/langgraph";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
// Define a Zod schema to represent the state
|
||||
const State = z.object({
|
||||
url: z.string(),
|
||||
result: z.string().optional(),
|
||||
});
|
||||
|
||||
const callApi = async (state: z.infer<typeof State>) => {
|
||||
// highlight-next-line
|
||||
const response = await fetch(state.url);
|
||||
const text = await response.text();
|
||||
const result = text.slice(0, 100); // Side-effect
|
||||
return {
|
||||
result,
|
||||
};
|
||||
};
|
||||
|
||||
// Create a StateGraph builder and add a node for the callApi function
|
||||
const builder = new StateGraph(State)
|
||||
.addNode("callApi", callApi)
|
||||
.addEdge(START, "callApi")
|
||||
.addEdge("callApi", END);
|
||||
|
||||
// Specify a checkpointer
|
||||
const checkpointer = new MemorySaver();
|
||||
|
||||
// Compile the graph with the checkpointer
|
||||
const graph = builder.compile({ checkpointer });
|
||||
|
||||
// Define a config with a thread ID.
|
||||
const threadId = uuidv4();
|
||||
const config = { configurable: { thread_id: threadId } };
|
||||
|
||||
// Invoke the graph
|
||||
await graph.invoke({ url: "https://www.example.com" }, config);
|
||||
```
|
||||
|
||||
=== "With task"
|
||||
|
||||
```typescript
|
||||
import { StateGraph, START, END } from "@langchain/langgraph";
|
||||
import { MemorySaver } from "@langchain/langgraph";
|
||||
import { task } from "@langchain/langgraph";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
// Define a Zod schema to represent the state
|
||||
const State = z.object({
|
||||
urls: z.array(z.string()),
|
||||
results: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const makeRequest = task("makeRequest", async (url: string) => {
|
||||
// highlight-next-line
|
||||
const response = await fetch(url);
|
||||
const text = await response.text();
|
||||
return text.slice(0, 100);
|
||||
});
|
||||
|
||||
const callApi = async (state: z.infer<typeof State>) => {
|
||||
// highlight-next-line
|
||||
const requests = state.urls.map((url) => makeRequest(url));
|
||||
const results = await Promise.all(requests);
|
||||
return {
|
||||
results,
|
||||
};
|
||||
};
|
||||
|
||||
// Create a StateGraph builder and add a node for the callApi function
|
||||
const builder = new StateGraph(State)
|
||||
.addNode("callApi", callApi)
|
||||
.addEdge(START, "callApi")
|
||||
.addEdge("callApi", END);
|
||||
|
||||
// Specify a checkpointer
|
||||
const checkpointer = new MemorySaver();
|
||||
|
||||
// Compile the graph with the checkpointer
|
||||
const graph = builder.compile({ checkpointer });
|
||||
|
||||
// Define a config with a thread ID.
|
||||
const threadId = uuidv4();
|
||||
const config = { configurable: { thread_id: threadId } };
|
||||
|
||||
// Invoke the graph
|
||||
await graph.invoke({ urls: ["https://www.example.com"] }, config);
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Resuming Workflows
|
||||
|
||||
Once you have enabled durable execution in your workflow, you can resume execution for the following scenarios:
|
||||
|
||||
- **Pausing and Resuming Workflows:** Use the [interrupt][langgraph.types.interrupt] function to pause a workflow at specific points and the [Command][langgraph.types.Command] primitive to resume it with updated state. See [**Human-in-the-Loop**](./human_in_the_loop.md) for more details.
|
||||
:::python
|
||||
|
||||
- **Pausing and Resuming Workflows:** Use the @[interrupt][interrupt] function to pause a workflow at specific points and the @[Command] primitive to resume it with updated state. See [**Human-in-the-Loop**](./human_in_the_loop.md) for more details.
|
||||
- **Recovering from Failures:** Automatically resume workflows from the last successful checkpoint after an exception (e.g., LLM provider outage). This involves executing the workflow with the same thread identifier by providing it with a `None` as the input value (see this [example](../how-tos/use-functional-api.md#resuming-after-an-error) with the functional API).
|
||||
:::
|
||||
|
||||
:::js
|
||||
|
||||
- **Pausing and Resuming Workflows:** Use the @[interrupt][interrupt] function to pause a workflow at specific points and the @[Command] primitive to resume it with updated state. See [**Human-in-the-Loop**](./human_in_the_loop.md) for more details.
|
||||
- **Recovering from Failures:** Automatically resume workflows from the last successful checkpoint after an exception (e.g., LLM provider outage). This involves executing the workflow with the same thread identifier by providing it with a `null` as the input value (see this [example](../how-tos/use-functional-api.md#resuming-after-an-error) with the functional API).
|
||||
:::
|
||||
|
||||
## Starting Points for Resuming Workflows
|
||||
|
||||
* If you're using a [StateGraph (Graph API)][langgraph.graph.state.StateGraph], the starting point is the beginning of the [**node**](./low_level.md#nodes) where execution stopped.
|
||||
* If you're making a subgraph call inside a node, the starting point will be the **parent** node that called the subgraph that was halted.
|
||||
Inside the subgraph, the starting point will be the specific [**node**](./low_level.md#nodes) where execution stopped.
|
||||
* If you're using the Functional API, the starting point is the beginning of the [**entrypoint**](./functional_api.md#entrypoint) where execution stopped.
|
||||
:::python
|
||||
|
||||
- If you're using a @[StateGraph (Graph API)][StateGraph], the starting point is the beginning of the [**node**](./low_level.md#nodes) where execution stopped.
|
||||
- If you're making a subgraph call inside a node, the starting point will be the **parent** node that called the subgraph that was halted.
|
||||
Inside the subgraph, the starting point will be the specific [**node**](./low_level.md#nodes) where execution stopped.
|
||||
- If you're using the Functional API, the starting point is the beginning of the [**entrypoint**](./functional_api.md#entrypoint) where execution stopped.
|
||||
:::
|
||||
|
||||
:::js
|
||||
|
||||
- If you're using a [StateGraph (Graph API)](./low_level.md), the starting point is the beginning of the [**node**](./low_level.md#nodes) where execution stopped.
|
||||
- If you're making a subgraph call inside a node, the starting point will be the **parent** node that called the subgraph that was halted.
|
||||
Inside the subgraph, the starting point will be the specific [**node**](./low_level.md#nodes) where execution stopped.
|
||||
- If you're using the Functional API, the starting point is the beginning of the [**entrypoint**](./functional_api.md#entrypoint) where execution stopped.
|
||||
:::
|
||||
|
||||
Reference in New Issue
Block a user