VectorStoreRetrieverMemory
VectorStoreRetrieverMemory 就是基于 向量数据库(Vector Database) 实现的一种记忆管理机制,结合了向量存储和检索技术,可以完成高效的上下文管理。简单来说就是把用户和AI的对话数据转成向量存储到数据库里,根据用户的输入来查询相关的上下文,从而帮助AI实现有感知的回答
我们重新回到之前做的RAG bot的demo:
第一步:更改Prompt Template
import { ChatPromptTemplate } from "@langchain/core/prompts";
export const createTemplate = () => {
const chatPrompt = `你是一个熟读了新海诚小说《天气之子》的读者,精通根据作品原文详细解释和回答问题,在回答问题的时候你会引用作品原文。
并且在回答的时候仅根据原文来回答,尽可能详细地回答用户的问题。如果原文中没有相关内容,你可以根据情况自由回答,如果实在是无法判断用户的问题,你可以回答“这个问题我不知道”。
以下是原文中跟用户回答相关的内容
{context}
这是聊天记录,其中input后跟的是用户的问题,output是AI的回答
{chat_history}
现在,你要基于原文和聊天记录,回答以下问题:
{question}`;
const prompt = ChatPromptTemplate.fromTemplate(chatPrompt);
return prompt;
};
我们新添加了聊天记录的Prompt,向AI提醒接下来的对话要加入聊天记录进行分析
第二步:新建基于Qdrant的Memory
// createMemoryFromQdrant.ts
import { VectorStoreRetrieverMemory } from "langchain/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { QdrantVectorStore } from "@langchain/qdrant";
import { QdrantClient } from "@qdrant/js-client-rest";
import "dotenv/config";
export const createMemoryFromQdrant = async () => {
const client = new QdrantClient({ url: process.env.QDRANT_API_URL });
const embeddings = new OpenAIEmbeddings({
configuration: {
baseURL: process.env.OPENAI_API_URL,
},
model: process.env.OPENAI_EMBEDDING_MODEL,
});
const vectorStore = await QdrantVectorStore.fromExistingCollection(
embeddings,
{
client: client,
collectionName: "ten_ki_no_ko_history",
}
);
const memory = new VectorStoreRetrieverMemory({
vectorStoreRetriever: vectorStore.asRetriever(1),
memoryKey: "ten_ki_no_ko_history",
});
return memory;
};
创建一个基于指定的collectionName的Memory,每次找出一条类似的聊天记录
第三步:新建load和save方法
// saveMemoryToQdrant.ts
import { createMemoryFromQdrant } from "./createMemoryFromQdrant";
export interface Memory {
humanMessage: string;
aiMessage: string;
}
export const saveMemoryToQdrant = async (messages: Memory) => {
const memory = await createMemoryFromQdrant();
await memory.saveContext(
{ input: messages.humanMessage },
{ output: messages.aiMessage }
);
};
// loadMemoryFromQdrant.ts
import { createMemoryFromQdrant } from "./createMemoryFromQdrant";
export const loadMemoryFromQdrant = async (prompt: string) => {
const memory = await createMemoryFromQdrant();
const res = await memory.loadMemoryVariables({ prompt });
return res["ten_ki_no_ko_history"];
};
这两个方法分别负责的是保存聊天记录,以及通过Prompt(提问)来找出数据库里最类似的那段聊天记录。
第四步:新建一个关于Memory的Retriever
// getMemoryRetrieverChain.ts
import { RunnableSequence } from "@langchain/core/runnables";
import { loadMemoryFromQdrant } from "./loadMemoryFromQdrant";
export const getMemoryRetrieverChain = async () => {
const memoryRetrieverChain = RunnableSequence.from([
(input) => input.question,
loadMemoryFromQdrant,
]);
return memoryRetrieverChain;
};
第五步:添加Memory的Retriever,把历史记录的值加入Prompt Template
import { ChatOpenAI } from "@langchain/openai";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnableSequence } from "@langchain/core/runnables";
import { getContextRetrieverChain } from "./getContextRetrieverChain";
import { createTemplate } from "./creatTemplate";
import { getMemoryRetrieverChain } from "./getMemoryRetrieverChain";
export const getLastRagChain = async () => {
const model = new ChatOpenAI({
configuration: {
baseURL: process.env.OPENAI_API_URL,
},
model: process.env.OPENAI_MODEL,
});
const contextRetrieverChain = await getContextRetrieverChain();
const memoryRetrieverChain = await getMemoryRetrieverChain();
const prompt = createTemplate();
const ragChain = RunnableSequence.from([
{
context: contextRetrieverChain,
chat_history: memoryRetrieverChain,
question: (input) => input.question,
},
prompt,
model,
new StringOutputParser(),
]);
return ragChain;
};
第六步:在入口文件,运行结束之后,存入一下聊天记录到数据库
import { getLastRagChain } from "./getLastRagChain";
import { saveMemoryToQdrant } from "./saveMemoryToQdrant";
import { saveQdrant } from "./saveQdrant";
const run = async () => {
await saveQdrant();
const ragChain = await getLastRagChain();
const question = "你好,我是读者,我的名字叫做八六";
const res = await ragChain.invoke({
question,
});
console.log(res);
await saveMemoryToQdrant({
humanMessage: question,
aiMessage: res,
});
};
run();
这样一来,我们运行了一次,就可以在Qdrant的web UI上看到history的collection:
然后我们再询问一下我是谁:
const question = "我的名字叫什么?你会怎么给我推荐这本书?";
这样就实现记忆功能啦。
这是临时起意想起的向量数据库查询记忆的方法,有点紧急更新的味道,所以下一篇要紧跟这里,并尝试重构bot的demo,加上复数个Memory管理来实现真正的比较完善的记忆体系,请期待。