Diagramma dell'architettura del runtime OpenHarness per agenti AI

Come costruire OpenHarness: un runtime per agenti AI con strumenti, memoria e coordinazione

OpenHarness è un agent runtime progettato per essere trasparente, modificabile e completamente controllabile a livello di flusso. Invece di trattare un framework per agenti come una scatola nera, questo tutorial lo ricostruisce pezzo per pezzo, svelando come il sistema riceve un compito, lascia che il modello decida l’azione successiva, valida ed esegue le chiamate agli strumenti, restituisce osservazioni e ripete il ciclo fino al completamento.

L’implementazione è pensata per essere eseguibile senza chiavi API né infrastrutture complesse. Puoi sperimentare l’architettura direttamente sul tuo computer. Vediamo i blocchi fondamentali.

Il cuore del runtime

Il codice parte con l’importazione dei moduli standard di Python. Non ci sono dipendenze esterne, solo asyncio, dataclasses, json, typing e qualche altro modulo della libreria standard. La funzione run_async permette di eseguire coroutine anche all’interno di un loop già in esecuzione, gestendo scenari complessi con nest_asyncio o thread separati.

Le strutture dati fondamentali sono semplici dataclass: Usage traccia i token consumati, ToolCall rappresenta una chiamata a uno strumento (con id, nome e argomenti), AssistantTurn contiene il testo prodotto dal modello e le eventuali tool call, e Message modella un singolo messaggio nella conversazione. Il conteggio dei token è approssimativo (circa 4 caratteri per token), volutamente provider-agnostico.

La classe CostMeter accumula l’uso dei token e lo converte in costo stimato in dollari, usando un price book predefinito per modelli come Claude Sonnet 4 e GPT-4.1.

Strumenti, permessi e lifecycle

La definizione degli strumenti avviene attraverso la classe base BaseTool. Ogni strumento ha un nome, una descrizione, un modello di input (una dataclass) e un livello di permesso (PermissionKind: READ, WRITE, EXECUTE o META). Il metodo schema() genera automaticamente lo schema JSON a partire dal modello di input, usando la funzione build_json_schema. La validazione e il coercing dei tipi sono gestiti da instantiate, che converte i parametri JSON grezzi nei tipi Python corretti.

Il ToolContext funge da contenitore per tutti i servizi e lo stato condiviso che gli strumenti possono necessitare a runtime. La divisione per permessi permette di implementare politiche di sicurezza granulari: uno strumento di sola lettura (READ) non potrà mai modificare file, mentre uno EXECUTE potrà lanciare comandi.

Il lifecycle dell’agente comprende hook personalizzabili: before_tool_execution, after_tool_execution, on_retry, on_completion. Questi permettono di aggiungere logica personalizzata come notifiche, logging, o interruzioni condizionali.

Memoria, contesto e retry logic

La memoria è implementata come una semplice coda di messaggi, con una strategia di compaction: quando la lunghezza totale della conversazione supera una soglia, i messaggi più vecchi vengono riassunti dal modello stesso, preservando le informazioni essenziali. Il contesto viene compresso periodicamente per evitare di superare i limiti di token del modello.

Il sistema di retry è configurabile: numero massimo di tentativi, backoff esponenziale, e la possibilità di escludere errori specifici dal retry (ad esempio, errori di validazione irreversibili). Dopo ogni fallimento, l’errore viene reinserito nella conversazione come osservazione, dando al modello la possibilità di correggere la chiamata.

Skills e multi-agente

Le skills sono insiemi di strumenti raggruppati per dominio. Ogni skill ha un nome, una descrizione e uno o più strumenti. Il modello può scegliere di attivare una skill in base al contesto del task, e la harness carica dinamicamente solo gli strumenti rilevanti.

La coordinazione multi-agente si basa su un orchestratore centrale che assegna subtask a diversi agenti specializzati. Ogni agente ha i propri strumenti, memoria e policy di permessi. L’orchestratore raccoglie i risultati parziali, gestisce le dipendenze e decide quando il task globale è completo. La comunicazione tra agenti avviene attraverso un bus di messaggi interno, con un formato standard per richieste e risposte.

Cosa significa per il risparmiatore

  • Se lavori con strumenti di AI agentica, questo tutorial mostra come evitare vendor lock-in: costruendo un runtime modulare con Python puro, puoi migrare tra modelli (Claude, GPT, mock) senza riscrivere la logica di orchestrazione.
  • Il sistema di cost tracking è trasparente: ogni chiamata al modello viene registrata con conteggio dei token e stima del costo. Puoi prevedere la spesa operativa prima di andare in produzione, semplicemente eseguendo il runtime in locale.
  • Le politiche di permessi e retry permettono di implementare controlli di sicurezza senza dipendere da servizi esterni. Per chi gestisce dati finanziari o personali, questo è un vantaggio: non devi condividere i tuoi dati con un provider di orchestrazione esterno.
  • L’architettura multi-agente è utile per workflow complessi (analisi di portafoglio, reportistica): puoi avere un agente che legge i dati, uno che li analizza, uno che scrive il report, ognuno con i propri strumenti e permessi limitati.

Articoli simili