Skip to content

操作回执:Agent 说改了 1 行,数据库实际改了 301 行 ​

封面

讨论 Agent 操作数据库时,大家最怕的通常是“删库”式的事故:一条 DROP TABLE 删掉整张表,或者一次误操作删掉整块存储盘,连同上面的数据库一起消失。

这次的事故安静得多,也因此更难发现。

一个客服 Agent 被要求把 42 号订单标记为“已发货”。它执行了一条范围很窄的 SQL:

sql
UPDATE orders SET status = 'shipped' WHERE id = 42;

工具返回成功,Agent 回复:“已完成,更新 1 行。”聊天窗口里一切正常。

但数据库实际提交了 301 行 变更。另外 300 张订单里,有不少原本是“已取消”,现在也被标成了“已发货”。放在真实业务里,下游系统只认订单状态,可能出现两种后果:

  • 仓储系统照着状态备货、发货,而这些订单早已被取消,没人付过钱;
  • 系统自动给客户发“您的订单已发货”通知,实际上并没有包裹寄出,客户白等一场。

这是开源工具 Deedglass 在 10 月发布的一个演示,昨天在 Hacker News 上被讨论。数据是虚构的,但它暴露的问题很真实:Agent 的操作回执,凭什么可信?

先说结论:Agent 的自我报告不是证据,工具和数据库的返回值也只是证据的一部分。要知道一次写操作实际改了什么,只能看数据库自己的变更日志。

一、Agent 没撒谎,数据库的回执本来就只说了一半 ​

第一反应很容易是“模型又幻觉了”。但事实并非如此。

多出来的 300 行从哪来 ​

Deedglass 的作者在原生 psql 里直接执行了同一条语句:

  • 执行前,已发货订单 160 张;
  • 数据库返回 UPDATE 1;
  • 执行后,已发货订单 461 张。

多出来的 300 行,来自表上一个多年前写下的旧触发器,逻辑是:“一张订单发货时,把同批次其余未发货订单也标为已发货。”

它服务的仓储流程早已不存在,触发器却一直留在库里。

UPDATE 1 到底数的是什么 ​

按 PostgreSQL 官方文档,返回值里的数字是“该查询更新的行数”。触发器在同一事务里改动的其他行,不计入这个数字。

于是整条链路上,每一环都“如实”转述了上一环:

  1. 数据库如实返回 UPDATE 1;
  2. SQL 工具如实把这个结果交给 Agent;
  3. Agent 如实告诉用户“更新 1 行”;
  4. 用户相信了。

没有任何一环撒谎,但最终信息是错的。

触发器只是其中一种 ​

能让数据库在一条语句之外“顺手”多做事的机制还有很多:

  • 视图上的重写规则;
  • 名字看起来无害、实际会写数据的函数;
  • 外键级联;
  • 各种扩展。

调用方只看返回值,这些副作用一个都看不到。

二、三级证据:声称、回读、证明 ​

Deedglass 的价值,不在于它发现了一个触发器,而在于它把 Agent 写操作的“证据”分成了三个等级。

等级证据来源能证明什么看不到什么
声称(Claimed)工具或 Agent 的返回结果调用成功了数据库实际改了什么
回读(Checked)写完后再查一次目标行你点名的那一行确实被这次操作写过你没点名的其他行
证明(Proven)数据库的变更日志这个事务实际提交了哪些行未监控的表、数据库之外的动作

最反直觉的是第二级 ​

很多团队以为,写完之后再 SELECT 一次就算核验了。

演示里,回读确实通过了:42 号订单确实被这次操作改成了“已发货”。

回读只能确认你问到的那一行,永远发现不了你没问到的 300 行。

第三级证据从哪来 ​

来源是 PostgreSQL 的逻辑解码(logical decoding)。

数据库为了复制,会把每次提交的行级变更写入预写日志(WAL)。逻辑解码能把它还原成:哪个事务改了哪些行,每一行改前、改后各是什么。

这个记录不关心 SQL 怎么写、哪个工具发的、Agent 事后怎么说,只记录实际提交了什么。

怎么比对 ​

Deedglass 把两样东西放在一起:

  • 预期:语句点名的范围,即 orders 表中 id = 42 的 1 行;
  • 实际:变更日志中这个事务的范围,即 orders 表中 301 行,其中 300 行在点名范围之外。

两者一致,判为“相符”;不一致,判为“不符”,并给出逐行差异。

结果会封存成一条带签名的记录,可以离线验证,不必信任 Deedglass 的服务器。

我们在《别信 agent 说的“我读完了”》里讨论过:Agent 的最终回答不是证据。这个案例更进一步:连工具和数据库的返回值,也只是证据链的一部分。

三、事前规则管得住请求,管不住数据库接下来做什么 ​

这个案例里有一个容易被忽略的细节:权限规则判断得完全正确。

演示中的规则只允许 Agent 做一类写操作:按订单号修改单张订单的状态。它会检查:

  • SQL 的类型;
  • 涉及哪些表;
  • 是否带条件;
  • 有没有未知函数。

这条语句全部通过。换成人工审核,也会批准。

问题不在请求,而在数据库收到请求之后做了什么。这正是事前控制的天然边界:

策略判断的是“Agent 想做什么”,看不到“数据库实际做了什么”。

所以 Agent 的写操作需要两段式控制:

Agent 写操作的两段式控制:事前策略判断请求,事后用变更日志核对实际提交,不符时由人会签撤销

事前,用策略决定这次调用能不能执行,以及执行后需要哪一级证据。

事后,用变更日志核对实际提交的范围,把预期和实际的差异封存成记录。

出问题时,因为变更日志里有每一行改前和改后的值,可以逐行撤销。演示里的撤销分三步:

  1. 先生成撤销计划,列出 301 行的原值;
  2. 一位运维人员会签后才执行;
  3. 撤销本身再经过一次变更日志核对。

执行后,各状态订单数与 Agent 操作前完全一致:已发货 160、已付款 223、已取消 37。

撤销也要人会签,理由很朴素:撤销同样是一次写操作,同样可能出错。

四、网关管“能调什么”,核验管“实际改了什么” ​

同一天,Uber 工程团队公开了自己的 MCP 网关设计。它从另一端回答了同一个问题。

Uber 的网关做了什么 ​

网关目前承载 800 多个 MCP 服务器、5000 多个工具,分成两层:

  • 注册中心(控制面):维护所有工具的定义、负责团队和启用状态。新发现的工具默认禁用,所属团队审核后才能启用。
  • 代理网关(数据面):把 MCP 调用翻译成内部的 HTTP、gRPC 等请求,再把结果转回给 Agent。

在这两层之上,网关还做了三件和权限相关的事:

  • 工具级授权:对人、服务和 Agent 应用不同的访问策略;
  • 敏感数据屏蔽:工具返回结果时,自动屏蔽个人信息等敏感字段;
  • 响应投影:Agent 只声明需要哪些字段,网关在返回前裁掉其余内容,减少上下文消耗。

身份:这次调用最终代表谁 ​

Uber 另一篇文章介绍了身份层的做法:Agent 调用使用短期有效、只能单跳使用的令牌,令牌里携带完整的调用者链条。网关据此判断“这次调用最终代表谁”。

这和我们在《多跳委托》里讨论的逐跳收敛思路一致。

两者拼在一起 ​

把 Uber 和 Deedglass 放在一起,正好是 Agent 写操作控制的两半:

  • 网关回答:谁、以什么身份、能调用哪个工具、能看到哪些字段;
  • 核验回答:这次调用之后,系统里实际发生了什么变化。

缺了任何一半都不行:

  • 只有网关,你知道 Agent 被允许做什么,但不知道它实际造成了什么;
  • 只有核验,你能事后发现问题,但事前没有挡住不该发生的调用。

两者的共同点是把控制放在了提示词之外:规则由基础设施执行,证据由系统自身产生,都不依赖模型自觉。

五、这套做法的边界 ​

Deedglass 在文中把局限写得很清楚,这部分比演示本身更值得看。

Deedglass 自己承认的四条 ​

它在提交之后运行。 核验发现不符时,301 行已经真实存在于数据库里,直到撤销完成。如果下游系统在这段时间里读取了数据,比如仓库已经开始拣货,撤销数据库撤不回现实世界的动作。

只看被监控的表。 如果触发器写入的是未纳入监控的表,或者调用了数据库之外的服务,核验看不到。所以要监控的是 Agent 能直接或间接触及的所有表,而不只是它点名的那张。

它信任数据库本身。 变更日志是数据库自己的记录。如果数据库服务器或超级用户账号已被攻破,这套证据也不再可靠。

目前只支持 Postgres。 对其他系统,只能退回到前两级证据:工具的成功返回,或通过 API 回读。

落地还有一笔运维成本 ​

开启逻辑解码需要把 wal_level 设为 logical,并创建复制槽。

复制槽有个常见的坑:如果长期没人消费,数据库会一直保留预写日志,最终可能占满磁盘。所以这类组件要纳入监控和运维值守,不能装完就不管。

Uber 的数据缺口 ​

Uber 那篇文章给出了规模数字,但没有公布任务成功率、延迟、成本等量化指标,外部很难评估效果。

六、这对我们意味着什么 ​

对数据工程师:老库里的副作用要先盘点 ​

这个案例的“元凶”不是 Agent,而是一个被遗忘的旧触发器。

过去,人写 SQL 时多少知道库里有哪些历史包袱;Agent 不知道,它只会按字面执行。

在给 Agent 开写权限之前,值得先盘点一遍这些 Agent 看不见的副作用:

  • 触发器;
  • 重写规则;
  • 级联删除;
  • 会写数据的函数。

我们在《Agent 直连数据库的三道关》里讲过角色、列权限和行级安全,它们仍是第一道防线;盘点副作用是第二道。

对 Agent 平台设计者:回执要包含“预期”和“实际” ​

一个可信的写操作回执,至少应该同时给出两样东西:

  • 这次操作打算改什么;
  • 系统实际改了什么。

只返回“成功,1 行”,等于把核验的责任推给了读回执的人,而读回执的人通常没有能力核验。

对审计和合规:日志不等于证据 ​

普通日志记录的是“某个组件说发生了什么”,可能被修改,也可能本身就记错。

可以作为证据的记录,需要满足三个条件:

  • 来自系统自身的事实源;
  • 能防篡改;
  • 可以离线验证。

《数据血缘只能追到表》里讨论过 AI 答案的溯源问题。Agent 写操作的溯源是同一件事的另一面:不只要知道答案从哪来,还要知道动作改了什么。

小结 ​

  • Agent 的“1 行”只是数据库告诉它的。 返回值只统计语句直接命中的行,触发器、规则、级联造成的变更都不在里面。
  • 写完再回读,只能确认你问到的那一行。 要知道实际提交了什么,证据必须来自数据库自己的变更日志。
  • 事前权限和事后核验缺一不可。 网关决定 Agent 能调用什么,核验确认调用之后实际改了什么;两者都应由基础设施执行,而不是靠提示词。
  • 核验不能替代窄权限,撤销也撤不回现实。 它是在角色、列权限和行级安全之上的补充,而且发现问题时,变更已经发生过。

最后留一个问题:你的系统里,Agent 的写操作回执来自哪里?是 Agent 的自我报告、工具的返回值,还是数据库自己的记录?

本文部分内容由 AI 辅助生成,经人工审校和补充后发布。

参考资料 ​

  1. Arjun Prabhakaran. "Your AI agent said it updated 1 row. Postgres changed 301." Deedglass, 2026-10-14. https://deedglass.com/blog/agent-said-1-row-postgres-changed-301
  2. Deedglass Docs. "Catch '1 row reported, 301 committed'." https://docs.deedglass.com/guides/catch-a-mismatch/
  3. Deedglass. "Logical decoding for AI agent audits in Postgres." https://deedglass.com/glossary/logical-decoding-agent-audit
  4. Hacker News. "Show HN: Deedglass – An agent said it updated 1 row; Postgres changed 301." 2026-11-02. https://news.ycombinator.com/item?id=50337736
  5. PostgreSQL Global Development Group. "UPDATE." PostgreSQL 18 Documentation. https://www.postgresql.org/docs/current/sql-update.html
  6. PostgreSQL Global Development Group. "Overview of Trigger Behavior." PostgreSQL 18 Documentation. https://www.postgresql.org/docs/current/trigger-definition.html
  7. Uber Engineering. "Designing MCP Gateway: Uber's MCP Management Platform." https://www.uber.com/us/en/blog/designing-mcp-gateway/
  8. Uber Engineering. "Solving the Identity Crisis for AI Agents." https://www.uber.com/us/en/blog/solving-the-agent-identity-crisis/
  9. InfoQ. "Uber Builds MCP Gateway to Govern Access to 800+ Servers and 5,000+ AI Agent Tools." 2026-11. https://www.infoq.com/news/2026/11/uber-mcp-gateway-ai-agents/
  10. ZenML LLMOps Database. "Uber: Scaling Enterprise Agent Access with a Centralized MCP Gateway." https://www.zenml.io/llmops-database/scaling-enterprise-agent-access-with-a-centralized-mcp-gateway
  11. LLM Digest. "What happened in AI — Nov 2, 2026." https://www.llm-digest.com/daily