普通网友 2025-12-11 18:00 采纳率: 98.6%
浏览 1
已采纳

如何正确使用LangChain工具装饰器注册自定义工具?

在使用 LangChain 的 `@tool` 装饰器注册自定义工具时,常见问题是如何正确处理函数签名与输入参数的类型注解。若未为函数参数添加明确的类型提示(如 `str`、`int`),LangChain 可能无法正确解析工具的输入结构,导致运行时报错或提示“Tool missing required input”。此外,多个工具命名冲突或重复注册也会引发异常。如何正确定义带类型注解的函数,并通过 `@tool` 成功注册为 LangChain Tool 实例,确保其能被 Agent 正确调用,是开发者常遇到的技术难点。
  • 写回答

1条回答 默认 最新

  • 火星没有北极熊 2025-12-11 18:18
    关注

    1. 基础概念:LangChain 中的 @tool 装饰器作用与机制

    在 LangChain 框架中,@tool 装饰器用于将普通 Python 函数注册为可被 Agent 调用的工具(Tool)。这些工具本质上是封装了特定功能的接口,例如调用 API、执行数据库查询或处理文本。当使用 @tool 时,LangChain 会自动解析函数签名,并生成对应的输入 schema,以便 LLM 理解如何调用该工具。

    然而,若函数未提供明确的类型注解,如 def search(query):,LangChain 将无法推断参数类型,导致运行时报错:“Tool missing required input”。这是因为底层依赖 Pydantic 构建输入模型,而 Pydantic 需要类型提示来生成有效的 JSON Schema。

    2. 常见问题分析:类型注解缺失与命名冲突

    • 问题一:缺少类型注解 —— 如 def get_weather(location) 未标注类型,LangChain 无法生成有效输入结构。
    • 问题二:多参数无默认值顺序混乱 —— 当函数有多个参数但未使用关键字参数时,Agent 可能传参错误。
    • 问题三:工具名称重复 —— 多个函数使用相同名字注册,引发 ValueError: Tool already exists
    • 问题四:返回值类型不匹配描述 —— 工具文档描述与实际返回格式不符,影响 Agent 解析结果。

    3. 正确实现方式:带类型注解的函数定义示例

    from langchain_core.tools import tool
    
    @tool
    def search_web(query: str, max_results: int = 5) -> str:
        """根据关键词搜索网络并返回前N条结果摘要。"""
        # 模拟搜索逻辑
        return f"搜索 '{query}' 的 {max_results} 条结果"
    
    @tool
    def get_user_info(user_id: int) -> dict:
        """根据用户ID获取用户信息。"""
        return {"id": user_id, "name": "Alice", "status": "active"}
    

    上述代码中,每个参数都带有明确的类型注解(str, int, dict),且包含可选参数的默认值,确保 LangChain 能正确构建输入 schema。

    4. 深入机制:LangChain 如何解析 @tool 函数

    步骤说明
    1. 函数装饰@tool 拦截函数定义,提取元数据
    2. 类型检查通过 inspect.signature() 获取参数及其类型注解
    3. Schema 生成基于类型创建 Pydantic Model,用于 LLM 输入验证
    4. 注册工具将函数包装为 Tool 实例,加入工具池
    5. Agent 调用LLM 生成符合 schema 的 JSON 输入,触发执行

    5. 冲突规避策略:唯一命名与模块化管理

    为避免命名冲突,建议采用以下实践:

    1. 使用业务域前缀,如 finance_calculate_tax 而非 calculate
    2. 在不同模块中注册工具时,显式指定 name 参数:@tool("user_profile_fetch")
    3. 集中管理工具注册,通过工厂函数统一暴露;
    4. 利用 Python 包结构隔离工具集,按功能划分文件夹。

    6. 高级用法:自定义 Pydantic 模型作为输入类型

    from pydantic import BaseModel
    from langchain_core.tools import tool
    
    class TranslationRequest(BaseModel):
        text: str
        source_lang: str = "en"
        target_lang: str = "zh"
    
    @tool
    def translate_text(request: TranslationRequest) -> str:
        """翻译指定文本内容。"""
        return f"Translated '{request.text}' from {request.source_lang} to {request.target_lang}"
    

    此方式允许更复杂的输入结构,提升工具表达能力,同时保持类型安全。

    7. 调试与验证流程图

    graph TD A[定义函数] --> B{是否添加类型注解?} B -- 否 --> C[报错: Missing required input] B -- 是 --> D[应用 @tool 装饰器] D --> E{工具名是否唯一?} E -- 否 --> F[抛出 ValueError] E -- 是 --> G[生成 Tool 实例] G --> H[注册到 Agent 工具列表] H --> I[LLM 生成调用请求] I --> J[验证输入 schema] J --> K[执行函数并返回结果]

    8. 最佳实践总结清单

    • 始终为所有参数添加类型注解;
    • 为可选参数设置默认值以支持部分输入;
    • 避免使用内置名称(如 input, type)作为参数名;
    • 使用字符串文档说明工具用途和参数含义;
    • 测试工具独立可调用性,确保不依赖外部状态;
    • 在异步场景下使用 @tool(async_=True)
    • 对复杂输入使用 Pydantic 模型而非扁平参数;
    • 通过日志记录工具调用过程,便于调试;
    • 定期审查工具命名规范,防止技术债务积累;
    • 结合 OpenAPI 规范导出工具接口,促进团队协作。
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 12月12日
  • 创建了问题 12月11日