
1. 本章目标前面几章中模型主要做两类事情根据 Prompt 生成内容根据知识库资料回答问题但真实业务中模型经常需要调用外部能力。例如查询订单状态查询商品库存计算优惠价格查询用户等级调用知识库检索这些外部能力在 LangChain 中通常会封装成 Tool。学完本章后你应该能够理解 Tool 的作用使用tool定义工具编写工具描述和参数类型手动调用工具理解模型工具调用的基本流程使用create_agent创建简单 Agent让 Agent 根据问题选择工具完成电商客服 Agent 案例理解 Agent、Tool、Harness 的关系2. 为什么需要 Tool function call大模型本身擅长语言理解和生成但它并不知道实时业务数据(就是数据库数据)。例如用户问我的订单 A1001 发货了吗模型不能凭空知道订单状态。它需要调用一个业务系统接口查询订单状态工具再根据查询结果回复用户。流程可以理解为用户问题 - 模型判断需要查订单 - 调用订单查询工具 - 得到订单数据 - 模型组织自然语言回复Tool 就是把外部能力包装成模型可以调用的函数。3. Tool 和普通函数的区别普通 Python 函数def get_order_status(order_id: str) - str: return 订单已发货LangChain Toolfrom langchain.tools import tool tool def get_order_status(order_id: str) - str: 根据订单号查询订单状态。 return 订单已发货Tool 仍然是 Python 函数但多了几个关键信息信息作用工具名称模型识别可以调用哪个工具参数类型模型知道应该传什么参数工具描述模型判断什么时候使用这个工具返回值工具调用后返回给模型的数据LangChain 官方也强调工具描述会帮助模型理解什么时候使用该工具。4. 定义第一个 Tool创建01_tool_basic.pyfrom langchain.tools import tool tool def get_order_status(order_id: str) - str: 根据订单号查询订单状态。 fake_orders { A1001: 已付款等待发货, A1002: 已发货快递单号 SF123456, A1003: 已签收, } return fake_orders.get(order_id, 没有查询到该订单) print(get_order_status.name) print(get_order_status.description) print(get_order_status.args)运行python 01_tool_basic.py输出中可以看到工具名称工具描述参数结构这些信息会提供给模型帮助模型决定如何调用工具。5. 手动调用 ToolTool 可以像普通组件一样手动调用。继续修改01_tool_basic.pyresult get_order_status.invoke( { order_id: A1002, } ) print(result)完整代码from langchain_core.tools import tool tool def get_order_status(order_id): 该工具通过订单id获取订单状态 order_status{ A1001: 已付款等待发货, A1002: 已发货快递单号 SF123456, A1003: 已签收 } return order_status.get(order_id) # 假如工具不指定name,那么默认name函数的名字 print(get_order_status.name) print(get_order_status.description) print(get_order_status.args) resultget_order_status.invoke({ order_id:A1002 }) print(result) #已发货快递单号 SF123456 print(type(result).__name__) # str运行python 01_tool_basic.py输出已发货快递单号 SF123456手动调用 Tool 的意义是先确认工具本身没有问题再交给 Agent 使用。6. Tool 描述要写清楚工具描述非常重要因为模型会根据描述判断是否调用工具。不推荐tool def query(order_id: str) - str: 查询。 ...推荐tool def get_order_status(order_id: str) - str: 根据订单号查询订单状态包括是否付款、是否发货、快递单号和签收状态。 ...好的工具描述应该说明工具能做什么什么时候应该使用参数是什么含义返回什么结果工具名称也建议使用英文小写加下划线get_order_status calculate_discount search_product_inventory这种命名兼容性更好也更容易阅读。7. 多参数 Tool业务工具通常会有多个参数。创建02_discount_tool.pyfrom langchain_core.tools import tool tool def calculate_discount_price(ori_price,discount_rate): 根据原价和折扣率计算折扣以后的价格 return round(ori_price*discount_rate,2) resultcalculate_discount_price.invoke({ ori_price:100, discount_rate:0.8 }) print(result)运行python 02_discount_tool.py输出折后价格为 239.20 元参数类型要写清楚original_price: float discount_rate: float模型会根据类型和描述生成工具调用参数。8. 使用 Pydantic 描述工具参数当参数较多或者需要更清楚的字段说明时可以使用 Pydantic。创建03_tool_schema.pyfrom pydantic import BaseModel, Field from langchain.tools import tool class InventoryInput(BaseModel): product_id: str Field(description商品编号例如 P1001) warehouse: str Field(description仓库名称例如 上海仓、北京仓) tool(args_schemaInventoryInput) def get_inventory(product_id: str, warehouse: str) - str: 查询指定商品在指定仓库中的库存数量。 fake_inventory { (P1001, 上海仓): 35, (P1001, 北京仓): 12, (P2001, 上海仓): 0, } count fake_inventory.get((product_id, warehouse)) if count is None: return 没有查询到该商品的库存信息 return f{product_id} 在 {warehouse} 当前库存为 {count} 件 print(get_inventory.args) result get_inventory.invoke( { product_id: P1001, warehouse: 上海仓, } ) print(result)运行python 03_tool_schema.pyField(description...)可以让模型更清楚地理解每个字段的含义。这在企业项目中很实用尤其是参数名称比较抽象时。思考get_inventory 中不使用InventoryInput不是也可以获取到答案吗为何要使用这个 PyDantic 呢答案是可以做限制比如 pydantic 类可以稍加修改class InventoryInput(BaseModel): product_id: str Field(description商品编号例如 P1001,pattern^P[0-9]{4}$) warehouse: str Field(description仓库名称例如 上海仓、北京仓) 添加了一个正则表达式的判断此时如果你传递的商品标号不符合要求就会报错比如result get_inventory.invoke( { product_id: AAA1001, warehouse: 上海仓, } )直接报错pydantic_core._pydantic_core.ValidationError: 1 validation error for InventoryInput product_id String should match pattern ^P[0-9]{4}$ [typestring_pattern_mismatch, input_valueAAA1001, input_typestr] For further information visit https://errors.pydantic.dev/2.13/v/string_pattern_mismatchfrom langchain_core.tools import tool from pydantic import BaseModel, Field class InventoryInput(BaseModel): product_id: str Field(description商品编号例如 P1001,pattern^P[0-9]{4}$) cangku: str Field(description仓库名称例如 上海仓、北京仓) tool(args_schemaInventoryInput) def get_product_kc(product_id,cangku): 查询指定商品在指定仓库中的库存数量。 fake_inventory { (P1001, 上海仓): 35, (P1001, 北京仓): 12, (P2001, 上海仓): 0, } kcfake_inventory.get((product_id,cangku)) return kc resultget_product_kc.invoke({ product_id:P1001, cangku:上海仓, }) print(result)9. 什么是 Agent一、通俗定义Agent智能体 是具备自主思考、规划、调用工具、完成完整目标的人工智能程序。 区别于普通大模型只能回答单次提问Agent 能自己拆解任务、分步执行、纠错、调用外部工具不用人类一步一步指挥。简单一句话会自己干活、自主完成复杂任务的 AI 智能助手。二、三大核心特征和普通 Chat 区分开感知观察 获取外部信息用户需求、网页数据、文件、数据库、实时天气、API 返回结果等。思考规划推理 拆解大目标成小步骤判断下一步该做什么遇到失败自动调整方案。 例用户要 “写一份郑州旅游 3 天攻略”Agent 会自己拆分查景点→查票价→查交通→整理行程。行动执行工具调用 主动调用外部能力搜索引擎、计算器、代码解释器、表格、数据库、软件接口、机器人硬件等。Agent 可以理解为会根据任务自动选择工具的大模型应用普通 Chain 的流程通常比较固定Prompt - Model - ParserAgent 的流程更灵活用户问题 - 模型思考是否需要工具 - 选择工具 - 调用工具 - 阅读工具结果 - 继续调用工具或生成最终答案例如用户问帮我查一下订单 A1002 是否发货Agent 可能会选择get_order_status(order_idA1002)工具返回已发货快递单号 SF123456Agent 再回复订单 A1002 已发货快递单号是 SF123456。10. Agent、Tool、Harness 的关系LangChain 当前文档中有一个很重要的说法Agent Model[tool] HarnessHarness 马具 可以理解为 驾驭、管控、利用可以用课程里的方式理解概念作用Model负责理解问题、决定下一步、生成回答Tool负责执行具体外部能力Harness负责把模型、工具、提示词和运行流程组织起来Agent在 Harness 中运行的模型工具调用循环create_agent就是 LangChain 提供的一个 Agent Harness。它会负责把工具信息交给模型接收模型发起的工具调用执行工具把工具结果交回模型循环直到模型给出最终答案这一章先使用create_agent快速构建 Agent。Agent Model大模型 Harness调度执行框架Model 只会动脑的谋士谋士很聪明擅长分析、思考、下达指令但是手不能动、跑不出去、不能自己查资料也没法自动循环干活。谋士只能被动接收信息输出想法没人跑腿他什么实事都做不了。Harness 跑腿管家管家负责整套流程调度接收谋士的指令、外出办事、把结果带回给谋士循环往复。-------------------------------------------一句话总结隐喻Model 是大脑只会思考决策Harness 是整套执行闭环负责传话、办事、循环、重复提问大脑直到任务完成。补充区分避免混淆很多人把 Tool工具和 Harness 搞混Tool 「查库存这个动作」办事的方法Harness 「管家」负责接收谋士指令调用工具并且循环调度11. 安装依赖如果前面章节已经安装过 LangChain 和 DeepSeek 相关依赖只需要确认有pip install langchain langchain-openai python-dotenv国内镜像pip install langchain langchain-openai python-dotenv -i https://pypi.tuna.tsinghua.edu.cn/simple本章使用 DeepSeek 的 OpenAI 兼容接口。.envDEEPSEEK_API_KEY你的DeepSeek API Key DEEPSEEK_BASE_URLhttps://api.deepseek.com12. 创建第一个 Agent创建04_first_agent.pyimport os from dotenv import load_dotenv from langchain.agents import create_agent from langchain.chat_models import init_chat_model from langchain_core.tools import tool from openai import base_url, api_key from utils.model_factory import get_deepSeek_model load_dotenv() tool def get_order_status(order_id): 该工具通过订单id获取订单状态 order_status{ A1001: 已付款等待发货, A1002: 已发货快递单号 SF123456, A1003: 已签收 } return order_status.get(order_id) modelinit_chat_model( modeldeepseek-v4-flash, model_provideropenai, base_urlos.getenv(DEEPSEEK_BASE_URL), api_keyos.getenv(DEEPSEEK_API_KEY), temperature0.7 ) agentcreate_agent( modelmodel, tools[get_order_status], system_prompt你是一名电商客服助手。回答要礼貌、简洁不要编造工具返回值中不存在的信息。 ) responseagent.invoke({ messages:[ { role: user, content: 帮我查一下订单 A1002 发货了吗, } ] }) for message in response[messages]: print(type(message).__name__) print(message.content) print(**30) #print(response[messages][-1].content)运行python 04_first_agent.py输出类似订单 A1002 已发货快递单号是 SF123456。这段代码中最重要的是agent create_agent( modelmodel, tools[get_order_status], system_prompt..., )tools参数传入工具列表Agent 就能在需要时调用这些工具。13. 查看 Agent 的消息结果agent.invoke()返回的不是普通字符串而是一个包含消息状态的结果。可以打印所有消息for message in result[messages]: print(type(message).__name__) print(message) print(- * 60)你会看到大致流程HumanMessage用户问题 AIMessage模型请求调用工具 ToolMessage工具返回结果 AIMessage模型最终回答这就是 Agent 的核心循环模型 - 工具 - 模型打印一次完整消息这样能更直观看到 Agent 不是“一次模型调用”。14. 多工具 Agent一个 Agent 可以拥有多个工具。创建05_multi_tools_agent.pyfrom langchain.agents import create_agent from langchain.chat_models import init_chat_model from langchain_core.tools import tool from utils.model_factory import get_deepSeek_model tool def get_order_status(order_id): 该工具通过订单id获取订单状态 order_status{ A1001: 已付款等待发货, A1002: 已发货快递单号 SF123456, A1003: 已签收 } return order_status.get(order_id) tool def calculate_discount_price(ori_price,discount_rate): 根据原价和折扣率计算折扣以后的价格 return round(ori_price*discount_rate,2) tool def get_product_kc(product_id,cangku): 查询指定商品在指定仓库中的库存数量。 fake_inventory { (P1001, 上海仓): 35, (P1001, 北京仓): 12, (P2001, 上海仓): 0, } kcfake_inventory.get((product_id,cangku)) return kc modelget_deepSeek_model() agentcreate_agent( modelmodel, tools[ get_product_kc, calculate_discount_price, get_order_status ], system_prompt你是一个电商助手可以通过工具查看库存计算折扣价格以及查看订单状态 ) questions[ 订单 A1001 现在是什么状态, 商品 P2001 还有库存吗, 299 元的商品打八折后是多少钱, ] for question in questions: resultagent.invoke({ messages:[ { role: user, content: question, } ] }) print(问题是:question) print(AI输出的结果是:result[messages][-1].content) print(~*40)运行python 05_multi_tools_agent.pyAgent 会根据问题自动选择工具用户问题可能调用的工具订单 A1001 什么状态get_order_status商品 P2001 有没有库存get_inventory299 元打八折calculate_discount_price这就是 Agent 相比固定 Chain 更灵活的地方。15. 企业案例电商客服 Agent15.1 案例需求现在要实现一个电商客服 Agent。用户可能咨询订单状态商品库存退款规则优惠价格Agent 需要根据问题自动选择合适工具并生成客服回复。15.2 项目结构chapter09/ ├── tools.py ├── customer_agent.py ├── main.py15.3 编写业务工具创建tools.pyfrom pydantic import BaseModel, Field from langchain.tools import tool ORDERS { A1001: { status: 已付款等待发货, shipping_no: None, product_id: P1001, }, A1002: { status: 已发货, shipping_no: SF123456, product_id: P2001, }, A1003: { status: 已签收, shipping_no: YT998877, product_id: P3001, }, } INVENTORY { P1001: { name: 无线静音鼠标, stock: 35, price: 129, }, P2001: { name: 蓝牙机械键盘, stock: 0, price: 299, }, P3001: { name: Type-C 扩展坞, stock: 8, price: 199, }, } class DiscountInput(BaseModel): original_price: float Field(description商品原价单位为元) discount_rate: float Field(description折扣率例如 0.8 表示八折) tool def get_order_status(order_id: str) - str: 根据订单号查询订单状态、物流单号和商品编号。 order ORDERS.get(order_id) if not order: return 没有查询到该订单 shipping_no order[shipping_no] or 暂无快递单号 return ( f订单 {order_id} 状态{order[status]} f快递单号{shipping_no} f商品编号{order[product_id]} ) tool def get_product_inventory(product_id: str) - str: 根据商品编号查询商品名称、库存数量和原价。 product INVENTORY.get(product_id) if not product: return 没有查询到该商品 return ( f商品 {product_id}{product[name]} f库存{product[stock]} 件 f原价{product[price]} 元 ) tool(args_schemaDiscountInput) def calculate_discount_price( original_price: float, discount_rate: float, ) - str: 根据商品原价和折扣率计算折后价格。 if original_price 0: return 原价必须大于 0 if discount_rate 0 or discount_rate 1: return 折扣率必须在 0 到 1 之间 final_price original_price * discount_rate return f折后价格为 {final_price:.2f} 元 tool def get_refund_policy(order_status: str) - str: 根据订单状态查询退款规则。order_status 可以是未发货、已发货、已签收。 if 未发货 in order_status or 等待发货 in order_status: return 订单未发货时用户可以直接申请退款。 if 已发货 in order_status: return 订单已发货时需要等待商品送达后申请退货退款。 if 已签收 in order_status: return 订单签收后如商品存在质量问题可以在 7 天内申请售后。 return 没有匹配到明确的退款规则这里先用内存字典模拟数据库。真实项目中工具函数内部可以查询 MySQL、Redis、HTTP 接口或企业系统。15.4 创建 Agent创建customer_agent.pyfrom langchain.agents import create_agent from model_factory import get_chat_model from tools import ( calculate_discount_price, get_order_status, get_product_inventory, get_refund_policy, ) SYSTEM_PROMPT 你是一名电商客服助手。 工作要求 1. 根据用户问题选择合适工具。 2. 不要编造订单、库存、价格和退款规则。 3. 如果工具没有查到数据要如实告诉用户。 4. 回答要礼貌、简洁、清楚。 5. 涉及订单状态、库存、价格时优先调用工具确认。 def create_customer_agent(): return create_agent( modelget_chat_model(), tools[ get_order_status, get_product_inventory, calculate_discount_price, get_refund_policy, ], system_promptSYSTEM_PROMPT, )15.5 编写终端程序创建main.pyfrom customer_agent import create_customer_agent def main() - None: agent create_customer_agent() print(电商客服 Agent 已启动输入 exit 退出。) while True: question input(\n用户).strip() if question.lower() exit: print(程序已退出。) break if not question: print(问题不能为空。) continue result agent.invoke( { messages: [ { role: user, content: question, } ] } ) print(f客服{result[messages][-1].content}) if __name__ __main__: main()15.6 运行项目运行python main.py测试问题帮我查一下订单 A1002 发货了吗可能输出订单 A1002 已发货快递单号是 SF123456。继续测试商品 P2001 还有库存吗可能输出商品 P2001 是蓝牙机械键盘目前库存为 0 件暂时无货。再测试299 元的键盘打八折多少钱可能输出299 元打八折后的价格是 239.20 元。测试组合问题订单 A1002 已经发货了我还能退款吗 你也可以这么问 订单 A1002 我还能退款吗 此时你会发现这个问题触发了三次AI回答Agent 可能先调用订单查询工具再调用退款规则工具然后回答订单 A1002 当前已发货。根据退款规则已发货订单需要等待商品送达后申请退货退款。16. 查看工具调用过程为了看清 Agent 是否真的调用工具可以打印消息类型。修改main.py中的调用部分result agent.invoke( { messages: [ { role: user, content: question, } ] } ) for message in result[messages]: print(type(message).__name__) print(message) print(- * 60) print(f客服{result[messages][-1].content})你会看到用户消息模型发起的工具调用工具返回消息模型最终回复如果 Agent 没有调用预期工具优先检查工具名称是否清楚工具描述是否明确参数类型是否正确system prompt 是否要求必要时调用工具用户问题是否提供了足够参数17. Agent 适合什么场景Agent 适合任务步骤不完全固定的场景。例如场景说明客服助手用户问题类型多可能需要查订单、库存、售后规则数据分析助手可能需要查数据库、算指标、生成解释运维助手可能需要查日志、查服务状态、执行诊断命令办公助手可能需要查日程、发邮件、整理文档如果流程非常固定普通 Chain 更简单。例如输入评论 - 情感分类 - 输出 JSON这种任务没有必要做成 Agent。18. Tool 设计原则18.1 工具职责要单一推荐get_order_status get_product_inventory calculate_discount_price不推荐handle_all_customer_questions工具越大模型越难判断什么时候调用也越难测试。18.2 返回结果要清楚工具返回给模型看的内容要明确订单 A1002 状态已发货快递单号SF123456商品编号P2001不要返回含义不明的内容ok18.3 工具内部要做参数校验例如折扣率不能大于 1if discount_rate 0 or discount_rate 1: return 折扣率必须在 0 到 1 之间不要完全相信模型一定会传入正确参数。18.4 高风险操作要谨慎查询类工具风险较低查询订单 查询库存 查询物流写入类工具风险更高取消订单 修改价格 退款 发送邮件 删除文件高风险操作应该加入人工确认、权限校验和操作日志。19. 常见问题19.1 Tool 的 docstring 重要吗非常重要。模型会根据工具描述判断什么时候使用工具。描述太模糊时模型可能不用工具或者用错工具。19.2 为什么 Agent 没有调用工具常见原因用户问题不需要工具工具描述不清楚参数缺失模型判断直接回答更合适system prompt 没有明确要求关键数据要通过工具确认可以通过打印消息列表查看模型是否产生了工具调用。19.3 为什么 Agent 调错工具优先检查工具描述是否有重叠。例如两个工具都写成查询信息模型就很难区分。应该改成根据订单号查询订单状态 根据商品编号查询商品库存19.4 Tool 可以连接数据库吗可以。Tool 本质上是 Python 函数。函数内部可以查询数据库调用 HTTP 接口读取文件调用 RAG 检索执行业务逻辑19.5 Agent 和 LangGraph 是什么关系LangChain 的create_agent底层使用 LangGraph 的运行时。可以先把create_agent理解为一个已经封装好的 Agent 写法。后面单独学习 LangGraph 时会自己定义节点、边、状态和条件跳转。20. 本章重点本章需要重点掌握Tool 是模型可以调用的外部能力tool可以把 Python 函数包装成工具工具名称、参数类型、docstring 都会影响模型选择Tool 可以手动invoke()测试Agent 会根据任务决定是否调用工具create_agent可以快速创建 AgentAgent 的消息中可以看到工具调用过程多工具 Agent 适合客服、订单、库存、价格等业务场景Tool 内部应该做参数校验和异常处理高风险写操作需要人工确认和权限控制Agent 的核心流程用户问题 - 模型判断 - 调用工具 - 工具返回结果 - 模型生成最终回答