Work · PoC Lead · 2025

メディア業界向け 業務オペレーション支援 AI エージェント基盤 (PoC)

業務オペレーション最適化を、Agent で支援する。

メディア業界向けの業務オペレーション最適化を目的とした社内 PoC。従来は手動で行われていた調整・編集作業を AI エージェントで支援。Main Agent + 6 Sub Agent のマルチエージェント構成で、Human-in-the-Loop で編集案を提示しオペレーターが承認するワークフローを設計しました。

trace · 9f2-a1c● live
00.12mainPLAN: 6スロット最適化
00.34scheduleDIFF: 3 → 5 swap
00.51fairnessconstraint check
01.04verifyVERIFICATION
01.18mainhuman approval req?
Main + 6 SubA2Atrace_id
Stack
PythonFastAPINext.jsTypeScriptOpenAPIAI AgentGoogle ADKA2A ProtocolPostgreSQLDockerClaude Code
Role / Scope
  • リードとして推進
  • Main Agent + 6 Sub Agent のマルチエージェント基盤設計
  • Sub Agent の I/O コントラクト設計
  • FastAPI による OpenAPI コードファースト設計
  • Orval による TypeScript SDK 自動生成
  • W3C Trace Context による trace_id 伝播設計
  • 構造化 JSON ログ基盤
  • 仕様変更が多い PoC でも差し替えしやすい構成
Challenge

PoC なので仕様が日替わりで変わる。Agent の責務もちょくちょく動く。一方で運用側の信頼を取るには「なぜそういう判断になったか」を後から追えるトレーサビリティが要る。仕様変更の容易さと観測性の両方を、最初から組み込む必要があった。

Design Decisions
01

コントラクト先行

OpenAPI を Single Source of Truth にして、TypeScript SDK は orval で自動生成。Agent の責務を入れ替えたり書き直したりしても、フロントの呼び出しコードは API スキーマ側に従う。Agent の中身を書き直す回数が多い PoC では、これが一番効いた。

02

trace_id を最初から

W3C Trace Context を Agent 境界・LLM 呼び出し・Queue にまで全部伝播させた。1 リクエストの全履歴を後から jq で再構成できる状態にする。「あの 1 リクエストの全履歴を見たい」は調査開始の 8 割を占めるので、ここに最初から投資する判断は迷わなかった。

03

PLAN → DIFF → COMMANDS → VERIFICATION

Agent の出力は「変更そのもの」ではなく「変更提案」として表現する。PLAN(やろうとしていること)→ DIFF(具体的な差分)→ COMMANDS(実行用コマンド)→ VERIFICATION(検証結果)の 4 ステップで返す。Human-in-the-Loop の承認フローが自然に乗っかる。

04

構造化ログ

JSON ログを書く時点で span_id / agent_name / model / trace_id を必須項目にする。書く側は窮屈だが、jq で grep する側が一気に楽になる。後から「あの Agent でだけ起きてる」「あの model 呼び出しでだけ遅い」みたいな絞り込みが 1 行でできるようになる。

Deep dive · Architecture Patterns

Multi-agent + Human-in-the-Loop の設計。

業務システムを直接書き換えず、参照と提案に役割を絞った PoC。 外部仕様 · 業務固有情報は伏せ、汎用的に再利用できる設計パターンを抜粋しています。

§ ASystem Architecture

FE / BE / Agents / Storage を疎結合に。

Frontend は自動生成 SDK でしか BE に触れず、Agent は社内 RDB に View 経由で参照だけ。書き込みは内部 DB に閉じ、外部システムへの反映は人手に委ねる構成。

Frontend
ui
App Router 画面
Next.js 15 · TS · Tailwind v4
codegen
TypeScript SDK
OpenAPI から自動生成 (Orval)
realtime
SSE クライアント
EventSource · 進捗ストリーム
Backend
api
REST API
FastAPI · Python 3.12
realtime
SSE エンドポイント
ジョブ進捗を逐次配信
service
Job Service
状態管理 · 永続化 · 監査
contract
openapi.json
Single Source of Truth
Agents
orchestrator
Main Agent
司令塔 · Sub を順次呼び出し
ADK · A2A
Sub Agents × N
役割ごとに分割 · A2A 通信
store
PostgreSQL 16
ジョブ · イベント · 実行履歴
external
社内 RDB · Read-only View
Agent から View 経由でのみ参照
config
業務ルール定義
JSON で管理 · LLM が参照
OpenAPI = SSoT
型定義は 1 箇所。SDK は自動生成、手書き fetch は禁止。
trace_id 伝播
W3C Trace Context を FE → BE → Agent 全てに継承し、全ログの必須項目に。
Read-only に閉じる
外部 RDB へのアクセスは View 経由・参照のみ。書き込みは内部 DB に閉じる。
§ BMulti-agent flow

Main Agent + 6 Sub Agent のラウンドトリップ。

役割で分けた 6 Sub を Main から順に呼び、各完了で SSE を Push。決定的な処理と LLM 解釈の責務を分け、後段から差し戻しやすい設計に。

Main Agent各 Sub を順に呼び、戻ってから次に渡すラウンドトリップ方式↳ 各 Sub 完了で SSE を Push
01deterministic
データ取得
業務 View から必要なドメインデータを Read-only で取得。
02deterministic
割付
制約条件 (尺・容量) に対し機械的に候補を割り振る。
03LLM
並び替え
業務ルール (must / should / 例外) を LLM が解釈し再配置。
04LLM
採点
結果に対し LLM がスコアと reasoning を生成。
05deterministic
整形
出力フォーマットを統一・差分の説明可能性を確保。
06deterministic
永続化 / 通知
DB に書き込み、SSE で完了イベントを配信。
Human-in-the-Loop完了後、作業者が結果と reasoning を確認 → 必要に応じて手動で業務システムへ反映。auto-apply は意図的に非対応
NOTEPhase 1 では parallel ではなくラウンドトリップを採用。各 Sub の入出力を観測しやすく、後段からの差し戻しと A/B 検証を優先した設計。
§ CKey patterns

OpenAPI · trace_id · View · SSE。

今回特に意識した 4 つの設計判断。どれもプロジェクト固有ではなく、別のサービスでもそのまま使える形に整理しました。

01

OpenAPI コードファースト

FastAPI の型定義から openapi.json を自動生成 → Orval で TypeScript SDK を生成 → FE は SDK のみを通して BE と通信。

  • 型はサーバ側に 1 箇所
  • 手書き fetch を禁止
  • 破壊的変更は型エラーで検知
02

W3C Trace Context 伝播

Frontend → Backend → Agent → LLM 呼び出し → 内部ジョブまで traceparent を継承。構造化ログに必須項目化。

  • 1 リクエストの全履歴を後から再構成
  • Agent 境界も span として残す
  • LLM のリクエスト ID を span に紐付け
03

Read-only に閉じた外部接続

外部 RDB は View 経由・参照のみ。書き込みは内部 DB に限定し、業務システムへの反映は Human-in-the-Loop に委ねる。

  • 障害時の二次被害を回避
  • 監査ログを内部に集約
  • 業務システム側の権限境界を侵さない
04

イベント駆動 (SSE + 構造化ログ)

ジョブの進捗は SSE で逐次配信。Sub Agent 完了 / エラー / 制約違反などを意味のあるイベントに分割。

  • UI のリアルタイム更新
  • ログから挙動を再現可能
  • 後段の通知・ダッシュボードに転用可
§ DJob state machine

pending → running → completed / failed / cancelled。

ジョブのライフサイクルを 5 状態に正規化し、SSE と DB の両方で観測可能にする。エラー時も reasoning を残して終了する。

pending
POST /jobs · 受付
streaming
running
Main → Sub × N · SSE
completed
DB 永続化 + 通知
running → failed
例外 · 制約違反 · タイムアウト
reasoning と context を残して終了
running → cancelled
作業者キャンセル
SSE 切断 + ジョブ中断