Engineering the Agentic Stack · Teil 6

Harness Engineering für AI Agents: den Abschluss verifizieren

Automatische Übersetzung

Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.

Ein Agent kann seinen Turn beenden, obwohl die Arbeit noch nicht abgeschlossen ist. Dieser Artikel richtet sich an Engineers, die Coding Agents entwickeln oder überprüfen und entscheiden müssen, wann sie ein Ergebnis akzeptieren, eine unsichere Operation wiederholen oder die Arbeit in einer anderen Session fortsetzen. Er zeigt außerdem, wie man misst, ob eine zusätzliche Harness-Komponente diese Ergebnisse so weit verbessert, dass sich ihre Kosten lohnen.

Das Harness ist der Anwendungscode rund um den Reasoning Loop des Models. Es liefert Context, prüft vorgeschlagene Actions, protokolliert, was passiert ist, und entscheidet, ob die Nachweise für einen Abschluss ausreichen. Die Runtime führt akzeptierte Actions aus und hält Session, Sandbox und Checkpoint verfügbar. Die früheren Artikel erklären den Reasoning Loop, Memory, Tools und Permissions. Hier geht es darum, was das Harness tut, nachdem das Model eine Action vorgeschlagen oder „fertig“ gesagt hat.

Die Store- und Payment-Beispiele unten sind fiktive Lehrfälle. Das begleitende Lab ist eine deterministische Simulation; es misst keinen laufenden Coding Agent.

Was als fertig gilt

Angenommen, ein Coding Agent soll in einem fiktiven Store-Repository den Schwellenwert für einen automatischen Rabatt von 10 % von $100 auf $75 senken. Er ändert src/checkout.py. Ein Warenkorb von $80 sollte nun einen Rabatt von $8 erhalten. Die Aufgabe hat zwei erforderliche Checks:

  • pytest tests/test_checkout.py prüft die Berechnung.
  • pnpm playwright test tests/checkout_discount.spec.ts prüft das sichtbare Checkout-Ergebnis in einem lokalen Store mit geseedeten Testdaten.

Eine plausible abschließende Antwort ist keiner dieser Checks. Das Harness sollte nur einen Candidate akzeptieren, dessen vollständiger getesteter Snapshot identifiziert ist und dessen zwei erforderliche Checks auf genau diesem Snapshot bestanden wurden. Ein sauberer Commit kann ihn identifizieren; andernfalls verwende einen Digest oder ein Manifest, das relevante untracked Files einschließt. Halte die Tests außerhalb des vom Agent beschreibbaren Scopes oder genehmige Änderungen an ihnen vor dem Grading unabhängig.

Hier haben Model, Harness und Runtime unterschiedliche Aufgaben. Implementierungen können mehrere davon in einem Prozess zusammenfassen, aber ihre Failure Boundaries bleiben unterschiedlich:

KomponenteAufgabeBeispiel für einen Coding Agent
ModelSchlägt Text, einen Tool Call oder eine abschließende Antwort vorSchlägt ein Edit an src/checkout.py vor
Reasoning LoopWählt den nächsten Schritt aus dem verfügbaren ContextInspizieren, editieren, testen, erneut inspizieren
HarnessLiefert Context, validiert und autorisiert Proposals, dispatcht akzeptierte Calls, protokolliert Results und prüft den AbschlussErlaubt Edits unter src/ und verlangt beide benannten Tests
RuntimeFührt akzeptierte Calls aus und hält State außerhalb des Worker-Prozesses am LebenSession Log, Sandbox, Checkpoint Store, Trace Backend

OpenAIs Codex Loop Walkthrough beschreibt den Zyklus: Das Harness stellt einen Prompt zusammen, bittet das Model um die nächste Action, sendet einen akzeptierten Tool Call an die Runtime, hängt das Result an und fragt erneut, bis es das Ergebnis akzeptiert oder die Kontrolle an den User zurückgibt. Für die Rabattänderung sieht ein Durchlauf so aus:

  1. Der Context Builder stellt Task, Repository Instructions, relevante Files, frühere Tool Results und den aktuellen Plan bereit.
  2. Das Model schlägt einen edit_file-Call mit einem Pfad und Replacement Text vor.
  3. Die Tool Boundary validiert die Argumente, prüft den Pfad gegen den erlaubten Scope und fordert ein Approval an, wenn die Operation eines benötigt.
  4. Die Runtime wendet das Edit in der Sandbox an und gibt ein strukturiertes Result zurück.
  5. Das Harness führt pytest tests/test_checkout.py und anschließend pnpm playwright test tests/checkout_discount.spec.ts aus und liest beide Exit Codes.
  6. Ein fehlgeschlagener Check wird zu neuem Context für den nächsten Model-Turn; zwei bestandene Checks machen die Änderung zu einem Completion Candidate.
  7. Das Harness protokolliert Commands, Exit Codes, getesteten Snapshot, Grader und Environment-Versionen dauerhaft; ein Trace kann auf diesen Record verweisen.

Ein Zug der Rabattänderung in drei Lanes. Das Harness baut den Context auf, das Model schlägt einen edit_file-Call auf src/checkout.py vor, und das Harness prüft den Pfad: src/checkout.py ist erlaubt, ../../secrets.env wird abgelehnt, bevor sich eine Datei ändert. Die Runtime wendet die Änderung in der Sandbox an. Das Harness führt pytest und Playwright aus und liest die Exit Codes. Bestehen nicht beide Tests auf demselben Snapshot, geht der Fehler als Context für den nächsten Zug zurück zu Schritt 1; bestehen sie, hält das Harness Snapshot, Befehle, Exit Codes, Grader- und Umgebungsversionen fest, und ein Trace verweist auf diesen Eintrag.Ein Zug der Rabattänderung in drei Lanes. Das Harness baut den Context auf, das Model schlägt einen edit_file-Call auf src/checkout.py vor, und das Harness prüft den Pfad: src/checkout.py ist erlaubt, ../../secrets.env wird abgelehnt, bevor sich eine Datei ändert. Die Runtime wendet die Änderung in der Sandbox an. Das Harness führt pytest und Playwright aus und liest die Exit Codes. Bestehen nicht beide Tests auf demselben Snapshot, geht der Fehler als Context für den nächsten Zug zurück zu Schritt 1; bestehen sie, hält das Harness Snapshot, Befehle, Exit Codes, Grader- und Umgebungsversionen fest, und ein Trace verweist auf diesen Eintrag.

Nach Schritt 2 wurde noch keine Datei geändert. Das Harness kann ../../secrets.env noch ablehnen, für einen destruktiven Command ein Approval verlangen oder einen Run stoppen, der sein Budget ausgeschöpft hat. Nach Schritt 5 liest das Harness die Exit Codes selbst, sodass das Model sein eigenes Edit nicht als bestanden markieren kann.

Ein Completion Record für diese Aufgabe braucht den Candidate Snapshot, beide Commands und Exit Codes sowie die Versionen von Testumgebung und Grader. Er sollte dauerhaft gespeichert sein und außerhalb der Kontrolle des Candidates liegen. Ein Trace kann Proposal, genehmigten Call, Runtime Result und Acceptance Record für die Diagnose verknüpfen. Ein Trace allein ist nicht die Acceptance-Entscheidung.

Ein Trace enthält auch den Request nicht unbedingt. Er kann das Quelldokument oder ein Middleware-Event festhalten, ohne zu zeigen, was der zusammengesetzte Request am Ende enthielt. Wenn du feststellen musst, was das Model erhalten hat, erfasse es am letzten Punkt, bevor der Call hinausgeht, nach jeder Transformation.

OpenAIs Case Study zu Harness Engineering beschreibt eine bootfähige Anwendung pro Worktree sowie Browserzugriff, Logs, Metrics und Traces. Dieses Setup machte ein Latency-Ziel für eine User Journey für den Agent überprüfbar. Es zeigt die Voraussetzung hinter einer Acceptance-Regel: Die relevante Anwendung und ihre Signals müssen in der Testumgebung verfügbar sein. Lopopolo, der Autor dieser Case Study, pflegt einen Field Guide für Harness Engineering mit denselben zwei Hebeln: das Model unverändert lassen und Context sowie Tools darum herum entwickeln. Der Qualitätsmaßstab einer Organisation, ihre Procedures und Approval-Regeln liegen außerhalb dessen, was ein allgemeines Model weiß; das Harness liefert sie als Repository Instructions, Permission Rules und Acceptance Checks.

Betrachte drei oberflächlich ähnliche Ergebnisse:

Nachweise am Ende des RunsEntscheidung des HarnessWarum
Unit-Test besteht; Browser-Ergebnis fehltFortsetzen oder als unvollständig stoppenFür einen erforderlichen Check gibt es kein Ergebnis
Beide Tests bestehen, dann editiert der Agent eine DateiBeide Checks erneut ausführenDie protokollierten Ergebnisse gehören zu einem älteren Snapshot
Beide Tests bestehen auf demselben identifizierten SnapshotKommt für die Acceptance infrageDie erforderlichen Nachweise passen zum Candidate

„Kommt infrage“ lässt Raum für ein menschliches Approval oder eine andere erforderliche Regel; es erlaubt dem Model nicht, seine eigene Arbeit zu bestätigen. Ein Evaluator mit frischem Context kann ein visuelles Ergebnis anhand einer Rubric prüfen, wenn ein Exit Code das Kriterium nicht ausdrücken kann. Vergleiche diesen Evaluator mit Human Reviews, bevor du ihn über den Abschluss entscheiden lässt. Einen erforderlichen deterministischen Check kann er nicht ersetzen.

Für den Research Agent dieser Serie würde eine vorgeschlagene deterministische Abschlussregel das Report-Schema prüfen und bestätigen, dass die angefragten Ticker vorkommen. Diese Checks können nicht belegen, dass die Analyse sachlich stimmt; dafür braucht es weiterhin eine Prüfung der Quellen. Anthropics Evaluation Guidance empfiehlt für Coding-Aufgaben stabile Umgebungen und gründliche Tests sowie eine menschliche Kalibrierung von Model Gradern.

Wo jede Regel durchgesetzt wird

Die Anforderung, dass tests/checkout_discount.spec.ts besteht, gehört in deterministischen Code und nicht in den Prompt. Ein Prompt kann das Model daran erinnern, den Test auszuführen. Er kann das Model nicht daran hindern, ohne Nachweise Erfolg zu melden. Andere Regeln passen an andere Stellen:

Regel platzieren inGeeignet fürBeispiel
Prompt oder SkillSuchreihenfolge, Coding Conventions und PlanformatVor dem Editieren von Checkout-Code AGENTS.md lesen
Tool BoundaryArgumentvalidierung, erlaubte Pfade, Approvals und Tool-ZugriffWrites nur unter src/ erlauben
Deterministischer CodeBudgets, Timeouts, Retries, Test-Exit-Codes und Release-AnforderungenRun offen halten, solange der Playwright-Test fehlschlägt
Evaluator mit frischem ContextVisual Review oder Kriterien, die ein Urteil erfordernEin generiertes Diagramm mit einer schriftlichen Rubric vergleichen

Die früheren Parts dieser Serie entscheiden jeweils eine Sache innerhalb dieses Walkthroughs:

Früherer PartWas er für diesen Turn entscheidetWo er im Walkthrough wirkt
Part 2 – MemoryWelcher frühere State in den Prompt gelangtSchritt 1, der Context Builder
Part 3 – Tool UseWelche Actions existieren und wie ein validiertes Result aussiehtValidierung in Schritt 3 und Result in Schritt 4
Part 4 – SecurityOb dieser konkrete Call jetzt ausgeführt werden darfSchritt 3, Pfadprüfung und Approval
Part 6 – dieser ArtikelOb die resultierenden Nachweise den Run beendenSchritte 5 bis 7, die Acceptance Checks

Die Tabelle ordnet Verantwortlichkeiten zu, nicht die Reihenfolge der Execution. Parts 3 und 4 teilen sich Schritt 3: Derselbe Harness-Code, der ein fehlerhaftes Argument ablehnt, weist auch einen Call zurück, der zwar erlaubt, aber noch nicht genehmigt ist. Wenn Validierung und Autorisierung in getrennten Services laufen, übergib die validierten Argumente über diese Grenze, damit die Autorisierungsentscheidung für den Call gilt, der tatsächlich ausgeführt wird.

Einen Timeout als unbekanntes Ergebnis behandeln

Das Rabatt-Edit ist lokal, und seine Tests lassen sich erneut ausführen. Eine Payment-Migration im selben fiktiven Store bringt ein anderes Problem mit sich. Angenommen, der Agent ruft create_test_order gegen einen Staging Service auf, und der Call läuft in einen Timeout. Der Service hat die Order möglicherweise angelegt, obwohl das Harness nie eine Response erhalten hat. Ein blinder Retry kann eine zweite Order anlegen.

Der Name der Action oder eine vom Model generierte Tool-Call-ID reicht nicht aus, um sie wiederherzustellen. Persistiere vor dem Dispatch eine von der Anwendung verwaltete Operation ID zusammen mit den genehmigten Argumenten. Der Service muss Retries für diese ID deduplizieren oder einen Status Lookup anbieten, mit dem sich das Ergebnis abgleichen lässt. Verwende nach einem Neustart für dieselbe beabsichtigte Order dieselbe ID erneut; ein geänderter Order-Request braucht eine neue Entscheidung. AWS’ Guidance zu idempotenten APIs beschreibt den Client Identifier und die serverseitige Atomarität, die dafür nötig sind. Prüfe, wie lange der Provider Keys aufbewahrt: Der Contract von Stripe erlaubt es beispielsweise, sie nach mindestens 24 Stunden zu entfernen, sodass ein späterer Retry einen Abgleich statt eines erneuten Sendens braucht.

Die Abbildung folgt einem create_test_order-Call mit Timeout zweimal: einmal blind erneut gesendet und einmal erneut gesendet mit der gespeicherten Operation ID.

Zwei Abläufe zwischen dem Harness und einem Staging-Order-Service. Beim blinden Resend ruft das Harness create_test_order auf, der Service legt Order 123 an, die Response geht verloren und das Harness sieht einen Timeout; das Harness ruft erneut auf, und der Service enthält nun Order 123 und eine zweite Order. Beim Resend mit derselben Operation ID speichert das Harness zuerst die Operation ID und die genehmigten Argumente, ruft create_test_order mit der ID auf, und die Response geht verloren. Es sendet erneut mit derselben ID, der Service legt keine neue Order an und liefert Order 123, und das Harness hält das abgeglichene Ergebnis fest.Zwei Abläufe zwischen dem Harness und einem Staging-Order-Service. Beim blinden Resend ruft das Harness create_test_order auf, der Service legt Order 123 an, die Response geht verloren und das Harness sieht einen Timeout; das Harness ruft erneut auf, und der Service enthält nun Order 123 und eine zweite Order. Beim Resend mit derselben Operation ID speichert das Harness zuerst die Operation ID und die genehmigten Argumente, ruft create_test_order mit der ID auf, und die Response geht verloren. Es sendet erneut mit derselben ID, der Service legt keine neue Order an und liefert Order 123, und das Harness hält das abgeglichene Ergebnis fest.

Das Harness kann diese Entscheidungen nur dann ohne Rückfrage an das Model treffen, wenn jedes Tool einen expliziten Contract hat. Für create_test_order umfasst dieser Contract:

  • validierte Argumente, damit fehlerhafte Eingaben vor der Execution abgelehnt werden;
  • ein strukturiertes Result wie { "order_id": "123", "created": true }, damit spätere Checks keinen Freitext parsen müssen;
  • eine Effect Category: ob der Call nur liest oder eine Datei, einen Datensatz oder einen externen Service verändert, und ob eine Wiederholung sicher ist;
  • eine Timeout- und Retry Policy, damit eine verlorene Response keine unbegrenzte Folge von Calls auslöst;
  • eine Permission Rule: Das Lesen des Order-Status darf automatisch laufen, während das Anlegen einer Order eine Bestätigung erfordern kann.
Vom Harness beobachtetes ResultNächste Action
Ein read-only get_order_status-Call gibt 503 zurückInnerhalb eines Limits wiederholen, wenn dieser Lookup sicher wiederholbar ist
create_test_order läuft in einen Timeout; der Lookup findet die Order unter ihrer Operation IDDie vorhandene Order verwenden und das abgeglichene Result protokollieren
create_test_order läuft in einen Timeout; der Service kann weder deduplizieren noch den Status prüfenDas Ergebnis als unsicher belassen und vor einer weiteren Erstellung eine Entscheidung anfordern

Dieser letzte Zustand muss einen Worker-Absturz überstehen. Ihn „fehlgeschlagen“ zu nennen, wäre falsch: Die Order könnte existieren. Der dauerhafte Operation Record braucht den genehmigten Payload, die Operation ID, den Dispatch-Status, das Server-Result oder den Status Lookup und jedes Approval. Der Trace hilft, diesen Record zu finden; das Transcript zeigt nur, was das Model gesehen hat.

Eine Tool Description hilft dem Model, eine Action zu wählen. Sie erteilt keine Permission und beweist nicht, dass ein Retry sicher ist. Wenn der Order Service über MCP bereitgestellt wird, muss der Client trotzdem seine eigene Validierung und Trust Policy anwenden, und der Server muss Zugriffskontrollen durchsetzen. Die MCP-Tools-Spezifikation besagt, dass Clients Behavior Annotations von Tools als nicht vertrauenswürdig behandeln müssen, sofern sie nicht von einem vertrauenswürdigen Server stammen. Ein Server, der create_test_order als read-only beschreibt, macht es dadurch nicht read-only. Die Spezifikation schreibt keine einheitliche Trust-Einstellung vor; schreibe deshalb für dein Deployment eine explizite Policy, die festlegt, welche Server-Metadaten Permission- oder Retry-Entscheidungen beeinflussen dürfen.

MCP bietet außerdem einen Standardweg, lang laufende Calls nachverfolgbar zu halten. Mit der offiziellen Tasks-Erweiterung kann ein Server auf tools/call statt mit einem Endergebnis mit einem Task-Handle antworten. Der Client fragt dann mit tasks/get den Stand ab oder bricht die Arbeit mit tasks/cancel ab. Die Tasks-Spezifikation sieht vor, dass Clients Task-IDs persistieren sollten, damit das Polling nach einem Absturz fortgesetzt werden kann; speichere die Task-ID daher im selben Operation Record. Ein Task im Status completed bedeutet nicht, dass das Tool erfolgreich war: Ein Tool Result mit isError: true schließt den Task trotzdem ab, und nur ein Protokollfehler setzt ihn auf failed. Client und Server handeln die Unterstützung für jede Anfrage aus; prüfe daher, dass beide Seiten sie implementieren, bevor du dich darauf verlässt.

Fortschritt an die nächste Session übergeben

Nun muss der fiktive Agent den Checkout von Payment Adapter v1 auf v2 migrieren. Er hat Handler und Client geändert, eine lokale Payment Sandbox gestartet, und tests/payment_migration.spec.ts schlägt noch fehl. Die Aufgabe dauert länger als eine Model-Session. Eine Summary der alten Conversation kann behaupten, dass die Sandbox läuft, obwohl der Prozess längst beendet ist.

Die nächste Session braucht drei verschiedene Arten von State:

StateWas die nächste Session prüfen muss
ConversationDen Request, Entscheidungen und relevante Tool Results
Workspace und RuntimeAktuelle Files, Branch, Prozesse und Testumgebung
Task ProgressAbgeschlossene Checks, ausstehendes Approval oder unsicherer Write und nächste Action

Compaction ersetzt ältere Messages durch eine kürzere Summary, damit die aktuelle Session weiterlaufen kann. Ein Progress Handoff ist Document Memory für die nächste Session. Wenn die alte Conversation veraltete Annahmen enthält, kann das Harness eine neue Session mit dem Handoff und dem aktuellen Workspace starten; einen abgestürzten Worker zu ersetzen und seine Prozesse wiederherzustellen, ist eine separate Aufgabe der Runtime.

Ein Checkpoint kann den Execution State bewahren, beweist aber nicht, dass eine externe Payment-Operation committed wurde oder dass ein Prozess noch läuft. Ein Progress Handoff hilft, wenn dem nächsten Model Context der nutzbare Task Status fehlt. Protokolliere Branch, geänderte Files, letzten Command und sein Result, ausstehende Operation IDs und den nächsten offenen Schritt. Prüfe diese Angaben beim Fortsetzen gegen den Live-Workspace und den Service, bevor du handelst. Anthropics Experimente mit Long-Running Coding Agents nutzten Git History und eine Progress-Datei, damit spätere Sessions die Arbeit wieder aufnehmen konnten. Anthropics späterer Report zum Harness Design merkt an, dass Handoffs zusätzliche Orchestration, Tokens und Wall Time kosten.

Der Handoff sollte an Bedingungen geknüpft sein. Ein kleines Edit, das in einer Session fertig wird, braucht keinen. Selbst eine lange Aufgabe braucht vielleicht keine weitere Datei, wenn ihr Checkpoint den erforderlichen Fortschritt bereits enthält und die nächste Session ihn nutzen kann. Prüfe, ob eine fortgesetzte Session abgeschlossene Arbeit wiederholt, einen ausstehenden Write übersieht oder auf einer veralteten Umgebung handelt. Behebe den fehlenden State, statt standardmäßig eine Summary hinzuzufügen.

Den Failure diagnostizieren, bevor man eine Komponente hinzufügt

Die drei Fälle oben erzeugen unterschiedliche Traces: einen fehlenden Acceptance-Test, einen unsicheren externen Write und wiederholte Arbeit nach dem Fortsetzen. Ihre Fixes gehören an unterschiedliche Stellen. Ein Browser-Ergebnis, das im Acceptance Record fehlt, verlangt einen erforderlichen Completion Check. Eine unsichere Order verlangt serverseitige Deduplizierung oder einen Abgleich. Ein wiederholtes Edit nach einer neuen Session verlangt nutzbaren Progress State.

Protokolliere für den nächsten Run Task- und Harness-Version, vorgeschlagene Action, Approval, Tool Result, getesteten Snapshot, Acceptance-Ergebnis, Latency und Kosten. Erfasse die Input-Token-Zahlen getrennt von den abgerechneten Input-Kosten, einschließlich Cached Reads: Zwei Harnesses, die sich identisch verhalten, können unterschiedlich abgerechnet werden, je nachdem, wie jeder seinen Request zusammensetzt und ob der Cache des Providers warm war. Halte Secrets und unnötige personenbezogene Daten aus Traces heraus. Verknüpfe externe Operation Records per ID, da ein clientseitiger Timeout nicht zeigen kann, was der Server getan hat. Wenn ein Failure erneut auftritt, schreibe einen Regression Case, der ihn reproduziert. AI Agent Evaluation in Production entwickelt diesen Prozess vom Trace zum Test.

Jedes wiederkehrende Symptom legt eine kleine Änderung und eine Messgröße nahe, die zeigen würde, ob sie geholfen hat:

Wiederkehrendes SymptomKleine Änderung zum AusprobierenWas zu messen ist
Read-only Lookups schlagen vorübergehend fehlBegrenzter Retry mit BackoffRecovery-Rate, zusätzliche Calls, Wall Time
Fortgesetzte Sessions wiederholen abgeschlossene ArbeitStrukturierter Progress HandoffDoppelte Tool Actions nach dem Fortsetzen
Erforderliche Tests fehlen beim AbschlussAbschluss ablehnen, bis jeder erforderliche Check bestehtAufgaben, die ohne alle erforderlichen Checks akzeptiert wurden
Visuelle Fehler überstehen deterministische ChecksEvaluator mit frischem Context und einer RubricGefundene Fehler, falsche Ablehnungen, Review-Zeit
Der Agent editiert außerhalb seines ScopesEngere Tool PermissionBlockierte Calls und manuelle Overrides
Abgerufene Memory verdrängt die aktuelle AufgabeAbgerufene Fakten begrenzen; vor dem Einfügen rankenFür Recall verbrauchte Tokens, abgeschlossene Aufgaben, Kosten
Eine genannte Regel oder ein Status erreicht das Model nieDen zusammengesetzten Request erfassen, bevor der Call hinausgehtRequests ohne die Regel; Runden, bis sie wieder auftaucht

Bevor du eine Retry Policy, einen Handoff oder einen Evaluator hinzufügst, benenne den Failure, den er ändern soll, und die Metrik, die eine Verbesserung zeigen würde. Rechne auch die Kosten mit: Mehr akzeptierte Aufgaben nützen nichts, wenn die zusätzlichen Calls die Review-Zeit verdoppeln oder doppelte Orders anlegen. Autorisierung, Isolation, Datenschutzregeln und verpflichtende Acceptance Checks sind Anforderungen; ein kleiner Erfolgs-Benchmark ist kein Grund, sie zu entfernen. Teste diese Einschränkungen mit expliziten verbotenen Fällen.

Eine optionale Änderung nach der anderen messen

Eine Ablation vergleicht ein Harness mit und ohne eine Komponente, während der Rest des Experiments unverändert bleibt. Verwende dieses Protokoll:

  1. Friere Model-Version und Provider-Endpoint, Task-Instanzen, Umgebung samt Compute-Limits, Grader und Prompts außerhalb der getesteten Komponente ein. Allein Ressourcenlimits können den Score eines Agent-Benchmarks verändern; der Evals-Artikel behandelt Anthropics Messung.
  2. Gib beiden Varianten dasselbe Gesamtbudget an Tokens, Zeit und Dollar.
  3. Lege die Anzahl der Versuche oder die Abbruchregel vor dem Vergleich fest.
  4. Führe in beiden Varianten dieselben Task-Instanzen aus und wiederhole jede Aufgabe, weil der Model-Output variiert.
  5. Berichte den Mittelwert mit seiner Streuung oder seinem Konfidenzintervall.
  6. Zähle jeden gestarteten Versuch, einschließlich Timeouts, Policy-Stopps, Harness-Abstürzen und Evaluator-Failures.

Für die Payment-Migration ist der optionale Progress Handoff ein Kandidat. Starte jedes Control-Treatment-Paar vom selben Repository-Commit und demselben geseedeten Checkpoint, mit dem Handoff als einzigem Schalter.

Die primäre Messgröße könnten doppelte Tool Actions nach dem Fortsetzen sein: eine Operation an einem Artefakt, das die vorherige Session bereits abgeschlossen hatte. Berichte außerdem akzeptierte Aufgaben, durchgerutschte Fehler, Wall Time, Model- und Evaluator-Kosten sowie Review-Minuten. Lege Anzahl der Versuche und Abbruchregel vorab fest und berichte dann die Streuung statt nur den Mittelwert. Wenn der Handoff Duplikate reduziert, aber mehr kostet, als er einspart, ist das ein nützliches negatives Ergebnis.

Eine Tabelle zeigt: Model-Version und Endpoint, Task-Instanzen, Repository-Commit und vorbereiteter Checkpoint, Umgebung und Compute-Limits, Grader und Prompts sowie Budget sind in beiden Armen identisch, Security- und Acceptance-Checks sind in beiden aktiv, und nur das Progress Handoff unterscheidet sich: aus in der Kontrollgruppe, an in der Behandlungsgruppe. Ein Raster paart für jeden Task und jede Wiederholung einen Kontrolllauf und einen Handoff-Lauf vom selben Start; ein Handoff-Lauf lief in einen Timeout und zählt trotzdem. Eine Karte listet, was pro Arm zu berichten ist: zuerst doppelte Tool-Aktionen nach dem Resume, dann akzeptierte Tasks, durchgerutschte Defekte, Wall Time, Kosten und Review-Minuten, jeweils mit Mittelwert und Streuung.Eine Tabelle zeigt: Model-Version und Endpoint, Task-Instanzen, Repository-Commit und vorbereiteter Checkpoint, Umgebung und Compute-Limits, Grader und Prompts sowie Budget sind in beiden Armen identisch, Security- und Acceptance-Checks sind in beiden aktiv, und nur das Progress Handoff unterscheidet sich: aus in der Kontrollgruppe, an in der Behandlungsgruppe. Ein Raster paart für jeden Task und jede Wiederholung einen Kontrolllauf und einen Handoff-Lauf vom selben Start; ein Handoff-Lauf lief in einen Timeout und zählt trotzdem. Eine Karte listet, was pro Arm zu berichten ist: zuerst doppelte Tool-Aktionen nach dem Resume, dann akzeptierte Tasks, durchgerutschte Defekte, Wall Time, Kosten und Review-Minuten, jeweils mit Mittelwert und Streuung.

Das SWE-agent-Paper liefert ein eng abgegrenztes Beispiel für eine Interface-Ablation. Mit GPT-4 Turbo auf dem SWE-bench-Lite-Split mit 300 Aufgaben und einer Obergrenze von $4 pro Aufgabe änderte es jeweils ein Interface-Feature:

Interface-ÄnderungGelöst
Vollständiges SWE-agent-Interface (Referenz)18.0%
Editor ohne Linting15.0%
Vollständige Datei statt eines Viewers mit 100 Zeilen12.7%
Vollständige Observation History statt der letzten fünf15.0%

Das sind Ergebnisse für dieses Model und diesen Benchmark, keine zu erwartenden Verbesserungen für diesen fiktiven Store. Übertragbar ist die Methode: eine benannte Interface-Entscheidung unter einem festen Setup testen und die Bedingungen zusammen mit der Zahl berichten.

Gebündelte Vergleiche sind häufig, und sie können die Änderung, die geholfen hat, nicht benennen. LangChain berichtet für deepagents-cli eine Verbesserung auf Terminal-Bench 2.0 von 52.8% auf 66.5% bei festem Model, nachdem System Prompt, Tools und Middleware gemeinsam geändert wurden. OpenAIs ARC-AGI-3-Report zeigt das umgekehrte Risiko: Ein Score hat sich fast verdreifacht, als das Harness Reasoning beibehielt und Compaction nutzte, zwei API-Einstellungen, die gemeinsam geändert wurden. Protokolliere API, Beibehaltung des Reasonings, Compaction Policy und Budgets zusammen mit der Model-ID; andernfalls kann eine scheinbare Model-Regression eine fehlende Fähigkeit im Adapter sein.

Wer jeweils nur eine Änderung testet, findet auch Änderungen, die zu klein wirken, um etwas auszumachen. Anthropics Postmortem zu Claude Code vom April 2026 berichtet, dass die API nicht betroffen war, aber drei Änderungen auf Produktebene die Qualität senkten: ein niedrigerer Standardwert für den Reasoning-Aufwand, ein Bug, der nach einer Leerlaufphase der Session bei jedem Turn das frühere Reasoning löschte, und eine System-Prompt-Zeile, die den Text zwischen Tool Calls begrenzte. Als Anthropic eine Ablation dieser einen Zeile auf einer breiteren Menge von Evals durchführte, zeigte eine davon einen Rückgang von 3% für Opus 4.6 und Opus 4.7. Eine Prompt-Zeile oder eine Standardeinstellung ist eine Harness-Änderung; teste sie wie eine.

Validiere Komponenten erneut, wenn sich das Model ändert. Anthropics Report zu Long-Running Applications ist eine qualitative Case Study: Als Opus 4.6 erschien, entfernte das Team Harness-Komponenten nacheinander und stellte fest, dass Evaluator-Calls bei Aufgaben, die das Model nun allein erledigte, zu Overhead wurden, während sie nahe an seiner Leistungsgrenze weiterhin halfen.

Nimm einen Safety Stop des Providers in die Failure Suite auf: Er sollte den Dispatch stoppen und zum Review führen, nicht zu einem Retry. Part 4 behandelt die Details.

Den Vergleich im begleitenden Lab ausprobieren

Das harness-demo-Projekt bei Commit 517353f3 macht die Ablationsregel ausführbar. Seine zwölf generischen synthetischen Aufgaben, etwa fix-parser-edge-case, split-large-module und wire-browser-test, deklarieren jeweils einen Schwierigkeitsgrad und vier Bedingungen: ein unzuverlässiges Tool, verlorener Fortschritt, eine übersehene Implementierungslücke und ein mehrdeutiger Abschluss. Der Simulator leitet eine fünfte für schwierige Aufgaben ab, die zusätzlich eine Progress-Datei brauchen: Ohne context_reset behält Compaction veraltete Annahmen bei. Eine reine Funktion markiert eine Aufgabe als bestanden, wenn die gewählte Konfiguration jede zutreffende Bedingung abdeckt. Es gibt kein Model, kein externes Tool und keine echte Coding-Aufgabe. Ergebnisse und Token-Zahlen sind simulierte Lehrdaten und kein Beleg dafür, dass ein Harness mit fünf Komponenten einen laufenden Agent verbessert.

Klone die fixierte Revision und führe Checks und Simulation mit installiertem uv aus:

git clone https://github.com/slavadubrov/harness-demo.git
cd harness-demo
git checkout 517353f3a47541d66099983170fe90812b6ef23b
make check
make run
make failures

make check führt Ruff und sieben Unit-Tests aus, darunter einen Test, der einen Vergleich mit zwei geänderten Komponenten ablehnt. make run gibt eine kumulative Lehrmatrix und fünf Leave-one-component-out-Paare aus. make failures benennt jeden simulierten Failure; die vollständige Konfiguration endet mit all synthetic tasks pass.

In die vollständige Konfiguration zurückgebrachte KomponenteOhne sieMit ihr
Retry Policy8/1212/12
Progress Handoff7/1212/12
Fresh Evaluator8/1212/12
Fail-closed Acceptance7/1212/12
Context Reset10/1212/12

Die Zeilen sind deterministische Folgen der Task-Flags und des Grader-Codes. Sie sind keine Schätzungen einer Effektgröße in einem laufenden System. Die vorherige kumulative Matrix fügt in einem Übergang mehrere Komponenten hinzu und kann nicht zeigen, welche davon ihre Änderung verursacht hat.

Die Datei model.py des Labs verhindert, dass eine gebündelte Änderung als Ablation bezeichnet wird: Sie vergleicht jedes Komponentenfeld eines Paars, und ihr Regressionstest liefert ein ungültiges Paar, bei dem Retry und Evaluator beide geändert sind. Diese eigenständige Version des Guards läuft auf einem gültigen Progress-Handoff-Paar:

from dataclasses import dataclass, fields

@dataclass(frozen=True)
class Config:
    progress_handoff: bool = False
    evaluator: bool = False
    retry_policy: bool = False
    fail_closed_acceptance: bool = False
    context_reset: bool = False

def changed_components(control: Config, treatment: Config) -> tuple[str, ...]:
    return tuple(
        field.name
        for field in fields(control)
        if getattr(control, field.name) != getattr(treatment, field.name)
    )

control = Config(progress_handoff=False, evaluator=True, retry_policy=True)
treatment = Config(progress_handoff=True, evaluator=True, retry_policy=True)
assert changed_components(control, treatment) == ("progress_handoff",)

Um die Methode in ein echtes Projekt zu übertragen, ersetze die synthetischen Task-Flags durch eine versionierte Task Suite und die simulierten Token-Zahlen durch Trace-Messungen. Halte die Acceptance-Regeln außerhalb der Kontrolle des Candidates. Ein festes Model allein ist noch kein kontrollierter Vergleich, wenn sich auch Prompts, Testdaten, Permissions oder die Reasoning- und Compaction-Einstellungen der API geändert haben.

Den Code hinter einem Verhalten finden

Ein Harness, das seine Ablationen besteht, kann das Model, für das es abgestimmt wurde, trotzdem überdauern, und eine spätere Änderung braucht den Code, der ein Verhalten implementiert. Eine Anfrage wie „Secrets in jedem Capture-Pfad maskieren“ benennt ein Verhalten, keine Datei. In einem produktiven Harness kann sich dieses Verhalten über mehrere Execution Stages und gemeinsamen State erstrecken.

Ein Preprint von Wang et al. aus dem Jahr 2026 schlägt für diese Suche ein Harness Handbook vor; das Paper nennt sie Behavior Localization. Statische Analyse extrahiert einen Programmgraphen, ein LLM gruppiert dessen Einheiten zu Execution Stages, und ein Maintainer oder Coding Agent steigt von einem Systemüberblick zu quellbasierten Einträgen für eine Funktion oder Datei ab. Jeder Eintrag muss sich gegen das Live-Repository auflösen lassen und wird nach jeder Änderung neu synchronisiert.

Die Anfrage „Secrets in jedem Capture-Pfad maskieren“ gelangt in ein Handbook an der Systemübersicht, die zu vier Execution-Stufen führt; die Anfrage berührt Stufe 2 und Stufe 4. Jede dieser Stufen listet Einträge für eine Funktion oder eine Datei. Jeder Eintrag wird gegen das Live-Repository geprüft: drei werden aufgelöst, einer wird nicht gefunden. Nur aufgelöste Einträge gehen in den Edit-Plan, und das Handbook wird nach jedem nicht leeren Diff neu synchronisiert.Die Anfrage „Secrets in jedem Capture-Pfad maskieren“ gelangt in ein Handbook an der Systemübersicht, die zu vier Execution-Stufen führt; die Anfrage berührt Stufe 2 und Stufe 4. Jede dieser Stufen listet Einträge für eine Funktion oder eine Datei. Jeder Eintrag wird gegen das Live-Repository geprüft: drei werden aufgelöst, einer wird nicht gefunden. Nur aufgelöste Einträge gehen in den Edit-Plan, und das Handbook wird nach jedem nicht leeren Diff neu synchronisiert.

Bei zwei Open-Source-Harnesses erzeugte ein Planner, der über das Handbook routete, Edit-Pläne, die Judges häufiger bevorzugten, und das mit weniger Tokens. Die Studie bewertete Pläne statt ausgeführter Änderungen und berichtete keine Unsicherheit; behandle den Ansatz daher als Forschungsstand.

Den Failure nutzen, um den Verantwortlichen zu finden

Diese Serie hat sich vom Reasoning Loop nach außen aufgebaut. Wenn ein Run fehlschlägt, suche die erste Verantwortlichkeit, die den Failure verhindern oder erkennen kann:

Was passiert istZu untersuchen
Schlechter nächster Schritt, obwohl die richtigen Informationen verfügbar warenModel und Reasoning Loop
Frühere Entscheidung verloren oder abgeschlossene Arbeit wiederholtContext-Zusammenstellung und Memory
Konnte eine Action nicht ausdrücken oder ihr Result nicht lesenTool Contract
Hat eine verbotene Action ausgeführtPermission Check und der durchsetzende Service
Worker ist abgestürzt und seine Umgebung ist verschwundenRuntime und Checkpoint
Erfolg ohne erforderliche Nachweise gemeldetAcceptance-Regel des Harness und getesteter Snapshot

Ich würde mit einem leistungsfähigen Model, eng gefassten Tools, einer Sandbox und einem expliziten Acceptance Check beginnen. Protokolliere den ersten Failure, füge die kleinste Kontrolle hinzu, die ihn adressiert, und vergleiche mit deaktivierter Kontrolle. Halte Regel und Trace zusammen, damit das nächste Model-Upgrade dir einen Grund gibt, die Komponente zu behalten oder zu entfernen. Protokolliere für jede Komponente, wer sie pflegt, wie viele Tokens oder Sekunden sie hinzufügt und welcher Regressionstest ihre Entfernung rechtfertigen würde. Wer sechs Monate später progress_handoff=True sieht, sollte die fehlgeschlagenen Traces finden können, die sie gerechtfertigt haben, und die Regression Cases, die sie weiterhin dort halten.

Referenzen

Der Code des Market Analyst Agent aus dieser Serie ist auf GitHub verfügbar.