如何在Langchain.js的ConversationalRetrievalQAChain中实现相似度阈值回退至无向量库上下文LLM
在Langchain.js中实现ConversationalRetrievalQAChain的阈值回退逻辑
默认的ConversationalRetrievalQAChain无法直接实现相似度阈值判断,需要手动拆解检索、上下文构建和LLM调用流程,自定义逻辑实现回退。以下是具体实现步骤和代码:
核心思路
- 直接调用向量存储的
similaritySearchWithScore方法,获取带相似度分数的检索结果 - 设置阈值过滤有效文档,无有效文档时跳过上下文注入
- 结合对话内存,动态构建提示词并调用LLM
- 手动维护对话内存的更新
具体实现代码
1. 自定义带阈值过滤的检索函数
// 自定义相似度阈值(根据业务场景调整) const SIMILARITY_THRESHOLD = 0.7; /** * 检索符合相似度阈值的文档 * @param {string} query 用户提问 * @param {object} filters 检索过滤器(可选) * @returns {Document[]} 符合条件的文档列表 */ async function retrieveRelevantDocs(query, filters) { // 调用向量存储的带分数检索方法 const docsWithScores = filters ? await vectorStore.similaritySearchWithScore(query, 1, filters) : await vectorStore.similaritySearchWithScore(query, 1); // 过滤掉分数低于阈值的文档,只保留内容 const validDocs = docsWithScores .filter(([_, score]) => score >= SIMILARITY_THRESHOLD) .map(([doc]) => doc); return validDocs; }
2. 构建动态查询处理逻辑
结合对话内存和LLM,根据检索结果决定是否注入上下文:
async function processUserQuery(query, filters) { // 从内存中加载对话历史 const memoryVars = await memory.loadMemoryVariables({}); const chatHistory = memoryVars.history || ""; // 获取符合阈值的相关文档 const validDocs = await retrieveRelevantDocs(query, filters); let prompt; if (validDocs.length > 0) { // 有有效文档,构建带上下文的提示词 const context = validDocs.map(doc => doc.pageContent).join("\n\n"); prompt = `根据以下上下文和对话历史回答问题: 上下文: ${context} 对话历史: ${chatHistory} 当前问题:${query}`; } else { // 无有效文档,直接使用对话历史和当前提问 prompt = `根据对话历史回答问题: 对话历史: ${chatHistory} 当前问题:${query}`; } // 调用LLM生成回答 const response = await model.call(prompt); // 更新对话内存 await memory.saveContext({ input: query }, { output: response }); return response; }
3. 使用PromptTemplate优化提示词(可选)
如果需要更规范的提示词管理,可以用Langchain的PromptTemplate:
import { PromptTemplate } from "langchain/prompts"; // 定义带上下文的提示模板 const withContextPrompt = new PromptTemplate({ template: `根据以下上下文和对话历史回答问题: 上下文: {context} 对话历史: {chatHistory} 当前问题:{question}`, inputVariables: ["context", "chatHistory", "question"] }); // 定义无上下文的提示模板 const withoutContextPrompt = new PromptTemplate({ template: `根据对话历史回答问题: 对话历史: {chatHistory} 当前问题:{question}`, inputVariables: ["chatHistory", "question"] }); async function processUserQuery(query, filters) { const memoryVars = await memory.loadMemoryVariables({}); const chatHistory = memoryVars.history || ""; const validDocs = await retrieveRelevantDocs(query, filters); let response; if (validDocs.length > 0) { const context = validDocs.map(doc => doc.pageContent).join("\n\n"); const formattedPrompt = await withContextPrompt.format({ context, chatHistory, question: query }); response = await model.call(formattedPrompt); } else { const formattedPrompt = await withoutContextPrompt.format({ chatHistory, question: query }); response = await model.call(formattedPrompt); } await memory.saveContext({ input: query }, { output: response }); return response; }
替代方案:自定义Chain(进阶)
如果希望保留Langchain的Chain结构,可以通过BaseChain自定义实现阈值判断逻辑,但手动实现的方式更直观灵活,适合快速落地需求。
内容的提问来源于stack exchange,提问作者Zainul Abideen
相关产品推荐
相关产品推荐

