Documentation Index
Fetch the complete documentation index at: https://langchain.idochub.dev/llms.txt
Use this file to discover all available pages before exploring further.
许多人工智能应用程序通过自然语言与用户交互。然而,某些用例要求模型能够直接与外部系统(例如 API、数据库或文件系统)进行交互,并使用结构化输入。
工具是代理调用以执行操作的组件。它们通过允许模型通过明确定义的输入和输出与外部世界交互,从而扩展了模型的能力。工具封装了一个可调用函数及其输入模式。这些工具可以传递给兼容的聊天模型,使模型能够决定是否调用某个工具以及使用哪些参数。在这种情况下,工具调用使模型能够生成符合指定输入模式的请求。
创建工具
基本工具定义
创建工具最简单的方法是使用 @tool 装饰器。默认情况下,函数的文档字符串将成为工具的描述,帮助模型理解何时使用该工具:
from langchain_core.tools import tool
@tool
def search_database(query: str, limit: int = 10) -> str:
"""在客户数据库中搜索匹配查询的记录。
参数:
query: 要查找的搜索词
limit: 返回结果的最大数量
"""
return f"找到 {limit} 个关于 '{query}' 的结果"
类型提示是必需的,因为它们定义了工具的输入模式。文档字符串应信息丰富且简洁,以帮助模型理解工具的目的。
自定义工具属性
自定义工具名称
默认情况下,工具名称来自函数名称。当您需要更具描述性的名称时,请覆盖它:
@tool("web_search") # 自定义名称
def search(query: str) -> str:
"""在网络上搜索信息。"""
return f"搜索结果: {query}"
print(search.name) # web_search
自定义工具描述
覆盖自动生成的工具描述,以便为模型提供更清晰的指导:
@tool("calculator", description="执行算术计算。用于任何数学问题。")
def calc(expression: str) -> str:
"""计算数学表达式。"""
return str(eval(expression))
高级模式定义
使用 Pydantic 模型或 JSON 模式定义复杂输入:
from pydantic import BaseModel, Field
from typing import Literal
class WeatherInput(BaseModel):
"""天气查询的输入。"""
location: str = Field(description="城市名称或坐标")
units: Literal["celsius", "fahrenheit"] = Field(
default="celsius",
description="温度单位偏好"
)
include_forecast: bool = Field(
default=False,
description="是否包含5天预报"
)
@tool(args_schema=WeatherInput)
def get_weather(location: str, units: str = "celsius", include_forecast: bool = False) -> str:
"""获取当前天气及可选预报。"""
temp = 22 if units == "celsius" else 72
result = f"{location} 当前天气: {temp} 度 {units[0].upper()}"
if include_forecast:
result += "\n未来5天: 晴天"
return result
与代理一起使用工具
代理通过添加推理循环、状态管理和多步执行,超越了简单的工具绑定。
要查看如何与代理一起使用工具的示例,请参阅
代理。
高级工具模式
ToolNode
ToolNode 是一个预构建的 LangGraph 组件,用于处理代理工作流中的工具调用。它与 create_agent() 无缝协作,提供高级工具执行控制、内置并行性和错误处理。
配置选项
ToolNode 接受以下参数:
from langchain.agents import ToolNode
tool_node = ToolNode(
tools=[...], # 工具或可调用对象的列表
handle_tool_errors=True, # 错误处理配置
...
)
此节点可以执行的工具列表。可以包括:
- LangChain
@tool 装饰的函数
- 具有适当类型提示和文档字符串的可调用对象(例如函数)
控制如何处理工具执行失败。
可以是:
bool
str
Callable[..., str]
type[Exception]
tuple[type[Exception], ...]
默认值:内部 _default_handle_tool_errors
错误处理策略
ToolNode 通过其 handle_tool_errors 属性提供内置的工具执行错误处理。
要自定义错误处理行为,您可以将 handle_tool_errors 配置为布尔值、字符串、可调用对象、异常类型或异常类型的元组:
True:捕获所有错误并返回包含异常详细信息的默认错误模板的 ToolMessage。
str:捕获所有错误并返回包含此自定义错误消息字符串的 ToolMessage。
type[Exception]:仅捕获指定类型的异常并返回其默认错误消息。
tuple[type[Exception], ...]:仅捕获指定类型的异常并返回它们的默认错误消息。
Callable[..., str]:捕获与可调用对象签名匹配的异常并返回调用它时的字符串结果。
False:完全禁用错误处理,允许异常传播。
handle_tool_errors 默认为可调用对象 _default_handle_tool_errors,该对象:
- 捕获工具调用错误
ToolInvocationError(由于模型提供的无效参数)并返回描述性错误消息
- 忽略工具执行错误(它们将使用模板字符串
TOOL_CALL_ERROR_TEMPLATE = "错误: {error}\n 请修复您的错误。" 重新引发)
使用不同错误处理策略的示例:
# 使用默认错误消息模板字符串重试所有异常类型
tool_node = ToolNode(tools=[my_tool], handle_tool_errors=True)
# 使用自定义消息字符串重试所有异常类型
tool_node = ToolNode(
tools=[my_tool],
handle_tool_errors="我遇到了问题。请尝试重新表述您的请求。"
)
# 仅在 ValueError 时使用自定义消息重试,否则引发异常
def handle_errors(e: ValueError) -> str:
return "提供的输入无效"
tool_node = ToolNode([my_tool], handle_tool_errors=handle_errors)
# 仅在 ValueError 和 KeyError 时使用默认错误消息模板字符串重试,否则引发异常
tool_node = ToolNode(
tools=[my_tool],
handle_tool_errors=(ValueError, KeyError)
)
与 create_agent() 一起使用
将配置好的 ToolNode 直接传递给 create_agent():
from langchain_openai import ChatOpenAI
from langchain.agents import ToolNode, create_agent
import random
@tool
def fetch_user_data(user_id: str) -> str:
"""从数据库中获取用户数据。"""
if random.random() > 0.7:
raise ConnectionError("数据库连接超时")
return f"用户 {user_id}: John Doe, [email protected], 活跃"
@tool
def process_transaction(amount: float, user_id: str) -> str:
"""处理金融交易。"""
if amount > 10000:
raise ValueError(f"金额 {amount} 超过最大限额 10000")
return f"为用户 {user_id} 处理了 ${amount}"
def handle_errors(e: Exception) -> str:
if isinstance(e, ConnectionError):
return "数据库当前过载,但可以安全重试。请使用相同的参数再试一次。"
elif isinstance(e, ValueError):
return f"错误: {e}. 尝试以较小的金额处理交易。"
return f"错误: {e}. 请再试一次。"
tool_node = ToolNode(
tools=[fetch_user_data, process_transaction],
handle_tool_errors=handle_errors
)
agent = create_agent(
model=ChatOpenAI(model="gpt-4o"),
tools=tool_node,
prompt="您是一位金融助手。"
)
agent.invoke({
"messages": [{"role": "user", "content": "为 user123 处理一笔 15000 美元的付款。生成收据电子邮件并发送给用户。"}]
})
当您将 ToolNode 传递给 create_agent() 时,代理将使用您的确切配置,包括错误处理、自定义名称和标签。当您需要对工具执行行为进行细粒度控制时,这非常有用。
状态、上下文和内存
state:代理在其执行过程中维护状态 - 这包括消息、自定义字段以及您的工具需要跟踪的任何数据。状态在图中流动,并且可以被工具访问和修改。
InjectedState:一种注解,允许工具在不向 LLM 暴露的情况下访问当前图状态。这使得工具可以读取诸如消息历史或自定义状态字段之类的信息,同时保持工具模式的简洁性。
工具可以使用 InjectedState 注解访问当前图状态:from typing_extensions import Annotated
from langchain.agents.tool_node import InjectedState
# 访问当前对话状态
@tool
def summarize_conversation(
state: Annotated[dict, InjectedState]
) -> str:
"""总结到目前为止的对话。"""
messages = state["messages"]
human_msgs = sum(1 for m in messages if m.__class__.__name__ == "HumanMessage")
ai_msgs = sum(1 for m in messages if m.__class__.__name__ == "AIMessage")
tool_msgs = sum(1 for m in messages if m.__class__.__name__ == "ToolMessage")
return f"对话包含 {human_msgs} 条用户消息,{ai_msgs} 条 AI 响应,以及 {tool_msgs} 条工具结果"
# 访问自定义状态字段
@tool
def get_user_preference(
pref_name: str,
preferences: Annotated[dict, InjectedState("user_preferences")] # InjectedState 参数对模型不可见
) -> str:
"""获取用户偏好值。"""
return preferences.get(pref_name, "未设置")
状态注入的参数对模型隐藏。在上面的示例中,模型仅在工具模式中看到 pref_name - preferences 不包含在请求中。
Command:工具可以使用的一种特殊返回类型,用于更新代理的状态或控制图的执行流程。工具不仅可以返回数据,还可以返回 Command 来修改状态或将代理定向到特定节点。
使用返回 Command 的工具更新代理状态:from langgraph.types import Command
from langchain_core.messages import RemoveMessage
from langgraph.graph.message import REMOVE_ALL_MESSAGES
from langchain_core.tools import tool, InjectedToolCallId
from typing_extensions import Annotated
# 通过删除所有消息来更新对话历史
@tool
def clear_conversation() -> Command:
"""清除对话历史。"""
return Command(
update={
"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)],
}
)
# 更新代理状态中的 user_name
@tool
def update_user_name(
new_name: str,
tool_call_id: Annotated[dict, InjectedToolCallId]
) -> Command:
"""更新用户名。"""
return Command(update={"user_name": new_name})
runtime:您的代理的执行环境,包含在代理执行过程中持续存在的不可变配置和上下文数据(例如,用户 ID、会话详细信息或应用程序特定配置)。
工具可以通过 get_runtime 访问代理的运行时上下文:from dataclasses import dataclass
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain_core.tools import tool
from langgraph.runtime import get_runtime
USER_DATABASE = {
"user123": {
"name": "Alice Johnson",
"account_type": "Premium",
"balance": 5000,
"email": "[email protected]"
},
"user456": {
"name": "Bob Smith",
"account_type": "Standard",
"balance": 1200,
"email": "[email protected]"
}
}
@dataclass
class UserContext:
user_id: str
@tool
def get_account_info() -> str:
"""获取当前用户的账户信息。"""
runtime = get_runtime(UserContext)
user_id = runtime.context.user_id
if user_id in USER_DATABASE:
user = USER_DATABASE[user_id]
return f"账户持有人: {user['name']}\n类型: {user['account_type']}\n余额: ${user['balance']}"
return "未找到用户"
model = ChatOpenAI(model="gpt-4o")
agent = create_agent(
model,
tools=[get_account_info],
context_schema=UserContext,
prompt="您是一位金融助手。"
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "我的当前余额是多少?"}]},
context=UserContext(user_id="user123")
)
store:LangChain 的持久层。代理的长期记忆存储,例如跨对话存储的用户特定或应用程序特定数据。
工具可以通过 get_store 访问代理的存储:from langgraph.config import get_store
@tool
def get_user_info(user_id: str) -> str:
"""查找用户信息。"""
store = get_store()
user_info = store.get(("users",), user_id)
return str(user_info.value) if user_info else "未知用户"
要更新长期记忆,您可以使用 InMemoryStore 的 .put() 方法。以下是一个跨会话的持久内存完整示例:from typing import Any
from langgraph.config import get_store
from langgraph.store.memory import InMemoryStore
from langchain.agents import create_agent
from langchain_core.tools import tool
@tool
def get_user_info(user_id: str) -> str:
"""查找用户信息。"""
store = get_store()
user_info = store.get(("users",), user_id)
return str(user_info.value) if user_info else "未知用户"
@tool
def save_user_info(user_id: str, user_info: dict[str, Any]) -> str:
"""保存用户信息。"""
store = get_store()
store.put(("users",), user_id, user_info)
return "成功保存用户信息。"
store = InMemoryStore()
agent = create_agent(
model,
tools=[get_user_info, save_user_info],
store=store
)
# 第一个会话:保存用户信息
agent.invoke({
"messages": [{"role": "user", "content": "保存以下用户:userid: abc123, name: Foo, age: 25, email: [email protected]"}]
})
# 第二个会话:获取用户信息
agent.invoke({
"messages": [{"role": "user", "content": "获取 ID 为 'abc123' 的用户信息"}]
})
# 以下是 ID 为 "abc123" 的用户信息:
# - 姓名: Foo
# - 年龄: 25
# - 电子邮件: [email protected]