mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 04:31:58 +00:00
双盲复算前端(三):检查器字段、节点卡片摘要,以及检查器与节点卡片的钩子
- nodeDefs:照约定一字不差地加 FieldDef.render 与 FieldRenderProps;NodeDef 加可选的 summary(ctx) 钩子 (ctx:id、config、画布上的 nodes 与 edges,只返回字符串);dual_check 换成真字段:要复算的口径卡、复算的指标、 口径说明、启用、审核人不采用原流程的结果时(基本),盲算模型的接入与模型、允许的相对误差(高级) - Inspector:照约定一字不差地在 FieldInput 的 switch 前交给 field.render - canvas/DualCheckFields.tsx(新):口径卡下拉分上游、其余两组,不在上游或不在画布上照实提醒;指标逐个勾, 全勾回到缺省不写、最后一个不让取消;口径说明逐个指标写,被取消的指标上已写的说明列出来可删;盲算模型照「结论句 裁判」的写法(只填模型时写明按模型名用哪个接入);相对误差的占位写出设置里的值;启用三态,跟随设置不留 null。 每个控件只写自己的键,留空写回 undefined - NodeCard:用只返回字符串的 studio 选择器调摘要钩子,有就用、没有照旧 summarize;摘要写「复算「卡名」 · N 个指标」, 关闭时标出;去掉 dual_check 的占位 case - 预览用例加检查器(小画布用真的节点卡片 + 360px 检查器),check-verify 加检查器写配置、摘要跟着变、窄屏和对比度 - 去掉 nodeDefs 里已经不成立的「下面两种还是占位」注释
This commit is contained in:
@@ -24,7 +24,9 @@
|
||||
"original_rendered": "45,678.5元",
|
||||
"blind_rendered": "51,388.3元",
|
||||
"delta_pct": 12.5,
|
||||
"diff": ["原流程按支付时间 paid_at 筛选时间范围,盲算按下单时间 created_at 筛选"],
|
||||
"diff": [
|
||||
"原流程按支付时间 paid_at 筛选时间范围,盲算按下单时间 created_at 筛选"
|
||||
],
|
||||
"fallback": false
|
||||
},
|
||||
{
|
||||
@@ -79,7 +81,10 @@
|
||||
"diff": {
|
||||
"equivalent": false,
|
||||
"items": [
|
||||
{ "aspect": "time", "text": "原流程按支付时间 paid_at 筛选时间范围,盲算按下单时间 created_at 筛选" }
|
||||
{
|
||||
"aspect": "time",
|
||||
"text": "原流程按支付时间 paid_at 筛选时间范围,盲算按下单时间 created_at 筛选"
|
||||
}
|
||||
],
|
||||
"fallback": false,
|
||||
"text_diff": [
|
||||
@@ -140,7 +145,10 @@
|
||||
"diff": {
|
||||
"equivalent": false,
|
||||
"items": [
|
||||
{ "aspect": "other", "text": "两条 SQL 的写法不同,未能归纳成具体差异,请对照原文" }
|
||||
{
|
||||
"aspect": "other",
|
||||
"text": "两条 SQL 的写法不同,未能归纳成具体差异,请对照原文"
|
||||
}
|
||||
],
|
||||
"fallback": true,
|
||||
"text_diff": [
|
||||
@@ -150,5 +158,217 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"graph": {
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start",
|
||||
"type": "input",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 120
|
||||
},
|
||||
"data": {
|
||||
"label": "输入",
|
||||
"config": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "week",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fetch",
|
||||
"type": "agent",
|
||||
"position": {
|
||||
"x": 280,
|
||||
"y": 120
|
||||
},
|
||||
"data": {
|
||||
"label": "取数",
|
||||
"config": {
|
||||
"prompt": "查询本周各门店的订单",
|
||||
"tools": [
|
||||
"db_query__shop"
|
||||
],
|
||||
"assign_to": "kpi"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card",
|
||||
"type": "metrics",
|
||||
"position": {
|
||||
"x": 560,
|
||||
"y": 40
|
||||
},
|
||||
"data": {
|
||||
"label": "周报口径卡",
|
||||
"config": {
|
||||
"caliber": "周报口径",
|
||||
"caliber_version": "v1",
|
||||
"metrics": [
|
||||
{
|
||||
"id": "gmv",
|
||||
"name": "销售额",
|
||||
"unit": "元",
|
||||
"expression": "vars.kpi.gmv"
|
||||
},
|
||||
{
|
||||
"id": "orders",
|
||||
"name": "订单数",
|
||||
"unit": "单",
|
||||
"expression": "vars.kpi.orders"
|
||||
},
|
||||
{
|
||||
"id": "aov",
|
||||
"name": "客单价",
|
||||
"unit": "元",
|
||||
"expression": "vars.kpi.gmv / vars.kpi.orders"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "card2",
|
||||
"type": "metrics",
|
||||
"position": {
|
||||
"x": 560,
|
||||
"y": 260
|
||||
},
|
||||
"data": {
|
||||
"label": "月报口径卡",
|
||||
"config": {
|
||||
"caliber": "月报口径",
|
||||
"caliber_version": "v2",
|
||||
"metrics": [
|
||||
{
|
||||
"id": "m_gmv",
|
||||
"name": "月销售额",
|
||||
"unit": "元",
|
||||
"expression": "vars.kpi.gmv"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dc",
|
||||
"type": "dual_check",
|
||||
"position": {
|
||||
"x": 840,
|
||||
"y": 40
|
||||
},
|
||||
"data": {
|
||||
"label": "复核周报口径",
|
||||
"config": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "done",
|
||||
"type": "output",
|
||||
"position": {
|
||||
"x": 1120,
|
||||
"y": 40
|
||||
},
|
||||
"data": {
|
||||
"label": "成果",
|
||||
"config": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "结果",
|
||||
"value": "{{ last_message }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "e1",
|
||||
"source": "start",
|
||||
"target": "fetch"
|
||||
},
|
||||
{
|
||||
"id": "e2",
|
||||
"source": "fetch",
|
||||
"target": "card"
|
||||
},
|
||||
{
|
||||
"id": "e3",
|
||||
"source": "fetch",
|
||||
"target": "card2"
|
||||
},
|
||||
{
|
||||
"id": "e4",
|
||||
"source": "card",
|
||||
"target": "dc"
|
||||
},
|
||||
{
|
||||
"id": "e5",
|
||||
"source": "dc",
|
||||
"target": "done"
|
||||
}
|
||||
]
|
||||
},
|
||||
"providers": [
|
||||
{
|
||||
"id": "prov-a",
|
||||
"name": "云端接入",
|
||||
"kind": "anthropic",
|
||||
"base_url": null,
|
||||
"default_model": "model-a",
|
||||
"models": [
|
||||
{
|
||||
"id": "model-a"
|
||||
},
|
||||
{
|
||||
"id": "model-b"
|
||||
}
|
||||
],
|
||||
"enabled": true,
|
||||
"extra": {}
|
||||
},
|
||||
{
|
||||
"id": "prov-b",
|
||||
"name": "备用接入",
|
||||
"kind": "openai",
|
||||
"base_url": null,
|
||||
"default_model": "model-c",
|
||||
"models": [
|
||||
{
|
||||
"id": "model-c"
|
||||
}
|
||||
],
|
||||
"enabled": false,
|
||||
"extra": {}
|
||||
}
|
||||
],
|
||||
"verify": {
|
||||
"provider": null,
|
||||
"model": null,
|
||||
"dual_check": {
|
||||
"enabled": true,
|
||||
"tolerance": 1e-06,
|
||||
"max_queries": 6,
|
||||
"timeout_s": 120
|
||||
},
|
||||
"challenge": {
|
||||
"enabled": true,
|
||||
"max_claims": 5,
|
||||
"max_queries": 4,
|
||||
"timeout_s": 90
|
||||
},
|
||||
"inquiry": {
|
||||
"enabled": true,
|
||||
"max_queries": 6,
|
||||
"timeout_s": 120
|
||||
},
|
||||
"daily_max_usd": 2.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { useState } from 'react'
|
||||
import type { JSX } from 'react'
|
||||
import { ReactFlow, ReactFlowProvider } from '@xyflow/react'
|
||||
import { ApprovalCard } from '../../run/RunPanel'
|
||||
import type { Approval } from '../../types'
|
||||
import { Inspector } from '../../canvas/Inspector'
|
||||
import { NodeCard } from '../../canvas/NodeCard'
|
||||
import { toFlow, useStudio } from '../../store/studio'
|
||||
import { useCatalog } from '../../store/catalog'
|
||||
import type { Approval, GraphSpec, Provider } from '../../types'
|
||||
import data from './dual_check.json'
|
||||
|
||||
/**
|
||||
@@ -8,8 +14,10 @@ import data from './dual_check.json'
|
||||
* - ?syn=dual-check-approval:正式运行里两路对不上的审批卡,宽栏(两条 SQL 左右并排)
|
||||
* - ?syn=dual-check-approval-rail:同一张卡摆在 360px 的画布右栏里(两条 SQL 上下排)
|
||||
* - ?syn=dual-check-approval-exploratory:探索运行,后果里不提出具
|
||||
* - ?syn=dual-check-inspector:一张小画布(真的节点卡片)加 360px 的检查器,选中双盲复算节点;检查器改的是
|
||||
* studio store 里的节点配置,卡片上的摘要跟着变
|
||||
*
|
||||
* 完整对照的工件(GET /api/artifacts/{id})和审批回复接口由检查脚本用 page.route 伪造,直接打开时卡上照实说
|
||||
* 完整对照的工件(GET /api/artifacts/{id})、审批回复、设置、校验都由检查脚本用 page.route 伪造,直接打开时卡上照实说
|
||||
* 无法加载完整对照。提交后 onResolved 只把运行 id 记到 window.__dualResolved,不去接事件流
|
||||
*/
|
||||
const approval = (patch: Partial<Approval> = {}): Approval => ({
|
||||
@@ -26,6 +34,36 @@ function Card({ value }: { value: Approval }) {
|
||||
)
|
||||
}
|
||||
|
||||
const nodeTypes = { card: NodeCard }
|
||||
|
||||
/**
|
||||
* 检查器用例:第一次渲染前把夹具里的图放进 studio store、选中双盲复算节点,模型接入直接写进目录 store(不走接口)。
|
||||
* 用例都在一个预览页里预先加载,store 只能在这里(用到时)改,不能在模块顶层改
|
||||
*/
|
||||
function InspectorCase() {
|
||||
useState(() => {
|
||||
const { nodes, edges } = toFlow(data.graph as unknown as GraphSpec)
|
||||
useStudio.setState({ nodes, edges, selectedId: 'dc' })
|
||||
useCatalog.setState({ providers: data.providers as unknown as Provider[] })
|
||||
return true
|
||||
})
|
||||
const nodes = useStudio((s) => s.nodes)
|
||||
const edges = useStudio((s) => s.edges)
|
||||
return (
|
||||
<div className="flex h-full min-h-0 max-w-full" data-fixture="dual-check-inspector">
|
||||
<div className="min-w-0 flex-1 max-sm:hidden" data-dual-canvas="">
|
||||
<ReactFlowProvider>
|
||||
<ReactFlow nodes={nodes} edges={edges} nodeTypes={nodeTypes} fitView nodesDraggable={false} nodesConnectable={false}
|
||||
proOptions={{ hideAttribution: true }} />
|
||||
</ReactFlowProvider>
|
||||
</div>
|
||||
<div className="h-full w-[360px] max-w-full shrink-0 overflow-hidden border-l bg-panel" data-dual-inspector="">
|
||||
<Inspector />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const cases: Record<string, () => JSX.Element> = {
|
||||
'dual-check-approval': () => (
|
||||
<div className="mx-auto max-w-3xl p-4 text-sm" data-fixture="dual-check-approval"><Card value={approval()} /></div>
|
||||
@@ -40,4 +78,5 @@ export const cases: Record<string, () => JSX.Element> = {
|
||||
<Card value={approval({ run_class: 'exploratory' })} />
|
||||
</div>
|
||||
),
|
||||
'dual-check-inspector': () => <InspectorCase />,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user