長時間稼働エージェントのハーネス設計 — Anthropic engineeringが示す失敗モードと対処パターン
Anthropicのengineeringブログが示した長時間エージェント運用の知見。コンテキストウィンドウを超えて働かせる際の失敗モードと、Feature List・段階的commit・init.shの対処をまとめます。
Anthropicのengineeringブログが2025年11月26日に公開した「Effective Harnesses for Long-Running Agents」は、Claude Opus 4.5を複数のコンテキストウィンドウにまたがる長時間タスクで動かすために、ハーネス(エージェント実行基盤)側で何を用意するかを示した記事です。検証台はclaude.aiのクローンをゼロから作らせるタスクで、200以上の機能を複数セッションで積み上げる設定でした。
本記事は、このブログの失敗モードと対処パターンを、実装の観点で読み替えます。
対象は、Claude Agent SDKやClaude Codeで自前のエージェントを設計する開発者です。
背景 — compactionがあっても長時間タスクは崩れる
長時間エージェントの中心的な難しさは、エージェントが「離散的なセッション」で働き、新しいセッションは前の記憶なしに始まる点にあります。ブログはこれを、前の勤務帯の記憶を持たないエンジニアが交代で入ってくるプロジェクトにたとえています。
Claude Agent SDKには、コンテキストを使い切らずに作業を続けるcompaction(会話の圧縮)があります。理屈の上では、これで任意の長さの作業を続けられるはずです。ところがブログは「compactionだけでは足りない」と述べています。Opus 4.5をAgent SDKのループで回し、「claude.aiのクローンを作れ」という高水準のプロンプトだけを渡しても、本番品質のWebアプリには届かなかったそうです。
失敗は2つの型で現れました。
- 詰め込みすぎ: アプリ全体を一度に実装しようとして、実装の途中でコンテキストが尽きる。次のセッションは、半端な実装と記録のない状態から始まる
- 早期完了宣言: プロジェクトの後半で、新しいエージェントが周囲を見回し、進捗があるのを見て「終わった」と宣言する
前者はcompactionを使っていても起きます。圧縮が、次のエージェントに明確な指示を渡しきるとは限らないためです。
ブログはこの問題を2つに分けます。1つ目は、必要なすべての機能の土台になる初期環境を用意すること。2つ目は、各セッションで少しずつ進めつつ、終了時には環境をきれいな状態に戻すことです。
「きれいな状態」の定義
mainにマージできる品質
重大なバグがなく、コードが整理され、ドキュメントがある状態です。
次の機能にすぐ入れる
無関係な散らかりを片付けなくても、開発者が新しい機能に着手できる状態を指します。
つまり長時間タスクの可否は、モデルの能力だけでなく、エージェントを動かす環境側の設計に左右されます。
初期化エージェントとコーディングエージェント
ブログの解決策は、役割の違う2つのセッションです。最初のセッションだけ専用のプロンプトを使い、以降のセッションは別のプロンプトで回します。
2つのセッションの役割
初期化エージェント
環境を作る役です。init.sh、作業の履歴を残すclaude-progress.txt、追加したファイルが分かる最初のgit commitを用意します。
コーディングエージェント
少しずつ進める役です。1回のセッションで前進し、次のセッションへの構造化された更新を残して終わります。
ブログの脚注によると、これらを別のエージェントと呼ぶのは、最初のユーザープロンプトが違うからにすぎません。システムプロンプト、ツール群、ハーネス全体は両者で同一です。つまり別のモデルや別の基盤を用意する話ではなく、同じハーネスに渡す最初の指示を変える設計です。
この構成の背景には、Claude 4のプロンプティングガイドに載っている、複数コンテキストウィンドウ向けのハーネス構造があります。最初のコンテキストウィンドウにだけ「別のプロンプト」を使う、というものです。ブログの付属コードはGitHubのclaude-quickstartsリポジトリの自律コーディングのデモとして公開されています。
ハーネスを構成する仕組み
ハーネス側で用意する仕組みは、失敗モードごとに次のように対応づけられます。ブログの表を日本語にしたものです。
| 失敗モード | 初期化エージェント側 | コーディングエージェント側 |
|---|---|---|
| プロジェクト全体の早期完了宣言 | 初期化エージェント側仕様から機能一覧のJSONを作る | コーディングエージェント側セッション冒頭で一覧を読み、機能を1つ選ぶ |
| 環境のバグや未記録の進捗 | 初期化エージェント側gitリポジトリと進捗メモを用意する | コーディングエージェント側冒頭で進捗とログを読み基本テストを走らせ、終了時にcommitと進捗更新 |
| 機能の早すぎる完了マーク | 初期化エージェント側機能一覧を用意する | コーディングエージェント側慎重にテストしてから「passing」にする |
| アプリの動かし方の学習に時間がかかる | 初期化エージェント側init.shを書く | コーディングエージェント側セッション冒頭でinit.shを読む |
Feature List — 完了の定義をモデルから取り上げる
初期化エージェントは、ユーザーの最初のプロンプトを展開した機能要件の一覧を書きます。claude.aiクローンでは200以上で、「新規チャットを開き、質問を入力して、AIの応答が見える」といった粒度です。最初はすべて「failing」にしておき、後続のエージェントが完成形の見取り図を持てるようにします。
ブログに載っているエントリの形は次のとおりです。
{
"category": "functional",
"description": "New chat button creates a fresh conversation",
"steps": [
"Navigate to main interface",
"Click the 'New Chat' button",
"Verify a new conversation is created",
"Check that chat area shows welcome state",
"Verify conversation appears in sidebar"
],
"passes": false
}コーディングエージェントには、このファイルのpassesフィールドの状態変更だけを許します。さらに、強い言葉で縛っています。たとえば「It is unacceptable to remove or edit tests because this could lead to missing or buggy functionality」(テストを削除・編集するのは許されない。機能の欠落やバグにつながるため)と書かれています。
形式にJSONを選んだ理由は、試行錯誤の結果です。モデルは、JSONファイルをMarkdownファイルより不適切に書き換えたり上書きしたりしにくいと分かったためです。機械が読み書きする状態ファイルにはJSONを基本にする、という指針になります。
実装の観点では、完了の定義をモデルの自己申告ではなく外部ファイルに置くことと、編集規則をプロンプトで縛ることの2点が要です。計画ファイルを使うClaude Codeのワークフローと並べると、計画ファイルが作業の分割を担うのに対し、Feature Listは完了基準の固定を担う、という違いとして読めます。
段階的な進捗 — gitとprogress fileの併用
初期環境ができたら、コーディングエージェントには一度に1機能だけ作業させます。この漸進的なやり方が、詰め込みすぎ対策として決定的でした。
さらに、コードを変更するたびに環境をきれいな状態へ戻させる必要があります。ブログによれば、それを引き出すのに最も効いたのは、説明的なメッセージでgit commitさせることと、進捗の要約を進捗ファイルに書かせることでした。これで、悪い変更をgitでrevertして動く状態へ戻せます。
エージェントが「何が起きたか」を推測し、基本のアプリを動かし直すのに時間を使う無駄も消えます。gitは機械が読む変更の履歴、claude-progress.txtは次のセッションへの引き継ぎメモと、役割が分かれています。具体的な書式はClaudeの長時間タスクをtests.jsonとprogress.txtで管理するにまとめています。
Testing — ブラウザ操作で完了を判定する
観察された失敗モードがもう1つあります。十分にテストせずに機能を完了と見なす癖です。明示的に指示しないと、Claudeはコードを変更し、単体テストや開発サーバーへのcurlでテストまでするのに、機能がエンドツーエンドでは動かないことに気づきません。
そこで、ブラウザ自動化のツールを使い、人間のユーザーとして全テストを行うよう明示しました。Puppeteer MCP(Model Context Protocol)サーバー経由でClaudeがスクリーンショットを撮りながらテストすると、コードを読むだけでは分からないバグを見つけて直せるようになり、性能が大きく改善したと報告されています。
限界もあります。Claudeの視覚とブラウザ自動化ツールの制約のため、あらゆるバグを見つけるのは難しいままです。たとえばPuppeteer MCP越しには、ブラウザ標準のalertモーダルが見えません。その種のダイアログに依存する機能は、バグが残りやすい傾向がありました。
スクリーンショットを毎ターン撮ってClaudeに見せる構成では、画像が積み上がるほどプロンプトキャッシュが壊れやすくなる、という指摘があります(Anthropicのブログ自体には書かれていません)。間引きやcache_controlの置き方はComputer Useでプロンプトキャッシュを保つスクリーンショット管理で扱っています。
init.sh — セッション開始の手順を固定する
すべてのコーディングエージェントは、セッションの冒頭で状況をつかむ手順を踏みます。ブログが箇条書きにしているのは最初の3項目で、4つ目の起動確認はinit.shの説明から補っています。
セッション冒頭の手順(ブログの3項目と起動確認)
- 1
作業ディレクトリを確かめる
pwdを実行します。編集できるのはこのディレクトリの中のファイルだけです。 - 2
最近の作業を読む
gitのログと進捗ファイルを読み、直近の作業を把握します。
- 3
次の機能を選ぶ
機能一覧を読み、未完了のうち優先度が最も高いものを選びます。
- 4
開発サーバーを起こして基本動作を試す
init.shで開発サーバーを動かし、新機能に入る前に基本のエンドツーエンドテストを通します。
手順4が要点です。claude.aiクローンでは、サーバーを起動し、Puppeteer MCPで新しいチャットを開いてメッセージを送り、応答を受け取るところまでを毎回行いました。アプリが壊れたまま放置されていないかをすぐ見抜き、先に直せます。壊れた状態で新機能に着手すると、問題を悪化させるおそれがあるためです。
init.shには、アプリの動かし方を毎回調べ直す手間を省いてトークンを節約する効果もあります。ブログに載っているセッションの書き出しは、pwd、claude-progress.txtとfeature_list.jsonの読み込み、git log --oneline -20の確認、サーバーの再起動、基本機能の動作確認という順でした。
付属のquickstartに見る実装
付属のquickstartは、この2エージェント構成を動かす最小のハーネスです。実行するとプロジェクトにはfeature_list.json、app_spec.txt、init.sh、claude-progress.txt、.claude_settings.jsonが生成されます。起動コマンドは次のとおりです。
python autonomous_agent_demo.py --project-dir ./my_project
python autonomous_agent_demo.py --project-dir ./my_project --max-iterations 32行目の--max-iterationsは、試しに回数を絞るためのオプションです。READMEは、この処理が長くかかることを警告しています。最初のセッションで200件の機能一覧を書き出すのに数分かかり、止まったように見えることがあります。2回目以降は1回あたり5〜15分、全機能を作り切るには複数セッションで数時間かかるとのことです。
セッション間のつなぎも決まっています。各セッションは新しいコンテキストウィンドウで走り、進捗はfeature_list.jsonとgit commitで受け渡され、3秒の間を置いて自動で次のセッションに進みます。Ctrl+Cで止め、同じコマンドを再実行すれば再開できます。
安全面では、多層の防御が入っています。
- OSレベルのサンドボックスでBashコマンドを隔離する
- ファイル操作をプロジェクトディレクトリ内に制限する
- Bashを許可リスト(
ls、cat、grep、npm、node、gitなど)に限る
許可リストにないコマンドは、セキュリティフックが止めます。長時間の無人実行では、環境を整える側の設計に権限の絞り込みも含まれる、という例です。
単発エージェントとの設計差
ここからは編集側の解釈です。ブログの対処は、単発エージェントの設計をそのまま延ばしたものではなく、別の設計問題への答えに見えます。
単発エージェントの設計は、良いシステムプロンプトを書き、必要なツールを過不足なく定義し、ツールの戻り値を簡潔にしてコンテキスト消費を抑える、という最適化でした。長時間エージェントではこれに、次の問題が加わります。
- 状態の永続化: コンテキストを超えて引き継ぐ情報の選別と置き場所
- 完了の客観化: モデルの「終わった」を鵜呑みにしない仕組み
- 環境の冪等性: セッションを何度立ち上げても同じ状態に戻せること
- 失敗の局所化: 1セッションが破綻しても全体が壊れない粒度。git revertが効く単位です
Feature List・progress file・init.sh・git commitは、この4つに対応づけて読めます。Claude Codeで進むworktree分離やCheckpointingも、同じ問題群を別の角度から扱うものと読めます(Claude Code v2.1.50の更新も参照)。
宿題 — 単一エージェントか、役割分担か
ブログ末尾のFuture workは、2つの未解決点を挙げています。1つ目は、汎用の単一コーディングエージェントが最良なのか、マルチエージェント構成のほうが良いのかが分からない点です。テスト担当、QA担当、コード整理担当のような専門エージェントが、開発工程の各サブタスクをより上手くこなす可能性があります。
役割を分ければ各エージェントのコンテキストは軽くなります。一方で、誰がFeature Listを更新し、誰が進捗ファイルを書くのかという、エージェント間の状態同期のコストが生まれます。ClaudeのSub-agent機能やAgent SDKの複数エージェント構成はこの方向の延長にありますが、ブログ自身、どちらが良いかの結論は出していません。
2つ目は、このデモがフルスタックのWebアプリ開発に最適化されている点です。ブログは、科学研究や金融モデリングのような他の長時間タスクへの一般化を今後の方向として挙げ、教訓の一部または全部が当てはまるだろうと見ています。
自前エージェントに持ち込むときの点検項目
自分のハーネスに当てはめるときの確認項目を挙げます。
- 完了の定義は外部ファイルか: JSONで明示し、モデルの自己申告に頼らせない
- 状態ファイルの形式は用途に合うか: 機械が更新するならJSON、人が読むだけならMarkdown
- セッション冒頭の立ち上げが1コマンドか:
init.sh相当を最初に走らせる - commitの粒度は1機能か: revertで巻き戻せる単位にする
- テストをエージェント自身が実行できるか: Puppeteer MCPなど、結果を自分で見る経路を持たせる
- 近道を明示的に塞いだか: テストの削除や書き換えの禁止をプロンプトに書く
まとめ
ブログが示したのは、長時間エージェントの失敗を、モデルの能力不足ではなくハーネスの穴として塞ぐやり方です。自分の構成を点検するなら、「完了を誰が判定しているか」と「次のセッションが何を読んで始まるか」の2点を先に確かめるのが近道です。
Agent SDKで複数セッションのエージェントを作る場合は、付属のquickstartをそのまま動かして、Feature Listとprogress fileがどう受け渡されるかを観察するところから始められます。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。