This commit is contained in:
Eugene Yurtsev
2025-06-17 13:08:37 -04:00
parent ad6dd59d53
commit 3fddba5106
6 changed files with 1 additions and 847 deletions
-243
View File
@@ -11,8 +11,6 @@ hide:
This guide shows you how to set up and use LangGraph's **prebuilt**, **reusable** components, which are designed to help you construct agentic systems quickly and reliably.
:::python
## Prerequisites
Before you start this tutorial, ensure you have the following:
@@ -230,244 +228,3 @@ response["structured_response"]
- [Deploy your agent locally](../tutorials/langgraph-platform/local-server.md)
- [Learn more about prebuilt agents](../agents/overview.md)
- [LangGraph Platform quickstart](../cloud/quick_start.md)
:::
:::js
## Prerequisites
Before you start this tutorial, ensure you have the following:
- An [Anthropic](https://console.anthropic.com/settings/keys) API key
## 1. Install dependencies
If you haven't already, install LangGraph and LangChain:
```
npm install langchain @langchain/langgraph @langchain/anthropic
```
## 2. Create an agent
Use [`createReactAgent`][create_react_agent] to instantiate an agent:
```ts
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { initChatModel } from "langchain/chat_models/universal";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const getWeather = tool( // (1)!
async (input: { city: string }) => {
return `It's always sunny in ${input.city}!`;
},
{
name: "getWeather",
schema: z.object({
city: z.string().describe("The city to get the weather for"),
}),
description: "Get weather for a given city.",
}
);
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest"); // (2)!
const agent = createReactAgent({
llm,
tools: [getWeather], // (3)!
prompt: "You are a helpful assistant", // (4)!
});
// Run the agent
await agent.invoke({
messages: [{ role: "user", content: "what is the weather in sf" }],
});
```
1. Define a tool for the agent to use. For more advanced tool usage and customization, check the [tools](./tools.md) page.
2. Provide a language model for the agent to use. To learn more about configuring language models for the agents, check the [models](./models.md) page.
3. Provide a list of tools for the model to use.
4. Provide a system prompt (instructions) to the language model used by the agent.
## 3. Configure an LLM
Use [`initChatModel`](https://api.js.langchain.com/functions/langchain.chat_models_universal.initChatModel.html) to configure an LLM with specific parameters, such as temperature:
```ts
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { initChatModel } from "langchain/chat_models/universal";
// highlight-next-line
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest", {
// highlight-next-line
temperature: 0,
});
const agent = createReactAgent({
// highlight-next-line
llm,
tools: [getWeather],
});
```
See the [models](./models.md) page for more information on how to configure LLMs.
## 4. Add a custom prompt
Prompts instruct the LLM how to behave. They can be:
- **Static**: A string is interpreted as a **system message**
- **Dynamic**: a list of messages generated at **runtime** based on input or configuration
=== "Static prompt"
Define a fixed prompt string or list of messages.
```ts
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { initChatModel } from "langchain/chat_models/universal";
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getWeather],
// A static prompt that never changes
// highlight-next-line
prompt: "Never answer questions about the weather.",
});
await agent.invoke({
messages: "what is the weather in sf",
});
```
=== "Dynamic prompt"
Define a function that returns a message list based on the agent's state and configuration:
```ts
import { BaseMessageLike } from "@langchain/core/messages";
import { RunnableConfig } from "@langchain/core/runnables";
import { initChatModel } from "langchain/chat_models/universal";
import { MessagesAnnotation } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
const prompt = (
state: typeof MessagesAnnotation.State,
config: RunnableConfig
): BaseMessageLike[] => { // (1)!
const userName = config.configurable?.userName;
const systemMsg = `You are a helpful assistant. Address the user as ${userName}.`;
return [{ role: "system", content: systemMsg }, ...state.messages];
};
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getWeather],
// highlight-next-line
prompt,
});
await agent.invoke(
{ messages: [{ role: "user", content: "what is the weather in sf" }] },
// highlight-next-line
{ configurable: { userName: "John Smith" } }
);
```
1. Dynamic prompts allow including non-message [context](./context.md) when constructing an input to the LLM, such as:
- Information passed at runtime, like a `userId` or API credentials (using `config`).
- Internal agent state updated during a multi-step reasoning process (using `state`).
Dynamic prompts can be defined as functions that take `state` and `config` and return a list of messages to send to the LLM.
For more information, see [Context](./context.md).
## 5. Add memory
To allow multi-turn conversations with an agent, you need to enable [persistence](../concepts/persistence.md) by providing a `checkpointer` when creating an agent. At runtime you need to provide a config containing `thread_id` — a unique identifier for the conversation (session):
```ts
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { MemorySaver } from "@langchain/langgraph-checkpoint";
import { initChatModel } from "langchain/chat_models/universal";
// highlight-next-line
const checkpointer = new MemorySaver();
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getWeather],
// highlight-next-line
checkpointer, // (1)!
});
// Run the agent
// highlight-next-line
const config = { configurable: { thread_id: "1" } };
const sfResponse = await agent.invoke(
{ messages: [{ role: "user", content: "what is the weather in sf" }] },
config // (2)!
);
const nyResponse = await agent.invoke(
{ messages: [{ role: "user", content: "what about new york?" }] },
config
);
```
1. `checkpointer` allows the agent to store its state at every step in the tool calling loop. This enables [short-term memory](./memory.md#short-term-memory) and [human-in-the-loop](./human-in-the-loop.md) capabilities.
2. Pass configuration with `thread_id` to be able to resume the same conversation on future agent invocations.
When you enable the checkpointer, it stores agent state at every step in the provided checkpointer database (or in memory, if using `InMemorySaver`).
Note that in the above example, when the agent is invoked the second time with the same `thread_id`, the original message history from the first conversation is automatically included, together with the new user input.
For more information, see [Memory](./memory.md).
## 6. Configure structured output
To produce structured responses conforming to a schema, use the `responseFormat` parameter. The schema can be defined with a `zod` schema. The result will be accessible via the `structuredResponse` field.
```ts
import { z } from "zod";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { initChatModel } from "langchain/chat_models/universal";
const WeatherResponse = z.object({
conditions: z.string(),
});
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getWeather],
// highlight-next-line
responseFormat: WeatherResponse, // (1)!
});
const response = await agent.invoke({
messages: [{ role: "user", content: "what is the weather in sf" }],
});
// highlight-next-line
response.structuredResponse;
```
1. When `responseFormat` is provided, a separate step is added at the end of the agent loop: agent message history is passed to an LLM with structured output to generate a structured response.
To provide a system prompt to this LLM, use an object `{ prompt, schema }`, e.g., `responseFormat: { prompt, schema: WeatherResponse }`.
!!! Note "LLM post-processing"
Structured output requires an additional call to the LLM to format the response according to the schema.
## Next steps
- [Deploy your agent locally](../tutorials/langgraph-platform/local-server.md)
- [Learn more about prebuilt agents](../agents/overview.md)
- [LangGraph Platform quickstart](../cloud/quick_start.md)
:::
-281
View File
@@ -43,8 +43,6 @@ when you have values that don't change mid-run.
Specify configuration using a key called **"configurable"** which is reserved
for this purpose:
:::python
```python
agent.invoke(
{"messages": [{"role": "user", "content": "hi!"}]},
@@ -52,23 +50,11 @@ agent.invoke(
config={"configurable": {"user_id": "user_123"}}
)
```
:::
:::js
```ts
await agent.invoke(
{ messages: "hi!" },
// highlight-next-line
{ configurable: { userId: "user_123" } }
)
```
:::
### State (mutable context)
State acts as short-term memory during a run. It holds dynamic data that can evolve during execution, such as values derived from tools or LLM outputs.
:::python
```python
class CustomState(AgentState):
# highlight-next-line
@@ -85,29 +71,6 @@ agent.invoke({
"user_name": "Jane"
})
```
:::
:::js
```ts
const CustomState = Annotation.Root({
...MessagesAnnotation.spec,
userName: Annotation<string>,
});
const agent = createReactAgent({
// Other agent parameters...
// highlight-next-line
stateSchema: CustomState,
})
await agent.invoke(
// highlight-next-line
{ messages: "hi!", userName: "Jane" }
)
```
:::
!!! tip "Turning on memory"
@@ -130,8 +93,6 @@ Common use cases:
- Role or goal customization
- Conditional behavior (e.g., user is admin)
:::python
=== "Using config"
```python
@@ -201,90 +162,8 @@ Common use cases:
})
```
:::
:::js
=== "Using config"
```ts
import { BaseMessageLike } from "@langchain/core/messages";
import { RunnableConfig } from "@langchain/core/runnables";
import { initChatModel } from "langchain/chat_models/universal";
import { MessagesAnnotation } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
const prompt = (
state: typeof MessagesAnnotation.State,
// highlight-next-line
config: RunnableConfig
): BaseMessageLike[] => {
// highlight-next-line
const userName = config.configurable?.userName;
const systemMsg = `You are a helpful assistant. Address the user as ${userName}.`;
return [{ role: "system", content: systemMsg }, ...state.messages];
};
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getWeather],
// highlight-next-line
prompt
});
await agent.invoke(
{ messages: "hi!" },
// highlight-next-line
{ configurable: { userName: "John Smith" } }
);
```
=== "Using state"
```ts
import { BaseMessageLike } from "@langchain/core/messages";
import { RunnableConfig } from "@langchain/core/runnables";
import { initChatModel } from "langchain/chat_models/universal";
import { Annotation, MessagesAnnotation } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
const CustomState = Annotation.Root({
...MessagesAnnotation.spec,
// highlight-next-line
userName: Annotation<string>,
});
const prompt = (
// highlight-next-line
state: typeof CustomState.State,
): BaseMessageLike[] => {
// highlight-next-line
const userName = state.userName;
const systemMsg = `You are a helpful assistant. Address the user as ${userName}.`;
return [{ role: "system", content: systemMsg }, ...state.messages];
};
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getWeather],
// highlight-next-line
prompt,
// highlight-next-line
stateSchema: CustomState,
});
await agent.invoke(
// highlight-next-line
{ messages: "hi!", userName: "John Smith" },
);
```
:::
## Accessing Context in Tools { #tools }
:::python
Tools can access context through special parameter **annotations**.
* Use `RunnableConfig` for config access
@@ -351,167 +230,7 @@ Tools can access context through special parameter **annotations**.
"user_id": "user_123"
})
```
:::
:::js
Tools can access context through:
* Use `RunnableConfig` for config access
* Use `getCurrentTaskInput()` for agent state
=== "Using config"
```ts
import { RunnableConfig } from "@langchain/core/runnables";
import { initChatModel } from "langchain/chat_models/universal";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const getUserInfo = tool(
async (input: Record<string, any>, config: RunnableConfig) => {
// highlight-next-line
const userId = config.configurable?.userId;
return userId === "user_123" ? "User is John Smith" : "Unknown user";
},
{
name: "get_user_info",
description: "Look up user info.",
schema: z.object({}),
}
);
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getUserInfo],
});
await agent.invoke(
{ messages: "look up user information" },
// highlight-next-line
{ configurable: { userId: "user_123" } }
);
```
=== "Using state"
```ts
import { initChatModel } from "langchain/chat_models/universal";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { Annotation, MessagesAnnotation, getCurrentTaskInput } from "@langchain/langgraph";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
const CustomState = Annotation.Root({
...MessagesAnnotation.spec,
// highlight-next-line
userId: Annotation<string>(),
});
const getUserInfo = tool(
async (
input: Record<string, any>,
) => {
// highlight-next-line
const state = getCurrentTaskInput() as typeof CustomState.State;
// highlight-next-line
const userId = state.userId;
return userId === "user_123" ? "User is John Smith" : "Unknown user";
},
{
name: "get_user_info",
description: "Look up user info.",
schema: z.object({})
}
);
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getUserInfo],
// highlight-next-line
stateSchema: CustomState,
});
await agent.invoke(
// highlight-next-line
{ messages: "look up user information", userId: "user_123" }
);
```
:::
### Update Context from Tools
:::python
Tools can update agent's context (state and long-term memory) during execution. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts. See [Memory](./memory.md#read-short-term) guide for more information.
:::
:::js
Tools can modify the agent's state during execution. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts.
```ts
import { Annotation, MessagesAnnotation, LangGraphRunnableConfig, Command } from "@langchain/langgraph";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { ToolMessage } from "@langchain/core/messages";
import { initChatModel } from "langchain/chat_models/universal";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
const CustomState = Annotation.Root({
...MessagesAnnotation.spec,
// highlight-next-line
userName: Annotation<string>(), // Will be updated by the tool
});
const getUserInfo = tool(
async (
_input: Record<string, never>,
config: LangGraphRunnableConfig
): Promise<Command> => {
const userId = config.configurable?.userId;
if (!userId) {
throw new Error("Please provide a user id in config.configurable");
}
const toolCallId = config.toolCall?.id;
const name = userId === "user_123" ? "John Smith" : "Unknown user";
// Return command to update state
return new Command({
update: {
// highlight-next-line
userName: name,
// Update the message history
// highlight-next-line
messages: [
new ToolMessage({
content: "Successfully looked up user information",
tool_call_id: toolCallId,
}),
],
},
});
},
{
name: "get_user_info",
description: "Look up user information.",
schema: z.object({}),
}
);
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [getUserInfo],
// highlight-next-line
stateSchema: CustomState,
});
await agent.invoke(
{ messages: "look up user information" },
// highlight-next-line
{ configurable: { userId: "user_123" } }
);
```
:::
-107
View File
@@ -27,8 +27,6 @@ A human can review and edit the output from the agent before proceeding. This is
</figure>
:::python
## Review tool calls
To add a human approval step to a tool:
@@ -36,7 +34,6 @@ To add a human approval step to a tool:
1. Use `interrupt()` in the tool to pause execution.
2. Resume with a `Command(resume=...)` to continue based on human input.
```python
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt
@@ -236,110 +233,6 @@ for chunk in agent.stream(
print("\n")
```
:::
:::js
## Review tool calls
To add a human approval step to a tool:
1. Use `interrupt()` in the tool to pause execution.
2. Resume with a `Command({ resume: ... })` to continue based on human input.
```ts
import { MemorySaver } from "@langchain/langgraph-checkpoint";
import { interrupt } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { initChatModel } from "langchain/chat_models/universal";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
// An example of a sensitive tool that requires human review / approval
const bookHotel = tool(
async (input: { hotelName: string; }) => {
let hotelName = input.hotelName;
// highlight-next-line
const response = interrupt( // (1)!
`Trying to call \`book_hotel\` with args {'hotel_name': ${hotelName}}. ` +
`Please approve or suggest edits.`
)
if (response.type === "accept") {
// proceed to execute the tool logic
} else if (response.type === "edit") {
hotelName = response.args["hotel_name"]
} else {
throw new Error(`Unknown response type: ${response.type}`)
}
return `Successfully booked a stay at ${hotelName}.`;
},
{
name: "bookHotel",
schema: z.object({
hotelName: z.string().describe("Hotel to book"),
}),
description: "Book a hotel.",
}
);
// highlight-next-line
const checkpointer = new MemorySaver(); // (2)!
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [bookHotel],
// highlight-next-line
checkpointer // (3)!
});
```
1. The [`interrupt` function][langgraph.types.interrupt] pauses the agent graph at a specific node. In this case, we call `interrupt()` at the beginning of the tool function, which pauses the graph at the node that executes the tool. The information inside `interrupt()` (e.g., tool calls) can be presented to a human, and the graph can be resumed with the user input (tool call approval, edit or feedback).
2. The `InMemorySaver` is used to store the agent state at every step in the tool calling loop. This enables [short-term memory](./memory.md#short-term-memory) and [human-in-the-loop](./human-in-the-loop.md) capabilities. In this example, we use `InMemorySaver` to store the agent state in memory. In a production application, the agent state will be stored in a database.
3. Initialize the agent with the `checkpointer`.
Run the agent with the `stream()` method, passing the `config` object to specify the thread ID. This allows the agent to resume the same conversation on future invocations.
```ts
const config = {
configurable: {
// highlight-next-line
"thread_id": "1"
}
}
for await (const chunk of await agent.stream(
{ messages: "book a stay at McKittrick hotel" },
// highlight-next-line
config
)) {
console.log(chunk);
console.log("\n");
};
```
> You should see that the agent runs until it reaches the `interrupt()` call, at which point it pauses and waits for human input.
Resume the agent with a `Command({ resume: ... })` to continue based on human input.
```ts
import { Command } from "@langchain/langgraph";
for await (const chunk of await agent.stream(
new Command({ resume: { type: "accept" } }), // (1)!
// new Command({ resume: { type: "edit", args: { "hotel_name": "McKittrick Hotel" } } }),
// highlight-next-line
config
)) {
console.log(chunk);
console.log("\n");
};
```
1. The [`interrupt` function][langgraph.types.interrupt] is used in conjunction with the [`Command`][langgraph.types.Command] object to resume the graph with a value provided by the human.
:::
## Additional resources
* [Human-in-the-loop in LangGraph](../concepts/human_in_the_loop.md)
+1 -54
View File
@@ -13,8 +13,6 @@ hide:
![MCP](./assets/mcp.png)
:::python
Install the `langchain-mcp-adapters` library to use MCP tools in LangGraph:
```bash
@@ -60,57 +58,6 @@ weather_response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
)
```
:::
:::js
Install the `@langchain/mcp-adapters` library to use MCP tools in LangGraph:
```bash
npm install @langchain/mcp-adapters
```
## Use MCP tools
The `@langchain/mcp-adapters` package enables agents to use tools defined across one or more MCP servers.
```ts
// highlight-next-line
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
import { initChatModel } from "langchain/chat_models/universal";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
// highlight-next-line
const client = new MultiServerMCPClient({
mcpServers: {
"math": {
command: "python",
// Replace with absolute path to your math_server.py file
args: ["/path/to/math_server.py"],
transport: "stdio",
},
"weather": {
// Ensure your start your weather server on port 8000
url: "http://localhost:8000/sse",
transport: "sse",
}
}
})
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
// highlight-next-line
tools: await client.getTools()
});
const mathResponse = await agent.invoke(
{ messages: [ { role: "user", content: "what's (3 + 5) x 12?" } ] }
);
const weatherResponse = await agent.invoke(
{ messages: [ { role: "user", content: "what is the weather in nyc?" } ] }
);
await client.close();
```
:::
## Custom MCP servers
@@ -159,4 +106,4 @@ if __name__ == "__main__":
## Additional resources
- [MCP documentation](https://modelcontextprotocol.io/introduction)
- [MCP Transport documentation](https://modelcontextprotocol.io/docs/concepts/transports)
- [MCP Transport documentation](https://modelcontextprotocol.io/docs/concepts/transports)
-160
View File
@@ -40,8 +40,6 @@ LangGraph comes with a set of prebuilt components that implement common agent be
Using LangGraph for agent development allows you to focus on your application's logic and behavior, instead of building and maintaining the supporting infrastructure for state, memory, and human feedback.
:::python
## Package ecosystem
The high-level components are organized into several packages, each with a specific focus.
@@ -191,161 +189,3 @@ function initializeWidget() {
window.addEventListener("DOMContentLoaded", initializeWidget);
document$.subscribe(initializeWidget);
</script>
:::
:::js
## Package ecosystem
The high-level components are organized into several packages, each with a specific focus.
| Package | Description | Installation |
|--------------------------|-----------------------------------------------------------------------------|----------------------------------------------------|
| `langgraph` | Prebuilt components to [**create agents**](./agents.md) | `npm install @langchain/langgraph @langchain/core` |
| `langgraph-supervisor` | Tools for building [**supervisor**](./multi-agent.md#supervisor) agents | `npm install @langchain/langgraph-supervisor` |
| `langgraph-swarm` | Tools for building a [**swarm**](./multi-agent.md#swarm) multi-agent system | `npm install @langchain/langgraph-swarm` |
| `langchain-mcp-adapters` | Interfaces to [**MCP servers**](./mcp.md) for tool and resource integration | `npm install @langchain/mcp-adapters` |
| `agentevals` | Utilities to [**evaluate agent performance**](./evals.md) | `npm install agentevals` |
## Visualize an agent graph
Use the following tool to visualize the graph generated by [`createReactAgent`][create_react_agent] and to view an outline of the corresponding code. It allows you to explore the infrastructure of the agent as defined by the presence of:
- [`tools`](./tools.md): A list of tools (functions, APIs, or other callable objects) that the agent can use to perform tasks.
- `preModelHook`: A function that is called before the model is invoked. It can be used to condense messages or perform other preprocessing tasks.
- `postModelHook`: A function that is called after the model is invoked. It can be used to implement guardrails, human-in-the-loop flows, or other postprocessing tasks.
- [`responseFormat`](./agents.md#6-configure-structured-output): A data structure used to constrain the type of the final output (via Zod schemas).
<div class="agent-layout">
<div class="agent-graph-features-container">
<div class="agent-graph-features">
<h3 class="agent-section-title">Features</h3>
<label><input type="checkbox" id="tools" checked> <code>tools</code></label>
<label><input type="checkbox" id="preModelHook"> <code>preModelHook</code></label>
<label><input type="checkbox" id="postModelHook"> <code>postModelHook</code></label>
<label><input type="checkbox" id="responseFormat"> <code>responseFormat</code></label>
</div>
</div>
<div class="agent-graph-container">
<h3 class="agent-section-title">Graph</h3>
<img id="agent-graph-img" src="../assets/react_agent_graphs/0001.svg" alt="graph image" style="max-width: 100%;"/>
</div>
</div>
The following code snippet shows how to create the above agent (and underlying graph) with [`createReactAgent`][create_react_agent]:
```typescript
<div class="language-typescript">
<pre><code id="agent-code" class="language-typescript"></code></pre>
</div>
<script>
function getCheckedValue(id) {
return document.getElementById(id).checked ? "1" : "0";
}
function getKey() {
return [
getCheckedValue("responseFormat"),
getCheckedValue("postModelHook"),
getCheckedValue("preModelHook"),
getCheckedValue("tools")
].join("");
}
function dedent(strings, ...values) {
const str = String.raw({ raw: strings }, ...values)
const [space] = str.split("\n").filter(Boolean).at(0).match(/^(\s*)/)
const spaceLen = space.length
return str.split("\n").map(line => line.slice(spaceLen)).join("\n").trim()
}
Object.assign(dedent, {
offset: (size) => (strings, ...values) => {
return dedent(strings, ...values).split("\n").map(line => " ".repeat(size) + line).join("\n")
}
})
function generateCodeSnippet({ tools, pre, post, response }) {
const lines = []
lines.push(dedent`
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";
`)
if (tools) lines.push(`import { tool } from "@langchain/core/tools";`);
if (response || tools) lines.push(`import { z } from "zod";`);
lines.push("", dedent`
const agent = createReactAgent({
llm: new ChatOpenAI({ model: "o4-mini" }),
`)
if (tools) {
lines.push(dedent.offset(2)`
tools: [
tool(() => "Sample tool output", {
name: "sampleTool",
schema: z.object({}),
}),
],
`)
}
if (pre) {
lines.push(dedent.offset(2)`
preModelHook: (state) => ({ llmInputMessages: state.messages }),
`)
}
if (post) {
lines.push(dedent.offset(2)`
postModelHook: (state) => state,
`)
}
if (response) {
lines.push(dedent.offset(2)`
responseFormat: z.object({ result: z.string() }),
`)
}
lines.push(`});`);
return lines.join("\n");
}
function render() {
const key = getKey();
document.getElementById("agent-graph-img").src = `../assets/react_agent_graphs/${key}.svg`;
const state = {
tools: document.getElementById("tools").checked,
pre: document.getElementById("preModelHook").checked,
post: document.getElementById("postModelHook").checked,
response: document.getElementById("responseFormat").checked
};
document.getElementById("agent-code").textContent = generateCodeSnippet(state);
}
function initializeWidget() {
render(); // no need for `await` here
document.querySelectorAll(".agent-graph-features input").forEach((input) => {
input.addEventListener("change", render);
});
}
// Init for both full reload and SPA nav (used by MkDocs Material)
window.addEventListener("DOMContentLoaded", initializeWidget);
document$.subscribe(initializeWidget);
</script>
:::
-2
View File
@@ -23,7 +23,6 @@ Then, navigate to [Agent Chat UI](https://agentchat.vercel.app), or clone the re
UI has out-of-box support for rendering tool calls, and tool result messages. To customize what messages are shown, see the [Hiding Messages in the Chat](https://github.com/langchain-ai/agent-chat-ui?tab=readme-ov-file#hiding-messages-in-the-chat) section in the Agent Chat UI documentation.
:::python
## Add human-in-the-loop
Agent Chat UI has full support for [human-in-the-loop](../concepts/human_in_the_loop.md) workflows. To try it out, replace the agent code in `src/agent/graph.py` (from the [deployment](./deployment.md) guide) with this [agent implementation](./human-in-the-loop.md#using-with-agent-inbox):
@@ -33,7 +32,6 @@ Agent Chat UI has full support for [human-in-the-loop](../concepts/human_in_the_
!!! Important
Agent Chat UI works best if your LangGraph agent interrupts using the [`HumanInterrupt` schema][langgraph.prebuilt.interrupt.HumanInterrupt]. If you do not use that schema, the Agent Chat UI will be able to render the input passed to the `interrupt` function, but it will not have full support for resuming your graph.
:::
## Generative UI