构建基础聊天机器人¶
翻译说明
本文档由墨攻网络安全大模型团队翻译
在本教程中,你将构建一个基础聊天机器人。这个聊天机器人是以下一系列教程的基础,你将逐步添加更复杂的功能,并在此过程中了解关键的 LangGraph 概念。让我们开始吧! 🌟
前提条件¶
在开始本教程之前,请确保你能访问支持工具调用功能的 LLM,例如 OpenAI、Anthropic 或 Google Gemini。
1. 安装包¶
安装所需的包:
Tip
注册 LangSmith 以快速发现问题并提高 LangGraph 项目的性能。LangSmith 允许你使用跟踪数据来调试、测试和监控使用 LangGraph 构建的 LLM 应用。有关如何开始的更多信息,请参阅 LangSmith 文档。
2. 创建 StateGraph¶
现在你可以使用 LangGraph 创建一个基础聊天机器人。这个聊天机器人将直接响应用户消息。
首先创建一个 StateGraph。StateGraph 对象将我们的聊天机器人的结构定义为"状态机"。我们将添加 nodes 来表示 llm 和聊天机器人可以调用的函数,并添加 edges 来指定机器人应如何在这些函数之间转换。
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
class State(TypedDict):
# Messages 的类型是 "list"。注解中的 `add_messages` 函数
# 定义了这个状态键应该如何更新
# (在这种情况下,它将消息追加到列表中,而不是覆盖它们)
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
import { StateGraph, MessagesZodState, START } from "@langchain/langgraph";
import { z } from "zod";
const State = z.object({ messages: MessagesZodState.shape.messages });
const graph = new StateGraph(State).compile();
我们的图现在可以处理两个关键任务:
- 每个
node可以接收当前State作为输入并输出对状态的更新。 - 由于预构建的归约器函数,对
messages的更新将追加到现有列表而不是覆盖它。
概念
定义图时,第一步是定义其 State。State 包括图的 schema 和处理状态更新的归约器函数。在我们的示例中,State 是一个有一个键的 schema:messages。归约器函数用于将新消息追加到列表而不是覆盖它。没有归约器注解的键将覆盖以前的值。
要了解有关状态、归约器和相关概念的更多信息,请参阅 LangGraph 参考文档。
3. 添加节点¶
接下来,添加一个 "chatbot" 节点。**节点**表示工作单元,通常是常规函数。
让我们首先选择一个聊天模型:
import { ChatOpenAI } from "@langchain/openai";
// or import { ChatAnthropic } from "@langchain/anthropic";
const llm = new ChatOpenAI({
model: "gpt-4o",
temperature: 0,
});
我们现在可以将聊天模型合并到一个简单的节点中:
def chatbot(state: State):
return {"messages": [llm.invoke(state["messages"])]}
# 第一个参数是唯一的节点名称
# 第二个参数是每次使用节点时将被调用的函数或对象
graph_builder.add_node("chatbot", chatbot)
import { StateGraph, MessagesZodState, START } from "@langchain/langgraph";
import { z } from "zod";
const State = z.object({ messages: MessagesZodState.shape.messages });
const graph = new StateGraph(State)
.addNode("chatbot", async (state: z.infer<typeof State>) => {
return { messages: [await llm.invoke(state.messages)] };
})
.compile();
注意 chatbot 节点函数如何将当前 State 作为输入并返回一个包含键 "messages" 下更新的 messages 列表的字典。这是所有 LangGraph 节点函数的基本模式。
我们 State 中的 add_messages 函数将把 LLM 的响应消息追加到状态中已有的任何消息。
MessagesZodState 中使用的 addMessages 函数将把 LLM 的响应消息追加到状态中已有的任何消息。
4. 添加 entry 点¶
添加一个 entry 点来告诉图**每次运行时从哪里开始工作**:
import { StateGraph, MessagesZodState, START } from "@langchain/langgraph";
import { z } from "zod";
const State = z.object({ messages: MessagesZodState.shape.messages });
const graph = new StateGraph(State)
.addNode("chatbot", async (state: z.infer<typeof State>) => {
return { messages: [await llm.invoke(state.messages)] };
})
.addEdge(START, "chatbot")
.compile();
5. 添加 exit 点¶
添加一个 exit 点来指示**图应该在哪里完成执行**。这对于更复杂的流程很有帮助,但即使在像这样的简单图中,添加结束节点也能提高清晰度。
import { StateGraph, MessagesZodState, START, END } from "@langchain/langgraph";
import { z } from "zod";
const State = z.object({ messages: MessagesZodState.shape.messages });
const graph = new StateGraph(State)
.addNode("chatbot", async (state: z.infer<typeof State>) => {
return { messages: [await llm.invoke(state.messages)] };
})
.addEdge(START, "chatbot")
.addEdge("chatbot", END)
.compile();
这告诉图在运行 chatbot 节点后终止。
6. 编译图¶
在运行图之前,我们需要编译它。我们可以通过在图构建器上调用 compile() 来实现。这将创建一个 CompiledGraph,我们可以在状态上调用它。
import { StateGraph, MessagesZodState, START, END } from "@langchain/langgraph";
import { z } from "zod";
const State = z.object({ messages: MessagesZodState.shape.messages });
const graph = new StateGraph(State)
.addNode("chatbot", async (state: z.infer<typeof State>) => {
return { messages: [await llm.invoke(state.messages)] };
})
.addEdge(START, "chatbot")
.addEdge("chatbot", END)
.compile();
7. 可视化图(可选)¶
你可以使用 get_graph 方法和其中一种"绘制"方法(如 draw_ascii 或 draw_png)来可视化图。draw 方法各需要额外的依赖项。
from IPython.display import Image, display
try:
display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
# 这需要一些额外的依赖项并且是可选的
pass
你可以使用 getGraph 方法可视化图,并使用 drawMermaidPng 方法渲染图。
import * as fs from "node:fs/promises";
const drawableGraph = await graph.getGraphAsync();
const image = await drawableGraph.drawMermaidPng();
const imageBuffer = new Uint8Array(await image.arrayBuffer());
await fs.writeFile("basic-chatbot.png", imageBuffer);

8. 运行聊天机器人¶
现在运行聊天机器人!
Tip
你可以随时通过输入 quit、exit 或 q 退出聊天循环。
def stream_graph_updates(user_input: str):
for event in graph.stream({"messages": [{"role": "user", "content": user_input}]}):
for value in event.values():
print("Assistant:", value["messages"][-1].content)
while True:
try:
user_input = input("User: ")
if user_input.lower() in ["quit", "exit", "q"]:
print("Goodbye!")
break
stream_graph_updates(user_input)
except:
# 如果 input() 不可用时的后备方案
user_input = "What do you know about LangGraph?"
print("User: " + user_input)
stream_graph_updates(user_input)
break
import { HumanMessage } from "@langchain/core/messages";
async function streamGraphUpdates(userInput: string) {
const stream = await graph.stream({
messages: [new HumanMessage(userInput)],
});
import * as readline from "node:readline/promises";
import { StateGraph, MessagesZodState, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";
const llm = new ChatOpenAI({ model: "gpt-4o-mini" });
const State = z.object({ messages: MessagesZodState.shape.messages });
const graph = new StateGraph(State)
.addNode("chatbot", async (state: z.infer<typeof State>) => {
return { messages: [await llm.invoke(state.messages)] };
})
.addEdge(START, "chatbot")
.addEdge("chatbot", END)
.compile();
async function generateText(content: string) {
const stream = await graph.stream(
{ messages: [{ type: "human", content }] },
{ streamMode: "values" }
);
for await (const event of stream) {
for (const value of Object.values(event)) {
console.log(
"Assistant:",
value.messages[value.messages.length - 1].content
);
const lastMessage = event.messages.at(-1);
if (lastMessage?.getType() === "ai") {
console.log(`Assistant: ${lastMessage.text}`);
}
}
}
const prompt = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
while (true) {
const human = await prompt.question("User: ");
if (["quit", "exit", "q"].includes(human.trim())) break;
await generateText(human || "What do you know about LangGraph?");
}
prompt.close();
Assistant: LangGraph is a library designed to help build stateful multi-agent applications using language models. It provides tools for creating workflows and state machines to coordinate multiple AI agents or language model interactions. LangGraph is built on top of LangChain, leveraging its components while adding graph-based coordination capabilities. It's particularly useful for developing more complex, stateful AI applications that go beyond simple query-response interactions.
恭喜! 你已经使用 LangGraph 构建了第一个聊天机器人。这个机器人可以通过接收用户输入并使用 LLM 生成响应来进行基本对话。你可以查看上面调用的 LangSmith Trace。
以下是本教程的完整代码:
from typing import Annotated
from langchain.chat_models import init_chat_model
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list, add_messages]
graph_builder = StateGraph(State)
llm = init_chat_model("anthropic:claude-3-5-sonnet-latest")
def chatbot(state: State):
return {"messages": [llm.invoke(state["messages"])]}
# 第一个参数是唯一的节点名称
# 第二个参数是每次使用节点时将被调用的函数或对象
graph_builder.add_node("chatbot", chatbot)
graph_builder.add_edge(START, "chatbot")
graph_builder.add_edge("chatbot", END)
graph = graph_builder.compile()
import { StateGraph, START, END, MessagesZodState } from "@langchain/langgraph";
import { z } from "zod";
import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
model: "gpt-4o",
temperature: 0,
});
const State = z.object({ messages: MessagesZodState.shape.messages });
const graph = new StateGraph(State);
// 第一个参数是唯一的节点名称
// 第二个参数是每次使用节点时将被调用的函数或对象
.addNode("chatbot", async (state) => {
return { messages: [await llm.invoke(state.messages)] };
});
.addEdge(START, "chatbot");
.addEdge("chatbot", END)
.compile();
下一步¶
你可能已经注意到,机器人的知识仅限于其训练数据中的内容。在下一部分中,我们将添加一个网络搜索工具来扩展机器人的知识并使其更强大。