第12章 複数ステップのタスク — 計画→実行→確認、失敗したらやり直す

📖 この章のゴール:難しいタスクを「計画 → 実行 → 確認 → 直す」の流れで進められるようになる。ループ(第4章)はもうできているので、新しく道具を足すのではなく、システムプロンプトで“段取り”を促し確認(テストを走らせる)と再試行(やり直し)を入れて、エージェントの信頼性を上げます。 ← 目次・はじめにへもどる


📱 Claude Code ではこう見える

第1章で見た「このテスト、落ちてるから直して」の場面を、今度は段取りに注目して見てみましょう。Claude Code はこう動きます。

  1. いきなり書き換えず、まず「やることリスト(TODO)」を立てる
    • ① 失敗しているテストの中身を読む
    • ② 原因のファイルを読む
    • ③ 直す
    • ④ テストを走らせて確認する
    • ⑤ まだ失敗していたら、原因を直してもう一度
  2. リストを上から順にこなしていく(ファイルを読む → 直す)
  3. 必ずテストを走らせて結果を見るnpm test
  4. まだ失敗していたら、エラーを読んで原因を直し、また走らせる
  5. テストが通ったら「直りました」と報告して止まる

ポイントは2つあります。ひとつは、最初に計画(やることリスト)を立ててから動くこと。もうひとつは、「直した」で終わらせず、必ずテストを走らせて“本当に直ったか”を確かめること。そして通るまで、何度でもやり直す

この「計画 → 実行 → 確認 → 直す」という進め方こそ、難しいタスクをエージェントが最後までやり切るための“段取り”です。この章では、これを自分のエージェントにも入れていきます。


🤔 なぜ?/やらないとどうなる

土台はもうある——足すのは「段取り」と「確認」(🟢 基礎)

うれしいことに、仕組みはもう第4章で完成しています。エージェントループ——「考える → 道具を使う → 結果を見て、また考える」のくり返し——が、複数ステップのタスクを回すエンジンそのものだからです。

[第4章で作ったループ]  考える → 道具 → 結果を見る → また考える → …(最大10回)

このループは、すでに「何度も往復する」力を持っています。だから新しいエンジンは要りません。この章で足すのは、エンジンに運転のしかた(段取り)を教えることだけです。具体的には2つ。

  • 🗺 まず計画を立てさせる:いきなり手を動かす前に、「やることを箇条書きにしてから始めて」と促す。
  • 必ず確認させる:直したあとに「テスト(や型チェック)を走らせて、本当に通るか確かめて」と促す。失敗していたら、原因を直してやり直す

この2つを、システムプロンプト(頭脳に最初に渡しておく“やり方”の指示。第10章)に書き足すだけ。道具は1つも増えません。

🍳 たとえ:見習い(あなたのコード)とシェフ(LLM)の往復は、もうできています。でも、難しい料理を頼むと、シェフはいきなり鍋を火にかけて、味見もせずに「できました」と言ってしまうことがある。そこで「まず手順を口に出してから作って」「最後に必ず味見して、ダメならやり直して」としつけておく。これが今回やることです。新しい道具(鍋)ではなく、段取りの“しつけ”を足すわけです。

「確認」を抜くと“できたつもり”で終わる(🟢 基礎)

いちばん大事なのが、確認のステップです。なぜそんなに大事なのか。

LLM は文章を作るのがとても上手なので、コードを書き換えると「直しました」と自信たっぷりに言ってきます。でも、その“直し”が本当に正しいかどうかは、実際に動かしてみるまで誰にも分かりません。LLM 自身にも分かっていません。書いた瞬間は、ただの「たぶん合っているはずの文章」なのです。

💡 ここが肝心:「直した」と「直った」は、まったく別のことです。

  • 「直した」=コードを書き換えた(ファイルが変わっただけ)
  • 「直った」=書き換えた結果、テストが通った(本当に動いた)

確認(テストを走らせる)をはさまないと、エージェントは「直した」をもって「直った」と思い込み、まだ壊れたまま「完了しました」と報告してしまいます。これを本書では“できたつもり”(偽の完了)と呼びます。これが、複数ステップのタスクでいちばん多い失敗です。

🍳 たとえ:味見をしないシェフ。レシピ通りに作ったから「完成です」と出してくる。でも塩を入れ忘れているかもしれない。口に入れて確かめる(テストを走らせる)まで、料理は完成していないのです。

サボると何が起きる?(🔧 応用)

確認を入れないまま難しいタスクを頼むと、こんなことが起きます。

  • 偽の完了:「直りました!」と報告 → でも実際にはテストはまだ赤いまま。あなたが自分で走らせて初めて気づく。
  • 直したつもりで別の場所を壊す:1か所直したら、その影響で別のテストが落ちる(これも、走らせないと気づけない)。
  • 計画なしで迷子になる:大きなタスクを順序立てずに始めて、途中で「あれ、何をしようとしてたんだっけ」と行き当たりばったりになる。

逆に「計画 → 実行 → 確認 → 直す」を入れておくと、エージェントは自分の仕事を自分でチェックして、ダメなら直すようになります。これがあるかないかで、「たまに当たるAI」と「最後までやり切るAI」の差が生まれます。

🧠 ちなみに、この「直す → 確認する → まだダメなら直す」を自分でくり返すことを、自己修正(じこしゅうせい)と呼びます。エージェントの信頼性は、賢さよりも、この自己修正のループが回っているかで決まる、と言ってもいいくらいです。


🛠 こう作る

新しいファイルや道具は作りません。SYSTEM_PROMPT に“段取り”を書き足すのがメインです。そのあと、テストを直す一連の流れを通しで見てみます(任意で、計画を見える形にする小さな道具も紹介します)。

① システムプロンプトに「段取り」を書き足す(🟢 基礎)

第10章で SYSTEM_PROMPT(頭脳に最初に渡す“性格・やり方”の指示)を作りました。そこに、手順のルールを足します。

const SYSTEM_PROMPT = `あなたは慎重なコーディングエージェントです。
難しいタスクは、次の順番で進めてください。

1. まず、やることを短い箇条書き(計画)にしてユーザーに示す。
2. 計画の上から順に、道具を使って実行する。
3. コードを直したら、必ず run_command でテストや型チェックを走らせて確認する。
4. 確認が失敗したら、エラーの原因を読み取って直し、もう一度確認する。
5. 確認が通ったら、何をしたかを短くまとめて終了する。

「直した」だけで終わらせないこと。テストが通って初めて「完了」です。`;

1行ずつ読むと:

  • 1〜2行目:頭脳に「あなたは慎重なコーディングエージェント」と人格を与え、「この順番で進めて」と前置きします。性格づけは第10章でやった通りです。
  • 「1. まず…計画を…示す」:いきなり手を動かさず、最初に計画(やることリスト)を言葉にしてから始めて、というルール。これで“行き当たりばったり”を防ぎます。
  • 「2. 計画の上から順に…実行する」:立てた計画を順番にこなして、という指示。
  • 「3. …必ず run_command でテストや型チェックを走らせて確認する」:これがこの章の心臓。直したら必ず確認run_command(第7章で作った、コマンドを走らせる道具)を使わせます。
  • 「4. …失敗したら…直し、もう一度確認する」:これが自己修正のループ。失敗を読んで直し、また確認する、を回させます。
  • 「5. …通ったら…まとめて終了する」:通ったら、だらだら続けずにまとめて止まる
  • 最後の1行:“できたつもり”を禁止するダメ押し。「テストが通って初めて完了」と、いちばん大事なルールをもう一度言い切っています。LLM への指示は、大事なことを最後にもう一度書くと効きやすいです。

💡 ここがこの章のキモです。ループ(第4章)はそのまま。足したのは、システムプロンプトの“言葉”だけ。同じエンジン、同じ道具でも、「計画してから動け」「必ず確認しろ」と書いておくだけで、エージェントの動き方が大きく変わります。これが第3の主役・システムプロンプトの力です(くわしくは第10章)。

ループ本体(runAgent)や道具(run_command など)は、第4章〜第7章のまま、一切変えませんmessages.createsystem: SYSTEM_PROMPT を渡しているので(第2章で確立)、この新しい段取りが毎回の往復に効いてきます。

② ウォークスルー:落ちているテストを直す一連(🟢 基礎)

では、agent.ts を起動して、第1章の例を実際に頼んでみましょう。hello.test.ts というテストが落ちている、という設定です。

> hello.test.ts が落ちてる。原因を調べて直して、テストが通るまでお願い

エージェントの中では、第4章のループが回りながら、こんなやりとりが起きます。ループの1周(step)ごとに追ってみます。

[step 0] 頭脳:まず計画を立てる
  → 「① テストを読む ② 対象コードを読む ③ 直す ④ テストを走らせる ⑤ ダメなら直す」と表示
  → read_file("hello.test.ts") を呼ぶ
[あなたのコード] テストの中身を読んで結果を返す
  → 「expect(hello()).toBe('Hello, world!') を期待」と分かる

[step 1] 頭脳:本体を読む
  → read_file("hello.ts") を呼ぶ
[あなたのコード] 中身を返す
  → 「return 'Hello world!'(カンマ抜け)」が原因と分かる

[step 2] 頭脳:直す
  → write_file("hello.ts", "...return 'Hello, world!';...") を呼ぶ
[あなたのコード] 書き込んで「書き込みました」を返す

[step 3] 頭脳:必ず確認する  ← ここがこの章の肝
  → run_command("npm test") を呼ぶ
[あなたのコード] テストを走らせる
  → 結果(成功 or 失敗)を返す

ここで分岐します。もし、まだ失敗していたら——

[step 3 の結果] 「FAIL hello.test.ts / received 'Hello, world!!'(!が2つ)」
[step 4] 頭脳:エラーを読んで原因を直す  ← 自己修正
  → write_file("hello.ts", "...直したコード...") を呼ぶ
[あなたのコード] 書き込む
[step 5] 頭脳:もう一度確認する
  → run_command("npm test") を再び呼ぶ
[あなたのコード] 走らせる → 今度は「PASS」
[step 6] 頭脳:通った! → 「直りました。原因はカンマ抜けでした」とまとめて終了

1行ずつ読むと(流れの読み方):

  • step 0:いきなり書き換えず、まず計画を立てて表示しています。システムプロンプトの「1.」が効いた瞬間です。
  • step 1〜2:計画通り、読む → 直すを順にこなしています。ここは第5章・第6章で作った read_file / write_file がそのまま働いています。
  • step 3:直したあと、勝手に「完了」と言わずに run_command("npm test") で確認しています。システムプロンプトの「3.」の効果です。ここを抜くと“できたつもり”になります。
  • step 4〜5:1回目の直しが不十分でも、エラーを読んで原因を直し、また確認しています。これが自己修正のループ(「4.」の効果)。
  • step 6:テストが通ったのを確認してから「完了」と言って止まっています。「直った」を確かめてからの完了です。

💡 注目してほしいのは、この一連が「第4章のループ」の中で自然に起きていることです。step が進むたびに「考える → 道具 → 結果を見る」がくり返され、システムプロンプトの段取りに沿って、計画 → 実行 → 確認 → 直す、が進みます。新しい仕掛けはゼロ。ループ+システムプロンプト=複数ステップのタスク、というわけです。

③(任意)計画を“見える形”にする:宣言させる or 小さな道具(🔧 応用)

上のやり方では、計画は頭脳がテキストで話すだけでした。これで十分ですが、計画をもっとはっきり見える形にしたいなら、2つの方法があります。

方法A:テキストで宣言させる(道具なし・おすすめ) システムプロンプトに一行足すだけです。

// SYSTEM_PROMPT の「1.」をこう具体化する
// 「まず『計画:』で始まる箇条書きを必ず出力してから、実行に移ること。」

1行ずつ読むと:

  • LLM に「必ず『計画:』という見出しの箇条書きから始めて」と形を指定しているだけです。道具は要りません。出力に「計画:」の行が出てくるので、あなたも進み具合を追いやすくなります。まずはこれで十分です。

方法B:簡単な update_todo 道具を足す(🔧 応用) 「やることリスト」を道具として持たせ、進捗を更新させる方法です。Claude Code の TODO 表示に近い動きになります。

let todos: string[] = [];

async function update_todo(items: string[]): Promise<string> {
  todos = items;
  return "TODO:\n" + items.map((t, i) => `${i + 1}. ${t}`).join("\n");
}

1行ずつ読むと:

  • 1行目:いまの「やることリスト」を入れておく入れもの(todos)を用意します。
  • 3行目:update_todo という道具。LLM が決めたやることの配列items)を受け取ります。
  • 4行目:受け取ったリストで todosまるごと更新します。
  • 5行目:「1. …/2. …」と番号つきの文字列に整えて返します。これがそのまま LLM への「いまのTODOはこれです」という確認になります。

あとは第3章〜第5章で何度もやった通り、tools にメニューを1つ足し、executeToolswitchcase "update_todo": return await update_todo(input.items); を1行足すだけです(やり方は第7章とまったく同じなので省略します)。これで LLM が「計画を更新します」と言いながら update_todo を呼び、リストが画面に出るようになります。

💡 どちらでもOKです。まずは方法A(テキストで宣言)で十分。「進捗をきれいに表示したい」「あとから計画を機械的に扱いたい」と思ったら、方法Bに進んでください。この章の本質は“計画させること”であって、その見せ方ではありません

🔧 もっと考えさせたいとき:adaptive thinking(深入りはしない)

とても複雑なタスク(込み入った原因の切り分けなど)では、頭脳に考える時間を多めに取らせることもできます。messages.createthinking を足す方法です。

// 複雑なタスクのときだけ(ふだんは不要)
// const res = await anthropic.messages.create({
//   model: MODEL, max_tokens: 1024, system: SYSTEM_PROMPT, messages, tools,
//   thinking: { type: "adaptive" },
// });

1行ずつ読むと:

  • thinking: { type: "adaptive" } を足すと、頭脳が必要に応じて考える量を自分で増やしてから答えるようになります。難しい問題ほどじっくり、簡単な問題はさっと、と自動で加減してくれるイメージです。

🔧 ただし、本書の基本は thinking を省いたシンプルな形です(第2章のルール)。計画と確認のしくみが先で、thinking はあくまでここぞというときの追加オプション。「そういう手もある」とだけ覚えておけば十分で、いまは深入りしません。


⚠️ ハマりどころ

  • 確認ステップを抜く=偽の完了 この章でいちばん大事なワナです。「直しました」で止めると、実際には直っていないのに完了扱いになります。コードを変えたら、必ずテスト(や型チェック)を走らせて結果を見る——ここを省略しないこと。システムプロンプトに書くだけでなく、AIに頼むときのチェックリストでも毎回確認しましょう。

  • 無限にやり直そうとする(リトライの上限) 「失敗したら直してやり直す」は強力ですが、直しても直しても通らないとき、エージェントは延々と試し続けてしまいます。これを止めるのが、第4章で入れたループ上限for (let step = 0; step < 10; step++))です。上限に達したら止まる作りになっているので、この上限は絶対に外さないこと。もし上限で力尽きたら、「10回試したが通らなかった。最後のエラーはこれ」と人間に引き継ぐのが健全です。

  • 計画は“強制しないと無視される” 「できれば計画を立てて」くらいの弱い書き方だと、LLM はいきなり手を動かしがちです。システムプロンプトでは「まず計画を示してから実行する」と手順を言い切ること(「できれば」ではなく「まず必ず」)。それでも省略されるなら、方法A(「『計画:』から始めよ」)のように出力の形を指定すると効きます。

  • 大きすぎるタスクは分割する 「アプリ全体を作り直して」のような巨大な依頼を一気に渡すと、計画も実行もぼやけて、ループ上限に当たって中途半端に終わりがちです。大きなタスクは、人間の側で小さく割って渡すのがコツ(「まずモデルだけ」「次にテストだけ」)。エージェントはひと口サイズのタスクを、計画 → 実行 → 確認で確実にこなすのが得意です。

  • 確認コマンドが“いつも成功扱い”になっていないか テストを走らせても、失敗が stdout ではなく stderr に出て見落とすことがあります(第7章のハマりどころ参照)。本書の run_commandstdout + stderr の両方を返しているので拾えますが、自作する場合は「失敗の出力もちゃんと頭脳に渡っているか」を確認しましょう。失敗が見えなければ、エージェントは自己修正できません。


🤖 AIに頼むなら(Vibe codingのコツ)

複数ステップのタスクを頼むときは、「計画して」「必ず確認して」「ダメならやり直して」の3点を、依頼文にもはっきり入れると、エージェントが最後までやり切ってくれます。エージェント自身を改良したいとき(システムプロンプトを書き換えたいとき)も同じです。

🗣 プロンプト例(自分のエージェントを“段取り上手”にしたい): 「TypeScript(Node.js, ESM)のAIエージェントの SYSTEM_PROMPT を書き換えて、難しいタスクを『計画 → 実行 → 確認 → 直す』で進めるようにしたい。具体的には、①まずやることを箇条書きで示してから始める ②コードを直したら必ず run_command でテストや型チェックを走らせて確認する ③失敗したらエラーを読んで直し、もう一度確認する ④通ったら短くまとめて終了、という順番を守らせて。『直した』だけで終わらせず『テストが通って初めて完了』だと言い切る一文も入れて。ループの上限(暴走防止)は今のまま残すこと。」

🗣 プロンプト例(タスクを頼むとき): 「hello.test.ts が落ちている。まず原因を調べる手順を箇条書きで出してから、直して。直したら必ず npm test を走らせて、通るまで直して。10回試しても通らなければ、最後のエラーを見せて止めて。」

出てきた結果(やシステムプロンプト)を見るときの確認ポイント:

  • 最初に計画(やることリスト)を立ててから動いているか?(いきなり書き換えていないか)
  • コードを直したあと、必ずテスト/型チェックを走らせて確認しているか?(“できたつもり”になっていないか)
  • 確認が失敗したら、原因を直してやり直しているか?(自己修正のループが回っているか)
  • やり直しに上限があるか?(無限リトライにならないか=第4章のループ上限を残しているか)
  • 「直した」ではなく、「テストが通った」をもって完了としているか?
  • タスクが大きすぎないか?(必要なら小さく分けて渡せているか)

📝 ことばメモ

  • 計画 → 実行 → 確認(けいかく・じっこう・かくにん):難しいタスクの進め方。まずやることを箇条書きにし(計画)、順にこなし(実行)、本当にできたかを確かめる(確認=テストや型チェックを走らせる)。この章で足した“段取り”
  • 確認(検証/verify〔べりふぁい〕):直した結果が本当に正しいか、実際に動かして確かめること。テストを走らせる・型チェックする、など。これを抜くと“できたつもり”になる
  • 自己修正(じこしゅうせい):確認が失敗したとき、エージェントが自分でエラーを読み、原因を直し、また確認することをくり返すこと。信頼性のかなめ。第4章のループの上で起きる
  • “できたつもり”(偽の完了):実際には直っていないのに、「直しました」と完了扱いにしてしまう失敗。確認ステップを省くと起きる
  • リトライ上限(じょうげん):やり直しの回数の上限。直らないタスクで無限に試し続けるのを防ぐ(第4章の step < 10)。力尽きたら人間に引き継ぐ
  • adaptive thinking(あだぷてぃぶ・しんきんぐ):頭脳に考える量を自分で加減させるオプション(thinking: { type: "adaptive" })。複雑なタスク向けの追加機能。本書では基本は使わない

➡️ 次の章へ

これで、難しいタスクを計画 → 実行 → 確認 → 直すで進められるようになりました。新しい道具は足さず、システムプロンプトで段取りを促し、確認と自己修正を入れただけで、エージェントは“最後までやり切る”信頼性を手に入れます。

ところで、ここまでのエージェントは、長い作業のあいだずっと黙っていて、最後にまとめて答えるだけでした。これだと「いま何をしているの? 止まってる?」と不安になります。次の第13章では、Claude Code のように頭脳が考えている様子・道具を呼んでいる様子を、その場で1文字ずつ見せる——ストリーミングを作ります。エージェントの“中の動き”が見えるようになると、ぐっと本物らしくなりますよ。

次の第13章へ →

← 目次・はじめにへもどる