Prévisualiser les réponses avec les deltas d'événements
Affichez le texte de réponse de l'agent sous forme d'aperçu en direct pendant que le modèle est encore en train de le générer.
Par défaut, le texte de réponse de l'agent parvient au flux d'événements de la session sous forme d'événements agent.message mis en mémoire tampon. Chacun n'est émis qu'une fois terminée la requête au modèle qui l'a produit. Les « event deltas » (deltas d'événements) vous permettent d'afficher ce texte de manière incrémentale, sous forme d'aperçu en direct, pendant que le modèle est encore en train de le générer.
Les aperçus sont une aide à l'affichage fournie au mieux, et le agent.message mis en mémoire tampon constitue toujours l'enregistrement faisant autorité. Un client qui ignore les aperçus reçoit tout de même un flux complet et correct.
Activer les aperçus
Les aperçus sont activés sur demande, par connexion de flux. Ajoutez le paramètre de requête event_deltas[] au flux que vous lisez, et répétez-le une fois pour chaque type d'événement que vous souhaitez prévisualiser. Les valeurs acceptées sont agent.message et agent.thinking. Toute autre valeur renvoie une erreur 400, tout comme une requête comportant plus de 100 valeurs.
Les deux points de terminaison de flux acceptent ce paramètre :
- Flux au niveau de la session :
GET /v1/sessions/{session_id}/events/stream - Flux d'un fil de session :
GET /v1/sessions/{session_id}/threads/{thread_id}/stream
Les aperçus d'un sous-agent apparaissent sur le flux du fil propre à ce sous-agent.
[] est un motif de glob du shell, donc mettez l'URL entre guillemets chaque fois que vous construisez la requête dans un shell. Les exemples encodent les crochets en pourcentage sous la forme %5B%5D, ce qui fonctionne également.
Événements d'aperçu
Lorsqu'un événement faisant l'objet d'un aperçu commence, le flux émet un event_start portant le type et l'id de l'événement à venir :
{
"type": "event_start",
"event": {
"type": "agent.message",
"id": "sevt_01abc..."
}
}Pour agent.message, le début est suivi d'événements event_delta portant du texte incrémental. Chaque delta désigne l'événement qu'il prolonge dans event_id et le bloc de contenu qu'il prolonge dans delta.index :
{
"type": "event_delta",
"event_id": "sevt_01abc...",
"delta": {
"type": "content_delta",
"index": 0,
"content": {
"type": "text",
"text": "Here is the summary"
}
}
}Pour agent.thinking, seul l'event_start est émis, pour signaler qu'un bloc de réflexion a commencé. Aucun événement event_delta ne suit. L'événement agent.thinking mis en mémoire tampon qui conclut l'aperçu est un signal de progression et ne contient aucun contenu de réflexion.
Contrairement aux événements persistés, event_start et event_delta n'ont ni id ni processed_at propres. Le seul identifiant qu'ils portent est l'id de l'événement qu'ils prévisualisent. Leurs chaînes de type font également exception à la convention de nommage {domain}.{action} des événements persistés.
Accumuler et réconcilier
Chaque SDK qui prend en charge les deltas d'événements inclut un utilitaire d'accumulation qui gère pour vous le suivi des index. Le modèle manuel présenté dans cette section fonctionne dans tous les langages lorsque vous avez besoin d'un suivi personnalisé. Appliquez-le aux types d'événements générés.
Dans le modèle manuel, conservez le texte d'aperçu dans une table temporaire indexée par (event_id, index), et traitez l'événement mis en mémoire tampon comme l'enregistrement de référence. Réconciliez les deux pour chaque requête au modèle.
Un tour s'ouvre avec un unique événement session.status_running. Lors d'un tour qui se termine normalement, chaque requête au modèle produit ensuite ces événements, dans l'ordre :
span.model_request_startevent_start- Les événements
event_delta - Le
agent.messagemis en mémoire tampon span.model_request_end(dans l'onglet Événements de span)
Sur le réseau, voici la partie prévisualisée de cette séquence, entrelacée avec les autres événements mis en mémoire tampon de la connexion :
event_start {"event": {"type": "agent.message", "id": "sevt_01abc..."}}
event_delta {"event_id": "sevt_01abc...", "delta": {"type": "content_delta", "index": 0, "content": {"type": "text", "text": "..."}}}
...
agent.message {"id": "sevt_01abc...", "content": [...]}La ligne event_delta se répète une fois par fragment de texte. Traitez chaque événement à son arrivée :
- Sur
event_start, notez l'idannoncé. Les identifiants concordent toujours :event_start.event.id, chaqueevent_delta.event_idet l'idduagent.messagemis en mémoire tampon ont la même valeur. - Sur chaque
event_delta, ajoutezdelta.content.textà l'entrée située à(event_id, delta.index)et affichez le texte en cours. Le premier delta pour unindexcrée cette entrée. - Lorsque le
agent.messagemis en mémoire tampon arrive, associez-le parid, supprimez l'aperçu accumulé et affichez plutôt le contenu du message. - Sur
span.model_request_end, fermez tout aperçu qui n'a pas été réconcilié par son événement mis en mémoire tampon. Plus aucun delta n'arrivera pour celui-ci. Si le tour rencontre une erreur ou est interrompu, l'événement mis en mémoire tampon peut ne jamais arriver, maisspan.model_request_endarrive tout de même.
Ce modèle repose sur deux garanties :
- La concaténation des deltas d'un aperçu dans leur ordre d'arrivée, indexés par
(event_id, index), donne un préfixe decontent[index].textdans l'événement mis en mémoire tampon. Il ne s'agit pas nécessairement du texte complet, car des deltas peuvent être abandonnés en cas de charge. - Une connexion émet au plus un
event_startparevent_id, et l'événement mis en mémoire tampon est la dernière chose que cette connexion délivre pour cetid.
Utilitaires d'accumulation des SDK
L'utilitaire de chaque SDK gère le suivi des index. Les utilitaires Go, Java, Ruby et C# indexent également l'aperçu en cours d'accumulation par l'id de l'événement. Avec les utilitaires Python, TypeScript et PHP, gérez vous-même cette table et intégrez chaque delta dans l'entrée correspondant à son id.
Les exemples suivants activent les aperçus agent.message et les réconcilient avec l'événement mis en mémoire tampon :
# Instantanés d'aperçu, indexés par id d'événement. accumulate_managed_agents_event agrège chaque
# event_start / event_delta en un instantané agent.message ; l'agent.message
# mis en mémoire tampon le remplace.
previews: dict[str, BetaManagedAgentsAgentMessageEvent] = {}
# Activez les aperçus agent.message sur cette connexion
with client.beta.sessions.events.stream(
session.id, event_deltas=["agent.message"]
) as stream:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [{"type": "text", "text": "Describe the repo in one sentence."}],
},
],
)
for event in stream:
match event.type:
case "event_start":
snapshot = accumulate_managed_agents_event(None, event)
if snapshot is not None:
previews[event.event.id] = snapshot
print(f"event_start {event.event.type} {event.event.id}")
case "event_delta":
preview = accumulate_managed_agents_event(previews.get(event.event_id), event)
if preview is not None:
previews[event.event_id] = preview
text = "".join(block.text for block in preview.content)
print(f"event_delta preview: {text!r}")
case "agent.message":
# L'événement mis en mémoire tampon fait foi : il remplace et ferme l'aperçu
preview = accumulate_managed_agents_event(previews.pop(event.id, None), event)
text = "".join(block.text for block in preview.content)
print(f"agent.message {event.id} {text!r}")
case "span.model_request_end":
# Plus aucun delta n'arrivera. Fermez tout aperçu dont
# l'événement mis en mémoire tampon n'est jamais arrivé.
for event_id in previews:
print(f"span.model_request_end closing preview for {event_id}")
previews.clear()
case "session.status_idle":
breakPrévisualiser les événements des fils de session
Dans une session multiagent, chaque fil de session possède son propre flux d'événements. Il accepte le même paramètre event_deltas[] avec les mêmes valeurs.
Une connexion ne prévisualise que le fil qu'elle lit. Le flux au niveau de la session prévisualise le fil principal, et les aperçus d'un fil enfant n'y sont jamais republiés. Pour suivre le texte d'un sous-agent pendant que le modèle le génère, ouvrez le flux du fil de ce sous-agent.
Le chemin d'un flux de fil se termine par /threads/{thread_id}/stream. /events/stream n'existe qu'au niveau de la session, il n'y a donc pas de point de terminaison /threads/{thread_id}/events/stream.
event_start et event_delta ont la même forme sur un flux de fil que sur le flux au niveau de la session, et le modèle accumuler et réconcilier s'applique tel quel. Exécutez une instance d'accumulateur par connexion de flux.
# Listez les threads de la session et choisissez un enfant : les threads enfants portent un
# parent_thread_id non nul, et le parent_thread_id du thread principal est null.
child_thread = next(
thread
for thread in client.beta.sessions.threads.list(session.id)
if thread.parent_thread_id is not None
)
# Le stream du thread enfant prend le même paramètre event_deltas que le
# stream de session.
with client.beta.sessions.threads.events.stream(
child_thread.id,
session_id=session.id,
event_deltas=["agent.message"],
) as stream:
for event in stream:
match event.type:
case "event_delta":
print(event.delta.content.text, end="")
case "agent.message":
# L'événement mis en mémoire tampon fait autorité ; affichez son contenu
print()
for block in event.content:
if block.type == "text":
print(block.text, end="")
print()
case "session.thread_status_idle":
breakLa boucle de lecture se termine sur session.thread_status_idle, l'événement émis lorsque le tour du fil de session se termine et que le fil devient inactif.
Limitations
- Au mieux : En cas de charge, le serveur peut abandonner des deltas pour un événement. Dans ce cas, vous recevez un préfixe contigu du texte, puis plus aucun delta pour cet événement. Le
agent.messagemis en mémoire tampon arrive tout de même complet. Ne considérez jamais un aperçu accumulé comme définitif. - Pas de relecture à la reconnexion : Les deltas ne sont délivrés qu'à la connexion qui les a activés, tant qu'elle est ouverte. Cela s'applique aussi bien au flux au niveau de la session qu'à chaque flux de fil de session. Une connexion ouverte après le début d'une requête au modèle ne reçoit aucun delta pour cet événement en cours. Il n'existe aucun moyen de redemander les deltas manqués.
- Un seul fil, texte uniquement : Les aperçus couvrent le texte de l'assistant sur le fil que lit la connexion. L'utilisation d'outils, les résultats d'outils et les résultats MCP ne sont jamais prévisualisés.
- Jamais persistés :
event_startetevent_deltan'existent que sur le flux en direct. Ils n'apparaissent pas dans l'historique des événements de la session (GET /v1/sessions/{session_id}/events) ni dans l'historique des événements d'aucun fil de session.
Dépanner les aperçus
| Ce que vous voyez | Ce que cela signifie |
|---|---|
Un flux avec des événements mis en mémoire tampon mais sans event_start ni event_delta | La connexion que vous lisez n'a pas activé les aperçus, ou le tour n'a jamais concerné le fil que vous diffusez. event_deltas[] s'applique par connexion, et non par session. Pour savoir quel fil s'est exécuté, listez les fils de la session (GET /v1/sessions/{session_id}/threads). |
| Un flux qui se coupe pendant un aperçu | Les deltas ne sont pas rejoués. Suivez la procédure de reconnexion : rouvrez le flux et listez l'historique des événements. L'historique inclut tous les événements mis en mémoire tampon émis pendant votre déconnexion, y compris le agent.message qu'attendait votre aperçu. |
| Une erreur 404 sur l'URL du flux | Le chemin ou un ID est incorrect, ou la requête ne comporte aucun en-tête bêta managed-agents. Les points de terminaison des fils sont soumis à l'en-tête bêta ; sans cet en-tête, ils n'existent donc pas. |
Une erreur 400 mentionnant event_deltas | Seuls agent.message et agent.thinking sont acceptés. |
Étapes suivantes
Envoyez des événements, diffusez les réponses en streaming, et interrompez ou redirigez votre session en cours d'exécution.
Coordonnez plusieurs agents au sein d'une même session.
Was this page helpful?