← 返回信息流
AI 资讯Hacker News·9 天前

规模化编排 AI 代码审查

原标题:Orchestrating AI code review at scale

速览

本文聚焦于在大型软件工程中实施 AI 代码审查的策略。重点讨论了如何编排和优化 AI 工具,以应对代码库规模扩大带来的挑战。旨在提升代码质量并加速开发迭代周期。

AI 深度解读

规模化编排 AI 代码审查:Cloudflare 的工程实践

背景

代码审查(Code Review)是捕获 Bug 和共享知识的有效机制,但同时也是导致工程团队瓶颈的最可靠方式之一。在一个典型的审查流程中,合并请求(Merge Request, MR)会进入队列,审查者最终进行上下文切换以阅读差异(diff),留下一些关于变量命名等细枝末节的意见,作者随后回复,循环往复。在 Cloudflare 的内部项目中,首次审查的中位等待时间往往以小时计。

起初,Cloudflare 尝试了与其他公司类似的路径:试用多种 AI 代码审查工具。虽然许多工具表现良好且提供了不错的自定义配置能力,但对于 Cloudflare 这样规模的组织而言,这些现成工具在灵活性和定制化方面仍显不足。

随后,团队尝试了更直接的方法:提取 Git diff,将其放入一个粗糙的提示词(Prompt)中,要求大语言模型(LLM)查找 Bug。结果正如预期般嘈杂:充斥着模糊的建议、幻觉出的语法错误,以及对已经包含错误处理的函数建议“考虑添加错误处理”。团队很快意识到,这种朴素的摘要方法在复杂的代码库中无法达到预期效果。

因此,Cloudflare 没有从头构建一个单体式的代码审查智能体,而是决定围绕开源编码智能体 OpenCode 构建一个原生于 CI(持续集成)的编排系统。

核心内容

Cloudflare 构建了一套协调的 AI 智能体自助餐(smörgåsbord),当工程师打开合并请求时,系统会对其进行初步审查。系统不依赖拥有巨大通用提示词的单一模型,而是启动多达七个专门的审查者,分别覆盖安全性、性能、代码质量、文档、发布管理以及内部《工程法典》(Engineering Codex)的合规性。这些专家智能体由一个协调者智能体(Coordinator Agent)管理,负责去重其发现、判断问题的实际严重程度,并发布单个结构化的审查评论。

该系统已在内部数万个合并请求中运行,能够批准干净的代码,以惊人的准确性标记真实 Bug,并在发现严重问题或安全漏洞时主动阻止合并。以下是其技术架构和实现细节的深度解析。

架构设计:插件化一切

为了构建能在数千个仓库中运行的内部工具,硬编码版本控制系统或 AI 提供商会导致系统在短期内需要重写。Cloudflare 需要支持 GitLab(以及未来的其他系统)、不同的 AI 提供商以及不同的内部标准,且各组件之间无需相互知晓。

系统基于可组合的插件架构构建,入口点将所有配置委托给相互组合的插件,以定义审查的运行方式。

插件生命周期与执行流

每个插件实现 ReviewPlugin 接口,包含三个生命周期阶段:

  1. Bootstrap(引导):并发运行,非致命。如果模板获取失败,审查将继续而不会中断。
  2. Configure(配置):顺序运行,致命。如果 VCS 提供商无法连接 GitLab,则没有继续作业的必要。
  3. postConfigure(后置配置):在配置组装后运行,处理异步工作,如获取远程模型覆盖。

ConfigureContext 为插件提供了影响审查的控制面。插件可以注册智能体、添加 AI 提供商、设置环境变量、注入提示词片段以及更改细粒度的智能体权限。没有任何插件能直接访问最终配置对象,它们通过上下文 API 贡献,核心组装器将所有内容合并到 OpenCode 消耗的 opencode.json 文件中。

这种隔离确保了 GitLab 插件不会读取 Cloudflare AI Gateway 配置,反之亦然。所有 VCS 特定的耦合都隔离在单个 ci-config.ts 文件中。

典型内部审查的插件清单

| 插件名称 | 职责 | | :--- | :--- | | @opencode-reviewer/gitlab | GitLab VCS 提供商、MR 数据、MCP 评论服务器 | | @opencode-reviewer/cloudflare | AI Gateway 配置、模型层级、故障转移链 | | @opencode-reviewer/codex | 针对工程 RFC 的内部合规性检查 | | @opencode-reviewer/braintrust | 分布式追踪和可观测性 | | @opencode-reviewer/agents-md | 验证仓库的 AGENTS.md 是否更新 | | @opencode-reviewer/reviewer-config | 来自 Cloudflare Worker 的远程按审查者模型覆盖 | | @opencode-reviewer/telemetry | 发后即忘(Fire-and-forget)的审查跟踪 |

底层 OpenCode 的使用

选择 OpenCode 作为编码智能体的原因包括:

  • 内部广泛使用,团队对其工作原理非常熟悉。
  • 开源特性允许上游贡献功能/修复,并便于调查问题(截至撰文时,Cloudflare 工程师已在开源项目中提交了超过 45 个 PR)。
  • 优秀的开源 SDK 使得构建无缝工作的插件变得容易。
  • 最关键原因:其结构为“服务器优先”,文本用户界面和桌面应用作为客户端。这使得团队能够以编程方式创建会话、通过 SDK 发送提示词,并从多个并发会话中收集结果,而无需绕过 CLI 接口。

编排的两层结构

  1. 协调者进程(Coordinator Process): 使用 Bun.spawn 将 OpenCode 作为子进程生成。提示词通过 stdin 传递而非命令行参数,以避免 Linux 内核的 ARG_MAX 限制(此前在大型合并请求中曾遇到 E2BIG 错误)。进程以 --format json 运行,所有输出作为 JSONL 事件出现在 stdout 上。

    const proc = Bun.spawn(
      ["bun", opencodeScript, "--print-logs", "--log-level", logLevel,
       "--format", "json", "--agent", "review_coordinator", "run"],
      {
        stdin: Buffer.from(prompt),
        env: {
          ...sanitizeEnvForChildProcess(process.env),
          OPENCODE_CONFIG: process.env.OPENCODE_CONFIG_PATH ?? "",
          BUN_JSC_gcMaxHeapSize: "2684354560", // 2.5 GB 堆限制
        },
        stdout: "pipe",
        stderr: "pipe",
      },
    );
    
  2. 审查插件(Review Plugin): 在 OpenCode 进程内部,运行时插件提供 spawn_reviewers 工具。当协调者 LLM 决定审查代码时,会调用此工具,通过 OpenCode 的 SDK 客户端启动子审查者会话。

    const createResult = await this.client.session.create({
      body: { parentID: input.parentSessionID },
      query: { directory: dir },
    });
    // 异步发送提示词(非阻塞)
    this.client.session.promptAsync({
      path: { id: task.sessionID },
      body: {
        parts: [{ type: "text", text: promptText }],
        agent: input.agent,
        model: { providerID, modelID },
      },
    });
    

    每个子审查者在各自的 OpenCode 会话中运行,拥有独立的智能体提示词。协调者不查看也不控制子审查者使用的工具。子审查者可以自由读取源文件、运行 grep 或搜索代码库,并最终以结构化 XML 返回其发现。

关键要点

  • 从单体到编排:单一的 LLM 提示词无法应对大规模工程复杂性,Cloudflare 转向了基于多个专用智能体的编排系统。
  • 插件化架构:通过可组合的插件架构隔离 VCS 提供商(如 GitLab)和 AI 提供商(如 Cloudflare AI Gateway),确保系统的可扩展性和维护性。
  • 专用智能体分工:启动多达七个专门审查者,分别负责安全、性能、合规性等特定领域,由协调者智能体进行去重和严重程度评估。
  • OpenCode 的核心地位:利用 OpenCode 的“服务器优先”架构和 SDK,实现了编程式会话管理和并发结果收集,避免了 CLI 接口的限制。
  • 工程细节优化:通过 stdin
查看原文 →blog.cloudflare.com