Tools 扩展了 agents 的能力,让它们能够获取实时数据、执行代码、查询外部数据库,并在外部世界中采取行动。 在底层,tools 是具有明确定义输入和输出的可调用函数,会被传给 chat model。模型会根据对话上下文决定何时调用 tool,以及提供什么输入参数。
如需了解模型如何处理 tool calls,请参阅 Tool calling。使用 LangSmith 跟踪 tool calls 并调试错误。按照 tracing quickstart 完成设置。建议同时设置 LangSmith Engine,它会监控 traces、检测问题并提出修复建议。

Create tools

Basic tool definition

创建 tool 最简单的方式,是从 langchain package 导入 tool 函数。可以使用 zod 定义 tool 的输入 schema:
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"),
    }),
  }
);
Server-side tool use: 某些 chat models 具有在 server-side 执行的 built-in tools(web search、code interpreters)。详情请参阅 Server-side tool use
Tool 名称优先使用 snake_case,例如使用 web_search 而不是 Web Search。一些模型 providers 对包含空格或特殊字符的名称有问题,或会直接拒绝并报错。坚持使用字母数字字符、下划线和连字符,有助于提升跨 providers 的兼容性。

Access context

当 tools 能够访问对话历史、用户数据和持久记忆等 runtime 信息时,它们最为强大。本节介绍如何在 tools 内访问和更新这些信息。

Context

Context 提供调用时传入的不可变配置数据。它适用于用户 ID、会话详情,或对话期间不应改变的应用专属设置。
thread_id(通过 config={"configurable": {"thread_id": ...}} 传入)限定对话范围:message history 和 checkpoints;而 context 携带 tools 和 middleware 在调用时读取的每次运行数据。在生产环境中,通常同时传入二者:每个对话使用稳定的 thread_id,每次 invoke 都传入 context 对象。
Tools 可以通过 config 参数访问 agent 的 runtime context。将 contextthread_id 一起传入,使对话跨轮次持久化:
import * as z from "zod";
import { ChatOpenAI } from "@langchain/openai";
import { createAgent, tool } from "langchain";

const getUserName = tool(
  (_, config) => {
    return config.context.user_name;
  },
  {
    name: "get_user_name",
    description: "Get the user's name.",
    schema: z.object({}),
  },
);

const contextSchema = z.object({
  user_name: z.string(),
});

const agent = createAgent({
  model: new ChatOpenAI({ model: "google-genai:gemini-3.5-flash" }),
  tools: [getUserName],
  contextSchema,
});

const result = await agent.invoke(
  {
    messages: [{ role: "user", content: "What is my name?" }],
  },
  {
    configurable: { thread_id: crypto.randomUUID() },
    context: { user_name: "John Smith" },
  },
);

Long-term memory (Store)

BaseStore 提供跨对话保留的持久存储。与 state(短期记忆)不同,保存到 store 的数据在未来会话中仍可用。 通过 config.store 访问 store。Store 使用 namespace/key 模式组织数据:
import * as z from "zod";
import { createAgent, tool } from "langchain";
import { InMemoryStore } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";

const store = new InMemoryStore();

// Access memory
const getUserInfo = tool(
  async ({ user_id }) => {
    const value = await store.get(["users"], user_id);
    console.log("get_user_info", user_id, value);
    return value;
  },
  {
    name: "get_user_info",
    description: "Look up user info.",
    schema: z.object({
      user_id: z.string(),
    }),
  }
);

// Update memory
const saveUserInfo = tool(
  async ({ user_id, name, age, email }) => {
    console.log("save_user_info", user_id, name, age, email);
    await store.put(["users"], user_id, { name, age, email });
    return "Successfully saved user info.";
  },
  {
    name: "save_user_info",
    description: "Save user info.",
    schema: z.object({
      user_id: z.string(),
      name: z.string(),
      age: z.number(),
      email: z.string(),
    }),
  }
);

const agent = createAgent({
  model: new ChatOpenAI({ model: "gpt-5.4" }),
  tools: [getUserInfo, saveUserInfo],
  store,
});

// First session: save user info
await agent.invoke({
  messages: [
    {
      role: "user",
      content: "Save the following user: userid: abc123, name: Foo, age: 25, email: foo@langchain.dev",
    },
  ],
});

// Second session: get user info
const result = await agent.invoke({
  messages: [
    { role: "user", content: "Get user info for user with id 'abc123'" },
  ],
});

console.log(result);
// Here is the user info for user with ID "abc123":
// - Name: Foo
// - Age: 25
// - Email: foo@langchain.dev

Stream writer

在执行期间从 tools stream 实时更新。这对于在长时间运行的操作期间向用户提供进度反馈很有用。 使用 config.writer 发出自定义更新:
import * as z from "zod";
import { tool, ToolRuntime } from "langchain";

const getWeather = tool(
  ({ city }, config: ToolRuntime) => {
    const writer = config.writer;

    // Stream custom updates as the tool executes
    if (writer) {
      writer(`Looking up data for city: ${city}`);
      writer(`Acquired data for city: ${city}`);
    }

    return `It's always sunny in ${city}!`;
  },
  {
    name: "get_weather",
    description: "Get weather for a given city.",
    schema: z.object({
      city: z.string(),
    }),
  }
);

Execution info

通过 runtime.execution_info 在 tool 内访问 thread ID、run ID 和 retry state:
import { tool } from "langchain";
import * as z from "zod";

const logExecutionContext = tool(
  async (_input, runtime) => {
    const info = runtime.executionInfo;
    console.log(`Thread: ${info.threadId}, Run: ${info.runId}`);
    console.log(`Attempt: ${info.nodeAttempt}`);
    return "done";
  },
  {
    name: "log_execution_context",
    description: "Log execution identity information.",
    schema: z.object({}),
  }
);
需要 deepagents>=1.9.0(或 @langchain/langgraph>=1.2.8)。

Server info

当 tool 在 LangGraph Server 上运行时,通过 runtime.server_info 访问 assistant ID、graph ID 和 authenticated user:
import { tool } from "langchain";
import * as z from "zod";

const getAssistantScopedData = tool(
  async (_input, runtime) => {
    const server = runtime.serverInfo;
    if (server != null) {
      console.log(`Assistant: ${server.assistantId}, Graph: ${server.graphId}`);
      if (server.user != null) {
        console.log(`User: ${server.user.identity}`);
      }
    }
    return "done";
  },
  {
    name: "get_assistant_scoped_data",
    description: "Fetch data scoped to the current assistant.",
    schema: z.object({}),
  }
);
当 tool 未在 LangGraph Server 上运行时,serverInfonull
需要 deepagents>=1.9.0(或 @langchain/langgraph>=1.2.8)。

Tool execution

在 LangChain 中,tools 由 agents 使用(例如通过 create_agent),tool error handling 通过 middleware 配置。 对于 LangGraph workflows,tool execution 由 ToolNode 处理。请参阅 ToolNode

Tool return values

可以为 tools 选择不同返回值:
  • 返回 string,用于人类可读的结果。
  • 返回 object,用于模型应解析的结构化结果。
  • 当需要写入 state 时,返回带可选 message 的 Command

Return a string

当 tool 应提供普通文本供模型读取,并用于下一次响应时,返回字符串。
import { tool } from "langchain";
import * as z from "zod";

const getWeather = tool(({ city }) => `It is currently sunny in ${city}.`, {
  name: "get_weather",
  description: "Get weather for a city.",
  schema: z.object({ city: z.string() }),
});
行为:
  • 返回值会转换为 ToolMessage
  • 模型会看到该文本并决定下一步操作。
  • 除非模型或另一个 tool 之后修改,否则不会改变任何 agent state 字段。
当结果天然是人类可读文本时使用这种方式。

Return an object

当 tool 生成模型应检查的结构化数据时,返回对象(例如 dict)。
import { tool } from "langchain";
import * as z from "zod";

const getWeatherData = tool(
  ({ city }) => ({
    city,
    temperature_c: 22,
    conditions: "sunny",
  }),
  {
    name: "get_weather_data",
    description: "Get structured weather data for a city.",
    schema: z.object({ city: z.string() }),
  },
);
行为:
  • 对象会被序列化并作为 tool output 返回。
  • 模型可以读取特定字段并基于它们推理。
  • 与返回字符串一样,这不会直接更新 graph state。
当下游推理受益于显式字段,而不是自由格式文本时使用这种方式。

Return a Command

当 tool 需要更新 graph state(例如设置用户偏好或应用状态)时,返回 Command。 可以返回包含或不包含 ToolMessageCommand。 如果模型需要看到 tool 已成功执行(例如确认偏好更改),请在更新中包含 ToolMessage,并使用 runtime.tool_call_id 作为 tool_call_id 参数。
import { tool, ToolMessage, type ToolRuntime } from "langchain";
import { Command } from "@langchain/langgraph";
import * as z from "zod";

const setLanguage = tool(
  async ({ language }, config: ToolRuntime) => {
    return new Command({
      update: {
        preferredLanguage: language,
        messages: [
          new ToolMessage({
            content: `Language set to ${language}.`,
            tool_call_id: config.toolCallId,
          }),
        ],
      },
    });
  },
  {
    name: "set_language",
    description: "Set the preferred response language.",
    schema: z.object({ language: z.string() }),
  },
);
行为:
  • Command 使用 update 更新 state。
  • 更新后的 state 可供同一次 run 中的后续步骤使用。
  • 对可能由并行 tool calls 更新的字段使用 reducers。
当 tool 不只是返回数据,而是还要修改 agent state 时使用这种方式。

Error handling

使用 LangChain agent middleware 处理 tool errors,以重试失败的 tool calls 或返回自定义错误 messages:
import { createAgent, createMiddleware, ToolMessage } from "langchain";

const handleToolErrors = createMiddleware({
  name: "HandleToolErrors",
  wrapToolCall: async (request, handler) => {
    try {
      return await handler(request);
    } catch (error) {
      return new ToolMessage({
        content: `Tool error: Please check your input and try again. (${error})`,
        tool_call_id: request.toolCall.id!,
      });
    }
  },
});

const agent = createAgent({
  model: "google-genai:gemini-3.5-flash",
  tools: [],
  middleware: [handleToolErrors],
});

State injection

Tools 通过 ToolRuntime 访问 graph state。有关 state、context、store 和 streaming APIs,请参阅 Access context

Dynamic tool selection

使用 dynamic tools 时,agent 可用的 tools 集合会在 runtime 修改,而不是一开始全部定义。并非每个 tool 都适合所有场景。Tools 太多可能会压垮模型(上下文过载)并增加错误;tools 太少会限制能力。Dynamic tool selection 可根据认证状态、用户权限、feature flags 或对话阶段调整可用 toolset。 根据 tools 是否提前已知,有两种方法:
当所有可能的 tools 在 agent 创建时都已知时,可以预先注册它们,并根据 state、permissions 或 context 动态过滤暴露给模型的 tools。
仅在达到特定对话里程碑后启用高级 tools:
import { createMiddleware, tool } from "langchain";
import { createDeepAgent } from "deepagents";

const stateBasedTools = createMiddleware({
    name: "StateBasedTools",
    wrapModelCall: (request, handler) => {
        // Read from State: check authentication and conversation length
        const state = request.state as typeof request.state & {
            authenticated?: boolean;
        };
        const isAuthenticated = state.authenticated ?? false;
        const messageCount = state.messages.length;

        let filteredTools = request.tools;

        // Only enable sensitive tools after authentication
        if (!isAuthenticated) {
            filteredTools = request.tools.filter(
                (t: any) => typeof t.name === "string" && t.name.startsWith("public_"),
            );
        } else if (messageCount < 5) {
            filteredTools = request.tools.filter(
                (t: any) => typeof t.name === "string" && t.name !== "advanced_search",
            );
        }

        return handler({ ...request, tools: filteredTools });
    },
});

const agent = await createDeepAgent({
    model: "claude-sonnet-4-20250514",
    tools: tools,
    middleware: [stateBasedTools] as any,
});
此方法最适合以下情况:
  • 所有可能的 tools 在编译/启动时已知
  • 你想根据权限、feature flags 或对话状态过滤
  • Tools 是静态的,但其可用性是动态的
更多示例请参阅 Dynamically selecting tools

Prebuilt tools

LangChain 提供大量 prebuilt tools 和 toolkits,用于 web search、code interpretation、database access 等常见任务。这些即用型 tools 可以直接集成到 agents 中,无需编写自定义代码。 请参阅 tools and toolkits integration 页面,查看按类别组织的可用 tools 完整列表。

Server-side tool use

某些 chat models 具有由模型 provider 在 server-side 执行的 built-in tools。这包括 web search 和 code interpreters 等能力,不需要你定义或托管 tool 逻辑。 有关启用和使用这些 built-in tools 的详情,请参阅各个 chat model integration pagestool calling documentation