Instradare un turno dell'agente in corso (anteprima)

Instradamento consente di reindirizzare un agente ospitato a esecuzione prolungata mentre è ancora in esecuzione. Un nuovo input si mette in coda dietro al turno attivo e il gestore in esecuzione si conclude in modo cooperativo. Questo approccio evita di rifiutare il nuovo turno o correre due turni contemporaneamente.

Note

Gli agenti a esecuzione prolungata sono in anteprima. Le API e le versioni dei pacchetti sono soggette a modifiche.

Attivare l'instradamento

Protocollo di risposte

Imposta steerable_conversations=True su ResponsesServerOptions:

app = ResponsesAgentServerHost(
    options=ResponsesServerOptions(steerable_conversations=True),
)

Un secondo turno che arriva su code di conversazioni occupate e il gestore corrente annulla in modo cooperativo. Questo approccio evita la restituzione di 409 conversation_locked. Inviare il messaggio di follow-up come una nuova risposta con previous_response_id impostato sulla risposta in corso e lo stesso agent_session_id.

Invocazioni e primitive di task

Passare steerable=True a @multi_turn_task:

@multi_turn_task(name="conv", steerable=True)
async def conv(ctx: TaskContext[dict]) -> dict:
    return await llm(ctx.input)

# A .start() against an in-flight chain queues instead of raising.
r1 = asyncio.create_task(conv.start(task_id="c1", input={"msg": "Plan a trip to Rome"}))
await asyncio.sleep(0.05)
r2 = asyncio.create_task(conv.start(task_id="c1", input={"msg": "Actually, Paris"}))
# r1 resolves with turn 1's outcome; r2 resolves with turn 2's outcome.

Senza steerable=True, un oggetto simultaneo .start() in una catena in volo genera TaskConflictError.

Concludere il turno attivo

Quando un elemento viene accodato, il framework segnala il gestore in esecuzione tramite il segnale di annullamento cooperativo. Un handler instradabile dovrebbe verificarlo in corrispondenza di punti sicuri e terminare anticipatamente, in modo che il turno in coda possa subentrare:

@multi_turn_task(name="conv", steerable=True)
async def conv(ctx: TaskContext[dict]) -> dict:
    for step in plan:
        if ctx.cancel.is_set() and ctx.pending_input_count > 0:
            # A newer turn is waiting - stop at this boundary and let it run.
            return partial_result
        await do_step(step)
    return final_result

Osservabilità della guida nel contesto:

Campo Meaning
ctx.is_steered_turn True se questo turno è stato promosso dalla coda.
ctx.pending_input_count Numero di turni più recenti attualmente in coda.
ctx.cancel Segnale di annullamento cooperativo; impostato quando un turno in coda è in attesa.

Ordinare i turni con una precondizione

Quando un client deve tenere conto dell'ordine dei messaggi, passare if_last_input_id in modo che un chiamante non aggiornato non possa aggiungere messaggi dopo che un altro chiamante ha fatto avanzare la catena. È l'equivalente, per la coda di input, di un HTTP If-Match:

await conv.start(task_id="c1", input=new_msg, if_last_input_id=prev_input_id)

Se l'ultimo input accettato della catena non corrisponde più, la chiamata genera LastInputIdPreconditionFailed.

Gestire una coda completa

La coda di instradamento è delimitata. Quando è piena, un'operazione di accodamento solleva SteeringQueueFull; mostrare all'utente un chiaro messaggio di "attendere" invece di scartare l'input silenziosamente:

try:
    run = await conv.start(task_id="c1", input=new_msg)
except SteeringQueueFull:
    return {"status": "busy", "detail": "Too many pending turns; try again shortly."}

Le conversazioni instradabili sono sequenziali, non ramificate: l'input più recente può mettersi in coda dietro al turno attivo o interromperlo, ma la conversazione mantiene comunque un unico turno più recente e una cronologia ordinata.