You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
目标受众: 正在建数据 Agent 的技术负责人、平台/数据工程师、AI 工程师
关系: 读过 #36 再读这篇;#36 是 What/Why,这篇是 How + 落地后的诚实复盘。
回到 #36 的承诺
#36 提了四层参考架构(L1 基础设施 / L2 执行+权限 / L3 语义契约 / L4 Agent+Skills),核心论点是:行业通病是跳过 L3——L4 直接连 L2,后果是真相碎片化、变更静默出错、准确性靠运气。
#36 还留了两个"下一步"没做完,只画了蓝图:
这篇就是这三件事的落地报告。不是又一份设计,是做出来之后的证据 + 诚实的坑。
L3:从"文档态"到"契约态"
文档态知识会静默腐烂
一个成熟的数据 Agent skill 家族有一个
knowledge/目录——几千行高质量 markdown,描述表语义、必填 filter、粒度规则、已知陷阱。它看起来是契约层。它不是。它是文档态:Agent 会读的散文。真正的 SQL 内联在每个 skill 的代码里,语义事实活在旁边的注释里。于是:
修法:契约即数据 + fail-closed 的门
两个产物,都零依赖(纯标准库,所以能作为裸文件拷贝分发到每个消费 skill):
catalog(机器可读契约): 一个纯 dict 模块,逐表声明:列、枚举值、必填 filter、粒度注记、和已知陷阱(在这个数据源上本身就坏的 SQL 形状的可匹配签名)。它是"数据是什么"的权威。不是散文——是校验器能对着执行的结构。validate(那道门): 一个函数,在任何 SQL 执行前,拿它引用的每张表对照 catalog 检查。Fail-closed:一条命中已知陷阱、或漏了必填 filter 的查询抛异常、跑不起来。不在 catalog 里的表放行(超出契约范围,记日志)。"Robust" 的定义是:一条违规查询无法执行——不是"文档更漂亮了"。
放在哪:单一权威
契约和它治理的 SDK 放在一起(共享查询代码已经在那,靠现成的同步机制分发),不是放在给人看的判断文档里。给人看的文档讲为什么/怎么用、指向契约;catalog 是唯一的、机器可读的是什么。一个权威,两类受众。
最核心的设计判据:Trap vs Mandatory-filter(它们是两个物种)
这是整个设计里最重要的一条区分。做错了,门会同时既误杀又漏放。
优先用 trap。只有当你能说清"为什么没有任何一条合法查询可能省掉它"时,才加 mandatory-filter——然后立刻去找那条省掉它的查询。
实战里,一个我笃定是不变量的 filter("所有分析查询都会过滤 record-type = X"),结果有三条合法的省略者(一个 owner 查找、一个跨类目聚合、一个按账户抓取)。每次"收窄"这个 filter,都还能再抓到一条真实查询。正确的修法不是继续收窄——是把它删掉,把这个约定写进粒度注记。约定属于 review,不属于 fail-closed 的门。
Trap 的轴:表级 vs 引擎级
Trap 有一个必须选对的轴,否则会同时既过度触发又触发不足:
CAST(x AS DOUBLE)→ 要DOUBLE PRECISION;SUBSTR→SUBSTRING)。把这类 trap 按引擎建,应用到任何表解析到该引擎的查询上——让同一个构造在新引擎上报错、在旧引擎上放行。选对这个轴,才能让一张表保留它合法的子查询、另一张表拒绝它;让一个方言构造在新引擎上触发、在旧引擎上通过。
两条不可妥协的纪律(用血换来的)
1. 只 trap 观测到的失败——下 trap 前先活体探测。
不要因为文档说某模式会坏就 trap 它。先拿它去真实数据源上跑。我对每个方言模式都做了活体探测:确认坏的形式会报错 且 正确的形式能跑,然后才写 regex——并验证 regex 会跳过正确的变体(
AS DOUBLE不能匹配AS DOUBLE PRECISION;SUBSTR(不能匹配SUBSTRING()。一个在共享门上误杀的 trap,比没有 trap 更糟。2. 拒绝 trap 那些易误杀的模式。
两个候选方言 trap 被故意留作文档,而非门:一个保留字裸词(会匹配列别名、注释、数据),一个在另一个引擎上合法的分区构造。纪律的一部分,是知道哪些规则你绝不该机械化。
L2:受约束 NL2SQL——三道结构性闸门
L3 建好后,#36 的第二级("受约束生成,~95%,只服务探索性提问")就有了地基。L2 的入口把一个自然语言问题变成一个安全的答案,靠的是每条路径都过的三道结构性闸门 + 强制的出处披露:
门 3 是这轮最硬的一条认知:"要 Agent 记得调用的安全门,不是门"
第一版的 tier 纪律是一个建议性函数
require_tier()——要 Agent 在把数字当权威呈现前记得调用它。这正是那个反复出现的失败模式:一个只有 Agent 记得才生效的门。adversarial 判它 CRITICAL。改成结构性:
usage_context变成必填参数(无默认);一个leadership/formal/report/authoritative上下文在构造上就拿不到 constrained(~95%)答案——直接抛异常。你无法为一个权威场景取得一个 ~95% 的数字,by construction。只有exploratory能走 constrained。门 2:
semantic_check——挡"合法但错"L3 挡的是契约无效(未知表/列、漏必填 filter、命中 trap)——"这查询到底能不能跑"。它挡不住一条完美合法、但语义错误的 SQL:数字回来了,看着没问题,却被静默地错误校准。这些才是真正上过线的 bug:
__TOTAL__哨兵维度全钉住)。semantic_check是专挡这一类的 fail-closed 预检。它是"查询跑了"和"答案对了"之间的差别。为什么 adversarial gate 是全部的重点
跨 6 个 pipeline run,我自己的绿色测试套件从来都不够。每一个 run,一个 fresh-context 的 adversarial reviewer 都找到我的测试抓不到的东西——因为我的测试只覆盖了我想到的形状:
IN (SELECT …)重写而不是= (SELECT …)——我的 trap regex 只匹配=。(这个门存在的意义就是挡这个 bug,而它正从下一个 LLM 会产生的变体走回来。)而且 L2 这轮更狠——我 pass-1 的修复,pass-2 发现它误杀了真实查询:收窄门的动作,把一条合法的"三 measure 并排选"的真实周报查询给拒了;账户名里含某个枚举词的查询触发了误杀。
进化循环:L2→L1 自动沉淀(以及一条诚实的人工闸门边界)
#36 画的进化循环是:高频 L2 查询自动沉淀为 L1 认证模式,让契约"越用越聪明"。落地后,这个循环的形状是四段——但有一条关键的诚实边界必须讲清:
为什么阶段 3 必须是人工闸门,绝不能自动写
这是落地时我们钉死的一条设计红线,也是 #36 蓝图里最容易被误读的地方:
L2 的门 3 结构性地保证:领导层拿不到一个 ~95% 的答案。可一旦"自动 promote"把某条 constrained 查询变成 certified,它就自动进入了 leadership 的合法路径——一条从没被人核验过 caliber 的 SQL,现在被当成权威数字。这不是进化,是给自己开的后门。
所以正确的循环是 观测 → 聚类 → 浮出候选(报告) → 人工认证 → 发布,不是 auto-write。自动化负责发现和推荐(这部分省人力、越用越聪明);人工闸门负责把一条查询认证为权威(这部分是信任的来源,不能省)。
衡量进化健康度
诚实残留(这是防复发,不全是抓新 bug)
诚实比完美重要。落地后清楚知道还没根除的:
usage_context——门 3 结构性地拒绝了 leadership→constrained,但"这次提问算不算 leadership"这个分类,仍是 Agent 下的判断。披露缓解,非结构性根除。semantic_check是启发式 backstop,不是证明——L3 的 trap(只匹配坏 SQL)才是更强的保证。语义门覆盖已知的"合法但错"形状,不覆盖未知的。转移方法(任何数据 Agent 契约层都能用)
回到大 thesis
这是四层数据 Agent 架构里"L3 从文档态到契约态"这一步的落地(L1 基础设施 · L2 执行+权限 · L3 语义契约 · L4 Agent/skills)。行业通病是整个跳过 L3(L4 直连 L2)。有一个散文的 L3 是半步;把它做成可执行、fail-closed 才是把"文档化的数据语义"变成"系统产不出错查询"。再往上一级,就是这篇讲的 L2 受约束 NL2SQL + 进化循环(自动发现、人工认证)。
方法论层面。所有领域细节(真实表名、数字、客户)是内部信息,已刻意排除——可复用的模式才是重点。落地证据来自 6 个 pipeline run 构建的一个数据 Agent skill 家族的 L3 契约层 + L2 受约束 NL2SQL,每个 run 都过强制的 fresh-context adversarial gate。
All reactions