【49.Python+AI】用LangChain搭一个RAG问答机器人:从文档上传到智能回答全流程

📖 文章简介: 本文手把手教你用LangChain框架从零搭建一个完整的RAG问答机器人。内容覆盖Document Loader加载多格式文档、Text Splitter进行语义化分块、Vector Store向量化存储、Retrieval Chain检索链的组装与调优,全流程逐行代码讲解。文中配以Mermaid流程图展示RAG的数据管道,包含一个可直接运行的完整Demo——上传PDF,AI基于文档内容回答问题并附带引用来源,适合想快速落地第一个RAG应用的开发者。


在这里插入图片描述

🎬 个人主页: 源码骑士

专栏传送门: 《Android开发基础》《python基础课程》

⭐️热衷从源码视角拆解技术底层原理,将复杂架构讲得通俗易懂


🎬 源码骑士的简介:
5年Android Framework系统开发经验,曾主导多项系统级性能优化专项
技术栈覆盖Android系统全链路(Binder/Handler/AMS/WMS/启动流程)及Java后端全家桶(Spring + MyBatis + Redis + Oracle)
累计产出原创技术文章100+篇,文章以流程图为特色,被读者评价为"看一篇胜过啃一周源码"


导入语

前面你选好了向量数据库、搞懂了Embedding模型。但真正上手搭RAG时,你会面对一长串问题:PDF怎么加载?怎么切成合适的块?块存在哪?检索怎么和LLM串起来?用户问的问题怎么在检索之后喂给模型?

LangChain就是来解决这些"组装问题"的。它把RAG的每个环节抽象为标准组件,你可以像搭乐高一样把它们拼起来。这篇文章的目标:用一个完整的Python脚本,从PDF加载到智能问答,每一步都有代码、每一行都有注释。 看完你就能在自己的文档上跑起来。


1 ~> 用LangChain搭RAG:四步走

📄 PDF文档

Document Loader
加载并解析

Text Splitter
语义分块

Embeddings
向量化

Vector Store
存储索引

❓ 用户问题

Retriever
检索相关块

Prompt模板
拼接上下文+问题

LLM
生成回答


2 ~> 完整代码实现

2.1 环境准备

pip install langchain langchain-openai chromadb pypdf tiktoken

2.2 完整RAG流水线

# rag_bot.py —— 从PDF到智能问答,一个文件搞定

from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate

# ========== Step 1:加载PDF ==========
loader = PyPDFLoader("公司规章制度.pdf")
documents = loader.load()
print(f"加载了 {len(documents)} 页文档")
# 每一页是一个Document对象,包含page_content和metadata(页码等)

# ========== Step 2:文本分块 ==========
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 每块500字符
    chunk_overlap=100,     # 块之间重叠100字符(保持语义连续性)
    separators=["\n\n", "\n", "。", ",", " ", ""]  # 优先按段落→句子→词分割
)
chunks = text_splitter.split_documents(documents)
print(f"分割为 {len(chunks)} 个文本块")

# ========== Step 3:向量化并存储 ==========
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    collection_name="company_rules",
    persist_directory="./chroma_db"  # 持久化,下次不用重新向量化
)

# ========== Step 4:构建检索+生成链 ==========
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.2)

# 定义Prompt模板:告诉模型如何使用检索到的上下文
system_prompt = (
    "你是一个企业知识库助手。根据以下检索到的文档片段回答问题。"
    "如果文档中没有相关信息,就说'文档中未提及',不要编造。"
    "回答时注明引用的来源。\n\n"
    "检索到的文档片段:\n{context}"
)
prompt = ChatPromptTemplate.from_messages([
    ("system", system_prompt),
    ("human", "{input}"),
])

# 组装:文档填充链 + 检索链
document_chain = create_stuff_documents_chain(llm, prompt)
retriever = vector_store.as_retriever(
    search_type="similarity",  # 相似度检索
    search_kwargs={"k": 4}     # 每次检索4个最相关块
)
rag_chain = create_retrieval_chain(retriever, document_chain)

# ========== 开始问答 ==========
if __name__ == "__main__":
    print("📚 RAG问答机器人已就绪,输入 'quit' 退出\n")
    while True:
        question = input("你的问题:")
        if question.lower() == "quit":
            break
        
        response = rag_chain.invoke({"input": question})
        print(f"\n回答:{response['answer']}\n")
        
        # 显示引用来源
        print("引用来源:")
        for i, doc in enumerate(response["context"], 1):
            print(f"  [{i}] 第{doc.metadata.get('page', '?')}页: {doc.page_content[:100]}...")
        print("-" * 50)

2.3 关键参数调优

参数 默认值 调优建议
chunk_size 500 短文本:200300;长文档:8001000
chunk_overlap 100 通常设为chunk_size的20%,保持语义连续性
search_kwargs["k"] 4 简单问题23个,复杂推理问题68个
temperature 0.2 RAG场景保持低值,0~0.3足够
separators 见上文 中文文档优先用做分隔符

3 ~> Document Loader支持的文件格式

# PDF
from langchain_community.document_loaders import PyPDFLoader

# Word文档
from langchain_community.document_loaders import Docx2txtLoader

# 纯文本
from langchain_community.document_loaders import TextLoader

# CSV(每行作为一条记录)
from langchain_community.document_loaders import CSVLoader

# 网页
from langchain_community.document_loaders import WebBaseLoader

# 整个目录
from langchain_community.document_loaders import DirectoryLoader

思考 && 总结

  1. LangChain把RAG拆成四个标准步骤: Load → Split → Embed & Store → Retrieve & Generate。每步都有几行代码的模板,组合起来就是一个完整应用。
  2. RecursiveCharacterTextSplitter是中文文档最好的分块器: 按段落→句子→词的顺序递进分割,比固定长度切分保持语义完整性更好。
  3. create_retrieval_chain一行封装了整个"检索+生成"链: 背后是Retriever查向量库 → 拼接Prompt → 喂给LLM的完整流水线。
  4. chunk_overlap是防止"关键信息被切断"的保险: 100字符重叠意味着同一句话可能在相邻两个块中都完整出现。
  5. 温度设低是RAG的铁律: 你的答案应该来自检索到的文档,不是模型的"自由发挥"。

30行核心代码就能搭一个可用的RAG问答系统——这就是LangChain的抽象能力。但理解每个组件在做什么,比会用API更重要。


结尾

各位小伙伴,本文的内容到这里就全部结束了!源码骑士 — Android Framework & 全栈开发

👀 关注 | ❤️ 点赞 | ⭐ 收藏:把完整脚本存好下个项目直接改 | 💬 评论 | 🔄 一键四连!🗡️ 寄语:最好的学习方式就是用自己的文档跑一遍——找一份PDF,让AI基于它回答你的问题。

结语:找一份你熟悉的PDF文档,把上面的脚本拷过去,改一下文件路径。跑起来,问它一个问题——看到AI基于你的文档给出准确回答的那一刻,你就真正理解了RAG。不要忘记给博主"一键四连"哦!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐