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

Create tools

Basic tool definition

创建 tool 最简单的方式是使用 @tool decorator。默认情况下,函数的 docstring 会成为 tool 的描述,帮助模型理解何时使用它:
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}'"
Type hints 是必需的,因为它们会定义 tool 的输入 schema。Docstring 应信息充分且简洁,以帮助模型理解 tool 的用途。
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 的兼容性。

Customize tool properties

Custom tool name

默认情况下,tool 名称来自函数名。如果需要更具描述性的名称,可以覆盖它:
@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

Custom tool description

覆盖自动生成的 tool 描述,以便为模型提供更清晰的指导:
@tool("calculator", description="Performs arithmetic calculations. Use this for any math problems.")
def calc(expression: str) -> str:
    """Evaluate mathematical expressions."""
    return str(eval(expression))

Advanced schema definition

使用 Pydantic models 或 JSON schemas 定义复杂输入:
from pydantic import BaseModel, Field
from typing import Literal

class WeatherInput(BaseModel):
    """Input for weather queries."""
    location: str = Field(description="City name or coordinates")
    units: Literal["celsius", "fahrenheit"] = Field(
        default="celsius",
        description="Temperature unit preference"
    )
    include_forecast: bool = Field(
        default=False,
        description="Include 5-day forecast"
    )

@tool(args_schema=WeatherInput)
def get_weather(location: str, units: str = "celsius", include_forecast: bool = False) -> str:
    """Get current weather and optional forecast."""
    temp = 22 if units == "celsius" else 72
    result = f"Current weather in {location}: {temp} degrees {units[0].upper()}"
    if include_forecast:
        result += "\nNext 5 days: Sunny"
    return result

Reserved argument names

以下参数名为保留名称,不能用作 tool arguments。使用这些名称会导致 runtime errors。
参数名称用途
config保留用于在内部将 RunnableConfig 传给 tools
runtime保留用于 ToolRuntime 参数(访问 state、context、store)
如需访问 runtime 信息,请使用 ToolRuntime 参数,而不是将自己的 arguments 命名为 configruntime 如果你使用 InjectedStateInjectedStoreget_runtime()InjectedToolCallId,请参阅 Migrate from older injection patterns

Access context

当 tools 能够访问对话历史、用户数据和持久记忆等 runtime 信息时,它们最为强大。本节介绍如何在 tools 内访问和更新这些信息。 Tools 可以通过 ToolRuntime 参数访问 runtime 信息,该参数提供:
ComponentDescriptionUse case
State短期记忆,即当前对话中存在的可变数据(messages、计数器、自定义字段)访问对话历史,跟踪 tool call 次数
Context调用时传入的不可变配置(用户 ID、会话信息)根据用户身份个性化响应
Store长期记忆,即跨对话保留的持久数据保存用户偏好,维护知识库
Stream Writer在工具执行期间发出实时更新为长时间运行的操作显示进度
Execution Info当前执行的身份和重试信息(thread ID、run ID、attempt number)访问 thread/run IDs,根据重试状态调整行为
Server Info在 LangGraph Server 上运行时的 server-specific metadata(assistant ID、graph ID、authenticated user)访问 assistant ID、graph ID 或 authenticated user 信息
Config执行对应的 RunnableConfig访问 callbacks、tags 和 metadata
Tool Call ID当前工具调用的唯一标识符将 tool calls 与 logs 和模型调用关联

Short-term memory (State)

State 表示在对话期间存在的短期记忆。它包括 message history,以及你在 graph state 中定义的任何自定义字段。
在 tool signature 中添加 runtime: ToolRuntime 以访问 state。该参数会自动注入并对 LLM 隐藏,不会出现在 tool 的 schema 中。

Access state

Tools 可以使用 runtime.state 访问当前对话 state:
from langchain.tools import tool, ToolRuntime
from langchain.messages import HumanMessage

@tool
def get_last_user_message(runtime: ToolRuntime) -> str:
    """Get the most recent message from the user."""
    messages = runtime.state["messages"]

    # Find the last human message
    for message in reversed(messages):
        if isinstance(message, HumanMessage):
            return message.content

    return "No user messages found"

# Access custom state fields
@tool
def get_user_preference(
    pref_name: str,
    runtime: ToolRuntime
) -> str:
    """Get a user preference value."""
    preferences = runtime.state.get("user_preferences", {})
    return preferences.get(pref_name, "Not set")
runtime 参数会对模型隐藏。在上面的示例中,模型只会在 tool schema 中看到 pref_name

Update state

使用 Command 更新 agent state。这对于需要更新自定义 state 字段的 tools 很有用。 在更新中包含 ToolMessage,让模型能够看到 tool call 的结果:
from langchain.agents import AgentState
from langchain.messages import ToolMessage
from langchain.tools import ToolRuntime, tool
from langgraph.types import Command


class CustomState(AgentState):
    user_name: str


@tool
def set_user_name(new_name: str, runtime: ToolRuntime[None, CustomState]) -> Command:
    """Set the user's name in the conversation state."""
    return Command(
        update={
            "user_name": new_name,
            "messages": [
                ToolMessage(
                    content=f"User name set to {new_name}.",
                    tool_call_id=runtime.tool_call_id,
                )
            ],
        }
    )
当 tools 更新 state variables 时,请考虑为这些字段定义 reducer。由于 LLMs 可以并行调用多个 tools,reducer 会决定当并发 tool calls 更新同一个 state 字段时如何解决冲突。

Context

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

from langchain.agents import create_agent
from langchain.tools import tool, ToolRuntime
from langchain_core.utils.uuid import uuid7
from langchain_openai import ChatOpenAI


USER_DATABASE = {
    "user123": {
        "name": "Alice Johnson",
        "account_type": "Premium",
        "balance": 5000,
        "email": "alice@example.com",
    },
    "user456": {
        "name": "Bob Smith",
        "account_type": "Standard",
        "balance": 1200,
        "email": "bob@example.com",
    },
}


@dataclass
class UserContext:
    user_id: str


@tool
def get_account_info(runtime: ToolRuntime[UserContext]) -> str:
    """Get the current user's account information."""
    user_id = runtime.context.user_id

    if user_id in USER_DATABASE:
        user = USER_DATABASE[user_id]
        return (
            f"Account holder: {user['name']}\n"
            f"Type: {user['account_type']}\n"
            f"Balance: ${user['balance']}"
        )
    return "User not found"


model = ChatOpenAI(model="google_genai:gemini-3.5-flash")
agent = create_agent(
    model,
    tools=[get_account_info],
    context_schema=UserContext,
    system_prompt="You are a financial assistant.",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What's my current balance?"}]},
    config={"configurable": {"thread_id": str(uuid7())}},
    context=UserContext(user_id="user123"),
)

Long-term memory (Store)

BaseStore 提供跨对话保留的持久存储。与 state(短期记忆)不同,保存到 store 的数据在未来会话中仍可用。 通过 runtime.store 访问 store。Store 使用 namespace/key 模式组织数据:
对于生产部署,请使用 PostgresStore 这样的持久 store 实现,而不是 InMemoryStore。设置详情请参阅 memory documentation
from typing import Any
from langgraph.store.memory import InMemoryStore
from langchain.agents import create_agent
from langchain.tools import tool, ToolRuntime
from langchain_openai import ChatOpenAI

# Access memory
@tool
def get_user_info(user_id: str, runtime: ToolRuntime) -> str:
    """Look up user info."""
    store = runtime.store
    user_info = store.get(("users",), user_id)
    return str(user_info.value) if user_info else "Unknown user"

# Update memory
@tool
def save_user_info(user_id: str, user_info: dict[str, Any], runtime: ToolRuntime) -> str:
    """Save user info."""
    store = runtime.store
    store.put(("users",), user_id, user_info)
    return "Successfully saved user info."

model = ChatOpenAI(model="gpt-5.4")

store = InMemoryStore()
agent = create_agent(
    model,
    tools=[get_user_info, save_user_info],
    store=store
)

# First session: save user info
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
agent.invoke({
    "messages": [{"role": "user", "content": "Get user info for user with id 'abc123'"}]
})
# Here is the user info for user with ID "abc123":
# - Name: Foo
# - Age: 25
# - Email: foo@langchain.dev

Stream writer

在执行期间从 tools stream 实时更新。这对于在长时间运行的操作期间向用户提供进度反馈很有用。 使用 runtime.stream_writer 发出自定义更新:
from langchain.tools import tool, ToolRuntime

@tool
def get_weather(city: str, runtime: ToolRuntime) -> str:
    """Get weather for a given city."""
    writer = runtime.stream_writer

    # Stream custom updates as the tool executes
    writer(f"Looking up data for city: {city}")
    writer(f"Acquired data for city: {city}")

    return f"It's always sunny in {city}!"
如果在 tool 内使用 runtime.stream_writer,该 tool 必须在 LangGraph execution context 中调用。更多详情请参阅 Streaming

Execution info

通过 runtime.execution_info 在 tool 内访问 thread ID、run ID 和 retry state:
from langchain.tools import tool, ToolRuntime

@tool
def log_execution_context(runtime: ToolRuntime) -> str:
    """Log execution identity information."""
    info = runtime.execution_info
    print(f"Thread: {info.thread_id}, Run: {info.run_id}")
    print(f"Attempt: {info.node_attempt}")
    return "done"
需要 deepagents>=0.5.0(或 langgraph>=1.1.5)。

Server info

当 tool 在 LangGraph Server 上运行时,通过 runtime.server_info 访问 assistant ID、graph ID 和 authenticated user:
from langchain.tools import tool, ToolRuntime

@tool
def get_assistant_scoped_data(runtime: ToolRuntime) -> str:
    """Fetch data scoped to the current assistant."""
    server = runtime.server_info
    if server is not None:
        print(f"Assistant: {server.assistant_id}, Graph: {server.graph_id}")
        if server.user is not None:
            print(f"User: {server.user.identity}")
    return "done"
当 tool 未在 LangGraph Server 上运行时,server_infoNone,例如本地开发或测试期间。
需要 deepagents>=0.5.0(或 langgraph>=1.1.5)。
较早的示例使用 InjectedStateInjectedStoreget_runtime()InjectedToolCallId。现在请改用 ToolRuntime,它为 state、context、store 和 execution metadata 提供一个显式接口。

Previous pattern

from langchain.tools import tool, InjectedState

@tool
def summarize(state: InjectedState) -> str:
    """Summarize the conversation."""
    messages = state["messages"]
    return f"Conversation length: {len(messages)} messages."
from langchain.tools import tool, ToolRuntime

@tool
def summarize(runtime: ToolRuntime) -> str:
    """Summarize the conversation."""
    messages = runtime.state["messages"]
    return f"Conversation length: {len(messages)} messages."
如需了解 agent 级迁移(例如 create_react_agent 和自定义 state),请参阅 LangChain v1 migration guide

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 应提供普通文本供模型读取,并用于下一次响应时,返回字符串。
from langchain.tools import tool


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

Return an object

当 tool 生成模型应检查的结构化数据时,返回对象(例如 dict)。
from langchain.tools import tool


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

Return a Command

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


@tool
def set_language(language: str, runtime: ToolRuntime) -> Command:
    """Set the preferred response language."""
    return Command(
        update={
            "preferred_language": language,
            "messages": [
                ToolMessage(
                    content=f"Language set to {language}.",
                    tool_call_id=runtime.tool_call_id,
                )
            ],
        }
    )
行为:
  • Command 使用 update 更新 state。
  • 更新后的 state 可供同一次 run 中的后续步骤使用。
  • 对可能由并行 tool calls 更新的字段使用 reducers。
当 tool 不只是返回数据,而是还要修改 agent state 时使用这种方式。

Error handling

使用 LangChain agent middleware 处理 tool errors,以重试失败的 tool calls 或返回自定义错误 messages:
from collections.abc import Callable

from langchain.agents import create_agent
from langchain.agents.middleware import wrap_tool_call
from langchain.messages import ToolMessage
from langchain.tools.tool_node import ToolCallRequest


@wrap_tool_call
def handle_tool_errors(
    request: ToolCallRequest,
    handler: Callable[[ToolCallRequest], ToolMessage],
) -> ToolMessage:
    """Convert tool exceptions into ToolMessages the model can handle."""
    try:
        return handler(request)
    except Exception as e:
        return ToolMessage(
            content=f"Tool error: Please check your input and try again. ({e})",
            tool_call_id=request.tool_call["id"],
        )


agent = create_agent(
    model="google_genai:gemini-3.5-flash",
    tools=[],
    middleware=[handle_tool_errors],
)

State injection

Tools 通过 ToolRuntime 访问 graph state。有关 state、context、store 和 streaming APIs,请参阅 Access context
from langchain.tools import tool, ToolRuntime

@tool
def get_message_count(runtime: ToolRuntime) -> str:
    """Get the number of messages in the conversation."""
    messages = runtime.state["messages"]
    return f"There are {len(messages)} messages."

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:
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable

@wrap_model_call
def state_based_tools(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
    """Filter tools based on conversation State."""
    # Read from State: check if user has authenticated
    state = request.state
    is_authenticated = state.get("authenticated", False)
    message_count = len(state["messages"])

    # Only enable sensitive tools after authentication
    if not is_authenticated:
        tools = [t for t in request.tools if t.name.startswith("public_")]
        request = request.override(tools=tools)
    elif message_count < 5:
        # Limit tools early in conversation
        tools = [t for t in request.tools if t.name != "advanced_search"]
        request = request.override(tools=tools)

    return handler(request)

agent = create_agent(
    model="gpt-5.4",
    tools=[public_search, private_search, advanced_search],
    middleware=[state_based_tools]
)
此方法最适合以下情况:
  • 所有可能的 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