返回探索
指南VibeFix 编辑部更新于 2026年10月24日

别把整库钥匙交给 Agent:vibe 项目数据访问护栏实战

给编码 Agent 接数据库前,先把护栏立好:只读账号、人工审批的写路径、最小权限与拒绝策略、不可逆动作确认关卡、完整审计日志与预览模式,一个下午就能落地。

技术人员在 NERSC 数据中心机柜前用笔记本电脑工作的实拍照片,象征人在机器旁把关数据访问。

给编码 Agent 接数据库,是 vibe 开发里最容易一时爽、事后哭的操作。项目跑起来之后,你总会想让 Agent 帮你查一下用户数据、修一条脏数据、跑一次迁移。它一开口,你顺手就把生产数据库连接串贴进了对话。这一贴,等于把整座仓库的钥匙交给了它:读、写、删、改表结构,全凭它一句话。更麻烦的是,Agent 不会像人一样犹豫,它执行得越快,错得越彻底。

本篇不讲大道理,只讲一套一个人一个下午就能落地的护栏:只读账号、人工审批的写路径、最小权限角色、不可逆操作前的确认关卡、每一次数据动作都留痕,外加演练用的预览模式。做完这套,你仍然可以让 Agent 帮你干活,但它再也碰不到不该碰的东西,删库之前必须先经过你。

先划清边界:这篇不讲什么

为了避免和站内已有的指南撞题,先把边界说清楚。本篇只讲 Agent 访问项目数据时的权限、审批与审计:给 Agent 多大的数据权限、写操作怎么经过人批、不可逆动作前怎么设卡、每一次动作怎么留痕。它不讲通用密钥存放与轮换,那是已发布的凭据管理指南的范围;不讲隐私法规与合规义务,那是已发布的隐私合规指南的范围;不讲第三方接口超时、重试与熔断,那是已发布的接口韧性指南的范围;也不讲多租户之间的数据隔离,那是已发布的多租户隔离指南的范围。同样,它不讲团队内部人与人之间的权限模型,那是 RBAC 指南的地盘。本篇的唯一主角,是 Agent 这个特殊的、速度极快又不会自我怀疑的数据访问者。

第一原则:永远不要把主账号给 Agent

很多项目的数据库只有一个超级账号:能建表、能删库、能改权限,连接串就躺在环境变量里。开发时图方便,Agent 用的也是这个账号。这是最危险的默认状态。正确的做法是把账号分层:人用一个账号,Agent 用另一个权限更小的账号,生产写操作再走第三条路。分层之后,就算 Agent 被诱导、被误导,或者单纯理解错了你的意图,它能造成的破坏也被锁死在权限天花板之下。

动手之前先做一次权限盘点。打开数据库,列出所有账号和它们的权限,问自己三个问题:这个账号能删表吗?能改表结构吗?能看到全量用户敏感字段吗?只要有一个答案是 yes,而这个账号又会被 Agent 用到,就说明护栏还没建好。盘点本身只要十分钟,却是后面所有步骤的地基。

第一步:给 Agent 一个只读账号

Agent 日常九成的数据需求其实是读:查一下这条记录为什么异常、统计一下某个功能的用量、确认迁移前的数据分布。这些需求一个只读账号全能满足,而且出不了大事。只读不是口头约定,必须是数据库层面真正收紧的权限。

以 PostgreSQL 为例,给 Agent 建一个专用只读角色,只给 SELECT,不给 INSERT、UPDATE、DELETE,更不给 DDL:

-- 1. 创建 Agent 专用角色
CREATE ROLE agent_reader LOGIN PASSWORD '用密码管理器生成的强口令';
-- 2. 只给连接和查表权限,不给写权限
GRANT CONNECT ON DATABASE app_db TO agent_reader;
GRANT USAGE ON SCHEMA public TO agent_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO agent_reader;
-- 3. 新表默认也只读,避免以后漏授权
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO agent_reader;
-- 4. 明确收回危险权限,写两遍也不嫌多
REVOKE INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM agent_reader;

几个细节值得抠。第一,只读账号也要限流:给它单独的连接池上限,比如最多 5 个连接,避免 Agent 写个烂查询把连接池打满,拖垮正常用户。第二,敏感列要单独处理:如果用户表里有手机号、邮箱、身份证号这类字段,不要靠 Agent 自觉不看,用视图把敏感列挡掉,或者给 Agent 一个脱敏视图,只暴露它真正需要的列。第三,Agent 的连接串和人的连接串必须分开存放,命名也要分开,比如 AGENT_DATABASE_URL 和 DATABASE_URL,混用一个变量名是事故的温床。

如果你用 MySQL,思路完全一样,只是语法换成 GRANT SELECT ON app_db.* TO 'agent_reader'@'%'; 并且记得 FLUSH PRIVILEGES。换数据库不换原则:Agent 能读什么,由数据库说了算,不由提示词说了算。提示词可以被绕过、被误解、被注入,数据库权限不会。

第二步:写操作走另一条路,而且必须经过人批

只读账号建好之后,Agent 迟早会说:我查到了,帮你改一下吧。这时候千万别临时把写权限放开。正确姿势是:写操作不直接执行,而是先变成一份待审批的提案,你看过、改过、点了头,它才真正落库。

对个人项目来说,最简单的审批流不需要任何新系统,用一张 proposals 表加一个人工执行的脚本就够了。Agent 的产出不是 SQL 执行结果,而是一份结构化的改动提案:要改哪张表、影响多少行、原值是什么、新值是什么、为什么要改、回滚怎么做。提案先写进表里,状态是 pending,你review 之后才由你本人(不是 Agent)执行。

-- 待审批改动提案表(Agent 只能 INSERT,不能 UPDATE 状态)
CREATE TABLE agent_change_proposals (
  id          BIGSERIAL PRIMARY KEY,
  proposed_by TEXT NOT NULL DEFAULT 'agent',
  title       TEXT NOT NULL,
  sql_text    TEXT NOT NULL,
  affected_rows INT,
  reason      TEXT,
  rollback_sql TEXT,
  status      TEXT NOT NULL DEFAULT 'pending',
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Agent 只能提交提案,不能自己改状态、不能删除提案
GRANT INSERT, SELECT ON agent_change_proposals TO agent_reader;
-- 审批与执行只由你的个人账号操作,不授权给 Agent

审批时你要亲手核对四件事。第一,把 SQL 贴到只读环境先跑一遍 SELECT 版本,确认影响行数和 Agent 说的一致。第二,看 WHERE 条件有没有漏:没有 WHERE 的 UPDATE 和 DELETE,一律打回。第三,确认回滚方案真实可用,而不是一句以后再说。第四,问一句有没有更小的改法:能改一行就别改一千行,能先灰度一部分用户就别全量推。批完之后,执行动作由你本人完成,或者由一个需要你手动确认的脚本完成,Agent 只负责准备,不负责扣扳机。

这条路看起来多了一步,但它买到的东西很实在:你永远有机会在灾难发生前喊停。而且提案表本身就是一份天然的审计记录,事后想查谁提议改了什么、为什么改,翻表就有。

第三步:最小权限角色与显式拒绝策略

只读账号解决的是数据库这一层,云上资源还要再加一层。很多 vibe 项目的数据不只在数据库里,还在对象存储、队列、第三方后台里。Agent 如果拿着你的云账号密钥,同样可以删存储桶、发短信、调付费接口。所以云侧也要按最小权限来:给 Agent 的云身份只开它当前任务需要的权限,其余一律不给;对高危动作再加一条显式拒绝。

以云厂商的身份策略为例,思路是先给一个窄角色,再叠加拒绝规则。窄角色只允许读指定的存储桶、读指定的队列;拒绝规则则明确写死:禁止删除存储桶、禁止删除数据库实例、禁止修改计费与身份策略、禁止把数据导出到外部地址。允许清单和拒绝清单同时存在时,拒绝优先,这是兜底的关键。即使以后有人误给 Agent 多加了一个宽泛权限,拒绝规则仍然能把它拦在高危动作之外。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": [
        "s3:DeleteBucket",
        "rds:DeleteDBInstance",
        "iam:*",
        "billing:*"
      ],
      "Resource": "*"
    }
  ]
}

这段策略只是一个示意骨架,落地时要按你实际用的云厂商语法改写,并且先在测试环境验证:确认 Agent 的正常读操作不受影响,高危动作确实被拒绝。还有一条铁律:Agent 的云凭证和你的个人凭证分开,Agent 的凭证定期轮换,项目结束或换 Agent 时立即吊销。凭证分开存放、分开轮换、分开吊销,这三件事做到了,事后止损才有抓手。

第四步:不可逆动作前的确认关卡

有些动作一旦执行就收不回来,必须单独设卡。本篇把下面五类动作划为不可逆或高危,默认全部拦截,Agent 只能提案,不能直行:删除数据、迁移表结构、批量更新、批量删除、向外发送(发邮件、发短信、调外部接口、公开数据)。判断标准很简单:执行后能不能一键完整恢复?不能,就进关卡。

关卡的设计要具体,不能只写一句请确认。每一类高危提案都必须附带五样东西:影响范围(多少行、哪些用户)、执行前备份的快照编号、回滚 SQL 或回滚步骤、执行窗口(建议放在低峰期)、以及你本人的显式批准记录。缺一样,提案就不完整,直接打回。特别是迁移操作,流程应该是:先在测试库跑一遍迁移,再跑一遍回滚,确认两者都通,才有资格进生产审批。没有在测试库验证过的迁移,不进生产。

批量操作还有一条硬性护栏:先设行数上限。任何批量更新默认上限 100 行,超过就必须拆批、分批审批。Agent 常常低估批量操作的影响面,一句帮你把所有旧数据都清理一下,背后可能是几十万行。行数上限逼着它先算清楚、先给样本、先小批量验证,而不是一上来就全量开火。

第五步:预览模式先行,正式执行后置

审批流之外,还有一个成本极低的好习惯:干跑模式。所有写操作在真正执行之前,先跑一遍预览:对 UPDATE 和 DELETE,先跑对应的 SELECT,输出将要影响的行数和前 10 行样本;对迁移,先输出变更前后的表结构对比;对外部发送,先输出收件人清单和内容样本,不真正发出。预览结果要落到提案记录里,和 SQL 放在一起,供你审批时对照。

预览模式的好处不只是防错,它还逼 Agent 把意图说清楚。一个只能说出我要改数据的 Agent,和一个能说出我要改这 37 行、样本如下、回滚如下的 Agent,完全是两种可信度。对个人开发者来说,这一步不需要框架支持:约定好 Agent 先输出预览、你确认后再执行,就已经是可用的干跑模式。等项目变大,再把它固化成脚本或 CI 检查也不迟。

第六步:审计日志,记下 Agent 的每一次数据动作

护栏建好之后,还差最后一块:留痕。没有日志的护栏等于没有护栏,出事之后你连谁干的、什么时候干的、影响了多大都说不清。审计日志要记的字段很固定:时间、操作者(Agent 还是人)、账号、操作类型、目标表与行、SQL 或操作摘要、影响行数、审批记录编号、执行结果。记全这几项,事后复盘才有材料。

审计表本身也要防篡改。最关键的一条:Agent 对审计表只有追加权限,没有修改和删除权限。如果 Agent 能删自己的日志,日志就失去了意义。审计表可以这样设计:

-- Agent 数据动作审计表:只追加,不可改、不可删
CREATE TABLE agent_audit_log (
  id          BIGSERIAL PRIMARY KEY,
  actor       TEXT NOT NULL,
  account     TEXT NOT NULL,
  action      TEXT NOT NULL,
  target_table TEXT,
  row_count   INT,
  sql_summary TEXT,
  proposal_id BIGINT,
  result      TEXT,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);
GRANT INSERT, SELECT ON agent_audit_log TO agent_reader;
-- 注意:不要给 Agent UPDATE 或 DELETE 权限
-- 审计表定期备份到 Agent 接触不到的位置

日志记下来还不够,要定期看。每周花十分钟翻一次审计日志,重点看三类记录:被拒绝的高危动作、批量操作、以及审批之外的写尝试。被拒绝的记录尤其值得看,它往往暴露了 Agent 理解偏差最大的地方,也是你下次改进提示词和护栏的线索。日志没人看,就只是占磁盘的文本。

一个下午的落地顺序

如果你只有一个下午,按这个顺序做,每一步都有明确的完成标准。第一小时:权限盘点加只读账号。完成标准是 Agent 用只读连接串能查数据、写操作被数据库直接拒绝。第二小时:提案表加审批流程。完成标准是你能收到一份带回滚方案的提案,并且只有你能执行它。第三小时:云侧最小权限与拒绝规则。完成标准是高危云操作被显式拒绝,正常读操作不受影响。第四小时:高危动作清单、行数上限、预览约定、审计表与每周复查提醒。完成标准是你故意让 Agent 提一个删表提案,它被拦住、被记下来、等你审批。

做完之后,再做一次破坏性演练:让 Agent 尝试删一张测试表、批量改一千行、把数据导出到外部。预期结果是三件事全部被拦住,并且审计日志里都有记录。演练通过,这套护栏才算真正立住了;演练中任何一处漏网,都是上线前必须补的洞。

常见踩坑

第一,提示词里写仅供读取不等于只读。提示词约束在 Agent 理解正确时才有效,数据库权限在任何时候都有效,两者不能互相替代。第二,测试库和生产库用同一套凭证。Agent 在测试库练手时拿到的如果是生产凭证,练手就可能变成生产事故,两个环境的凭证必须物理隔离。第三,备份只存在脑子里。迁移前没有可恢复的快照,审批就等于空谈,备份编号要写进提案里,而不是事后去找。第四,审批流太重导致绕过。一旦审批流程繁琐到让人想走后门,护栏就会被架空,个人项目的审批要保持轻量:一张表、一次核对、一次手动执行,刚好够用。第五,权限给出去就不再收回。项目阶段变了、Agent 换了、任务结束了,权限要及时回收,长期有效的宽权限是最常见的慢性泄漏。

检查清单:贴在项目 README 里

  • Agent 使用独立只读账号,数据库层面无写权限、无 DDL 权限。
  • Agent 与人的连接串、云凭证分开存放、分开轮换、分开吊销。
  • 写操作只以提案形式提交,含 SQL、影响行数、回滚方案与原因。
  • 审批时先在只读环境验证影响行数,WHERE 缺失的写操作一律打回。
  • 删除、迁移、批量更新、批量删除、外部发送五类动作默认拦截,需人工批准。
  • 批量操作默认上限 100 行,超限拆批、分批审批。
  • 迁移先在测试库跑通正向与回滚,才有资格进生产审批。
  • 执行前必有备份快照编号,写进提案记录。
  • 所有写操作先预览:影响行数与样本行先行,正式执行后置。
  • 审计日志记录每次 Agent 数据动作,Agent 仅可追加、不可改删。
  • 每周复查审计日志,重点看被拒高危动作与异常批量操作。
  • 敏感列通过视图或脱敏处理,不直接暴露给 Agent 账号。
  • 连接池与行数上限已设,烂查询拖不垮生产。
  • 破坏性演练通过:删表、批量改、外部导出三项均被拦住并留痕。

权限回收:任务结束就收钥匙

护栏建好不等于一劳永逸,权限也要有生命周期。一次性的调试任务结束,Agent 账号就该立即停用或降权;换了一个 Agent 工具,旧的连接串和云凭证要同步吊销重发;项目从个人实验变成对外服务,Agent 的默认权限要重新评审一次,而不是沿用实验期的宽松设置。建议给每个 Agent 凭证写上用途和到期日,到期自动提醒复核,宁可多提醒一次,也不要让一个早已没人记得的凭证长期有效。回收动作本身也要记进审计日志:谁在什么时候吊销了哪个账号,事后同样要能查到。权限发出去容易、收回来难,把回收写进流程,护栏才算闭环。

结语:让 Agent 跑得快,也要让它刹得住

vibe 开发的魅力在于速度,但速度本身不是护栏。真正让你敢把数据交给 Agent 的,不是它有多聪明,而是它犯错时破坏半径有多小、你喊停时有多及时、事后复盘时证据有多完整。只读账号划清了底线,审批流夺回了扳机,拒绝策略兜住了底,审计日志留下了证据,预览模式给了你思考的时间。这五件套加起来不到一个下午的工作量,换来的,是你以后每次让 Agent 碰数据时都不用提心吊胆。市面上的受治理访问工具可以作为补充思路,但本篇的方法不依赖任何特定产品,只用数据库和云厂商自带的权限机制就能落地。先把护栏立好,再谈让 Agent 跑多快。

阅读 0评论 0

评论 (0)

ME
0/1000
评论加载中...
浏览项目广场发布你的项目

相关文章

BalticServers 机房实拍:一排亮着蓝色指示灯的服务器机柜,提醒读者每个 AI 功能都依赖可能出故障的基础设施。
指南
模型挂了怎么办:vibe 项目 AI 功能的降级与兜底策略

你的 vibe 项目的 AI 功能迟早会遇到服务商宕机、限流、超时和坏输出。上线前就把降级模式设计好:超时重试、降级阶梯、诚实缓存、排队通知与故障夜处置清单,本文一次讲清。

开发工作流后端工程项目创作