Files
AgentLab/docs/design/normal-user-apps.md
T
ThearchyHelios 9d8b41430e 完善口径管理、主动调查与后台任务,修复通用问题
- 完善口径编辑、只读试运行、统一质量验证与执行语义指纹
- 增加可执行指标约束、有预算的主动调查和过程评测
- 提取发布检查、证据读取与起草服务,支持任务停止、重试和检查点恢复
- 增加版本传递影响分析,修复并发覆盖、敏感值遮罩、异常评分及页面状态问题
- 修复 SQLite 复合查询追溯和 microVM 可选依赖缺失处理
- 纳入当前设计文档及部署配置调整

验证:相关主回归 3822 项通过,4 项跳过、8 项预期失败;补充回归通过。
前端构建、文案检查及 git diff --check 通过。
全量检查仍有环境权限、PostgreSQL 可选依赖和既有部署配置、SQLite 文件基线差异导致的未通过项。
2026-11-06 17:14:45 +08:00

65 KiB
Raw Blame History

普通用户建应用:方案

设计文档 · 草案,待拍板 · 2026-10-15

  • 本文档只定方案,不含代码改动。第五部分「实施组织」在本文签字后另行审批,再开工。
  • 代码位置按 main@69c5c74(应用平台合入后)标注,三路只读调研逐条核过。
  • 承接《应用平台与助手按需查看:总方案》(docs/design/app-platform-and-look.md,下称「总方案」)。本文不改它,引用写作「总方案 §x.y」。
  • 文中出现的数据源名、表名、应用名、人名都是虚构的。本文不含本机开发环境的计数。

目录

  • 摘要
  • 第一部分 现状与问题
  • 第二部分 关键决定(N1–N11)
  • 第三部分 规格
  • 第四部分 界面
  • 第五部分 实施组织
  • 第六部分 验证与验收
  • 第七部分 风险与对策
  • 第八部分 待拍板
  • 附录 A 代码位置索引 · B 术语

摘要

问题。 应用平台已经能做相当复杂的应用:多级审批、会签、时限与升级、写回外部系统、看板、按新口径补算。但能做出来的只有懂编排的人。普通人卡在这几处:

  • 必须先在画布上搭好并发布工作流,应用只能从已发布版本生成;
  • 从问数据出发要跳四次:做成应用 → 提取工作流 → 去画布发布 → 回来做成应用;
  • 满屏专家词:固定版本、处置、岗位绑定、视图来源、出具契约、口径卡、cell(nodes.fetch, 0, 'gmv')、PT48H;
  • 发布前看不到真数,判断不了「这是不是我要的」;
  • 两类最常见的需求做不了:上传文件,以及「填报登记」(每次提交存一行,再汇总着看)。

做法。 把「搭应用」改成「说需求 → 看蓝图 → 用真数试 → 确认」,用的就是项目的路线「模板定骨架、助手填关节」:

  1. 一条链路,两种终点(N1)。说需求、蓝图、模板填关节、试运行,两类人走同一条链路,只在最后一步分开:
    • 搭建者:一次确认,发布成正式应用;
    • 使用者:存成个人应用,只自己看得见、只读、跑探索运行,想给别人用就提交给搭建者审核发布。
  2. 应用蓝图(N2):用户的话和工作流、应用之间的中间件,用白话卡片呈现。卡片写的是看哪些数、谁发起谁审批、多久更新一次、要不要写回系统,用户逐张确认或修改。卡片的改动是确定性的操作,不是让模型重写。
  3. 场景模板(N3):首批五个——周期看板、审批流程、异常处置、填报登记、核对写回。骨架锁住,助手只能填模板声明的「关节」:取数的 SQL、口径卡的指标式;其余关节由蓝图确定性地填。
  4. 用真数试运行(N4):跑一次探索运行,草稿应用直接对着这次运行解析,印章写「试运行」。流程类的审批步骤派给试运行的人自己点,写入一律走测试环境或演练。
  5. 一次确认发布(N5):服务端按「发布单」编排——保存版本、发布工作流、建应用、绑岗位、建定时任务、授权、跑第一次——每步可续做。确认前给一页白话摘要;查出的问题分成「你来定」「助手可以修」「要找管理员」三类。
  6. 在页面上直接改(N6):应用页进入编辑状态后点哪改哪;改取数逻辑就回到蓝图,重走试运行和发布单,口径变不变用白话说清。
  7. 补两个能力缺口:
    • 记录表(N8):平台托管、只许追加,用序号钉版本,按授权分行可见;
    • 文件(N9):按哈希存,以引用进运行输入,可以交给 agent 读,也可以作为一期数据导入。
  8. 白话层(N10)和生成的耗时与稳定性(N11):所有检查问题有一份白话说法;补上问数据和画布两端「助手反问」的收尾。

已拍板(2026-10-14、10-15):

  • 两类人都做,共用一条链路(N1);
  • 填报登记要做行级可见(N8);
  • 表单只许加字段,改字段或删字段就出新的表版本,旧记录照原样保留(N8);
  • 试运行只给有管理权限的人看(N4);
  • 先写设计文档,签字后实施;验收结束后实施(已结束)。

待拍板:见第八部分,共 8 项,每项附推荐。


第一部分 现状与问题

1.1 现在建一个应用要走的路

一个从零开始的看板应用,最少要按顺序做这些事,每步各自提交:

  1. 在画布上搭工作流并保存,形成版本快照;
  2. 发布工作流(受管级只有管理员能发);
  3. 建定时任务——必须在第 4 步之前,否则派生出来的视图不绑定时任务(apps/derive.py:85-90);
  4. 派生应用草稿(POST /api/apps/derive,不落库);
  5. 建应用(POST /api/apps);
  6. 绑岗位(PATCH draft_bindings);
  7. 授权(PUT grants,立即生效);
  8. 发布应用。

中途失败会留下半截状态:已发布但没有应用的工作流、按 cron 在跑的定时任务、停在草稿的应用。草稿应用的授权已经生效,使用者打开会得到 409「还没有发布」(api/apps.py:83)。这些步骤分散在画布、定时任务、应用编辑器三处。

1.2 普通人卡在哪

卡点 现象 出处
要会搭图 应用只能钉已发布版本 总方案 A1
术语 口径卡表达式、ISO 8601 时长、出具契约 JSON、视图来源、处置 端到端验收「普通用户视角」13 条
看不到真数 草稿预览只读钉住版本已有的封存运行,新应用没有 apps/resolve.py:454-476
路径长 问数据做成应用要跳四次 ChatPage.tsx:484-492
做不了 没有文件控件;没有平台托管的可写表 apps/spec.py:35-38;data/table_versions.py:1-33

1.3 现有零件(直接复用)

  • 生成管线:Generation 加 Applier/Checker 协议,新的生成目标插进来就能用(api/copilot_gen.py:567,look/base.py:87-110)。

  • 按需查看:表、能力、工作流版本三类对象(look/service.py)。

  • 护栏:

    • 润色边界 polish_problem(apps/ops.py:264);
    • 自动修复的 forbidden_changes(engine/autofix.py:1240)。

    两者都是「只许改哪些」的确定性护栏,模板关节照这个写法。

  • 确定性派生:derive_app(apps/derive.py)。

  • 从运行提取工作流:/copilot/from-run,带来源记录 workflows.origin(api/copilot.py:2468-2550)。

  • 数据目录:表的中文名、粒度、业务日期、列的含义和度量类型(data/catalog.py:12-21),是把白话对到表的依据。

  • 按运行解析:视图来源 instance 已经能按给定的那次运行解析,不查类别和哈希(apps/resolve.py:541-546)。

  • 所有运行都封存,探索运行也一样(engine/runner.py:1013-1045);探索运行的写入走测试环境或演练(capabilities/endpoints.py:101)。

  • 问数据对使用者的约束(auth/chat_policy.py):

    • 节点和工具的白名单;
    • 强加 MASK_RESULTS、READ_ONLY(api/runs.py:108-123);
    • 记忆隔离。
  • 文件相关:

    • 上传限额 read_upload(api/datasources.py:905-918);
    • 按哈希存原件 raw_store(data/raw_store.py:33-43);
    • 按配方导入「上传新一期」(api/table_imports.py:160-486)。
  • 表版本链:快照的版本链,以及「改列不兼容」的判定 INCOMPATIBLE_KINDS(data/recipe_accumulate.py:38-41)。

  • 数据版本:data_versions 进封存范围和印章(engine/runner.py:242-262,apps/resolve.py:238-240)。

  • 名录:auth/directory.py,按登录方式核对人和组。

  • 白话分类的依据:

    • 检查问题已经有 code;
    • 自动修复有 kind: auto|choice|assist(engine/autofix.py:125-141);
    • 只能由人处理的有 APP_HUMAN_ONLY(api/copilot_gen.py:416-421)。

1.4 核实过的缺口

# 缺口 出处
G1 没有带参数的模板,也没有「模板里哪些地方能改」的机制;GraphSpec 只有 nodes/edges/viewport/defaults;「以此模板新建」只是复制 engine/schema.py:115;WorkflowPicker.tsx:354;api/workflows.py:249
G2 应用视图只认正式运行;印章没有「探索」「试运行」 apps/resolve.py:95,149,203-221
G3 探索运行没有版本号和哈希,三种视图来源都取不到它 engine/launch.py:166-174
G4 跨步没有事务:工作流发布撤不回(只能退役),应用只能归档、不能删 api/apps.py:273
G5 检查问题只有技术说法,没有白话 engine/governance.py:217-271
G6 只问不改的一轮:问数据的 store 没有 ask 分支,可能一直停在「进行中」、问题不进历史;画布也不记这一轮 store/chat.ts:1433;store/studio.ts:2149
G7 使用者不能建应用;应用视图、印章、读权限都只认正式运行 api/apps.py:191;auth/access.py:74-96
G8 工件库只存 JSON;运行输入不能引用文件;入口提交是 JSON core/artifact_store.py:45-115;engine/nodes/io.py:14-33
G9 平台托管的表都是不可变快照,只能整期替换或按期累积;没有逐行追加,也没有行级可见 data/table_versions.py:1-33;engine/masking.py
G10 没有按名字检索的口径库;复用口径只能逐级下拉选 caliber_from engine/schema.py:1460;Inspector.tsx:1065
G11 组件没有「隐藏」字段,只能删掉或写可见性表达式;改动要先保存才看得到 apps/spec.py:97-107;apps/resolve.py:632
G12 生成没有并行、没有提示缓存;问数据校验不过时前端整轮重来 look/service.py:192;store/chat.ts:1241

1.5 理念对照

本方案不加新理念,把总方案的八条用到「创建」上:

  • P1 照实说:试运行、探索、个人应用的数都在印章上写明来历;检查问题用白话说清是什么、为什么、谁来改。
  • P2 留痕:
    • 蓝图每一次改动都留痕;
    • 发布单每一步的结果都留痕;
    • 生成的工作流记下出自哪个蓝图、哪个模板。
  • P3 不挡生成:模型失败时,模板照样能按蓝图确定性地实例化,缺的关节标「待填」交给人。
  • P4 只读与遮罩:
    • 个人应用只读、结果遮罩;
    • 记录表按授权分行可见;
    • 文件只给读得到那次运行或那一行的人。
  • P5 一种说法:白话层只有一份,按问题编号映射。
  • P6 模板定骨架:模板的节点和连线锁死,助手只填声明过的关节。
  • P7 界面层不放逻辑:卡片的修改是对蓝图的确定性操作;页面上直接改走的仍是应用操作。
  • P8 生效可查可对账:记录表的写入走动作(生效记录、幂等键);文件的哈希进封存。

不可回退决定的对照见 §2.12。


第二部分 关键决定

每个决定写四样:决定、理由、否决的做法、代价。

2.1 N1 一条链路,两种终点

决定

链路是:说需求 → 蓝图 → 选模板并实例化 → 试运行 → 终点。

  • 起点三个,都落到同一个蓝图:
    1. 说说你要什么:开一段蓝图对话,助手先问清楚,再整理成卡片;
    2. 从场景模板开始:选一个模板,卡片按模板预置;
    3. 从已有的开始:从问数据的一轮回答(运行)开始,或者从一个已发布的工作流版本开始。前者用 from-run 提取,后者直接导入图,模板记为「自由形」。
  • 终点两种:
    • 搭建者:发布单(N5)一次确认,产出已发布的工作流、正式应用、定时任务、授权;
    • 使用者:存成个人应用(N7),自己用。想给别人用就「提交审核」,搭建者打开同一个蓝图,走试运行和发布单。

理由:一条链路只维护一份,「问 → 建」的沉淀路径是现成的(P2)。使用者沉淀出来的东西不绕过发布门禁:个人应用永远是探索,正式应用只能由搭建者发布(不可回退决定:正式运行只从已发布版本发起)。

否决的做法

  • 给使用者一个简化版画布:等于让使用者搭图,权限和门禁都要另开口子;
  • 使用者的应用也走正式发布:让 user 角色产出承诺,违背角色分层。

代价:个人应用和正式应用是两种对象状态,界面要说清区别。

2.2 N2 应用蓝图 agentlab.blueprint/1

决定

  • 定位:蓝图是用户的话和工作流、应用之间的结构化中间件,存成一个可变对象 Blueprint。它有:
    • 拥有者、可见范围(个人 / 共享);
    • 状态:起草中 / 试运行过 / 已发布 / 个人应用 / 审核中;
    • 来源:对话、模板、运行、工作流版本。
  • 一张蓝图由几张「卡片」组成,每张卡片对应用户能理解的一件事:
卡片 内容 落到哪里
目的 一句话:这个应用解决什么问题、给谁用 应用名称、说明
场景 选定的模板(N3) 骨架
看哪些数 每一项:白话名、对到哪个库哪张表(或复用哪张已发布的口径卡)、粒度、业务日期、筛选条件、单位 SQL 关节、口径卡关节
多久更新 每天、每周一、每月 1 日……时区;统计哪一期(本周、上周、上月) 定时任务、入口字段的模板值
谁来做 发起人、各步处理人(人、组、岗位)、几个人同意算通过、时限、超时给谁 人工节点配置、岗位绑定
填什么 表单字段:名称、类型、必填、上下限、选项、文件 入口表单、待办表单、记录表的列
写回哪里 目标能力(从能力登记里选)、字段对应、要不要人确认;能力没登记查询的,再定结果不明时谁去核实(只由人定,同「谁能看」) 调用工具节点、应用的核实岗位
谁能看 使用、查看全部、管理三档的人和组 授权
  • 每一项都带状态,照数据目录项的写法 {value, source, status}:

    • source:用户填的 / 助手提的 / 模板默认 / 从目录或口径卡对到的;
    • status:已确认 / 待确认 / 缺。

    试运行前,所有「缺」都要补齐;「待确认」可以先试运行,但发布时必须确认。

  • 助手只提议:输出 bp_* 操作(bp_set、bp_add、bp_remove、bp_ask),由 BlueprintApplier 确定性落地。用户在卡片上改,走的也是同一组操作,只是 source 记成用户。

  • 对到数据靠 look:助手看「看哪些数」时,先查数据目录(表的中文名、粒度、业务日期、列的度量类型),再查已发布工作流里的口径卡(N2 新增 look 类别 caliber,补 G10)。对到已有口径卡时优先复用(caliber_from),不重写指标式。

  • 反问:缺的信息由助手用 bp_ask 问,一次问一件事、给出候选(例如「销售额按下单日还是支付日算?」并附两个选项)。这一轮就此结束,回答是下一轮(G6 一并修好,N11)。

理由

  • 卡片是用户能判断对错的颗粒度(P1);
  • 模型产出的是提议,落地是确定性的(P7);
  • 每一项都有来源和状态,不会把模型猜的当成用户说的(P2)。

否决的做法

  • 让模型直接生成工作流再给用户看图:普通人看不懂图;
  • 一问一答的长表单向导:问题顺序固定,答不上来就卡住;
  • 蓝图只存在前端:刷新就丢,也没法交给搭建者审核。

代价:多了一种对象和一套操作;模板变了,蓝图的卡片要跟着兼容(蓝图记下模板版本)。

2.3 N3 场景模板 agentlab.template/1:骨架锁住,关节可填

决定

  • 模板是一份登记,和组件目录同一种做法(总方案 A2):
    • 后端 app/apps/templates/,每个模板一个模块;
    • 开发者扩展时,模板定义、实例化函数、测试三样都要给,缺一样启动即报错。
  • 一个模板包含:
    • 骨架:带占位的图(节点和连线固定);
    • 关节:骨架里允许填的位置,每个都有类型、来源、校验;
    • 应用骨架:页面布局,用派生规则加模板自带的组件排布;
    • 卡片映射:蓝图的哪张卡片填哪些关节;
    • 说明和适用条件:给用户和助手看。
  • 关节分两种:
    • 确定性关节:由蓝图直接填,不经模型。包括:

      • 表单字段 → 入口节点 fields;
      • 处理人和策略 → 人工节点 assignee/policy/due/on_timeout;
      • 金额线这类规则 → 分支节点的比较条件,只许「字段 比较 常量」;
      • 更新频率 → 定时任务;
      • 写回目标 → 调用工具节点的 tool 和参数映射。
    • 生成关节:只有两类交给模型——

      • 取数的 SQL(sql);
      • 口径卡的指标式(metric_expr,只许 cell() 取格加口径卡的算术)。

      模型在 TemplateApplier 下工作,只收 fill_joint 操作,而且只收这个模板声明过的关节 id,别的一律拒绝并照实交回(照 polish_problem 的写法)。

  • 护栏:实例化后的图和骨架逐节点比对,只许关节位置不同,用 forbidden_changes 的同一套比对。之后照常走 validate、GraphChecker(SQL 猜列、数据源范围)和发布前检查。
  • 来源记录:生成的工作流写 workflows.origin = {blueprint_id, template: "<id>@<version>", run_id?},补上「只写不读」的现状:WorkflowOut 和版本历史显示「出自蓝图 X、模板 Y」。
  • 首批五个(详见 §3.3):周期看板、审批流程、异常处置、填报登记、核对写回。外加「自由形」:从已有的运行或工作流导入,没有关节,改图只能回画布。
  • 模型失败时(P3):生成关节留空、标「待填」,蓝图卡片上提示「这一项助手没填上,请补充或换个说法」;确定性关节照样落地。

理由

  • 模板定骨架、助手填关节,正是项目路线;
  • 关节越少,生成越快、越稳(N11),检查也越有效;
  • 锁骨架就锁住了治理关注的结构:出具契约、人工节点、写入位置。

否决的做法

  • 模板只是一张样例图,复制了让助手随便改:等于自由生成,护栏全靠事后检查;
  • 用模板引擎在 JSON 上做字符串替换:类型和校验都丢了;
  • 一个模板覆盖所有场景、用大量开关:组合爆炸,测不过来。

代价:每个模板要写实例化和测试;用户的需求落在模板之外时,只能走「自由形」,或者由搭建者新增模板。

2.4 N4 用真数试运行

决定

  • 试运行是一次探索运行,跑的是蓝图实例化出来的、还没发布的图,来源记为 origin_kind=trial、origin={blueprint_id}。

  • 草稿应用对着这次运行解析:

    • 新增解析模式 resolve_page(draft=True, trial=run);
    • 草稿里所有视图来源(最近封存、按需、本实例)都指向这次运行;
    • 钉住项记成 {workflow_id, version: null, graph_hash: <这次运行的图哈希>}。

    沿用 instance 分支「按给定运行解析」的路径(apps/resolve.py:541-546)。不改 agentlab.app/1 的视图来源种类,试运行只是解析时的旁路参数。

  • 印章:

    • 来源写「试运行」;
    • 注明「数据是真的;这不是正式结果,发布后才会按定时任务出数」;
    • 出具档位照实算;没封存的照实写。

    趋势只有一个点时写「试运行只有一次,没有趋势」,不报错(补 _series 拿不到钉住项就报错的问题,apps/resolve.py:813)。

  • 输入:

    • 看板类:按「多久更新」卡片算出最近一期(例如上一 ISO 周),用户可以改;
    • 流程类:由用户填一份示例表单。
  • 流程类的试运行:

    • 人工步骤派给试运行的人自己(来源记为 trial),按钮写「以试运行身份同意/驳回」;
    • 写入一律走测试环境;没有测试环境的演练,不发送;
    • 时限按真实设置显示,但试运行不触发升级——调度跳过 origin_kind=trial 的待办。
  • 谁能看:只有对这个蓝图有管理权的人(拍板项):搭建者看自己的、有 manage 的看共享蓝图的;个人应用的试运行只有拥有者看。试运行是探索运行,遮罩按看的人的角色照常处理。

  • 发布时对齐:发布单按图哈希判断「发布的版本就是试运行过的那张图」:

    • 一致:钉住项直接换成版本,节点 id 一一对上;
    • 不一致(试运行之后又改过):摘要里写明「发布的内容和最近一次试运行不同」,建议再试一次,不强拦。

理由:先看真数再决定(P1);不发布就不产生正式运行(不可回退决定);写入不出测试环境(总方案 A5)。

否决的做法

  • 先发布一个隐藏版本再试:版本表里满是试验版本,退役和口径升版的判断都被污染;
  • 用假数据预览:普通人恰恰要看真数才判断得了。

代价:试运行要花一次模型或查询的钱;流程类的试运行要人点一遍。

2.5 N5 一次确认发布:发布单

决定

  • 发布单(PublishPlan)是服务端的编排对象:先算出要做的每一步和白话摘要,用户确认后按序执行,每步的结果记下来;失败了停在那一步,修好后从那一步接着做。
  • 步骤(顺序就是为了让中途失败时留下的状态无害):
    1. 保存版本:按图哈希判断,已有相同的版本就复用;
    2. 全量预检(只读):工作流发布前检查 + 按将要生成的版本跑应用检查 + 定时任务预览 + 名录核对。任何 error 都在这一步停下,什么都没写;
    3. 发布工作流(受管级只有管理员);
    4. 建定时任务(停用状态),这样派生时视图能绑上它;
    5. 建应用、派生、写岗位绑定;
    6. 发布应用:钉住项升版时按蓝图里的处置;第一次发布没有处置;
    7. 启用定时任务;
    8. 授权:最后一步,避免使用者先看到一个打不开的应用;
    9. 跑第一次(看板类默认勾选):立即运行一次,应用一打开就有数。
  • 白话摘要:每一步一句「会发生什么」,例如:
    • 「发布『门店周报』:此后每周一 09:00 按这一版自动出数」;
    • 「张三、李四可以使用;财务组的人负责审批」;
    • 「口径变了:以前的周报按新口径重算(recompute),旧数据留底」。
  • 问题分三类,按问题编号走白话层(N10):
    • 你来定:岗位没绑人、处置没选;
    • 助手可以修:autofix 的 auto/assist、应用检查里不在 APP_HUMAN_ONLY 的;
    • 要找管理员:受管级、降级、内网端点、能力治理。
  • 补偿:
    • 第 3 步之后的失败不撤回已发布的工作流(发布本来就撤不回),摘要照实说「工作流已发布,应用还没有」;
    • 停用的定时任务在应用发布前不会跑,不产生无主的正式运行;
    • 应用停在草稿时不授权,使用者看不到。
  • 权限:执行发布单的人要有其中每一步的权限,预检时就按角色算好(例如搭建者发不了受管级,摘要里直接说)。

理由:一次确认、照实摘要(P1);每一步留痕(P2);中途失败的状态无害、可续做。

否决的做法

  • 一个大事务把八步包起来:跨定时任务、应用、工作流的事务在 SQLite 单进程下做得到,但出错后整体回滚会吞掉已经通过的检查结果,用户得从头来;而且发布动作本身就带审计,不应该回滚;
  • 前端按顺序调现有接口:刷新页面就丢进度,权限和补偿都在客户端,不可靠。

代价:多一个对象和一张表;现有各接口的校验要能被发布单按「只检查不提交」的方式调用。

2.6 N6 在页面上直接改

决定

  • 改呈现:
    • 有 manage 的人在应用页点「编辑」进入编辑状态,ViewRenderer 叠一层编辑层:按 data-node 选中组件(apps/ViewRenderer.tsx:75-84),就地改文字、隐藏、上下挪、删除。
    • 落地走现有的应用操作 app_update、app_move、app_remove,自动存草稿,然后按草稿重新解析。
  • 加「隐藏」:组件加 hidden: true 字段(补 G11),解析时跳过、编辑状态下照常画成半透明。这和可见性表达式不是一回事:可见性是按运行时条件显示,隐藏是作者不要它。
  • 加一张数据卡片:候选从钉住版本里列,用同一份派生规则 _results(口径卡指标、报告、取数节点、趋势),新接口 GET /api/apps/{id}/candidates。用户挑一个,就落成一个 app_add。
  • 改取数逻辑(「销售额要去掉退款」):
    • 不在页面上改,回到蓝图:改「看哪些数」卡片 → 重新实例化 → 试运行 → 发布单;
    • 发布单在摘要里用白话说清口径变了、以前的数怎么处理(处置),沿用总方案的口径升版规则。
  • 改完要发布:呈现的改动只进草稿,用户点「发布」走应用发布(不动工作流),沿用现有规则。

理由:普通人改看得见的东西最直接(P1);改取数会动口径,必须经过试运行和处置(不可回退决定:口径升版须处置)。

否决的做法

  • 页面编辑器允许拖拽任意布局:像素级设计不是目标(总方案 §1.3);
  • 页面上直接改 SQL:绕过试运行和处置。

代价:编辑状态和查看状态要共用一套渲染,交互细节要打磨。

2.7 N7 个人应用

决定

  • 对象:

    • 个人应用是 scope=personal 的 App,拥有者是创建它的使用者;
    • 它的图不进工作流表(使用者没有工作流权限),存在蓝图上,带图哈希;
    • 没有应用版本,改了就是改了,每次刷新按当前的图跑。
  • 能用的东西,与问数据同一份白名单(auth/chat_policy.py):

    • 节点:input、llm、agent、report、output、retrieve、transform、branch、metrics、merge;
    • 工具:只读内置工具和只读数据源查询;
    • 运行时强加 MASK_RESULTS、READ_ONLY;
    • 不能有人工节点、不能写入、不能调写入能力。

    能用的模板因此只有:周期看板、异常处置的「只看」部分、填报登记的「只看自己的」汇总。

  • 刷新:

    • 打开时用最近一次成功的刷新;
    • 点「刷新」发起一次探索运行(origin_kind=personal_app),身份是拥有者;
    • 受每日上限约束,和按需视图共用设置项 apps.on_demand_daily_limit,按人计。
    • 是否允许每天自动刷新,见待拍板 Q4。
  • 视图来源:新增 exploratory,只在个人应用里合法——取这个应用、这个拥有者、这张图哈希下最近一次成功封存的探索运行。印章来源写「探索 · 未经发布审核」,档位照实写。

  • 谁看得见:只有拥有者。管理员在设置里看得到「谁有几个个人应用」,可以归档,但不看内容(P4)。

  • 提交审核:

    • 拥有者点「提交审核」,选一位搭建者,或者交给搭建者组;
    • 开一条待办(种类 review,派给对方);
    • 搭建者打开时得到这个蓝图的一份共享副本(原个人应用不动),在副本上走试运行和发布单;
    • 发布后通知提交人,提交人自己决定个人应用留不留。
  • 开关:管理员设置 apps.user_personal_apps,默认关(同问数据的开关,待拍板 Q3)。

理由:使用者能自助(产品目标);个人应用永远是探索、只读、遮罩(P4,不可回退决定:没发布的图只能跑探索);共享必须经搭建者发布(承诺由搭建者签)。

否决的做法

  • 给使用者开「个人工作流」:工作流表的权限、门禁、定时任务全要另开一套;
  • 个人应用允许分享给别人:等于绕过发布门禁。

代价:视图来源多一种、印章多一种;个人应用的图和工作流的图是两种存法。

2.8 N8 记录表(填报登记)

决定

  • 存储:每个记录表一个平台托管的 SQLite 文件(data/records/<表 id>.db),只许追加。

    • 每行带系统列:_seq(自增序号)、_id、_submitted_by、_submitted_at、_run_id(哪次运行写的)、_supersedes(更正时指向被更正的行)、_schema_version。
    • 业务列由「填什么」卡片生成。
  • 作为数据源:登记成 origin=records 的只读数据源,看板照常用 db_query__<名字> 查,数据目录照常起草。

  • 写入只经动作:

    • 新增平台内置能力 records.append(效果:写入;允许无人值守,因为平台自己的表、幂等键就是动作键);
    • 填报登记模板的骨架是:入口 → [可选的审批] → 调用工具节点 records.append → 成果;
    • 生效记录、幂等、审计全部沿用总方案 A5,一次提交只落一行,重放不会重复写。
  • 更正不改原行:更正是一次新的提交,带 _supersedes。默认查询看「当前有效」的行(没被更正过的),更正历史可查。

  • 钉版本:

    • 运行开始时,在 data_versions 里记下这张表的高水位 _seq;
    • 查询时连接上建同名的 TEMP VIEW(SQLite 解析不带库名的表名时,先找 temp 库),只露出 _seq ≤ 高水位 的行。

    于是同一次运行查几遍都是同一份数,印章写「数据截至第 N 条」,按需缓存键也带上它(P8)。

  • 行级可见:

    • 同一套临时视图再加一条按人过滤:use 只看 _submitted_by = 自己,observe 和 manage 看全部(拍板项);
    • 过滤在查询工具的连接上做,模型和 SQL 都绕不过去。
    • 看板的汇总:看板读的是正式运行的封存结果,正式运行以服务身份跑、看到全部行,所以汇总是全量的。这是作者的选择,发布单摘要里写明「使用者能看到全部提交的汇总数」。
    • 逐行列表:用新组件 records_list,每次打开按看的人实时过滤(同 instance_list),不走封存。
  • 表单变了:

    • 只加可选字段:ALTER TABLE ADD COLUMN,同一表版本;
    • 改类型、删字段、加必填字段:出新的表版本(新文件 <表 id>.v2.db),旧记录照原样保留。不兼容的判定沿用 INCOMPATIBLE_KINDS。
    • 应用版本钉住表版本;看板要跨版本汇总时,由模板生成 UNION ALL 的视图,列对不上的补空。
  • 遮罩:业务列可以在「填什么」卡片里标「敏感」,进数据源的遮罩列表(mask_columns)。

理由:只追加加序号,在单进程 SQLite 下就能做到可复现(P8,不可回退决定:不上 Postgres、不加节点锁);写入走动作,审批和对账都是现成的;行级可见在连接层做,模型和 SQL 都绕不过去(P4)。

否决的做法

  • 每次提交出一个新快照(复用 table_versions):一次提交一个文件,量一大就扛不住;
  • 允许原地改行:数就不可复现了;
  • 行级可见交给模型在 SQL 里加 where:靠不住。

代价:

  • 多一种数据源和一个写入服务;写入走单进程锁,和导入的锁分开;
  • 高频填报(每秒多次)不是目标场景,写在 README 里。

2.9 N9 文件

决定

  • 存储:
    • 按 sha256 内容寻址存,复用 raw_store 的做法:data/files/<前 2 位>/<sha>,文件只读(0444);
    • 新表 files 记 id、sha256、原名、大小、类型、上传人、上传时间、绑定的运行或记录(可空)、用途;
    • 大小限额沿用 max_upload_mb;类型白名单见待拍板 Q5。
  • 上传两步走:
    1. POST /api/files 带上下文(应用 id 加入口,或者待办 id),先上传,拿回引用 {file_id, name, size, sha256, type};
    2. 再提交表单。表单字段 type: object、x-widget: file,值就是引用。
  • 提交时绑定:服务端核对引用属于提交人、还没被别的提交用过,然后把它绑到这次运行(一次性),引用连同 sha256 进运行输入,随封存一起固定(P8)。没绑定的上传 24 小时后清理。
  • 怎么用文件:
    • 交给 agent 或报告读:新增模板函数 file_text(input.附件),运行时按类型抽文本(PDF、Word、表格、纯文本),沿用知识库上传的抽取,有长度上限,超出照实截断并标出;
    • 作为一期数据导入:平台内置能力 table_import.new_period(source, file),按这个上传源已有的配方导入,效果是写入(数据变了),走动作和确认;
    • 附在记录上:记录表的文件列存引用。
  • 下载:GET /api/files/{id},只给读得到绑定的那次运行、或那一行记录的人(复用 can_read_run 和记录的行级可见)。文件不做遮罩,界面在上传处写明「能看到这条记录的人都能下载附件」。
  • 使用者:个人应用里可以上传文件交给 agent 读(只读),不能导入。

理由:内容寻址加封存,证据站得住(P8);上传和提交分开,表单仍是 JSON,人工节点的表单也能用;读权限跟运行走,没有新的权限面(P4)。

否决的做法

  • 文件放进工件库:工件库只存 JSON,按运行归属,上传时还没有运行;
  • base64 塞进表单值:撑爆输入和事件流。

代价:多一张表、一个清理任务;抽文本的质量受文件格式影响,照实标「抽取不完整」。

2.10 N10 白话层

决定

  • 新增 app/plain.py:问题编号 → {title 一句话, why 为什么要紧, who: you|assistant|admin, action 怎么办}。覆盖:
    • 工作流发布门禁(contract.*、governed.*、action.*、process.*);
    • 应用检查(app.*);
    • 发布单的步骤失败。
  • 没登记白话的问题照旧显示技术说法,并在检查脚本里报「有问题编号没有白话」,防止遗漏。
  • 前端的发布摘要、蓝图卡片、试运行页都用白话;画布和应用编辑器保留技术说法,另加一行白话。
  • who 的判定照这三样:自动修复的 kind、APP_HUMAN_ONLY、需要管理员的问题编号。

理由:P5 一种说法;P1 照实说给听得懂的人。

否决的做法:让模型临场解释——每次说法不同,还可能说错。

代价:每加一个问题编号都要写白话,检查脚本会提醒。

2.11 N11 生成:耗时与稳定性

决定

  • 调用次数的预算:
    • 起草蓝图:1 次主调用,加最多 2 次查看;
    • 填关节:每一组独立的关节(例如两组互不相干的指标)各 1 次调用,并行执行(新增,补 G12),各自最多 1 次查看;
    • 其余全部是确定性的。
  • 模型:蓝图和关节都用设置里的助手模型;首次使用时提示「推荐用快速模型」。验收按真实模型记录每一步的耗时(第六部分 W4)。
  • 反问收尾(补 G6):
    • 问数据:store 加 ask 分支——这一轮记成「助手反问」,问题进历史;输入框提示「回答助手的问题」;
    • 画布:这一轮照样记下,问题进历史;
    • 蓝图对话:问题和候选画成可点的选项。
  • 问数据校验不过时:不再整轮重来,改为把问题交回同一轮的自查修正(Checker 已有),省一次完整生成。

理由:普通人等不了几分钟(验收里有模型在画布写 SQL 首个操作要 486 秒);反问是蓝图的核心交互,不能丢。

否决的做法:一次调用生成整张图加应用——慢、不稳、难修。

代价:并行调用增加瞬时并发,受模型服务的限流约束,失败时退回串行。

2.12 和不可回退决定的对照

不可回退决定 本方案
正式运行只从已发布的不可变版本、按 graph_hash 发起 试运行和个人应用都是探索运行;发布单发布后才有正式运行;按图哈希对齐试运行和版本
口径升版必须处置 改取数回到蓝图,发布单要求处置,摘要用白话说明
叙述层不做算术 关节里的指标式只许 cell() 加口径卡算术;卡片上的数来自绑定
没发布的图只能跑探索 个人应用永远是探索;印章写明
不上 Postgres、不加节点锁 记录表单文件 SQLite、只追加、单进程锁;临时视图钉版本

第三部分 规格

3.1 数据模型

新表

表 主要字段
blueprints id、name、owner、scope(personal / shared)、status(drafting / trialed / published / personal / in_review)、spec(agentlab.blueprint/1)、template_id、template_version、source(conversation / template / run / workflow_version,带 id)、workflow_id、app_id、last_trial_run_id、graph(个人应用的图)、graph_hash、copied_from(审核副本指向原蓝图)、created_at、updated_at
blueprint_events blueprint_id、seq、actor、verified、op(bp_*)、before、after、at——蓝图的留痕
publish_plans id、blueprint_id、actor、steps(每步 kind、status、result、error_code、at)、summary(白话摘要)、level、status(planned / running / stopped / done)、created_at
files id、sha256、name、size、mime、uploaded_by、uploaded_at、context(app_id、entry 或 task_id)、bound_run_id、bound_record(表 id、_id)、purpose
records_tables id、app_id、name、source_id(登记的数据源)、version(表版本)、columns(列定义 + 敏感标记)、created_at;表版本另起一行,旧版本只读保留

新列(走 _COLUMN_MIGRATIONS)

  • apps.scope(默认 shared)、apps.owner、apps.blueprint_id;
  • runs.origin_kind 新增取值 trial、personal_app(总方案 X3 的列表加两项,仍只能由服务端写);
  • tasks.kind 新增 review(个人应用提交审核);
  • 组件 spec 加 hidden(apps/spec.py 的组件模型,extra=forbid 下显式加字段)。

3.2 蓝图 agentlab.blueprint/1

{
  "format": "agentlab.blueprint/1",
  "purpose": {"value": "每周看各门店销售额、订单数和退款率", "source": "user", "status": "confirmed"},
  "template": {"id": "periodic_board", "version": 1},
  "data": [
    {"id": "gmv", "label": {"value": "销售额"}, "unit": {"value": "元"},
     "table": {"value": {"source": "shop", "table": "orders"}, "source": "catalog", "status": "proposed"},
     "measure": {"value": "sum(amount)", "source": "assistant", "status": "proposed"},
     "date": {"value": {"column": "paid_at", "rule": "按支付日"}, "source": "catalog", "status": "confirmed"},
     "filter": {"value": "不含已退款", "source": "user", "status": "confirmed"},
     "reuse": null}
  ],
  "cadence": {"value": {"preset": "weekly", "weekday": 1, "time": "09:00", "tz": "Asia/Shanghai", "period": "last_iso_week"}},
  "people": [], "fields": [], "writeback": null,
  "access": {"use": [{"kind": "group", "name": "门店"}], "observe": [], "manage": []},
  "joints": {"sql_main": {"status": "filled", "by": "assistant"}, "metric_gmv": {"status": "pending"}}
}
  • 每一项都是 {value, source, status};source 取 user / assistant / template / catalog / caliber;status 取 confirmed / proposed / missing。
  • measure 是白话或简单聚合的提议,不是最终表达式;最终的 SQL 和指标式在关节里,由 TemplateApplier 落地并经过检查。
  • reuse:对到已发布口径卡时写 {workflow_id, version, node_id, metric_id},实例化成 caliber_from。

操作:bp_set(path, value)、bp_add(path, item)、bp_remove(path)、bp_ask(question, options[])、bp_template(id)。路径用 JSON 指针。模型和用户共用这组操作;BlueprintApplier 按卡片的白名单校验(比如 people[].policy.kind 只能是 any / all / quorum)。

3.3 模板 agentlab.template/1 与首批五个

模板定义(后端模块):

id, version, label, description, applies_when(给助手看的适用条件)
cards: 用到哪些卡片、各自的约束
skeleton(bp) -> GraphSpec        # 带关节占位
joints: [{id, kind, node_id, field, from_card | generated, check}]
app_skeleton(bp, graph) -> agentlab.app/1   # 在 derive_app 的结果上按模板排布
schedule(bp) -> 定时任务参数 | None
tests: 每个模板至少一张完整蓝图的实例化 + 检查 + 运行

首批五个

模板 骨架 生成关节 确定性关节 应用页
周期看板 periodic_board 入口(统计期)→ 取数(tool)→ 口径卡 → 成果(指标出具) SQL、指标式 统计期字段、定时任务、出具契约的必需指标 印章、指标块、趋势、明细表、「按条件查看」(按需)
审批流程 approval 入口(表单)→ [按金额线分支] → 人工(一级或多级)→ [写回] → 已通过 / 已退回 无 表单、金额线、处理人与策略、时限与升级、写回目标 发起按钮、我发起的、实例进度、待办
异常处置 exception_handling 定时:入口 → 取数(异常清单)→ 分支(有 / 无)→ 人工(负责人填处置说明,表格附在待办上)→ [复核] → 成果 异常的 SQL 负责人、时限、复核策略 异常数指标、本期异常表、处置进度
填报登记 records 入口(表单)→ [审批] → records.append → 成果;另配一张汇总看板(周期看板的骨架,查记录表) 汇总的 SQL、指标式 表单与表结构、审批、行级可见 填报按钮、我的记录(records_list)、汇总看板
核对写回 reconcile_writeback 定时:取数 → 口径卡(核对差异)→ 人工复核(确认要写回的)→ 写回 → 成果 取数的 SQL、差异的指标式 复核人、写回目标与字段对应、确认 差异指标、待复核清单、写回记录
  • 每个模板都能被个人应用用到的部分(只读)标出来:周期看板全部、异常处置去掉人工步骤只看清单、填报登记只看汇总和「我的记录」。
  • 「自由形」:从已有运行或工作流版本导入,没有关节,蓝图只有目的、谁能看、多久更新三张卡片;要改图只能回画布(搭建者)。

3.4 关节类型

kind 填什么 谁填 检查
sql 调用工具节点的 args.sql 模型(look 表) 只读守卫、SQL 猜列、数据源范围、目录检查(GraphChecker)
metric_expr 口径卡一个指标的 expression 模型 只许 cell()、口径卡算术、比较;静态追到原始数据(engine/lineage.static_stops)
fields 入口或人工节点的表单 蓝图「填什么」 agentlab.form/1 校验
assignee / policy / due / on_timeout 人工节点配置 蓝图「谁来做」 流程检查(engine/lint_process.py)、岗位派生
threshold 分支条件「字段 比较 常量」 蓝图 表达式解析器,禁止算术
capability 调用工具节点的 tool 和参数映射 蓝图「写回哪里」 能力登记、写入只在调用工具节点(总方案 A5)、参数按声明类型
contract 成果节点的出具契约(必需指标) 蓝图「看哪些数」 出具契约检查(含指标出具)

TemplateApplier.apply(op) 只收 fill_joint {joint, value},joint 必须是这个模板声明的生成关节;TemplateChecker.issues() 跑上表的检查加骨架比对,交回模型改(最多 2 轮,沿用 Generation 的修正阶段)。

3.5 试运行解析

  • POST /api/blueprints/{id}/trial {inputs?}:实例化 → 保存草稿图 → 发起探索运行(origin_kind=trial)→ 返回 run_id;结束后蓝图状态记 trialed、last_trial_run_id。
  • GET /api/blueprints/{id}/preview?page=…&run=…:派生(或沿用)应用草稿,调 resolve_page(draft=True, trial=run):
    • 所有视图来源解析到这次运行;趋势、按需视图只给这一个点;
    • 印章 from = 试运行、trial = true、notes 加「这不是正式结果」;
    • 流程组件(实例进度、待办)对着这次运行;待办卡按钮写「以试运行身份…」。
  • 权限:蓝图的 manage(共享)或拥有者(个人)。

3.6 发布单

  • POST /api/blueprints/{id}/publish-plan {level}:只读,返回 {steps[], summary[], problems[{code, plain, who, fix?}], ok}。
  • POST /api/publish-plans/{plan_id}/run:按步执行;每步结束写 steps[i];某步失败时状态 stopped,返回白话原因。
  • POST /api/publish-plans/{plan_id}/resume:修好后从停下的那一步接着做;已完成的步骤按结果里的 id 核对,不重做。
  • 每一步调用现有服务函数(发布、定时任务、派生、应用发布、授权),不经 HTTP;预检阶段这些函数以「只检查」方式调用。
  • 审计:发布单的开始、每步、结束各写一条(publish_plan.*),和各对象自己的审计互相引用。

3.7 记录表

  • 写入服务:records/store.py。
    • append(table, values, run_id, actor, action_key):在单进程锁内校验列、写一行、返回 _seq 和 _id;
    • 同一个 action_key 第二次调用返回第一次的结果(幂等)。
  • 能力 records.append:
    • 内置登记;参数是 {table, values};
    • 效果是写入,允许无人值守,查询能力是 records.lookup(action_key)。
  • 查询连接:
    • data/engine.py 打开 origin=records 的源时,在连接上建 TEMP VIEW:每张业务表一个同名视图;
    • 视图条件是 _seq <= 高水位,加 _superseded = 0,加行级过滤;
    • 高水位来自运行的 data_versions;行级过滤取自运行的发起人和这个应用的授权:正式运行以服务身份跑、不过滤;records_list 按看的人过滤。
  • 表版本:「填什么」卡片变化时,按 INCOMPATIBLE_KINDS 判定,兼容的就 ADD COLUMN,否则新建版本文件并登记一行 records_tables。

3.8 文件

  • 接口:
    • POST /api/files(multipart;context 必填)→ 引用;
    • GET /api/files/{id}(按绑定对象核对读权限);
    • DELETE /api/files/{id}(只能删自己还没绑定的)。
  • 控件:x-widget: file,字段 type: object,属性 {file_id, name, size, sha256, type};accept、max_mb 进 x-file。前后端校验同一份:apps/forms.py、lib/schemaForm.ts。
  • 模板函数 file_text(ref, max_chars=…):运行时抽文本,结果记进运行事件(字数、是否截断),抽取的文本按工件存,进证据。
  • 清理:调度循环每小时清一次 24 小时前上传、没有绑定的文件。

3.9 个人应用

  • POST /api/blueprints/{id}/save-personal:校验图满足 chat_policy(节点、工具、无人工、无写入),建或更新 scope=personal 的 App,图存在蓝图上。
  • POST /api/apps/{id}/refresh(个人):发起探索运行(origin_kind=personal_app,身份是拥有者,强加 MASK_RESULTS、READ_ONLY),受每日上限约束。
  • 视图来源 exploratory:只在 scope=personal 的应用里合法(应用检查拦),解析时取最近一次成功封存的 personal_app 运行。
  • POST /api/apps/{id}/submit-review {to}:开 review 待办;搭建者处理时调 POST /api/blueprints/{id}/copy 得到共享副本。
  • can_read_run 加一条:拥有者能读自己个人应用的运行(auth/access.py:74-96)。

3.10 接口一览

接口 权限
POST /api/blueprints(起点:对话 / 模板 / 运行 / 版本) builder;个人:user + 开关
GET/PATCH /api/blueprints/{id}(PATCH 收 bp_* 操作数组) 拥有者或共享蓝图的 manage
POST /api/copilot/blueprint-stream 同上
POST /api/copilot/fill-joints-stream 同上
POST /api/blueprints/{id}/trial、GET …/preview 同上
POST /api/blueprints/{id}/publish-plan、`/api/publish-plans/{id}/run resume`
POST /api/blueprints/{id}/save-personal、/api/apps/{id}/refresh、/submit-review user + 开关,拥有者
POST /api/blueprints/{id}/copy builder
GET /api/templates 登录即可
GET /api/apps/{id}/candidates manage
POST/GET/DELETE /api/files… 见 §3.8
GET /api/apps/{id}/records(records_list 用) use(只看自己的)/ observe

3.11 事件

  • 运行事件:run.origin 的 kind 加 trial、personal_app;file.extracted(文件抽文本:字数、是否截断);records.appended 是动作事件(action.*)的一种,不另起。
  • 蓝图事件存 blueprint_events,不进运行事件。

3.12 权限矩阵(开登录时)

操作 admin builder user
建共享蓝图、试运行、发布单(已发布级) ✓ ✓ ✗
发布单(受管级、降级) ✓ ✗ ✗
建个人应用、刷新、提交审核 ✓ ✓ 开关开着时 ✓
审核个人应用(得到共享副本) ✓ ✓ ✗
看别人的个人应用内容 ✗ ✗ ✗
上传文件 有对应入口或待办的人 同左 同左
下载文件 读得到绑定的运行或记录的人 同左 同左
记录表逐行查看 全部 全部(有 observe) 只看自己的(use)

关登录时全部放行,个人应用的开关照样生效(关着就没有入口)。

3.13 新增检查

  • 蓝图(bp.*,蓝图卡片和发布单预检里显示):
    • bp.missing:缺必填卡片项;
    • bp.unconfirmed:待确认项未确认就发布;
    • bp.data_unmapped:看哪些数没对到表或口径;
    • bp.people_unbound:岗位没有人;
    • bp.reconciler_unbound:写回的能力没登记查询,结果不明时没人核实(同上,只挡发布;发布单上应用检查说同一件事的 app.reconciler_unbound 和核实岗位的 app.slot_unbound 不再重复列);
    • bp.template_mismatch:卡片内容超出模板能力。
  • 模板(tpl.*):
    • tpl.skeleton_changed:实例化的图改了骨架;
    • tpl.joint_unfilled:生成关节没填;
    • tpl.joint_refused:模型想改非关节的位置。
  • 应用:
    • app.view_source_personal:exploratory 来源出现在共享应用里,error;
    • app.hidden_bound:被隐藏组件是唯一绑定某个视图的组件,warning。
    • app.records_rows_exposed:看板的表格绑定的查询直接列出记录表的逐行(没有聚合),warning。正式运行看得到全部行,这样等于所有使用者都能看到别人的记录,绕过了行级可见;逐行查看请用 records_list。
  • 个人应用:personal.policy,图里有使用者不能用的节点或工具(沿用 chat_graph_problem 的说法)。
  • 白话:以上全部登记进 app/plain.py。

第四部分 界面

4.1 新建应用

  • 入口:应用首页右上「新建应用」。使用者在开关开着时也有这个入口,只能建个人应用。
  • 三个起点卡片:
    • 说说你要什么:大输入框加例句;
    • 从场景模板开始:五张模板卡,每张写适用场景、会产出什么、要准备什么;
    • 从已有的开始:最近的问数据回答,加上已发布的工作流(后者只给搭建者)。
  • 问数据的「做成应用」按钮:改为「做成应用(蓝图)」,直接带着这次运行进蓝图,不再跳画布发布框。

4.2 蓝图页

  • 左侧:对话。助手的反问画成可点的选项,「这一轮在等你回答」常显。
  • 右侧:卡片列,每张卡片头上标状态(已确认、待确认、缺)。卡片里每一项都可以点开改,改完标来源「你」。
  • 对到数据的项:显示对到的表、中文名、粒度、业务日期,带一句「为什么对到这张表」,来自 look 和目录。
  • 底部:「用真数试一次」(缺项时置灰,写清缺什么);试过之后才有「发布」(搭建者)、「存成我的应用」(使用者)。

4.3 试运行页

  • 就是应用的样子,顶部横幅写「试运行:数据是真的,但这不是正式结果」,配试运行的输入和「换一期再试」。
  • 流程类:右侧列出试运行里的待办,自己点同意或驳回;写入步骤标「测试环境」或「演练」。
  • 出错时用白话说哪一步、为什么,加「交给助手修」或「回到卡片改」。

4.4 发布摘要

  • 一页:每一步一句白话。问题分三栏:你来定、助手可以修、要找管理员。
  • 确认按钮写清结果,例如「发布并每周一 09:00 自动出数」。
  • 执行时逐步打勾;停下时写在哪一步、为什么、修好后「接着做」。

4.5 页面上直接改

  • 应用页右上「编辑」(manage)。
  • 编辑状态下,组件悬停描边,点中出小工具条:改文字、隐藏、上移、下移、删除。
  • 页底「加一张数据卡片」,弹出候选列表,按口径卡、报告、明细、趋势分组。
  • 顶部「改取数」回到蓝图。
  • 改动自动存草稿,顶部写「有未发布的改动」和「发布」。

4.6 个人应用

  • 应用首页「我的应用」分组,卡片角标「个人」。
  • 印章写「探索 · 未经发布审核」,顶部有「刷新」(写明今天还剩几次)和「提交审核」。
  • 审核中写状态;审核发布后写「已有正式版本:去看看」。

4.7 新组件和控件

  • 文件控件:拖放或点选;显示文件名、大小、上传进度、类型和大小限制;上传失败照实说。
  • records_list:列表、筛选、按时间排序;每行可以展开看全部字段和附件;被更正过的行显示「已更正」,可以展开看历史;使用者只看自己的,标题写「我的记录」。

4.8 文案与检查脚本

  • 文案:termsApp.ts 新增蓝图、模板、试运行、发布单、个人应用、记录、文件各组说法;后端白话层 app/plain.py。
  • 新检查脚本:
    • check-blueprint:起点、卡片、反问、试运行、发布摘要;
    • check-personal:使用者建、刷新、提交审核;
    • check-records:填报、行级可见、更正、表版本;
    • check-files:上传、绑定、下载权限。
  • 要更新的:check-apps(编辑状态、隐藏、候选)、check-chat(反问收尾、做成应用改道)、check-studio(反问记录)。
  • 检查脚本的新约束:每个问题编号都要有白话。

第五部分 实施组织(供下次审批,这一步不执行)

  • 分支:从最新 main 开集成分支 feat/normal-user-apps,所有包在同一分支、同一工作区。

  • 第 0 步契约骨架:编排者一次提交,包括:

    • 新表和新列、agentlab.blueprint/1 和 agentlab.template/1 的 pydantic 模型;
    • 关节类型和操作的白名单、app/plain.py 的框架;
    • 新路由的桩(501)、records.append 和 table_import.new_period 的能力登记;
    • 前端类型和接口桩、占位路由。

    之后冻结热点文件:db/models*.py、db/base.py、core/events.py、typesApp.ts。

  • 一个 workflow,六个包,每包一个完整代理,然后一个统一复核、一个集中修复:

包 范围
B1 蓝图与模板(后端) Blueprint 对象和操作、模板登记和五个模板、实例化和骨架比对、BlueprintApplier/TemplateApplier 与 Checker、两个生成入口、look 新类别 caliber、并行填关节
B2 试运行与发布单(后端) 试运行解析(trial 旁路)、派生自图、发布单的预检 / 执行 / 续做、白话层的内容、反问收尾的服务端部分
B3 记录表与文件(后端) 记录表存储与连接层的临时视图、records.append、表版本、records_list 解析、文件存储 / 接口 / 绑定 / 清理、file_text、table_import.new_period
B4 个人应用(后端) scope 与拥有者、exploratory 视图来源与印章、刷新与上限、提交审核与副本、can_read_run、开关
F5 创建链路(前端) 新建应用三个起点、蓝图页、试运行页、发布摘要、问数据「做成应用」改道、反问在问数据和画布的收尾
F6 页面编辑与新组件(前端) 编辑层、隐藏、候选、文件控件、records_list、个人应用的界面
  • 合并顺序:B1 → B2 → B3 → B4 → F5 → F6,编排者做集成修正。
  • 验证分层(沿用):代理只跑相关测试和 tsc、check-copy;完整关口由编排者在合并后跑一次。
  • 真实模型验收和端到端验收:都在关口之后跑。
  • 合入 main 之前:先问用户。

第六部分 验证与验收

  • W1 看板(搭建者,从一句话开始):「每周一看各门店上周销售额、订单数、退款率,不含退款单」。

    • 反问一次(按下单日还是支付日);
    • 卡片对到正确的表和业务日期;
    • 试运行出真数,点数字追到单元格;
    • 发布摘要一页,确认后 9 步全部完成,应用打开就有数;
    • 从说完需求到看到试运行结果,记录用时和模型调用次数。
  • W2 审批(搭建者,从模板开始):报销审批,金额线 5000,财务组全员同意,48 小时升级给总监,通过后写到假外部系统。

    • 试运行里自己点完审批,写入走测试环境;
    • 发布后真实走一单;
    • 中途故意让岗位没绑人:发布单停在预检,白话说「谁来做」缺人。
  • W3 填报登记:门店每日报损。

    • 两位使用者各提交几条,彼此看不到对方的记录;观察者看全部;汇总看板是全量的;
    • 更正一条:原行保留,汇总按更正后的算;
    • 改表单:加一个可选字段不出新版本;把数字字段改成文字,出 v2,旧记录还在。
    • 同一次运行查两遍,数一样(高水位)。
  • W4 真实模型:本机助手模型、deepseek-v4.1-flash、glm-5.3,跑 W1、W2 的蓝图和关节,记录:

    • 调用次数、查看次数、首个操作时间、总用时;
    • 反问是否发生、关节检查的修正轮数。

    目标:

    • 起草蓝图 ≤ 3 次调用;
    • 填关节在快速模型上一分钟内完成;
    • 卡片和关节出错时,用户能看懂、能改。
  • W5 个人应用(使用者):

    • 开关打开后,使用者从问数据的一次回答建「我的退款周报」;
    • 刷新受每日上限约束;印章写「探索」;结果遮罩;
    • 提交审核后,搭建者在副本上发布,提交人收到通知;
    • 管理员看不到个人应用的内容。
  • W6 文件:

    • 报销单附发票 PDF,agent 读出金额;
    • 门店上传本周报表 Excel,作为一期导入(需确认);
    • 不能读那次运行的人下载 403;没绑定的上传 24 小时后被清理(用缩短的时间测)。
  • 关口:后端全量 pytest、tsc、check-copy、泄露扫描、check-all --stacks 2(含新增的四个检查)、check-auth;真浏览器端到端按 W1–W6 截图复核。


第七部分 风险与对策

风险 对策
蓝图对错数据(白话对到错的表) 对到的项默认「待确认」,卡片上写为什么对到这张表;试运行看真数;数据目录越完整越准,卡片上提示补目录
模型慢或不稳 关节少、并行、确定性兜底(P3);验收按真实模型记录耗时
模板覆盖不了需求 「自由形」加搭建者回画布;模板登记可扩展;收集落在模板外的需求
发布单中途失败 步骤顺序让失败无害;可续做;摘要照实说停在哪
记录表并发写 单进程锁、事务短;README 写明不面向高频写入
行级可见被绕过 在连接层建临时视图,模型和 SQL 都看不到底表;测试覆盖直接写 main.<表> 的 SQL 被只读守卫拒
看板把记录逐行列给所有人 正式运行看全部行是设计如此;应用检查对「逐行列出记录表」的表格给警告(app.records_rows_exposed),发布摘要写明使用者能看到什么
个人应用变成「影子正式应用」 永远探索、印章写明、不能分享;共享只能经审核发布
文件泄露 下载按绑定对象核对读权限;界面写明谁能下载;不做公开链接
白话说错 白话层只有一份,按编号映射,检查脚本防遗漏

第八部分 待拍板

# 问题 推荐
Q1 记录表的存储 平台托管 SQLite、只追加、序号高水位钉版本、连接层临时视图做行级可见(N8)
Q2 填报提交走不走工作流 走:模板里 records.append 写入节点,可以加审批,沿用动作的幂等和对账
Q3 使用者建个人应用的开关 管理员开关,默认关(同问数据)
Q4 个人应用的刷新 只手动刷新,加每日上限;不做定时自动刷新(自动刷新等于给使用者开定时任务)
Q5 文件的类型和大小 白名单:PDF、Word、Excel、CSV、图片(PNG/JPG)、纯文本;大小沿用 max_upload_mb(默认 50MB);未绑定的 24 小时清理
Q6 首批模板 五个:周期看板、审批流程、异常处置、填报登记、核对写回,外加自由形
Q7 流程类试运行的审批 派给试运行的人自己点;写入走测试环境或演练;不触发超时升级
Q8 发布单里「跑第一次」 看板类默认勾选,流程类不跑

附录 A 代码位置索引(main@69c5c74)

位置 内容
backend/app/api/copilot_gen.py:567 Generation;:589 起草、:593 修正、:38 修正轮数
backend/app/look/base.py:22-27,87-110 查看上限;Applied / Applier / Checker 协议
backend/app/api/copilot.py:1679 /generate-stream;:1744 只问不改的一轮;:2364 /app-stream;:2468-2550 from-run 与来源记录
backend/app/apps/ops.py:264 polish_problem(护栏的写法)
backend/app/engine/autofix.py:1240 forbidden_changes(骨架比对的写法)
backend/app/apps/derive.py:85-90 派生时视图绑定时任务
backend/app/apps/resolve.py:82-108,454-476,541-546,203-221,813 最近封存的规则;草稿解析;按运行解析;印章来源;趋势无钉住项报错
backend/app/apps/spec.py:35-42,47,82,97-107 表单控件、视图来源、组件模型
backend/app/engine/launch.py:95-176 运行类别的判定
backend/app/auth/chat_policy.py:25-73 使用者能用的节点和工具
backend/app/api/runs.py:108-123 使用者运行的限制
backend/app/auth/access.py:74-96 can_read_run
backend/app/data/catalog.py:12-21 数据目录项的形状
backend/app/data/raw_store.py:33-43 按哈希存原件
backend/app/data/table_versions.py:1-33,344-360 表的版本模型和存放
backend/app/data/recipe_accumulate.py:38-41 不兼容的列变化
backend/app/engine/runner.py:242-262,1013-1045 data_versions 的固定;封存
backend/app/api/workflows.py:427-503 发布
backend/app/api/schedules.py:461,scheduler/service.py:39-56 建定时任务;模板变量
backend/app/api/apps.py:186,203,251,370,474,273 建应用、派生、草稿、发布、授权、归档
frontend/src/store/chat.ts:1241,1433 问数据整轮重来;没有反问分支
frontend/src/apps/ViewRenderer.tsx:75-84 组件包在 data-node 里
frontend/src/pages/ChatPage.tsx:479-523 问数据的「做成应用」

附录 B 术语

  • 蓝图:用户的需求被整理成的一组白话卡片,是生成工作流和应用的依据。
  • 卡片:蓝图里用户能判断对错的一件事(看哪些数、谁来做……)。
  • 场景模板:一类应用的骨架,带可填的关节。
  • 关节:模板骨架里允许填的位置;确定性关节按卡片填,生成关节由助手填。
  • 试运行:跑一次还没发布的图,用真数看应用的样子;不是正式结果。
  • 发布单:一次确认发布的编排,逐步执行、可续做。
  • 个人应用:使用者自己建、只有自己看得见的应用,永远是探索。
  • 记录表:平台托管、只许追加的表,填报登记的每次提交是一行。
  • 高水位:一次运行开始时记录表的最大序号,这次运行只看到这之前的行。
  • 白话层:问题编号到白话说法的唯一映射。