Chain-of-Thought (CoT) は「一歩一歩考える」パターン。複雑な推論を中間ステップに分解することで、最終的な答えの精度を上げる。シンプルだが強力。ただし万能じゃない。推論不要の問題には無駄。
5 分で掴む核心
- CoT = 推論を中間ステップに分解。「Think step by step」で発動
- なぜ効くか:各ステップでエラーを検出しやすい、複雑さを分割
- Zero-shot CoT: プロンプトに「Let's think step by step」と追加するだけ
- Few-shot CoT: 例を示して推論の「型」を教える
- いつ使うか:多段階の推論が必要なとき。単純な事実検索には不要
10 分コース:12.1-12.3 → 12.5 → Shannon Lab
12.1 なぜ「一歩一歩」が効くのか?
質問:「12 個のリンゴを 3 人で分けたら、一人何個?」
LLM への入力:
Q: 12 個のリンゴを 3 人で分けたら、一人何個?
A:
直接回答:4
CoT 回答:
12 個のリンゴを 3 人で分けます。
12 ÷ 3 = 4
一人あたり 4 個です。
この簡単な例では差がないように見える。でも問題が複雑になると、差が大きくなる。
複雑な例
質問:
太郎は最初 100 万円持っていた。
株で 20% 利益を得た。
その後、その利益の半分を寄付した。
税金で元本の 10% を払った。
最終的にいくら残った?
直接回答(よく間違える):
108 万円
CoT 回答:
ステップ 1: 初期資産 = 100 万円
ステップ 2: 株の利益 = 100 万 × 20% = 20 万円
ステップ 3: 利益後の資産 = 100 万 + 20 万 = 120 万円
ステップ 4: 寄付額 = 利益の半分 = 20 万 ÷ 2 = 10 万円
ステップ 5: 寄付後の資産 = 120 万 - 10 万 = 110 万円
ステップ 6: 税金 = 元本の 10% = 100 万 × 10% = 10 万円
ステップ 7: 最終資産 = 110 万 - 10 万 = 100 万円
最終的に 100 万円残った。
なぜ CoT が効くのか?
- エラー検出:各ステップが見えるので、どこで間違えたか分かる
- 複雑さの分割:一度に考えることを減らす
- ワーキングメモリ:中間結果を「書き留める」ことで追跡しやすい
- 暗黙の知識の引き出し:ステップを踏むことで関連知識が活性化される
12.2 Zero-shot CoT vs Few-shot CoT
Zero-shot CoT
例を示さず、プロンプトで「考えさせる」だけ:
func zeroShotCoT(query string) string {
prompt := fmt.Sprintf(`%s
Let's think step by step.`, query)
return callLLM(prompt)
}
日本語では「一歩一歩考えてみましょう」「順を追って考えてみましょう」も効く。
メリット:シンプル、プロンプト作成が楽 デメリット:推論の「型」を制御しにくい
Few-shot CoT
例を示して、推論の「型」を教える:
func fewShotCoT(query string) string {
examples := `## Example 1
Q: 8 人の子供が 24 個のキャンディを分けます。一人何個?
A: 全部で 24 個のキャンディがあります。
8 人で分けるので、24 ÷ 8 = 3。
一人あたり 3 個です。
## Example 2
Q: 店で 300 円のノートを買い、500 円を出しました。おつりは?
A: ノートは 300 円です。
500 円を出しました。
おつり = 500 - 300 = 200 円です。
`
prompt := fmt.Sprintf(`%s
## Your Turn
Q: %s
A:`, examples, query)
return callLLM(prompt)
}
メリット:推論の「型」を制御できる、ドメイン固有の推論パターンを教えられる デメリット:良い例を作るのに労力がかかる
比較
| 方式 | プロンプト作成 | 推論の制御 | 適用範囲 |
|---|---|---|---|
| Zero-shot | 簡単 | 低い | 汎用的な推論 |
| Few-shot | 例の準備が必要 | 高い | ドメイン固有の推論 |
12.3 Shannon での CoT 実装
Shannon では、CoT を推論パターンの一つとして実装している:
type ChainOfThoughtConfig struct {
Mode string // "zero-shot" or "few-shot"
Examples []Example // few-shot モード用
MaxSteps int // 最大推論ステップ数
ExtractAnswer bool // 最終答えを抽出するか
}
func ChainOfThought(ctx workflow.Context, query string, config ChainOfThoughtConfig) (*CoTResult, error) {
var prompt string
if config.Mode == "few-shot" && len(config.Examples) > 0 {
prompt = buildFewShotPrompt(query, config.Examples)
} else {
prompt = buildZeroShotPrompt(query)
}
// LLM 呼び出し
response, err := callLLM(ctx, prompt)
if err != nil {
return nil, err
}
// 推論ステップを抽出
steps := extractSteps(response)
// 最終答えを抽出(オプション)
var finalAnswer string
if config.ExtractAnswer {
finalAnswer = extractFinalAnswer(response)
}
return &CoTResult{
FullResponse: response,
Steps: steps,
FinalAnswer: finalAnswer,
StepCount: len(steps),
}, nil
}
推論ステップの抽出
func extractSteps(response string) []string {
// パターン 1: "Step 1:", "Step 2:", ...
stepPattern := regexp.MustCompile(`Step\s*\d+[:.]\s*(.+?)(?=Step\s*\d+|$)`)
// パターン 2: 番号付きリスト "1.", "2.", ...
numberedPattern := regexp.MustCompile(`\d+\.\s*(.+?)(?=\d+\.|$)`)
// パターン 3: 箇条書き "- ", "* "
bulletPattern := regexp.MustCompile(`[-*]\s*(.+?)(?=[-*]|$)`)
// 優先順位で試す
if matches := stepPattern.FindAllStringSubmatch(response, -1); len(matches) > 0 {
return extractMatches(matches)
}
// ... 他のパターン
}
12.4 CoT の改良版
Self-Consistency CoT
同じ質問に複数回 CoT を実行し、多数決で答えを決める:
func selfConsistencyCoT(ctx context.Context, query string, n int) (string, error) {
answers := make(map[string]int)
for i := 0; i < n; i++ {
result, err := chainOfThought(ctx, query)
if err != nil {
continue
}
answers[result.FinalAnswer]++
}
// 最も多い答えを選ぶ
var bestAnswer string
var maxCount int
for answer, count := range answers {
if count > maxCount {
maxCount = count
bestAnswer = answer
}
}
return bestAnswer, nil
}
メリット:推論のばらつきを減らす、信頼性が上がる デメリット:コストが n 倍
CoT + Verification
推論の各ステップを検証する:
func verifiedCoT(ctx context.Context, query string) (*Result, error) {
result, _ := chainOfThought(ctx, query)
// 各ステップを検証
for i, step := range result.Steps {
isValid := verifyStep(ctx, step, result.Steps[:i])
if !isValid {
// 問題のあるステップから再推論
return rerunFrom(ctx, query, result.Steps[:i])
}
}
return result, nil
}
12.5 いつ CoT を使うべきか?
| タスクタイプ | CoT が効くか | 理由 |
|---|---|---|
| 算数の文章題 | 効く | 多段階の計算が必要 |
| 論理パズル | 効く | 推論のチェーンが必要 |
| コード生成(複雑) | 効く | ロジックを分解できる |
| 因果推論 | 効く | 因果の連鎖を追跡 |
| 事実の検索 | 効かない | 推論不要 |
| 単純な変換 | 効かない | ワンステップで完了 |
| 創作(詩、物語) | 効きにくい | 創造性に推論は不要 |
判断基準
この問題を解くのに、
複数のステップを頭の中で踏む必要があるか?
YES → CoT を試す価値あり
NO → 直接回答で十分
12.6 よくある落とし穴
落とし穴 1:不要なときに使う
// 悪い例:単純な質問に CoT
// Q: 日本の首都は?
// A: 日本という国があります。首都とは...(長い推論)...東京です。
// 良い例:直接回答
// A: 東京
func shouldUseCoT(query string) bool {
// 複雑度を推定
complexity := estimateComplexity(query)
return complexity > threshold
}
落とし穴 2:推論が正しいと仮定する
// 悪い例:推論を検証しない
steps := extractSteps(response)
return steps[len(steps)-1] // 最後のステップ = 答え、と仮定
// 良い例:答えを明示的に抽出、または検証
finalAnswer := extractFinalAnswer(response)
if config.VerifyAnswer {
isValid := verifyAnswer(query, finalAnswer)
if !isValid {
return retry(query)
}
}
落とし穴 3:ステップ数が多すぎる
// 悪い例:無限に推論し続ける
prompt := "Think through this step by step, don't stop until you're sure."
// 結果:20 ステップの推論でトークンを使い果たす
// 良い例:ステップ数を制限
prompt := "Think through this in 3-5 steps, then give your final answer."
12.7 CoT と Planning の違い
一見似ているが、目的が違う:
| 特徴 | CoT | Planning |
|---|---|---|
| 目的 | 推論の精度を上げる | 実行を制御する |
| 出力 | 推論ステップ + 答え | 行動計画 |
| 実行 | 推論のみ(ツール呼び出しなし) | ツール呼び出しあり |
| 再実行 | なし(一度で完結) | ステップごとに実行 |
| 典型的なタスク | 数学、論理パズル | リサーチ、自動化 |
組み合わせ
Planning の計画段階で CoT を使う:
func planWithCoT(ctx context.Context, query string) (*Plan, error) {
// CoT で計画を立てる
prompt := fmt.Sprintf(`Task: %s
Think step by step about how to accomplish this task:
1. What information do we need?
2. What tools should we use?
3. In what order should we do things?
Then provide a structured plan.`, query)
reasoning := callLLM(prompt)
plan := extractPlan(reasoning)
return plan, nil
}
この章のまとめ
核心は一言で:Chain-of-Thought は「一歩一歩考える」ことで、複雑な推論の精度を上げる。シンプルだが強力。
要点
- 基本原理:推論を中間ステップに分解
- Zero-shot:「Let's think step by step」で発動
- Few-shot:例を示して推論の「型」を教える
- 使いどころ:多段階の推論が必要なとき
- 注意:不要なときに使うと逆効果
Shannon Lab(10 分で始める)
このセクションで、本章の概念を Shannon のソースコードにマッピングする。
必読(1 ファイル)
patterns/chain_of_thought.go:ChainOfThought関数を見て、Zero-shot と Few-shot の実装を理解する。extractStepsでステップ抽出のロジックを確認する
選読で深掘り(興味に応じて 2 つ)
activities/llm_call.go:CoT プロンプトがどう LLM に渡されるか理解するpatterns/plan_execute.go:Planning と CoT がどう違うか比較する
演習
演習 1:CoT プロンプト設計
以下の問題に対する Few-shot CoT プロンプトを設計せよ。例を 2 つ含めること。
問題:ある会社の従業員は昨年 100 人だった。今年 20% 増えて、
来年さらに 10% 増える予定。来年の従業員数は?
演習 2:CoT vs 直接回答
以下のタスクについて、CoT を使うべきか判断し、理由を説明せよ:
- 「Python で Hello World を出力するコードを書いて」
- 「3 人の子供が 15 個のクッキーを平等に分けた後、各子供が 2 個ずつ食べた。残りは全部で何個?」
- 「今日の天気を教えて」
- 「AさんがBさんより背が高く、BさんがCさんより背が高い。AさんとCさんではどちらが背が高い?」
演習 3(上級):Self-Consistency の実装
Self-Consistency CoT の擬似コードを書け。以下を考慮すること:
- 同じ質問に n 回 CoT を実行
- 答えを抽出して集計
- 多数決で最終答えを決定
- 信頼度(最も多い答えの割合)も返す
もっと深く学びたい?
- Chain-of-Thought Prompting Elicits Reasoning in Large Language Models - CoT の原論文
- Self-Consistency Improves Chain of Thought Reasoning - Self-Consistency CoT
- Large Language Models are Zero-Shot Reasoners - Zero-shot CoT
Part 4 のまとめ
これで Part 4「単一 Agent パターン」が完了した。3 つのパターンを学んだ:
| パターン | 核心 | いつ使うか |
|---|---|---|
| Planning | 先に考え、後で行動 | タスクが複雑で、実行計画が必要なとき |
| Reflection | 出力して、振り返り、改善 | 品質を反復的に高めたいとき |
| Chain-of-Thought | 一歩一歩考える | 多段階の推論が必要なとき |
これらは組み合わせて使える:
- Planning + Reflection: 計画を立て、各ステップの出力を改善
- Planning + CoT: 計画段階で推論を分解
- Reflection + CoT: 推論の各ステップを評価・改善
次章の予告
Part 4 では「単一 Agent」がいかに賢く動くかを学んだ。でも、一人じゃできないこともある。
Part 5 では マルチエージェント編成 に入る。複数の Agent がどう協力するか、誰がリーダーになるか、どうコミュニケーションするか。
第 13 章「編成の基礎」で、オーケストレーターの役割とルーティング判断を学ぶ。
準備はいい?次のパートへ進もう。