Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add tool use for Command concepts #2669

Merged
merged 10 commits into from
Dec 10, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 24 additions & 30 deletions docs/docs/concepts/low_level.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,50 +339,44 @@ def my_node(state: State) -> Command[Literal["my_other_node"]]:
)
```

`Command` has the following properties:

| Property | Description |
| --- | --- |
| `graph` | Graph to send the command to. Supported values:<br>- `None`: the current graph (default)<br>- `Command.PARENT`: closest parent graph |
| `update` | Update to apply to the graph's state. |
| `resume` | Value to resume execution with. To be used together with [`interrupt()`][langgraph.types.interrupt]. |
| `goto` | Can be one of the following:<br>- name of the node to navigate to next (any node that belongs to the specified `graph`)<br>- sequence of node names to navigate to next<br>- `Send` object (to execute a node with the input provided)<br>- sequence of `Send` objects<br>If `goto` is not specified and there are no other tasks left in the graph, the graph will halt after executing the current superstep. |
With `Command` you can also achieve dynamic control flow behavior (identical to [conditional edges](#conditional-edges)):

```python
from langgraph.graph import StateGraph, START
from langgraph.types import Command
from typing_extensions import Literal, TypedDict
def my_node(state: State) -> Command[Literal["my_other_node"]]:
if state["foo"] == "bar":
return Command(update={"foo": "baz"}, goto="my_other_node")
```

class State(TypedDict):
foo: str
!!! important

def my_node(state: State) -> Command[Literal["my_other_node"]]:
return Command(update={"foo": "bar"}, goto="my_other_node")
When returning `Command` in your node functions, you must add return type annotations with the list of node names the node is routing to, e.g. `Command[Literal["my_other_node"]]`. This is necessary for the graph compilation and rendering, and tells LangGraph that `my_node` can navigate to `my_other_node`.

Check out this [how-to guide](../how-tos/command.ipynb) for an end-to-end example of how to use `Command`.

def my_other_node(state: State):
return {"foo": state["foo"] + "baz"}
### Human-in-the-loop
vbarda marked this conversation as resolved.
Show resolved Hide resolved

builder = StateGraph(State)
builder.add_edge(START, "my_node")
builder.add_node("my_node", my_node)
builder.add_node("my_other_node", my_other_node)
`Command` is an important part of human-in-the-loop workflows: when using `interrupt()` to collect user input, `Command` is then used to supply the input and resume execution via `Command(resume="User input")`. Check out [this conceptual guide](./human_in_the_loop.md) for more information.

graph = builder.compile()
```
### Using inside the tools
vbarda marked this conversation as resolved.
Show resolved Hide resolved
vbarda marked this conversation as resolved.
Show resolved Hide resolved

With `Command` you can also achieve dynamic control flow behavior (identical to [conditional edges](#conditional-edges)):
A common use case is updating graph state from inside a tool. For example, in a customer support application you might want to look up customer account number or ID in the beginning of the conversation. To update the graph state from the tool, you can return `Command(update={"my_custom_key": "foo", "messages": [...]})` from the tool:

```python
def my_node(state: State) -> Command[Literal["my_other_node"]]:
if state["foo"] == "bar":
return Command(update={"foo": "baz"}, goto="my_other_node")
@tool
def lookup_customer_id(customer_name: str):
vbarda marked this conversation as resolved.
Show resolved Hide resolved
customer_id = get_customer_id(customer_name)
return Command(update={
# update the state key
"customer_id": customer_id,
# update the message history
"messages": [ToolMessage(content="Successfully looked up customer ID")]
})
```

!!! important
You MUST include `messages` (or any state key used for the message history) in `Command.update` when returning `Command` from a tool. This is necessary for the resulting message history to be valid (LLM providers require AI messages with tool calls to be followed by the tool result messages).

When returning `Command` in your node functions, you must add return type annotations with the list of node names the node is routing to, e.g. `Command[Literal["node_b", "node_c"]]`. This is necessary for the graph compilation and rendering, and tells LangGraph that `node_a` can navigate to `node_b` and `node_c`.

Check out this [how-to guide](../how-tos/command.ipynb) for an end-to-end example of how to use `Command`.
If you are using tools that update state via `Command`, we recommend using prebuilt [`ToolNode`][langgraph.prebuilt.tool_node.ToolNode] which automatically handles tools returning `Command` objects and propagates them to the graph state. If you're writing a custom node that calls tools, you would need to manually propagate `Command` objects returned by the tools as the update from node.

## Persistence

Expand Down
Loading