第77回 実務で回す外部モデル・ベンダー運用の自動化 — Pythonで作るSLA監視・障害切替・請求監査の手順

外部APIやモデルベンダーを使うと、便利な反面「ある日突然止まる」「請求が膨らんでいるのに気づかない」といった実務の悩みが出やすいです。本記事では、現場担当者がすぐに使える実務手順とPythonスクリプトのサンプルを中心に、監視・自動フェイルオーバー・請求突合・契約チェックまでを落ち着いて整理します。初めて運用を作る方にも読みやすいよう、注意点と失敗しやすいポイントも明示します。

設計方針

まずは運用の目的を明確にします。重要なのは「サービスの可用性を保ちつつ、コスト異常を早期検知する」ことです。以下の方針で進めます。

  • 必須メトリクスを定義し、可視化としきい値で自動検知する
  • 障害時は自動で代替ベンダーに切替える(カナリア→全面)
  • 請求データは月次で突合し、異常はアラートで経営や請求担当へ通知する
  • 契約・データ取扱いのチェックリストを用意し、定期レビューを行う

必須メトリクス(推奨)

メトリクス 意味 監視頻度 しきい値例
レイテンシ(p95, p99) 応答時間の指標 1分〜5分 p95 > 1.5x 平常値
エラー率(4xx/5xx) 失敗リクエストの割合 1分〜5分 エラー率 > 2% かつ増加傾向
コスト/リクエスト 単位リクエスト当たりの課金 日次/週次 前月比 +30% など
モデルバージョン 利用中のモデル識別 変更時に検知 想定外のバージョンは警告

監視とアラート

既存の監視基盤がある場合は、そこにメトリクス送信を組み込みます。ない場合は簡易的なヘルスチェックをPythonで作り、ログとSlack/メールに通知する運用から始めましょう。

簡易ヘルスチェック(考え方)

  • 定期的にエンドポイントへリクエストを送り、応答時間とステータスを記録する
  • 直近の失敗回数や平均レイテンシでしきい値判定する
  • しきい値超過でSlack/メールに通知、必要に応じて自動フェイルオーバーをトリガーする

Pythonヘルスチェック:サンプル

以下は最小構成の例です。requests と logging を使います。SlackはWebhookを想定しています。

import requests
import time
import logging

HEALTH_URL = "https://api.vendor.example/health"
SLACK_WEBHOOK = "https://hooks.slack.com/services/xxxx/xxxx/xxxx"
CHECK_INTERVAL = 60  # 秒

logging.basicConfig(level=logging.INFO)

def notify_slack(text):
    try:
        requests.post(SLACK_WEBHOOK, json={"text": text}, timeout=5)
    except Exception as e:
        logging.exception("Slack通知失敗: %s", e)

while True:
    try:
        start = time.time()
        r = requests.get(HEALTH_URL, timeout=10)
        latency = time.time() - start
        if r.status_code == 200:
            logging.info("OK status=200 latency=%.2fs", latency)
        else:
            logging.warning("NG status=%s latency=%.2fs", r.status_code, latency)
            notify_slack(f"ヘルスチェック異常: status={r.status_code} latency={latency:.2f}s")
    except Exception as e:
        logging.exception("ヘルスチェック例外")
        notify_slack(f"ヘルスチェック例外: {e}")
    time.sleep(CHECK_INTERVAL)

注意点:運用ではタイムアウトやリトライ、バックオフを入れてノイズを減らします。短時間の変動で不用意に切替しないために、閾値は小さいウィンドウ(例:5分内の連続失敗)で判定します。

自動フェイルオーバー

自動切替は便利ですが誤動作がリスクになります。安全にするには段階的な切替(カナリア→スケールアップ→全面切替)とロールバック手順を用意してください。

フェイルオーバーの基本手順

  • 障害検出(監視ルール)→ カナリアトラフィック(例: 5%)を代替ベンダーへ切替
  • カナリアで正常なら段階的拡大、問題あれば即ロールバック
  • 全面切替時はAPIキーやルーティング(AWS、GCP、ロードバランサ等)を更新
  • 切替は自動でも必ずログ・通知を行い、復旧後は原因分析を必須にする

フェイルオーバー実装サンプル(フラグ管理+リトライ)

簡易なフラグベースの切替例。実運用では機能フラグ管理サービスやロードバランサで行ってください。

import requests
import time
import logging

PRIMARY_URL = "https://api.primary-vendor.example/endpoint"
SECONDARY_URL = "https://api.secondary-vendor.example/endpoint"
API_KEY_PRIMARY = "sk-primary-..."
API_KEY_SECONDARY = "sk-secondary-..."

current = 'primary'

def call_api(payload):
    global current
    url = PRIMARY_URL if current == 'primary' else SECONDARY_URL
    key = API_KEY_PRIMARY if current == 'primary' else API_KEY_SECONDARY
    headers = {"Authorization": f"Bearer {key}"}
    try:
        r = requests.post(url, json=payload, headers=headers, timeout=10)
        if r.status_code == 401:
            # 認証切れは即座に切替判断
            logging.warning("認証エラー: %s", r.status_code)
            return False
        if r.status_code >= 500 or r.status_code == 429:
            # ベンダー側エラーやレート制限
            return False
        return r.json()
    except Exception:
        return False

def failover_to_secondary():
    global current
    logging.info("フェイルオーバー: primary -> secondary")
    current = 'secondary'

# 呼び出し側例
payload = {"text": "hello"}
resp = call_api(payload)
if resp is False:
    # 再試行と最終的に切替を検討
    for i in range(3):
        time.sleep(2 ** i)
        resp = call_api(payload)
        if resp is not False:
            break
    else:
        failover_to_secondary()

失敗しやすい点:認証切れやAPI仕様変更は自動検知の対象に必ず入れてください。また、切替後のメトリクス監視も必須です。

請求・SLA突合

請求ミスは見過ごしがちです。毎月の突合の自動化と、SLA違反の検出ルールを準備しましょう。

月次突合の基本

  • ベンダーの請求CSV/レポートを定期取得(APIまたはSFTP)
  • 社内ログ(リクエスト数・モデル名・課金単価)と照合
  • 差分が一定以上なら自動でアラート、担当者が確認するワークフローへ

請求突合サンプル(CSV読み込みと簡易チェック)

import csv
from decimal import Decimal

# vendor_billing.csv の想定列: date, usage_count, billed_amount

def reconcile(vendor_csv_path, internal_usage_count, unit_price):
    with open(vendor_csv_path, newline='') as f:
        reader = csv.DictReader(f)
        total_billed = Decimal('0')
        total_usage = 0
        for row in reader:
            total_usage += int(row['usage_count'])
            total_billed += Decimal(row['billed_amount'])
    expected = Decimal(str(internal_usage_count)) * Decimal(str(unit_price))
    diff = total_billed - expected
    return {
        'vendor_usage': total_usage,
        'vendor_billed': float(total_billed),
        'expected_billed': float(expected),
        'diff': float(diff)
    }

# 例
print(reconcile('vendor_billing.csv', internal_usage_count=12345, unit_price=0.0004))

ポイント:単価がAPI毎やモデル毎に異なることがあります。必ずモデルIDやエンドポイント別に切り分けて突合してください。

契約・コンプライアンスチェック

運用前に確認すべき契約項目とデータ取扱いの要点を表にまとめます。実務で見落としがちな点を中心にします。

チェック項目 確認ポイント 失敗しやすい点
SLA(可用性・復旧目安) 月間可用性、復旧時間(RTO/RPO) 可用性のみで復旧手順が未定義
データ取扱い 学習利用の可否、保持期間、匿名化要件 送信データの機密性を事前に評価していない
責任分界 障害時の連絡窓口、賠償範囲 ベンダー側の責任範囲が曖昧
ログと監査 ログ保持期間、アクセス権管理 ログが分散して突合できない

運用試験と手順書化

運用は作って終わりではなく、定期的な演習が効果を上げます。以下の演習を推奨します。

障害シナリオ一覧とテスト手順(例)

シナリオ 模擬方法 確認項目
遅延増大 ヘルスチェックで意図的にタイムアウトを返す アラート発報、カナリア切替の挙動
部分応答(5xx増加) エンドポイントで500応答を返すテスト 自動リトライ→切替、ログの記録
認証切れ APIキーを無効化してレスポンスを確認 即時通知、キー差替の手順確認
過剰請求 請求CSVを改ざんしたサンプルで突合テスト 差分検出とワークフロー起動

運用ルーチン例:デイリーのヘルスチェック確認(5分)、週次のカナリア演習、月次の請求突合とSLAレポート。

実践サンプルコード(まとめて)

ここまでの要素を統合したミニマムな監視+フェイルオーバーのスクリプト構成案です。実運用では設定を環境変数やVaultで管理してください。

# 管理用の主要ファイル例
# - health_check.py  : ヘルスチェックとSlack通知
# - failover.py      : フェイルオーバー判定とフラグ更新
# - reconcile.py     : 請求CSVの突合

# 上記それぞれを定期実行(cronやシステムタイマー)で回す構成が現実的です。

次の一手(読後すぐできること)

読後30分で始められる作業プラン:

  • 1) 上のヘルスチェックスクリプトを1台のサーバ/コンテナで動かす(15分)
  • 2) Slack Webhook を用意して通知の動作を確認する(10分)
  • 3) 月次突合用の簡易CSVテンプレートを作成し、サンプルデータで突合を試す(5分)

また、月次SLAレポートのテンプレートと、フェイルオーバー演習のチェックリストを用意しておくと運用が回りやすくなります。

まとめ

外部モデル・ベンダーの運用は「監視」「自動切替」「請求突合」「契約チェック」をセットで設計することが重要です。まずは小さく始めて、監視→通知→手動切替→自動切替へと段階的に自動化を進めてください。記事内のサンプルは実務で使える最小単位の例です。実運用に組み込む際は、認証情報の安全管理、切替の権限・手順、定期的な演習を必ず組み合わせてください。

次回以降の連載では、具体的なダッシュボード化手順や、モデルごとのコスト最適化(プロンプト最適化やバッチ化)についても扱う予定です。