news 2026/4/30 14:24:12

LangChain实现简易版-----PDF 文档问答机器人

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChain实现简易版-----PDF 文档问答机器人

文档加载器 + 文本分割器 + PromptTemplate + LLM

原理(极简版,不学向量也能懂)

  1. 加载 PDF 全部文本
  2. 分割成多个语义完整文本块
  3. 用户提问 → 简单匹配最相关的文本块
  4. 把「相关文档片段 + 用户问题」塞进提示词
  5. 强制 LLM只能看给的文档片段回答,不准瞎编

第一步 安装依赖

pip install -U langchain langchain-openai langchain-community pypdf python-dotenv

第二步 完整可运行代码(纯基础知识点,无 RAG 无向量库)

# 文档问答机器人 import os from dotenv import load_dotenv from langchain_core import documents from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_community.document_loaders import PyPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter # 加载环境变量 load_dotenv() # ====================== 1. 配置 ================================= PDF_PATH = "Dubbo面试.pdf" CHUNK_SIZE = 1200 # 单个块最大的字符数(推荐1000 - 2000) CHUNK_OVERLAP = 200 # 相邻块的重叠字符数(推荐200 - 400) 保留上下文 # 初始化大模型 LLM llm = ChatOpenAI( api_key=os.getenv("QWEN_API_KEY"), base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", model='qwen3-max', # 必须用 pro模型,支持工具调用 temperature=0 ) # ======================2, 加载PDF ================================= def load_PDF(pdf_path: str): """ 加载本地 PDF 文档 :param pdf_path: PDF 文件路径 :return: Document 对象列表(每个元素是 PDF 的一页) """ print(f'📄 正在加载,PDF:{PDF_PATH}...') # 初始化PDF加载器 loader = PyPDFLoader(pdf_path) #加载PDF 每个元素 对应pdf一页 documents = loader.load() print(f'✅️PDF文档加载完成!共{len(documents)}页 \n') return documents # ======================= 3. 文本语义分割 =============================== def split_documents(documents): """ 用 RecursiveCharacterTextSplitter 进行语义完整的文本分割 :param documents: 加载后的 Document 对象列表 :return: 分割后的文本块列表 """ print("✂️ 正在进行语义完整的文本分割...") # ✅️ 核心:初始化 RecursiveCharacterTextSplitter (最推荐的分割器) text_splitter = RecursiveCharacterTextSplitter( chunk_size=CHUNK_SIZE, chunk_overlap=CHUNK_OVERLAP, # 分割优先级(按顺序,优先保留段落、句子) separators=["\n\n", "\n", "。", "!", "?", " ", ""], ) # 执行分割 split_chunks = text_splitter.split_documents(documents) print(f"✅ 文本分割完成!共生成 {len(split_chunks)} 个文本块\n") return split_chunks # ====================== 4. 简单的文本匹配 ========================== def get_relevant_chunk(question, chunks): """ 简易关键词匹配:找出和问题最相关的文档块 不用向量,纯字符串包含匹配 """ # 提取问题关键词 (简单按空格拆分) q_words = set(question.replace(",", " ").replace("。", " ").split()) print(f'q_words: {q_words}') best_chunk = None max_score = 0 for chunk in chunks: content = chunk.page_content # 统计命中关键词数量 hit = sum(1 for word in q_words if word in content) if hit > max_score: max_score = hit best_chunk = content # 没有任何匹配 返回空 if max_score == 0: return None return best_chunk # ============================ # 5,构建问答prompt + 普通chain ============================== # 强约束 只能用文档内容 不能编造 prompt = ChatPromptTemplate.from_template(""" 你是文档专属问答助手,**严格遵守以下规则**: 1. 只能依据【文档内容】回答用户问题 2. 文档里没有相关信息,直接回复:文档中没有相关内容 3. 绝对不能自己编造、不能使用外部知识 4. 回答简洁准确 【文档内容】 {context} 【用户问题】 {question} """) # 普通链式组装 chain = prompt | llm | StrOutputParser() # ===================== 6. 对话主循环 ========================================== def main(): print("===== 📚 简易PDF文档问答机器人(无向量库、无RAG)=====") print("正在加载并处理文档...\n") # 加载+分割 documents = load_PDF(PDF_PATH) chunks = split_documents(documents) print("🤖 文档加载完毕,可以开始提问,输入 q 退出\n") while True: question = input("你:").strip() if question.lower() == "q": print("🤖 再见!") break if not question: continue # 匹配相关文档片段 context = get_relevant_chunk(question, chunks) if not context: print("🤖:文档中没有相关内容\n") continue # 传入文档片段+问题,让LLM回答 ans = chain.invoke({ "context": context, "question": question }) print(f"🤖:{ans}\n") if __name__ == "__main__": main()

第三步 .env 文件配置

DOUBAO_API_KEY=你的豆包密钥

四、用到的知识点(全是你学过的)

  1. PyPDFLoader文档加载器
  2. RecursiveCharacterTextSplitter语义文本分割器
  3. ChatPromptTemplate提示词模板
  4. ChatOpenAI大模型调用
  5. LCEL 链式调用|
  6. StrOutputParser输出解析器

五、运行效果

  • 问文档里有的内容 → 精准基于文档回答
  • 问文档里没有的 → 自动回复:文档中没有相关内容
  • 不会瞎编、不会扯外面知识
===== 📚 简易PDF文档问答机器人(无向量库、无RAG)===== 正在加载并处理文档... 📄 正在加载,PDF:Dubbo面试.pdf... ✅️PDF文档加载完成!共15页 ✂️ 正在进行语义完整的文本分割... ✅ 文本分割完成!共生成 16 个文本块 🤖 文档加载完毕,可以开始提问,输入 q 退出 你:Dubbo 支持哪些协议,每种协议的应用场景,优缺点 q_words: {'支持哪些协议,每种协议的应用场景,优缺点', 'Dubbo'} 🤖:Dubbo 支持以下协议,每种协议的应用场景和优缺点如下: - **dubbo**:单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用,以及消费者远大于提供者。传输协议 TCP,异步,Hessian 序列化。 - **rmi**:采用 JDK 标准的 rmi 协议实现,传输参数和返回参数对象需实现 Serializable 接口,使用 java 标准序列化机制,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议 TCP。多个短连接,TCP 协议传输,同步传输,适用常规的远程服务调用和 rmi 互操作。在依赖低版本的 Common-Collections 包时,java 序列化存在安全漏洞。 - **webservice**:基于 WebService 的远程调用协议,集成 CXF 实现,提供和原生 WebService 的互操作。多个短连接,基于 HTTP 传输,同步传输,适用系统集成和跨语言调用。 - **http**:基于 Http 表单提交的远程调用协议,使用 Spring 的 HttpInvoke 实现。多个短连接,传输协议 HTTP,传入参数大小混合,提供者个数多于消费者,需要给应用程序和浏览器 JS 调用。 - **hessian**:集成 Hessian 服务,基于 HTTP 通讯,采用 Servlet 暴露服务,Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与 Hessian 服务互操作。多个短连接,同步 HTTP 传输,Hessian 序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件。 - **memcache**:基于 memcached 实现的 RPC 协议。 - **redis**:基于 redis 实现的 RPC 协议。 你:q 🤖 再见!

🔴 先预判一下你的问题

这个报错ValueError: Invalid input type <class 'dict'>是 LangChain 里最经典的链结构错误,大概率是这 3 种情况之一:

  1. 直接把字典传给了大模型你可能写了llm.invoke({"question": "xxx"}),但大模型只接受字符串或消息列表。

  2. 链的顺序写错了你可能写了{...} | llm,但正确的应该是prompt | llm

  3. PromptTemplate 后面没接 llm,或者接错了你可能在链里混用了不同类型的 Runnable,导致输出格式不对。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 14:22:40

如何用MarkMap思维导图工具3步可视化你的Markdown笔记

如何用MarkMap思维导图工具3步可视化你的Markdown笔记 【免费下载链接】markmap Build mindmaps with plain text 项目地址: https://gitcode.com/gh_mirrors/ma/markmap 你是否曾面对密密麻麻的Markdown文档感到头疼&#xff1f;信息碎片化、结构不清晰、重点难以把握—…

作者头像 李华
网站建设 2026/4/30 14:16:02

车载总线故障智能诊断【附代码】

✅ 博主简介&#xff1a;擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导&#xff0c;毕业论文、期刊论文经验交流。 ✅ 如需沟通交流&#xff0c;扫描文章底部二维码。&#xff08;1&#xff09;电平跳变复位采样与自适应波特率估计&#xff1a;针对LIN/CA…

作者头像 李华
网站建设 2026/4/30 14:07:24

终极输入法词库转换解决方案:深蓝词库转换完全指南

终极输入法词库转换解决方案&#xff1a;深蓝词库转换完全指南 【免费下载链接】imewlconverter ”深蓝词库转换“ 一款开源免费的输入法词库转换程序 项目地址: https://gitcode.com/gh_mirrors/im/imewlconverter 还在为更换输入法时词库无法迁移而烦恼吗&#xff1f;…

作者头像 李华