土曜の午前中に、blog draft agent が沈黙した
先月の土曜、朝いちで管理画面を開いたら blog draft agent が動いていなかった。原因を追った結果、ある table の RLS policy を前日に自分で変えた副作用だった。エージェント本体には何のバグもない。共有 state — つまり Supabase の権限設定 — が壊れていただけだ。
その時、思い直したことがある。この構成でよかった、と。エージェント同士を直接呼び合う設計にしていたら、この原因追跡はもっと長くかかっただろう。
これから3回にわたって、フレームワークを使わずに Supabase だけを中心に据えたマルチエージェント運用の話を書く。1回目の今回は、なぜフレームワークを外したか、そしてDBをhubにするという設計判断についてだ。

なぜフレームワークを敬遠したか
筆者は最初、素直に LangGraph を試した。次に CrewAI。どちらも触った印象は悪くない。CrewAI はプロトタイピングが速い。LangGraph はグラフとしての制御が明快だ。
ただ、実測レポートを見ると気になる数字が並ぶ。LangGraph はノードあたり 120ms 前後のオーケストレーションオーバーヘッド、CrewAI は LLM のロール委譲を挟むためタスク遷移でおよそ 450ms、トークン消費もおよそ 18% 増える、といった報告がコミュニティに複数上がっている。Confluent などが event-driven 対 polling で「レイテンシ 70〜90% 削減」と語る話も同時期に流れていた。
コストとしての 18% は、実はそこまで大きな問題ではなかった。月 $1000 の LLM 予算なら $180 増える話で、それだけなら払う判断もあり得る。
引っかかったのは別のところだ。1人スタジオで運用するとき「制御フロー自体を、エージェントの数だけ設計する」という作業が時間を食いすぎる。フレームワークは制御フローを書く手段を提供してくれるが、制御フローそのものは自分で書かねばならない。エージェントが4つ5つと増えると、そこにかかる時間は線形以上に伸びる感覚があった。
フレームワークが悪いのではない。用途とマッチしない、というだけの話である。コミュニティで見かける「CrewAI で PoC を作って、本番は LangGraph に移す」というパターンも、複数人で持続的に育てるチーム前提の道筋に見える。1人で回すには、そもそも別の軸で考える必要があった。
設計判断: DB を hub にする
そこで方針を変えた。エージェント同士を、直接呼ばない。
かわりに、Supabase の table 群だけを read/write する。あるエージェントの出力は必ず何かの table に書かれる。次のエージェントは、その table を polling するか、cron で起動されて読みに行く。エージェント間には API 呼び出しが存在しない。共有されるのは state だけだ。
秒単位のレイテンシが必要な用途なら、この選択は成立しない。ブログ生成、リサーチ、CRM の更新、日次のブリーフ — こういう workload の SLA は「今日中」だったり「1時間以内」だったりする。polling でも実用上は困らない。event-driven が必要な場所だけ、Supabase Realtime を後から足せばよい、と割り切った。
正直なところ、この構成に落ち着くまで数ヶ月はブレていた。最初は LangGraph の checkpoint に価値を感じていた時期があるし、CrewAI の role 定義の綺麗さに惹かれた時期もある。実運用で何回か壊れて、何回か復旧して、そのたびに「何が壊れたのか」を追う難易度で判断が固まっていった。
table を hub にすると、壊れた時に見る場所が1箇所に絞られる。Supabase の Table Editor を開けば、今どのエージェントがどこまで進んでいるかが目視できる。SQL を1行叩けば同じことがもっと厳密にわかる。この監査性は、日常運用に地味に効く。
いま並んでいるエージェント
参考までに、今回の構成で並んでいるエージェントを挙げておく。実装の詳細は次回に譲るとして、名前と扱う table だけ。
ブログドラフト生成: news_items から未処理を読み、blog_posts に draft を書く
X トレンドリサーチ: 外部検索を叩いて x_trends に書く
デイリーブリーフ: 複数ソースを raw_signals から要約し、brief_editions に書く
プロスペクト発掘: prospect_companies を新規挿入・既存更新する
エージェントごとに read する table と write する table が違うだけで、他のエージェントの存在は一切知らない。この「知らなさ」が、後から新しいエージェントを足すときに効いてくる。差分は table の追加か、既存 table への status 値を1つ増やすだけで済むことが多い。
全体の構成図
言葉より、絵にした方が早い。
[news scraper cron] ─write─▶ news_items ◀─read─ [blog draft agent]
│write
▼
admin UI ◀─read─── blog_posts (status=draft)
│human confirm
▼
blog_posts (status=published) ─read─▶ [x post agent]
[external search cron] ─write─▶ x_trends ◀─read─ [x post agent]
[prospect finder cron] ─write─▶ prospect_companies ◀─read─ [enrichment agent]
[multi-source scraper] ─write─▶ raw_signals ◀─read─ [daily brief agent]
│write
▼
brief_editions矢印がすべて table を経由している点だけ見てほしい。エージェント同士が直接つながっている線は、この図に1本もない。
各 table には status カラムを持たせている。例えば blog_posts はこうだ。
-- 抜粋
status text not null default 'draft'
check (status in ('draft', 'pending_review', 'published', 'archived'))エージェントは自分の write でこの status を進める。human confirm が挟まる箇所では、admin UI が手で status を切り替える。cron の次回起動時に、次段のエージェントが「新しい status のもの」を拾う。これだけで handoff が動く。
フレームワークとの棲み分け
誤解されたくないので付け加えておく。この記事は「フレームワークは不要だ」という主張ではない。
LangGraph が刺さる場面は明確にある。条件分岐が深い、checkpoint を多用したい、複数人でエージェント handoff を設計する — こういう文脈では、DB 中心の疎結合はむしろ管理コストが上がる。フレームワークが提供する型の恩恵の方が大きい。
今回書いているのは、条件が3つ重なった場合だけの話だ。1人か少人数、batch と on-demand が混ざる workload、LLM コストに敏感。この三条件が揃うなら、DB を hub にする構成は選択肢として実務的だ、というだけの話になる。
次回について
次回は、各エージェントの実装を read / write / confirm の3動作に還元して掘る。cron と on-demand の混在をどう捌くか、handoff で status を進めるときのロック戦略はどうしたか、そのあたりを書く予定だ。設計判断より一歩コードに寄る回になる。
筆者は 5years+ で AI・自動化の設計と実装を担当している。