第五章 工具

发布时间:2026/11/6 1:54:28
第五章 工具 工具的基本用法比较简单高级用法比较复杂。本章先介绍基本用法下一张再介绍高级用法。一、基本用法1.1 tool 装饰器基本用法from langchain.tools import tool tool def get_weather(city: str) - str: 获取指定城市的当前天气。 return f{city}晴天25°C模型看到的不是代码而是这样一份“说明书”有一个叫 get_weather 的工具描述是“获取指定城市的当前天气”需要传入一个叫 city 的字符串参数。1.2 复杂参数如果参数比较复杂比如需要限制“查询条数必须在 1 到 20 之间”可以用 Pydantic 的 args_schema 来写一份更严格的说明书from pydantic import BaseModel, Field class SearchInput(BaseModel): 搜索参数。 query: str Field(description搜索关键词) max_results: int Field(default5, ge1, le20, description返回结果数量1-20) tool(args_schemaSearchInput) def search(query: str, max_results: int 5) - str: 在网络上搜索信息。 return f找到 {max_results} 条关于 {query} 的结果模型看到 max_results 的描述和 ge1, le20 的约束就会知道“这里最多只能传 20”。如果模型传了 50LangChain 会在工具执行之前就拦截并报错而不是等你收到一个非法的查询请求才发现问题。二 返回值类型工具的返回有四种类型类型说明str工具本身返回的是字符串LangChain 会把它包装成 ToolMessage内容直接呈现给模型阅读dict字典同上command普通工具是只读的它接收参数返回结果但通过返回command永久地改变 Agent 的运行环境。return_directTrue正常是工具把结果返回给模型模型再重新组织语言如果工具返回return_directTrue代理在执行完这个工具后**立即停止**把工具的输出直接返回给调用者跳过后续任何额外的模型调用2.1 知识补充2.1.1 ToolMessageHumanMessage、AIMessage、ToolMessage分别代表用户说的、模型说的、工具说的。它们都继承自BaseMessage这个基础抽象类。因此它们都拥有如下核心的成员属性类型说明contentstr或list消息的核心内容。简单文本场景下是字符串复杂场景如图片、工具调用块下是结构化的内容块列表。typestr消息的角色标识。这是一个抽象属性由子类具体实现例如ToolMessage的type固定为tool。idstr消息的唯一标识符用于追踪和日志namestr可选可选的参与者名称。主要用于多智能体系统帮助区分是哪个智能体发出的消息。additional_kwargsdict用于存放提供商特定的附加参数保持原始格式。response_metadatadict提供商返回的原始元数据如响应头、Token 用量等此外它还提供了一些便捷属性例如.text用于自动提取并拼接所有文本内容块ToolMessage是一个具体的消息类专门用于承载工具执行完毕后返回给模型的结果工具执行完之后返回的是str或dictLangChain把它转成ToolMessage传给模型以闭合模型的工具调用请求。它除了拥有上述成员外还新增了以下关键成员属性类型是否必填说明tool_call_idstryes最核心的字段。它是一个唯一标识符用于精确匹配这条工具结果响应的是模型发出的哪一次工具调用请求。模型可以并行请求多个工具这个 ID 是区分它们的唯一依据。statussuccess或errorno表示工具调用是成功还是失败默认是successartifactAny任意类型no用于存放不希望直接发送给模型的额外产物。例如工具返回了一张大图的 Base64 数据你可以把摘要文本放在content里给模型看而完整的图片数据放在artifact里供你自己的应用使用。2.1.2 AgentStateAgentState是一个TypedDict对象就是个字典。它只有一个成员字段类型描述messagelist[BaseMessage]当前线程的完整对话历史。仅追加新消息只被添加从不被替换作用可以把它看成智能体在执行任务时随身携带的“工作记忆”或“共享状态板”。模型、工具、中间件等所有组件都通过读写这块状态板上的信息来协同工作。在实际使用时可以通过子类化AgentState来添加任何业务所需的字段这本质上是扩展了智能体的“工作记忆”。from langchain.agents import AgentState, create_agent class MyState(AgentState): user_id: str preferred_language: str call_count: int在创建 Agent 时启用它state_schema参数agent create_agent( modelopenai:gpt-5.5, tools[], state_schemaMyState, # 关键告诉 Agent 使用你扩展后的状态 )在工具返回的command中就可以修改AgentState的这些自定义字段然后中间件就可以读取根据字段的内容采取不同的行动。记忆AgentState只是本次运行的“工作记忆”在下一轮对话时就会清空。# 不传 checkpointer agent create_agent(model..., tools[...]) # 第一次调用 agent.invoke({messages: [{role: user, content: 我叫小明}]}) # Agent 只能看到“我叫小明”这一条消息它不知道之前发生过什么 # 第二次调用 agent.invoke({messages: [{role: user, content: 我叫什么}]}) # Agent 看到的是一个全新的状态只有“我叫什么”这一条消息 # 它无法回答“你叫小明”因为它根本不记得第一次调用如果要保存记忆需要checkpointer。from langgraph.checkpoint.memory import InMemorySaver agent create_agent( ..., checkpointerInMemorySaver(), # 启用持久化 ) config {configurable: {thread_id: user-123-conv-1}} agent.invoke({messages: 我叫小明}, config) # 下一次调用Agent 依然记得“小明” result agent.invoke({messages: 我叫什么}, config) # result 中会包含 “你叫小明”2.2 commandCommand作为工具返回值让工具从“只能回答结果”升级为“能改写状态、能指定下一步去哪”。它把工具的权限从单纯的信息提供者扩展成了流程干预者。Command来自langgraph.types它的完整构造签名包含四个参数参数类型作用说明updatedict修改图的状态AgentState改写状态板上的字段gotostr/Send/ 序列强制指定下一个执行的节跳过默认路由直接“空降”到目标节点graphstr/Command.PARENT指定指令发送到哪个图当前图、父图、或兄弟子图resumeAny为中断的流程提供恢复值配合interrupt()让暂停的流程继续跑其中update、goto、graph是设计为从工具或节点函数返回的resume是唯一一种作为invoke()输入使用的模式用来恢复被人机协同暂停的流程。2.2.1 update它是工具改写AgentState的唯一标准方式。from langchain.agents import AgentState class SupportState(AgentState): current_step: str triage issue_type: str tool def record_issue(issue: str, runtime: ToolRuntime[None, SupportState]) - Command: 记录问题类型并切换到解决阶段。 return Command( update{ issue_type: issue, current_step: resolve, messages: [ ToolMessage( contentf问题类型已记录{issue}, tool_call_idruntime.tool_call_id, ) ], } )update里的字段必须是你在AgentState子类中显式定义过的自定义字段。代码中的 current_step、issue_type 在自定义之前是不存在的。要区别AgentStat子类中的成员的不同行为1、current_step、issue_type是简单的字符串所以update的行为就是“覆盖”2、SupportState继承了AgentStat的message它内置绑定了Reducer比如用operator.add做累加所以会按 reducer 的规则合并。update里必须包含ToolMessage。因为模型调用工具时期待一个回应。带有匹配tool_call_id的ToolMessage完成了这次“请求-响应”循环没有它对话历史就会格式错误模型发出的工具调用请求会处于“悬空”状态。关于 其它的参数goto、graph、resume以后用到时再介绍。2.3 return_directTrue正常情况下Agent 的循环是模型决定调用工具 → 工具执行 → 结果返回给模型 → 模型基于结果决定下一步return_directTrue的作用是让工具的输出直接成为最终答案结束智能体。此时无需模型组织语言工具的输出就是Agent的输出。1、最简单示例单独使用不作为command的返回值tool(return_directTrue) def fetch_order_status(order_id: str) - str: 获取客户订单的当前状态。 return f订单 {order_id} 已发货预计两天后送达。2、结合Command但必须附带匹配的ToolMessage否则对话历史会变得不合法。tool(return_directTrue) def fetch_and_store_order(order_id: str, runtime: ToolRuntime) - Command: status f订单 {order_id} 已发货。 return Command( update{ last_order_status: status, # 写入状态 messages: [ ToolMessage(contentstatus, tool_call_idruntime.tool_call_id) # 必须附带 ], } )问题既然 return_directTrue 结束了流程再用update更新状态有什么意义如果这次invoke结束后状态既不会被保存、也不会被任何后续逻辑读取那么update确实没有实际意义。但return_directTrue只是结束了这一次 Agent 循环它并不等于“状态从此无人问津”。update有没有意义取决于这一次循环结束后还有没有别的东西会去读这块状态。例1配置了 checkpointer且同一个 thread 后续还会被调用agent create_agent( model..., tools[fetch_and_store_order], checkpointerInMemorySaver(), # 关键状态被保存 ) config {configurable: {thread_id: order-conv-1}} # 第一次调用return_direct 结束但 last_order_status 被写入检查点 agent.invoke({messages: 帮我查一下订单 A17}, config) # 第二次调用同一个 thread状态从检查点恢复 result agent.invoke({messages: 刚才查的订单状态是什么}, config)例2这个Agent是个子图父图能够获取到update的信息继续操作例3agent.stream(stream_modeupdates)观察运行过程update的内容会作为状态更新事件被流出来这属于可观测性用途而不是功能性用途。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询