4 Commits

Author SHA1 Message Date
ThearchyHelios 1b31c49a00 问题聚类的类 id 由成员定,「不是口径」按成员记
类的 id 以前按种子问题的原文算:种子是一类里最新的那一句,时间相同的几句读出来先后不定、来了一句新问法种子就换了,
两次读取 id 就变,认证队列里点过的「不是口径」随之失效。

- app/chat/questions:Question.key 是稳定的 id(问数据的那一轮 turn:…、别的探索运行 run:…);QuestionCluster.id 是成员的
  id 排好之后取的哈希,同一批问题两次读取一样;recent_questions 同一时刻问的几句按 id 排出先后,不靠库里读出来的先后。
- ledger/certify:问题聚类那一项的 id 用类的 id;「不是口径」时服务端按同一批问题重新聚一次,记下这一类现在的成员(不收
  客户端传的成员,找不到、取向量出错就只记 id);之后这一类过半的成员是某一次记下的,就照样不进队——来了几句新问法 id
  会变,按成员认不失效。以前按种子原文算的 id 记的照样认;记的成员读不懂的照实忽略。
- api/ledger:dismiss 把当前身份交给 certify(重新聚类要按这个人读得到的问题)。

测试:tests/test_ledger_certify.py 新增三条——同一批问题换个先后读,种子不同、类的 id 一样;点过「不是口径」之后换个说法
再问一句,成员变了照样不进队,读不懂的老记录不报错也不藏;按旧的种子原文 id 记的照样生效。把 id 改回按种子原文、把按
成员认去掉再跑,各自的用例失败。
2026-10-17 12:15:07 +08:00
ThearchyHelios 72d9d2770c 认证队列的确定性重复按规范形分组:条件换序、AND 两边换位的进同一组
以前按 normalize_sql 的规范文本分组,WHERE 的条件照原文的先后,同一个形状换了条件顺序就拆成两组,凑不够门槛进不了队。

- sql_semantics.sql_shape:在判等价同一套不认列的规范形式(_light)上把字面量抹成只看类型(字还是数)的占位,再把交换律
  成立的地方按抹掉之后的写法重排——AND / OR 的各项、IN 列表、= / <> / + / * 的两边、比大小的两边(换边时大于小于跟着
  换);抹掉之后一样的几项按原值排,同一组里字面量的位置对得上。不另写解析器。认不出的(解析不了、sqlglot 会读错的写法、
  字面量对不回 normalize_sql 的)为 None,照旧按规范文本分组(只会分得细)。positions 把规范形里的字面量对回 normalize_sql
  的位置,固化成模板时用。
- ledger/certify:Shape 加 key / canonical / positions;队列按(数据源、规范形)分组,跨轮变了的位置按规范形对齐,再换回
  代表那条的位置;固化时同组的别的运行也按规范形认、按同样的对齐认参数。这一项的 id 按规范形算;以前按规范文本记的
  「不是口径」照样认(组里任何一条的规范文本对得上就算)。

测试:tests/test_ledger_certify.py 新增四条——三种换序写法同一个形状、字面量位置对得上;七种真不同(换列、OR、少条件、
换运算符、数对字、IN 列表、换聚合)不是同一个形状;真跑三轮换序的问数据进同一组、多筛一个地区的不进,固化出两个日期
参数;按旧 id 记的「不是口径」照样生效。把分组改回按文本再跑,换序那条失败。
2026-10-17 12:09:58 +08:00
ThearchyHelios 9040289e1f 固化成口径草稿:口径卡用传入的名字、报告指令不写死代表那一轮的日期、指标名取目录中文名、已固化去重
验收报告里 certify.freeze 的四处:
- 口径卡的 caliber 用的是代表问题的原文,没用传入的 name。改成 name(去首尾空白)优先,没传才用代表问题。
- 报告的 instructions 写死成「回答:<代表问题>」,问题里带着那一轮的日期(「上周(10月5日到11日)」「九月第一周」),
  参数化之后这句就错了。有入口参数时改写运行时渲染的参数(「这次的参数:p1 = {{ input.p1 }},…;时间范围、筛选的
  取值以参数为准」),不写问题原文;没有参数时取数的字面量都是口径的常量,照旧写问题原文。
- 指标名直接用列名。现在能从数据目录取到中文名的用中文名:先看查询快照冻结的目录(schema_artifact,和口径比对用的
  同一份,ledger.match._frozen),老快照没冻结的看数据源现在的目录;只认列名本身就是目录里的一列(SELECT amount、
  SUM(amount) AS amount),gmv 这类算出来起的别名取不到,照旧用列名。
- 认证队列里 cluster 项的 frozen_into 把同一个工作流列了好几次:固化时同组的几次运行都记进 origin.sources,一项里
  的几次运行就各指一遍同一个工作流。shape 项同样会重。加 _frozen_of 按工作流去重(保持先后),三种项都用它。

测试(test_ledger_certify):原来的固化用例改成断言指令里没有代表问题和日期、有 {{ input.p1 }} {{ input.p2 }},
没传名字时口径卡照旧用代表问题,shape 和 cluster 两项的 frozen_into 都只有一个工作流;新增一条:传了名字的口径卡
用名字,目录里有中文名的列(amount → 金额)用中文名、取不到的照旧(n),没有参数时指令照旧写问题原文。去掉修复
回跑两条都挂。
2026-10-17 08:35:57 +08:00
ThearchyHelios 538799e70d 沉淀:认证队列、固化成口径草稿、发布后补比;账本统计补上对账与认证队列的转化(口径账本做法 8、10,拍板 L9)
为什么:同一类问题被反复问、Agent 反复写出同一个形状的 SQL,就该沉淀成一份口径;以前 from-run 保留 Agent,沉淀出的模板没有固定 SQL,以后永远比对不上。第 2 波的统计在 E2 反馈、E3 认证队列上照实写了「目前没有数据」,这一波补上能补的。

做了什么(新增 ledger/certify.py):
- GET /api/ledger/certify-queue(搭建者):确定性重复(问数据运行里被报告引用、没对上口径的查询,按数据源 + normalize_sql 文本分组,≥3 次运行且来自 ≥2 段对话;跨运行变化的字面量是参数候选,展示的形状不带取值)、问题聚类(app/chat/questions 全站唯一的那份)、被反复用的答案(导出 ≥2 次用第 2 波的 report.exported 审计、做成过应用看蓝图来源、在画布里打开或提取过看工作流 origin)。每项给代表问题、轮次、相关口径、是否已固化。
- POST /api/ledger/certify-queue/dismiss:「不是口径」写审计 ledger.certify.dismiss,之后不再进队。
- 固化成口径草稿(from-run 加 freeze、peers 后转调 certify.freeze):报告引用的查询变成固定 SQL 的取数节点,跨轮变化的字面量换成 '{{ input.pN }}'(缺省值取代表运行的),单行结果的数值列成口径卡指标,报告核对规则按受管要求写、契约 report_from + metrics_from + required(多行加 cells);过 validate_graph 和受管门禁。新工作流 origin.sources 记来源问题。
- 发布之后补比:api/workflows.commit_publish 提交后调 certify.after_publish,来源运行按新口径补比一次(新的 trigger=certified,界面写「按之后发布的口径回看」,时间线和账本接口同一句)。
- 统计:E2 加对过账的答案数、参数经人核对的、指标由人选的次数(反馈照实写第 4 波);E3 认证队列的转化(固化、发布、不是口径)。

测试:tests/test_ledger_certify.py(参数推断与模板、三种信号进队、门槛、不是口径、固化草稿过受管门禁、发布后来源运行补比成等价已证明并出队、再问标等价已证明、统计)。
2026-10-16 17:06:41 +08:00