前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
AI 面试
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
AI 热点
开发者导航
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
AI 面试
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
AI 热点
开发者导航
返回 AI 情报前线
All News · 全部资讯4039
  • GitHub Copilot企业版支持团队级配置
  • 千问3.8-Max正式发布:2.4T参数MoE与1M上下文
  • 邮件触发 AI Agent 的五大工具对比
  • 搭建多模态视觉模型自动评测流水线
  • InAgent破90%:系统工程驱动Agent新高
  • 阿里 Qwen3.8-Max 开源权重即将发布
  • AirLLM 让 70B 模型跑进 4GB 显存:从原理到实战
  • LLM 概念链式学习:依赖顺序的完整词汇表
  • 生产级 Multi-Agent 系统的 4 层架构
  • 阿里发布 Qwen 3.8-Max,自进化 Agent 16 天自主开发系统
  • 人脸识别系统安全认证陷阱:"通过认证"不等于"持续安全"
  • SRE角度的LLM部署指南:从理解工作负载开始
  • Google AI工具完全导航:AI Studio、Gemini Enterprise Agent等工具对照指南
  • AI Avatar 中的幻觉防控实战方案
  • 推理工程大师课:自回归和扩散模型的部署优化
  • 开源:Agent 工作流完成状态检查工具
  • Prompt Injection 的根本症结:授权架构缺陷
  • 用 NumPy 实现 Transformer 自注意机制可视化
  • 多个 Claude Code Agent 通过 Discord 协作实战
  • MCP 服务器高工具数优化:渐进式披露架构
  • AI代码审查工具陷阱:无状态导致重复评论
  • CLAUDE.md指南:如何识别真正值得的指令
  • 用 XML 标签结构化 Prompt 提升 LLM 输出质量
  • 用 Evals 测试 AI Agent 行为正确性的实践
  • AI 应用成本优化:廉价过滤优先策略
  • 时区陷阱速查表 + 开源 MCP:16+ 时间戳工具
  • npm 供应链 RAT 攻击:阿里开发者中招 3 月
  • Claude 使用效能倍增:6 个实战提示技巧
  • AI Agent 与 LLM 应用的生产安全防护指南
  • 如何识别虚假/AI 生成的安全漏洞报告
  • AWS将Superblocks vibe-coding工具嵌入企业私有云
  • Agent 内存架构实践:存什么、取什么、忘什么
  • MCP 服务暴露数据缺陷:AI agent 盲目信任虚假关系对
  • MCP 实战:为 PDF 工具集构建 Agent 接口
  • 自托管 AI agent:SQLite 低成本长期记忆架构
  • 多语言搜索引擎生产方案:Elasticsearch + pgvector 实战
  • 深度复盘:$12K AI 架构教训——多模型时代解偶之道
  • LLM 推理服务深度解析:Prefill、Decode、KV Cache 机制
  • 阿里Qwen 3.8连续编码16天,所有代码上线
  • 自修复 Agent:从错误检测到自动验证的完整闭环
  • GitHub评论可直接触发Copilot自动化
  • AI 编程工具在生产中遗留的隐患代码
  • README 文档成为 AI Agent 的隐蔽攻击面
  • AI 助手生成的 Pydantic v1 代码为何在 v2 中失效
  • OWASP Agent 安全框架:从 LLM 风险到自主 AI 系统
  • MCP 工具描述字符串的隐蔽提示注入风险
  • AI 生成测试的覆盖率陷阱:可执行≠有效
  • LLM 工具调用的底层实现原理深析
  • 多视角 AI 审查:三个 Agent 独立评审发现不同问题的实践
  • AI 生成的类型注解如何骗过 mypy
  • AI 生成依赖表中的安全漏洞陷阱
  • 已加载 51 / 4039
8.0
热点
AI SCORE
技术实践2026-08-04 05:00

AI代码审查工具陷阱:无状态导致重复评论

dev.to · AI#AI工具#代码评审#系统设计
Editor brief · 编辑速览

剖析GitHub PR审查工具每次提交都重新评审全PR的设计缺陷,根本原因是无法记忆历史评论。展示了构建Stateful AI系统的关键架构决策。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

我开发了 ai-pr-reviewer——一个 GitHub Action,它在 pull request 上运行由 LLM 驱动的代码审查,发布的内联评论就像人类审查者一样。它工作了。然后我开始在自己的 PR 上实际使用它,注意到了一些令人恼火的事情:每当我向一个开放的 PR 推送一个新提交时,它就会重新发布我已经看过的评论——包括我已经修复的那些,以及我已经在回复中辩称它是错误的那一条。

不是重复的文本。相同的内容,每次推送都被新鲜地审查一遍。

Example inline review comment posted by the bot

原因

编排逻辑大约和你预期的一样简单:

async reviewPullRequest(req: ReviewRequest): Promise<void> {
  const { owner, repo, prNumber, headSha } = req;

  const rawDiff = await this.github.fetchPullRequestDiff(owner, repo, prNumber);
  const diff = this.filterDiff(rawDiff);
  // ... size guardrail, then hand off to the LLM
  const result = await this.llm.reviewDiff(diff);
  await this.github.postReview(owner, repo, prNumber, headSha, result.summary, result.comments);
}

fetchPullRequestDiff 始终拉取整个 PR 的完整 base...head diff——而不仅仅是自上次运行以来的变化。每次推送都会从零开始重新触发这个操作,对上次说过的任何内容都没有记忆。PR 十次提交进去了,它就在重新读取和重新判断同样的九次提交的代码,已经审查过九次了。

修复:一个标记,而不是数据库

显而易见的修复是"记住你已经审查过的东西"。不那么明显的部分是在哪里记住它。我不想仅仅为了这个添加数据库——整个服务都是有意设计为无状态的。但 GitHub 已经保留了 PR 上发布的每次审查的完整历史。所以:在每次审查正文中打上一个隐藏标记,并使用 GitHub 自己的 API 作为真实来源。

// Hidden in every review body we post, so we can recognize our own past
// reviews on a PR (and find the commit they were posted against) without
// needing a database — GitHub's own review list is the source of truth.
const REVIEW_MARKER = '<!-- ai-pr-reviewer:review -->';

然后,在审查之前,查看 PR 的审查历史记录,找到我们自己的上一次审查,并拉取它所针对的提交:

async findLastReviewedCommit(owner: string, repo: string, prNumber: number): Promise<string | null> {
  const reviews = await this.octokit.paginate(this.octokit.pulls.listReviews, {
    owner, repo, pull_number: prNumber, per_page: 100,
  });

  const ours = reviews.filter((review) => review.body?.includes(REVIEW_MARKER));
  if (ours.length === 0) return null;

  // Sort by id (monotonically increasing, assigned at creation) rather
  // than trusting listReviews' response order to stay oldest-first.
  ours.sort((a, b) => a.id - b.id);
  return ours[ours.length - 1].commit_id ?? null;
}

有了这个,编排器就改为从那里 diff 到新的 head,而不是从 PR 的 base 开始:

private async fetchDiff(owner: string, repo: string, prNumber: number, headSha: string): Promise<string> {
  const lastReviewedSha = await this.github.findLastReviewedCommit(owner, repo, prNumber);

  if (!lastReviewedSha) {
    return this.github.fetchPullRequestDiff(owner, repo, prNumber); // first review on this PR
  }
  if (lastReviewedSha === headSha) {
    return ''; // nothing's changed since we last looked
  }

  try {
    return await this.github.fetchDiffSince(owner, repo, lastReviewedSha, headSha);
  } catch {
    return this.github.fetchPullRequestDiff(owner, repo, prNumber); // e.g. old commit unreachable after a force-push
  }
}

三种状态,三种行为:从未审查过这个 PR → 完整 diff。已审查过这个确切的提交 → 什么都不做,甚至不调用 LLM。审查过一个早期提交 → 仅 diff 新部分。

修复发现了自己的 Bug

这是我没有预料到的部分:这个仓库自己给自己测试——它审查自己的 pull request。当我为这个确切的变更打开 PR 时,它审查了自己,并指出 ours[ours.length - 1] 是在信任 listReviews 的响应顺序保持按时间最早到最新的,这实际上不是有文档保证的——按 id 排序(如上所示)是修复方案,在机器人在自己的 PR 上标记它后添加。一个去重特性被被它构建来修复的东西的正确性审查,感觉像是它在工作的好迹象。

结果

端到端验证:自上次审查以来没有变化的 PR 现在被完全跳过——没有 LLM 调用,没有重复的评论。带有新提交的 PR 只会在 delta 上被审查。

完整的 diff 和测试:PR #9。如果你想尝试:它现在已在 GitHub Marketplace 上,工作流文件中只需一行。

我是 Niv,一名后端技术主管,从事 NestJS/AI 基础设施项目工作。GitHub · LinkedIn

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
MCP 服务器高工具数优化:渐进式披露架构
下一篇
CLAUDE.md指南:如何识别真正值得的指令