Claude Agent SDKを使って、ユーザの回答に応じて、質問を分岐させるデモをやってみました。
Claude Code CLIがインストールされていれば、APIキーなどの設定なしに動かすことができます。(loginができていればOK)
"""対話型デシジョンツリーのデモ
ClaudeSDKClient の複数ターン会話を使い、
ユーザーの回答に応じて質問を分岐させ、最適な結論へ導く例。
テーマ: プロジェクトに最適な技術スタックの推薦
"""
import asyncio
import functools
from typing import Any
from claude_agent_sdk import (
AssistantMessage,
ClaudeAgentOptions,
ClaudeSDKClient,
ResultMessage,
TextBlock,
)
SYSTEM_PROMPT = """\
あなたは技術スタック選定のエキスパートです。
ユーザーの回答に基づき、以下のデシジョンツリーに沿って質問を進め、
最適な技術スタックを推薦してください。
## ルール
- 1回のメッセージにつき、必ず1つだけ質問してください。
- 各質問には番号付きの選択肢(2〜4個)を提示してください。
- ユーザーが番号または選択肢名で回答したら、次の分岐に進んでください。
- 最終ノードに到達したら、推薦する技術スタックとその理由を詳しく説明してください。
- 回答が選択肢にない場合は、もう一度同じ質問を丁寧に繰り返してください。
## デシジョンツリー
Q1: プロジェクトの種類は?
├─ 1. Webアプリ → Q2へ
├─ 2. モバイルアプリ → Q5へ
├─ 3. データ分析/ML → Q7へ
└─ 4. CLI/自動化ツール → Q9へ
Q2: チームの規模は?(Web)
├─ 1. 個人〜少人数(1-3人) → Q3へ
└─ 2. 中〜大規模チーム(4人以上) → Q4へ
Q3: 重視するポイントは?(Web/少人数)
├─ 1. 開発スピード → 【結論A】Next.js + Supabase
├─ 2. パフォーマンス → 【結論B】SvelteKit + SQLite (Turso)
└─ 3. フルスタック一体型 → 【結論C】Rails / Laravel
Q4: フロントエンドの複雑さは?(Web/大規模)
├─ 1. シンプル(管理画面等) → 【結論D】React + Express + PostgreSQL
├─ 2. リッチUI(SaaS等) → 【結論E】Next.js + tRPC + Prisma + PostgreSQL
└─ 3. リアルタイム重視 → 【結論F】React + Elixir/Phoenix + PostgreSQL
Q5: 対象プラットフォームは?(モバイル)
├─ 1. iOS のみ → 【結論G】Swift + SwiftUI
├─ 2. Android のみ → 【結論H】Kotlin + Jetpack Compose
└─ 3. 両方 → Q6へ
Q6: ネイティブ性能は必要?(クロスプラットフォーム)
├─ 1. 高性能が必要 → 【結論I】React Native / Flutter
└─ 2. Webベースで十分 → 【結論J】Capacitor + Next.js (PWA)
Q7: 主な用途は?(データ/ML)
├─ 1. データ分析・可視化 → 【結論K】Python + Pandas + Streamlit
├─ 2. 機械学習モデル開発 → Q8へ
└─ 3. データパイプライン → 【結論L】Python + dbt + Airflow
Q8: モデルの規模は?(ML)
├─ 1. 小〜中規模 → 【結論M】Python + scikit-learn + MLflow
└─ 2. 大規模 / ディープラーニング → 【結論N】Python + PyTorch + Weights & Biases
Q9: 言語の好みは?(CLI/自動化)
├─ 1. Python → 【結論O】Python + Typer + Rich
├─ 2. 高速さ重視 → 【結論P】Rust + clap
└─ 3. 汎用性重視 → 【結論Q】Go + Cobra
## 結論テンプレート
最終ノードに到達したら、以下の形式で推薦してください:
---
🎯 推薦スタック: [スタック名]
選定理由:
- [理由1]
- [理由2]
- [理由3]
始め方:
1. [最初のステップ]
2. [次のステップ]
3. [その次のステップ]
---
それでは、Q1 から始めてください。
"""
async def read_input(prompt: str) -> str:
"""イベントループをブロックせずにユーザー入力を読み取る"""
loop = asyncio.get_running_loop()
return await loop.run_in_executor(
None, functools.partial(input, prompt),
)
async def main() -> None:
options = ClaudeAgentOptions(
system_prompt=SYSTEM_PROMPT,
max_turns=1,
)
print("=== Claude Agent SDK: デシジョンツリー デモ ===\n")
print("質問に番号で回答すると、次の質問へ分岐します。")
print("最終的にプロジェクトに最適な技術スタックを推薦します。")
print("'exit' で終了します。\n")
async with ClaudeSDKClient(options=options) as client:
# 最初の質問を Claude に出させる
await client.query("始めてください。")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"\n{block.text}")
# 対話ループ
while True:
user_input = await read_input("\n回答> ")
user_input = user_input.strip()
if not user_input:
continue
if user_input.lower() == "exit":
print("終了します。")
break
await client.query(user_input)
is_final = False
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"\n{block.text}")
if "推薦スタック" in block.text:
is_final = True
if is_final:
print("\n--- 推薦完了 ---")
another = await read_input("\nもう一度選定しますか? [y/n] > ")
if another.strip().lower() == "y":
await client.query("最初からやり直してください。Q1 から始めてください。")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"\n{block.text}")
else:
print("終了します。")
break
if __name__ == "__main__":
asyncio.run(main())

制御をシステムプロンプトに頼ってしまう点が、プログラマ的に抵抗があるのですが、これが今のスタイルなのでしょう。Webアプリ版もつくりましたが、Claudeのインスタンスをつくる機能がこのSDKにはあるようです。
