サードパーティとのインテグレーションに基づくAI機能
GitLab 15.11 で導入。
機能
- 長時間実行されるAPIリクエストの非同期実行
- GraphQLアクションがリクエストを開始
- バックグラウンドワーカーの実行
- GraphQLサブスクリプションはリアルタイムで結果を返します。
- 抽象化
- OpenAI
- グーグル バーテックスAI
- アンソロピック
- 速度制限
- サーキットブレーカー
- マルチレベル機能フラグ
- グループレベルでのライセンスチェック
- 除雪車の実行追跡
- Prometheus で使用されたトークンのトラッキング
- 入力のモデレーションチェック設定
- 応答の自動マークダウンレンダリング
- 実験およびサードパーティのグループレベルの集中設定
- GitLabチームメンバーが認証情報なしでAI APIを探索できる実験用APIエンドポイント
- OpenAI
- グーグル バーテックスAI
- アンソロピック
フィーチャーフラグ
以下の2つの機能フラグを任意のAIフィーチャーワークに適用します:
- すべてのAI機能に適用される一般的なもの。
- その機能に固有のフラグ。機能フラグ名は、ライセンスされた機能名とは異なる必要があります。
すべての機能フラグの一覧と使用方法については、機能フラグトラッカーを参照してください。
新しいAIアクションの実装
新しいAIアクションを実装するには、優先AIプロバイダに接続します。このAPIへの接続には
- Experimental REST API。
- 抽象化レイヤー。
すべてのAI機能は実験的なものです。
ローカルでAI機能をテスト
-
必要な一般機能フラグを有効にします:
Feature.enable(:ai_related_settings) Feature.enable(:openai_experimentation) Feature.enable(:tofa_experimentation_main_flag) Feature.enable(:anthropic_experimentation)
- SaaSをシミュレートするためにGDKをシミュレートし、テストするグループがUltimateライセンスを持っていることを確認します。
-
Experimental features
を有効にしThird-party AI services
- Ultimateライセンスを持つグループに移動します。
- グループ設定>一般->権限とグループ機能
- 実験機能の有効化
- サードパーティAIサービスの有効化
- テストしたい機能の機能フラグを有効にします。
- 必要なアクセストークンを設定します。アクセストークンを受け取るには
- Vertexの場合は、以下の手順に従ってください。
- AnthropicやOpenAIのような他のプロバイダーについては、
@m_gill
、@wayne
、@timzallmann
が技術スタックのオーナーであるアクセスリクエストを作成してください。
埋め込みデータベースのセットアップ
埋め込みデータベースを使用する機能については、追加のセットアップが必要です。
- GDKでpgvectorを有効化
-
GDKで埋め込みデータベースを有効化
gdk config set gitlab.rails.databases.embedding.enabled true
- 走る
gdk reconfigure
- データベースマイグレーションを実行し、埋め込みデータベースを作成します。
GitLab ドキュメンテーションチャット(レガシーチャット)のセットアップ
GitLabチャットの埋め込みデータベースを設定します:
- Rails コンソールを開きます。
- このスクリプトを実行して、埋め込みデータベースにデータを投入します。
GCP Vertexアクセスの設定
ローカル開発用のGCPサービスキーを取得するには、以下の手順に従ってください:
- このページにアクセスし、指示に従ってサンドボックスGCP環境を作成するか、このテンプレートを使用して既存のグループ環境へのアクセスをリクエストしてください。
- GCPコンソールで、
IAM & Admin
>Service Accounts
、”Create new service account “ボタンをクリックします。 - サービスアカウントには、使用目的に応じた名前を付けます。Create and Continue」を選択します。
Grant this service account access to project
の下で、ロールVertex AI User
を選択します。Continue
を選択します。Done
- 新しいサービスアカウントを選択し、
Manage keys
>Add Key
>Create new key
。これにより、サービスアカウントの非公開JSON認証情報がダウンロードされます。 - Railsコンソールを開きます。設定を次のように更新します:
Gitlab::CurrentSettings.update(vertex_ai_credentials: File.read('/YOUR_FILE.json'))
# Note: These credential examples will not work locally for all models
Gitlab::CurrentSettings.update(vertex_ai_host: "<root-domain>") # Example: us-central1-aiplatform.googleapis.com
Gitlab::CurrentSettings.update(vertex_ai_project: "<project-id>") # Example: cloud-large-language-models
内部チームのメンバーは、これらのエンドポイントの設定にこのスニペットを使用できます。
OpenAI アクセスの設定
Gitlab::CurrentSettings.update(openai_api_key: "<open-ai-key>")
Anthropicのアクセス設定
Feature.enable(:anthropic_experimentation)
Gitlab::CurrentSettings.update!(anthropic_api_key: <insert API key>)
埋め込みフィクスチャの作成と使用
GitLab Documentation用のembeddingsを開発者用データベースに投入するには、事前に生成されたembeddingsとRakeテストを使うことができます。
RAILS_ENV=development bundle exec rake gitlab:llm:embeddings:seed_pre_generated
私たちが使っている DBCleaner gem は、テストを実行する前にデータベースのテーブルをクリアします。ドキュメントの埋め込みを格納するテーブルtanuki_bot_mvc
に完全に埋め込む代わりに、事前に生成されたフィクスチャから選択した埋め込みをテーブルに追加することができます。
例えば、”How can I reset my password “という質問が、関連する埋め込みを正しく取得し、回答しているかテストするために、質問に対する上位N個の埋め込みをフィクスチャに抽出し、少数の埋め込みだけを素早くリストアすることができます。抽出処理を容易にするために、Rakeタスクが書かれています。新しいフィクスチャを生成するためにRakeタスクを実行します。
RAILS_ENV=development bundle exec rake gitlab:llm:embeddings:extract_embeddings
埋め込みデータを使用する必要がある仕様では、RSpecの設定フック:ai_embedding_fixtures
。
context 'when asking about how to use GitLab', :ai_embedding_fixtures do
# ...examples
end
GitLab Duo チャットでの作業
GitLab Duo Chatで作業するためのガイドラインをご覧ください。
実験的 REST API
実験的な REST API エンドポイントを使用して、AI 機能の実験やプロトタイプ作成をすばやく実行できます。
エンドポイントは以下の通りです:
https://gitlab.example.com/api/v4/ai/experimentation/openai/completions
https://gitlab.example.com/api/v4/ai/experimentation/openai/embeddings
https://gitlab.example.com/api/v4/ai/experimentation/openai/chat/completions
https://gitlab.example.com/api/v4/ai/experimentation/anthropic/complete
https://gitlab.example.com/api/v4/ai/experimentation/tofa/chat
これらのエンドポイントはプロトタイプ用であり、顧客に機能を提供するためのものではありません。実験的なエンドポイントは、GitLabチームメンバーの本番環境でのみ利用可能です。GitLab API トークンを使って認証してください。
抽象化レイヤー
GraphQL API
抽象化レイヤーを使用してAIプロバイダーAPIに接続するには、aiAction
と呼ばれる拡張可能なGraphQL APIを使用します。input
はキーと値のペアを受け付けます。key
は実行する必要があるアクションです。変異リクエストごとに1つのAIアクションしか許可しません。
突然変異の例
mutation {
aiAction(input: {summarizeComments: {resourceId: "gid://gitlab/Issue/52"}}) {
clientMutationId
}
}
例として、「コードを説明する」アクションを作りたいとします。そのために、input
を新しいキーexplainCode
で拡張します。変異は次のようになります:
mutation {
aiAction(input: {explainCode: {resourceId: "gid://gitlab/MergeRequest/52", code: "foo() { console.log()" }}) {
clientMutationId
}
}
GraphQL API はOpenAI Clientを使用してレスポンスを送信します。
他のクライアントも利用可能であり、OpenAIを使用すべきではないことを覚えておいてください。
応答の受け取り方
OpenAI API のリクエストはバックグラウンドジョブで処理されるため、リクエストは保持されず、レスポンスはaiCompletionResponse
のサブスクリプションを通じて送信されます:
subscription aiCompletionResponse($userId: UserID, $resourceId: AiModelID!) {
aiCompletionResponse(userId: $userId, resourceId: $resourceId) {
responseBody
errors
}
}
現在の抽象化レイヤーの流れ
以下のグラフはOpenAIを例にしています。別のプロバイダを使用することもできます。
サーキットブレーカー
CircuitBreaker 関数は、サーキットブレーカー保護機能を持つコードを実行する必要があるクラスに含めることができる再利用可能なモジュールです。このコンサーンは、サーキットブレーカー機能を持つコードブロックをラップするrun_with_circuit
メソッドを提供し、カスケード障害を防ぎ、システムの回復力を向上させます。サーキット・ブレーカー・パターンの詳細については、以下を参照してください:
CircuitBreakerの使用
CircuitBreakerを使用するには、クラスに含める必要があります。例えば
class MyService
include Gitlab::Llm::Concerns::CircuitBreaker
def call_external_service
run_with_circuit do
# Code that interacts with external service goes here
raise InternalServerError
end
end
end
call_external_service
メソッドは、外部サービスと対話するメソッドの例です。外部サービスと対話するコードをrun_with_circuit
でラップすることで、このメソッドはサーキットブレーカ内で実行されます。サーキット・ブレーカは、circuit
メソッドによって作成および設定されます。このメソッドは、CircuitBreaker
モジュールがインクルードされると自動的に呼び出されます。このメソッドは、InternalServerError
エラーを発生させる必要があります。このエラーは、コード・ブロックの実行中に発生した場合、エラーしきい値にカウントされます。
サーキットブレーカはエラーの数とリクエストのレートを追跡し、設定されたエラーのしきい値またはボリュームのしきい値に達すると回路を開きます。回路が開かれている場合、後続のリクエストはコードブロックを実行することなく高速に失敗します。サーキットブレーカは回路を再び閉じる前に、サービスの可用性をテストするために定期的に少数のリクエストを通過させます。
設定
サーキットブレーカは、回路が開くエラー数と要求数を制御する2つの定数で設定されています:
ERROR_THRESHOLD
VOLUME_THRESHOLD
これらの値は、特定のサービスや使用パターンに応じて調整することができます。InternalServerError
は、コードブロックの実行中に発生した場合にエラーしきい値にカウントされる例外クラスです。これは、外部サービスとやりとりするコードによって発生したときにサーキットブレーカーをトリガーする例外クラスです。
CircuitBreaker
モジュールは、サーキットブレーカの実装を提供するためにCircuitbox
gem に依存しています。デフォルトでは、サービス名は懸念モジュールが含まれているクラス名から推測されます。異なる名前にする必要がある場合は、service_name
メソッドをオーバーライドしてください。テスト
CircuitBreaker
を使用するコードをテストするには、RSpec
の共有サンプルを使用し、service
とsubject
の変数を渡します:
it_behaves_like 'has circuit breaker' do
let(:service) { dummy_class.new }
let(:subject) { service.dummy_method }
end
新しいアクションの実装方法
新しいメソッドの登録
Llm::ExecuteMethodService
にアクセスし、これから作成する新しいサービスクラスで新しいメソッドを追加します。
class ExecuteMethodService < BaseService
METHODS = {
# ...
amazing_new_ai_feature: Llm::AmazingNewAiFeatureService
}.freeze
サービスの作成
-
ee/app/services/llm/
の下に新しいサービスを作成し、BaseService
から継承します。 -
resource
は、操作したいオブジェクトです。Ai::Model
を含むオブジェクトであれば何でもかまいません。例えば、Project
、MergeRequest
、Issue
などです。
# ee/app/services/llm/amazing_new_ai_feature_service.rb
module Llm
class AmazingNewAiFeatureService < BaseService
private
def perform
::Llm::CompletionWorker.perform_async(user.id, resource.id, resource.class.name, :amazing_new_ai_feature)
success
end
def valid?
super && Ability.allowed?(user, :amazing_new_ai_feature, resource)
end
end
end
作成者
機能の作成者は、ポリシーを使用して認可を行うことをお勧めします。現在のところ、以下のチェックをカバーする必要があります:
- 一般的なAI機能フラグが有効であること。
- 機能別フラグが有効
- ネームスペースに、その機能に必要なライセンスがあります。
- ユーザがグループ/プロジェクトのメンバであること
-
experiment_features_enabled
のメンバーであり、third_party_ai_features_enabled
フラグがNamespace
この例では、allowed?(:amazing_new_ai_feature)
コールを実装する必要があります。例として、コメント要約機能のイシュー・ポリシーを参照してください。この例では、イシューにもこの機能を実装します:
# ee/app/policies/ee/issue_policy.rb
module EE
module IssuePolicy
extend ActiveSupport::Concern
prepended do
with_scope :subject
condition(:ai_available) do
::Feature.enabled?(:openai_experimentation)
end
with_scope :subject
condition(:amazing_new_ai_feature_enabled) do
::Feature.enabled?(:amazing_new_ai_feature, subject_container) &&
subject_container.licensed_feature_available?(:amazing_new_ai_feature)
end
rule do
ai_available & amazing_new_ai_feature_enabled & is_project_member
end.enable :amazing_new_ai_feature
end
end
end
リクエストとレスポンスのペアリング
複数のユーザーのリクエストを並行して処理することができるため、レスポンスを受け取るときに、レスポンスとその元のリクエストをペアリングするのが難しい場合があります。requestId
リクエストとレスポンスは同じ requestId
UUIDを持つことがrequestId
保証されているので requestId
、このrequestId
フィールドはこの目的のために使う requestId
ことができます。
キャッシュ
AIのリクエストとレスポンスはキャッシュすることができます。キャッシュされた会話は、AI機能とユーザーとの対話を表示するために使用されます。現在の実装では、このキャッシュは、ユーザーがリクエストを繰り返したときにAIサービスへの連続コールをスキップするためには使用されません。
query {
aiMessages {
nodes {
id
requestId
content
role
errors
timestamp
}
}
}
このキャッシュは、チャット機能で特に役立ちます。その他のサービスでは、キャッシュは無効になっています。(cache_response: true
オプションを使用することで、サービスに対して有効にすることができます)。
キャッシュには以下の制限があります:
- メッセージは Redis ストリームに保存されます。
- ユーザーごとにメッセージのストリームは1つです。これは、現在すべてのサービスが同じキャッシュを共有していることを意味します。必要であれば、ユーザーごとに複数のストリームに拡張することもできます(Redisが推定メッセージ量を処理できることをインフラチームに確認した上で)。
- 直近の50メッセージ(リクエスト+レスポンス)のみが保存されます。
- ストリームの有効期限は、最後のメッセージを追加してから3日間です。
- ユーザーは自分のメッセージにのみアクセスできます。キャッシュ・レベルでの作成者は存在せず、(現在のユーザー以外がアクセスする場合は)サービス・レイヤでの作成が期待されます。
名前空間の設定に基づき、このリソースに対して機能が許可されているかどうかをチェックします。
AI 機能の使用を制限する、ルート名前空間レベルで許可される 2 つの設定があります:
experiment_features_enabled
-
third_party_ai_features_enabled
.
指定された名前空間でその機能が許可されているかどうかを確認するには、以下を呼び出します:
Gitlab::Llm::StageCheck.available?(namespace, :name_of_the_feature)
Gitlab::Llm::StageCheck
クラスに機能の名前を追加します。そこには、実験的な機能とベータ版の機能を区別する配列があります。
これで、次のようなさまざまなケースに対応できるようになります:
- その機能がどの配列にもない場合、チェックは
true
を返します。例えば、その機能がGAに移動され、サードパーティの設定を使用していない場合です。 - feature が GA にあり、サードパーティの設定を使用している場合、このクラスは名前空間のサードパーティの設定に基づいて適切な回答を返します。
フィーチャーを実験フェーズからベータフェーズに移動するには、フィーチャーの名前をEXPERIMENTAL_FEATURES
配列からBETA_FEATURES
配列に移動します。
AI APIへの呼び出しとプロンプトの実装
CompletionWorker
はCompletions::Factory
を呼び出します。 は Service を初期化し、API への実際の呼び出しを実行します。この例では、OpenAIを使用し、2つの新しいクラスを実装します:
# /ee/lib/gitlab/llm/open_ai/completions/amazing_new_ai_feature.rb
module Gitlab
module Llm
module OpenAi
module Completions
class AmazingNewAiFeature
def initialize(ai_prompt_class)
@ai_prompt_class = ai_prompt_class
end
def execute(user, issue, options)
options = ai_prompt_class.get_options(options[:messages])
ai_response = Gitlab::Llm::OpenAi::Client.new(user).chat(content: nil, **options)
::Gitlab::Llm::OpenAi::ResponseService.new(user, issue, ai_response, options: {}).execute(
Gitlab::Llm::OpenAi::ResponseModifiers::Chat.new
)
end
private
attr_reader :ai_prompt_class
end
end
end
end
end
# /ee/lib/gitlab/llm/open_ai/templates/amazing_new_ai_feature.rb
module Gitlab
module Llm
module OpenAi
module Templates
class AmazingNewAiFeature
TEMPERATURE = 0.3
def self.get_options(messages)
system_content = <<-TEMPLATE
You are an assistant that writes code for the following input:
"""
TEMPLATE
{
messages: [
{ role: "system", content: system_content },
{ role: "user", content: messages },
],
temperature: TEMPERATURE
}
end
end
end
end
end
end
OpenAIは複数のAIプロバイダをサポートしているため、同じ例でそれらのプロバイダを使用することもできます:
Gitlab::Llm::VertexAi::Client.new(user)
Gitlab::Llm::Anthropic::Client.new(user)
AiアクションのGraphQLへの追加
TODO
セキュリティ
人工知能(AI) 機能のセキュアコーディングガイドラインを参照してください。