エージェントハネスの継続的な改善について(10 分読了)
Cursor は、モデル能力の向上に伴い静的なガードレールから動的なコンテキスト取得へ移行し、小技の積み重ねとモデル固有のチューニングによりエージェントハネスを継続的に最適化している。
キーポイント
静的ガードレールから動的コンテキストへ
初期のコード構文エラー検出やツール呼び出し制限などの静的なガードレールは、モデル能力向上に伴い撤廃され、作業中に必要な情報を動的に取得する方式へと進化している。
モデル固有の最適化戦略
新モデルへの早期アクセス時には、その特性や癖に合わせてハネスを数週間かけてカスタマイズし、速度・知能・効率性を劇的に向上させている。
継続的な小技の積み重ね
画期的な飛躍よりも、エージェントがソフトウェア構築をより上手に行えるよう、多数の小さな最適化を徹底的に積み重ねるアプローチが主流となっている。
影響分析・編集コメントを表示
影響分析
この記事は、AI エージェント開発における「コンテキストエンジニアリング」のパラダイムシフトを示しており、単なるプロンプト設計からモデル能力に依存した動的なリソース管理へと進化している現状を浮き彫りにしています。開発者や製品チームにとって、ハードコーディングされたルールよりも柔軟な動的コンテキスト戦略の重要性を理解し、モデルごとの最適化コストを投資対効果として再評価する必要があることを示唆しています。
編集コメント
モデルの賢さが増すにつれて、人間が細かく制御するルール(ガードレール)を減らし、代わりにモデルに柔軟な判断と動的な情報収集を任せる方向へ舵を切っている点は非常に示唆に富んでいます。
私たちは、Cursor エージェント・ハネスを構築する際、あらゆる意欲的なソフトウェア製品に取り組むのと同じアプローチを取ります。作業の多くはビジョン主導であり、理想的なエージェント体験がどのようなものであるかという見解から始まります。
そこから、そのビジョンにどう近づけるかという仮説を立て、評価や実際の使用状況からの定量的・定性的な信号を用いてそれらをテストし、反復します。このプロセスには、変更が実際にハネスを改善したかどうかを判断するために、適切なオンラインおよびオフラインの計測(インストルメンテーション)が必要です。
新しいモデルへの早期アクセスを得ると、これらすべてのアプローチが収束します。私たちは数週間をかけて、ハネスを各モデルの強みや特徴に合わせてカスタマイズし、特別にチューニングされたハネス内の同じモデルが、明らかに高速化され、賢くなり、より効率的になるまで調整を行います。
時折、飛躍的な改善が見つかることもあります。しかし、より一般的には、ハネスの改善とは、ソフトウェア構築においてエージェントをより良くするための小さな最適化を執拗に積み重ねていくことです。
コンテキストウィンドウの進化
大規模言語モデル(LLM)と対話する核心は コンテキストウィンドウ にあります。エージェントに何かを構築させる際、コンテキストウィンドウはシステムプロンプトとツール記述から始まり、次に会話の現在の状態が続きます。最後にユーザーのリクエストが加わります。
このウィンドウをどのように埋め込み管理するかという方法は、Cursor の歴史の中で大きく進化してきました。
2024 年後半に初めて コーディングエージェント を開発した当時、モデルは自身で文脈を選択する能力がまだ低く、ガードレールを構築するために多くのコンテキストエンジニアリングへの投資を行いました。例えば、編集のたびにリンティングエラーや型エラーをエージェントに提示したり、要求された行数が少なすぎた場合にファイル読み込みを再実行させたり、1 回のターンで呼び出せるツールの最大数を制限したりすることです。
また、各セッション開始時に常に利用可能な大量の静的コンテキストも提供していました。その中には、コードベースのフォルダ構成、クエリに意味的に一致するコードスニペット、ユーザーが手動で添付したファイルの圧縮版などが含まれていました。
それらの多くはもう過去のものとなりました。
現在でも、オペレーティングシステム、git のステータス、現在および直近で閲覧されたファイルなど、有用な静的コンテキストの一部は引き続き含んでいます。しかし、モデルの能力向上に対応するため、ガードレールを撤廃し、エージェントが作業中に取得できるより動的なコンテキストを提供する方向へ転換しました。以前の投稿で、動的コンテキスト の背後にあるいくつかの技術について詳しく解説しましたが、その多くはすでに他のコーディングエージェントにも採用されています。現在私たちの取り組みの中心は、エージェントがより多様な方法で動的にコンテキストを引き出し、世界と相互作用できるようにすることです。
image
image
ハーネス変更の評価方法
エージェントの良し悪しは、ハーネスとモデルが共同して決定しますが、「良い」という状態を明確に定義するのは困難です。その実態を把握するために、私たちは複数の層に分けた測定手法を構築しました。
私たちは独自の評価スイート(eval suite)と共に公開ベンチマークも維持しており、特に CursorBench は、品質に対する迅速かつ標準化された指標を提供し、時間経過に伴う比較を可能にしています。しかし、どんなに優れたベンチマークであっても実際の使用状況を完全に再現するものではなく、それらだけに依存すれば重要なシグナルを見逃してしまう可能性があります。
そのため、私たちはオンライン実験も実施しており、2 つ以上のハーネス変種を並列で展開し、実際の利用状況において A/B テスト(A/B test)を行います。これらのテストにおけるエージェントの品質は、多様な指標を通じて測定されます。レイテンシ(latency)、トークン効率(token efficiency)、ツール呼び出し数(tool call count)、キャッシュヒット率(cache hit rate)などは比較的明確な指標ですが、これらは方向性を示すには有用であるものの、エージェントが実際に良い仕事をできたかどうかという、より曖昧で重要な問いに答えるものではありません。そのような問いについては、以下の 2 つの方法で測定を行っています。
まず一つ目は、エージェント生成コードの「維持率」です。エージェントが提案した一連の変更に対して、一定期間ごとにユーザーのコードベースに残っている割合を追跡します。これにより、ユーザーがエージェントの出力を手動で調整する必要があったり、反復して修正を依頼したりするタイミングを理解でき、初期応答の品質が低かったことを示す指標となります。
二つ目は、言語モデルを使用してユーザーのエージェントへの初期応答に対する反応を読み取ることで、ユーザーが満足したかどうかを意味的に捉えることです。ユーザーが次の機能に進むことはエージェントが仕事を果たしたという強力なシグナルであり、スタックトレース(stack trace)を貼り付けるのは機能しなかったことを示す信頼性の高いシグナルです。
これらのオンラインテストは、有望に見えるアイデアを棚上げするよう指示することもあります。ある実験では、コンテキスト要約のためにより高価なモデルを試しましたが、エージェントの品質にほとんど影響を与えず、そのコスト増に見合う価値がないことがわかりました。
劣化の追跡と修復
モデルや機能を追加するにつれて、ハッチ(harness)はソフトウェアと同様に複雑さが増し、潜在的な状態も多くなります。これに伴い、バグが発生する範囲も広がり、その多くは大規模な運用環境でしか検出できません。
エージェントのツールはバグが発生する最も広範な領域の一つであり、ツールの呼び出しエラーは Cursor 内のセッションに極めて有害となり得ます。エージェントがしばしば自己修正できる場合もありますが、エラーはコンテキスト内に残存し、トークンを浪費するとともに「コンテキストローテーション(context rot)」を引き起こします。これは蓄積されたミステイクによってモデルのその後の判断の質が低下する現象です。
時には、ツールの呼び出しに失敗した後、エージェントがブロックされたり、完全に軌道から外れたりすることがあります。ツールの呼び出し数やエラー率といった指標は、エージェントが適切に機能したかどうかを直接測定するものではありませんが、より広範な問題を示す指標として機能します。
未知のエラーはすべてハッチ(harness)内のバグを表しており、私たちはそれに応じて対応しています。しかし、多くのエラーは「予想される」ものです。例えば、モデルが誤った編集を提案したり、存在しないファイルの読み取りを試みたりする場合です。これらの予想されるエラーは原因別に分類されます。「InvalidArguments」と「UnexpectedEnvironment」はモデルのミステイクやコンテキストウィンドウ内の矛盾を捉え、「ProviderError」は GenerateImage や WebSearch などのツールにおけるベンダーの障害を捉えます。
他にも、ユーザーによる中止(UserAborted)やタイムアウト(Timeout)など複数の分類があり、これらによって予想されるエラーの大部分が網羅されています。
image
image
これらの指標に基づいてアラートを定義し、本番環境に流入した重大な回帰を検知します。未知のエラーは常にバグであるため、任意のツールの未知エラー率が固定された閾値を超えた場合は必ずアラートが発令されます。ただし、予期されるエラーがハッチ(harness)内のバグによるものか、それとも予期される動作なのかを判断するのは難しい場合があります。
例えば、grep 検索のタイムアウトは、ツールのパフォーマンス問題によるものかもしれませんし、コードベースが非常に巨大でモデルが非効率的なクエリを形成した結果である可能性もあります。これに対処するため、予期されるエラーがベースラインを著しく上回った場合に発令される異常検知アラートを設けています。異なるモデルがツール呼び出しを失敗する頻度が異なるため、ベースラインはツールごとおよびモデルごとに計算しています。
また、週次で Automation を実行しており、これにはモデルにログの検索方法を教え、新規または直近で急増した課題を特定し、調査を含むバックログへのチケット作成・更新を行うスキルが装備されています。私たちは多くの課題に対する修正処理を開始するために Cloud Agents に大きく依存しており、Linear から直接トリガーすることも可能です。
このプロセスは、エージェントハッチ(harness)のために自動化された「ソフトウェアファクトリー」を構築する一環です。今年初めに集中的なスプリント期間中に、予期せぬツール呼び出しエラーを桁違いに削減することに成功しました。
異なるモデル向けにハッチをカスタマイズする
すべてのハッチ抽象化はモデル非依存であり、サポートするすべてのモデルに対して大幅なカスタマイズが可能です。例えば、OpenAI のモデルはパッチベース形式を使用してファイルを編集するように訓練されていますが、Anthropic のモデルは文字列置換のデータで訓練されています。どちらのモデルでも両方のツールを使用可能ですが、見慣れないツールを使うと推論トークンのコストが増加し、エラーも多くなります。そのため、当社のハッチでは各モデルに、トレーニング時に使用していたツール形式を割り当てています。
このカスタマイズは非常に深く、異なるプロバイダー向けや、さらに異なるモデルバージョン向けの独自のプロンプト設計にも及びます。OpenAI のモデルは指示の遵守においてより文字通りで精密な傾向があり、一方 Claude は直感的であり、不正確な指示に対してやや寛容です。
ローンチ前に新しいモデルへの早期アクセスを得た場合、私たちは最も近い既存のモデルのハネスから始め、反復的な改善を開始します。オフライン評価を実行してモデルが混乱する箇所を特定し、チームメンバーに実際に使用してもらって問題を表面化させ、それに応じてハネスを調整します。このように反復を重ね、出荷しても問題ないと感じるモデルとハネスの組み合わせができるまで続けます。
このチューニングプロセスの多くは、新しいモデルの強みに合わせてハネスをカスタマイズするものですが、時にはハネスで緩和できる本質的なモデル特有の挙動に直面することもあります。例えば、あるモデルが「コンテキスト不安」と呼ばれる現象を発展させるのを観察しました。これは、コンテキストウィンドウがいっぱいになると、タスクが大きすぎるように見えて作業を拒否し、曖昧な返答をするというものです。プロンプトの調整を通じてこの挙動を軽減することができました。
チャット中におけるモデル切り替えの支援
ユーザーが会話中にモデルを切り替えることをサポートするハネスを設計するのは特に難しく、異なるモデルは異なる動作、プロンプト、ツール形状を持っているからです。
ユーザーがモデルを切り替えると、Cursor は自動的にそのモデルに適切なハネスへ切り替わり、そのモデル用にカスタマイズされた一連のプロンプトとツールを使用します。ただし、モデルは依然として、別のモデルによって生成され、トレーニングデータとは異なる分布にある会話履歴に対してこれらのツールを適用する必要があります。
これに対処するため、モデルがチャット途中から別のモデルに引き継ぐ際に実行すべき指示や、会話履歴には登場するものの自身のツールセットに含まれないツールの呼び出しを避けるよう誘導するためのカスタム指示を追加しました。
image
image
2 つ目の課題は、キャッシュがプロバイダーおよびモデル固有であるため、切り替えを行うとキャッシュミスが発生し、最初のターンが遅くなりコストが高くなる点です。私たちはこれを緩和するために、切り替え時に会話の要約を行う実験を行いました。これにより、キャッシュによるペナルティを軽減するクリーンな要約をモデルに提供できますが、ユーザーが複雑なタスクの深部に達している場合、重要な詳細情報が要約によって失われる可能性があります。一般的には、特に切り替える理由がない限り、会話の期間中は1 つのモデルを使い続けることを推奨しています。
会話途中でのモデルの切り替えに伴う課題を回避する別の方法として、新しいコンテキストウィンドウから開始されるサブエージェント(subagent)を使用するという手があります。最近、ハーンセスに、特定のモデルで実行される subagent を直接ユーザーが要求できる機能を追加しました。
ハーネスとソフトウェア開発の未来
AI支援型ソフトウェアエンジニアリングの未来はマルチエージェントシステムになります。すべてのサブタスクを単一のエージェントで処理するのではなく、システムは専門化されたエージェントやサブエージェント間で委譲することを学びます:計画担当のエージェント、高速な編集を担当する別のエージェント、デバッグを担当する第三のエージェントなど、それぞれが最も得意とする領域にスコープを限定します。
これをうまく機能させることは、本質的にハーネスの課題です。システムはどのエージェントを派遣すべきか、そのエージェントの強みに合わせてタスクをどう枠組み化するべきか、そして結果を一貫したワークフローにどう統合するかを知る必要があります。そのような調整をオーケストレーションする能力は、単一のエージェントではなく、ハーネスの中に存在することになります。これは、ハーネスエンジニアリングが常にエージェントの成功にとって重要であったことを意味しますが、今後さらに重要な役割を果たすようになるということです。
原文を表示
We approach building the Cursor agent harness the way we'd approach any ambitious software product. Much of the work is vision-driven, where we start with an opinion about what the ideal agent experience should look like.
From there, we form hypotheses about how to get closer to that vision, run experiments to test them, and iterate using quantitative and qualitative signals from evals and real usage. That process depends on having the right online and offline instrumentation, so we can tell when a change actually makes the harness better.
When we get early access to new models, all of these approaches converge. We spend weeks customizing our harness to a model's strengths and quirks until the same model inside our specially tuned harness is noticeably faster, smarter, and more efficient.
Occasionally we discover step-change improvements. More often, though, improving the harness is a matter of obsessively stacking small optimizations that together make agents better at building software.
Evolving the context window
At the heart of interacting with large language models is the context window. When asking the agent to build something, the context window starts with the system prompt and tool descriptions, followed by the current state of the conversation, and finally the user's request.
The way we populate and manage that window has evolved significantly over the history of Cursor.
When we first developed our coding agent in late 2024, models were much worse at choosing their own context and we invested lots of context engineering work into creating guardrails—for example, surfacing lint and type errors to the agent after every edit, rewriting its file reads when it requested too few lines, and even limiting the maximum number of tools it could call in one turn.
We also provided substantial amounts of static context that was always available to the agent at the start of each session. At various points, that included the folder layout of the codebase, code snippets that semantically matched the query, and compressed versions of files that the user manually attached.
That is mostly long gone.
We still include some useful static context (e.g., operating system, git status, current and recently viewed files). But we’ve adapted to increasing model capability by knocking down guardrails and providing more dynamic context, which can be fetched by the agent while it works. In an earlier post, we did a deep dive into some of our techniques behind dynamic context, many of which have since been adopted by other coding agents. Much of our work now focuses on providing more ways for the agent to dynamically pull context and interact with the world.

Two ways of assessing harness changes
The harness and the model together determine how good the agent is, but "good" is hard to pin down. To locate it, we've built several layers of measurement.
We maintain public benchmarks alongside our own eval suite, CursorBench, which gives us a fast, standardized read on quality and lets us compare across time. But even the best benchmarks only approximate real usage, meaning we’d miss important signals if we relied on them entirely.
So we also run online experiments where we deploy two or more harness variants side by side and A/B test them on real usage. We measure agent quality in these tests through a variety of metrics. Some are straightforward like latency, token efficiency, tool call count, and cache hit rate. Those are directionally useful but still don’t get at fuzzier and more important questions of whether the agent actually did a good job. We measure those in two ways.
The first is the “Keep Rate” of agent-generated code. For a given set of code changes that the agent proposed, we track what fraction of those remain in the user’s codebase after fixed intervals of time. This allows us to understand when users have to manually adjust the agent's output, or need to iterate and have the agent fix things, indicating the agent’s initial response was of lower quality.
Second, we use a language model to read the user's responses to the agent’s initial output in order to capture semantically whether the user was satisfied or not. A user moving on to the next feature is a strong signal the agent did its job, while a user pasting a stack trace is a reliable signal that it didn't.
Sometimes these online tests tell us to shelve an idea that seems promising. In one experiment, we tried a more expensive model for context summarization and observed it made a negligible difference in agent quality that wasn’t worth the higher cost.
Tracking and repairing degradations
As we add more models and capabilities, the harness gets more complex with more potential states, just like any piece of software. With this comes more surface area for bugs to crop up, many of which we can only detect at scale.
The agent’s tools are one of the broadest surfaces for bugs, and tool call errors can be extremely harmful to a session in Cursor. While the agent can often self-correct, errors remain in context, wasting tokens and causing “context rot,” where accumulated mistakes degrade the quality of the model's subsequent decisions.
Sometimes, the agent can be blocked or go off the rails completely after a failed tool call. Though metrics like tool call volume and error rate don’t directly measure whether the agent did a good job, they act as indicators that can point to a broader issue.
Any unknown error represents a bug in the harness, and we treat it accordingly. But many errors are “expected,” for example the model occasionally proposing an incorrect edit or trying to read a file that doesn't exist. We classify these expected errors by cause. InvalidArguments and UnexpectedEnvironment capture model mistakes and contradictions in the context window, while ProviderError captures vendor outages from tools like GenerateImage or WebSearch.
We have several other classifications like UserAborted and Timeout which altogether encompass most expected errors.

We define alerts based on these metrics to catch significant regressions that make it into production. Since unknown errors are always bugs, we alert whenever the unknown error rate for any tool exceeds a fixed threshold. But it can be tricky to tell whether expected errors represent a bug in the harness or expected behavior.
For example, a grep search timeout might be because of a performance issue with the tool, or the codebase might just be huge and the model formed an inefficient query. To deal with this, we have anomaly detection alerts which fire when expected errors significantly exceed the baseline. We compute baselines per-tool and per-model, because different models may mess up tool calls at different rates.
We also run a weekly Automation equipped with a skill that teaches the model how to search through our logs, surface issues that are new or recently spiked, and create or update tickets in a backlog with an investigation. We lean heavily on Cloud Agents to kick off fixes for many issues at once, and can even trigger them directly from Linear.
This process is part of the way we’re instantiating an automated “software factory” for our agent harness. Over the course of a focused sprint earlier this year, we drove unexpected tool call errors down by an order of magnitude.
Customizing the harness for different models
All of our harness abstractions are model agnostic and can be heavily customized for every model we support. For instance, OpenAI's models are trained to edit files using a patch-based format, while Anthropic's models are trained on string replacement. Either model could use either tool, but giving it the unfamiliar one costs extra reasoning tokens and produces more mistakes. So in our harness, we provision each model with the tool format it had during training.
This customization goes very deep, and includes custom prompting for different providers and even for different model versions. OpenAI’s models tend to be more literal and precise in their instruction following, whereas Claude is a bit more intuitive and more tolerant to imprecise instructions.
When we get early access to a new model ahead of launch, we start from the closest existing model's harness and begin iterating. We run offline evals to find where the model gets confused, have people on our team use it and surface problems, and tweak the harness in response. We iterate like this until we have a model-harness combination we feel good about shipping.
Much of this tuning process is about customizing the harness to a new model’s strengths, but sometimes we encounter genuine model quirks that we can mitigate with the harness. For example, we observed one model develop what we came to call context anxiety: As its context window filled up, it would start refusing work, hedging that the task seemed too big. We were able to reduce the behavior through prompt adjustments.
Facilitating mid-chat model switching
It’s especially tricky to design the harness to support users switching models mid conversation, because different models have different behaviors, prompts, and tool shapes.
When a user switches models, Cursor automatically switches to the appropriate harness, with that model’s customized set of prompts and tools. However, the model still has to apply those tools to a conversation history that was produced by a different model and is out of distribution from what it was trained on.
To address this, we add custom instructions that tell the model when it's taking over mid-chat from another model. These instructions also steer it away from calling tools that appear in the conversation history but aren't part of its own tool set.

A second challenge is that caches are provider- and model-specific, so switching means a cache miss and a slower, more expensive first turn. We have experimented with mitigating this by summarizing the conversation at switch time, which provides the model with a clean summary that reduces the cache penalty. But if the user is deep into a complex task, the summary can lose important details. We generally recommend staying with one model for the duration of a conversation unless you have a reason to switch.
Another way to sidestep the challenges of mid-conversation model switching is to instead use a subagent, which starts from a fresh context window. We recently added to the harness the ability for users to directly ask for a subagent to be run with a particular model.
The harness and the future of software development
The future of AI-assisted software engineering will be multi-agent. Instead of running every subtask through a single agent, the system will learn to delegate across specialized agents and subagents: one for planning, another for fast edits, and a third for debugging, each scoped to what it does best.
Making that work well is fundamentally a harness challenge. The system needs to know which agent to dispatch, how to frame the task for that agent's strengths, and how to stitch the results into a coherent workflow. The ability to orchestrate that kind of coordination will live in the harness rather than any single agent. This means that, while harness engineering has always been important for agent success, it's only going to be more critical going forward.
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み