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:
- manda il system prompt, gli input dichiarati e l'eventuale feedback.
- esegue i tool che il modello chiede e rimanda i risultati.
- valida l'artefatto di
submitcontro lo Schema diwritese 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) conworkspaceAccess: write. ws.execews.testconexecute: 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.