前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
返回 AI 情报前线
All News · 全部资讯10183
  • 用 AWS 注册表实现 Agent 动态工具发现
  • 用 X.509 为多 Agent 建立可验证身份
  • Eon 用统一 MCP 网关简化多云排障
  • 给企业Agent搭建带权限的统一语义层
  • AWS 智能体跨库查询如何落实用户级权限
  • 提示注入检测为何被同义改写绕过
  • Synapse 打通编程助手的记忆与配置
  • vLLM 与 SGLang 新增 TPU 原生后端
  • 给水印检测加门控,减少误选与修复风险
  • 用本地语音组件搭建可打断的 AI 助手
  • Claude Code 误删复盘:命令钩子的盲区
  • Mac 跨应用文本助手如何读取界面
  • 用 AI 编程智能体清理电视广告与预装应用
  • DeepSeek开放生产推理框架DeepInfer
  • 京东大促期间暂停外部安全测试
  • Coral NPU补齐矩阵单元与开源编译链
  • 向量检索与知识图谱如何协同支撑 RAG
  • 让编程助手接入浏览器完成调试闭环
  • 先验证分类器,再让 AI 排查运行轨迹
  • 录制模型响应,隔离评测代码与模型变动
  • 审查 AI 生成 PR,先核对任务边界
  • 用有效吞吐压测确定模型接口并发上限
  • Agent 改一个字段,如何证明操作正确
  • 用多任务决策模型自动分流 CVE 漏洞
  • 逐步核算 Token,定位 Agent 的结构性浪费
  • Agent 生成 SQL,如何控制重跑漂移
  • 审核超时后的任务释放与计费隔离
  • DeepSeek 开源大规模推理框架 DeepInfer
  • DeepInfer 开源,公开大规模推理部署能力
  • Cloudflare预览低价结构化抽取模型
  • Headroom 在本地压缩智能体上下文
  • 智能体改记忆,如何验证没有误伤
  • llama-server 工具控制与停止条件现兼容问题
  • 模型快十倍,智能体为何只快一点
  • 用决策模型做评测,先调分类阈值
  • 编码 Agent 实测:成本之外看交付细节
  • Mercury 3 发布:主打高速生成与百万上下文
  • 用 Node 拆解向量池化与检索质量
  • 补齐 Oracle 方言约束,轻量模型首答率跃升
  • 拆解 Claude Code 的完整智能体循环
  • 邮件 Agent 重试前先校验退信
  • 用信号队列约束 Agent 等待邮件
  • 用 Cloudflare 搭建 Agent 收信中枢
  • 一次字符串替换如何让线上文章翻倍
  • AI 搭项目失败,先确认代码是否拉取成功
  • 检索匹配再高,也救不了过期知识库
  • 本地模型帮你给常用命令起快捷名
  • MCP 无状态改版涉及哪些迁移工作
  • 检查 Supabase 服务端鉴权的信任来源
  • 流式 AI 接口延迟该如何拆分测量
  • 模型故障切换如何追踪实际调用路径
  • 已加载 51 / 10183
8.0
热点
AI SCORE
编程提效2026-10-20 18:16

审查 AI 生成 PR,先核对任务边界

dev.to · AI#代码审查#Agent#工程质量
Editor brief · 编辑速览

文章建议在 Agent 开工前记录并提交任务范围,再用确定性脚本筛出需要重点审查的差异块。示例指出,重试修复可能夹带默认配置修改和测试断言弱化,仅看测试通过难以发现范围偏移。

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

完整中文译文

大多数 Agent 编写的 PR 并不难读,难的是界定范围。逐行看,代码通常没什么问题;你看不到的是,Agent 在编写过程中,悄悄给自己划定了怎样的任务范围。于是,你花了四十分钟,还原那些从未写下来的意图,最后因为测试全绿而批准合并。

有一种成本更低的操作顺序:在 Agent 开始运行之前写清范围,把它和任务说明一起提交,再让一个确定性脚本告诉你,哪些变更块值得重点关注。脚本不判断代码是否正确。它告诉你,哪些地方需要认真讨论正确性。

失效模式:悄无声息的范围扩张

一个典型的 Agent PR 写着:“处理 429 响应,避免无限重试。”diff 却修改了这些文件:

src/http/client.py —— 符合预期,这就是任务要改的地方。

src/config/defaults.py —— 不符合预期。默认尝试次数从 3 改成了 1。

tests/http/test_client.py —— 六条断言被替换成了一条 assert response is not None。

没有哪一行明显有错。问题恰恰就在这里。你需要审查一个从未说明理由的决策,它混在一份大部分变更块都是机械性修改的 diff 里,而 review 界面又赋予所有变更相同的视觉权重。

接下来,reviewer 通常会选择两种糟糕策略中的一种:对每个变更块投入同样的注意力,速度慢,一天看到第三个 PR 就精疲力竭;或者粗略浏览,相信测试,速度快,却也正是断言弱化得以合并的方式。这两种做法,都是在弥补元数据的缺失。

第一步:提交范围契约

scope.json 放在任务说明旁边,属于同一个 PR,而且要在编写任何代码之前提交。

{
  "task": "handle 429 from provider without infinite retry",
  "allowed_paths": ["src/http/*", "tests/http/*"],
  "forbidden_paths": ["src/config/*", ".github/*", "pyproject.toml"],
  "allow_delete": false,
  "max_files": 8,
  "max_changed_lines": 250,
  "human_required_if": ["out_of_scope", "deps", "ci", "skip_added", "assertion_weakened"]
}

三条规则,决定了这个文件是有用的约束,还是摆设:

  • 禁止规则优先于允许规则。允许 src/http/、禁止 src/config/,就意味着修改配置会被标记为问题,无须临场判断。
  • 不仅要限制路径,还要限制变更规模。一个本应修改 5 个文件的任务,Agent 却需要修改 40 个文件,这反映的是任务本身的问题,而不是 Agent 的速度。
  • 默认设置 allow_delete: false。无论 diff 来自人还是 Agent,删除内容都是让失败测试通过的最省事办法。

第二步:生成审查材料包

生成材料包的脚本刻意保持简单:只用标准库,读取 git,不联网,不调用模型。关键就在于确定性。一个每次运行都可能给出不同答案的分类器,无法充当合并门禁。

#!/usr/bin/env python3
"""review_packet.py - classify an agent PR diff against scope.json.

Exit 0 = no human-required signal. Exit 1 = human review required.
Globbing is fnmatch-simple; see limitations below.
"""
import argparse, fnmatch, json, re, subprocess, sys

HUNK = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@ ?(.*)$")
MECHANICAL = re.compile(r"^\s*(?:import\s|from\s+\S+\s+import\s|#|//|/\*|\*|$)")
SKIP_ADDED = re.compile(r"(?:pytest\.mark\.skip|\.skip\(|xit\(|xdescribe\(|t\.Skip\()")
WEAKENED = re.compile(r"(?:assert\s+True|assert\s+[\w.]+\s+is\s+not\s+None|except\s+Exception\s*:\s*pass)")

RISK = {
    "deps":  ["requirements*.txt", "pyproject.toml", "package.json", "package-lock.json", "go.mod"],
    "ci":    [".github/workflows/*", ".gitlab-ci.yml", "Dockerfile*", "Makefile"],
    "db":    ["*/migrations/*", "*.sql"],
    "infra": ["terraform/*", "*.tf", "helm/*"],
}
TESTS = ["tests/*", "*/tests/*", "test_*.py", "*_test.go", "*.test.ts", "*.spec.ts"]

def git(*args):
    return subprocess.run(("git",) + args, capture_output=True, text=True, check=True).stdout

def matches(path, globs):
    return any(fnmatch.fnmatch(path, g) for g in globs)

def files_changed(base):
    out = git("diff", "--numstat", "--no-renames", f"{base}...HEAD")
    rows = []
    for line in out.splitlines():
        add, dele, path = line.split("\t")
        rows.append({"path": path, "add": int(add or 0), "del": int(dele or 0)})
    return rows

def hunks(base, path):
    out = git("diff", "--no-color", "-U0", "--no-renames", f"{base}...HEAD", "--", path)
    start, header, body = None, "", []
    for line in out.splitlines():
        m = HUNK.match(line)
        if m:
            if start is not None:
                yield {"line": start, "header": header, "body": body}
            start, header, body = int(m.group(1)), m.group(3), []
        elif start is not None and line[:1] in "+-" and not line.startswith(("+++", "---")):
            body.append(line)
    if start is not None:
        yield {"line": start, "header": header, "body": body}

def label(path, hunk, contract):
    added = [l[1:] for l in hunk["body"] if l.startswith("+")]
    text = "\n".join(l[1:] for l in hunk["body"])
    if matches(path, contract["forbidden_paths"]):
        return "out_of_scope"
    for kind, globs in RISK.items():
        if matches(path, globs):
            return kind
    if any(SKIP_ADDED.search(l) for l in added):
        return "skip_added"
    if WEAKENED.search(text):
        return "assertion_weakened"
    if not matches(path, contract["allowed_paths"]):
        return "out_of_scope"
    if all(MECHANICAL.match(l) for l in added):
        return "mechanical"
    return "semantic"

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--base", default="origin/main")
    ap.add_argument("--contract", default="scope.json")
    ap.add_argument("--format", choices=["json", "markdown"], default="markdown")
    args = ap.parse_args()

    contract = json.load(open(args.contract))
    rows = files_changed(args.base)
    gate = set(contract["human_required_if"])

    findings, changed = [], sum(r["add"] + r["del"] for r in rows)
    for f in rows:
        for h in hunks(args.base, f["path"]):
            findings.append({"kind": label(f["path"], h, contract), "path": f["path"],
                             "line": h["line"], "header": h["header"]})

    human = [f for f in findings if f["kind"] in gate]
    if len(rows) > contract["max_files"] or changed > contract["max_changed_lines"]:
        human.append({"kind": "over_budget", "path": "-", "line": 0,
                      "header": f"{len(rows)} files, {changed} lines"})

    if args.format == "json":
        print(json.dumps({"task": contract["task"], "human_required": human,
                          "all": findings}, indent=2))
    else:
        print(f"# Review packet - {contract['task']}")
        print(f"changed: {len(rows)} files, {changed} lines\n")
        print(f"## hum

针对 PR 分支运行,而不是针对工作区运行:

python scripts/review_packet.py --base origin/main --contract scope.json > packet.md
echo "$?"   # 1 means: a human has to open this, and the packet says where

429 示例的输出,经过删节:

# Review packet - handle 429 from provider without infinite retry
changed: 4 files, 75 lines

## human required (3)
- [out_of_scope] src/config/defaults.py:12  -retries = 3 +retries = 1
- [assertion_weakened] tests/http/test_client.py:88  def test_retry_budget()
- [skip_added] tests/http/test_client.py:140  def test_gives_up_after_limits()

## mechanical, in scope
- src/http/client.py:3

材料包允许你快速略过什么

mechanical 是唯一可以快速放行的标签:允许路径内的 import 调整、仅涉及注释的修改、格式调整。快速浏览,运行测试套件,然后继续。其余内容都要仔细阅读。

范围内的 semantic 变更才是真正需要下功夫的地方:控制流、错误处理、新的默认值、发生变化的数字字面量。对于这些变更块,必须先确认新增测试在基线提交上会失败,才能相信它确实有效。材料包里的任何信号,都不能替代这项检查。

材料包要求你测试什么

右侧一栏刻意写成了可以照章执行的规则。它的作用是,让你不必在下午六点、开着十一个标签页时,重新推导一遍审查策略。

模型真正能帮上忙的地方,以及它绝不能介入的地方

披露:本文是 MonkeyCode 产品推广活动的一部分。

上面的分类器识别的是变更的外在特征:哪个文件、哪个变更块、哪类风险。它无法解释为什么修改了 src/config/defaults.py。这个任务适合交给语言模型,而且任务范围应该很窄:为每个超出范围的变更块写一句话,解释可能的修改原因,让人能在几秒钟内接受或否定这个解释,而不必重新阅读 diff。

这段说明放进 notes 字段。它绝不能修改 kind,绝不能改变退出码,也绝不能隐藏任何 human required 条目。实际操作时,先运行确定性的材料包脚本,再把材料包和超出范围的变更块单独交给模型。这样,便宜的 token 就用在了那 5% 存在歧义的内容上,而不是生成一份没人看的完整 diff 摘要。

这也是免费模型访问对这套工作流有意义的原因。一个每调用一次都要花钱的门禁,会在有人注意到账单的那一周被关闭;一个能在免费额度内运行的门禁,才会持续运行。运营方表示,当前优惠包括 10M 免费 token,以及一个免费服务器选项。请把它视为某次促销活动的现状,而不是永久 SLA;在让流水线依赖它之前,先核实最新条款。如果你想把材料包端点部署成内部 webhook,而不是笔记本上的脚本,它的载荷很小,只有路径、行数和变更块标题,这恰好适合用一台小型免费服务器提供服务。

有一条硬性约束:在阅读托管端点的数据保留条款之前,不要把私有仓库的 diff 粘贴进去。利用免费访问在本地运行模型调用,可以让 diff 留在你的机器上。这是我会优先选择、也会一直保留为默认值的配置。

局限性,以及不适合使用这个工具的情况

这个变更块分类器只做语法层面的判断。整文件重命名、第三方随附代码、生成代码,以及仅重新格式化的提交,都可能被错误分类。如果你的 PR 经常同时包含 git mv 和内容修改,--no-renames 就会把并未真正删除的内容报告为删除。

通配符匹配使用的是 fnmatch,而不是 gitignore。**/migrations/* 不会匹配嵌套路径。如果你的目录树很深,就把 matches() 换成基于 pathspec 的检查。

只有先写契约,这套方法才有效。对已经存在的 diff 补写 scope.json,会把门禁变成事后找理由:所有内容都被声明为范围内,材料包也就客客气气地点头同意。

范围内的错误,工具看不见。src/http/client.py 里的重试循环可以被正确标记为 semantic,同时仍然无限循环。材料包负责引导注意力,不负责提供判断。

如果你没有能针对 PR 基线执行 git 的 CI runner,或者 Agent 已经在直接向受保护分支写入代码,就先别用这个工具。此时缺失的控制措施是分支保护,而不是审查脚本。如果团队里根本没人会读材料包,那么加上它,只是在形式主义上多走几步。

在下一个 Agent 任务开始之前,先写好 scope.json。控制在十行以内。

对最近五个已经合并的 Agent PR 运行材料包。只看 human required 部分。如果里面有未经讨论就被合并的内容,那就是你当前的风险基线。

增加一个 CI job,在退出码为 1 时阻止合并。从第一天起就不要把它设为仅供参考;仅供参考的门禁,最后都会退化成评论。

等确定性部分经过一周使用、获得信任之后,再为有歧义的变更块添加模型说明。

节省下来的审查时间是实实在在的,但更持久的收益是另一件事:每个合并的 Agent PR,现在都附带了一份书面声明,说明它被允许修改哪些内容。下一位 reviewer 不必再猜,审计时也一样。

如果你想完整试一遍,先复制 review_packet.py 和 scope.json,它们不需要账号,也不需要网络。等材料包已经让你看清哪些变更块值得再找一双眼睛检查时,再添加模型说明。

后续你可以考虑屏蔽此人,和/或举报滥用行为。

Original source

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

阅读英文原文
上一篇
录制模型响应,隔离评测代码与模型变动
下一篇
用有效吞吐压测确定模型接口并发上限