You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在LangChain中使StuffDocumentsChain具备ConversationChain的对话记忆能力?

问题与解决方案:实现带对话记忆的StuffDocumentsChain行为

问题背景

想要使用StuffDocumentsChain并实现ConversationChain的对话记忆功能,但现有基于ConversationalRetrievalQAChain的代码无法满足需求。

当前代码

import fs from 'fs';
import path from 'path';
import { OpenAI } from "langchain/llms/openai";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { HNSWLib } from "langchain/vectorstores/hnswlib";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { ConversationalRetrievalQAChain } from "langchain/chains";

const model = new OpenAI({openAIApiKey: 'sk-...', modelName: 'gpt-3.5-turbo'});
const text = fs.readFileSync(path.resolve(__dirname, './data.txt'), 'utf-8');
const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000 });
const docs = await textSplitter.createDocuments([text]);
const vectorStore = await HNSWLib.fromDocuments(docs, new OpenAIEmbeddings(
    {openAIApiKey: 'sk-...', modelName: 'text-embedding-ada-002'}
));

const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever()
);

const question = 'Hello my name is Archie';
const res = []
res.push(await chain.call({
    question,
    chat_history: []
}))
console.log(res[0])
res.push(await chain.call({
    question: "What is my name?",
    chat_history: [question, res[0].text]
}))
console.log(res[1]);

当前输出

{ text: 'That is not a question, but nice to meet you Archie!' }
{
  text: "I'm sorry, but I cannot provide you with an answer as the given context does not contain any relevant information to your question."
}

期望输出

{ text: 'Hi Archie, nice to meet you! How can I assist you today?' }
{
  text: "Your name is Archie, as you mentioned earlier. Is there anything else you would like to know or discuss?"
}

解决方案

核心问题分析

当前代码失效的原因有两个:

  1. 对话历史格式错误:ConversationalRetrievalQAChain要求chat_history是对话对数组(每一项是[用户提问, AI回答]的数组),而非扁平的文本列表。
  2. 默认提示模板限制:默认提示会优先从检索到的文档中找答案,忽略对话历史里的信息,导致模型无法识别用户之前提到的名字。

修改后的代码

import fs from 'fs';
import path from 'path';
import { OpenAI } from "langchain/llms/openai";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { HNSWLib } from "langchain/vectorstores/hnswlib";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { ConversationalRetrievalQAChain } from "langchain/chains";
import { PromptTemplate } from "langchain/prompts";

const model = new OpenAI({openAIApiKey: 'sk-...', modelName: 'gpt-3.5-turbo', temperature: 0.7});
const text = fs.readFileSync(path.resolve(__dirname, './data.txt'), 'utf-8');
const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000 });
const docs = await textSplitter.createDocuments([text]);
const vectorStore = await HNSWLib.fromDocuments(docs, new OpenAIEmbeddings(
    {openAIApiKey: 'sk-...', modelName: 'text-embedding-ada-002'}
));

// 自定义提示模板:优先参考对话历史,再结合文档内容
const customPrompt = PromptTemplate.fromTemplate(`
给定以下对话历史和一个新的问题,先从对话历史中寻找相关信息回答问题;如果对话历史没有相关内容,再结合提供的文档上下文回答。
对话历史:{chat_history}
文档上下文:{context}
新问题:{question}
请给出友好、自然的回答:
`);

const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
    {
        qaChainOptions: {
            prompt: customPrompt,
        },
        returnSourceDocuments: false
    }
);

const question = 'Hello my name is Archie';
const res = []
// 第一次调用:对话历史为空
res.push(await chain.call({
    question,
    chat_history: []
}))
console.log(res[0])
// 第二次调用:使用对话对格式的历史记录
res.push(await chain.call({
    question: "What is my name?",
    chat_history: [[question, res[0].text]]
}))
console.log(res[1]);

关键修改点

  1. 修正对话历史格式:将chat_history改为[[用户提问, AI回答]]的二维数组格式,让模型正确识别对话上下文。
  2. 自定义提示模板:明确告知模型优先从对话历史中提取信息,再使用文档内容,避免模型忽略对话记忆。
  3. 调整模型温度:设置temperature: 0.7让回答更自然友好,接近期望输出的语气。

若需明确使用StuffDocumentsChain

如果要直接使用StuffDocumentsChain结合对话记忆,可以手动组合链结构:

// 额外导入需要的类
import { StuffDocumentsChain } from "langchain/chains";
import { ConversationChain } from "langchain/chains";
import { BufferMemory } from "langchain/memory";

// 创建StuffDocumentsChain用于文档处理
const docChain = new StuffDocumentsChain({
    llm: model,
    documentPrompt: PromptTemplate.fromTemplate("文档内容:{pageContent}"),
    documentSeparator: "\n\n",
    // 自定义文档处理的提示
    combineDocumentsPrompt: PromptTemplate.fromTemplate(`
结合以下文档内容和对话历史回答问题:
对话历史:{chat_history}
文档:{context}
问题:{question}
回答:
`),
});

// 创建带对话记忆的ConversationChain
const conversationChain = new ConversationChain({
    llm: model,
    memory: new BufferMemory({ memoryKey: "chat_history" }),
});

// 组合使用:先通过对话记忆获取历史,再传入StuffDocumentsChain
const answer1 = await conversationChain.call({ input: question });
console.log(answer1);

// 处理第二个问题时,先获取对话历史,再结合文档
const chatHistory = conversationChain.memory.chatHistory.messages.map(msg => `${msg.type === 'human' ? '用户' : 'AI'}: ${msg.text}`).join('\n');
const answer2 = await docChain.call({
    question: "What is my name?",
    chat_history: chatHistory,
    input_documents: docs, // 传入处理后的文档
});
console.log(answer2);

内容的提问来源于stack exchange,提问作者Arsenius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:59:56