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

使用LangChainJS+OpenAI处理本地文档时源文档始终返回undefined

问题:LangChainJS中ConversationalRetrievalQAChain返回的源文档为undefined

我正在使用LangChainJS搭配OpenAI处理本地文档,编写了如下代码,但获取到的回答对应的源文档始终显示为undefined:

import { OpenAI } from "langchain/llms/openai";
import { ConversationalRetrievalQAChain } from "langchain/chains";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { Chroma } from "langchain/vectorstores/chroma";
import { ChatOpenAI } from "langchain/chat_models/openai";
import readline from 'readline';

const rl = readline.createInterface({
    input: process.stdin,
});

/* Initialize the LLM to use to answer the question */
const model = new OpenAI({
    openAIApiKey: Bun.env.OPENAI_API_KEY,
    streaming: true,
    callbacks: [
        {
            handleLLMNewToken(token) {
                process.stdout.write(token.replace(/^\n/, ""));
            },
        },
    ],
});

const GPTchat = new ChatOpenAI({
    openAIApiKey: Bun.env.OPENAI_API_KEY,
    modelName: "gpt-3.5-turbo-0613",
    temperature: 0.9,
});

/* Load the vector database of the embedings */
const vectorStore = await Chroma.fromExistingCollection(
    new OpenAIEmbeddings(),
    { collectionName: "data" }
);

/* Create the chain */
const chain = ConversationalRetrievalQAChain.fromLLM(
    model,
    vectorStore.asRetriever(),
);

async function converse(prompt, chatHistory, senderId) {
    const followUpRes = await chain.call({
        question: prompt,
        chat_history: chatHistory,
        search: false,
        returnSourceDocuments: true
    });
    chatHistory = `${prompt}${followUpRes.text}`;
    console.log(followUpRes);
    return { text: followUpRes.text, source: followUpRes.source };
}

let firstRun = true;
let chatHistory = [];

async function chat() {
    process.stdout.write(firstRun ? "User: " : "\nUser: ");
    firstRun=false;
    const prompt = await new Promise((resolve) => rl.question("", resolve));
    process.stdout.write("AI GPT: ");
    const { text, source } = await converse(prompt, chatHistory, 1);
    if (source) {
        console.log(`\nSource: ${source}`);
    }
    chat(chatHistory);
}

chat();

示例输出:

hajsf@DESKTOP-JS1NVNB:~/wa$ bun qa.js
[0.02ms] ".env"
User: Hi
AI GPT:  Hi there! What can I help you with?{
  text: " Hi there! What can I help you with?",
  __run: undefined
}

User: When can I take my annual leave
AI GPT:  You must complete 12 months of service to be entitled to take your annual leave and you must submit your request in the ESS Portal to your department manager at least 3 months earlier.{
  text: " You must complete 12 months of service to be entitled to take your annual leave and you must submit your request in the ESS Portal to your department manager at least 3 months earlier.",
  __run: undefined
}

问题原因与修复方案

核心错误点

  1. 参数命名错误:chain.call()中使用的returnSourceDocuments: true不符合LangChainJS的参数规范,正确参数为return_source_documents: true(下划线命名)。
  2. 源文档字段访问错误:返回结果中的源文档存储在sourceDocuments字段,而非source,直接访问followUpRes.source会得到undefined。
  3. 对话历史格式错误:chat_history要求为数组格式(每个元素是[问题, 回答]的字符串对或对象),但当前代码将其拼接为字符串,会导致后续对话上下文失效。
  4. 无效参数设置:ConversationalRetrievalQAChain无search: false参数,该设置会被忽略。

修改后的关键代码

async function converse(prompt, chatHistory, senderId) {
    const followUpRes = await chain.call({
        question: prompt,
        chat_history: chatHistory,
        return_source_documents: true // 修正参数名
    });
    // 正确维护对话历史,保持数组格式
    chatHistory.push([prompt, followUpRes.text]);
    console.log(followUpRes);
    // 提取源文档信息,可根据需求取metadata或内容片段
    const source = followUpRes.sourceDocuments?.map(doc => {
        return doc.metadata?.source ? `文件:${doc.metadata.source}` : `内容片段:${doc.pageContent.slice(0, 150)}`;
    }).join('\n');
    return { text: followUpRes.text, source };
}

// 初始化对话历史为数组
let chatHistory = [];

async function chat() {
    process.stdout.write(firstRun ? "User: " : "\nUser: ");
    firstRun=false;
    const prompt = await new Promise((resolve) => rl.question("", resolve));
    process.stdout.write("AI GPT: ");
    const { text, source } = await converse(prompt, chatHistory, 1);
    if (source) {
        console.log(`\nSource: ${source}`);
    }
    chat(); // 无需传递chatHistory,外部变量会自动更新
}

修改说明

  • 修正return_source_documents参数名,确保链返回源文档数据。
  • 通过followUpRes.sourceDocuments获取源文档数组,每个元素包含pageContent(文档内容)和metadata(上传时的元数据,如文件路径)。
  • 用数组存储对话历史,确保后续对话能正确复用上下文。
  • 移除无效的search: false参数。

内容的提问来源于stack exchange,提问作者Hasan A Yousef

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 23:28:15