Commit Graph

137 Commits

Author SHA1 Message Date
ThearchyHelios 855a559300 分析结论进证据链(引擎):台账 kind analysis 的条目在写作目录里按台账先后编 A1、A2……(只收报告祖先里的,报告节点的「证据来自」按做它的节点过滤),条目照 catalog_result 的约定带着从工件取回、复验过哈希的整份结论,取不回、哈希不符的记明原因;存进报告文档的目录只留摘要,整份结论按工件取回。引用:[[a:A3]] 渲染判定语(一个片段,种类 analysis,整段涂白、不算裸数字),[[a:A3.键|换算]] 渲染具名标量(换算同指标),[[v:A3.r0.列]]、[[table:A3 …]] 取结果表(表头写结果表的列名,表头里的数不算裸数字),[[see:A3]]、[[see:A3.r0]] 当依据;写错的编号、键、越界的行、判定语加换算都照实说为什么。受管级别的正式出具、契约没开「单元格引用」时,Agent 现场做的分析和它查的格同一个规矩,分析卡做的照常引用。写作目录加「分析结论」一段(判定语、判定标准、具名的数、结果表前 table_top_k 段与其余几段、附带说明、算不了的原因、来自哪几个 Q、下钻自哪个 A、输入查询的 SQL 检查问题,外加引用写法一句),放不下时判定和具名的数一律列出、结果表排在查询的行前面补、最后才退;口径卡指标上的可比性(有条件可比、不可比)照实写出原因。SQL 问题顺着分析结论的输入往下找。另有复算(analysis_recheck:算子没落地、调用写错、出意外都照实写「复算失败:原因」,方法升版写明前后两版)、复算用的输入快照、关键数的代入式,证据面板、导出、可验证报告共用 2026-10-21 13:07:36 +08:00
ThearchyHelios a86991b57f 双盲复算:盲算触顶、按无法复算记时(_limit_text 那一路),把盲算交回的理由(渲染后的正文,已经过遮罩)截一段带进原因(「盲算已达每项最多查询次数(1 次);盲算交回的理由:…」),审批卡、缺口说明上看得到它查到了什么,没有理由的照旧。原值所在的格子落在「查询区间里这张表没有数据」的列上时不派盲算、按无法复算记;盲算交回的格子落在这样的列上时不判一致、按无法复算记;原因都写「查询区间里这张表没有数据(…)」 2026-10-20 10:33:34 +08:00
ThearchyHelios 6fa2e297db 口径卡、报告、出具、复核认「查询区间里这张表没有数据」:口径卡按输入的快照 id + 列认(cell()、按位置取的格、Agent 经 cite_fields 交来的字段,按列,同一条查询里有数的列不受影响;转抄的指标照抄),给指标标 no_data 并写明原因,清单注明「这段时间表里没有数据:不要当成 0 解读」,发一条 metric_no_data;有出具契约的成果按缺口降档(契约收来的指标,和报告直接引用的这样的格)。写作目录里这样的指标、查询格子里这样的列都写清,写作规则加一句(目录里有这样的项才加)。证据面板的指标标「区间内无数据」并写明原因,查询步骤列出这几列。复核把 query_no_data、metric_no_data 记成缺口(DEGRADED),归进新的 REBUILD:是缺口、却值得换一张有这段数据的表重建一次,should_retry 和 chat/turn.retry_after_review 都认,问数据据此给出「按复核重来」的指示 2026-10-20 10:29:01 +08:00
ThearchyHelios 301bbd55f3 口径卡分母为 0 时算不出来就是空值,和「缺少输入」分开说;指标清单按格式渲染。助手节点手册讲口径卡的那段写明分母可能为 0 时写「… if 分母 else None」、不要写 else 0,附示例;画布校验加一条提醒 metrics.zero_for_undefined(按语法树认:条件表达式一边有除法、另一边是常量 0,两边对调、套在 round 里、嵌套的都认)。输入都齐、计算式自己交回空值的指标多记 undefined: true(状态仍是 missing_input,按状态认的地方和老运行不受影响),清单、写作目录、引用解析不了的原因、运行对比、证据面板都说「算不出来(这组输入下计算式没有值,比如分母为 0)」;推演把它当不带值的字段。指标清单({{ nodes.卡.text }})改用卡上渲染好的 rendered:比率 0.0007 显示成 0.07%、金额带千分位,和报告里 [[m:]] 一致;有值却显示不出来的照实说,不拼原值 2026-10-20 10:04:53 +08:00
ThearchyHelios ba4c604539 数字标记后面重复写的单字单位也去掉(部署上的「覆盖 8条 条产线」):以前单字的单位后面紧跟着字时一律不动,现在只在它和后面的字连起来是一个更长的单位(人次、个月、台套、元/…,_LONGER_UNIT)时不动,让「人」对「人次」这样的不一致照旧露出来;「条产线」「人参加」照样去掉重复的那个字,流式渲染一致。写作规则加一条 UNIT_RULE([[m:]]、[[v:]] 自带单位,后面不要再写一遍),助手节点手册讲 [[m:]] 的那一行也写明 2026-10-19 17:55:02 +08:00
ThearchyHelios 41b350b87c 相对日期一律按运行当天算:助手节点手册的搭图规则(画布、问数据共用)加第 6 条——「今天、昨天、上周、本月、去年」按运行当天算,SQL 用方言里取当天的写法、agent 先调 current_time,不用 MAX(日期) 去推「上周」,要说数据最新到哪天就另取一列;报告写作规则加一条(period_rule),告诉写作者今天是哪天、上周是哪几天:数据没覆盖问的那段时间时先照实说、写明数据最新到哪天,最近一段的数可以给但写具体起止日期,不叫它「上周」「本月」 2026-10-19 16:53:46 +08:00
ThearchyHelios 5dfe2e7fd1 report.failed_queries 不再把「写错一条、下一条就改对了」当取数失败:同一个节点里,失败之后还有一条同一数据源的成功平台查询、读的表覆盖了失败那条的表(按 sql_semantics.facets 取表;失败那条解析不了就按同一数据源之后有成功的查询认),这次失败算已被替代,不进写作提示、不记 report_queries_failed;一次成功的都没有时照旧写「这次一次成功的查询都没有」 2026-10-19 16:47:07 +08:00
ThearchyHelios 1e87504e5c 复核把「首稿没过、重写后已通过」当缺口:report_repair、report_rewrite 这类「改过一次」的信号按同一个报告节点最后一次 report.checked 定档——最终通过的(没有违规;裁判摘要里不再有证据不支持的句子)不进复核的异常清单,复核模型被别的信号叫起来时,说明里另起一节「已经处理好的」写「报告「x」首稿有 N 处没过核对,重写后已全部通过」,不再写成「核对仍未全部通过、可能不准」;最终没过的照旧记缺口。report_repair、report_rewrite 的日志带上处数 2026-10-19 16:43:54 +08:00
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 c59b085922 子工作流里的写入跨升级认旧键时按事件和内容认是谁的、是哪一笔(复核 L1、L2):升级前子图里的写入按子图里的 id 记键,和父图同名节点的键一样。以前父图有同名节点时一律不认旧键:子图里结果未知的那一笔核实为已生效后接着跑,用新键又发一笔;父图那个节点按自己的键找到子图那一笔、交回它的回执,自己那笔一直发不出去。现在一条记录是哪个节点写的按它的 action.* 事件上记的节点 id 认(认不出的按能力和内容指纹):按自己的键找到别人那一笔的换一个只属于自己的键;认旧键时还要内容指纹是这一次的——跨升级的循环第二轮按老算法同样是第 1 次执行,找到的是第一轮那一笔,金额不同就照常发,不再记「已经生效」。去掉不再用的最外层节点 id 传递;改对两处过时的说明(审批恢复时子图从自己的断点接着跑;终态事件的途经驳回含子工作流里的)。补跨版本测试:同一个进程里先装成升级前的代码跑到挂起、再换回现在的代码接着跑 2026-10-19 11:43:41 +08:00
ThearchyHelios 8ba0b27097 同一个子工作流节点执行多次时子图里的节点分得开:子图这一次跑过的每一步交回父图的 trail(节点 id 接上前缀),下一次执行从那里接着数第几次执行(engine/evidence.next_exec),台账、证据面板不再几条都记第 1 次;写外部系统的幂等键按这次运行里的节点 id 算(「sub/pay」)。以前循环里第二轮的子图写入算出和第一轮同一个键,交回第一轮的回执、外部系统少收一笔,父图里同名的节点、套同一个工作流的两个子工作流节点也撞键。升级前停在子图里的运行恢复时照旧按子图里的 id 认出已经生效的那一笔、不重发(父图里有同名节点时不认) 2026-10-19 09:16:14 +08:00
ThearchyHelios 04d80472f9 子工作流里的反方审稿按全局 id 记它审的报告:审稿记录和工件上的报告写这次运行里的 id(「sub/write」,同 report.checked、报告文档,第 1 波 B1 修过报告节点同样的问题),证据面板连子工作流里的审稿一起认(_Sealed.nodes_of nested),点开子图里那份报告的句子认得出审它的那次审稿;审批卡上的报告按全局 id 记,打开证据面板、审批质询对得上;单句面板的报告名写「子工作流 › 报告」,不写内部 id 2026-10-19 09:08:45 +08:00
ThearchyHelios 17e99cd76c 驳回出口之后,待办、通知、Webhook 照实说转到哪:中断内容带上驳回之后去哪(engine/replay.reject_route:接了驳回出口写转到哪几步,没接的写运行就此结束,审批没接、没设驳回即终止的写这一路到此为止),待办卡的提示、驳回按钮、驳回后的提示、处理过的「结果」照它说,不再一律写「转入驳回分支」「本次运行终止」;超时驳回不再露出英文结论码;双盲裁决、动作确认被驳回的提示同样照它说。子工作流里的关卡走驳回出口时,名字记成「子工作流 › x」(RunContext.global_label),走过的那几步交回父图的 trail,父运行的终态事件照实带上途经驳回,实例页、通知写「「子工作流 › 把关」被驳回,转到「子工作流 › 退回」」,不写内部 id、不写成已完成。定时任务的 Webhook:照驳回那条路成功结束的,标题、状态写途经驳回,另起一行写谁被驳回、转到哪一步 2026-10-19 08:59:15 +08:00
ThearchyHelios 06286deefa 上游查库全失败时报告照实说没查到:报告节点写作前从上游 Agent 的调用记录里找出失败的平台查询(engine/nodes/report.failed_queries),写作提示里多一节「没有查到的数据」列出哪几次、原因第一行;一次成功的查询都没有时不许写单元格引用、照实写没查到和怎么排查,重写请求里同一句;记一条 report_queries_failed 提醒。以前写作者照着上游文字里的 SQL 和列名写出引用不存在的 Q1…,重写一次还是那样,答案里留着没填上的 ⟦?v:Q1…⟧ 2026-10-19 08:23:29 +08:00
ThearchyHelios 32dbfcfb52 复核修正(后端):子图里的报告不再被判「疑似被修改」、驳回出口的双盲裁决照样降档、可验证报告与推演按卡所在的层找产出、子图那一步写名字
- B1 子工作流里有报告撰写节点时:它的 report.checked 进了父运行(节点 id「sub/write」),报告文档却记子图里的 id「write」,
  证据接口、可验证报告、质询拿两边一比判「疑似被修改」,评测读答案也说答案不能当证据。
  - nodes/report:报告文档、产出工件记这次运行里的节点 id(ctx.run.global_id),和事件同一个;nodes/io 子图自己的出具契约
    按同一个 id 核对,_issuance.report.node_id 同样写这次运行里的 id
  - api/evidence:_reports 默认只认父图上的报告(子图的报告是子工作流那一步内部的东西,父图的答案是子工作流出口的值),
    nested=True 连子图里的一起给;点名要子图报告的(?report=sub/write)照样点得开(_named_reports);按成果挑报告的兜底
    不再挑中子图的报告。成果上没有 _issuance 时退回最后一条 issuance 事件,子图成果节点的那条不认
  - bundle:出具档位(_tier)同样不认子图成果节点的 issuance,verifier.js 同一个认法;子图报告的文档在引用链上照常核对
- B2 双盲裁决被驳回、走驳回出口接到出具成果时:节点产出换成 exit_payload,出具缺口和 dual_check 台账条目跟着丢了,审核人驳回的
  那组数照样完整出具(比选「两边都不对」还宽)。NodeRejected 加 exit_updates(节点在驳回之前已算出的产出字段、台账、用量),
  compiler._rejected_exit 并进去;双盲复算驳回时照记缺口「指标「X」两路结果不一致,审核人驳回了裁决」和一条结论为驳回的
  dual_check。核对过别的关卡:人工节点、工具调用审批、动作确认、代码执行审批都在产出缺口、台账之前就被驳回,没有可丢的
- B4 子图里的卡转抄同一张子图里的卡、按位置取格:可验证报告(bundle/build._outputs_of)和推演(api/whatif._sources)按这次
  运行里的 id 查产出,子图卡上记的是子图里的 id,查不到就整张遮住、推演不了;父图有同名节点时还拿父图那个当依据。改成按卡
  所在那一层的 id 查(engine/lineage.in_scope / scoped_id,证据面板 _InputMasks 同一个认法),转抄的卡认不出时的兜底同样换层
- B6 子图里的一步出错、被驳回后 Run.error_node_id 是「sub/x」:实例页(apps/resolve)、通知(notify/service)照终态事件上记的
  名字写「子工作流 › x」(run_node_label),不露出内部 id

测试:test_subgraph_evidence(子图含报告:证据图按父图、评测读答案不报被改、子图报告点得开、可验证报告核对通过且不写子图的
档位、子图自己的契约照常;子图卡在可验证报告里不被遮、推演还原得出、父图同名节点不当依据;子图那一步的名字)、
test_dual_check_node(驳回出口接到出具成果照样降档、台账照记、证据面板认得)。每条都去掉修复回跑过,确实失败
2026-10-18 18:15:37 +08:00
ThearchyHelios 3998e821dc 子工作流的证据交回父图(后端):子图每一步进同一个运行、台账和口径卡交回、出口字段带出处、父图跨子图往下追
调查时发现:run_subgraph 只设了节点 id 前缀(node_prefix)没用上,子图在父节点里用 ainvoke 跑,LangGraph 不把它的
custom 事件交给父图的流——子图里每一步在运行的事件里一条都没有,时间线看不到、封存也不含它查过的数。「事件本来就在同一个
运行里」此前并不成立(docs/design/orchestration-round/00-总方案.md §2)。

- engine/context + nodes/multi:子图拿到父运行的写入口(STREAM_KEY)和前缀(PREFIX_KEY),子图里的节点经它发事件,节点 id
  「子工作流节点 id/…」,嵌套的一层层接上;工件按这次运行里的 id 记。子图里报的错(含被驳回)交回时节点 id 换成这一层的、
  名字接上子工作流(runner 的 run.failed / run.cancelled 写「子工作流 › 那一步」),不再和父图里同名的节点混在一起
- nodes/multi:子图的台账条目加前缀并进父图 evidence(父图报告的目录据此收进子图里的查询和口径卡);子图口径卡的产出以
  「子工作流节点 id/卡」进父图 nodes;出口每个字段按出口模板认出处(模板整串就是一个取值链、渲染出来正是那个值的才认,
  同口径卡认输入的那一套),出自子图运行输入的按子工作流节点的「输入」模板再往父图追一层,追不到的记 subgraph_input
- engine/compiler:子工作流节点自己的 node.finished 不再重记交回来的台账条目(子图那几步自己的 node.finished 已经带着)
- nodes/metrics:父图口径卡读 nodes.<子图>.output.<字段>(或 assign_to 变量的字段)时接上那个字段的出处,另记 subgraph、
  field;经子图出口转抄子图口径卡的指标照样带上不完整、存疑;cell(nodes.<子图>.output.<原样交出的查询结果>, …) 按台账
  认出子图里的那份快照
- engine/lineage:静态认法跨子图往下追(出口模板 → 子图里接着认;子图的运行输入按「输入」模板回父图认;最多 3 层),
  和运行时同一套;whole_ref 是两边共用的「整串一个取值链」判断。lint_process.load_subgraphs 往下读嵌套的子工作流,
  subgraph_spec 给静态认法取图;没读好放进来的照旧停在子工作流节点
- nodes/report:报告收证据的范围加上上游子工作流交回来的(evidence_from 写了子工作流节点的一并算)
- nodes/io:指标出具的缺口里,停在子图里的说停在那个子工作流(和静态检查同一句)
- api/evidence:卡上记的节点 id 按卡所在那一层认(_scoped:子图里的卡写子图里的 id),转抄、输入、追不到的那几步一路给到
  子图里的那一格;子图里的节点按 node.started 记的名字接成「子工作流 › 节点」(node_label、subgraph_label);双盲复算条目
  里的卡 id 同样换成这次运行里的
- apps/resolve:实例进度不单列子图里的步骤
- 口径账本的 use_caliber 不改;比对读封存的查询,子图里的查询如今在封存范围里,照常认得出

测试:test_subgraph_evidence(子图每一步进运行、报错点名子图里那一步、实例进度、父图口径卡跨子图追到查询格并完整出具、
子图里追不到的父图降档、不是整串取值链的停在子工作流、运行输入往父图追、嵌套两层、静态检查与运行时一致(含两层和输入
模板)、父图报告引用子图里的查询和口径卡并一路点到那一格、封存与可验证报告、子图里的遮罩照父图的运行)。去掉「交写入口」
回跑,五条失败(快照不在封存范围里)。后端全量 14472 通过
2026-10-18 16:11:15 +08:00
ThearchyHelios d57cdcaffc 驳回出口(后端):审批、补充输入、编辑稿件、动作确认、代码和工具审批、双盲裁决接了「驳回」出口时沿它往下走
以前这六类关卡被驳回一律抛 NodeRejected,运行收成 cancelled + end_reason=rejected,没有「驳回之后走另一条路」
(docs/design/orchestration-round/00-总方案.md §3)。现在画布上多一个可选的「驳回」出口(sourceHandle="rejected",和
分支、审批同一套连线机制):

- engine/schema:GATE_TYPES(人工、调用工具、沙箱代码、双盲复算)、reject_targets(驳回出口连到哪几步)、approve_mode;
  画布校验:别的节点接驳回出口报错(graph.reject_exit_invalid,条件分支的 case 标识可以叫 rejected,不算);审批同时设了
  「驳回即终止运行」和驳回边的给一条提示(human.stop_on_reject_exit)
- engine/compiler:接了驳回出口的关卡按出口路由(驳回出口 / 其余是普通出口,连线没写 sourceHandle 的照样是普通出口);
  NodeRejected 被接住时这一步正常结束:产出 NodeRejected.exit_payload()(rejected、decision、actor、note、kind、text,
  审批另带 approved=false、「结果存为变量」照旧写 false),node.finished 带 outcome=rejected、哪种关卡、结论、谁、备注、
  转到哪几步(to),trail 记 outcome=rejected;不重试、不被「出错时继续」接住,照旧
- engine/context:NodeRejected 加 stop(审批明说了驳回即终止的,驳回边照旧走不到)和 exit_payload;动作确认抛在发送之前,
  走出口时什么都没发
- engine/runner:成功结束的运行途经驳回出口时,run.finished 带 rejected_via(在哪一步、谁、结论、转到哪),照实说「途经
  驳回」;没有途经的终态事件一字不变
- engine/governance:发布检查各级都拦「驳回出口那条路走不到成果节点或结束」(reject_exit.no_end);退回上游再问一次的放过
- engine/layout:驳回出口排在普通出口下面
- apps/resolve:实例进度写「被驳回,转到「X」」(动作确认写「未确认(被驳回),转到…」,超时驳回写「超时驳回,转到…」);
  实例状态(实例页头、列表、通知同一句)写「「x」被驳回,转到「y」」,走到另一个成果节点的照旧写那个成果节点;退回修改后
  又批准了的按批准算;子工作流里的步骤不进实例进度
- 没接出口:行为一字不变(node.failed reason=rejected、运行 cancelled + rejected);审批没设驳回即终止、没接驳回边的老规矩
  照旧。接了驳回边的审批,实例进度那一步的说法从「已驳回」改成「被驳回,转到「已退回」」(test_app_instance_page 跟着改)

另:engine/context 的 RunContext 加 prefix / global_id、事件经 stream_writer 发(子工作流把父运行的写入口交下来),
这一步本身不改变最外层的事件,下一个提交接上子工作流

测试:test_reject_exit(五类关卡接了出口各走一遍、批准走普通出口、超时驳回走出口、没接一字不变、驳回即终止优先、
重试和出错时继续不管驳回、退回修改再问一次、校验与发布检查、布局、实例进度);test_process_actions(动作确认走出口
不发送);test_dual_check_node(裁决走出口)
2026-10-18 15:56:29 +08:00
ThearchyHelios d4f1ba8dd4 给评测留一个按引用的列投影的入口(referenced_of),顺手折行、补模块说明
- ledger/match.referenced_of(sealed):这次运行封存的报告引用了哪条查询的哪几列,正好是 compare_queries 的 referenced。
  评测逐题比 SQL(eval/score.sql_match,另一组在改)传它,就和账本一样按引用的列投影,不必自己再拼一遍。compute 和它
  共用读指标集的那一段(_card_loader)。
- data/sql_semantics 模块说明的「几处共用这里」补上模板绑定、按引用的列投影、同一个形状三处。
- 本轮几处新代码折到 120 列以内(只换行,不改逻辑)。

测试:tests/test_ledger_projection.py 的整题等价那条另核 referenced_of 交回的就是被引用的那一列。
2026-10-17 12:22:48 +08:00
ThearchyHelios c3123e3818 转抄口径卡指标的出处跨卡往下追,对账、召回运行按追到底的结果判档
对账卡的「口径」、召回卡的「旧版 / 新版」写成 nodes.<口径卡>.metrics[i].value,出处停在「来自口径卡指标」,对账运行、
召回运行一律降档出具(「输入没追到原始数据」);口径卡自己的指标明明追得到取数查询。

- engine/lineage:metric_copy 认出转抄一个指标的值的写法;traced() 对 via=metric 的输入看 upstream.traced;
  MAX_COPY_DEPTH = 8。
- engine/nodes/metrics:转抄的输入记 upstream {metric, artifact, depth, traced}——那张卡的指标集落了工件、那个指标的输入
  都追到了原始数据(它自己转抄的已经一层层追过,记在它的输入上)、层数没超上限才算追到;超了就算没追到(防环:循环里
  抄自己上一轮的卡每一轮多一层)。那个指标不完整、所依据的查询没通过 SQL 检查的,转抄出来的同样不完整、存疑,出具照样
  降档:信任按追到底的结果给,不多给。
- api/evidence:转抄的输入后面接一步 upstream(那张卡的那个指标:值、原式、代入式、它的输入、追没追到,取值按同一套遮罩),
  转抄的转抄接着追(同一个指标只接一次、最多 8 层),它的输入落在查询快照上的照常给查询步骤;出处链里遮着的列把这几步
  的输入也算上。reconcile.py、recall.py 生成的式子不用改。
- 前端证据面板:新的一节「转抄的口径卡指标」,输入来源里转抄的那一项写清追没追到、点一下跳到那一步,那一步的输入点一下
  跳到口径的取数查询(文案进 lib/terms.ts)。check-ledger 加一段:对账结果里点「按口径」的数,一路点到口径的取数查询。

测试:tests/test_metric_copy_lineage.py(转抄、转抄的转抄完整出具;转抄没追到的照样停在那张卡;上游不完整的转抄也不完整;
层数上限;真跑对账、召回都完整出具,出处链接上口径卡那一步、给出口径的取数查询)。去掉 upstream 再跑,5 条失败。
2026-10-17 12:05:12 +08:00
ThearchyHelios 1c8ca4c3d7 合入 W3-c 评测执行:后台评测与无头作答、六种期望五种结论的评分、重评、结论裁判记评测账、统计(Wilson 区间、按角色用量)、对比与 A/B(McNemar)、目录贡献预设、预算与止损、清理;定时任务接题集目标,定时评测有退步 / 没有评完推全局渠道;定时页照实显示非工作流目标 2026-10-16 18:46:44 +08:00
ThearchyHelios 2371e75f17 评测执行的主干:后台评测、无头作答、评分、统计、对比与 A/B、清理(下一轮第 3 波 W3-c 第一段)
为什么:第 2 波只有题集和期望答案的来源,评测本身跑不起来。这一段把细案 01 做法 9–12、16、17 的后端接上,
量的是用户在问数据页拿到的同一轮,而不是另抄一份编排。

- eval/answer.py:无头作答,串起 answer_ops → chat/turn 的决定 → launch_run(origin=eval,带目录视图、只读、遮罩)
  → 等终态(停在审批的取消、记「评测不代批」;驳回记路径「被驳回」)→ review_of_run → 按同一规则重来一次;
  工作流题按钉住版本的图跑探索运行(不发正式运行,免得被 pick_base 和应用视图取到)。用量按角色记
- eval/runner.py:用 core/job_runtime,一次评测一个任务,题 × 重复 × 臂一项;A/B 几臂共用期望、同一题同时开跑;
  开评拍目录快照、记上传表格版本和表结构哈希的前后;并发不超过 max_parallel 和全局并发的四分之一;每题时限;
  评测单独一本账(开跑前估算、每题预留后找补、触顶不再开新题);止损;能停、只重试没做完的、重启标中断不续跑
- eval/score.py:六种期望、五种结论(量纲不同单列)、维度配对与弱匹配、路径不对直接不通过、口径 dual 两版都报;
  逐题 E1 用口径账本的 compare_queries(总方案 X2,不另写 signals);SCORER_VERSION 可重评(eval/rescore.py,只读封存)
- eval/judging.py:结论裁判走按需裁判同一条路(evidence._judge_now),文字要点由裁判判、单列不计准确率;花费经
  judge.charged_to 记在评测那本账上(engine/judge.py 只加了一个上下文变量和取账本的一处)
- eval/stats.py、eval/compare.py:准确率与可追溯准确率 + Wilson 区间、各项分布、P50/P90、按角色成本、引用率、逐题 E1;
  选基准、逐题转移与归因、指纹差异、A/B 配对表与精确 McNemar;「目录贡献」预设对照(当前 vs 关,可加只用已核实)
- eval/cleanup.py + core/artifact_store.forget:保留最近 N 次 + 钉住的基线 + A/B 另几臂,删结果、明细工件、底下的运行和
  引用行,工件文件一条引用都不剩才删;总表永久保留
- engine/tolerance.py:双盲复算的 _agree / _delta_pct 原样搬来共用(dual_check 改 import,行为不变)
- 设置加 eval 一组(builder 读、admin 写)和 GET /api/settings/eval/spend;题集预算在全站缺省上覆盖,每日上限只能往下收
- api/evals.py:发起(单臂 / A/B / 目录贡献预设)、估算、进度、逐题、停止、重试、重评、钉基线、和上一次比、配对表、清理
- main.py 的 lifespan:影子起草和评测的启动整理、关停(评测先于运行停,停时取消自己发起的运行)
- 测试:test_eval_e2e(dual_check_shop + 剧本模型:按支付日 330 通过且 SQL 等价,按下单日 310 不通过、差在 time;
  目录对照 1:0;和上一次比找到退步、归因做法变);通知事件、定时接口形状的老测试按新事件和新字段更新
2026-10-16 16:38:09 +08:00
ThearchyHelios 1f8250699c 口径卡来源支持节点改名映射 caliber_from.nodes(口径账本做法 4 ③)
为什么:组合式问数、对账、召回要把一份口径的取数节点拷进另一张图,同一张图里放两份口径时节点 id 会撞,拷过来的取数节点得改名(c1_fetch)。以前 caliber_from 只认同名的取数节点,改了名口径卡就取不到输入。

做了什么:
- metrics._pinned_caliber 认 caliber_from.nodes {原 id: 新 id}:按语法树把式子里 nodes.<原 id>(含 cell(nodes.X, …) 的第一个参数、nodes['X'])的根换成新 id 再算,字符串字面量里的字不动;指标集的 source 照记映射。映射形状不对节点失败并说清哪一处。
- 画布校验 _caliber_from_issues:映射的形状和运行时同一个判断(rename_problem),改名后的节点要在图里、在口径卡上游。
- 升版检查照常挡、照常记事件,事件带上 caliber_nodes;证据的 _caliber_source / _caliber_upgrade 照带映射。
- 运行对比按映射把式子换回那一版里的写法再比:同一份口径换了节点名不算口径变了。推演按卡上记的改名后的式子原样重算。
- 没写 nodes 的老图一字不差:式子不经 unparse、来源只有三项、升版事件没有新键(单独测)。

测试:tests/test_caliber_from_nodes.py(改写规则、运行时取值、证据链、推演、校验、升版检查、对比、老图不变)。
2026-10-16 16:20:25 +08:00
ThearchyHelios 696cd17a4a 合入 W1-b 已知遗留:人工驳回记成已取消 + end_reason=rejected(不重试、不被出错时继续吞掉),各处照实写「被驳回」;个人渠道「我发起的实例完成 / 失败」生效;导入清单如实记「是否认证」;使用者选人按候选池、开登录时服务端核对;自由形蓝图按导入的图列「谁来做」;tick 按 id 断言 2026-10-16 12:51:43 +08:00
ThearchyHelios 4c0e83bc80 目录视图接缝 data/catalog_view.py(细案 01 做法 5,总方案 X7):读目录的四处统一经它,「开目录 / 关目录 / 只用已核实的项 / 目录快照」的对照才做得了
- CatalogView{mode: live | snapshot | off, statuses: all | reviewed, snapshot}:缺省 live + all,行为和以前一样;reviewed 只留已核实、已确认的项(驳回的项原样留着交给各处,照旧经 visible_notes 去掉,冻结快照和 SQL 检查靠驳回的关系编号认出不该现推的关系——为此在 catalog.py 加 restrict_notes,没给 visible_notes 加参数)
- 视图从哪来:运行从 Run.options['catalog_view'] 读(catalog_view.of_run:RunContext.extra 里的发起快照,审批恢复、续跑沿用),ToolContext 多一个 catalog_view,节点(调用工具、llm/agent、多 agent)构造时带上;生成过程从参数读(下一个提交接 answer_ops)
- 四处消费一律经它:运行时 db_schema / db_query(tools/datasource._catalog_of,冻结进表结构快照的也是这一份)、look 表详情(TableKind.load、copilot_context.look_service 收 catalog_view)、自查的 SQL 检查器(sqlcheck.load_checkers 收 catalog_view,改用 catalog.read_catalogs 一次读出)、双盲复算(_Sources 带视图,交给盲算的目录和比较 SQL 用的中文名都按它;核查 agent、反方审稿的 db_schema 也按这次运行的视图)
- 冻结快照:不是缺省视图的在引用 meta 里记下 catalog_view(不进内容哈希,缺省视图不记,快照和以前一字不差),证据面板据此写得出「本次运行目录:关」
- snapshot 模式只定读法(kind=eval_catalog_snapshot 的工件,{sources: {源 id: {tables: {表: {version, notes}}}}}),内容由第 2 波的题集包写;快照不在了照实报错(CatalogSnapshotMissing),不悄悄退回当前目录
- 新增 tests/test_catalog_view.py:四种视图在四处给的是同一份、缺省等于当前目录、运行从 options 读且冻结 meta 记下、lint(消费模块不许直接 read_catalog / read_entry / 查 CatalogNote);去掉 _catalog_of 里的视图回跑两项失败
2026-10-16 10:28:01 +08:00
ThearchyHelios 845809486a 驳回不再记成失败(后端):人的驳回收成 cancelled + end_reason=rejected,消费方都读这一个字段
为什么:动作确认、工具 / 代码审批、审批驳回即终止、填写 / 编辑被驳回、双盲裁决的驳回以前抛普通 NodeError,运行记成 failed。
通知推「实例失败」,定时推「运行失败」,发布单把驳回过的试运行说成「试运行失败了」并挡住发布,记录页还给「继续运行」
(接着跑只会读到同一个驳回答复、再停一次)。人的决定被说成故障,每个消费方各写一份映射,已经漏了四处(05-已知遗留 (a)、总方案 X9)。

做了什么:
- engine/context 新增 NodeRejected(NodeError),带 node_id、kind、decision(reject / expired_reject)、actor、note;六处驳回改抛它,
  原话一字不变。待办超时驳回的答复带 expired,Decision.expired 据此记成 expired_reject。
- runner._drive 在通用 except 之前单独接住:status=cancelled、end_reason=rejected、error 写原话、error_node 写那一步,
  run.cancelled 带 reason/node_id/label/kind/decision/actor/note;关审批、回收沙箱、关流、封存走取消的那一套。手动停止
  (执行中取消、放弃断点上的运行)写 end_reason=stopped,run.cancelled 带 reason=stopped。
- 继续运行:被驳回的 409 code=run_rejected「被驳回的运行不能接着跑:改好内容后重新发起」;存量 failed+驳回(失败那一步上
  最近一条处理完的待办是驳回,和实例页同一份判断 resolve.legacy_rejected)也拒。RunOut 带 end_reason。
- apps/resolve.instance_state:cancelled+rejected 和存量 failed+驳回说同一句(「「付款」未确认(被驳回)」「「x」被驳回」)。
- notify_run:状态那一句取自 instance_state,被驳回的走「实例结束」、ok=False,走到「已退回」成果的不再写「已完成」;
  手动停止照旧不推。顺带修 04-对外集成与触达 B1:发起人是认证身份(run.origin.actor_verified)时 users 传 [started_by],
  个人渠道订阅了实例完成 / 失败终于收得到;自报署名不算。
- 定时 webhook:cancelled+rejected 按「运行结束」推「未出具(被驳回)」、写驳回的那一步,不写备注和原话、不画成功色;
  手动停止仍不推。
- 发布单:试运行被驳回不算 plan.trial_failed,记 warning plan.trial_rejected「最近一次试运行在「x」被驳回、没有走完」(带白话)。
- (h) 重启时排队中的运行改成 failed 时补发 run.failed(同一句原因),时间线里有了失败原因这一行;不写结束时间、不封存,同以前。
- (f) tasks.tick / actions.tick 交回这一拍处理过的 id 列表(调度循环只记条数),测试改成只断言自己造的待办;先加了预置
  一条无关到期待办的 fixture,旧的按条数断言当场失败(3 == 1),改完通过。

已封存的老运行不改写。测试:新增 test_run_rejected(五种关卡参数化、超时驳回、继续运行拒绝含存量、手动停止、补发 run.failed、
instance_state 新老形状同一句)、test_notify_run(驳回走实例结束、手动停止不推、已退回不写已完成、个人渠道 B1 两条);
webhook、发布单、动作确认、双盲、审批的老断言跟上。去掉 runner 里接 NodeRejected 的分支回跑,新测试 8 条失败。
2026-10-16 10:14:27 +08:00
ThearchyHelios 7739340607 普通用户建应用集成收尾:
- 问数据和个人应用的图放行绑定只读查询等合规工具的调用工具节点,只读和遮罩在这条路径上生效;被治理成写入的工具在只读运行里直接拒;知识库范围在工具层固定(图级默认值、参数名纠错、Agent 都改不了)
- 个人应用的刷新和个人蓝图的试运行只有拥有者能读:运行、事件、证据、工件、文件、运行与审批列表、对比、可验证报告、质询、假设分析、提取工作流一并收紧;个人蓝图的试运行不论谁发起都按个人应用约束
- 共享蓝图只给拥有者、对应应用的管理者、管理员,看不到的一律 404;审核副本归审核人;管理员看别人的个人应用只有元数据、可以归档
- 记录表跨版本汇总(只读挂载旧版本、按当前列 UNION ALL、各版本各自固定高水位,缺的版本照实写进印章);停用前发起的运行可以继续写旧版本;更正沿用原提交人、更正人记审计
- 约定收口:待办种类 review、事件 file.extracted、表单错误 file、模板定义带示例与预置;前端类型一次补齐、去掉临时的局部类型;审核待办卡不再给打不开的链接;读附件事件有中文说法;检查脚本补断言
2026-10-15 13:55:19 +08:00
ThearchyHelios 941723b17c 端到端验收修复与指标出具:
- 身份与工具:登录页建第一个管理员(认人接口的 401 说明能不能建);搭建者有只读的人和组名录,选人给候选并核对名字;授权三行带标题;端点请求头名校验(空名、非法名存不进、发不出时说清原因);写入能力在工具页试运行一律演练、不发送,HTTP 状态照实说成败;写记忆不算只读、查询能力只能选同一端点上的只读能力;审批与遮罩的说法和实际规则一致;开登录时不要署名、导入和发布记登录的人;使用者不显示推演等只给搭建者的操作,403 按是否开登录说话;登录页只认一次人
- 流程与实例:图里用到的岗位(派给谁、超时升级给谁、发送确认派给谁)全部派生、没绑人的发布时报错;写外部系统的参数按能力声明的类型收拢后再算哈希和发送,收不成就不发;发起人的实例页只看这一步的状态,处理人才给待办卡;会签进度带登录核实和时间,升级后照实写规则;实例状态写流程的结论(已退回、未确认、等待核实写入结果),核实生效后不再挂失败原因;流程应用首页不派生最近封存运行的结果块,看板没数据时是安静的空态;样例⑩的金额带类型和下限、待办标题带金额,表单上下限前后端一起生效
- 看板与证据:定时任务模板加 ISO 周、上一 ISO 周、本月、上月;出处链穿过原样解析查询结果的整形节点,追不到原始数据的照实说停在哪里,样例⑨改用 cell();应用首页和版本历史都能做成应用;指标块单位不重复;版本列表带退役说明;定时任务页头跟着刷新;删视图要确认、新视图能起名;编辑器属性用中文名、术语带解释
- 指标出具(用户 2026-10-15 拍板):契约只写「指标来自」、上游没有报告撰写节点、成果里没有模型写的话时按指标出具——必需指标齐、指标输入追到原始数据、成果字段里的数回指得上才完整出具;受管级要求每个指标静态地追到原始数据(governed.metric_untraced);出具横幅和应用印章写明按指标
- 受管工作流降为已发布只给管理员(Q8 顺延,服务端同样拦);工作流接口带当前发布版的级别,画布上改过之后也认得出受管工作流
2026-10-15 07:15:37 +08:00
ThearchyHelios d67349562a 应用平台集成(后端应用与流程):身份断言 sub 为发起人、确认人进 act;应用助手对话(取或建、带历史)与助手润色只能改呈现;按新口径补算(只对无副作用的视图、加锁、同一输入只补一次);钩子按名字关联、入站按发布版声明认;应用详情只对 manage 给草稿与岗位绑定;删工作流的阻挡算上已退役版本;待办恢复失败退回待处理;调用工具节点作证核对快照归属;子图固定版本须已发布且未退役(发起与发布检查都查);视图里端点快照按能力的遮罩字段遮(含统一复核后的修复) 2026-10-14 21:55:51 +08:00
ThearchyHelios acdc9677a8 应用平台 BE-2 流程:待办服务(派给谁、any/all/quorum 汇总、条件 UPDATE 抢恢复、提醒升级超时驳回、审批接口改为垫片、存量补建),人工节点新配置与校验,写外部系统的动作流程(生效记录先写后发、确认卡哈希、结果未知挂起与核实、永不自动重发),重放豁免与升级安全测试,正式运行不许带图接着跑、接着跑不覆盖发起人(F2),运行来源,通知发送核心与渠道,入站签名钩子,process.* 检查,报销审批模板,假外部系统(含统一复核后的修复) 2026-10-14 20:05:23 +08:00
ThearchyHelios 00bd51b9a5 应用平台 BE-3 应用:「已发布且未退役」的唯一判定与版本退役,正式运行接受固定的已发布版本并核对 expect_hash(F1),口径升版只看已发布版本(F3),有应用固定或生效记录时拒绝删除工作流(F4);表达式的根名字与禁止算术参数,表单子集校验与输入节点字段转换,应用对象、发布、版本、授权、页面解析与视图模型(三种来源、缓存、印章、证据指针、遮罩)、入口发起实例、应用操作、app.* 检查、确定性派生、账本只读接口、指标证据链接口(含统一复核后的修复) 2026-10-14 20:05:23 +08:00
ThearchyHelios 5dba66c2b6 应用平台 BE-1 治理底座:身份三种模式(off 默认 / local / oidc)、会话与 CSRF、中间件写入身份、角色与组与审计、按权限矩阵挂路由级依赖、运行与工件的读权限和 user 角色遮罩、reset-admin 找回;能力登记(同步、治理与乐观锁、发起快照、发布版本、作证判定)、登记的端点与平台执行器(分环境、演练、幂等头、身份断言、端点快照作证)、密钥加密与掩码、MCP 的 SSRF 与认证头、写入能力只进调用工具节点、正式运行里只读免审批的能力直接执行、四条 action.* 发布检查(含统一复核后的修复) 2026-10-14 20:05:23 +08:00
ThearchyHelios ce71f7d5c1 期 5 验收修复 N1 第 2 轮:取自遮罩格的指标只按确定取自遮罩列的认、转抄上游口径卡的也认;显示文字核对不误判正常写法,总览有一项无法核对就不给绿色的通过
复核第 1 轮(abb2707)找到四处:写作目录按证据面板宁可多遮的那一套认指标出处,SQL 认不准时没碰号码列的销售额也成了
「已遮罩」,正式运行引用它就判失败(相对基线回退);「紧挨着负号」连破折号「——」「—」和汉字「一」都算,「运费统一10元」
「合计——7,036.50元」原样导出变红(相对基线回退);紧挨着改字只堵住了数字、负号、小数点,接「万」、画删除线、从右往左
书写的字母、l / I / O 照样全绿,根子在总览把无法核对的项也给绿色;第二张口径卡转抄遮罩指标(nodes.<卡>.metrics[0].value、
vars.<assign_to>.metrics[0].value、nodes.<卡>.text)时号码照样进写作目录、文档证据目录和可验证报告。

取自遮罩格的指标只按确定的那一套认(写作目录、报告自查、可验证报告同一个判断)
- 写作目录(api/evidence.py 的 withheld_metrics)认指标出处时遮罩列只认确定取自遮罩列的(_InputMasks 的 sure:读得出来的
  快照按单元格引用自查的 _cited_masks,读不出来的按数据源现在设的列名),和 [[v:…]] 引用同一格的自查同一个口径:WITH 写了
  列名表、VALUES 当表、子查询里有没起别名的号码表达式、只装着分档标签的列,指标照常显示、照常引用;改了名字、套了函数的
  号码列照旧「已遮罩」。证据面板、推演照旧宁可多遮。报告自查按目录里记的 masked 认,跟着同一个判断
- 可验证报告认老文档(证据目录里还写着原值,整份只给哈希)按同一个判断:api/bundle.py 先按证据面板的那一套认好每份查询
  快照里确定取自遮罩列的列和这次运行查到过的遮罩值(certain_masks),交给 build_bundle(sure_columns、sure_secrets),
  _Store.metric_word 的 sure 按它们认。认口径卡能不能随文件附带照旧宁可多遮(卡上记着输入的值),SQL 认不准的卡同基线只给 id

转抄上游口径卡的指标按上游那个指标认
- recompute.card_input 认出输入转抄了哪张卡的哪几个指标:nodes.<卡>.metrics[i].value、vars.<卡的 assign_to>.metrics[i]、
  整个指标、输入、渲染算转抄那个指标;整张卡、清单文字、整份指标列表、下标不是字面量的同整份查询结果(值里有遮罩值才不给,
  数指标个数的照给);只取名称、单位、口径名的不算
- 证据面板 / 写作目录(_InputMasks._copied)、可验证报告(_Store._copied_kind)都用同一个 _metric_masks / metric_word 往上追,
  上游取自遮罩格,转抄的也不给;追了 CARD_DEPTH(8)张还没追到头(循环里抄自己上一轮的产出)的宁可多遮
- 写作目录读快照前把运行状态里每张口径卡的输入一并读好,不在事件循环里读盘

显示文字核对(verifier.js)
- 判不通过只剩紧挨着的数字、小数点,和真当负号用的三个(半角连字符减号、数学减号、全角减号)紧挨着数字开头、前面又不是
  字母数字时(「SKU-12」「A-3」的连字符是编号的一部分)。破折号(——、—、–、―,小号的、竖排的)、波浪线、汉字「一」、
  假名长音、制表符横线这些易混字当普通的字。连字符、不断行连字符、上下标负号、粗减号这类平时不当负号用的短横紧挨着数字
  开头判无法核对(以前判不通过;基线也是无法核对)
- 判无法核对(带原因):后面接着量级单位(万、亿、千、百、兆、萬、億、%、‰、‱、k、K、M、B 及全角,中间隔着空格也算;
  核对过的字以「万」「亿」结尾再接一个也算),核对过的字画着删除线或紧挨着画了删除线的字,紧挨着长得像数字的字母和数字
  符号(l、I、O、上下标数字、圈码、罗马数字、汉字〇),从右往左书写的字母和方向控制字符(照旧)
- 总览(overallOf):一项无法核对都没有、每份报告都核对上才是绿色的「全部通过」;有无法核对的写中性的「可核对的项全部通过,
  另有 N 项无法核对」,下面醒目地写明这个文件判断不了真伪,要在系统中核对封存(运行 ID、GET /api/runs/<id>/verify、定时任务
  通知里的清单哈希前 12 位)。以前无法核对的项照样给绿色,伪造者只要让读者看到的那个数落进无法核对就行
- 交出 layoutOf 给 node 里逐条核排法;开发数据 39 份报告 1591 处、验收数据 58 份报告 1695 处引用按新规则扫过,没有一处判
  不通过或无法核对

测试
- 新增 tests/test_report_masked_metric_sure.py(四种 SQL 认不准的销售额、分档标签照给照引、确定取自号码列的照旧遮、老文档
  照常附带、写作目录和可验证报告逐个指标同一个判断)、tests/test_report_masked_card_copies.py(三种转抄写法在写作目录、文档、
  自查、证据面板、可验证报告里都不给号码,没有报告的运行、老文档,转抄普通指标照给,认转抄的各种写法,追不到头的宁可多遮)、
  tests/test_bundle_prose_export.py(「统一」「——」「—」「SKU-」「共…家」「–」真跑报告撰写节点、从接口导出,全部通过、总览绿)
- test_bundle_shown:负号只认三个、前面是汉字的照判不通过;像负号的短横、量级单位、删除线、长得像数字的字母各自判无法核对、
  总览中性;正常写法原样导出全绿;总览只在一项无法核对都没有时是绿的
- 样本页新增 prose(写作者自己写在引用旁边的普通文字,原样导出全绿);check-bundle 补数学减号、紧挨着汉字加负号判不通过,
  接万(隔空格)、接亿元、接 %、接 k、订单号接万、画删除线另写一个数、前面加小写 L、后面接大写 O、长得像负号的连字符判无法
  核对,各自点名原因、总览中性并写明去系统里核对;有无法核对项的样本页(base、legacy、continued、missing-card、masked-card、
  masked-source、nan)总览改断言中性,从右往左书写的字母那条不再断言总览是绿的
- 每处改动都做了「去掉实现、测试变红」:写作目录不用 sure、老文档不用 sure、sure 列不用、转抄认不出(三处各自)、追不到头
  按核对不了、负号的字母数字例外、负号恢复成 \p{Pd} 全部、数学减号全角减号不算、量级单位、隔空格、以万结尾、删除线、像数字
  的字母、像负号的短横、总览有无法核对照样绿
2026-10-12 19:09:50 +08:00
ThearchyHelios abb2707904 期 5 验收修复 N1:取自遮罩格的口径卡指标不进报告文档和可验证报告;显示文字核对堵住紧挨着改字的绕过
验收第二轮发现:口径卡 phone_raw = cell(q_cust, 0, 'phone') 被 [[m:phone_raw]] 引用时自查不拦,号码写进正文;写作目录、
文档的证据目录收着上游口径卡的全部指标和原值;可验证报告里口径卡的节点完成事件(预览逐条写着「指标(名称)= 值」)一律
附规范串,没有报告的运行也照样带出号码;另外只改紧挨着被引用数字的普通正文(前面补一位数、加负号、后面接「0 万」)、重算
全部哈希,读者看到的是另一个数,核对页只标无法核对,总览仍是绿的。

报告撰写节点
- 写作之前按证据面板指标步骤的那一套认出值取自遮罩格的指标(api/evidence.py 的 withheld_metrics → _metric_masks,
  recompute.withheld_value):cell() 取的遮罩格、改了名字的遮罩列、按位置取的一格一行、整份结果里带着遮罩值的。写作
  目录、存进文档的证据目录里这些指标的值、渲染、标签都写「已遮罩」并记 masked(evidence.withhold_metrics,没被引用的
  也一样),写作目录另说不要引用
- 只按出处认(by_value=False):认不出出处、值碰巧等于遮罩列某一格的普通指标(写死的目标 20000)不算,不然它在报告里
  成了「已遮罩」、引用它的正式运行判失败、写作者也改不掉(自查第 3 轮去掉按值认是同一个道理);证据面板展示上照旧多遮
- 引用这类指标照样解析、正文写「已遮罩」(换算不再算),自查记 masked_cell(evidence.masked_metrics),和引用遮罩格同一
  套处置:交回重写,仍不改按「违规处置」办;缺口、日志、判失败的下一步说「指标」
- 写成「已遮罩」的别名记进节点产出(masked_metrics),出口契约复核按同一份重建目录(report_catalog),正文里的「已遮罩」
  对得上,不说文档被改过;写作时按上游状态建目录,循环里重跑不沿用上一次记下的那份
- 证据面板点开这一处:值、渲染都是「已遮罩」,按文档写的比渲染,不报渲染不一致

可验证报告(bundle/build.py、verifier.js)
- 口径卡的节点完成事件:卡只给 id(有输入取自遮罩列,或者有指标的值不给)的只给哈希,和报告引不引用无关;没有台账的
  老运行按预览里的 artifact、节点产出工件认,都读不出来的按运行里有没有带遮罩的快照
- 卡只给 id 的判断另按指标认(_Store.metric_word,和写作目录同一个判断):整行、下标不是字面量的一格、整份结果转成的
  文字,以前只认字面量下标的一格,卡原样嵌了进去
- 证据目录里还写着这类原值的老文档(升级前生成的,或者生成之后数据源才设的遮罩)整份只给哈希、不附带它引用的工件,
  报告节点的完成事件也只给哈希;快照读不出来时按它的数据源现在设的遮罩认,卡读不出来时按这次运行查到过的遮罩值认。
  核对页照实说明原因(中性色),这份报告算未能核对,总览不是绿的;文档里写成「已遮罩」的指标所在的卡一律只给 id
- 被引用的数字紧挨着(中间没有空白、标点)数字、负号、小数点判不通过,点名「正文里紧挨着这个数的字改变了读者看到的
  数」;排在看不见、改变方向的字前面判,同一句里再放一个这种字也降不成无法核对。服务端组装的文档两个数之间补了空格,
  原样导出照旧全绿(开发数据里 39 份报告、505 处引用,没有一处紧挨着)

测试
- 新增 tests/test_report_masked_metrics.py(目录、自查、重写、标注 / 失败、出口契约、证据面板、循环)、
  tests/test_bundle_masked_metrics.py(没有报告的运行、新文档、老文档连同核对页、普通卡、和写作目录同一个判断、手搭的
  几条分支);样本页 masked-card 改成现在的报告撰写节点写的文档,新增 masked-old-doc;check-bundle 补数字前加数字、加
  负号、后面接「0 万」三种篡改,各自变红、点名,原有的紧挨着补位、连字符两条改成不通过,报告文档标成遮罩那条改成总览
  不是绿的
- test_report_masked_values 的 [[m:phone_raw]] 改成按出处拦、写死的热线号码照常引用;几处按旧的报告正文(「13位」
  「11位」「26位」)找片段的测试改成按引用找(test_evidence_metric_masks、test_evidence_masked_aliases、
  test_whatif_masked_values),断言照新的正文写
2026-10-12 17:23:27 +08:00
ThearchyHelios 652576bab3 验收第二轮的两处说法:审批质询整理回答那一步超时时,不再建议「缩小问题范围」(问题再小也要整理),改说调大时长上限或换用更快的核查模型,查询中途触顶的那句另补上可调大上限;反方判「被推翻」却没给理由时一句话说清「但没有给出理由」,不再先说「理由没有引用到任何查询结果」再接「反方没有给出理由」,固定的那句补上句号 2026-10-12 15:55:19 +08:00
ThearchyHelios 7cec3079f0 合入期 5 验收修复 M2:证据面板与推演按 SQL 认出改了名字的遮罩列;推演里取自遮罩格的指标不露原值;报告自查拦下引用遮罩列的格子(按列名对整个数据源生效,和证据面板同一个口径);结论句裁判的摘录按快照逐份遮确定取自遮罩列的列;片段接口在没开结论句裁判时照实说;遮罩分析按快照工件 id 缓存 2026-10-12 14:17:45 +08:00
ThearchyHelios 1cfa5a524d M2 复核第 4 轮:报告自查认遮罩列和证据面板同一个口径,哪张表(含视图)里叫这个名字的列都算;结论句裁判的摘录按快照逐份遮确定取自遮罩列的列;没起别名、盖住全部结果列的表达式算确定取自遮罩列,值照收
第 3 轮的「遮罩表」推断和退回的裁判摘录各开了一个口子,收值时又把按位置认得出的列丢了:

- 报告自查去掉「遮罩表」推断(_mask_owners)。数据源的 mask_columns 只认列名、不支持写成「表.列」,按列名在整个数据源上
  生效:引用的那一格按 SQL 认出取自任何一张表里叫这个名字的列就记 masked_cell,视图(customer_spend.name)、没有第二个
  遮罩列的表(employees.name)、门店名(stores.name)都算,和证据面板的遮法一致。以前经视图引用客户姓名、引用店员姓名,
  正式运行照常成功,姓名写进了报告。SQL 血缘只用来点名出处,和排除值确实取自普通表别的列的(SELECT region AS phone
  FROM stores);视图、表结构里没有的表、写了别的库名的表、没标 is_view 的老表结构、没有冻结表结构的快照看不出列的出处,
  照算(_frozen_schema 只把 is_view 明写为 false 的表当普通表)。违规说明照实写取自哪张表的哪一列:「列「store」按 SQL
  认出取自 stores.name,数据源对列名「name」设置了遮罩(按列名对所有表生效)」
- 交给结论句裁判的摘录按快照逐份遮(api/evidence.py 的 _judge_loader):每份快照里确定取自遮罩列的列(_mask_sets 的
  sure)并进交给 judge.prepare 的那一份快照的 mask_columns(工件本身不动),裁判照它自己的办法遮掉,judge.py 不用改。
  c.phone AS tel 的号码不再进摘录;不按数据源名合并(另一份快照里同名、取自门店热线的列照给),不用宁可多遮的 hidden
  (整份遮的快照、只数个数的列照给)。按需裁判和报告撰写节点里当场裁判同一个函数。摘录变了的句子复用键随之变,此前
  判过的会重判一次
- 收「这次运行查到过的遮罩值」:_sure 以前凡是要遮的列盖住了全部结果列、名字又对不上,就当认不准、整份算,
  SELECT UPPER(phone) FROM customers 一个值都收不到,证据面板之后照原样显示 SQL 里写着的号码。现在结果后面垫一列再认一遍
  (_whole),垫的那一列也算进来的才是整份算的;按位置认得出的照收,整份遮的照旧只遮显示、不收值

测试:test_report_masked_owners.py 改名 test_report_masked_any_table.py 并按新口径改写(经视图改名 / 不改名、店员、门店名、
客户名真跑判失败且成果里没有值;* 展开、WITH、UNION、视图里改成遮罩列名字的列、没有表结构、没标 is_view、表结构里没有的
表、写了库名的表);新增 test_judge_excerpt_masks.py(按需、当场两条路的摘录都不给改了名字的号码列,同名的门店热线照给;
没起别名的 UPPER(phone) 摘录也不给);test_evidence_mask_memo.py 加没起别名的表达式照收值;违规说明的断言跟着改
2026-10-12 13:50:31 +08:00
ThearchyHelios 89a4bc8cfc 合入期 5 验收修复 M1:核查 agent 正文里的依据标记不再断句——只装着依据的括号(含【】[]〔〕[])连同里面的停顿标点一起去掉,去掉之后按真正挨上的两个字补空格,两个数不再并成一个;核查 agent 的提示词改成依据写在句末;收尾超时照实记 timeout_s;没给理由时照实说没有给出理由 2026-10-12 13:12:20 +08:00
ThearchyHelios 65d6a1e63c M2 复核第 3 轮:报告自查只查引用、别的表里同名的列不算;裁判摘录退回只按数据源设的遮罩列;收遮罩值只收确定取自遮罩列的值;遮罩分析和收到的值按快照工件记在进程里
第 2 轮的按值自查和面板那套宁可多遮的认法带进了误判,正式运行因此失败,代价远大于收益,这一轮回到规格原意:

- 报告自查去掉按值在正文里找遮罩值(engine/evidence.py 的 masked_values、report.py 的收值):两个字的姓名会撞上「销售
  高峰」这类常用词,同名列的值会撞上门店名;照抄成正文的号码本来就会被「数字没有出处」拦下。时间线日志 report_masked_cell
  不再带 written,decode.ts、terms.ts、证据审计的 CSV 回到「引用了遮罩列」一种说法,check-decode 同步
- 自查认引用时从 _mask_sets 的 sure 起,再按 SQL 血缘(sqlglot.lineage,按快照里冻结的表结构)核一遍值取自哪张表的哪一列
  (api/evidence.py 的 _cited_masks / _owned_columns):遮罩列不带表名,同名的列在几张表里都有、其中有的表另有别的遮罩列时
  (customers 有 name 和 phone,stores 只有 name),只认那几张表,门店名不算客户姓名;值根本不取自遮罩列的(region AS
  phone)也不算。认不出出处的照算,没探查过表结构的照列名认。违规说明点名它取自的那一列遮罩列
- 交给裁判的摘录退回 M2 之前的做法,只按数据源设的列名遮(删掉 _judge_masks):并进面板那套宁可多遮的列后,整份遮的快照、
  只数个数的列从摘录里消失,被引用的金额、人数裁判看不到。按需裁判和节点里当场裁判都改回
- 证据面板、推演收「这次运行查到过的遮罩值」只收 sure 的列(_snapshot_secrets,同 masking 收值的口径):SQL 认不准、整份
  遮的快照只遮显示,里面的区域名、金额不再收成遮罩值
- 按快照工件 id 在进程里记下遮罩分析和收到的值(_SnapshotMemo,键里带数据源现在设的遮罩、在不在、类型,设置改了不拿旧的;
  只记读得出来的快照,存取加锁,分析结果冻结、交出去的值另抄一份):之后点开片段、推演都不再把全部快照读盘、复验、分析,
  只读这一步要显示的那一份。30 份 500 行的快照,第一次约 0.7 秒,之后不到 1 毫秒

测试:新增 test_report_masked_owners.py(高峰、门店名写成文字照常出具,照抄号码由数字核对拦下,引用门店名不算、引用客户名算,
同一条查询里两张表的同名列、只设 name、* 展开、WITH、UNION、没有表结构、表值函数)、test_evidence_mask_memo.py(整份遮
的快照不收值、只读一次只分析一次、设置改了重新收、推演共用、再点开片段只读本步快照);裁判摘录改测被引用的金额和计数
都在摘录里;按值自查的用例改成不按值认的回归
2026-10-12 12:33:32 +08:00
ThearchyHelios ce4be9cc8f M2 复核第 2 轮:报告自查按值认出正文里写着的遮罩值,只数个数的列不再判违规;遮没遮核对不了的输入写「无法核对」;整句面板挂的依据按证据图的标签写
- 自查按值认(engine/evidence.py 的 masked_values,report.py 的 _MaskedCells):写作目录里确定取自遮罩列的值出现在正文里——照抄成文字(姓名这类文字列数字核对管不到)、写成数字(换了写法的长号码组装时被拆成几个数字片段,按整句认)、经指标带出来([[m:phone_raw]] 的值就是号码)——记一条 masked_cell(by_value),不带值,交回写作者时附上值换成「已遮罩」的那一句。只收确定取自遮罩列的列、不收分档标签;运行输入里写着的值是发起运行的人自己填的,不算。只读数据源现在设了遮罩的快照,认引用时读过的不再读,没设遮罩的运行一份都不读
- 时间线:日志 report_masked_cell 多带 written(其中按值认出的几处),标题分「引用了…格子」「写进了…值」「引用、写进了…」;缺口、日志按两种分别说;违规清单、CSV 的叫法改成「写进了遮罩列的值」
- 只数个数的列(COUNT(*) FILTER (WHERE phone IS NULL)、SUM(phone IS NULL)、AVG(phone IS NOT NULL)、SUM(amount) FILTER (WHERE phone IS NOT NULL)):认遮罩列的那一套把 FILTER 条件、汇总里的真假条件当成值的来历,数据质量报告引用「手机号缺失的人数」在正式运行里判失败。自查认之前先改写(_counted:FILTER 同 WHERE 去掉,汇总里的真假条件改成 CASE WHEN … THEN 1 ELSE 0 END),不判违规;证据面板、推演照可验证报告的认法多遮(只影响展示),根上的改法在 masking 里
- 证据面板:遮没遮核对不了的输入(快照、取值链上的产出读不出来),值和代入式里写「无法核对」,不写「已遮罩」,数据源可能根本没设遮罩
- 整句面板「挂的依据」不再直接读文档目录里的「名称 = 渲染值」:按证据图的标签写(服务端已把用到遮罩列的指标值换成「已遮罩」),证据图没回来时指标只写名字
- on_demand:当场裁判过、文档里却压根没有这句的判定时,不说「原因见判定」
- 测试:合并查询自查的用例补上拼接、upper、WITH;推演对整行、下标不是字面量的一格说「用到了遮罩列」(顺着取值链认出来的,不是按值猜的);计数指标 cnt 的原值照给;SQL 认不准、只数个数的反例
2026-10-12 10:48:19 +08:00
ThearchyHelios 25011d074b M2 复核第 1 轮:合并查询按合并 SQL 认遮罩列,报告自查只拦确定取自遮罩列的格子,推演和证据面板对指标原值同一个判断,面板认法和可验证报告同一个函数
接手被限流打断的第 1 轮修复,补完后提交。处理的复核发现:
- 合并查询:合并 SQL 里对遮罩列改名、套函数、拼接、汇总、经 WITH 转一道得来的结果列,以前没有逐格来历就认不出,片段接口、勾行接口原样给出号码、报告自查也漏判。现在 _merge_mask_sets 把各输入要遮的列名当作遮罩列、按 SQLite 读法认合并 SQL;输入快照读不出来又可能带着遮罩的,它的列全部当遮罩列
- 报告自查:_mask_sets 分出 hidden(面板、推演按它遮,宁可多遮)和 sure(自查按它判违规)。SQL 认不准、整份算进来的列,只装着分档标签的列不再判 masked_cell,正式运行不会因为和遮罩列无关的金额判失败;sure 按数据源自己的方言认
- 判失败的下一步:违规里有引用遮罩列的,只说去改写作指令,不建议换模型、不建议先改成标注
- 时间线、缺口不再说「这些值会随报告出具」「出具已按缺口降档」:探索运行、没设出具契约的成果不出具,只说已记为缺口
- 只读引用到的快照:报告节点组装好一稿之后只认正文引用到的那几份查询快照,认过的不再读
- 审计表、导出的 CSV 给 masked_cell 单列一行
- 推演和证据面板:指标原值给不给是同一个判断(recompute.withheld_value),输入分类同一套(input_origin 的 wide:整行、下标不是字面量的、整份结果都认)。数行数的 len(vars.res.rows) 两边都照给,str(vars.res.rows)、整行、数据整形取出来的号码两边都不给;遮没遮核对不了的写「无法核对」,不写「已遮罩」
- 认不出出处的输入按值认:值就是这次运行查到过的遮罩值的不给、不推演
- 证据图 evidence[].label 用到遮罩列的指标写「名称 = 已遮罩」
- 证据面板认遮罩列调可验证报告的那个函数(bundle/build.py 的 _derived_columns),再按数据源方言认一遍,可验证报告整份遮的面板不会照给
- 查询步骤的 SQL、合并 SQL、SQL 检查片段、合并警告里写着的遮罩值展示时换成「已遮罩」,底下写明只是展示时换的
- 交给裁判的摘录:按需裁判、节点内当场裁判都把 SQL 里改了名字的遮罩列并进数据源名下(_judge_masks)
- on_demand:当场裁判过、这句没判成(触顶、出错、被截断)时照实说没判成
2026-10-12 10:17:40 +08:00
ThearchyHelios c97390511e M1 复核修正(第 1 轮):去掉只装着依据的括号不再把两边的数字粘成另一个数;括号去掉之后外面照一个依据再收拾;换行不算句末;核查 agent 的理由按渲染后的正文判有没有给;反方审稿收尾超时按时长上限说
渲染(evidence._see_cuts):
- 括号是两边唯一的分隔、去掉之后数字字母会粘成一串的(「[[v:Q1.r0.销售额]]([[see:Q1]])2 月」渲染成「4,919.52 月」,片段
  本身没错、月份不算裸数字,哪项核对都查不出来),括号换成一个空格;半角逗号夹着依据、去掉后一个之后同理(「212,5」→
  「212, 5」)。切口可以带替换串,连成一片的按并集换
- 只装着依据的括号去掉之后,整对括号当一个依据看外面:括号前悬着的停顿标点(「华南居首,([[see:Q1]])。」)、到句末只隔着
  的空白、夹在两个分隔标点中间的后一个,和不带括号的写法收拾得一样
- 依据后面到句末标点只隔着空白的,空白一起去掉(「华南居首 [[see:Q1]] 。」不再剩「华南居首 。」)
- 换行不再算句末:列表项末尾的「;」、软换行处连着两个分句的「,」是写作者自己的,依据挂在它后面正合规矩,照旧留着(上一轮
  把它们删了)
- 没有依据的文档渲染、内容哈希不变(随机两万份和基线逐份比过);流式分块输出仍等于整篇渲染
- 依据写在「依据」「见」「取自」「来源」这类引出语后面时引出语悬空,渲染时去不掉(引出语在依据前面,流式渲染得攒住每个可能
  是引出语的字):测试里改成严格的 xfail 写明已知限制,不再把验收里的缺陷钉成预期;核查 agent 遇到这种写法记一条日志

核查 agent(verify_agent):
- 有没有给理由按渲染出来的正文判:只写了依据([[see:Q1]]、「([[see:Q1]])」)显示出来是空的,占位词后面挂着依据的显示出来
  只剩占位词,都算没给。以前按原文判,标记里的字也算,理由文档照样交回:审批卡画出一片空白,判「被推翻」的因为引用到了 Q1
  被当成有出处、出具降档;封存之后的质询回答只剩系统说明却记成回答成功
- 「无」「暂无」「none」只在理由是另一个结果的说明时(双盲复算、反方审稿)算没给;结构里只有理由一项(审批质询)时理由就是
  回答,「无。」是一个回答,不再报「模型没有给出回答内容」让提问人重问、再花一次钱
- 收尾超时的原话收成常量 SETTLE_TIMEOUT,调用方据此换成自己的说法

反方审稿:交结论那次超过时长上限的句子,理由写「反查未完成:已达上限(每句时长上限 N 秒)」(收紧过的说运行时长上限将至),
和时间线上的 challenge_limit 一致,不再把核查 agent 的内部原话摆上审批卡。

测试:补节点级用例钉住收尾超时之后两个节点对外的说法(challenge_limit 的 limit、claims_skipped、cause,双盲复算的无法复算
原因和 dual_check_limit,收紧过的两种);理由只有依据、占位词挂依据、「无。」作为回答的各层用例。每处改动都做过去掉实现、
测试变红的验证。
2026-10-07 01:19:26 +08:00
ThearchyHelios 169c500526 K3b 复核修正(第 1 轮):数值格按位置认、(表达式).* 带外括号也整份遮、同名 CTE 按作用域认、认不全的写法名单合一、整份遮不再把每一格收成遮罩值、证据摘录分段遮、反方最后一道关只看正文
- 同名列(回退):column_types 按列名记,同名的几列只留得下后一列的类型,文字的备注被当成数值列,号码不再按值换、
  原样交给模型并落库。现在只在列名唯一时按列类型认数值列(_typed_numbers),同名的按格子本身认:整数、小数才算数,
  写成字符串的数按文字照换、记进 masked_cells;列名唯一、列类型是 number 的 NUMERIC / DECIMAL 字符串照原样
- (表达式).* 外面套了括号、起了别名(((p.card).*)、(p.card).* AS x,Postgres 实测照样展开):以前只遮第一列。选出项
  先去掉外括号、别名再认(_unwrapped),(s).*、(s.*) AS x 照旧当 s.*
- WITH 里和数据源里的表同名的一段(WITH sales AS (SELECT * FROM sales WHERE …)):不带 RECURSIVE 的一段看不见它自己和
  排在后面的段(_scoped_cte),同名的按数据源里的表认;以前认成它自己,* 展开什么都不带出,整行引用的号码原样交出
- 认不全的写法:和可验证报告合成一份名单(masking 的 OPAQUE_SQL、INDIRECT_FUNCTIONS、INDIRECT_NAMES,bundle 引用后另加
  系统 schema),补上 MySQL 会执行的注释、U&"…"、pg_stat_get_activity、逻辑解码、MySQL 的 column_statistics /
  general_log / PROCESSLIST / performance_schema、MariaDB 的 column_stats、Oracle 的直方图 / *_tab_columns / V$SQL 一族;
  去掉注释再认一遍(函数名和括号之间夹注释);去掉 DuckDB、SQL Server 的函数(query 误伤同名 CTE、字符串);没有 * 的
  一支选出项和结果列数对不上的、Postgres 解析不了的整份遮
- 整份遮只收确定取自遮罩列的那几列的值(_derived_masks 的 collect,报告快照收值同一规则):以前每一格都收,区域名、
  列名在同一次核查里处处被换掉,封存的 SQL 也被改写。快照另记 masked_whole,格子引用照实说写法认不全
- 证据摘录分段遮(inquiry.ExcerptMask,judge.prepare 的 mask 取代 kinds):SQL 照封存 SQL 的办法换,范围阈值、LIMIT 照
  原样,和遮罩列做等值、IN、LIKE 比较的字面量不论收没收到都换;格子逐格遮,数值格照原样;质询把被问的那句(连同表格)
  当作已经摆在眼前,摘录、查询工具、回答里都不换
- 反方审稿:理由文档的目录列名照快照换(同质询的 _renamed_entry),agent 自己 SQL 里照原样留着的阈值、行数在正文里
  不换(_shown),最后一道关只看正文和原文,不再把 agent 起的列名、阈值当成露了遮罩值整句改判

补测试:同名列一文一数、DECIMAL 字符串(含端到端引用取值),外括号 × 复合类型 / 记录函数 / 匿名 ROW × 最外层 /
UNION 前后支 / 和 * 写在一起,同名 CTE 四种写法和 RECURSIVE 对照,十七种认不全的写法和三种不该误伤的写法,整份遮后的
收值与封存 SQL,Postgres 解析不了的整行引用,摘录的阈值、数值格、IN / NOT IN 字面量、被问那句(反方、质询各端到端),
反方别名里的阈值(经受住、被推翻各一条)、找某个值的别名、正文里的阈值。逐项去掉实现,对应的测试都变红。
改了文件所有权以外的 bundle/build.py:_OPAQUE_SQL、_INDIRECT_READS 改为引用 masking 的同一份名单(另加系统 schema,
覆盖面不小于原来)。
2026-10-07 01:09:36 +08:00
ThearchyHelios fa50456e0f M2 报告自查认得出引用了取自遮罩列的格子:写作者写了 [[v:Qn.rX.列]]、而这一列是遮罩列或按 SQL 认出取自遮罩列(c.phone AS tel)时,自查记一条 masked_cell,像没有出处的数字一样交回写作者重写;仍不改的按「违规处置」办——失败的判失败,标注的照常产出,时间线另起一行 report_masked_cell,节点产出写一条 issuance_gaps,出口照单收下、降档
验收里写作要求点名引用 tel,自查判 ok、violations 为空,号码随报告和可验证报告出去,一步提示都没有。写作目录不遮罩是已知设计,所以拦在自查:
- compose_doc 新增 masked({查询快照工件: 取自遮罩列的结果列}),engine/evidence.py 的 masked_cells 按它查,和句中的 [[see:]]、缺失断言一样只在组装时查;整表展开出来的格也算
- 报告节点按证据面板同一个函数(api/evidence.py 的 _snapshot_masks)算目录里每份快照的遮罩列,交给 compose_doc
- 违规、日志、出具缺口只点名引用和列名,不写那一格的值:它们会进 report.checked、事件和可验证报告
- review.py 的码表登记 report_masked_cell(degraded)
2026-10-07 00:19:21 +08:00
ThearchyHelios 735dc32052 核查 agent 的依据照写作者的规矩挂在句末;收尾超时照实记时长上限;占位词不当理由
- 交结果的提示(VERIFY_FINAL_PROMPT,双盲复算、反方审稿、审批质询共用)以前写「提到某次查询时写 [[see:Q1]]」,模型就
  把它写进括号、句中;反方审稿的系统提示自己也把它写在括号里(「([[v:Qn.rX.列名]] 或 [[see:Qn]])」)。改成和写作者
  一致:依据挂在句末(「……结果一致。[[see:Q3]]」),说明它渲染后不显示,不要放进括号、句中或「依据」「见」「取自」
  后面。已经写进括号的由上一个提交的渲染收拾干净
- 模型自己停了查询、收尾那次调用超过时长上限时,limit 以前是 None:质询的报错写着「回答超过时长上限(180 秒)」,
  answer.limit 却是 null,界面少了「已达时长上限」;双盲复算、反方审稿把它当成别的原因,审批卡上照出内部原话。
  现在记 timeout_s;循环里先撞上的上限照旧优先(同金额被拒的收尾),结构化输出、退回普通调用两条路都一样
- 交回的理由是空的、只有标点,或者只是一个占位词(验收里盲算交了字面的「placeholder」,审批卡照着显示):当没给
  理由,不组装理由文档。反方审稿的记录里理由为空,审批卡照旧写「反方没有给出理由」;质询照「模型没有给出回答内容」
  报错;双盲复算的对照工件里说明为空

测试:交给模型的提示(含三处调用方的系统提示、任务)里不再示范括号里的依据;写进括号的依据在理由文档里渲染干净、
仍挂在句子上;审批质询的回答、反方审稿的理由、双盲复算的说明端到端都没有空括号;收尾超时 limit 为 timeout_s(核查
agent 和质询接口、事件里都是);占位词的理由三处都不当理由。
2026-10-07 00:05:17 +08:00
ThearchyHelios 6ba4d9f099 K3b:反方审稿查数据源类型时直接用整份报告用到的源,不再逐件取工件
claim_sources 已经认全了查询的、合并查询合并的、指标输入取自的、表和字段的数据源,_kinds 不必再把目录里每件工件都取一遍。
2026-10-06 23:23:40 +08:00
ThearchyHelios 888f746d31 K3b:交给核查 agent 的证据摘录先遮;反方审稿的理由过最后一道关
- 摘录由 judge.prepare 生成,以前 _hidden 只按列名遮:报告取数把遮罩列改了名字选出来(buyer_phone AS 手机号),质询、反方 agent 的任务里四个号码都在,模型照抄进理由。现在 prepare 收 kinds({数据源名: 类型},只有核查 agent 的摘录给),查询摘录、指标输入按 masking._derived_hidden 认出改了名字的遮罩列(和工具边界同一套规则,表结构按快照冻结的那份),认的过程出了意外整份的列都不给;结论句裁判自己的摘录不给 kinds,和以前逐字相同、复用键不变。反方审稿、审批质询再把摘录里别的列、SQL 里写着的遮罩值按值换成「已遮罩」(换了写法的号码也认),值同交给查询工具的那一份(报告快照里的,反方去掉这句结论原文里写着的);解析 SQL、按值换放进线程。
- 反方审稿的理由按值遮之后,正文、理由文档(正文、原文、目录里的列名)里还认得出遮罩值的,这句记为无法判断、理由换成系统说明、不存理由文档(_kept,同审批质询的 _leaked),遮不住的「被推翻」不再让出具降档。
补测试:质询、反方各一条端到端(任务里的摘录没有号码,手机号列不给,SQL 里的号码、备注里换了写法的号码换掉),反方理由里 agent 自己查出的号码换着写法也换掉,最后一道关;分别去掉按 SQL 认列、两处按值换、agent 查到的值、最后一道关,对应的测试都变红。
2026-10-06 23:11:22 +08:00
ThearchyHelios 58a6759fe2 合入期 5 前置修复 K3:核查 agent 的查询结果在工具边界先遮——取自遮罩列的结果列(别名、函数、子查询、WITH、*、UNION、表值函数、整行引用都认)在交给模型的内容和落库的快照里都换成「已遮罩」,模型看不到遮罩值;按值遮降为第二道防线;换了写法的长号码(空格、连字符、点、括号、全角、国家码)在回答、理由、工件和最后一道关里都认得出;认遮罩列的共用部分挪到 engine/masking.py 2026-10-06 22:19:42 +08:00
ThearchyHelios 6a7e51b81b K3 复核修正(第 2 轮):* 把遮罩列带进别的列名时按展开落到的位置遮;函数参数里的 JSON 箭头认作遮罩列;agent 查到的遮罩值和报告里的分开记,上限收满也不丢
- * 改名(复核点名的 blocker):WITH t(a, b, …) AS (SELECT * …) 这类列名表、UNION 后一支的 *、FROM 子查询或 LATERAL
  的列名表、标量子查询和 ARRAY 子查询里的 *,以前 * 本身不算取自哪一列,原值原样交给核查 agent、落库,第二道防线也
  认不出。现在 * 带出遮罩列的值时,它展开落到的那几列都算(masking._star_places):只展开一个来源、每一列都数得清的
  按位置只遮取自遮罩列的那几列——子查询按选出项数,数据源里的表按查询当时冻结的表结构数(_schema_columns;工具边界
  用数据源的 schema_cache,事后按快照的 schema_artifact,是同一份),几张列一样的月度表 SELECT * 连起来汇总,金额照常;
  数不清的(没有表结构、列数对不上、几个来源)整段遮。表结构只用来数位置,不用来断定表里没有遮罩列。标量子查询里的 *
  按 _value_columns 展开(展开数据源里的表一律算),EXISTS 里的 * 不算;递归的 CTE 不会绕不出来;带 EXCLUDE / REPLACE
  的 * 不按位置数
- JSON 箭头:函数参数里没写表名的 col->'path' 被 sqlglot 解析成以 col 为参数的 lambda,_value_columns 把 lambda 的
  参数名照列名收。SQLite json_each(profile->'$.tels')、Postgres jsonb_array_elements_text(profile->'tels')、MySQL
  JSON_UNQUOTE(profile->'$.tel') 这几种拆开、取出来的值在工具边界就遮
- 上限:run_verify_agent 以前先把调用方交进来的报告快照值并进 seen,收满 SECRETS_MAX 之后 agent 查到的值并不进去,后面
  的查询、回答时的第二道防线都认不出。现在两份分开记(masking.SeenValues:given / found),查询工具按值遮、交回调用方
  (VerifyOutcome.masked_values)时合起来,agent 查到的在前;反方审稿理由的按值遮也改成 agent 查到的先收
- 已知局限写进 inquiry 模块说明:表结构过期、列数没变却换了次序或改了名时可能数错;EXCEPT 后一支照 UNION 算(多遮)

测试:新增 test_masking_star_renames(真跑 SQLite 的各种 * 改名、按表结构数位置、表结构对不上、快照记着的表结构),
工具边界(交给模型的、落库的、通用工件接口)和审批质询端到端的 * 改名、JSON 箭头、上限收满;每处实现都做过去掉之后
测试变红的验证。
2026-10-06 20:29:58 +08:00
ThearchyHelios 6d9d31395b 出口契约认藏在代码块里的正文改为逐行标出代码:写明是文字的块不再因为一行 SQL、一行像 YAML 的键或 + / - 要点就整块当代码,公式、JSON、目录树这类像数据的行照代码看
上一轮先判整块:只要有一行以 SQL / Python 关键字或小写键开头,或者每行都以 + - 空格 @ 开头,整块就当代码,只认去掉
注释、字符串之后还成句的行。写成 markdown、text 的块里,+ / - 开头的正负面要点、正文后面另嵌的 ~~~sql、缩进的 select、
一行「- note: …」都让不带句读的结论认不出,藏进代码块反倒完整出具。反过来,写明是文字的块不去字符串,六字连写、结论词
又对公式、JSON、目录树、print、流程箭头这些行生效:结论句为 0 的报告贴一段带「占比」的公式就降档,缺口还说正文写在
代码块里。

- 逐行标出代码(_code_lines),不再整块判:块里再嵌、收得了的代码块(~~~sql、````,开了没收的那一行照普通的行看);
  SQL / Python 语句从开头那一行到这一段结束,续行也算;整段是 YAML 的(第一行是小写键,其余是键或缩进的行;写明是
  文字的块里另要有值为空的键,一行一个「- note: 一句话」是正文里的标签);像数据、公式的行(前面有字的等号和箭头、
  画目录树的制表符、JSON 的开头和「"键":」、函数调用、文件名、逗号隔开的表头)。代码的行只认去掉注释、字符串之后
  成句的,别的行照旧:写明是文字的按 _written,信息串为空的按 _prose_line
- diff:写明是文字的块里 + - 后面空一格的是列表项,紧挨着字写的才是 diff;信息串为空的块里第一个改动行是 + 的是
  正负面要点(diff 先列删掉的行),有 @@ 块头的照旧是 diff
- 一段到空行为止,只写着块注释的行不算空行;写明是文字的块里顶格的标题另起一段。块注释、三个引号括起的多行字符串
  涂成空格,行数不变
- SQL 关键字只认全小写、全大写,def / class 要写成定义的样子:「From 数据看……」「class A 门店……」是正文。代码的行里
  -- // 紧挨着前面的字也算注释
- 取舍写进 _fenced_body 的说明:一块里只要有一行在写正文就记缺口,拿不准的行宁可当代码,藏起来的正文总有别的行认得出

测试:复核里的写法逐一补上:+ / - 要点(markdown、text、信息串为空)、嵌 ~~~sql、缩进的 select、小写键的标签,正式
运行降档并只带这条缺口;带「占比」「增长率」的公式、写成 text 的 JSON 和目录树,在结论句为 0 的报告里照常完整出具。
单元参数另补 print、groupby、流程箭头、文件名、逗号表头、JSON 数组、多行 SQL 夹块注释、docstring、嵌套的 YAML、开了
没收和收尾更长的嵌套围栏、行首箭头、首字母大写的关键字、先列 + 的带 @@ diff,并钉住两条取舍:写成 text 又空一格的
diff 照列表看;信息串为空、整段都是小写键的照 YAML 看。长行耗时用例扩到七种长行、两种多行围栏、两种信息串。逐一去掉
36 处规则(diff、嵌套围栏、语句、关键字大小写、def / class、YAML、分段、像数据的七种写法、注释、块注释、多行字符串、
正文判法、结构片段、挂钩),各有用例变红。
2026-10-06 19:51:29 +08:00