Ein Benchmark muss beweisen, dass er der Uhr folgt
Sobald ein Agent jede Zahl drückt, die du ihm gibst, ist das Modell nicht mehr der Teil, über den es sich zu streiten lohnt. Dir gehört der Nachweis, dass Zahl und Ziel sich gemeinsam bewegen, und dieser Nachweis verfällt, sobald der Optimierer den Bereich verlässt, in dem du ihn geprüft hast.
Anthropic hat diese Woche beschrieben, wie ein kleines Team claude.ai und die Desktop-App in zwei Wochen ungefähr dreimal schneller gemacht hat. Die Zahlen sind fast absurd: Die Zeit bis zu einer Seite, in die man tippen kann, fiel bei einem frischen Laden im 75. Perzentil von 3,1 auf 0,55 Sekunden, und mehr als dreitausend Änderungen wurden gemergt, ohne einen Vorfall bei Kunden oder einen Rollback. Zwölf der dreizehn Ziele fielen bis Tag drei, großteils durch die geplanten Projekte, darunter ein statischer Composer, der direkt ins HTML gebacken wurde, und ein vorkompilierter V8-Code-Cache. Danach übernahm Claude den Großteil des Kletterns, über mehr als hundertfünfzig parallele Threads. Der Satz, den die Autoren ins Zentrum stellen, lautet: „With Claude, measuring something makes it tractable."
Der Post ist auch ungewöhnlich ehrlich, was den Haken angeht. Bevor ein neuer Benchmark bleiben durfte, schrieb jemand das hier in den Channel:
please prove that hill climbing against each of these can result in
measurable wall clock perf wins. we’ll unship the benches for any
candidates that cannot prove thatund der Beweis steht unter einem Diagramm mit dem Titel „Does the count track the clock?". Die Idee, einen Proxy zu validieren, bevor man einen Agenten daran hochklettern lässt, stammt also von ihnen und ist klar ausgesprochen. Was der Post nicht sagt: wie lange diese Antwort stimmt, wenn der Agent weiterklettert. Genau darüber will ich streiten.
Warum das Labor eine andere Zahl brauchte
Das Team wollte schneller iterieren, als es deployen konnte. Claude konnte stundenlang arbeiten, auch über Nacht, und bei jedem Prototyp auf Felddaten zu warten, hätte es ausgebremst. Also brauchten sie Labormessungen, und Wall-Clock-Zeit im Labor ist verrauscht; im Post heißt es: „milliseconds are too flaky to use as a CI gate." Die Ziele selbst blieben Wall-Clock-Werte, p75 echter Nutzer pro Journey. Die Laborzahlen waren Stellvertreter dafür.
Sam, einer der Engineers, fragte, ob man stattdessen JavaScript-Instruktionen zählen könne. Claude antwortete mit einer Methode: den Benchmark unter Valgrind mit node --predictable laufen lassen, ein Lauf, keine Statistik. Für Browser-Pfade, wo Chromium kein Zählen von Instruktionen anbietet, listete es andere deterministische Zähler auf: React-Commits pro Interaktion, Funktionsaufrufe aus V8s Precise Coverage, Layout- und Style-Neuberechnungen, DOM-Mutationen. Elf Minuten später liefen fünf Threads, einer pro Messung. Die Regel, die das Team für alle aufstellte:
We treated every new benchmark with some skepticism. Each one had two jobs:
first, a metric Claude could move in the lab; second, a guardrail in CI with
a number that could only ratchet down. If a benchmark was flaky, or if it
didn’t actually correlate with user latency, we threw it out rather than let
Claude climb the wrong hill.Was Goodhart tatsächlich gesagt hat
Charles Goodharts Formulierung von 1975 ging um Geldpolitik, und sie ist schärfer als die Paraphrase, die herumgeht:
any observed statistical regularity will tend to collapse once pressure is
placed upon it for control purposes.Das entscheidende Wort ist „observed". Die Regelmäßigkeit wurde beobachtet, als kein Druck auf ihr lag, und Druck ändert das Regime. Dafür muss niemand schummeln. Ein Optimierer muss nur immer wieder Richtungen finden, in die sich die Zahl bewegt, und ein Agent, der fünfzig oder hundert PRs gegen einen einzigen Benchmark öffnet, ist ungefähr so viel Druck, wie eine Zahl je abbekommen wird.
Manheims und Garrabrants Taxonomie der Goodhart-Effekte hat einen Namen für die nächstliegende Version dieses Problems: Extremal Goodhart. Eine Beziehung zwischen Proxy und Ziel gilt in dem Bereich, in dem sie beobachtet wurde, und Selektion drückt das System in Bereiche, in denen sie vielleicht nicht gilt. Sie unterteilen das weiter, in eine Beziehung, die von Anfang an nur ungefähr stimmte, und eine, die lokal stimmt, anderswo aber anders aussieht. Von außen lässt sich nicht sagen, welche hier zutrifft, und der Zähler kann es auch nicht. Die zwei gemessenen Pfade zeigen die Beziehung in dem Bereich, in dem der Agent gestartet ist, und nirgendwo sonst.
Ein verwandtes Argument habe ich vor Monaten zum Such-Ranking gemacht: Ein Ranker, der Qualität nicht messen kann, greift zu Proxys, die im Schnitt mit ihr korrelieren, und der Grenzfall geht im Durchschnitt verloren. Der Fall mit dem Agenten ist derselbe Mechanismus mit einem Unterschied, der zählt: Hier lässt sich das Ziel ablesen.
Zwei Punkte, zweimal gemessen
Das ist die Validierung, die der Post zeigt. Claude drückte den Instruktionszähler auf zwei heißen Pfaden: der Routine, die den Nachrichtenbaum einer Konversation zusammenbaut, und einem Scanner für Statuszeilen in der Ausgabe von Claude Code. Die Instruktionen fielen um 48 % und 31 %. Die Wall-Clock-Zeit fiel um 78 % und 44 %. Die Zähler kamen aus Valgrind mit node --predictable, die Zeiten aus demselben Benchmark unter normalem node mit warmem JIT.
Das ist ein guter Beleg dafür, dass der Proxy bei diesen Änderungen in die richtige Richtung zeigt. Proportional ist es sichtbar nicht. Etwa die Hälfte der Instruktionen zu entfernen, hat mehr als drei Viertel der Zeit entfernt. Die weggefallenen Instruktionen waren also viel teurer als die durchschnittliche. Der Post sagt auch, warum: Ein Viertel der Instruktionen des ersten Pfads waren megamorphe Dictionary-Lookups, die dieselbe Message-ID dreimal auflösten. Ein Instruktionszähler gewichtet einen langsamen Lookup und eine billige Addition gleich. Hier bewegte er sich zufällig in die richtige Richtung, weil der erste Fix teure Arbeit entfernt hat. Meine Lesart: Nichts garantiert, dass der nächste Fix auf demselben Pfad dieselbe Mischung hat, und der Zähler kann dir nicht sagen, wann das aufhört.
Was der Ratchet weiter durchsetzt
Nach dem Beweis checkte das Team zwei Ratchets ein: Jeder PR, der den Instruktionszähler auf diesen Pfaden erhöhte, fiel in der CI durch, „and a daily job lowered each ceiling whenever the count went down."
Die erste Hälfte ist ein Regressionsschutz, und zwar ein guter. In der zweiten Hälfte wird die Beziehung nicht mehr geprüft. Die Obergrenze bewegt sich allein anhand des Zählers. An diesem täglichen Job hängt kein Feldwert, und die gepaarte Zeitmessung, die den Proxy validiert hat, läuft nicht erneut, wenn er feuert. Jede Absenkung behauptet also stillschweigend wieder die ursprüngliche These, weniger Instruktionen auf diesem Pfad heißt weniger Latenz, an einem Punkt, der weiter von dem entfernt ist, wo irgendjemand gemessen hat.
Nimm einen Tausch, den der Ratchet nicht sieht. Ein Ergebnis zu memoisieren, fügt beim gemessenen kalten Aufruf Instruktionen hinzu und spart sie bei jedem späteren. Ob das ein Gewinn ist, hängt davon ab, wie oft der Pfad im echten Einsatz warm läuft, und das kann der Zähler nicht wissen. Die Obergrenze ist eine eingecheckte Baseline, also könnte ein Mensch sie im selben PR mit einem Satz Begründung anheben. Ich vermute, dass ein Agent, der auf einen Benchmark angesetzt ist, das selten versucht. Er hat den Auftrag, die Zahl zu senken, und für diesen Agenten liest sich die Obergrenze wie eine Wand. Also wird der Tausch nie vorgeschlagen.
Der Post zeigt auch die umgekehrte Korrektur. Ein PR mit 900 Zeilen bekam eine Antwort aus einer Zeile: „going to gavel that 2ms per send is not worth the complexity of maintaining this build plugin." Ein Mensch hat eine Verbesserung der Metrik aus Gründen verworfen, die die Metrik nicht sehen konnte. Der Ratchet schützt die Zahl davor, schlechter zu werden. Die Entscheidung, ob eine Verbesserung es wert ist, blieb bei Menschen, ebenso die Entscheidung, ob eine Regression es wert war.
Ich habe schon früher argumentiert, dass eine tragende Regel in eine Prüfung gehört, die der Harness ausführt und durchsetzt, statt in einen Satz, den das Modell abwägt. Der Ratchet ist genau dieser Schritt, richtig gemacht. Aber eine durchgesetzte Prüfung hält nur so gut wie die Behauptung, die sie durchsetzt, und „der Instruktionszähler folgt der Latenz auf diesem Pfad" ist eine empirische Behauptung mit Haltbarkeitsdatum. Das frühere Argument endete an derselben Grenze: Manche Verstöße zeigen sich in der Welt, nicht in der Aktion, die ein Gate prüft.
Jedes Instrument in der Kette ist ein Proxy
Die Schleife, die der Post beschreibt, gleicht das Labor tatsächlich mit dem Feld ab. Nachdem eine Änderung ausgeliefert war, beobachtete Claude das Deployment und las die Felddaten; wenn sich die Performance verbesserte, senkte es den Benchmark per Ratchet, wenn nicht, schaltete es das Flag ab und iterierte weiter. Alles, was ein für Nutzer sichtbares Problem verursachen konnte, lag hinter einem kurzlebigen Flag, riskante Änderungen gingen zuerst an Mitarbeiter, dann an ein Prozent der Nutzer, dann an alle, und jeder PR brauchte mindestens eine menschliche Freigabe. Die Schleife hat einen Zweig für einen Laborgewinn, der im Feld nicht auftaucht. Wie oft dieser Zweig ausgelöst hat, sagt der Post nicht.
Die naheliegende Schlussfolgerung wäre, dass der Feldwert der eigentliche Schutz ist, aber die besten Funde des Posts sprechen dagegen. Das Ruckeln der Sidebar, mit dem ein Thread begann, war für jeden Monitor unsichtbar, den sie hatten; Cumulative Layout Shift bewertete jede Verschiebung mit etwa 0,008, weit innerhalb der Schwelle „good" von 0,1. Ein übrig gebliebenes location.reload() verursachte eine halbe Million versteckter Reloads pro Tag, „that none of our load metrics could see." Die Layoutverschiebung nach dem internen Rollout des statischen Composers meldete ein Kollege mit einer Bildschirmaufnahme, kein Instrument. Jedes Mal waren auch die Feldmetriken blind, und ein Mensch, der auf einen Bildschirm schaute, war es nicht.
Also ist auch p75 echter Nutzer ein Proxy, einen Schritt näher am Ziel als ein Instruktionszähler und trotzdem nicht das Ziel. Die Kette läuft vom Instruktionszähler über die Laborzeit und das Feld-Perzentil und endet bei einem Menschen, der auf das Produkt schaut. Die Antwort des Teams war jedes Mal, ein weiteres Instrument zu bauen, und das ist die These des Posts, die genau so funktioniert wie gedacht. Meine ist enger. Jedes Glied wird gegen das nächsthöhere validiert, und nur in dem Bereich, den jemand geprüft hat. Eine Validierung ist also nur so aktuell wie das letzte Mal, als jemand dieses Glied gegen das darüber geprüft hat.
Vor dem ersten Lauf
Schreib das Ziel auf, für das die Zahl steht, und wie du es ablesen wirst, bevor du die Zahl wählst. Wenn du das Ziel nicht unabhängig beurteilen kannst, kannst du den Proxy trotzdem benutzen, aber du wirst nicht sehen, wenn er versagt.
Halte die Validierung als Behauptung mit Geltungsbereich fest: welche Pfade, welche Art von Änderung, wie gemessen. Die ersten gepaarten Läufe sagen dir, dass der Proxy dem Ziel für genau die Änderungen folgt, die sie erzeugt haben.
Häng die Nachprüfung an den Ratchet selbst. Die gepaarte Zeitmessung existiert schon als Benchmark unter normalem node. Lass sie jedes Mal laufen, wenn der tägliche Job eine Obergrenze senkt, logge das Verhältnis von Instruktionsänderung zu Zeitänderung und halte die Absenkung an, wenn dieses Verhältnis von dem validierten abdriftet. Das kostet einen zusätzlichen Benchmark-Lauf pro Bewegung der Obergrenze. Billig, verglichen mit einer Obergrenze, die monatelang eine veraltete Behauptung durchsetzt.
Das Anthropic-Team hat das meiste davon von Hand gemacht, mit einem namentlich benannten Verantwortlichen für jeden Thread. Das Klettern ist billig geworden. Zu prüfen, ob der Hügel noch dorthin führt, wo du hinwolltest, ist es nicht.