如何添加消息历史记录
本指南假设您熟悉以下概念
RunnableWithMessageHistory
允许我们将消息历史记录添加到某些类型的链中。
具体来说,它可用于任何接受以下之一作为输入的可运行对象:
- 一系列
BaseMessages
- 包含接受一系列
BaseMessage
的键的字典 - 包含接受最新消息(作为字符串或一系列
BaseMessage
)的键和接受历史消息的单独键的字典
并返回以下之一作为输出:
- 可以被视为
AIMessage
内容的字符串 - 一系列
BaseMessage
- 包含包含一系列
BaseMessage
的键的字典
让我们看一些示例,了解它的工作原理。
设置
我们将使用 Upstash 存储我们的聊天消息历史记录,并使用 Anthropic 的 claude-2 模型,因此我们需要安装以下依赖项
- npm
- Yarn
- pnpm
npm install @langchain/anthropic @langchain/community @upstash/redis
yarn add @langchain/anthropic @langchain/community @upstash/redis
pnpm add @langchain/anthropic @langchain/community @upstash/redis
您需要设置 ANTHROPIC_API_KEY
的环境变量,并获取您的 Upstash REST URL 和秘密令牌。
LangSmith
LangSmith 对于消息历史记录注入等情况特别有用,在这种情况下,否则很难理解各种链部分的输入。
请注意,LangSmith 不是必需的,但它很有用。如果您确实想使用 LangSmith,在您在上面的链接中注册后,请确保取消注释下面的内容并设置您的环境变量以开始记录跟踪
export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY="<your-api-key>"
# Reduce tracing latency if you are not in a serverless environment
# export LANGCHAIN_CALLBACKS_BACKGROUND=true
让我们创建一个简单的可运行对象,它接受字典作为输入并返回 BaseMessage
。
在本例中,输入中的 "question"
键代表我们的输入消息,而 "history"
键是注入历史消息的地方。
import {
ChatPromptTemplate,
MessagesPlaceholder,
} from "@langchain/core/prompts";
import { ChatAnthropic } from "@langchain/anthropic";
import { UpstashRedisChatMessageHistory } from "@langchain/community/stores/message/upstash_redis";
// For demos, you can also use an in-memory store:
// import { ChatMessageHistory } from "langchain/stores/message/in_memory";
const prompt = ChatPromptTemplate.fromMessages([
["system", "You're an assistant who's good at {ability}"],
new MessagesPlaceholder("history"),
["human", "{question}"],
]);
const chain = prompt.pipe(
new ChatAnthropic({ model: "claude-3-sonnet-20240229" })
);
添加消息历史记录
要将消息历史记录添加到我们的原始链中,我们将它包装在 RunnableWithMessageHistory
类中。
至关重要的是,我们还需要定义一个 getMessageHistory()
方法,它接受一个 sessionId
字符串并根据它返回 BaseChatMessageHistory
。给定相同的输入,此方法应返回等效的输出。
在本例中,我们还需要指定 inputMessagesKey
(将被视为最新输入消息的键)和 historyMessagesKey
(将向其添加历史消息的键)。
import { RunnableWithMessageHistory } from "@langchain/core/runnables";
const chainWithHistory = new RunnableWithMessageHistory({
runnable: chain,
getMessageHistory: (sessionId) =>
new UpstashRedisChatMessageHistory({
sessionId,
config: {
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
},
}),
inputMessagesKey: "question",
historyMessagesKey: "history",
});
使用配置调用
每当我们使用消息历史记录调用链时,都需要包含一个额外的配置对象,其中包含 session_id
。
{
configurable: {
sessionId: "<SESSION_ID>";
}
}
给定相同的配置,我们的链应该从相同的聊天消息历史记录中提取。
const result = await chainWithHistory.invoke(
{
ability: "math",
question: "What does cosine mean?",
},
{
configurable: {
sessionId: "foobarbaz",
},
}
);
console.log(result);
/*
AIMessage {
content: 'Cosine refers to one of the basic trigonometric functions. Specifically:\n' +
'\n' +
'- Cosine is one of the three main trigonometric functions, along with sine and tangent. It is often abbreviated as cos.\n' +
'\n' +
'- For a right triangle with sides a, b, and c (where c is the hypotenuse), cosine represents the ratio of the length of the adjacent side (a) to the length of the hypotenuse (c). So cos(A) = a/c, where A is the angle opposite side a.\n' +
'\n' +
'- On the Cartesian plane, cosine represents the x-coordinate of a point on the unit circle for a given angle. So if you take an angle θ on the unit circle, the cosine of θ gives you the x-coordinate of where the terminal side of that angle intersects the circle.\n' +
'\n' +
'- The cosine function has a periodic waveform that oscillates between 1 and -1. Its graph forms a cosine wave.\n' +
'\n' +
'So in essence, cosine helps relate an angle in a right triangle to the ratio of two of its sides. Along with sine and tangent, it is foundational to trigonometry and mathematical modeling of periodic functions.',
name: undefined,
additional_kwargs: {
id: 'msg_01QnnAkKEz7WvhJrwLWGbLBm',
type: 'message',
role: 'assistant',
model: 'claude-3-sonnet-20240229',
stop_reason: 'end_turn',
stop_sequence: null
}
}
*/
const result2 = await chainWithHistory.invoke(
{
ability: "math",
question: "What's its inverse?",
},
{
configurable: {
sessionId: "foobarbaz",
},
}
);
console.log(result2);
/*
AIMessage {
content: 'The inverse of the cosine function is the arcsine or inverse sine function, often written as sin−1(x) or sin^{-1}(x).\n' +
'\n' +
'Some key properties of the inverse cosine function:\n' +
'\n' +
'- It accepts values between -1 and 1 as inputs and returns angles from 0 to π radians (0 to 180 degrees). This is the inverse of the regular cosine function, which takes angles and returns the cosine ratio.\n' +
'\n' +
'- It is also called cos−1(x) or cos^{-1}(x) (read as "cosine inverse of x").\n' +
'\n' +
'- The notation sin−1(x) is usually preferred over cos−1(x) since it relates more directly to the unit circle definition of cosine. sin−1(x) gives the angle whose sine equals x.\n' +
'\n' +
'- The arcsine function is one-to-one on the domain [-1, 1]. This means every output angle maps back to exactly one input ratio x. This one-to-one mapping is what makes it the mathematical inverse of cosine.\n' +
'\n' +
'So in summary, arcsine or inverse sine, written as sin−1(x) or sin^{-1}(x), gives you the angle whose cosine evaluates to the input x, undoing the cosine function. It is used throughout trigonometry and calculus.',
additional_kwargs: {
id: 'msg_01PYRhpoUudApdJvxug6R13W',
type: 'message',
role: 'assistant',
model: 'claude-3-sonnet-20240229',
stop_reason: 'end_turn',
stop_sequence: null
}
}
*/
查看第二个调用的 Langsmith 跟踪,我们可以看到,在构建提示时,已注入一个“history”变量,它是一个包含两条消息的列表(我们的第一个输入和第一个输出)。