🦜🕸️LangGraph.js¶
⚡ 以图的形式构建语言代理 ⚡
概述¶
LangGraph.js 是一个用于构建具有 LLMs 的有状态、多参与者应用程序的库,用于创建代理和多代理工作流程。与其他 LLM 框架相比,它提供了以下核心优势:循环、可控性和持久性。LangGraph 允许您定义包含循环的流,这对大多数代理架构至关重要,使其有别于基于 DAG 的解决方案。作为一种非常底层的框架,它提供对应用程序流和状态的细粒度控制,这对创建可靠的代理至关重要。此外,LangGraph 包含内置持久性,支持高级的人机交互和内存功能。
LangGraph 灵感来自 Pregel 和 Apache Beam。公共接口借鉴了 NetworkX 的灵感。LangGraph 由 LangChain Inc 构建,LangChain Inc 是 LangChain 的创建者,但无需使用 LangChain 即可使用它。
主要功能¶
- 循环和分支:在您的应用程序中实现循环和条件语句。
- 持久性:在图中每个步骤之后自动保存状态。在任何点暂停和恢复图执行,以支持错误恢复、人机交互工作流、时间旅行等。
- 人机交互:中断图执行以批准或编辑代理计划的下一个操作。
- 流支持:在每个节点产生输出时流式传输输出(包括令牌流)。
- 与 LangChain 集成:LangGraph 与 LangChain.js 和 LangSmith 无缝集成(但不依赖于它们)。
安装¶
示例¶
LangGraph 的核心概念之一是状态。每次图执行都会创建一个状态,该状态在图中节点执行时在它们之间传递,并且每个节点在执行后使用其返回值更新此内部状态。图更新其内部状态的方式由选择的图类型或自定义函数定义。
让我们看一个可以使用搜索工具的代理示例。
首先安装所需的依赖项
然后设置所需的環境變數
可选,设置 LangSmith 以获得最佳的可观察性
现在让我们定义我们的代理
import { AIMessage, BaseMessage, HumanMessage } from "@langchain/core/messages";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph } from "@langchain/langgraph";
import { MemorySaver, Annotation } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
// Define the graph state
// See here for more info: https://github.langchain.ac.cn/langgraphjs/how-tos/define-state/
const StateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (x, y) => x.concat(y),
})
})
// Define the tools for the agent to use
const weatherTool = tool(async ({ query }) => {
// This is a placeholder for the actual implementation
if (query.toLowerCase().includes("sf") || query.toLowerCase().includes("san francisco")) {
return "It's 60 degrees and foggy."
}
return "It's 90 degrees and sunny."
}, {
name: "weather",
description:
"Call to get the current weather for a location.",
schema: z.object({
query: z.string().describe("The query to use in your search."),
}),
});
const tools = [weatherTool];
const toolNode = new ToolNode(tools);
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-20240620",
temperature: 0,
}).bindTools(tools);
// Define the function that determines whether to continue or not
// We can extract the state typing via `StateAnnotation.State`
function shouldContinue(state: typeof StateAnnotation.State) {
const messages = state.messages;
const lastMessage = messages[messages.length - 1] as AIMessage;
// If the LLM makes a tool call, then we route to the "tools" node
if (lastMessage.tool_calls?.length) {
return "tools";
}
// Otherwise, we stop (reply to the user)
return "__end__";
}
// Define the function that calls the model
async function callModel(state: typeof StateAnnotation.State) {
const messages = state.messages;
const response = await model.invoke(messages);
// We return a list, because this will get added to the existing list
return { messages: [response] };
}
// Define a new graph
const workflow = new StateGraph(StateAnnotation)
.addNode("agent", callModel)
.addNode("tools", toolNode)
.addEdge("__start__", "agent")
.addConditionalEdges("agent", shouldContinue)
.addEdge("tools", "agent");
// Initialize memory to persist state between graph runs
const checkpointer = new MemorySaver();
// Finally, we compile it!
// This compiles it into a LangChain Runnable.
// Note that we're (optionally) passing the memory when compiling the graph
const app = workflow.compile({ checkpointer });
// Use the Runnable
const finalState = await app.invoke(
{ messages: [new HumanMessage("what is the weather in sf")] },
{ configurable: { thread_id: "42" } }
);
console.log(finalState.messages[finalState.messages.length - 1].content);
这将输出
Based on the information I received, the current weather in San Francisco is:
Temperature: 60 degrees Fahrenheit
Conditions: Foggy
San Francisco is known for its foggy weather, especially during certain times of the year. The moderate temperature of 60°F (about 15.5°C) is quite typical for the city, which generally has mild weather year-round due to its coastal location.
Is there anything else you'd like to know about the weather in San Francisco or any other location?
现在,当我们传递相同的 "thread_id"
时,对话上下文通过保存的状态(即存储的消息列表)保留下来。
const nextState = await app.invoke(
{ messages: [new HumanMessage("what about ny")] },
{ configurable: { thread_id: "42" } }
);
console.log(nextState.messages[nextState.messages.length - 1].content);
Based on the information I received, the current weather in New York is:
Temperature: 90 degrees Fahrenheit (approximately 32.2 degrees Celsius)
Conditions: Sunny
New York is experiencing quite warm weather today. A temperature of 90°F is considered hot for most people, and it's significantly warmer than the San Francisco weather we just checked. The sunny conditions suggest it's a clear day without cloud cover, which can make it feel even warmer.
On a day like this in New York, it would be advisable for people to stay hydrated, seek shade when possible, and use sun protection if spending time outdoors.
Is there anything else you'd like to know about the weather in New York or any other location?
逐步分解¶
-
初始化模型和工具。
- 我们使用
ChatAnthropic
作为我们的 LLM。注意:我们需要确保模型知道它可以使用这些工具进行调用。我们可以通过将 LangChain 工具转换为 Anthropic 工具调用格式来实现这一点,方法是使用.bindTools()
方法。 - 我们定义了要使用的工具 - 本例中是一个天气工具。有关如何创建您自己的工具,请参阅 此处 的文档。
- 我们使用
-
使用状态初始化图。
- 我们通过传递状态接口 (
AgentState
) 来初始化图 (StateGraph
)。 StateAnnotation
对象定义了如何将每个节点的更新合并到图的状态中。
- 我们通过传递状态接口 (
-
定义图节点。
我们需要两个主要的节点
agent
节点:负责决定采取哪些(如果有)操作。tools
节点调用工具:如果代理决定采取操作,则此节点将执行该操作。
-
定义入口点和图边。
首先,我们需要设置图执行的入口点 -
agent
节点。然后我们定义一个普通边和一个条件边。条件边意味着目的地取决于图状态 (
AgentState
) 的内容。在我们的案例中,目的地直到代理 (LLM) 决定后才会知道。- 条件边:在调用代理后,我们应该:
- a. 如果代理说要采取操作,则运行工具,或者
- b. 如果代理没有要求运行工具,则结束(回复用户)
- 普通边:在调用工具后,图应该始终返回到代理以决定下一步操作。
- 条件边:在调用代理后,我们应该:
-
编译图。
- 当我们编译图时,我们将其转换为 LangChain 可运行对象,这会自动启用使用您的输入调用
.invoke()
、.stream()
和.batch()
。 - 我们还可以选择性地传递一个检查点对象以在图运行之间持久化状态,从而支持内存、人机交互工作流、时间旅行等。在我们的案例中,我们使用
MemorySaver
- 一个简单的内存中检查点。
- 当我们编译图时,我们将其转换为 LangChain 可运行对象,这会自动启用使用您的输入调用
-
执行图。
- LangGraph 将输入消息添加到内部状态,然后将状态传递到入口节点
"agent"
。 "agent"
节点执行,调用聊天模型。- 聊天模型返回一个
AIMessage
。LangGraph 将其添加到状态中。 -
图循环执行以下步骤,直到
AIMessage
上没有更多的tool_calls
- 如果
AIMessage
包含tool_calls
,则"tools"
节点执行。 "agent"
节点再次执行并返回一个AIMessage
。
- 如果
-
执行进展到特殊的
__end__
值并输出最终状态。因此,我们获得所有聊天消息的列表作为输出。
- LangGraph 将输入消息添加到内部状态,然后将状态传递到入口节点
文档¶
- 教程:通过引导示例学习使用 LangGraph 进行构建。
- 操作指南:在 LangGraph 中完成特定的事情,从流式传输到添加内存和持久性,再到常见的设计模式(分支、子图等)。如果您想复制并运行特定代码段,请访问此页面。
- 概念指南:对 LangGraph 背后的关键概念和原理进行深入解释,例如节点、边、状态等。
- API 参考:查看重要的类和方法,使用图和检查点 API 的简单示例,更高层的预构建组件等。
运行示例 Jupyter 笔记本¶
请注意,examples/
文件夹中的 *.ipynb 笔记本需要安装 tslab。为了在 VSCode 中运行这些笔记本,您还需要安装 Jupyter VSCode 扩展。克隆此存储库后,您可以在根目录中运行 yarn build
。然后您应该一切就绪!
如果您仍然遇到问题,请尝试将以下 tsconfig.json
文件添加到 examples/
目录
{
"compilerOptions": {
"esModuleInterop": true,
"moduleResolution": "node",
"target": "ES2020",
"module": "ES2020",
"lib": [
"ES2020"
],
"strict": true,
"baseUrl": ".",
"paths": {
"@langchain/langgraph": [
"../langgraph/src"
]
}
},
"include": [
"./**/*.ts",
"./**/*.tsx"
],
"exclude": [
"node_modules"
]
}