9 Commits

Author SHA1 Message Date
ThearchyHelios 4e3623885b 合入 W2-a 的复核修复(nr8/a:导入的 NOT NULL 补值与孤儿行、收尾 dispose、sslmode 换算、PostgreSQL 写入去 NUL、不分大小写搜索 contains_ci、json_eq 两库同语义、JSON 守卫、用户可见列表补决胜键、bubblewrap --clearenv、部署脚本与原子写的几处、README 连接数预算)
# Conflicts:
#	backend/app/api/schedules.py
#	backend/app/main.py
2026-10-19 12:21:38 +08:00
ThearchyHelios 0d9f1d9339 用户看得到的列表补决胜键(复核 R-a9):按名称、时间等可能并列的排序后面补上同一个模型的 id,并列时两种库排出来一样
- 实测 /api/evals/calibers、/api/schedules/trigger-options 按 Workflow.name 排序遇同名并列时 SQLite、PostgreSQL 顺序不同。用 AST 逐个过了一遍 order_by:最后一个排序键是 created_at / updated_at / last_active_at / at / name / 各种 *_at 之类、不唯一的列,又不带 DISTINCT、GROUP BY 的,补「, 模型.id」(降序补 id.desc()),共 104 处,分布在 api、apps、auth、blueprints、chat、data、engine、eval、ledger、look、memory、process、providers、scheduler(runs.py 里的三处在上一个提交里)
- 按唯一列(seq、version、唯一的 name)排的不动;按聚合、coalesce 之类表达式排的说明见 db/postgres
2026-10-19 12:07:06 +08:00
ThearchyHelios aa051b0a28 运行对比里子工作流的口径卡按卡所在那一层找产出:组装推演用的 Sources 时带上是哪张卡,节点产出、跑完的时刻按 engine/lineage.in_scope 换成卡那一层的 id(同推演 api/whatif._sources、证据面板 _InputMasks._output)。以前子图里的卡取值链写的是子图里的 id(nodes.parse),对比拿父图里同名的节点当它的产出,容器输入认到父图那次查询上;加一条子图卡的真跑对比测试 2026-10-19 09:03:37 +08:00
ThearchyHelios 5c6820f602 运行对比的默认基准(GET /api/runs/{id}/compare/base)带上 previous:基准是不是紧挨着的上一次,取 pick_base 现成算好的那个值,和定时任务自动对比记下的 previous 同一个语义
运行详情「更多」里的入口要按它定叫法:上一次失败、取消、还没结束时,基准是再往前成功的那一次,菜单得和触发历史一样写「与上一次成功的运行对比」,不能一律写「与上一次对比」。测试钉住:同一定时任务、退回同一工作流两种范围里,失败、取消、还没结束、没封存的都算隔着,别的运行类别、别的定时任务、比这次晚发起的不算;上一次失败时运行详情和触发历史拿到同一个基准、同一个 previous;中间那次删掉之后运行详情按现在的记录算成紧挨着的上一次
2026-10-06 16:42:29 +08:00
ThearchyHelios 1bee9b5100 触发历史不再给基准已过时的自动对比;对比页找不到默认基准时标题不再和「可在上方选择」打架,换基准的下拉和基准卡写明失败、取消的运行
- 中间失败的那次事后被接着运行成功之后,第 3 周存着的那份对比(比的是第 1 周,previous 为假)和运行详情「与上一次
  对比」的默认基准(现在是第 2 周)又对不上,两处却都写「上一次成功的运行」。auto_compare.outdated 对基准当时就不是
  紧挨着的上一次的那几份按 pick_base 现挑一遍,基准变了的算过时;api/schedules._fire_outs 对过时的给 null(不给链接、
  不给旧计数),两个同名入口比的始终是同一对运行。基准没变的照旧给:中间那次还没结束、后来失败、被删掉都一样。紧挨着
  的上一次不会过时(两次之间插不进新的运行),不核
- test_base_follows_launch_order_not_finish_time 补上:接着运行完之后第 3 周的触发记录不再带对比、任务列表的 last_fire
  同样不带、运行详情的默认基准是第 2 周;另加一条基准仍是挑出来的那一次时照旧给出
- 对比页找不到默认基准:标题由「未找到可作为基准的运行」改为「没有默认基准」——失败、取消但封存了的运行不当默认基准,
  却照样能在上方手动选;服务端的原因改成「没有找到上一次成功的运行:需要同一工作流、同一运行类别里比这次先发起、成功
  结束并已封存的运行」,运行详情弹出的提示单独读也说得清
- 换基准的下拉给失败、已取消的运行写上状态,基准卡也标出来;下拉说明补一句默认基准只取成功结束的运行
- 运行详情「更多」的菜单项点之前不知道基准是哪一次,基准不是紧挨着的上一次时仍叫「与上一次对比」,和触发历史的叫法
  不同:注释照实写明,留作后续
2026-10-06 13:33:40 +08:00
ThearchyHelios 544d44bdc2 运行对比的默认基准和定时任务的自动对比共用一个挑法:失败、取消的不当基准,按发起先后取上一次成功的运行
运行详情「更多」里的「与上一次对比」原先走 default_base:任何终态都算、按结束时间排,上一次失败时拿失败的那次当基准;
触发历史里同名的入口用的是自动对比的 _previous(同一定时任务里成功结束并封存的最近一次、按发起先后)。两处拿到的基准
不同,计数也对不上。

- api/compare.py 新增 pick_base:比这次先发起的运行里成功结束并封存的最近一次,同时给出是不是紧挨着的上一次。定时任务
  发起的先找同一定时任务,没有再退回同一工作流、同一运行类别(同样只挑成功的、按发起先后);schedule_only 时不退
- default_base 与自动对比(scheduler/auto_compare.py,原 _previous 删掉)都调它;找不到基准的说明改成「上一次成功的运行」
- 测试:上一次失败、取消时默认基准跳过它们,和自动对比挑的是同一次;按发起先后而非结束时间(含停在审批上的这次);
  之前没有成功的运行时回 404
2026-10-06 11:56:31 +08:00
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 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 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