别让 Data Agent 只会“给答案”:从 !assert 到 save,把分析变成可验证的生产数据资产

别让 Data Agent 只会“给答案”:从 !assert 到 save,把分析变成可验证的生产数据资产

大多数 Data Agent 的演示,都停在同一个漂亮瞬间:

用户问了一个问题,Agent 连上数据库,写出 SQL,返回一张表和几句结论。

这当然有价值。但如果结果只存在于聊天记录里,它还不是生产数据资产。下一次 BI 报表要用,调度任务要用,另一个 Agent 要接着分析,团队往往还得把聊天里的逻辑重新翻译成 ETL、表结构和验证脚本。

真正困难的,从来不是“让模型给出一个答案”,而是让这个答案满足四个条件:

  1. 可验证:脏数据或错误口径不能悄悄越过边界;
  2. 可落地:结果能写入明确的数据目标,而不是只显示在对话框里;
  3. 可回读:写入后的真实状态能被再次读取和核对;
  4. 可复用:BI、调度任务和后续 Agent 能把它当成普通数据资产继续消费。

过去几天,我们在 Infinity SQL 测试环境里分别验证了两个关键环节:

  • !assert:确定性质量门会在发现脏数据时抛错,并停止后续语句;
  • save:分析结果可以写入 MySQL 生产表形态,再通过load回读验证。

把它们放进同一套生产思路,Data Agent 才从“回答问题的聊天工具”迈向“交付可验证数据资产的执行系统”。

一条可信分析链路,至少要闭环四次

可以把理想的执行链路压缩成下面六个动作:

load → select → !assert → save → reload → verify 读取 计算 放行 写入 回读 核验

其中有两条边界尤其重要:

  • !assert决定结果能不能影响外部世界
  • reload + verify决定外部世界是否真的变成了预期状态

模型负责提出计算步骤,运行时负责执行;确定性规则负责放行,目标系统的回读结果负责验收。概率能力和确定性约束各司其职,才是可审计的 Agent 工作流。

第一关:不要让概率系统用概率审计概率

假设 Agent 收到四行订单事件,其中一行user_id是空值:

setevents=''' {"id":1,"user_id":"u1","amount":120} {"id":2,"user_id":"u2","amount":80} {"id":3,"user_id":null,"amount":56} {"id":4,"user_id":"u4","amount":230} ''';loadjsonStr.`events`asevents_table;selectcount(*)asnull_usersfromevents_tablewhereuser_idisnullasnull_check;!assert null_check''' :null_users == 0 '''"user_id 存在空值,阻断下游写入";select"quality gate passed"asmessageasoutput;

在测试环境中,引擎返回:

java.lang.RuntimeException: user_id 存在空值,阻断下游写入

更重要的是,后面的quality gate passed没有执行。

这和让模型“再检查一次结果是否合理”有本质区别。后者仍然是一次概率判断;!assert则把业务规则变成了运行时必须满足的确定条件。条件不成立,管道就停止。

!assert的设计还有一个很实用的特点:它断言的是一张表。

想验证什么,先用select把证据算成一张小表,再对表中字段做判断。Agent 不需要学习另一套测试语言,它已经会的查询能力可以直接变成质量门:

  • 行数必须大于零;
  • 主键不能为空;
  • 唯一键不能重复;
  • 新旧口径必须对账;
  • 金额波动不能超过业务阈值;
  • 写回前的目标分区必须符合预期。

因此,质量验证不再是分析完成后的人工附注,而是管道本身的一部分。

第二关:把质量门放在不可逆动作之前

质量门最有价值的位置,不是在所有探索之前,而是在每个不可逆动作之前:

自由探索区 外部影响区 load → 清洗 → 聚合 → 口径对账 → !assert → save / 发报告 / 触发下游

在自由探索区里,Agent 可以尝试不同查询、修正语法、比较多种解释。错误的成本很低。

一旦要写库、发报告或触发下游任务,风险就完全不同。此时应该由确定性规则决定能否跨过边界,而不是由模型用一句“看起来没问题”自我批准。

这和软件工程中的 CI 很像:

  • 开发者可以自由修改代码;
  • 测试不通过,代码不能合并;
  • Data Agent 可以自由探索;
  • 质量门不通过,结果不能写入生产目标。

关键不在于让 Agent 永远不犯错,而在于让错误无法悄悄扩大影响范围。

第三关:save让探索结果直接成为表

质量门通过之后,分析结果还需要一个明确的生产出口。

我们在另一个独立测试中,让同一条 InfiniSQL 管道读取 MySQL 订单、计算区域报表、写回region_report_daily,再回读验证:

loadjdbc.`biz_mysql.orders`asorders;selectregion,count(*)asorder_cnt,sum(amount)asgmvfromorderswherestatus='paid'groupbyregionasregion_report;saveoverwrite region_reportasjdbc.`biz_mysql.region_report_daily`;loadjdbc.`biz_mysql.region_report_daily`asverify_report;select*fromverify_reportorderbygmvdescasoutput;

测试环境的真实回读结果是:

区域已支付订单数GMV
华东8274,600
华北214,000
华南112,000
西南27,200

这里最值得关注的,不是少写了一段 ETL,而是分析与交付没有发生语言切换

  • 读取数据用load
  • 计算口径用select ... as
  • 写入目标用save
  • 验收结果仍然用loadselect

探索阶段确认过的逻辑,不需要再被另一套框架、另一种语言或另一个团队手工翻译。少一次翻译,就少一类口径漂移。

第四关:为什么写完还必须回读

很多自动化流程把“写入命令没有报错”当成成功,但对生产系统来说,这个证据还不够。

写入动作返回成功,只能说明请求完成;回读才能回答更关键的问题:

  • 目标表是否真的存在?
  • 字段和值是否符合预期?
  • 行数是否正确?
  • 覆盖写是否只影响了预定目标?
  • 下一个消费者能否用标准读取方式获得结果?

因此,reload不是多余的重复,而是一次状态确认。

一个更稳妥的生产模板应该是:

计算候选结果 ↓ 写前断言:数据质量、口径、目标范围 ↓ save:执行明确写入语义 ↓ load:从目标系统重新读取 ↓ 写后断言:行数、关键指标、幂等键、分区范围 ↓ 交付给 BI、调度任务或后续 Agent

写前断言约束“准备写什么”,写后断言验证“实际写成了什么”。两者解决的是不同问题。

save的四种语义,其实是四种风险选择

save支持四种明确模式:

模式语义适合场景主要风险
overwrite覆盖目标全量刷新报表错误目标会造成覆盖
append追加数据增量流水、事件日志重跑可能重复
ignore目标存在时跳过幂等初始化可能保留旧状态
errorIfExists目标存在时报错防止误覆盖需要显式处理冲突

这张表说明,save不是一个模糊的“帮我存一下”。每次写入都必须声明语义,也就是主动选择风险模型。

对 Agent 来说,默认策略应该更保守:

  1. 未确认目标时,优先errorIfExists
  2. 使用overwrite前,先断言目标名称、分区和关键指标;
  3. 使用append时,必须设计业务唯一键或幂等规则;
  4. 写入完成后,必须回读并核验;
  5. 高风险目标还需要权限、审计和人工审批。

语言可以提供确定性原语,但不能替团队替代所有生产治理。

从一次回答,到一个可持续消费的资产

当链路闭合之后,同一份结果会获得完全不同的生命周期。

如果它只在聊天记录里:

  • 用户离开页面后很难复用;
  • 口径与来源不容易追踪;
  • 下一个 Agent 需要重新理解上下文;
  • BI 和调度系统无法直接消费;
  • 失败后很难判断执行到了哪一步。

如果它通过质量门写入表,并完成回读验证:

  • 表名成为稳定引用;
  • 口径可以和管道一起审计;
  • BI 可以直接读取;
  • 调度任务可以按周期刷新;
  • 后续 Agent 可以从这张表继续分析;
  • 失败可以定位在读取、计算、放行、写入或回读中的具体阶段。

这才是“分析结果成为资产”的准确含义:不是把答案存成一段文本,而是把它变成一个有名字、有结构、有验证证据、能被其他系统继续消费的数据对象。

InfiniSynapse 真正需要提供的,不只是聊天体验

用户仍然可以用自然语言提问。自然语言入口没有问题,问题在于底层执行不能只依赖自然语言。

一个可信的产品层应该把用户意图逐步落到可检查的执行对象:

用户目标 → Agent 生成 InfiniSQL 管道 → 运行时执行并保留中间结果 → 确定性质量门决定是否放行 → 明确写入语义落到目标系统 → 回读与断言生成验收证据 → 结果进入 BI、调度或下一轮 Agent

在这个结构里,大模型负责理解模糊目标、生成候选步骤和解释结果;InfiniSQL 负责把数据操作、状态、验证与交付收束到同一套执行语言中。

这比单纯追求“Agent 一次回答得更聪明”更接近企业真实需求。企业需要的不是一句自信的回答,而是一条能够追溯、复跑、阻断和验收的链路。

必须讲清楚的事实边界

这篇文章组合了两个分别完成的测试,不应把它们夸大成一次端到端生产验收:

  • !assert测试验证的是:带空值的数据会触发运行时错误,后续语句停止;该脚本没有连接真实目标库执行写入。
  • save测试验证的是:区域报表写入 MySQLregion_report_daily后,可以被load回读,并返回上述四个区域的结果。
  • 当前证据没有证明生产 SLA、并发吞吐、事务回滚、权限模型或故障恢复时间。
  • 在真实生产环境中,还需要补充访问控制、凭据隔离、审计日志、目标白名单、幂等策略、事务边界、监控告警和人工审批。

诚实地保留这些边界,不会削弱方案,反而说明这是一套可以继续工程化验证的架构,而不是一段营销魔法。

结语:不要只问 Agent“答案是什么”

更好的问题是:

  • 这个答案基于哪份数据?
  • 哪些规则决定它可以被交付?
  • 它写到了哪里?
  • 写入后是否被重新读取和核验?
  • 下一个系统或 Agent 如何继续消费?
  • 如果失败,我们能否知道停在了哪一步?

load → select → !assert → save → reload → verify成为一条完整链路,Data Agent 才不只是会给答案。

它开始具备交付数据资产的能力。


本文中的!assert拦截与save写回/回读数据均来自 Infinity SQL 测试环境的真实执行结果。InfiniSQL 是 InfiniSynapse 数据分析 Agent 的底座引擎。