1. 面试题目 #
请设计并实现一个基于LangChain的文档问答系统,要求能够处理用户上传的文档(如PDF、文本文件等),并回答用户基于文档内容提出的问题。请详细说明系统架构、核心组件、实现步骤,并考虑系统的优化策略。
2. 参考答案 #
2.1 系统架构设计 #
文档问答系统主要包含两个核心阶段:
索引阶段(Indexing Phase):
- 文档加载:使用Document Loader加载各种格式的文档
- 文本分割:使用Text Splitter将大文档分割成小块,便于检索和适配LLM上下文窗口
- 向量化存储:将文本块转换为向量表示并存储到向量数据库中
检索生成阶段(Retrieval & Generation Phase):
- 相关文档检索:根据用户问题从向量数据库中检索最相关的文档块
- 答案生成:将检索到的文档块和用户问题输入LLM生成答案
2.2 核心组件实现 #
import "dotenv/config";
import { TextLoader } from "langchain/document_loaders/fs/text";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings, ChatOpenAI } from "@langchain/openai";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { RunnableSequence } from "@langchain/core/runnables";
import { StringOutputParser } from "@langchain/core/output_parsers";
// 1. 初始化模型和嵌入
const chatModel = new ChatOpenAI({
configuration: {
baseURL: process.env.OPENAI_API_BASE_URL,
},
apiKey: process.env.OPENAI_API_KEY,
modelName: "Doubao-1.5-pro-320K-256K",
});
const embeddings = new OpenAIEmbeddings({
configuration: {
baseURL: process.env.OPENAI_API_BASE_URL,
},
apiKey: process.env.OPENAI_API_KEY,
modelName: "Doubao-embedding-text-240715",
});
// 2. 文档加载和分割
const loader = new TextLoader("data/qiu.txt");
const docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 100,
});
const splitDocs = await splitter.splitDocuments(docs);
// 3. 向量化存储
const vectorStore = await MemoryVectorStore.fromDocuments(
splitDocs,
embeddings
);
// 4. 构建RAG链
const retriever = vectorStore.asRetriever(2);
const contextRetrieverChain = RunnableSequence.from([
(input) => input.question,
retriever,
(documents) => documents.map(doc => doc.pageContent).join("\n"),
]);
const TEMPLATE = `你是一个有用的问答助手。请基于提供的上下文回答问题。如果无法从上下文中找到答案,请说明你不知道,不要编造答案。
相关上下文:
{context}
请基于原文回答以下问题:
{question}`;
const prompt = ChatPromptTemplate.fromTemplate(TEMPLATE);
const ragChain = RunnableSequence.from([
{
context: contextRetrieverChain,
question: (input) => input.question,
},
prompt,
chatModel,
new StringOutputParser(),
]);
// 5. 使用示例
const answer = await ragChain.invoke({
question: "什么是球状闪电?",
});
console.log(answer);2.3 系统工作流程 #
索引阶段流程:
- 用户上传文档 → DocumentQA系统
- DocumentQA调用PDFLoader读取文档内容
- 对文档内容进行文本分割
- 将分割后的文本块存储到向量数据库
查询阶段流程:
- 用户提出问题 → DocumentQA系统
- DocumentQA从向量数据库检索相关内容
- 将检索到的内容和问题发送给LLM
- LLM生成答案并返回给用户
2.4 系统优化策略 #
检索优化:
- 算法多样性:使用语义搜索、混合搜索等多种检索算法
- 参数调优:调整检索参数如k值、相似度阈值等
- 重排序:使用专门的排序模型对检索结果进行二次排序
答案生成优化:
- 链类型选择:根据任务特点选择合适的Chain类型(stuff、map_reduce、refine等)
- 提示工程:优化提示模板,提供更好的示例和指令
- 记忆集成:添加记忆功能支持多轮对话
2.5 技术要点总结 #
- 向量数据库:用于存储文档的向量表示,支持快速相似度搜索
- 文本分割:平衡检索精度和计算效率的关键技术
- RAG架构:结合检索和生成的混合架构,提高答案准确性
- 提示工程:通过精心设计的提示模板引导LLM生成高质量答案
这个系统设计充分体现了现代AI应用中检索增强生成(RAG)的核心思想,通过结合传统信息检索和生成式AI的优势,实现了高效准确的文档问答功能。