Skip to content

Commit

Permalink
docs: update concepts/howto for Command (#800)
Browse files Browse the repository at this point in the history
Co-authored-by: jacoblee93 <[email protected]>
  • Loading branch information
vbarda and jacoblee93 authored Jan 29, 2025
1 parent fbe24a2 commit 6c80e18
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 5 deletions.
53 changes: 53 additions & 0 deletions docs/docs/concepts/low_level.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,59 @@ Use `Command` when you need to **both** update the graph state **and** route to

Use [conditional edges](#conditional-edges) to route between nodes conditionally without updating the state.

### Navigating to a node in a parent graph

If you are using [subgraphs](#subgraphs), you might want to navigate from a node a subgraph to a different subgraph (i.e. a different node in the parent graph). To do so, you can specify `graph: Command.PARENT` in `Command`:

```ts
const myNode = (state: typeof StateAnnotation.State) => {
return new Command({
update: { foo: "bar" },
goto: "other_subgraph", // where `other_subgraph` is a node in the parent graph
graph: Command.PARENT,
});
};
```

!!! note

Setting `graph` to `Command.PARENT` will navigate to the closest parent graph.

This is particularly useful when implementing [multi-agent handoffs](./multi_agent.md#handoffs).

### Using inside tools

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 information based on their 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:

```ts
import { tool } from "@langchain/core/tools";

const lookupUserInfo = tool(async (input, config) => {
const userInfo = getUserInfo(config);
return new Command({
// update state keys
update: {
user_info: userInfo,
messages: [
new ToolMessage({
content: "Successfully looked up user information",
tool_call_id: config.toolCall.id,
}),
],
},
});
}, {
name: "lookup_user_info",
description: "Use this to look up user information to better assist them with their questions.",
schema: z.object(...)
});
```

!!! important
You MUST include `messages` (or any state key used for the message history) in `Command.update` when returning `Command` from a tool and the list of messages in `messages` MUST contain a `ToolMessage`. 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).

If you are using tools that update state via `Command`, we recommend using prebuilt [`ToolNode`](/langgraphjs/reference/classes/langgraph_prebuilt.ToolNode.html) 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.

### Human-in-the-loop

`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 `new Command({ resume: "User input" })`. Check out [this conceptual guide](/langgraphjs/concepts/human_in_the_loop) for more information.
Expand Down
79 changes: 75 additions & 4 deletions examples/how-tos/command.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@
"};\n",
"```\n",
"\n",
"If you are using [subgraphs](/langgraphjs/concepts/low_level/#subgraphs), you might want to navigate from a node a subgraph to a different subgraph (i.e. a different node in the parent graph). To do so, you can specify `graph: Command.PARENT` in Command:\n",
"\n",
"```ts\n",
"const myNode = (state: typeof StateAnnotation.State) => {\n",
" return new Command({\n",
" update: { foo: \"bar\" },\n",
" goto: \"other_subgraph\", // where `other_subgraph` is a node in the parent graph\n",
" graph: Command.PARENT,\n",
" });\n",
"};\n",
"```\n",
"\n",
"This guide shows how you can use `Command` to add dynamic control flow in your LangGraph app."
]
},
Expand Down Expand Up @@ -146,7 +158,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 2,
"id": "d6711650-4380-4551-a007-2805f49ab2d8",
"metadata": {},
"outputs": [],
Expand Down Expand Up @@ -179,7 +191,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 3,
"id": "eeb810e5-8822-4c09-8d53-c55cd0f5d42e",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -211,9 +223,39 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 4,
"id": "d88a5d9b-ee08-4ed4-9c65-6e868210bfac",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Called A\n",
"Called B\n",
"{ foo: 'a|b' }\n"
]
}
],
"source": [
"await graph.invoke({ foo: \"\" });"
]
},
{
"cell_type": "markdown",
"id": "01a73d81-4e12-4378-b07d-1c5bf0b2ed71",
"metadata": {},
"source": [
"## Navigating to a node in a parent graph\n",
"\n",
"Now let's demonstrate how you can navigate from inside a subgraph to a different node in a parent graph. We'll do so by changing `node_a` in the above example into a single-node graph that we'll add as a subgraph to our parent graph."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "c459cd94-457e-420e-a227-80b33d95def9",
"metadata": {},
"outputs": [
{
"name": "stdout",
Expand All @@ -226,7 +268,36 @@
}
],
"source": [
"await graph.invoke({ foo: \"\" });"
"// Define the nodes\n",
"const nodeASubgraph = async (_state: typeof StateAnnotation.State) => {\n",
" console.log(\"Called A\");\n",
" // this is a replacement for a real conditional edge function\n",
" const goto = Math.random() > .5 ? \"nodeB\" : \"nodeC\";\n",
" // note how Command allows you to BOTH update the graph state AND route to the next node\n",
" return new Command({\n",
" update: {\n",
" foo: \"a\",\n",
" },\n",
" goto,\n",
" // this tells LangGraph to navigate to node_b or node_c in the parent graph\n",
" // NOTE: this will navigate to the closest parent graph relative to the subgraph\n",
" graph: Command.PARENT,\n",
" });\n",
"};\n",
"\n",
"const subgraph = new StateGraph(StateAnnotation)\n",
" .addNode(\"nodeA\", nodeASubgraph)\n",
" .addEdge(\"__start__\", \"nodeA\")\n",
" .compile();\n",
"\n",
"const parentGraph= new StateGraph(StateAnnotation)\n",
" .addNode(\"subgraph\", subgraph, { ends: [\"nodeB\", \"nodeC\"] })\n",
" .addNode(\"nodeB\", nodeB)\n",
" .addNode(\"nodeC\", nodeC)\n",
" .addEdge(\"__start__\", \"subgraph\")\n",
" .compile();\n",
" \n",
"await parentGraph.invoke({ foo: \"\" });"
]
}
],
Expand Down
2 changes: 1 addition & 1 deletion examples/how-tos/update-state-from-tools.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
" messages: [\n",
" new ToolMessage({\n",
" content: \"Successfully looked up user information\",\n",
" tool_call_id: conifg.toolCall.id,\n",
" tool_call_id: config.toolCall.id,\n",
" }),\n",
" ],\n",
" },\n",
Expand Down

0 comments on commit 6c80e18

Please sign in to comment.