Qwen Code GitLab 渠道接入指南:基于 Todos API 的 Issue/MR 智能响应机器人
2026/9/15 11:18:51 网站建设 项目流程

Qwen Code GitLab 渠道接入指南:基于 Todos API 的 Issue/MR 智能响应机器人

【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code

本篇指南讲解如何在 Qwen Code 中配置一个 GitLab 渠道(channel),让运行在终端中的 AI 编程代理能够监听 GitLab Todos、响应 Issue 与 Merge Request 中的 @ 提及、指派和评审请求,并自动在对应议题下回帖执行代码任务。读完本文,你将掌握 GitLab 渠道的 Token 准备、settings.json完整配置、action_prompt_template事件过滤与模板渲染机制、groupPolicy/senderPolicy安全模型、轮询与游标原理,以及启动渠道的 CLI 命令。

渠道定位:GitLab 作为消息源

GitLab 渠道(@qwen-code/channel-gitlab,实现于 packages/channels/gitlab/src/GitlabAdapter.ts)是一个轮询型(polling)渠道适配器。它并不监听 Webhook,而是以 GitLab 的Todos API为消息源,周期性拉取state=pending的待办事项,把"某人在 Issue/MR 中 @ 了机器人"这类事件转换成代理可执行的 prompt,再把代理的回复通过 Notes API 回帖到对应 Issue 或 MR 上。

从源码看,适配器继承自PollingChannelBase(见 packages/channels/base/src/PollingChannelBase.ts),底层通过@gitbeaker/rest(依赖声明见 packages/channels/gitlab/package.json)访问 GitLab REST API,并使用 zod 对持久化游标做运行时校验。

前提条件

  • 一个 GitLab 账号(建议使用专用机器人账号,避免与个人操作混淆);
  • 一个具备read_apiapi两个 scope 的Personal Access Token(PAT)read_api用于读取 todos 与项目数据,api用于在 Issue/MR 上发布评论(notes)。

创建 Token

  1. 进入 GitLabPreferences → Access Tokens
  2. 勾选read_apiapi两个 scope 创建 Token;
  3. 将 Token 安全地保存为环境变量,不要硬编码进配置文件。

从源码实现看,连接阶段会调用Users.showCurrentUser()获取机器人自身的用户名(GitlabAdapter.ts),用于后续的 @ 提及识别与"跳过自己发起的 todo"判断,因此 Token 必须对应用户本身。

基础配置

~/.qwen/settings.json中添加渠道配置:

{ "channels": { "my-gitlab": { "type": "gitlab", "token": "$GITLAB_TOKEN", "pollInterval": 60000, "senderPolicy": "open", "sessionScope": "chat_thread", "cwd": "/path/to/your/project", "groupPolicy": "open", "action_prompt_template": { "mentioned": "Project: %project% | URL: %project_url% | Author: %author% | Type: %target_type% | IID: %iid% | Title: %title% | Description: %description% | TodoID: %todo_id%" } } } }

设置环境变量:

export GITLAB_TOKEN="glpat-your_token_here"

自托管 GitLab

对于自托管实例,通过baseUrl指定实例地址:

{ "baseUrl": "https://gitlab.example.com" }

源码在connect()中会把baseUrl尾部的斜杠剥掉(replace(/\/+$/, '')),默认值为https://gitlab.com(GitlabAdapter.ts);测试用例也验证了带尾部斜杠的https://gitlab.example.com/会被归一化为无斜杠形式后再构造 Gitlab 客户端(见 GitlabAdapter.test.ts)。

配置选项一览

OptionDefaultDescription
token(required)PAT withread_api+apiscopes
pollInterval60000Poll interval in ms
baseUrlhttps://gitlab.comGitLab instance URL
action_prompt_template(required for processing)Maps GitLab action names to metadata templates
groupPolicy"disabled"Must be"open","allowlist"with the project listed, or"pairing"with the project approved
senderPolicy"allowlist"Who can trigger the bot

几个参数的关键行为补充:

  • pollInterval:轮询间隔(毫秒)。PollingChannelBase中对其做了合法性校验——必须是大于 0 的有限数值,否则回退到默认值60000(见 PollingChannelBase.ts)。
  • action_prompt_template处理 todo 的前提。源码在connect()pollOnce()两处都做了检查:若未配置或为空对象,渠道会打印warning: action_prompt_template is not configured; no todos will be processed,并直接跳过轮询(GitlabAdapter.ts)。测试用例也专门覆盖了"空模板不调用 TodoLists API"这一行为(GitlabAdapter.test.ts)。
  • groupPolicy:见下文专门小节,默认值"disabled"静默丢弃所有提及

action_prompt_template:事件过滤器与元数据模板

该字段是 GitLab 渠道的真正事件过滤器:只有配置了模板的 action 才会被分发处理,其余 action 一律跳过并标记为 done(静默消费,不产生任何输出)。

{ "action_prompt_template": { "mentioned": "Project: %project% | Author: %author% | Title: %title%" } }

directly_addressed(以@bot开头的评论)在未显式配置时,自动回退使用mentioned模板——这一回退逻辑实现在resolveTemplate()中:先精确查找templates[actionName],若 action 为directly_addressed且无显式模板,则返回templates['mentioned'](GitlabAdapter.ts),对应的行为测试见 GitlabAdapter.test.ts。

可用 Action Key

KeyTrigger
mentionedSomeone @mentions the bot in a comment or description (not at the start)
directly_addressedA commentstarts with@bot(falls back tomentionedtemplate)
assignedSomeone assigns the bot to an issue/MR
review_requestedSomeone requests the bot as a reviewer on an MR
approval_requiredAn MR requires the bot's approval (approval rules)
markedSomeone marks the bot's comment/issue/MR (star)
build_failedA CI/CD pipeline fails on the bot's branch/MR
unmergeableAn MR the bot is involved with becomes unmergeable (conflicts)
merge_train_removedAn MR is removed from the merge train

只有出现在action_prompt_template中的 key 会被处理;未配置的 action 会被跳过并静默标记为 done。

模板变量

VariableValue
%project%Project path (e.g.,owner/repo)
%project_url%Full project URL
%author%Todo author username
%target_type%IssueorMergeRequest
%iid%Issue/MR internal ID
%title%Issue/MR title
%description%Issue/MR description body
%todo_id%GitLab todo ID
%%Literal%(escape)

模板渲染由buildMetadata()完成:以%%%(\w+)%正则替换变量,%%转义为字面%未知变量原样保留在输出中(GitlabAdapter.ts)。测试用例如下:

  • 已知变量全部替换:'Project: owner/repo | URL: https://gitlab.com/owner/repo | Author: alice | Type: Issue | IID: 42 | Title: Test Issue | TodoID: 100'
  • 未知变量保留:'Known: owner/repo Unknown: %nonexistent%'

(见 GitlabAdapter.test.ts)

%description%的取值有额外语义:对于评论型提及(带#note_锚点),只有模板中包含%description%时才会额外调用Issues.show/MergeRequests.show抓取完整描述(needsDescription判断,GitlabAdapter.ts),抓取结果还会按chatId/targetType/iid做内存缓存。也就是说,把%description%从模板中移除可以省掉一次 API 调用。

Prompt 组装:主提示与结构化元数据分离

模板渲染结果进入envelope.metadata(结构化上下文),触发文本(todo.body评论内容,或描述)进入envelope.text(主提示)。基类最终组装出发送给代理的完整 prompt,形如:

[alice] please fix this bug Project: owner/repo | URL: https://gitlab.com/owner/repo | Author: alice | Type: Issue | IID: 42 | Title: Test Issue | Description: ... | TodoID: 100
  • 第 1 行:[sender]前缀 +envelope.text(其中@bot已被剥除);
  • 第 3 行:envelope.metadata(渲染后的模板,已做清洗)。

无需%body%变量——评论/描述文本永远是主提示内容,模板只是其下方的补充上下文。@bot的剥离由stripBotMention()完成:它用(?<=\s|^|[([{<:;"'])\@username(?=[^a-zA-Z0-9_./-]|$)正则(大小写不敏感)匹配并移除提及(见 packages/channels/gitlab/src/mention.ts)。

安全模型:senderPolicy 与 groupPolicy

⚠️ senderPolicy 风险

公开项目上,若设置senderPolicy: "open"任何 GitLab 用户只要 @ 提及机器人,就能提交 prompt 驱动你cwd目录中的代理执行操作。

  • 公开项目上务必使用senderPolicy: "allowlist"并显式列出allowedUsers
  • 连接时,allowedUsers会被统一转成小写并与发送者 ID 比对(GitlabAdapter.ts)。

⚠️ groupPolicy 下的项目级授权

需要注意:在groupPolicy: "pairing"下,授权是按项目授予的——一旦某项目通过审批,任何 GitLab 用户都能通过该项目的 Issue/MR 驱动机器人。GitLab 的所有流量都属于"群组流量",因此senderPolicyallowedUsers并不会拦截已批准项目的成员。

审批以项目路径(owner/repo)为键,项目重命名或迁移后该键会变化——任何项目重命名、转移或删除之后,都应撤销过期的群组审批。

groupPolicy 必须是 "open"、"allowlist" 或 "pairing"

groupPolicy必须设置为"open""allowlist"(且项目被显式列出)或"pairing",todo 才会被处理:

  • "pairing":来自未批准项目的首次提及会创建一个群组配对请求;用qwen channel pairing approve审批一次后,该项目产生的 todos 就会持续被分发;
  • 默认值"disabled":丢弃所有提及——todo 被标记为 done、游标照常推进,但不会发生任何分发。此时日志会记录preflight rejected reason=group_disabled,todo 仍被消费;
  • 如果你的机器人不响应提及,先检查groupPolicy是否还是"disabled"

源码在connect()时就会对非open/allowlist/pairing的取值打印告警(GitlabAdapter.ts),测试也验证了disabled会触发告警而pairing不会(GitlabAdapter.test.ts)。群组门控的完整求值顺序(disabled→ allowlist → pairing → mention gating)实现在 packages/channels/base/src/GroupGate.ts 的check()中。

工作原理:轮询、游标与分发

GitLab 适配器以 Todos API 为消息源,完整流程如下:

  1. 轮询GET /todos?state=pending获取待办;
  2. 首轮排空(first-poll drain):若游标从未初始化(initialized: false),所有现存 pending todos 会被直接标记为 done、不做分发,游标推进到最大 todo ID——防止首次启动时积压洪峰;
  3. 清理过期 todoid <= cursor的 todo 被 best-effort 标记为 done,避免每轮轮询重复拉取;
  4. 过滤:仅保留id > cursor且 action 命中action_prompt_template的 todo;
  5. 识别提及类型:通过target_url的锚点判断——
    • #note_123→ 评论提及,文本取todo.body(即评论内容);
    • 无锚点 → 描述提及,文本取 Issue/MR 的描述;
  6. 分发:通过handleInbound发出 envelope(要求groupPolicy"open"、或"allowlist"且项目已列出、或"pairing"且项目已批准);
  7. 推进游标best-effort 标记 todo done

上述逻辑对应源码中的pollOnce()(GitlabAdapter.ts)。几个值得注意的细节:

  • 游标(cursor):结构为{ lastProcessedId: number, initialized: boolean },首次启动初始化为{ lastProcessedId: 0, initialized: false };每个轮询周期结束会通过saveCursor()原子写入(先写.tmp再 rename)到~/.qwen/channels/<name>-<hash>-poll-cursor.json(见 PollingChannelBase.ts)。测试专门验证了 15 万条积压 todo 在首轮会被全部排空且零分发(GitlabAdapter.test.ts)。
  • 游标与分发解耦:无论分发成功还是失败,lastProcessedId都会推进。分发失败会在 Issue/MR 上回帖 ⚠️ 错误评论(内容为⚠️ Failed to process this request. Please re-mention the bot to retry.),不会重试——用户需要再次 @ 机器人来触发新 todo。
  • 自提及跳过:todo 作者就是机器人本人时直接跳过(todo.author.username === this.botUsername),防止机器人回复自己的评论形成死循环(GitlabAdapter.ts)。
  • 非 Issue/MR 目标跳过:Epic、Design、Alert 等目标类型一律跳过;target_url中的描述会先经fetchDescription()抓取(按chatId/type/iid缓存)。
  • envelope 固定标记isMentioned = true:因为 GitLab 在创建 todo 时已经确定了提及关系,适配器无需自行判断;@bot提及在分发前通过stripBotMention从消息文本中剥离(GitlabAdapter.ts)。
  • 轮询健壮性PollingChannelBase内置指数退避——轮询异常时按2s → 4s → … → 30s递增退避并写日志,成功后复位(PollingChannelBase.ts)。

回帖的线程模型

代理回复通过sendThreadMessage落回 GitLab:threadId必须是issue:<iid>mr:<iid>格式(例如issue:42mr:7),否则抛错;回复分别走IssueNotes.createMergeRequestNotes.create(GitlabAdapter.ts)。因此sessionScope建议保持chat_thread,让同一 Issue/MR 的多次交互落在同一会话上下文中。

响应反馈:👀 工作状态表情

对于已接受的评论提及(带#note_锚点的 note),渠道会在代理工作时给该 note 添加 👀 award emoji,待运行完成、失败或被取消后移除:

  • 添加与移除都是 best-effort:award emoji API 或权限失败只会写日志,绝不会阻塞最终回复
  • 描述提及(无#note_锚点)因为没有具体的 note 可以"点赞",不会添加 award emoji。

实现上,onPromptStart通过IssueNoteAwardEmojis.award/MergeRequestNoteAwardEmojis.award(emoji 名eyes)添加,onPromptEnd通过remove移除,并以reactionsMap 按messageId(即 todo id)管理状态(GitlabAdapter.ts);"不重复添加""失败不影响移除""MR 使用 MR 的 emoji API"等行为均有对应测试覆盖(GitlabAdapter.test.ts)。

已知限制

  • 首次启动跳过存量 pending todos:游标首启为{ lastProcessedId: 0, initialized: false },首个轮询周期会把所有已存在的 pending todos 标记为 done 而不分发(由initialized标志控制这一次性排空),避免积压洪峰;
  • 不读取历史对话:机器人只处理触发它的那一条内容,不携带此前会话历史;
  • 机密(internal)note 泄露风险:若有人在机密 note 中 @ 机器人,todo body 会包含内部文本并被代理处理;而机器人的回复总是以公开 note 发布,可能暴露内部讨论。GitLab 的 todo API 不暴露 note 可见性,适配器无法过滤——避免在机密 note 中 @ 机器人
  • 需要read_api+apiPAT scope;拥有这两个 scope 的群组级或项目级 token 同样可用;
  • Epic、Design、Alert 的 todos 会被跳过,仅处理 Issue 与 MR。

启动渠道

qwen channel start my-gitlab

启动后渠道即按pollInterval周期轮询。若机器人不响应提及,优先按以下顺序排查:action_prompt_template是否配置了对应 action、groupPolicy是否为"disabled"senderPolicy/allowedUsers是否拦截了发送者、Token 是否具备read_api+apiscope。更完整的渠道管理与安全配置说明可参考 docs/users/features/channels 目录下的其他渠道文档。

【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询