Molti agenti, una sola architettura: cosa succede quando due di loro cambiano lo stesso sistema
Tre agenti, tre pull request, tre opinioni ragionevoli su dove debba stare la logica di retry.
Uno mette il retry nel client HTTP. Uno avvolge l'handler. Uno aggiunge una queue e la svuota. Leggi una qualsiasi delle tre da sola e la approveresti. Leggile nello stesso pomeriggio e ti accorgi che il sistema ora ritenta in tre punti, con tre policy di backoff diverse, e questo non l'ha deciso nessuno.
Questa è la forma del problema non appena più di un agente lavora su una codebase nello stesso momento. Ogni agente è corretto localmente. L'incoerenza è globale, e diventa visibile solo a chi fa la review per ultimo.
Perché il file di regole non arbitra questa cosa
La risposta standard è un file di regole: CLAUDE.md, AGENTS.md, .cursor/rules. Abbiamo scritto del perché quei file invecchiano male, e l'invecchiamento qui è il problema minore. Quello grosso è che un file di regole non può arbitrare.
È prosa. Due agenti a cui dai lo stesso paragrafo ne produrranno due letture diverse, entrambe difendibili, e non c'è nessun punto in cui quelle letture si incontrano. Vive per repository, quindi una regola su un confine di servizio sta in un repo mentre il servizio dall'altra parte del confine sta in un altro. E non ha stato: non può sapere che un altro agente ha proposto qualcosa quaranta minuti fa, perché è un file, e i file non sanno le cose.
Quello che serve per arbitrare non è prosa migliore. È una cosa condivisa che entrambi gli agenti leggono e scrivono, che può contenere una decisione, e che può accorgersi di un disaccordo.
Cosa ti dà un modello e non ti dà un documento
Un modello di architettura sono elementi e relazioni che puoi interrogare. Sistemi, container, component, gli archi tra di loro, e attaccate a quegli archi le cose che rendono un design un design: la decisione che ha reso deliberato un confine, l'owner da avvisare, il contratto su cui un consumer fa affidamento.
Ne derivano tre cose, e ognuna è un meccanismo, non un'intenzione.
Ogni agente può leggere gli stessi byte. L'action generate-context scrive un archyl.txt a partire dal modello, in markdown di default, con commit automatico nel repository come opzione. Nove agenti che leggono un file generato sono una situazione diversa da nove agenti che parafrasano ciascuno un documento in prosa. Non è furbo. È semplicemente condiviso.
Il disaccordo si può intercettare in ingresso. L'action conformance-check esegue le regole di architettura sui file modificati da una pull request, annota le violazioni inline, e fa fallire il check alla severity che scegli. fail-on accetta error, warning o none. Se "i retry stanno nel client" è una regola e non una frase, i due agenti che li hanno messi altrove lo scoprono in CI invece che in review.
Una decisione ha un posto dove stare. Gli ADR si attaccano agli elementi C4 che vincolano. Il motivo per cui la queue esiste sta sulla queue, non in un thread Slack di marzo che nessun agente ha mai visto.
La parte che avevamo sbagliato
Qui questo ha smesso di essere un post di blog su una bella idea.
Gli agenti non cambiano il modello direttamente. Aprono un Change Request: una proposta, revisionata e mergiata da una persona. Quando un Change Request viene creato, archyl registra la versione del modello su cui è stato costruito. Quando viene mergiato, la versione si incrementa. Questo è esattamente il meccanismo che vorresti per questo problema.
Non avevamo mai collegato le due cose.
La versione base veniva scritta alla creazione e riletta da nessuna parte. Il che voleva dire che questa sequenza funzionava, in silenzio e fino in fondo:
- L'agente A e l'agente B leggono entrambi il modello. Entrambi vedono la versione 7.
- A apre un Change Request. B apre un Change Request. Entrambi sono basati sulla versione 7.
- Il Change Request di A viene mergiato. Il modello è ora alla versione 8.
- Il Change Request di B viene mergiato. Era scritto su un modello che non esiste più.
Nessun avviso, nessun conflitto, nessuna nota nella history. Il secondo insieme di modifiche atterra sopra il primo, e se si contraddicono, la contraddizione è ora l'architettura documentata. Questo è un merge a cui è stata tolta la rilevazione dei conflitti, ed è esattamente il fallimento che tutta la storia dei "molti agenti" dovrebbe prevenire.
Quindi l'abbiamo sistemato. Mergiare un Change Request la cui versione base non corrisponde più al progetto ora fallisce con un 409 Conflict e un messaggio che nomina entrambe le versioni:
architecture request is based on version 7 but the model is now at version 9;
rebase the request and merge again
Quello che rende la cosa sicura, però, non è il confronto. Due merge che arrivano nello stesso istante passerebbero entrambi un confronto e proseguirebbero entrambi. Quello che chiude quel buco è rendere condizionale l'incremento di versione stesso: il merge fa avanzare il modello solo se il modello è ancora sulla versione su cui il Change Request è stato costruito. Se si è già spostato, il merge non trova niente da far avanzare, tutto quanto fa rollback, e non viene applicata nemmeno una modifica. Il confronto a monte esiste solo perché il messaggio di errore possa dirti quanto sei indietro.
409 invece di 400 conta più di quanto sembri. Un agente che ritenta su un 400 va in loop per sempre, perché una richiesta malformata resta malformata. 409 dice il contrario: quello che hai mandato andava bene e ha smesso di essere applicabile. Prendi il modello attuale e riprova.
Cosa questo continua a non fare
Quattro limiti, tutti verificabili.
Nessun agente mergia niente. Non esiste un tool MCP che mergi un Change Request. Gli agenti propongono; una persona revisiona e mergia. È un confine deliberato e non abbiamo in programma di rimuoverlo, ma vuol dire che il loop non è completamente automatico e non dovresti progettare come se lo fosse.
La rilevazione dei conflitti è grossolana. La versione è per progetto, non per elemento. Due agenti che toccano angoli davvero scollegati dello stesso progetto andranno comunque in collisione sulla versione. Quella è la direzione sicura in cui sbagliare, ed è sbagliata.
Il recupero del contesto è lessicale. find_relevant_context assegna un punteggio agli elementi in base alla sovrapposizione di parole su nomi, descrizioni, tag e path. Non c'è nessun embedding e nessuna espansione dei sinonimi, quindi un task che parla di "checkout" non farà emergere un component chiamato OrderProcessor. Il vantaggio è reale (deterministico, nessun costo in token, nessun codice mandato da nessuna parte), ma è matching, non comprensione.
Le regole non si scrivono da sole. Tutto quanto sopra presuppone che qualcuno abbia espresso "i retry stanno nel client" come regola di conformità. Un set di regole vuoto non intercetta niente, per quanti agenti tu stia facendo girare.
Cosa farne questa settimana
Non devi comprare niente per scoprire a che punto sei.
Prendi l'ultima settimana in cui il tuo team ha mergiato più di una pull request scritta da un agente. Leggile insieme invece che in sequenza. Chiediti se due di esse hanno preso la stessa decisione in modo diverso, poi chiediti cosa, nel tuo setup attuale, te lo avrebbe detto.
Se la risposta è "se n'è accorto chi revisionava", funziona fino al giorno in cui chi revisiona ne sta leggendo nove.
I Change Request, le regole di conformità e il modello C4 fanno parte di archyl. Le GitHub Actions e le agent skills sono open source. Letture correlate: perché i tuoi agenti hanno un file di regole e non un modello, come funzionano i Change Request, e come il modello viene tenuto onesto.