mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-08 02:37:52 +02:00
x
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Breakpoints
|
||||
|
||||
Breakpoints pause graph execution at specific points, enabling [**human-in-the-loop**](./human_in_the_loop.md) workflows and debugging. Breakpoints are powered by LangGraph's [**persistence layer**](./persistence.md), which saves the state after each graph step.
|
||||
Breakpoints pause graph execution at specific points and enable stepping through execution step by step. Breakpoints are powered by LangGraph's [**persistence layer**](./persistence.md), which saves the state after each graph step. Breakpoints can also be used to enable [**human-in-the-loop**](./human_in_the_loop.md) workflows, though we recommend using the [`interrupt` function](#the-interrupt-function) for this purpose.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -15,68 +15,9 @@ To use breakpoints, you will need to:
|
||||
|
||||
There are two places where you can set breakpoints:
|
||||
|
||||
1. **Inside** a node using the [`interrupt` function](#the-interrupt-function) (or the older [`NodeInterrupt` exception](#nodeinterrupt-exception)).
|
||||
2. **Before** or **after** a node executes by setting breakpoints at **compile time** or **run time**. We call these [**static breakpoints**](#static-breakpoints).
|
||||
1. **Before** or **after** a node executes by setting breakpoints at **compile time** or **run time**. We call these [**static breakpoints**](#static-breakpoints).
|
||||
2. **Inside** a node using the [`NodeInterrupt` exception](#nodeinterrupt-exception).
|
||||
|
||||
The **recommended** way to set breakpoints is using the [`interrupt` function](#the-interrupt-function). This method is easier to use and more flexible than the older methods.
|
||||
|
||||
### The `interrupt` function
|
||||
|
||||
Use the [interrupt](../reference/types.md/#langgraph.types.interrupt) function to **pause** the graph at specific points to collect user input. The `interrupt` function surfaces interrupt information to the client, allowing the developer to collect user input, validate the graph state, or make decisions before resuming execution.
|
||||
|
||||
```python
|
||||
from langgraph.types import interrupt
|
||||
|
||||
def human_approval(state: State):
|
||||
...
|
||||
answer = interrupt(
|
||||
# Interrupt information to surface to the client.
|
||||
# Can be any JSON serializable value.
|
||||
{
|
||||
"question": "Can we proceed?",
|
||||
"llm_output": state["llm_output"]
|
||||
}
|
||||
)
|
||||
|
||||
if answer['approved']:
|
||||
# Proceed with the action
|
||||
...
|
||||
else:
|
||||
# Do something else
|
||||
...
|
||||
|
||||
|
||||
# Add the node to the graph
|
||||
graph_builder.add_node("human_approval", human_approval)
|
||||
# Compile the graph with a checkpointer
|
||||
graph = graph_builder.compile(checkpointer=checkpointer)
|
||||
|
||||
# Run the graph until the breakpoint
|
||||
thread_config = {"configurable": {"thread_id": "some_id"}}
|
||||
for event in graph.stream(inputs, thread_config, stream_mode="values"):
|
||||
print(event)
|
||||
```
|
||||
|
||||
```pycon
|
||||
{'__interrupt__': (
|
||||
Interrupt(
|
||||
value={'question': 'Can we proceed?', "llm_output": "..."},
|
||||
resumable=True,
|
||||
ns=['node:5df255f7-d683-1a99-b7c8-00dd534aed8e'],
|
||||
when='during'
|
||||
),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Graph execution can be resumed using the [Command](../reference/types.md#langgraph.types.Command) primitive. The `Command` primitive provides several options to control and modify the graph's state during resumption:
|
||||
|
||||
```python
|
||||
# Resume the graph with the user's input
|
||||
for event in graph.stream(Command(resume={"approved": True}), config=thread_config):
|
||||
print(event)
|
||||
```
|
||||
|
||||
### Static breakpoints
|
||||
|
||||
Static breakpoints are triggered either **before** or **after** a node executes. You can set static breakpoints by specifying `interrupt_before` and `interrupt_after` at **"compile" time** or **run time**.
|
||||
@@ -142,7 +83,8 @@ node at a time or if you want to pause the graph execution at specific nodes.
|
||||
|
||||
### `NodeInterrupt` exception
|
||||
|
||||
We recommend that you [**use the `interrupt` function instead**](#the-interrupt-function) of the `NodeInterrupt` exception. The `interrupt` function is easier to use and more flexible.
|
||||
We recommend that you [**use the `interrupt` function instead**](#the-interrupt-function) of the `NodeInterrupt` exception if you're trying to implement
|
||||
[human-in-the-loop](./human_in_the_loop.md) workflows. The `interrupt` function is easier to use and more flexible.
|
||||
|
||||
??? node "`NodeInterrupt` exception"
|
||||
|
||||
@@ -183,107 +125,6 @@ We recommend that you [**use the `interrupt` function instead**](#the-interrupt-
|
||||
print(event)
|
||||
```
|
||||
|
||||
## The `Command` primitive
|
||||
|
||||
When using the `interrupt` function, the graph will pause at the breakpoint and wait for user input.
|
||||
|
||||
Graph execution can be resumed using the [Command](../reference/types.md#langgraph.types.Command) primitive which can be passed through the `invoke`, `ainvoke`, `stream` or `astream` methods.
|
||||
|
||||
The `Command` primitive provides several options to control and modify the graph's state during resumption:
|
||||
|
||||
1. **Pass a value to the `interrupt`**: Provide data, such as a user's response, to the graph using `Command(resume=value)`. Execution resumes from the beginning of the node where the `interrupt` was used, however, this time the `interrupt(...)` call will return the value passed in the `Command(resume=value)` instead of pausing the graph. The `resume` value is only used when using `interrupt` as a breakpoint.
|
||||
|
||||
```python
|
||||
# Resume graph execution with the user's input.
|
||||
graph.invoke(Command(resume={"age": "25"}), thread_config)
|
||||
```
|
||||
|
||||
2. **Update the graph state**: Modify the graph state using `Command(update=update)`. Note that resumption starts from the beginning of the node where the `interrupt` was used. Execution resumes from the beginning of the node where the `interrupt` was used, but with the updated state.
|
||||
|
||||
```python
|
||||
# Update the graph state and resume.
|
||||
# You must provide a `resume` value if using an `interrupt`.
|
||||
graph.invoke(Command(update={"foo": "bar"}, resume="Let's go!!!"), thread_config)
|
||||
```
|
||||
|
||||
By leveraging `Command`, you can resume graph execution, handle user inputs, and dynamically adjust the graph's state.
|
||||
|
||||
## Using with `invoke` and `ainvoke`
|
||||
|
||||
When you use `stream` or `astream` to run the graph, you will receive an `Interrupt` event that let you know that a breakpoint has been hit.
|
||||
|
||||
`invoke` and `ainvoke` do not return the interrupt information. To access this information, you must use the [get_state](../reference/graphs.md#langgraph.graph.graph.CompiledGraph.get_state) method to retrieve the graph state after calling `invoke` or `ainvoke`.
|
||||
|
||||
```python
|
||||
# Run the graph up to the breakpoint
|
||||
result = graph.invoke(inputs, thread_config)
|
||||
# Get the graph state to get interrupt information.
|
||||
state = graph.get_state(thread_config)
|
||||
# Print the state values
|
||||
print(state.values)
|
||||
# Print the pending tasks
|
||||
print(state.tasks)
|
||||
# Resume the graph with the user's input.
|
||||
graph.invoke(Command(resume={"age": "25"}), thread_config)
|
||||
```
|
||||
|
||||
```pycon
|
||||
{'foo': 'bar'} # State values
|
||||
(
|
||||
PregelTask(
|
||||
id='5d8ffc92-8011-0c9b-8b59-9d3545b7e553',
|
||||
name='node_foo',
|
||||
path=('__pregel_pull', 'node_foo'),
|
||||
error=None,
|
||||
interrupts=(Interrupt(value='value_in_interrupt', resumable=True, ns=['node_foo:5d8ffc92-8011-0c9b-8b59-9d3545b7e553'], when='during'),), state=None,
|
||||
result=None
|
||||
),
|
||||
) # Pending tasks. interrupts
|
||||
```
|
||||
|
||||
## How does resuming from a breakpoint work?
|
||||
|
||||
!!! warning
|
||||
|
||||
Resuming from a breakpoint is **different** from traditional breakpoints or Python's `input()` function, where execution resumes from the exact point where the breakpoint was triggered or where the `input()` function was called.
|
||||
|
||||
A critical aspect of using breakpoints is understanding how resuming from a breakpoint works. When you resume execution after a breakpoint, the graph execution starts from the **beginning** of the **graph node** where the last breakpoint was triggered.
|
||||
|
||||
**All** code from the beginning of the node to the **breakpoint** will be re-executed.
|
||||
|
||||
```python
|
||||
counter = 0
|
||||
def node(state: State):
|
||||
# All the code from the beginning of the node to the breakpoint will be re-executed
|
||||
# when the graph resumes.
|
||||
global counter
|
||||
counter += 1
|
||||
print(f"> Entered the node: {counter} # of times")
|
||||
# Pause the graph and wait for user input.
|
||||
answer = interrupt()
|
||||
print("The value of counter is:", counter)
|
||||
...
|
||||
```
|
||||
|
||||
Upon **resuming** the graph, the counter will be incremented a second time, resulting in the following output:
|
||||
|
||||
```pycon
|
||||
> Entered the node: 2 # of times
|
||||
The value of counter is: 2
|
||||
```
|
||||
|
||||
Keep the following considerations in mind when using the `interrupt` function:
|
||||
|
||||
1. **Side effects**: Place side-effecting code, such as API calls, **after** the `interrupt` to avoid duplication, as these are re-triggered every time the node resumes.
|
||||
2. **Multiple interrupts**: Using multiple `interrupt` calls in a node can be very useful (e.g., for run-time validation), but the order and number of calls must remain consistent to prevent mismatched resume values. As a result, we recommend that you structure your code in a way that avoids providing both a `resume` and a state `update` value (e.g., `Command(resume=resume, update=update)`) at the same time.
|
||||
3. **Subgraphs**: If you're invoking a subgraph [as a function](low_level.md#as-a-function), the **parent** graph will be re-run from the **beginning of the node** where the subgraph was invoked.
|
||||
|
||||
## Best practices
|
||||
|
||||
* Use the [`interrupt`](#the-interrupt-function) function to set breakpoints and collect user input.
|
||||
* Use [`Command`](#the-command-primitive) to resume execution and control the graph state.
|
||||
* Consider putting all side effects (e.g., API calls) after the `interrupt` to prevent duplication. See [How does resuming from a breakpoint work?](#how-does-resuming-from-a-breakpoint-work)
|
||||
|
||||
## Additional Resources 📚
|
||||
|
||||
- [**Conceptual Guide: Persistence**](persistence.md): Read the persistence guide for more context about persistence.
|
||||
|
||||
@@ -34,6 +34,10 @@ def human_node(state: State):
|
||||
# Update the state with the human's input or route the graph based on the input.
|
||||
...
|
||||
|
||||
graph = graph_builder.compile(
|
||||
checkpointer=checkpointer # Required for `interrupt` to work
|
||||
)
|
||||
|
||||
# Run the graph and hit the breakpoint
|
||||
thread_config = {"configurable": {"thread_id": "some_id"}}
|
||||
graph.invoke(some_input, config=thread_config)
|
||||
@@ -42,7 +46,14 @@ graph.invoke(some_input, config=thread_config)
|
||||
graph.invoke(Command(resume=value_from_human), config=thread_config)
|
||||
```
|
||||
|
||||
Please read the [Breakpoints](breakpoints.md) guide for more information on using the `interrupt` function.
|
||||
## Requirements
|
||||
|
||||
To use `interrupt` in your graph, you need to:
|
||||
|
||||
1. [**Specify a checkpointer**](persistence.md#checkpoints) to save the graph state after each step.
|
||||
2. **Call `interrupt()`** in the appropriate place. See the [Design Patterns](#design-patterns) section for examples.
|
||||
3. **Run the graph** with a [**thread ID**](./persistence.md#threads) to pause execution at the breakpoint.
|
||||
4. **Resume execution** using `invoke`/`ainvoke`/`stream`/`astream` (see [**The `Command` primitive**](#the-command-primitive)).
|
||||
|
||||
## Design Patterns
|
||||
|
||||
@@ -293,6 +304,95 @@ def human_node(state: State):
|
||||
}
|
||||
```
|
||||
|
||||
## The `Command` primitive
|
||||
|
||||
When using the `interrupt` function, the graph will pause at the breakpoint and wait for user input.
|
||||
|
||||
Graph execution can be resumed using the [Command](../reference/types.md#langgraph.types.Command) primitive which can be passed through the `invoke`, `ainvoke`, `stream` or `astream` methods.
|
||||
|
||||
The `Command` primitive provides several options to control and modify the graph's state during resumption:
|
||||
|
||||
1. **Pass a value to the `interrupt`**: Provide data, such as a user's response, to the graph using `Command(resume=value)`. Execution resumes from the beginning of the node where the `interrupt` was used, however, this time the `interrupt(...)` call will return the value passed in the `Command(resume=value)` instead of pausing the graph. The `resume` value is only used when using `interrupt` as a breakpoint.
|
||||
|
||||
```python
|
||||
# Resume graph execution with the user's input.
|
||||
graph.invoke(Command(resume={"age": "25"}), thread_config)
|
||||
```
|
||||
|
||||
2. **Update the graph state**: Modify the graph state using `Command(update=update)`. Note that resumption starts from the beginning of the node where the `interrupt` was used. Execution resumes from the beginning of the node where the `interrupt` was used, but with the updated state.
|
||||
|
||||
```python
|
||||
# Update the graph state and resume.
|
||||
# You must provide a `resume` value if using an `interrupt`.
|
||||
graph.invoke(Command(update={"foo": "bar"}, resume="Let's go!!!"), thread_config)
|
||||
```
|
||||
|
||||
By leveraging `Command`, you can resume graph execution, handle user inputs, and dynamically adjust the graph's state.
|
||||
|
||||
## Using with `invoke` and `ainvoke`
|
||||
|
||||
When you use `stream` or `astream` to run the graph, you will receive an `Interrupt` event that let you know that a breakpoint has been hit.
|
||||
|
||||
`invoke` and `ainvoke` do not return the interrupt information. To access this information, you must use the [get_state](../reference/graphs.md#langgraph.graph.graph.CompiledGraph.get_state) method to retrieve the graph state after calling `invoke` or `ainvoke`.
|
||||
|
||||
```python
|
||||
# Run the graph up to the breakpoint
|
||||
result = graph.invoke(inputs, thread_config)
|
||||
# Get the graph state to get interrupt information.
|
||||
state = graph.get_state(thread_config)
|
||||
# Print the state values
|
||||
print(state.values)
|
||||
# Print the pending tasks
|
||||
print(state.tasks)
|
||||
# Resume the graph with the user's input.
|
||||
graph.invoke(Command(resume={"age": "25"}), thread_config)
|
||||
```
|
||||
|
||||
```pycon
|
||||
{'foo': 'bar'} # State values
|
||||
(
|
||||
PregelTask(
|
||||
id='5d8ffc92-8011-0c9b-8b59-9d3545b7e553',
|
||||
name='node_foo',
|
||||
path=('__pregel_pull', 'node_foo'),
|
||||
error=None,
|
||||
interrupts=(Interrupt(value='value_in_interrupt', resumable=True, ns=['node_foo:5d8ffc92-8011-0c9b-8b59-9d3545b7e553'], when='during'),), state=None,
|
||||
result=None
|
||||
),
|
||||
) # Pending tasks. interrupts
|
||||
```
|
||||
|
||||
## How does resuming from a breakpoint work?
|
||||
|
||||
!!! warning
|
||||
|
||||
Resuming from an `interrupt` is **different** from Python's `input()` function, where execution resumes from the exact point where the `input()` function was called.
|
||||
|
||||
A critical aspect of using breakpoints is understanding how resuming from a breakpoint works. When you resume execution after a breakpoint, the graph execution starts from the **beginning** of the **graph node** where the last breakpoint was triggered.
|
||||
|
||||
**All** code from the beginning of the node to the **breakpoint** will be re-executed.
|
||||
|
||||
```python
|
||||
counter = 0
|
||||
def node(state: State):
|
||||
# All the code from the beginning of the node to the breakpoint will be re-executed
|
||||
# when the graph resumes.
|
||||
global counter
|
||||
counter += 1
|
||||
print(f"> Entered the node: {counter} # of times")
|
||||
# Pause the graph and wait for user input.
|
||||
answer = interrupt()
|
||||
print("The value of counter is:", counter)
|
||||
...
|
||||
```
|
||||
|
||||
Upon **resuming** the graph, the counter will be incremented a second time, resulting in the following output:
|
||||
|
||||
```pycon
|
||||
> Entered the node: 2 # of times
|
||||
The value of counter is: 2
|
||||
```
|
||||
|
||||
## Gotchyas
|
||||
|
||||
!!! warning
|
||||
@@ -442,15 +542,6 @@ To avoid issues, refrain from dynamically changing the node's structure between
|
||||
{'human_node': {'age': 'John', 'name': 'N/A'}}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Best practices
|
||||
|
||||
* Use the [`interrupt`](breakpoints.md#the-interrupt-function) function to set breakpoints and collect user input.
|
||||
* Use [`Command`](breakpoints.md#the-command-primitive) to resume execution and control the graph state.
|
||||
* Consider putting all side effects (e.g., API calls) after the `interrupt` to prevent duplication.
|
||||
* Understand [how resuming from a breakpoint works](breakpoints.md#how-does-resuming-from-a-breakpoint-work) to avoid common gotchas.
|
||||
|
||||
## Additional Resources 📚
|
||||
|
||||
- [**Conceptual Guide: Persistence**](persistence.md#replay): Read the persistence guide for more context on replaying.
|
||||
|
||||
@@ -24,7 +24,7 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
- [LangGraph Glossary](low_level.md): LangGraph workflows are designed as graphs, with nodes representing different components and edges representing the flow of information between them. This guide provides an overview of the key concepts associated with LangGraph graph primitives.
|
||||
- [Common Agentic Patterns](agentic_concepts.md): An agent uses an LLM to pick its own control flow to solve more complex problems! Agents are a key building block in many LLM applications. This guide explains the different types of agent architectures and how they can be used to control the flow of an application.
|
||||
- [Multi-Agent Systems](multi_agent.md): Complex LLM applications can often be broken down into multiple agents, each responsible for a different part of the application. This guide explains common patterns for building multi-agent systems.
|
||||
- [Breakpoints](breakpoints.md): Breakpoints allow pausing the execution of a graph at specific points. Breakpoints are crucial for human-in-the-loop workflows, allowing human review before continuing.
|
||||
- [Breakpoints](breakpoints.md): Breakpoints allow pausing the execution of a graph at specific points. Breakpoints allow stepping through graph execution for debugging purposes.
|
||||
- [Human-in-the-Loop](human_in_the_loop.md): Explains different ways of integrating human feedback into a LangGraph application.
|
||||
- [Time Travel](time-travel.md): Time travel allows you to replay past actions in your LangGraph application to explore alternative paths and debug issues.
|
||||
- [Persistence](persistence.md): LangGraph has a built-in persistence layer, implemented through checkpointers. This persistence layer helps to support powerful capabilities like human-in-the-loop, memory, time travel, and fault-tolerance.
|
||||
|
||||
@@ -446,19 +446,6 @@ graph.invoke(inputs, config={"recursion_limit": 5, "configurable":{"llm": "anthr
|
||||
|
||||
Read [this how-to](https://langchain-ai.github.io/langgraph/how-tos/recursion-limit/) to learn more about how the recursion limit works.
|
||||
|
||||
## Breakpoints
|
||||
|
||||
Breakpoints pause graph execution at specific points, enabling [**human-in-the-loop**](./human_in_the_loop.md) workflows and debugging. Breakpoints are powered by LangGraph's [**persistence layer**](./persistence.md), which saves the state after each graph step.
|
||||
|
||||
You **MUST** use a [checkpointer](./persistence.md) when using breakpoints as breakpoints require the ability to save the state of the graph at the time of pausing.
|
||||
|
||||
There are two places where you can set breakpoints:
|
||||
|
||||
1. **Inside** a node using the [`interrupt` function](#the-interrupt-function) (or the older [`NodeInterrupt` exception](#nodeinterrupt-exception)).
|
||||
2. **Before** or **after** a node executes by setting breakpoints at **compile time** or **run time**. We call these [**static breakpoints**](#static-breakpoints).
|
||||
|
||||
Read more about breakpoints in the [Breakpoints conceptual guide](./breakpoints.md).
|
||||
|
||||
## `interrupt`
|
||||
|
||||
Use the [interrupt](../reference/types.md/#langgraph.types.interrupt) function to **pause** the graph at specific points to collect user input. The `interrupt` function surfaces interrupt information to the client, allowing the developer to collect user input, validate the graph state, or make decisions before resuming execution.
|
||||
@@ -480,6 +467,12 @@ Resuming the graph is done by passing a [`Command`](#command) object to the grap
|
||||
|
||||
Read more about how the `interrupt` is used for **human-in-the-loop** workflows in the [Human-in-the-loop conceptual guide](./human_in_the_loop.md).
|
||||
|
||||
## Breakpoints
|
||||
|
||||
Breakpoints pause graph execution at specific points and enable stepping through execution step by step. Breakpoints are powered by LangGraph's [**persistence layer**](./persistence.md), which saves the state after each graph step. Breakpoints can also be used to enable [**human-in-the-loop**](./human_in_the_loop.md) workflows, though we recommend using the [`interrupt` function](#interrupt-function) for this purpose.
|
||||
|
||||
Read more about breakpoints in the [Breakpoints conceptual guide](./breakpoints.md).
|
||||
|
||||
## Subgraphs
|
||||
|
||||
A subgraph is a [graph](#graphs) that is used as a [node](#nodes) in another graph. This is nothing more than the age-old concept of encapsulation, applied to LangGraph. Some reasons for using subgraphs are:
|
||||
|
||||
Reference in New Issue
Block a user