アーキテクチャ

アーキテクチャ

CC-Relayは、LLMアプリケーション向けに設計された高性能なマルチプロバイダーHTTPプロキシです。インテリジェントなルーティング、思考シグネチャのキャッシュ、プロバイダー間のシームレスなフェイルオーバーを提供します。

システム概要

  graph TB
    subgraph "クライアント層"
        A[Claude Code]
        B[カスタムLLMクライアント]
    end

    subgraph "CC-Relay プロキシ"
        D[HTTPサーバー<br/>:8787]
        E[ミドルウェアスタック]
        F[ハンドラー]
        G[ルーター]
        H[シグネチャキャッシュ]
    end

    subgraph "プロバイダープロキシ"
        I[ProviderProxy<br/>Anthropic]
        J[ProviderProxy<br/>Z.AI]
        K[ProviderProxy<br/>Ollama]
    end

    subgraph "バックエンドプロバイダー"
        L[Anthropic API]
        M[Z.AI API]
        N[Ollama API]
    end

    A --> D
    B --> D
    D --> E
    E --> F
    F --> G
    F <--> H
    G --> I
    G --> J
    G --> K
    I --> L
    J --> M
    K --> N

    style A fill:#6366f1,stroke:#4f46e5,color:#fff
    style D fill:#ec4899,stroke:#db2777,color:#fff
    style F fill:#f59e0b,stroke:#d97706,color:#000
    style G fill:#10b981,stroke:#059669,color:#fff
    style H fill:#8b5cf6,stroke:#7c3aed,color:#fff

コアコンポーネント

1. ハンドラー

場所: internal/proxy/handler.go

ハンドラーはリクエスト処理の中央コーディネーターです:

type Handler struct {
    providerProxies map[string]*ProviderProxy  // プロバイダーごとのリバースプロキシ
    defaultProvider providers.Provider          // シングルプロバイダーモードのフォールバック
    router          router.ProviderRouter       // ルーティング戦略の実装
    healthTracker   *health.Tracker             // サーキットブレーカートラッキング
    signatureCache  *SignatureCache             // 思考シグネチャキャッシュ
    routingConfig   *config.RoutingConfig       // モデルベースルーティング設定
    providers       []router.ProviderInfo       // 利用可能なプロバイダー
}

責務:

  • リクエストボディからモデル名を抽出
  • プロバイダーアフィニティのための思考シグネチャを検出
  • ルーター経由でプロバイダーを選択
  • 適切なProviderProxyに委譲
  • 思考ブロックを処理しシグネチャをキャッシュ

2. ProviderProxy

場所: internal/proxy/provider_proxy.go

各プロバイダーは、事前設定されたURLと認証を持つ専用のリバースプロキシを取得します:

type ProviderProxy struct {
    Provider           providers.Provider
    Proxy              *httputil.ReverseProxy
    KeyPool            *keypool.KeyPool  // マルチキーローテーション用
    APIKey             string            // フォールバック単一キー
    targetURL          *url.URL          // プロバイダーのベースURL
    modifyResponseHook ModifyResponseFunc
}

主な機能:

  • URL解析は初期化時に一度だけ実行(リクエストごとではない)
  • 透過認証(クライアント資格情報の転送)または設定された認証をサポート
  • ストリーミングレスポンス用のSSEヘッダー自動注入
  • レート制限分散のためのキープール統合

3. ルーター

場所: internal/router/

ルーターは各リクエストを処理するプロバイダーを選択します:

戦略説明
failover自動リトライ付きの優先度ベース(デフォルト)
round_robin順次ローテーション
weighted_round_robin重み付けに基づく比例配分
shuffle公平なランダム分散
model_basedモデル名プレフィックスによるルーティング

4. シグネチャキャッシュ

場所: internal/proxy/signature_cache.go

クロスプロバイダー互換性のために思考ブロックシグネチャをキャッシュします:

type SignatureCache struct {
    cache cache.Cache  // Ristrettoベースのキャッシュ
}

// キャッシュキーフォーマット: "sig:{modelGroup}:{textHash}"
// TTL: 3時間(Claude APIに合わせて)

リクエストフロー

マルチプロバイダールーティング

  sequenceDiagram
    participant Client as クライアント
    participant Handler as ハンドラー
    participant Router as ルーター
    participant ModelFilter as モデルフィルター
    participant ProviderProxy
    participant Backend as バックエンド

    Client->>Handler: HTTPリクエスト(modelフィールド付き)
    Handler->>Handler: ボディからモデルを抽出
    Handler->>Handler: 思考シグネチャの存在を確認
    Handler->>ModelFilter: FilterProvidersByModel(model, providers, mapping)
    ModelFilter->>ModelFilter: modelMappingに対する最長プレフィックスマッチ
    ModelFilter->>Router: フィルタリングされたプロバイダーリストを返す
    Handler->>Router: プロバイダーを選択(フィルタリングされたリストでfailover/round-robin)
    Router->>Router: フィルタリングされたプロバイダーにルーティング戦略を適用
    Router->>Handler: 選択されたProviderInfoを返す

    Handler->>Handler: 選択されたプロバイダーのProviderProxyを取得
    Handler->>ProviderProxy: auth/ヘッダー付きでリクエストを準備
    ProviderProxy->>ProviderProxy: 透過vs設定済み認証モードを決定
    ProviderProxy->>Backend: プロバイダーのターゲットURLにリクエストを転送
    Backend->>ProviderProxy: レスポンス(シグネチャヘッダー付き)
    ProviderProxy->>Handler: シグネチャ情報付きレスポンス
    Handler->>Handler: 思考が存在する場合はシグネチャをキャッシュ
    Handler->>Client: レスポンス

思考シグネチャ処理

拡張思考が有効な場合、プロバイダーは署名された思考ブロックを返します。これらのシグネチャは後続のターンで同じプロバイダーによって検証される必要があります。CC-Relayはキャッシュによってクロスプロバイダーシグネチャの問題を解決します:

  sequenceDiagram
    participant Request as リクエスト
    participant Handler as ハンドラー
    participant SignatureCache as シグネチャキャッシュ
    participant Backend as バックエンド
    participant ResponseStream as レスポンスストリーム (SSE)

    Request->>Handler: 思考ブロック付きHTTP
    Handler->>Handler: HasThinkingSignatureチェック
    Handler->>Handler: ProcessRequestThinking
    Handler->>SignatureCache: Get(modelGroup, thinkingText)
    SignatureCache-->>Handler: キャッシュされたシグネチャまたは空
    Handler->>Handler: 未署名ブロックを削除 / キャッシュされたシグネチャを適用
    Handler->>Backend: クリーンなリクエストを転送

    Backend->>ResponseStream: ストリーミングレスポンス(thinking_deltaイベント)
    ResponseStream->>Handler: thinking_deltaイベント
    Handler->>Handler: 思考テキストを蓄積
    ResponseStream->>Handler: signature_deltaイベント
    Handler->>SignatureCache: Set(modelGroup, thinking_text, signature)
    SignatureCache-->>Handler: キャッシュ完了
    Handler->>ResponseStream: modelGroupプレフィックスでシグネチャを変換
    ResponseStream->>Request: プレフィックス付きシグネチャを含むSSEイベントを返す

シグネチャ共有のためのモデルグループ:

モデルパターングループシグネチャ共有
claude-*claudeはい、すべてのClaudeモデル間で
gpt-*gptはい、すべてのGPTモデル間で
gemini-*geminiはい、センチネル値を使用
その他正確な名前共有なし

SSEストリーミングフロー

  sequenceDiagram
    participant Client as クライアント
    participant Proxy as プロキシ
    participant Provider as プロバイダー

    Client->>Proxy: POST /v1/messages (stream=true)
    Proxy->>Provider: リクエストを転送

    Provider-->>Proxy: event: message_start
    Proxy-->>Client: event: message_start

    Provider-->>Proxy: event: content_block_start
    Proxy-->>Client: event: content_block_start

    loop コンテンツストリーミング
        Provider-->>Proxy: event: content_block_delta
        Proxy-->>Client: event: content_block_delta
    end

    Provider-->>Proxy: event: content_block_stop
    Proxy-->>Client: event: content_block_stop

    Provider-->>Proxy: event: message_delta
    Proxy-->>Client: event: message_delta

    Provider-->>Proxy: event: message_stop
    Proxy-->>Client: event: message_stop

必須SSEヘッダー:

Content-Type: text/event-stream
Cache-Control: no-cache, no-transform
X-Accel-Buffering: no
Connection: keep-alive

ミドルウェアスタック

場所: internal/proxy/middleware.go

ミドルウェア目的
RequestIDMiddlewareトレーシング用のX-Request-IDを生成/抽出
LoggingMiddlewareタイミング付きでリクエスト/レスポンスをログ
AuthMiddlewarex-api-keyヘッダーを検証
MultiAuthMiddlewareAPIキーとBearerトークン認証をサポート

プロバイダーインターフェース

場所: internal/providers/provider.go

type Provider interface {
    Name() string
    BaseURL() string
    Owner() string
    Authenticate(req *http.Request, key string) error
    ForwardHeaders(originalHeaders http.Header) http.Header
    SupportsStreaming() bool
    SupportsTransparentAuth() bool
    ListModels() []Model
    GetModelMapping() map[string]string
    MapModel(requestModel string) string
}

実装されたプロバイダー:

プロバイダータイプ機能
AnthropicProvideranthropicネイティブフォーマット、フル機能サポート
ZAIProviderzaiAnthropic互換、GLMモデル
OllamaProviderollamaローカルモデル、プロンプトキャッシュなし

認証モード

透過認証

クライアントが資格情報を提供し、プロバイダーがサポートしている場合:

  • クライアントのAuthorizationまたはx-api-keyヘッダーがそのまま転送される
  • CC-Relayは純粋なプロキシとして動作

設定済み認証

CC-Relayの管理キーを使用する場合:

  • クライアント資格情報が削除される
  • CC-Relayが設定されたAPIキーを注入
  • レート制限分散のためのキープールローテーションをサポート
  graph TD
    A[リクエスト到着] --> B{クライアント認証あり?}
    B -->|はい| C{プロバイダーが<br/>透過認証をサポート?}
    B -->|いいえ| D[設定済みキーを使用]
    C -->|はい| E[クライアント認証を転送]
    C -->|いいえ| D
    D --> F{キープール利用可能?}
    F -->|はい| G[プールからキーを選択]
    F -->|いいえ| H[単一APIキーを使用]
    E --> I[プロバイダーに転送]
    G --> I
    H --> I

ヘルストラッキング & サーキットブレーカー

場所: internal/health/

CC-Relayはプロバイダーの健全性を追跡し、サーキットブレーカーパターンを実装します:

状態動作
CLOSED通常動作、リクエストは流れる
OPENプロバイダーが不健全とマーク、リクエストは即座に失敗
HALF-OPENクールダウン後に限定的なリクエストでプローブ

OPEN状態のトリガー:

  • HTTP 429(レート制限)
  • HTTP 5xx(サーバーエラー)
  • 接続タイムアウト
  • 連続失敗がしきい値を超過

ディレクトリ構造

cc-relay/
├── cmd/cc-relay/           # CLIエントリポイント
│   ├── main.go             # ルートコマンド
│   ├── serve.go            # サーブコマンド
│   └── di/                 # 依存性注入
│       └── providers.go    # サービスワイヤリング
├── internal/
│   ├── config/             # 設定の読み込み
│   ├── providers/          # プロバイダー実装
│   │   ├── provider.go     # プロバイダーインターフェース
│   │   ├── base.go         # ベースプロバイダー
│   │   ├── anthropic.go    # Anthropicプロバイダー
│   │   ├── zai.go          # Z.AIプロバイダー
│   │   └── ollama.go       # Ollamaプロバイダー
│   ├── proxy/              # HTTPプロキシサーバー
│   │   ├── handler.go      # メインリクエストハンドラー
│   │   ├── provider_proxy.go # プロバイダーごとのプロキシ
│   │   ├── thinking.go     # 思考ブロック処理
│   │   ├── signature_cache.go # シグネチャキャッシュ
│   │   ├── sse.go          # SSEユーティリティ
│   │   └── middleware.go   # ミドルウェアチェーン
│   ├── router/             # ルーティング戦略
│   │   ├── router.go       # ルーターインターフェース
│   │   ├── failover.go     # フェイルオーバー戦略
│   │   ├── round_robin.go  # ラウンドロビン戦略
│   │   └── model_filter.go # モデルベースフィルタリング
│   ├── health/             # ヘルストラッキング
│   │   └── tracker.go      # サーキットブレーカー
│   ├── keypool/            # APIキープーリング
│   │   └── keypool.go      # キーローテーション
│   └── cache/              # キャッシュレイヤー
│       └── cache.go        # Ristrettoラッパー
└── docs-site/              # ドキュメント

パフォーマンスの考慮事項

接続ハンドリング

  • コネクションプーリング: バックエンドへのHTTP接続を再利用
  • HTTP/2サポート: サポートされている場所での多重化リクエスト
  • 即時フラッシュ: SSEイベントはバッファリングなしでフラッシュ

並行性

  • リクエストごとのゴルーチン: 軽量なGoの並行性
  • コンテキスト伝播: 適切なタイムアウトとキャンセル
  • スレッドセーフなキャッシュ: Ristrettoが並行アクセスを提供

メモリ

  • ストリーミングレスポンス: レスポンスボディのバッファリングなし
  • シグネチャキャッシュ: LRU削除による制限されたサイズ
  • リクエストボディの復元: 効率的なボディの再読み込み

次のステップ