首页
/ agno 实战指南:Agent 动态工具集(Callable Tools)、Tool Choice 与调用次数限制

agno 实战指南:Agent 动态工具集(Callable Tools)、Tool Choice 与调用次数限制

2026-09-05 12:37:29作者:凤尚柏Louis

本文基于 agno 官方 Cookbook 中的 02_agents/04_tools 示例目录,系统讲解 Agent 工具层进阶能力的六个实战主题:用可调用工厂函数按用户角色动态装配工具集、以 session_state 直接作为工厂参数并控制缓存、动态组建 Team 成员、typing.Literal 限定工具参数取值、tool_call_limit 限制单次运行的工具调用次数,以及 tool_choice 控制模型选哪个工具。读完本文,你可以掌握 agno 中工具从"静态列表"升级为"运行时按需装配"的完整机制,并结合源码定位各参数的实际生效位置。

环境与运行方式

示例目录 cookbook/02_agents/04_tools 覆盖六类场景,文件与主题一一对应:

文件 主题
01_callable_tools.py 用可调用工厂按用户角色变换工具集
02_session_state_tools.py session_state 为参数,关闭缓存让工厂每次新鲜执行
03_team_callable_members.py 运行时动态组装 Team 成员
04_tools_with_literal_type_param.py typing.Literal 定义预定义取值的工具参数
tool_call_limit.py 限制每次运行的工具调用次数
tool_choice.py 控制 Agent 选择哪个工具

README 的说明,运行前置条件为:

  1. 使用 direnv allow 加载环境变量(包括 OPENAI_API_KEY);
  2. 执行 ./scripts/demo_setup.sh 创建演示环境(对应仓库中的 scripts/demo_setup.sh),之后用 .venvs/demo/bin/python 运行 Cookbook;
  3. 部分示例需要可选的本地服务(如 pgvector)或特定提供商的 API Key。

运行命令统一为:

.venvs/demo/bin/python cookbook/02_agents/04_tools/<file>.py

目录内还附有 TEST_LOG.md,记录了各示例的实测结果(如 03 示例约 87s 完成、04 示例验证了 JSON schema 为 Literal 类型正确生成 enum 约束),可作为运行预期参考。

可调用工具工厂:按用户角色装配工具集

静态 tools=[...] 列表对所有用户一视同仁,而 agno 允许把一个函数直接传给 Agent(tools=...)。该函数在每次运行的开始阶段被调用,从而让工具集可以随用户或会话变化。

01_callable_tools.py 的完整思路是:先定义三个普通函数工具——search_web(通用)、search_internal_docs(仅 admin)、get_account_balance(admin/finance),然后定义工厂函数:

from agno.run import RunContext

def tools_for_user(run_context: RunContext):
    """Return different tools based on the user's role stored in session_state."""
    role = (run_context.session_state or {}).get("role", "viewer")

    base_tools = [search_web]
    if role == "admin":
        base_tools.append(search_internal_docs)
    if role in ("admin", "finance"):
        base_tools.append(get_account_balance)
    return base_tools

agent = Agent(
    model=OpenAIResponses(id="gpt-5-mini"),
    tools=tools_for_user,  # 注意:传的是函数,不是函数结果
)

示例通过两次 print_response 演示了缓存语义:第一次以 user_id="viewer_user"session_state={"role": "viewer"} 运行,模型只能看到 search_web;第二次换用 user_id="admin_user"session_state={"role": "admin"},由于 user_id 变化,工厂函数会再次被调用并以新上下文重新装配出全部工具。

从源码注释与文件头说明看,工厂的参数是按签名名称注入的,支持三种形参:agent(Agent 实例)、run_context(当前的 RunContext,含 user_idsession_id 等字段)、session_state(当前会话状态字典)。默认情况下,工厂结果会按 user_id(或 session_id)缓存,即同一用户后续运行不会重复调用工厂——这正是示例中 viewer/admin 两次运行各自独立解析工具的原因。

以 session_state 为直接参数,并用 cache_callables 关闭缓存

02_session_state_tools.py 演示了工厂函数的另一种更简洁的写法:把形参命名为 session_state,即可直接收到会话状态字典,无需解包 run_context

def get_tools(session_state: dict):
    """Pick tools based on the 'mode' key in session_state."""
    mode = session_state.get("mode", "greet")
    if mode == "greet":
        return [get_greeting]
    else:
        return [get_farewell]

agent = Agent(
    model=OpenAIResponses(id="gpt-5-mini"),
    tools=get_tools,
    cache_callables=False,  # 关键:工厂每次运行都重新执行
    instructions=["Use the available tool to respond."],
)

这里的核心参数是 cache_callables=False。示例头注释明确指出:默认缓存下,同一用户第二次运行会复用第一次的工具集;关闭缓存后,工厂每次运行都会以最新的 session_state 重新执行,从而捕捉到两次运行之间 mode"greet" 变为 "farewell" 的变化。示例连续两次 print_response(分别传入 {"mode": "greet"}{"mode": "farewell"})验证了这一点:第一次模型只能调用 get_greeting,第二次则切换到 get_farewell

从源码看,cache_callablesAgent 的字段,默认值为 True(见 agent.pycache_callables: bool = True 的定义),因此"每次新鲜执行"必须显式关闭缓存。此外,_storage.py 在序列化 Agent 配置时会把非默认的 cache_callables=False 一并写入配置,说明该设置可随 Agent 配置持久化。

动态组装 Team 成员

同样的"可调用工厂"机制也适用于 Team:把函数传给 Team(members=...),团队成员的组合在运行时依据 session_state 决定。03_team_callable_members.py 中,writerresearcher 两个 Agent 已预先创建,工厂函数只负责按需挑选:

def pick_members(session_state: dict):
    """Include the researcher only when needed."""
    needs_research = session_state.get("needs_research", False)
    if needs_research:
        return [researcher, writer]
    return [writer]

team = Team(
    name="Content Team",
    model=OpenAIResponses(id="gpt-5-mini"),
    members=pick_members,
    cache_callables=False,
    instructions=["Coordinate the team to complete the task."],
)

运行部分用 {"needs_research": False}{"needs_research": True} 各跑一次,分别得到"仅 Writer"和"Researcher + Writer"两种团队形态。这里同样设置了 cache_callables=False,确保两次运行都重新执行工厂、按最新的 session_state 组装成员。可以推断,Team 的可调用成员与 Agent 的可调用工具共享同一套签名注入与缓存机制,只是返回值从工具列表换成了成员 Agent 列表。

用 typing.Literal 限定工具参数取值

当工具参数只接受有限枚举值时,typing.Literal 是最简洁的约束方式。04_tools_with_literal_type_param.py 同时覆盖了两种工具形态:Toolkit 方法工具与独立函数工具。

from typing import Literal
from agno.tools import Toolkit

class FileOperationsToolkit(Toolkit):
    def __init__(self):
        super().__init__(name="file_operations", tools=[self.manage_file])

    def manage_file(
        self,
        filename: str,
        operation: Literal["create", "read", "update", "delete"] = "read",
        priority: Literal["low", "medium", "high"] = "medium",
    ) -> str:
        """Manage a file with the specified operation."""
        return f"Performed '{operation}' on '{filename}' with {priority} priority"

def standalone_tool(
    action: Literal["start", "stop", "restart"],
    service_name: str,
) -> str:
    """Control a service with the specified action."""
    return f"Service '{service_name}' has been {action}ed"

两个要点:

  1. 两种工具形态都支持:Toolkit 内的实例方法(manage_file)和模块级独立函数(standalone_tool)都可以使用 Literal 参数,Agent 通过 tools=[FileOperationsToolkit(), standalone_tool] 同时挂载两者;
  2. schema 自动生成枚举约束:根据 TEST_LOG.md 的实测记录,agno 生成的 JSON schema 会正确地为 Literal 类型产生 enum 约束,即模型在函数调用时只能从给定集合中选值,而非自由发挥字符串。参数上的默认值(如 operation="read")也会体现在生成的 schema 中。

这对"操作类型"、"优先级"、"服务动作"这类有限取值参数非常实用,既减少了模型幻觉出非法值的可能,也让工具的文档字符串(docstring)成为参数说明的主要载体。

tool_call_limit:限制单次运行的工具调用次数

如果希望约束 Agent 在单次运行中最多执行多少次工具调用,可以使用 tool_call_limit 参数。tool_call_limit.py 给出了最小示例:

from agno.tools.yfinance import YFinanceTools

agent = Agent(
    model=OpenAIResponses(id="gpt-5-mini"),
    tools=[YFinanceTools()],
    tool_call_limit=1,  # 每次运行只允许一次工具调用
)

agent.print_response(
    "Find me the current price of TSLA, then after that find me the latest news about Tesla.",
    stream=True,
)

示例的验证意图写在注释里:提示词要求"先查 TSLA 当前股价,再查特斯拉最新消息",理想情况下模型会发起两次工具调用;在 tool_call_limit=1 下,它只能完成第一个工具调用,第二个调用会被框架拒绝,从而演示限额生效的行为。

从源码链路看,tool_call_limitAgent 上的 Optional[int] 字段(默认 None 即不限制)。它在运行阶段被逐层透传到模型请求中:_response.py_run.py 多处均将 agent.tool_call_limit 传入底层执行上下文,意味着该限制作用于运行循环层面,而不是依赖模型自行克制。同时 _storage.pytool_call_limit 非空时会将其写入 Agent 配置实现持久化。

tool_choice:控制模型选择哪个工具

tool_choice 用于显式控制模型的选工具行为,tool_choice.py 用一个 get_weather 工具对比了三种取值:

no_tools_agent = Agent(
    name="No-Tools Agent",
    model=OpenAIResponses(id="gpt-5.2"),
    tools=[get_weather],
    tool_choice="none",  # 完全禁止工具调用
)

auto_tools_agent = Agent(
    name="Auto-Tools Agent",
    model=OpenAIResponses(id="gpt-5.2"),
    tools=[get_weather],
    tool_choice="auto",  # 模型自行决定是否调用工具
)

forced_tool_agent = Agent(
    name="Forced-Tool Agent",
    model=OpenAIResponses(id="gpt-5.2"),
    tools=[get_weather],
    tool_choice={"type": "function", "name": "get_weather"},  # 强制调用指定函数
)

三种取值的语义:

  • "none":本轮完全禁用工具。即使挂了 get_weather,模型也只能靠自身回答(示例中它面对"What is the weather in San Francisco today?"将给出不带工具数据的回答);
  • "auto":默认行为,由模型根据提示词自主判断是否调用工具;
  • {"type": "function", "name": "get_weather"}:强制模型调用指定函数,适用于"这一步必须走某个工具"的流程控制场景(如强制先查天气再组织回答)。

需要说明的是,TEST_LOG.md 记录了该示例在某次自动测试中因 API 调用耗时超过了 120s 超时(示例需连续运行三个 Agent),属于运行环境/网络因素,示例逻辑本身与上述参数语义一致。

关键参数小结

参数 所在对象 默认值 作用
tools Agent 传列表挂载固定工具;传函数则按签名注入 agent/run_context/session_state 动态装配
members Team 传函数可在运行时按 session_state 动态组装成员
cache_callables Agent / Team True(见 agent.py 控制可调用工厂结果是否按 user_id/session 缓存;False 时每次运行重新执行
tool_call_limit Agent None(不限制,见 agent.py 限制单次运行允许的工具调用次数
tool_choice Agent "none" / "auto" / {"type": "function", "name": ...} 三种控制模式

小结

02_agents/04_tools 这组示例展示的是 agno 工具层从"静态配置"到"运行时策略"的完整进阶路径:可调用工厂(toolsmembers 传函数)+ 签名注入(session_state 直传)+ cache_callables 缓存开关,构成按用户/会话动态定制能力面的机制;typing.Literal 从参数 schema 层面收敛模型取值;tool_call_limittool_choice 则分别在"调用次数"和"选哪个工具"两个维度上施加运行时控制。所有示例均可按上述环境步骤在仓库内直接复现,各参数在 Agent 源码 与运行链路(_run.py_response.py)中的透传位置也为进一步排查行为提供了代码级依据。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384