Controllo della disponibilità degli strumenti

Annotazioni

L'API di esposizione progressiva dello strumento (FunctionInvocationContext.add_tools / remove_tools) è attualmente solo Python.

Questa pagina illustra tre tecniche complementari per controllare quali strumenti un modello può chiamare e in quale ordine, tutti all'interno di un'esecuzione di un singolo agente, senza richiedere un flusso di lavoro:

  • Esposizione progressiva degli strumenti : aggiungere o rimuovere strumenti in fase di esecuzione dall'interno di uno strumento o un middleware di funzione, quindi il modello vede solo gli strumenti che è pronto per l'uso.
  • Filtro middleware — utilizzare un middleware di funzione per convalidare gli argomenti della chiamata e restituire un feedback correttivo senza eseguire la funzione sottostante.
  • Prima chiamata forzata : usare tool_choice per richiedere al modello di chiamare uno strumento specifico prima di qualsiasi altro.

Annotazioni

I vincoli di ordinamento pairwise, ad esempio "chiamare get_record sempre prima update_record" non richiedono un flusso di lavoro. Le tecniche su questa pagina gestiscono tale schema in un'unica esecuzione. I flussi di lavoro servono per una vera orchestrazione in più fasi tra esecuzioni diverse o rami paralleli.

Esposizione progressiva degli strumenti

L'esposizione progressiva degli strumenti consente di avviare un'esecuzione con un piccolo set di strumenti e aggiungere o rimuovere strumenti in risposta ai risultati degli strumenti precedenti, tutti all'interno della stessa esecuzione. Il modello vede solo il set aggiornato nella successiva iterazione del ciclo di chiamata di funzione; le chiamate agli strumenti già richieste nel batch in anteprima vengono comunque eseguite prima che la modifica venga applicata.

L'API è sperimentale e vive su FunctionInvocationContext:

Membro Descrizione
ctx.tools L'insieme attivo e modificabile list degli strumenti per l'esecuzione corrente. None quando la funzione viene richiamata all'esterno di un ciclo di chiamata di funzione.
ctx.add_tools(tools) Aggiungere uno o più strumenti. I callable vengono incapsulati come FunctionTool. Aggiungere nuovamente lo stesso oggetto è un no-op; un oggetto diverso con un nome duplicato provoca ValueError. Tutto o niente: se uno strumento nel batch genera un aumento, non viene aggiunto alcuno.
ctx.remove_tools(tools) Rimuovi per nome, oggetto tool o oggetto invocabile. I nomi non presenti nell'elenco vengono ignorati automaticamente.

Entrambi gli helper emettono ExperimentalWarning la prima volta che vengono chiamati in un processo (ID della funzionalità PROGRESSIVE_TOOLS). La chiamata di uno degli helper all'esterno di un ciclo di chiamata di funzione genera RuntimeError.

Importante

L'elenco degli strumenti viene reimpostato sul set originale in ogni nuova agent.run() chiamata, quindi tutti i cancelli riaccendono automaticamente per ogni turno.

Annotazioni

L'esposizione progressiva degli strumenti si applica solo al ciclo di chiamata a funzioni standard. Non è disponibile per i provider CodeAct (agent-framework-monty, agent-framework-hyperlight), in cui il modello vede una singola superficie di esecuzione del codice anziché singoli schemi degli strumenti. La chiamata add_tools o remove_tools dall'interno di una sandbox CodeAct genera RuntimeError. Per modificare il set di strumenti per un agente CodeAct, usare i metodi del provider tra add_tools / remove_tool / clear_tools le esecuzioni.

Modello dello strumento di caricamento

Registrare preventivamente un piccolo insieme di strumenti "loader" e consentire al modello di caricare strumenti aggiuntivi su richiesta. In questo modo lo schema iniziale viene ridotto, migliorando l'accuratezza della selezione degli strumenti e riducendo i costi.

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())

L'esempio eseguibile completo si trova in python/samples/02-agents/tools/dynamic_tool_exposure.py.

Modello di controllo

Registrare inizialmente solo lo strumento di lettura. Lo strumento di lettura aggiunge lo strumento di scrittura dopo un recupero riuscito, quindi il modello non può chiamare lo strumento di scrittura prima dell'esecuzione dello strumento di lettura.

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
)

Poiché ctx.tools si reimposta su [get_record] all'inizio di ogni esecuzione, il gate si riarma automaticamente a ogni turno di conversazione.

Controllo del middleware

Il middleware della funzione può esaminare gli argomenti di una chiamata allo strumento in sospeso e rifiutarlo prima dell'esecuzione della funzione sottostante impostando context.result senza chiamare call_next(). La stringa assegnata a context.result viene restituita al modello come risultato della funzione, fornendo un feedback correttivo.

Ciò è utile per i controlli a livello di argomento che richiedono informazioni non disponibili in fase di definizione dello schema, ad esempio verificando che un aggiornamento sia destinato allo stesso elemento recuperato in precedenza nell'esecuzione.

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()

Aggiungere il middleware all'agente:

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

Per ulteriori informazioni sul middleware di funzione, vedere Definizione del middleware e Override dei risultati.

Forzare una chiamata di strumento con tool_choice

Per fare in modo che il modello richiami uno strumento specifico come prima azione, passa tool_choice con la modalità "required" e required_function_name. Il framework viene reimpostato tool_choiceNone automaticamente dopo la prima iterazione in modo che il modello sia libero nelle iterazioni successive.

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

Il tool_choice campo accetta un ToolMode dict o le stringhe abbreviate "auto", "required"o "none":

from agent_framework import ToolMode

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

Semantica e avvertenze

Behavior Dettagli
Effetto di iterazione successiva add_tools / remove_tools le mutazioni sono visibili al modello nell'iterazione del ciclo successivo. Le chiamate agli strumenti già inoltrate nel batch corrente vengono comunque completate.
Batch in corso Se il modello richiede più strumenti in un unico batch, tutti vengono eseguiti prima dell'invio dell'elenco degli strumenti aggiornato.
Nomi duplicati L'aggiunta dello stesso oggetto esatto è un no-op. L'aggiunta di un oggetto diverso il cui nome corrisponde a uno strumento esistente genera ValueError. L'intero batch viene convalidato prima di qualsiasi aggiunta, quindi un duplicato a metà di un elenco lascia invariato l'elenco attivo.
Errore fuori ciclo Chiamata add_tools o remove_tools quando ctx.tools is None genera RuntimeError. Ciò si verifica quando la funzione viene richiamata direttamente (ad esempio tramite FunctionTool.invoke) anziché tramite il ciclo dell'agente.
Stato sperimentale Entrambi gli helper emettono ExperimentalWarning alla prima chiamata in ciascun processo. Sopprimi con warnings.filterwarnings("ignore", category=UserWarning) , se desiderato.
Ambito di esecuzione L'elenco degli strumenti live è una nuova copia creata da normalize_tools all'inizio di ogni chiamata a agent.run(). Il contenitore originale tools del chiamante non viene mai modificato.
Esclusione CodeAct Non disponibile per i provider agent-framework-monty o agent-framework-hyperlight CodeAct.

Passaggi successivi