全部版块 我的主页
论坛 数据科学与人工智能 人工智能
121 0
2025-11-19

工具在 LangChain 中的作用

在 LangChain 中,工具的设计遵循特定的输入/输出契约,使用 Pydantic v2 模型来定义参数和返回结构,确保了数据的一致性和可靠性。这种设计思路强调 RORO(接收一个对象,返回一个对象)原则。

工具的执行体可以是纯函数或者更复杂的逻辑处理,内部可能涉及 API 调用、数据库操作、MCP 等实际业务逻辑的实现。

async def

工具元数据包含了名称、描述、类型、超时时间和并发限制等关键信息。这些元数据帮助 LLM 根据描述和 schema 来决定何时以及如何调用工具。

通过特定的组件,LangChain 能够将上述各个部分组合成可复用的动作单元,无论是集成到 Agent、Router 还是 LCEL Flow 中,都能保持接口的一致性。

Tool
BaseTool

核心属性与类型

属性 含义 实践要点
工具唯一标识 使用动词开头的标识符 确保每个工具都有一个清晰且唯一的标识
功能说明 提供给 LLM 的参考信息 详细说明输入约束、单位和可能的失败模式
Pydantic BaseModel 输入 用于类型检查和默认值设置 确保所有输入都经过统一的类型验证和默认值处理
直接返回给用户的输出 确定工具输出是否直接展示给用户 在没有 Agent 的场景中,此选项通常设为 True
异步/同步执行入口 定义工具的执行方式 对于 I/O 密集型任务,建议使用异步执行
自定义异常处理 处理工具执行过程中可能出现的异常 将异常转换为用户友好的错误消息
过滤和监控 用于路由器或审计目的 实现工具的过滤和监控功能,支持更精细的管理和分析
name
query_weather
description
args_schema
return_direct
coroutine
func
handle_tool_error
tags
metadata

常见的工具类型包括:

  • 基础工具:最简单的封装形式,适用于同步函数。
  • 异步工具:具有原生异步入口,适合处理异步任务。
  • 带参数校验的工具:强制使用 args_schema,方便处理复杂的参数需求。
  • 自定义工具:允许完全自定义行为,例如实现缓存、批处理和跟踪等功能。
Tool
AsyncTool
StructuredTool
BaseTool

三种创建工具的方式

3.1 装饰器方式(最轻量)

from langchain.agents import tool
@tool("get_exchange_rate", return_direct=True)
def get_exchange_rate(pair: str) -> str:
    """输入类似 'USD/CNY',返回六位小数的即期汇率。"""
    if "/" not in pair:
        raise ValueError("货币对格式应为 XXX/YYY")
    base, quote = pair.upper().split("/")
    rate = fetch_rate(base=base, quote=quote)  # 同步 HTTP
    return f"{base}/{quote}={rate:.6f}"

特点:自动推断单个参数的类型,非常适合快速开发演示。

@tool

3.2 结构化工具(推荐默认)

from langchain.tools import StructuredTool
from pydantic import BaseModel, Field

class WeatherInput(BaseModel):
    city: str = Field(..., description="城市英文名,如 beijing")
    is_metric: bool = Field(default=True, description="是否使用摄氏度")

async def fetch_weather(city: str, is_metric: bool) -> dict:
    units = "metric" if is_metric else "imperial"
    return await weather_client.query(city=city, units=units)

weather_tool = StructuredTool.from_function(
    name="query_weather",
    description="查询城市实时天气,失败返回错误信息",
    func=fetch_weather,
    coroutine=fetch_weather,  # 支持异步
    args_schema=WeatherInput,
)

特点:显式的 schema 定义,支持同时注册同步和异步方法,适应多种应用场景。

StructuredTool.from_function
func
coroutine

3.3 手动子类化(高级可控)

from langchain.tools import BaseTool
from pydantic import BaseModel

class StockInput(BaseModel):
    symbol: str

class StockTool(BaseTool):
    name = "query_stock"
    description = "获取股票实时价,示例:symbol=TSLA"
    args_schema = StockInput

    def _run(self, symbol: str) -> str:
        price = sync_client.get_price(symbol)
        return f"{symbol} 当前价格 {price}"
BaseTool

异步方法 _arun 定义如下:

async def _arun(self, symbol: str) -> str:
    price = await async_client.get_price(symbol)
    return f"{symbol} 当前价格 {price}"

创建了一个 StockTool 实例。

特点包括但不限于:支持扩展属性、在功能模块内加入缓存、熔断机制及链路追踪等自定义逻辑。

_run/_arun

同步与异步调用路径

Agent/LCEL 在同步环境中运行时,它会调用相应的同步函数,最终触发一系列操作,这些操作可能包括但不限于以下几种情况:

Tool.__call__()

最终可能触发的操作有:

_run
或者
func

此流程适用于脚本、后台任务或 FastAPI 的同步端点。

而在异步事件循环中(例如 FastAPI

async def
、LangServe、Jupyter asyncio),Agent 将运行异步代码,使用特定的方法来执行任务:

await tool.arun(...)

这将导致执行

_arun
coroutine

在混合场景下,用户可以通过同步方式

tool.invoke()
和异步方式
await tool.ainvoke()
手动控制调用过程,例如:

result_sync = weather_tool.invoke({"city": "beijing"})
    result_async = await weather_tool.ainvoke({"city": "shanghai"})

最佳实践

建议对外部公开

func
coroutine
,同时在内部实现中保持逻辑的一致性,以便于维护和测试。

工具异常处理

为了确保输入的有效性,可以利用 Pydantic 进行提前验证,若验证失败,则直接抛出异常,Agent 将接收到结构化的错误提示。

field_validator
ValidationError

对于预期中的异常情况,可以通过自定义错误信息来提高用户体验:

from langchain.tools import ToolException
    async def safe_fetch_weather(city: str, is_metric: bool) -> dict:
        if not city:
            raise ToolException("city 不能为空")
        try:
            return await weather_client.query(city=city, units="metric")
        except httpx.HTTPStatusError as exc:
            raise ToolException(f"上游接口 {exc.response.status_code} 错误")
    def render_tool_error(error: Exception) -> str:
        if isinstance(error, ToolException):
            return str(error)
        return "内部错误:请稍后重试"

通过以上配置,可以创建一个结构化的工具实例,用于查询城市的实时天气:

weather_tool = StructuredTool.from_function(
        name="query_weather",
        description="查询城市实时天气",
        func=fetch_weather,
        coroutine=fetch_weather,
        args_schema=WeatherInput,
        handle_tool_error=render_tool_error,
    )

日志与可观察性

在工具内部记录关键信息,如日志、执行时间以及重试次数,有助于故障排查。

trace_id

幂等与重试

对于可以重放的调用,建议在工具内部实现幂等性和自动重试机制,这样外部 Agent 无需关心重试逻辑。

async_retry

全局异常处理函数

可以构建一个全局的工具错误处理函数,该函数可以根据服务名称格式化错误信息,提供更具体的错误反馈:

from collections.abc import Callable
    def build_global_tool_error_handler(service_name: str) -> Callable[[Exception], str]:
        def handle(error: Exception) -> str:
            logger.error("tool=%s error=%s", service_name, error)
            if isinstance(error, ToolException):
                return str(error)
            return f"{service_name} 暂时不可用,请稍后重试"
        return handle
    global_tool_error_handler = build_global_tool_error_handler("weather")
    weather_tool = StructuredTool.from_function(
        ...,
        handle_tool_error=global_tool_error_handler,
    )

通过这种方式,不仅可以根据工具名称和 trace_id 提供统一格式化的错误信息,还能通过工厂函数为不同的工具生成专门的错误处理器,既符合类型注解的要求,又便于集中管理和维护。

handle_tool_error
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群