Beschikbaarheid van tools beheren

Note

De API voor de gefaseerde beschikbaarstelling van tools (FunctionInvocationContext.add_tools / remove_tools) is momenteel alleen beschikbaar voor Python.

Deze pagina bevat drie aanvullende technieken voor het beheren van welke hulpprogramma's een model kan aanroepen en in welke volgorde, allemaal binnen één agent worden uitgevoerd, zonder dat hiervoor een werkstroom nodig is:

  • Progressieve blootstelling aan hulpprogramma's : u kunt hulpprogramma's tijdens runtime toevoegen aan of verwijderen uit een hulpprogramma of functie-middleware, zodat het model alleen hulpprogramma's ziet die u kunt gebruiken.
  • Middleware-gating : gebruik functie-middleware om aanroepargumenten te valideren en corrigerende feedback te retourneren zonder de onderliggende functie uit te voeren.
  • Geforceerde eerste aanroep : gebruik tool_choice dit om te vereisen dat het model een specifiek hulpprogramma aanroept voordat andere.

Note

Paarsgewijze volgordebeperkingen, zoals 'altijd aanroepen get_record voor update_record' vereisen geen werkstroom. De technieken op deze pagina verwerken dat patroon binnen één uitvoering. Workflows zijn bedoeld voor echte meerstapsorkestratie over meerdere uitvoeringen heen of via parallelle takken.

Gefaseerde beschikbaarstelling van hulpmiddelen

Met progressieve toolblootstelling kunt u een uitvoering starten met een kleine set hulpprogramma's en hulpprogramma's toevoegen of verwijderen in reactie op eerdere resultaten van het hulpprogramma, allemaal binnen dezelfde uitvoering. Het model ziet alleen de bijgewerkte set voor de volgende iteratie van de functie-aanroepende lus; hulpprogramma-aanroepen die al zijn aangevraagd in de in-flight batch, worden nog steeds uitgevoerd voordat de wijziging van kracht wordt.

De API is experimenteel en leeft op FunctionInvocationContext:

Lid Description
ctx.tools De actuele, wijzigbare list tools voor de huidige uitvoering. None wanneer de functie wordt aangeroepen buiten een lus voor functieaanroepen.
ctx.add_tools(tools) Voeg een of meer hulpprogramma's toe. Aanroepbare objecten worden verpakt als FunctionTool. Het opnieuw toevoegen van hetzelfde object is een no-op; een ander object met een dubbele naam veroorzaakt ValueError. Alles of niets: als een van de hulpprogramma's in de batch een fout zou veroorzaken, wordt er niets toegevoegd.
ctx.remove_tools(tools) Verwijderen op naam, hulpmiddelobject of aanroepbaar. Namen die niet aanwezig zijn in de lijst, worden op de achtergrond genegeerd.

Beide helpers verzenden ExperimentalWarning de eerste keer dat ze worden aangeroepen in een proces (functie-id PROGRESSIVE_TOOLS). Het aanroepen van een van beide helpers buiten een lus voor functieaanroepen leidt tot RuntimeError.

Important

De lijst met hulpprogramma's wordt opnieuw ingesteld op de oorspronkelijke set voor elke nieuwe agent.run() aanroep, zodat alle poorten automatisch opnieuw worden ingeschakeld voor elke beurt.

Note

Progressieve blootstelling aan hulpprogramma's is alleen van toepassing op de standaardfunctieaanroepende lus. Het is niet beschikbaar voor CodeAct-providers (agent-framework-monty, agent-framework-hyperlight), waarbij het model één surface voor het uitvoeren van code ziet in plaats van afzonderlijke hulpprogrammaschema's. Het aanroepen van add_tools of remove_tools binnen een CodeAct-sandbox leidt tot RuntimeError. Als u de set hulpprogramma's voor een CodeAct-agent wilt wijzigen, gebruikt u de eigen add_tools / remove_tool / clear_tools methoden van de provider tussen uitvoeringen.

Laadprogrammapatroon

Registreer vooraf een kleine set 'loadertools' en laat het model naar behoefte aanvullende tools ophalen. Hierdoor blijft het eerste schema klein, waardoor de nauwkeurigheid van de selectie van hulpprogramma's wordt verbeterd en de kosten worden verlaagd.

import asyncio
import warnings
from typing import Annotated

from agent_framework import Agent, FunctionInvocationContext, tool
from agent_framework.openai import OpenAIChatClient
from pydantic import Field

warnings.filterwarnings("ignore", category=UserWarning)  # suppress ExperimentalWarning for brevity


@tool(approval_mode="never_require")
def factorial(n: Annotated[int, Field(description="A non-negative integer.")]) -> str:
    """Compute the factorial of n."""
    if n < 0:
        return "Error: n must be a non-negative integer."
    result = 1
    for value in range(2, n + 1):
        result *= value
    return f"{n}! = {result}"


@tool(approval_mode="never_require")
def fibonacci(n: Annotated[int, Field(description="The 0-based index in the Fibonacci sequence.")]) -> str:
    """Compute the n-th Fibonacci number."""
    if n < 0:
        return "Error: n must be a non-negative integer."
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return f"fib({n}) = {a}"


# The ctx parameter is injected by the framework and is NOT visible to the model.
@tool(approval_mode="never_require")
def load_math_tools(ctx: FunctionInvocationContext) -> str:
    """Load additional math tools (factorial, fibonacci) so they can be used."""
    ctx.add_tools([factorial, fibonacci])
    return "Loaded math tools: factorial, fibonacci. You can now call them."


async def main() -> None:
    agent = Agent(
        client=OpenAIChatClient(),
        name="MathAgent",
        instructions=(
            "You are a math assistant. "
            "If you need math capabilities that are not yet available, call load_math_tools first."
        ),
        tools=[load_math_tools],  # agent starts with only the loader
    )
    print(await agent.run("What is 5 factorial?"))


asyncio.run(main())

Het volledige uitvoerbare voorbeeld bevindt zich op python/samples/02-agents/tools/dynamic_tool_exposure.py.

Schakelpatroon

Registreer in eerste instantie alleen het leeshulpprogramma. De read-tool voegt de write-tool pas toe na een succesvolle fetch, zodat het model de write-tool niet kan aanroepen voordat de read-tool is uitgevoerd.

from agent_framework import Agent, FunctionInvocationContext, tool
from agent_framework.openai import OpenAIChatClient

_last_fetched_id: str | None = None


@tool(approval_mode="never_require")
def get_record(record_id: str, ctx: FunctionInvocationContext) -> str:
    """Fetch a record. Unlocks update_record for the same record."""
    global _last_fetched_id
    _last_fetched_id = record_id
    ctx.add_tools(update_record)  # gate: expose the write tool now
    return f"Record {record_id}: title='Example record', status='open'"


@tool(approval_mode="never_require")
def update_record(record_id: str, status: str) -> str:
    """Update the status of a record."""
    return f"Updated record {record_id} to status '{status}'."


agent = Agent(
    client=OpenAIChatClient(),
    name="RecordAgent",
    instructions="You help manage records. Fetch a record before updating it.",
    tools=[get_record],  # update_record is hidden until get_record runs
)

Omdat ctx.tools aan het begin van elke uitvoering wordt teruggezet naar [get_record], wordt de controle automatisch opnieuw geactiveerd voor elke gespreksbeurt.

Middleware-gating

Functiemiddleware kan de argumenten van een in behandeling zijnde hulpprogramma-aanroep inspecteren en deze weigeren voordat de onderliggende functie wordt uitgevoerd, door context.result in te stellen zonder call_next() aan te roepen. De tekenreeks die aan het model is toegewezen context.result , wordt geretourneerd als het resultaat van de functie, waardoor deze corrigerende feedback geeft.

Dit is handig voor controles op argumentniveau die informatie nodig hebben die niet beschikbaar is op schemadefinitietijd, bijvoorbeeld om te controleren of een update is gericht op hetzelfde item dat eerder in de uitvoering is opgehaald.

from collections.abc import Awaitable, Callable

from agent_framework import FunctionInvocationContext

_last_fetched_id: str | None = None


async def enforce_read_before_write(
    context: FunctionInvocationContext,
    call_next: Callable[[], Awaitable[None]],
) -> None:
    """Reject update_record calls that target a different record than the one fetched."""
    if context.function.name == "update_record":
        requested_id = context.arguments.get("record_id") if hasattr(context.arguments, "get") else None
        if requested_id != _last_fetched_id:
            # Set result without calling call_next — the function never executes.
            context.result = (
                f"Error: you must fetch record '{requested_id}' before updating it. "
                f"Last fetched record was '{_last_fetched_id}'."
            )
            return
    await call_next()

Voeg de middleware toe aan de agent:

agent = Agent(
    client=OpenAIChatClient(),
    name="RecordAgent",
    instructions="Fetch a record before updating it.",
    tools=[get_record, update_record],
    middleware=[enforce_read_before_write],
)

Zie Middleware definiëren en resultaatoverschrijvingen voor meer informatie over functiemiddleware.

Een aanroep van een hulpprogramma afdwingen met tool_choice

Als u wilt dat het model een specifiek hulpprogramma als eerste actie aanroept, geeft u door tool_choice met de modus "required" en een required_function_name. Het framework wordt automatisch opnieuw ingesteld tool_choiceNone na de eerste iteratie, zodat het model vrij is op volgende iteraties.

result = await agent.run(
    "Update record REC-42 to status 'in-progress'.",
    tool_choice={"mode": "required", "required_function_name": "get_record"},
)

Het tool_choice veld accepteert een ToolMode dict of de verkorte tekenreeksen "auto", "required"of "none":

from agent_framework import ToolMode

tool_choice: ToolMode = {"mode": "required", "required_function_name": "get_record"}

Semantiek en opmerkingen

Gedrag Het detail
Effect van de volgende iteratie add_tools / remove_tools mutaties zijn zichtbaar voor het model op de volgende lus iteratie. Toolaanroepen die al in de huidige batch zijn verzonden, worden toch voltooid.
In-flight batch Als het model verschillende hulpprogramma's in één batch aanvraagt, worden alle uitgevoerd voordat de bijgewerkte lijst met hulpprogramma's wordt teruggestuurd.
Dubbele namen Het opnieuw toevoegen van hetzelfde object is een no-op. Het toevoegen van een ander object met dezelfde naam als een bestaand hulpmiddel veroorzaakt ValueError. De volledige batch wordt gevalideerd voordat er iets wordt toegevoegd, dus een duplicaat halverwege een lijst laat de actuele lijst ongewijzigd.
Fout buiten de lus Het aanroepen van add_tools of remove_tools wanneer ctx.tools is None leidt tot RuntimeError. Dit gebeurt wanneer de functie rechtstreeks wordt aangeroepen (bijvoorbeeld via FunctionTool.invoke) in plaats van via de agentlus.
Experimentele status Beide hulpfuncties genereren ExperimentalWarning bij de eerste aanroep per proces. Onderdrukken met warnings.filterwarnings("ignore", category=UserWarning) indien gewenst.
Toepassingsgebied per uitvoering De lijst met live tools is een verse kopie die aan het begin van elke agent.run()-aanroep wordt gemaakt op basis van normalize_tools. De oorspronkelijke tools-container van de aanroeper wordt nooit gewijzigd.
CodeAct-uitsluiting Niet beschikbaar voor agent-framework-monty of agent-framework-hyperlight CodeAct-providers.

Volgende stappen