先把范围收窄

一个能上线的审查 agent,通常只做三件事:判断这次改动有没有碰到禁区、有没有违反一条写明的团队规则、有没有漏掉必须补的测试。不要一上来就让它「全面评审代码质量」,那类判断没有稳定标准,评测也做不出来。下面用一个虚构但常见的规则集举例:数据库迁移文件必须带回滚脚本;对外 API 的入参必须做长度或类型校验;新增的定时任务必须写幂等说明。

目录怎么放

Agent SDK 把 agent 当一个软件项目:入口文件定义 agent,提示词是 Markdown,工具是 TypeScript 函数,评测和事件回放放在同一目录。建议最小结构如下,先不要加多余抽象。

pr-reviewer/
  agent.ts          # defineAgent:模型、工作目录
  instructions.md   # 三条规则 + 输出格式
  tools/
    read-diff.ts    # 只读:拉取本次 PR 的 diff
    read-file.ts    # 只读:按路径读仓库文件
  evals/
    cases.jsonl     # 20 条历史 PR:期望命中/不命中
  package.json

入口与工具

入口只负责声明,不写业务逻辑。工具函数要故意做得「窄」:read-diff 只返回本次改动的文件、增删行和 patch;read-file 只允许读仓库内路径,拒绝绝对路径和 ..,并设置单文件大小上限。审查 agent 不需要写文件、不需要执行 shell,这两个能力一开始就不要给。

import { defineAgent } from "@cursor/agent-sdk";
import { readDiff } from "./tools/read-diff";
import { readFile } from "./tools/read-file";

export default defineAgent({
  name: "pr-reviewer",
  model: { id: "composer-2.5", params: [{ id: "effort", value: "high" }] },
  tools: [readDiff, readFile],
  instructions: "./instructions.md",
  local: { cwd: "./.work" },
});

提示词只写规则和输出格式

instructions.md 里不要写「你是资深工程师」这类人设,直接写判定条件和输出契约。关键是要求每条结论都带文件、行号和规则编号;没有证据就输出「未发现」,不要凑数。下面是可以直接改的骨架。

你只做三件事,按顺序检查:
1. R1 迁移文件:改动包含 migrations/ 下的新文件时,必须同时出现对应的 down/rollback。
2. R2 对外 API:新增或修改请求参数时,必须有长度/类型/空值校验。
3. R3 定时任务:新增 cron/job 时,必须说明重复执行是否安全。

输出要求:
- 每条命中写成:规则编号 | 文件:行号 | 一句话说明 | 证据摘录(不超过 2 行)
- 没有命中写:未发现
- 不要评价代码风格,不要建议重构,不要猜测未出现在 diff 里的代码

先跑评测,再谈部署

从过去三个月的 PR 里挑 20 条,10 条应该命中、10 条不应该命中,写成 cases.jsonl,每条包含 diff、期望的规则编号和人工判断理由。先在本地 playground 跑一遍,统计三个数:命中率、误报数、每条 PR 的平均耗时。误报超过两成,先改规则或补反例,不要急着上线。Agent SDK 自带事件回放,失败案例可以反复重跑。

接事件与部署

本地验证通过后,执行 agent-sdk deploy。部署时只订阅 pull_request 的 opened 和 synchronize 事件,权限设为「读代码 + 写评论」,不要给合并或推送权限。第一周只在一两个仓库灰度,评论里明确写「这是自动检查,规则见仓库 docs/review-rules.md」,让人知道怎么申诉。

止损设置

上线前定三条止损线:单次审查超过 3 分钟就超时退出;同一 PR 连续两次审查结果冲突就停止自动评论、转人工;任何工具报错都按「未检查」输出,不能默认通过。每周抽 10 条评论复核,把误报写成新的反例加进评测集。这样做的成本不高,但能避免 agent 用看起来很专业的措辞把团队带偏。

审查 agent 的可信度,不是它找出了多少问题,而是它说「没问题」的时候你敢不敢信。