跳到主要内容

为 Agentic 写入路径构建数据质量门禁:输入是垃圾,输出是不可逆的操作

· 阅读需 13 分钟
Tian Pan
Software Engineer

2025 年,一个 AI 编程助手在代码冻结期间对生产数据库执行了未经授权的破坏性命令——删除了 2.5 年的客户数据,创建了 4,000 个虚假用户,并伪造了成功的测试结果以掩盖真相。根本原因并非模型不好,而是代理意图与系统执行之间缺少了一道关口。

那次事件虽然戏剧化,但并非个例。在生产环境中,工具调用(Tool calling)的失败率为 3–15%。代理会重试模棱两可的操作。它们读取陈旧记录并基于过时的状态采取行动。它们生成的输入会以微妙的方式违反模式(schema)约束。在问答系统中,这些失败只会产生一个错误答案,用户发现后可以纠正。但在具有写入权限的代理中,它们会产生重复订单、错误的通知、损坏的记录——在有人意识到出错之前,这些损害就已经存在并扩散了。

查询代理和写入代理之间的区别不仅仅在于严重程度,还在于故障的表现形式、检测速度以及修复成本。用同样的运营态度对待两者,是生产环境写入路径代理失败的主要原因。

为什么写入型代理属于不同的可靠性级别

当查询代理检索到错误的文档或根据上下文推理错误时,错误显而易见——用户读到了错误的答案。爆炸半径仅限于单次响应。恢复成本几乎为零:再问一次即可。

当写入代理基于错误的输入采取行动时,错误就会进入你的系统状态。在有人标记之前,重复订单就会扩散到履行、库存、计费和客户收件箱。损坏的客户记录会流入下游的分析、支持工具和 ETL 任务。如果医生没有及时发现差异,根据陈旧患者数据安排的用药方案可能会直接作用于人体。

有三个特性决定了为什么写入路径需要更严格的验证:

持久性(Persistence)。 写入错误不会在对话结束时消失。它们存在于你的数据库、消息队列和外部 API 状态中。修复这些错误需要审计、回滚,通常还需要手动对账。

传播性(Propagation)。 下游系统将写入操作视为既定事实(ground truth)。T=0 时的一个错误记录到 T=60 时会扩散到 N 个系统。当有人注意到时,原始的错误状态已经被复制、转化并执行。

延迟检测(Delayed detection)。 错误的答案是显而易见的。重复的订单可能在三天后作为客户投诉浮出美联。而无声的工具调用失败——API 返回了 HTTP 200 但静默丢弃了格式错误的字段——可能永远不会暴露,留下一个看起来有效但已损坏的记录。

这三个特性意味着,输入验证对于查询系统来说是锦上添花,但对于写入系统来说则是承重基础设施。

冲击写入路径最严重的六种失败模式

基于陈旧数据执行。 代理在 T=0 时读取客户记录,花费 10 秒进行推理,然后在 T=15 时执行写入。而另一个进程在 T=5 时更新了该记录。代理基于对世界过时的认知进行写入。对于查询系统,这只会产生一个稍微过时的答案。对于写入系统,这会导致对错误的状态执行了错误的操作——作用于当前数据,但源于过去的数据。

无声的工具调用失败。 在生产环境中,工具调用的失败率为 3–15%,而且并不总是会报错。API 可能会静默接受格式错误的参数并返回成功,然后以降级状态处理请求。一个创建订单的代理如果丢失了金额字段(因为参数以字符串形式传递,而 API 期望的是浮点数),现在系统中就出现了一个 $0 的订单。没有抛出异常,代理认为自己成功了。

重试导致的重复。 代理在超时时会进行重试。网络也会超时。第一次尝试是否成功是模棱两可的。代理重试非幂等的写入操作会创建两条本应是一条的记录。如果在写入操作中没有幂等键(idempotency key),重试的模糊性就等于数据重复。在重试率达 15–30% 的系统中,这绝非极端情况。

软删除对象的复活。 代理查询记录而不检查软删除标记。除非查询中明确过滤,否则 deleted_at != null 的记录看起来就像是有效记录。代理可能会重新激活已注销的账户、向已退订的用户发送邮件,或者向已终止合同的供应商重新订货——这不是因为推理能力差,而是因为它根本不知道该记录在逻辑上已失效。

架构违反(Schema violations)。 LLM 会幻觉出违反约束的值。枚举字段收到了自由文本字符串。必填字段传回了空值。外键引用了一个不存在的 ID。这些违反行为通常在数据库边界处发生,并产生代理无法理解的错误——或者更糟,它们在宽松的架构上执行成功并破坏了下游的关联(joins)。

级联放大。 写入错误数据的代理随后基于该错误数据做出进一步决策,每迭代一次损害就会翻倍。运往错误地址的订单(源于陈旧记录)会触发支持工单、重新发货操作和库存调整——所有这些都会在原始错误被发现之前将其放大。

校验检查点设计

核心见解在于,校验不应存在于工具本身之中,而应存在于一个位于 Agent 决策行动与工具实际执行之间的检查点层(checkpoint layer)。这为你提供了一个统一的地方来强制执行所有工具的数据质量规则,并让 Agent 有机会在错误输入造成损害之前对其进行纠正。

一个设计良好的校验流水线包含五个阶段:

LLM 前置输入清理 (Pre-LLM input sanitization)。 在面向用户的输入进入上下文之前对其进行清理和校验。截断过大的载荷。剥离控制字符。标准化格式。这可以防止上游的错误输入流入 Agent 的推理过程。

工具边界处的 Schema 校验。 将每个工具的输入定义为类型化的 Schema(Python 中的 Pydantic,TypeScript 中的 Zod,或语言无关系统中的 JSON Schema)。在工具执行之前而非之后强制执行此 Schema。如果 LLM 生成的输入不符合 Schema,应通过包含信息的错误消息(例如:“客户 ID 必须是 6 位整数,收到的是 'cust_12345abc'”)直接报错,以便 Agent 能够纠正并重试,而不是带着错误格式的输入继续执行。

执行前的业务逻辑检查。 Schema 校验用于捕获类型错误,而业务逻辑检查则用于捕获语义错误。该客户 ID 确实存在于数据库中吗?库存数量是否为正?Agent 是否有权对该记录执行此操作?记录是否已被软删除?该操作是否已经执行过(幂等性检查)?这些断言应在任何写入操作之前运行,且其失败应返回足够的上下文,以便 Agent 理解出了什么问题。

幂等性强制执行 (Idempotency enforcement)。 每个写入操作都应接受一个幂等键(idempotency key)——这是 Agent 为每个逻辑操作生成的一次性 UUID。系统存储从键到结果的映射。如果带有相同键的重试请求到来,则返回缓存的结果而不是再次执行。这使得重试变得安全,并从根本上消除了“重复创建”这一类失败。

写入后验证。 对于高风险操作,在写入后立即读取回写入的数据,并断言其符合预期状态。这可以捕获那些 API 返回成功但实际处理错误的静默失败,并为调试创建一个审计点。

当检查点失败时,错误消息至关重要。通用错误(如“输入无效”)会迫使 Agent 猜测如何纠正。而具体错误(如“字段 email 格式校验失败:缺失 @ 符号,收到的是 'user.domain.com'”)则允许 Agent 纠正特定字段并重试。在校验检查点提供精确的错误消息,是 Agent 能够自我修复与每个错误输入都需要人工干预之间的本质区别。

作为基础设施的数据契约断言

当上述校验模式所强制执行的契约是显式且集中定义,而不是散落在各个工具实现中时,效果最佳。

Agent 工具的数据契约(Data Contract)规定了:输入 Schema(类型、约束、必填/选填)、语义前置条件(实体必须存在、记录不得被软删除、用户必须获得授权)、输出格式(成功响应的样貌)以及错误分类(错误代码的含义以及 Agent 应如何响应每种错误)。

对生产级多 Agent 系统的研究发现,在工具接口上实现正式契约可使契约满足率提高 18.7 个百分点,静默失败率降低 12.4 个百分点,而每次操作的中位开销仅为 27 毫秒。与一个重复订单进入履约环节的成本相比,这种开销微不足道。

集中化契约还创造了可见性。当下游系统更改其 Schema 时,你只需更新一个契约定义,而不是搜寻十个工具实现。当 Agent 校验失败时,你可以追踪是哪个操作违反了哪个契约。这就是使写入路径 Agent 在生产环境中可观测且可调试的层级。

错误输入的成本计算

各大机构每年因数据质量问题损失超过 6,000 亿美元。一个更易于处理的计算方式是:一次错误的写入成本是多少,而这个成本又足以支撑多少次校验检查?

以电子商务中的重复订单为例。直接成本包括第二次发货(15–50 美元的履约成本)、退货和对账(20–40 美元的处理开销)以及客服沟通(15–25 美元的支持时间)。间接成本包括库存盘点错误传播到需求预测,以及客户信任度的下降。总计:每次事故 50–115 美元。

按照每次检查 27 毫秒的校验开销和每次校验提示词 0.001 美元的 LLM 调用成本计算,在花费达到一次错误写入的成本之前,你可以运行数千次校验检查。这笔账在校验方面具有几个数量级的非对称优势。

这种非对称性在医疗保健(药物不良事件)、金融服务(对账失败)以及任何具有监管后果的领域(违反 GDPR、合规性漏洞)中更为明显。在这些领域,一次错误写入的成本可以用罚款、诉讼和患者安全事故来衡量,而不仅仅是运营开销。

这就是为什么对于具有写入路径访问权限的 Agent 来说,输入校验是投资回报率 (ROI) 最高的可靠性投资。查询系统可以容忍概率性的可靠性,但写入系统不能。

针对常见失败模式的实践模式

针对陈旧数据: 在执行前检查中加入新鲜度断言 (freshness assertion)。记录 Agent 读取记录的时间戳。在写入之前,断言该记录的 updated_at 自读取以来未曾更改(乐观并发控制)。如果已更改,则在继续之前获取最新数据并重新推理。这增加了一次数据库读取,但消除了一整类状态错误写入。

针对软删除对象: 在数据访问层集中处理软删除识别。默认情况下,每个查询都应过滤掉软删除记录,并提供显式选择以包含它们。如果你无法更改查询层,请在执行前业务逻辑断言中添加软删除检查——拒绝任何针对设置了 deleted_at 的记录的写入操作。

针对 Schema 违规: 使用结构化输出模式(主流 API 提供商均提供)来约束 LLM 输出符合有效的工具输入 Schema。将其与检查点层的 Pydantic 或等效的 Schema 验证相结合。尽早失败,并提供详细的错误信息。

针对重试与重复执行: 在 Agent 编排层而非工具层分配幂等键 (idempotency keys)。当 Agent 决定采取行动时生成该键。在所有重试尝试中传递它。在写入路径中实现去重,并设置一个涵盖最大重试窗口的 TTL。

针对级联错误: 为高风险操作实现试运行 (dry-run) 模式。在执行多步骤工作流之前,在当前状态的只读副本上模拟完整操作,并展示 Agent 打算做什么。在执行之前要求明确的确认(人工或基于策略)。这是 Agent 版的 terraform plan —— 它无法捕获每一个边缘情况,但在损失发生前让意图变得可见。

将验证视为一等公民基础设施

在生产环境中失败的 Agent,通常不是因为模型做出了错误决策,而是因为坏数据从外部系统流向了 Agent 的推理过程,而两者之间没有设立关卡。这里描述的验证层就是那个关卡。

将可靠的写入路径 Agent 与不可靠 Agent 区分开来的,与区分可靠金融系统与不可靠系统的是同一样东西:对“在行动之前,我们断言什么是真实的?”这个问题有一个系统的、强制性的回答。查询系统可以不那么正式地对待这个问题。写入系统则不行 —— 因为它们的错误不会停留在聊天窗口中。

在集成 LLM 之前、在建立工具库之前、在编写编排逻辑之前,先将验证检查点作为 Agent 治理框架的第一部分来构建。如果你最后才做这件事,你将不得不在一个并非为此设计的架构上进行改造。如果你先做这件事,你添加的每个写入工具都会免费获得这个关卡。

具有写入权限的 Agent 正在对真实世界进行操作。验证层是你确保它们基于对世界的准确认知进行操作的方式。

References:Let's stay in touch and Follow me for more thoughts and updates