自律型データ分析
エージェントの全体像
Next.js 16 + FastAPI (GCP Cloud Run) + LangGraph + 永続 Jupyter Kernel + Claude API による 自律型データ分析エージェントのシステム構成・グラフルーティング・Kernel 永続化・選定理由・セキュリティ設計の詳細を、 エンジニアリング視点で解説します。
5 層フローで“設計通りに動く”を作る
ブラウザから Claude までを 5 層に分け、各層の役割と接続方式を明示。フロントは Vercel、API は GCP Cloud Run (東京)、SSE 配信は Cloudflare Worker 経由で run.app 直URL を隠蔽。
メイン 9 ノード profile → classify → [llm_only/quick_compute/needs_approval/python_explicit] → understand → plan → assess_plan → [HITL] → code_gen → execute → visualize → report。Refine 2 ノード(継続分析)・Summary 3 ノード(Thread 横断要約)の補助グラフを別途持つ。詳細は次セクション「Graph Routing」を参照。
ipykernel + jupyter_client で Thread ごとに独立した Python REPL を持ち、変数・DataFrame・学習済みモデルを次のクエリで参照可能。15 分アイドル shutdown + lazy 再起動。詳細は「Kernel 永続化」セクションへ。
run.app 直URL をクライアントに露出させない目的に加え、Cloud Run のカスタムドメイン無料 SSL がフラット 1 階層のみ対応のため、demo-analytics-api.beyonz.jp でリバースプロキシしている。SSE は Worker を中継しても切れない(Demo B で実証済み)。質問の重さに応じて自律ルーティング
classify ノードが質問を 5 値で判定し、即答・軽量集計・確認・本格分析・曖昧明確化のうち適切なルートを選ぶ。本格分析パスは assess_plan が計画リスクを評価して HITL(人の承認)を動的に on/off する。
classify の 5 値ルーティングADR-030 / ADR-035
llm_only→ answerプロファイルだけで答えられる即答系(「列は何?」「件数は?」など)。Python 実行なし
quick_compute→ mini_code_gen → execute → answer軽い集計(合計・平均・カウント)。最短ルートでコード生成→実行→回答
needs_approval→ answer(確認返答)意図が曖昧で本格分析の前に人へ確認したい質問。短い確認応答のみ
python_explicit→ understand → plan → ... → report本格的な統計分析・予測・可視化。フル経路を通る
曖昧明確化→ answer(質問返し)クエリ自体が曖昧。「どの観点で?」とエージェントから問い返す
旧設計(ADR-023 以前)は mode_router で 6 モード固定だったが、 「できないことを可視化してしまう」問題があったため撤去し、Claude が自由にコード生成する設計に転換。代わりに「質問の重さ」を classify が 5 値で振り分けることで、軽い質問に重い経路を走らせない仕組みを保っている。
assess_plan による動的 HITLADR-031
python_explicit 経路では plan ノードが立てた分析計画を assess_plan が評価し、リスクが高ければ human_gate で承認を要求、低ければスキップしてそのまま実行に進む。HITL を全質問で出すと体感が重く、全廃すると暴走を許す。「重い計画だけ止める」が落としどころ。
「売上の合計を出して」など、明確で副作用のない計画は確認なしで走る。商談での「軽快さ」を担保する側
「全顧客のクラスタリング + 特徴量重要度を回して〜」など計算量が重い or 解釈責任が大きい計画は人が承認するまで動かない
classify と assess_plan は別々の判断軸。前者は「答え方の重さ」、後者は「実行計画の重さ」を分けて評価することで、軽い質問は即答、重い計画は人が止められる構造になっている。プロンプトキャッシュ(ADR-038)でコストも抑制している。
Thread ごとに永続する Python セッション
ipykernel + jupyter_client で Thread ごとに独立した Python REPL を保持。前のコードで定義した変数・DataFrame・学習済みモデルを、次のクエリでそのまま参照できる。
永続 Kernel の効き目
ADR-024毎回 CSV を読み直し、前処理コードを再実行し、グラフを再生成。「さっきの分析の続きから」が言えず、汎用 AI チャットの劣化版に見えていた
Claude Code が開発者の常駐ツールであるように、AI Analytics Cockpit がアナリストの常駐ツールに。前回の DataFrame をそのまま使ってクラスタリングを追加、といった連続分析が可能
KERNEL SAFETY
「常駐型」を本番運用に乗せるための 4 つの安全策
15 分アイドルで shutdown
Thread が 15 分操作されないと Kernel プロセスを自動終了。Cloud Run のメモリを解放し、課金もコールド時間に押し込む
lazy 再起動
次のクエリ時に Kernel を自動再起動。ユーザー操作は途切れない。商談中に「久しぶりに戻ってきた」操作でも待ち時間は数秒
同時 Kernel 数の上限
1 ユーザー最大 3 Thread・グローバルで上限を設定。リソース枯渇前に新規 Thread 作成を「混雑中」モーダルで止める
中断・タイムアウト
Stop ボタン + SSE 切断検知 + Kernel interrupt の 5 秒待機(ADR-029)。生成コードが暴走しても最大 5 秒で止まる
複数 Thread の結果は別グラフ「Cross-thread Summary」で AI が横断要約できる(ADR-025)。1 つのデータセットを多角的に切る分析を 1 ユーザー同時最大 3 Thread + Summary Thread で並行進行できる。
なぜ LangGraph か
LangChain / Mastra / Dify との比較を 4 観点で。「データ分析を設計通りに動かす」要件には LangGraph + Python が最短だった理由。
状態が「明示的なグラフ」で見える
メイン 9 ノード + Refine 2 ノード + Summary 3 ノードを LangGraph で定義。どのノードでどの変数が更新されるかが構造化されているので、「なぜ AI がこう判断したか」を後から追える。フリーフォームの SDK だと暗黙の状態が増えてバグを再現できなくなる。
PYTHON-FIRST · ステートマシンを「コードで一望」できるHuman-in-the-Loop を動的に on/off できる
LangGraph の interrupt_before / MemorySaver で「特定ノードの前で止める」が宣言的に書ける。assess_plan が計画リスクを評価して停止可否を動的に切り替えるので、軽い質問は軽く・重い計画は止める、を両立できる。
PYTHON-FIRST · 「人が止められる AI」の必須条件Python データ分析エコシステムと地続き
pandas / scipy / statsmodels / Prophet / scikit-learn / seaborn を Kernel 内でそのまま使える。LangChain / Mastra / Dify は LLM 周辺のフレームワークが主で、データ分析特化の Python ライブラリとの相性は LangGraph + Jupyter Kernel の組み合わせが頭一つ抜ける。
PYTHON-FIRST · 「数字仕事」を本気でやるなら Python設計通りに動かす(フリーフォームを許さない)
汎用 AI チャットは「LLM 任せ」で動作が日によって変わる。LangGraph はグラフ定義の通りにしか動かない=商談先で見せた動作と同じ動作を翌週も再現できる。「捏造しない」「経路を勝手に変えない」が構造で保証される。
PYTHON-FIRST · Demo C の「数字が正しい」の土台BeyonZ の 3 デモは Demo A = GUI ファースト(Dify)/ Demo B = TypeScript ファースト(Mastra)/ Demo C = Python ファースト(LangGraph)と意図的に分業しており、案件の性質(GUI 構築の速さが要るか、フロントエンドとの統合が要るか、データ分析の深さが要るか)から逆算してフレームワークを選ぶ。
“自律 + コード実行”を本番に乗せるための 5 柱
LangGraph がコードを自律生成し Jupyter Kernel が実行する以上、セキュリティは「ユーザー間分離」だけでは足りない。コスト上限・利用回数・実行環境・安全網の多層構造で「設計通りに動く」を担保する。
データ分離(Supabase RLS)
Supabase の Row Level Security でユーザー間のデータを物理的に分離。`demo_c` スキーマで Demo B と論理分離もしておき、共有プロジェクトでも他デモの状態に触れない。
- 認証は Supabase Auth + JWT 検証(pyjwt + JWKS)
- RLS ポリシーで select / insert / update を user_id ベースで制限
- Thread / Dataset / 分析結果すべてに user_id を持たせる
コスト上限(daily_cost_cap)
Claude API のトークン消費を per-user / global の 2 段で監視。当日 UTC 基準でフィルタするので、過去の累計でブロックされず、毎日 00:00 UTC でリセットされる。
- per-user $5 / 当日 UTC / 超過時は 429 + 復帰時刻を返す
- global $50 / 暴走を全体で止める
- CostCap 専用ダイアログで「使用回数 0/5」誤表示を回避
利用回数制限(RateLimit)
1 ユーザー 1 日 5 回まで分析できる。商談デモ用の `RATE_LIMIT_EXEMPT_EMAILS` でデモアカウントだけ制限を外せる。
- 1 ユーザー 1 日 5 回(env で変更可)
- exempt 用 email 配列で運用アカウントのみバイパス
- 実本番運用では制限値を要件に合わせて変更可能
実行環境の隔離(Cloud Run + Worker)
GCP Cloud Run(東京・max-instances=1)で実行環境を隔離し、Cloudflare Worker が `run.app` 直URL を隠蔽。コードはユーザーごとに別 Kernel プロセスで実行されるので、別ユーザーの変数空間には触れない。
- Cloud Run max-instances=1 でコスト上限封止
- Cloudflare Worker でリバースプロキシ + 無料 SSL
- Kernel プロセスを Thread ごとに分離(同居しない)
コード実行の安全網(Kernel safety)
生成された Python コードが想定外の挙動を取っても、Kernel 側のラッパで吸収。タイムアウト・interrupt・json.dumps の numpy 対応 default 注入など、複数の安全網を Kernel bootstrap で仕込む。
- Stop ボタン + SSE 切断検知 + Kernel interrupt 5 秒待機(ADR-029)
- `json.dumps` の numpy/pandas 対応 default で `int64 not JSON serializable` を吸収(ADR-045)
- アイドル shutdown + lazy 再起動でメモリリーク回避