Skip to Content
用户指南功能特性代码审查

代码审查

使用 /review 审查代码变更的正确性、安全性、性能和代码质量。

快速开始

# 审查本地未提交的更改 /review # 审查 pull request(通过编号或 URL) /review 123 /review https://github.com/org/repo/pull/123 # 审查并在 PR 上发布行内评论 /review 123 --comment # 审查本地更改并将发现应用到工作树 /review --fix # 审查特定文件 /review src/utils/auth.ts # 快速未验证扫描(无 subagent) /review --effort low /review 123 --effort medium

如果没有未提交的更改,/review 会提示并停止——不会启动任何 agent。

Effort 级别

--effort low|medium|high 在深度和速度之间权衡:

级别运行内容发现上限结论是否发布到 PR
low3-6 个针对 diff 的定向行内角度(按 diff 大小缩放)加上 gap sweep——无 subagent,无构建/测试,无项目规则10(未验证)从不
medium完整流水线减去最昂贵的 pass:在缩减的维度集上进行并行 finder fan-out,加上构建/测试和单次验证 pass无上限(已验证)Approve 上限为 Comment从不
high完整流水线:14 个并行 agent → 分片验证 → 迭代反向审计无上限(已验证)Approve / Request changes / Comment使用 --comment

默认值:PR 审查为 high,本地和文件审查为 medium。有效的 --comment 会强制使用 high(发布的评论必须通过验证)——在非 PR 目标上 --comment 会被忽略并发出警告,不会更改 effort。Medium 保留了安全性和测试覆盖率 agent 以及构建/测试,但去掉了对手角色、diff 专家 finder 和反向审计——因此只有第二次审查才能发现的微妙 Critical 可能会漏掉;对安全性敏感或发布前的审查请使用 --effort high。只有 low 是未验证的。Worktree 隔离适用于同仓库 PR 审查;跨仓库 PR 以轻量模式运行(仅 diff,无 worktree 或构建/测试)。Low pass 标记为未验证,不发出结论,也不写入增量审查缓存,因此后续的 --effort high 运行不会被跳过为”已审查”;medium 是已验证的,但其 Approve 上限为 Comment,因为没有对第一次 pass 遗漏的内容进行二次审查。获取 diff 的机制在每个级别都相同——PR 审查始终使用隔离的 worktree 和相同的 base 解析,因此审查永远不会针对错误的 base。一个范围差异仍然存在:增量缓存仅用于 high,因此 high 重新审查可能只覆盖新的 commit(lastCommitSha..HEAD),而 low/medium 始终审查完整的 PR diff。

工作原理

/review 命令运行一个多阶段流水线:

步骤 1: 确定范围 + effort 级别(本地 diff / PR worktree / 文件) 将 diff 捕获到文件 + 将其分割为 chunk 步骤 2: 加载项目审查规则(medium/high) 步骤 3C:low effort:3-6 个行内角度 + gap sweep [0 次 subagent 调用] 步骤 3A:high,<=500 行源码且 <=3200 行总计:14 个 agent [14+ 次 LLM 调用] |-- Agent 0:Issue 保真度与根因归属 |-- Agent 1a:正确性——逐行扫描 | (含语言陷阱 + wrapper 路由检查) |-- Agent 1b:正确性——已移除行为审计 |-- Agent 1c:正确性——跨文件追踪器 |-- Agent 2:安全性 |-- Agent 3a:复用与重复 |-- Agent 3b:层次与抽象适配 |-- Agent 3c:一致性与清晰度 |-- Agent 4:性能与效率 |-- Agent 5:测试覆盖率 |-- Agent 6:无导向审计(3 个角色:6a/6b/6c) |-- Agent 8:Diff 专家 finder(0-2 个,仅在 | diff 领域需要时启用) '-- Agent 7:构建与测试(运行 shell 命令) 步骤 3B:high,>500 行源码或 >3200 行总计:territory × 维度 [N+5..7+3H 次调用] (N 个 chunk,5-7 个全 diff agent,每个重写较多的 源文件 H 有 3 个不变量 agent) |-- 每 ~400 行 diff 1 个 chunk agent(所有维度, | 仅限其 territory,返回覆盖收据) |-- 每个大幅重写的源文件有 3 个不变量 agent | (整个文件;state/timer、counter/ | return/error、config/early-return) |-- Agent 0:Issue 保真度 (整个 diff) |-- Agent 7:构建与测试 (整个仓库) |-- Agent 1b:已移除行为 (整个 diff—— | 跨 chunk 的一半;chunk 保留本地一半) |-- Agent 1c:跨文件追踪器 (整个 diff) |-- Agent 8:专家 finder(整个 diff,0-2 个) '-- 测试覆盖矩阵 (整个 diff) 步骤 4: 去重 --> 分片验证(每个 <=8 个发现) --> 聚合 [ceil(F/8) 次调用,F=发现数] 步骤 5: 迭代反向审计,按 chunk fan-out; 连续 2 轮无新发现后停止(上限 5 轮) 步骤 6: 展示发现 + 结论(high;low pass:仅发现) 规范化发现 -> .qwen/tmp/...-findings.json 步骤 6B:应用发现 + 记录每个发现的结果(仅 --fix) 步骤 7: 提交 PR 审查(行内评论,如请求;仅 high) 步骤 8: 保存报告 + 增量缓存(缓存:仅 high) 步骤 9: 清理(移除 worktree + 临时文件)

步骤 3A/3B/4/5 是 high-effort 流水线;在 --effort low|medium 下,单个行内 pass(步骤 3C)替代它们。

审查 Agent

Agent关注点
Agent 0:Issue 保真度关联 issue 证据、根因归属,以及 PR 是否解决了报告的问题
Agent 1a:逐行扫描遍历每个 hunk 及其所在函数:错误条件、边界差一、缺失 await、语言特定陷阱、wrapper/proxy 路由
Agent 1b:已移除行为审计遍历每行被删除/替换的代码:指出其强制的不变量,并寻找新代码在哪里重新建立了它——包括被移除的 export,其替代通常位于另一个文件且悄悄更改了默认值。在 3B 中以整个 diff 运行(chunk agent 保留本地一半)
Agent 1c:跨文件追踪器遍历每个已更改符号的调用方(consumer 方向)和每个新增字段的读取位置(producer 方向),以及同一 PR 内的被调用方变更
Agent 2:安全性注入、XSS、SSRF、认证绕过、敏感数据泄露
Agent 3a:复用与重复代码库中是否已有此功能?搜索该行为,指出应调用的现有 helper,并标记 diff 遗留的死代码
Agent 3b:层次与抽象修复是否在正确的深度——还是在共享基础设施上贴创可贴、为上游 bug 做下游补偿,或创建只服务一个调用点的抽象?
Agent 3c:一致性与清晰度兄弟一致性(一个并行族成员有而其对应成员没有的 guard)、与引用的本地示例不一致的惯例偏移、误导性名称/注释、不必要的复杂性
Agent 4:性能与效率N+1 查询、内存泄漏、不必要的重渲染、包体积
Agent 5:测试覆盖率diff 中未测试的代码路径、缺失的分支覆盖、薄弱的断言
Agent 6:无导向审计3 个并行角色(攻击者 / 凌晨 3 点 oncall / 维护者)—— 捕捉跨维度问题
Agent 7:构建与测试运行构建和测试命令,报告失败
Agent 8:Diff 专家 finder每次审查根据 diff 集中在具有已知失败模式的领域(重连逻辑、模块加载器、调度器、编解码器)时编写 0-2 个额外 finder

三个正确性 agent 是程序化的:每个由其遍历 diff 的方式定义(逐行 / 已删除行 / 跨文件边),而非由 bug 分类法定义——因此它们的覆盖是互补的而非重叠的。同样的推理将代码质量拆分为三个(3a/3b/3c):一个持有六项检查清单的 agent 完成了一项——在大幅重写的文件上衡量,一个持有八项检查清单的 agent 发现了 5 个缺陷中的 1 个,而同一模型分三路后找到了全部 5 个——因此质量清单在问题真正不同的地方被切割。所有 agent 并行运行(Agent 1 启动 3 个程序化变体,Agent 3 启动 3 个清单切片,Agent 6 启动 3 个角色变体并发执行,同仓库 PR 审查共计 14 个并行任务,加上 diff 领域需要时的 0-2 个 Agent 8 finder——因此实际为 14-16 个;Agent 0 在本地 diff 和文件路径审查中跳过,运行 13-15 个;跨仓库轻量模式还跳过 Agent 1c 和 7,运行 12-14 个)。

每个发现必须说明一个失败场景——触发它的具体输入、状态或时序,以及导致的错误结果(对于质量发现,则是具体的代价)。无法说明其场景的发现会在源头被丢弃,验证会通过真实代码重新追踪所声称的场景,而非判断发现的文字描述。

一旦 PR 的源码变更超过 500 行——或总 diff 行数超过 3,200 行,超过此限度后十一个全 diff 阅读器各自过于稀释而无法仔细阅读(这是一个注意力边界,而非更少调用的承诺——重写较多的文件和专家 finder 可能使 3B 花费更多)——这种维度 fan-out 会被territory × 维度 fan-out 替代:diff 被分割为约 400 行的 chunk——边界落在 hunk 边界上,太大的 hunk 仅在顶层声明处分割,永远不会在函数内部——每个 chunk 获得自己的 agent,将每个审查维度仅应用于该 chunk。

该门控故意计算源码行数而非 diff 行数。测试代码、文档和 lockfile 占据了 diff 的大部分——在此仓库最近 40 个合并的 PR 中,中位数 diff 有 41% 是测试——因此基于原始大小的门控会将 173 行的生产代码变更分割为 territory,仅仅因为它附带了 489 行新测试,使该生产代码只有一个审查者而非十个视角(读取 diff 的维度 agent——十二个减去 Issue 保真度和构建与测试)。无论如何分 chunk 都覆盖每一行,包括测试;门控决定的是有多少审查者以及每个审查者的任务。十个读取 diff 的视角都走一个大 diff 会将相同的前几个 hunk 读十遍;每个 chunk 一个 agent 意味着 diff 的每一行恰好有一个负责的审查者。每个 chunk agent 返回一个 Covered: 收据,没有收据的 chunk 会在运行继续前被重新审查——因此”无阻塞问题”永远不可能对无人阅读的代码报告。

一个被大幅重写的源码文件(300+ 行的现有文件中 40%+ 是新内容,或有 800+ 行变更)还会获得三个全文件不变量 agent。测试和生成的文件永远不符合条件——检查清单询问的是字段、timer 和错误分类,而重写后的测试文件没有这些。其 bug 通常不在任何单个 hunk 内部,而在_新行之间_——文件顶部附近设置的 timer 和两千行以下的 teardown 路径。每个 agent 阅读整个变更后的文件并遍历固定检查清单的两到三项:每个退出路径都清除的可变字段、每个关闭都取消的 timer(且取消不会丢弃已捕获的数据)、map 插入与删除匹配、每次入口都递增的重试计数器、实际被检查的状态返回值、穷举分类为永久性与暂时性的错误代码、每条路径都遵循的 config 字段,以及跳过必需副作用的 early return。

检查清单故意分三路。让一个 agent 对 2,400 行的文件执行全部八项检查只能做好其中一项;三个 agent 各执行两到三项检查则全部完成。Chunk agent 不能替代这一点——在 PR #6457 上,它们将这些缺陷中的每一个都包含在分配的 territory 内但没有报告任何一个。它们缺少的不是行数,而是问题。

发现以分片批次进行验证(每个验证 agent 最多 8 个发现,全部同时启动)。验证器只有通过引用与之矛盾的代码(或当 diff 自身的注释将被标记的行为记录为有意为之)时才能拒绝 Critical;任何不够确定的内容会被降级为低置信度而非删除——一个被静默拒绝的 Critical 对后续每个阶段都不可见,而降级后的仍然会到达人类手中。验证后,迭代反向审计按 chunk 每轮一个审计员进行 fan-out 来查找遗漏,每个审计员拥有累计发现列表。循环在连续两轮无新发现后停止(或 5 轮硬性上限——如实报告而非报告为收敛)。一轮无新发现不是收敛的证据,反向审计的发现与其他发现一样经过验证。

严重级别

严重级别含义是否作为 PR 评论发布?
Critical合并前必须修复(bug、安全问题、数据丢失、构建失败)是(仅限高置信度)
Suggestion建议改进是(仅限高置信度)
Nice to have可选优化否(仅终端显示)

低置信度的发现会显示在终端单独的”需要人工审查”部分,且永远不会作为 PR 评论发布。

Worktree 隔离

审查 PR 时,/review 会创建一个临时的 git worktree(.qwen/tmp/review-pr-<number>),而不是切换你当前的分支。这意味着:

  • 你的工作树、暂存更改和当前分支不会被修改
  • 依赖项安装在 worktree 中(如 npm ci),以确保构建/测试正常工作
  • 构建和测试命令在隔离环境中运行,不会污染本地构建缓存
  • 如果出现任何问题,你的环境不受影响——只需删除 worktree
  • 审查完成后,worktree 会自动清理
  • 如果审查被中断(Ctrl+C、崩溃),下次对同一 PR 执行 /review 时,会在重新开始前自动清理过期的 worktree
  • 审查报告和缓存保存在主项目目录中(而非 worktree)

跨仓库 PR 审查

你可以通过传递完整 URL 来审查其他仓库的 PR:

/review https://github.com/other-org/other-repo/pull/456

这将在轻量级模式下运行——无 worktree,无构建/测试。审查仅基于 diff 文本(通过 GitHub API 获取)。如果你具有写权限,仍可发布 PR 评论。

能力同仓库跨仓库
LLM 审查(Agent 0、1a、1b、2-6 + 验证 + 迭代反向审计)
Agent 1c:跨文件追踪器❌(无本地代码库可供搜索)
Agent 7:构建与测试❌(无本地代码库)
Agent 8:Diff 专家 finder(0-2 个,领域需要时启用)✅(仅需 diff)
PR 行内评论✅(如果你具有写权限)
增量审查缓存

PR 行内评论

使用 --comment 将发现直接发布到 PR 上:

/review 123 --comment

或者,在运行 /review 123 后,输入 post comments 以发布发现,而无需重新运行审查。

发布的内容:

  • 高置信度的 Critical 和 Suggestion 发现,作为特定行的行内评论,每个以 **[Critical]****[Suggestion]** 为前缀,以便区分阻塞问题和建议
  • 修复为单个局部编辑时,附带一个可一键应用的 ```suggestion 代码块
  • 对于 Approve/Request changes 结论:包含结论的审查摘要
  • 对于 Comment 结论且所有行内评论已发布:无单独摘要(行内评论已足够)
  • 每条评论底部的模型和 CLI 版本归属说明(例如,— qwen3-coder via Qwen Code /review (v0.21.2)

仅在终端显示的内容:

  • Nice to have 发现
  • 低置信度发现

自己提交的 PR: GitHub 不允许你对自己的 pull request 提交 APPROVEREQUEST_CHANGES 审查——两者都会因 HTTP 422 失败。当 /review 检测到 PR 作者与当前认证用户匹配时,它会自动将 API 事件降级为 COMMENT,无论结论如何,从而确保提交成功。终端仍会显示真实的结论(“Approve” / “Request changes” / “Comment”)——只有 GitHub 端的审查事件被中和。实际发现仍会作为特定行的行内评论出现,因此实质性反馈保持不变。

重新审查带有先前 Qwen Code 评论的 PR:/review 在已有先前 Qwen Code 审查评论的 PR 上运行时,它会在发布新评论前对它们进行分类。只有同行重叠(现有评论与新发现在相同的 (path, line) 上)才会提示你确认——这是你会在同一代码行上看到视觉重复的情况。来自较早提交的评论、已回复的评论(视为已解决)以及与新发现不重叠的评论会被静默跳过,并在终端输出一行日志让你知道过滤了什么。

APPROVE 前的 CI / 构建状态检查: 如果结论是 “Approve”,/review 会在提交前查询 PR 的 check-runs 和 commit statuses。如果有任何检查失败(或所有检查仍在 pending),API 事件会自动从 APPROVE 降级为 COMMENT,并在审查正文中说明原因。原因:LLM 审查是静态读取代码,无法看到运行时测试失败;在 CI 标红时批准会产生误导。行内发现仍会照常发布。如果你仍想批准(例如,已知的 CI 偶发失败),请在验证后手动提交 GitHub 批准。

应用发现(--fix

--fix--comment 的镜像。--comment 写入 pull request,因此需要一个;--fix 写入工作树,因此需要一个在审查结束后仍然存在的:

/review --fix # 本地未提交的更改 /review src/auth.ts --fix # 单个文件

PR 目标上会被忽略并发出警告——PR 审查在临时 worktree 中运行,审查结束后即被删除,因此在那里”修复”的编辑会在几分钟后被丢弃。请改用 --comment 发布发现。

有效的 --fix 将 effort 下限设为 medium,因为它编辑你的文件而 low 不进行验证:应用未验证的发现与发布未验证的发现是同样的错误,只是目标从某人的 PR 变成了你的工作树。它不会强制 high——medium 的发现已经过验证,而 high 增加的反向审计寻找的是_遗漏_的发现,这不是决定是否应用某个发现时所依赖的。

审查后,每个发现通过 edit 工具应用,然后逐一记录结果,共三种:

结果含义是否仍需处理?
fixed编辑已在你的工作树中
skipped真实问题,未应用——原因会一并报告
no_change_needed发现是错误的,或代码已经处理了它

当发现的修复会改变预期行为、需要在审查 diff 之外进行更改,或在二次检查时被确认为误报时,该发现会被跳过。

每个发现都会有一个结果,这是强制执行而非请求的。 账本通过 qwen review findings --outcomes 进行,它拒绝不覆盖所有发现的结果集——一个修复了九个发现中的六个并报告六个的修复器并没有对任何一个撒谎,它只是静默缩短了列表,你将无法看到消失的三个。

发现作为数据

已确认的发现在其他任何消费者使用之前被规范化为 .qwen/tmp/qwen-review-<target>-findings.json——终端报告、保存的 Markdown 报告和 PR 审查 JSON 都读取这一个产物,而非重新输入列表。每个发现携带唯一的 id(结果和 resolved 锚点的连接依据)、severityconfidencesourcesummary、限制在 60 个字符以内用于列表渲染的 shortSummaryfailureScenario,以及一个或多个 locations——按模式聚合的发现为每次出现保留一个 location,因此每个仍然获得自己的行内评论。

首先,审查会确认它在运行你的代码。 每个 qwen review … 步骤运行的是打包后的 bundle,而非工作树,因此自上次构建以来编辑过的审查命令不会生效,运行衡量的是旧行为。构建会记录它所打包的审查源码的摘要;parse-args 重新推导并进行比较,drive 也会再次检查,因为 verifier 简报直接将 agent 发送到那里而跳过了步骤 1。不匹配时会在 stderr 上说明 bundle 不是从这些源码构建的,以及如何重新构建。此检查在 CLI 解析为打包后的 dist/cli.jsqwen 二进制文件,或 node dist/cli.js)时运行;运行未打包输出的启动器(如 npm startnpm run dev)会跳过它。两种无法比较的情况会被区别对待:构建早于摘要记录的 checkout 会被告知检查无法运行及原因,而已安装的包——没有可比较的源码——则静默跳过。摘要覆盖审查命令、注册这些命令的文件、它们从目录外部导入的审查专用 lease,以及打包的审查 skill;它不跟踪这些文件导入的共享 helper,因此检查通过意味着审查代码与 bundle 匹配,而非整棵树都匹配。

基础树已经失败的 Critical 会被搁置,而非归档。 当测试命令失败且 merge base 可以构建时,test-delta 会记录哪些失败文件在没有 pull request 的情况下也会失败。规范化会读回该测量结果(qwen review findings --test-delta,与 --outcomes 并列):一个自身文本提到了这些文件之一的 Critical 会被降级为 Suggestion,保留其证据,获得降级它的测量结果和 heldByMeasurement 字段,并宣布降级。已经标红的测试不是这个 pull request 导致标红的测试——如果它现在因_新_原因失败,说明是哪个测试,引用双方,并重新以 Critical 归档:已经携带该测量结果但仍被提升的发现会保持你放置的位置。

该命令在写入时进行验证:重复的 id、没有失败场景的发现、空的 locations 数组或未知的 severity 都是错误,而非被静默损坏的条目。

PR 评论中的证据图片

GitHub 的 API 无法将图片附加到审查评论,因此 /review 可以将证据图片(TUI 屏幕截图、渲染输出比较)托管到你指定的仓库,并在评论中以 commit-pinned URL 引用它们。

export QWEN_REVIEW_ASSETS_REPO=your-org/your-repo # 你可以推送的仓库 /review 123 --comment

维护者通常将其指向被审查的仓库;其他人可以使用 fork 或临时仓库。图片会落在 pr-assets/<pr>-review 分支上,使用内容哈希命名,评论通过 commit-pinned URL 引用它们——即使分支后续移动也是不可变的,且在 GitHub Enterprise 上同样有效。

对于 GitHub 触发的审查(PR 审查工作流),相同的变量通过同名的仓库变量进行配置:未设置变量时,工作流传递空值,发布会被拒绝——不会有任何变化。在仓库的 Actions 变量中设置 QWEN_REVIEW_ASSETS_REPO(通常指向仓库自身)的维护者可以启用审查评论嵌入捕获的 PNG;当变量指向同一仓库时,它写入的分支会由 visuals 清理工作流清理,而 fork 或临时目标则自行管理保留策略。

发布与评论发布受到完全相同的限制:没有指定仓库则不发布,未授权的运行(无有效的 --comment)与 submit 一样被拒绝。仅接受图片类型(SVG 被故意排除),有大小限制,每个文件的字节必须与其扩展名声称的格式匹配——错误标记或无法识别的内容会被拒绝。manifest 记录每个推送的文件。没有指定目标时,发现在终端和保存的报告中将证据保留为本地文件路径——不会出错,评论只是保持纯文本。

后续操作

审查后,上下文感知的提示会以 ghost text 形式出现。按 Tab 键接受:

审查后的状态提示发生的情况
本地审查,未传递 --fixfix these issuesLLM 交互式修复每个发现
包含发现的 PR 审查post comments发布 PR 行内评论(不重新审查)
PR 审查,无发现post comments在 GitHub 上批准 PR(LGTM)
本地审查,全部通过commit提交你的更改

注意:fix these issues 仅适用于本地审查,原因与 --fix 相同——对于 PR 审查,worktree 会在审查后清理,因此不支持审查后的交互式修复——请使用 --commentpost comments 来发布发现。当传递了 --fix 时,发现已经携带结果,不会提供修复提示。

项目审查规则

你可以为每个项目自定义审查标准。/review 按以下顺序从这些文件中读取规则:

  1. .qwen/review-rules.md(Qwen Code 原生)
  2. .github/copilot-instructions.md(首选)或 copilot-instructions.md(回退——仅加载其中一个,不会同时加载)
  3. AGENTS.md## Code Review 部分
  4. QWEN.md## Code Review 部分

规则会作为附加标准注入到 LLM 审查 agent(0-6)中。对于 PR 审查,规则从基础分支读取,以防止恶意 PR 注入绕过规则。

仓库上下文

仓库可以通过将严格的 JSON manifest 提交到 .qwen/review-context.json,为审查者提供有界的、仓库特定的指导。在 medium 或 high effort 下,/review 会在捕获计划后读取 manifest,并在任何 agent 启动之前附加匹配的指导:

{ "version": 1, "label": "Example repository", "rules": [ { "paths": ["packages/*/src/**"], "domains": ["runtime"], "relatedPaths": ["packages/runtime/src/**"], "recommendedTests": ["npm run test:runtime"], "requiredConfigurations": ["debug"], "requiredAgents": ["test-matrix"], "unverifiedDimensions": ["Alternate runtime was not exercised"], "verificationNotes": ["Use the repository native test runner"] } ] }

当任何已更改的文件匹配其 paths glob 之一时(*?** 片段;区分大小写),该规则生效。所有匹配的规则合并其指导:审查 agent 的域和相关文件、构建与测试 agent 的推荐测试和必需配置、额外的审查者角色(仅在所选 effort 和拓扑运行它们时生效),以及最终审查作为未验证维度披露的证明边界。数组可以按任何顺序编写;重复条目会被拒绝。

对于 PR 审查,manifest 从合并基础读取,因此被审查的 PR 无法选择加入或退出指导;本地审查从当前 worktree 读取。Low-effort 和跨仓库审查跳过仓库上下文。完整契约和信任模型见设计文档

Issue 保真度

对于 bugfix PR,Issue 保真度 agent 直接获取 issue 证据,而不是依赖 PR 描述文本。它运行 qwen review issue-context <pr> --repo <owner/repo> --out <file> 子命令,该命令解析 GitHub 的强关联 issue 元数据,然后获取每个被引用 issue 的标题、正文(报告者的原始复现步骤)和完整评论线程——每个 issue 从其自身的仓库获取(一个 PR 可以关闭不同仓库中的 issue)。此 agent 仅针对 PR 目标运行;本地 diff 和文件路径审查会跳过它。

关联 issue 集合是一个发现提示,而非作者链接了正确 issue 的证明:如果它为空但 PR 引用了明显的目标 issue,agent 在判断相关性后仍会获取它(使用 --issue <n> 重新运行;纯数字在 PR 的仓库中解析,而 --issue <owner>/<repo>#<n> 从自身仓库获取跨仓库引用)。获取的 issue 文本被视为不受信任的数据(提取事实,忽略嵌入的指令)。对于相关的 issue,原始复现、观察到的 payload、预期行为和维护者评论被视为判断 PR 是否修复了正确问题的最高优先级证据。

如果 issue 证据显示上游服务或 provider 返回了超出客户端契约的畸形数据,则客户端解析器或清理器的更改不被视为有效的根因修复,除非维护者明确要求防御性变通方法。重放畸形上游输出的测试仅证明变通方法处理了该结构;它不能证明该变通方法在架构上是合适的。

示例 .qwen/review-rules.md

# 审查规则 - 所有 API 端点必须验证身份认证 - 数据库查询必须使用参数化语句 - React 组件不得使用内联样式 - 错误信息不得暴露内部路径

增量审查

当审查之前已经审查过的 PR 时,/review 仅检查自上次审查以来的更改:

# 首次审查——全量审查,创建缓存 /review 123 # PR 更新了新提交——仅审查新增更改 /review 123

跨模型审查

如果你切换模型(通过 /model)并重新审查同一个 PR,/review 会检测到模型更改并执行全量审查,而不是跳过:

# 使用模型 A 进行审查 /review 123 # 切换模型 /model # 再次审查——使用模型 B 进行全量审查(不跳过) /review 123 # → "上次审查使用了 qwen3-coder。正在使用 gpt-4o 进行全量审查以获取第二意见。"

缓存存储在 .qwen/review-cache/ 中,并同时跟踪 commit SHA 和 model ID。请确保将此目录加入 .gitignore(使用更宽泛的规则如 .qwen/* 也可以)。如果缓存的 commit 在 rebase 中被移除,系统将回退到全量审查。只有 high-effort 审查会查阅或写入缓存——--effort low|medium 的快速 pass 永远不会被视为”已审查”。

审查报告

对于同仓库审查,结果会作为 Markdown 文件保存在项目的 .qwen/reviews/ 目录中(跨仓库轻量级审查会跳过报告持久化):

.qwen/reviews/2026-04-06-143022-pr-123.md .qwen/reviews/2026-04-06-150510-local.md

报告包含:时间戳、diff 统计、构建/测试结果、所有发现及其验证状态,以及最终结论。章节标题和描述性文本遵循输出语言偏好;技术标识符(SHA、文件路径、门控名称、发现 id)保持原样。

Medium 和 high-effort 审查还会保存一个具有相同文件名但扩展名为 .json 的结构化 JSON 伴随文件(例如 2026-04-06-143022-pr-123.json),包含规范化发现和组合结论作为数据。Qwen Code 的 Web Shell 将该文档渲染为可筛选发现的交互式审查视图;Markdown 报告则保持为人类可读的存档。

流水线的确定性部分——参数解析(qwen review parse-args)和事件/正文决策(qwen review compose-review)——是经过测试的子命令而非提示文本,因此 --effort 语法、--comment 强制、结论上限和降级行为由单元测试固定,不会随模型漂移。

GitHub Enterprise: 在非 github.com 主机上审查 PR URL 时,该主机的每个 GitHub 调用都会被路由——审查子命令(match-remotemetafetch-prpr-contextcomment-statusissue-contextfetch-diffcomment-bodyplan-difftest-planpresubmitcompose-reviewsubmitpublish-assets)接受 --host 并在代码中设置,因此遗忘的 host 不会静默将审查重定向到 github.com

每次运行以一行机器可读的行结束(Review complete: <target> — <disposition>),因此脚本和 CI 包装器可以通过单个 ^Review complete: 匹配来检测完成和结果。

无头运行(qwen review run

/review 是交互式的。当脚本或 CI 作业需要运行审查并根据其结果采取行动时,请使用无头包装器:

qwen review run [target] [--json] [--fail-on request-changes] [--comment] [--quiet]

target 是 PR 编号、PR URL 或文件路径;省略则审查本地工作树。该命令以非交互方式运行当前构建的 CLI(stdin 关闭,因此斜杠命令检测仍然有效),将子进程的进度流式输出到 stderr,并将结论打印到 stdout——或使用 --json 输出完整的结果对象。结论从 compose-review 写入的产物中读取(与 skill 视为结论权威的 JSON 相同),而非从模型的文本中解析。

退出代码是门控应读取的契约:

退出码含义
0审查已完成(无论其决定如何)
1未达到结论——子进程失败、超时或未留下组合产物
3已完成且结论为 REQUEST_CHANGES并且设置了 --fail-on request-changes(可选阻塞)

3(而非 2)让门控区分”审查在阻塞”和”工具出错”——yargs 已将 1 用于使用错误——无需解析任何输出。--timeout-minutes(默认 120,下限为 1)终止挂起的审查并退出 1,取消命令(Ctrl+C / SIGTERM)会终止审查的进程组而非使其成为孤儿。

有时间预算的运行还可以导出截止时间,以便审查在仍有时间进行验证、组合和发布时停止其开放式反向审计循环:QWEN_REVIEW_DEADLINE_EPOCH 是运行将被终止的 Unix 秒时刻,QWEN_REVIEW_DEADLINE_RESERVE_SECONDS(默认 3600;0 仅保留轮次估计)是为最后一轮的验证、compose-review 和提交保留的尾部时间。当剩余预算不足以容纳另一轮加上该尾部时,轮次构建器将拒绝构建,组合结论会披露截断的审计(否则为 Approve 的结论被上限为 Comment)。缺失或格式错误的截止时间会使审查不受门控——外部超时仍然限制运行。

在该保留时间内嵌套了一个更小的组合下限QWEN_REVIEW_DEADLINE_COMPOSE_FLOOR_SECONDS(默认 1200;0 完全禁用此门控,在每个时间点包括超过截止时间后均生效)。保留时间是一个数字,涵盖”验证最后一轮 组合 提交”,这适合常规的逐发现重新追踪,但不适合安全性审查——后者的验证会无限制地重新运行真实的文件系统/git 工作负载。因此验证器——而非轮次构建器——受此下限门控:一旦剩余时间等于或低于下限,agent-prompt --role verify 会拒绝构建(一行 VERIFY BUDGET:,退出码 4),手头的发现保留其未验证标签(这会上限结论),然后 compose-review 和提交运行。下限严格低于保留时间,因此正常的运行会先触发反向审计门控而永远不会到达它;它是保留时间无法约束的那段时间的保障。

跨文件影响分析

专用的跨文件追踪器(Agent 1c)端到端负责此遍历。当代码更改修改了导出的函数、类或接口时,它会搜索所有调用方并检查兼容性:

  • 参数数量/类型更改
  • 返回类型更改
  • 移除或重命名公共方法
  • 破坏性 API 更改

它还遍历 producer 方向:diff 添加的每个字段、选项或可选参数都被追踪到其读取位置——包括 diff 从未涉及的文件。一个活跃的代码路径读取了没有任何东西填充的字段,意味着它门控的功能静默地什么都不做,这会在读取位置被标记为 Critical。

对于大型 diff(>10 个修改的符号),consumer 方向分析优先处理签名发生更改的函数;producer 方向永远不受预算限制,因为未更改的签名正是其要点。

审查预算

流水线中在 diff 大小上具有弹性的部分根据其进行缩放,缩放被写入 diff 计划,因此每个阶段读取一个数字而非各自独立决定:

预算字段作用范围缩放方式
inlineAngleslow 运行多少个角度(步骤 3C)3 个,加上每 60 行源码 1 个,上限为存在的 6 个角度
sweeplow 的 gap sweep 是否运行低于 25 行源码时关闭
specialistCapAgent 8 的上限低于 80 行源码时为 0,否则为 2
verifyShard每个验证 agent 的发现数固定为 8——是验证器的属性,而非 diff 的属性

它故意不做两件事。它永远不会将某个维度缩放到零:审查需要哪些 agent 由名册决定,名册读取 effort 级别,因此小 diff 仍然获得安全性 pass 和测试覆盖率 pass。它读取的是源码行数,而非 diff 行数——一个 40 行的生产代码变更附带 900 行新测试是一个小变更,同样的推理已经支配了 territory-fan-out 门控。

下限设置的原因:在九行的拼写错误修复上,六个行内遍历中有五个是对无物的遍历,而 sweep——一个新读者寻找第一次 pass 未覆盖的内容——在第一次 pass 已覆盖所有内容时没有什么可寻找的。Agent 8 的下限是实质性考量:“一个领域主导 diff”是一个判断,对四十行做出的判断每次都会找到一个主导领域,因为四十行通常就是一件事。

Token 效率

High-effort 流水线限制每个阶段(分片大小、审计轮次),但总调用数随发现数缩放——ceil(F/8) 个验证分片——以及在 3B 下随 chunk 数缩放(反向审计按 chunk 按轮次运行)。典型的 3A 概况:

阶段LLM 调用次数说明
审查 agent(步骤 3)14 (+0-2)并行运行;跨仓库跳过 Agent 1c 和 7(12),本地/文件跳过 Agent 0(13)
分片验证(步骤 4)ceil(F/8)F = 发现数;每个验证 agent 最多 8 个,同时启动
迭代反向审计(步骤 5)2-5 (3A);轮次 × chunk (3B)连续 2 轮无新发现后停止(上限 5);3B 按 chunk 按轮次 fan-out 一个审计员
总计~17-23 (~15-22)3A 同仓库:~17-23(典型 ~17-19);跨仓库或本地/文件:~15-22;3B 随 chunk 缩放(参见 DESIGN.md)

大多数 PR 收敛到范围的下限;上限防止在极端情况下成本失控。在 --effort low 下,审查完全在行内运行——0 次 subagent 调用——每个角度遍历 diff 一次而非总共一次。

不会标记的内容

审查会刻意排除以下内容:

  • 未更改代码中预先存在的问题(仅关注 diff)
  • 格式化程序会自动规范的样式或格式,或符合代码库约定的命名——但 linter 或类型检查器会标记的实质性问题(如未使用的变量、不可达代码、类型错误)除外,这些仍在审查范围内
  • 没有实际问题的主观”建议考虑做 X”
  • 未修复 bug 或风险的微小重构
  • 缺失的文档,除非逻辑确实令人困惑
  • 现有 PR 评论中已讨论过的问题(避免重复人工反馈)

设计理念

沉默胜于噪音。 每条评论都应值得读者花时间阅读。

  • 如果不确定某个问题是否真的是问题 → 不要报告
  • 每个发现都说明一个具体的失败场景(触发 → 错误结果)或具体的代价——无法说明的发现会在到达你之前被丢弃
  • 跨 N 个文件的相同模式 → 聚合为一个发现
  • PR 评论仅包含高置信度内容(且仅来自 high-effort、已验证的审查)
  • 排除符合代码库约定的表面样式/格式问题
Last updated on