265 lines
6.1 KiB
Markdown
265 lines
6.1 KiB
Markdown
# Persistence
|
|
|
|
Functionality to save and restore graph state.
|
|
|
|
## Overview
|
|
|
|
Persistence is a feature that **automatically saves** state at each stage of graph execution and allows you to restore it later.
|
|
|
|
## Basic Concepts
|
|
|
|
### Checkpoints
|
|
|
|
State is automatically saved after each **superstep** (set of nodes executed in parallel).
|
|
|
|
```python
|
|
# Superstep 1: node_a and node_b execute in parallel
|
|
# → Checkpoint 1
|
|
|
|
# Superstep 2: node_c executes
|
|
# → Checkpoint 2
|
|
|
|
# Superstep 3: node_d executes
|
|
# → Checkpoint 3
|
|
```
|
|
|
|
### Threads
|
|
|
|
A thread is an identifier containing the **accumulated state of a series of executions**:
|
|
|
|
```python
|
|
config = {"configurable": {"thread_id": "conversation-123"}}
|
|
```
|
|
|
|
Executing with the same `thread_id` continues from the previous state.
|
|
|
|
## Implementation Example
|
|
|
|
```python
|
|
from langgraph.checkpoint.memory import MemorySaver
|
|
from langgraph.graph import StateGraph, MessagesState
|
|
|
|
# Define graph
|
|
builder = StateGraph(MessagesState)
|
|
builder.add_node("chatbot", chatbot_node)
|
|
builder.add_edge(START, "chatbot")
|
|
builder.add_edge("chatbot", END)
|
|
|
|
# Compile with checkpointer
|
|
checkpointer = MemorySaver()
|
|
graph = builder.compile(checkpointer=checkpointer)
|
|
|
|
# Execute with thread ID
|
|
config = {"configurable": {"thread_id": "user-001"}}
|
|
|
|
# First execution
|
|
graph.invoke(
|
|
{"messages": [{"role": "user", "content": "My name is Alice"}]},
|
|
config
|
|
)
|
|
|
|
# Continue in same thread (retains previous state)
|
|
response = graph.invoke(
|
|
{"messages": [{"role": "user", "content": "What's my name?"}]},
|
|
config
|
|
)
|
|
|
|
# → "Your name is Alice"
|
|
```
|
|
|
|
## StateSnapshot Object
|
|
|
|
Checkpoints are represented as `StateSnapshot` objects:
|
|
|
|
```python
|
|
class StateSnapshot:
|
|
values: dict # State at that point in time
|
|
next: tuple[str] # Nodes to execute next
|
|
config: RunnableConfig # Checkpoint configuration
|
|
metadata: dict # Metadata
|
|
tasks: tuple[PregelTask] # Scheduled tasks
|
|
```
|
|
|
|
### Getting Latest State
|
|
|
|
```python
|
|
state = graph.get_state(config)
|
|
|
|
print(state.values) # Current state
|
|
print(state.next) # Next nodes
|
|
print(state.config) # Checkpoint configuration
|
|
```
|
|
|
|
### Getting History
|
|
|
|
```python
|
|
# Get list of StateSnapshots in chronological order
|
|
for state in graph.get_state_history(config):
|
|
print(f"Checkpoint: {state.config['configurable']['checkpoint_id']}")
|
|
print(f"Values: {state.values}")
|
|
print(f"Next: {state.next}")
|
|
print("---")
|
|
```
|
|
|
|
## Time Travel Feature
|
|
|
|
Resume execution from a specific checkpoint:
|
|
|
|
```python
|
|
# Get specific checkpoint from history
|
|
history = list(graph.get_state_history(config))
|
|
|
|
# Checkpoint from 3 steps ago
|
|
past_state = history[3]
|
|
|
|
# Re-execute from that checkpoint
|
|
result = graph.invoke(
|
|
{"messages": [{"role": "user", "content": "New question"}]},
|
|
past_state.config
|
|
)
|
|
```
|
|
|
|
### Validating Alternative Paths
|
|
|
|
```python
|
|
# Get current state
|
|
current_state = graph.get_state(config)
|
|
|
|
# Try with different input
|
|
alt_result = graph.invoke(
|
|
{"messages": [{"role": "user", "content": "Different question"}]},
|
|
current_state.config
|
|
)
|
|
|
|
# Original execution is not affected
|
|
```
|
|
|
|
## Updating State
|
|
|
|
Directly update checkpoint state:
|
|
|
|
```python
|
|
# Get current state
|
|
state = graph.get_state(config)
|
|
|
|
# Update state
|
|
graph.update_state(
|
|
config,
|
|
{"messages": [{"role": "assistant", "content": "Updated message"}]}
|
|
)
|
|
|
|
# Resume from updated state
|
|
graph.invoke({"messages": [...]}, config)
|
|
```
|
|
|
|
## Use Cases
|
|
|
|
### 1. Conversation Continuation
|
|
|
|
```python
|
|
# Session 1
|
|
config = {"configurable": {"thread_id": "chat-1"}}
|
|
graph.invoke({"messages": [("user", "Hello")]}, config)
|
|
|
|
# Session 2 (days later)
|
|
# Remembers previous conversation
|
|
graph.invoke({"messages": [("user", "Continuing from last time")]}, config)
|
|
```
|
|
|
|
### 2. Error Recovery
|
|
|
|
```python
|
|
try:
|
|
graph.invoke(input, config)
|
|
except Exception as e:
|
|
# Even if error occurs, can recover from checkpoint
|
|
print(f"Error: {e}")
|
|
|
|
# Check latest state
|
|
state = graph.get_state(config)
|
|
|
|
# Fix state and re-execute
|
|
graph.update_state(config, {"error_fixed": True})
|
|
graph.invoke(input, config)
|
|
```
|
|
|
|
### 3. A/B Testing
|
|
|
|
```python
|
|
# Base execution
|
|
base_result = graph.invoke(input, base_config)
|
|
|
|
# Alternative execution 1
|
|
alt_config_1 = base_config.copy()
|
|
alt_result_1 = graph.invoke(modified_input_1, alt_config_1)
|
|
|
|
# Alternative execution 2
|
|
alt_config_2 = base_config.copy()
|
|
alt_result_2 = graph.invoke(modified_input_2, alt_config_2)
|
|
|
|
# Compare results
|
|
```
|
|
|
|
### 4. Debugging and Tracing
|
|
|
|
```python
|
|
# Execute
|
|
graph.invoke(input, config)
|
|
|
|
# Check each step
|
|
for i, state in enumerate(graph.get_state_history(config)):
|
|
print(f"Step {i}:")
|
|
print(f" State: {state.values}")
|
|
print(f" Next: {state.next}")
|
|
```
|
|
|
|
## Important Considerations
|
|
|
|
### Thread ID Uniqueness
|
|
|
|
```python
|
|
# Use different thread_id per user
|
|
user_config = {"configurable": {"thread_id": f"user-{user_id}"}}
|
|
|
|
# Use different thread_id per conversation
|
|
conversation_config = {"configurable": {"thread_id": f"conv-{conv_id}"}}
|
|
```
|
|
|
|
### Checkpoint Cleanup
|
|
|
|
```python
|
|
# Delete old checkpoints (implementation-dependent)
|
|
checkpointer.cleanup(before_timestamp=old_timestamp)
|
|
```
|
|
|
|
### Multi-user Support
|
|
|
|
```python
|
|
# Combine user ID and session ID
|
|
def get_config(user_id: str, session_id: str):
|
|
return {
|
|
"configurable": {
|
|
"thread_id": f"{user_id}-{session_id}"
|
|
}
|
|
}
|
|
|
|
config = get_config("user123", "session456")
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Meaningful thread_id**: Format that can identify user, session, conversation
|
|
2. **Regular Cleanup**: Delete old checkpoints
|
|
3. **Appropriate Checkpointer**: Choose implementation based on use case
|
|
4. **Error Handling**: Properly handle errors when retrieving checkpoints
|
|
|
|
## Summary
|
|
|
|
Persistence enables **state persistence and restoration**, making conversation continuation, error recovery, and time travel possible.
|
|
|
|
## Related Pages
|
|
|
|
- [03_memory_management_checkpointer.md](03_memory_management_checkpointer.md) - Checkpointer implementation details
|
|
- [03_memory_management_store.md](03_memory_management_store.md) - Combining with long-term memory
|
|
- [05_advanced_features_human_in_the_loop.md](05_advanced_features_human_in_the_loop.md) - Applications of state inspection
|