maestro/docs/aao-gateway-overview.md
clade 7049a874f3 feat: initial public release (MAESTRO v0.1.0)
Open-source release of MAESTRO, an agent orchestration platform that runs
LLM-driven tasks through sandboxed tools, with a web UI. Apache-2.0.
See README.md and docs/ (getting-started, configuration, architecture).
2026-06-03 04:01:14 +00:00

13 KiB
Raw Blame History

AAO Gateway モード — 機能概要 (2026-05-20 時点)

このドキュメントは「LiteLLM Proxy 代替として AAO に追加された Gateway 機能」が今回の一連の作業でどう変わったかを日本語で解説します。技術詳細の設計 doc は末尾の関連ドキュメントセクションを参照。


TL;DR (3 行)

  • AAO 自身が OpenAI 互換 LLM Gateway として動けるようになりました。 他の AAO や任意の OpenAI クライアントから /v1/chat/completions を叩けます。
  • 設定 UI のトグル 1 つで on/off (同一プロセス内で起動・停止)。別プロセスで動かす運用も従来通り可能 (advanced)。
  • 仮想 API キーごとに 月次 token 予算 + RPM レート制限、Prometheus でリアルタイム監視。Telemetry 外部送信ゼロ。

なぜこれを作ったか

LiteLLM Proxy には次の懸念がありました:

  1. Telemetry: デフォルトで匿名利用データが外部送信される (opt-out 可だが要設定)。
  2. License 変更リスク: MIT → BSL/SSPL への変更前例があり、組織で長期運用するには不確実性が高い。
  3. 追加依存: Python サービス + Redis (任意) + DB を、AAO とは別に運用する必要がある。

これらを 「AAO 単一バイナリ + 既存 SQLite + telemetry 完全ゼロ」 で代替するのが本機能の目的です。LiteLLM 固有の高度機能 (sticky routing / canary / cost USD / OpenTelemetry / Slack アラート / multi-region federation 等) は 意図的にスコープ外にして、シンプルな in-house ゲートウェイに振り切りました。


どの機能が、どのタイミングで入ったか

時系列に沿って、追加された機能とユーザーへの影響を解説します。

Phase 1: Gateway モードの土台 (PR #326)

項目 内容
起動方法 AAO_MODE=gateway を環境変数指定すると、worker / scheduler / UI を起動せず、軽量に gateway サーバだけ立つ
エンドポイント /v1/chat/completions (SSE ストリーミング) + /v1/models + /health (LiteLLM 互換 JSON)
認証 Bearer Token (config.yaml に static な virtual keys)
ルーティング 複数 backend (llama-server / vLLM など) を least-busy (進行中リクエスト数が一番少ない backend) で振り分け
安全停止 SIGTERM 時に進行中 SSE を shutdown_graceful_sec (デフォルト 30s) 待って drain、gateway_shutdown SSE event でクライアントに retry を促す

Phase 2a: 仮想キーを DB で管理 + Admin REST API (PR #330)

  • API キー形式: sk-aao-<base62-32> (32 文字、23 bytes エントロピー、SHA-256 hash で DB 保存)
  • raw key は発行直後の 1 回だけ表示、以降は prefix しか見えない (LiteLLM 同様の安全設計)
  • Admin REST API で発行 / 一覧 / rotate / soft delete (revoked_at)
  • config.yaml の static key は起動時に自動で DB に移行
  • team フィールドでテナント分離

Phase 2b: 予算とレート制限 + UI 一式 (PR #332)

機能 実装
月次 token 予算 キーごとに tokens_budget。UTC 月初に counter リセット。超過した次のリクエストで 429 を返す (post-hoc enforcement)
RPM レート制限 キーごとに rate_limit_rpm。sliding 60s window、in-memory カウンタ + 30s 周期で DB flush
使用量集計 gateway_key_usage テーブルに (key_id, period_start) 単位で tokens_in / tokens_out / requests を記録
UI Settings → Tools → "Gateway Keys" タブ。発行・無効化・rotate・月次グラフ

Phase 3a: Polish bundle (PR #333)

Phase 1-2b で残った INVESTIGATE 8 件をまとめて解消:

  1. Orphan key 検出時の警告ログ
  2. Authorization: Bearer ... の regex を RFC 6750 厳密に
  3. 5 秒 LRU の key auth cache (DB 負荷削減)
  4. config 由来 key の drift resync (config.yaml を書き換えた時の DB 整合)
  5. PATCH で revoked key を編集しようとしたら 409 conflict
  6. Dead field cleanup
  7. MAX_TRACKED_KEYS LRU eviction (rate limiter)
  8. SSE error の sentinel 化: gateway_shutdown / gateway_timeout / budget_exhausted / rate_limited の 4 種をクライアントが識別可能に

Phase 3b: Prometheus exporter (PR #335)

  • Gateway 11 metrics: requests_total / tokens_total / backend_busy_slots / key_cache_size / latency histogram など
  • Worker 6 metrics: jobs_total / piece_runs / queue depth など
  • 全 metric に per-team label を載せて、テナント単位の利用量を分離可視化
  • /metricsデフォルトで localhost 限定 (127.0.0.1 / ::1 allowlist)、外部 Prometheus 用に bearer token opt-in
  • cardinality 暴走を防ぐため、label を 1 桁オーダーに抑える discipline

cleanup (PR #337)

  • AAO の LLM クライアント (openai-compat.ts) で gateway sentinel SSE error を parse

ops (PR #340)

  • scripts/gateway.sh start | stop | status | logs (.gateway.pid 管理、logs/gateway.log)
  • AAO_CONFIG env で config.yaml path を override 可能 (両モード共通)
  • GATEWAY_PORT env で listen port を override 可能
  • DB 共有設計 (worker と gateway は同一 SQLite を WAL で共有可能) を doc に明記

Phase 3c: UI 制御 + 同プロセス default ← 本セッションで完了 (PR #341)

ここが今回の最大の変化です。これまで「gateway 専用プロセスを別 port で起動する」モデルだったのを抜本的に変更:

Before (Phase 1-3b) After (Phase 3c)
AAO_MODE=gateway で gateway 専用プロセスを別 port (例: 9877) に起動 通常の worker AAO の 同一プロセス・同一 port で動作
Worker UI と gateway を物理的に分離 UI のトグル 1 つで gateway を mount / unmount
各 AAO に gateway 用の追加デプロイが必要 既存 AAO を起動して UI から有効化するだけ

新しい設定 UI (Settings → Tools → "Gateway Server")

  • Enable Gateway トグル: チェックで gateway 起動、外して停止
  • Backends list (フォーム編集):
    • Endpoint URL (http/https)
    • Backend ID (任意の文字列、ルーティング識別子)
    • Max slots (並列処理上限)
    • API key (backend 側に必要な場合)
  • 編集 + Save で hot reload: 進行中 SSE は gateway_shutdown event で drain、新接続から新 config で動作
  • リアルタイム状態 badge: running / disabled / misconfigured を 3 秒周期で表示

動作モード

  • Gateway 有効時: /v1/* paths が gateway sub-app にルーティングされ、/health は LiteLLM 互換 JSON を返す
  • Gateway 無効時: /v1/* は 404、/health は bridge の {status:'ok'} (Docker healthcheck 等の既存利用に影響なし)

Phase 3c で重要だった 8 件のバグ修正

レビューで critical な問題が見つかり、すべて修正済み:

  1. prom-client メトリクス重複登録クラッシュ: gateway 設定変更 (= bounce) 1 回ごとに Counter を新規登録 → 2 回目で throw して bridge プロセスごと死ぬ問題。WeakMap<Registry, Map<prefix, GatewayMetrics>> で memoize して解決
  2. BackendStatusRegistry が worker 用の list を見ていた: 同プロセス mode で gateway も worker と同じ registry を共有していたため、gateway 専用の backend が status 不明 → routing blind / /health 空 / metrics 0。Gateway は自前 registry を gateway.backends[] 上に build する設計に
  3. /health LiteLLM 互換が壊れていた: bridge の既存 /health ({status:'ok'}) が mountGateway より先に登録されていて、LiteLLM 互換 JSON が返らない問題。classifyGatewayPath を 3 値 (gateway-only / gateway-when-enabled / false) に拡張し、Express middleware の登録順序を修正
  4. transition 中の config が dropped される: state === 'starting' | 'stopping' 中に新規 config 適用が落ちる問題。pendingConfig queue + mutex chain replay で対応 (実用上は mutex serialize で到達不能だが、防御として実装)
  5. stop() が state を misconfigured のまま残す: 公開 stop API が state を disabled にリセットしない問題
  6. configsEquivalent の比較が不安定: JSON.stringify の key 順依存で偽 bounce が起きる問題。stable stringify に
  7. listenPort が env 依存: 実 listen port と表示 port が乖離する可能性。CoreServerOptions.listenPort で配線
  8. api_key 入力時に ${VAR} env 参照が黙って literal 保存される: amber warning text を form に表示

これらは round-1 / round-2 の adversarial review で発見・修正。テストでは隠蔽されやすいバグ (例: 1 番は beforeEach で fresh Registry を使うと検出できない) を含むため、shared-registry pattern の test を追加で書いて検証しています。


どう使うか (運用ガイド)

パターン A: 単一 AAO で gateway を有効化 (Phase 3c 以降の推奨)

1. scripts/server.sh start で AAO を通常起動
2. UI に admin としてログイン
3. Settings → Tools → "Gateway Server" を開く
4. "Enable Gateway" にチェック → Backends list を入力 → Save
5. Settings → Tools → "Gateway Keys" で API キーを発行 (raw key は発行直後のみ表示)
6. 他の AAO や OpenAI クライアントから:
     base_url: http://this-aao:9876/v1
     api_key:  sk-aao-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

パターン B: Gateway 専用サーバとしてデプロイ (advanced)

GPU サーバが多い、または worker UI を運用したくない場合:

AAO_MODE=gateway scripts/gateway.sh start
# AAO_CONFIG=/etc/aao/gateway.yaml で別 config を指定可能
# worker mode と同じ DB を共有可能 (キー管理が両モードで一貫)

パターン C: 既存 LiteLLM Proxy からの乗り換え

互換性のポイント:

  • Endpoint path: /v1/chat/completions/v1/models/health 同じ
  • Response header x-litellm-model-id をそのまま発行 (vendor-neutral な x-aao-backend-id も同値で同時発行)
  • Key format: sk-aao-* (LiteLLM の sk-* から prefix を変更、長さ・エントロピー同等)
  • /health の JSON shape: {healthy_endpoints, unhealthy_endpoints, healthy_count, unhealthy_count} で LiteLLM 互換

クライアント側の parseLiteLLMHealth 等の既存コードは無修正で動作します。


監視

/metrics で Prometheus 形式メトリクスを取得:

# Gateway 系
aao_gateway_requests_total{backend="llm-a",team="ops",status="ok"} 142
aao_gateway_tokens_total{backend="llm-a",team="ops",direction="out"} 95821
aao_gateway_backend_busy_slots{backend="llm-a"} 2

# Worker 系 (same-process mode 時に同じ endpoint から)
aao_worker_jobs_total{piece="chat",status="succeeded"} 38

外部 Prometheus からスクレイプする場合は config で provider.metrics.bearer_token を設定し、IP allowlist を緩和。


意図的にやっていない機能 (実需確認まで保留)

機能 保留理由
Sticky routing (cache hit 最適化) 1 backend 構成 + cache hit 率 実測無しでは ROI 不明
Canary routing (model rollout) 現状 model A→B 切り替え予定なし
Token → USD コスト換算 tokens 数で十分か admin の要望次第
Audit log テーブル compliance 要件無し時点では不要
Pre-reserve budget (並列 burst) 実際に N×max_tokens overshoot が観測されたら検討
OpenTelemetry trace 単一 org deploy では ROI 低い
Slack / Email アラート Prometheus Alertmanager で代用想定
Multi-AAO federation Prometheus federation で代用想定

次のステップ

  • Backend ルーティングの動作 (least-busy の精度)
  • SSE drain の挙動 (大量同時接続 + graceful shutdown)
  • Budget / RPM の境界値 (オフバイワン無いか)
  • Prometheus metrics の cardinality / scrape duration
  • UI hot reload の race condition
  • LiteLLM 互換性 (既存 client コードが無修正で動くか)

を検証。dogfooding で観測した問題から Phase 4 のスコープを決めます。


関連ドキュメント

  • In-product help: ui/src/content/help/11-llm-gateway.md
  • INVESTIGATE backlog (open follow-up issues): Gitea issue #338

マージ済み PR 一覧

Phase PR merge commit 日付
1 #326 178baa9 2026-05-18
2a #330 78a796d 2026-05-18
2b #332 b0569c7 2026-05-19
3a #333 30e9d78 2026-05-19
3b #335 9efc60f 2026-05-19
cleanup #337 bcfdd41 2026-05-20
ops #340 13bd3cd 2026-05-20
3c #341 561ff24 2026-05-20 (本セッション)

累計コード追加: 約 12,500 行 (テスト含む)、テスト件数 2720 (ベースライン 2707 から +13)。