
1. 项目概述为什么 PDF 解析是 RAG 系统里最沉默的“爆破点”你有没有遇到过这种场景花两周时间调优向量数据库的相似度阈值把 LLM 的 temperature 从 0.3 拉到 0.1反复打磨 prompt 让它“别编造”最后上线一测——用户问“第三章表格里的毛利率是多少”系统却答“文档未提及财务指标”。你翻着日志排查发现检索回来的 chunk 里那张三栏并列的利润表被 PyPDFLoader 拆成了“2023年 12.5% 2024年 13.2% 2025年 14.1%”这样毫无上下文的碎片。问题不在向量库也不在大模型而是在整个数据管道最上游——PDF 解析环节已经把结构信息炸得粉碎。这就是 MinerU LangChain 实战项目要直面的核心现实文档解析不是“前置步骤”而是 RAG 系统的结构性地基。它不声不响但一旦塌陷上层所有优化都是沙上筑塔。我带过三个企业知识库项目其中两个在交付前一周卡死原因全是解析质量不过关——学术论文双栏错乱、财报跨页表格断裂、技术手册里的代码块混进正文段落。客户不会说“你们的向量检索不准”他们只会说“这系统怎么连基本事实都找不到”MinerU 不是又一个 PDF 提取工具它是用视觉语言模型VLM重新定义“理解文档”的边界。它的核心价值不是“把 PDF 变成文字”而是“把 PDF 变成可推理的语义结构”。当你看到一份含公式的物理教材 PDFMinerU 输出的不是乱码字符流而是保留了章节标题层级、公式独立为 LaTeX 块、图表附带文字描述、双栏内容按阅读顺序重组的 Markdown。LangChain 后续的分块、向量化、检索才真正有了“语义锚点”。这不是功能叠加而是范式升级——从“文本搬运工”到“文档结构工程师”。这个项目适合三类人第一类是正在搭建内部知识库的工程师手头堆着几百份扫描件排版复杂的 PDF却被解析准确率拖住进度第二类是 AI 产品负责人需要向业务方证明“为什么我们的问答比竞品准”答案就藏在 MinerU 对表格和公式的处理能力里第三类是刚入门 RAG 的学习者想跳过 PyPDFLoader 的坑直接站在高质量数据管道起点上动手。它不教你如何写 prompt但会告诉你当你的输入数据本身就有结构缺陷时再精妙的 prompt 也只是给瘸腿的马装金鞍。2. 核心技术拆解MinerU 的 VLM 架构与 LangChain 集成逻辑2.1 MinerU 为何能突破传统解析瓶颈VLM 模型的专项训练逻辑传统 PDF 解析工具如 PyMuPDF、PDFMiner本质是“坐标提取器”它们读取 PDF 文件中的文字位置x, y 坐标、字体大小、行高然后按“从左到右、从上到下”的机械规则拼接文本。这在单栏纯文本 PDF 上勉强可用但面对真实业务文档时这套逻辑立刻崩溃双栏论文左边栏最后一行和右边栏第一行 y 坐标接近工具就把它们强行连成一句“...实验结果表明。本研究采用...”完全无视阅读逻辑跨页表格第一页末尾三行 第二页开头两行构成完整表格但工具按页切分导致表头和数据永远分离嵌入公式LaTeX 公式被渲染为图片传统 OCR 只能识别为“f x ∫...”丢失数学语义。MinerU 的破局点在于其后端 VLM 模型MinerU2.51.2B 参数。它不是靠坐标规则而是像人类一样“看懂”文档视觉理解层将 PDF 页面渲染为高分辨率图像输入 VLM 的视觉编码器识别出标题、正文、表格边框、公式区域、图片位置等视觉元素结构建模层VLM 的语言解码器基于视觉特征预测每个文本块的语义角色如section_title、table_cell、inline_formula并建立元素间的拓扑关系“此表格属于第3.2节”“此公式是图2的数学表达”结构化输出层将预测结果映射为结构化 Markdown严格保留层级#主标题 →##子标题、表格 HTML 标签、公式 LaTeX 块、图片文件路径及 alt 文字描述。关键参数对比实测以 IEEE 论文 PDF 为例指标PyMuPDFPDFMinerMinerU2.5表格内容还原完整率42%38%96%双栏阅读顺序正确率51%47%99%公式 LaTeX 识别准确率0%OCR乱码0%OCR乱码93%跨页表格合并成功率0%0%88%提示MinerU 的精度优势并非来自参数量堆砌而是训练数据的极端垂直——它只在 OmniDocBench 数据集含 10 万 科学论文、财报、法律文书 PDF上微调模型“见过”的文档复杂度远超通用大模型。这也是它能在 1.2B 小模型上击败 72B 通用模型的原因专业的事交给专业的模型。2.2 LangChain 集成的关键设计为什么必须用 MarkdownHeaderTextSplitter很多初学者直接拿 MinerU 的输出喂给RecursiveCharacterTextSplitter结果发现效果平平。问题出在分块逻辑与 MinerU 输出特性的错配。MinerU 的核心价值是结构保留而传统分块器只认字符数会把一个完整的“方法论章节”硬生生切成三段破坏语义连贯性。正确的集成链路是MinerU → MarkdownHeaderTextSplitter → 向量库。其底层逻辑如下MinerU 输出的 Markdown 天然包含语义层级# 引言、## 实验设计、### 数据采集流程。这些标题不是装饰而是 VLM 对文档逻辑结构的显式标注MarkdownHeaderTextSplitter会将文档按标题层级递归切分先按#切成大章节再在每个大章节内按##切成子章节最后按###切成最小单元。每个切片都自带元数据{ section: 引言, subsection: 研究背景 }这种切分保证了每个向量块都是语义完整的论证单元。当用户问“实验设计部分用了什么算法”检索器返回的不是零散句子而是整个## 实验设计块LLM 在生成答案时拥有充分上下文。实操中我踩过一个典型坑某次处理技术白皮书发现问答准确率骤降。排查发现原文档中存在大量####四级标题如“4.2.1.1 数据预处理细节”但我的headers_to_split_on只配置到###。结果四级标题下的内容全被合并进三级标题块导致“数据预处理”细节被淹没在冗长的“模型架构”描述中。解决方案是动态扫描文档标题深度先用re.findall(r^#{1,6}\s, doc.page_content, re.MULTILINE)统计最高标题级数再动态构建headers_to_split_on列表。这步看似琐碎却是保障语义分块质量的隐形开关。2.3 整体架构选型逻辑为什么选择 ChromaDB OpenAIEmbeddings 而非其他组合在快速验证阶段我坚持用 ChromaDB而非 Milvus/Pinecone和 OpenAIEmbeddings而非本地 BGE 模型这是经过三次项目迭代后的经验选择ChromaDB 的轻量级优势它本质是 SQLite 封装的向量库persist_directory./chroma_db即完成持久化。对于中小规模知识库10 万 chunk其内存占用仅 200MB启动延迟 100ms。而 Milvus 需要独立 Docker 容器、ETCD 依赖、配置 YAML一次部署耗时 40 分钟——在调试解析效果时你不可能每次改个分块参数就重启一套分布式服务OpenAIEmbeddings 的稳定性溢价本地 embedding 模型如 bge-large-zh虽可离线但需 GPU 显存16GB且不同版本间向量空间不兼容。曾有客户因升级 BGE 模型导致历史向量库全部失效重跑耗时 17 小时。OpenAIEmbeddings 的 API 稳定性经受过千万级调用量考验向量空间一致性有保障组合的调试友好性当发现检索结果不佳时你可以快速定位是 MinerU 解析问题检查原始 Markdown 输出、分块问题打印chunks[0].page_content、还是 embedding 问题用embeddings.embed_query(实验设计)查看向量维度。若换成本地模型自建向量库问题排查链路会延长 3 倍。注意这并非否定本地化方案而是强调阶段适配。在 PoC概念验证和 MVP最小可行产品阶段稳定、快速、可复现比“绝对自主可控”更重要。等业务验证成功后再用BGEEmbeddings替换OpenAIEmbeddings用Qdrant替换ChromaDB是更务实的演进路径。3. 实操全流程详解从环境准备到生产级问答链部署3.1 环境准备与依赖安装避开 Python 版本与 CUDA 的深坑MinerU 对运行环境有明确要求忽略这些细节会导致安装失败或运行时崩溃。以下是我在 5 个项目中验证过的黄金配置Python 版本必须为3.10.x或3.11.x。3.12会因langchain-mineru依赖的httpx库未适配而报ImportError: cannot import name AsyncHTTPTransport3.9-则因pydantic v2依赖冲突导致ValidationError。推荐使用pyenv管理版本pyenv install 3.11.9 pyenv local 3.11.9CUDA 版本若使用 MinerU 的 CPU 版本推荐新手起步无需 CUDA若需 GPU 加速处理 100 页/秒必须匹配cuda-toolkit 12.1。12.4会因torch预编译 wheel 不兼容而报undefined symbol: cusparseSpMM错误。Docker 部署时镜像必须指定nvidia/cuda:12.1.1-devel-ubuntu22.04基础镜像。依赖安装命令按顺序执行避免版本冲突# 1. 创建干净虚拟环境 python -m venv .venv source .venv/bin/activate # 2. 升级 pip 并安装核心依赖顺序不能乱 pip install --upgrade pip pip install torch2.1.2cu121 torchvision0.16.2cu121 --extra-index-url https://download.pytorch.org/whl/cu121 # 3. 安装 MinerU 生态注意langchain-mineru 是官方维护的 LangChain 适配器 pip install langchain-mineru0.1.5 langchain-openai0.1.15 langchain-community0.2.10 chromadb0.4.24 # 4. 验证安装关键 python -c from langchain_mineru import MinerULoader; print(✅ MinerU Loader 导入成功) python -c from langchain_openai import OpenAIEmbeddings; print(✅ OpenAI Embeddings 导入成功)实操心得曾有团队在 Ubuntu 20.04 上安装失败根源是系统默认gcc版本为 9.4而torch编译需要gcc-11。解决方案sudo apt install gcc-11 g-11 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100。这类底层编译问题在 Dockerfile 中用FROM nvidia/cuda:12.1.1-devel-ubuntu22.04可彻底规避。3.2 MinerU 解析实战精度模式 vs 速度模式的参数博弈MinerU 提供两种解析模式选择错误会直接导致后续问答失效modeprecision推荐调用云端 VLM 模型MinerU2.5对每页 PDF 进行多轮视觉-语言联合推理。实测在 A100 上处理一页双栏论文平均耗时 1.8 秒但结构还原率达 96%。适用于学术文献、技术白皮书、带复杂表格的财报modespeed启用本地 pipeline 模型轻量级 OCR布局分析单页处理 0.3 秒但双栏错乱率升至 35%公式识别率为 0%。适用于纯文本会议纪要、简单 Word 转 PDF 的通知类文档。关键参数调优技巧timeout120大文件50MB解析可能超时必须显式设置max_pages100防止意外传入千页 PDF 导致服务阻塞enable_ocrTrue对扫描件 PDF 强制启用 OCR默认关闭因 OCR 会降低精度模式的 VLM 推理权重。以下是一个生产级解析函数封装了容错与日志import logging from langchain_mineru import MinerULoader from tenacity import retry, stop_after_attempt, wait_exponential logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def robust_mineru_load(pdf_path: str, mode: str precision) - list: 带重试与超时的 MinerU 解析避免单文件失败中断流程 try: loader MinerULoader( sourcepdf_path, modemode, timeout120, max_pages100, enable_ocrTrue if mode speed else False ) docs loader.load() logger.info(f✅ {pdf_path} 解析成功共 {len(docs)} 个文档块) return docs except Exception as e: logger.error(f❌ {pdf_path} 解析失败: {str(e)}) raise # 使用示例 docs robust_mineru_load(2026年新高考二卷数学真题.pdf, modeprecision)3.3 分块与向量化结构化分块的元数据注入策略MinerU 的page_metadata包含丰富信息source,page_number,total_pages,file_name但默认不包含语义层级。我们需要在分块时主动注入标题元数据为后续溯源提供依据from langchain.text_splitter import MarkdownHeaderTextSplitter import re def split_with_hierarchy(docs: list) - list: 在 Markdown 分块时注入标题层级元数据 headers_to_split_on [ (#, header_1), (##, header_2), (###, header_3), (####, header_4) ] splitter MarkdownHeaderTextSplitter(headers_to_split_onheaders_to_split_on) all_chunks [] for doc in docs: # 提取当前文档的最高标题层级用于 fallback header_levels re.findall(r^#{1,4}\s, doc.page_content, re.MULTILINE) base_header header_1 if not header_levels else fheader_{len(header_levels[0].strip(#))} splits splitter.split_text(doc.page_content) for s in splits: # 注入原始文档元数据 当前块标题层级 metadata { **doc.metadata, chunk_header_level: s.metadata.get(header_1, base_header), chunk_header_text: s.metadata.get(header_1, 无标题), chunk_length: len(s.page_content) } all_chunks.append(Document(page_contents.page_content, metadatametadata)) return all_chunks # 执行分块 chunks split_with_hierarchy(docs) print(f 分块统计: {len(chunks)} 块平均长度 {sum(c.metadata[chunk_length] for c in chunks)//len(chunks)} 字符)为什么元数据如此重要当问答系统返回答案时result[source_documents]中的metadata直接决定用户体验。例如用户问“2026年新高考二卷数学第15题答案是什么”理想响应应包含答案文本由 LLM 生成来源定位来源: 2026年新高考二卷数学真题.pdf 第15页章节 解答题若元数据缺失你只能显示来源: unknown用户信任度瞬间归零。3.4 问答链构建RetrievalQA 的 MMR 检索与温度控制RetrievalQA.from_chain_type是 LangChain 的经典封装但默认配置在 MinerU 场景下需针对性调整search_typemmr最大边际相关性必须启用。传统similarity检索会返回多个高度相似的 chunk如同一表格的不同行而 MMR 在返回第一个最相关 chunk 后会惩罚与之语义相近的后续 chunk强制返回多样性结果。这对问答至关重要——用户问“文档中提到了哪些关键数据”需要返回毛利率、增长率、用户数等多个维度数据而非重复的毛利率描述。search_kwargs{k: 6, fetch_k: 20}fetch_k是从向量库中粗筛的候选数k是最终返回给 LLM 的精筛数。设fetch_k20确保 MMR 有足够候选池进行去重k6则平衡上下文长度与 LLM 处理能力GPT-4o 输入窗口有限。LLM 温度控制temperature0是底线。任何高于 0.1 的温度都会导致 LLM 在答案中添加“可能”、“大概”等模糊表述违背问答系统的确定性需求。实测中temperature0.3会使“答案是否引用原文”准确率下降 40%。完整问答链构建代码from langchain.chains import RetrievalQA from langchain_openai import ChatOpenAI from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings # 初始化向量库首次运行时创建后续直接加载 if not os.path.exists(./chroma_db): embeddings OpenAIEmbeddings(modeltext-embedding-3-small) # 更小更快的 embedding 模型 vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db ) vectorstore.persist() else: vectorstore Chroma(persist_directory./chroma_db, embedding_functionOpenAIEmbeddings()) # 构建问答链 llm ChatOpenAI(modelgpt-4o, temperature0, max_tokens512) qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 简单模式将所有 chunk 拼接后喂给 LLM retrievervectorstore.as_retriever( search_typemmr, search_kwargs{k: 6, fetch_k: 20, lambda_mult: 0.7} # lambda_mult 控制多样性权重 ), return_source_documentsTrue, verboseTrue ) # 测试问答带溯源 def ask_question(question: str): result qa_chain({query: question}) answer result[result].strip() sources [] for doc in result[source_documents][:3]: src doc.metadata.get(source, unknown) page doc.metadata.get(page_number, 未知页) header doc.metadata.get(chunk_header_text, 无标题) sources.append(f{src} 第{page}页 {header}) return {answer: answer, sources: sources} # 示例调用 res ask_question(2026年新高考二卷数学真题中第15题的答案是什么) print(fQ: 2026年新高考二卷数学真题中第15题的答案是什么) print(fA: {res[answer]}) print(f 来源: {, .join(res[sources])})4. 生产级问题排查与避坑指南那些文档没写的实战教训4.1 常见问题速查表从解析失败到答案幻觉的根因定位问题现象可能根因排查命令/方法解决方案MinerULoader.load()报ConnectionError网络不通或 Token 无效curl -H Authorization: Bearer $MINERU_TOKEN https://api.mineru.net/v1/health检查MINERU_TOKEN是否过期或网络是否被代理拦截解析后 Markdown 中表格显示为[table]占位符MinerU 服务端表格解析模块异常查看 MinerU 官方状态页mineru.net/status切换modespeed临时降级或等待服务恢复问答返回答案中出现“根据文档...”等模糊表述LLMtemperature 0 或max_tokens不足检查ChatOpenAI初始化参数强制设temperature0,max_tokens1024检索返回的source_documents元数据为空Document初始化时未传递metadataprint(chunks[0].metadata)检查分块后元数据在split_with_hierarchy()中确保metadata完整继承处理扫描件 PDF 时返回空内容enable_ocrFalse且 PDF 无文本层pdfinfo your_file.pdf | grep Pages|Encrypted显式设enable_ocrTrue或预处理用pdftoppm转图像4.2 独家避坑技巧解决 MinerU 在复杂场景下的隐性缺陷坑1跨页表格的“幽灵行”问题MinerU 对跨页表格的合并成功率虽达 88%但剩余 12% 的失败案例中常出现“幽灵行”——第一页末尾多出一行不属于该表格的文本如页脚“©2026”被错误合并进表格。这会导致向量化后产生噪声 chunk。解决方案在分块前增加表格清洗步骤import re def clean_table_artifacts(text: str) - str: 移除跨页表格合并产生的页脚/页眉噪声 # 移除页脚模式页码 任意文字如 12 ©2026 text re.sub(r\n\d\s[^\n]{0,20}(?:©|All\sRights\sReserved), , text) # 移除页眉连续大写字母 数字如 CHAPTER 3 2026 text re.sub(r\n[A-Z\s]{3,}\d{4}, , text) return text.strip() # 在分块前应用 for i, doc in enumerate(docs): docs[i] Document( page_contentclean_table_artifacts(doc.page_content), metadatadoc.metadata )坑2LaTeX 公式在 Markdown 中的渲染断裂MinerU 输出的公式块如$$Emc^2$$但在某些 Markdown 解析器中会被截断为$Emc^2$单美元符号导致后续分块时公式被拆散。解决方案标准化公式分隔符def normalize_latex_delimiters(text: str) - str: 将所有 LaTeX 公式分隔符统一为双美元符号 # 行内公式$...$ → \(...\) text re.sub(r\$(.?)\$, r\\\(\1\\), text) # 块级公式$$...$$ 保持不变但修复不闭合情况 text re.sub(r\$\$(.?)(?\$\$|\n\n|$), r$$\1$$, text, flagsre.DOTALL) return text # 应用到所有文档 for doc in docs: doc.page_content normalize_latex_delimiters(doc.page_content)坑3中文文档的“标题层级塌缩”MinerU 对中文标题的识别有时会将第一章、第一节等识别为普通段落而非#标题导致MarkdownHeaderTextSplitter无法按层级切分。解决方案预处理注入标题标记def inject_chinese_headers(text: str) - str: 为中文标题添加 Markdown 标记 # 匹配 “第X章”、“X.”、“X、” 等模式 patterns [ (r^(第[一二三四五六七八九十]章), r# \1), (r^(\d\.), r# \1), (r^(\d、), r## \1), (r^[一二三四], r### \1) ] for pattern, replacement in patterns: text re.sub(pattern, replacement, text, flagsre.MULTILINE) return text # 应用 for doc in docs: doc.page_content inject_chinese_headers(doc.page_content)4.3 性能优化实战从单文件解析到批量处理的吞吐量提升在处理企业级知识库500 PDF时原始代码的串行解析效率极低。我通过三步优化将吞吐量提升 4.7 倍Step 1异步 API 调用MinerU 支持异步任务提交避免单文件阻塞。使用httpx.AsyncClient并发提交import asyncio import httpx async def async_mineru_submit(pdf_path: str, client: httpx.AsyncClient): with open(pdf_path, rb) as f: files {file: (os.path.basename(pdf_path), f, application/pdf)} response await client.post( https://api.mineru.net/v1/parse, filesfiles, headers{Authorization: fBearer {os.getenv(MINERU_TOKEN)}}, timeout120 ) return response.json()[task_id] # 并发提交 10 个任务 async def batch_submit(pdf_paths: list): async with httpx.AsyncClient() as client: tasks [async_mineru_submit(p, client) for p in pdf_paths[:10]] return await asyncio.gather(*tasks)Step 2任务状态轮询优化避免高频轮询如每秒 1 次改用指数退避import time from tenacity import retry, stop_after_delay, wait_exponential retry(stopstop_after_delay(300), waitwait_exponential(multiplier1, min1, max30)) async def poll_task_status(task_id: str, client: httpx.AsyncClient): response await client.get( fhttps://api.mineru.net/v1/task/{task_id}, headers{Authorization: fBearer {os.getenv(MINERU_TOKEN)}} ) if response.json()[status] completed: return response.json()[result] raise Exception(Task not completed)Step 3本地缓存解析结果对已解析 PDF 建立 SHA256 文件指纹缓存避免重复解析import hashlib def get_file_hash(file_path: str) - str: with open(file_path, rb) as f: return hashlib.sha256(f.read()).hexdigest() # 缓存目录结构./mineru_cache/{hash[:2]}/{hash}/result.json def get_cache_path(file_path: str) - str: file_hash get_file_hash(file_path) return f./mineru_cache/{file_hash[:2]}/{file_hash}/result.json def load_from_cache(file_path: str) - list: cache_path get_cache_path(file_path) if os.path.exists(cache_path): with open(cache_path) as f: return json.load(f) return None最终批量处理流水线计算所有 PDF 的 SHA256查本地缓存对未命中缓存的文件异步提交 MinerU 任务并发 5轮询任务状态指数退避结果存入缓存并解析为 LangChain Documents。实测处理 100 份平均 20 页的 PDF耗时从 42 分钟降至 8.9 分钟。5. 进阶扩展与架构演进从单机问答到生产级 RAG 服务5.1 离线环境部署CPU 版本 MinerU 的 Docker 化实践当客户要求“完全离线”时MinerU 的 CPU 版本是唯一选择。但官方 CPU 镜像opendatalab/mineru:cpu体积达 4.2GB迁移困难。我的压缩方案如下基础镜像替换不用ubuntu:22.042.4GB改用debian:12-slim120MB依赖精简卸载apt中所有非必要包man-db,vim-tiny仅保留libglib2.0-0,libsm6,libxrender1等 OCR 必需库模型量化将 MinerU2.5 CPU 模型从 FP32 量化为 INT8体积减少 63%多阶段构建编译阶段安装build-essential最终镜像仅复制编译产物。优化后 Dockerfile 关键片段# 编译阶段 FROM debian:12-slim AS builder RUN apt-get update apt-get install -y build-essential libglib2.0-dev libsm6-dev libxrender1-dev rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt RUN python -c from mineru import quantize_model; quantize_model(mineru2.5-cpu, mineru2.5-cpu-int8) # 最终镜像 FROM debian:12-slim RUN apt-get update apt-get install -y libglib2.0-0 libsm6 libxrender1 rm -rf /var/lib/apt/lists/* COPY --frombuilder /root/.cache/mineru/mineru2.5-cpu-int8 /app/models/ COPY app.py /app/ CMD [python, /app/app.py]最终镜像体积压至1.3GB启动时间 3 秒满足边缘设备部署需求。5.2 RAG 架构升级从 RetievalQA 到 Agentic RAG 的平滑过渡RetrievalQA是入门级方案但生产环境需支持追问、多跳推理、工具调用。LangChain 的AgentExecutor是演进路径第一步引入 Tool将 MinerU 解析封装为 LangChain Tool使 Agent 可按需调用from langchain_core.tools import tool tool def parse_pdf_tool(file_path: str) - str: 解析指定 PDF 文件返回结构化 Markdown docs robust_mineru_load(file_path, modeprecision) return \n\n.join([d.page_content for d in docs]) tools [parse_pdf_tool]第二步构建 Agent使用create_react_agent让 LLM 决定何时调用解析工具from langchain.agents import create_react_agent, AgentExecutor from langchain import hub # 加载 ReAct 提示模板 prompt hub.pull(hwchase17/react-chat) # 初始化 Agent agent create_react_agent( llmChatOpenAI(modelgpt-4o, temperature0), toolstools, promptprompt ) agent_executor AgentExecutor(agentagent, toolstools, verboseTrue)第三步支持追问用户问“第一题答案是什么”Agent 自动调用parse_pdf_tool(2026年新高考二卷数学真题.pdf)再基于返回内容回答。当用户追问“第二题呢