Forschung

Unser Agent-Harness kontinuierlich verbessern

Stefan Heule & Jediah Katz11 Min. Lesezeit

Wir entwickeln das Cursor Agent-Harness so, wie wir jedes ambitionierte Softwareprodukt entwickeln würden. Ein großer Teil der Arbeit ist visionsgetrieben: Wir beginnen mit einer klaren Vorstellung davon, wie die ideale Agent-Erfahrung aussehen sollte.

Darauf aufbauend formulieren wir Hypothesen, wie wir dieser Vision näherkommen, führen Experimente durch, um sie zu testen, und iterieren auf Basis quantitativer und qualitativer Signale aus Evals und der realen Nutzung. Dieser Prozess hängt davon ab, dass wir über die richtige Online- und Offline-Instrumentierung verfügen, damit wir erkennen können, ob eine Änderung das Harness tatsächlich verbessert.

Wenn wir Early Access zu neuen Modellen bekommen, laufen all diese Ansätze zusammen. Wir verbringen Wochen damit, unser Harness an die Stärken und Eigenheiten eines Modells anzupassen, bis dasselbe Modell in unserem speziell abgestimmten Harness spürbar schneller, intelligenter und effizienter ist.

Gelegentlich entdecken wir sprunghafte Verbesserungen. Meistens besteht die Verbesserung des Harness jedoch darin, akribisch viele kleine Optimierungen aufeinander aufzubauen, die Agent zusammen besser darin machen, Software zu erstellen.

Die Weiterentwicklung des Kontextfensters

Im Zentrum der Interaktion mit großen Sprachmodellen steht das Kontextfenster. Wenn der Agent gebeten wird, etwas zu erstellen, beginnt das Kontextfenster mit dem System-Prompt und den Tool-Beschreibungen, gefolgt vom aktuellen Stand der Unterhaltung und schließlich der Anfrage des Benutzers.

Die Art und Weise, wie wir dieses Fenster füllen und verwalten, hat sich im Laufe der Geschichte von Cursor deutlich verändert.

Als wir Ende 2024 erstmals unseren Agent entwickelten, waren Modelle noch deutlich schlechter darin, ihren eigenen Kontext auszuwählen, und wir investierten viel Arbeit in das Kontext-Engineering, um Schutzmechanismen einzubauen – zum Beispiel, indem wir dem Agent nach jeder Bearbeitung Lint- und Typfehler anzeigten, seine Dateizugriffe umschrieben, wenn er zu wenige Zeilen anforderte, und sogar die maximale Anzahl an Tools begrenzten, die er in einem einzelnen Durchlauf aufrufen konnte.

Außerdem stellten wir große Mengen an statischem Kontext bereit, die dem Agent zu Beginn jeder Sitzung immer zur Verfügung standen. Dazu gehörten zu verschiedenen Zeitpunkten das Ordnerlayout der Codebasis, Codeausschnitte, die semantisch zur Anfrage passten, sowie komprimierte Versionen von Dateien, die der Benutzer manuell hinzugefügt hatte.

Das meiste davon ist inzwischen verschwunden.

Einige nützliche statische Kontextinformationen fügen wir zwar weiterhin hinzu (z. B. Betriebssystem, Git-Status sowie aktuelle und kürzlich angesehene Dateien). Aber wir haben uns an die steigenden Fähigkeiten der Modelle angepasst, indem wir Schutzmechanismen abgebaut und mehr dynamischen Kontext bereitgestellt haben, den der Agent während der Arbeit abrufen kann. In einem früheren Beitrag haben wir einige unserer Techniken hinter dynamischem Kontext in einem Deep Dive genauer beleuchtet, von denen inzwischen viele auch von anderen Agent übernommen wurden. Ein großer Teil unserer Arbeit konzentriert sich inzwischen darauf, dem Agent mehr Möglichkeiten zu geben, Kontext dynamisch abzurufen und mit der Welt zu interagieren.

Mit dynamischem Kontext kann das Modell entscheiden, wann zusätzliche Informationen wie frühere Unterhaltungen, aktive Terminal-Sitzungen oder relevante Tools in das Kontextfenster geholt werden.Mit dynamischem Kontext kann das Modell entscheiden, wann zusätzliche Informationen wie frühere Unterhaltungen, aktive Terminal-Sitzungen oder relevante Tools in das Kontextfenster geholt werden.

Zwei Möglichkeiten, Harness-Änderungen zu bewerten

Das Harness und das Modell bestimmen zusammen, wie gut der Agent ist, aber was „gut“ genau bedeutet, lässt sich nur schwer fassen. Um das messbar zu machen, haben wir mehrere Bewertungsebenen aufgebaut.

Wir pflegen öffentliche Benchmarks zusammen mit unserer eigenen Eval-Suite CursorBench, die uns schnell und standardisiert Aufschluss über die Qualität gibt und Vergleiche im Zeitverlauf ermöglicht. Aber selbst die besten Benchmarks bilden die reale Nutzung nur näherungsweise ab. Würden wir uns allein auf sie verlassen, gingen uns wichtige Signale verloren.

Deshalb führen wir auch Online-Experimente durch, bei denen wir zwei oder mehr Harness-Varianten parallel ausrollen und per A/B-Tests mit realer Nutzung vergleichen. In diesen Tests messen wir die Qualität des Agenten anhand verschiedener Metriken. Manche sind leicht zu erfassen, etwa Latenz, Token-Effizienz, Anzahl der Tool-Aufrufe und Cache-Trefferrate. Sie sind als Richtwert nützlich, beantworten aber noch nicht die wichtigeren, schwerer greifbaren Fragen dazu, ob der Agent tatsächlich gute Arbeit geleistet hat. Diese messen wir auf zwei Arten.

Die erste ist die „Keep Rate“ von Agent-generiertem Code. Für eine bestimmte Menge an Codeänderungen, die der Agent vorgeschlagen hat, verfolgen wir, welcher Anteil davon nach festgelegten Zeitabständen noch in der Codebasis des Benutzers vorhanden ist. So erkennen wir, wann Benutzer die Ausgabe des Agenten manuell anpassen müssen oder weiter iterieren und den Agenten Korrekturen vornehmen lassen müssen — ein Hinweis darauf, dass die erste Antwort des Agenten von geringerer Qualität war.

Zweitens nutzen wir ein Sprachmodell, das die Reaktionen des Benutzers auf die erste Ausgabe des Agenten liest, um semantisch zu erfassen, ob der Benutzer zufrieden war oder nicht. Wenn ein Benutzer einfach mit dem nächsten Feature weitermacht, ist das ein starkes Signal dafür, dass der Agent seine Aufgabe erfüllt hat. Fügt ein Benutzer dagegen einen Stack Trace ein, ist das ein verlässliches Signal dafür, dass das nicht der Fall war.

Manchmal zeigen uns diese Online-Tests auch, dass wir eine vielversprechende Idee besser verwerfen sollten. In einem Experiment probierten wir für die Kontextzusammenfassung ein teureres Modell aus und stellten fest, dass es die Qualität des Agenten nur minimal verbesserte — den höheren Preis also nicht rechtfertigte.

Regressionen erkennen und beheben

Je mehr Modelle und Fähigkeiten wir hinzufügen, desto komplexer wird das Harness und desto mehr mögliche Zustände gibt es – wie bei jeder Software. Damit wächst auch die Zahl der Stellen, an denen Bugs auftreten können, von denen wir viele erst im großen Maßstab erkennen.

Die Tools des Agenten sind eine der größten Fehlerquellen, und Fehler bei Tool-Aufrufen können für eine Sitzung in Cursor äußerst schädlich sein. Zwar kann sich der Agent oft selbst korrigieren, doch Fehler bleiben im Kontext, verschwenden Token und verursachen „Kontext-Verfall“, bei dem sich angesammelte Fehler negativ auf die Qualität der nachfolgenden Entscheidungen des Modells auswirken.

Manchmal kann der Agent nach einem fehlgeschlagenen Tool-Aufruf blockiert sein oder völlig aus der Spur geraten. Auch wenn Metriken wie die Anzahl der Tool-Aufrufe und die Fehlerrate nicht direkt messen, ob der Agent gute Arbeit geleistet hat, sind sie doch Indikatoren, die auf ein größeres Problem hinweisen können.

Jeder unbekannte Fehler ist ein Bug im Harness, und wir behandeln ihn entsprechend. Viele Fehler sind jedoch „erwartet“, zum Beispiel wenn das Modell gelegentlich eine falsche Bearbeitung vorschlägt oder versucht, eine Datei zu lesen, die nicht existiert. Wir klassifizieren diese erwarteten Fehler nach Ursache. InvalidArguments und UnexpectedEnvironment erfassen Modellfehler und Widersprüche im Kontextfenster, während ProviderError Ausfälle von Anbietern bei Tools wie GenerateImage oder WebSearch erfasst.

Außerdem haben wir weitere Klassifizierungen wie UserAborted und Timeout, die zusammen die meisten erwarteten Fehler abdecken.

In einem fokussierten Sprint Anfang dieses Jahres haben wir alle Tool-Aufrufe auf mindestens 2, oft sogar 3 Neunen Zuverlässigkeit gebracht.In einem fokussierten Sprint Anfang dieses Jahres haben wir alle Tool-Aufrufe auf mindestens 2, oft sogar 3 Neunen Zuverlässigkeit gebracht.

Wir definieren Warnmeldungen auf Basis dieser Metriken, um erhebliche Regressionen zu erkennen, die bis in die Produktion gelangen. Da unbekannte Fehler immer Bugs sind, lösen wir eine Warnung aus, sobald die Rate unbekannter Fehler bei einem beliebigen Tool einen festen Schwellenwert überschreitet. Bei erwarteten Fehlern ist es jedoch oft schwierig zu sagen, ob sie auf einen Bug im Harness oder auf erwartetes Verhalten zurückgehen.

Ein Timeout bei einer grep-Suche kann zum Beispiel auf ein Performance-Problem des Tools hindeuten – oder die Codebasis ist einfach sehr groß und das Modell hat eine ineffiziente Abfrage formuliert. Deshalb haben wir Warnungen zur Anomalieerkennung, die ausgelöst werden, wenn erwartete Fehler den Baseline-Wert deutlich überschreiten. Wir berechnen Baselines pro Tool und pro Modell, weil verschiedene Modelle Tool-Aufrufe mit unterschiedlicher Häufigkeit vermasseln können.

Wir führen außerdem wöchentlich eine Automatisierung aus, die mit einer Fähigkeit ausgestattet ist, die dem Modell beibringt, unsere Logs zu durchsuchen, neue oder zuletzt sprunghaft angestiegene Probleme sichtbar zu machen und Tickets in einem Backlog inklusive Untersuchung zu erstellen oder zu aktualisieren. Wir setzen stark auf Cloud-Agent, um Korrekturen für viele Probleme gleichzeitig anzustoßen, und können sie sogar direkt aus Linear auslösen.

Dieser Prozess ist Teil davon, wie wir für unser Agent-Harness eine automatisierte „Softwarefabrik“ aufbauen. Im Verlauf eines fokussierten Sprints Anfang dieses Jahres konnten wir unerwartete Fehler bei Tool-Aufrufen um eine Größenordnung senken.

Anpassung des Harness für verschiedene Modelle

All unsere Harness-Abstraktionen sind modellagnostisch und lassen sich für jedes von uns unterstützte Modell umfassend anpassen. So sind die Modelle von OpenAI beispielsweise darauf trainiert, Dateien in einem patchbasierten Format zu bearbeiten, während die Modelle von Anthropic auf String-Ersetzung trainiert sind. Beide Modelle könnten zwar jedes der beiden Tools nutzen, aber ein ungewohntes Tool kostet zusätzliche Reasoning-Token und führt zu mehr Fehlern. Deshalb stellen wir in unserem Harness jedem Modell das Tool-Format bereit, das es aus dem Training kennt.

Diese Anpassung geht sehr weit und umfasst benutzerdefiniertes Prompting für verschiedene Provider und sogar für verschiedene Modellversionen. Die Modelle von OpenAI neigen dazu, Anweisungen wörtlicher und präziser zu befolgen, während Claude etwas intuitiver ist und ungenaue Anweisungen eher toleriert.

Wenn wir vor dem Launch Early Access zu einem neuen Modell erhalten, beginnen wir mit dem Harness des ähnlichsten bestehenden Modells und iterieren von dort aus weiter. Wir führen Offline-Evals durch, um herauszufinden, wo das Modell durcheinandergerät, lassen unser Team damit arbeiten und Probleme aufdecken und passen das Harness entsprechend an. So iterieren wir, bis wir eine Modell-Harness-Kombination haben, die wir guten Gewissens ausliefern können.

Ein großer Teil dieses Abstimmungsprozesses besteht darin, das Harness an die Stärken eines neuen Modells anzupassen, aber manchmal stoßen wir auch auf echte Eigenheiten von Modellen, die wir mit dem Harness abmildern können. Zum Beispiel haben wir beobachtet, dass ein Modell etwas entwickelte, das wir schließlich Kontextangst nannten: Wenn sich sein Kontextfenster füllte, begann es, Aufgaben abzulehnen, und deutete an, dass die Aufgabe zu groß wirke. Durch Anpassungen am Prompt konnten wir dieses Verhalten verringern.

Modellwechsel mitten im Chat ermöglichen

Es ist besonders knifflig, das Harness so zu gestalten, dass Benutzer Modelle mitten in einer Unterhaltung wechseln können, weil verschiedene Modelle unterschiedlich reagieren und unterschiedliche Prompts und Tool-Schnittstellen haben.

Wenn ein Benutzer das Modell wechselt, wechselt Cursor automatisch zum passenden Harness mit dem für dieses Modell angepassten Satz an Prompts und Tools. Das Modell muss diese Tools jedoch weiterhin auf einen Unterhaltungsverlauf anwenden, der von einem anderen Modell erzeugt wurde und außerhalb der Verteilung dessen liegt, worauf es trainiert wurde.

Um das abzufangen, fügen wir benutzerdefinierte Anweisungen hinzu, die dem Modell mitteilen, wann es mitten im Chat von einem anderen Modell übernimmt. Diese Anweisungen halten es außerdem davon ab, Tools aufzurufen, die zwar im Unterhaltungsverlauf erscheinen, aber nicht zu seinem eigenen Tool-Set gehören.

Verhindern, dass Modelle Tools aufrufen, die nicht zu ihrem Tool-Set gehörenVerhindern, dass Modelle Tools aufrufen, die nicht zu ihrem Tool-Set gehören

Eine zweite Herausforderung ist, dass Caches anbieter- und modellspezifisch sind, sodass ein Wechsel zu einem Cache-Miss und zu einem langsameren, teureren ersten Turn führt. Wir mindern das, indem wir die Unterhaltung beim Wechsel zusammenfassen. So erhält das Modell eine saubere Zusammenfassung, die den Cache-Nachteil verringert. Wenn der Benutzer jedoch bereits tief in einer komplexen Aufgabe steckt, kann die Zusammenfassung wichtige Details verlieren. Deshalb empfehlen wir im Allgemeinen, für die Dauer einer Unterhaltung bei einem Modell zu bleiben, sofern es keinen guten Grund für einen Wechsel gibt.

Eine weitere Möglichkeit, die Herausforderungen eines Modellwechsels mitten in der Unterhaltung zu umgehen, ist die Nutzung eines Subagenten, der mit einem frischen Kontextfenster startet. Wir haben das Harness vor Kurzem um die Möglichkeit erweitert, dass Benutzer direkt darum bitten können, einen Subagenten mit einem bestimmten Modell auszuführen.

Das Harness und die Zukunft der Softwareentwicklung

Die Zukunft der KI-gestützten Softwareentwicklung wird von mehreren Agenten geprägt sein. Statt jede Teilaufgabe von einem einzelnen Agenten bearbeiten zu lassen, wird das System lernen, Aufgaben auf spezialisierte Agenten und Subagenten zu verteilen: einer für die Planung, ein anderer für schnelle Änderungen und ein dritter für die Fehlerbehebung, jeweils zugeschnitten auf das, was er am besten kann.

Damit das gut funktioniert, ist es im Kern eine Herausforderung für das Harness. Das System muss wissen, welchen Agenten es einsetzen soll, wie es die Aufgabe auf die Stärken dieses Agenten zuschneidet und wie es die Ergebnisse zu einem stimmigen Workflow zusammenführt. Die Fähigkeit, diese Art der Koordination zu orchestrieren, wird im Harness liegen und nicht bei einem einzelnen Agenten. Das bedeutet: Obwohl die Entwicklung des Harness schon immer wichtig für den Erfolg von Agenten war, wird sie künftig noch entscheidender werden.

Abgelegt unter: Forschung

Autors: Stefan Heule & Jediah Katz