Help us learn about your current experience with the documentation. Take the survey.

AI操作

本页面包含如何实现操作并将其迁移到AI网关的内容。

如何实现新操作

实现新的AI操作需要在不同组件中进行更改。我们将以希望实现一个允许用户根据给定提示重写问题描述的操作为例。

1. 将您的操作添加到云连接器功能列表中

云连接器配置存储访问服务所需的权限以及其他元数据。如果您的功能没有条目,将其作为云连接器单元原语添加

有关更多信息,请参见云连接器:配置

2. 在AI网关中创建提示定义

AI网关项目中,在ai_gateway/prompts/definitions下创建一个新的提示定义,路由为[ai-action]/base/[prompt-version].yml(请参见提示版本控制约定)。指定您希望使用的模型和提供程序以及将提供给模型的提示。您可以使用{}指定要插入提示中的输入。

# ai_gateway/prompts/definitions/rewrite_description/base/1.0.0.yml

name: 描述重写器
model:
  config_file: conversation_performant
  params:
    model_class_provider: anthropic
prompt_template:
  system: |
    您是一个有帮助的助手,用于重写资源的描述。您将获得当前描述和一个关于应如何重写的提示。仅回复您重写的描述。

    <description>{description}</description>

    <prompt>{prompt}</prompt>

当AI操作使用多个提示时,可以在树形结构中组织定义,形式为[ai-action]/[prompt-name]/base/[version].yaml

# ai_gateway/prompts/definitions/code_suggestions/generations/base/1.0.0.yml

name: 代码生成
model:
  config_file: conversation_performant
  params:
    model_class_provider: anthropic
...

若要为多个模型指定提示,请在定义的路径中使用模型的名称:

# ai_gateway/prompts/definitions/code_suggestions/generations/mistral/1.0.0.yml

name: 代码生成
model:
  name: mistral
  params:
    model_class_provider: litellm
...

3. 创建Completion类

  1. ee/lib/gitlab/llm/ai_gateway/completions/下创建一个新的completion,并继承自Base AI网关Completion。
# ee/lib/gitlab/llm/ai_gateway/completions/rewrite_description.rb

module Gitlab
  module Llm
    module AiGateway
      module Completions
        class RewriteDescription < Base
          extend ::Gitlab::Utils::Override

          override :inputs
          def inputs
            { description: resource.description, prompt: prompt_message.content }
          end
        end
      end
    end
  end
end

4. 创建服务

  1. ee/app/services/llm/下创建一个新的服务,并继承自BaseService
  2. resource是我们想要操作的对象。它可以包含Ai::Model关联的任何对象。例如,它可以是ProjectMergeRequestIssue
# ee/app/services/llm/rewrite_description_service.rb

module Llm
  class RewriteDescriptionService < BaseService
    extend ::Gitlab::Utils::Override

    override :valid
    def valid?
      super &&
        # 您可以限制您的服务适用于哪种类型的资源
        resource.to_ability_name == "issue" &&
        # 始终检查用户是否有权对该资源执行此操作
        Ability.allowed?(user, :rewrite_description, resource)
    end

    private

    def perform
      schedule_completion_worker
    end
  end
end

5. 在目录中注册功能

转到Gitlab::Llm::Utils::AiFeaturesCatalogue并为您的AI操作添加一个新条目。

class AiFeaturesCatalogue
  LIST = {
    # ...
    rewrite_description: {
      service_class: ::Gitlab::Llm::AiGateway::Completions::RewriteDescription,
      feature_category: :ai_abstraction_layer,
      execute_method: ::Llm::RewriteDescriptionService,
      maturity: :experimental,
      self_managed: false,
      internal: false
    }
  }.freeze

6. 添加默认提示版本查询

转到Gitlab::Llm::PromptVersions,并为您的AI操作添加一个条目,其中包含您想要的提示版本的查询(对于新功能,这通常是^1.0.0,请参见提示版本解析):

class PromptVersions
  class << self
    VERSIONS = {
      # ...
      "rewrite_description/base": "^1.0.0"

更新AI操作

要对模板、模型或AI功能的参数进行更改,请在AI网关中创建一个新的YAML版本文件:

# ai_gateway/prompts/definitions/rewrite_description/base/1.0.1.yml

name: 带有Claude 3.5的描述重写器
model:
  name: claude-3-5-sonnet-20240620
  params:
    model_class_provider: anthropic
prompt_template:
  system: |
    您是一个有帮助的助手,用于重写资源的描述。您将获得当前描述和一个关于应如何重写的提示。仅回复您重写的描述。

    <description>{description}</description>

    <prompt>{prompt}</prompt>

提示版本的增量部署

一旦稳定的提示版本被添加到AI网关中,就不应该再修改它。您可以通过向文件名添加预发布后缀来创建可变的提示版本(例如,1.0.1-dev.yml)。这将防止其自动提供给客户端。然后,您可以使用功能标志来控制此新版本的部署。对于GitLab Duo自托管版,强制版本将被忽略,并且仅使用PromptVersions中定义的版本。这样可以避免错误地启用未指定该版本的模型的版本。

如果您的AI操作实现为AiGateway::Completions::Base的子类,则可以通过在子类中覆盖提示版本来实现这一点:

# ee/lib/gitlab/llm/ai_gateway/completions/rewrite_description.rb

module Gitlab
  module Llm
    module AiGateway
      module Completions
        class RewriteDescription < Base
          extend ::Gitlab::Utils::Override

          override :prompt_version
          def prompt_version
            '1.0.1-dev' if Feature.enabled?(:my_feature_flag) # 您也可以根据`user`或`resource`进行范围限定
          end

          # ...

当您准备好使此版本稳定并开始自动提供给兼容客户端时,只需重命名YAML定义文件以删除预发布后缀,并移除prompt_version覆盖即可。

如何将现有操作迁移到AI网关

AI操作最初是在GitLab单体架构内实现的。作为我们AI网关作为单体架构访问模型的唯一访问点史诗的一部分,我们正在将提示、模型选择和模型参数迁移到AI网关中。这将通过解耦提示和模型更改与单体版本发布,从而提高我们向GitLab自管理用户交付改进的速度。要将现有操作迁移:

  1. 遵循如何实现新操作中的步骤1至3。
  2. 修改目录中您的AI操作的条目,将新的completion类列为aigw_service_class
class AiFeaturesCatalogue
  LIST = {
    # ...
    generate_description: {
      service_class: ::Gitlab::Llm::Anthropic::Completions::GenerateDescription,
      aigw_service_class: ::Gitlab::Llm::AiGateway::Completions::GenerateDescription,
      prompt_class: ::Gitlab::Llm::Templates::GenerateDescription,
      feature_category: :ai_abstraction_layer,
      execute_method: ::Llm::GenerateDescriptionService,
      maturity: :experimental,
      self_managed: false,
      internal: false
    },
    # ...
  }.freeze
  1. 创建prompt_migration_#{feature_name}功能标志(例如prompt_migration_generate_description

当功能标志启用时,将使用aigw_service_class处理AI操作。一旦验证了您的操作的正确运行,您可以移除aigw_service_class键并将service_class替换为新的AiGateway::Completions类,使其成为永久提供者。

有关迁移AI操作所需更改的完整示例,请参见以下MR:

GitLab-Rails中的授权

我们建议使用policies来处理功能的授权。目前我们需要确保涵盖以下检查:

一些基本的授权包含在抽象层类中,这些类是更多专用类的基类。

代码中需要包含什么:

  1. 检查功能标志兼容性:Gitlab::Llm::Utils::FlagChecker.flag_enabled_for_feature?(ai_action) - 包含在Llm::BaseService类中。
  2. 检查资源是否已授权:Gitlab::Llm::Utils::Authorizer.resource(resource: resource, user: user).allowed? - 也包含在Llm::BaseService类中。
  3. 这两个检查都包含在::Gitlab::Llm::FeatureAuthorizer.new(container: subject_container, feature_name: action_name).allowed?
  4. 对AI功能的访问取决于几个因素,例如:它们的成熟度、是否在自管理上启用、是否捆绑在附加组件中等。
    • 示例 与特定资源无关的策略。
    • 示例 与特定资源相关的策略。

有关更多信息,请参见GitLab AI网关文档关于AI网关中的身份验证和授权。

如果您的Duo功能涉及自主代理,您应该使用复合身份授权。

配对请求与响应

由于多个用户的请求可以并行处理,因此在接收响应时,可能难以将响应与其原始请求配对。requestId字段可用于此目的,因为请求和响应都保证具有相同的requestId UUID。

缓存

AI请求和响应可以被缓存。缓存的对话用于显示用户与AI功能的交互。在当前实现中,此缓存不用于跳过用户重复请求时的AI服务调用。

query {
  aiMessages {
    nodes {
      id
      requestId
      content
      role
      errors
      timestamp
    }
  }
}

此缓存用于聊天功能。对于其他服务,缓存已禁用。您可以通过使用cache_response: true选项为其启用。

缓存有以下限制:

  • 消息存储在Redis流中。
  • 每个用户只有一个消息流。这意味着所有服务当前共享同一个缓存。如果需要,这可以扩展为每个用户多个流(在与基础设施团队确认Redis能够处理估计的消息量之后)。
  • 仅保留最后50条消息(请求+响应)。
  • 流的到期时间为添加最后一条消息后的3天。
  • 用户只能访问自己的消息。在缓存级别没有授权,任何授权(如果由非当前用户访问)都应在服务层预期。

根据命名空间设置检查功能是否允许用于此资源

在根命名空间级别有一个设置允许使用AI功能:

  • experiment_features_enabled

要检查该功能是否允许用于给定的命名空间,请调用:

Gitlab::Llm::StageCheck.available?(namespace, :name_of_the_feature)

将功能名称添加到Gitlab::Llm::StageCheck类中。那里有不同的数组区分实验性和beta功能。

这样我们就为以下不同情况做好了准备:

  • 如果功能不在任何数组中,检查将返回true。例如,该功能通常可用。

要将功能从实验阶段移动到beta阶段,请将功能名称从EXPERIMENTAL_FEATURES数组移动到BETA_FEATURES数组。

实现对AI API和提示的调用

CompletionWorker将调用Completions::Factory,后者将初始化服务并实际调用API。在我们的示例中,我们将使用VertexAI并实现两个新类:

# /ee/lib/gitlab/llm/vertex_ai/completions/rewrite_description.rb

module Gitlab
  module Llm
    module VertexAi
      module Completions
        class AmazingNewAiFeature < Gitlab::Llm::Completions::Base
          def execute
            prompt = ai_prompt_class.new(options[:user_input]).to_prompt

            response = Gitlab::Llm::VertexAi::Client.new(user, unit_primitive: 'amazing_feature').text(content: prompt)

            response_modifier = ::Gitlab::Llm::VertexAi::ResponseModifiers::Predictions.new(response)

            ::Gitlab::Llm::GraphqlSubscriptionResponseService.new(
              user, nil, response_modifier, options: response_options
            ).execute
          end
        end
      end
    end
  end
end
# /ee/lib/gitlab/llm/vertex_ai/templates/rewrite_description.rb

module Gitlab
  module Llm
    module VertexAi
      module Templates
        class AmazingNewAiFeature
          def initialize(user_input)
            @user_input = user_input
          end

          def to_prompt
            <<~PROMPT
            您是一位编写以下上下文代码的助手:

            上下文: #{user_input}
            PROMPT
          end
        end
      end
    end
  end
end

因为我们支持多种AI提供商,您也可以对同一示例使用这些提供商:

Gitlab::Llm::VertexAi::Client.new(user, unit_primitive: 'your_feature')
Gitlab::Llm::Anthropic::Client.new(user, unit_primitive: 'your_feature')

附录A:提示版本控制约定

提示版本应符合语义化版本标准:MAJOR.MINOR.PATCH[-PRERELEASE]

  • MAJOR组件的变化反映了将与旧版GitLab不兼容的更改。例如,当新提示必须接收没有默认值的新属性时,因为如果此更改应用于所有GitLab版本,来自旧版本的请求将抛出错误,因为该属性不存在。

  • MINOR组件的变化反映了向后兼容的功能添加。例如,假设我们要使用一个更新的强大模型:旧版GitLab的请求仍然有效。

  • PATCH组件的变化反映了提示的小bug修复,比如拼写错误。

MAJOR组件确保旧版GitLab不会在新更改添加后中断,而不会阻止我们的代码库的发展。MINOR和PATCH的变化更为主观。

提示版本的不可变性

为了保证更改的可追溯性,只有带有预发布版本(例如1.0.1-dev.yml)的提示才可以在提交后被更改。定义稳定版本的提示是不可变的,更改它们将触发管道失败。

使用partials

为了更好地组织提示,可以使用partials将提示拆分为较小的部分。Partials也必须进行版本控制。例如:

# ai_gateway/prompts/definitions/rewrite_description/base/1.0.0.yml

name: 描述重写器
model:
  config_file: conversation_performant
  params:
    model_class_provider: anthropic
prompt_template:
  system: |
    {% include 'rewrite_description/system/1.0.0.jinja' %}
  user: |
    {% include 'rewrite_description/user/1.0.0.jinja' %}

提示版本解析

AI网关将获取最新可用的稳定版本,该版本匹配作为参数传递的提示版本查询。查询遵循Poetry的版本约束规则。例如,如果提示foo/bar具有以下版本:

  • 1.0.1.yml
  • 1.1.0.yml
  • 1.5.0-dev.yml
  • 2.0.1.yml

那么,如果调用/v1/prompts/foo/bar

  • {'prompt_version': "^1.0.0"},将选择提示版本1.1.0.yml
  • {'prompt_version': "1.5.0-dev"},将选择提示版本1.5.0-dev.yml
  • {'prompt_version': "^2.0.0"},将选择提示版本2.0.1.yml