尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

搭建AI PR审查Agent:从Uber 70%接管率到成本零增长的工程实践

搭建AI PR审查Agent:从Uber 70%接管率到成本零增长的工程实践 Uber 的工程师团队最近聊到的这个工程实践非常值得拆开看内部已经有 70% 的代码 PR 由 AI Agent 接管处理同时 AI 相关账单没有出现明显增长。这两个数据放在一起比单纯说一句“我们团队用 AI 写代码”有意义得多。70% 说明自动化已经跑通了真实生产流程不只是做个 Demo账单零增长说明这套系统不是靠硬堆预算烧出来的背后一定有明确的规则过滤、模型路由和成本降级策略。这篇文章不打算停留在新闻解读层面而是直接拆解一个问题如果我们也想在自己的代码仓库里搭一个类似的 PR 审查 Agent应该怎么做内容会覆盖这个案例的关键特征、最小可运行的系统设计、GitHub/GitLab 的 PR 数据获取、LLM 调用的 agent 循环、成本控制手段以及上线前必须注意的代码合规边界。读完你至少能搭出一个可以跑在本地方仓库上的审查机器人并知道怎么观察它的 token 消耗和审查质量。1. 核心能力速览先说清楚这不是一个在 GitHub 上搜到就能一键启动的开源项目而是一类工程实践。它的核心组件包括代码仓库 Webhook、PR Diff 获取、规则引擎、LLM Agent 调用、结果回写和成本控制。下面这张表总结了这类系统的关键能力节点能力项说明核心功能自动获取 PR 变更内容生成 review 建议并回写到 PR 评论主要卖点替代重复性人工 review覆盖 70% 左右的常规 PR成本可控实现方式Agent 连接代码仓库 API按规则过滤后调用 LLM再自动发布审查结论硬件门槛不需要 GPU常规 CPU 服务器即可运行核心瓶颈是 LLM API 的 token 和延迟典型依赖Python 3.10、Git、GitHub/GitLab Token、LLM API Key接口能力依赖仓库平台 Webhook 和 REST API也可以封装成 HTTP 服务批量任务支持按 PR 队列异步处理适合低峰期批量 review适合场景规范明确的成熟代码库、团队 review 人力紧张、需要统一代码风格约束不适合场景业务语义极重的遗留代码重构、需要多人线下讨论的架构级 PR从这套能力来看它的价值不是“帮你把代码写完”而是“在代码进主干之前先过一道自动化检查”。常规的 lint、格式、单测覆盖问题交给 Agent 处理人的 review 时间留给真正的架构和业务问题。2. 这个方案到底做了什么70% PR 被 Agent 接管的含义70% 这个数字并不意味着 70% 的合并决定由 AI 来做更合理的理解是70% 的 PR 在打开之后可以由 Agent 完成第一轮或者多轮自动化审查并且审查结果可以直接在 PR 页面上展示给开发者和维护者。换句话说机器先看人再看机器看过的部分人只需要确认有没有误报。这类系统在生产中一般按这个逻辑运行开发者提交 PR仓库 Webhook 把事件推给 Agent 服务。Agent 先拉取 PR 的 diff而不是整个代码库。规则引擎先跑一轮是否改动超过阈值、是否涉及安全敏感文件、是否有明显格式问题。规则处理不了的部分交给 LLM 做语义理解比如“这个函数是否可能空指针”“这里的事务范围对不对”“有没有遗漏异常处理”。Agent 把结论作为 review 评论提交到 PR带上置信度标签。维护者根据 Agent 的建议决定是否合并或者要求修改。“AI 账单零增长”这几个字拆开看主要有三种可能也是这类系统最常见的成本控制路径按需路由只有规则无法覆盖的 PR 才调用大模型简单 PR 用轻量模型或者直接跳过。上下文裁剪不把整个仓库塞给模型只传 diff 以及必要的上下文行token 消耗会小很多。预算熔断与降级当一段时间的 token 消耗超过阈值系统自动降低模型规格或者改为人工处理队列。对普通团队来说这个案例最重要的启发是AI 代码审查能不能落地关键不是模型多聪明而是你的规则、流程、成本预算能不能一起配套。模型只是其中一个环节。3. 适用场景与使用边界搭 PR 审查 Agent 之前先判断自己的团队到底适不适合。3.1 适合的场景规范化程度高的仓库已经有 commit 规范、目录规范、错误处理规范AI 可以按规则去检查一致性。PR 数量多、重复检查多很多 PR 的问题是类似的比如缺少输入校验、日志不完整、边界条件没考虑。Agent 处理这类问题效率很高。review 人力紧张核心维护者时间有限可以靠 Agent 完成第一轮粗筛把明显问题挡回去。团队的代码会经过统一 CI 流程说明你已经有比较完善的流水线Agent 只需要作为其中一环接入。3.2 不适合的场景早期快速原型代码变动大、结构不稳定agent 的误报率会很高容易让团队感到烦躁。架构级和业务决策型 PR涉及重大模块拆分的 PR需要人基于上下文讨论Agent 不能替代。强合规项目银行、医疗、涉密系统的代码如果对数据出域有严格限制外部 LLM API 可能根本不能使用需要先做数据安全评估。3.3 必须注意的边界AI 只做建议不做最终合并决策。风险等级高的时候系统应该主动要求人工复核。代码是公司资产。在把 diff 发送给任何 LLM API 之前确认你是否有权限做这个操作如果没有私有化部署条件优先选择数据协议允许的 API 服务或者做源码脱敏后再发送。不要在未经授权的情况下上传私有仓库代码。很多第三方 API 有数据留存条款团队合规同学必须先确认。审查结果不是绝对正确。LLM 会有幻觉也会有漏检必须有人的兜底环节。4. Agent 拦截 PR 的典型工作流在实现层面PR 审查 Agent 并不是一个“你上传 diff 它返回评论”的静态脚本而是一个可以连接多个工具的 Agent 循环。常见工作流可以拆成下面几段步骤系统行为对应能力触发仓库 Webhook 收到 pull_request 事件GitHub/GitLab Webhook 或轮询预处理拉取 PR 元数据、diff、changed files 列表仓库 REST API规则过滤检查改动文件数量、文件类型、是否触碰关键目录规则引擎上下文构建把 diff 和相邻上下文整理成适合模型输入的格式prompt 模板Agent 推理调用 LLM 生成问题列表和修改建议LLM API结果整理将模型输出解析为结构化 review 评论输出解析器回写在 PR 上创建 review附上严重等级和建议位置仓库 Review API兜底高风险内容转人工普通问题直接展示人工队列这里面最关键的设计点有两个规则过滤决定成本Agent 推理决定质量。如果所有 PR 都进入大模型token 开销会很快变得不可控如果规则过强很多真实问题又会被漏掉。好的做法是让规则引擎负责“低成本拦截”Agent 负责“语义理解和判断”。5. 本地搭建最小可运行的 PR 审查 Agent下面我以一个最小实现为例子演示怎么在自己仓库里跑通“获取 diff → 调用 LLM → 写回 review”的完整闭环。这里以 GitHub 为例GitLab 的接口路径和请求头需要按平台调整。5.1 环境准备建议先准备一台 Linux 或 macOS 服务器或者直接用你本地的开发机也可以。需要满足这些条件Python 3.10 及以上git 命令可用一个测试用 GitHub 仓库最好自己创建避免在正式仓库里测试一个 GitHub Personal Access Token需要repo权限一个 LLM API KeyOpenAI、Anthropic、Azure OpenAI、本地模型服务都可以不需要 GPU不需要 CUDA这类任务的主要消耗是 API 调用时长和 token 数量。mkdir pr-agent-demo cd pr-agent-demo python3 -m venv .venv source .venv/bin/activate pip install requests openai5.2 项目目录结构pr-agent-demo/ ├── agent.py # Agent 主循环 ├── config.py # 配置读取 ├── requirements.txt ├── rules.py # 规则过滤 └── outputs/ # 审查结果日志5.3 获取 PR 的 diffGitHub 的 REST API 支持直接返回一个 PR 的 diff 文本。只要在请求头里指定 Accept 为 diff 格式即可。import os import requests GITHUB_TOKEN os.getenv(GITHUB_TOKEN, ) REPO your-org/your-repo PR_NUMBER 1 headers { Authorization: ftoken {GITHUB_TOKEN}, Accept: application/vnd.github.v3.diff } url fhttps://api.github.com/repos/{REPO}/pulls/{PR_NUMBER} resp requests.get(url, headersheaders, timeout30) if resp.status_code 200: diff_text resp.text print(f拿到 diff长度: {len(diff_text)} 字符) else: print(f获取失败: {resp.status_code}) print(resp.text)如果仓库是私有的要确保 token 有对应权限如果是企业自建 GitHub地址要替换成内网域名。5.4 规则过滤低成本拦截再写一个规则模块先过滤掉明显不需要大模型处理的 PR。MAX_FILES 20 # 改动文件数超过阈值转人工 MAX_CHANGES 2000 # 改动行数过大提示人工介入 SENSITIVE_PATHS [src/auth, src/payments, deploy/] def need_llm_review(changed_files, additions): if len(changed_files) MAX_FILES: return False if additions MAX_CHANGES: return False for path in changed_files: for sensitive in SENSITIVE_PATHS: if path.startswith(sensitive): return True return True规则引擎的实际价值是把不需要模型参与的 PR 挡在 LLM 调用之前。比如只改了 README 的 PR完全不需要花 token 去 review。5.5 调用 LLM 生成 review 建议这里用一个简化版本的 Agent给模型 diff 和固定审查要求让它返回结构化问题列表。注意 prompt 要强调“不能乱猜问题”。from openai import OpenAI client OpenAI( api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL, https://api.openai.com/v1) ) PROMPT_TEMPLATE 你是一个严格的代码审查工程师。请分析下面的 PR diff只输出你确认存在的问题。 格式要求每条问题一行格式为 严重级别|问题描述|建议修改。 严重级别只允许 P0、P1、P2。 如果没有任何问题输出NO_ISSUE diff 内容 {} 请开始输出 def generate_review(diff_text: str) - str: prompt PROMPT_TEMPLATE.format(diff_text[:12000]) # 裁剪长度 resp client.chat.completions.create( modelos.getenv(LLM_MODEL, gpt-4o-mini), messages[{role: user, content: prompt}], temperature0.2, max_tokens3000, ) return resp.choices[0].message.content注意这里对 diff 做 12000 字符的截断只是示例。实际生产场景中应该基于 token 数量动态截断避免超长输入导致成本和延迟飙升。5.6 把 review 结果写回 PR拿到 LLM 输出后把结果作为 review 提交到 PR。def post_review(commit_id: str, review_body: str): url fhttps://api.github.com/repos/{REPO}/pulls/{PR_NUMBER}/reviews headers { Authorization: ftoken {GITHUB_TOKEN}, Accept: application/vnd.github.v3json } payload { commit_id: commit_id, body: AI Agent 自动审查结果\n\n review_body, event: COMMENT } resp requests.post(url, headersheaders, jsonpayload, timeout30) print(resp.status_code, resp.json() if resp.status_code 400 else resp.text)这里的commit_id需要从 PR 详情接口返回的head.sha字段获取。只提交评论不自动 approve 或 request changes保持人工决策权。5.7 主循环组装用一个简单的agent.py把所有步骤串起来import requests def review_pull_request(repo: str, pr_number: int): diff_text, commit_id, changed_files, additions fetch_pr_info(repo, pr_number) if not diff_text: print(无需审查) return if not need_llm_review(changed_files, additions): print(触发规则过滤转人工) return review generate_review(diff_text) if review.strip() NO_ISSUE: print(Agent 未发现明确问题) return post_review(commit_id, review) log_to_outputs(pr_number, review) if __name__ __main__: review_pull_request(your-org/your-repo, int(os.getenv(PR_NUMBER, 1)))这样一个最小可运行的 PR 审查 Agent 就成型了。首次验证时不需要马上接入 Webhook手动设置PR_NUMBER跑一次更稳妥。6. 功能测试与效果验证在正式接入仓库之前先用一个测试 PR 验证系统是否可靠。建议准备一个包含以下问题的测试 PR缺少None判空的函数异常处理空白明显可以合并的重复代码日志缺失的函数入口6.1 测试维度测试项操作预期结果获取 diff运行获取 diff 脚本能拿到完整变更内容规则过滤只改 README 的 PR日志提示“无需审查”规则过滤改动敏感目录文件的 PR走 LLM 审查流程LLM 输出空 diff 或短 diff不调用模型LLM 输出问题明显的 diff输出包含 P0/P1/P2 分级写回 PR审查结束后查看 PR 页出现 AI review 评论稳定性连续跑 20 次不超时、不报错、不重复评论6.2 判断成功标准能稳定拿到 PR 的 diff并且能定位到 commit_id。LLM 返回的问题和真实问题有较高重合度而不是无脑输出一堆“建议”。评论准时出现在 PR 页面且不会重复提交。token 消耗可以在日志中统计出来而不是黑盒。6.3 失败时排查什么拿不到 diff检查 token 权限、仓库可见性、网络策略。LLM 输出内容不稳定调整 prompt要求模型只输出明确问题并给出格式约束。评论没有出现先看调用返回值是不是 4xx/5xx再检查 commit_id 是否正确。重复评论需要在业务层做幂等处理同一 PR 的同一 commit 只允许提交一次。7. 成本控制AI 账单零增长怎么做到“70% PR 被 Agent 接管”听起来很重但现实是如果 100% 的 PR 都调用同一档大模型账单一定会很快涨上去。要做到成本可控核心思路是不要让所有请求走同一条昂贵的链路。7.1 成本估算公式先建立一个基础公式方便自己估算单次审查成本 输入 token 数 × 输入单价 输出 token 数 × 输出单价在实际日志里可以这样统计# 伪代码用于统计 token usage resp.usage print(prompt_tokens:, usage.prompt_tokens) print(completion_tokens:, usage.completion_tokens)7.2 降低 token 消耗的手段规则优先能用脚本检查的先检查比如 eslint、ruff、pre-commit。只有脚本覆盖不了的问题才交给模型。模型路由简单 PR 用小模型复杂 PR 或者安全相关改动才用大模型。路由条件可以按改动文件数、文件目录、diff 长度、是否包含敏感路径判断。diff 上下文裁剪不把整个文件发给模型只发送变更行之前的若干行上下文。缓存公共代码块同一个工具函数在不同 PR 里被重复改动可以把审查结果缓存下来避免重复调用。预算熔断设定一个每日/每周 token 预算超过就用降级模型或者暂时转人工队列。异步批量把非紧急 PR 放到夜间批量处理既能控制 API 并发也能避开高峰期限额。这种设计思路对应到那个案例里“账单单零增长”并不是因为 AI 用得少而是因为大量 PR 根本不会进入大模型队列进入队列的又会根据复杂度匹配不同档位模型成本被整体摊薄了。8. 接口 API 与批量任务设计把 PR 审查 Agent 封装成服务之后你可以用 Webhook 接收仓库事件也可以用定时任务扫描待处理 PR。8.1 封装成 HTTP 服务一个简单的 Flask 服务可以接收 GitHub Webhookfrom flask import Flask, request, jsonify import os app Flask(__name__) app.route(/webhook/pr, methods[POST]) def pr_webhook(): payload request.json action payload.get(action, ) if action not in [opened, synchronize]: return jsonify({status: ignore}) repo payload[repository][full_name] pr_number payload[pull_request][number] # 异步执行审查避免 Webhook 超时 submit_review_task(repo, pr_number) return jsonify({status: accepted}) if __name__ __main__: app.run(host0.0.0.0, port8000)8.2 批量任务队列如果 PR 数量比较多建议不要同步调用 LLM而是把任务写入队列由 worker 逐个消费。简单的做法是把待处理 PR 写入 Redis 队列或者数据库表再启动一个 worker 循环处理。# worker 示例 import time import redis r redis.Redis(host127.0.0.1, port6379, db0) def start_worker(): while True: task r.lpop(pr_review_queue) if task: repo, pr_number task.decode().split(:) review_pull_request(repo, int(pr_number)) else: time.sleep(2) if __name__ __main__: start_worker()8.3 失败重试LLM API 不稳定时常见失败是超时和限流。对这类任务建议设计重试策略超时重试每次间隔递增最多重试 3 次。限流重试等待Retry-After指定的秒数。对重复评论做幂等记录PR编号 head commit sha防止重复提交 review。审查失败时把任务标记为failed转人工处理不要静默丢弃。9. 资源占用与性能观察PR 审查 Agent 不是 GPU 密集型任务它的瓶颈集中在LLM API 延迟、Webhook 响应速度、队列消费速度三个地方。9.1 关键观察指标指标观察方式正常表现LLM 调用延迟API 返回耗时大部分在几秒到几十秒超长 diff 可能更久token 消耗usage 字段累计每 PR 的 token 消耗应保持相对稳定Webhook 响应时间网关日志必须低于平台超时限制否则用异步任务队列积压Redis queue 长度高峰后能逐步消费完不是无限增长错误率API 错误日志低于 1%主要是限流和超时9.2 如何降低资源占用不要同步调用 LLMwebhook 只负责入队。给 LLM 客户端设置超时和最大重试次数。控制并发数避免同时开几十个请求把 API 限额打满。本地日志做轮转避免审查日志占满磁盘。如果部署在服务器上单机 2 核 4G 内存足够跑框架和 Worker真正消耗在外部 API 上。9.3 端口与进程问题如果服务启动后访问不到先确认进程是否在跑再检查端口是否被占用# 检查端口 lsof -i :8000 # 换端口启动 python agent_service.py --port 8001Webhook 回调地址也需要保证仓库平台能访问到。如果仓库在内网Webhook 地址必须是内网可达 URL如果跑在公网务必加签名校验。10. 常见问题与排查方法问题现象可能原因排查方式解决方案拿不到 PR difftoken 权限不足或仓库不存在查看 API 返回状态码和错误信息确认 token scope、仓库名是否为 owner/repo 格式调用 LLM 报 401API Key 配置错误或环境变量未加载打印配置值确认 base_url 是否正确修正 Key 或 base_url评论没有出现在 PR 上commit_id 不正确或 review 接口失败打印响应体看错误信息改用head.sha字段同一 PR 被重复审查幂等逻辑缺失查看日志中 PR 处理次数以 PR 编号加 commit sha 做唯一索引误报太多prompt 约束不够抽样对比人工 review 结果强化 prompt要求只输出确定问题成本上升过快规则过滤没生效检查多少请求进入了 LLM增加规则拦截启用模型路由Webhook 收不到事件地址配置错误或平台无法访问查看平台投递日志使用内网/公网可达地址添加签名校验Agent 长时间卡住LLM 调用无超时或重试机制打印调用耗时和异常加超时、重试和队列超时拒绝11. 最佳实践与使用建议11.1 小流量灰度第一周不要对所有 PR 开启 Agent先选定一个目录或者只处理opened事件等模型输出稳定后再扩大到synchronize事件和其他目录。灰度期间重点观察误报率和工程师反馈。11.2 规则与模型结合能通过脚本解决的问题不要用模型去“判断”。对代码格式、未使用变量、明显越权路径等使用静态检查工具模型只负责语义层面的问题。这样成本低稳定性高。11.3 人工兜底与审计Agent 的 review 结果必须有人的确认环节尤其对P0级别问题要严格控制权限。建议给每条评论加上“AI Agent 自动生成”的标记和对应的日志链接。后期如果出了事故能够定位是哪一次审查、哪一个模型、哪一个 prompt。11.4 隐私与数据合规代码的审查数据往往比代码本身更敏感因为 diff 里会暴露内部架构和业务逻辑。在上生产之前确认该仓库的代码是否可以发送给外部模型 API如果不能外发就部署私有化模型服务或者对变量名、注释做脱敏处理对 AI 审查日志设置访问权限不要默认公开定期清理日志中的敏感内容。11.5 输出格式统一让 LLM 输出统一格式建议使用 JSON 而不是自由文本{ issues: [ { level: P1, file: src/user_service.py, line: 42, message: user_id 为空时会触发空指针异常, suggestion: 增加空值判断 } ] }结构化输出可以直接对接飞书、钉钉、Slack 通知也方便之后做统计和误报分析。11.6 效果评估每个迭代周期统计以下数据Agent 审查问题里被人工确认为有效问题的比例被 Agent 拒绝后又修改再提交的 PR 比例平均每个 PR 的审查时长单 PR token 成本。这些指标比单纯看“覆盖了多少 PR”更能说明问题。12. 总结与下一步这个案例最值得试的点不是“复刻 Uber 的 70% 数字”而是把规则过滤、模型路由、Agent 审查、成本熔断、人工兜底这套链路跑通。普通团队完全可以从一个小的自动化起步先写一个脚本拉取 diff再接入统一审查 prompt最后再上 Webhook 和批量队列。最先应该验证的是“这个模型的误报率你能不能接受”因为它直接决定工程师愿不愿意用。最容易踩的坑是两类一是所有 PR 都扔给大模型成本失控二是没有人工确认机制模型幻觉导致错误评论直接误导开发者。这两点都要在系统设计阶段就考虑进去。后续可以扩展的方向包括把 Agent 从“审查代码”扩展到“生成修复建议并创建变更分支”配合 CI 在阻塞合并前自动执行低风险修复再进一步采集人工 review 记录用真实反馈微调 prompt 或者本地模型。等这一套稳定了就把它接入团队自己的文档系统和变更管理流程同样可以复用这套“规则优先、模型兜底、成本可控”的架构思路。如果你正在搭自己的 AI 代码审查工具建议把这份流程收藏备用从最小的 PR 数据闭环开始验证。
返回列表