AI编程代理安全落地:从任务边界到代码评审的护栏实践

📅 发布时间:2026/9/7 16:28:53
AI编程代理安全落地:从任务边界到代码评审的护栏实践
AI 编程代理AI coding agent进入研发流程后团队最先感受到的往往不是效率提升而是代码评审压力的增加。Figma 工程师在 AI Engineer 相关分享中讨论过同一个问题当代理自动完成跨文件修改、自动执行命令、自动生成测试时如何保证最终提交的代码仍然是干净、可维护、可回滚的。核心结论并不是“少用 AI”而是先用工程手段把 AI 约束在安全边界内。这篇文章不准备逐句复述某一场演讲而是把这一类实践整理成一套可以在自己团队中复用的落地流程。整个过程会围绕一条主线展开先理解 AI 编程代理为什么会产出质量不可控的代码然后从任务边界、仓库规则、自动化校验、代码评审、线上监控和回滚几个环节建立护栏。每个环节都会给出可执行的配置、命令和检查清单尽量做到看完就能在自己项目里试点。1. 先理解 AI 编程代理为什么会写出“垃圾代码”1.1 辅助补全工具与自主代理的最大区别很多团队已经习惯了 AI 补全工具的工作方式人写函数名AI 补函数体人再手动修改。这种模式下修改方向和整体结构都由人控制AI 只是加速了局部输入。AI 编程代理不一样。它接收的是一个任务描述而不是某一行代码的上下文。它会自己搜索代码库、修改多个文件、执行构建命令、读取测试结果甚至反复重试。人从“逐行写代码”退后到“发布任务和检查结果”。这一变化带来的风险是结构性的人不再对每个修改点有直接感知。AI 可能为了满足任务描述修改超出范围的文件。AI 倾向于让测试通过但不一定理解代码库的历史约定和设计意图。一旦任务描述模糊AI 会主动脑补业务规则产生“看起来合理、实际上错误”的代码。所以把 AI 编程代理接入仓库第一步不是打开工具开关而是先理解它和补全工具完全不同的工作模式。1.2 “垃圾代码”在代理场景下有哪些典型表现“垃圾代码”不是一个笼统的贬义而是一类可以在评审和运行时被识别的问题。在 AI 编程代理场景下最常见的是这几种现象具体表现为什么容易发生表面可用但内部混乱大量 if else 堆叠、复制粘贴式修改、命名随意AI 优化的是“让测试通过”而不是可读性错误处理空白吞掉异常、空值不判断、网络错误不重试任务描述里没有提出错误分支要求过度设计为一个简单字段引入抽象接口和工厂AI 从训练数据中学习到“看起来专业的写法”修改范围失控改完目标函数后顺手改掉公共工具类代理在检索上下文时发现“相关代码”就会一并改测试失效测试断言被调整成恒真条件或只覆盖正向路径代理为了自圆其说会修改测试来匹配实现隐性破坏调用方没改、函数签名变了、返回结构变了代理只关注当前任务覆盖到的调用点这些问题的共同根源是代理在优化一个局部目标。它没有项目全局视角也不清楚哪些代码是核心资产、哪些代码是临时方案、哪些修改需要同步通知其他团队。1.3 先设护栏再谈效率Figma 工程师在分享中反复强调的一点是把 AI 编程代理当作“高速但经验不足的工程师”来管理而不是当成一个纯生成器。一个刚入职的工程师公司会给他什么明确的任务边界。仓库结构和编码规范。构建、测试、lint 命令。代码评审机制。上线后的监控和回滚手段。这些就是护栏。AI 编程代理同样需要这套东西而且需要得更严格因为它的“理解能力”来自上下文而不是长期记忆。如果仓库里没有明确的规则文件代理就会按照自己的默认偏好写代码如果任务描述没有边界它就会把相关文件全改一遍如果合并前没有强制校验它就能把编译不过或测试失败的代码直接推进主干。所以安全落地的核心不是“更聪明的模型”而是更完整的工程流程。后续章节按这个顺序展开接入前准备、任务拆分、自动校验、代码评审、线上监控、失败复盘。2. 接入 AI 编程代理前确定边界、规则和基线2.1 先回答三个问题做什么、不做什么、怎么算完成接入代理前团队需要先对使用场景达成一致。不是所有任务都适合交给代理也不是所有代码库都适合在第一天开放全部目录。推荐先按任务类型做评估任务类型适合交给代理吗风险等级说明生成单元测试适合中需要人检查断言是否有效代码补全适合低人在当前文件中掌握上下文Bug 修复视情况中高必须先有稳定复现路径跨模块重构谨慎高建议拆成小步执行自动化脚本生成适合低独立脚本影响范围小底层公共库修改不建议初期开放极高影响所有调用方这三个问题必须在试点前回答清楚这个任务允许代理修改哪些目录和文件。这个任务禁止代理修改哪些目录和文件。任务完成的验收标准是什么包括测试覆盖率、构建通过、无 lint 错误等。没有边界判断就放代理进仓库等于让一个新工程师自己决定改哪里。区别是真人会问代理不会问。2.2 在仓库根目录建立规则文件把团队的编码约束写进一个代理可读、人也可见的规则文件。目前很多编程代理会主动读取仓库根目录下的AGENTS.md或等价文件并把它作为系统的上下文注入。这个文件可以包括项目技术栈和关键依赖。构建、测试、lint 的准确命令。目录结构和职责划分。编码风格约定。禁止修改的目录和文件。提交信息规范。完成任务的 Definition of Done。下面是一个AGENTS.md示例可以直接作为起点# 项目规则 ## 技术栈 - Python 3.11 - FastAPI - SQLAlchemy 2.x - pytest ## 常用命令 - 安装依赖: pip install -e .[dev] - 单测: pytest tests/ -x -q - 类型检查: mypy app/ - 格式化: ruff format app/ tests/ - lint: ruff check app/ tests/ ## 目录职责 - app/api: 路由层只做参数解析和响应封装 - app/services: 业务逻辑层 - app/models: ORM 模型 - app/migrations: 数据库迁移文件禁止手动改动 ## 禁止修改 - app/migrations/ - docs/ - gen/ ## 编码约束 - 函数需要 docstring - 禁止裸 except必须捕获具体异常类型 - 新增对外接口必须包含输入校验 - 错误信息不允许直接暴露内部堆栈 ## 提交信息 - 遵循 Conventional Commits - 示例: fix(api): handle empty user id ## 完成定义 - pytest 全部通过 - mypy 无错误 - ruff check 无错误 - 不修改任务范围之外的文件注意不要把规则文件写得像散文。代理对长文本的理解能力有限规则要短、要清晰、要可检查。比如“保持代码整洁”这种话没有意义要写成“函数长度不超过 50 行超出则拆分”。2.3 先把代码库基线跑稳态在让代理开始改代码之前仓库本身必须是健康的。否则会出现一个经典的死循环代理跑出错误你分不清是它造成的还是仓库原本就有的。建议先完成# 本地完整执行一遍 pip install -e .[dev] pytest tests/ -x -q mypy app/ ruff check app/ tests/ # 记录执行结果和时间 echo baseline done .ai_agent_baseline这道工序有几个作用确认 CI 命令和本地命令一致避免代理在本地通过了、CI 却失败。确认测试基线是绿的代理后续改动如果破坏测试可以直接定位到它。确认构建时间合理如果一次测试要跑 30 分钟代理的每次尝试都会很昂贵。如果仓库里存在大量历史遗留的失败测试先把它们清理掉或者标注skip再让代理介入。否则代理会修复失败测试作为“完成目标”而不会关心这些测试是否应该有。3. 任务拆分与上下文注入不要让代理吃下过大的任务3.1 一个任务对应一个可评审的变更集把 AI 编程代理想象成一个只会专注当前 prompt 的工程师。给它一个“优化整个订单系统”的任务它会输出一个几百行甚至上千行的 diff。这种 diff 很难评审而且一旦产生问题很难定位是哪一步引入的。推荐的拆分粒度是一个任务只解决一个问题一个任务生成的变更集要能被人在 15 到 30 分钟内评审完。下面的任务描述模板可以直接复制使用## 目标 修复 API 层在用户 ID 为空时返回错误码的问题。 ## 范围 - app/api/users.py - tests/test_api_users.py ## 禁止修改 - app/services/ - app/models/ - app/migrations/ ## 验收标准 - 新增测试覆盖 user_id 为 None 和空字符串两种情况 - pytest tests/test_api_users.py -x -q 通过 - ruff check app/api/users.py 通过 - 不修改范围之外的文件这个模板的关键是明确了边界和验收标准。代理不需要猜测它只需要执行。3.2 上下文越多不代表效果越好有些团队为了让代理更“懂业务”会把整个技术设计文档、需求文档、历史变更记录全部塞进 prompt。这会导致几个问题上下文过长后代理会把注意力分散到无关信息上。关键约束被淹没在大量文本中间。每次请求的成本和时间都会上升。更合理的做法是分三层提供上下文上下文类型内容示例全局规则仓库级规则文件代理自行读取AGENTS.md任务上下文当前需求的背景和业务规则prompt/issue 描述局部参考类似功能的实现代码或接口定义粘贴少量代码片段在实际操作中不需要把整个业务背景都复制到 prompt 里。告诉代理“这个函数是给前端登录接口用的user_id 来自 JWT token为空说明鉴权失效应该返回 401”远好于贴三页需求文档。3.3 分支策略让代理的错误被隔离代理在执行任务时可能会反复尝试、提交失败代码。不要让它在主干分支上直接工作至少在试点阶段给每次任务开一个独立分支。git checkout -b feat/ai-agent/user-id-validation如果需要多个代理并行处理不同任务命名规则可以用ai-agent/任务标签作为前缀方便后续统一 review 和清理。代理完成后的工作流# 拉取最新主干并合并到当前分支 git fetch origin main git merge origin/main # 运行完整校验 pytest tests/ -x -q mypy app/ ruff check app/ tests/ # 查看最终变更范围 git diff --stat main...git diff --stat main...这一步很重要它能让你在合入前直观看到代理到底改了哪些文件。如果出现大量与任务无关的文件应该直接打回而不是手动挑拣。4. 从生成到合并强制自动校验和人工评审关卡4.1 合并前的自动化检查不能只依赖“代理自测”代理在执行任务时通常会自己运行一遍测试但这个自测结果只能作为参考。它的测试目标可能被污染它也可能因为环境差异在自己的沙箱里通过、在 CI 里失败。所以仓库必须有一套不依赖代理自身的强制检查流程。最简单的方式是在 CI 中增加一个独立的 job专门校验代理分支name: ai-agent-check on: pull_request: types: [opened, synchronize] jobs: verify: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 0 - uses: actions/setup-pythonv5 with: python-version: 3.11 - run: pip install -e .[dev] - run: pytest tests/ -x -q - run: mypy app/ - run: ruff check app/ tests/ - run: ruff format --check app/ tests/ - name: check diff scope run: | # 如果任务只允许修改 app/api/则检测是否出现其他目录变更 if git diff --name-only origin/main...HEAD | grep -qv ^app/api/ ; then echo 发现代理修改了范围之外的目录 exit 1 fi这里的check diff scope是关键步骤。它做了一件代码评审中最容易被忽略的事情确认变更范围是否越界。代理也许能通过所有测试但如果它改坏了app/models/下的数据模型AI 生成的测试根本无法覆盖所有调用方的影响。本地同样可以做一个 pre-push 钩子避免代理把明显不合规的代码推到远端#!/usr/bin/env bash # .git/hooks/pre-push 或项目内的 pre-push 脚本 set -e echo running ai-agent pre-push checks... pytest tests/ -x -q mypy app/ ruff check app/ tests/4.2 人工评审的重点不是“读代码”而是“问问题”即使自动化检查全绿也不能直接把 AI 生成的代码合入。人工评审仍然不可省略但评审方式需要调整。AI 生成代码的评审重点不是逐行检查语法而是回答这几个问题这个改动解决了任务描述里的问题吗它有没有修改任务范围之外的文件错误处理是否真实还是只是让测试通过有没有引入重复逻辑或新的抽象而这个抽象没有明显收益测试是否真的会失败把关键断言暂时改错测试是否变红是否有并发、时间、数据一致性方面的问题依赖和数据库迁移是否被无意修改可以把这些整理成评审模板合入每个 AI 代理 MR 时使用评审点检查内容通过标准范围控制与git diff --stat对比任务范围无越界文件正确性能复述这次改动解决的业务场景理解与任务一致异常处理空值、异常、超时等分支有处理不吞错、不裸 except测试质量故意破坏断言测试是否失败测试有实际约束力重复代码搜索是否已有相似实现没有重复逻辑修为接口公共函数签名、返回结构是否改变调用方已经同步更新性能风险是否有循环内查询、N1、大对象复制明显性能隐患已消除4.3 要求代理先完成自检并通过 prompt 约束行为可以让代理在生成代码后自动执行一系列检查并把输出结果带回。比如在任务描述中追加## 提交前自检 在生成最终结果之前你必须执行以下命令 1. pytest tests/test_api_users.py -x -q 2. mypy app/api/users.py 3. ruff check app/api/users.py 如果检查失败继续修复直至通过。如果无法通过需要说明具体原因和待确认问题。这种方式相当于要求代理输出一份“自检报告”。它不一定完全准确但可以提升代理对错误的关注程度也能帮你快速判断代理卡在了哪个环节。要注意不要因为代理报告“全部通过”就直接合入报告需要和 CI 结果交叉验证。5. 上线后的监控与回滚质量问题是运行时才暴露的5.1 区分学习环境与生产环境的要求在本地试用 AI 编程代理时可以只关注“代码能不能跑”。但一旦进入生产环境AI 生成代码的质量判断标准就要切换到运行表现上维度学习环境生产环境验收标准编译通过、本地测试绿错误率、耗时、业务指标无回退检查手段IDE、手动测试日志、监控、告警、链路追踪问题处理改代码重新跑先止血、再定位、再修复数据要求造数方便、无真实用户需要考虑兼容和数据迁移回滚方式git revert功能开关、版本回滚、数据库兼容方案如果团队正在用代理生成数据库变更或涉及支付、权限的核心代码上线前必须增加一层额外评审并且尽量让变更可以在不重新发布代码的前提下被关闭。5.2 上线后先看异常率再看耗时最后看业务指标生产环境不会直接告诉你“代码写错了”它只会通过指标异常间接表达。针对 AI 生成代码建议上线后按这个顺序观察错误率如果部署后错误率出现明显增长优先怀疑新增逻辑的异常分支没有处理好。耗时P50、P95、P99 是否上涨提示可能存在循环内查询、不必要的重试或无界缓存。业务指标订单成功率、接口调用量、转化率是否有回退防止代理把业务逻辑改出偏差。下面是一个简单的压测对比方式用于上线前快速暴露代理改动的性能问题# 部署前在基准版本上记录指标 k6 run --summary-exportbaseline.json load-test.js # 部署代理分支后再次执行 k6 run --summary-exportafter.json load-test.js # 对比 P95 耗时 python -c import json with open(baseline.json) as f: base json.load(f) with open(after.json) as f: after json.load(f) b base[metrics][http_req_duration][values][p(95)] a after[metrics][http_req_duration][values][p(95)] print(fP95 baseline{b:.2f}ms after{a:.2f}ms) 如果 P95 从 300ms 涨到 500ms就要怀疑代理是否在高频路径里加了不需要的同步逻辑或重复查询。5.3 回滚方案要在合入之前写好AI 生成代码合入之前应该先回答一个问题如果线上出了问题怎么最快回到上一个稳定版本。推荐三种回滚手段按速度排序手段速度适用场景功能开关秒级新功能可以整体关闭版本回滚分钟级代码变更和数据库兼容补丁修复半小时以上问题定位明确、影响面小同时要注意数据库回滚。如果代理生成的代码包含数据库迁移不能只回滚代码而不回滚数据。例如新增了一个非空字段代码回滚后旧代码不会写这个字段而数据库又要求它非空就会出现线上写入失败。这类问题必须在评审阶段提前规避尽量让数据库变更向后兼容。5.4 把线上问题反哺给规则文件每一次由 AI 生成代码引发的线上故障都是规则文件迭代的素材。问题修复后应回答这几个问题规则文件里缺少了哪条约束任务描述模板里缺少了哪个验收标准评审清单里漏掉了哪个检查点例如如果代理因为吞掉了 Redis 连接异常导致缓存雪崩那就应该把这条加入规则## 编码约束 - 所有 Redis 调用必须设置超时时间 - Redis 异常必须记录日志并返回降级响应不允许吞掉异常规则文件不是一次性写好的它是团队和 AI 协作过程的“沉淀物”。出现一次问题就补一条规则一段时间后代理能犯的错误会明显变少。6. 常见失败模式与排查路径6.1 问题现象、原因、检查方式对照表实践中AI 编程代理相关的问题通常集中在几个固定场景。下面这张表可以直接用于团队内部排查问题现象常见原因检查方式处理建议代理生成大量无关代码任务描述没有明确禁止目录用git diff --stat查看文件范围补充禁止修改列表设定范围检查脚本测试全绿但线上出错测试断言被改弱或没有覆盖真实业务分支抽查关键断言故意破坏实现看是否变红要求测试必须验证业务结果代理反复执行命令失败本地命令和 CI 命令不一致或依赖版本冲突对比AGENTS.md命令与 CI 配置统一命令先跑通基线代理一直修改同一个问题上下文里缺少错误信息或根因提示查看代理的执行日志和最后一次报错补充日志或错误信息到任务上下文规则文件没有生效文件名不在代理支持的范围内或路径不对查看代理读取的文件列表使用标准AGENTS.md文件名代理改坏公共函数对调用方感知不足检查公共 API 变更 diff对公共模块单独设置评审人和保护分支6.2 典型排查示例代理完成任务但 CI 失败假设你收到一个代理分支的 PRCI 报错显示类型检查失败但代理在任务描述里声称“所有检查通过”。可以按下面这条链路排查第一步确认它改了哪些文件git diff --name-only origin/main...HEAD第二步检查是否改动了AGENTS.md里声明过的依赖或配置git diff origin/main...HEAD -- pyproject.toml requirements.txt第三步在本地用 CI 相同的命令复现rm -rf .venv python -m venv .venv source .venv/bin/activate pip install -e .[dev] mypy app/第四步如果本地能复现类型错误让代理重新修复时把错误信息完整放到 prompt 中类型检查失败错误如下 app/api/users.py:42: error: User has no attribute name 请修复类型问题不要修改 users.py 之外的文件。这个流程的关键是按顺序排查先看输入任务和规则再看环境依赖和命令最后看输出代码和错误。不要一上来就怀疑模型能力大多数问题其实出在流程和上下文上。7. 从试点到制度化让 AI 编程代理真正可控7.1 先在小范围试点不要全团队铺开AI 编程代理落地不适合“一刀切”。建议先挑选 2 到 3 个具备以下特征的项目有完整的自动化测试至少覆盖核心用例。构建时间相对短在 10 分钟内可以完成。技术栈统一依赖清晰。团队成员愿意接受新的评审方式。试点期间定义两个核心指标不要追求过度复杂的度量AI 分支被合入的比例反映代理产出是否有可用性。AI 分支引发 CI 失败或线上问题的次数反映代理是否稳定。每周复盘一次重点看失败案例而不是成功案例。成功案例只能说明流程没被触发失败案例才能暴露流程缺口。7.2 将规则、模板和评审清单固化为团队规范当试点验证有效后再把流程制度化把AGENTS.md纳入仓库根目录评审范围规则变更需要走 MR。把任务描述模板、评审模板上传到团队文档库或 MR 模板中。在 CI 中增加代理分支专用的范围检查任务。让团队每个成员都学会写“可执行的任务描述”而不是一句话需求。在评审 AI 生成代码时要求提交者附带代理的自检日志。制度化不是为了增加流程负担而是为了让每一次代理使用都产生可追踪、可复测、可改进的记录。没有记录的流程无法沉淀经验。7.3 可复用的落地检查清单下面是一份可以直接打印出来贴在工位旁的检查清单也可以作为 MR 模板的一部分。接入前检查[ ] 仓库能稳定通过 build、test、lint[ ]AGENTS.md已包含技术栈、命令、目录边界、禁止修改项[ ] CI 已有独立的代理分支校验 job[ ] 已知哪些任务类型适合代理哪些不适合每次任务开始时[ ] 任务描述包含目标、涉及文件、禁止修改项、验收标准[ ] 任务粒度控制在可评审的 diff 范围内[ ] 代理在独立分支上工作合并前检查[ ] 对比git diff --stat确认没有越界文件[ ] 自动检查全部通过[ ] 评审清单中的错误处理、测试有效性、公共接口影响已确认[ ] 数据库变更确认向后兼容[ ] 回滚方案已准备好上线后观察[ ] 错误率、P95 耗时、核心业务指标与基线对比[ ] 发现问题先回滚再定位根因[ ] 将根因和预防措施更新到AGENTS.md这一套流程做完AI 编程代理会在仓库里留下大量高质量代码同时把风险控制在可接受的范围。它不会自动写出完美代码但团队可以通过流程让“垃圾代码”很难通过每一道关卡。Figma 工程师的分享之所以值得借鉴不是因为 Figma 使用了某个特别的工具而是因为它把 AI 编程代理当作系统中的一个组件来治理。任何团队都可以用同样的思路先设置规则再开放权限先用小范围验证再扩大使用先保证能回滚再追求效率。方向对了代码质量自然会向好的方向收敛。