LangChain实战:三步构建企业级智能问答系统,附完整代码
2026/7/9 4:03:27 网站建设 项目流程

引言

随着大语言模型(LLM)的普及,开发者不再满足于简单的对话补全,而是希望将 LLM 与外部数据、工具以及复杂业务逻辑相结合,构建真正落地的 AI 应用。LangChain 正是为此而生的开源框架,它提供了一系列标准化的组件和链式调用能力,帮助开发者高效地组合模型、数据检索、记忆、代理等模块。

本文将从实际需求出发,带你深入理解 LangChain 的核心概念,并通过一个完整的、可运行的代码示例,从零开始构建一个基于私有 PDF 文档的智能问答系统。读完本文,你将能够:

  • 掌握 LangChain 中模型、提示、链、检索、记忆等核心抽象
  • 学会使用 LangChain 加载并处理私有文档
  • 实现一个带记忆的多轮对话问答机器人
  • 了解常见问题与最佳实践

无论你是刚接触 LangChain 的新手,还是希望系统梳理概念的开发者,这篇实战指南都能为你提供清晰的路线图。


核心概念:LangChain 的六脉神剑

在动手写代码前,我们需要先理解 LangChain 的六大基础组件,它们是构建一切应用的基石。

1. 模型(Models)

LangChain 封装了两种主要模型:LLM(纯文本补全)和聊天模型(Chat Models,如gpt-3.5-turbo)。它们都通过统一的接口调用,方便替换。例如:

from langchain.chat_models import ChatOpenAI llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

2. 提示(Prompts)

提示管理是 LLM 应用的关键。LangChain 提供了PromptTemplateChatPromptTemplate等工具,支持变量插值、Few-shot 示例以及消息角色(system、human、ai)的组合。

from langchain.prompts import ChatPromptTemplate template = ChatPromptTemplate.from_messages([ ("system", "你是一个友好的AI助手"), ("human", "{user_input}") ])

3. 链(Chains)

链是 LangChain 的核心思想,它将多个组件串联起来形成一个处理管道。比如最简单的LLMChain就是把提示模板和模型组合起来。更复杂的还有RetrievalQAConversationalRetrievalChain等,这些封装好的链大大降低了开发门槛。

4. 检索(Retrieval)

为了让 LLM 能够回答非公开知识,我们需要将文档切片、向量化并存入向量数据库。用户提问时,先检索相关文档片段,再与问题一起交给模型回答。这一流程常被称为RAG(Retrieval-Augmented Generation)。LangChain 提供了各种文档加载器、文本分割器、嵌入模型接口和向量数据库集成。

5. 记忆(Memory)

默认情况下,LLM 是无状态的。LangChain 的Memory组件可以保存对话历史,实现多轮对话能力。常见的记忆类型有ConversationBufferMemory(全量缓存)、ConversationSummaryMemory(摘要记忆)等。

6. 代理(Agents)

代理将 LLM 作为“大脑”,能够自主决定使用哪些工具、按什么顺序执行,最终完成复杂任务。本文以 RAG 为主,代理部分可视为进阶内容。

理清这些组件后,我们开始实战。


实战示例:私有 PDF 智能问答系统

我们以一个典型场景为例:公司内部有许多产品使用手册(PDF 格式),希望搭建一个智能问答机器人,员工可以用自然语言提问,机器人能基于手册内容给出精准回答,并支持多轮对话。

环境准备

确保已安装 Python 3.9+,并安装以下依赖:

pip install langchain openai chromadb pypdf tiktoken streamlit

我们需要一个 OpenAI 的 API Key,请将其设置为环境变量OPENAI_API_KEY

步骤一:加载并处理文档

首先,将 PDF 加载为文本,然后进行分块。分块的原因有两个:向量模型通常有 token 上限;小块文本检索时更精准。

# 1. 文档加载 from langchain.document_loaders import PyPDFLoader loader = PyPDFLoader("path/to/your/product_manual.pdf") documents = loader.load() # 每页作为一个 Document 对象 print(f"文档页数: {len(documents)}") # 2. 文本分割 from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每块最大字符数 chunk_overlap=50, # 块之间重叠字符数,避免在语义边界切断 separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""] ) chunks = text_splitter.split_documents(documents) print(f"分割后的文本块数量: {len(chunks)}")

注意:separators列表定义了分割优先级,先从两个换行(段落)开始,如果没有则用单个换行,再逐级下降。这能保留更好的语义结构。

步骤二:创建向量数据库

将文本块转化为向量,并存储到 Chroma(一个轻量级本地向量数据库)中。

# 3. 嵌入与向量存储 from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma embeddings = OpenAIEmbeddings() # 指定持久化目录,避免每次运行都重新计算嵌入 persist_directory = "./chroma_db" vectordb = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory=persist_directory ) vectordb.persist() # 持久化到磁盘 print("向量数据库创建完成")

后续再次使用时,可以直接加载已有数据库:

vectordb = Chroma( persist_directory=persist_directory, embedding_function=embeddings )

步骤三:构建对话型检索链

我们使用ConversationalRetrievalChain一步到位:它内部整合了问题改写(基于对话历史生成独立问题)、文档检索、上下文整合和答案生成。

from langchain.chat_models import ChatOpenAI from langchain.chains import ConversationalRetrievalChain from langchain.memory import ConversationBufferMemory # 初始化语言模型 llm = ChatOpenAI( model_name="gpt-3.5-turbo", temperature=0.1, # 降低随机性,保证回答精确 max_tokens=512 ) # 创建记忆组件,用于存储对话历史 memory = ConversationBufferMemory( memory_key="chat_history", # 链内部会使用这个key从记忆读取历史 return_messages=True, # 以消息对象列表形式返回 output_key="answer" # 指定输出存储的键 ) # 构建对话检索链 qa_chain = ConversationalRetrievalChain.from_llm( llm=llm, retriever=vectordb.as_retriever(search_kwargs={"k": 4}), # 每次检索返回4个相关块 memory=memory, verbose=False # 设为True可查看内部运行细节 ) print("对话检索链创建成功,开始对话吧!")

步骤四:测试对话

# 模拟用户提问 print("第一轮:") response = qa_chain({"question": "如何重置设备的网络设置?"}) print(f"AI: {response['answer']}\n") print("第二轮(跟随上下文):") response = qa_chain({"question": "对刚才提到的步骤有没有更详细说明?"}) print(f"AI: {response['answer']}\n") print("第三轮:") response = qa_chain({"question": "如果重置后仍无法连接怎么办?"}) print(f"AI: {response['answer']}\n")

运行以上代码,你将看到一个能够记住“刚才提到的步骤”的问答系统。记忆组件会自动将对话历史加入提示,使得模型可以正确理解上下文。

完整可运行代码

下面将所有步骤整合为一个完整的 Python 脚本qa_bot.py,你只需修改 PDF 路径即可运行:

import os from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.chat_models import ChatOpenAI from langchain.memory import ConversationBufferMemory from langchain.chains import ConversationalRetrievalChain # 1. 加载文档 pdf_path = "./user_manual.pdf" # 请替换为你的PDF路径 loader = PyPDFLoader(pdf_path) docs = loader.load() # 2. 分割文本 splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""] ) chunks = splitter.split_documents(docs) # 3. 向量化并存储 embeddings = OpenAIEmbeddings() vectordb = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_manual_db" ) vectordb.persist() # 4. 初始化 LLM 和记忆 llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.1) memory = ConversationBufferMemory( memory_key="chat_history", return_messages=True, output_key="answer" ) # 5. 构建对话检索链 qa = ConversationalRetrievalChain.from_llm( llm=llm, retriever=vectordb.as_retriever(search_kwargs={"k": 4}), memory=memory ) # 6. 命令行交互循环 print("智能问答机器人已启动,输入'退出'结束对话。") while True: user_input = input("\n用户: ") if user_input.lower() in ["退出", "exit", "quit"]: print("再见!") break try: result = qa({"question": user_input}) print(f"AI: {result['answer']}") except Exception as e: print(f"发生错误: {e}")

运行后即可在命令行进行多轮对话。


常见问题 & 注意事项

1. Token 超限怎么办?

当对话历史越来越长,加上检索到的文档块,提示可能会超出模型的上下文长度。解决方案:

  • 限制检索返回数量(k值调小)
  • 使用ConversationSummaryMemory替代全量缓存,自动生成摘要
  • 启用 token 计数,对历史进行截断
  • 选择更大上下文窗口的模型(如 gpt-3.5-turbo-16k)

2. 回答质量不理想

常见原因及优化方向:

  • 文档切割不合理:块太大检索不准,块太小丢失上下文。建议chunk_size=500~1000overlap=50~100为初始值,根据实际文档粒度调整。
  • 检索结果相关性低:可以在检索时增加相似度阈值过滤,或使用带有元数据的筛选条件。
  • 提示模板可定制:通过from_llmcondense_question_promptcombine_docs_chain_kwargs传入自定义提示,引导模型更好地利用上下文。

3. 持久化与成本

  • 嵌入计算会消耗 API 费用,如果文档不变,应持久化向量数据库,避免重复计算。
  • Chroma 默认使用内存模式,指定persist_directory后才会存盘。
  • 在本地频繁测试时,可以先用较小的文档集验证流程。

4. 向量数据库选择

Chroma 适合原型和小规模应用。生产环境可考虑:

  • FAISS(本地,速度快)
  • PineconeWeaviateQdrant等云原生向量数据库

LangChain 对它们都有良好支持。

5. 安全性

  • 切勿将 API Key 硬编码在代码中,使用环境变量或密钥管理服务。
  • 对于面向最终用户的产品,注意控制数据访问权限,防止检索到未授权文档。

总结

本文从零开始,详细拆解了 LangChain 构建 AI 应用的全过程。我们从核心概念入手,理解了模型、提示、链、检索和记忆的职责;随后通过一个完整的 PDF 问答项目,展示了从文档加载、分割、向量化到构建带记忆检索链的每个环节。所有代码均可直接运行,希望能为你开启 LangChain 实战之旅提供坚实基础。

LangChain 的威力远不止于此,它还支持代理、工具调用、回调系统等高级特性。但万变不离其宗,理解这六脉神剑的组合方式,你就能轻松驾驭更复杂的场景。有什么问题欢迎在评论区交流,一起探索大语言模型应用的无限可能。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询