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

6.7 KiB

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

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

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

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

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

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

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

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

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

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

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

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

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

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.