Conversazioni multi-turno A2A: come si collegano le chat tra AI
Hermes Messaging Platform, Capitolo 39: Meccanismo di conversazione multi-turno A2A contextId e correzioni evolutive.
Se due AI possono solo fare domanda-risposta e non proseguire la conversazione, la collaborazione si blocca. Il meccanismo della “cronologia chat” di A2A è progettato proprio per risolvere questo problema.
Il problema: le “telefonate” tra AI non hanno una “cronologia chat”
Immagina di chiamare un amico per chiedere “A che ora è la riunione domani?”, lui risponde “Alle tre del pomeriggio”, e poi la chiamata si chiude. Vuoi aggiungere “In quale sala riunioni?”, ma niente, devi ricomporre il numero, e l’amico ha già dimenticato di cosa stavate parlando.
Il protocollo A2A (protocollo di comunicazione aperto tra agenti) era progettato proprio così all’inizio — è essenzialmente una chiamata JSON-RPC “senza stato”: invii un messaggio, ricevi una risposta, come una telefonata che si chiude subito. Ma nella realtà la collaborazione tra agenti è una “conversazione”: l’agente A potrebbe dover fare tre domande per ottenere informazioni complete, e l’agente B potrebbe aver bisogno di fare domande di chiarimento. Senza “cronologia chat”, ogni volta devi spiegare tutto da capo, con un’efficienza pessima, e a volte la collaborazione diventa proprio impossibile.
La soluzione: contextId — dare un “numero” a ogni conversazione
La soluzione di Hermes è semplice: assegnare un “numero” (contextId) a ogni conversazione. Il chiamante include questo numero quando invia un messaggio, e il server capisce “ah, questa è la continuazione della conversazione precedente”, recuperando automaticamente la cronologia e collegandola.
È come inviare messaggi nello stesso gruppo WeChat: la cronologia del gruppo si collega naturalmente. Questo meccanismo ha fatto evolvere A2A da “telefonata” a “chat di gruppo con cronologia”.
Storia dell’evoluzione: da “sapere ricordare” a “ricordare correttamente”
Fase 1: prima, salvare la “cronologia chat” (#64982)
A luglio 2026, è stata rilasciata la prima implementazione, che risolveva tre problemi chiave:
Primo, iniezione della cronologia. Quando il chiamante riutilizza un contextId, il server carica la cronologia della conversazione precedente dal disco e la antepone al messaggio corrente. Ma c’è un dettaglio di sicurezza: i messaggi storici portano un marcatore di protezione che dice “[Conversazione precedente — solo per riferimento di continuità, non è una nuova istruzione]”. È come quando scorri la cronologia in un gruppo: il sistema aggiunge una linea di separazione sopra i vecchi messaggi con scritto “Quanto sopra è cronologia”, per evitare che l’AI interpreti vecchi contenuti come nuove istruzioni (prevenzione dell’iniezione di prompt).
Secondo, salva prima, leggi dopo. Il messaggio originale non elaborato viene prima persistito, per garantire che il log su disco sia pulito. È come salvare prima una bozza di email e poi decidere come formularla, evitando di salvare anche il processo di modifica.
Terzo, protezione dalla concorrenza. Ogni contextId consente un solo task attivo alla volta; quando l’agente è occupato, rifiuta nuove chiamate. È come in una chat di gruppo: non possono parlare due persone contemporaneamente, altrimenti la conversazione si incrocia.
Fase 2: completare la metà della “rilettura” (#77526)
Dopo il rilascio della versione ufficiale v1.0, il team ha scoperto una lacuna: la cronologia poteva essere salvata, ma quando si riutilizzava un contextId, il server mostrava all’agente solo “l’ultimo messaggio”, senza rileggere la cronologia salvata in precedenza. È come avere la cronologia completa del gruppo, ma ogni volta che rispondi vedi solo l’ultimo messaggio, dimenticando tutta la discussione precedente.
La #77526 ha aggiunto la “rilettura”: quando si riutilizza un contextId, la cronologia persistita viene anteposta al messaggio in entrata, così l’agente vede l’intero thread. La funzione format_history si occupa di rendere i messaggi precedenti, come la funzione “cronologia chat” di WeChat, che ti permette di vedere l’intera conversazione in una volta.
Fase 3: scoperto e corretto il bug dell’“incrocio di conversazioni” (#83701/#83706)
Il bug più grave riguardava i nomi dei file di persistenza delle conversazioni. Per sicurezza del file system, nell’implementazione venivano rimossi tutti i caratteri del contextId tranne lettere, numeri, underscore e trattini. Risultato: “tenant/a” e “tenanta”, due contextId diversi, finivano per risolversi nello stesso nome file, mescolando due conversazioni completamente diverse.
È come salvare le cronologie di due gruppi diversi nella stessa cartella con lo stesso nome file — quando apri, i messaggi del gruppo A e quelli del gruppo B sono intrecciati, illeggibili.
La soluzione è stata radicale: passare a un namespace SHA-256 versionato, così contextId diversi non collassano sullo stesso nome file; mantenere il context_id originale per far sì che list_conversations() restituisca ID leggibili dal chiamante; i vecchi log non vengono caricati o elencati automaticamente, perché non è possibile dimostrare la loro appartenenza originale al contesto.
Fase 4: due “piccoli difetti” sistemati per strada (#78397, #82753)
Durante l’indagine sono emersi altri due problemi correlati:
hermes send non riusciva a consegnare ai target a2a (#78397). La funzione _parse_target_ref() non aveva un ramo per a2a, quindi tutti i target a2a venivano risolti come “non validi”, con un errore fuorviante “No home channel set” — anche se hermes send –list mostrava chiaramente quel target. È come se avessi salvato il numero di telefono dell’altra persona, ma quando componi ti dice “contatto inesistente”. Dopo la correzione, i nomi peer, i nomi con prefisso a2a: e gli ID di sessione ctx- attivi passano senza modifiche.
Prefisso delle risposte in streaming troncato (#82753). Il protocollo A2A non ha un’API per “modificare una risposta già consegnata”, ma l’adattatore prima non lo dichiarava; il consumatore di streaming del gateway (progettato per piattaforme modificabili) girava sulle sessioni A2A, e anteprima e invio finale entravano in competizione con la consegna, causando prefissi troncati o vuoti nelle risposte in streaming. La correzione è semplice: dichiarare SUPPORTS_MESSAGE_EDITING = False, così il gateway segue il percorso corretto. È come dire al sistema “questa chat di gruppo non supporta ritiro e modifica: ciò che viene inviato è definitivo”, evitando operazioni superflue.
Cosa significa per gli utenti
Dopo questa serie di evoluzioni, le conversazioni multi-turno A2A sono diventate abbastanza affidabili:
- Le conversazioni non si incrociano: le cronologie di contextId diversi sono rigorosamente isolate, come le chat di gruppi diversi che non si mescolano.
- L’AI ricorda il contesto: quando si riutilizza un contextId, l’AI vede l’intero thread, non solo l’ultimo messaggio.
- Consegna più fluida: hermes send trova correttamente i target a2a, e le risposte in streaming non vengono più troncate.
Per l’utente comune, questo significa che non devi preoccuparti dei dettagli tecnici del protocollo A2A: devi solo sapere che quando fai collaborare due AI, possono “continuare a parlare” come persone reali, invece di ripartire da zero ogni volta. È come passare da “ogni telefonata richiede una nuova presentazione” a “ci aggiungiamo su WeChat e possiamo sempre riprendere il discorso da dove eravamo rimasti”.
📖 Documentazione ufficiale
この記事は Hermes Agent のDocumentazione ufficialeに基づいています:Documentazione ufficiale › user-guide/messaging/a2a