Approbations des sous-agents

Référence API : Approbations

Un seul gestionnaire on_approval répond aux demandes d'approbation de tout l'arbre, celles du parent et de chaque sous-agent. Avec permission_mode="ask", un appel d'outil d'un sous-agent attend une approbation, comme celui du parent. Passez le gestionnaire au démarrage de la session. Il répond pendant qu'un appel qui fait avancer l'exécution, comme session.run, est en attente.

En mode "ask", le démarrage d'un sous-agent demande une approbation, car le sous-agent peut agir. Les autres outils de gestion des sous-agents, comme la messagerie, l'attente et l'interruption, ne demandent jamais d'approbation.

import asyncio

from pydantic import BaseModel

from mistralai import agents

PRICES = {"atlas": "1,200 EUR", "nova": "850 EUR"}


class PriceArgs(BaseModel):
    product: str


@agents.tool(name="lookup_price", description="Price of one product.", input_schema=PriceArgs)
async def lookup_price(args: PriceArgs) -> dict[str, str]:
    return {"price": PRICES.get(args.product.casefold(), "unknown")}


agent = agents.Agent(
    model="mistral-large-4",
    permission_mode="ask",
    instructions=(
        "You answer questions about our products. Start one researcher per product, "
        "wait for every result, then answer in one sentence."
    ),
    subagents=[
        agents.Subagent(
            name="researcher",
            description="Looks up one product and reports its price.",
            instructions="Call lookup_price once, then answer with the product name and its price.",
            tools=[lookup_price],
            builtins=agents.Builtins(subagents=False),
        )
    ],
)


def price_policy(
    ctx: agents.ApprovalCallbackContext, request: agents.ApprovalRequest
) -> agents.AllowApproval | agents.KeepCallbackOpen:
    allowed = ("agents.spawn", "client.lookup_price")
    return agents.AllowApproval() if request.tool.id in allowed else agents.KeepCallbackOpen()


async def main() -> None:
    session = await agent.start_session(on_approval=price_policy)  # answers the whole tree
    state = await session.run("What do the Atlas and the Nova cost?")
    if isinstance(state, agents.Completed):
        print(state.text)
    await session.cancel()


asyncio.run(main())
The Atlas costs 1,200 EUR and the Nova costs 850 EUR.

Le gestionnaire a autorisé les deux appels agents.spawn du parent et l'appel lookup_price de chaque sous-agent de recherche.

  • L'identifiant stable d'un outil Python est client.<name>. Prenez votre décision en fonction de request.tool.id, jamais de request.tool.name. Consultez Approbations pour comprendre pourquoi.
  • ctx.session.id et ctx.session.turn_id identifient la session à l'origine de la demande. Pour une demande d'approbation d'un sous-agent, ils désignent la session et le tour propres au sous-agent.
  • Un gestionnaire qui renvoie agents.KeepCallbackOpen() laisse l'approbation en attente.

Les Références sur les approbations détaillent ce qu'un gestionnaire reçoit et renvoie.

Lorsqu'aucun gestionnaire ne répond à une demande d'approbation du parent ou d'un sous-agent, session.run renvoie Blocked, et state.callbacks liste les approbations en attente dans tout l'arbre. Chacune identifie la session et le tour à l'origine de la demande. state.turn_id désigne le tour du parent. Un appel à turn.answer() sur ce tour répond aussi à une demande d'approbation d'un sous-agent, dans la session de ce sous-agent. Attendez ensuite de nouveau le résultat du tour du parent avec turn.result(after=state), comme dans Traiter les approbations pas à pas. Remplacez main() par :

async def main() -> None:
    session = await agent.start_session()
    state = await session.run("What do the Atlas and the Nova cost?")
    while isinstance(state, agents.Blocked):
        turn = session.turn(state.turn_id)
        for callback in state.callbacks:
            if isinstance(callback, agents.OpenApproval):
                print(callback.session_id == session.id, callback.request.tool.id)
                await turn.answer(callback.id, agents.AllowApproval())
        state = await turn.result(after=state)
    print(state.text)
    await session.cancel()
True agents.spawn
True agents.spawn
False client.lookup_price
False client.lookup_price
The Atlas costs 1,200 EUR and the Nova costs 850 EUR.

:::warning Un gestionnaire qui autorise tout approuve aussi les appels de sous-agents démarrés lors de tours précédents Le gestionnaire répond à toutes les demandes d'approbation de l'arbre, y compris celles des sous-agents qu'un tour précédent a laissés en cours d'exécution. Supposons que le tour 1 ait laissé un sous-agent de déploiement en attente d'approbation pour un appel deploy, et que le tour 2 demande « La CI est-elle au vert ? ». Un gestionnaire qui autorise tout au tour 2 approuve aussi le déploiement. Écrivez le gestionnaire pour tous les outils de l'arbre, pas seulement pour ceux qu'utilise la tâche en cours. :::

Problèmes courants

Problèmes courants

SymptômeCauseSolution
session.run renvoie Blocked avec un callback dont le session_id n'est pas celui de la racineUn sous-agent attend une approbation et aucun gestionnaire n'a répondu à sa demande.Répondez à la demande avec turn.answer(callback.id, ...) sur le tour de la racine, ou passez on_approval= à start_session.
UserWarning: This agent declares subagents, but its permission policy denies 'agents.spawn', ...Une politique default="deny" ne contient aucune règle pour agents.spawn, l'outil qui démarre un sous-agent. Chaque démarrage est refusé.Ajoutez agents.PermissionRule(tools=("agents.spawn",), decision="allow"), ou "ask", avant la première règle qui correspond à cet outil.