LangChain 系列之Tools:让大模型真正连接业务系统
前面几章我们把 RAG 的底层链路讲完了文档进来切分向量化入库检索重排最后把上下文交给模型。但这还不够。RAG 让模型会“查资料”。Tools 让模型能“办事情”。没有 Tools大模型只是一个会聊天的脑子。它能分析能总结能解释。但它不能查订单不能改数据库不能调行情接口也不能创建工单。有了 Tools模型才真正接上业务系统。一、Tool 到底是什么Tool翻译过来是“工具”。但在 LangChain 里它不是普通工具类。它是模型和真实业务系统之间的一层安全接口。模型不能直接碰数据库。模型也不应该直接执行转账、退款、删除、下单这类动作。正确做法是你把确定性的业务能力封装成 Tool模型只负责判断“该不该调用、调用哪个、传什么参数”。真正执行动作的是你写的代码。所以Tool 的本质可以压成一句话Tool 有名字、有说明、有参数结构、有执行函数的业务能力。二、为什么 Agent 离不开 ToolsAgent 不是“更会聊天的模型”。Agent 的核心是模型可以反复决定下一步。官方文档里对 Agent 的描述很直接Agent 是模型在循环中调用工具直到任务完成。它的外壳包括模型、Prompt、Tools 和 Middleware。这句话非常关键。因为没有 ToolsAgent 的循环只能在文本里打转有了 Tools它才能从文本世界走进业务世界。三、一个 Tool 长什么样从外面看一个 Tool 很简单。from langchain.tools import tooltooldef query_order_status(order_id: str) - str:查询订单状态。return order_service.query(order_id)但从源码看这个函数会被包装成一个 Tool 对象。它最重要的不是函数体而是这几件事name 告诉模型“我是谁”。description 告诉模型“什么时候该用我”。args_schema 告诉模型“参数怎么传”。invoke/run 是执行入口。return_direct 决定工具结果是否直接返回用户。这几个字段决定了 Tool 能不能被模型正确选择、正确调用、正确解释。四、Tool 的源码入口BaseTool源码里Tool 的根基是 BaseTool。BaseTool 继承 RunnableSerializable。也就是说Tool 不是孤立对象它也是 LangChain Runnable 体系的一员。这就是为什么 Tool 可以被 invoke可以被回调追踪可以被放进 Agent也可以被 LangGraph 的 ToolNode 执行。class BaseTool(RunnableSerializable[str | dict[str, Any] | ToolCall, Any]):name: strdescription: strargs_schema: ArgsSchema | None Nonereturn_direct: bool Falsedef invoke(self, input, configNone, **kwargs):tool_input, kwargs _prep_run_args(input, config, **kwargs)return self.run(tool_input, **kwargs)这段源码链路说明了三个事实。第一Tool 可以接收字符串、字典也可以接收模型生成的 ToolCall。第二invoke 不直接执行你的函数它先调用 _prep_run_args把输入、配置、回调等信息整理好。第三真正执行会进入 run再进入 _parse_input、_to_args_and_kwargs、_run。五、tool 装饰器做了什么tool 看起来只是一个装饰器。但它背后做了很多事。它会读取函数名作为默认工具名。它会读取 docstring作为工具描述。它会读取函数签名和类型注解推断参数结构。如果传了 args_schema它会使用你显式定义的参数模型。也就是说tool 的工作就是把一个普通 Python 函数变成模型能看懂、Agent 能调用、系统能校验的 Tool。tool(web_search)def search(query: str) - str:Search the web for information.return search_api(query)函数名可以改。描述可以改。参数结构可以自定义。但生产环境里最重要的是 description 和 args_schema。description 写得模糊模型就会乱选工具。args_schema 写得松模型就会乱传参数。六、Tool 和 StructuredTool 的区别早期很多人会把 Tool 理解成“一个字符串输入一个字符串输出”。这个理解太窄了。真实业务里一个接口往往有多个参数订单号、用户 ID、时间范围、分页参数、过滤条件。这时就需要 StructuredTool。普通 Tool 更适合简单函数。StructuredTool 更适合多参数业务接口。BaseTool 子类适合企业级深度封装。HeadlessTool 适合 schema 在服务端注册、执行在外部系统的场景。Retriever Tool 则把知识库检索包装成 Agent 可以调用的工具。七、Tool 的参数为什么必须严格模型传参不是天然可靠。它可能把 order_id 传成“帮我查一下 123456”。也可能漏掉必填字段。还可能把日期范围写错。所以 Tool 的参数必须结构化。from pydantic import BaseModel, Fieldclass OrderInput(BaseModel):order_id: str Field(description订单号)include_logistics: bool Field(defaultTrue, description是否返回物流信息)tool(args_schemaOrderInput)def query_order_status(order_id: str, include_logistics: bool True) - dict:查询订单状态和物流信息。return order_api.query(order_id, include_logistics)这不是为了写得漂亮。这是为了让模型知道参数含义也是为了让系统能在执行前做校验。源码里BaseTool._parse_input 会根据 args_schema 解析和校验输入。校验失败不应该继续执行工具。企业项目里这一步非常关键。因为 Tool 一旦执行后面连的可能就是订单库、资金系统、客服系统、股票行情系统。八、Tool 的返回值怎么选Tool 的返回值不是随便 return。返回什么决定模型后面怎么处理。如果结果要继续让模型总结就返回字符串或对象。如果结果已经是最终答案就用 return_directTrue。如果工具需要修改 Agent 状态例如保存用户偏好、更新上下文、写长期记忆可以返回 Command。官方文档也明确区分了几类返回值字符串、对象、Command以及 return_direct。九、ToolRuntime工具如何拿到状态、上下文和存储早期写工具很多人只会传业务参数。但生产环境里工具经常还需要知道当前用户是谁、当前会话是什么、线程 ID 是什么、有没有上下文、有没有长期记忆。这就需要 ToolRuntime。from langchain.tools import tool, ToolRuntimetooldef get_account_info(runtime: ToolRuntime) - str:获取当前用户的账户信息。user_id runtime.context.user_idreturn account_service.get(user_id)ToolRuntime 可以访问 state、context、store、stream_writer、execution_info 等运行时信息。更重要的是runtime 这类参数是注入参数通常不会暴露给模型。这就把“模型可见的参数”和“系统内部运行参数”分开了。这点对权限控制非常重要。用户 ID、租户 ID、权限范围不应该让模型自己猜也不应该让用户从 Prompt 里传。十、Tool 不是越多越好很多人做 Agent 的第一个错误就是一次性塞几十个工具。模型看到太多工具会混乱。工具描述太像会误选。工具参数太复杂会乱传。所以 Tool 设计有三个原则。第一少而准。一个工具只做一件事。第二名字清楚。不要叫 tool1、query、search_all。第三描述明确。告诉模型什么时候用什么时候不用。比如订单系统不要只写“查询订单”。应该写当用户提供订单号并询问物流、支付、退款、发货状态时使用。十一、企业级 Tool 必须过安全网Tool 连着真实业务。所以它不能像 Demo 一样裸跑。权限要在工具执行前判断。参数要在工具执行前校验。高危动作要人工确认。执行过程要有超时、重试、审计、脱敏。大模型不是权限系统。Prompt 也不是权限系统。能不能执行工具不能由模型说了算必须由业务系统说了算。十二、Tool 错误应该怎么处理工具一定会失败。接口超时。数据库异常。参数错误。权限不足。第三方限流。如果直接把异常堆栈扔给模型模型可能会胡乱解释。正确做法是把异常转成 ToolMessage让模型知道工具失败了但不要暴露敏感细节。wrap_tool_calldef handle_tool_errors(request, handler):try:return handler(request)except Exception:return ToolMessage(content工具调用失败请检查参数或稍后重试。,tool_call_idrequest.tool_call[id],)生产环境里错误处理建议分三层。第一层参数错误直接提示模型换参数。第二层临时失败自动重试。第三层高危或不可恢复错误转人工或返回兜底答案。十三、在 Java Python 架构里Tools 应该放在哪里对于 Java 后端项目不建议把所有 AI 能力都塞进 Spring Boot。更稳的方式是Java 主服务管业务、权限、审计、数据Python AI 服务管 LangChain、Agent、Tools、RAG。Tools 不应该绕过 Java 主服务直接打数据库。最稳的链路是模型请求 ToolPython Tool 调 Java 内部接口Java 再做鉴权、限流、审计、业务校验最后返回结果。这样做的好处很明显。AI 服务可以快速迭代。业务规则仍然掌握在 Java 服务里。权限和审计不会失控。十四、生产环境的 Tool 设计清单写 Tool 之前先问这 10 个问题。十六、总结RAG 解决“模型不知道”的问题。Tools 解决“模型不能做”的问题。Tool 不是一段随便暴露给模型的函数。它是模型和业务系统之间的协议层、安全层、执行层。源码上Tool 继承 RunnableSerializable通过 invoke 进入 run再经过参数解析、类型校验、真实函数执行最后把结果返回给模型。工程上Tool 必须有权限、校验、审计、超时、重试、脱敏和人工确认。真正成熟的 Agent不是模型更强而是工具设计得更稳。一句话收尾Tools 是 Agent 的手脚但这双手脚必须戴上工程化的安全手套。内容来源LangChain 系列之Tools让大模型真正连接业务系统功能变化与行业影响解析_热闻岛