Planning パターンの本質は「先に考え、後で行動」。計画と実行を分離すると、推論と実行それぞれの独立した最適化、再計画、進行状況の追跡が可能になる。ただし万能薬じゃない。シンプルなタスクに過剰な計画は逆に遅くなる。


5 分で掴む核心

  1. Planning の本質は「先に考え、後で行動」。計画と実行を分離すると柔軟性が生まれる
  2. Planning には二つのモードがある。UPFRONT (事前計画) と STEP-BY-STEP (逐次計画)
  3. UPFRONT は構造が明確なタスク向け。STEP-BY-STEP は不確実性が高いタスク向け
  4. 再計画 (Replanning) が大事。失敗したら計画全体を作り直すのではなく、残りを微調整する
  5. 過剰計画は禁物。シンプルなタスクに複雑な計画は、計画なしより遅くなる

10 分コース:10.1-10.3 → 10.5 → Shannon Lab


10.1 なぜ先に計画して後で行動する必要があるのか?

LLM が直接タスクを実行するとき、何も考えずにツールを呼び出し始めることがある。検索して、結果を見て、次に何をするか考えて、また検索して…

これは問題になりうる:

  1. 一貫性がない:毎回走るたびに結果が違う
  2. 局所最適に陥る:目の前のステップだけ見て全体像を無視する
  3. 進捗が追跡できない:今どこまで進んだのか、あとどれくらいかかるのか分からない
  4. 無駄なループ:似たようなことを何度も繰り返す

計画と実行を分離すると、多くの利点が得られる

利点説明
推論の独立最適化計画段階ではより強力なモデルを使い、実行段階ではコスト効率の高いモデルを使える
進捗追跡「全部で 5 ステップ、今 3 番目」のように伝えられる
障害からの回復3 番目のステップで失敗したら、2 番目から再開できる。最初からやり直す必要がない
再計画対応実行中に状況が変わったら計画を動的に調整できる
デバッグ容易性計画を見れば Agent が何をしようとしているか分かる

10.2 二つの Planning モード

Shannon では二つの Planning 戦略を提供する:

二つの Planning モード

UPFRONT モード:全部計画してから実行

すべてのステップを一度に計画し、順番に実行する。計画中は実行しない。

向いているシナリオ

  • タスクの構造が明確で、すべてのステップが予測可能
  • 各ステップが比較的独立している
  • 前のステップの結果が後のステップに大きく影響しない

タスク:ある会社の基本情報を調べる

計画:
1. 公式サイトで会社概要を取得
2. 創業者の経歴を検索
3. 最近のニュースを検索
4. 競合他社を特定
5. 結果を報告書にまとめる

実行:ステップ 1、2、3、4、5 を順番に実行

STEP_BY_STEP モード:考えながら進む

一度に一ステップだけ計画し、実行後に次を計画する。各ステップは前のステップの結果に基づいて調整できる。

向いているシナリオ

  • 前のステップの結果を見ないと次に何をすべきか分からない
  • 不確実性が高く、臨機応変な対応が必要
  • 方向性の探索が必要

タスク:なぜこのサービスがこんなに遅いのか調査する

ステップ 1:CPU 使用率を確認  正常(30%
ステップ 2:(CPU は正常なので)メモリ使用状況を確認  95%、メモリ不足!
ステップ 3:(メモリ不足なので)どのプロセスがメモリを食っているか確認  Java プロセスが 8GB
ステップ 4:Java アプリのヒープ設定を確認  max heap が大きすぎる

いつどちらを使うか?

タスクの特徴推奨モード理由
ステップが明確で変化しないUPFRONT一度に計画して効率を上げる
情報収集が主UPFRONT各検索は比較的独立している
デバッグ/トラブルシューティングSTEP_BY_STEP前の発見に基づいて方向を調整する必要がある
探索的リサーチSTEP_BY_STEP何が見つかるか分からない
ユーザーとのマルチターン対話STEP_BY_STEPユーザーのフィードバックが次のステップに影響する

10.3 UPFRONT モードの実装

Shannon での完全な実装フローを見てみよう:

ステップ 1:タスク分解

まず LLM にタスクを構造化された計画に分解させる:

// タスク分解の入力
type TaskDecompositionInput struct {
    Query         string   // ユーザーのクエリ
    Context       string   // 追加のコンテキスト
    AvailableTools []Tool  // 利用可能なツール
    MaxSteps      int      // 最大ステップ数、デフォルト 10
}

// 分解結果
type TaskDecompositionResult struct {
    Steps []PlanStep // 計画されたステップ
    Goal  string     // 最終目標
}

type PlanStep struct {
    Index       int      // ステップ番号
    Description string   // このステップで何をするか
    Tool        string   // 使うツール(あれば)
    Inputs      string   // ツールの入力パラメータ
    DependsOn   []int    // 依存するステップ
}

ステップ 2:プロンプト構築

タスク分解のプロンプトは非常に重要。Shannon の設計:

func buildDecompositionPrompt(input TaskDecompositionInput) string {
    prompt := `You are a planning agent. Break down the user's request into clear, executable steps.

## Rules:
1. Each step should be atomic and achievable
2. Use available tools when appropriate
3. Steps should be in logical order
4. Keep total steps under %d
5. Each step should have clear inputs and expected outputs

## Available Tools:
%s

## User Request:
%s

## Output Format (JSON):
{
    "goal": "What we're trying to achieve",
    "steps": [
        {
            "index": 1,
            "description": "What this step does",
            "tool": "tool_name or null",
            "inputs": "tool inputs if applicable",
            "depends_on": []
        }
    ]
}
`
    return fmt.Sprintf(prompt, input.MaxSteps, formatTools(input.AvailableTools), input.Query)
}

ステップ 3:計画の実行

計画ができたら順番に実行する:

func executePlan(ctx context.Context, plan TaskDecompositionResult, agent *Agent) (*ExecutionResult, error) {
    results := make(map[int]StepResult)

    for _, step := range plan.Steps {
        // 依存関係の確認
        for _, depIdx := range step.DependsOn {
            if results[depIdx].Status != "success" {
                return nil, fmt.Errorf("dependency step %d failed", depIdx)
            }
        }

        // ステップの実行
        stepResult, err := executeStep(ctx, step, results, agent)
        if err != nil {
            // 再計画を試みることもできる
            return nil, err
        }

        results[step.Index] = stepResult
    }

    return &ExecutionResult{
        StepResults: results,
        Success:     true,
    }, nil
}

実装参考 (Shannon): go/orchestrator/internal/workflows/patterns/plan_execute.go


10.4 STEP_BY_STEP モードの実装

STEP_BY_STEP モードのコアは「ループ」:観察 → 計画 → 実行 → 観察...

func stepByStepPlanning(ctx context.Context, query string, agent *Agent, maxSteps int) (*Result, error) {
    var history []StepRecord

    for i := 0; i < maxSteps; i++ {
        // 1. 現在の状態に基づいて次のステップを計画
        nextStep, done, err := planNextStep(ctx, query, history, agent)
        if err != nil {
            return nil, err
        }

        // 2. タスク完了?
        if done {
            return synthesizeResult(history), nil
        }

        // 3. このステップを実行
        result, err := executeStep(ctx, nextStep, agent)

        // 4. 履歴を更新
        history = append(history, StepRecord{
            Step:   nextStep,
            Result: result,
            Error:  err,
        })

        // 5. 失敗しても続行可能(次の計画で調整)
    }

    return synthesizeResult(history), nil
}

func planNextStep(ctx context.Context, query string, history []StepRecord, agent *Agent) (*PlanStep, bool, error) {
    prompt := buildNextStepPrompt(query, history)

    response, err := agent.LLM.Generate(ctx, prompt)
    if err != nil {
        return nil, false, err
    }

    // LLM がタスク完了と判断した場合
    if response.Done {
        return nil, true, nil
    }

    return response.NextStep, false, nil
}

履歴管理

履歴管理が重要——前のステップで何をしたか、何が得られたかを LLM に伝える必要がある:

func buildNextStepPrompt(query string, history []StepRecord) string {
    prompt := `## Original Task:
%s

## What has been done:
%s

## What to do next:
Based on the above, determine the next step. If the task is complete, respond with {"done": true, "summary": "..."}.
Otherwise, provide the next step: {"done": false, "next_step": {...}}.
`
    historyStr := formatHistory(history)
    return fmt.Sprintf(prompt, query, historyStr)
}

func formatHistory(history []StepRecord) string {
    var sb strings.Builder
    for i, record := range history {
        sb.WriteString(fmt.Sprintf("Step %d: %s\n", i+1, record.Step.Description))
        if record.Error != nil {
            sb.WriteString(fmt.Sprintf("  Result: Failed - %s\n", record.Error))
        } else {
            sb.WriteString(fmt.Sprintf("  Result: %s\n", summarize(record.Result)))
        }
    }
    return sb.String()
}

10.5 再計画 (Replanning)

計画通りに進まないこともある。再計画のトリガー条件:

  1. ステップ実行失敗:ツール呼び出しエラー、タイムアウト
  2. 予期せぬ結果:期待したデータが得られなかった
  3. 新しい情報:計画時に知らなかったことが分かった
  4. ユーザー割り込み:ユーザーが目標や制約を変更した

再計画の戦略

type ReplanStrategy int

const (
    ReplanFromCurrent  ReplanStrategy = iota // 現在のステップから再計画
    ReplanFromScratch                        // 最初から再計画
    SkipAndContinue                          // このステップをスキップして続行
    AbortWithPartial                         // 中止して部分的な結果を返す
)

func handleStepFailure(ctx context.Context, failedStep PlanStep, history []StepRecord, originalPlan *Plan) (ReplanStrategy, *Plan, error) {
    // 失敗原因を分析
    failureAnalysis := analyzeFailure(failedStep, history)

    switch failureAnalysis.Type {
    case "tool_unavailable":
        // 代替ツールがあれば再計画
        return ReplanFromCurrent, replanWithAlternativeTool(originalPlan, failedStep), nil

    case "missing_info":
        // 情報が不足している場合、情報収集ステップを追加
        return ReplanFromCurrent, insertInfoGatheringStep(originalPlan, failedStep), nil

    case "invalid_approach":
        // アプローチ自体が間違っている場合、最初から再計画
        return ReplanFromScratch, nil, nil

    case "non_critical":
        // 重要でないステップならスキップ
        return SkipAndContinue, nil, nil

    default:
        // 判断できない場合は中止
        return AbortWithPartial, nil, nil
    }
}

部分的な再計画

全部作り直すのではなく、残りの部分だけを再計画する:

func replanRemainingSteps(ctx context.Context, completedSteps []StepRecord, failedStep PlanStep, originalGoal string) ([]PlanStep, error) {
    prompt := `## Original Goal:
%s

## Completed Steps:
%s

## Failed Step:
%s

## Task:
Create a new plan for the remaining work. Build upon what's already done.
Don't repeat completed steps.
`
    // ...
}

10.6 高度なトピック:適応的計画

計画の粒度をタスクの複雑さに応じて動的に調整する:

func adaptivePlanning(ctx context.Context, query string, agent *Agent) (*Plan, error) {
    // 1. タスクの複雑さを評価
    complexity := assessComplexity(query)

    // 2. 複雑さに応じて計画戦略を選択
    switch {
    case complexity.Score < 0.3:
        // シンプルなタスク:計画なしで直接実行
        return nil, nil  // 直接実行を示す

    case complexity.Score < 0.6:
        // 中程度の複雑さ:軽量な計画
        return lightweightPlan(ctx, query, agent)

    case complexity.Score < 0.8:
        // 複雑:完全な UPFRONT 計画
        return fullUpfrontPlan(ctx, query, agent)

    default:
        // 非常に複雑:STEP_BY_STEP + 階層的計画
        return hierarchicalPlan(ctx, query, agent)
    }
}

func assessComplexity(query string) ComplexityAssessment {
    // ヒューリスティックまたは LLM ベースの評価
    // 考慮要素:
    // - 暗黙のステップ数
    // - 必要なツールの種類
    // - 情報の依存関係
    // - ドメインの専門性
}

階層的計画

非常に複雑なタスクは、高レベル計画とサブ計画に分解できる:

type HierarchicalPlan struct {
    HighLevelPlan []Phase      // 高レベルのフェーズ
    SubPlans      map[int]Plan // 各フェーズの詳細計画
}

type Phase struct {
    Index       int
    Name        string
    Description string
    SubTaskQuery string // このフェーズ用の計画クエリ
}

// 例:
// Phase 1: 情報収集
//   - Step 1.1: 公式サイト検索
//   - Step 1.2: ニュース検索
// Phase 2: 分析
//   - Step 2.1: データ整理
//   - Step 2.2: トレンド分析
// Phase 3: レポート作成
//   - Step 3.1: 概要執筆
//   - Step 3.2: 詳細執筆

10.7 よくある落とし穴

落とし穴 1:過剰計画

// 悪い例:シンプルなタスクに複雑な計画
// ユーザー:「今日の天気は?」
// Agent:計画を作成中...5 ステップの計画を生成...
//        ステップ 1: 位置特定...

// 良い例:シンプルなタスクは直接実行
if isSimpleQuery(query) {
    return directExecution(query)
}

落とし穴 2:計画の粒度が不適切

// 悪い例:粒度が粗すぎる
// ステップ 1: 会社を調査する

// 悪い例:粒度が細かすぎる
// ステップ 1: ブラウザを開く
// ステップ 2: Google にアクセス
// ステップ 3: 検索ボックスをクリック

// 良い例:適切な粒度
// ステップ 1: 会社の公式サイトで基本情報を取得
// ステップ 2: TechCrunch で最近のニュースを検索

落とし穴 3:再計画のし過ぎ

// 悪い例:小さな問題でも完全に再計画
if stepFailed {
    return replanFromScratch()  // コストが高い!
}

// 良い例:失敗の種類に応じて対応
if stepFailed {
    strategy := analyzeAndChooseStrategy(failure)
    switch strategy {
    case Minor:
        return retry(step)
    case Recoverable:
        return replanRemaining()
    case Critical:
        return replanFromScratch()
    }
}

10.8 他のフレームワークとの比較

フレームワークPlanning アプローチ特徴
LangGraphState + Edge でフローを定義柔軟だが定義が必要
AutoGPT暗黙の計画(Agent ループ内)シンプルだが制御しにくい
BabyAGIタスクキュー + 優先度タスク管理に特化
Shannon明示的な Plan-Execute透明性が高く、デバッグしやすい

この章のまとめ

核心は一言で:Planning パターンは「先に考え、後で行動」。計画と実行を分離することで、より制御可能で透明性の高い Agent が得られる。

要点

  1. 二つのモード:UPFRONT は構造が明確なタスク向け、STEP_BY_STEP は不確実性が高いタスク向け
  2. 再計画が重要:計画は固定ではなく、状況に応じて調整する
  3. 適切な粒度:粗すぎても細かすぎてもダメ
  4. 過剰計画を避ける:シンプルなタスクに複雑な計画は逆効果
  5. 進捗追跡:計画があればユーザーに進捗を伝えられる

Shannon Lab(10 分で始める)

このセクションで、本章の概念を Shannon のソースコードにマッピングする。

必読(1 ファイル)

  • patterns/plan_execute.goPlanAndExecute 関数を見て、計画と実行がどう分離されているか理解する。executePlanStep を見てステップの実行フローを理解する

選読で深掘り(興味に応じて 2 つ)


演習

演習 1:モード選択

以下のシナリオで UPFRONT と STEP_BY_STEP のどちらを使うか判断し、理由を説明せよ:

  1. 「5 社の競合他社の基本情報を集める」
  2. 「このシステムのパフォーマンス問題を調査する」
  3. 「今月の売上レポートを作成する」
  4. 「ユーザーの好みに基づいて旅行プランを提案する」

演習 2:計画の粒度

「テスラの 2024 年の業績を分析するレポートを作成する」というタスクの計画を 5-7 ステップで書け。各ステップには:

  • 説明
  • 使用ツール(あれば)
  • 依存関係

演習 3(上級):再計画シナリオ

以下のシナリオでどう再計画するか説明せよ:

シナリオ:「Apple の最新決算を分析する」というタスクで:

  • ステップ 1:Apple IR サイトで決算資料を取得 → 成功
  • ステップ 2:売上の前年比較を計算 → 成功
  • ステップ 3:業界データベースで競合比較 → 失敗(データベースアクセス不可)

再計画時に考慮すべき点は?どんな代替案がある?


もっと深く学びたい?


次章の予告

Planning は「事前に考える」パターンだった。でも時には「事後に振り返る」ことも重要だ。

Agent が答えを出した後:

  • 本当に正しいか?
  • 何か見落としていないか?
  • もっと良い答えはないか?

次章では Reflection パターン を説明する。自己評価と反復改善で、より質の高い出力を目指す。

準備はいい?次へ進もう。

この記事を引用する / Cite
Zhang, W. (2026). 第 10 章:Planning パターン. In AI Agent アーキテクチャ:単体からエンタープライズ級マルチエージェントへ. https://waylandz.com/ai-agent-book-ja/第10章-Planningパターン
@incollection{zhang2026aiagent_ja_第10章_Planningパターン,
  author = {Zhang, Wayland},
  title = {第 10 章:Planning パターン},
  booktitle = {AI Agent アーキテクチャ:単体からエンタープライズ級マルチエージェントへ},
  year = {2026},
  url = {https://waylandz.com/ai-agent-book-ja/第10章-Planningパターン}
}