Files
gh-hiroshi75-protografico-p…/skills/langgraph-master/04_tool_integration_command_api.md
2025-11-29 18:45:58 +08:00

281 lines
6.7 KiB
Markdown

# Command API
An advanced API that integrates state updates and control flow.
## Overview
The Command API is a feature that allows nodes to specify **state updates** and **control flow** simultaneously.
## Basic Usage
```python
from langgraph.types import Command
def decision_node(state: State) -> Command:
"""Update state and specify the next node"""
result = analyze(state["data"])
if result["confidence"] > 0.8:
return Command(
update={"result": result, "confident": True},
goto="finalize"
)
else:
return Command(
update={"result": result, "confident": False},
goto="review"
)
```
## Command Object Parameters
```python
Command(
update: dict, # Updates to state
goto: str | list[str], # Next node(s) (single or multiple)
graph: str | None = None # For subgraph navigation
)
```
## vs Traditional State Updates
### Traditional Method
```python
def node(state: State) -> dict:
return {"result": "value"}
# Control flow in edges
def route(state: State) -> str:
if state["result"] == "value":
return "next_node"
return "other_node"
builder.add_conditional_edges("node", route, {...})
```
### Command API
```python
def node(state: State) -> Command:
return Command(
update={"result": "value"},
goto="next_node" # Specify control flow as well
)
# No edges needed (Command controls flow)
```
## Advanced Patterns
### Pattern 1: Conditional Branching
```python
def validator(state: State) -> Command:
"""Validate and determine next node"""
is_valid = validate(state["data"])
if is_valid:
return Command(
update={"valid": True},
goto="process"
)
else:
return Command(
update={"valid": False, "errors": get_errors(state["data"])},
goto="error_handler"
)
```
### Pattern 2: Parallel Execution
```python
def fan_out_node(state: State) -> Command:
"""Branch to multiple nodes in parallel"""
return Command(
update={"started": True},
goto=["worker_a", "worker_b", "worker_c"] # Parallel execution
)
```
### Pattern 3: Loop Control
```python
def iterator_node(state: State) -> Command:
"""Iterative processing"""
iteration = state.get("iteration", 0) + 1
result = process_iteration(state["data"], iteration)
if iteration < state["max_iterations"] and not result["done"]:
return Command(
update={"iteration": iteration, "result": result},
goto="iterator_node" # Loop back to self
)
else:
return Command(
update={"final_result": result},
goto=END
)
```
### Pattern 4: Subgraph Navigation
```python
def sub_node(state: State) -> Command:
"""Navigate from subgraph to parent graph"""
result = process(state["data"])
if need_parent_intervention(result):
return Command(
update={"sub_result": result},
goto="parent_handler",
graph=Command.PARENT # Navigate to parent graph
)
return {"sub_result": result}
```
## Integration with Tools
### Control After Tool Execution
```python
def tool_node_with_command(state: MessagesState) -> Command:
"""Determine next action after tool execution"""
last_message = state["messages"][-1]
tool_results = []
for tool_call in last_message.tool_calls:
tool = tool_map[tool_call["name"]]
result = tool.invoke(tool_call["args"])
tool_results.append(
ToolMessage(
content=str(result),
tool_call_id=tool_call["id"]
)
)
# Determine next node based on results
if any("error" in r.content.lower() for r in tool_results):
return Command(
update={"messages": tool_results},
goto="error_handler"
)
else:
return Command(
update={"messages": tool_results},
goto="agent"
)
```
### Command from Within Tools
```python
from langgraph.types import interrupt
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send email (with approval)"""
# Request approval
approved = interrupt({
"action": "send_email",
"to": to,
"subject": subject,
"message": "Approve sending this email?"
})
if approved:
result = actually_send_email(to, subject, body)
return f"Email sent to {to}"
else:
return "Email cancelled by user"
```
## Dynamic Routing
```python
def dynamic_router(state: State) -> Command:
"""Dynamically select route based on state"""
score = evaluate(state["data"])
# Select route based on score
if score > 0.9:
route = "expert_handler"
elif score > 0.7:
route = "standard_handler"
else:
route = "basic_handler"
return Command(
update={"confidence_score": score},
goto=route
)
```
## Error Recovery
```python
def processor_with_fallback(state: State) -> Command:
"""Fallback on error"""
try:
result = risky_operation(state["data"])
return Command(
update={"result": result, "error": None},
goto="success_handler"
)
except Exception as e:
return Command(
update={"error": str(e)},
goto="fallback_handler"
)
```
## State Machine Implementation
```python
def state_machine_node(state: State) -> Command:
"""State machine"""
current_state = state.get("state", "initial")
transitions = {
"initial": ("validate", {"state": "validating"}),
"validating": ("process" if state.get("valid") else "error", {"state": "processing"}),
"processing": ("finalize", {"state": "finalizing"}),
"finalizing": (END, {"state": "done"})
}
next_node, update = transitions[current_state]
return Command(
update=update,
goto=next_node
)
```
## Benefits
**Conciseness**: Define state updates and control flow in one place
**Readability**: Node intent is clear
**Flexibility**: Dynamic routing is easier
**Debugging**: Control flow is easier to track
## Considerations
⚠️ **Complexity**: Avoid overly complex conditional branching
⚠️ **Testing**: All branches need to be tested
⚠️ **Parallel Execution**: Order of parallel nodes is non-deterministic
## Summary
The Command API integrates state updates and control flow, enabling more flexible and readable graph construction.
## Related Pages
- [01_core_concepts_node.md](01_core_concepts_node.md) - Node basics
- [01_core_concepts_edge.md](01_core_concepts_edge.md) - Comparison with edges
- [02_graph_architecture_subgraph.md](02_graph_architecture_subgraph.md) - Subgraph navigation