Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota:
La API de exposición progresiva de herramientas (FunctionInvocationContext.add_tools / remove_tools) es actualmente solo Python.
En esta página se tratan tres técnicas complementarias para controlar qué herramientas puede llamar un modelo y en qué orden, todo dentro de una sola ejecución de agente, sin necesidad de un flujo de trabajo:
- Exposición progresiva de herramientas — añada o quite herramientas en tiempo de ejecución desde dentro de una herramienta o del middleware de funciones, de modo que el modelo solo vea las herramientas que puede usar.
- Control mediante middleware — use un middleware de función para validar los argumentos de la llamada y devolver información correctiva sin ejecutar la función subyacente.
-
Primera llamada forzada : use
tool_choicepara requerir que el modelo llame a una herramienta específica antes de cualquier otra.
Nota:
Las restricciones de ordenación en pares, como "llamar get_record siempre antes update_record" no requieren un flujo de trabajo. Las técnicas de esta página controlan ese patrón dentro de una sola ejecución. Los flujos de trabajo se utilizan para una verdadera orquestación de varios pasos entre ejecuciones o ramas paralelas.
Exposición progresiva de herramientas
La exposición progresiva de herramientas le permite iniciar una ejecución con un pequeño conjunto de herramientas y agregar o quitar herramientas en respuesta a los resultados anteriores de la herramienta, todo dentro de la misma ejecución. El modelo solo ve el conjunto actualizado en la siguiente iteración del bucle de llamada a funciones; las llamadas a herramientas ya solicitadas en el lote en ejecución todavía se ejecutan antes de que el cambio surta efecto.
La API es experimental y reside en FunctionInvocationContext:
| Miembro | Descripción |
|---|---|
ctx.tools |
La lista activa y mutable list de herramientas para la ejecución en curso.
None cuando se invoca la función fuera de un bucle de llamada de función. |
ctx.add_tools(tools) |
Agregue una o varias herramientas. Los invocables se encapsulan como FunctionTool. Volver a agregar el mismo objeto es un no-op; un objeto diferente con un nombre duplicado genera ValueError. Todo o nada: si alguna herramienta del lote provocara un error, no se añade ninguna. |
ctx.remove_tools(tools) |
Quite por nombre, objeto de herramienta o invocable. Los nombres que no están presentes en la lista se omiten silenciosamente. |
Ambas funciones auxiliares emiten ExperimentalWarning la primera vez que se invocan en un proceso (identificador de función PROGRESSIVE_TOOLS). Llamar a cualquiera de los asistentes fuera de un bucle de llamada de función genera RuntimeError.
Importante
La lista de herramientas se restablece al conjunto original con cada nueva llamada a agent.run(), por lo que todas las compuertas se rearman automáticamente en cada turno.
Nota:
La exposición progresiva de herramientas solo se aplica al bucle estándar de llamada a funciones. No está disponible para los proveedores de CodeAct (agent-framework-monty, agent-framework-hyperlight), donde el modelo ve una sola superficie de ejecución de código en lugar de esquemas de herramientas individuales. Llamar a add_tools o remove_tools desde dentro de un espacio aislado de CodeAct genera RuntimeError. Para cambiar el conjunto de herramientas de un agente codeAct, use los propios add_tools / remove_tool / clear_tools métodos del proveedor entre ejecuciones.
Patrón cargador-herramienta
Registre un pequeño conjunto de herramientas de "cargador" por adelantado y deje que el modelo extraiga herramientas adicionales a petición. Esto mantiene el esquema inicial pequeño, lo que mejora la precisión de la selección de herramientas y reduce el costo.
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())
El ejemplo ejecutable completo está en python/samples/02-agents/tools/dynamic_tool_exposure.py.
Patrón de activación
Registre inicialmente solo la herramienta de lectura. La herramienta de lectura agrega la herramienta de escritura después de una captura correcta, por lo que el modelo no puede llamar a la herramienta de escritura antes de que se haya ejecutado la herramienta de lectura.
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
)
Dado que ctx.tools se restablece a [get_record] al inicio de cada ejecución, la compuerta se vuelve a armar automáticamente en cada turno de conversación.
Control de acceso del middleware
El middleware de función puede inspeccionar los argumentos de una llamada a herramienta pendiente y rechazarlo antes de que se ejecute la función subyacente estableciendo context.result sin llamar a call_next(). La cadena asignada a context.result se devuelve al modelo como resultado de la función, lo que le proporciona comentarios correctivos.
Esto resulta útil para las comprobaciones de nivel de argumento que necesitan información que no está disponible en el momento de la definición del esquema, por ejemplo, comprobando que una actualización tiene como destino el mismo elemento que se capturó anteriormente en la ejecución.
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()
Agregue el middleware al agente:
agent = Agent(
client=OpenAIChatClient(),
name="RecordAgent",
instructions="Fetch a record before updating it.",
tools=[get_record, update_record],
middleware=[enforce_read_before_write],
)
Para obtener más información sobre el middleware de funciones, consulte Definición del middleware y Anulaciones de resultados.
Forzar una llamada a una herramienta con tool_choice
Para hacer que el modelo llame a una herramienta específica como primera acción, pase tool_choice con el modo "required" y un required_function_name. El framework restablece automáticamente tool_choice a None después de la primera iteración, por lo que el modelo queda libre en las iteraciones posteriores.
result = await agent.run(
"Update record REC-42 to status 'in-progress'.",
tool_choice={"mode": "required", "required_function_name": "get_record"},
)
El tool_choice campo acepta un ToolMode dict, o las cadenas abrevias "auto", "required"o "none":
from agent_framework import ToolMode
tool_choice: ToolMode = {"mode": "required", "required_function_name": "get_record"}
Semántica y advertencias
| Comportamiento | Detalle |
|---|---|
| Efecto de iteración siguiente |
add_tools
/
remove_tools Las mutaciones son visibles para el modelo en la siguiente iteración de bucle. Las llamadas de herramienta ya enviadas en el lote actual se completan independientemente de. |
| Lote en proceso | Si el modelo solicita varias herramientas en un lote, todas se ejecutan antes de que se devuelva la lista de herramientas actualizada. |
| Nombres duplicados | Volver a agregar el mismo objeto exactamente es un no-op. Agregar un objeto diferente cuyo nombre coincide con una herramienta existente genera ValueError. Todo el lote se valida antes de cualquier adición, por lo que un duplicado a mitad de una lista deja la lista activa sin cambios. |
| Error de bucle externo | Llamar a add_tools o remove_tools cuando ctx.tools is None genera RuntimeError. Esto sucede cuando la función se invoca directamente (por ejemplo, mediante FunctionTool.invoke) en lugar de hacerlo a través del bucle del agente. |
| Estado experimental | Ambas funciones auxiliares emiten ExperimentalWarning en la primera llamada de cada proceso. Suprima con warnings.filterwarnings("ignore", category=UserWarning) si lo desea. |
| Ámbito de cada ejecución | La lista activa de herramientas es una copia nueva creada a partir de normalize_tools al inicio de cada llamada a agent.run(). El contenedor original tools del código que realiza la llamada nunca se modifica. |
| Exclusión de CodeAct | No disponible para los proveedores agent-framework-monty o agent-framework-hyperlight de CodeAct. |