SuperAPI AI 工具集

ag2-add-custom-tool

智能体与模型

查看项目

更新于 10-15 11:11

技能介绍

用途

ag2-add-custom-tool 用于给 AG2 的 Agent 添加由 Python 代码实现的新能力。当用户希望 Agent 采取真实世界操作(调用 API、查询数据库、执行计算、返回图片)时使用。若需要的是已内置的工具(如网页搜索、代码执行、shell),文档建议改用 ag2-use-builtin-tools 与 ag2-shell-tool。

主要能力

  • 通过 @tool 装饰器把 Python 函数变成 Agent 可调用的工具。装饰器会根据函数签名、类型提示和 docstring 生成面向 LLM 的 schema。文档强调 docstring 就是 LLM 看到的描述,应按面向 LLM 读者来编写。
  • 也可以直接把未装饰的普通函数传入 tools=[...],AG2 会自动包装。
  • 可以用 @agent.tool 把工具挂到已有 agent 上。
  • 同时支持 def 和 async def。同步工具默认在线程中运行,避免阻塞 I/O 冻结事件循环;对极快的纯 Python 工具可以用 @tool(sync_to_thread=False) 退出该行为。原生异步工具直接在主场事件循环中运行。
  • 支持用 Annotated[T, Field(...)] 给 LLM 设定严格边界,框架会把这些约束转发进 JSON Schema。
  • 可以在装饰器上覆盖工具名称和描述。
  • 支持返回类型化的 Input / ToolResult:普通 str 返回值会自动包装为 TextInput;更丰富的载荷可以返回 Input 子类型或组合 ToolResult,包括 DataInput、ImageInput。任意格式的原始字节可用 BinaryInput(data=..., media_type="application/pdf")。
  • 当工具已经知道确切的最终答案时,可用 final=True 结束回合,跳过额外的 LLM 往返。文档说明 final=True 的 ToolResult 必须只包含一个部分(TextInput 或 DataInput)。
  • 依赖注入:工具可以在不把这些值暴露给 LLM 的情况下获取执行期的值。文档给出 Context(通过 ctx.dependencies 访问)、Inject(如 Annotated[object, Inject("http_session")])、Variable(如 Annotated[str, Variable()])以及 Depends 等机制。文档说明 Inject 注解会从面向 LLM 的 schema 中被剥离。

使用方式

60 秒示例:

from ag2 import Agent, tool
from ag2.config import OpenAIConfig

@tool
def calculate_shipping_cost(destination: str, weight_kg: float) -> str:
    """Calculates shipping cost for a package to a destination."""
    return "$15.00"

agent = Agent(
    "shipping",
    prompt="Use tools when helpful.",
    config=OpenAIConfig(model="gpt-4o-mini"),
    tools=[calculate_shipping_cost],
)

普通函数自动包装:

def get_weather(location: str) -> str:
    """Returns the current weather for a given location."""
    return "Sunny, 22°C"

agent = Agent("weather", tools=[get_weather])

挂到已有 agent:

agent = Agent("calc")

@agent.tool
def multiply(a: int, b: int) -> int:
    """Multiplies two integers and returns the result."""
    return a * b

纯 Python 工具退出线程:

@tool(sync_to_thread=False)
def format_name(first: str, last: str) -> str:
    """Formats a full name."""
    return f"{last.upper()}, {first.capitalize()}"

异步工具:

import aiohttp

@tool
async def fetch(url: str) -> str:
    """Fetches a URL with aiohttp."""
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as r:
            return await r.text()

用 Pydantic Field 校验输入:

from typing import Annotated
from pydantic import Field
from ag2 import tool

@tool
def set_temperature(
    temp: Annotated[int, Field(description="Target temperature.", ge=10, le=30)],
    mode: Annotated[str, Field(description="Mode.", pattern="^(heat|cool|auto)$")],
) -> str:
    """Sets the thermostat."""
    return f"Set to {temp}°C in {mode} mode."

覆盖工具名与描述:

@tool(name="custom_math_tool", description="Performs advanced math.")
def math_op(a: int, b: int) -> int:
    return a + b

返回类型化 Input / ToolResult:

from ag2 import DataInput, ImageInput, TextInput, ToolResult, tool

@tool
def get_status(task_id: str) -> TextInput:
    return TextInput(f"Task {task_id} is in progress.")

@tool
def get_user_profile(user_id: str) -> DataInput:
    return DataInput({"id": user_id, "name": "Alice", "role": "admin"})

@tool
def fetch_chart(chart_id: str) -> ImageInput:
    return ImageInput(f"https://charts.example.com/{chart_id}.png")

@tool
def analyze_product(product_id: str) -> ToolResult:
    """Returns image + structured metadata in one tool call."""
    return ToolResult(
        ImageInput(f"https://cdn.example.com/products/{product_id}.jpg"),
        {"id": product_id, "name": "Widget Pro", "stock": 42},
    )

用 final=True 提前结束回合:

from ag2 import ToolResult, tool

@tool
def handoff_to_human(ticket_id: str) -> ToolResult:
    """Escalates and returns the final user-facing message verbatim."""
    return ToolResult(f"Ticket {ticket_id} was escalated.", final=True)

依赖注入:

from typing import Annotated
from ag2 import Context, Inject, Variable, tool

@tool
def query_db(query: str, ctx: Context) -> str:
    """Runs a SQL query."""
    db = ctx.dependencies["db"]
    return db.execute(query)

@tool
def fetch(url: str, http: Annotated[object, Inject("http_session")]) -> str:
    """Fetches with a shared HTTP session."""
    return http.get(url).text

@tool
def send(text: str, api_key: Annotated[str, Variable()]) -> str:
    """Sends a message via the configured channel."""
    ...

适用场景

Agent 需要基于 Python 代码执行实际操作时:调用外部 API、查询数据库、做计算、返回文本、结构化数据、图片或二进制内容;需要给 LLM 设定参数约束;需要在不向 LLM 暴露敏感值的前提下注入执行期依赖;或在工具已确定最终答案时减少一次 LLM 往返。

限制与常见陷阱

  • 。docstring 过于模糊:LLM 依靠它判断何时调用工具,文档建议用 "Calculates shipping cost based on destination and weight" 这类描述,而不是 "Shipping calc"。
  • 没有类型提示:没有类型提示框架无法生成有用的 JSON Schema,LLM 可能完全不会调用工具。
  • 阻塞事件循环:如果写同步(def)工具、包含重 CPU 或网络操作,又传 sync_to_thread=False,会阻塞循环。默认行为(在线程中运行)是安全的;只有在做廉价的纯 Python 工作时才退出。
  • 工具内部使用函数级 import:仓库约定不允许,import 应提升到模块顶部。
  • 工具体内嵌套函数定义:同样不允许,因为会在每次调用时重新创建函数。
  • 想要结构化数据却直接返回 dict:应包装成 DataInput(...),框架才会按结构化处理,而不是强制转成文本。
  • 忘记 final=True 只能包含一个部分:把多个 Input 与 final=True 组合会报错。
  • 文档还指向更深层的参考资料:references/dependency_injection.md(Context vs Inject vs Variable vs Depends、默认值、工厂、可变性、覆盖)、website/docs/user-guide/tools/tools.mdx(完整 @tool 参考,包括合成出的 JSON Schema)、website/docs/user-guide/depends.mdx(Depends 生命周期、基于 yield 的清理、缓存、测试覆盖)、website/docs/user-guide/multimodal/inputs.mdx(Input 工厂层级与提供方支持矩阵)、website/docs/user-guide/tools/toolkits.mdx(把相关工具打包成可复用 Toolkit)、website/docs/user-guide/tools/tool_middleware.mdx(围绕单个工具的异步钩子:校验、脱敏、审批,另见 ag2-hitl)。