Skip to main content

如何将 BaseChatMessageHistory 与 LangGraph 一起使用

前提条件

本指南假定您已熟悉以下概念:

我们建议新的 LangChain 应用程序利用 LangGraph 内置的持久化功能 来实现内存管理。

在某些情况下,用户可能需要继续使用现有的聊天消息历史持久化解决方案。

在这里,我们将展示如何将 LangChain 聊天消息历史BaseChatMessageHistory 的实现)与 LangGraph 结合使用。

设置

process.env.ANTHROPIC_API_KEY = "YOUR_API_KEY";
yarn add @langchain/core @langchain/langgraph @langchain/anthropic

聊天消息历史

消息历史需要通过对话 ID 或者可能是(用户 ID,对话 ID)的二元组来进行参数化。

许多LangChain 聊天消息历史将具有一个sessionId或者某个namespace,以便跟踪不同的对话。请参考具体的实现以查看其参数化方式。

内置的InMemoryChatMessageHistory不包含这种参数化功能,因此我们将创建一个字典来跟踪消息历史。

import { InMemoryChatMessageHistory } from "@langchain/core/chat_history";

const chatsBySessionId: Record<string, InMemoryChatMessageHistory> = {};

const getChatHistory = (sessionId: string) => {
let chatHistory: InMemoryChatMessageHistory | undefined =
chatsBySessionId[sessionId];
if (!chatHistory) {
chatHistory = new InMemoryChatMessageHistory();
chatsBySessionId[sessionId] = chatHistory;
}
return chatHistory;
};

与 LangGraph 一起使用

接下来,我们将使用 LangGraph 设置一个基本的聊天机器人。如果你不熟悉 LangGraph,可以查看以下 快速入门教程

我们将为聊天模型创建一个 LangGraph 节点,并手动管理对话历史,同时考虑作为 RunnableConfig 一部分传递的对话 ID。

对话 ID 可以作为 RunnableConfig 的一部分传递(如本例所示),也可以作为 图状态 的一部分传递。

import { v4 as uuidv4 } from "uuid";
import { ChatAnthropic } from "@langchain/anthropic";
import {
StateGraph,
MessagesAnnotation,
END,
START,
} from "@langchain/langgraph";
import { HumanMessage } from "@langchain/core/messages";
import { RunnableConfig } from "@langchain/core/runnables";

// Define a chat model
const model = new ChatAnthropic({ modelName: "claude-3-haiku-20240307" });

// Define the function that calls the model
const callModel = async (
state: typeof MessagesAnnotation.State,
config: RunnableConfig
): Promise<Partial<typeof MessagesAnnotation.State>> => {
if (!config.configurable?.sessionId) {
throw new Error(
"Make sure that the config includes the following information: {'configurable': {'sessionId': 'some_value'}}"
);
}

const chatHistory = getChatHistory(config.configurable.sessionId as string);

let messages = [...(await chatHistory.getMessages()), ...state.messages];

if (state.messages.length === 1) {
// First message, ensure it's in the chat history
await chatHistory.addMessage(state.messages[0]);
}

const aiMessage = await model.invoke(messages);

// Update the chat history
await chatHistory.addMessage(aiMessage);

return { messages: [aiMessage] };
};

// Define a new graph
const workflow = new StateGraph(MessagesAnnotation)
.addNode("model", callModel)
.addEdge(START, "model")
.addEdge("model", END);

const app = workflow.compile();

// Create a unique session ID to identify the conversation
const sessionId = uuidv4();
const config = { configurable: { sessionId }, streamMode: "values" as const };

const inputMessage = new HumanMessage("hi! I'm bob");

for await (const event of await app.stream(
{ messages: [inputMessage] },
config
)) {
const lastMessage = event.messages[event.messages.length - 1];
console.log(lastMessage.content);
}

// Here, let's confirm that the AI remembers our name!
const followUpMessage = new HumanMessage("what was my name?");

for await (const event of await app.stream(
{ messages: [followUpMessage] },
config
)) {
const lastMessage = event.messages[event.messages.length - 1];
console.log(lastMessage.content);
}
hi! I'm bob
Hello Bob! It's nice to meet you. How can I assist you today?
what was my name?
You said your name is Bob.

与 RunnableWithMessageHistory 一起使用

本指南直接使用了 BaseChatMessageHistorymessagesaddMessages 接口。

或者,你可以使用 RunnableWithMessageHistory,因为 LCEL 可以在任何 LangGraph 节点 中使用。

为此,请将以下代码替换为:

const callModel = async (
state: typeof MessagesAnnotation.State,
config: RunnableConfig
): Promise<Partial<typeof MessagesAnnotation.State>> => {
if (!config.configurable?.sessionId) {
throw new Error(
"确保配置包含以下信息: {'configurable': {'sessionId': 'some_value'}}"
);
}

const chatHistory = getChatHistory(config.configurable.sessionId as string);

let messages = [...(await chatHistory.getMessages()), ...state.messages];

if (state.messages.length === 1) {
// 首条消息,确保其在聊天记录中
await chatHistory.addMessage(state.messages[0]);
}

const aiMessage = await model.invoke(messages);

// 更新聊天记录
await chatHistory.addMessage(aiMessage);
return { messages: [aiMessage] };
};

使用当前应用程序中定义的 RunnableWithMessageHistory 相应实例。

const runnable = new RunnableWithMessageHistory({
// ... 来自现有代码的配置
});

const callModel = async (
state: typeof MessagesAnnotation.State,
config: RunnableConfig
): Promise<Partial<typeof MessagesAnnotation.State>> => {
// RunnableWithMessageHistory 负责读取消息历史
// 并使用新的人类消息和 AI 响应更新它。
const aiMessage = await runnable.invoke(state.messages, config);
return {
messages: [aiMessage],
};
};

Was this page helpful?


You can also leave detailed feedback on GitHub.