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

基于第三方集成的 AI 功能

GitLab Duo 功能由 AI 模型和集成驱动。本文档概述了如何在 GitLab 中开发 AI 功能。

有关在开发环境中设置 GitLab Duo 许可证的详细说明,请参见 本地开发的 GitLab Duo 许可证

本地开发环境中设置 GitLab Duo 功能的说明

必需:配置许可证

请参见 本地开发的 GitLab Duo 许可证

必需:安装 AI 网关

原因:除 Duo Workflow 外的所有 Duo 功能都通过 AI 网关路由 LLM 请求。

方法

按照 这些说明 使用 GDK 安装 AI 网关。

必需:运行 gitlab:duo:setup 脚本

原因:这能确保您的实例或组具备测试本地 Duo 功能所需的正确许可证、设置和功能标志。

方法

  1. GitLab.com(SaaS)模式

    GITLAB_SIMULATE_SAAS=1 bundle exec 'rake gitlab:duo:setup'

    此操作会:

    • 创建一个名为 gitlab-duo 的测试组,其中包含一个名为 test 的项目
    • 为该组应用 Ultimate 许可证
    • 为该组设置 Duo Enterprise 座位
    • 启用该组的所有功能标志
    • 更新组设置以启用所有可用的 GitLab Duo 功能

    或者,如果您想为该组添加 GitLab Duo Pro 许可证(仅启用部分功能),可以运行:

    GITLAB_SIMULATE_SAAS=1 bundle exec 'rake gitlab:duo:setup[duo_pro]'
  2. GitLab 自托管/专用模式

    GITLAB_SIMULATE_SAAS=0 bundle exec 'rake gitlab:duo:setup'

    此操作会:

    • 创建一个名为 gitlab-duo 的测试组,其中包含一个名为 test 的项目
    • 为实例应用 Ultimate 许可证
    • 为实例设置 Duo Enterprise 座位
    • 启用实例的所有功能标志
    • 更新实例设置以启用所有可用的 GitLab Duo 功能

    或者,如果您想为实例添加 GitLab Duo Pro 插件(仅启用部分功能),可以运行:

    GITLAB_SIMULATE_SAAS=0 bundle exec 'rake gitlab:duo:setup[duo_pro]'

本地开发提示

  1. 当响应在用户界面中显示过慢时,考虑重启 Sidekiq:运行 gdk restart rails-background-jobs。如果无效,尝试 gdk kill 后再执行 gdk start
  2. 或者完全绕过 Sidekiq 并同步运行服务。这有助于调试错误,因为 GraphQL 错误现在会出现在网络检查器中而非 Sidekiq 日志里。为此,临时修改 Llm::CompletionWorker 类中的 perform_for 方法,将 perform_async 改为 perform_inline

功能开发(抽象层)

功能标志

对任何 AI 功能工作应用以下功能标志:

  • 一个适用于所有其他 AI 功能的全局标志(ai_global_switch),默认启用。
  • 特定于该功能的标志。功能标志名称 必须与许可功能名称不同

查看 功能标志跟踪史诗,了解所有功能标志及其使用方法。

将功能标志推送到 AI 网关

您可以将 功能标志 推送到 AI 网关。即使功能位于 AI 网关中,这也便于逐步推出面向用户的变化。

参考以下示例:

# 将功能标志状态推送到 AI 网关。
Gitlab::AiGateway.push_feature_flag(:new_prompt_template, user)

之后,您可以在 AI 网关中以如下方式使用功能标志状态:

from ai_gateway.feature_flags import is_feature_enabled

# 检查功能标志 "new_prompt_template" 是否已启用。
if is_feature_enabled('new_prompt_template'):
  # 从新提示模板构建提示
else:
  # 从旧提示模板构建提示

重要:在 清理 步骤中,务必先 在 AI 网关仓库中移除功能标志,然后再 移除 GitLab-Rails 仓库中的标志。如果您先清理 GitLab-Rails 仓库中的标志,由于这是默认状态,AI 网关中的功能标志会立即禁用,可能导致意外行为。

重要:清理 AI 网关中的功能标志会立即将其变更分发到所有 GitLab 实例,包括 GitLab.com、GitLab 自托管和 GitLab 专用版。

技术细节

  • push_feature_flag 运行在启用的功能标志上时,标志名称会被缓存到当前上下文中,随后当 GitLab-Sidekiq/Rails 向 AI 网关发送请求时,会附加到 x-gitlab-enabled-feature-flags HTTP 头中。
  • 当前端客户端(例如 VS Code 扩展或 LSP)请求 用户 JWT(UJWT)进行直接 AI 网关通信时,GitLab 会返回:
    • 公共头信息(包括 x-gitlab-enabled-feature-flags
    • 生成的 UJWT(有效期 1 小时)

前端客户端需要在过期时重新生成 UJWT。通过 ChatOps 进行的后端更改会使头值变得陈旧。这些头值会在下一次 UJWT 生成时刷新。

同样,我们也有 push_frontend_feature_flag 用于将功能标志推送到前端。

GraphQL API

若要通过抽象层连接到 AI 提供商 API,请使用名为 aiAction 的可扩展 GraphQL API。input 接受键值对,其中 key 是需要执行的操作。每个突变请求只允许一个 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 随后会使用 Anthropic Client 发送响应。

如何接收响应

对 AI 提供商的 API 请求由后台作业处理。因此我们不保持请求存活,前端需要匹配订阅中的请求与响应。

仅使用 userIdresourceId 来确定正确响应可能会导致问题。例如,当两个 AI 功能使用相同的 userIdresourceId 时,两个订阅都会收到彼此的响应。为防止干扰,我们引入了 clientSubscriptionId

要在 aiCompletionResponse 订阅中匹配响应,可以向 aiAction 突变提供 clientSubscriptionId

  • clientSubscriptionId 应在每个功能和页面内唯一,以免与其他 AI 功能冲突。建议使用 UUID
  • 仅当 clientSubscriptionId 作为 aiAction 突变的一部分提供时,才会用于广播 aiCompletionResponse
  • 如果未提供 clientSubscriptionId,则仅使用 userIdresourceId 进行 aiCompletionResponse

以总结评论为例,我们在突变中提供一个 randomId

mutation {
  aiAction(
    input: {
      summarizeComments: { resourceId: "gid://gitlab/Issue/52" }
      clientSubscriptionId: "randomId"
    }
  ) {
    clientMutationId
  }
}

在我们的组件中,我们使用 userIdresourceIdclientSubscriptionId"randomId")监听 aiCompletionResponse

subscription aiCompletionResponse(
  $userId: UserID
  $resourceId: AiModelID
  $clientSubscriptionId: String
) {
  aiCompletionResponse(
    userId: $userId
    resourceId: $resourceId
    clientSubscriptionId: $clientSubscriptionId
  ) {
    content
    errors
  }
}

聊天订阅 的行为有所不同。

为避免过多并发订阅,应在发送突变后再订阅,可通过使用 skip() 实现。

说明不同的 ID 参数

在使用 aiAction 突变时,多个 ID 参数用于正确路由请求和响应。以下是各参数的作用:

  • user_id(必需)

    • 用途:标识和验证请求用户身份
    • 用于:权限检查、请求归属和响应路由
    • 示例:gid://gitlab/User/123
    • 注意:此 ID 由 GraphQL API 框架自动包含
  • client_subscription_id(流式传输或多功能场景推荐)

    • 客户端生成的 UUID,用于跟踪特定请求/响应对
    • 流式响应或多功能共享同一页面时必需
    • 示例:"9f5dedb3-c58d-46e3-8197-73d653c71e69"
    • 简单隔离请求且无流式传输时可省略
  • resource_id(某些功能需上下文 - 可选)

    • 用途:引用提供 AI 操作上下文的特定 GitLab 实体(项目、问题、MR)
    • 用于:权限验证和上下文信息收集
    • 实际示例:"gid://gitlab/Issue/164723626"
    • 注意:部分功能可能无需特定资源
  • project_id(某些功能需上下文 - 可选)

    • 用途:标识 AI 操作的项目上下文
    • 用于:项目级权限检查和上下文
    • 实际示例:"gid://gitlab/Project/278964"
    • 注意:部分功能可能无需特定项目

当前抽象层流程

以下图表以 VertexAI 为例。您可以使用不同提供商。

flowchart TD
A[GitLab 前端] -->B[AiAction GraphQL 突变]
B --> C[Llm::ExecuteMethodService]
C --> D[其中一个服务,例如:Llm::GenerateSummaryService]
D -->|计划任务| E[AI 工作进程:Llm::CompletionWorker]
E -->F[::Gitlab::Llm::Completions::...#96; 类使用 #96;::Gitlab::Llm::Templates::...#96; 类]
F -->G[#96;::Gitlab::Llm::VertexAi::Completions::...#96; 类调用 #96;::Gitlab::Llm::Templates::...#96; 类]
G -->|调用| H[Gitlab::Llm::VertexAi::Client]
H --> |响应| I[::Gitlab::Llm::GraphqlSubscriptionResponseService]
I --> J[GraphqlTriggers.ai_completion_response]
J --> K[::GitlabSchema.subscriptions.trigger]

多模型复用现有 AI 组件

我们致力于优化每个 LLM 的 AI 组件(如提示、输入/输出解析器、工具/函数调用),但为每个模型拆分组件会增加维护成本。因此,只要不影响功能质量,通常建议多模型复用现有组件。以下是经验法则:

  1. 对多个模型迭代现有提示模板。除非导致特定模型的质量下降,否则不要引入新模板。
  2. 对多个模型迭代现有输入/输出解析器和工具/函数调用。除非导致特定模型的质量下降,否则不要引入新的。
  3. 若检测到特定模型存在质量下降,应针对该模型拆分共享组件。

示例 显示,我们可以将 Claude 特定的 CoT 优化应用到 Mixtral 等其他模型,只要不造成质量下降即可。

监控

安全

请参阅 人工智能(AI)功能的安全编码准则

帮助