从文档切分到Agent穿甲——LangChain 1.0五步造一个会干活的生产级AI
从文档切分到 Agent 穿甲——LangChain 1.0 五步造一个会干活的生产级 AI你是不是也卡在这五步上想做一个企业级 AI Agent搜了一堆教程学了文档切分、Embedding、RAG、Tool、中间件——但每个都是孤立的知识点串不起来知道要切分文档但不知道切完之后怎么跟向量库对接知道 Embedding 是把文字变成数字但不知道它在整条链路里到底扮演什么角色知道 RAG 是检索增强生成但不知道它跟 Tool 有什么区别知道中间件可以做日志/重试/人工审批但不知道它卡在 Agent 生命周期的哪个环节今天这篇就用一条完整的链路把五步全部串起来。┌─────────────────────────────────────────────────────────────────────┐ │ 生产级 AI Agent 开发全链路 │ │ │ │ 第1步 第2步 第3步 第4步 第5步 │ │ 文档切分 ──→ Embedding ──→ RAG检索 ──→ Tool调用 ──→ 中间件 │ │ │ │ 把200页 把文字变 用问题找 让AI能 给Agent穿 │ │ 手册切成 成1024维 相关文档 调外部工具 上日志/重试│ │ 小文档块 向量 喂给大模型 干活 /审批铁甲 │ │ │ │ Recursive DashScope Retriever tool Middleware │ │ TextSplitter Embeddings LCEL Chain create_agent │ │ PyPDFLoader Chroma Prompt Agent循环 │ │ Milvus │ └─────────────────────────────────────────────────────────────────────┘下面逐步拆解。第1步文档加载与切分——把 200 页手册变成一口能吃下的小块这一步在链路中的角色原料预处理。你有一堆 PDF、Markdown、TXT——模型一次吃不下整份文档必须先切块。200页员工手册.pdf │ ▼ PyPDFLoader每页→1个Document │ ▼ RecursiveCharacterTextSplitter按段落递归切分 │ ▼ 16个小文档块每块约300字带元数据核心代码fromlangchain_community.document_loadersimportPyPDFLoader,TextLoaderfromlangchain_text_splittersimportRecursiveCharacterTextSplitter# 1. 加载 PDF每页变成一个 DocumentloaderPyPDFLoader(data/employee_handbook.pdf)documentsloader.load()# 2. 递归切分优先按段落切段落太长再按句号切splitterRecursiveCharacterTextSplitter(chunk_size300,# 每块最大 300 字符chunk_overlap50,# 相邻块重叠 50 字符防止句子被切断separators[\n\n,\n,。,,,,, ,])chunkssplitter.split_documents(documents)print(f切分完成{len(documents)}页 →{len(chunks)}个文档块)两个关键参数参数作用建议值chunk_size每个文档块最大长度300-500普通文本chunk_overlap相邻块重叠长度chunk_size 的 10%-20%比喻切分就像把一整头牛切成牛排——不能乱剁要顺着纹理段落切每块大小适中切面要有重叠 overlap不然肉汁上下文就流失了。深入阅读本文只讲核心概念。完整教程Document 对象、元数据保留、Path 遍历、企业级预处理脚本请看系列第 1 篇。第2步Embedding 与向量数据库——给文字装上「语义 GPS」这一步在链路中的角色把文字变成数字让计算机能算「语义相似度」。切分后的文档块是文字但文字没法直接做数学比较——Embedding 把每个文档块变成一个 1024 维的浮点数向量存进向量数据库。文档块未发货可以退款吗 │ ▼ Embedding 模型text-embedding-v4 │ ▼ 向量[0.012, -0.035, 0.078, ..., -0.008] ← 1024 个数字 │ ▼ 存入向量数据库Milvus / Chroma关键点语义相近的文字向量距离就近。用户问没发货能退吗虽然跟文档里写的订单未发货时用户可以直接申请退款用词不同但向量距离很近——这就是语义检索的本质。核心代码fromlangchain_community.embeddingsimportDashScopeEmbeddingsfromlangchain_community.vectorstoresimportMilvusimportos# 1. 初始化 Embedding 模型embeddingsDashScopeEmbeddings(modeltext-embedding-v4,dashscope_api_keyos.getenv(DASHSCOPE_API_KEY),)# 2. 把文档块写入 Milvus 向量数据库vector_storeMilvus(embedding_functionembeddings,collection_nameemployee_handbook,connection_args{uri:http://localhost:19530},index_params{index_type:HNSW,metric_type:COSINE,params:{M:16,efConstruction:128}},auto_idTrue,)# 3. 写入数据第1步切分好的 chunksidsvector_store.add_documents(chunks)print(f已写入{len(ids)}条向量)# 4. 语义检索测试resultsvector_store.similarity_search(迟到扣多少钱,k3)fordocinresults:print(doc.page_content[:100])print(f 来源{doc.metadata.get(source,未知)})向量数据库选型速查数据库类型适用场景推荐指数Milvus开源大规模企业 RAG百万级以上首选Chroma开源本地开发中小规模入门首选Pinecone商用无需运维开箱即用预算充足选pgvector扩展已有 PostgreSQL 的项目复用现有设施比喻Embedding 就是给每段文字装一个「语义 GPS 坐标」。向量数据库就是一座「坐标仓库」——用户提问时先把问题也变成坐标然后在仓库里找最近的几个坐标。深入阅读向量维度原理、余弦相似度计算、HNSW 索引参数调优等完整内容请看系列第 2 篇。第3步RAG 检索增强生成——让 AI 不再编答案这一步在链路中的角色把检索到的文档喂给大模型。前两步建好了索引现在用户提问时先从向量库检索相关文档块 → 把文档块拼进 Prompt → 让模型根据文档回答。用户公司迟到扣多少钱 │ ▼ Retriever 从向量库检索 Top 3 文档块 │ ▼ 拼接上下文 根据以下资料回答问题 [文档块1] 迟到早退按分钟扣罚... [文档块2] 超过三次需部门审批... 问题公司迟到扣多少钱 │ ▼ DeepSeek 根据上下文生成答案 │ ▼ 根据员工手册迟到按每分钟扣罚...核心代码fromlangchain.agentsimportcreate_agentfromlangchain_core.promptsimportChatPromptTemplate# 1. 从向量库获取 Retrieverretrievervector_store.as_retriever(search_kwargs{k:3})# 2. 检索相关文档docsretriever.invoke(迟到扣多少钱)# 3. 整理上下文context\n\n.join(doc.page_contentfordocindocs)# 4. 构造 PromptpromptChatPromptTemplate.from_messages([(system,根据以下资料回答问题。资料不足时说明无法确定。\n\n资料\n{context}),(human,{question}),])# 5. 调用模型fromutils.model_factoryimportget_deepseek_model modelget_deepseek_model()chainprompt|model responsechain.invoke({context:context,question:迟到扣多少钱})print(response.content)RAG vs 直接问模型对比维度直接问模型RAG知识来源模型训练数据企业文档时效性训练截止日期实时更新准确性可能编造基于文档回答来源追溯无法追溯可展示出处适用场景通用知识企业内部知识比喻直接问模型就像闭卷考试——它只能凭记忆答记不清就编。RAG 就是开卷考试——先翻到相关页码检索再照着内容答生成答案有据可查。深入阅读Retriever 高级配置、LCEL 链式组合、上下文压缩、流式输出等完整内容请看系列第 3 篇。第4步Tool 工具调用——给 AI 装上真干活的机械臂这一步在链路中的角色让 Agent 能调外部系统。RAG 解决了「知识」问题但 Agent 还需要「行动」——查订单、算价格、调 API。Tool 就是把外部能力包装成模型可调用的函数。用户帮我查一下订单 A1002 发了没 │ ▼ Agent 推理需要查订单 → 调用 get_order_status 工具 │ ▼ Tool 执行get_order_status(order_idA1002) │ ▼ 返回结果已发货快递单号 SF123456 │ ▼ Agent 组织语言回复用户核心代码fromlangchain_core.toolsimporttoolfromlangchain.agentsimportcreate_agentfromutils.model_factoryimportget_deepseek_model# 1. 定义工具tooldefget_order_status(order_id:str)-str:根据订单号查询订单状态。fake_orders{A1001:已付款等待发货,A1002:已发货快递单号 SF123456,A1003:已签收,}returnfake_orders.get(order_id,未找到该订单)tooldefcalculate_discount_price(price:float,discount:float)-float:根据原价和折扣比例计算折后价格。returnprice*discount# 2. 创建 Agentmodelget_deepseek_model()agentcreate_agent(modelmodel,tools[get_order_status,calculate_discount_price],system_prompt你是一个电商客服助手。根据用户问题选择合适的工具。,)# 3. 运行resultagent.invoke({messages:[{role:user,content:帮我查一下订单 A1002 发了没}]})print(result[messages][-1].content)# 输出订单 A1002 已发货快递单号 SF123456。Tool vs RAG什么时候用哪个对比维度RAGTool解决什么知识检索业务执行数据来源文档库实时系统/数据库/API典型场景“公司迟到扣多少”“查我的订单状态”返回内容文本片段结构化数据触发方式每次问答都检索Agent 按需调用比喻RAG 是给 Agent 配了一本「参考书」——翻到相关页码照着答。Tool 是给 Agent 配了一部「电话」——遇到答不了的问题打个电话问业务系统。深入阅读工具描述六要素、Agent 执行循环原理、多工具电商客服完整案例等请看系列第 4 篇。第5步中间件——给 Agent 穿上生产级铁甲这一步在链路中的角色生产级保障。前四步搭好了 Agent 的核心能力但上生产还缺日志、重试、限流、对话摘要、人工审批。中间件就是在 Agent 执行流程的特定阶段自动触发的钩子。Agent 执行流程 中间件拦截点 agent.invoke() │ ▼ ┌─ before_agent ──→ 初始化/权限校验 ──────────────┐ │ │ │ ┌─ before_model ──→ 输入校验/日志 ──────────┐ │ │ │ │ │ │ │ ┌─ 模型实际调用 ─────────────────────┐ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────┘ │ │ │ │ │ │ │ └─ after_model ──→ 结果分析/风险检测 ────────┘ │ │ │ └─ after_agent ──→ 收尾/日志上报/持久化 ───────────┘ │ ▼ 返回结果核心代码两个最常用的预置中间件fromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimportSummarizationMiddleware,HumanInTheLoopMiddlewarefromlanggraph.checkpoint.memoryimportInMemorySaver# 1. 对话摘要中间件防止 Token 超限summarizationSummarizationMiddleware(modelmodel,trigger(tokens,1000),# 达到 1000 token 触发摘要keep(messages,2),# 保留最近 2 条原始消息)# 2. 人工审批中间件敏感操作需人工确认hitlHumanInTheLoopMiddleware(interrupt_on{dangerous_write:{allowed_decisions:[approve,reject,edit]}})agentcreate_agent(modelmodel,tools[get_order_status,dangerous_write],middleware[summarization,hitl],checkpointerInMemorySaver(),# HITL 必须配置system_prompt你是一个企业运维助手。,)中间件分类速查类型触发时机典型场景before_model模型调用前输入校验、日志记录after_model模型响应后结果分析、风险检测wrap_model_call包裹模型调用计时、重试、限流dynamic_prompt模型调用前千人千面 PromptSummarizationMiddlewareToken 超阈值长对话自动压缩HumanInTheLoopMiddleware敏感工具调用前人工审批/编辑/拒绝比喻前四步造了一个能干活的机器人但它还是裸奔的——没有安全帽限流、没有保险绳重试、没有审批流程HITL。中间件就是给机器人穿上铁甲让它能安全上岗。深入阅读MCP 协议、异步编程、装饰器/类中间件完整实战、洋葱模型等请看系列第 5 篇。全链路总结五步如何串成一条完整流水线把五步放在一起就是一个完整的生产级 AI Agent 系统┌──────────────────────────────────────────────────────────────────────┐ │ 生产级 AI Agent 全链路 │ │ │ │ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌────────┐ ┌──────┐ │ │ │ 文档切分 │───→│ Embedding│───→│ RAG检索 │───→│ Tool │───→│中间件│ │ │ │ │ │ 向量库 │ │ 上下文 │ │ Agent│ │ │ │ │ └─────────┘ └──────────┘ └─────────┘ └────────┘ └──────┘ │ │ │ │ │ │ │ │ │ 原料预处理 语义索引 知识检索 行动执行 生产保障 │ │ │ │ │ │ │ │ │ Recursive Milvus Retriever create_agent 中间件 │ │ TextSplitter Chroma LCEL tool 链 │ │ PyPDFLoader HNSW Prompt before│ │ 索引 after │ │ wrap │ └──────────────────────────────────────────────────────────────────────┘一句话串起五步切分文档成小块第1步→ 把小块变成向量存入数据库第2步→ 用户提问时检索相关文档喂给模型第3步→ 模型根据问题选择工具执行业务操作第4步→ 全程有中间件做日志、重试、摘要、人工审批保障第5步。各步核心 API 速查表步骤核心 API作用第1步TextLoader/PyPDFLoader加载 TXT/MD/PDF第1步RecursiveCharacterTextSplitter递归切分文档第1步split_documents()切分并保留元数据第2步DashScopeEmbeddings中文 Embedding 模型第2步embed_documents()/embed_query()文档/查询向量化第2步Milvus/Chroma向量数据库第2步similarity_search()语义检索第3步as_retriever()VectorStore → Retriever第3步ChatPromptTemplate构造上下文 Prompt第3步prompt | modelLCEL 链式调用第4步tool定义工具第4步create_agent()创建 Agent第4步agent.invoke()运行 Agent第5步SummarizationMiddleware对话摘要第5步HumanInTheLoopMiddleware人工审批第5步before_model/after_model装饰器钩子第5步wrap_model_call包裹式钩子第5步AgentMiddleware类中间件基类第5步MultiServerMCPClientMCP 多服务端连接