请描述使用 LangChain 构建一个文档问答系统的关键技术组件及实现步骤
- 标签:大模型
🏛 业务架构师
用 LangChain 做文档问答系统,我按"组件—步骤—上线"三段讲:
关键技术组件:DocumentLoader(加载 PDF/Word/Confluence)、TextSplitter(递归切分,300~500 字)、Embeddings+VectorStore(BGE + Milvus/pgvector)、Retriever(混合检索+元数据过滤)、Prompt Template(含"仅依据资料回答"约束与引用编号)、Memory(多轮对话)、LCEL 串装、LangSmith 做追踪评测。
实现步骤:
- 知识接入:文档解析清洗,保留来源/部门/时效元数据;
- 索引构建:切分→向量化→入库;
- 检索链路:查询改写→混合检索 Top 20→Rerank 取 Top 3~5;
- 生成链路:
retriever | prompt | model | parser组装,输出带引用的答案; - 评估:建 100+ 条评测集,跑命中率与忠实度;
- 运营:badcase 回流→调切块/检索参数→回归测试。
业务落地建议:第一期只做单部门知识库(范围小、数据干净、指标清晰),把评测集和流水线跑成资产,再横向复制到其他部门——知识问答系统的护城河不在代码,在于持续运营的数据闭环。
🏗 系统架构师
用 LangChain 建文档问答系统,我按组件清单 + 实现步骤 + 架构决策讲:
关键技术组件:
- 加载与解析:DocumentLoader(PDF/Word/Markdown)或 Unstructured;
- 分块:RecursiveCharacterTextSplitter / MarkdownHeaderTextSplitter(结构感知);
- 嵌入与存储:Embeddings 模型 + VectorStore(PGVector/Milvus),chunk 附带来源、标题、权限元数据;
- 检索:Retriever(向量 + BM25 混合,加自查询过滤),必要时 Rerank;
- 生成:ChatModel + PromptTemplate(含"仅依据上下文回答,无依据则说不知道")+ StrOutputParser;
- 记忆:ChatMessageHistory(多轮追问);
- 观测:Callback 接 LangSmith 做 trace。
实现步骤:LCEL 串成 retriever | format_docs | prompt | model | parser,套 create_retrieval_chain 加引用;流式输出透传前端。
三个架构决策(比代码重要):一是权限过滤放在 Retriever 内(pre-filter),防止越权文档进上下文;二是引用溯源——答案携带 chunk ID 映射到原文档页码,可审计;三是评估先行——上线前建 200 条金标集跑 recall/faithfulness,上线后 trace 采样归因。PoC 一天能搭,生产化工作量在数据治理和可观测上。
💻 全栈开发专家
用 LangChain 构建文档问答(RAG)系统,我按组件和步骤过一遍:
技术组件:DocumentLoader → TextSplitter → Embedding → VectorStore → Retriever → Prompt → LLM → 引用组装。生产链路再加 Rerank 和评估。
实现步骤(Python 版):
python
# 1. 离线索引
docs = DirectoryLoader("./docs", loader_cls=TextLoader).load()
chunks = RecursiveCharacterTextSplitter(
chunk_size=512, chunk_overlap=64).split_documents(docs) # 保留 metadata
vs = Milvus.from_documents(chunks, OpenAIEmbeddings(),
connection_args={"uri": "http://milvus:19530"})
# 2. 在线检索+生成
retriever = vs.as_retriever(search_kwargs={"k": 6})
def format_docs(ds): return "\n\n".join(
f"[{i}] {d.page_content}" for i, d in enumerate(ds, 1))
rag = ({"context": retriever | format_docs, "question": RunnablePassthrough()}
| ChatPromptTemplate.from_template(
"仅根据以下编号资料回答,引用 [n]:\n{context}\n\n问题:{question}")
| llm | StrOutputParser())上线前的三件事(新手最容易漏):一是 chunk 的 metadata 必须带 source/页码,否则引用溯源做不了;二是建 50~100 条评测集卡 recall@5 和忠实度;三是加 rerank(bge-reranker)和拒答分支(检索分数低于阈值时回「未找到」),这两个动作对用户信任度影响最大。