Commit Graph

579 Commits

Author SHA1 Message Date
ThearchyHelios b7da8a9af9 出口契约认藏在代码块里的正文:只挂 [[see:]] 依据的、不带句读的列表项也认,代码里的注释和字符串不算句子,英文句子改成线性扫描
上一轮只看片段里的引用和带全角句读的行,几种写法仍会让正文藏在代码块里照样完整出具,另有几种代码被当成正文:
- [[see:]] 记在句子的 see 上、不在片段里:只用 see 挂依据的列表现在也认
- 中文报告的列表项常不带句读:以 - * + • 或「1.」「1)」开头、后面有两个以上汉字的行算成句;信息串为空、看得出是
  SQL / Python 的块里(有 SELECT、FROM、def、import 这类开头的行),以 * + - 开头的是运算的续行,不当列表项
- 信息串写成 plaintext、txt、plain 的和 text 一样算写文字的代码块
- 判句之前去掉字符串字面量(可带 f、r、b、u、N 前缀;英文的 it's 这类不动)和行尾注释(-- // #),# 开头的行、
  跨行的 /* … */ 块注释都不算:SQL 行尾的中文注释、print 里带全角标点的字符串不再让 0 句的报告降档
- 认英文句子的正则对很长、不带句末标点的一行是平方级回溯(出口复核在事件循环上同步执行),改成按词串逐个数单词,
  耗时随行长线性增长

check-decode 补一项:从 io.py 读出 GAP_FENCED_BODY,钉住它归「校验不完整」、不进核查的两组。补参数化用例和端到端
用例:只挂 see 的列表、不带句读的列表、plaintext 包住正文的正式运行降档并带这条缺口;带行尾注释的 SQL 照常完整出具;
四万多字的长行一秒内判完。
2026-10-06 18:03:12 +08:00
ThearchyHelios 04606cfe94 双盲复算写业务日期:真写按写入时的版本重读上传表格导入时认作日期的列,几个列表块写同一张表时每一块都记为日期才算
record_choices 的表结构是写入时现读的,候选里的 upload_dates 却是发起审批时读的:审批停着时上传源换了版本、把日期列
改成文字,两份拼在一起会写出一条哪个版本单独看都不会给出的人工确认。现在候选里带 upload_dates 的,真写在读数据源的
同一个会话里按当前版本重读、覆盖候选里的那份,和表结构出自同一个版本,版本没变时预告和真写仍一字不差;候选里没有
这一项的是升级前发起的审批,卡上预告的是不写,照旧不认。

recipe_dates 以前只要有一个列表块把某列记为日期就算日期列:两个工作表的列表块写同一张表、同一列一个日期一个文字时,
表形状一样(都存成 TEXT),配方校验不拦,按文字导入的那一块存的是单元格原文。现在写这一列的块都记为日期才算。

补测试:审批停着时修改配方把支付日期改成文字,裁决后照实说按文字保存、不写;没有 upload_dates 的老候选不重读、照旧
不写,带着的按写入时的版本重读后照写;两块类型不一致的列不算日期。
2026-10-06 18:03:12 +08:00
ThearchyHelios c8800d75e3 模型接入报错的说法与修复候选收尾(前端):助手栏照实显示存着的模型设定、能重新选「跟随默认」清掉失效的接入;检查器模型一栏说清留空用哪个接入;停用接入的后果写全;问题只认自己那一处的修复
- 助手栏「助手使用的模型」:读 GET /api/copilot/model 的 error,照实写在下拉下面,齿轮上加红点(收着时也看得出来)。存着的
  设定下拉里列不出来(点名了接入,或模型不在可选列表中)时列成一项并选中,不再停在「跟随默认」——以前看着一切正常、
  发出一句话才报错,而且再选「跟随默认」不算改动、清不掉存着的接入,报错又指到改不了它的设置页。这时发出的请求不带
  模型,服务端按存着的整组取,和下拉上显示的一致;
- 检查器模型一栏:节点(没写就是全图默认)点名了接入时,空选项和说明写「使用接入「X」的默认模型」;X 不存在、已停用时
  空选项说状态、不写说明,不再和检查器顶上接入那条问题矛盾(同后端去掉「没有指定模型」那一条);
- 设置页:接入编辑框「启用」旁、卡片上「已停用」的悬停说明写全停用的后果(指定它或它的模型的节点和设置运行时报错),
  不只说下拉里看不到;门控、裁判、核查三格存着的接入名恰好是某个启用接入的模型时,补一句在这一组哪一栏用它,和服务端
  保存时 422 的那句一致;
- 发布前检查:点名的模型接入只认节点、字段都对得上的修复,没有就是这一处没有修复,不再退到同节点、同编号别处的修复
  (同后端 annotate)——以前给不出修复的那一行下面会画着另一处的选项;
- 说法收进 terms.ts(PROVIDER_REF_TEXT、ASSISTANT_MODEL_TEXT),getModel / setModel 的返回补上类型(CopilotModel)。

检查:check-studio 加一节(检查器空选项与说明、助手栏的错误与重新选择、发出的请求不带模型、390 宽不多撑出横向溢出),
check-manage 加一节(停用后果、接入名恰好是模型名)并跟上 422 的新说法,check-publish 的伪造门禁照后端「没有修复时
不指到发布前检查」,加一节给不出修复的那一行不借同节点别处的修复。
2026-10-06 17:51:26 +08:00
ThearchyHelios 450758185d K3:核查 agent 看到的查询结果先遮、存下的快照也遮
双盲复算、反方审稿、审批质询共用的 engine/verify_agent.py 以前把查询结果原样交给模型(db_query 自己落快照,ToolMessage
里是原样的结果),遮罩全靠审批质询事后按值认,期 4 起六轮复核每轮都找到新的漏法;运行详情「工件」页签走的通用接口
GET /api/artifacts/{id} 还把核查的查询快照原样给出。根本的修法是不让模型看到遮罩值。

- tools/datasource:查询工具加遮罩开关 mask_results(缺省关,Agent 节点、调用工具节点照旧)。打开时数据源设了遮罩的,
  先按 engine/masking.py 的 mask_query_result 遮好再交给模型、再落快照:列名直接对上的、按 SQL 认出取自遮罩列的
  (别名、函数、子查询、WITH、SELECT *,复用 _derived_hidden;SQL 解析不了又写着遮罩列的,整份结果的列都遮)整列
  换成「已遮罩」、类型记成文本;同一份结果里别的格子整格是遮罩值的换掉,文字格里夹着的那一截换掉(换了写法的号码
  也认);列名不换。结果里一列都没取自遮罩列的(数值的汇总、只数个数、只在条件里用到)原样。快照里 mask_columns
  另记下查询时遮了的、改了名字的结果列(证据面板、质询工件据此画成遮罩列),masked_at_query 记下查询时就整列遮掉的列。
  这一次遮掉的值并进 ToolContext.extra[MASKED_VALUES_KEY],只在内存里。
- verify_agent:建查询工具时和 force_readonly 一样打开遮罩开关;格子引用指到查询时就遮了的列,照实说是遮罩列、不采信;
  遮掉的值经 VerifyOutcome.masked_values 交回调用方(不进 as_dict、不往外交);系统提示补一句「已遮罩」是什么。
- 审批质询:按值遮降为第二道防线。agent 自己的快照查询时就遮好了,收不到遮罩值,改由 masked_values 并进来(_plan 的
  seen),回答、查询清单照旧按值遮;「已遮罩」本身不收成遮罩值。事后取质询工件时内存里的值已经不在,SQL 检查结果的
  片段、说明照事件里记下的那条 SQL 换(_recorded_swaps:逐词对出回答时换掉的字面量和 LIKE 里换掉的那一截)。
- 双盲复算取盲算值、反方审稿的引用、证据面板、可验证报告(带遮罩的快照只给哈希,照旧)、推演、运行对比(不收核查的
  查询)都按快照里已经是「已遮罩」照常工作,文件不用改。

测试:核查 agent 选出遮罩列(直接、别名、函数、子查询、WITH、SELECT *)时交给假模型的 ToolMessage、落库的快照、通用
工件接口、tool.end 预览、收尾交给模型的查询结果里都是「已遮罩」;没碰遮罩列的聚合查询原样、照常取值;文字格夹着的
号码换掉那一截;引用遮罩格子不采信;SQL 认不出时整份遮;开关缺省关、没设遮罩的源快照一字不差;列名不改;审批质询、
反方审稿、双盲复算各补一条端到端;质询工件的检查片段照记下的 SQL 换。逐处去掉实现(开关、并进内存的值、照记下的
SQL 换、跳过「已遮罩」、遮罩格子的说法、记下改了名字的列、文字格按子串换、不改列名)对应的用例都会变红。
2026-10-06 17:36:55 +08:00
ThearchyHelios 4673e51bb8 模型接入报错的说法与修复候选收尾(后端):修复规划只否决这次改动造成的失败、问题只认自己那一处的修复、没有修复时提示不指到发布前检查、落到演示类接入照实说;各处说法对齐
修复规划(autofix.provider_choice,原 _plan_provider 的主体,provider_refs 也用它判断给不给得出修复):
- 改动波及的一处,要是点名的接入没变、而那个接入本来就不存在或已停用,它落不稳不是这次改动造成的,不再拿它否决候选。
  以前两处互相牵连(节点点名的接入写错、模型取全图默认里停用接入的模型)时两条问题都没有修复,规划器自己卡住;
- 点名的模型接入一处一条修复,annotate 只认(编号、节点、字段)都对得上的那一条,认不出就是没有,不再退到同节点、
  同编号别处的修复(以前全图默认那条的 fix 会指到某个节点的修复上);
- 一个候选都落不稳时,问题提示不再写「或在「发布前检查」中改用其他接入」,只说去设置里核对、启用;
- 修完以后落到演示类接入(kind 为 mock)时,「不指定接入」的说明写明「演示用,不调用真实模型」,和「改用演示接入」
  那一项一致——K1 要防的就是悄悄换成演示模型。

说法:
- 双盲复算、反方审稿的问题写「……的「盲算模型 · 接入」指定的模型接入」,不说「盲算模型指定的接入」(修复标题仍按角色说);
- 点名的接入名恰好是某个启用接入的模型时,运行时报错、画布问题、保存设置的 422、助手的报错都补一句这是哪个接入的
  模型、怎么用它(factory.model_note;设置页写那一组模型、接入两栏的叫法),免得照「启用它」去启用那条旧接入;
- 保存设置 422:下拉框选的写「指定的」,不写「填写的」;去掉和设置页空选项对不上的留空半句;助手那一项叫「助手使用的
  模型」;核查的格式检查写「留空表示跟随证据裁判模型」,同设置页;
- 「交给助手」后只剩接入问题时的说明:一句一个意思、不再带冒号(界面上前面已有「助手:」),「核对名称」有宾语,不说
  「节点」;有一条带修复时才指到各条问题的修复;
- 助手的模型取不到时,提示指到真正能改的地方:「编排」页助手栏的「助手使用的模型」(设置页没有这一项);
- 节点点名了接入、没写模型时校验说「将使用接入「X」的默认模型」(编号 model.unset);X 本身不存在或已停用时画布校验、
  发布一类都去掉这一条,只留接入那条,不再一条说照常执行、一条说会失败。
2026-10-06 17:29:18 +08:00
ThearchyHelios b8dfbdb335 对比入口的注释换个说法:「接着跑完」改成「接着运行完」,和界面文案的用词一致 2026-10-06 17:25:07 +08:00
ThearchyHelios 2a64099cb8 对比页没带运行时的说明把菜单项的两种叫法都写上:上一次没有成功时运行详情的菜单里写的是「与上一次成功的运行对比」,说明只写「与上一次对比」会让人找不到
check-compare 跟着核对两种叫法都在
2026-10-06 17:24:17 +08:00
ThearchyHelios 0f48bd17c4 运行详情「工件」页签:双盲复算对照、反方审稿记录、质询回答单独归到「核查记录」一组;窄屏上说明换到第二行,第几件和第几次尝试在截断范围外,悬停写全说明
分组:以前这三种都算进「证据快照」,那一组的说明却写着「查询、工具调用、知识检索的原始结果」。model.ts 加 artifactGroup(证据快照、核查记录、节点产出三组,按种类分),筛选多一档「核查记录」,件数、悬停说明跟着改:证据快照写上口径卡、报告文档和表结构,核查记录说清装的是什么、核查时执行的查询快照仍归在证据快照。别的种类的归类不变(isEvidence 改成只认证据快照这一组,认不出的种类照旧算证据快照)。口径卡里容器型变量输入的完整值(metric_input)以前露键名,补上名字「指标输入」和说明。

窄屏:以前一行里挤着种类、说明、校验值、大小和时刻,390px 时说明只剩三个字、序号也被截掉。行按列表这一栏的宽度排(容器查询,35rem 以下),窄时说明换到第二行占满整行,第一行留种类、校验值、大小和时刻(时刻和种类对齐);序号和第几次尝试从被截断的说明里挪出来;按钮的悬停写「打开某种工件:整句说明 #n」。容器设在列表上、不设在整个页签上,查看工件的弹窗(fixed 定位)不落进容器里。新文案放在 terms.ts 的 RUN_ARTIFACTS_TEXT。

新预览用例 ?syn=run-artifacts(虚构的门店周报,22 件,各种工件都有,含封存后追加的长问题质询回答)。check-verify:分组和 isEvidence 的口径、四档筛选的件数和悬停、点各档只剩该组;390px 深浅色下说明都在第二行、被截断的说明看得见至少 200px(以前 0 到 63px)、第几件整个落在行内、时刻在第一行和种类对齐、不横向溢出、对比度。check-inquiry:工件页签的读法改成按 data 属性,质询回答归在核查记录、悬停带整句说明
2026-10-06 17:15:52 +08:00
ThearchyHelios 02e9bcd908 可验证报告:理顺 _contained 的说明(派生来源的列外层只能按名字取,或者再用一个 * 展开,那个 * 照样要过这一关) 2026-10-06 17:15:13 +08:00
ThearchyHelios 50f16aece5 可验证报告接口测试:真跑一次,遮罩库上只汇总消费额的查询快照随文件附带
照证据测试的夹具真跑:会员库遮了 phone 列,另加一次只汇总消费额的查询(没碰号码列)。导出的页面里,数据源查询工具
真落的这份快照按原字节附带、和服务端的文件一致,它的查询事件带规范字符串,报告里引用它的数字和快照里那一格对得上;
选出了号码的那份照旧只给 id,页面里找不到号码。这条也守着「认得这份快照」那一关:查询工具以后往快照里加了键,这里
会先变红,得同时想清楚可验证报告该不该认它。在改动前的 build.py 上这条是红的。
2026-10-06 17:12:56 +08:00
ThearchyHelios 8ede152b6f 可验证报告:口径卡取的列是 SQL 里改了名字的遮罩列时,整张卡同样只给哈希
口径卡上记着每个输入的值和代入式,有输入取自遮罩列的整张卡只给哈希;但以前只按列名认:查询写 phone AS tel、经子查询
或 WITH 转一道、套个函数,卡上取 tel 那一格就是号码,卡照样原样嵌进要发给别人的文件。现在那几列算不算遮罩列另按 SQL
认改了名字的(_Store.hidden → _derived_columns,用 inquiry._derived_hidden,和质询、双盲复算同一个认法;SQL 按每种
读得通的读法各认一遍合起来,都读不通的写着遮罩列就整份的列都算,出错时整份的列都算)。同一条查询里没取自遮罩列的
列照常嵌入。新测试覆盖别名、子查询、WITH 加函数三种改名,以及没改名的列、认的过程出错;每处都做过去掉实现测试变红的
验证。
2026-10-06 17:11:41 +08:00
ThearchyHelios 02883dea7b 可验证报告:查询快照只给哈希时照实说明原因,样本页和浏览器检查补「遮罩源上的汇总查询可核对」
核对页对只给哈希的查询快照原来写「含遮罩列,未随文件附带」。现在没碰遮罩列的快照照常附带,只给哈希的是这条查询
用到或可能用到设了遮罩的列(条件里写到的、解析不了的也算),原文改成「这条查询用到或可能用到设了遮罩的列,快照未随
文件附带,只能核对到哈希」;口径卡的说法不变。核对逻辑不变,只是按工件种类挑说法(verifier.js 的 maskedQuery);
内联脚本的 CSP 哈希由 render_html 按脚本现算,不用另改。顺手改正 verifier.js 开头一句过时的注释:口径卡现在会因遮罩
只给哈希,导出时不会出现的是报告文档标着遮罩。

tests/bundle_samples.py 加样本页 masked-source:会员库设了号码遮罩(查询当时记下了、数据源现在也设着),报告引用会员
按地区汇总的查询和选出了号码的那份。check-bundle 断网打开它:汇总快照在 ③ 通过、引用它的两个数字在 ⑤ 通过、点开看得
到汇总的 SQL 和被引用的那一行;选出了号码的快照和引用它的数字无法核对并写明原因;总览「可核对的项全部通过」,页面里
找不到号码。另在这一页上篡改:汇总快照改一个字节 ③ 变红,改它的数字的引用值并重算全部哈希 ⑤ 变红。base 页上的原因
文案跟着改。test_bundle_build 守住样本页的形状。
2026-10-06 17:11:41 +08:00
ThearchyHelios 1136b2d09d K3:换了写法的号码在回答正文、质询工件的文字格和最后一道关里也认得出
验收准备时复现过:模型把号码写成 199-1474-2785、199 1474 2785、199.1474.2785、(199)1474-2785、+8619914742785、
全角数字,回答正文、事件、回答文档原样带着,最后一道关 _leaked 也拦不住,正式运行里永久封存。

engine/masking.py 的 _Secrets 新加 loose_spans:在正文里找允许夹分隔符的数字串(数字含全角,之间夹一到三个空格、
连字符、点、括号,含全角;开头可带 + 和括号),从每一截起往后拼,规范化(全角转半角、去分隔符、去 0086 / 86 国家码)
之后在「遮罩值的纯数字写法」集合里查——集合查找,不现编正则,两万个遮罩值照样不卡。拼出来的要是完整的号码:和别的
数字只隔着一个连字符或点连着的(13900000001-2、日期)、只有两截中间一个点的(小数)不算,同原来按词认的边界。
括号配成对:(199)1474-2785 整个换掉,「(电话 199-1474-2785)」两个括号留着。存的值本身带分隔符或国家码的,也按
纯数字写法认。

- scrub:正文(prose)缺省这样认,文字格(质询工件里夹着的、整格就是号码的)也认;SQL 里不认,199-1474-2785 在
  SQL 里是一个算式。
- protect:提问人换着写法写进问题的号码照样免遮;SQL 里照原样留着的阈值换了写法也只在正文里不遮。
- inquiry._leaked:组装之后还认得出换了写法的号码,整份不交。

反方审稿的理由正文目前没有按值遮这一步(理由里只引用反方自己的查询格子,查询结果在核查 agent 的工具边界先遮,见
下一个提交),这里不另加。补单元测试(18 种写法 × 三种存法、不误伤日期小数编号、文字格、最后一道关、提问人写的、
阈值、两万个值的耗时)和端到端测试(11 种写法写进回答都换成「已遮罩」,事件、回答文档里也没有;备注里夹着换了
写法的号码时回答整份不交、质询工件只换那一截)。去掉实现后这些用例都会变红。
2026-10-06 17:11:00 +08:00
ThearchyHelios fbcff1a585 测试里三行超过 120 字的断开写,和周围代码的行宽一致 2026-10-06 17:06:23 +08:00
ThearchyHelios faf21894a3 可验证报告:数据源设了遮罩时,能证明没碰遮罩列的查询快照照常随文件附带
以前只要查询当时记下了数据源的遮罩列,或者数据源现在设了遮罩,整份快照就只给哈希——哪怕这条查询只按地区汇总了
金额。数据源里只要有一列设了遮罩(客户手机号),报告里引用查询格子的数字就全成了「无法核对」,对外几乎没法核。

现在 build._untouched 全部满足才嵌入,任何一条不满足、判断过程出错都照旧只给哈希:
- 认得这份快照:数据源查询工具落的那种、哈希对得上;不是合并查询;没有不认识的键(以后核查 agent 在查询时就遮过、
  另记下遮了哪些列的快照,照现有规则只给哈希);
- 结果的列名对不上遮罩列;
- SQL 原文里没写到遮罩列,条件里、COUNT 里也不行:SQL 随快照一起附带,WHERE phone = '…' 的字面量就是遮罩值;
  MySQL 会执行的注释、Postgres 的 Unicode 转义名字这类语法树认不准的写法也不行;
- SQL 解析得了(不知道是哪种库,按每种支持的库的读法各解析一遍,读得通的每一种都要过关),没有认不出结果里哪一列
  取自哪一列的写法:列名表、整行引用、可能展开出遮罩列的 *(展开了数据源里的表,又不在只按名字往外传的派生来源、
  EXISTS / IN 子查询里);按 SQL 认遮罩列的那一套(inquiry._derived_hidden,双盲复算、质询同一个)也认不出哪个
  结果列取自遮罩列。

查询事件(tool.end)的规范字符串跟着快照走:快照随文件附带的,事件也带内容。新测试覆盖按地区汇总(查询时记下的、
数据源现在设的遮罩各一种)、WITH / 子查询 / LATERAL / EXISTS 里的 *、反引号,以及选出遮罩列、别名、子查询、CTE、
SELECT *、函数包着、只在条件里、整行引用、列名表、解析不了、改过的、不认识的键、合并查询、判断出错;每一条规则都做过
去掉实现测试变红的验证。
2026-10-06 17:04:45 +08:00
ThearchyHelios 7f7b690307 upload_dates 直接取现行配方(current_recipe_id):发布、启用旧版本时它和当前快照、表结构一起换,不必再经快照和最后一期导入去找;补测试:修改配方把支付日期改成文字后只剩下单日期,回滚到旧版本两列又都是日期 2026-10-06 17:04:42 +08:00
ThearchyHelios f1e6716d84 compose_doc 的说明对上 K5:doc["source"] 是调用方交来的原文(报告节点交来前去掉了整篇外层围栏,见 report._unfenced),不再说是模型的原文;只改说明,文档字段和内容哈希不变 2026-10-06 17:01:45 +08:00
ThearchyHelios 3e02446542 出口契约:报告的正文仍写在代码块里、一句结论都没切出来时记出具缺口,不再完整出具
K5 让报告节点去掉整篇外层的 markdown 围栏,规格有意不去的写法(围栏外有前言、只有开头没有收尾、信息串写成 text)仍会让整段正文落在代码块里:结论句 0 句,裁判不调用、反方审稿记 no_claims,没挂依据的、证据相矛盾的都数出 0 句,正式运行照样完整出具,读的人以为结论都核对过。

- nodes/io.py 的 _fenced_body:文档里一句结论都没有,却有信息串为空 / markdown / md / text 的代码块,里面是成句的文字(一行里有汉字又有中文句读,或五个以上英文单词以句末标点收尾)或者写着引用标记,就是正文被藏进了代码块。信息串为空的代码块里也常是 SQL:看内容,代码注释行(--、//、/* */)里的中文句子不算;sql、python、json 这类信息串不看
- _claims 在结论句策略为 judge 或 require_citation(报告节点的或出具契约自己写的)时,碰到这样的报告记一条缺口 GAP_FENCED_BODY「报告「X」的正文写在代码块里,结论句未能核对」,出具降档。缺口不带核查节点的前缀,时间线写在「校验不完整」下、出具横幅归「校验未全部完成」(run/decode.ts 的 splitIssuanceGaps,报告标题像核查节点名的也一样,界面不用改)
- 行为不变:有正常结论句的、代码块是代码的、结论句策略为 off 的

测试(新文件 test_contract_fenced_body):认得出的几种(前言、没收尾、text、无信息串、大写 MD、英文、只有列表但写着引用)与认不出的几种(sql、python、json、无信息串的 SQL、SQL 注释里的中文句子、数字表、外面有结论句、没有代码块);正式运行(裁判 + 反方审稿 + 出具契约)里前言、没收尾、text 三种降档并只带这一条缺口,以前是完整出具;正常报告、整篇外层围栏被去掉的、带 SQL 代码块的照常完整出具;require_citation 写在报告节点或契约上同样记,off 不管。去掉挂钩、去掉 0 句前提、去掉注释行排除、去掉信息串过滤、去掉引用信号、去掉句读要求各做过一次,测试变红。
2026-10-06 17:01:44 +08:00
ThearchyHelios 8c76f91ca9 K3:分档标签列在派生来源里被同名的遮罩列挡住时不再按标签认(J2 第 3 轮复验的缺口)
SQLite 让派生来源里同名的几列都留着,外层按名字取的是排在最前面的那一列。以前认标签列只看起了这个名字的选出项,
SELECT vip_level AS 档 FROM (SELECT *, CASE WHEN amount > 1000 THEN 1 ELSE 0 END AS vip_level FROM sales) t 交回的是
真实等级,却被当成只装着标签:等级 1、2、3 撞上 THEN 1 ELSE 0 不收进遮罩值,之后 WHERE vip_level = 1 在查询清单、
事件、质询工件里都照原样封存。

按复核建议保守处理(engine/masking.py):
- _block_label_of(单支、多支的第一支):第一个同名项前面有 * 带得出这一列(_star_label 不是 _ABSENT),或者有没起
  别名、去掉括号和 COLLATE 后同名的项((vip_level)、vip_level COLLATE NOCASE,SQLite 给子查询的结果列起名时去掉
  它们),都不算只装着标签;
- _star_label:没写表名的 * 展开了数据源里的表(或 VALUES、函数这类说不清出哪些列的来源)为 None——* 把表里的同名列
  照样带出来,不像直接写列名那样会报列名有歧义。

补单元和端到端测试:* 在前、t.* 在前、CTE 里 * 在前、JOIN 子查询 * 在前、(col) / ((col)) / COLLATE 在前、多支的
第一支,以及标签排在 * 前面、* 只带出派生来源里的标签时照旧算标签列。三条规则各自去掉,对应的用例都会变红。
另把「SQLite 里带小数的取模先取整」写进已知局限。
2026-10-06 17:00:48 +08:00
ThearchyHelios 0772b42bfd K3 第一步:把认遮罩列、遮罩值的共用部分原样挪到 engine/masking.py
按 SQL 认出取自遮罩列的结果列(_derived_hidden 一套,含分档标签列 _label_columns)、按值认遮罩值(_Secrets)、
快照遮好的样子(_masked_view),连同它们用到的写死值的算法,从 engine/inquiry.py 原样挪到新的 engine/masking.py,
一个字没改。接下来核查 agent 的查询工具要在交给模型之前先遮,工具这一层不该反过来依赖审批质询的模块。

inquiry 照旧按原名导出这些名字(nodes/dual_check 按 inquiry._derived_hidden 引用,测试按 inquiry 的名字引用),
行为不变:质询、双盲复算、反方审稿的相关测试全过。
2026-10-06 16:55:29 +08:00
ThearchyHelios 50f15b40c4 合入期 5 前置修复 K1:点名的模型接入不存在或已停用时照实报错,不再悄悄换成演示模型——各条用到模型的路径照实失败或记为核查不可用;保存设置时 422、画布校验给警告、发布与发布前检查挡住并给修复候选(不指定模型、改用启用的接入),只写模型而提供它的接入已停用也查得出 2026-10-06 16:55:27 +08:00
ThearchyHelios 2cf329648d 上传表格的日期列按导入时配方里的逻辑类型认:双盲复算选了时间列不同的一边时,上传源也能写业务日期
J1 给业务日期加了类型门槛(两边的时间列都在表结构里声明为日期 / 时间类型才写),而上传表格导入时日期一律按文字保存、表结构里总是 TEXT,上传源因此永远写不了业务日期。导入时配方里其实记着每一列的逻辑类型(导入确认时界面上标的「日期」):列表块里类型为 DATE 的列、交叉表的日期轴。查过别处:构建回执里只有存储类型(列表的 DATE 列和文字列一样记 TEXT、role 都是 text),简单导入的回执只有 INTEGER / REAL / TEXT,所以只按配方认。

- dual_check_facts.recipe_dates:一份配方里逻辑类型是日期的列({表: [列]}),表和列按 recipe_types.derive_tables 推,和建表同一份;日期轴写进它各分段的表,列表合计行另存的表不带日期列;类型按 _DATED_TYPE 认,只有时刻的不算
- dual_check_facts.upload_dates:只读,按数据源当前快照找配方(source_versions.snapshot_recipe_id,期 3 之前的快照取最后一期导入的),和 schema_cache 出自同一个版本;手工源、简单导入、快照或配方记录不在、配方解析不了一律给空
- 节点的 _source_info 带上 upload_dates,放进候选:审批卡的预告(preview / preview_joint)和裁决后的真写认的是发起审批时读的同一份,逐字一致;旧的 checkpoint 里没有这一项,照旧不写
- _dated_problem:上传源里按文字保存的列在 upload_dates 里的算日期列(按表结构认出的表名、列名去对,大小写不论);不在的、读不出配方的照现在的原因说「来自上传的表格,表格里的日期一律按文字保存」;声明成别的类型的、只有时刻的照旧

测试:derive 一级补导入认作日期的写、只有一边是日期 / 读不出配方 / 记在别的表上的照旧说按文字保存、手工源不认 upload_dates、按表结构的名字对、preview_joint 同样认;新文件 test_dual_check_upload 经真实导入接口建上传源(暂存 → 改配方 → 试运行 → 提交、简单导入),配方里两列都是日期的审核人选盲算后业务日期记「支付日期」(人工确认、备注写运行),支付日期是文字的选哪边都不写、预告和跳过原因一字不差,配方记录改坏了的照旧不写;去掉认日期、去掉候选里的 upload_dates、去掉日期轴各做过一次,测试变红。
2026-10-06 16:54:30 +08:00
ThearchyHelios 6514db6118 运行详情「更多」里的对比入口按默认基准是不是紧挨着的上一次定叫法:上一次失败、取消、还没结束时写「与上一次成功的运行对比」,和定时任务触发历史里的同名链接一字不差、点进去是同一个基准
拿到能对比的运行(已封存、属于已保存的工作流、不在运行中)就预先问一次 compare/base,按答复里的 previous 定叫法;按运行 id 加清单哈希记,失败后接着运行完重新封存了会再问。问到之前这一项只写「正在查找上一次成功的运行…」、不给点:菜单开着时不会先写一个叫法、问到了再换成另一个,也点不到说错了的入口。预先问出错、找不到默认基准都不提示,叫法退回「与上一次对比」,点了照常再问一次、照实说。点了总是再问一次、按那一次的答复跳:详情页可能开了很久,预先问到的基准不一定还是现在的。

类型 RunCompareBase 加 previous(老服务端不给,按 true 算,同 ScheduleFireCompare);两种叫法放在 RUN_COMPARE_TEXT(menu、menuLastOk),SCHEDULE_TEXT 的 compareLinkLastOk 和 compareLink 一样改成取它的 getter,过时的说明一并改掉。check-compare:夹具里本次的默认基准带 previous: false(和触发历史那一行同一个假定);上一次失败了、上一次就是成功的两种,运行详情和触发历史两个入口叫法一致、打开同一对运行;预先问的那一次压住时菜单项只写正在查找、问到后直接是最终叫法(记下菜单开着时出现过的每一种状态核对);预先问出错、找不到基准时不提示、点了照常再问;点了之后对比页不再自己找一遍
2026-10-06 16:52:30 +08:00
ThearchyHelios 5c6820f602 运行对比的默认基准(GET /api/runs/{id}/compare/base)带上 previous:基准是不是紧挨着的上一次,取 pick_base 现成算好的那个值,和定时任务自动对比记下的 previous 同一个语义
运行详情「更多」里的入口要按它定叫法:上一次失败、取消、还没结束时,基准是再往前成功的那一次,菜单得和触发历史一样写「与上一次成功的运行对比」,不能一律写「与上一次对比」。测试钉住:同一定时任务、退回同一工作流两种范围里,失败、取消、还没结束、没封存的都算隔着,别的运行类别、别的定时任务、比这次晚发起的不算;上一次失败时运行详情和触发历史拿到同一个基准、同一个 previous;中间那次删掉之后运行详情按现在的记录算成紧挨着的上一次
2026-10-06 16:42:29 +08:00
ThearchyHelios 0a690a599b 发布前修复点名的模型接入:按执行时的取法算修完落到哪里,只写了模型而接入已停用的也拦下
- 「不指定接入」以前只去掉接入、留下同一处写着的模型,复核又只查点名的接入:按模型名落回停用的接入,或者成员跟随节点、节点跟随全图默认,拿别家的接入去调这个模型。检查、发布都通过,正式运行在那一处照样失败
- 现在每个候选(不指定、改用某个启用的接入)先在副本上改,按执行器的取法(成员先看自己、再看节点、再看全图默认;节点看全图默认;都没写按模型名找;核查和裁判整组交给设置)算出改动波及的每一处用哪个接入、哪个模型。落到的接入不提供那一处的模型、按模型名落到停用接入或认不出的模型、落到内置演示模型的,连同一级写着的模型一并去掉再算,还落不稳的候选不给;候选说明写清执行时改用哪里、模型去没去掉、换上的接入实际用哪个模型
- 门禁把哪一级都没写接入、只写了模型而提供它的接入已停用的那一处也记成 provider.disabled(落在写模型的字段,原因和运行时同一句):复核拒得掉这类修复,检查器里只能选模型的节点发布前也查得出来;这一处配「不指定模型」或改用一个启用接入的修复
- 按模型名找接入、没写时退回哪个接入抽成 factory.provider_for_model、fallback_provider,运行时和门禁共用一份
- 问题面板、修复预览写得出「模型」「成员「x」的模型」;check-publish 的伪造门禁跟上新的候选,补只写模型的一段
- 测试:复核发现的几种形状逐一钉住(含发布前检查修好、保存、发布后正式运行成功的端到端一条);依赖旧做法的两条测试改成新的正确行为
2026-10-06 16:31:06 +08:00
ThearchyHelios 3f17fd3c71 合入期 5 前置修复 K4:运行把这一段执行的截止时刻交给节点(engine/deadline.py),反方审稿与双盲复算开始每一项前看剩余时间,按剩余时间收紧每项时长、给下游留出余量,来不及的记为未能反查 / 无法复算并写明「运行时限将至」,整次运行不再被时长上限中止;继续运行取 checkpoint 里的结果不重复反查 2026-10-06 16:26:45 +08:00
ThearchyHelios 4314a7fc44 合入期 5 前置修复 K2:核查与裁判模型的输出额度提到 16384,服务方嫌大时退回 4096(和助手共用 providers/budget.py);每次调用认截断,因推理占满额度没交出结果时照实说(verify_truncated、judge_truncated),不再说成盲算没查到、没按结构交结果 2026-10-06 16:26:37 +08:00
ThearchyHelios 54ab06d615 合入期 5 前置修复 K5:写作模型把整篇报告包在 markdown 代码块里时去掉外层围栏(重写的几轮同样处理),结论句照常可审、裁判和反方审稿有句子可看;别的语言的代码块、围栏外有文字、多个代码块、开闭不配对的原样不动 2026-10-06 16:26:37 +08:00
ThearchyHelios 0a875fd3af 合入期 4 整合 J4:质询回答工件在记录页有名字和说明;问答串在审批列表取失败时不再漏条目;运行对比的默认基准与定时任务自动对比用同一条规则(同一任务、同一工作流、比这次先发起的上一次成功并封存的运行),触发历史在基准不是紧挨着的上一次时写明「与上一次成功的运行对比」 2026-10-06 16:23:40 +08:00
ThearchyHelios ed888d59df 合入期 4 整合 J3:出具横幅按「核查未通过 / 核查未完成 / 校验未全部完成」分组;审批卡把运行类别交给反方审稿概况;证据接口的核查类型契约挪进 types.ts;反方审稿加单句模式、证据面板不再谈出具;证据接口带上句子的反方判断映射,正文给被审过的句子挂句末标记;质询图标平时对比度达标 2026-10-06 16:23:33 +08:00
ThearchyHelios 39068c9636 合入期 4 整合 J2:审批质询遮罩收口——阈值只在同一个数在别处确实被换掉时才遮、夹住某一个值的边界按条件与作用域认、质询工件里未遮罩文字格夹着的遮罩值按值换掉;算式边界、分档标签列、CAST 类型参数等变体照实处理,已知局限写进模块说明 2026-10-06 16:23:26 +08:00
ThearchyHelios 2da4d34fec 合入期 4 整合 J1:双盲复算写业务日期加类型门槛(两边的时间列都在表结构里声明为日期 / 时间类型才写,原因说清是哪一列);跟上 SQL 差异说明收尾的测试与预览夹具(time_range 改为兜底变体,新增 time_column);按位置取自另一份快照遮罩列的输入判为无法复算;可验证报告里被改过、解析不出的口径卡按内容认遮罩 2026-10-06 16:23:26 +08:00
ThearchyHelios 76b485a0ca 审批质询遮罩收口(第 3 轮复核):标签列按作用域认取自哪个来源,UNION 另一支、子查询里的遮罩列流进标签列照收;取模、整除、整数相除按算出来的值认;CAST 的类型参数不当边界换
复核留下的一条 major(第 2 轮只修了一部分)和两条次要问题:

- 分档标签列:以前按名字认,从「所有名字都只装着标签」起往下剔。遮罩列原样选出(SELECT vip_level)时名字就是它自己,
  永远剔不掉;结果的 UNION 按位置那一遍只往里加标签,另一支取的是遮罩列也不剔;派生来源里的 UNION 根本不按位置看。
  于是标签在前、遮罩列在后(不起别名、别名不同、外层再套子查询、CTE 里的标签再 UNION),遮罩列经按原名选出的子查询、
  以标签的别名传出,或者两个子查询 JOIN 起来同名,等级 1 撞上 THEN 1 都不收,WHERE vip_level = 1 在查询清单、事件、
  质询工件里照原样封存(对基线是回退)。现在 _label_columns 按作用域认每一项取自哪个来源:结果的这一列在 UNION 的
  每一支里都要是结果全为写死常量的 CASE / IIF,或者取自子查询、CTE、LATERAL 里只装着标签的一列(派生来源里的 UNION
  也按位置看每一支,写了列名表的按列名表);取自数据源里的表的一律不算,哪怕和标签同名。没写表名的列,派生来源出
  这一列的就是它(SQL 执行过,别的来源就没有同名的列,不然报列名有歧义),按这一列 USING、NATURAL 合成的除外;递归的
  CTE、UNION 里落在某一支 * 里的列认不准,照收(多遮)
- 取模、整除:_arithmetic、_worked_out 补上 %、MOD、DIV、//(往零取整,同 SQL)。顺带 / 照数据源的算法算:SQLite、
  Postgres 里两个写成整数的数相除只留整数部分(sqlglot 记在 typed 上),以前一律按小数算,age >= 25 AND age < 53 / 2
  在 SQLite 里夹住了 25 却认不出;写成小数的(53.0 / 2)、MySQL、DuckDB 照小数算
- CAST 的类型参数(第 2 轮引入,只多遮):_halves 只收往上走得到这一边的字面量(_lifts),CAST(30000 AS DECIMAL(10, 2))
  的 10、2 不再当成算出边界的数换掉,SQL 检查结果的片段里别处的 10、2 也不跟着换;_wraps 里 CAST 只算被转换的那个值,
  Oracle 的 CAST … DEFAULT 0 ON CONVERSION ERROR 里的 0 照原样
- 模块说明的已知局限照实更新:标签列认得出、认不准的几种写法;认得出的算式补上取模、整除和整数相除,认不出的补上
  位运算、POWER、CAST 改了值的

测试:单元补标签在前、遮罩列在后的八种写法(不起别名、别名不同、子查询里的 UNION、写了列名表的 CTE、CTE 里的标签再
UNION、经按原名选出的子查询传出、两个子查询 JOIN 同名、RIGHT JOIN USING 同名列),按作用域仍认得出的五种标签(外层连上
数据源里的表不写表名、按别的列 USING、两层子查询写了表名、UNION 两支、派生来源的 UNION 经 * 带出);取模、整除、整数
相除的六种写法和照小数算的三种;CAST 的类型参数和 DEFAULT。端到端补 UNION 标签在前的两种写法、取模和整数相除算出来的
边界。每处实现都逐个去掉验证过测试变红(十一个变异全红)
2026-10-06 16:01:28 +08:00
ThearchyHelios 384af7a5f2 界面跟上点名接入的新规矩:设置页标出已停用的接入、选不了别的停用接入,接入表变了画布重新校验,问题面板和修复预览不露键名
点名停用的接入以前照常调用,现在运行时照实报错,界面要提前说:
- 设置页门控、裁判、核查三格:存着的接入已停用和「不存在」一样在那一格下面写明后果;下拉里别的停用接入列着但选不了(只留已选着的那个);模型一格不再写「留空则使用」一个调不了的接入的默认模型。服务端按项拒收(422 带 field)时整句落到那一格下面、焦点移过去,保存条连同原因一起写,不再截掉「请在……中启用它」。
- 画布校验现在读接入表:编排页记下校验时的接入表签名(id、名称、启用与否),从设置里改完接入回来、或目录刷新后接入表变了,就重新校验一次,不再停着一句过时的「已停用」。
- 问题面板、修复预览里点名接入的字段写「模型接入」「成员「分析师」的模型接入」「工作流默认设置 · 模型」,不露 provider、model 键名;修复预览给列表项写名字。同一节点上自己的接入和成员的接入是两处问题,合并、找修复都连字段一起认,计数不再少算。

检查:check-publish 加点名接入的修复(一处一行、候选、预览、应用、390px 不溢出)、问题面板字段叫法、在设置里删了接入回到画布重新校验;check-manage 加设置页已停用接入的三格说明、选项、按项拒收;check-evidence 里全图默认的 model 改按「模型」核对(不借裁判叫法的原意不变)。
2026-10-06 15:14:40 +08:00
ThearchyHelios 3c51c40e9d 点名的模型接入:发布前检查给每一处配修复,提示指到界面上真能改的地方,复核发现的几处漏查、误查补齐
多数节点(模型调用、Agent、报告撰写、多 Agent 协作、条件分支、结构校验、知识检索)、协作成员和全图默认的接入在检查器里没有下拉框,以前点名的接入坏了只能去设置里建一个同名接入。现在门禁接受接入表(governance.lint_for_publish 的 providers,接口层读好传进来,同数据目录 SQL 检查器),点名接入的问题成了门禁的一部分,自动修复给每一处一条要人选的修复:改用一个启用的接入,或不指定接入;选的接入不提供这一处写着的模型时模型一并去掉。一处一条(修复 id 带字段,复核只认那个字段),协作节点自己的接入和成员的接入各修各的。校验和发布检查的提示按位置写:有下拉框的说「在节点中换一个接入」,别处说「在发布前检查中改用其他接入」;剩下的只有接入问题时交给助手的说明也改成指向这些修复。

复核发现一并修:核查节点自己的开关关着、结构校验不许重试时不再查(执行时根本不取模型,以前会挡住发布);报告没写裁判配置时查全图默认里的那一份(以前放行,正式运行却全部未裁判);接入名不是字符串时查助手模型的接口不再 500,设置接口对门控、助手两组的这类值回 422 带字段;助手、表格导入、数据目录起草用的模型点名的接入不存在或已停用时照实说是哪个接入、去哪里改(以前说「尚未配置完成」,停用接入的下一步丢了),删掉两处已走不到的分支。

测试补齐:结构校验让模型修复、知识检索让模型重排、按需裁判各路径的不存在与已停用,助手、定时正式运行、裁判的两种情况,反方审稿节点上点名的接入;修复的候选、应用、拒收和图级默认。
2026-10-06 15:14:26 +08:00
ThearchyHelios 5d4d1fdc15 审批质询遮罩收口(第 2 轮复核):算式边界、套了 CAST / 汇总的列、换个写法的数、两头不是整数、范围再排除一头都认得出;只装着标签的列才不收标签;截断片段整段去掉
复核留下的三条 major 和两条次要问题:

- 范围的一头是算式(age <= 25 + 0、buyer_phone BETWEEN 13900000000 + 1 AND 13900000009 - 8):夹住的值认得出,算式里的数
  却照原样留着,一算就是那个号码。现在几个写死的数的加减乘除按算出来的那一个值认(_lifts):夹住某一个值的边界、等值、
  IN 里的算式,里面的数都换;选出列里算出来的号码按值换;没夹住的算式阈值和写成一个数的阈值一样照原样,正文里复述它
  (连同算出来的值)不遮,算出来的值在别处换掉了也不留。原有两条用例(BETWEEN 13900000001 AND 13900000002 - 1、
  … AND 13900000001 + 0)的期望随之改成算式里的数都换(多换一个 1、0,号码照样不露)
- 几种找值写法以前认不出:被比较的一边套了 CAST、汇总(CAST(age AS INTEGER) = 25、HAVING MAX(age) BETWEEN 25 AND 25)
  改按它的值取自哪些列认(_columns_of);同一个数换个写法(25.0、025、2.5e1、013900000001)按规范写法和遮罩值比
  (_canonical);两头不是整数的范围(age > 24.5 AND age < 25.5、BETWEEN 25 AND 25.9)在整数上隔开紧挨着的两个整数;
  范围再用不等、NOT IN 去掉一头(buyer_phone >= 13900000001 AND buyer_phone <= 13900000002 AND buyer_phone <> 13900000002,
  以前号码被当成阈值、回答正文跟着不遮):等值一类只在同一个条件里和范围合起来看,整数上紧挨着的被排除的数越过去接着
  看(age <= 30 时 27 照样成立,不算夹住),跨条件配对仍只看范围比较,不等、IN 单拿出来不当「写出了这个值」
- 算式求值改成不递归(_constants):visits <= 1+1+…写到 1500 项时以前超过递归深度,整条 SQL 退回按词认,同一句里夹住的
  年龄照原样封存
- 分档标签不收进遮罩值的规则太宽(第 1 轮引入):改了名字的遮罩列(vip_level AS 等级)的 1、2、3 撞上同一条 SQL 里按
  条件数个数的 THEN 1 ELSE 0,1 就不算遮罩值,WHERE vip_level = 1 照原样封存。现在只跳过只装着标签的列的格子
  (_label_columns:选出项本身是结果全为常量的 CASE / IIF,或者经别名、子查询、CTE 原样传下去的,没起别名的按位置认;
  同一个名字在哪一处装着别的就照收)
- SQL 检查结果的说明:码值表里的码值和别的字一样按值换,说明照实写(多遮,不漏),补测试钉住
- 截断的片段停在带空格的遮罩文字值中间(地址):以前只去掉最后一段,前半截还留着;现在从没闭合的引号、注释起整个去掉
- 模块说明的已知局限照实更新:两个产品取舍保持;夹住的认法补上不等、NOT IN、两头不是整数和越过排除数的规则;新增一条
  列出认得出的找值写法和仍认不出的(值藏进和列一起算的算式、拼接、函数拼出来的值)

测试:单元补算式边界、套了 CAST / 汇总、换个写法、两头不是整数、排除一头、不该夹住的几种(范围加不等仍不只一个值、
IN 列表旁的阈值、选出列里的等值和 WHERE 的范围)、上千项连加、改了名字的遮罩列撞上标签、标签列经子查询 / CTE / 没起
别名、截断片段、码值表;端到端补长号码的五种写法(算出来的边界、算出来的等值、两头不是整数、范围再排除一头、补零)、
年龄的六种写法和改了名字的等级列。每处实现都逐个去掉验证过测试变红
2026-10-06 15:08:56 +08:00
ThearchyHelios 6a54ab06c2 J3 复核修正(第 1 轮):过期的证据图重取一次,只有反方标记的句子在面板里先写判断,没反查成的不说成反方模型的判断
证据图缓存会过期:正文有运行就取证据图,之后一直用缓存。审批卡里展开过报告的话,缓存的是运行停在审批上那一刻的图:报告
改写后(第二次审批、同一张卡换稿)新稿在那份图里找不到,一个标记都挂不上;审批之后才审完的反方审稿,成果区也看不到;证据页
挂上时只管「运行已结束、图没封存」,停在第二次审批上的运行、别处正在取的那份都不管,画的是旧稿、审计表却是新取的。改成:
- EvidenceDoc(useRunGraph)挂上、换稿那一刻手里已有的那份,找不到正文这一稿,或者正文是运行的成果(EvidenceField 给
  settled)而那份没封存,就按(运行、报告、工件)重取一次;重取回来还是这样就作罢,挂上时还没有、正在取的那份不算过期;
- 证据页挂上时手里那份不是「封存了、封存的就是这一版(manifest_seq)」就重取,不看运行停在哪;别处正在取的取回来再看一眼;
  重取回来之前占位,第一次渲染就按挂上那一刻手里的那份定,不先画一帧旧稿。

整句面板的反方审稿:只有反方标记的句子(没有可点片段、也没有裁判判定)只能从标记点进来,面板却只认片段接口的
unit.challenge,还在取时这一节空着,答 500 时整节消失、也不说为什么。EvidencePanel 收下正文的标记(marks),片段接口还没答、
答不了、答了却没有可用内容时先按标记写出判断(ChallengeSummary 导出的 ClaimVerdictPending),说正在获取、未能获取(带重试,
重试后焦点落到「反方的理由与查询」或新的「重试」上)、无法查看;拿到 unit.challenge 换成完整的单句展示。

没反查成的句子:单句展示里一定没反查成的那一句不再写「判断由反方模型给出」(那是服务端按规则记的无法判断,反方模型没看过);
无法判断的句末标记悬停说明、先行版的落款写「可能是反方模型的判断,也可能是未能反查时记下的」,说不准的仍写判断出自反方模型。

其余几处:
- 挂上时一个键盘目标都没有、反方标记随证据图后来才到:Tab 位交给第一枚标记(以前三枚都是 tabindex=-1,Tab 跳过整份报告);
- 只有反方标记的句子,读屏不再把「反方审稿」念两遍;
- 带字的醒目标记悬停、展开时不铺浅底(10px 的失败色、提醒色字叠在 14% 的浅底上,浅色主题只有 4.0 左右),改描 1px 内边,
  和裁判徽标并在一起的同样;无法判断换成省略号盾牌,不再和裁判的「?」、质询的问号挤成三个问号,也和经受住的勾分得开。

检查:check-evidence 新增 mark-panel(片段接口压着、答 500、重试,390px 不溢出)、mark-tab(标记晚到进 Tab 顺序)、stale(审批卡
换稿、同一张卡换稿、成果区、证据页停在审批上和别处正在取,各重取一次、对上了不再取、旧稿一帧都不画)三节,marks 一节补读屏原文
逐字比、展开和悬停时的对比度、图标形状、无法判断的说法,verify 一节改等单句展示(原来等的选择器单句模式里没有,每遍白等 4 秒);
新预览用例 evidence-marks-text、evidence-marks-stale(dev/fixtures/evidence_marks.tsx)。check-challenge 新增 challenge-claim-quiet:
单句、不谈出具,给齐正式运行、运行 id、degrades 也不写降档、不取运行类别(以前这个开关在哪个用例里都观察不到)。
2026-10-06 14:36:21 +08:00
ThearchyHelios 53f06855dc 运行时限测试的两处说明对上代码:下游余量那条写剩 239 秒时不再开始(代码查的就是 239),慢写报告那条写明写报告的执行器换成了要 3 秒的替身 2026-10-06 14:32:30 +08:00
ThearchyHelios 48f656f362 证据裁判截断的回复整批不采信,截断另记 judge_truncated,时间线的下一步指到换模型
复核第 1 轮的发现:
- 有的网关把截断的强制工具调用解析成半截清单:第一条判定写完、第二条只写了编号。_ask 拿到清单就交出去,不再看截断,于是记成一句支持、一句格式不对、两句未给出判定,llm.end 也不标截断。现在回复被截断就整批记 truncated(最后一条可能只写了一半,也可能判定写完、理由写到一半,分不清哪条完整),纯文本调用同理
- 截断的那一批原来只发 llm.end(时间线标题「结论句裁判未能执行」)和 judge_failed(下一步「检查裁判模型的接入后重新运行」):截断时接入是好的,同一个模型重新运行多半还是截断。现在另发 judge_truncated(max_tokens、unjudged),judge_failed 不再带截断;时间线上调用那一行说「裁判模型的输出达到上限(N token)被截断」,judge_truncated 那一行的下一步指到在报告撰写节点的「结论句裁判」或设置里换不带推理的裁判模型,入口落到节点的结论句裁判;check-decode 加一节
- 复核层(review.py)认得 judge_truncated:是缺口,记成它自己的 kind
- 退回小额度按 budget_refused 认,和核查同一种记法记在进程里:之后每份报告、每次点击直接用小的
测试补上半截清单(剧本和真实 ChatAnthropic)、纯文本清单后面还在写、截断和格式不对各记各的日志、跨报告记住被嫌大、自建服务的 max_new_tokens
2026-10-06 14:30:15 +08:00
ThearchyHelios 4905de728c 核查截断的回复一律不采信,触顶原因的先后照旧,截断的建议按模型从哪来的说
复核第 1 轮的发现(主要一条):Anthropic 的工具调用写到一半到了上限时,回复照样带着 tool_use(input 是空的,有的网关给半截对象)。工具循环只在没有工具调用时才看截断,于是一轮轮执行空参数的查询,撞到步数上限,最后记成「没有按结构交结果」;收尾的强制工具调用截在半路时解析出空对象或半截对象,空对象记成没按结构交,反方审稿的半截对象(结论写完、理由写到一半)被当成「经受住」,质询把半句话当成回答。
- 工具循环:回复被截断就停,带着的工具调用不执行(半截的 SQL 查出来的数是错的),也不收尾
- 截断前查到过东西的也不再按查到的收尾:截断前查到的不一定是要的那个数,交出去的结果只在 notes 里写一句「可能不完整」,审批卡、反方结论、质询回答都看不到,残缺的结果还照常参与比对和处置。同时长到点一样算半路被掐断,原因照截断说,查过的照记;TRUNCATED_SETTLED 和日志里的 delivered 字段去掉
- 收尾:回复被截断时,解析出来的、从回复里抠出来的都不采信(结构化、普通调用都是),也不退回普通调用
- 上一轮的 give_up 把触顶原因的先后从「先 stopped」换成了「先 refused」:去掉 give_up,收尾被上限拦下时照旧报先撞上的那个(双盲复算按它写无法复算的原因,反方审稿按每日上限决定还派不派后面的句子)
- 截断的原因开头只说「未能给出结果」(质询里提问人看到的就是这句,不说「核查未能完成」);建议在节点里说「在节点上指定不带推理的模型,或在设置里更换核查模型」(节点上的模型优先于设置,和时间线上的下一步一致),质询只说设置
- 退回小额度按 budget_refused 认,按 (接入, 模型, 额度) 记在进程里
- 时间线:verify_truncated 的副标题固定说「这次核查未能给出结果」,check-decode 跟上;review.py 里 verify_truncated 的注释跟上
测试补上真实 ChatAnthropic(只有 HTTP 层是假的)的循环截断、收尾截断(空对象、半截对象),真实 ChatOpenAI 写到一半的工具调用参数,Bearer 鉴权下 SDK 在本地拒绝时退回 4096,自建服务的 max_new_tokens,跨次核查记住被嫌大,触顶原因的先后
2026-10-06 14:30:03 +08:00
ThearchyHelios 5afa7a9a70 核查、证据裁判退回小额度时多认两种拒绝,被嫌大过的接入记在进程里
复核第 1 轮的发现:
- 自建服务(text-generation-inference 一类)嫌额度大时回 422,原话说的是 max_new_tokens,不含 max_tokens,认不出就不退回;核查、裁判的额度从 4096 提到 16384 之后才容易撞上。新增 WIDER_BUDGET_WORDS 和 budget_refused,只给核查、裁判用;助手改图仍按 budget_rejected 认,BUDGET_WORDS 不动(data/recipe_ai.py 的测试拿它比对)
- Bearer 鉴权的 Anthropic 接入(factory 换上的 SDK 客户端没设超时)调 Opus 4、4.1 这一代的 id 时,16384 的非流式请求 anthropic SDK 在本地就抛 ValueError(Streaming is required…),请求根本没发出去。budget_refused 把它也认成额度被挡下,退回小额度;重来不会重复输出,也没花钱
- 退回的开关原来只在一组调用里共用:双盲复算的几路盲算、反方审稿的每一句、每次质询、每次点击裁判各自先按 16384 撞一次。BudgetFallback 加 key:被嫌大一次就记进进程里的 _REJECTED,之后同一个 key 新建的一开始就用小的(同 factory 的 _NO_STREAM_USAGE,重启就忘);不给 key 的(助手改图)不记也不查,行为不变
2026-10-06 14:29:48 +08:00
ThearchyHelios b2878fde59 时间线认得核查日志里的 cause: run_deadline:单次运行时限将至没核成的那几行说时限,下一步说怎么腾出时间,不再叫人调高核查的上限或去查模型和数据源;设置页双盲复算、反方审稿的时长设成不限时写明仍受单次运行时限约束
为什么(复核第 1 轮):反方审稿、双盲复算为后续节点预留时间,没开始的、缩短时长后到时没做完的以前读成「反方审稿已达时长上限」「未能反查」「无法复算」,下一步是「调高反方审稿的上限」「检查核查模型和数据源」「调高……中的上限后重新运行」——调高了只会更快撞线,模型和数据源本来就没问题。服务端现在另起一条、带 cause 和 run_seconds(单次运行时限),这里照它说。设置页「不限」的说明原来写「仅受模型接口自身的超时约束」,可这两种核查在运行里执行,时限将至时照样按剩余时间缩短。

- decode.ts:dual_check_unchecked、challenge_unreviewed、challenge_limit 带 cause: run_deadline 时,标题写「运行时限将至,……」,副标题写「单次运行时限为 600 秒,已为后续节点预留时间」(双盲复算前面再点名指标),下一步写减少句数或指标、在核查前加人工审批(时限按审批前后分段计算)、请管理员调大单次运行时限(AGENTLAB_MAX_RUN_SECONDS);不带 cause 的照旧
- terms.ts:叫法放在 DUAL_CHECK_TEXT(共用的副标题和双盲复算两句)、CHALLENGE_TEXT.log(反方审稿三句)、VERIFY_SETTING_TEXT.unlimitedNote 多一种 runTimeout
- VerifySettings.tsx:时长设成不限时,审批质询照旧(它不在运行的那一段里执行),双盲复算、反方审稿用 runTimeout 的说法

检查:check-decode 新增一节(追加在文件末尾),先核对后端还是这么发,再看三种日志带不带 cause 各怎么说、同一节点里两条分得开、缺字段不崩;对着改之前的 decode.ts 这一节 7 项变红。check-verify 新增一节(追加在文件末尾):三项时长设成不限时各自的说明,390px 深浅色不横向溢出、对比度 ≥ 4.5;改之前这一项变红。
2026-10-06 14:25:53 +08:00
ThearchyHelios e3f5d566d0 核查因运行时长上限将至没核成的,时间线另起一条并带上原因(cause: run_deadline);收紧过的核查 agent 触顶时也改说时长上限将至;给下游留的余量看下游还有几个要调模型的节点,时长上限调短时余量跟着缩小
为什么(复核第 1 轮):没开始的句子、指标和收紧后到点的,以前和别的原因记在同一条日志里,没有可机读的字段,界面只能照旧叫人调高每句、每项的上限(调高了只会更快撞线),或去查核查模型和数据源(它们本来就没问题);前面有一句因别的原因没审时,运行时长上限这个原因还被藏进「等」里。双盲复算只给下游留 60 秒,惯常接法「双盲复算 → 写报告 → 反方审稿 → 成果」里写报告一慢,运行照样在写报告时被中止;时长上限调到 180 秒左右时,固定的 60 秒余量又让核查一项都开始不了。

- 反方审稿:没开始的句子另发一条 challenge_unreviewed,带 cause、run_seconds,不混进别的原因;收紧后到点的那条 challenge_limit 照记 timeout_s(老界面认得),另带 cause、run_seconds 和触顶的句数 claims,同一节点里有句子触了自己的上限时两条分得开。产出的 unreviewed 照算
- 双盲复算:没开始、收紧后到点的盲算结果带 cause、run_seconds,_compare 记无法复算时一并带上(只这一行),dual_check_unchecked 按有无 cause 分两条发,时限将至的那条另带 cause、run_seconds,原话列指标名(最多 5 个)
- deadline.relabel 包住交给核查 agent 的 emit:收紧过的那一项到点时,verify_limit 改说「本次运行即将达到 600 秒的时长上限,时长已按剩余时间收紧到 52.3 秒」,带 cause;别的事件、别的上限原样转发,没收紧时原样交回
- 余量按份算:一份取 60 秒和这一段的 10% 中大的,这一段不到 240 秒时按 25%;下游这一段里(到人工审批为止)每个还要调模型的节点(模型、Agent、协作、子工作流、写报告、双盲复算、反方审稿)各再加一份,合计不超过这一段的一半。默认 600 秒的一段、下游只有成果节点时和以前一样
- 没有截止时刻时一切照旧;决定都还在原来的 @task 里,接着运行取 checkpoint

测试:反方审稿、双盲复算各补一条「另一个原因 + 时限将至」两条分开说、带不带 cause 的用例,反方审稿补一条触了自己上限和收紧后到点分得开的用例;原有用例补上 cause、run_seconds、原话的断言。假的钟钉住下游有写报告时第 3 项只查 40~50 秒(没有时 100~110 秒);真的钟用 10 秒一段、盲算查到时长用完、写报告要 3 秒,以前在写报告时被中止,现在运行成功。单元测试钉住短时长的余量、下游每个节点加一份和一半的上限、数下游节点(人工审批之后不算、汇合只算一次、环回自己不算)、relabel 改什么不改什么。去掉下游余量、去掉 relabel 后对应用例都变红。
2026-10-06 14:25:38 +08:00
ThearchyHelios dde0927460 审批质询遮罩收口(第 1 轮复核):「夹住某一个值」在整条 SQL 里认,只有分档的几种写法各算各的;分档标签不当遮罩值;SQL 检查结果的片段、说明照整条 SQL 换
- 夹住:上一轮只在同一个条件里配对,把找值拆到 ON 和 WHERE、WHERE 和 HAVING、INTERSECT / EXCEPT 两支、包在 CASE / 函数 / 算式里、子查询和 CTE 里,完整手机号就照原样封存、回答正文也跟着不遮。现在整条 SQL 里配对,只有 CASE 的不同分支、IIF 的真假两支、SELECT 里不筛行的不同项(选出列、GROUP BY、ORDER BY)、UNION 的不同支各算各的(_apart,看最近公共祖先);同一个条件里照旧按合起来的真假细看。整数上 <= v、>= v 也隔开相邻的整数(age <= 24 OR age >= 26 就是 age <> 25);BETWEEN v AND v、>= v AND <= v、> v OR < v 条件里哪一层这样写都算(被更宽的范围吸收、旁边有矛盾的条件、隔着别的条件写也认得出),整数的半开区间仍只看整个条件,成百个相邻分档照旧不遮。条件的各层摊开一次反复用,600 档的耗时和上一轮相当
- 分档标签:CASE / IIF 结果全是写死常量的,标签文字是 agent 写在 SQL 里的,不收进遮罩值,规格原句的 SQL、回答里的档名照原样;当分组键(每一档一行)或套在汇总里按条件数个数(SUM(CASE … THEN 1 ELSE 0 END))不算派生值,格子照常显示,分得清哪一档几笔;逐行的标签列、汇总遮罩列本身的值照旧整列遮
- 质询工件的 SQL 检查结果:片段是检查器重新写出的表达式,单拿出来认不出在找哪一个值,改为按整条 SQL 里整个换掉的字面量换(简单 CASE、行值、BETWEEN v AND v 里的短的数也换掉,阈值照原样);说明里照引的取值(「没有码值「3」」)一样换;截断处半截的号码去掉
- 同一个短的数在别处换掉时,别的列上的阈值、LIMIT 照原样:一律换会回退第 2 轮起的「age = 25 AND amount > 25 … LIMIT 25」,写进已知局限并补测试钉住
- 模块说明的已知局限照实改写(分档拆进两个 CTE / FROM 子查询、同一选出列里两档汇总相除会多遮,拆进 CASE 分支 / 不同选出列认不出,运行详情的工件页签不经这里的遮罩)
- 测试:端到端改回规格原句(文字档位、SUM(CASE …)、年龄段 CASE),补 ON/WHERE、WHERE/HAVING、INTERSECT、包在 CASE 里、CTE、相邻整数的端到端用例,单元补拆分写法、被吸收的 BETWEEN v AND v、分组标签、检查结果片段与说明;每处去掉实现都验证过变红
2026-10-06 14:05:52 +08:00
ThearchyHelios b080302e77 可验证报告复核修正(第 1 轮):只给 id 的遮罩工件,服务端的文件和 id 对不上的标 mismatch、解析不出来的标 unparsable,核对页对不上的判不通过、解析不出来的照实说认不出是否含遮罩列
上一轮让被改过、解析不出的口径卡不再原样嵌入(号码不进文件),可这样一来它在页面上和正常的遮罩卡一模一样:「? 无法核对 含遮罩列,未随文件附带」,总结论从「有 1 项不通过」变成「可核对的项全部通过」,篡改的证据丢了;截断的卡服务端其实认不出输入,却被说成「含遮罩列」。现在 _Artifacts.add 对只给 id 的遮罩工件(口径卡、查询快照都算,带遮罩的快照被改过原来就有同样的问题)照实带上服务端看到的:store.intact 为假标 mismatch,解析不出对象标 unparsable。verifier.js 在遮罩分支认这两个标记:mismatch 判不通过,原因「导出时服务端的这份文件与工件 id(哈希)不一致,可能被改过;按含遮罩列处理,未随文件附带」;unparsable 是正常的无法核对,原因「导出时服务端的这份文件无法解析,认不出是否含遮罩列,按含遮罩列处理,未随文件附带」,引用它的数字跟着用这句。pytest 补被改过、截断、不是对象、哈希对得上却解析不出的卡和被改过的遮罩快照;check-bundle 在 masked-card 样本上补两条篡改检查。
2026-10-06 13:43:04 +08:00
ThearchyHelios a7b8854dc4 双盲复算复核修正(第 1 轮):业务日期只认带日期的类型、原因按哪一边说清;遮罩列在子查询或 WITH 里改了名不再收窄,交给盲算的分组取值按 SQL 认出取自遮罩列的;按位置取的格点名原因、认不出出处的不写进计算式、不再改变容器输入的判法
业务日期的类型门槛:只有时刻的 TIME / TIMETZ 不再算(一天里的几点几分不是日期,MySQL 还常拿它存时长;和 catalog.is_date_column 同一个口径,记成业务日期后 SQL 检查会对按真实日期列统计的查询一直报错误级),原因说「只有时刻,不含日期」。类型照表结构里记的说「类型是 X」(introspect 存的是 SQLAlchemy 反射出来的类型,SQLite 里写成 TIMESTAMPTZ 的反射成 NUMERIC),没写类型的列反射成「NULL」,按没有声明类型说。被采纳的一边有问题说「被采纳的盲算按时间筛的列……」,未被采纳的一边另说清为什么也要看它(两边都是日期列时,审核人选的才是业务日期按哪一列)。数据源还没有可用的表结构(未探查、探查失败)时说表结构不可用,不说这一列不在表结构里。上传表格建的数据源(origin=upload,节点认出后经候选的 uploaded 交给 derive)日期一律存成文字,原因说「来自上传的表格,表格里的日期一律按文字保存」,不说成用户声明了 TEXT;预告和真写逐字一致。

遮罩:收窄补等值条件、交给盲算的分组取值,都按 SQL 认出取值来自遮罩列的名字(复用审批质询的 inquiry._derived_hidden:起别名、套函数、经子查询或 WITH 转一道、写在列名表里的都认,解析不了又写着遮罩列的整份算)。以前外层按 area 分组、area 在子查询里由 region 改名而来时照常补上「area = '华东'」,差异说明还原成「region = '华东'」进了审批卡、对照工件和目录日志,「华东」也进了盲算的任务;和遮罩列无关的 WITH 照常收窄。按位置取自遮罩列的输入直接交回「输入 vars.res.rows[0][0] 取自设置了遮罩的列,不能用于复算」(同 cell() 的说法),「核对不了」改成「无法核对」。伸进查询结果的行、却认不出取自哪一列的(下标不是字面量、改过形状的结果没带快照 id 和数据源名)计算式里写成「其他输入」,不把卡上的值交给盲算。按位置取的格落在的快照只为认遮罩算遮罩,不再进重算的快照清单:以前同一个指标里装着它整份行的容器输入会对上它、被它的遮罩列挡下。
2026-10-06 13:42:51 +08:00
ThearchyHelios f815b1232a 审批质询收尾:审批列表取不到时别的审批里问的单独一组写「无法确认提问时的报告版本」并给重试;质询回答的问题摘要截在空格上也补省略号;工件页签里运行结束之后才写下的工件不给航迹跳转
- 审批列表没取到时,那几条以前被标成「报告其他版本上的质询」:同一屏上卡里挂在句子下面的一条,证据页签却说是别的
  版本,审批人会以为报告换过稿,也没有办法重取。现在单独一组照实说没能确认、给「重试」,取到后按结果各归其位(这张
  审批里问的回到句子下面,审的是另一稿的才算其他版本);取到了列表却没有那张审批的、报告没有报告文档的照旧算对不上
- 质询回答工件的说明:服务端先去掉问题首尾的空白再截前 40 字,摘要以空格结尾只会是截断截在了空白上。以前先去空白再
  数字数,少一个字就漏了省略号,半句话读成整个问题;改为按原样数、显示时才去空白
- 运行详情「工件」页签:晚于运行结束的工件(封存后追加的质询回答和它查询的快照)以前也给「T+…」跳转,点了把航迹拖到
  运行结束之后几十分钟、几天;现在只写时间。pages/runs/ArtifactsPane.tsx 只加了结束时刻这一道边界,运行中、停在审批上
  的运行照旧(航迹还没有结束时刻)
- 预览用例 inquiry-run-artifacts 换成带起止时间的航迹、多一件以空格结尾的问题摘要;check-inquiry 补对应断言,以及审批
  列表取不到时的标题、重试、重试后的归拢和深浅色对比度
2026-10-06 13:33:54 +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 9f0e453884 整篇包在 ``markdown 里、中间再套一段一样长的 ``sql 时照样去掉外层围栏:带信息串的一行开、不带的一行收,两两配对。以前一样长的内层围栏一律不去,模型最常见的这种写法仍是 0 句结论,裁判和反方审稿没有句子可审,正式运行照样出具
规则:中间和外层同种符号的围栏行,比外层短的跳过,比外层长的不去(规格原文),一样长的按配对看——带信息串的开一个代码块,不带的收掉它;没有可收的 ``` 说明外层在这里就收了(好几个代码块),开着时又来一个 ```sql、扫到最后还开着的,都不去。去掉外层后 compose_doc 和前端每见到一行 ``` 开合一次,配好的一对正好切成一个代码块,原 docstring「见到 ``` 就收」的理由不成立,改准确了。

一样长的行只认 compose_doc 和前端认得的写法(信息串是紧跟着的一个词):``` sql 这种它们不认,算一行正文。不然它会和后面的 ``` 配成一对、去掉外层,而后面那个 ``` 在它们眼里成了开头,把结论句吞进代码块。比外层长的照旧按行首判,````sql title 这样的也不去。

外层反引号里套 ~~~、四个以上反引号的代码块照常去掉外层:compose_doc 和前端只认 ``` 围栏,这段按正文核对,和模型不包外层、直接这么写时一样,比整篇一个代码块强;docstring 写明,测试钉住。

测试:same-fence-inside 挪到去的一边,补多段内层代码块、~~~ 里套 ~~~、反引号里套 ~~~;不去的补没有可收的 ```、开着时又来 ```python、内层没收、``` sql 开头、更长的带词围栏。compose_doc 一级补 ```markdown 套 ```sql(SQL 仍是代码块,结论句 2 句)和 ~~~ 块按正文核对。端到端(正式运行,裁判 + 反方审稿 + 出具契约)加一组套 ```sql 的写法:两句都判、都审,完整出具。裁判退回改写(rewrite_once)那一稿也包在代码块里时照样去掉:改写稿被采用,第二轮判到改过的那句,以前这条路没有测试守着。每条判断各做过一次去掉、测试变红。
2026-10-06 13:32:38 +08:00
ThearchyHelios ed6f5d064a 截断和退回小额度在真实的 ChatOpenAI、openai SDK 上也核对一遍:只有 HTTP 层是假的(经 OpenAI 兼容网关接入的推理模型就是这一路)
核查:工具循环那次只有 reasoning_content、finish_reason=length;收尾走 json_schema 时 SDK 抛 LengthFinishReasonError(用量从异常里取);网关嫌 16384 太大回 400 时退回 4096,之后工具循环和收尾都用小的。裁判:json_schema 那次被截断记未裁判(truncated)、不退回纯文本;网关回 400 时退回 4096。请求体里的额度(max_completion_tokens)逐个核对。
2026-10-06 13:17:19 +08:00