Passa al contenuto principale

Harness

Cosa decide come un agente parla con un modello, e cosa aggiunge ogni harness sugli altri.

Un manifest Agent dice cosa un agente deve produrre. Un harness decide come parla con un modello. L'harness è un plugin dietro la porta harnesses, risolto da Agent.spec.harness. basic è quello di default.

L'harness basic​

basic è un ciclo agentico nativo sulla porta dei modelli, senza LangChain. Ripete chiamata al modello, tool call e risultati finché il modello chiama submit:

  1. manda il system prompt, gli input dichiarati e l'eventuale feedback.
  2. esegue i tool che il modello chiede e rimanda i risultati.
  3. valida l'artefatto di submit contro lo Schema di writes e termina.

I tool di un agente basic sono quelli built-in del workspace e ogni server mcp: (vedi Plugin). Il tool submit è sempre offerto, costruito dallo Schema di output.

Submit, artefatto e route​

submit porta un artefatto, valido contro lo Schema che l'agente dichiara in writes. Quando il nodo sceglie fra più destinazioni (choose), il submit porta anche route: una o più fra le destinazioni offerte, almeno il min dell'edge.

Un artefatto non valido o una route sbagliata tornano al modello come risultato di tool correttivo, fino a limits.maxSubmitRetries. Oltre, il turno fallisce e il ticket va in needs_human.

Una risposta senza tool call e senza submit riceve lo stesso trattamento correttivo.

Il blocco <budget>​

Prima di ogni chiamata al modello, il motore aggiunge un blocco <budget> dopo la cronologia, come ultimo messaggio della sola chiamata in uscita, mai alla cronologia salvata. Elenca ogni dimensione con un limite con quanto è usato, il limite e la percentuale, per agente e, per costo e tempo, per ticket. Si legge dal ledger, quindi sopravvive a una pausa o a un riavvio.

L'ordine della richiesta è il system prompt, i tool in ordine deterministico, la cronologia e poi il blocco volatile (il budget e la lista dei todo). Niente di volatile sta dentro il prefisso, che resta identico byte-a-byte tra le chiamate di un turno, così il provider può mettere in cache system prompt, tool e cronologia.

Riassunto​

Oltre summarizeTokens, una chiamata al modello dell'agente sostituisce i messaggi più vecchi con un breve riassunto. Il system prompt, il primo messaggio utente e gli ultimi summarizeKeepMessages messaggi restano. La chiamata di riassunto passa dal ledger e conta nel budget.

Fallback dei modelli​

Un alias Model può nominare dei fallbacks. Quando una chiamata al provider fallisce con un errore, non un verdetto, l'harness riprova la stessa chiamata sull'alias successivo, in ordine, e solo allora fa fallire il turno. Ogni tentativo passa dal ledger.

Prima ancora, il provider stesso riprova una risposta che può sparire da sola: 429 Too Many Requests, 500, 502, 503, 504, o nessuna connessione. Riprova fino a 3 volte, aspettando il Retry-After che l'endpoint manda (al massimo 30 s), oppure 1, 2 e 4 s. Un 4xx non viene riprovato, e nemmeno un timeout di lettura, che il provider potrebbe aver già fatturato. Un turno che fallisce comunque passa a una persona come HarnessFailed: riprova, prendi in carico o chiudi.

L'harness deep​

deep è il ciclo basic più un piano e i subagenti. Mantiene ogni garanzia di basic (submit, verdetti, blocco <budget>, token e salvataggio, riassunto, fallback), e aggiunge:

  • i tool sui file (ws.write, ws.edit, ws.delete) con workspaceAccess: write.
  • ws.exec e ws.test con execute: true.
  • write_todos: il piano vive nello stato del turno, ed è mostrato al modello accanto al blocco <budget>, alla sola chiamata successiva.
  • task(subagent, instructions): esegue un subagente in un sotto-turno tutto suo, e ne restituisce al padre la risposta finale.

Subagenti​

Agent.spec.subagents dichiara i subagenti a cui l'agente può passare lavoro:

spec:
harness: deep
workspaceAccess: write
tools: ["ws.read", "ws.write"]
subagents:
- name: tester
description: esegue i test del progetto
systemPromptFile: tester.md
tools: ["ws.read", "ws.exec"]
model: cheap # opzionale; ereditato dall'agente quando assente

tools dev'essere un sottoinsieme dei tool dell'agente. Il file del prompt e l'alias del modello devono esistere, controllati al caricamento della configurazione. Un subagente non ha submit, e non può chiamare né task né write_todos. Le sue chiamate passano dallo stesso ledger, quindi contano sul budget dell'attivazione. Una pausa o un crash dentro un sotto-turno riprende dentro di esso: il token porta l'intero stack.

L'harness acp​

acp esegue un agente di coding esterno dentro la sandbox del nodo, e lo guida via Agent Client Protocol. Il motore è il client, e non offre filesystem né terminale, quindi l'agente usa i propri tool nel container.

kind: AcpAgent
metadata: {name: opencode}
spec:
command: [opencode, acp]
env: [OPENROUTER_API_KEY] # solo nomi: il valore viene dall'ambiente del motore
model: deepseek/deepseek-v4-flash
reportsCost: true

L'Agent lo nomina con harness: acp e agent: opencode, e la sandbox del progetto lo esegue (sandbox.egress lo limita all'host del modello). Ogni turno, tool call e costo riportato passa dal ledger come per qualsiasi agente. Una soglia di budget lo mette in pausa, il limite lo ferma, e una tool call ripetuta fa scattare il loop guard.

Permessi​

Agent.spec.permissions mappa i tipi di tool ACP (read, edit, execute, fetch, …) su allow, deny o ask. Il default è ask. allow e deny rispondono subito all'agente. ask mette in pausa il nodo con una richiesta umana (allow o deny), e la risposta viene rigirata all'agente quando il turno riprende.

L'artefatto​

L'artefatto si legge dal diff del workspace e dal messaggio finale dell'agente. I campi che somigliano a file (files, paths) prendono i file cambiati. Quelli che somigliano a un riassunto (summary, message, …) prendono il messaggio finale. Ogni altro campo passa da una chiamata di estrazione sull'alias model dell'agente, attraverso il ledger.

Un artefatto non valido manda un prompt correttivo all'agente, fino a maxSubmitRetries. Un agente acp il cui Schema ha campi oltre a file e riassunto deve dichiarare model.

Una pausa o un crash riprende la sessione ACP. L'id di sessione è salvato con l'attivazione, e il motore chiede all'agente di ricaricarlo con session/load. Quando l'agente non può, ne apre una nuova con il transcript salvato. Un turno già pagato non viene mai ripetuto.

Vedi anche​