AI图鉴

AI图鉴首页 › AI 资讯 › LangChain Tools:从基础定义、上下文访问到错误处理

LangChain Tools:从基础定义、上下文访问到错误处理

来源:掘金 · 发布时间:10-23 · 由 AI图鉴每小时自动聚合

本文介绍 LangChain Tools:用@tool装饰器快速封装可调用函数,支持 参数校验、访问 State,Store,Context 等场景 还有 使用中间件来处理 tools 错误

tools 拓展了 agent 的能力,使其能够获取实时数据、执行代码、查询外部数据库,并在现实环境中执行操作。 底层来看,tools 是具备规范输入与输出的可调用函数,会传递给对话模型。模型会根据对话上下文决定何时调用工具,以及提供哪些输入参数。

1. 怎样创建 tools

创建工具最简单的方式是使用

@tool装饰器。默认情况下,函数的文档字符串会作为工具的描述,帮助模型判断何时调用该工具,需要注意的是 类型提示是必需的,因为它们定义了工具的输入模式。文档字符串应信息丰富且简洁,以帮助模型理解工具的用途,以及是否调用这个 tool;

@tool(name?:str, des?:str) 或者 @tool(args_schema=PydanticModel | JsonSchema) 是一个语法糖装饰器,它的核心作用是:把一个普通的 Python 函数,瞬间变成大模型能看懂、能调用的“工具”对象(Tool)。

具体体现在三个方面:

- 自动提取元数据(Schema):它会自动读取函数的名字、函数文档(Docstring,极其重要!)以及参数的类型注解(Type Hints),并自动转换成大模型 API 需要的 JSON 格式描述。

- 标准化接口:将函数封装为 LangChain 的 BaseTool 实例,方便无缝接入 Agent、LangGraph 或直接绑定到 Chat Model。

- 参数校验:底层基于 Pydantic 自动对大模型传过来的参数进行校验,防止类型错误

# lc-tools-an-memory.ipynb

# 创建 chat model 并绑定 agent

import os

from dotenv import load_dotenv

from langchain.agents import create_agent

from langchain.chat_models import init_chat_model

from langchain.messages import HumanMessage

from langchain.tools import tool

load_dotenv()

aliyun_base_url = os.getenv("ALIYUN_BASE_URL")

aliyun_api_key = os.getenv("ALIYUN_API_KEY")

model = init_chat_model(

model="qwen3.7-flash-2026-07-15",

model_provider="openai",

base_url=aliyun_base_url,

api_key=aliyun_api_key,

)

@tool

def love_answer(username: str) -> str:

# 类型提示是必需的,因为它们定义了工具的输入模式。文档字符串应信息丰富且简洁,以帮助模型理解工具的用途,让模型知道这个工具是否需要调用,如何使用这个工具

"""如果有用户提问有人爱他吗,那么就直接返回 `这个世界上有很多人爱你,更甚者山川河流也爱你!`

Args:

username (str): 用户名

"""

return f"这个世界上有很多人爱你 {username},更甚者山川河流也爱你!"

agent = create_agent(

model=model,

system_prompt="兼爱非攻、仁者爱人",

tools=[love_answer],

)

response = agent.invoke(

{"messages": [HumanMessage("天明问有人爱他吗?")]},

)

print("\nresponse tool_calls: \n", response['messages'][1].tool_calls)

print("\nresponse content: \n", response['messages'][-1].content)

从 agent.invoke 可以看到 message 中的 tool_calls 可以将 tool 解析初 name 和 args 还有一些 tool 的元数据。

@tool 的 description 参数覆盖自动生成的工具描述,为模型提供更清晰的指引:

@tool 的第一个参数 默认情况下,工具名称取自函数名。当你需要更具描述性的名称时可对其进行重写

⚠️:config/runtime 不可用作工具参数。使用这些名称会引发运行时错误。

2. 怎么做 tools 参数验证

Pydantic 模型用作 tools 的数据校验

# 其他的 tools 使用 Pydantic 模型或 JSON 模式定义复杂输入

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:

return "something"

JSON schemas 用作 tools 的数据校验

weather_schema = {

"type": "object",

"properties": {

"location": {"type": "string"},

"units": {"type": "string"},

"include_forecast": {"type": "boolean"}

},

"required": ["location", "units", "include_forecast"]

}

@tool(args_schema=weather_schema)

def get_weather(location: str, units: str = "celsius", include_forecast: bool = False) -> str:

"""Get current weather and optional forecast."""

return "something"

3. 大模型调用工具(Tool Calling)的过程是怎样的?

大模型调用工具并不是由大模型自己在服务器上直接运行你的 Python 代码,而是一个“人机协作、分步交互”的过程。

整个标准流程分为 5 个步骤:

- 绑定(Binding) 开发者在代码中定义好工具(使用 @tool),然后将这些工具通过 .bind_tools() 传递给大模型:

# @tool 的 description 参数覆盖自动生成的工具描述,为模型提供更清晰的指引:

# @tool 的第一个参数 默认情况下,工具名称取自函数名。当你需要更具描述性的名称时可对其进行重写

@tool("get_weather", description="根据 city 获取天气")

def get_weather_by(city: str) -> str:

"""获取天气(优先级没有 @tool 的 description 高) """

return f"{city}的天气是晴天,25℃"

在这个阶段,大模型的系统提示词(System Prompt)里会被悄悄注入这些工具的定义,让模型知道:“我有 get_weather 这个工具可以用”。

- 用户提问与模型意图识别(Model Request) 用户向大模型发起提问,例如:“帮我查一下北京今天天气怎么样?” 大模型收到提问后进行思考:

“这个问题需要查实时天气,我不会,但我手头有一个叫 get_weather 的工具正好能干这事。”

- 模型返回“工具调用请求”(Tool Call Response) 大模型不会直接回答最终结果,而是返回一个特殊的结构化响应(在 LangChain 中表现为带有 tool_calls 属性的 AIMessage):

模型输出:我要调用 get_weather 函数,传入参数 city="北京"。

- 本地拦截与执行(Local Execution) 你的后端代码(或 LangChain 的 Agent 框架)截获了大模型的这个请求:

代码解析出大模型要调用的函数名是 get_weather,参数是 {"city": "北京"}。

你的程序在本地安全环境中真正执行了你写的 Python 函数 get_weather(city="北京")。

函数返回结果:"北京的天气是晴天,25℃"。

- 反馈结果与生成最终回答(Final Response) 你的代码把刚才函数执行的结果打包成一个特殊的“工具消息”(ToolMessage),再次发送给大模型:

输入:用户提问 + 大模型的Tool Call请求 + 本地工具的执行结果

大模型思考:哦,结果出来了,是“北京的天气是晴天,25℃”。

模型输出最终答案:“根据查询,北京今天的天气是晴天,气温为 25℃ 噢!”

4. tools 具备访问 context 的能力

工具在能够获取对话历史、用户数据、持久化内存等运行时信息时,才能发挥最大能力, Tools 通过 ToolRuntime 参数可以访问到运行时的更多信息,ToolRuntime 提供了如下信息 为你将图片内容整理为中文 Markdown 表格如下:

访问短期记忆 (State)

状态是对话存续期间有效的短期记忆。它包含消息历史记录以及你在 Graph State(LangGraph 中) 中定义的所有自定义字段。

将 runtime: ToolRuntime 添加到你的工具签名中以访问 state。调用时,ToolNode 会自动注入该值;该参数不会包含在发送给模型的工具定义中。使用 runtime.state 读取当前对话状态,比如如下使用 runtime.state["messages"] 来获取当前的会话 list

from langchain.tools import tool, ToolRuntime

@tool

def get_last_user_message(runtime: ToolRuntime, username: str) -> str:

"""Get the most recent message from a specific user by name.

Args:

username: The name of the user whose last message you want to find.

"""

messages = runtime.state["messages"]

# 倒序查找指定用户的最后一条消息

for message in reversed(messages):

print("DEBUG message:::::: ", message) # 方便在终端观察

# 确保是 HumanMessage 且 name 匹配

if isinstance(message, HumanMessage) and getattr(message, "name", None) == username:

return message.content

return "No user messages found"

# 别忘了导入 HumanMessage

agent = create_agent(

model=model,

tools=[get_last_user_message],

system_prompt=SystemMessage("如果需要查找特定角色的发言,请使用对应的工具。"),

)

response = agent.invoke({

"messages": [

HumanMessage(

content="如果有一天,你会来看一看我吗?",

name="天明",

id="user-unique-id",

),

HumanMessage(

content="看你做什么呢,天明?",

name="高月",

id="qianlong-unique-id",

),

HumanMessage(

content="姬如千泷不论山高路远,必来!",

name="高月",

id="qianlong-unique-id",

),

HumanMessage(

content="高月的最后一句话是啥?",

name="墨子",

id="mozi-unique-id",

),

]

})

print("\nresponse content: \n", response["messages"][-1].content)

更新短期记忆状态 (State)

使用 Command 更新 agent 的 state。这对于需要更新自定义状态字段的工具很有用。更新时需要用到

ToolMessage,这样模型就能看到工具调用的结果,如下展示更新短期记忆 state 的 age 和 name:

from langchain.agents import AgentState

from langchain.messages import ToolMessage

from langchain.tools import ToolRuntime, tool

from langgraph.types import Command

# 1. 定义自定义状态

class CustomState(AgentState):

user_name: str

user_age: int

# 2. 定义可以通过 Command 修改状态的工具

@tool

def set_user_info(new_name: str, new_age: int, runtime: ToolRuntime) -> Command:

"""Set the user's name in the conversation state."""

return Command(

update={

"user_name": new_name,

"user_age": new_age,

"messages": [

ToolMessage( # 更新状态必须使用 ToolMessage

content=f"User name successfully set to {new_name}.",

tool_call_id=runtime.tool_call_id,

)

],

}

)

# 别忘了导入 HumanMessage

agent = create_agent(

model=model,

tools=[set_user_info],

state_schema=CustomState, # 关联自定义状态

system_prompt=SystemMessage(

content=(

"你是一个智能助手。如果用户要求设置用户信息时,请调用 set_user_info 工具;"

)

),

)

response = agent.invoke(

{

"user_name": "未知", # 初始化 CustomState 中的字段

"user_age": 0, # 初始化 CustomState 中的字段

"messages": [

HumanMessage(

# 此处更改的是 agent 的 user_name 状态字段

content="如果有一天,你会来看一看我吗?,我要求设置我的名字为: '荆天明',age 18 岁",

name="天明",

id="user-unique-id",

),

],

}

)

print("\n--- 最终回复内容 ---")

print(response["messages"][-1].content)

print("\n--- 最终状态 (State) ---")

print("user_name:", response.get("user_name"))

print("user_age:", response.get("user_age"))

tools 访问 context

context 提供调用时传入的不可变配置数据。可用于存放用户ID、会话信息,或是对话过程中不应发生变更的应用专属配置(比如context_schema、checkpointer)

- 比如 context_schema 指定 agent 的 上下文约束

- 比如指定 checkpointer,checkpointer 的作用:

- 实现多轮对话记忆与状态持久化(Persistence):每次 Agent 执行完一步(或者一次 invoke 结束),checkpointer 都会自动把当前的完整状态(包括对话历史 messages、自定义的状态变量等)保存到存储介质中(如保存到内存中 checkpointer=InMemorySaver()、数据库等)

- 支持“人工干预”(Human-in-the-loop):利用 checkpointer,Agent 可以在执行到危险步骤时暂停(Interrupt),把当前状态安全地存下来;等人类管理员点击“同意”后,Agent 可以从断点处直接恢复执行,而不需要重新从头跑一遍。

- **时间旅行与调试(Time Travel & Replay):**因为每一个步骤的状态都被 checkpointer 记录了下来,你可以随时查看历史某一个时刻的“现场”。

- 容错与故障恢复: 如果后端服务在执行某个复杂的工具调用时突然崩溃(例如网络超时、服务器重启),只要配置了持久化的 checkpointer(如 Redis、PostgreSQL 存盘),系统就可以从上一个成功的检查点恢复,而不至于丢失所有的上下文和进度

from dataclasses import dataclass

from langgraph.checkpoint.memory import InMemorySaver

from langchain_core.utils.uuid import uuid7

# 模拟数据库 个人资产信息表

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",

},

}

# 定义上下文, user_id 代表当前用户标识,不会改变

@dataclass

class UserContext:

user_id: str

# 2. 定义获取用户信息 的 tool,

@tool

def get_account_info(runtime: ToolRuntime[UserContext]) -> str:

"""Get the current user's account information."""

# runtime.context 获取 context 的当前用户信息

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"

agent = create_agent(

model=model,

tools=[get_account_info],

context_schema=UserContext, # 定义 context 的类型

checkpointer=InMemorySaver(), # 创建一个内存保存器,它负责把每一次对话的状态(包括消息历史、中间变量等)实时保存到内存(RAM),让大模型不会 “失忆”,程序重启后数据就会丢失。

# checkpointer=InMemorySaver 标识在生产环境中,通常会替换为 Redis、PostgreSQL 等持久化 Checkpointer。

system_prompt=SystemMessage(content=("You are a financial assistant.")),

)

"""

thread_id 的作用:

1. 确保每个对话的会话状态(如用户名、年龄等)不会相互干扰。如果你有多个用户在同时和机器人聊天,你可以为每个用户生成不同的 thread_id。这样他们的聊天记录和状态就会被 InMemorySaver 隔离开,互不干扰

2. 关联记忆(Thread ID),thread_id 就像是一个“聊天房间号”或“会话 ID”。

当你调用 agent.invoke(..., config=config) 时,会拿着这个 thread_id 去 InMemorySaver/Redis/DB 中寻找 里查找:“有没有这个房间之前的聊天记录?”。如果有,就加载进来;如果没有,就新建一个。

"""

thread_id = str(uuid7())

config = {"configurable": {"thread_id": thread_id}}

context = UserContext(

user_id="user123"

) # 加入当前 context 中的 user 的 id 是 Alice Johnson 的 user_id

# 调用的时候指定 context 和 配置(比如 thread_id)

res1 = agent.invoke(

{

"messages": [

HumanMessage(

"What's my current balance?",

),

],

},

config=config,

context=context,

)

print("res1.content:", res1["messages"][-1].content)

print("res1.token_usage", res1["messages"][-1].response_metadata["token_usage"])

res2 = agent.invoke(

{

"messages": [

HumanMessage(

"What's my current balance?",

),

],

},

config=config,

context=context,

)

print("res2.content:", res2["messages"][-1].content)

print("res2.token_usage", res2["messages"][-1].response_metadata["token_usage"])

tools 访问 store (长期记忆)

BaseStore 提供可跨会话保留的持久化存储。不同于State(短期记忆),保存到该 Store 中的数据在后续会话中仍然可用。

通过 runtime.store访问该存储。此存储采用命名 namespace/key 的模式来组织数据

⚠️ ⚠️ ⚠️ 对于生产环境部署,请使用持久化存储实现,例如 PostgresStore、MongoDBStore 或 RedisStore,而不要使用 InMemoryStore

from langgraph.store.memory import InMemoryStore

from langchain.agents import create_agent

from langchain.tools import tool, ToolRuntime

from langchain_openai import ChatOpenAI

# store.get访问 Store 长期记忆

@tool

def get_user_info(user_id: str, runtime: ToolRuntime) -> str:

# 注释文档帮助 LLM 理解 tool 怎么使用

"""查找用户信息"""

store = runtime.store

# users 是一个 namespace, 可以理解为数据库中的表

user_info = store.get("users", user_id)

return str(user_info.value) if user_info else "Unknown user"

# 保存/更新 Store 长期记忆

@tool

def save_user_info(

user_id: str, name: str, age: int, email: str, runtime: ToolRuntime

) -> str:

"""保存用户信息"""

store = runtime.store

store.put("users", user_id, {"name": name, "age": age, "email": email})

return "Successfully saved user info."

# 定义一个统一的 config(方便在同一线程或上下文中管理)

config = {"configurable": {"thread_id": "session_1"}}

agent = create_agent(

model=model,

tools=[get_user_info, save_user_info],

store=InMemoryStore(), # 创建一个内存存储器(不是 InMemorySaver), 生产环境中通常会替换为 Redis、PostgreSQL 等持久化存储。

system_prompt=SystemMessage(content=("You are a assistant.")),

)

# 要求保存用户信息

agent.invoke(

{

"messages": [

HumanMessage(

"保存这个用户的信息 userid: abc123, name: kejuqu, age: 18, email: kejuqu@langchain.dev"

)

]

},

config=config,

)

# 要求获取用户信息

res = agent.invoke(

{"messages": [HumanMessage("请获取用户信息 userid: abc123")]},

config=config,

)

print("res: ", res["messages"][-1].content)

自定义 Stream writer (流写入器)

在长时间运行的任务中向用户反馈进度, 利用工具 runtime 中流式输出实时更新信息。

即使用 runtime.stream_writer 发送自定义更新信息:

from pydantic import BaseModel

class SatelliteConnection(BaseModel):

status: str

message: str

def today_weather(self) -> str:

return "晴天,25度"

def connect_satellite() -> SatelliteConnection:

return SatelliteConnection(s

本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。

相关 AI 工具

相关免费羊毛

同源资讯

阅读原文(juejin.cn)

标题、摘要与正文均来自该信源的公开页面,版权归原作者所有;本站自动聚合与翻译,仅作出处跳转。

← 返回 AI 资讯列表AI工具图鉴首页

返回 AI图鉴首页 · 提交工具 · 商务合作 · 关于本站 · 590 款 AI 工具分类导航 · 每日 AI 热门项目与动态