From 3e0aacac18f82bf71e0a5d043aa64a06e95bb8a7 Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Wed, 7 Oct 2026 00:24:19 +0800 Subject: [PATCH] =?UTF-8?q?M2=20=E6=97=B6=E9=97=B4=E7=BA=BF=E8=AE=A4?= =?UTF-8?q?=E5=BE=97=E6=8A=A5=E5=91=8A=E5=BC=95=E7=94=A8=E4=BA=86=E9=81=AE?= =?UTF-8?q?=E7=BD=A9=E5=88=97=E7=9A=84=E9=82=A3=E4=B8=80=E8=A1=8C=EF=BC=9A?= =?UTF-8?q?decode.ts=20=E6=8A=8A=E6=97=A5=E5=BF=97=20report=5Fmasked=5Fcel?= =?UTF-8?q?l=20=E8=AF=B4=E6=88=90=E3=80=8C=E6=8A=A5=E5=91=8A=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E4=BA=86=20N=20=E5=A4=84=E5=8F=96=E8=87=AA=E9=81=AE?= =?UTF-8?q?=E7=BD=A9=E5=88=97=E7=9A=84=E6=A0=BC=E5=AD=90=EF=BC=8C=E5=B7=B2?= =?UTF-8?q?=E6=A0=87=E6=B3=A8=E3=80=8D=EF=BC=8C=E5=89=AF=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E7=82=B9=E5=90=8D=E5=BC=95=E7=94=A8=EF=BC=88=E4=B8=8D=E5=86=99?= =?UTF-8?q?=E5=80=BC=EF=BC=89=EF=BC=8C=E4=B8=8B=E4=B8=80=E6=AD=A5=E8=AF=B4?= =?UTF-8?q?=E5=87=BA=E5=85=B7=E5=B7=B2=E9=99=8D=E6=A1=A3=E3=80=81=E5=8E=BB?= =?UTF-8?q?=E6=94=B9=E5=86=99=E4=BD=9C=E6=8C=87=E4=BB=A4=EF=BC=9B=E8=AF=81?= =?UTF-8?q?=E6=8D=AE=E9=9D=A2=E6=9D=BF=E7=9A=84=E8=BF=9D=E8=A7=84=E6=B8=85?= =?UTF-8?q?=E5=8D=95=E7=BB=99=20masked=5Fcell=20=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=87=AA=E5=B7=B1=E7=9A=84=E5=8F=AB=E6=B3=95=E3=80=8C=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E4=BA=86=E9=81=AE=E7=BD=A9=E5=88=97=E3=80=8D=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E8=90=BD=E6=88=90=E3=80=8C=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=A0=B8=E5=AF=B9=E6=9C=AA=E9=80=9A=E8=BF=87=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 文案放在 terms.ts 新块 MASKED_CELL_TEXT;check-decode.mjs 加一节守住后端字段(count、refs、columns)、标题、副标题、缺字段时的说法。 --- frontend/src/lib/terms.ts | 11 ++++++++++ frontend/src/run/EvidencePanel.tsx | 3 ++- frontend/src/run/decode.ts | 13 +++++++++++- scripts/check-decode.mjs | 32 ++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/terms.ts b/frontend/src/lib/terms.ts index 285c6ae4..2a309a53 100644 --- a/frontend/src/lib/terms.ts +++ b/frontend/src/lib/terms.ts @@ -247,6 +247,17 @@ export const ABSENCE_CLAIM_TEXT = { + '或换用上下文窗口更大的写作模型后重新运行', } +/** + * 报告直接引用了取自遮罩列的格子(报告自查的 masked_cell;c.phone AS tel 这种改了名字的也算),重写后仍在、 + * 「违规处置」为标注时照常产出:时间线上那一行(日志 report_masked_cell)和证据面板违规清单里的叫法 + */ +export const MASKED_CELL_TEXT = { + label: '引用了遮罩列', + title: (n?: number) => `报告引用了${n ? ` ${formatNumber(n)} 处` : ''}取自遮罩列的格子,已标注`, + next: '数据源设置了遮罩的列不应写进报告,这几处的值会随报告出具,出具已按缺口降档。' + + '请在报告撰写节点的指令中去掉对这些列的引用后重新运行', +} + /** 证据面板和证据条的文案 */ export const EVIDENCE_TEXT = { allCited: (n: number) => `${formatNumber(n)} 个数字都有出处`, diff --git a/frontend/src/run/EvidencePanel.tsx b/frontend/src/run/EvidencePanel.tsx index 487eb6e0..03c32b98 100644 --- a/frontend/src/run/EvidencePanel.tsx +++ b/frontend/src/run/EvidencePanel.tsx @@ -19,7 +19,7 @@ import { import { humanizeError } from '../lib/errors' import { formatDateTime, formatNumber, NONE, shortId } from '../lib/format' import { - ABSENCE_CLAIM_TEXT, CHALLENGE_TEXT, DUAL_CHECK_TEXT, EVIDENCE_TEXT, JUDGE_TEXT, MERGE_TEXT, NODE_TYPE_LABEL, RUN_SQL_CHECK_TEXT, + ABSENCE_CLAIM_TEXT, CHALLENGE_TEXT, DUAL_CHECK_TEXT, EVIDENCE_TEXT, JUDGE_TEXT, MASKED_CELL_TEXT, MERGE_TEXT, NODE_TYPE_LABEL, RUN_SQL_CHECK_TEXT, SQL_CHECK_TEXT, nodeTypeLabel, } from '../lib/terms' import { useCatalog } from '../store/catalog' @@ -1835,6 +1835,7 @@ const CODE_LABEL: Record = { unknown_entity: '疑似不存在的名称', unverified_entity: '无法核实的名称', absence_claim: ABSENCE_CLAIM_TEXT.label, + masked_cell: MASKED_CELL_TEXT.label, } /** 违规清单:报告撰写节点核对出来的全部问题,画得出线的能定位,画不出的说清在哪 */ diff --git a/frontend/src/run/decode.ts b/frontend/src/run/decode.ts index 499944a0..07ac6e5c 100644 --- a/frontend/src/run/decode.ts +++ b/frontend/src/run/decode.ts @@ -3,7 +3,7 @@ import type { NodeState, RunPhase } from './trace' import { formatDuration, formatNumber } from '../lib/format' import { ABSENCE_CLAIM_TEXT, CATALOG_DRIFT_TEXT, CATALOG_PATCH_TEXT, COPILOT_CONTEXT_TEXT, JUDGE_TEXT, MERGE_TEXT, RUN_SQL_CHECK_TEXT, SCHEMA_PARTIAL_TEXT, SQL_CHECK_TEXT, TYPE_LABEL, - CATALOG_TABLE_FIELD_LABEL, CHALLENGE_TEXT, DUAL_CHECK_TEXT, INQUIRY_TEXT, JUDGE_TRUNCATED_TEXT, NODE_TYPE_LABEL, + CATALOG_TABLE_FIELD_LABEL, CHALLENGE_TEXT, DUAL_CHECK_TEXT, INQUIRY_TEXT, JUDGE_TRUNCATED_TEXT, MASKED_CELL_TEXT, NODE_TYPE_LABEL, VERIFY_TRUNCATED_TEXT, claimTally, evidenceTally, issuanceLabel, nodeTypeLabel, @@ -873,6 +873,17 @@ function explainLog(code: string | undefined, message: string, d: Record 0 ? d.count : undefined + const refs = strs(d.refs) + return { + title: MASKED_CELL_TEXT.title(n), + sub: refs.length ? refs.map((r) => `[[${r}]]`).join('、') : undefined, + next: MASKED_CELL_TEXT.next, + } + } case 'report_catalog_trimmed': { // 写作目录超出篇幅(report.py _trimmed_message)。读结构化字段(queries、omitted、nodes),不从原话里认 const total = typeof d.queries === 'number' ? d.queries : undefined diff --git a/scripts/check-decode.mjs b/scripts/check-decode.mjs index f371282e..ca354137 100644 --- a/scripts/check-decode.mjs +++ b/scripts/check-decode.mjs @@ -1377,6 +1377,38 @@ await section('报告核对 report.checked、口径卡的台账(可点击证 && !!rcAbsent?.detail?.includes('缺失断言') && !!rcAbsent?.detail?.includes('依据不足'), `${rcAbsent?.status} ${rcAbsent?.detail}`) }) +await section('报告引用了取自遮罩列的格子(report_masked_cell):标题说几处、已标注,副标题点名引用,不写那几格的值', async () => { + // 契约:data = {level: warn, code: report_masked_cell, message, count, refs, columns},重写后仍在、「违规处置」为标注时才发 + const E = (seq, type, node_id, data = {}) => ({ seq, type, node_id, ts: 1790000000 + seq, data }) + const reportPy = readFileSync(`${root}backend/app/engine/nodes/report.py`, 'utf8') + check('后端发引用了遮罩列的警告,带着 count、refs、columns', reportPy.includes('code="report_masked_cell"') + && reportPy.includes('count=len(hits), refs=refs') && reportPy.includes('columns=list(dict.fromkeys('), + '改了 report.py 的字段就同步改 decode.ts 的 explainLog 和这里的夹具') + const MASKED = '报告引用了 2 处取自遮罩列的格子(「v:Q2.r0.tel」、「v:Q2.r1.tel」),已要求模型重写 1 次仍未改掉,这些值会随报告出具。' + + '报告照常产出,这几处已标注:数据源设置了遮罩的列不应写进报告,请在报告撰写节点的指令中去掉对这些列的引用' + const steps = flatten(mod.decodeRun([E(1, 'node.started', 'w', { node_type: 'report', label: '写报告' }), + E(2, 'log', 'w', { level: 'warn', code: 'report_masked_cell', message: MASKED, count: 2, + refs: ['v:Q2.r0.tel', 'v:Q2.r1.tel'], columns: ['tel'] }), + E(3, 'report.checked', 'w', { doc_artifact: 'abc', repairs: 1, ok: false, on_violation: 'flag', failed: false, + stats: { numbers: 1, numbers_cited: 1, uncited_numbers: 0, unresolved: 0 }, + violations: [{ code: 'masked_cell', ref: 'v:Q2.r0.tel', column: 'tel', + message: '引用了取自遮罩列的格子 [[v:Q2.r0.tel]]:列「tel」取自数据源中设置了遮罩的列,值不应写进报告' }] }), + ])) + const mc = steps.find((s) => s.code === 'report_masked_cell') + check('标题说几处引用了遮罩列、已标注', mc?.title === '报告引用了 2 处取自遮罩列的格子,已标注', mc?.title) + check('……副标题点名那几处引用(不写值)', mc?.sub === '[[v:Q2.r0.tel]]、[[v:Q2.r1.tel]]', mc?.sub) + check('……下一步说遮罩列不应写进报告、出具已降档、去改指令;是提醒(warn),原话留在展开区', + !!mc?.next?.includes('不应写进报告') && !!mc?.next?.includes('降档') && !!mc?.next?.includes('指令') + && mc?.level === 'warn' && mc?.detail === MASKED, mc?.next) + const rc = steps.find((s) => s.code === 'report_checked') + check('……核对那一行照常完成(标注,不判失败),违规清单里写着这一条的说明', rc?.status === 'done' + && !!rc?.detail?.includes('取自数据源中设置了遮罩的列'), `${rc?.status} ${rc?.detail}`) + const bare = flatten(mod.decodeRun([E(1, 'log', 'w', { level: 'warn', code: 'report_masked_cell', message: '报告引用了遮罩列' })])) + .find((s) => s.code === 'report_masked_cell') + check('……缺字段时标题照样说人话,不出 undefined', bare?.title === '报告引用了取自遮罩列的格子,已标注' + && bare?.sub === undefined, `${bare?.title} / ${bare?.sub}`) +}) + await section('工具门控 tool.gated(工具信任三档)', async () => { // 契约:data = {tool, verdict: allow|escalate, reason, model, duration_ms, call_id?, agent?},node_id 是所在节点 const E = (seq, type, node_id, data = {}) => ({ seq, type, node_id, ts: 1790000000 + seq, data })