Commit Graph

217 Commits

Author SHA1 Message Date
ThearchyHelios d176ff77ff 运行对比复核修正(第 1 轮):输入的出处追到合并查询的上游、Agent 引用的列和行段,没出处的数、数据没变数却变了的不再报成数据变化
出处判定(每个指标的分类靠它):
- 合并查询:读合并结果的指标按别名一路追到喂它的每一份上游快照(嵌套的合并也追),上游 SQL 结构变了是 sql_changed,
  不拆、不归因;合并节点多接、少接了输入也算结构变化
- 图里写死的字面量改了('paid' 改成 'refunded'、系数、LIMIT):执行的 SQL 只差字面量,但图里的 SQL 改过,算模板变化;
  图里一字未改(定时运行代入的日期)才是参数。图里的 SQL 只改了空白、大小写的不算改过
- Agent 按出处交来的字段:还要引用同一列、同一行(段),两边都引用整份结果的不算换了;换了列、换了行段的说不清
  (incomparable),不再把没引用的区域算成「数据没了」去归因
- 模型、人直接交出的值(Agent 没按出处交、模型调用、人工审批……):值变了说不清是数据还是模型本身,判 incomparable;
  它自己查的库结构变了、配置变了照常标出
- 代码等确定性节点:上游的查询结果两次一模一样、交出的值却变了的,判 incomparable,不再说「变化来自上游数据」;
  上游只有运行输入变了的算参数
- 图级变化落到节点上:连线有增删的终点节点、默认设置改了且没有自行设置这几项的模型类节点,都算改了配置;
  「模板和取数的 SQL 都没有变化」只在整张图都没变时说
- 运行输入是列表、对象的,按还原出来的完整值比,长度不变、内容变了的是参数变化
- 同一节点按 SQL 配对时先配字面量也相同的:Agent 换了先后查东区、南区,两条各自配上自己,不再配成两条「只有参数不同」
- 一边记不出输入是哪个节点写的,说清是哪一边没记录(不再写出「None」)

口径与数值:
- 口径变了的指标,喂它的取数也变了、说不清的照样写进说明(只在一边才有的输入是改表达式带来的,不重复说);
  分类次序写进 compare() 的说明
- 千分位和不分组互换只影响显示,写进说明、不算口径变化;比率与否不同才算
- 升级前的口径卡没记小数位、显示格式:按那次运行图里的指标定义核对,定义也没有就不比,不再误报「小数位由 未设置 改为 1」
- 百分比:基准极小、这次很大时十进制量化放不下,退回浮点算,不再整个对比报 500
- 没设小数位的指标拆分:三个数按同一精度量化(最多 15 位有效数字),转成浮点交出去后两效应之和仍逐位等于总变化
- 维度归因标出用的是哪个输入、两边各自哪份快照(path、base_artifact、target_artifact)

接口:已经结束、只是没有封存清单的运行,409 说「没有封存清单,不能用于对比」,不再说「运行结束后才能对比」。

测试:补上复核指出的必测项(只一边对得上不给占比、只截断基准一侧、换了数据源、按旧口径小数位取整、写它的节点多查一次、
口径和 SQL 一起变不归因、小数位和比率变化、日期类型和常量列不当维度、合计行浮点误差、只改名称、结束过又接着运行的不当
基准),以及上面每一处修正的用例。32 处变异逐条打红验证,全部被测试抓到。
2026-10-05 10:20:29 +08:00
ThearchyHelios 63c54e1c20 运行对比:compare() 的说明里写全返回的形状,接口说明指过来时前端照着就能读
除接口约定的字段外,几处补充也写明:模板节点的 labels(配置项的中文叫法)、输入的 masked 和两边的快照 id、查询的
两边快照 id、differences 和 problem;归不了因时 rows 为空、total_delta 为空、原因在 note。
2026-10-05 09:00:09 +08:00
ThearchyHelios 1e90a5a502 运行对比:结果里不出现非数和无穷大,维度列的非数当文字配对
接口按 allow_nan=False 写 JSON,结果里混进一个 NaN 或 Infinity 就是 500。指标值、输入值、十进制相减原本就只出
有限的数;剩下两处补上:维度列里的非数(float NaN)写成文字再当键,度量加起来、占比算出来超出浮点范围的给空。
2026-10-05 08:58:18 +08:00
ThearchyHelios 467f9599fd 运行对比:补合并查询、循环里多次执行的配对测试,口径卡读不出来时的说法补上「的」
合并查询只记在节点台账里、SQL 另在 merge.end 里,快照文件不在了照样比得了 SQL;循环里同一个节点执行几次的按先后
一一配上,多出来的一次标「删除」。口径卡指标集读不出来、被改过时的说明改成「口径卡「X」的指标集与哈希不一致」。
2026-10-05 08:55:57 +08:00
ThearchyHelios c89aaba10c 运行对比:Agent 换了查询先后时按 SQL 配对,Agent 改了配置时按出处交来的字段不再报成数据变化
查询配对改成一轮比一轮宽:同一节点、同一工具里规范化 SQL 相同的先配上,剩下的再按「第几次」配,最后跨节点按
SQL 配。以前严格按第几次配,Agent 这次换了查询的先后,两条都被配成「结构变了」,没按出处交的字段跟着被判成
SQL 变了。

Agent 按出处交来的字段:SQL 没变而 Agent 自己的配置(提示词、工具)变了时,它可能换了取哪一格,归为模板变化
(sql_changed)并写明是哪个 Agent;调用工具节点的格子由口径卡的 cell() 定,不受影响。
2026-10-05 08:52:04 +08:00
ThearchyHelios 86e52192a9 运行对比后端(变化三分法):两次运行按指标拆清是数据变了、口径变了还是模板 / SQL 变了,数据变化按维度归因
GET /api/runs/{id}/compare/base 找默认基准:定时任务发起的先找同一定时任务更早结束的一次,否则同一工作流、
同一运行类别;只挑封存过的终态运行,找不到 404。GET /api/runs/{id}/compare?base= 对比两次运行:两次都要
已结束、封存且核对一致(照搬证据接口的封存判定),否则 409;不是同一个工作流、同一次运行 422。只读,不追加
事件、不落工件。

engine/compare.py 是纯函数:
- 查询按「节点 + 第几次 + 工具」配对,配不上再按规范化 SQL 配;normalize_sql 判 same / params_only /
  changed,结构变了的用 diff_facets 写出差在哪(时间范围排在最后);核查类查询不参与
- 口径卡同一节点按指标 id 配对,表达式、口径版本、小数位、单位、显示格式不同算口径变了
- 每个指标按 added/removed → 口径 → SQL 或上游配置 → 说不清 → 只参数 → 数据 的次序分类;输入落在快照上
  的直接比两边快照的 SQL,不落在快照上的看写它的节点和画布上游
- 口径变了时严格拆分:旧表达式配这次的输入用推演重算的还原和求值算 f_old(x_new),十进制精确相减,两效应之和
  逐位等于总变化;SQL 也变了、用到遮罩列、单位不同、旧叶子没记录的不拆并说明
- 维度归因按维度键配对两边的行、取变化最大的 10 个;度量之和等于输入的值(含合计行那一格)才给占比;截断、
  度量列遮罩照实写;遮罩列、日期列、每期都不同的列不当维度
2026-10-05 08:49:52 +08:00
ThearchyHelios 26595b70f7 合入期 1 数字推演沙盘:证据面板里改口径卡的输入、排除查询结果的行,即时看指标和报告引用处的新值;推演只读、不写运行记录,醒目标注推演非出具;口径卡的容器型输入此后另存完整值
# Conflicts:
#	scripts/check-all.mjs
2026-10-05 07:53:42 +08:00
ThearchyHelios c8d0e5d18b 数字推演复核修正(第 3 轮):决定取哪一格的容器不再给可勾的行段,包着它的引用给了新值时不再说不准
- cell(nodes.q, len(vars.res.rows) - 5, …) 里的 vars.res.rows 这种决定取哪一格的数组字段、整份结果,第 2 轮起排除它的行
  只回「说不准」,可 row_span 照给:界面让人勾 10 行,勾了推演不出来,「不支持推演的输入」里又说它不能用。现在这种容器
  不给行段;决定取哪一格的单格照给(勾掉它确定是排除后无法计算)。前端 row_span 的类型说明跟着补上这一条
- 第 2 轮加的「说不准」不看包着它的引用有没有给新值:那一格改成 100、再排除别的行,以前能算出 100,第 2 轮起回说不准。
  _refs 现在记下每个嵌在里面的引用归哪个最外层引用(嵌几层都记最外层);最外层引用都给了新值时,只嵌在里面的输入无关
  紧要(_Plan.moot):它那一行排除了不再报排除后无法计算,对不上快照的也不再说不准。它自己也是最外层引用的照常拿值,
  只是取哪一格不再说不准
- 补测试:选取用的容器不给行段、只有那一格那一行勾了有确定结果(单元和真跑);那一格给了新值后排除任何行都照常算;
  既数行数又选取的容器;嵌在下标里的单格、对不上快照的下标(含嵌两层)、两个引用共用一个下标要都给新值;碰到了却没变
  时 maybe_changed 为假、下游口径卡不标(单元和真跑);嵌在下标里的遮罩格让整个指标推演不了(BLOCKED 加一例);读不出来
  的快照上嵌在里面的那一格不给行段(原先守它的测试被这次的选取判断盖住了)
- 逐个做过变异,全部变红:选取容器给行段、moot 不过滤、说不准不看 settled、_doubt 不看 moot、只记直接上一层、settled
  用 any、moot 含最外层引用,以及复核点名的 maybe_changed 只看碰没碰到、遮罩只看最外层。第 2 轮的两份变异单重放后也都
  变红,只有「row_span 只看快照读不读得出」成了等价变异:完整值找不回来的容器在最外层时整个指标推演不了,嵌在里面时
  又是选取用的,两头都已不给行段
2026-10-05 07:37:02 +08:00
ThearchyHelios bfc0e88e9a 数字推演复核修正(第 2 轮,后端):整个指标推演不了的不改动也照实说,完整值找不回来的数组排除行后不再报「无变化」
- 用到了遮罩列、最外层输入还原不了(容器的完整值找不回来、所在的查询快照读不出来)、卡上没记全输入的指标,以前不改动时
  报 unchanged(界面写「无变化」),输入照样可改、行照样可勾,改了、勾了才说推演不了。现在不论有没有改动都交回
  unsupported / masked 和原因,所有输入 editable 为假、不给 row_span,unsupported 里另有一条 input 为空的(界面据此只写
  原因),之后只列卡住它的输入。同一格既遮着、快照又读不出来时说遮罩
- 下游口径卡改看重算新给的 maybe_changed(改动碰到了某个指标,它又变了或说不准):推演不了的指标 changed 一律为空,
  只看 changed 分不出「什么都没改」,一打开就会把下游标成可能受影响
- agent 数组字段的完整值找不回来(节点产出不在、被改过,另存的完整值读不出来)时,排除它行段里的行以前因为值为空被忽略,
  指标报「无变化」、报告引用处报「不受影响」。现在算碰到了它(excluded_rows 照记,输入 changed 为真、new 为空),
  完整值找不回来的输入一律不给行段
- 决定取哪一格的容器(cell(nodes.q, len(vars.res.rows) - 1, …) 里的 vars.res.rows)少了行时,以前拿卡上记的那一格算出
  「无变化」,现在说不准(unsupported)
- 补测试:四类整个推演不了的指标不改动、改动后都照实说(单元和真跑);数组完整值找不回来时排除行段内一行(单元和真跑,
  含报告引用处);查询当时记下、跑完又从数据源去掉的遮罩(推演和勾行接口);写输入的节点和快照在同一条事件里交回
  (合并查询的边界);给了新值的输入不再说不准;数组只认行段内被排除的行;同一份快照交回两次按第一次;读得出来却不是
  查询结果的快照;嵌在里面、读不出来的那一格不进代入式。逐个做过变异:复核列出的四处、第 1 轮漏掉的三处和这次新加的
  判断改掉后都变红
2026-10-05 06:49:33 +08:00
ThearchyHelios 09237a4c99 数字推演复核修正(第 1 轮,后端):排除行时说不准的指标不再报「无变化」,快照读不出来时不给值,遮罩按用到的快照在线程里算
- 排除了行,指标却有输入对不上任何快照(老运行没存完整值的容器、加工过的结果、按位置从整份结果里取的一格、由查询结果
  算出来的数):以前一律报「无变化」、报告引用处报「不受影响」,或者拿原值算出一个不对应任何一份数据的数。现在回
  unsupported 并写明是哪个输入说不准;写它的节点在那份快照交回之前就跑完的影响不到它,照常不算(按事件序号比先后)。
  卡上没记全输入的指标,有任何改动都标 unsupported
- 查询快照文件不在、内容与哈希不一致时,落在它上面的 cell()、agent 字段不给值、不能改、不给代入式,用到它的指标推演
  不了;遮罩照样按台账记的数据源算,不再因为快照坏了就失效
- 遮罩改成用到哪份快照才算哪份:读快照放进重算的工作线程,查库交回事件循环。以前每次请求都在事件循环里把这次运行的
  全部快照读一遍、复验一遍哈希
- 输入带 row_span(实际用到的快照行);新增 GET …/evidence/whatif/rows,按页取封存范围内一份快照的行(遮罩列写
  「已遮罩」,只读)。界面据此给整份结果勾行、只让用到的行可勾
- 不能改、也没有行可勾的输入(对不上快照的列表、文本)列进「不支持推演的输入」并写明原因
- 下游口径卡顺着画布的边找,经中间节点用到卡上值的也标;改动碰到了指标、它却推演不了时也标「可能受影响」
- 超大整数不再让接口 500:新值超出可计算范围回 422,算出浮点放不下的整数按「不是有限的数值」说明;非法值的说明不露出
  None、单引号这些 Python 写法
- 补测试并逐个做过变异:封存核对没过、事后加的遮罩、合并查询的遮罩、指标集被改(409)、两份报告、循环里先算卡后算
  产出、手写事件核对取哪次执行和边界、重算的几处保护、勾行接口。取整和口径卡逐个对拍(含二进制半中间的数、负小数位):
  metrics.py 只许加 value_artifact,取整没法抽成共用函数,先用对拍守着
2026-10-05 05:43:17 +08:00
ThearchyHelios e7c8d2a1f3 可验证报告复核修正(第 1 轮):运行信息拿已核对的事件对照,报告徽标连同数字一起算,遮罩判定宁严勿宽,各分支在浏览器里有确定的检查
- 页面核对加 ⑥ 运行信息:运行结果看封存范围内最后一条结束类事件的类型,运行类别看运行开始事件记下的运行参数,
  出具档位认最后一条出具判定(不再信元信息指的那一条);页面显示事件里的值,元信息写得不一样算不通过、计入总览。
  工作流名、版本、起止时间、节点名称绑不进封存,单列一组写明取自运行记录,封存说明里也写明要到系统里一并对照
- 报告标题旁的徽标连同正文里的数字一起算,旁边写分项:重算全部哈希的伪造只在 ⑤ 露馅,徽标不能还写着通过。
  分块条目多时不通过和无法核对的都列在外面,有无法核对的显示「部分无法核对」和分项计数;⑤ 改叫「数字与取值」并分开数,
  读屏分别读作数字、取值,「显示格式由服务端渲染」只附在带出处的数字上;无出处数字的说法改成未使用引用标记
- 服务端不会给出的状态算不通过:被引用的工件不在清单里、非查询快照标着遮罩、工件既没内容也没说明;报告文档导出时
  已不在服务端,总览不再是绿色的通过,写「有 N 份报告未能核对」,报告处写明是服务端已没有这份文档
- 认 json.dumps 写出的裸 NaN / Infinity,没被改过的文件不再误判不通过;哈希对得上但读不懂的照实写「无法核对」,
  和哈希不一致分开说
- 宽屏打开出处面板时正文让出右边一栏,表格里右对齐的数字不再被面板盖住;补上斜体(不用后行断言,较早的 Safari 也能执行)
- 封存哈希、运行 ID、工作流名直接写进页面,不执行脚本时写「尚未核对」而不是一直「正在核对」;脚本挪进 head,
  执行后核一遍页面顶部写出的封存哈希和数据里核对用的一致
- 成果节点和运行结束事件的规范字符串,只在封存范围内每次数据源查询都对得上无遮罩的快照(或确实没交回数据)时才给:
  升级前的运行从工具快照认出查询快照,协作团队成员的查询认不出就按带遮罩算,数据源现在设了遮罩的按工具名就挡
- 封存核对不一致的 409 改成一句固定的话,不再叠冒号、让人去本来就在的记录页;失败结束的运行在页面封存说明和导出提示里
  写明之后继续运行会重新封存;记录页上左边写着「已封存」却不能导出时,原因写在屏幕上
- tests/bundle_samples.py 用虚构数据真实生成九份样本页(遮罩快照、两份报告、早期运行、失败结束、两条出具判定、
  服务端缺文件、裸 NaN、别的运行的文档),check-bundle 断网打开逐项断言,在上面篡改引用链、运行信息、拿掉被引用的口径卡,
  量宽屏面板,看不执行脚本的样子;把核对改成恒通过、徽标不看数字等七种改坏法都会变红。pytest 补遮罩判定的三种形状、
  事后设遮罩(去掉接线即变红)、页面静态内容和样本页本身
2026-10-05 05:30:50 +08:00
ThearchyHelios 24fc21cb56 数字推演:算出无穷大、非数时说清算不出来,不让接口在 JSON 编码时 500
改了输入之后溢出成无穷大(或算出非数),以前照 ok 交回,Starlette 编 JSON 时拒收这种浮点数,整个请求 500。
现在按计算出错处理(status: error,写明原因);老卡上本来就记着无穷大的值和输入,交回时换成 null(口径卡渲染成「—」)。
2026-10-05 04:14:56 +08:00
ThearchyHelios cb141cea88 数字推演:用到遮罩列的指标不给代入式;真卡上核对还原是否忠实、封存后插进来的卡不认
- 代入式里代进去的就是遮罩列的那一格:用到遮罩列的指标,推演结果里的 substituted 一律为空(输入值本来就不给)
- 测试:真跑出来的每张卡(cell() 取数、整份查询结果、nodes.* 容器、agent 数组字段的两种引用、下游口径卡),什么都不改
  强制重算,值和卡上记的一模一样
- 测试:封存之后插进来的 node.finished 指向一张伪造的指标集,推演照旧用封存范围内的那张
2026-10-05 04:13:33 +08:00
ThearchyHelios 183814d2c1 可验证报告页面补全:列出参与清单核对的原文供页面外自行重算,写明运行结果,自查认法与服务端一致,打印时收起面板
- 封存清单一项可展开参与核对的每一行(序号|类型|节点|内容哈希),读的人不信页面脚本也能复制出去用别的工具算;
  check-bundle 在页面之外用 node 重算这段原文,等于封存哈希
- 元信息加「运行结果」(已完成 / 已失败 / 已取消):失败的运行报告照样导得出去,读的人要知道它没跑完
- 报告节点认最后一次记下了文档的自查,和服务端、证据接口同一个认法,后面多一条没记文档的自查不再误报不通过;
  运行记录里有自查、文件里却没附带文档的报告照实报不通过
- 核对下不了结论(数据块坏了、内置 SHA-256 自检不过)时也标上核对结束,等结果的一方不必干等;打印时收起出处面板
- check-bundle:拿不到导出页面时,依赖它的几节跳过并注明原因,不再一个原因报五次
2026-10-05 04:04:06 +08:00
ThearchyHelios a0016493de 可验证报告服务端:GET /api/runs/{id}/bundle.html 把已封存运行的报告导出成自包含的 HTML,浏览器断网也能核对
- bundle/build.py(纯函数):封存范围内每条事件带 seq、类型、节点和 data_sha256(与 runner 封存同一算法),浏览器据此重算清单;
  白名单事件(运行开始、报告自查、口径卡升版、出具判定、报告和口径卡的节点完成、数据源没有遮罩的查询返回)另带规范字符串,
  运行里有带遮罩的快照时,成果节点和运行结束事件的预览可能带出查询结果,也只给哈希;报告文档和它引用的口径卡、查询快照按原字节
  base64 嵌入,遮罩快照(查询时记下的或数据源现在设的)只给 id;封存之后追加的事件单列、不进清单
- template.html + verifier.js:样式和脚本内联、零外链,内容安全策略按哈希只放行核对脚本;数据块转义 < > & 和 U+2028/2029。
  浏览器逐项核对事件内容、封存清单、工件哈希、引用链、报告里每个数字(照搬 cell_value / same_value 判等),
  crypto.subtle 拿不到时用自检过的纯 JS SHA-256;醒目写出封存哈希,并写明只证明内部一致、真伪要去系统里对照
- 接口复用证据接口的封存判定:未封存、或运行还没结束(接着跑的运行留着旧封存,跑完会换掉清单哈希)回 409,封存核对不一致回 409;
  附件名「<运行名>-可验证报告.html」按 RFC 5987 编码;只读,不追加事件
- artifact_store 加 read_bytes:原样取回工件字节,默认复验哈希,verify=False 交给浏览器去核
2026-10-05 03:58:02 +08:00
ThearchyHelios 9852e4d33a 数字推演接口 POST /api/runs/{run_id}/evidence/whatif
证据面板点开口径卡上的一个数,改输入的值、排除它背后查询结果的几行,看指标和报告里引用它的地方会变成多少。
推演不是出具:不追加事件(last_seq 不变)、不落工件、不改报告。

- 只认这次运行封存范围内的工件:沿用证据接口的 _sealed / _sealed_loader 白名单,另加封存的节点产出和封存的指标集
  里写着的 value_artifact;排除行只认封存范围内的查询快照
- 口径卡按台账(一期以前按节点产出)找,循环里算过几次的取报告引用的那一份;nodes.* 按口径卡那次执行之前的产出还原
- 遮罩和证据面板查询步骤同一套:查询当时记下的加数据源现在设的,合并查询按它合并的数据源算
- 未封存(停在审批、还在跑)的运行也能推演,sealed 为假;封存核对没过的同样标出
- 请求形状不对回 422 whatif_bad_request,改动不合法回 422 whatif_bad_input(中文说明),卡、指标、报告不存在回 404
- 只在确有指标变化时列出引用了这张卡的下游口径卡
2026-10-05 03:44:29 +08:00
ThearchyHelios fb15805ead 数字推演的重算(engine/recompute.py):改输入、排除快照行,按口径卡同一套规则算出新值
纯函数,不读库、不写工件。口径卡怎么算的这里就怎么算:同一条原式、同一个求值器,浮点按卡上的小数位 round,渲染走
render_metric。重算前先还原每个输入当时的值:

- 标量(含 cell() 的格、agent 的单格字段和数组元素)用卡上记的值;它们落在快照的哪一行记在 locator 里,排除了那一行
  就当缺失,指标标「排除后无法计算」
- 容器只记了「[12 项]」:agent 数组字段从节点产出的 data 还原、按 locator.rows 去掉被排除的行;nodes.* / input.* 从
  口径卡执行前那个节点的产出还原;vars.* 只认 value_artifact,老运行照实说当时没有保存完整值;和某份快照 rows 一样
  的容器(整份查询结果赋给了变量)排除行就去掉那几行
- 遮罩列取来的输入不给值、不能改、它的指标不重算:新值和原值一比就能算出遮住的那一格
- 嵌在下标、cell() 行列里的引用决定取哪一格,不能直接改

只重算碰到改动的指标;不合法的改动(路径不存在、值不是数、行号越界、快照不在封存范围、改容器或文本)抛 WhatIfError。
另有报告里引用这些指标的片段会写成什么(按片段自己的换算渲染)、引用了这张卡的下游口径卡(只标不重算)。
比率、百分数的变化量写「个百分点」。
2026-10-05 03:44:29 +08:00
ThearchyHelios 60cd5a47bb 口径卡给容器型 vars 输入另存完整值(value_artifact),供数字推演重算
卡里的输入对列表、字典只记「[12 项]」这样的大概,事后要排除几行、改一个数再算一遍,手里没有完整的值。
nodes.*、input.* 的完整值在那个节点封存的产出工件里,agent 字段能按出处从查询快照还原;vars 是谁写的、
写进去的是不是节点产出本身说不准,只能在口径卡算的那一刻存下。

- 此后容器型 vars.* 输入落一份 metric_input 工件 {path, value}(put_json,随运行封存),输入里记下 value_artifact;
  id 写在内容寻址的指标集里,不另起台账条目
- 大过 4 MB 的不存,写失败也不存:口径卡照常算出来,只是这个输入以后推演不了
- 金样测试(改动之前的代码真跑出来的工件):只有标量输入的卡逐字节相同、工件 id 不变;带容器型 vars 输入的卡
  去掉新键后和以前一字不差
2026-10-05 03:26:43 +08:00
ThearchyHelios 19d7fd7162 合入期 0 核查工具箱:SQL 语义(规范化、方面、差异说明)、只读核查 agent、核查每日花费账、核查设置组、数据源查询工具的强制只读 2026-10-05 02:52:16 +08:00
ThearchyHelios d5e9ef39ae SQL 语义复核修正(第 2 轮):CTE、子查询起的名字不再进比较,条件里的子查询选出来的列名也不算;派生来源按位置编号,编号错开时再按不写编号的写法配一遍
上一轮只去掉了条件里子查询的表别名,子查询整段写进外层条件的 key 时还带着各自起的名字:标量子查询、IN 子查询给选出来的列起的名字(AVG(amount) AS avg_amount),子查询里 FROM 的子查询别名(s 换成 agg),以及外层引用 CTE、FROM 里的子查询算出来的列时的来源名(派生来源的 key 就是别名)。CTE 改个名,外层的 total > 100 就被报成两边各有一个、互相矛盾的条件;双盲复算里盲算的名字本来就是自己起的,会被说成换了口径。

现在派生来源(引用 CTE 的表、FROM / JOIN 里的子查询和 LATERAL 子查询)按位置编号作比较用的 key:从外往里一层一层地编,CTE 在第一次用到的地方编号,写成 CTE 还是 FROM 里的子查询、CTE 定义的先后都不影响;给人看的写法照写名字。不拿里面那段查询的写法作 key:里面差一个条件,外层按它的列写的条件就全跟着变,同一处不同会报好几遍。条件里的子查询写进外层 key 时,引用 CTE 的表、子查询(含 LATERAL)、子查询自带的 CTE 定义都换成编号,只看取值的子查询(IN、EXISTS、标量子查询)去掉选出来的列名;FROM 里的子查询、CTE 定义选出来的列名留着,外层按名字引用它们。LATERAL 的别名原来没登记成来源,按「别名写错了」照写,一并改成派生来源。

编号只看位置:一边多出一个排在前面的 CTE(比如盲算先关联一个筛 VIP 的 CTE),后面的编号就错开了,同名的 CTE 反倒比原来按名字比时多报出两条自相矛盾的话。Part 加 anon(派生来源一律写「@」、不写编号),diff_facets 每个方面先按编号比,比不上的再按 anon 一对一地配,配上的不算不同;比较两边的先后先按 anon 排、分不出再按编号;连接先配完全一样的再按关联的表配,免得错开的两个 CTE 被配成一对、说成关联条件不同。编号对得上时照样分得清是哪个 CTE 的同名列(p.total > r.total 写反成 r.total > p.total 照报)。筛选条件配对改按列的比较写法(Part.column_keys,CTE、子查询的列写「@.列」),CTE 名不同、阈值不同时说成「按 total > 100 筛选,盲算按 total > 200 筛选」,不再拆成两条各说各的。

仍按名字比的只有 CTE、子查询选出来、外层按名字引用的列(一边叫 total、一边叫 sum_amt),写进了模块说明。
2026-10-05 02:37:33 +08:00
ThearchyHelios 132d126c6f 核查 agent 复核修正:数值字段的各种写法都换成格子引用、认不出的结构下的数不采信;退回普通调用时把结构写进消息,没交的字段不再算「查不到」;收尾的钱按最贵的样子留、留过的不再被上限拦;永远不抛
数只认格子:原来只认 type: number / integer,可为 null 的数(anyOf,Pydantic 的 Optional[float] 就这么写)、$ref 引用进来的、allOf 包着的数值字段都没换成格子引用,模型写的数原样收下。现在先展开本地 $ref、并掉 allOf,anyOf / oneOf 里有一支能放数就整个算数值字段;取值时按值的形状挑分支。认不出的结构(没写类型、文本字段、没写 items 的数组、没写属性的对象)下模型写的数一律丢掉、照记下来,码值里的数照收。嵌套的引用、数组写成一段 JSON 文本的照样认,还不是数组的照实记下格式不对。数值字段引用到日期、名称这类不是数的格子,记成 unresolved,不拿去和原流程的数比。

退回普通调用:原来消息里只有「按给定结构填写」,结构却不在请求里,模型猜不到字段名,交回来对不上的字段全成了 missing,双盲复算会说盲算什么也没查到。现在把结构(JSON Schema)写进消息;没交的字段记 unresolved(模型没有交这个字段),交了 null 才是 missing;一个要求的字段都没交就是没按结构交结果,照错误交回。模型建结构化输出就报错的,直接走普通调用,不白算一次调用。

上限与金额:一条查询自己的时限到了(ToolTimeout 也是 TimeoutError)原来被当成整个核查的时长到了,现在先认 ToolTimeout,原因交回模型、核查照常往下走。收尾要把查到的结果交给模型,原来留钱按眼下的消息估,最后一条查询的大段结果一进来就不够,查完了付不起收尾、结果白查;现在按收尾最贵的样子留(查询结果按 _digest 的上限、最后的说明截到 2000 字、结构也算),留过钱的收尾不再被上限拦下,实际用量超出估算或并行的核查先用掉当天余额时,最多超出一次收尾调用的钱。每日上限缺省按设置里的「核查每日金额上限」,明确传 None 才是不限,质询接口不必各自记得去读。

永远不抛:每日账写不进去、建工具出错这类意外原来会直接抛出,节点失败、质询接口回 500;现在整段包住,按已经攒下的用量、查询照实交回。
2026-10-05 01:46:55 +08:00
ThearchyHelios a427b53874 SQL 语义复核修正:同一个口径不再因为子查询里的别名、HAVING 里的别名被报成不同;取哪几行逐段比,连同 OFFSET 和取前几行时的排序
条件里套着的子查询(EXISTS、IN (SELECT …))原来按外层的来源整个照抄进外层那个条件,别名原样留着:带别名和不带别名的同一条 NOT EXISTS 被报成两处筛选不同,双盲复算就会说盲算换了口径。现在每段 SELECT 在语法树上记段号(复制出来的子查询靠它找回原树上那一段算好的来源,相关子查询照样到外层找),改写时每一列按它所在那一段认,子查询里的表去掉别名;比较两边排序时也按那一段的来源排。HAVING 引用 SELECT 别名(HAVING s > 100)换成别名指的表达式,和 GROUP BY 别名一个读法,聚合函数里的同名列不换。

取哪几行原来只看最外层 LIMIT 的行数:CTE 里 LIMIT 10 和 LIMIT 100、OFFSET 0 和 OFFSET 10、取前 10 行时降序和升序都报成没有不同,取到的却是不同的行。Facets 加 limits,逐段收集(最外层、CTE、FROM 里的子查询;条件里的子查询已经整个写在条件里,不单算,免得报两遍)行数、跳过的行数,以及这一段的排序(ORDER BY 1、别名换成 SELECT 里的表达式);OFFSET 0 算没写,排序只在限了行的段里比。原来的 limit(最外层行数)照旧保留。
2026-10-05 01:46:55 +08:00
ThearchyHelios c8363206bd 发起时就定下的事件和运行记录同一个事务写进事件流:受管级别那条日志、目录变化、口径升版处置以前在 run_manager.start 返回之后另外写,这时运行已经建好、执行任务已经起了。其中任何一次写失败(库被锁超过等待时长之类),launch_run 抛出普通异常,运行却在执行;定时任务按「LaunchRefused 以外的异常记未能发起」会把一次正在执行的正式运行记成未能发起,封存范围里也少了受管级别那条日志(governed_formal 找不到它就退回看工作流现在的 status,中途改过画布的会按已发布级别出具)。另外这几条和执行抢先后,运行结束得快时可能落到封存之后。
- run_manager.start 加 notes:(类型, node_id, 数据) 的列表,先 flush 拿到运行的 id,和运行记录同一个事务提交,提交之后广播,最后才建执行任务。写不进去就不建运行,launch_run 抛出的任何异常都说明运行没有开始执行
- launch_run 在发起前把这几条事件备好交给 start,不再事后用 run_manager.note 写。顺序和以前实际落库的一样(日志、目录变化、口径升版处置,然后 run.started),现在是确定的
- 没有 notes 的发起(探索运行、问数据)和以前完全相同

没有照复核建议的「写失败就记日志、照常返回运行」:那样正式运行会在缺了受管级别记录的情况下照常出具,可能悄悄降成已发布级别。

测试:用触发器让口径升版处置那条写不进去,launch_run 抛出 IntegrityError、库里没有多出运行(旧代码留下一条正在执行的运行,这条变红);服务发起的正式运行里,受管级别日志和口径升版处置排在 run.started 前面,都在封存范围里
2026-10-05 01:18:43 +08:00
ThearchyHelios fc57ed0e32 追加事件收紧几处,封存后追加的质询不计进运行用量。复核提出:停在审批时追加只看运行还停着,关于第一张审批卡的质询问完时运行可能已经批过它、停到了第二张上,问答照样写进去;没有终态事件的老封存运行追加之后 verify 由一致变成不一致;返回给调用方的事件 id 和库里的不一样;测试证明不了先落库再广播;封存后追加的质询平时不计用量,继续运行之后又被计进去。
- append_events 加 approval_id(只用于 interrupted):问的是哪张审批卡,那张卡仍待审批才写。核对放在和其余条件同一次条件更新里(EXISTS 待审批的那条),批过了、不是这次运行的卡都不写、返回 None。条件更新写明不同步会话里的运行:条件里带了 EXISTS 时 SQLAlchemy 会悄悄换一种同步办法、给语句加上 RETURNING
- manifest_seq 为空的老封存运行,封到哪一条是 verify_manifest 按事件类型推出来的:有终态事件的封到最后一条终态事件,追加在后面不碍事,照旧追加;没有终态事件的封到最后一行,追加一条范围就挪到它身上,我们自己写的批注被当成篡改,现在不追加
- _stage 落库时用事件自己的 id:广播出去的、交回给调用方的和库里的是同一个 id
- 运行用量不计数据带 post_seal 的质询:运行用量是这次运行自己花的,结束时就定下了、写进了终态事件;封存后的批注另有自己的账,同按需裁判的判定。继续运行之后它落进新的封存范围,照样不计

测试:两道审批的运行批过第一道、停在第二道,钉住第一道的质询不写、钉住第二道的写,读完运行之后那张卡被批了也不写,返回的 id 和序号与落库的一致;广播时查库,每条都已落库,落空的一条都不广播(把广播挪到条件更新之前这条就变红,以前的测试照样通过);老封存三种(无终态、run.finished、run.failed)追加前后 verify 都一致,无终态的不写;失败封存的运行追加 post_seal 质询后继续运行,运行用量里没有它。去掉审批卡的核对、改回老封存一律追加、改回计 post_seal、去掉落库 id,对应的测试各自变红
2026-10-05 01:18:03 +08:00
ThearchyHelios f8b2857630 进程重启后接号不再撞上已有的序号:以前恢复、继续运行、放弃、启动时补日志都只按 run.last_seq 接号,而 last_seq 会被写小。按需裁判记判定(api/evidence.py 的 _append_judged)的条件更新不带 last_seq 条件:它取了号还没写,封存后追加(run_append)抢先提交了,它再提交时把 last_seq 写回自己那个更小的号。同一进程里序号计数只增不减,不碍事;重启后计数从 0 起,继续运行写出的 run.resumed 就和封存后追加的事件同号,重新封存时两条同号的事件一起进了清单,而清单按序号排序算哈希。
- runner 加 _seq_floor:接号起点取 last_seq 和库里实际最大序号中大的那个。恢复、继续运行、放弃都改用它,run_append 原来手写的同一段兜底也换成它
- 启动时(setup)先把落后的 last_seq 按库里实际的最后一条改正,再处理上次留下的运行中记录。_append_judged 规格不许改,它重启后也只按 last_seq 接号,不改正的话它第一次追加就撞号;有 (run_id, seq) 索引,五千次运行、一百五十万条事件实测约 7 毫秒。updated_at 不动,这是补记录,不是运行有了变化
- _stage 不带运行对象时的更新只往大里改:另开会话记的事件和执行任务同时在写,号大的先提交、号小的后提交,以前会把 last_seq 写小

测试:last_seq 写小、清掉本进程计数之后继续运行、恢复、放弃都接在实际的最后一条后面,全流无重复序号、verify 通过;_stage 先提交大号再提交小号,last_seq 不变;按真实的交错(裁判停在条件更新之前,封存后追加先提交)把 last_seq 写小,重启(shutdown、清计数、setup)后 last_seq 改正、别的运行原样不动,再裁判、继续运行都不撞号。去掉启动时的改正,只有裁判那条变红;去掉 _seq_floor,继续运行、恢复、放弃三条变红;_stage 改回无条件写,对应那条变红
2026-10-05 01:17:18 +08:00
ThearchyHelios f1c068d6a9 核查工具箱收尾:连接的方面补上另一侧(关联条件里出现的别的表,CROSS JOIN 写它之前的来源),双盲复算对照数据目录里的关系时用得上;只读 agent 收尾前先拿到收尾用的模型再预留金额,模型拿不到时不在每日账上白记一次调用;收尾消息的参数改名 limit_words,说清是哪个上限的说法 2026-10-05 00:42:02 +08:00
ThearchyHelios 1efa5f7b20 核查用的只读 agent:engine/verify_agent.py 的 run_verify_agent 给双盲复算、反方审稿、审批质询共用。工具只建给定数据源的 db_schema / db_query,一律 force_readonly;自己管工具调用循环(不依赖 NodeContext / @task),查询次数、时长、金额(这一次、全局每日)任何一项到顶就停止查询、按已有结果收尾,收尾那次时长到顶另给宽限,金额每一轮都给它留出钱;模型不在价格目录里时金额上限无效,照裁判的说法记一条说明;每次调用记进核查的每日账。成功的查询按本 agent 自己的顺序编 Q1、Q2…,给了 emit 才发 llm / tool 事件(role: verify,查询的 tool.end 带 query_artifact,随运行封存、能下钻),绝不往证据台账写 kind: query,免得进了报告的写作目录。最后按调用方的结构交结果:数值字段换成格子引用 {alias, row, column},由代码从快照取值,模型自己写的数字不采信、取不到是 None;理由里的数字写成 [[v:Qn.rX.列]],用本 agent 自己的台账 build_catalog + compose_doc 组装,裸数字会被标出;结构化输出失败退回普通调用抠 JSON。上传表格按调用方给的数据版本查。永远不抛,模型没配好、调用失败、触顶都落在返回值里 2026-10-05 00:34:17 +08:00
ThearchyHelios e46ff55fd4 数据源查询工具加 force_readonly:核查的 agent 另起一路、没有审批关卡兜底,要查的源却可能可写。开了就不管源可不可写,一律按只读守卫检查:先过一遍和 run_query 相同的检查(多条语句、结构变更照原来的说法拒),再按只读判,写语句交回「这次查询只允许读取数据」、不执行,数据原样;查询照常落查询快照。工具描述跟着写「只读」,审批判定也不再把写语句当危险调用。只靠守卫这一道:连接池按源缓存、不分只读与否,不能为此另起一个只读池(缓存键相同,会把这个源平时用的可写连接换成只读)。build_datasource_tools 透传这个参数,不传时一切照旧 2026-10-05 00:26:04 +08:00
ThearchyHelios 031aae9343 SQL 语义:data/sql_semantics.py 给双盲复算、运行对比、反方审稿共用。normalize_sql 按方言解析、重新生成(去注释、统一关键字和空白、按方言转换不加引号的名字),再把字符串和数字换成占位符:只改了字面量的两条 SQL 规范文本相同(定时运行每期日期不同,不算模板变了),字面量按规范文本里的先后列出;解析不了退回逐词规范化,照样认得出只改了字面量。facets 把查询拆成表、连接(类型 / 关联进来的表 / ON 条件)、WHERE 合取式、时间条件(列 / 运算符 / 取值)、分组、聚合(函数 / 参数 / 去重)、HAVING、LIMIT:别名换成真实表名,比较两边排好顺序,GROUP BY 1 和别名换成表达式,CTE、子查询逐段收集,只透出一张表的 CTE 换回那张表的列,逗号连接写在 WHERE 里的等式算关联条件,相关子查询到外层找别名;解析不了带 parse_error,从不抛。diff_facets 每处不同给一句中文(「A 按支付时间 paid_at 筛时间,B 按下单时间 created_at」「A 排除了 is_deleted = 1 的行,B 没有」「A 对 order_id 去重计数,B 没去重」),两边叫法可以换成「原流程」「盲算」,列的中文名可以用 catalog_labels 从数据目录取;只换了一张表时说一次「查的是哪张表」,其余按不写表名的写法比。复用 sqlcheck 的 dialect_of、_conjuncts、_bare_column、_unparen 2026-10-05 00:22:51 +08:00
ThearchyHelios 6b272de6f9 审批质询的用量计进运行用量:质询不在图里执行、不发 llm.end,花的 token 和金额写在 approval.inquiry 事件数据的 usage 里(input_tokens、output_tokens、cost_usd、calls,可带 unpriced_calls),以前运行用量只认图的状态和 llm.end,质询花的钱在运行页上看不见。_usage_of_events 把质询的用量单独加总放在 inquiry 下:llm.end 这本账要和状态那本比大小,质询花的两本里都没有,混进去比会被状态那本盖掉。_finalize 照旧算出图的用量(成功取状态,其余两本取大)后再加上质询的,明细另记在运行用量的 inquiry 下;停在审批时被放弃的运行,放弃时补上这次停下期间的质询。停过两次、两次都问过的,按上次记下的 inquiry 先减后加,每次质询只计一遍。没有质询的运行用量和以前逐字相同 2026-10-05 00:18:22 +08:00
ThearchyHelios 3dbd69b561 往停下的运行里追加事件统一走 engine/run_append.py 的 append_events(run_id, items, *, require, seal=None):审批质询要在运行停在审批时把问答写进运行、随运行结束一起封存,探索运行封存之后还要能追加批注。事件流是封存的依据,清单按 seq 排序、run_events 的 (run_id, seq) 又不唯一,撞号会让封存核对时对时不对,所以照 _append_judged 的做法:同一运行在进程内排队,读 last_seq 后从 bus 接号(另取库里实际的最大序号兜底,last_seq 被别处写小了也不撞号),写事件和条件更新 last_seq 在同一事务里——interrupted 要求运行仍停在审批,sealed 要求仍是终态且封存没变,任一条落空就回滚、返回 None,调用方据此说明未写入运行记录;提交之后才广播。sealed 的事件带 post_seal: true;运行记录上的封存若早于最后一条结束事件(继续运行后又结束、还没重新封存的那一下),不算现在的封存,不追加,免得事件被接下来的封存收进去;调用方可传 seal 固定开始时看到的封存。事件类型加 approval.inquiry。测试用人工审批停住的运行:追加两条后批准、封存,verify 通过且改动追加的事件 verify 就对不上;恢复、继续运行、重新封存、别处写入抢在中间时都不写、last_seq 不动;连续和并发追加序号严格递增、全流无重复;恢复后前面的节点不重复执行,Agent 里批过的工具不重复执行(含升级前写下的断点) 2026-10-05 00:14:40 +08:00
ThearchyHelios 0eaaf71346 运行记下选项快照和来源:runs 加 options(JSON,默认 {})和 schedule_id(可空,建索引)两列。后面的核查设置要在发起时定下、整次运行沿用,审批停下期间改了设置,恢复和重放仍按发起时的(同 approval_default 的道理);定时任务要能找回自己发起的运行。RunManager.start 收 options、schedule_id 并落库,选项先深拷一份,调用方之后改自己的 dict 不影响快照;选项非空时写进每一段的 run.started(进封存范围),为空时 run.started 和以前逐字相同;恢复、继续运行照运行记录沿用,节点从 RunContext.extra 的 run_options 读同一份拷贝。RunOut 带 schedule_id,GET /api/runs 可按 schedule_id 筛,launch_run 原样往下交。旧库走 _COLUMN_MIGRATIONS 补列,存量运行回填 {} 和 NULL;create_all 不碰已有的表,新列的索引另由 _INDEX_MIGRATIONS 补建,索引名和新库 create_all 建出来的相同 2026-10-05 00:07:07 +08:00
ThearchyHelios aa828021d7 核查设置:设置里加 verify 一组,双盲复算、反方审稿、审批质询共用。核查模型(接入、模型)空着沿用证据裁判模型的取值链;双盲复算 {启用, 允许的相对误差默认 1e-6, 查询次数, 时长}、反方审稿 {启用, 每份报告最多审几句, 每句查询次数, 时长}、审批质询 {启用, 每次查询次数, 时长},另有核查每日金额上限,上限存 null 表示不限。写法照裁判一组:PUT 先查后写,有一项不对整个请求回 422、说明写设置页上的叫法;库里存坏的项读出来按默认,三项各自那一组逐项补。verify_settings(session) 给节点和质询接口读,verify_model_spec(session, node_config) 按节点 → 设置 → judge_model_spec 取模型,整组取一级、不跨级拼。核查的每日计数不从设置接口进出,另加只读的 GET /api/settings/verify/spend 给设置页看今天花了多少 2026-10-05 00:06:03 +08:00
ThearchyHelios b459c3bb58 核查花费账:engine/spend.py 照结论句裁判的每日计数做一本核查用的每日账(设置表 verify_spend 一行,按本地日期滚动)。调用前按估算预留,加上这笔会超过每日上限就不记、返回 None,上限为 None 表示不限;调用后按实际用量找补,跨了天不找补;价格目录里没有的模型只计次数。和裁判分开记:核查一次要查好几轮库、花得多,记在同一本账上会把当天的裁判挤到判不了、让报告出具无故降档。读改写按事件循环各持一把锁,并发预留不会一起越过上限 2026-10-05 00:06:03 +08:00
ThearchyHelios 7820556ab6 发起运行的规则抽成 engine/launch.py 的 launch_run:以前全写在 api/runs.py 的 start_run 里、只认 HTTP 请求,后面的定时任务要用同一套规则发起正式运行,另抄一份迟早走样。正式运行只能从当前发布版本发起、口径升版未声明策略就拦、绑定的工具不存在就拦、发起后记受管级别那条日志和目录变化、口径升版处置,全部搬进服务;拒绝时抛 LaunchRefused(status_code, message, code),start_run 只剩一层包装,把它换回和以前逐字相同的响应(带机器码的仍回 {detail, code})。正式运行的快照图不成形,以前由 main.py 的 ValueError 兜底回 400,现在在服务里换成同样的 400 拒绝,调度器也拿得到明确的拒绝。node_setting、tool_bindings、missing_tools 跟着搬过去,runs.py 再导出,旧的导入路径照旧可用;runner 固定数据版本取绑定时改从 engine/launch 导入,不再依赖接口层。新测试先把 HTTP 的 17 种拒绝(含工具缺失的数据、工具、混合、超过 8 处四种说法)按原话写死、在旧代码上通过,再逐条比对直接调 launch_run 的结果;另测服务发起的正式运行照样写治理事件 2026-10-05 00:03:24 +08:00
ThearchyHelios a3058e7df8 复核套件骨架(后端):先把后续几个工作包要用的位置占好,各包只改自己的文件。节点类型加双盲复算 dual_check、反方审稿 challenge,执行器是占位(记一条 info 日志后跳过,产出 skipped 和原因),校验按普通节点走,叫法进 labels,暂不写进助手的节点说明。出具契约留一个通用口子:成果节点上游任何节点产出里的 issuance_gaps(字符串列表)收进缺口,前面加上「节点类型「节点名」:」,出具随之降档、不会升档;只认这个成果的上游,写坏的条目跳过。路由先登记 schedules、compare、bundle、inquiry、whatif 五个空前缀,挂在 /api/runs 下的四个排在 runs.router 之前,以后加固定路径不会被 /{run_id} 吃掉。定时任务的调度先放空的 start / stop,AGENTLAB_SCHEDULER 默认开、0 / false / off 关,写进设置;启动时开着才 start,测试环境默认关 2026-10-04 23:40:20 +08:00
ThearchyHelios 536c9de116 证据目录的省略警告照实说省略了什么:放不下不一定省略了查询。真实运行里查询全部列出、只是有的只列前几行,警告却写「0 次只列编号和摘要()。写作模型看不到这些查询的明细」。现在分三种写:有查询只列编号和摘要的照旧;查询都列出、有的只列前几行的写「N 次查询都列出了数据,其中 M 次只列前 3 行」并按来源列次数;只省略了表和字段说明的照实说。事件多带 compact、compact_rows,各来源带 compact;时间线按同样三种写标题。另外把测试夹具、注释和 check-decode 里沿用的那次运行的真实查询次数换成虚构的次数或概括说法 2026-10-04 12:28:07 +08:00
ThearchyHelios 46330dfd96 模型不在价格目录里时运行金额写「金额无法估算」,不写 $0:真实运行里 token 记到了上百万,金额却是 $0,看着像没花钱。llm.end 发出时,用了 token、模型却不在价格目录里的标 unpriced(目录里标 0 价的本地模型不算);运行用量按 llm.end 数出 unpriced_calls,成功的运行也补上(状态里的用量分不出 0 是免费还是没价)。运行详情、画布运行面板、航迹的金额:一分都没估出来写「金额无法估算」,估出一部分写「≥ $x」,悬停说几次调用不在价格目录中。check-trace 补断言;流式用量测试和注释里的真实调用次数换成概括说法 2026-10-04 12:27:38 +08:00
ThearchyHelios 720f842b82 结论句裁判小批分波地问:一次调用最多 8 句(原来 40 句),第一批单独问、之后每波最多 4 批同时问,几批共用剩下的时长;超时只丢没回来的那几批,回来的判定照样留下。真实运行里推理模型拿着 40 句的摘录在 30 秒的时长上限内没回来,交给审批的报告一句都没判成,审批卡只能写全部未裁判;同一个模型 5 句一批 3 秒多就回来了。金额上限按已花的加这一波已定下的估算算;第一批调不通(鉴权、网关挂了)时只撞这一次,后面的统一记未裁判 2026-10-04 12:27:38 +08:00
ThearchyHelios fa842a7bec 交给人工审批的报告,探索运行也在节点里裁判结论句:审批人批准前就能看到判了几句、几句证据相矛盾。按需裁判要等运行封存之后才能判,封存前放开会动证据清单的完整性约束,所以这种报告改在节点里当场判,和正式运行同一条路;没有审批的探索运行照旧按需判,不在节点里花钱。审批节点审哪几份报告的认法抽成 reviewed_report_ids,审批卡和报告节点共用,两边说的「要人审」对得上。审批卡上没开裁判的探索报告改写「这份报告没有开启结论句裁判……开启后,交给审批的报告会在批准前判完」;开了裁判却没在节点里判的老运行另给一句。助手指引、出口契约注释、前端类型注释同步;审批暂停时按需裁判仍拒绝,测试改成只看点击不多一次调用 2026-10-04 12:03:14 +08:00
ThearchyHelios ee0700eca6 合入 FA:结构化抽取截断重试、写作清单按模型窗口定预算并按来源均衡、缺失断言交回重写、日期时间按可读格式显示 2026-10-04 11:43:21 +08:00
ThearchyHelios 470f8b6641 报告里单元格的 ISO 日期时间显示成「2026-06-30 08:21:39」,只改显示
报告表格里的时间以前照快照原样显示成 2026-06-30T08:21:39,读起来费劲。[[v:]] 和 [[table:]] 渲染出来的 ISO 日期时间改成用空格隔开(小数秒、时区照留),写作目录和流式输出同一套写法。快照里的值、引用记下的 value、eid、证据下钻都不变。

复核按文档生成时的规则:老文档存的是带 T 的字,拿新规则重新渲染会对不上、被当成文档被改过。所以显示规则也记版本:新文档里真有日期时间被改写时在顶层记 cell_display: 2(参与内容哈希),没有被改写的文档和以前一字不差;没有这个字段的按 1 版(原样)复核,记了认不出的值报 bad_schema。出口复核、期 4 的金样(test_provenance_unchanged)都照旧通过;裁判摘录是给模型看的,不改
2026-10-04 11:33:09 +08:00
ThearchyHelios 4e1adec205 审批卡标明运行类别、附上审的报告的核对概况:真实运行里审批说明写着「批准后才会正式出具」,那却是一次探索运行;报告里一句结论和证据相矛盾,审批人批准时完全不知道。人工审批的卡上加一行运行类别和批了会怎样(探索运行写「批准后输出,这是探索运行,不是正式出具」,正式运行写从已发布版本发起);human 节点挂起时,上游直接连着的、或标题和说明里引用了正文(nodes.<id> 或报告的 assign_to)的报告撰写节点各带一份核对概况(payload.reports:同 report.checked 的 stats、结论句策略、裁判摘要、报告文档工件 id),卡上写「N 个数字都有出处」和结论句的核对状态(节点里判过的写判了几句、几句证据相矛盾;没开裁判或探索运行写「结论句尚未核对」),并给查看逐段证据的入口,点数字打开证据面板。check-stream 补断言 2026-10-04 11:29:04 +08:00
ThearchyHelios 546d0e9326 没有数据目录的大库:助手「参考了 N 张表」那一行提示先起草目录。真实运行里的大库一张目录都没有,挑表和 SQL 检查都用不上目录,用户也不知道该先去起草。服务端 context 操作的每个数据源多带 source_id 和 catalog_tables(去掉驳回项后还有内容的表数);前端 copilotContext 在要挑表的大库 catalog_tables 为 0 时给出下一步「这个数据源还没有数据目录,起草后挑表和 SQL 检查会更准」,入口直达 /data/catalog/:sourceId(几个库时点名,老服务端不给目录表数时不提示)。check-decode、check-stream、check-chat 补断言 2026-10-04 11:28:49 +08:00
ThearchyHelios 799c80b97e 清单有省略时不许断言「没有数据」:写作规则加一条,核对认出缺失断言交回重写一次,仍有的只标注
一次真实运行里写作者只看到上百次查询里的前二十来次,却写出「某类数据缺失、本轮没有查到任何结果」这样的断言,可那个 Agent 实际查到了上百条记录。

- 写作规则:清单有省略、或有来源的结构化结论为空时,不得断言没有查到、不存在、缺失、没有数据,只能写「本报告未覆盖」并说明依据不足
- 组装文档:清单有省略时按一组明确的说法认出缺失断言(没有查到、未查询到、查不到、没有任何记录、数据为空、数据缺失、不存在相关记录、暂无数据等;「不存在明显差异」「不代表没有数据」这类不算,代码块不查),记新违规 absence_claim,带 span、所在片段和原句
- 和句中的 [[see:]] 一样归入 REWRITE_ONLY:交回重写一次,重写后仍有的只标注、不判失败;只剩这几类时不再按最多重写次数反复重写。仍有时报告节点发 report_absence_claim(带 count、texts),运行复核算作缺口
- 证据面板的违规清单标「缺失断言」并写明为什么依据不足,时间线给 report_absence_claim 专门的一行,文案在 terms.ts;check-decode、check-evidence 补断言
- 没展开的查询的摘要在没有表结构快照时从查询快照里的 SQL 认表名
- 回归测试:合成的门店、会员、卡券、活动库真跑「4 个 Agent 并行查库 → 报告」(查询次数虚构、分布悬殊),覆盖抽取截断重试、两次截断记空值、清单均衡和缺失断言的重写
2026-10-04 11:28:23 +08:00
ThearchyHelios 4e876ff9d8 写作者的出处清单按写作模型的窗口定篇幅,放不下时分优先级、按来源均衡,省略了什么照实说
一次真实运行里证据台账有上百次查询(4 个 Agent 查询次数悬殊),清单写死 12000 字,十万字以上的目录被截到一万两千字以内,只有前二十来次查询进了写作者的视野;查询按编号排,前面的 Agent 占满了预算,有的 Agent 一次都没露面。

- 预算:写作模型上下文窗口的 40%(字数按 1 字约 1 token 保守算),窗口先看模型接入里给这个模型登记的 context,再看模型目录;拿不到按 128K(和 Agent 护栏同一个保守值),即 51200 字;下限 12000、上限 160000
- 第一优先:各 Agent 经按出处核对交来的结构化字段(换成报告目录里的全局编号,值按单元格引用的渲染规则写,给出可照抄的引用标记)、口径卡指标和运行输入,一律列出;结构化结论为空的来源写明不代表没有数据
- 查询按产出它的节点分组、轮流展开:字段引用到的查询先展开,再给每次查询列前 3 行,有余量再补到 20 行;表和字段、知识库检索最多占预算的四分之一
- 没展开的查询列出编号和一行摘要(表、行数);约定只能在句末写 [[see:Qn]] 作依据,不猜行列、不凭摘要写数,数字照样逐字核对
- 清单开头明说目录有省略、各来源各省略了几次;报告节点发 report_catalog_trimmed 警告(带 queries、omitted、nodes),时间线给它专门的标题和下一步,运行复核算作缺口
- 放得下时清单和以前一字不差(多出的只有结构化字段那一节)
2026-10-04 11:17:13 +08:00
ThearchyHelios bf98ce16b8 运行复核把 agent_field_truncated 算作缺口(degraded),和 agent_field_unverified 同一档:字段都记空值、答案能用,缺口是输出额度在推理阶段用完了 2026-10-04 11:14:41 +08:00
ThearchyHelios f4a9955cc3 助手写查库 SQL 时给结果列起中文别名:真实运行里报告表格的表头全是英文缩写的原始列名([[table:]] 原样用查询结果的列名)。节点手册里 Agent 查库的说明和写 SQL 的硬性要求都加上一条:结果列用中文别名,按方言加标识符引号(MySQL / MariaDB 用反引号,PostgreSQL、Oracle、SQLite 用双引号,不用单引号),只给最外层 SELECT 起别名;自查的列名核对把带引号的中文标识符抹掉再判,不再因为有带引号的中文别名就整条放弃核对、把猜错的英文列名一起放过去 2026-10-04 11:13:12 +08:00
ThearchyHelios a46bad0592 助手搭多来源汇总的分析报告时默认开结论句裁判:节点手册的 report 一条写明几个 Agent 分头查库、或者几次查询合在一起写成的分析报告默认写 claims: judge,不写 judge 的预算(取设置里的默认),正式运行在节点里自动判、探索运行由读的人按需判,用户明确不要的不加。和受管级别「judge 必须写预算、拿不准就问」的取舍:那条只管要按受管级别发布的模板,普通草稿里默认开的 judge 不为预算问人;发布前修复缺预算要问的规矩不变。问数据路径原本定了不替人打开结论句策略,保留,并写明这条默认在问数里不适用 2026-10-04 11:10:28 +08:00
ThearchyHelios fa5cb1d47b 助手写人工审批的说明不再许诺「正式出具」:真实运行里一次探索运行的审批写着「批准后才会正式出具」,成果节点也没有出具契约。节点手册的 human 一条写明只有成果节点配了出具契约、从已发布版本发起的正式运行才会出具,审批说明写「批准后输出」,运行类别由审批卡标明;自查新增 human_formal_issuance:这一轮写的审批标题、说明里有「正式出具」就交回模型改 2026-10-04 11:07:40 +08:00