基于第三方集成的 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 功能所需的正确许可证、设置和功能标志。
方法:
-
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]' - 创建一个名为
-
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]' - 创建一个名为
本地开发提示
- 当响应在用户界面中显示过慢时,考虑重启 Sidekiq:运行
gdk restart rails-background-jobs。如果无效,尝试gdk kill后再执行gdk start。 - 或者完全绕过 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-flagsHTTP 头中。 - 当前端客户端(例如 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 请求由后台作业处理。因此我们不保持请求存活,前端需要匹配订阅中的请求与响应。
仅使用 userId 和 resourceId 来确定正确响应可能会导致问题。例如,当两个 AI 功能使用相同的 userId 和 resourceId 时,两个订阅都会收到彼此的响应。为防止干扰,我们引入了 clientSubscriptionId。
要在 aiCompletionResponse 订阅中匹配响应,可以向 aiAction 突变提供 clientSubscriptionId。
clientSubscriptionId应在每个功能和页面内唯一,以免与其他 AI 功能冲突。建议使用UUID。- 仅当
clientSubscriptionId作为aiAction突变的一部分提供时,才会用于广播aiCompletionResponse。 - 如果未提供
clientSubscriptionId,则仅使用userId和resourceId进行aiCompletionResponse。
以总结评论为例,我们在突变中提供一个 randomId:
mutation {
aiAction(
input: {
summarizeComments: { resourceId: "gid://gitlab/Issue/52" }
clientSubscriptionId: "randomId"
}
) {
clientMutationId
}
}在我们的组件中,我们使用 userId、resourceId 和 clientSubscriptionId("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 组件(如提示、输入/输出解析器、工具/函数调用),但为每个模型拆分组件会增加维护成本。因此,只要不影响功能质量,通常建议多模型复用现有组件。以下是经验法则:
- 对多个模型迭代现有提示模板。除非导致特定模型的质量下降,否则不要引入新模板。
- 对多个模型迭代现有输入/输出解析器和工具/函数调用。除非导致特定模型的质量下降,否则不要引入新的。
- 若检测到特定模型存在质量下降,应针对该模型拆分共享组件。
示例 显示,我们可以将 Claude 特定的 CoT 优化应用到 Mixtral 等其他模型,只要不造成质量下降即可。
监控
- 每个 Ai 操作的错误率和响应延迟 apdex 可在 Sidekiq 服务仪表盘 的 SLI 详情:
llm_completion下找到。 - 各 Ai 功能的消耗令牌数、使用情况和其他统计信息可在 Periscope 仪表盘 上找到。
- AI 网关日志。
- AI 网关指标。
- 通过代理的功能使用仪表盘。
安全
请参阅 人工智能(AI)功能的安全编码准则。