| Status | Authors | Coach | DRIs | Owning Stage | Created | 
|---|---|---|---|---|---|
| ongoing | @reprazent | @andrewn@stanhu | @m_gill@mksionek@marin | devops modelops | 2023-07-14 | 
AIゲートウェイ
要約
AI-gatewayは、セルフマネージド、専用、GitLab.comのどのインスタンスを使用していても、GitLabの全ユーザーにAI機能へのアクセスを提供するスタンドアロンサービスです。
当初、AI-gatewayのデプロイは全てGitLab(組織)が管理し、GitLab.comと全てのGitLabセルフマネージドインスタンスは同じゲートウェイを使用します。しかし、将来的には地域ごとのゲートウェイや、必要であれば顧客ごとのゲートウェイをデプロイすることも可能です。
AI-GatewayはAPI-Gatewayで、クライアント(この場合はGitLabインストール)からのトラフィックを受け取り、異なるサービス(この場合はAI-providerとそのモデル)に誘導します。このNorth/Southトラフィックパターンによって、どのリクエストをどこに送るかを制御し、必要に応じてリダイレクトされたリクエストの内容を翻訳することができます。
GitLabのコントロール下でホスティングされたサービスを使うことで、全てのGitLabインスタンスにスケーラブルにAI機能を提供することができます。この小さなステートレスサービスのスケーリングは、依存関係(データベース、Redis)を持つGitLab-Railsのスケーリングよりも簡単です。
これにより、セルフマネージドインストールのユーザーは、独自のモデルをホストしたり、サードパーティプロバイダに接続したりすることなく、AIを使った機能にアクセスできるようになります。
言語Python
AI-Gatewayはもともと、IDEからのコード提案のリクエストを処理する “model-gateway “としてスタートしました。Pythonで書かれています。
Pythonはオブジェクト指向言語であり、RubyistにとってはAI-gatewayという若いコードベースを通して十分な馴染みがあります。また、Pythonの経験があるデータエンジニアやMLエンジニアが貢献するのも簡単です。
API
AI-gatewayの安定した基本API
AI-gatewayのAPIは様々なGitLabインスタンスから利用されるため、安定しつつも柔軟なAPIを設計することが重要です。
そのために、構築するユースケースごとにAPIエンドポイントを実装します。つまり、GitLabとAI-gatewayの間のインターフェースは、私たちが構築し所有するものです。これにより、将来のスケーラビリティ、Composer、セキュリティが保証されます。
APIはバージョン管理されていませんが、後方互換性があります。詳細はバージョン間の互換性をご覧ください。AI-gatewayは最後の2つのメジャーバージョンをサポートします。例えば、GitLab 17.2で作業する場合、GitLab 17とGitLab 16の両方をサポートします。
GitLab-railsと同様に、レート制限、サーキットブレーカー、シークレットリダクションのような一般的な機能をこのレベルのスタックに追加することができます。
プロトコル
AI-gatewayサービスにはシンプルなJSON APIを使うことにしました。これによって、現在のmodel-gatewayにすでにあるものの多くを再利用することができます。また、エンドポイントのバージョンに依存しないようにすることもできます。動的な情報を含むことができる初歩的なエンベロープだけを期待するAPIを持つことができます。これらのAPIを複数のバージョンのGitLabや、GitLabを通してゲートウェイを使う他のクライアントと互換性があるようにしなければなりません。AI-gatewayはこれをサポートする必要がありますが、バージョンごとに異なるエンドポイントをサポートする必要はありません。
また、GitLabインスタンス間の通信プロトコルとしてgRPCも検討しましたが、これらの項目については異なっていました:
| gRPC | REST + JSON | 
|---|---|
| + バージョンレスで進化しやすい厳密なプロトコル定義 | - 厳密なスキーマがないため、実装は複数のバージョンをサポートするための十分な配慮が必要 | 
| + vscodeのための新しいRuby-gRPCサーバ:ロードする依存関係を制限できるので、おそらく高速になります(モジュラーモノリス)。 | - 既存のvscode用Grape API:起動時間が遅く、不要なリソースがロードされることを意味します。 | 
| + 双方向ストリーミング | - リクエストとレスポンスをストリーミングするストレートな方法(まだ追加可能) | 
| - 新しい Python-gRPC サーバ: gRPC-Python サーバを動かした経験がありません。 | + 既存の Python fastapi サーバー。 | 
| - GitLabを通してvscodeからai-gatewayに未知のメッセージを渡すのは難しい | + 古いGitLabインスタンスを通して、新しいvscode + 新しいai-gatewayをより簡単にサポート。 | 
| - 他のクライアント(vscode, jetbrains, 他のエディタ)でのgRPCの未知のサポート | + すべての外部クライアントでのサポート | 
| - プロトコル不一致の可能性 (VSCode –REST–> Rails –gRPC–> AI ゲートウェイ) | + スタック全体で同じプロトコル | 
議論すでに部分的に存在する機能を移植するために、この反復でREST+JSONを選択したからといって、gRPCやWebsocketを使用した新しい機能を除外する必要はありません。例えばチャット機能は、リクエストとレスポンスをストリーミングする方が良いかもしれません。私たちはユースケースごとにエンドポイントを提案しているので、バージョン間の互換性を念頭に置く限り、異なる機能は異なるプロトコルを選択することもできます。
単一目的のエンドポイント
AIを使用する機能については、ダイレクトプロキシとして公開するプロバイダAPIよりも、安定したAPIを持つ単一目的のエンドポイントを構築することを好みます。
特定のエンドポイントを持つ機能もあれば、エンドポイントを共有できる機能もあります。例えば、コード提案やチャットは独自のエンドポイントを持つことができ、イシューやマージリクエストを要約するいくつかの機能は同じエンドポイントを使用することができますが、ペイロードで提供される情報を区別することができます。
最終的なゴールは、AIゲートウェイに触れることなく機能を構築するためのAIを公開するAPIを構築することです。これは、私たちがGitalyを構築した方法に似ています。Gitalyに必要な機能を追加し、可能であれば既存のエンドポイントを再利用しました。新しいエンドポイント((RPC))を実装する必要がある場合、私たちは初期費用としていくらか負担しなければなりませんでしたが、必要な機能のほとんどが実装されれば、長期的には報われることになります。
これは、プロンプトをAIゲートウェイ内に構築する必要があることを意味するものではありません。しかし、プロンプトが単一目的のエンドポイントへのペイロードの一部である場合、ペイロードはプロンプトに関する他のメタデータとともに、プロンプトがどのモデルのためにビルドされたかを指定する必要があります。こうすることで、プロンプトのペイロードの1つがAIゲートウェイによってサポートされなくなった場合に、リクエストを優雅にデグレードしたり、その他の方法でサポートしようとしたりすることができます。これによって、AIの状況が変化したときに、古いGitLabのインストールで機能が壊れるのを避けることができます。
クロスバージョンの互換性
単一の目的のエンドポイントを構築する際には、これらのエンドポイントが間接的に外部のクライアントによって異なる GitLab インスタンスで使われることを念頭に置く必要があります。これを実現するために、情報を提供するための非常にシンプルなエンベロープを用意しました。AI-gatewayがプロンプトを作ったり、選択したモデルにクエリをかけたりするために使える情報を含む一連のprompt_components 。
各プロンプトコンポーネントは3つの要素を含んでいます:
- 
type:これは、payloadで提示される情報の種類です。AI-gatewayは知らないタイプを無視する必要があります。
- 
payload:AI-gatewayがAIプロバイダーに送信するペイロードを構築するために使用できる実際の情報。ペイロードはタイプやペイロードを提供するクライアントのバージョンによって異なります。つまり、AI-gatewayはすべてのフィールドをオプションとみなす必要があります。
- 
metadata:プロンプトのこの部分をビルドしたクライアントに関する情報。これはGitLabによって遠隔測定に使われるかもしれませんし、使われないかもしれません。このフィールドは必須ではありません。
この中で頻繁に変更される可能性があるのはpayload の部分だけです。そこでは、すべてのフィールドがオプションであることを確認し、フィールド名の変更や削除、再利用を避ける必要があります。
これが必要なときは、古いバージョンのフィールドのサポートをゲートウェイに構築し、GitLabの少なくとも2つのメジャーバージョンに対応できるようにする必要があります。例えば、prompt_components のペイロードに2つのバージョンのプロンプトを追加することが考えられます:
{
  prompt_components: [
    {
      "type": "prompt",
      "metadata": {
        "source": "GitLab EE",
        "version": "16.3",
      },
      "payload": {
        "content": "You can fetch information about a resource called an issue...",
        "params": {
          "temperature": 0.2,
          "maxOutputTokens": 1024
        },
        "model": "text-bison",
        "provider": "vertex-ai"
      }
    },
    {
      "type": "prompt",
      "metadata": {
        "source": "GitLab EE",
        "version": "16.3",
      },
      "payload": {
        "content": "System: You can fetch information about a resource called an issue...\n\nHuman:",
        "params": {
          "temperature": 0.2,
        },
        "model": "claude-2",
        "provider": "anthropic"
      }
    }
  ]
}
APIがペイロードの内容に基づいて、プロンプトをどちらのプロバイダにも向けることができるようにします。
payload の内容を文書化して検証するには、JSON-schemaを使用してその形式を指定します。
機能例: コード提案
例えば、大まかなコード提案サービスは次のようになります:
POST /internal/code-suggestions/completions
{
  "prompt_components": [
    {
      "type": "prompt",
      "metadata": {
        "source": "GitLab EE",
        "version": "16.3",
      },
      "payload": {
        "content": "...",
        "params": {
          "temperature": 0.2,
          "maxOutputTokens": 1024
        },
        "model": "code-gecko",
        "provider": "vertex-ai"
      }
    },
    {
      "type": "editor_content",
      "metadata": {
        "source": "vscode",
        "version": "1.1.1"
      },
       "payload": {
        "filename": "application.rb",
        "before_cursor": "require 'active_record/railtie'",
        "after_cursor": "\nrequire 'action_controller/railtie'",
        "open_files": [
          {
            "filename": "app/controllers/application_controller.rb",
            "content": "class ApplicationController < ActionController::Base..."
          }
        ]
      }
    }
  ]
}
レスポンスは次のようになります:
{
  "response": "require 'something/else'",
  "metadata": {
    "identifier": "deadbeef",
    "model": "code-gecko",
    "timestamp": 1688118443
  }
}
metadata フィールドには、AIゲートウェイのテレメトリーエンドポイントで使用できる情報が含まれています。
コード提案のためのテレメトリを受け取る方法は#415745で議論されています。私たちはすべてのAI関連機能のアーキテクチャを考え出すよう努力します。
AIプロバイダーの公開
GitLab-Railsにはすでに多くのAI機能が組み込まれており、現在プロンプトをビルドしてさまざまなAIプロバイダに直接送信しています。この記事を書いている時点で、GitLabは以下のプロバイダのAPIクライアントを持っています:
これらの機能をセルフマネージド・インスタンスでも使えるようにするには、GitLab.comやセルフマネージド・インスタンス、あるいは専用インストールがこれらの機能を顧客に提供できるように、それぞれのエンドポイントを提供する必要があります。
最初の反復では、リクエストをAIプロバイダーにプロキシするエンドポイントを構築することができます。これにより、AI-Gatewayを介したリクエストのルーティングへのマイグレーションが容易になるはずです。例として、Anthropicのエンドポイントは次のようになります:
POST /internal/proxy/anthropic/(*endpoint)
*endpoint は、例えば/v1/completeのように、クライアントが何を呼び出すかを指定することを意味します。リクエストボディはすべてAIプロバイダーに転送されます。AI-gatewayはリクエストが正しく認証されていることを確認します。
GitLabとAIプロバイダーの間にプロキシを置くことで、AIプロバイダーへのリクエストをコントロールすることができます。To-Doは、プロバイダのAPIが変更されたり、特定のプロバイダと連携しないことになったとしても、古いGitLabインストールの機能をサポートし続けることができることを意味します。
APIプロバイダを使う機能を、その機能専用のAPIに直接移行することには価値があると思います。そうすることで、AIプロバイダーが進化しても、私たちの管理下にあるAIゲートウェイを変更することで、これらの機能を改善することができます。セルフマネージメントや専用インストールを使用している顧客は、GitLabインスタンスをアップグレードすることなく、より良いAIサポート機能を得ることができます。
現在実験的な機能はこれらの汎用APIを使うことができますが、セルフマネージド・インストールで一般的に使えるようにする前に、単一の目的のAPIエンドポイントに変換することを目指すべきです。そうすることで、AIプロバイダーの状況が変化しても、機能を長期的にサポートしやすくなります。
GitLabのチームメンバーが利用できるExperimental REST APIも、短期的にはこのプロキシを使うべきです。長期的には、開発者がGitLab所有の認証を使って複数のAIプロバイダで実験できるように、別のプロキシにアクセスできるようにすべきです。こうすることで、新しいことを試している開発者のトラフィックと、有料顧客にサービスを提供しているフリートとを分離することができます。
GitLabインスタンスのAPI
これは外部クライアントがローカルのGitLabインスタンスで利用できるAPIです。例えば、セルフマネージドインスタンスと対話するVSCodeなど。
VSCodeエクステンションは開発者によって最新に保たれているかもしれません。しかし、彼らが仕事で使っているGitLabインスタンスはマイナーバージョン遅れています。安定性と柔軟性という点では、AIゲートウェイと同じ要件がクライアントにも当てはまります。
最初のイテレーションでは、VSCodeエクステンションとWeb-IDEが送信する現在のRESTペイロードを維持しつつ、それを適切なGitLabインストールに向けることが考えられます。GitLab-railsはペイロードを解釈することなくAI-gateway用のエンベロープにラップすることができます。
そうすることで、エクステンションからリクエストを受け取ったGitLabインスタンスは、それをエンリッチしてAI-Gatewayに渡すためにそれを理解する必要がなくなります。GitLabはprompt_components に情報を追加することができ、すでにそこにあったものすべてをそのままAI-gatewayに渡すことができます。
リクエストが他のクライアント(例えばVSCode)から開始された場合、GitLab-railsは他の拡張やプロンプトに加えてペイロード全体を転送する必要があります。これは、古いGitLabインストールを経由して最近のAI-gatewayに移動する、新しいバージョンのクライアントからの変更に対応できるようにするために必要です。
議論この最初のイテレーションは、REST+JSONのアプローチも使っています。これは、現在VSCodeエクステンションがモデルゲートウェイと通信している方法です。これは、既存のペイロードをエンベロープにラップするための、より小さな反復であることを意味します。バージョン間の互換性という利点もあります。しかし、将来の反復でもREST+JSONを使用する必要があるということではありません。各機能は独自のエンドポイントを持つため、プロトコルも異なる可能性があります。
認証と作成者
GitLabは認証の最初のレイヤーを提供します:ユーザーを認証し、ユーザーが使おうとしている機能をライセンスが許可しているかどうかをチェックします。これはGitLabにすでに組み込まれている認証とライセンスチェックを使って行うことができます。
AI-gateway上でのGitLabインスタンスの認証については#177で説明します。AI-gatewayはAIプロバイダーにプロキシされたエンドポイントを公開するので、認証トークンの有効期限を限定することが重要です。
埋め込み
エンベッディングは、例えば次のようなリクエストを通して、単一のエンドポイントですべてのフィーチャーに対してリクエストすることができます:
POST /internal/embeddings
{
  "content": "The lazy fox and the jumping dog",
  "content_type": "issue_title",
  "metadata": {
    "source": "GitLab EE",
    "version": "16.3"
  }
}
content_type とcontent のプロパティは、将来、適切なものに基づいて異なるモデルからエンベッディングを作成するために使用されるかもしれません。
レスポンスには、使用されるプロバイダとモデルの他に、埋め込みベクトルが含まれます。例えば
{
  "response": [0.2, -1, ...],
  "metadata": {
    "identifier": "8badf00d",
    "model": "text-embedding-ada-002",
    "provider": "open_ai",
  }
}
エンベッディングを保存する際には、モデルとプロバイダのデータを含めるようにします。エンベッディングがプロンプトを生成するために使用される場合、我々はエンベッディングの品質を判断することができるように、ペイロードにそのメタデータを含めることができます。
デプロイ
現在、AIゲートウェイとなるモデルゲートウェイは、gitlab-org/modelops/applied-ml/code-suggestions/ai-assistのプロジェクトリポジトリからデプロイされています。
Kubernetesクラスターに独自のプロジェクトでデプロイされています。現在、エンジニアが直接テストに使用しているステージング環境があります。
将来的にはRunwayを使ってデプロイされる予定です。その際、本番環境とステージング環境のデプロイが存在することになります。ステージングデプロイは、デプロイが本番環境に到達するのを阻止する可能性のある自動 QA 実行に使用できます。
さらなるテスト戦略については、&10563で説明します。
代替ソリューション
applied-ml/code-suggestions/ai-assist#161で代替案が議論されています。
 
