Steuern der Verfügbarkeit von Tools

Note

Die API zur progressiven Tool-Exposition (FunctionInvocationContext.add_tools / remove_tools) ist derzeit nur für Python verfügbar.

Diese Seite behandelt drei sich ergänzende Techniken zur Steuerung dessen, welche Tools ein Modell aufrufen kann und in welcher Reihenfolge, alles innerhalb eines einzelnen Agentenlaufs, ohne dass dafür ein Workflow erforderlich ist:

  • Schrittweise Tool-Freigabe — fügen Sie zur Laufzeit innerhalb eines Tools oder einer Funktions-Middleware Tools hinzu oder entfernen Sie sie, sodass das Modell nur die Tools sieht, die es zu verwenden bereit ist.
  • Middleware-Gating – Verwenden Sie Funktions-Middleware, um Aufrufargumente zu überprüfen und Korrekturfeedback zurückzugeben, ohne die zugrunde liegende Funktion auszuführen.
  • Erzwungener erster Aufruf – Verwenden Sie die Verwendung tool_choice , um das Modell aufzufordern, ein bestimmtes Tool vor allen anderen aufzurufen.

Note

Paarweise Sortiereinschränkungen wie "Immer aufrufen get_record vor update_record" erfordern keinen Workflow. Die Verfahren auf dieser Seite verarbeiten dieses Muster innerhalb eines einzelnen Durchlaufs. Workflows sind für eine echte mehrstufige Orchestrierung über mehrere Ausführungen hinweg oder über parallele Zweige hinweg gedacht.

Schrittweise Anzeige von Tools

Mit progressiver Toolexposition können Sie eine Ausführung mit einer kleinen Gruppe von Tools starten und Tools als Reaktion auf frühere Toolergebnisse hinzufügen oder entfernen– alles innerhalb derselben Ausführung. Das Modell sieht die aktualisierte Menge erst bei der nächsten Iteration der Schleife für Funktionsaufrufe; bereits im laufenden Batch angeforderte Tool-Aufrufe werden noch ausgeführt, bevor die Änderung wirksam wird.

Die API ist experimentell und lebt auf FunctionInvocationContext:

Mitglied Beschreibung
ctx.tools Die Live-, änderbare list Tools für die aktuelle Ausführung. None wenn die Funktion außerhalb einer Funktionsaufrufschleife aufgerufen wird.
ctx.add_tools(tools) Fügen Sie ein oder mehrere Tools hinzu. Callables werden in FunctionTool eingeschlossen. Das erneute Hinzufügen desselben Objekts bleibt ohne Wirkung; ein anderes Objekt mit demselben Namen löst ValueError aus. Ganz oder gar nicht: Wenn ein Tool im Batch einen Fehler auslösen würde, werden keine Tools hinzugefügt.
ctx.remove_tools(tools) Entfernen nach Name, Toolobjekt oder Callable. Namen, die in der Liste nicht vorhanden sind, werden ignoriert.

Beide Helfer geben ExperimentalWarning das erste Mal aus, wenn sie in einem Prozess aufgerufen werden (Feature-ID PROGRESSIVE_TOOLS). Der Aufruf einer der beiden Hilfsfunktionen außerhalb einer Funktionsaufrufschleife löst RuntimeError aus.

Important

Die Toolliste wird bei jedem neuen agent.run() Aufruf auf die ursprüngliche Zusammenstellung zurückgesetzt, sodass alle Schranken bei jedem Schritt automatisch erneut aktiviert werden.

Note

Die progressive Belichtung des Tools gilt nur für die standardmäßige Funktionsaufrufschleife. Es ist nicht für CodeAct-Anbieter (agent-framework-monty, agent-framework-hyperlight) verfügbar, bei denen das Modell eine einzige Oberfläche zur Codeausführung statt der Schemata einzelner Tools sieht. Der Aufruf von add_tools oder remove_tools innerhalb einer CodeAct-Sandbox löst RuntimeError aus. Um den Werkzeugsatz für einen CodeAct-Agenten zu ändern, verwenden Sie zwischen den Ausführungen die anbietereigenen add_tools / remove_tool / clear_tools Methoden.

Ladeprogramm-Toolmuster

Registrieren Sie vorab eine kleine Auswahl an „Loader“-Tools und lassen Sie das Modell bei Bedarf weitere Tools hinzuziehen. Dadurch bleibt das anfängliche Schema klein, wodurch die Genauigkeit der Werkzeugauswahl verbessert und Kosten reduziert werden.

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

Das vollständige ausführbare Beispiel finden Sie unter python/samples/02-agents/tools/dynamic_tool_exposure.py.

Schaltmuster

Registrieren Sie zunächst nur das Lesetool. Das Lesetool fügt das Schreibtool nach einem erfolgreichen Abruf hinzu, sodass das Modell das Schreibtool nicht aufrufen kann, bevor das Lesetool ausgeführt wurde.

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
)

Da ctx.tools zu Beginn jedes Durchlaufs auf [get_record] zurückgesetzt wird, wird das Gate für jede Gesprächsrunde automatisch erneut scharfgeschaltet.

Middleware-Zugriffssteuerung

Funktions-Middleware kann die Argumente eines ausstehenden Toolaufrufs prüfen und ihn ablehnen, bevor die zugrunde liegende Funktion ausgeführt wird, indem context.result gesetzt wird, ohne call_next() aufzurufen. Die dem Modell zugewiesene context.result Zeichenfolge wird als Ergebnis der Funktion zurückgegeben, sodass sie Korrekturfeedback erhält.

Dies ist nützlich für Prüfungen auf Argumentebene, die Informationen benötigen, die zum Zeitpunkt der Schemadefinition nicht verfügbar sind, beispielsweise um zu überprüfen, ob sich eine Aktualisierung auf dasselbe Element bezieht, das zuvor während der Ausführung abgerufen wurde.

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

Fügen Sie die Middleware dem Agent hinzu:

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

Weitere Informationen zur Middleware von Funktionen finden Sie unter Definieren von Middleware und Ergebnisüberschreibungen.

Erzwingen eines Toolaufrufs mit tool_choice

Um das Modell dazu zu veranlassen, als ersten Schritt ein bestimmtes Tool aufzurufen, übergeben Sie tool_choice mit dem Modus "required" und einem required_function_name. Das Framework setzt tool_choice nach der ersten Iteration automatisch auf None zurück, sodass das Modell in den folgenden Iterationen frei ist.

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

Das tool_choice Feld akzeptiert ein ToolMode Diktieren oder die Abkürzungszeichenfolgen "auto", oder "required""none":

from agent_framework import ToolMode

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

Semantik und Vorbehalte

Behavior Einzelheit
Nächste Iterationseffekt add_tools / remove_tools Änderungen sind für das Modell in der nächsten Iteration der Schleife sichtbar. Toolaufrufe, die bereits im aktuellen Batch verteilt wurden, sind unabhängig davon abgeschlossen.
In-Flight-Batch Wenn das Modell mehrere Tools in einem Batch anfordert, werden alle ausgeführt, bevor die aktualisierte Toolliste zurückgesendet wird.
Doppelte Namen Das erneute Hinzufügen des genauen Objekts ist eine no-op. Das Hinzufügen eines anderen Objekts, dessen Name mit einem vorhandenen Werkzeug übereinstimmt, löst ValueError aus. Der gesamte Batch wird validiert, bevor überhaupt etwas hinzugefügt wird, sodass ein Duplikat mitten in einer Liste die aktive Liste unverändert lässt.
Fehler außerhalb der Schleife Der Aufruf von add_tools oder remove_tools, wenn ctx.tools is NoneRuntimeError auslöst. Dies geschieht, wenn die Funktion direkt aufgerufen wird (z. B. über FunctionTool.invoke) statt über die Agentschleife.
Experimenteller Status Beide Hilfsprogramme geben beim ersten Aufruf pro Prozess aus ExperimentalWarning . Bei Bedarf mit warnings.filterwarnings("ignore", category=UserWarning) unterdrücken.
Bereich pro Ausführung Die Liste der Live-Tools ist eine neue Kopie, die zu Beginn jedes agent.run()-Aufrufs aus normalize_tools erstellt wird. Der ursprüngliche tools-Container des Aufrufers wird niemals verändert.
CodeAct-Ausschluss Nicht verfügbar bei agent-framework-monty oder agent-framework-hyperlight CodeAct-Anbietern.

Nächste Schritte