TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

LangChain 工具调用:@tool 怎么写

用 @tool 或 tool 函数把普通函数变成智能体可以调用的工具,讲清起名、参数声明、返回方式和出错处理。

这篇适合谁,读完能做到什么

适合已经跑通过第一个 LangChain 智能体、想让它调用自己写的函数的读者。读完你能用 @tool 定义工具,给工具起名和写说明,理解参数声明的规则,控制工具返回结果的方式,并知道出错时怎么处理。

工具是什么

工具是有明确输入和输出的可调用函数,会被传给聊天模型。模型根据对话内容决定要不要调用某个工具,以及传哪些参数[1]。工具调用可以用 LangSmith 追踪,出错时也便于排查[1]。

Python:用 @tool 定义工具

最简单的做法是使用 langchain.tools 里的 @tool 装饰器。默认情况下,函数的文档字符串会成为工具的描述,模型靠它判断什么时候该用这个工具[1]:

from langchain.tools import tool

@tool
def search_database(query: str, limit: int = 10) -> str:
    """Search the customer database for records matching the query.

    Args:
        query: Search terms to look for
        limit: Maximum number of results to return
    """
    return f"Found {limit} results for '{query}'"

Python 的类型注解是必需的,因为它定义了工具的输入结构。文档字符串应当简洁,说清用途和参数即可[1]。

JavaScript:用 tool 和 zod 定义工具

JavaScript 版本从 langchain 导入 tool,用 zod 描述输入结构。名称、描述和 schema 都写在第二个参数里[1]:

import * as z from "zod"
import { tool } from "langchain"

const searchDatabase = tool(
  ({ query, limit }) => `Found ${limit} results for '${query}'`,
  {
    name: "search_database",
    description: "Search the customer database for records matching the query.",
    schema: z.object({
      query: z.string().describe("Search terms to look for"),
      limit: z.number().describe("Maximum number of results to return"),
    }),
  }
);

给工具起名字和写说明

Python 中工具名默认取自函数名。需要更有描述性的名字时,可以写成 @tool("web_search") 这样的形式[1]:

@tool("web_search")  # Custom name
def search(query: str) -> str:
    """Search the web for information."""
    return f"Results for: {query}"

print(search.name)  # web_search

@tool 还接受 description 参数,可以覆盖自动生成的描述[1]。命名上建议使用 snake_case,例如 web_search,不要用 Web Search。部分模型提供方会拒绝带空格或特殊字符的名字,只用字母、数字、下划线和连字符兼容性最好[1]。

复杂参数怎么声明

参数较多时,可以用 Pydantic 模型定义输入,再通过 args_schema 参数交给 @tool,每个字段用 Field(description=...) 写清说明。也可以直接传入 JSON Schema[1]。

两个保留参数名

config 和 runtime 是保留名字,不能用作工具参数,否则运行时会报错[1]。需要读取对话状态、上下文或长期存储时,给工具加一个 runtime: ToolRuntime 参数。它由 ToolNode 在调用时注入,不会出现在发给模型的参数结构里;读取对话状态用 runtime.state[1]。

工具的返回值

工具可以返回字符串、对象、多模态内容,也可以返回 Command。普通返回值会被包装成 ToolMessage 交回模型处理[1]。

直接返回:跳过模型

把 return_direct 设为 True 后,工具的输出会直接作为智能体的结果,不再经过模型加工[1]。这个开关的规则需要注意:如果模型在同一轮里同时调用了普通工具,智能体不会结束,而是把所有结果交回模型;只有这一轮的每个工具都设置了 return_direct=True,循环才会结束[1]。结果还需要模型继续推理、总结或与其他工具串联的,就不适合设置它[1]。

如果工具返回的是 Command 并且指向当前图,那么 Command.update 里必须带上 tool_call_id 相同的 ToolMessage,否则 ToolNode 会抛出 ValueError[1]。

出错怎么办

工具出错时,可以借助智能体的中间件(middleware)重试失败的调用,或者返回自定义的错误信息[1]。

ToolRuntime 能拿到什么

ToolRuntime 把运行时信息分成几类。state 是短期状态,即当前对话的消息等内容;context 是只读的配置,适合放用户 ID 这类调用方传入的信息;store 是长期记忆,可以跨对话保留数据。此外还有 stream writer、执行信息、服务端信息、config 和工具调用 ID[1]。需要什么就读什么,不必全部用到。

三种返回方式怎么选

  • 普通返回字符串或对象:结果会包装成 ToolMessage,模型接着处理,适合结果还需要推理或总结的情况[1]。
  • return_direct=True:工具输出直接作为结果返回,不再经过模型,适合结果本身就是最终答案的情况[1]。
  • 返回 Command:可以在返回的同时更新状态,但要带上匹配的 ToolMessage[1]。

工具出错时的思路

工具可能因为参数不对、外部服务不可用或其他原因失败。把错误处理放在中间件里,可以统一决定是重试,还是把一条说明返回给模型,让它换一种方式继续[1]。

读完以后可以检查的几件事

  • 工具名是否是 snake_case,并且没有使用 config、runtime 这两个保留名[1]。
  • 需要运行时信息的工具,是否通过 runtime: ToolRuntime 参数读取[1]。
  • 直接返回的工具,是否是本轮唯一被调用的工具,否则智能体不会结束[1]。

为什么工具名和描述很重要

模型只能通过工具的名字和描述来判断它的用途,并决定要不要调用,因此两者都要写清楚。默认情况下,描述来自函数的文档字符串;需要更精确的描述时,可以在 @tool 上直接指定[1]。

工具调用的基本流程

模型看到工具的名字、描述和参数结构之后,决定要不要调用某个工具,并生成调用所需的参数。工具运行后,它的返回值进入对话,模型再根据结果决定下一步:继续调用别的工具,或者直接回答[1]。理解这个循环,有助于判断一个工具应该返回什么。

常见问题

  • 工具名报错:通常是名字里有空格或特殊字符,改成 snake_case 即可,见上文[1]。
  • 模型自带的服务端工具:部分模型内置网络搜索、代码解释器等工具,它们在服务端执行,与自己写的工具不是一回事[1]。
  • 参数名冲突:config 和 runtime 不能用,见「两个保留参数名」一节[1]。

下一步

LangChain 提供了大量预置工具和工具包,覆盖网络搜索、代码解释、数据库访问等场景,完整列表在集成页面[1]。另外,LangChain 也支持从 MCP 服务器获取工具[1]。

本文改编自 LangChain 开源文档中的 Tools 页面[1],原文以 MIT 许可证发布。

来源

  1. 1
    ToolsLangChain docs (MIT)

引用本页: LangChain 工具调用:@tool 怎么写. TellWorld, https://www.tellworld.net/zh/practice/practice-langchain-tools. 更新于 2026-11-14

相关动态