在使用 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. 冲突规避策略:唯一命名与模块化管理
为避免命名冲突,建议采用以下实践:
- 使用业务域前缀,如
finance_calculate_tax而非calculate; - 在不同模块中注册工具时,显式指定 name 参数:
@tool("user_profile_fetch"); - 集中管理工具注册,通过工厂函数统一暴露;
- 利用 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 规范导出工具接口,促进团队协作。
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报- 问题一:缺少类型注解 —— 如