Orkas Orkas
Pagina iniziale Blog Architettura
Architettura

Riscrivere le fondamenta dell'agente: una ristrutturazione completa di Orkas

Come Orkas ha ricostruito le fondamenta del suo agente nel corso dei rilasci della serie 1.0: un ambiente di esecuzione nello stesso processo, rotazione dei fornitori, orchestrazione dinamica delle chat di gruppo, hosting aperto, memoria e autoevoluzione.

Quando un prodotto basato su agenti IA matura, la parte più costosa non sono le funzionalità: sono le fondamenta. Questo articolo ripercorre la ristrutturazione dalle fondamenta che Orkas ha realizzato nel corso delle versioni 1.0: una revisione completa dell'invocazione dei modelli, del ciclo degli agenti, dell'orchestrazione multiagente e dell'ecosistema di strumenti, con i compromessi alla base di ogni decisione.

In breve A cosa è servita la riscrittura Il risultato è l'app che puoi installare oggi: open source con licenza MIT, con priorità al locale e, se vuoi, utilizzabile con le tue chiavi dei fornitori.
Scarica Orkas — gratis

Perché intervenire sulle fondamenta

Orkas è un ambiente di lavoro desktop per agenti IA con priorità al locale: tutto il lavoro degli agenti viene eseguito all'interno di un processo sul computer dell'utente, i dati risiedono in locale e la sincronizzazione cloud end-to-end avviene su richiesta. Nelle prime versioni le funzionalità si sono accumulate rapidamente: una biblioteca di abilità, una base di conoscenza, connettori, un sistema multiagente in stile chat di gruppo. Ma più andavamo avanti, più diventava chiaro che il vero collo di bottiglia non era una singola funzionalità, bensì tre aspetti "fondamentali".

  1. Se il livello di invocazione dei modelli segue la vecchia strada della conversazione, resta vincolato a una serie di presupposti sbagliati. Invocare un modello di grandi dimensioni come se fosse una "chat con una domanda e una risposta" introduce implicitamente una serie di impostazioni predefinite sensate per una chat, ma non per un agente: un limite fisso ai token di output, chiamate agli strumenti in serie, timeout nascosti, un unico fornitore fissato nel codice. Un agente è un flusso di lunga durata che esegue decine di turni consecutivi, si avvicina regolarmente al limite della finestra di contesto, deve leggere file in parallelo e può essere interrotto dall'utente in qualsiasi momento: ciascuna di quelle impostazioni predefinite finirà per creare problemi in produzione. Peggio ancora, le capacità più preziose di un agente desktop — operazioni granulari sui file, ricerca locale, esecuzione di una shell, esecuzione parallela con più esecutori, risoluzione di attività di lunga durata — sono proprio quelle escluse da questo insieme di presupposti.

  2. L'orchestrazione era una "pianificazione statica". La prima versione era un motore basato su piani/DAG: prima si chiedeva al modello di scomporre l'attività in un grafo di pianificazione, poi un esecutore assegnava il lavoro seguendo il grafo. Sembra ordinato, ma la realtà di un agente è altamente dinamica: la lettura di un file rivela la necessità di cambiare direzione e il risultato di una sottoattività determina a chi affidare il passo successivo. Congelare le decisioni in un grafo generato in anticipo significa dover correggere nell'esecutore ogni caso in cui "il piano non riesce a stare al passo con la realtà".

  3. L'ecosistema era un catalogo chiuso. Le abilità potevano provenire solo dal marketplace ufficiale, i connettori costituivano un catalogo fissato nel codice e gli strumenti di agenti esterni già presenti sul computer dell'utente erano una completa scatola nera per Orkas. Un utente che volesse integrare un progetto di terze parti, il proprio server MCP o consentire a un agente già presente sul computer di richiamare le abilità e la base di conoscenza di Orkas non poteva farlo: dal punto di vista architetturale, nulla di tutto questo era possibile.

L'idea alla base di questa ristrutturazione è semplice: riprendere nelle nostre mani le fondamenta dell'agente. In concreto, si articola in quattro direttrici intrecciate: un ambiente di esecuzione sviluppato internamente e ospitato nel processo, un livello dei modelli indipendente dal fornitore, un'orchestrazione dinamica tramite chat di gruppo e il passaggio da un catalogo chiuso a una piattaforma ospitante aperta. Vediamole una per una.

1. Portare sul desktop l'intero insieme di capacità di un agente di programmazione

Il desktop è l'ambiente naturale dell'agente: qui ci sono un vero file system, una vera shell e una vera catena di strumenti locali. Un assistente che può solo conversare spreca questo ambiente; ciò che sfrutta davvero il vantaggio del desktop è un insieme completo di capacità da agente di programmazione: lettura e scrittura dei file fino al singolo intervallo di caratteri, ricerca tra più file, esecuzione di bash e strumenti di sistema, avvio di più esecutori in parallelo e capacità di risolvere problemi di lunga durata, continuando per decine di turni finché un'attività complessa non è davvero conclusa.

Il risultato principale della ristrutturazione serve proprio a portare questo insieme di capacità in modo nativo nel processo di Orkas: un ambiente di esecuzione per agenti autonomo, caricabile dinamicamente e interno al processo, chiamato core-agent nel codice. Non è l'ennesimo involucro per una chat: è un motore per agenti controllato direttamente da Orkas.

La decisione architetturale chiave è stata dividerlo in due livelli:

  • Livello del motore (pacchetto autonomo): la pura meccanica dell'agente: il ciclo di chiamata agli strumenti, gli eventi in streaming, la compattazione del contesto, la classificazione degli errori e i nuovi tentativi, l'astrazione del fornitore, la sandbox, la scansione delle abilità, la memoria, l'autoevoluzione. Non sa nulla della logica applicativa di Orkas: non legge le directory dei dati applicativi, non comprende il formato dei file delle conversazioni e non tocca mai l'IPC.
  • Livello dell'adattatore (nel processo principale): collega il motore a Orkas: persistenza delle sessioni, rotazione dei fornitori, autorizzazioni degli strumenti, registro delle abilità, connettori, base di conoscenza e vari strumenti di generazione. Traduce gli eventi nativi del motore nei formati degli eventi di Orkas, così il livello applicativo vede sempre e soltanto un'interfaccia stabile.

Questo confine tra motore e adattatore è alla radice di tutta la flessibilità che segue. Il motore può essere testato e fatto evolvere in modo indipendente; il livello dell'adattatore può assorbire in sicurezza la complessità specifica di Orkas — rotazione, periodi di attesa, sandbox, autorizzazioni — senza contaminare il motore. La revisione architetturale del team lo ha riassunto in una frase: questa complessità è giustificata: non fondete i livelli.

In cosa consiste davvero questo insieme di capacità

Avere il controllo dell'ambiente di esecuzione non serve a mettersi in mostra, ma a permettere all'agente di "sporcarsi davvero le mani" sul desktop. Le capacità si dividono grosso modo in quattro gruppi:

  • Operazioni granulari sui file e ricerca locale. read_file supporta la lettura per intervalli di caratteri ed estrae automaticamente il testo dai documenti PDF / Office; edit_file esegue sostituzioni precise del tipo "vecchia stringa → nuova stringa" e richiede una lettura prima di qualsiasi scrittura; write_file salva il contenuto prodotto e ne tiene traccia; stat_file rileva le dimensioni; search_files individua i file per nome o espressione glob, mentre grep_files cerca nei contenuti di più file. Questo gruppo permette all'agente di "esplorare il codice e modificare i file" in un vero ambiente di lavoro, come farebbe un ingegnere, anziché limitarsi a ingerire e produrre blocchi interi.
  • Bash e strumenti di sistema. Un esecutore di shell in sandbox, con una modalità di esecuzione in background — le attività lunghe si separano dal turno corrente e i log vengono scritti in un file — e controlli graduati in base al rischio per le operazioni pericolose. Gran parte del potenziale di un agente desktop deriva proprio dalla possibilità di comandare direttamente la catena di strumenti di sistema.
  • Più esecutori in parallelo. All'interno di un singolo turno, gli strumenti indipendenti di sola lettura vengono eseguiti contemporaneamente; a livello di attività, Commander può anche distribuire sottoattività indipendenti a più esecutori in parallelo (vedi sezione 3). Parallelizzare dove è sicuro farlo è la chiave per ridurre la durata effettiva delle "attività di lunga durata" a tempi accettabili.
  • Ragionamento e risoluzione di attività di lunga durata. Un ciclo capace di eseguire decine di turni consecutivi, gestire il proprio contesto, recuperare dagli errori e non restare mai bloccato a girare a vuoto: è questa la linea di demarcazione tra "portare a termine un lavoro complesso" e "rispondere a una domanda".

Come è stato reso pronto per la produzione sul piano ingegneristico

"Scrivere il proprio ciclo" sembra un modo per cercarsi problemi e comporta effettivamente costi di manutenzione. In cambio, però, offre un controllo granulare sull'intero ciclo di vita dell'agente. Questo controllo non è astratto: è un insieme di miglioramenti concreti, ciascuno legato alla capacità di una delle funzioni precedenti di "reggere" in produzione:

  • Finestra di contesto reale + compattazione solo all'80%. Il motore legge la finestra di contesto reale di ciascun modello, compresi quelli con finestre da un milione di token, e attiva la compattazione solo quando l'utilizzo raggiunge l'80%, anziché partire prudenzialmente dal 60% e scartare il 40% del contesto utile. Esiste anche una protezione contro la "compattazione senza beneficio": se la parte finale conservata riempie già la finestra — per esempio, perché contiene il risultato della lettura di un file molto grande — la compattazione non può liberare spazio; quindi il motore si limita a registrare un avviso e la salta, senza mai sprecare una chiamata di riepilogo. La possibilità per un'attività di lunga durata di "ricordare ciò che è successo prima" dipende interamente da questo.

  • Strumenti consecutivi di sola lettura in parallelo. Quando il modello avvia, in un unico turno, lettura di file, ricerca di file e ricerca sul web — più strumenti indipendenti di sola lettura — il motore raggruppa quelli consecutivi che possono essere parallelizzati e li esegue contemporaneamente; gli strumenti di scrittura costituiscono barriere naturali e mantengono l'ordine dichiarato. Le chiamate agli strumenti e i relativi risultati vengono registrati rigorosamente nell'ordine dichiarato, quindi la concorrenza non viola mai il protocollo. Gli strumenti di sola lettura più comuni passano in un colpo solo dall'esecuzione seriale a quella parallela, e il tempo effettivo dell'intero gruppo si riduce sensibilmente.

  • Lettura prima della scrittura + controllo ottimistico della concorrenza. Prima di modificare un file è necessario leggerlo; il motore registra uno stato di riferimento del file letto e, al momento della modifica, verifica che non sia cambiato. Quando più esecutori paralleli modificano contemporaneamente lo stesso file, quello che arriva dopo riceve un chiaro errore di "stato non aggiornato", anziché sovrascrivere silenziosamente le modifiche altrui. Con più esecutori che operano in parallelo nello stesso ambiente di lavoro, questa protezione è indispensabile.

  • Interruzioni durante l'esecuzione integrate immediatamente. Quando un utente aggiunge un'altra riga mentre l'agente è a metà del lavoro, al punto di passaggio del ciclo degli strumenti il motore integra il messaggio in coda nell'input del turno corrente, anziché aspettare di avviarlo come turno separato. Così "correggere la rotta durante l'esecuzione" diventa un'interazione naturale.

  • Rilevamento dei cicli ripetitivi. Quando la stessa chiamata a uno strumento si ripete consecutivamente, il motore prima invia un richiamo, alla 3ª occorrenza, e poi impone l'arresto, alla 5ª; qualsiasi firma diversa azzera il conteggio, quindi variazioni legittime come paginazione e polling non provocano falsi interventi. Quando il modello si blocca, non consuma più token silenziosamente.

  • Rimozione del limite rigido all'output del turno principale. L'output del turno principale non è più vincolato a un limite molto basso, quindi i report lunghi e le modifiche estese non vengono troncati silenziosamente; le chiamate ausiliarie, come compattazione e riflessione, continuano invece a usare prudenzialmente un limite ridotto.

C'è un dettaglio molto "locale" che merita di essere citato: la stima dei token per testi misti in cinese e inglese. Una stima generica sottostima di due o tre volte una conversazione interamente in cinese; il motore tratta diversamente i caratteri cinesi e inglesi in base alla classe di caratteri, ed è questo a rendere affidabile la soglia di compattazione. È il genere di dettaglio a cui un SDK generico non penserà al posto tuo.

Nel loro insieme, questi miglioramenti rispondono alla domanda "perché non usare semplicemente un SDK già pronto": perché le capacità più forti di un agente desktop risiedono proprio nel livello che un SDK non espone; per renderle pronte per la produzione, il ciclo deve essere sotto il proprio controllo.

2. Mantenere il modello sempre online: il wrapper dei fornitori a più livelli

L'obiettivo del livello dei modelli si riassume in una frase: qualunque problema si verifichi con una determinata chiave, un determinato fornitore o una determinata rete, questo turno della conversazione dell'utente deve sopravvivere, per quanto possibile. A questo scopo, il livello dell'adattatore sovrappone alcuni wrapper all'astrazione del fornitore del motore: rotazione, periodo di attesa, registrazione e adattamento esterno.

La scelta progettuale più importante è che il componente di rotazione si trova sotto quello di esecuzione. Il motore scrive il messaggio dell'utente nella sessione persistente prima ancora di chiamare un fornitore; gestire nuovi tentativi e rotazione a livello del motore richiederebbe di reinviare il messaggio dell'utente oppure di implementare un intero meccanismo di ripristino della sessione. Posizionando il componente di rotazione sotto il motore, il messaggio dell'utente viene scritto esattamente una volta e "riprovare con un altro candidato" resta completamente trasparente allo stato della sessione.

Anche le decisioni del componente di rotazione sono prudenti e ruotano attorno al confine del primo evento di contenuto:

  • Un errore prima che il modello emetta contenuti sostanziali, ossia testo o chiamate a strumenti: si può passare in sicurezza al candidato successivo;
  • Una volta emesso il primo evento di contenuto: si interrompe la rotazione e si lascia propagare l'errore verso l'alto, perché il modello potrebbe aver già eseguito un turno completo e ripeterlo produrrebbe nuovamente gli effetti collaterali.

La classificazione degli errori decide se "ruotare, non ruotare o riprovare". Gli errori a livello di account, come autenticazione fallita, saldo insufficiente, limiti di frequenza o abbonamento scaduto, fanno impostare un periodo di attesa e avviare la rotazione; gli errori transitori di rete, come il ripristino forzato di una connessione, non comportano periodi di attesa e fanno riprovare sul posto alcune volte senza mantenere stato; richieste malformate, errori relativi alle regole sui contenuti ed errori 5xx del server, che fallirebbero allo stesso modo anche con un'altra chiave, vengono invece propagati direttamente senza rotazione. Il periodo di attesa è un'indicazione di dieci minuti, interna al processo e non persistente: è soltanto un segnale a breve termine, non vale la pena scriverlo su disco a ogni errore e il riavvio del processo è proprio il momento giusto per verificare di nuovo.

Sul fronte dell'"elenco" dei fornitori, la ristrutturazione riconduce tre tipi di origini a un'unica astrazione:

  • LLM gestito da Orkas: un proxy lato server, pronto all'uso dopo l'accesso, con il server che instrada le richieste tra modelli di testo e immagini;
  • Chiave personale: i principali fornitori standard di modelli di grandi dimensioni;
  • Adattatori esterni a connessione diretta: un insieme di modelli che richiedono una connessione diretta o prevedono una propria fatturazione, adattati manualmente alla stessa interfaccia dei fornitori.

Per i livelli superiori, tutto questo appare come un'unica coppia stabile (provider, model): rotazione, periodi di attesa e adattamento esterno sono tutti nascosti all'interno del livello dell'adattatore.

3. La chat di gruppo come orchestrazione: dal piano-DAG statico a Commander nel ciclo

Questa è la parte della ristrutturazione che richiede più di tutte un cambio di prospettiva.

Il vecchio modello era la pianificazione statica: il modello genera prima un piano/DAG e l'esecutore segue il grafo. Il nuovo modello elimina completamente quel grafo e lo sostituisce con un'orchestrazione dinamica tramite chat di gruppo, con Commander nel ciclo.

La metafora è una stanza di chat di gruppo:

  • Commander è il padrone di casa della stanza, non un middleware invisibile;
  • Gli agenti esecutori sono membri a pieno titolo e di pari livello nella stanza;
  • Tutte le interazioni sono messaggi asincroni, accodati attraverso un unico bus di messaggi; non esiste un percorso privato per distribuire il lavoro in parallelo.

L'"assegnazione" di Commander non è un @somebody scritto nel testo: un LLM che scrive @AgentA nel corpo del messaggio sta solo riproducendo Markdown dai dati di addestramento, e non è affidabile. Il vero segnale di assegnazione è una chiamata strutturata a uno strumento e, dopo la ristrutturazione, converge in tre azioni dal significato chiaro:

  • dispatch_to — incaricare un agente di eseguire il lavoro fino al completamento e restituire il risultato, con Commander che ne elabora la sintesi. Più attività indipendenti possono essere distribuite ed eseguite contemporaneamente.
  • run_worker — una sottoattività di cui Commander mantiene la responsabilità, con restituzione sincrona del risultato; un esecutore anonimo è la "mano" di Commander, invisibile all'utente, mentre un esecutore con un nome è uno specialista visibile.
  • hand_off_topassare la conversazione all'agente; Commander si ritira e l'agente risponde direttamente all'utente, senza un'ulteriore sintesi in questo turno.

Perché una chat di gruppo, anziché un orchestratore o un albero di sottoagenti

Trasformare il sistema multiagente in una chat di gruppo porta diversi vantaggi che un orchestratore tradizionale o un albero di sottoagenti non possono offrire:

  • Porzioni di visibilità. Ogni messaggio viene aggiunto solo alla porzione di "chi può vederlo". Quando un agente esecutore si avvia, ripercorre soltanto la propria porzione, così l'output voluminoso di un altro agente non ne contamina il contesto. Commander vede tutto.
  • Stato minimo. Lo stato centrale dell'intera orchestrazione consiste soltanto in "chi ha attualmente la parola", più un registro leggero delle attività. Nessun DAG, nessuna macchina a stati complessa.
  • Riproduzione e sincronizzazione naturali. I messaggi si ordinano naturalmente per marca temporale, quindi sia il ricaricamento sia la sincronizzazione tra dispositivi si basano direttamente sul flusso dei messaggi. La parte mobile realizza il controllo remoto proprio a partire da questo flusso: tutta l'elaborazione degli agenti avviene sul desktop, mentre il dispositivo mobile si limita a una visualizzazione speculare, senza bisogno di un protocollo di orchestrazione specifico.

La revisione architetturale del team è stata altrettanto netta su questo punto: un bus di chat di gruppo più Commander nel ciclo è la forma del sistema multiagente di Orkas; aggiungere all'interno del processo un altro percorso parallelo di assegnazione ai sottoagenti violerebbe invece l'invariante di "un solo percorso di assegnazione nella chat di gruppo".

La novità di questa versione: il passaggio interattivo

L'elemento più recente lungo questa direttrice è il passaggio interattivo a un agente.

Il problema è concreto: un agente di tipo "tutor" insegna qualcosa all'utente per un turno, l'utente vuole continuare con altre domande, ma il sistema restituisce forzatamente la parola a Commander, costringendo l'utente a menzionare di nuovo quell'agente con @ a ogni singola riga.

La soluzione è una titolarità della parola stabilita dal server + un destinatario deciso dal modello:

  • La titolarità della parola diventa un campo di stato persistente, conservato tra i ricaricamenti, e sfrutta l'evento di cambio di stato esistente per sincronizzarsi automaticamente su tutti i dispositivi: non serve un nuovo tipo di evento.
  • Dopo che Commander usa hand_off_to per dare la parola a un agente interattivo, i successivi messaggi dell'utente "senza @" arrivano direttamente a quell'agente, finché l'agente non restituisce autonomamente il controllo o l'utente non si rivolge di nuovo a Commander.
  • L'agente restituisce il controllo con un marcatore <handback />; l'analisi verifica rigorosamente che la corrispondenza sia autentica, così un <handback isolato nel testo non viene scambiato per un passaggio di controllo.
  • Se al momento della restituzione del controllo il registro contiene attività non concluse, Commander le riprende dal registro e prosegue.

Questo porta con sé anche un miglioramento dell'esperienza: le bolle del ciclo di Commander. In precedenza, il ciclo "assegna → leggi il risultato → assegna di nuovo" di Commander all'interno di un turno veniva appiattito in un'unica bolla e, al ricaricamento, poteva persino finire in fondo, fuori ordine. La ristrutturazione suddivide un turno in più segmenti a ogni punto di assegnazione visibile, ciascuno dei quali è un messaggio autonomo con una marca temporale crescente: per la prima volta l'utente può vedere Commander "percorrere il ciclo di orchestrazione", e anche l'ordine dopo il ricaricamento è corretto.

Infine, due reti di sicurezza trasversali: l'interruzione di gruppo è l'unico percorso di arresto per tutti i partecipanti — nel momento in cui l'utente preme Interrompi, viene attivato il segnale di annullamento di ogni esecutore, includendo anche i sottoesecutori anonimi grazie a una corrispondenza di ripiego — e la già citata correzione di rotta tramite interruzione, che integra nel turno corrente l'intervento dell'utente durante l'esecuzione.

4. Da un catalogo chiuso a una piattaforma ospitante aperta

Se le prime tre direttrici miravano a rendere solide le fondamenta, questa punta ad aprire tutte le porte e le finestre — trasformare Orkas da un catalogo chiuso in una piattaforma ospitante aperta — mantenendo però il confine di sicurezza senza cedere di un millimetro.

La ristrutturazione ha smantellato sistematicamente diversi colli di bottiglia "chiusi":

  • Pacchetti esterni. L'utente fornisce l'indirizzo di un repository e Orkas lo ospita in locale, clonato tale e quale in una cartella: mai normalizzato, mai riscritto, mai sincronizzato sul cloud, perché contiene directory di dipendenze di terze parti. Uno strumento autonomo da riga di comando gestisce il ciclo di installazione, aggiornamento, avvio e arresto, verifica se il pacchetto ha "forma di abilità", cioè contiene un file di descrizione dell'abilità, o "forma di CLI", cioè contiene un punto di ingresso eseguibile, e scrive i metadati in un registro esterno alla directory del pacchetto, così gli aggiornamenti futuri tramite pull non entrano mai in conflitto. L'installazione delle dipendenze passa per una conferma in due fasi del tipo "chiedi una volta e ricorda"; per i punti di ingresso eseguibili vengono generati shim e aggiunti al PATH dello strumento bash, così il modello può chiamare direttamente queste CLI di terze parti.

  • Caricamento delle abilità da più radici. L'unico punto di ingresso per l'esecuzione delle abilità è passato dal riconoscere solo due radici a quattro livelli — personalizzate / marketplace / pacchetto esterno / globali — risolti in base alla priorità; gli script all'interno di un pacchetto esterno preferiscono l'ambiente delle dipendenze incluso nel pacchetto stesso. Questo è il punto di controllo con il rischio di regressione più elevato ed è supportato da una matrice completa di fixture di test.

  • Interoperabilità delle abilità globali. Orkas legge direttamente dalle directory globali delle abilità già gestite da altri strumenti per agenti sul computer dell'utente, ottenendo interoperabilità a livello di abilità: un'abilità che l'utente ha raccolto in un ambiente è utilizzabile anche in Orkas. Il fatto che l'utente inserisca un'abilità in quelle directory costituisce di per sé l'autorizzazione, quindi la funzione è attiva per impostazione predefinita, pur mantenendo un interruttore generale. Queste descrizioni di abilità di terze parti costituiscono una superficie non attendibile per l'iniezione di prompt, quindi passano attraverso il caricatore di "livello aperto", sono visibili soltanto a Commander e non possono strutturalmente entrare nell'elenco delle abilità consentite di un agente.

  • MCP configurato dall'utente. I connettori non sono più un catalogo fissato nel codice. L'utente può aggiungere qualsiasi server MCP: nella forma HTTP remota, a basso rischio, oppure nella forma di sottoprocesso locale, ad alto rischio. Il modulo stesso è il luogo in cui si esprime il consenso — il comando digitato manualmente dall'utente viene mostrato tale e quale —, la configurazione del trasporto, inclusi i segreti, viene interamente salvata in un archivio cifrato e le istanze personalizzate hanno sempre un prefisso fisso, così non possono mai spacciarsi per un connettore ufficiale del catalogo.

  • Ponte inverso: consentire agli agenti esterni del computer di accedere a loro volta a Orkas. È la parte più interessante. Gli strumenti per agenti esterni già presenti sul computer dell'utente erano una scatola nera per Orkas; ora, quando Orkas li incarica di un lavoro, introduce un canale di collegamento che permette loro, in senso inverso, di elencare, leggere ed eseguire le abilità di Orkas, chiamare i connettori e cercare nella base di conoscenza. Il ponte funziona tramite un canale locale tra processi, senza aprire porte di rete, autenticato con una credenziale monouso, unica per ogni esecuzione e distrutta nel momento in cui l'esecuzione termina. Ogni chiamata a un connettore con effetti esterni passa attraverso una finestra di conferma dell'utente: non un giudizio euristico di lettura o scrittura basato sul nome dello strumento, che rischierebbe di essere troppo permissivo, ma una conferma per ciascuna coppia (agente, connettore), con un'opzione "consenti sempre".

  • Approccio di programmazione per i casi meno comuni. L'albero decisionale di Commander acquisisce un ramo: quando non esiste un agente, un'abilità o un connettore corrispondente, valutare la risoluzione diretta con bash e un breve script, eseguirla nel turno corrente, verificare l'output ed eventualmente proporre di consolidarla in un'abilità personalizzata. A questo si accompagnano l'esecuzione di bash in background — le attività lunghe si separano dal turno corrente e i log vengono scritti in un file — e le directory autorizzate dall'utente.

Aperto, ma sotto controllo

Quando si aprono porte e finestre, ciò che si teme di più è la corrente d'aria. La disciplina di questa ristrutturazione è la seguente: non viene toccato nemmeno uno dei punti obbligati di avvio delle "azioni pericolose". MCP si avvia da un solo punto, l'esecuzione delle abilità passa attraverso un unico esecutore e bash attraversa un unico esecutore in sandbox. A questo si aggiungono diversi livelli di difesa:

  • Le operazioni sui file passano sempre attraverso la sandbox dei percorsi — ambiente di lavoro + allegati correnti + directory esplicitamente autorizzate dall'utente —, mentre l'accesso alle directory delle credenziali, alle directory di sistema e alle directory di Orkas non può essere autorizzato;
  • Le operazioni bash pericolose — esfiltrazione, eliminazione distruttiva, elevazione dei privilegi, percorsi sensibili — attivano una richiesta di autorizzazione, con le opzioni "solo questa volta / per questa esecuzione / nega", e i log registrano soltanto categoria e lunghezza: mai il testo del comando;
  • L'installazione di pacchetti esterni si blocca per sicurezza e rifiuta categoricamente i pacchetti che contengono collegamenti simbolici, per impedire che vengano usati per rendere accessibili in lettura file sensibili esterni alla sandbox, e l'origine della clonazione è limitata a un elenco di protocolli consentiti;
  • Tutte le configurazioni di trasporto contenenti credenziali e tutti i segreti vengono cifrati a riposo, e le credenziali del ponte sono isolate per singola esecuzione;
  • La distribuzione open source / ospitata rimuove le capacità esclusive dell'host tramite una regola di esclusione.

In una frase: ogni azione esplicita dell'utente — installare / autorizzare / inviare un modulo / fare clic su conferma — costituisce la prova del consenso, e ogni consenso resta confinato entro il limite che gli compete.

5. Diventare più intelligenti tra una sessione e l'altra: memoria e autoevoluzione

La revisione delle fondamenta ha rifatto anche due sottosistemi che "rendono l'agente più intelligente quanto più viene usato", entrambi secondo la stessa disciplina ingegneristica: disattivati per impostazione predefinita, delimitati, osservabili.

La memoria tra sessioni usa un recupero ibrido: ricerca semantica vettoriale + ricerca per parole chiave (BM25), combinate tramite RRF, fusione dei ranghi reciproci, per evitare che l'insuccesso di un singolo canale comprometta il recupero; i dati vengono salvati in un archivio locale con indice a testo completo. La memoria è di due tipi: le note dell'agente e il profilo delle preferenze dell'utente. Entrambi hanno un limite di caratteri, vengono controllati per individuare minacce di iniezione prima della scrittura e vengono inseriti come istantanea fissa nel prompt di sistema all'inizio di ogni turno. L'intero sistema di memoria serve soltanto a far comprendere meglio all'agente l'utente corrente; i dati restano sempre in locale e l'utente può consultarli, modificarli ed esportarli in qualsiasi momento nelle Impostazioni.

L'autoevoluzione consiste in una biblioteca di abilità privata dell'agente, archiviata separatamente dalla biblioteca condivisa della piattaforma, più un livello di riflessione metacognitiva. Il motore decide se riflettere in base a un insieme di segnali ponderati: correzioni dell'utente, con il peso più alto, recupero da un errore non banale, complessità dell'attività, manifestazione o superamento di una debolezza nota, inefficacia di un'abilità... La riflessione si attiva solo quando i segnali ponderati superano una soglia. La riflessione stessa è un'attività periodica in background — circa un ciclo ogni 12 ore, un intervallo di attesa di diverse ore e un meccanismo di ripiego su più giorni — che usa un modello piccolo ed economico per leggere un riepilogo dell'attività recente e decidere se creare o correggere un'abilità e aggiornare il "profilo delle competenze" dell'agente.

Il punto di sicurezza più importante: l'autoevoluzione è abilitata soltanto per le sessioni a cui è esplicitamente associato un agente; la sessione predefinita di Commander non evolve. La riflessione ha un doppio limite di token, per conteggio e totale, il fallimento di un agente non blocca gli altri e il costo per esecuzione viene mantenuto estremamente basso. Rendere l'agente più intelligente, senza lasciarlo sfuggire al controllo.

Filosofia ingegneristica: complessità giustificata — non eliminarla per semplificare

Durante la ristrutturazione il team ha svolto più cicli di revisione architetturale, e una conclusione è tornata ripetutamente, tanto da meritare di essere evidenziata: distinguere l'"ipertrofia organizzativa" dalla "complessità giustificata" e intervenire soltanto sulla prima.

  • Le molteplici strategie di unione del motore di sincronizzazione, il ciclo dell'agente sviluppato internamente, il wrapper dei fornitori a più livelli, il confine del controllo remoto da dispositivi mobili: sembrano complessi, ma ogni livello ha una ragione d'essere — coerenza eventuale tra più dispositivi, integrazione profonda, rotazione tra più chiavi, confine tra dispositivi definito dal prodotto. "Semplificarli" a forza porterebbe soltanto alla perdita di dati e a confondere la separazione dei livelli.
  • Ciò su cui si deve davvero intervenire sono i "moduli onnipotenti" e le duplicazioni locali: estrarre dal bus di chat di gruppo sovraccarico le funzioni pure senza stato — assemblaggio dei prompt, strumenti di Commander, turno CLI — e riunire in un unico componente condiviso il modello della "finestra di conferma", duplicato più volte.

A sostenere questo tipo di giudizio c'è un insieme di regole rigorose, scritte nel documento dei vincoli del progetto: confini — processo unico, IPC come unico percorso, ambiente di esecuzione caricabile soltanto dinamicamente —, stratificazione — direzione delle dipendenze di ogni livello —, unica fonte di verità — categorie, tassonomia della telemetria, domini — e una "verifica dei prompt" obbligatoria per ogni commit che interviene sui prompt. A consentire la revisione delle fondamenta senza crolli non è un qualche progetto ingegnoso: è il rispetto continuo di queste invarianti.

Conclusione

Mettendo insieme le quattro direttrici, questa ristrutturazione dalle fondamenta dota Orkas di una base per gli agenti sotto il proprio controllo, indipendente dal fornitore, orchestrata dinamicamente, aperta all'esterno e capace di autoevolversi:

  • Un ambiente di esecuzione interno al processo, diviso nei due livelli motore/adattatore, che porta nativamente sul desktop tutte le capacità di un agente di programmazione — operazioni sui file, ricerca locale, strumenti di sistema, più esecutori in parallelo, risoluzione di problemi di lunga durata — e le rende tutte pronte per la produzione;
  • Un livello dei modelli articolato su più strati, che mantiene viva la conversazione il più possibile nonostante i problemi di chiavi, fornitori e rete;
  • Un'orchestrazione multiagente in stile chat di gruppo, con Commander nel ciclo, che sostituisce la "pianificazione statica" con la "decisione dinamica" e rende per la prima volta naturali i passaggi tra agenti;
  • Un ecosistema che passa da un catalogo chiuso a una piattaforma ospitante aperta, con pacchetti esterni, abilità globali, MCP personalizzati e ponte inverso tutti accessibili, mentre i punti obbligati di avvio restano saldamente al loro posto;
  • E memoria e autoevoluzione disattivate per impostazione predefinita, delimitate e osservabili.

Le funzionalità si possono aggiungere una alla volta, ma vale la pena rivedere seriamente le fondamenta una volta sola. Una volta fatto, qualsiasi cosa si costruisca sopra procede più rapidamente: ed è proprio questo il risultato a cui mirava la ristrutturazione.