Die fünf Schichten eines Agent-Harness, zusammen mit Code, der ohne sie fehlschlägt.
Die fünf Schichten eines Agenten-Engines, jede mit Code, der ohne sie versagt: Ausführungsgrenze, Sandboxing, Speicher, Überprüfungszyklen und der Kontextpipeline.
Quellen: `The Complete Guide to Agent Harnesses (With Code)`
Stellen Sie sich ein Team vor, das eine Zeile zur Konfiguration eines Agenten hinzufügt: Keine Änderung wird vorgenommen, ohne dass der Mensch sie genehmigt. Die Formulierung ist klar, die Zeile befindet sich ganz oben, und der Agent lädt sie zu Beginn jeder Sitzung.
Dann kommt ein Nachmittag, an dem eine einzige Sitzung elf Mal diese Zeile überschreitet und etwa siebzigsechs ungenehmigte Änderungen in das Endprodukt für den Kunden einfügt. Es erscheint kein Fehler. Nichts hält an, um nachzufragen. Zwei Stunden später geht jemand das Dokument durch, markiert jede Zeile, die der Agent eigenmächtig überarbeitet hat, und findet insgesamt vierundvierzig davon in einer Datei, die den Namen des Kunden trägt.
Die Anweisung an sich war in Ordnung. Sie befand sich nur am falschen Ort.
Was stattdessen funktioniert, ist ein Hook – ein kleiner Script, der vom Harness unmittelbar vor einem Toolaufruf ausgeführt wird. Er prüft den ausstehenden Aufruf und kann mit einer Ablehnung antworten. Der Agent möchte möglicherweise dennoch die Änderung vornehmen, doch diese kommt niemals im Dateiinhalt an. Ein solcher Hook irrt sich manchmal, in der Regel weil die Regel zu eng gefasst wurde, und der Vorteil besteht darin, dass man den Code öffnen und auf die genaue Bedingung zeigen kann, die zu dem Fehler führte. Ein Satz in einer Anweisung lässt hingegen nichts zum Hinweisen übrig.
Die Distanz zwischen einer von Ihnen angekündigten Regel und einer von Ihnen durchgesetzten Regel wird vom Harness überbrückt. Er umfasst fünf Bereiche:
- Die Aktionen, die der Agent ausführen kann
- Die Orte, die er erreichen kann
- Der Zustand, der auch nach einem Neustart erhalten bleibt
- Der Prüfer seiner Ausgabe
- Der Aufwand für das, was ihm angezeigt wird
Jeder Bereich erhält den untenstehenden Code. Es gibt eine Reihe von ausführbaren Demonstrationen, bei denen jede Schicht zunächst auf dem eigenen Laptop getestet wird, bevor sie in der Produktion versagt. Dies richtet sich an Personen, die bereits einen Agenten veröffentlicht haben und beobachtet haben, wie er Fehler aufwies.
Was ein Harness Ihnen tatsächlich bietet
Beginnen wir mit etwas, das fast zu einfach ist, um es auszusprechen. Der Hauptloop eines Programmier-Agenten besteht aus sechs Zeilen:
while agent.turns < max_turns:
call = agent.next_call(observations)
if call is None:
break
result = execute(call)
observations.append(result)
Diese Zeilen stellen den Agenten dar. Mit Fehlerbehandlung und Wiederholungsversuchen kommt man auf etwa zwanzig. Unabhängig vom bewerteten Framework umschließt dieses Framework diese Schleife, und der interessante Teil ist execute. Diese Funktion legt fest, was das Modell tun kann, worauf es zugreifen kann, was es beibehält, wer es überprüft und was schließlich vor ihm liegt.
Zusammen bilden diese Entscheidungen die Grundlage, und viele Kommentare behaupten, sie seien wichtiger als die Wahl des Modells. Die Daten stützen eine begrenztere Aussage, die zufälligerweise praktischer ist.
Der Harness gewinnt beim Preis. Ein Preprint vom Juni 2026 verglich drei Harnesses (Goose, OpenCode, OpenHands-SDK) anhand von zwei Modellen unter Verwendung eines stratifizierten Satzes von 50 Aufgaben aus Terminal-Bench Pro, der schwierigeren Variante des Terminal-Bench-Agenten-Systems. Der Tokenverbrauch pro gelöster Aufgabe unterschied sich zwischen den Harnesses um bis zu das Vierzigfache. Der Wechsel zu einem besseren Modell führte zu einer Veränderung dieses Wertes nur um das 1,0- bis 1,3-fache.
Er gewinnt nicht bei der Leistungsfähigkeit. Die Bestehensraten unterschieden sich zwischen den Harnesses um 0 bis 8 Punkte, und mit Bootstrap-Konfidenzintervallen lag alles außer dem größten Unterschied bei null. Die Autoren selbst bezeichnen die Genauigkeitsergebnisse angesichts von n=50 als beschreibend.
Einfach ausgedrückt kann eine Aufgabe mit demselben Erfolgsgrad bei einem Modell unter einem bestimmten Framework vierzig Mal teurer sein als unter einem anderen. Alles, was danach kommt, senkt die Kosten und verringert unerwartete Probleme – doch nichts davon verleiht einem schwachen Modell mehr Intelligenz.
Die Regel in Code umsetzen
Die Angabe einer Regel in einem Systemprompt bedeutet im Grunde ein Wagnis: Man setzt darauf, dass das Modell sie sich merkt, sie wie beabsichtigt interpretiert und sich daran hält, selbst wenn der Kontext überladen ist. In der Regel gewinnt das Modell. Genau dieser Erfolgsgrad macht es jedoch riskant, denn dann hören die Menschen auf, nachzuprüfen.
Regeln auf Codeebene werden bei jedem einzelnen Aufruf ausgeführt – selbst dann, wenn das Modell sie bereits vergessen hat.
Ein Agent-Loop weist fünf Stellen auf, an denen Code die Ausführung steuern kann. Jeder Abschnitt unten erläutert dessen Zweck, den zugehörigen Code sowie das Verhalten, wenn diese Schicht fehlt. Niemand benötigt alle fünf auf einmal. Wählen Sie die Schicht, deren Ausfall Sie sich in dieser Woche am leichtesten vorstellen können.
Schicht 1: die Ausführungsgrenze
Was darf der Agent tun?
Die Grenze ist eine Funktion, die zwischen der Auswahl des Modells und dem tatsächlichen Tool-Aufruf eingefügt ist. Beginnen Sie mit einem ungeschützten Agenten. Seine Anweisung besagt, niemals eine Datei ohne vorherige Anfrage zu löschen, und drei Tool-Aufrufe warten in der Warteschlange.
SYSTEM_PROMPT = "IMPORTANT: never delete a file without asking the user first."
def execute(call):
if call.name == "delete_file":
FILES.pop(call.args["path"], None)
return f"deleted {call.args['path']}"
Beim Ausführen löscht der Agent cache.tmp, harmlose Mülldateien, und anschließend client-deliverable.md, was genau so bedeutet, wie es klingt. Es wurden keine Fragen gestellt, obwohl die Anweisung von Anfang an die entsprechende Regel enthielt.
Eine vor dem Tool platzierte Funktion löst dieses Problem:
def boundary(rules):
def wrap(execute):
def guarded(call):
for name, deny_if, reason in rules:
if deny_if(call):
return f"DENIED by {name}: {reason}"
return execute(call)
return guarded
return wrap
NEEDS_APPROVAL = [(
"delete-needs-approval",
lambda c: c.name == "delete_file" and not c.args.get("approved_by_human"),
"deletion requires an explicit human approval flag on the call",
)]
Mit demselben Agenten, Skript und Prompt wird nichts gelöscht.
Ebene 2: Sandboxing
Was kann der Agent erreichen?
Eine Sandbox legt zunächst fest, auf welche Verzeichnisse und Hosts der Prozess des Agents zugreifen darf. Erstellen Sie sie von Grund auf: Beginnen Sie ohne Voraussetzungen und gewähren Sie nur das, was für die Aufgabe erforderlich ist, durch eine Liste der erlaubten Pfade, eine Liste der erlaubten Hosts sowie einen Prozess, der von allem anderen unbeeinflusst bleibt.
Die weit verbreitete Alternative ist eine Liste der verbotenen Pfade:
DENY = ["secrets/"]
def execute(call):
path = call.args["path"]
if any(path.startswith(d) for d in DENY): # checks the spelling
return "DENIED by deny-list"
real = os.path.normpath(path) # the ../ collapses HERE, after the check
return DISK.get(real, "not found")
Starten Sie es, und der Agent versucht es zweimal:
read('secrets/api_key') -> DENIED by deny-list
read('work/../secrets/api_key') -> sk-live-DO-NOT-LEAK
Die Prüfung untersuchte den eingegebenen Text, während die Dateisuche der Position folgte, auf die der Text verweist. Zwei Anfragen, zwei Schreibweisen, eine Datei.
Die Korrektur besteht darin, zunächst den Pfad zu normalisieren und anschließend zu vergleichen:
ALLOW_ROOTS = ["work"]
def resolve(path):
real = os.path.normpath(path) # resolve FIRST
if not any(real == r or real.startswith(r + os.sep) for r in ALLOW_ROOTS):
return None
return real
Sowohl Schreibweisen führen nun zu secrets/api_key, das sich nicht unter einem erlaubten Wurzelverzeichnis befindet, weshalb beide abgelehnt werden. Hier ist es entscheidend, zuzulassen anstatt abzulehnen, denn eine Ablehnungsliste blockiert nur die Schreibweisen, die ihr Ersteller vorgesehen hat.
Diese Beispiele sind gewöhnliche Python-Funktionen, sodass kein Container zum Ausprobieren benötigt wird. In einer echten Bereitstellung ist die gleiche Überprüfung die Prozessgrenze selbst: Ein Benutzer oder Container, der außerhalb von work/ keine Leseberechtigung hat, mit derselben Reihenfolge der Operationen.
Layer 1 kann diese Aufgabe nicht übernehmen. Eine Überprüfung innerhalb Ihres Programms erkennt nur die Aufrufe, die sie kennt, während ein Shell-Befehl, ein Symbolischer Link oder ein Pfad mit .. darauf zugreifen kann, ohne zu einem dieser Aufrufe zu gehören.
Layer 3: Speicherpersistenz
Was bleibt nach einem Kontext-Reset bestehen?
Eine Gewohnheit bringt mehr Vorteile als die meisten: Gespräche ohne weiteres Nachdenken abzubrechen. Der Kontext wird überladen, die Qualität sinkt und der Agent beginnt, mit sich selbst im Widerspruch zu geraten. Viele Menschen lassen solche Sitzungen einfach weiterlaufen. Es ist besser, frühzeitig neu zu starten, vorausgesetzt, alles Wichtige wird an einem Ort gespeichert, den ein Neustart nicht löschen kann – sei es kompilierte Code, Konfigurationen, die eigentliche Plattform oder zusammengefasste Notizen.
Bestimmen Sie daher absichtlich, wohin jeder Zustandswert gehört. Es gibt drei Optionen: das Gespräch, wo er zusammen mit der Sitzung verschwindet; die Festplatte, wo er erhalten bleibt und später abgerufen werden kann; sowie die Plattform-Konfiguration, wo er jede nachfolgende Sitzung beeinflusst.
Eine einzige Verteilungsfunktion deckt alle drei Fälle ab:
CONVERSATION, DISK, HARNESS_CONFIG = [], {}, {}
def remember(kind, key, value):
"""'chat' dies with the session, 'disk' survives it,
'config' shapes every session that follows."""
{"chat": lambda: CONVERSATION.append(value),
"disk": lambda: DISK.__setitem__(key, value),
"config": lambda: HARNESS_CONFIG.__setitem__(key, value)}[kind]()
Die Funktion ist im Grunde nutzlos. Sie liefert lediglich eine Regel, wonach nichts gespeichert werden darf, ohne angegeben zu werden, zu welchem der drei Orte es gehen soll. Teams treffen diese Wahl selten ausdrücklich, was erklärt, warum Agenten manche Dinge zu vergessen scheinen und bei anderen stur falsch liegen. In der Demonstration kommt die Konversationsliste leer zurück, während die Entscheidung sowie die Regel weiterhin auf der Festplatte und in der Konfiguration vorhanden sind.
Layer 4: Überprüfungszyklen
Wer prüft die Ausgabe, und was hindert den Prüfer daran, die Probleme einfach zu beheben?
Ein Überprüfungszyklus bedeutet einen zweiten Blick auf die Arbeit des Agenten, der von etwas durchgeführt wird, das Probleme melden darf, aber nicht beheben kann.
Der zweite Teil wird von den Leuten übergangen. Lassen Sie einen Unteragenten des Reviewers schreiben, der dann stillschweigend alle gefundenen Fehler behebt. Das erscheint effizient, doch dadurch geht die Information verloren, die man eigentlich haben wollte: Man kann das richtige Werk nicht mehr vom falschen Werk unterscheiden, das heimlich repariert wurde.
Halten Sie den Reviewer daher nur für Lesen zugänglich. Er erstellt eine Kritik, und das Handeln auf dieser Kritik ist ein weiterer Schritt mit eigenen Spuren. Sie getrennt zu halten bewahrt ein Signal, das man lesen kann.
Eine weitere Taktik ist das Probelauf-Verfahren. Für jede irreversible Aktion akzeptiert das Tool ein Flag, beschreibt standardmäßig, was es getan hätte, und handelt erst, wenn jemand das Flag absichtlich deaktiviert. Das unbedenkliche Verhalten ist Standard, sodass ein Übersehen keine Folgen hat. In der Demonstration gibt das Reparaturtool an, es würde total.py überschreiben, schreibt aber nichts, wodurch der Fehler am Ende weiterhin bestehen bleibt.
Eine execute enthält beide Regeln:
def execute(call):
if call.name == "review":
snapshot = dict(CODE) # a copy, so the reviewer cannot write
src = snapshot[call.args["path"]]
return f"VERDICT: {'off-by-one' if '+ 1' in src else 'looks good'}"
if call.name == "apply_fix":
if call.args.get("dry_run", True): # on by default, turned off on purpose
return f"DRY RUN: would rewrite {call.args['path']}, nothing written"
CODE[call.args["path"]] = call.args["new"]
return f"wrote {call.args['path']}"
Der Prüfer erkennt den Fehler durch das „off-by-one“-Problem, hat aber keine Möglichkeit, daran etwas zu ändern.
Layer 5: Kontextpipelines
Was erreicht das Modell – und zu welchem Preis?
Die Kontextpipeline beschreibt, wie Informationen in das Modell gelangen und wie viel davon zugelassen wird.
Dabei entsteht der Kostenfaktor: Jeder eingehende Token wird in diesem Schritt sowie in allen folgenden Schritten bezahlt, wodurch die vierzigfache Differenz zwischen den verschiedenen Lösungsansätzen entsteht – und zwar hier, nicht im Layer 1.
Eine naive Implementierung lädt alles hoch: die gesamte Datei zu einer Frage bezüglich einer Funktion, das volle Suchergebnis für eine einfache Anfrage sowie die gesamte Historie. Das funktioniert zwar, doch bereits ab Schritt zwanzig muss man erneut für alles bezahlen, was bereits in den Schritten eins bis neunzehn gelesen wurde.
Die Lösung besteht aus Delegation und Verfeinerung. Ein Unteragent arbeitet in einem separaten Fenster, liest Zehntausende von Tokens und gibt ein oder zweitausend zurück. Der Hauptthread überspringt die unverarbeitete Suchausgabe und erhält nur die Antwort.
In Code kommt es darauf an, welcher Zähler ansteigt:
def subagent_search(query):
"""Its own window. The main thread never pays for this reading."""
global subagent_tokens
subagent_tokens += sum(len(v.split()) for v in CORPUS.values())
return next(f"{n}: {b.split('ANSWER:')[1].strip()}"
for n, b in CORPUS.items() if "ANSWER:" in b)
def execute(call):
global main_tokens
distilled = subagent_search(call.args["q"])
main_tokens += len(distilled.split()) # the only line that bills you
return distilled
Die Demonstration ist absichtlich klein und ehrlich gestaltet. Die Fragen und Antworten sind in beiden Durchläufen identisch, doch der Hauptthread verarbeitet in einem Fall 9.602 Token und im anderen nur 2.
Das klärt auch, warum verschachtelte Subagenten enttäuschen. Wenn jeder Schritt auf den vorherigen wartet, entsteht eine Kontextisolation und es gibt keinerlei parallele Beschleunigung. Echte, unabhängige Schritte sollten nebeneinander ausgeführt werden. Andernfalls muss man akzeptieren, dass Isolation das ist, was man erhält – das rechtfertigt oft den Preis, solange man weiß, was die Rechnung umfasst.
Was bisher noch niemand sagen kann
Das öffentliche Vertrauen zu diesem Thema hat die dahinterliegenden Beweise überholt, weshalb die Einschränkungen wichtig sind.
- Hier wurde nichts von Experten überprüft. Stand August 2026 handelt es sich bei jeder Studie, die das Harness isoliert, um einen arXiv-Preprint ohne Peer-Review. Das sorgfältigste Experiment bezeichnet sich selbst als frühe Arbeit unter Workshop-Prüfung, und der bekannteste Rahmenartikel bezeichnet sich als Positionspapier ohne eigenes Experiment. Behandeln Sie alles davon, einschließlich dieses Textes, als unüberprüft und wiederholen Sie den Vergleich in Ihren Aufgaben, bevor Sie Budgete darauf basierend planen.
Die begleitenden Skripte
Zehn kurze Skripte begleiten diese Idee. Jede Schicht wird zusammen mit ihrem Schutzmechanismus ausgeführt, während derselbe Agent ohne diesen ausgeführt wird. Sie laufen offline ohne API-Schlüssel, da ein skriptbasiertes Ersatzmodell das ursprüngliche Modell ersetzt und eine feste Abfolge von Tool-Aufrufen auslöst. Den Unterschied bei der Ausführung zu beobachten, ist besser als auf Vertrauen zu setzen.
git clone https://github.com/paoloap-py/agent-harness-guide
cd agent-harness-guide
python3 run_all.py # all five layers, guard off then on, side by side
python3 test_harness.py # asserts every difference above, 10 checks
Die README beschreibt die fünf Ebenen als Prüfliste mit einer wiederkehrenden Frage: Setzt der Code dies durch oder nur ein Satz in einer Anweisung? Jedes Kästchen, das man nicht durch Angabe eines Dateipfads ankreuzen kann, stellt eine Regel dar, auf die man nur hoffen kann.
Wo das anwendbar ist
Eine gute Regel am falschen Ort zählt kaum. Im obigen Szenario führte das zu fünfzehn Verstößen, elf davon bereits am selben Nachmittag, sowie einem Tag, den niemand zurückhaben möchte. Nur durch den Umzug der Regel wurde sie wirksam – kein einziges Wort darin änderte sich.
Das ist das Wesentliche. Das Modell ist etwas, das man kauft. Alles drumherum muss man selbst aufbauen, und genau das bestimmt, wie viel das System kostet und wie oft es Dinge tut, die man nie genehmigt hat. Beides gehört zur Ingenieurkunst, doch nur das Zweite kann man selbst gestalten.
FAQ
Was genau ist ein Harness für einen Agenten? Alles, was um das Modell herumgefasst wird: der Tool-Loop, die Prüfungen vor der Ausführung, der Sandbox, die Persistenz zwischen Sitzungen, der Überprüfer sowie der Pipeline, der entscheidet, was in jeder Schrittfolge enthalten ist. Der Loop besteht aus sechs Zeilen, oder zwanzig mit Fehlerbehandlung, und alles Weitere gehört zum Harness.
Ist der Harness wichtiger als das Modell? Was die Kosten angeht, ja – der Tokenverbrauch pro gelöstem Auftrag kann bis zu 40-mal variieren. Ob die Aufgaben gelöst werden, hängt davon nicht ab. Gemessene Unterschiede in der Erfolgsrate zwischen verschiedenen Harnesses liegen zwischen 0 und 8 Punkten, wobei es Intervalle gibt, die den Nullwert überschreiten; die Autoren von Terminal-Bench kommen zu dem Schluss, dass die Wahl des Modells in der Regel mehr für die Leistung ausschlaggebend ist.
Wo ist der Harness am teuersten? Im Kontext: Jedes Mal, wenn dem Modell ein Token gezeigt wird, entsteht dafür eine Gebühr. Dadurch wird Layer 5 zum teuersten Element und erklärt, warum die Übertragung einer Suche an einen Subagenten die Rechnung ändert, ohne die Antwort zu verändern.
Zählen Berechtigungsregeln zur Sicherheit?
Nein. Eine Harness-Prüfung betrachtet nur den Aufruf, den der Agent tatsächlich durchführt, während ein Shell-Befehl, ein Symbollink oder ein ..-Pfad auf dasselbe Datei zugreifen kann, ohne überprüft zu werden. Die eigentliche Beschränkung kommt vom Betriebssystem: Starten Sie den Prozess mit keinerlei Zugriffsrechten und fügen Sie anschließend hinzu, was die Aufgabe benötigt.
Soll man einen Harness selbst erstellen oder einen verwenden? Verwenden Sie lieber einen vorhandenen Harness und bleiben Sie dabei. Der Wechsel auf einen anderen löscht das angesammelte Wissen in Ihren Konfigurationsdateien, und jede nach einem echten Fehler hinzugefügte Regel muss erneut erarbeitet werden.
Verwandte Artikel
- Selbstverbessernde Agenten durch Fähigkeiten: Wiederverwendung, Auswahl, Reparatur und Überprüfung — Ein vierteiliges Konzept für Agenten, die ihre eigenen Fähigkeiten verbessern: Speichern von Funktionscode, Verfeinerung von Beschreibungen, Vorschlag überprüfter Korrekturen sowie Überprüfung der Ausgabe vor der Weitergabe.
- Ein Produktions-Harness für Claude Code: Sechs Schichten von Slack bis zum Merged MR — Wie ein sechsschichtiger Harness Slack-Tickets in integrierten Code umwandelt: Eingang von Ereignissen, geschützte Agentenschleifen, dauerhafter Zustand, Selbstheilung, Überwachbarkeit und Steuerung durch den Operator.