Agent Mentor Learn
状態管理と永続化: 長いタスクを中断から生き延びさせる · 第 5 回 / 全 6 回

レッスン5: 巻き戻しとフォーク: チェックポイントの第二の価値

学習目標:

  • 障害復旧を超えたチェックポイントの2つの能動的な使い道——以前の場面へ巻き戻してやり直す、第二のタイムラインをフォークして探索する——を説明し、どちらも再開とまったく同じチェックポイント列の上に成り立っていることを理解する
  • チェックポイントを「最新の1つだけ保持」からターンごとに保持される列へと変え、rewindTo(turn)を実装し、巻き戻すのは判断の場面であって、すでに起きた外部の副作用ではないことを説明する
  • forkFrom(turn, branchName)を実装して同じ場面から独立したタイムラインをコピーし、チェックポイント・Git・副作用台帳がそれぞれ何を担当するかの境界線を引く

前提: レッスン1〜4を終え、checkpoint.jsonのフィールド構成(versiontaskturnstokensUsedmessagespendingToolUse)とアトミックな書き込み、再開が宙ぶらりんな呼び出しをどう調整するか、そしてレッスン4の副作用台帳と冪等キーを知っていること | 前: レッスン4 << | 次: レッスン6 >>

チェックポイントはヒューズだけではない

最初の数レッスンでは、チェックポイントを災害に対する保険として扱ってきました。プロセスがクラッシュし、直近のチェックポイントからループを拾い直す、という使い方です。そう使うこと自体に何の問題もありませんが、クラッシュした後にしか手を伸ばさないのであれば、チェックポイントはほとんどの時間ただ眠っていることになります。クラッシュしなかったのだから、保存した状態は無駄だったのでしょうか。

無駄ではありませんでした。積み上がったチェックポイントの連なりは、実のところタスクのタイムラインです。各ターンで何を考えていたか、何をしようとしていたか、どの効果をすでにコミットしていたか——そのすべてが痕跡として残っています。障害復旧の先に、このタイムラインはさらに2つの能動的な使い道を支えます。以前のターンへ巻き戻してやり直すことと、あるターンからフォークして2本目のルートを並行して走らせることです。ハーネスエンジニアリングを軸に構成されたコミュニティのロードマップは、永続性コンポーネントをまさにこの3つで括ってまとめています。あらゆるノードで状態をチェックポイントし、再開・巻き戻し・フォークができるようにする1。これはロードマップ側の枠組みとしての主張であって、実装を規定するものではありませんが、一つのことを指し示しています。再開はチェックポイントの用途の3分の1にすぎず、残る2つがこのレッスンの主題だ、ということです。

  • 巻き戻し: タスクはクラッシュしていないが、進路を外れた。判断の場面を、間違える前のターンまで戻して、やり直す。
  • フォーク: どちらのルートが良いか確信が持てないので、同じ場面から独立したタイムラインを2本コピーし、それぞれ走らせて、結果を選ぶ。

どちらも「災害後の後始末」ではありません。どちらも、タスクが順調に走っている最中に持ち上がりうるものです。

巻き戻し: 巻き戻るのは判断の場面であって、外の世界ではない

20数ターンのツール呼び出しを走らせるタスクを思い浮かべてください。15ターン目でモデルが誤った判断を下します——編集するファイルを間違えて選ぶ、あるいは曖昧な要件について誤った前提を置く。そこから10ターン、モデルはその間違いの上に積み上げ続けます。このシリーズの第8コース「Context Engineering: Spending Finite Attention Where It Counts」では、コンテキストが長く雑然と積み上がるほど、モデルがそこから情報を正確に思い出す能力が落ちていくことを扱いました。しかもこの区間の履歴は、その上に誤った判断まで抱えています。進路を外れた長いコンテキストの中でモデルにもがき続けさせるくらいなら、誤判断の手前である14ターン目まで場面を戻し、そこからやり直すほうがよいのです。

そのためには、チェックポイントはもはや「最新の1つだけ」ではいられません。これまでのレッスンのsaveCheckpointは毎回同じcheckpoint.jsonを上書きしていたので、復旧時には最後に書かれた状態しか取り出せませんでした。障害復旧には十分ですが、巻き戻しには使えません。14ターン目の場面は、15ターン目にとっくに上書きされているからです。巻き戻しを支えるには、チェックポイントをターンごとの列として保持し、ターン番号と保存地点をファイル名に入れる必要があります。checkpoints/turn-014-A.jsoncheckpoints/turn-014-B.json、といった具合です。各ターンの中で、保存地点Aはモデルが計画を提示したがツールはまだ走っていない時点、保存地点Bはそのターンのツール結果がmessagesに書き戻され、ターンが本当に完了した時点に置かれます。既定では「ターンNに戻る」はそのターンが終わった後の場面、つまりそのターンで最後に書かれた保存地点を意味します。

rewindTo(14)が返す場面を手にした後の流れは、再開とまったく同じです。このmessagesを使って履歴を組み直し、このturnsのカウントからループを続けます。唯一の違いは、今回モデルが向き合うのが、判断が下される前のきれいな場面であって、15ターン目が汚染したコンテキストではない、という点です。

ただし、はっきり口に出して言っておくべきことが一つあります。巻き戻しが戻すのは判断の場面であって、外の世界ではありません。16ターン目の誤判断がすでに高影響ツールを呼んでいたら——たとえば実際にメールを送っていたら——14ターン目に巻き戻してもそのメールは戻ってきません。チェックポイントが保存するのはmessagesturnspendingToolUse、そのほかあなたがスナップショットに定義した状態フィールドであって、すでに着地した外部のアクションを取り消すようには作られていませんし、取り消すこともできません。レッスン4の副作用台帳(effects.json)は追記のみというルールに従い続けます。巻き戻して15ターン目からやり直した後、たとえモデルが今回まったく別のアクションを選んだとしても、台帳には新しいレコードが増えるだけで、古いレコードが消えることはありません。破棄された10ターンで起きたことは、それでも台帳に痕跡を残します。これはまさに、レッスン4の冪等性の見方を巻き戻しの場面に持ち込んだものです。

フォーク: 一つの場面から2本のタイムラインを走らせる

巻き戻しは「このルートは間違いだった、戻ってやり直す」を解決します。しかし時には、問いが「間違っていたか」ではなく「どちらが良いか確信が持てない」であることもあります。2つのリファクタリング案がどちらも筋が通っていて、選ぶ前にそれぞれ走らせて比べたい、という場合です。そのときは破壊的にどちらかを選ぶのではなく、同じチェックポイントから独立したタイムラインを2本コピーして、それぞれ走らせます。

forkFrom(14, "plan-b")の後、checkpoints-plan-b/は自分専用のチェックポイント列と空の副作用台帳を持ちます。14ターン目以降、このタイムラインがどこへ向かうか、何ターン走るか、いくつチェックポイントを落とすか——そのどれも本線に干渉しません。

フォークした2本のタイムラインが独立しているという事実は、高影響ツールにとっては警告でもあります。両方のタイムラインが同じ、本当に外部に届くアクションを呼ぶ場合——たとえばどちらも同じメールを送る必要がある場合——承認なしにそれぞれを最後まで走らせると、各タイムラインが1回ずつ送信し、副作用が二重になります。そうしたツールに承認ゲートを取り付けるか、フォークの間はドライランモードに切り替えるかは、フォークする前にやっておく価値があります。巻き戻しても台帳は巻き戻らないのと同じ理屈です。チェックポイントは2つにコピーできますが、すでに着地した外部の効果は「並行世界ごとに1つずつ」へコピーすることはできません。

製品との比較: Claude Code はこれを機能として出荷している

ここまでの巻き戻しとフォークは、Claude Code がすでに製品グレードの機能として提供しているものです——これはあくまで比較のためであり、教えている対象そのものではありません。そのチェックポイント機構は、各ユーザープロンプトの前にコードの状態を自動で取得します2。ユーザープロンプトごとに新しいチェックポイントが作られ2、Claude Code はチェックポイントを会話とともに保存するので、セッションを再開した後でも/rewindを実行できます2

その/rewindメニューは「何を復元するか」を3つの選択肢に分けています。"Restore conversation: rewind to that message while keeping current code"、"Restore code: revert file changes while keeping the conversation"、あるいは "Restore code and conversation: revert both code and conversation to that point"2(順に、コードは現在のまま会話だけをそのメッセージまで巻き戻す/会話は保ったままファイル変更を戻す/コードと会話の両方をその時点まで戻す)。これは、このレッスンの「巻き戻すのは判断の場面である」という一文をそのまま製品化したものになっています。判断の場面(会話)だけを戻すことも、コードごと戻すことも選べます。公式ドキュメントはチェックポイントの一般的なユースケースもいくつか挙げていて、"Exploring alternatives: try different implementation approaches without losing your starting point"(出発点を失わずに異なる実装アプローチを試す)や "Recovering from mistakes: quickly undo changes that introduced bugs or broke functionality"(バグを持ち込んだり機能を壊したりした変更を素早く取り消す)といったものです2。注意しておきたいのは、これらのユースケースはドキュメント上、チェックポイント機能(/rewind)の下に一括で列挙されているのであって、「巻き戻し」と「フォーク」に振り分けられているわけではない、という点です。とはいえ、このレッスンの2つの使い道——「間違えたので戻ってやり直す」と「確信が持てないのでフォークして試す」——と突き合わせてみると、方向性は一致しています。

フォークの側については、Claude Code は/branchまたはclaude --continue --fork-sessionを提供しています。"To branch off and try a different approach while preserving the original session intact, use /branch or claude --continue --fork-session"2(元のセッションを無傷のまま保ちながら分岐して別のアプローチを試すには、/branch または claude --continue --fork-session を使う)。

境界と役割分担: チェックポイント、Git、副作用台帳はそれぞれ何を担当するか

Claude Code のドキュメントは、それ自身の境界線も引いています。そのチェックポイント機構は "does not track files modified by bash commands"2(bash コマンドが変更したファイルは追跡しない)、そして "Only direct file edits made through Claude's file editing tools are tracked"2(Claude のファイル編集ツールを通じた直接のファイル編集のみが追跡される)。同じ理屈で、あなた自身のハーネスのチェックポイントがカバーするのも、スナップショットに明示的に定義した状態フィールドだけです——messagesturnstokensUsedpendingToolUse。ツールが外の世界に加えた変更——データベースへの書き込み、別サービスの呼び出し、メールの送信——はチェックポイントの管轄外です。それは副作用台帳の仕事です。

公式ドキュメントはこの仕組みの役割をはっきり述べています。チェックポイントはセッションレベルの素早い復旧のために設計されており、長期的な履歴と共同作業については "continue using version control, such as Git, for commits, branches, and long-term history."2(コミット、ブランチ、長期履歴には Git のようなバージョン管理を使い続けること)。3つはそれぞれ別の領域を担当しており、並べてみるとより明確になります。

仕組み担当するもの時間スケール
チェックポイント実行中の場面 — messages、ターン数、まだ実行されていないツール呼び出し分単位、セッションレベル
Gitコード自体の履歴 — コミット、ブランチ、共同作業恒久的、共同作業
副作用台帳すでに起きた外部の副作用 — 送信されたメール、書き込まれたレコード追記のみ、恒久保持

保持コスト: トレードオフを選ぶ

ターンごとのチェックポイント列を保持することはタダではありません。1ターンあたり2つの保存地点があり、タスクが長く走るほどディスク上にファイルが積み上がります。Anthropic の "you should consider adding complexity only when it demonstrably improves outcomes"3(複雑さは、成果を明らかに改善する場合にのみ追加を検討すべき)という原則は、ここにもそのまま当てはまります。タスクが数ターンしか走らず、巻き戻しがめったに必要にならないのであれば、列全体を保持するのは見合わないかもしれません——これまでのレッスンのように、最新の1つだけ保持すれば十分です。逆に、タスクが数十ターン走り、いくつかのアプローチを試すために巻き戻しやフォークが日常的に必要になるなら、保持した列は元を取ります。何かがうまくいかなくても最初からやり直す必要がなく、試行錯誤のコストが下がるからです。これは結局のところ、タスクの規模に応じた判断であって、どちらのやり方が本質的に正しいかという問題ではありません。

まとめ

  • チェックポイントは障害復旧の保険であるだけではありません。保持されたチェックポイントの連なりはタスクのタイムラインであり、再開の先に巻き戻しとフォークを支えます。あるコミュニティのロードマップは、この3つをまとめて永続性コンポーネントが担当するものとして枠づけています1
  • 巻き戻しが戻すのは判断の場面であって、外の世界ではありません。本当に起きた外部のアクションは巻き戻しでは取り消されず、副作用台帳は追記のみのままです——レッスン4の冪等性の見方を巻き戻しの場面に持ち込んだものです
  • 巻き戻しを支えるには、上書きして最新の1つだけを残すのではなく、チェックポイントをターンごとの列(turn-014-A/B.jsonのような形)として保持する必要があります。rewindTo(turn)は既定でそのターンの最後に書かれた保存地点を返します
  • フォークは同じ場面から独立したタイムラインをコピーし、それぞれが専用のチェックポイント列と副作用台帳を持ちます。両方のタイムラインが同じ高影響ツールに当たるなら、承認ゲートを取り付けるかドライランモードに切り替えるのを忘れないでください——さもないと副作用が二重になります
  • Claude Code はすでに巻き戻しとフォークを製品機能として出荷しています。プロンプトごとに自動作成されるチェックポイント、会話とコードを別々に復元できる/rewind、フォークのための/branch--fork-sessionです2。ただしそれ自身の境界も引いています。追跡するのは Claude 自身のファイル編集ツールによる編集だけで、bash コマンドによる変更は追跡しません2。そして位置づけはセッションレベルの素早い復旧であり、長期的な履歴と共同作業は依然として Git が担当します2
  • 3つの異なる仕事: チェックポイントは分単位の実行中の場面を、Git は恒久的で共同作業のためのコード履歴を、副作用台帳はすでに起きた外部の副作用を担当します。ターンごとのチェックポイント列を丸ごと保持することにはディスクのコストがあり、それが見合うかどうかはタスクの規模次第であって、どちらのやり方が本質的に正しいかという話ではありません3

>> レッスン6: ハンズオン: ハーネスにチェックポイントと再開を配線する

Footnotes

  1. The 2026 Agent Engineering Roadmap — GitHub (codejunkie99/agent-roadmap-2026) — https://github.com/codejunkie99/agent-roadmap-2026 2

  2. Checkpointing — Claude Code Docs — https://code.claude.com/docs/en/checkpointing 2 3 4 5 6 7 8 9 10 11 12

  3. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2

練習

01

以下の4つのシナリオそれぞれについて、巻き戻し・再開・フォーク・Git のどれを使うべきでしょうか。それぞれ答えと、その理由を述べてください。

レベル1: 4つのシナリオ、正しい道具を選ぶ
  1. あるタスクが20数ターン走った後、12ターン目でモデルが誤ったリファクタリング案を選んでいたことに気づいた。以降のターンはすべてその誤った案の上に積み上がっているが、プロセス自体はまだ正常に走っており、クラッシュはしていない。
  2. 同じタスクが18ターン目に達したところで、ホストマシンが再起動され、プロセスが完全に落とされ、何も完了しなかった。
  3. あるモジュールを2つのサービスに分けるべきか3つに分けるべきか確信が持てず、エージェントにそれぞれの案を1回ずつ走らせて、結果を比較したい。
  4. このコードが3日前にどうなっていたか、そして誰がいつ変更したかを知りたい。
完了基準 · ローカルでチェック
02

以下のコードは、これまでのレッスンの古いバージョンです。毎回同じcheckpoint.jsonを上書きするので、再開は支えられますが巻き戻しは支えられません。このレッスンが求める形に書き直してください。ファイル名はturn-NNN-A.json/turn-NNN-B.jsonというターンごとの保持ルールに従うこと。再開用に、既定で最新ターンの最後に書かれた保存地点を返すloadLatest()を用意すること。そして特定のターンの場面を取り出すrewindTo(turn)を用意すること。書き終えたら検証を書いてください。5ターン分を連続して保存し、rewindTo(3)を呼び、返ってきた場面のturnsが3であること、そして副作用台帳effects.jsonの長さが巻き戻っていないことを確認します。

レベル2: saveCheckpoint をターンごとの列に変える
完了基準 · ローカルでチェック