mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 10:41:59 +00:00
2f90f0eb3a
- QueryCore 固定执行(K09):按已发布的不可变口径跑一次,交回类型化标量、发布/manifest 引用与逐查询证据 - 依赖闭包(K08):按选中的指标算出要拷的取数节点、要的入口字段,接进 derive / compose / metrics - SQL 模板参数完整性(K01):试跑、组合问数、派生图、运行时取数共用一套语义,缺参数在渲染前就拒 - 受限字段变更服务(K03)与口径领域边界(K01):改动打在整图深拷贝上,身份与证据归属不可改 - 版本化受控模板绑定(K04):业务口径按显式绑定落到 SQL,对不上就报 unsupported / needs_update - 验证记录(K05)与最小审阅:样例匹配不等于正式批准,审批人只从服务端身份取 - 发布依据清单(K07):发布时按不可变图物化内容寻址清单,正式用途按它判 - 增量重导入(K11)与 B/L/R 三方合并、受限自然语言修改提案(K12):只产候选,落图仍走既有写路径 - 接口与前端:口径草稿页接上字段变更、绑定状态、验证与重导入,新增 caliber-drafts / studio 检查脚本 验证:前端 tsc -b 通过。
191 lines
14 KiB
JavaScript
191 lines
14 KiB
JavaScript
// 口径账本的检查夹具(check-ledger、check-evidence、check-chat 共用):GET /api/runs/{id}/ledger 的答复形状照服务端
|
||
// app/ledger/view.py 的 run_view(labels.view_label、issue.Plan.action、reconcile.actions_of / reconciled_view),
|
||
// 挂在可点击证据的夹具(frontend/src/run/__tests__/evidence-doc.json,compose_doc 真跑出来的周报,只用通用名)上:
|
||
// s6「45,678.5元」来自受管口径「周报口径」v2,等价已证明;s10「1,234单」来自已发布口径「订单口径」v1,助手选用;
|
||
// s8「8.7%」相关口径「订单口径」v1(差在哪:少了一个筛选条件),可以对账;s12「37.0元」这次的推理;
|
||
// s23「2.35%」没有比对(比对超时)。
|
||
// 口径名、表名、数都是虚构的通用名。
|
||
|
||
/** 口径的几份(calibers) */
|
||
export const WEEKLY = { caliber: 'wf-weekly@2', workflow_id: 'wf-weekly', version: 2, level: 'governed', level_text: '受管',
|
||
workflow_name: '周报口径', graph_hash: 'aa11' }
|
||
export const ORDERS = { caliber: 'wf-orders@1', workflow_id: 'wf-orders', version: 1, level: 'published', level_text: '已发布',
|
||
workflow_name: '订单口径', graph_hash: 'bb22' }
|
||
|
||
const caliberLabel = (c, how, extra = {}) => ({
|
||
kind: 'caliber', how, caliber: c.caliber, workflow_id: c.workflow_id, version: c.version, level: c.level,
|
||
level_text: c.level_text, workflow_name: c.workflow_name, ...extra,
|
||
})
|
||
|
||
export const DIFF = { items: [{ aspect: 'filter', a: null, b: "status = '[已遮罩]'", text: '口径只保留状态为已支付的行,这次没有这个条件' }],
|
||
fallback: false, text_diff: [] }
|
||
|
||
/** 一份比对过的账本。whole:整题判断(缺省不等价);actions、issued、reconciled 可以换 */
|
||
export function ledgerOf(runId, { whole, actions, issued = [], reconciled = [], retro = false, labels } = {}) {
|
||
return {
|
||
run_id: runId, state: 'matched', note: '封存后比对,不在封存范围内', post_seal: true, artifact: 'cm-1', algo: 'v1',
|
||
trigger: retro ? 'on_demand' : 'sealed', retro, matched_at: '2026-10-15T01:00:00+00:00',
|
||
...(retro ? { retro_note: '按现在的口径回看' } : {}),
|
||
calibers: [WEEKLY, ORDERS],
|
||
queries: [
|
||
{ artifact: 'q-gmv', node_id: 'ag', source: 'shop', tables: ['orders'], status: 'proven',
|
||
candidates: [{ ...WEEKLY, node_id: 'fetch', status: 'proven', how: 'independent' }] },
|
||
{ artifact: 'q-orders', node_id: 'ag', source: 'shop', tables: ['orders'], status: 'related',
|
||
candidates: [{ ...ORDERS, node_id: 'fetch', status: 'related', comparable: true, via: 'heuristic', diff: DIFF,
|
||
binding: [{ field: 'start', value: '2026-09-01' }] }] },
|
||
],
|
||
reports: { write: { labels: labels ?? {
|
||
s6: caliberLabel(WEEKLY, 'independent', { binding: [{ field: 'week', value: '2026-W37' }], card: 'caliber', metric: 'gmv',
|
||
queries: ['q-gmv'] }),
|
||
s10: caliberLabel(ORDERS, 'composed', { binding: [{ field: 'store', masked: true, value: '已遮罩' }], card: 'caliber',
|
||
metric: 'orders', queries: ['q-gmv'] }),
|
||
s8: { kind: 'related', related: [{ caliber: ORDERS.caliber, workflow_id: ORDERS.workflow_id, version: 1, level: 'published',
|
||
level_text: '已发布', workflow_name: '订单口径', node_id: 'fetch' }], queries: ['q-orders'] },
|
||
s12: { kind: 'reasoning', query: 'q-gmv' },
|
||
s23: { kind: 'unknown', reason: '比对超过时限,没有做完', query: 'q-gmv' },
|
||
} } },
|
||
primary: 'write',
|
||
whole: whole ?? { equivalent: false, reason: '有的数是这次的推理', counts: { caliber: 2, related: 1, reasoning: 1, unknown: 1 } },
|
||
actions: actions ?? [RECONCILE],
|
||
issued, reconciled,
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 答案身上的召回标记(服务端 ledger/recall.answer_marks):周报口径升到 v3 重算过、数没变(同一份口径核对过两次,只写最近
|
||
* 那一版);目录里影响数字的两项被驳回、按新目录检查新出现 1 条错误级问题,另有一次出现了新码值(同归「数据目录」那一类,
|
||
* 项数相加);看的这个人手上有一条说的是这个答案的 recall 待办(订单口径升到 v2,数变了)
|
||
*/
|
||
export const RECALL = {
|
||
checked: [
|
||
{ workflow_id: 'wf-weekly', workflow_name: '周报口径', from_version: 1, to_version: 2, recall_run: 'run-rc0', changed: false,
|
||
at: '2026-10-14T01:00:00+00:00' },
|
||
{ workflow_id: 'wf-weekly', workflow_name: '周报口径', from_version: 2, to_version: 3, recall_run: 'run-rc1', changed: false,
|
||
at: '2026-10-15T01:00:00+00:00' },
|
||
],
|
||
basis: [
|
||
{ kind: 'catalog', items: 2, new_errors: 1, at: '2026-10-15T02:00:00+00:00' },
|
||
{ kind: 'values', items: 1, new_errors: 0, at: '2026-10-15T03:00:00+00:00' },
|
||
],
|
||
tasks: [{ id: 'tk-recall-1', reason: 'caliber', to_version: 2 }],
|
||
}
|
||
|
||
/**
|
||
* 看差多少(s8 那条查询、订单口径):参数是推断的,要先核对。
|
||
* `available: true` 是服务端**确实给了身份**(workflow_id / version)的那一项:界面据此摆按钮。
|
||
*/
|
||
export const RECONCILE = {
|
||
kind: 'reconcile', query: 'q-orders', caliber: ORDERS.caliber, workflow_id: ORDERS.workflow_id, version: 1, node_id: 'fetch',
|
||
workflow_name: '订单口径', level_text: '已发布', available: true, needs_check: true, check_reason: '参数按这次的 SQL 推断,请核对',
|
||
binding: [{ field: 'start', value: '2026-09-01' }], fallback: false,
|
||
fields: [{ name: 'start', required: true, has_default: false }, { name: 'store', required: false, has_default: true }],
|
||
}
|
||
|
||
/**
|
||
* 当前这个身份**没有使用授权**的那一项:服务端只给说明,不把口径身份带出来(view._action_view 对 available 为假的那几项
|
||
* 把 workflow_id / version 抹掉)。界面照旧写清是哪份口径、差在哪,但不摆按钮——也就发不出一个 workflow_id / version
|
||
* 空着的请求。base 是原来那一项(默认订单口径那一项),要换一份口径就给 over 覆盖 caliber / query 这些。
|
||
*/
|
||
export const noAccessReconcile = (base = RECONCILE, over = {}) => {
|
||
const { workflow_id, version, ...rest } = base
|
||
return { ...rest, ...over, available: false }
|
||
}
|
||
export const RECONCILE_NO_ACCESS = noAccessReconcile()
|
||
|
||
/** 按口径正式出具(整题等价于周报口径):搭建者能一键,不自动的原因写在 auto_reason */
|
||
export const ISSUE = {
|
||
kind: 'issue', workflow_id: WEEKLY.workflow_id, version: 2, caliber: WEEKLY.caliber, workflow_name: '周报口径', how: 'independent',
|
||
level: 'governed', level_text: '受管', available: true, auto: false, auto_reason: '设置里关了自动按口径出具',
|
||
}
|
||
|
||
/** 整题等价时的标签(三个数都来自周报口径) */
|
||
export const WHOLE_LABELS = {
|
||
s6: caliberLabel(WEEKLY, 'independent', { binding: [{ field: 'week', value: '2026-W37' }], queries: ['q-gmv'] }),
|
||
s10: caliberLabel(WEEKLY, 'independent', { binding: [{ field: 'week', value: '2026-W37' }], queries: ['q-gmv'] }),
|
||
s12: caliberLabel(WEEKLY, 'independent', { binding: [{ field: 'week', value: '2026-W37' }], queries: ['q-gmv'] }),
|
||
}
|
||
export const WHOLE = { equivalent: true, caliber: WEEKLY.caliber, workflow_id: WEEKLY.workflow_id, version: 2, level: 'governed',
|
||
level_text: '受管', workflow_name: '周报口径', how: 'independent', binding: [{ field: 'week', value: '2026-W37' }],
|
||
missing: [], missing_required: [], counts: { caliber: 3, related: 0, reasoning: 0, unknown: 0 } }
|
||
|
||
/**
|
||
* 逐格判(服务端 ledger/match._cells,algo 4)的真实例子:同一条查询(q-gmv)报告引用了好几列,s6 销售额那一格证明了
|
||
* 等价于周报口径,s10 订单数那一格只是相关(差在哪先说多查了几列、再说计数对求和),整题不等价、没有出具入口
|
||
*/
|
||
export const CELL_DIFF = { items: [
|
||
{ aspect: 'projection', a: null, b: null, text: '这次多查了 3 列,不影响这个数' },
|
||
{ aspect: 'aggregate', a: 'COUNT(*)', b: 'SUM(amount)', text: '这次数的是行数,口径对 amount 求和' }],
|
||
fallback: false, text_diff: [] }
|
||
export const CELL_LABELS = {
|
||
s6: caliberLabel(WEEKLY, 'independent', { binding: [{ field: 'start', value: '2026-10-05' }, { field: 'end', value: '2026-10-11' }],
|
||
query: 'q-gmv', node_id: 'fetch', column: 'gmv' }),
|
||
s10: { kind: 'related', query: 'q-gmv', related: [{ caliber: WEEKLY.caliber, workflow_id: WEEKLY.workflow_id, version: 2,
|
||
level: 'governed', level_text: '受管', workflow_name: '周报口径',
|
||
node_id: 'fetch', comparable: true, diff: CELL_DIFF }] },
|
||
}
|
||
export const CELL_WHOLE = { equivalent: false, reason: '有的数和相关口径不一致,不来自口径',
|
||
counts: { caliber: 1, related: 1, reasoning: 0, unknown: 0 } }
|
||
|
||
/** 对过的一次账(单行:一组对照)。status 可以换 */
|
||
export const reconciledOf = (status = 'succeeded') => ({
|
||
run_id: 'run-rec-1', status, query: 'q-orders', caliber: ORDERS.caliber, workflow_id: ORDERS.workflow_id, version: 1,
|
||
inputs_from: 'checked', rows: null, card: 'reconcile',
|
||
groups: [{ label: '订单数', ours: 'ours_0', caliber: 'cal_0', diff: 'diff_0', rel: 'rel_0' }], created_at: '2026-10-15T02:00:00Z',
|
||
})
|
||
|
||
/** 对账卡上四个指标的展示值(/evidence/metrics/reconcile/{id} 的 rendered) */
|
||
export const RECONCILE_VALUES = { ours_0: '1,234单', cal_0: '1,180单', diff_0: '54单', rel_0: '4.58%' }
|
||
|
||
/** 对账运行的指标出处(形状同片段接口:chain、seal),给 EvidenceDrawer 画 */
|
||
export const metricDetail = (runId, metric) => ({
|
||
run_id: runId, node: 'reconcile', metric, rendered: RECONCILE_VALUES[metric] ?? '—', eid: `ev:metric:${metric}`,
|
||
chain: [{ step: 'metric', alias: 'm', metric, name: metric, value: 1, rendered: RECONCILE_VALUES[metric], node_id: 'reconcile',
|
||
caliber: '口径对账', version: 'v1', expression: 'cell(nodes.ours_q1, 0, \'orders\')', sealed: true, inputs: [] }],
|
||
seal: { sealed: true, ok: true, covered: true }, redacted: { columns: [] },
|
||
})
|
||
|
||
/**
|
||
* 对账卡上「按口径」那个数的出处(服务端 api/evidence._upstream_steps 的形状):它转抄口径卡的指标
|
||
* (nodes.card.metrics[0].value),出处跨卡接上口径卡「订单数」那一步,再到口径的取数查询(q-cal-fetch)
|
||
*/
|
||
export const copiedMetricDetail = (runId, metric) => ({
|
||
run_id: runId, node: 'reconcile', metric, rendered: RECONCILE_VALUES[metric] ?? '—', eid: `ev:metric:${metric}`,
|
||
chain: [
|
||
{ step: 'metric', alias: 'm', metric, name: '订单数 · 口径', value: 1180, rendered: RECONCILE_VALUES[metric], node_id: 'reconcile',
|
||
caliber: '口径对账', version: 'v1', expression: 'nodes.card.metrics[0].value', substituted: '1180', recompute_ok: true,
|
||
sealed: true, hash_ok: true, untraced: [],
|
||
inputs: [{ path: 'nodes.card.metrics[0].value', value: 1180, node_id: 'card', via: 'metric', field: 'metrics[0].value',
|
||
status: 'ok', upstream: { metric: 'orders', artifact: 'ms-card', depth: 1, traced: true } }] },
|
||
{ step: 'upstream', of: 'nodes.card.metrics[0].value', node_id: 'card', label: '口径卡', artifact: 'ms-card', metric: 'orders',
|
||
traced: true, depth: 1, hash_ok: true, sealed: true, name: '订单数', value: 1180, unit: '单', rendered: '1,180单',
|
||
caliber: '订单口径', version: 'v1', expression: "cell(nodes.fetch, 0, 'orders')", substituted: '1180', recompute_ok: true,
|
||
status: 'ok', untraced: [],
|
||
inputs: [{ step: 'input', path: "cell(nodes.fetch, 0, 'orders')", value: 1180, node_id: 'fetch', via: 'tool_cell',
|
||
status: 'verified', artifact: 'q-cal-fetch', locator: { row: 0, column: 'orders' }, eid: 'ev:cell:q-cal-fetch' }] },
|
||
{ step: 'query', alias: null, artifact: 'q-cal-fetch', node_id: 'fetch', tool: 'db_query__shop', source: 'shop',
|
||
sql: "SELECT COUNT(*) AS orders FROM orders WHERE status = 'paid'", columns: ['orders'], rows: [[1180]], row_offset: 0,
|
||
row_index: [0], total_rows: 1, highlight: { rows: [0], cols: ['orders'], cells: [[0, 'orders']] }, sealed: true, hash_ok: true },
|
||
],
|
||
seal: { sealed: true, ok: true, covered: true }, redacted: { columns: [] },
|
||
})
|
||
|
||
/** 认证队列:三类来源各一项 */
|
||
export const QUEUE = {
|
||
items: [
|
||
{ id: 'shape:aa11', kind: 'shape', source: 'shop', shape: 'SELECT SUM(amount) FROM orders WHERE paid_at >= ? AND status = ?',
|
||
runs: 4, conversations: 3, question: '上周销售额是多少',
|
||
turns: [{ run_id: 'run-s1', conversation_id: 'c-1', turn_id: 't-1', question: '上周销售额是多少' },
|
||
{ run_id: 'run-s2', conversation_id: 'c-2', turn_id: 't-2', question: '这周销售额是多少' }],
|
||
params: [{ index: 0, distinct: 3 }], constants: 1, related: [{ caliber: ORDERS.caliber, workflow_id: ORDERS.workflow_id, version: 1, level: 'published' }],
|
||
representative: { run_id: 'run-s1', query: 'q-1' }, peers: ['run-s2', 'run-s3'], frozen_into: [] },
|
||
{ id: 'cluster:bb22', kind: 'cluster', question: '各门店的退款率', count: 3,
|
||
turns: [{ run_id: 'run-c1', turn_id: 't-c1', question: '各门店的退款率' }, { run_id: 'run-c2', turn_id: 't-c2', question: '门店退款率怎么样' }],
|
||
frozen_into: [] },
|
||
{ id: 'reused:cc33', kind: 'reused', question: '本月新客数', uses: { exports: 2, app: false, extracted: false, canvas: true },
|
||
turns: [{ run_id: 'run-r1', conversation_id: 'c-3', turn_id: 't-r1', question: '本月新客数' }],
|
||
representative: { run_id: 'run-r1' }, frozen_into: [{ workflow_id: 'wf-new-old', name: '本月新客数(固化的口径草稿)', status: 'draft' }] },
|
||
],
|
||
considered: { runs: 120, matched: 100, questions: 90 },
|
||
threshold: { runs: 3, conversations: 2, exports: 2 },
|
||
}
|