ThearchyHelios
|
cd9bf0aedb
|
合并查询节点(四):前端——节点库、卡片摘要和类型色,检查器从上游查询节点里选输入并填别名,运行详情展示输入、合并 SQL 和警告,证据面板展示合并的来源和逐格来历,推断来源写明经过的合并;补上解码、回答流、证据面板、编排页的检查
|
2026-10-03 10:22:46 +08:00 |
|
ThearchyHelios
|
e4f8ee5d08
|
Excel 导入改造期 4:证据下钻——报告里引用上传表格的数,点开能看到它来自原表格的哪一格;裁判核对时看得到来源、口径、接受理由和区域外文字
推断的来源(新 engine/direct_select、data/provenance、data/provenance_db;api/evidence 新增推断来源接口)
- 只对直接引用一列的查询下钻:受限文法用和执行时相同的 SQLite 分词读法,别名互换、自连接、表达式、聚合、子查询、集合运算(含藏在注释、字符串、方括号、转义、BOM 之后的)一律只给表级来历;再用 EXPLAIN 字节码和授权回调兜底
- 结果带齐主键、值非空,按主键参数化回查只读快照、类型和值严格一致才给格子;不一致时整份重算库哈希,哈希不符标红
- 从导入清单和溯源找回格子:宽表、交叉表逆透视的长表(日期取自表头格、年份取自统计期、行标签、分段标题)、列表、合计表、按期累积的并集(换算到所属那一期);原文与规范写法不同时一并列出
- 清单读不到、登记哈希与清单不一致、快照已回收、遮罩列,都只给表级来历或不给,写明原因;原件已清除、接受已作废照实显示;未封存的运行照常推断并标明
- 相关核对:这一行、这一期的核对结论与接受理由
裁判摘录(engine/judge)
- 带期 4 标记的报告,上传表格的查询旁加来源行、口径行、接受理由、可见单元格里的区域外文字和人工录入的统计期;限定语写明这些数未经系统核对,长度有上限、截断时写明
- 隐藏行列里的区域外文字不送;设了遮罩的源只写条数
前端证据面板(run/EvidenceProvenance;ManifestBody 从版本页抽出复用)
- 「推断的来源」一节与已封存的证据分开标注:格子坐标、取自哪些格、年份来源、规范写法、回查、相关核对、数据版本与各期;标红与拒绝原因按状态显示
不变的部分
- 文档顶层加 provenance 标记,只有新报告走新路径;期 4 之前的运行、非上传源、手工源的面板、复核、裁判摘录逐字不变,由金样测试钉住
- 执行器给区域外文字记录是否在隐藏行列;不建「导入校验」系统表(D6)
|
2026-10-02 14:38:10 +08:00 |
|
ThearchyHelios
|
2cdbf9a733
|
Excel 导入改造期 3:网格交互、自助修复、按期累积——每月的报表可以累积成一份跨月的表,漂移了点修复按钮就能接着导入,网格上框一块就能写进配方,版本页可以回滚、移除一期、清除原件
按期累积(新 recipe_accumulate;table_versions 扩展)
- 按统计期出的交叉表报表首次导入时默认「按期累积」,由人确认;一期一个构建,两期及以上物化成一份新的 SQLite 快照,不用视图
- 统计期完全相同要勾「替换该期」,部分重叠拒收,中间有空缺要确认;物化前逐期核对库哈希和行号,并集上另加行数守恒、主键唯一、日期落在所属统计期三条核对
- 配方演进分三档:新增列(早期各期为空值)、退役的列和表保留旧值(只在这次才退役时要勾)、不兼容变化只能显式「重新开始累积」
- 快照 id 在累积时含目标配方和模式;同样的输入物化两次库哈希相同,同一文件换名再传直接复用
- 说明按期合并,任何一期没核对过就不写「已核对」;各期维度取值不同、退役列、合计表都有提示,经过数字检查
修复按钮(新 recipe_fixes;执行器给问题带结构化参数)
- 加入标签、移除标签、声明为合计行、分段标题改名、编辑恒等式成员、按锚点忽略行/列/区域外文字、声明占位符、声明隐藏行处理、工作表改名
- 服务端算补丁,先预览新旧配方对照、再应用,可以撤销;补丁封闭,不引入正则、自由文本常量和写死坐标;修复进确认清单
- 只改分段标题、新标题仍含原来选的词时沿用已确认的常量,不算破坏性变更;修复后只查改动过的段的认领,没改的关系交给试运行核对
框选转锚点(新 recipe_select)
- 网格上框选(鼠标、键盘、区域输入),按八种用途换算成按文字认的锚点,立刻干跑比对;配方里不存坐标,泛化不了时列出原因
- 整张表挪位后同一配方照样认得;框进合计行、夹着小计、表下说明都有明确的处理
确认与对照(recipe_confirm、recipe_diff、新 recipe_compare)
- 改配方后确认清单只列有变化的项(同时按本期数据比较),不用每修一次重勾十来项;新旧配方对照界面;按规则重新起草并把名字对齐到现行配方
- 改配方后已体现在配方里的回答保留,矛盾的列出来请人重答
- 「排除的行」写进回执和导入清单:隐藏行、跳过的空行、按配方忽略的行、停止后的文字行、只核对不另存的合计行
版本页(新 source_versions、api/source_versions;前端 VersionsDialog)
- 快照与导入记录列表、启用旧版本(可写理由)、查看清单、移除一期、作废接受、清除原件;写操作带界面看到的当前版本,并发改动回 409,不静默丢期
- 除当前版本和被运行引用的版本外,再保留最近 3 个版本供回滚;遮罩列丢失要明确确认
期 1、期 2 遗留
- 重传撞上被改过的构建文件:能用本次上传恢复就隔离旧文件再恢复,否则回 409,不再是 500
- 跳过空行的列表不再把表下隔一行的说明读成数据;不像说明的单格行要人确认;受影响的配方构建换 id
- 版本存储加进程级文件锁:第二个进程拿不到锁时不清理、不回收,写操作回 503(部署本来就是单进程)
|
2026-10-02 07:38:44 +08:00 |
|
ThearchyHelios
|
2d5e50fa88
|
Excel 导入改造期 2:配方引擎与确认向导——表从哪一行哪一列开始、哪些是合计、日期怎么补年份,都写进一份可校验的配方;首次导入经人确认,之后每期按配方重放并核对
配方语言(agentlab-recipe/2,新 recipe_types、recipe)
- 封闭、不含正则、保存时静态校验:常量只能从表里的候选文字里选,占位符只能映射成空值,单位从固定词表里取
- 交叉表和列表两种版式;分段各自定位、不依赖先后顺序;合计按标签语义区间核对,关系恒等式、口径不同的声明都写在配方里
- 固定的解析器集合(parsers/2):全角、各类横线、千分位、月日写法宽容归一,规范写法存库、原文另记
执行器(新 xlsx_cells、recipe_engine)
- 两遍读取逐行对账,每个非空格都要有去向(格子账);交叉表逆透视成长表,按统计期补年份并断言覆盖整期
- 合计行改作核对并另存一张表,不和明细混在一列;区域外文字、隐藏行列、合并单元格、空行之后的文字都单独报出
- STRICT 加主键写临时库;按锚点段记溯源,供后续证据下钻用;大表流式读取,扫描在 sheetData 处停止
校验与说明(新 recipe_checks、recipe_notes)
- SQL 核对分通过、不一致、无法核对三种结果,失败分结构、数据质量、需确认三类;结构类直接拒收
- 数据质量类可以写理由接受,接受不改库、只改状态和说明
- 表的说明按核对结果生成、不含数字,经过数字检查;没核对过的不写「已核对」
起草(新 recipe_suggest、recipe_ai)
- 先用规则起草:建议卡片加待确认问题(关系要不要登记、占位符表示什么)
- 规则起草不完整时才提示 AI 兜底:界面先展示要发出的全部文字,用户同意才发;只发结构,数字、隐藏行列、遮罩列不发,公式先脱敏;同意绑定模型和服务方;AI 只写配方,有限次修订,记 token 和费用;截断单独识别
- 无论规则还是 AI 起草,都必须人工确认才能启用
导入流程(新 recipe_imports、recipe_confirm、recipe_diff、api/table_imports)
- 暂存 → 起草 → 回答问题或改配方 → 试运行 → 勾确认项、写接受理由 → 确认并启用;接入期 1 的构建、导入记录、快照和运行固定版本
- 上传新一期按已确认的配方重放,不调用模型,出回执和差异卡,手动启用
- 改配方报破坏性变更(breaking:*)、单位变化、工作表改名的兜底,都要逐项勾选确认
前端导入向导(新 pages/import)
- 原始网格(行列号、去向着色)、建议卡片、配方面板(表单,不露正则)、试运行回执、确认清单、差异卡、AI 起草的同意对话框
|
2026-10-01 22:00:07 +08:00 |
|
ThearchyHelios
|
8059cd5147
|
Excel 导入改造期 1:止血与版本底座——写错的列名不再静默得 0,导入不再悄悄读错,每次上传生成不可变的版本,运行固定发起时的数据版本
连接层
- 所有 SQLite 数据源关闭双引号字符串兼容(DQS),写错的双引号列名直接报错;设置失败就拒绝连接,启动时自检
- 连接缓存按数据源加版本快照分键、按源整体失效、最多 32 个;版本快照以 immutable 只读打开,建连接前核对文件哈希,并在借出连接时核对文件指纹,被改过就拒绝查询
- 结构探查遇到取不了列的视图时逐个对象重取,只跳过坏的那个并注明原因
解析(新 xlsx_scan + tabular)
- 先流式扫描真实边界和版式(不信 dimension),再按边界读值,不整表进内存;按 Excel 自身上限设硬上限,并发解析最多两个
- 隐藏和深度隐藏的工作表不导入;稀疏区域、无缓存值的公式、同名工作表、DTD 拒收
- 去掉空行和两侧空列;列名表名统一规范化(新 names 模块)并保留原表头,撞名不再互相覆盖
- 前导零、超长数字按文本;千分位按数字;日期不带 T;带单位的数字文本写进回执
- 数字列混入非数字、交叉表和多块结构必须由用户决定(422),可选存空值或按原样导入(未规整);STRICT 建表,整份在一个事务里写完
版本底座(新 table_versions、raw_store)
- 每次上传写新库文件、落盘、0444,事务里写构建、导入记录、快照后才切指针;失败时旧版完整可查
- 原件按哈希存档,不提供下载,可以清除;上传源不许改连接字段、不再重新探查;手工 SQLite 源不许指向上传目录和应用自己的数据文件(按文件身份判断)
- 启动时清理半成品与孤儿文件、给老上传补初始版本;被运行引用的版本不回收,发起运行与回收互斥
- 上传接口的解析移到线程池
运行固定数据版本
- 发起时把图里(含子工作流)用到的上传源的当前快照记进运行记录和 run.started,续跑、审批恢复沿用;数据源工具按固定的快照生成描述、表结构并查询
- 固定的版本没了、源被删除后同名重建,都明确报错,不改用当前版本或同名的新源;查询快照记 data_version
证据层
- 新报告记 entity_syntax: 2,[[t:明细]]、[[c:明细.金额]] 和 SQL 里不加引号的中文表名都认得,名字按 SQLite 的口径查找;老报告按原规则复核,结果不变
- 写作规则改为中文表名、字段名一律用 [[t:]] / [[c:]];未规整的表在工具描述、写作目录和裁判摘录里标出
前端
- 上传弹窗接上导入前的决定(混合列、交叉表)和导入回执(跳过的隐藏表、类型转换、警告、原表头对照、未规整标记),上传前告知原件会保存
- 数据源卡片按 origin 认上传的表格,显示当前版本;上传的表格不再给「探查结构」
|
2026-10-01 13:42:04 +08:00 |
|
ThearchyHelios
|
78f24dfe3f
|
Docker 镜像内置 microVM:构建时装好运行时、预拉沙箱镜像;deploy.sh 检测到 KVM 先试启动一台,起得来才交给容器;没有 KVM 时服务端不再选 microVM
- microvm_sandbox:Linux 上 available() 要求 /dev/kvm 存在且可读写,不可用时给出具体原因(没装、运行时没装、没有 KVM、没有权限)
- 新增 app.sandbox.selftest:真起一台 VM 执行一行代码,部署脚本用它判断
- Dockerfile:WITH_MICROVM(默认 1)构建期 install 并 pull python:3.12-slim,自带的 msb、libkrunfw 改为链接,镜像约多 150 MB
- deploy.sh:ENABLE_MICROVM=auto|0|1,有 KVM 时用临时容器试启动,成功才加 --device /dev/kvm 和所属组;部署完和 status 打印实际用的沙箱
- package-docker.sh:--no-microvm;部署说明、配置模板、README 同步
|
2026-09-30 11:19:25 +08:00 |
|
ThearchyHelios
|
fec356089c
|
Merge pull request #15 from ThearchyHelios/feat/deploy-scripts
生产部署:prod.sh 直接运行,package-docker.sh 打 Docker 离线包(镜像 + 一键部署脚本 + 配置 + 说明)
|
2026-09-30 10:10:06 +08:00 |
|
ThearchyHelios
|
bd8306bf95
|
结论句裁判改造:「不支持」拆成「证据相矛盾」和「证据不足」,补全交给裁判的证据,旧判定可按新规则重判
- 判定取值改为:支持 / 部分支持 / 证据相矛盾 / 证据不足 / 不是结论。只有证据和句子冲突才判相矛盾;
证据里没有相关信息判证据不足,并写明缺什么(比如 SQL、字段清单)
- 出具判档不变松:证据相矛盾按 on_unsupported 降档或不予出具;证据不足记一条缺口,最多降档、不能判完整出具;
改造前的「证据不支持」照旧当相矛盾处理
- 补全证据摘录:表名附字段清单和类型(最多 60 个,快照不全时注明,遮罩列不列);口径卡指标附上每个输入
取自哪次查询、哪一格,以及那次查询的 SQL;一律只读封存范围内的工件
- 理由上限从 60 字放宽到 120 字,在句子边界截断,不再截在半句中间
- 裁判模型:按需裁判也判断是否和写作模型相同、是否在价格表里,证据面板里醒目提示;
设置页没配裁判模型时写明「未单独配置,将使用助手的模型」
- 新判定带规则版本:改造前的判定在界面上标「按旧规则判定」,可以请模型按当前规则重新判断
- 界面:证据相矛盾用红色;证据不足用中性色和 ○,写明缺什么;讲方法、讲结构的句子在证据面板里直接列出
对应的 SQL 和字段清单;出具横幅、审计表、报告节点印章的计数都分开这两档
- 顺带写稳 check-publish 里一条等按钮状态的断言(并行时偶发)
|
2026-09-30 10:04:52 +08:00 |
|
ThearchyHelios
|
6af4e396b9
|
生产部署:prod.sh 直接运行,package-docker.sh 打 Docker 离线包(镜像 + 一键部署脚本 + 配置 + 说明)
- 服务端可以托管前端构建产物(AGENTLAB_WEB_DIST):页面路由回退到 index.html,/api 下不存在的路径照旧是 JSON 404,
挡住路径穿越;index.html 不缓存,assets/ 带哈希的文件长缓存;没设时行为完全不变
- scripts/prod.sh:不用 Docker 的生产运行。按需构建前端,单进程、不热重载,默认只听本机,--host 开放到局域网并提示没有登录
- Dockerfile(多阶段构建,Linux 镜像):前端在构建机架构上构建,运行阶段用 python:3.12-slim,依赖装进独立的 venv,
以非 root 用户(uid 10001)运行,数据在 /data,带健康检查;.dockerignore 排除 .env、data/、各种库文件,镜像里实测没有密钥和数据库
- scripts/package-docker.sh:构建指定平台的镜像(默认按本机架构,可选 linux/amd64),打成
release/agentlab-<版本>-linux-<架构>.tar,里面有 image.tar、deploy.sh、agentlab.env、README.md、SHA256SUMS
- deploy/deploy.sh:目标机上只依赖 Docker,离线可用。install / start / stop / restart / status / logs / upgrade / uninstall;
检查架构和配置,自动导入镜像,替换容器时保留旧容器以便回退,数据永远保留,日志轮转
- 修正 bubblewrap 的可用性判断:以前只看有没有装 bwrap,容器里和 Ubuntu 24.04 默认配置下装了却建不了命名空间,
每次执行代码都失败;现在真起一次试,不能用就退回下一个后端,并写明原因
- README 增加「生产部署」一节,部署细节见 deploy/README.md
|
2026-09-30 09:48:16 +08:00 |
|
ThearchyHelios
|
03c18c4d78
|
界面文案整改:口语改书面、术语统一、不再露出实现细节;修掉几处说错的提示;新增文案检查防回潮
一、文案(前端约 1600 处,后端传到界面的约 880 处)
- 术语统一:工作流层面用「运行」(继续运行、重新运行、试运行),节点和工具层面用「执行」,不再用「跑」;
总时长 / 执行时长 / 等待审批;服务端;助手;Agent;token;工作流;固定版本;模型接入;系统提示;批准 / 驳回;受管级别
- 口语改书面:取不到、没取回来、读不懂、连不上、多半、照样、兜底、揉成这类说法改成「无法 / 未能 / 失败 / 请」;去掉拟人写法
- 不再露出实现细节:界面上不写 checkpoint、同一拍、哈希(凭证处除外),不写改版历史(「以前…」「老数据」);
报错、校验、门禁、自动修复里裸写的配置键名和枚举值换成界面上的中文标签,由新的 backend/app/engine/labels.py 统一提供;
发布弹窗里结论句的选项不再是 off / require_citation
- 同一件事只留一种说法:生成阶段文案、出具档位说明、画布锁定提示各合成一份
- 去掉界面上的工业场景示例(MES、ERP、「张工」「王工」)和真实产品名,换成通用示例
二、说错的提示(都有测试证明)
- 数据库测连接失败时的专用提示以前从不出现(按报错文字全等比较,永远比不上):改为按错误类型判断,不再拼出重复的前缀
- 「设置 → 供应商」改指「设置 → 模型接入」;「在设置里建数据源」改指「数据」页
- 缺少 API Key、模型接入已停用的报错不再夹英文,写明去哪里填
- 两处原因说错(「这种引用在后续版本支持」「这一版还不支持」)改成真实原因
- 节点说明里不准确的说法改正:代码节点在 microVM 或系统沙箱中执行;报告撰写、口径卡的说明不再说过头
- 导出的证据 CSV 里两列同名,第三列改为「片段编号」
三、前后端对齐
- 前端按后端原文识别日志、报错的地方,新旧两种写法都认得(历史运行里存的是旧文字),逐条补了新旧原文的核对
- 后端交回模型的原指令放进单独的 for_model 字段,前端在解析响应时去掉,不会显示到界面上
四、检查
- 检查脚本里按原文匹配的断言改成新文案;改完会变成永远成立的反向断言逐条修正,并做了变异验证
- 新增 check-copy(静态扫描前端界面字符串和后端上界面的文案):再出现「跑」「后端」「多半」「令牌」、
界面上的 Copilot、裸写的配置键名、半角标点等就报错;确需保留的写进 scripts/copy-allow.json 并注明理由
|
2026-09-30 08:05:15 +08:00 |
|
ThearchyHelios
|
80ac1d9c88
|
dev.sh 支持 --host:前端在局域网上也能打开;认不出的参数直接报错
- 以前 dev.sh 不读任何参数,./scripts/dev.sh --host 被悄悄忽略,前端照旧只听本机
- --host 让前端开发服务器监听所有网卡,启动时打印局域网地址;后端仍只听 127.0.0.1,
页面里的 /api 由前端开发服务器在本机转发,局域网里的设备不需要、也不该直连后端
- 用法说明里写明:AgentLab 没有登录,同一网络里打得开页面的人都能用它,只在信得过的网络里开
- 认不出的参数报错并打印用法(-h / --help 看用法)
|
2026-09-29 17:28:34 +08:00 |
|
ThearchyHelios
|
04450e8f51
|
check-all 提速:从约 10 分钟降到约 3 分钟;可选隔离环境,边改代码边跑也不被打断
- 关浏览器不干等:系统 Chrome 建过浏览器上下文之后,关浏览器要空等约 4.5 秒才退出。
check-all 给检查用的 Chrome 套一层转发,见到「关浏览器」就替它应答、再让它退出(几十毫秒),
其余消息原样转发,检查脚本一行不改;CHECK_FAST_CLOSE=0 关掉。最慢的两项因此从 317 秒、281 秒降到 184 秒、117 秒
- 拆开最慢的两项:run-states 拆成 3 份、canvas-fx 拆成 2 份,分到不同的道;每一段恰好跑一次,断言数不变,
以后新加的段自动落进「其余」那一份;汇总表按原检查名合成一行,没过的只重跑那一份
- 量时间的几份同一时刻最多跑一份(原来是整项互斥),这条链排在最前面;默认 4 道改成 8 道
- 新增 --stacks N(要 CHECK_DATA_SRC 指定数据目录):从当前工作区另起 N 套后端和前端,
各用一份数据拷贝,端口自动挑空闲的;后端不热重载、前端不监听文件,代码中途被改也不会打断检查;
跑完、出错或被中断都会把进程和临时目录清干净
- 前端开发服务器新增两个可选开关(AGENTLAB_VITE_NO_WATCH、AGENTLAB_VITE_CACHE_DIR),不设时行为不变
- 写稳一处断言竞态:数据页点亮「数据」的判断,路由状态在地址变化之后才提交,改成最多等 3 秒再判,判定条件不变
实测(17 项、4361 条断言,一条没删、没放松):原来 9 分 58 秒;默认跑法 3 分 26 秒;
--stacks 2 --lanes 12 约 3 分 03 秒(另加约 10 秒起服务),连续 3 次全绿
|
2026-09-29 16:05:49 +08:00 |
|
ThearchyHelios
|
050bea4b72
|
可点击证据第五期:老工作流一键升级为可追溯结构;受管问数据图的 cells 一键选;pytest 并行
一、一键升级(只给预览,不自动保存)
- engine/upgrade.py:确定性改写,纯函数、重复执行结果不变
- R1 契约的叙述指向模型调用节点 → 换成报告撰写节点(system 和 prompt 合成写作要求,模型配置照搬),契约改成 report_from
- R2 没有契约、但模型写的文字进了出口,而且上游有取数 → 同样换成报告撰写
- R3 input → 智能体 → 出口(问数据的典型图)→ 中间插入报告撰写节点,出口字段接报告的正文;
只改直接相连的出口,经过别的节点读它的出口原样保留并写明原因
- R4 配了 output_schema 的智能体 → 打开 cite_fields
- R5 代码节点的产出被口径卡读 → 不改,只建议标成取数,等人确认
- 一个叙述里混了几个写作者的,不猜,整步不做并写明原因
- POST /api/copilot/upgrade-evidence:返回改动、说明和改完的检查结果;assist 时再交给 Copilot 把纯算术的代码节点
改写成口径卡表达式,不是纯算术的保留并给警告
- 升级和发布前修复走同一道「不许降低要求」的检查,只多两个由升级点名的例外:模型调用换成报告撰写、R3 改接的那条连线;
改完不能更糟
- 校验对符合 R1–R4 的旧结构给一条建议「可以升级为可追溯结构」,不算提示、不挡发布
- Copilot 自查:报告撰写节点上游没有任何能引用数字的来源时打回
二、界面
- 问题面板的快速修复「升级为可追溯结构(预览改动)」:逐项写「节点 · 字段:原值 → 新值」,节点类型的变化写成
「模型调用 → 报告撰写」,新插的节点和改接的连线另外写明;「应用」后走现有的保存,全图默认不丢
- 画布改过、存失败、画布被锁都有明确状态;保存失败后手动存成功会清掉「没存上」的提示,
画布和预览对不上时「重试保存」不再盲存;校验接口失败时升级块照样在
- 记录页里没有逐段证据的运行给横幅「这次的报告没有逐段证据 → 升级这张图」,跳到编排页并打开升级预览
- 修掉一处旧缺陷:应用修复时只换配置、不换节点类型,模型调用换成报告撰写后存下去的还是模型调用
三、受管门禁:cells 一键选
- 图里没有口径卡、契约又没写 cells: true 的受管图,以前能过门禁、正式运行却每次失败;现在门禁拦下,
发布弹窗给两个选项:写 cells: true,或交给 Copilot 改成口径卡指标(人亲手选的不算替人打开,Copilot 自己打开照样拦)
- 「修完要更好」的复核不再把改完才冒出来、只能由人拍板的这一处算作改坏:Copilot 补上契约、把 cells 留给人问,
这一步会被采用,不再被判「冒出新的问题」作废
四、测试
- 开发依赖加上 pytest-xdist(锁文件只多了 pytest-xdist 和 execnet):全量后端测试 -n auto 约 16 秒,原来串行约 2 分钟
- 修掉并行下暴露的一处测试竞态:运行先落终态、再另一个事务封存,测试改成等封存完成再核对
|
2026-09-29 13:27:27 +08:00 |
|
ThearchyHelios
|
9727c21d8d
|
可点击证据第四期:结论句由模型裁判;只引单元格的问数据图能发成受管
一、结论句裁判
- 报告节点新增 claims: judge:写完、核对完之后,把结论句和它引用的证据摘录交给裁判模型,
判为支持 / 部分支持 / 不支持 / 不是结论,附一句理由;判定只标注,不改写正文
- 送裁判的句子按固定规则预筛:带引用的、含方向词或因果词的送去判,短句、过渡句不送
- 证据摘录:指标写名称、值和原式;查询给 SQL 前 300 字和被引用的行,被引用的列一定给到;遮罩列一律不给;
规则里写明「数字已经由系统核对过,不要复算」
- 一份报告合并成一次调用,超出句数就分批;结构化输出失败时退回 JSON 解析,再失败记为未裁判
- 裁判模型的优先级:节点上写的 > 设置里的「证据裁判模型」> Copilot 的模型;思考关闭;
和写作模型相同时给警告(等于自己审自己)
- rewrite_once(默认关):把不支持的句子和理由交回写作者只改这几句,再裁一次;
改写稿冒出原稿没有的问题就不采用,保留原稿和原来的判定
二、成本上限
- 每份报告的句数、金额、时长,探索运行每次点击的金额,全局每日金额,每一项都可以自定义,也都可以设成不限;
默认 40 句、$0.05、30 秒,每次点击 $0.01,每天 $2
- 调用前先估算,超出上限按优先级截断:先判带引用的,再判含方向词、因果词的,其余记为未裁判并写明「已到上限」;
不报错,也不让报告失败
- 每日花费按本地日期计数,持久化在设置表里;重跑同一次运行不重复计费;价格目录里没有的模型只计次数,并写明估不出金额
三、正式运行与探索运行
- 正式运行在报告节点内裁判,判定写进报告文档、随文档封存;不支持的按 on_unsupported(degrade / withhold)降档或不予出具,
裁判没跑成记缺口,不能判完整出具
- 探索运行按需裁判:POST /api/runs/{id}/evidence/judge,点开哪句判哪句;判定作为 evidence.judged 事件追加在封存之后,
标「封存后追加」,封存核对仍然一致;只读封存范围内的工件;同一句不重复判;运行在裁判期间被接着跑时不记录
- 正式运行调这个接口返回 409,写明「正式运行的裁判在节点内完成」
四、治理与自动修复
- G3 放开 judge:受管级别写 judge 必须写预算(judge.max_cost_usd,或显式写 null 表示不限)
- 发布前自动修复:缺预算时给选项(默认金额 / 不限),不替人选;
从 require_citation 改成 judge 不算降低要求,judge 改回 off、withhold 放宽成 degrade 算降低要求
- Copilot 手册:受管模板可以写 judge 并带预算;问数据按需裁判,不替人打开结论句策略
五、界面
- 结论句句末放徽标:支持 ◆、部分支持 ◇、不支持 ! 整句浅底、未裁判 ?;不用表示「已核对」的绿色;n / N 也跳到不支持的句子
- 证据面板里「模型的解释」:带「模型判断 · 模型名 · 非确定」,封存后追加的另外标出;
「请模型判断这句」只在探索运行、还没判过时出现,触顶时写明已到上限和怎么调
- 出具横幅多一段结论句计数;设置页新增「证据裁判」一组(裁判模型、各项上限、今日花费),设成不限时写明费用受什么约束;
报告节点可以配 judge;运行轨迹里裁判相关的事件都有中文标题
六、门禁修正
- 引用模式下,图里没有口径卡(只引查询单元格)的,出具契约不再要求 metrics_from,受管门禁也不再要求 required:
strict 加 cells 已经保证每个数都点得开、对不上就不予出具;有口径卡的照旧要求。
以前问数据的图怎么都发不成受管,Copilot 补上契约也会被判「冒出新的问题」作废
|
2026-09-29 11:10:53 +08:00 |
|
ThearchyHelios
|
d071bf119f
|
可点击证据第三期:表名字段名和引文也点得开、可疑实体与结论句、受管门禁 G1–G5、证据审计导出
一、表结构、实体与引文
- 数据源工具查库时顺带存一份表结构快照(schema_snapshot),和查询快照一起进台账、进封存;
台账里的查询记下 SQL 用到的表
- 报告里的表名、字段名可以点:反引号里的名字一律核对,正文里的裸名字只在明显是标识符时才自动链接,
常见英文单词不链接;点开看到它出现在哪次查询、类型、所属的表,快照被截断时写明
- 找不到的名字标成可疑实体:在快照、SQL、结果列里都没有的记「可能是编造的名字」并给出最接近的 3 个;
快照被截断、核对不了的记「核对不了」,不冤枉成编造
- 检索引文 [[q:K1|原文]]:原文在检索快照的命中片段里逐字出现(空白归一化后)才算有出处,点开高亮原文位置
- 自动链接的名字不算句子的引用,只有写作者写的引用才算
二、结论句与出具
- 报告节点新增 claims:off(默认,保持原样)或 require_citation(没有引用的结论句计入缺口)
- 受管级别的正式运行里,可疑实体和没有引用的结论句按「有缺口」降档,不拦截;探索运行和已发布级别只标注
- 契约的 claims 和报告节点的设置取更严的那个;受管正式运行里写 ignore 也按降档处理,并给出警告
- 出具横幅除了数字,也写出可疑实体和没有引用的结论句
三、受管门禁 G1–G5(只看图和配置)
- G1 带契约的出口,成果字段的文字只能来自报告撰写节点
- G2 模型写的文字不绕过报告撰写节点直接流进出口
- G3 报告撰写节点写明 numbers: strict、on_violation: fail、claims: require_citation
- G4 口径卡的输入不来自计算角色的沙箱代码
- G5 给口径卡供数的 agent 配 output_schema 并开 cite_fields
- 受管级别是错误、挡住发布;已发布级别只给警告;已经发布的版本照常运行,下次发布时按新规则检查
- 每条规则都接进发布前自动修复(一键修复 / 给选项 / 交给 Copilot);禁止的改动补上关 cite_fields、
删 output_schema、放宽契约 claims;Copilot 兜底不许自己把代码节点标成 source、不许放宽报告节点
- 发布弹窗里选「交给 Copilot」时明确写「正在交给 Copilot」
四、旧运行与审计
- 没有契约的旧答案:数字和已封存的查询单元格、口径卡值按数值比对,每个数最多 3 个候选,
默认折叠,写明「猜测的来源,不能当证据」
- 有契约的旧运行:候选只从已封存的口径卡里找,人为插进工件表的行不当证据
- 证据审计:GET /api/runs/{id}/evidence/audit 按「无证据 / 可疑实体 / 有出处 / 旧运行猜测」分组,
可导出 JSON 和 CSV(带封存状态);记录页和画布上的报告节点显示「有出处 N · 无证据 M」
五、SQL 守卫修正(安全)
- 去注释改成单遍扫描,认得字符串和各方言的引号(单引号转义、MySQL 反斜杠与 #、PostgreSQL 的 $$ 与嵌套注释、
Oracle 的 q'[…]'、SQLite 的方括号);以前字符串里的 -- 会被当成注释,SELECT '--'; DROP … 这类写法能骗过多语句拦截
- 看不到服务器设置时每种读法都扫一遍,读法之间切分不一致就拒绝,任何一种读法是写操作就按写操作处理
- 执行查询和工具危险判定都按数据源的方言检查
六、其他
- 修掉检查脚本里画布检查的一处竞态,并行估时按实测更新
|
2026-09-29 05:26:34 +08:00 |
|
ThearchyHelios
|
fcdcc30d4e
|
可点击证据第二期:取数链路打通、问数据的答案逐段可点;发布前自动修复
一、可点击证据第二期
取数链路
- 智能体、工具节点、代码节点、知识检索取数时都记进证据台账;报告目录按台账编出 Q1… / K1…
- 报告可以引用快照里的一格([[v:Q1.r0.gmv]]),也可以由系统从快照生成整张表([[table:Q1 cols=… rows=0-4]]),
表里每一格都是可点的片段;取值一律以快照为准,取回快照时复验哈希
- 口径卡表达式新增 cell(x, row, column),lineage 精确到单元格
- 智能体开 cite_fields(配合 output_schema)后,循环结束做一次结构化抽取,每个字段标出取自哪次查询的哪一格,
再到快照里核对:一致记 verified;不一致以快照为准并告警;查不到的记为空值,永远不兜底成 0
- 代码节点新增 evidence_role(source / compute)和 code_sha;沙箱算出来的数不能被报告直接引用,要进口径卡
- 查询快照记下列类型,文本列按文本渲染(年份 "2026" 不再显示成「2,026」)
- 升级前发起的运行(停在审批上的)一律走旧逻辑:新的 task 只追加在循环之后,恢复时不会错号
证据接口与面板
- 片段链接上查询步骤:SQL、被引用的行加前后各 2 行、高亮到格;数据源可设 mask_columns 遮罩列,
遮罩在查询时记进快照,数据源事后改名或删除也照样遮罩(在有身份体系之前,遮罩只减少暴露,不是安全边界)
- 输入来源写明核对结果(与快照一致 / 模型报的值和快照不一致 / 没查到记为空);口径卡缺输入时也给出完整的链
- 受管出具要在契约里声明 cells 才能引用单元格;受管与否记在所钉版本上,改画布不再让受管版本的正式运行降级
- 口径卡可以用 caliber_from 钉住另一个已发布工作流里的口径卡,上游有新版本时按 upgrade_policy 处置,
和子工作流同一套规则;子工作流节点也补上了升版处置的配置项
Copilot、校验与报错
- 问数据的答案里有数字时,搭 input → 智能体 → 报告 → 出口,报告直接引用查过的单元格
- 写 SQL 前核对数据源的表结构,SQL 里出现表结构里没有的列会被打回
- 不再搭「智能体文字 → 转换节点解析 JSON」这种图;转换节点的 JSON 报错分清是上游模型写坏了、
模板里裸插了模型文字(给出 | json 的改法),还是模板本身写错了
- 工具节点遇到「查询失败」「SQL 被拒绝」判为失败;发起运行前检查绑定的工具是否存在,不存在时直接拒绝并给出入口
二、发布前自动修复
- 问题带上机器编号(code)和修复 id;新增只读接口 POST /api/workflows/{id}/publish-check 和 /autofix
- 修复分三类:答案唯一、不降低要求的一键修复;要人拿主意的给选项(不替人选);结构性问题交给 Copilot
- 一律先预览,不自动保存、不自动发布;删节点、删契约、放宽审批、关掉 strict、改级别等降低要求的改动一律拒绝,
Copilot 的方案也要过这一关;应用后重跑检查,错误必须变少且不能冒出新的
- 发布弹窗打开就先检查;问题面板新增「发布前检查」,平时就能看到、就地修掉
- 门禁修正:契约有 report_from 时不再要求 narrative(模板⑨可以发成受管);全图默认审批为「全部放行」时同样拦下
- 修掉画布保存会抹掉全图默认(defaults)的老问题:以前任何一次保存都会把它清空,审批 always 因此被悄悄放宽
验证(沙箱):tsc 干净,生产构建通过,pytest 2268 passed(两遍),check-all 17 项检查、3915 项全过
(新增 check-publish);关键行为做了变异验证、全部抓到。
|
2026-09-28 21:20:04 +08:00 |
|
ThearchyHelios
|
62aac187d8
|
工具审批三档、智能体护栏取代固定 12 步、检查脚本分道并行
MCP / 自定义工具的审批三档(tools/trust.py)
- 平台看不出这两类工具做什么,以前审批关卡一律当它们安全、照跑不误。现在默认每次调用都等审批;
工具页可以逐个改成「始终允许 · 门控把关」或「始终允许」,审批卡上的「始终允许」等于前者
- 门控把关:每次调用先由门控模型审一遍工具说明、参数和节点任务;判可疑、出错、超时、答不成格式,
一律交还给人,不会变成放行。判定包进 checkpoint,审批恢复重放时不再问一遍
- agent 节点、工具节点、协作团队三处关卡都接上;团队成员停不下来等人,门控没放行的调用不执行
- 每次运行在发起时快照档位(runs.tool_trust),恢复和接着跑沿用同一份;正式运行不看档位,一律等审批;
升级前发起的运行照旧不问——它们可能正停在某个审批上,多问一次答复就会对错号
- 接口:GET /api/tools 带 trust / trust_key,PUT /api/tools/trust;设置新增门控模型;新事件 tool.gated
- 界面:工具页三选一、审批卡「始终允许」、设置页门控模型、运行记录解码 tool.gated;
顺手修了 MCP 工具详情 404、工具审批被驳回却显示成放行
智能体护栏(engine/guards.py),取代固定的「最多 12 步」
- 步数只留兜底:默认 100,设置里可改;以前画布自动写进节点的 12 按「没填」处理
- 同一个工具、同样的参数第二次不再执行,把上次的结果交还给它;连续 3 步拿不到新信息就收尾
- 每个节点的令牌预算(默认 200 万)和金额预算(默认不限),都可以在设置里改成不限
- 上下文用到窗口的 60% 先压缩早期的工具结果,到 85% 就收尾;临近任何一个上限先提醒模型一次
- 下游结构化校验失败时写明是上游哪个 agent 提前收了尾;复核信号带上收尾原因,
问数据页只在真是步数用满时才给「放宽步数重跑」
- 上限在发起时快照(runs.agent_limits);升级前发起的运行一字不改走旧逻辑
检查脚本
- check-all 默认 4 道并行:量时间最多的两项互不重叠;并行时没过的最后单独重跑一遍,重跑才过的
单列成「偶发」。16 项全量从约 21 分钟降到约 7 分钟(加上一次重跑约 10 分钟);--lanes 1 回到串行
顺带修的
- 设置接口里的 limits 改为只读:库里存着旧版设置页写进去的一份(max_agent_steps: 25),从不生效,
却盖住了真实上限,问数据页照着错的数许诺
- test_kb 里嵌入模型那条测试不再真连 api.openai.com(代理慢时一条要等近 4 分钟)
- check-stream 读滚动容器前先等它挂上,并行跑时不再偶发
验证(沙箱):tsc 干净,生产构建通过,pytest 1655 passed(两遍),check-all 16 项检查、3562 项全过;
新增后端测试 53 条,关键逻辑做了 22 处变异验证、全部抓到。
|
2026-09-28 11:47:45 +08:00 |
|
ThearchyHelios
|
e4f2693918
|
可点击证据第一期:报告里的每个数字都点得开出处
「引用即渲染」:写报告的模型只写引用标记([[m:指标id]]、[[i:输入字段]]、[[see:…]]),数值由系统按口径卡
填进去,每个数字都挂着一条能复算的证据链。本期是数字层闭环;实体、结论句的概率性证据放在后续几期。
内核(engine/evidence.py,口径卡、出具契约、表达式等)
- 口径卡记录每个指标的来源:原式、代入式(每个输入写成「值 ← 路径」)、复算结果、口径与版本
- 出具契约的数值匹配改为按位置标注;同一个值有几个出处时记为「出处不唯一」并列出候选,不再随便挑一个
- 证据 id 由内容派生,事后对不上就是被改过;零值、结构性数字(列表序号、代码块标签)单独处理
- 口径卡新增 on_missing:缺输入时默认仍是整个节点失败,可以改成记为空值、交给契约判档
报告撰写节点(report)与引用模式的出具契约
- 流式生成时 token 里就是真实数字,不会漏出原始标记;裸数字、不存在的引用当场判违规,交回去重写
(max_repairs,默认 1 次);仍不过时按 on_violation 失败(正式运行默认)或把违规标在报告里(探索运行默认)
- report.checked 事件记下核对结果,落在封存范围内;fail 模式下仍不过时带 failed,运行记录里画成失败
- 出具契约写了 report_from 就按引用核对:取回文档时复验哈希,独立重算一遍,全部带引用才判完整出具
- 成果字段逐字等于报告正文时标注 _evidence;复核只能加说明,不能改写带证据的答案(前后端各拦一道)
证据接口
- GET /api/runs/{id}/evidence 返回证据图;GET …/evidence/segments/{id} 返回一个片段的证据链
- 只认封存范围内的事件能追到的工件,不读 artifacts 表;封存被改过时不把任何一项标成已封存
- 没有报告的旧运行降级为 none 或 legacy_contract
界面
- EvidenceDoc 按块、句、片段渲染报告,片段是和正文一样的按钮;整份报告只占一个 Tab 位,
←/→ 在片段间移动、↑/↓ 按句、n/N 跳到下一处「无证据」、Enter 打开面板、Esc 关闭并把焦点还回去
- EvidencePanel:宽屏侧边、窄屏底部抽屉、360px 画布右栏里栏内展开;显示指标步骤、代入式、复算结果、
封存状态。正文不是封存的那份报告时明确说出来,不拿别的报告的封存画绿
- 出具横幅多一行「N/N 数字有出处 · 无证据 M」和「定位下一处」;没有 _evidence 的旧回答渲染一字不差
- 画布认得报告撰写节点;Copilot 手册补上口径卡、报告节点、出具契约的写法;新增模板「⑨ 可追溯周报」
验证(沙箱):tsc 干净,生产构建通过,pytest 1601 passed,check-all 16 项检查全绿(新增 check-evidence)。
|
2026-09-28 11:47:23 +08:00 |
|
ThearchyHelios
|
b418c0d951
|
最终验收:148 项逐条复核(原审查 142 条 + 平台问题 6 条),返工后 145 项解决
四组验收员拿审查原文在沙箱里逐条复现:133 项已解决、15 项部分解决、没有未解决或回退的;
另发现 10 个新问题。按归属返工后,剩 3 项低严重度:MCP / 自定义工具的危险标记(待用户拍板)、
选中环与运行态同色(需跨三处协调改)、「接着跑」这个说法是否更换(多处引用原文,暂留)。
这次返工改了
- 右栏:老运行没有 active_ms 时执行时长改按航迹算,不再显示最后一段的「22 ms」;循环出口行不再差一;
取消在一轮中途时泳道脚注不写「0 ms」;分支、循环出口显示 case 的说明而不是 key;
展开已完成轮次不再误报「N 条新进展」;旧数据里已完成的模型调用不再一律标成「已取消」
- 后端:跳过事件带节点名和类型;restore 版本时把已发布 / 受管退回草稿;只是打开旧会话不再把它顶到最前、
标成「刚刚」;协作团队全程没有成员交付成果、调度者却判完成时按失败或降档处理,不再当成功交付
- 画布:循环体有「循环体 · 最多 N 轮」的底框,出口标签不再被自己的走线穿过;运行中校验角标退让;
768 宽下工具栏不溢出;运行被拦时点名是哪个节点;正式运行弹层写明以谁的名义发起
- 问数据:离线时能力条不把「没取回来」说成 0;窄屏下会话列表收起;check-chat 在零点前后不再误报
- 公共层:FastAPI 422 校验错误翻成中文,不再把英文原文透给用户
验证(沙箱):tsc 干净,pytest 878 passed(两遍),check-all 15 项检查全绿。
|
2026-09-28 02:42:40 +08:00 |
|
ThearchyHelios
|
3e311ec551
|
第三波收尾:跨模块整合、全站扫尾、检查脚本整合;网页工具认得本机代理的 fake-ip
整合(3c-integrate)
- 公共层:跨天时长(formatSpan / formatOffset / formatLapse)、本机署名(lib/actor)、检索分类色令牌、
数据源目录(useDatasources)收进公共层;失败说明能一次列出多个写坏的自定义工具,查询超时指到数据源的查询时限
- 管理页收尾:设置页接入统一的离开前确认;检查脚本不再把沙箱里的真实值打进输出
- 画布 ↔ 右栏:点出具印章、点节点能跳到右栏对应位置;「定位」直接聚焦到出问题的字段
- 回答流 / 问数据 / 记录页:失败信息带「去哪修」且不再被翻译两遍;协作团队的结局按执行截取;
等审批时断流重连、而运行已在空档里跑完的,补全事件
扫尾(3c-sweep)
- 术语统一:开始运行 / 继续运行、工作流最大步数;未保存的运行统一叫「未保存的工作流」(后端新运行同步)
- 沙箱报错不再带 Python 异常类名;新增 check-tokens,未定义的 CSS 变量为 0,两个旧别名迁移后删除
检查脚本(3c-checks)
- check-ui 跟上新界面(运行列表按 data 属性找行),加上令牌与选中态的回归断言,输出里的名字打码
- 新增 check-all:按顺序跑全部检查、汇总退出码,--repeat 列出偶发项
- 单节出错只记这一节失败、不再带崩整个脚本;外壳遥测浮层「重新检测」时焦点不再掉到 body
网页工具与 fake-ip
- Clash / mihomo 一类代理开 TUN + fake-ip 时,查什么都先拿到 198.18 段的假地址,被当成内网拦截。
新增 AGENTLAB_HTTP_TOOL_FAKE_IP_RANGES(默认空):落在段里的解析结果改向公共 DNS(DoH)核实真实地址,
公网放行、内网照拦、核实不了按拦截处理。DoH 默认先问境外的,境内公共 DNS 会对被封锁的域名返回污染结果
- 列表型环境变量接受逗号分隔(原来注释说逗号分隔、实际只认 JSON 数组);域名解析改为异步,不再阻塞事件循环
验证(沙箱):tsc 干净,pytest 866 passed(两遍),检查脚本全绿。
|
2026-09-27 23:37:32 +08:00 |
|
ThearchyHelios
|
587bf7e27c
|
第三波前两批:六个「看着成功其实没干活」的平台漏洞堵上,记录页能回放,管理页与画布收尾
起因是一次探索运行:Copilot 改节点时把工具绑定整份抹掉,agent 没有工具只能写「假设调用」,
校验节点的自动修复编出 total_count=0 让 schema 通过,协作成员把工具调用标记当文字输出了四轮,
轮数用完后节点把原话当成功结果交出去——运行标成已完成,数据库一次都没查。
平台可信度(先写失败测试)
- Copilot 修改节点改为合并:没写的字段保留,null 才删除;协作成员按名字合并。前后端同一套规则,
工具集合变小时自查报警、改图回执列出变化
- 新校验规则:提示词点名了某个工具而节点没有绑定它,判错误,Copilot 自查会交回去修
- 校验修复只许改格式:冒出原文没有的值就判修复无效;修复这次模型调用计入用量
- 识别模型把工具调用写成文字的情况(DSML、<tool_call> 等标记):纠正一次,仍然如此就判失败并说明原因
- 协作团队用完轮数不再按成功交付:默认失败,可选降档,点出一次都没派到的成员
- 审批恢复后,同一节点里已经执行过的工具调用复用结果,不再重复执行
后端
- 查询时限交给数据库执行(Oracle call_timeout、PG statement_timeout、MySQL max_execution_time、
SQLite progress handler),可按数据源配置;超时报错与实际耗时一致
- 等审批的运行可以真正取消;会话列表带最后一轮状态;测连接结果持久化;探查结构可只看不存;
自定义工具可草稿试跑、写坏的参数定义保存时拒绝;MCP 服务改动后作废旧工具缓存;
检索命中带贡献分解;重建索引在后台跑并报进度;报错统一收进 core/errors
前端
- 公共层:管理页共用件、失败说明(lib/explain)、健康状态进公共库;目录按表各自加载并有超时,
一张表卡住不再拖垮整站;上传显示真实字节进度;离线横幅只播报不变的句子
- 画布:卡片只说游标所在的那一次执行;等审批时可放弃运行;团队收尾判定与未完成说清楚;
回放用量取那一刻的值
- 编排页:Copilot 合并语义与工具变化回执;「用完轮数时」选项;正式运行期间锁住所有改图入口
- 回答流 / 问数据:新失败类型说人话并给下一步;画布与右栏互相定位;数据源范围、回收站、重新核对
- 记录页:「航迹」页签复用画布时间轴,可拖动回放、列出关键时刻;列表行带执行 / 等人 / 失败缩略条
- 外壳:统一的离开前确认(data router),导航、⌘K、快捷键、后退都会先问
验证(沙箱):tsc 干净,pytest 838 passed(两遍),14 个检查脚本中 13 个全绿;
check-ui 余 1 项旧断言(按旧日期格式找运行行),3c 更新。
|
2026-09-27 10:16:32 +08:00 |
|
ThearchyHelios
|
e10c4f23fc
|
界面与交互全面翻修(前两波):运行态有了 HUD 和时间轴,终态会收尾,全站令牌、组件、文案统一
来自 2026-09 的 UI/UX 审查(142 条)。本提交是前两波,第三波(记录页接入时间轴、跨页互链、check-ui 更新、全站扫尾)另行提交。
地基
- 设计令牌:全局规则和 .btn/.field/.chip 放进 CSS 层,Tailwind 工具类重新生效,选中态、开启态不再被吞掉;字号阶梯;
辅助文字在所有底色上 >= 4.5:1;状态色专用,节点类型色与之拉开色相;「跟随系统」真的跟随,首帧不闪深色;新字标与 favicon
- 基础组件:confirmDialog / promptDialog 取代原生对话框;toast 出错不自动消失、可复制、读屏可播报;离线横幅与断线恢复;
九种剪影的状态徽标;唯一一份状态文案(lib/status)、格式化(lib/format)、术语(lib/terms)、报错人话(lib/errors)
- 运行态内核:事件折成航迹(run/trace.ts),可投影到任意时刻;取消 / 失败 / 服务重启后统一收尾,不再永远转圈;
run.status 随事件回写;token 事件不再堆进 events
- 后端:协作成员逐个完成即发事件,调度者决策有事件;时长分执行 / 墙钟 / 等人;API 时间带时区;
中断的运行也发 stream.end;运行列表可筛选翻页;删除运行 / 工作流有保护;运行默认值接通;可测未保存的配置;
报错说清发生了什么、为什么、怎么办
功能
- 画布:状态四重编码、固定高度遥测槽、按缩放三档、运行胶囊与 HUD、航迹时间轴与回放、执行轨迹边、
等待闸门、跟随执行、正式运行只读、一次性时刻(开场点亮、回扫、阻断级联、出具印章)
- 编辑:撤销重做、版本历史、问题面板可定位、三栏可收起、模板与表达式字段区分、变量血缘、快捷键、带搜索的选择器
- 回答流 / 问数据:贴底才跟随滚动、工件下钻、出具横幅列出缺口、答案可复制导出、按会话的忙闲、
答案不再在 2000 字处截断、重启后这一轮会收尾
- 记录页:待审批 / 运行中 / 失败页签、筛选翻页、实时接流、排错路径指到节点、封存凭证常驻
- 管理页:新的「数据」页、写操作失败都有反馈、弹窗里先测连接、统一持久的连接状态
- 外壳:导航改版、⌘K 命令面板、? 快捷键帮助、404、页面标题与 favicon 状态点、后台提醒
验证(沙箱):tsc 干净,pytest 579 passed,14 个检查脚本中 13 个全绿;check-ui 剩 1 项旧断言(按旧日期格式找运行行),第三波更新。
|
2026-09-26 23:11:31 +08:00 |
|
ThearchyHelios
|
95ff6470d1
|
后端依赖锁到测过的那一套:pyproject 卡版本范围,requirements.lock 写精确版本
langgraph 和四个 langchain 包原来完全不写版本,fastapi、sqlalchemy、aiosqlite 只有下限,
uv.lock 还在 .gitignore 里。按旧写法今天新装,22 个包和测过的版本不同,其中 sqlalchemy
会从 2.0.52 跳到 2.1.1。
1. pyproject.toml:LangGraph / LangChain 一族放行到下一个小版本——引擎依赖它们的断点
格式、interrupt / resume、流式事件和 thinking 块,这些在小版本里也改过行为。aiosqlite
与 SQLAlchemy 同样卡小版本:db/base.py 给 aiosqlite 0.22 的 stop() 打了补丁,连接池
强制断开时会走到它。mcp 卡在大版本 1。
2. backend/requirements.lock:后端全部依赖(含间接依赖、全部 extras)的精确版本,
--universal 跨平台。锁的是 473 条测试跑过的那个 conda 环境:本机要装的 96 个包逐一
对上,另外 3 个只在 Windows / emscripten 上装。
3. environment.yml 装包时以锁文件为约束(-c),换机器装到的是同一套。
4. scripts/lock-deps.sh 重新生成锁文件:已锁的版本默认不动,升级用 --upgrade-package /
--upgrade。README 补了用法,补装 extras 的命令都带上 -c。
验证:用同版本 Python 新建空环境,照 conda 的方式按 environment.yml 的 pip 行安装——
装到的 96 个包和测过的环境完全一致,473 passed。
|
2026-09-26 09:49:16 +08:00 |
|
ThearchyHelios
|
86a0928512
|
知识库页不再把"退回本地哈希"藏起来:配了语义模型却没连上时说实话,也不再劝人重建
本机配的是 LM Studio 上的 text-embedding-qwen3-embedding-4b。服务启动时它没起,
load_setting 连不上,整个进程就一直用本地哈希——它后来起了也不会自己连上,原因只在
服务端日志里留一句。页面按"配的是不是 local"判断,于是:
- 不标"没有语义能力",看着一切正常,检索却一直是纯关键词;
- 高亮"重建索引(73 段)"——那些"对不上"的向量正是 qwen3 建好的,一点就拿哈希
覆盖掉,连上之后还得再重建一遍。
改法:
1. 后端记下保存的配置为什么没生效(embeddings.unavailable_reason),状态接口多报
fallback / fallback_reason:配的是语义模型、实际在用本地哈希。重新配置成功就清掉。
2. 页面按实际生效的 embedder(has_semantics)标"没有语义能力"。退回时说清配的是哪个、
在哪、为什么没连上,给「重新连接」——发的就是保存着的那份配置,服务起来后点它即可,
不用重启;"重建索引"收起,存量提示改口成先别重建。明确选了本地哈希的照旧:标没有
语义能力,重建按钮还在。
验证:test_kb 新增一条,走完"启动连不上 → 状态里报退回并带原因 → 重新连上后消失 →
明确选本地不算退回"。check-ui 新增一组伪造状态接口的断言(退回 / 重新连接 / 明确选
本地,写请求一律拦下);把页面换回旧版本跑,这组挂 8 项。后端 471 passed,tsc 与
check-decode / guards / stream / ui 全绿;本机状态接口现在如实报出退回和原因。
|
2026-09-25 19:09:19 +08:00 |
|
ThearchyHelios
|
3b2c082743
|
check-ui 跟上「在画布里打开」的新行为,而且不再往库里写图
429398a 把「在画布里打开」改成新建一张工作流、送去它自己的地址,但 check-ui 还断言
落在 /studio,那次提交也没跑 check-ui。更糟的是按钮本身会写库:照旧断言跑一次,库里
就多一张「问数据:…」。
现在拦下建图那次 POST,拿它自己发出去的图回一个"已建好",之后对这张图的读取也由检查
自己答;校验、变量分析只算不写,放行;其余写操作一律拦掉。断言改成:建了新图且送到
它的地址、建的是这一轮的图、没被别的工作流盖掉、没弹"未保存改动",最后核对库里的
工作流数前后不变。
check-ui 另有一项"本地哈希要标出没有语义能力"仍不过:配置的向量模型连不上、实际退回
local-hashing 时,页面按 kind === 'local' 判断,不提示。这是本分支之前的问题(11780ed),
这里不动。
|
2026-09-25 17:04:56 +08:00 |
|
ThearchyHelios
|
0997603ccf
|
画完先自查:条件写错在校验时就拦下,Copilot 交付前按同一套规则查一遍、查出问题先改
开发库里四次运行死在同一句"循环条件写错了:表达式里不允许出现 Set"。条件原文是
`{{ vars.gate }} != "ok"`——把模板写法带进了表达式:`{{ x }}` 在 Python 里是"装着
一个集合的集合"。Copilot 的提示词写着"模板语法在任意字符串里用",模型照做而已;
校验又不看表达式,写错了只有跑到那一步才知道,前面的步骤白跑。
1. 表达式:解析和静态检查收成一个 parse_expression,运行时、校验、Copilot 自查共用,
不会出现"校验说能跑、跑起来报错"。多套的 {{ }} 意思无歧义,在语法树上剥掉(字符串
里的花括号不动),按 vars.x 理解并提示。真写错的给能照改的提示:| 过滤器 → len(x)、
集合 → 列表、不认识的函数列出能用的、= 当 ==、引号里的 {{ }} 永远不成立、推导式、
切片、私有属性。and / or 短路掉的那半边也查。
2. 校验:按节点类型和模式挑出真正求值的字段(分支条件、while 条件、整形表达式、口径卡
指标、skip_if)。写错是 error、挡住运行;漏写 vars. 的名字提示会取到空值;while 没写
条件、整形没填表达式也说出来。foreach 的 condition、模板模式的 expression 不求值,不查。
3. 代码节点:assign_to 拿的是 stdout。「测试 1」修好条件后跑通了、结果却是空的——代码
最后一行是裸表达式,那是 notebook 的行为。设了 assign_to 却找不到任何输出的 Python
代码,校验直接拦下,提示 print(json.dumps(结果))。宁可漏判不可误判:子进程、exec、
附带文件里的 print 都算有输出;整行只有一个 {{ }} 的代码是运行时才拿到的(⑤号示例
整段就是 {{ vars.code }}),看不见,不猜。库里 20 张工作流无一误拦。
4. Copilot 自查:搭完用同一套校验过一遍,error 级的问题连同图交回模型——只修这些、只
输出改动,最多两轮。改好了才自动运行;改不好照实交付、列出剩下的问题,不自动开跑。
没问题的图不多花一次调用;只是多套了 {{ }} 的不返工。提示词改成"模板只在普通文本
字段里用",并列出表达式字段的对 / 错写法和代码节点必须 print。
5. 前端:自查这一段在 Copilot 的过程里看得见——发现几处问题、交回去改、改好了没有;
改不好标红并说明没有自动运行。"搭好了,共几步"那一行不会被挤掉。
测试:新增 test_expressions(开发库里真实死过的四条条件、静态提示、按模式挑字段、代码
输出、带 {{ }} 的循环真跑一遍)与 test_copilot_selfcheck(脚本化模型:改好 → 自动运行;
改不好 → 不运行、最多两轮;干净的图一次调用;多套 {{ }} 不返工)。test_continue_run 里
用来制造运行时失败的表达式换成 `2 ** 100000 > 1`——原来那条现在在校验阶段就被拦下了。
check-decode 补了自查的解码断言。
|
2026-09-25 17:04:39 +08:00 |
|
ThearchyHelios
|
429398a156
|
前端四处直接伤人的地方:输入法回车、覆盖别的图、停止不停、整站白屏
1. 中文输入法选词的回车被当成发送。问数据页输入框不判组字状态(Composer 判
了,这里没有):用户每选一个词,半句问题就被送去建图、跑图。同样的漏洞还在
会话重命名、知识库搜索、{{ }} 补全、⌘Enter 运行上。收成 ui.isComposing,六处
统一——Safari 结束组字那一下 isComposing 已经是 false、keyCode 还是 229,两个
都看。
2. 「在画布里打开」后 ⌘S 覆盖上一次打开的工作流。它只调了 setGraph:换掉画布
上的节点,store 里的 workflow 还是上一张图,保存就把那张图整张覆盖了(旧版本
还在版本表里,界面上没有入口找回);没打开过图时 ⌘S 则什么都不做。现在建成
一张新工作流(以这一轮的问题命名)、走它自己的地址,原来的图不受影响。
3. 问数据页的「停止」不停。它只关了自己这头的事件流,从不调 runs.cancel,而后端
断开订阅不会取消运行——模型和 SQL 其实还在跑、还在计费,界面上却已经显示
"已取消"。等人工审批的那一轮照旧不动。
4. Copilot 编出一个不存在的节点类型,整站白屏、没保存的编辑一起丢。三层都补:
- 后端 _apply_op 拒收未知类型,不进图、不转发,最终校验里说明跳过了哪一步;
- 前端流式加节点时再拦一道;sourceHandles、属性面板遇到未知类型不再崩;
- 全站加页面级 ErrorBoundary:导航栏留着,换一页自动恢复;编辑中的图在 store
里,点「重试」时它还在。
新增 scripts/check-guards.mjs(真浏览器,拦下所有非 GET 请求,不写库不花钱):
组字回车与 keyCode 229 不发送、普通回车照常发送、未知类型不白屏、渲染真出错时
只坏这一页且换页恢复,连跑三次全绿。等待一律等事件,只有"不该发生"的断言用
窗口。后端补了未知类型不进图的用例,413 → 414 passed;tsc 通过,decode / stream
/ canvas-layout 三个检查不回归。
|
2026-09-25 15:41:30 +08:00 |
|
ThearchyHelios
|
8905fe5e60
|
多 agent 节点在画布上展开协作矩阵,运行时的动效统一成一套语汇
多 agent 节点原本是个黑盒:卡片转三十秒圈,然后吐一段文本。"它同时派了三个
人"、"谁还在跑"、"并行到底省了多少"全都要点开右栏才知道,而右栏是线性的
时间线,看不出并发的形状。
现在它跑起来会在卡片里展开一块协作矩阵。三条设计上的选择:
- 花名册固定在原地,不按派单顺序重排——几轮下来眼睛知道谁在哪一行
- 条形长度按**这一轮里最慢的那个人**归一化。三个人并排三条长短不一的条,
墙钟为什么等于最长那条、而不是三条之和,不用解释就看懂了;页脚给出
并行一共省下多少
- 本轮没被派到的人不显示成"待命",而是留着上一轮的成绩(压暗)——它已经
交回来了,说它待命是错的,看的人会以为它还没干活
矩阵和右栏泳道读的是同一份翻译:把 decodeRun 里那段攒泳道的逻辑抽成
reduceTeam(不可变,每次返回新对象,画布那份挂在 zustand 上需要新引用)。
两边各写一遍的话,同一次运行在右栏和画布上会给出不同的并行度——而"几个人
同时在跑、省了多少"正是这个节点唯一值得看的东西。抽完之后 check-decode
一条没红。
其余节点按同一套语汇动:边框上转一圈光弧 + 扫描线 = 正在跑,绿色扫一遍 =
刚跑完(只扫一次——常驻动画会让整张画布一直在动),短促抖动 = 刚失败,
琥珀呼吸 = 在等人。循环卡片显示"第 1/5 轮";分支跑完点亮命中的出口、压暗
其余,实际走过的边留在画布上,那就是这次的执行路径。连线上的虚线换成顺着
路径跑的光点:虚线只让人觉得"在动",光点能看出往哪走,速度按路径长度归一化
(长边上的光点走得慢会像卡住了)。
原来那个往外扩的声纳圈(pulse-ring)删掉了。同一个意思说两遍只会更吵,而且
声纳扩出去会和邻居卡片的边框糊在一起。
一个前提先验证过:offset-path 在 SVG 子元素上能不能对齐路径。测下来和
getPointAtLength 的位置差 0px,才敢用它。
排版要按**展开后**的高度给多 agent 节点留位置(layout.py 的 _height 按名册
人数算)。不留的话它一跑起来就盖住下面那张卡,而且运行结束后矩阵不会收起来
(那正是要回看的东西),重叠是永久的。
前庭敏感的人由 prefers-reduced-motion 兜住:光弧、扫描线、流光条、光点整体
不绘制,而不是把时长压到 0.01ms——那样它们会停在动画的最后一帧,光弧卡在
角落、光点全堆在起点,看着像渲染坏了。静态下"谁在跑"由实心圆点和"进行中"
三个字承担。
新增 scripts/check-canvas-fx.mjs:把脚本化的事件灌进 store(走的是页面上那一份
applyEvent / NodeCard / FlowEdge,只有事件来源是脚本),断言协作矩阵真的长出来、
并行度算对、条形长度和耗时成正比、分支命中的出口被点亮、关掉动效时光效确实不
绘制。真跑一张多 agent 的图要连模型、要等十几秒,而且撞不上"三个人同时还在跑"
那一瞬间——那样的检查会变成偶发失败。另外用仓库里 fixtures.json 那份真实导出
的事件验过一遍,含老运行那种只有中文消息串的 log(走正则兜底)。
顺手修掉检查脚本互相下绊子:check-canvas-fx 原来会留下一张没坐标的工作流
(节点全叠在原点),而 check-ui 打开 /studio 会落到"最近更新"的那张上,于是
点节点被挡住。现在落库前先让后端排好版。
292 passed(新增 2 条:展开的多 agent 卡片必须在排版里留出位置)。
|
2026-09-25 12:47:07 +08:00 |
|
ThearchyHelios
|
75be5f485e
|
画布上的线不再叠在一起:排版按车道留走廊,走线不再交给 smoothstep
原来所有边都交给 React Flow 的 smoothstep,它的竖直段一律落在源和目标的
正中间。同一列的边于是全挤在同一个 x 上:一张"入口扇出三路 → 归并 → 分支
→ 出具/驳回"的图上,跨层的 a3→join 和 m2→join 的横段**完全压在同一条线
上**,长边还直接从"归并B"卡片中间穿过去。排版那边同样糙:同层按 id 字典序
竖排,所以 out_no 排在 out_yes 上面("n" < "y"),分支的 yes/no 是反的;
层内没有任何减少交叉的排序;层间距写死 300。
后端 app/engine/layout.py 重写成分层布局:先剔回边再按最长路径分层(loop
的回边不参与分层,否则循环体会被压到循环节点左边);层内中位数启发式上下扫
六轮取交叉最少的一版,分支/循环的出口再按声明顺序占位重排;y 先均匀堆叠再按
邻居重心迭代松弛,同列节点因此和上下游对齐,长边也走得直;最后按"穿过这条
走廊的连线在竖直方向最多重叠几条"给走廊留宽——区间图上的贪心染色,和前端
用的是同一套,走廊留窄了前端再聪明也只能把线叠起来。
前端 canvas/routing.ts 自己算走线:出边沿右边缘错开、入边沿左边缘错开(入边
按上游纵向位置排序),竖直段按区间染色分配车道——真叠在一起的才各占一条,
首尾相接的(同一个扇出点)共用一条,看起来就是一条总线;跨层的长边和循环的
回边在中间各列的纵向占用里找空档绕过去,不再压过卡片。边换成自定义组件后
React Flow 不再自动加动画类,实测确认 .edge-active 仍然生效。
顺带修掉"排完版看不见图":整张图换过坐标后(自动排版 / Copilot 生成完 /
切图)自动重新取景。坐标变了视角没变,人盯着的是一片空白。
新增 scripts/check-canvas-layout.mjs:把夹具图交给真后端 /api/copilot/layout
排版,再直接 import('/src/canvas/routing.ts') 用页面上跑的那份代码算路径,
按几何断言没有两条线压在同一段上、没有线穿过别人的卡片、两端都接在节点边缘,
另有 1325 条随机坐标的 fuzz 防手拖之后算出 NaN 路径(NaN 的 path 什么都不
画,界面上就是"线没了",一声不吭)。这条检查做过变异验证——把 laneX 改成
永远返回走廊中点,它立刻报 a3→join × m2→join,正是原图上那处重叠。
285 passed(新增 12 条排版不变量)。check-ui 69 项通过,唯一失败项"本地哈希
要标出没有语义能力"已 git stash 到基线复现,与本次无关。
|
2026-09-24 22:58:54 +08:00 |
|
ThearchyHelios
|
c8393c3c6b
|
移除仓库中的商业数据:删掉 21 个相关用例,其余标识符脱敏
这个仓库是公开的,而历史里带着一份客户 BI 仓的逐字转储:ANALYTICS(原
schema 名)下 53 张视图名、实测行数、字段结构表,外加一句写明取数方是谁
的注脚。还有一处把真人姓名和工作单位写进了检查脚本的断言里。这些都不是
化名,是能直接拿去对库的东西。
按"含敏感词的测试直接删"处理,共 21 个用例:
test_agent_tool_loop.py 整个文件(2 个,agent 工具循环的真图验收)
test_conversations.py 9 个(会话 CRUD、历史文本、上下文进 prompt)
test_datasource.py 5 个(探查失败的实情、候选 schema 提示)
test_copilot_context.py 3 个(数据源摘要的限定名)
test_review.py 1 个(干净运行无信号)
test_tool_args.py 1 个(call_tool 端到端)
辅助函数和产品源码不在"测试"之列,改为脱敏——否则删一个 _seeded 会连带
带走 8 个没被点名的用例。标识符一律换成中性名:shop→shop、
ANALYTICS→ANALYTICS、prod_shop→prod_shop、真人姓名→张三。
markdown 样本里那两条不是改标识符能解决的:行数、字段结构、业务域描述
整段都是客户的。换成同构的合成内容——领域换了,markdown 构造逐项对齐
(标题层级、有序/无序列表、三列表、带 inline code 的单元格、引用块),
渲染器的覆盖面没有变窄。check-stream 里那句"用的是真实输出"跟着改成实话。
286 → 265 passed,差值正好是删掉的 21 个。tsc 干净,decode / stream / ui
三个 check 不回归。
历史里的同一批内容另行用 git filter-repo 处理。
|
2026-09-20 22:52:28 +08:00 |
|
ThearchyHelios
|
00f8dd78a9
|
时间线上认得出并行分支了:3 路并行 · 合计 6.6s,实际 3.0s
图上一个节点连出多条边就是 fan-out,LangGraph 在同一个 superstep 里并发执行
——这一直是真并发。但在时间线上它们只是穿插出现的几行,"这三路是同时跑的、
因此省下了 3.6 秒"一个字都没说。上一轮给协作团队做了泳道,这条同样的信息
在 fan-out 这边还是缺的。
现在按时间跨度把同时跑的节点收进一个分组,标题写明几路、meta 写明省了多少:
⑂ 3 路并行 合计 6.6s,实际 3.0s
├ 查销售库 2.2s
├ 查库存库 3.0s
└ 查客户库 1.5s
**判据是"同一批出发",不是"跨度重叠"。** 第一版按包含关系排除嵌套,结果漏掉了
先跑完的那一路:三路同时开始但先后结束时,长的那条在时间上确实"包含"短的。
实测 leg0 [0.05,2.21] / leg1 [0.05,3.01] / leg2 [0.05,1.51],被漏掉的正是
最快的 leg2——而漏掉最快那路会让"省下多少"算少。改成看开始时刻是否落在同一批
(250ms 窗口),fan-out 的几路实测相差不到 1ms,子工作流那种真嵌套则是父节点
明显更早开始,自然分得开。
组内要求两两都同批,而不是链式传递:否则 A|B 重叠、B|C 重叠但 A|C 不重叠的
三个节点也会被串成一组,而它们并不是同时在跑。瞬时节点(<30ms)不参与——
它们谁跟谁都"重叠",是噪音不是信息。
**误报比漏报更糟**:它会让人以为省了时间。所以断言里专门守了反向——db、human、
loop_approve 三份顺序执行的样本一个都不能被判成并行。
fixtures 里新增的 fanout 是真跑出来的,不是手写的:三路分别 sleep 1.8 / 2.6 /
1.1 秒,事件原样收进来,所以 6.6s / 3.0s 这两个数和后端量到的是同一份。
验证:check-decode 新增 6 条(含三份顺序样本的反向断言);check-stream 新增
10 条,宽窄两栏都验了不溢出;后端 276 passed 不受影响;tsc 干净。
|
2026-09-20 21:09:30 +08:00 |
|
ThearchyHelios
|
c3f7d6ffcd
|
协作团队真的能并行了,界面上也看得出来
先说一个和名字不符的事实:这个节点叫「多 Agent 协作」,但在此之前它是一条
严格的串行流水线。调度协议写死成 `next: string`——"一次只能派一个"被编进了
协议本身,模型再想并行也表达不出来。三个专家各花 10 秒,总是 30 秒。
(真并行一直在图级 fan-out:实测 3 个各 sleep 2 秒的节点连在同一个上游,
全部 +0.00s 开始、+2.37s 结束,墙钟 2.6 秒。只是协作团队走不到那条路。)
**协议改成 assignments 数组**,一轮可以派多个互不依赖的专家,asyncio.gather
并发执行,总耗时按最慢的那个算。max_parallel 可配,设成 1 即退回严格串行。
**并发只在任务真的互不依赖时才成立**,这件事的成本必须说清楚:同一轮发出去的
专家拿到的是**同一份进展快照**,谁也看不见谁这一轮干了什么。派错了不报错,
只会让两个人基于一样的旧信息重复劳动,而这在结果里很难看出来。所以判断责任
压在调度者身上,提示词里把反例写出来:
可以同时派:「查 A 的资料」和「查 B 的资料」
不能同时派:「查资料」和「根据资料写报告」
不能同时派:「写初稿」和「校对初稿」
并明确告诉它拿不准就分两轮——代价只是慢一点。
**每个人单独计时**,而不是事后拿整轮墙钟去摊。界面要显示"并行省下多少",
那个数必须是量出来的:按 N×墙钟 推算等于假设每个人都跑满了最慢那条,
而实际上快的那个可能只花了三分之一。测试里专门有个快慢不对称的用例守这条。
**调度日志改成结构化字段**(agents / done / reason / parallel)。以前只有一句
中文消息串,解码层得拿正则去拆「调度 → X(理由)」——后端文案一改就散架,
而这次正是改了文案。老运行的事件没有这些字段,正则那条路留着兜底。
**界面加泳道。** 每个成员一行、每轮一格,条形宽度按耗时归一化,所以一眼看得出
谁和谁是同时干的、谁是这一轮的瓶颈。顶上一行汇总"3 名成员 · 3 轮 · 2 轮并行,
省下 11.8s";点任意一格展开那个人这一轮的任务和回复。全程串行时不装作有并行,
而是说"本次任务互相依赖,逐轮进行"。
泳道排在子步骤**前面**:先给一眼看清的形状,再往下是每一步的内容。反过来的话,
得把十几行步骤读完才知道这个团队是怎么分工的。窄栏(画布右栏 360px)下隐去
"N 人同时"的文字——并行与否看条形本来就一目了然,而缩写成"并2"没人看得懂。
验证:后端 276 passed(新增 test_supervisor.py 7 条,核心那条用**墙钟**判定
并发而不是看事件顺序——顺序只能说明"发出去了",说明不了"同时跑");
check-stream 新增 10 条泳道断言,含"同一轮里慢的那个条更长"和两种栏宽不溢出;
tsc 干净,三个 check 不回归。
|
2026-09-20 20:57:03 +08:00 |
|
ThearchyHelios
|
7b04162540
|
Excel/CSV 变成能查的表、PPT 读得进来、跑挂了能接着跑、文档和工具有了地址
四件事,其中 Excel 那条不是"多支持一种格式",是补一个和地基冲突的缺口。
**Excel / CSV → 可以用 SQL 查的表。**
这个项目有条不可回退的决策:叙述层无算术权限,所有算术下沉到 SQL 或口径卡,
叙述只能引用(engine/issuance.py)。表格传进知识库只能被切块检索,数字就成了
模型从片段里"读"出来的——复核层拦得住凭空多出的数字,拦不住"从一堆片段里
读错了一格"。所以表格必须变成表。
不新增数据源类型:kind='sqlite' 早就支持,build_url 拿 database 当文件路径。
落成 .db 文件再建一条 DataSource 指过去,下游一行都不用改——SQL 守卫、结构
探查、db_query__<name> 工具、查询快照进工件库、Copilot 的数据源清单,全都白拿。
建表走 sqlite3 直连而不是 run_query:guard 的 _ALWAYS_DENIED 里有 create,
那是对的(模型不该能建表),而导入是我们自己发起的可信操作。
四个语义是这块的全部分量:
- **类型要推,不能全存 TEXT。** SQLite 是动态类型,但列亲和性决定排序和比较:
整数列落成 TEXT 的话 '10' < '9',SUM() 在混入空串时静默出错,而没有任何
地方会报。逐列扫描推断 INTEGER/REAL/TEXT,空单元格存 NULL 不存 "",
日期存 ISO(字典序恰好等于时间序)。测试里那条 ORDER BY 要是 9<10 而不是
'10'<'9',守的就是整块的意义。
- **表头取第 1 行,但不猜。** 不做"自动找表头"的启发式:猜错的表现是列名变成
一行数据,没有任何征兆,直到某天有人发现汇总一直少一行。改成 header_row
可填,并把推断出的列名和类型当场回显——错了一眼看见。
- **同名重传原地替换,id 和 name 都不变。** 名字会成为工具名(db_query__sales)
而工具名写进了保存过的图,另起一个 sales_2 等于悄悄让那些图失效。
- **中文原样保留。** 一开始把 sheet 名清洗成了 ASCII,结果「明细」「汇总」都变成
sheet、sheet_2,模型和用户都认不出哪张是哪张——而 SQLite 的标识符本来就允许
非 ASCII,那道清洗纯属自找麻烦。现在只替换会断句的空白和标点。
实测:两 sheet 的中文 xlsx → 两张表,金额列 INTEGER,跨表 JOIN + SUM 跑通,
Copilot 的工具清单里带着表名。CSV 按 utf-8-sig / utf-8 / gb18030 依次试——
GBK 的 CSV 是 Excel 另存为的默认行为,按 UTF-8 读会得到乱码列名,而乱码不报错。
知识库那边顺手改了指路:传 .xlsx/.csv 进去不再说"不是 UTF-8 文本",
而是告诉你该传到数据源那边、以及为什么。
**PPT。** 文本框和表格分开取(表格的 has_text_frame 是 False),幻灯片之间
留空行当段落边界。.ppt/.doc/.xls 这些老二进制格式认出来再拒,说清楚"另存为
新格式"——以前它们会掉进"不是 UTF-8 文本"那个兜底,对着一个 .ppt 说这话,
用户根本不知道该怎么办。
**问数据页的「接着跑」。** 挂了那一轮不该只剩一行红字:断点一直在 checkpoint
里躺着。按钮挂在 approvalsFor 那个每轮插槽里(本来就托着「看执行过程」和
「跑一下」)。不带图——问数据页改不了图,这里就是"用原来的配置重试失败的
那一步",配置本身错了要到画布上改。实测:续的是同一次运行(8→13 条事件),
只跑了 lp 往后,轨迹里有 run.resumed。
**文档 / 工具的地址。** /knowledge/kb/:docId 是新页面——文档原来连详情视图都
没有,而切块这层做了不少事(按行边界留重叠、跨块续表头),结果一处都看不见。
现在逐段列出来带字数和 token 数,还标出"这份文档里混着两个模型建的向量"
(换模型只重建了一部分时真会发生,而那会让这部分内容悄悄退回关键词检索)。
新增 GET /kb/documents/{id}。工具那边 tab key 从 tools 改成 library,
否则地址是 /tools/tools/<id>。
验证:后端 265 passed(新增 test_tabular.py 16 条);tsc 干净;三个 check 全过,
check-ui 补了文档/工具两节;四块都在真浏览器/真后端上端到端跑过。
开发库里造的演示数据源、会话和运行已清干净。
|
2026-09-20 16:19:07 +08:00 |
|
ThearchyHelios
|
542bcb65bb
|
一次对话有自己的地址了:/chat/<chatID>,运行、画布、各页标签同理
在此之前 react-router 只管到页面一级,「页面内部在看哪个」全在 store 和
localStorage 里,全站一处 useParams / useSearchParams / useNavigate 都没有。
后果:
- 一次对话没有地址。想把「这个结论是怎么查出来的」发给同事,只能发截图。
- 刷新回到上次那个对话靠 localStorage,换台机器、换个浏览器就丢。
- 浏览器前进/后退在页面内部彻底失效——切了五个对话,后退一下直接离开问数据页。
- /settings 永远落在「模型接入」,指不到数据源那一屏。
/chat/:conversationId /studio/:workflowId /runs/:runId
/knowledge/:tab /tools/:tab /settings/:tab
每页写两条显式路由(带 id / 不带 id)而不是可选参数 `:id?`:「没带 id」是真要
单独处理的一种情况(落到上次那个、或列表第一个),藏在一个问号里下次就没人
记得它存在了。
**URL 驱动 store,不是反过来。** 两边都能写就一定会打架——点一下列表,store
改了、地址栏没动,刷新又跳回旧的。所以 useConversations.currentId 降级成 URL
的派生缓存,唯一写入口是 ChatPage 的那个同步 effect;列表里的切换、新建、删除
一律改成导航。conversations.load() 不再自作主张决定「当前看哪个」,
remove() 改成返回「接下来该去哪」交给调用方。
localStorage 留着,但它现在只回答另一个问题:**打开不带 id 的 /chat 该去哪**。
**指向不存在的东西**要优雅:等列表加载完再判(在那之前"找不到"是假的,照着它
跳会让每次深链接进入都先闪一下错误提示),落到一个真实的,并说明为什么换了
地方。RunsPage 特别一点——列表只取前 100 条,而一条老运行的链接必须也能打开,
所以详情按 id 直接取,不在列表里找。
**画布多两道闸。** 选择器里本来就有"未保存改动"的确认,但那道闸在它的 onClick
里,而前进/后退会绕过选择器直接换图——改了一半的画布就这么没了。URL→画布那个
effect 里补一道,取消就把地址退回去。
过程中实测抓到三个问题,都修了:
- 同一个坏地址弹三次"那张工作流不在了":effect 在请求回来之前又跑了两遍,
加了在途去重(chat 那边同样的毛病弹两次)。
- 「在画布里打开」现在直接把人送去 /studio,而那是一张还没存、没有 id 的草稿图。
不带 id 就落到第一张图的逻辑会拿第一张把它盖掉,还会先弹一句莫名其妙的
"有未保存的改动"——加了 nodeCount 判断让开。这个坑其实原来就在,只是以前
要手动点导航才踩得到。
- 删掉当前打开的那张图之后地址还指着它,刷新会撞上"不在了"。
顺带:三个 tab 页共用一个 useTabRoute(放在 Tabs 旁边,tab 和它的地址是同一件
事,拆两处迟早各改各的),认不出的 tab 名回落并把地址一起纠正过来。
「在画布里打开」不再是 toast 让用户自己去点导航,直接送过去。
验证:tsc 干净;后端 249 passed(没碰后端,跑一遍确认没带坏);
check-decode / check-stream 不回归;check-ui 新增「URL 指得到」一节 19 条,
真浏览器里验了深链接、切换改地址、刷新、后退、坏地址、tab 直达、
草稿图不被盖掉、越过列表上限的老运行。
|
2026-09-20 14:09:40 +08:00 |
|
ThearchyHelios
|
cc608f56ec
|
跑完先过一道复核再交给你:异常说破、数字不许现编、出错自动改一次
「跑完了」和「答得对」之间一直没有任何一层。库里 264 条 succeeded 里,
14 条带着 warn、4 条 agent 步数用满,而交到用户手上的答案对此只字不提。
最糟的一次(run dd9927e6)交出去的干脆是「本轮只执行了第一个工具」——
一句内部管道文案冒充结论。
出具体系(issuance.py)本来就是干这个的,但它挂在输出节点的可选契约上,
还要配口径卡提供受控指标集;问数据页生成的图基本都是 input → agent → output,
走不到那条路。所以补一层轻的。
**干净的运行一次模型都不调。** 先用规则扫一遍事件流(engine/review.py:scan),
没扫出东西就到此为止。实测 15ms 返回。这是整层设计的前提,测试里用
「monkeypatch 一个会抛异常的模型,断言它没被碰过」来守。
**分类挂在 code 上,不是中文消息串。** 12 处 warn 发射点都带上 code。
这样「流式失败,回退非流式」能归成 benign 直接跳过——它对答案内容零影响,
为它叫一次模型等于每次网络抖一下就多花一笔;而「步数用满」归 broken。
原来这两者在事件流里长得一模一样。工具报错之后同一个工具又跑成了,
算 degraded 不算 broken——否则参数自愈每修一次就报一次「答案不可信」。
**复核不许现编数字。** 它手上只有原答案和一份异常清单,没有任何原始数据,
写出来的新数字只能是凭空来的。直接复用 issuance.trace_numbers,把原答案的
数字当受控指标集,对不上就整条退回原答案、只留说明。硬拦截,不是提示。
**规则管重试资格,模型只有否决权。** 一开始写成两边取并,实测 loop_limit
时模型判 retry=true——而重跑一遍循环还是会撞上限,白烧一次建图加一次跑图。
反过来之后,「检索什么都没查到」这类也不会被模型强推重跑。
**重试带着失败的那张图,不是推倒重来。** 原来传 base_graph=null,模型只看到
「原问题 + 一句失败原因」,很可能原样再建出同一张坏图。而且它连自己上次建了
什么都看不到——会话历史按 status != running 过滤,正在重试的这一轮恰好被
排除在外。现在交回去走后端的改图分支,调大步数、改正参数就是改一个字段。
上限一次。
**答案等复核完成再显示。** 端到端抓到的:答案先摆出来,十秒后说明才追上去——
这十秒里用户读的是一个没人核对过的结论,正是要修的那件事本身。改成落库立刻
(防关标签页丢结果)、显示等复核,并给复核加 25s 上限。说明排在成果上方,
broken 用报警色、degraded 用普通边框——混成一个样子用户就学会了一概忽略。
复核结论跟着轮次落库,刷新后还在;也进下一轮的对话历史,否则模型会拿一个
已知不可信的结论当事实继续往下推。
验证:后端 242 passed(新增 test_review.py 25 条);tsc 干净;
check-decode / check-stream / check-ui 全过,check-stream 新增 9 条渲染断言。
两条端到端都在真浏览器 + 真模型上跑通:检索降级那次说明正确排在答案上方;
重试那次建图请求 2 次、第二次 base_graph 带着失败的 3 个节点、第一版没漏出去。
复核结果不是事件流,decode.ts 翻译不到它,所以渲染测试放在 check-stream。
|
2026-09-20 11:18:53 +08:00 |
|
ThearchyHelios
|
11780ed0b4
|
设置页能接本机的向量模型了,不用改代码
上一版只有三个写死的选项:本地哈希 / OpenAI 3-small / 3-large。想用本机起的
LM Studio、Ollama、vLLM,只能去代码里调 configure——等于没有。
现在可以填任意 base_url。配套加了一个探测接口(GET /kb/embedding/probe),
按地址列出那台服务上有哪些模型,从列表里选。模型名手填太容易错——LM Studio
里叫 text-embedding-qwen3-embedding-4b,不是 Qwen3-Embedding-4B,而填错的
表现是切换时 404,人还以为是服务没起。
base_url 跟着落库。不存的话重启后会悄悄退回本地哈希,而库里的向量是上一个
模型建的,检索整体退回关键词——用户只看到"重启之后搜得不准了"。
几处说给用户听的:
- 当前用的是什么、几维,直接显示在最上面
- 本地哈希旁边标"没有语义能力"——它是默认值,而这个缺陷不说没人会发现
- 切换成功的提示里带上新维度和"多少条存量向量需要重建"。换模型后维度多半
变了,存量全部作废,这件事要当场说,不能等人发现搜不准了再去猜
check-ui 加了一节,真浏览器里验过:展开、填地址、探测到本机的
text-embedding-qwen3-embedding-4b、出现「用它」。
|
2026-09-20 09:34:55 +08:00 |
|
ThearchyHelios
|
a027261701
|
Copilot 会自己记该记的了;顺带让写记忆这件事看得见
以前只有用户明说「记住」才会写:17 轮有图的对话里只有 4 轮带记忆节点,全在
那个用户主动谈记忆的会话里。于是他说过一次「我叫张三」,换个话题再问就
谁也不知道了。
现在 answer 路径多一步判断:这句话里有没有值得长期记住的东西,有就在主链路
前面加一个 memory(write) 节点。边界比指令本身要紧,所以写死了四类不该记的:
这一轮的问题本身、查出来的数(会过期且能重查)、一次性指令、以及**用户没说过
而你推测出来的**。另外两条是从召回的实际情况倒推的——content 要脱离上下文也
成立(召回时没有这轮对话,「他说他叫张三」读起来不知道「他」是谁),以及
拿不准就不记:记错了污染以后每一次对话,漏记一条用户再说一遍就是了。
**动手前先修了一个前提**:写记忆在界面上是隐形的。memory 节点发的是 info 级
日志,而解码层明确丢弃所有 info(decode.ts),也就是说自动记忆 = 系统背着
用户往一个跨会话、长期有效的地方存关于他的信息,而他看不见。
新增 memory.end 事件,解码层按动作分开渲染:写入把**记了什么**原样显示
(只报「写入 1 条」等于没说,用户没法判断它记的是不是他想让它记的)、召回说
「想起 N 条」、清空标成警告。
真模型验过(deepseek-v4-pro):
「我平时叫小蒋,以后报表金额用万元」
→ content='用户平时叫小蒋,他要求以后报表里的金额统一用万元作单位'
scope 未写、跑完落库、事件里带着内容
→ 换个说法召回得到:「该怎么称呼我」「金额用什么单位」都命中
三条对照确认不滥记:
shop 里有几家店 → 没记
重来 → 走 reply,连图都没建
把上一轮的结果按月份排一下 → 没记
|
2026-09-19 22:41:27 +08:00 |
|
ThearchyHelios
|
df935599f2
|
知识库:换 embedding 模型不再 500,检索能回指了
三件事,都是"这三个模块一条测试都没有"的后果。
**换 embedder 会让检索整体 500。** 库里是 512 维的本地向量、查询是 1536 维的
远端向量时,cosine 直接 `shapes (1536,) and (512,) not aligned`。from_blob 有
dim 参数,但 kb.py 和 memory/store.py 调用时都没传;而且存量向量属于哪个模型
代码里没有任何地方记着,所以也无从降级。
Chunk / MemoryItem 各加 embed_model + embed_dim(走现成的 _COLUMN_MIGRATIONS,
存量回填空值,正好表示"不知道是谁建的")。检索遇到对不上的向量整体退回纯
关键词并说破,不是把它们当 0 分静默沉底——那样用户看到的是结果莫名其妙变差,
而不是"这里有一批索引该重建了"。配套 kb.reindex + POST /api/kb/reindex。
长期记忆那份一起修了。判重那条更危险:维度碰巧相同但模型不同时 cosine 算出来
是随机数,会把不相关的旧记忆认成"重复",然后把新内容丢掉。
**默认 embedder 没有语义能力,而开关藏在一个环境变量里。** LocalEmbedder 是
特征哈希 + TF,和 BM25 吃同一批词;alpha 默认 0.5 等于给了它一半权重,同义
改写一条都召不回。开启真模型的唯一开关是 AGENTLAB_USE_OPENAI_EMBEDDINGS——
没界面、没人会发现。改成设置项(照 COPILOT_SETTING_KEY 的路子),知识库页顶部
能选能看能重建。
配置失败必须响:第一版写成静默退回本地,但选了 OpenAI 却拿到哈希向量的人,
在搜不出同义表达时完全无从怀疑到这里。现在抛 EmbedderUnavailable,API 400
原样交回"检查 OPENAI_API_KEY 是否配了",且不会把配置改掉。
**检索结果不进工件库。** db_query、工具节点、agent 工具调用都落 put_json 快照,
run_retrieve 什么都不落——于是结论只要来自知识库,"这个数是哪来的"就断在这儿,
而数字回指正是这个项目的签名能力。现在落 kind="retrieval_snapshot",并发一条
retrieve.end 带 artifact id。解码层补了正式映射(原来会掉进"未知事件"兜底,
画成一坨原始 JSON)。
新增 test_kb.py 11 条,补上这三个模块此前的零覆盖,包括 embeddings.py 里
注释记着、但一直没有测试守的那个归一化边界(单条候选被 min_score 误杀)。
端到端在真实服务上验过:撞上存量向量返回 200 + 降级说明(以前 500)、
重建后 stale 归零、配不起来的远端模型 400 且配置不变。
|
2026-09-19 09:27:07 +08:00 |
|
ThearchyHelios
|
cd7ced7673
|
不是每句话都值得跑一遍库:模型自己选要不要执行
以前必执行——final 一到就无条件 launch。库里那条会话 7 轮全建了图、全跑了
一遍 153 张表的库,其中:
- 「重试」「继续找找」这种对过程说的话,各自重建了一整张图去查
- 一句明确要「设计一个工作流,使得每次执行的时候都给出管理员账户信息」的,
建完也直接跑了——用户要的是那张图,不是这一次的结果
流协议给模型两个新出口,三条路径二选一:
- {"op":"reply","text":"…"} 不建图不跑。问的是前几轮已经查出来的东西
(口径、怎么算的),或者是对过程说的话,或者需要先反问清楚
- done 里 run:false 建图但停住,界面上给「跑一下」
- done 里 run:true(默认) 不变
reply 一到服务端就收流:让它走到后面那套排版校验上,空图会撞出"图校验未
通过:图是空的,先拖一个节点进来"——上一个提交刚治过同类的坑。
放开「直接回答」等于绕开了这条路径上最硬的一条约定(涉及数据必须真查),
所以两头都上:prompt 里写死 reply 只能复述或解释前面几轮已有的结果、不得
新断言任何数据事实,凡是要动到库才知道的哪怕觉得上一轮见过也重新查;界面
上那一轮的结论**前面**挂一条声明,说清楚这一条没有查库。check-stream 里有
一条断言专门盯它排在结论前面——排在后面等于先让人读完再说来源不可靠。
需要留意:这依赖模型照约定表态,不是硬规则。判断跑偏的话收紧那几句约定。
|
2026-09-18 22:30:45 +08:00 |
|
ThearchyHelios
|
152264b29c
|
长报告不再被咬掉一口:折叠只在行边界切
用户看到的是一份读起来完整、末尾却挂着半行原始 markdown 的报告:
userID 用户名 邮箱 昵称 providerID 商家名称 联系人邮箱
| 1 | ThearchyHelios | user@example.com | … | Test Marchant By Shop
表头是制表符(已渲染成表格),数据行是竖线(还是原始文本)——折叠是
text.slice(0, 3000) 按字符硬切的,切点落进表格中间,前面几行渲染成表格、
最后半行没能成行。报告本身是完整的,只是被折叠了,但界面上看不出来。
改成只在行边界切;切点落进表格时整块表格都不要——宁可少显示一段,也不给
一张半截的表。另外原来只有一个"展开全部"按钮,看上去像报告就这么长,
现在先说"…后面还有,这里先折叠了"。
顺带治了落库那一处:answerText 在 4000 字处无声截断,刷新页面后那份就永久
少一截,断点同样常常落在表格中间。上限提到 20000,真切到了会写明已截断、
完整内容见运行记录。喂给下一轮的那份另有更狠的截断,两回事。
check-stream 多一节,盯的就是"折叠后不许留下没渲染的原始表格行"。
把切法改回硬切,它会立刻抓到残留的 `| 24 | user_23 | …`。
|
2026-09-18 22:29:24 +08:00 |
|
ThearchyHelios
|
dcadc0c48b
|
对话有上下文了,左边也能选会话
三条 run(d983c4e6 / 14103db5 / 08c3de1c)在用户看来是一次对话,系统里却
毫无关联。查下来不是哪里传漏了,是整层缺失:
- 每轮 base_graph=None 从零建图,Copilot 不知道上一轮问过什么
- 每轮 thread_id = run.id,三条独立的 checkpoint 线程
- 前端 turns 只活在 zustand 里,刷新页面就全没了——那三轮当时在界面上
已经找不回来,只剩 runs 表里三条互不相干的记录
- 后端 13 张表里没有会话这个概念
加两张表(conversations / conversation_turns)。失败的轮次也记:只记成功的,
历史就成了一份美化过的记录,而人回来最想搞清楚的恰恰是上次卡在哪。
Copilot 的上下文带三样:前几轮问答、上一轮那张图(走 _slim 去掉坐标噪音)。
只给问答文字不够——"再按月份拆一下"会让模型重新设计一张图,实测经常接到另
一张表上,答案对不上前一轮。运行时另外注入 {{ input.history }},管的是指代:
用户问"它们平均多少",图里的 agent 得知道"它们"是上一轮那 12 家店。
左侧会话栏只在问数据页出现,可折叠、可改名删除,当前会话存 localStorage。
轮次按会话分桶缓存——切走再切回来要看得到原来的步骤,回源只拿得到问答。
历史轮次的执行过程不预加载,点「看执行过程」才取事件、走现成的 decodeRun。
画布右栏的 Copilot 依附工作流各有一条会话,连说两次"再加一个节点"能接上了
(base_graph 带的是图的结果,带不出"你刚才要我干什么"),但不进左侧列表。
过程中修掉的两个自己引入的坑:
- 第一版在列表为空时自动建一条会话。create 是异步的、StrictMode 把 effect
跑两遍,于是每次进页面都留下两三条"新对话"。改成用户开口那一刻按需建
- 加了"在上一轮的图上改"这条指令之后,模型照改图协议只发 update_node,
而问数据页这条路径的累加器从空图起步,操作全部落空,用户收到的是
"图校验未通过:图是空的,先拖一个节点进来"(会话 f5db9809 的第二轮,
而它的 explanation 证明上下文其实完全读懂了)。指令改成沿用 id 和 config
但完整重发,另加一道兜底:空图且上一轮有图时把操作重放到那张图上
check-ui 多一节「会话」:切换、刷新还在、逛一圈不多出空会话。
|
2026-09-18 17:58:04 +08:00 |
|
ThearchyHelios
|
105120f22f
|
成果区渲染 Markdown:「1+1 等于 **2**」不再原样带着星号
模型几乎总是用 Markdown 组织回答,而成果区是 whitespace-pre-wrap 的纯文本,
于是它的结构全丢了:标题、列表、表格、粗体一律变成满屏符号。
**手写而不是装库。** 依赖只有七个、全是必需的,react-markdown 那一串
(unified / mdast / micromark)比这个文件大一个数量级。而要渲染的东西是
有限的——统计了库里 252 段真实输出,出现过的构造只有:
有序列表 40% · 无序列表 15% · 粗体 13% · 标题 3% · 行内代码 3%
表格 3% · 分隔线 2% · 代码块 1% · 引用 1% · 斜体 1% · 链接 0.8%
所以只做这些,**不声称支持完整 CommonMark**。认不出的语法原样当文本显示
——那正是现在的行为,下限不会比今天差。包大小 +37KB(gzip +12KB)。
安全:全程构造 React 元素,一处 dangerouslySetInnerHTML 都没有。模型输出
是不可信内容,走 innerHTML 等于给自己开个 XSS 口子。链接只放行
http/https/mailto,`javascript:` 退回纯文本。
一个只有拿真实输出才会撞上的 bug:模型很爱写 **`table_name`**(粗体套
代码),而粗体在扫描顺序上先命中,把那对反引号当普通文本吃掉了——渲染出来
是加粗的 `v_demo_table` 带着反引号。强调内部要再解析一层(深度卡在
三层,每层文本严格更短)。另外把模块级正则的 g 标志去掉了:lastIndex 有
状态,递归调用时会从上一层留下的位置接着扫。
审批卡里的待审草稿也走同一个渲染——要人家判断"这条能不能发",却让他对着
一堆星号看,是在给审批添难度。
check-stream 补 12 条,测例是从库里导出的真实输出 + 一段构造的恶意输入
(真实输出不会自带攻击,但渲染层要么天生免疫,要么就是个口子):残留标记、
粗体/代码/列表/表格、粗体套代码、javascript: 链接不可点、正常链接可点、
原始 HTML 不变成真标签且当文本显示、注入脚本没执行。
|
2026-09-18 12:51:55 +08:00 |
|
ThearchyHelios
|
1444c774cf
|
模板字段加 {{ }} 补全:从源头消灭那类静默失败
模板取不到值时渲染成空字符串、不报错,所以拼错一个变量名在运行时完全静默。
上一个 commit 是事后校验(标红+建议),这个是事前:打 {{ 就弹出这张图里
实际有的变量,每条带说明和产出它的节点。
**只挂在真正过模板渲染的字段上。** 这一条是整件事最容易做错的地方:
这套里有五个字段长得像但走的是表达式求值——skip_if、loop.condition、
cases[].condition、transform.expression、metrics[].expression——那里要写
`vars.count == 0` 这样的裸路径,不带大括号。在那些字段上挂 {{ 触发器等于
手把手教用户写错。同理 supervisor 的 agents[].system 长得和 llm.system
一模一样,但后端是 cfg.get("system") 直接取值、根本不渲染,那里弹补全会
是个谎;它走的是 AgentList 里的裸 textarea,天然不受影响。
几个不写就会坏的细节:
- 这是完全受控组件。插入后 onChange 会一路走到 store 再把 value 灌回来,
DOM 光标会跳到末尾——必须等那一帧过去再自己 setSelectionRange 放回去。
测试里专门验了"插入后继续打字,字落在后面而不是中间"。
- 候选框用文档流里的块,不是绝对定位浮层:属性面板是 overflow-y-auto、
外层 InspectorSheet 是 overflow-hidden,浮层到边缘直接被切掉。
- 用 onMouseDown 不是 onClick——onClick 在 blur 之后才触发,那时候弹层
已经关了。
- Escape 要 stopPropagation:外面 InspectorSheet 也在听 Escape,不拦的话
按一下会连整个属性面板一起关掉。
- 光标前已经打了 | 或 [ 就不再弹——那时候是在写过滤器或下标,不是在选变量。
check-ui 补一组:变量表列得出来、抽屉挤压后画布没被压没(React Flow 容器
高度归零会直接报 error 004)、打 {{ 弹候选、插入成完整写法、光标位置正确。
|
2026-09-18 10:55:56 +08:00 |
|
ThearchyHelios
|
f43f56abb7
|
把 Copilot 的思考显示出来:模型一直在想,只是被丢掉了
用户问能不能看到"正在理解需求、规划步骤"里面发生了什么。查下来是这样:
模型确实在想,reasoning 花了实打实的 token,但一路上被丢了三次。
1. langchain 的 ChatOpenAI **故意**不保留第三方的 reasoning_content——它的
文档原话是 "Non-standard response fields added by third-party providers
are not extracted or preserved",建议换 ChatDeepSeek 之类的专用包。可
"用哪个供应商"在这个产品里是用户在设置页配的(DeepSeek / Qianfan /
vLLM / OpenRouter 都可能),为每种装一个专用包不现实。在 factory 里
子类化捞回 additional_kwargs,只加字段,不动 langchain 对正文的处理。
2. thinking_text 只认 Anthropic 的 thinking 块。补上兼容端点那一种。
3. 前端把一段思考的标题固定成开头 60 个字。思考是流式的,开头往往是
"用户想要一个……"这类复述,之后几十秒里它一直在推进——固定显示开头
等于把一段活的叙述冻在起点上,看着就像卡住了。改成:还在想的时候显示
最新一句,想完了换回开头那句(那时它是一条历史记录)。
实测一次真实生成,60 秒里侧栏逐句变化:
3s 可用数据源 bi
9s 人工节点 approve 模式,审核
18s 我们可以用 db_query__sales 执行 SELECT COUNT(*) FROM orders 等
36s 更合理:rejected -> query_agent(重新
45s 对于 bi 有 53 个对象,靠
57s (plan 到达,思考收尾,换回开头那句)
顺带运行期的 LLM 节点也跟着有了思考——走的是同一个 thinking_text,之前
同样只有 Anthropic 系才有。已验证流式合并后思考连成完整一段,不是只剩
最后一片。
tests/test_thinking.py 守两种形态、合并、以及"不产思考的模型不硬凑"。
check-decode 补 5 条守"还在想显示最新一句、想完了换开头"。
|
2026-09-18 09:55:42 +08:00 |
|
ThearchyHelios
|
aa18904af0
|
运行和 Copilot 分成两层;顺带修了一个会丢事件的时序缺陷
用户:「点击右上角运行后,会跳到流程;然后下方就又有 Copilot 对话框,把
结果和 Copilot 又放在一块儿了」。说得对,而且我上一版的理由是错的。
我当时把建图和跑图排进同一条时间轴,说它们是"一件事的两半"。它们在时间上
先后发生,但不是同一种东西——最直接的证据在我自己的代码里:运行那一轮的
question 只能填 undefined。一个没有提问的"对话轮次",本来就不是对话。
更实际的问题是那个输入框:它贴在运行结果下面,暗示你可以回复这次运行。
但你不能——在那儿打字改的是画布上的图。这是个假的可供性。两者的生命周期
也对不上:Copilot 的轮次会累积,运行永远只有最新一次。
现在右栏是一个常驻的家(Copilot 对话)加两层临时盖上来的东西(节点属性、
运行过程)。运行那一层没有输入框,有自己的头部和停止按钮;对话那一层底部
留一条运行进度,点一下过去。
---
顺着这条线查下去,发现一个更严重的问题:**后端在丢事件**。
浏览器侧连跑 5 次,2 次丢了中间一条。抓 WebSocket 原始帧才看清:所有帧的
replay 都是 false——历史读回来是空的,全部走实时;丢的是 run.started,
它在客户端订阅之前就广播了,而那时还没落库,所以历史里也没有。
根因是 _emit 的顺序:先广播、后落库。订阅者的完整性靠"先订阅 → 读历史 →
接实时"拼起来,它需要这条保证——提交早于我这次 SELECT 的,在历史里;提交
晚于我这次 SELECT 的,广播也晚于我订阅,在实时流里。后半句只有在"提交先于
广播"时才成立。交换顺序后,6 次 WebSocket 探测 + 5 次浏览器运行全部无缺口。
(上一轮我修过这个文件的另一个窗口——先读历史后订阅。那个修复是对的,但
只堵住了两步之间的缝,堵不住订阅建立之前的那一段。)
tests/test_emit_order.py 守这条不变式:广播的那一刻,这条事件必须已经能从
另一个 session 查到。去掉修复后它确实失败。
---
还修了三处:
- 停在人工介入时,"在不在等人"原先取自审批列表,而那是 4 秒轮询一次的;
中断后会有几秒钟界面说"这次运行已结束",而它正等着你点通过。改成从事件
推导(isAwaitingHuman),即时且和显示的内容一致。
- 新增 tests/conftest.py 把整个测试会话关进临时目录。test_emit_order 第一版
直接用 SessionLocal 往开发库里写,几行假运行排到了运行列表最前面。已清掉
那 12 行。这该是全局保障,不是只修一个文件。
- 开发期把 store 挂到 window.__studio,好让 playwright 直接读状态——"事件
收全了没有"在界面上看不出来,只能问 store。已确认不进生产包。
|
2026-09-18 09:39:24 +08:00 |
|
ThearchyHelios
|
3de7567377
|
右栏重构:助手常驻,属性改为盖上去的一层
两个问题都源于同一件事——一栏 360px 被要求同时干两件活。
**没地方引导写问题。** Copilot 输入框是个 38px 高的框,挤在一条长长的流
底下,比它上面任何一块都不起眼;例句被截断成 16 个字的 chip,示范"该说到
什么颗粒度"这个作用就没了,只剩装饰。改成一个组件两种体量:没说过话时它
就是这一栏的主体(竖直居中、三行高、自动聚焦、完整句子的例句),并且告诉
用户它能够到什么(N 个数据源 / M 个工具)——不然"查销售库"这种话有没有意义
只能靠猜。有了对话之后它退到底部两行。不做成两个组件:那就是两个实例,
切换时会卸载其中一个,草稿、光标、输入法候选词全没。
**Copilot 和属性挤在一起。** 它们原是 [助手|属性] 两个 tab,而 tab 是互斥的
——跑图跑到一半点开节点看配置,整条执行过程就从眼前消失,切回来滚动位置和
输入草稿也没了(切 tab 会卸载组件)。改成属性滑出来盖在助手上:助手一直在
下面活着,属性是临时造访者。头部给一个明确的"‹ 助手",Esc 关得掉;盖住
期间底部留一条执行进度,点一下回去——不能因为换了实现就把原来最难受的
那个场景原样留着。
运行控件搬去工具栏。它原先和 Copilot 输入框一起常驻助手栏,两个都是"主要
动作"互相压着;而它们是两种意图(跑这张图 / 改这张图)。运行是对整张图的
动作,和保存、发布同类。有输入字段就弹表单,没有就一键直接跑。
顺带修了两处状态说反的地方:
- 停在人工介入时,承载的节点还在转蓝圈。转圈说的是"它在忙,你等着",而
实际正相反——它在等你。改成待办色 + 手势图标。
- "继续执行"的去重原先靠"界面上还有没有 waiting 步骤",上一条改动多了一处
产生 waiting 的地方就失效了。改用事件相邻性判断——那是这两条事件的固有
性质,不会因为别处改了展示而变。
动效:面板滑入、例句依次入场、正在出字的那一步有一道光扫过(比转圈多说
一件事:它在产出,不只是在等)、空态标记轻微呼吸。全部 240ms 以内,缓动
统一 cubic-bezier(.32,.72,0,1)。另外补上了此前完全没有的
prefers-reduced-motion——这套界面里动的东西不少(边在流动、节点在脉冲、
面板在滑),系统里关了还照播,对前庭敏感的人是实打实的难受。
验证:check-ui 重写了助手栏那一节,核心断言是"选中节点再回来,输入的字
还在"——那正是 tab 方案做不到的。新增一条用 emulateMedia 验证关掉动效后
动画确实停了。真跑了一次带人工审批的图,期间打开属性面板确认进度条还在。
四套检查 + 82 个后端测试全过,生产构建通过。
|
2026-09-18 00:05:22 +08:00 |
|
ThearchyHelios
|
3ff6840b99
|
多 agent 节点不再永远转圈,调度决策也说出来了
运行 #6038f5:supervisor 节点跑完 31.7s 之后,researcher 那一行还在转圈。
agent.step.start / agent.step.end 是这套事件里**唯一**一对没做配对的
(node、llm、tool、sandbox、human 都配了)。start 那条建出来就是 running,
end 另起一行,于是没人去关它。这正是 decode.ts 自己文档里的第 2 条原则
——成对事件合并成一行——我写了却没用在 agent 上。
配对键用 (node_id, agent, round):光用 agent 名不够,supervisor 会在多轮里
反复派给同一个 agent,第二轮的 end 会错配到第一轮的 start 上。
顺带把 supervisor 的调度决策捞出来。后端把它标成 info 日志,而 info 是整类
丢弃的,结果"为什么派给 researcher"、"为什么只跑一轮就结束"在界面上完全
没有交代——那个节点就是一个跑了 31 秒的黑盒。带 round 字段的 info 才是调度
决策,和普通排查日志区分得开。FINISH 译成"结束协作"而不是"交给 FINISH",
后者会让人以为还有个叫 FINISH 的成员。
真实运行已收进 fixtures,check-decode 加 9 条(含"普通 info 日志仍然不进
主流程"和"warn 日志照常显示"这两条反向约束)。
|
2026-09-17 19:14:06 +08:00 |
|
ThearchyHelios
|
f0fd63f0b9
|
UI 重构:Copilot 进侧栏,三个页面共用一条助手流
用户的原话是「copilot(包括问答)可以是侧边栏的效果」「显示他的简要思考节点,
使得用户知道他大致在想什么,以及用了哪些工具」。
画布右栏从「运行 / 属性 → 时间线 / 成果 / 事件」(两层嵌套 5 个视图)变成
「助手 / 属性」一层。助手栏里是一条流:你让它做什么、它做了什么、结果是什么。
Copilot 建图和跑图在协议上完全无关(SSE 操作流 vs 事件 WebSocket,不共享
seq 和 node_id),但对用户是一件事的两半,decode.ts 把两种流译成同一种 Step
之后才能排进同一条时间轴。
Copilot 不再是弹窗。弹窗的问题不是样式:每改一次图都要重开一次,关掉之后
需求文本就没了,想微调只能重打;而画布左下角那条浮条(z-30)和 Toast
(z-100)、Modal(z-50)三层各自为政,本身也是重复的一份 Copilot 状态渲染。
两个都删了,输入框就在栏里,说完一句接着说下一句。右栏没删——它是发起运行
和处理审批的唯一入口,RunLauncher 原样保留,只是跑起来会自动收起给执行过程
让位。tab 用 hidden 切换而不是卸载,不然点一下「属性」看个节点再切回来,
输入框草稿和展开状态全丢。
问数据页和运行页换成同一个 AssistantStream + 同一个 decode.ts。RunPanel 里
那套平行的 EVENT_META + describe() 删了:留着两套翻译,同一次运行在不同页面
会读出不同的故事,而用户没法判断哪个是真的。同理合并了两个耗时格式化函数
(画布节点显示"0ms",右边同一步什么都不显示,两块还并排放着)。运行页列表
加了内容摘要——原本每行只有"临时图 · 1.2s · 340 tok",十条长得一模一样,
要分清哪条是哪条只能挨个点开。
跑真实运行时发现两个缺陷:
1. 后端丢事件(app/core/bus.py)。WebSocket 先读历史、再订阅,两步之间图
正好跑出来的事件既不在历史里也不在实时流里,永远丢了。而 subscribe() 是
异步生成器,真正注册还要等到第一次迭代,窗口比看上去更大。实测一次跑图
开头的 node.finished/node.started 消失,界面上表现为某个节点永远在转圈、
它的子步骤跑到了顶层。改成先 attach 占位、再读历史,重叠部分按 seq 去重。
tests/test_bus_attach.py 里有一条刻画旧行为的测试,说明这个窗口确实存在。
2. 循环里的多轮审批被算错。我在 S1 按内容去重,但三轮审批的 title 一模一样,
和 LangGraph 的重放无法区分——结果第 2、3 次真实审批被当成重放吞掉,而
三次"你驳回了/你放行了"各自成行。改成开/闭配对:开着就是同一次,闭了才是
新一次;决定折进那条审批行而不是另起一行。真实运行(驳回 → 改写 → 再驳回
→ 改写 → 放行)已收进 fixtures。
验证:三层各守一件事,共 76 条断言全过。check-decode 守翻译(含新的多轮审批
和截断结果集),check-stream 守渲染,新增 check-ui 守"接起来还对不对"——真
浏览器真后端,包括「切走再切回来草稿还在」「唯一的运行入口还在」「原始事件
切得到也切得回」。另外真跑了一次带三轮审批的图和两次 Copilot 生成(60s,
deepseek-v4-pro),截图逐帧看过。后端 78 个测试全过。
|
2026-09-17 18:29:16 +08:00 |
|
ThearchyHelios
|
c1c13fad41
|
助手流组件:一份 Step,宽屏和 360px 窄栏共用
AssistantStream 是纯展示的,不连 store。这样画布右栏和问数据页能用同一个
组件,而不是"一个组件三种停靠模式"——后者要求同一个实例出现在三个 DOM
父节点里,React 里除非 portal 否则不成立,硬做就变成跨路由 remount,
输入草稿和展开状态全丢。差别只在 dense 一个开关。
渲染真实事件时发现的几个问题,顺手一起修了:
- 取数运行的最终成果是一个**被截断的双重转义 JSON**(后端按字符数硬切,
切点落在 JSON 中间是常态),严格 JSON.parse 必然失败,于是这个产品最
典型的一次运行,成果区呈现为满屏 \"attribute01\", \"attribute02\"。
parseQueryResult 改为解析失败时截到最后一条完整记录再解析——78 列 1 行
的表格,比一屏转义文本有用得多。并区分"查询撞了行数上限"和"预览被切断":
前者该去调查询,后者该去看完整工件,混成一句"已截断"等于没说。
- 审批恢复过的运行有"开始执行"+"继续执行"两条生命周期,run.finished 只
收了第一条(find 而非 forEach),"继续执行"永远转圈——明明已经跑完。
- 处理完的审批保留着 warn 级别,一直是橙色,看着像还有事没办。
- 一屏 0ms:那些纯赋值节点确实不花时间,把没信息量的数字摆出来反而把真正
慢的那一步淹了。低于 10ms 不显示,同时空耗时给 undefined 而不是空串。
- output 里 {"result": ""} 很常见(图跑通但出口没接上),会渲染出一条什么
都没有的分隔线。
验证:新增 scripts/check-stream.mjs,真浏览器跑真组件——5 类真实运行 ×
(无报错/无转义 JSON/无 0ms/不横向溢出)+ 展开看 SQL 和结果表 + 空态。
check-decode.mjs 补 11 条,两个脚本共 68 条断言全过。frontend/preview.html
是离线预览页,只有 dev server 加载(已确认不进 vite build 产物)。
|
2026-09-17 17:00:50 +08:00 |
|
ThearchyHelios
|
ca6e672dfa
|
事件解码器:全站唯一的"事件 → 人话"翻译层
UI 重构的地基。现有的 describe()(RunPanel.tsx:392)是工程师视角——
db_query__warehouse({"sql":...}) 是函数调用语法、1234+567 tok · $0.0021 是账单、
exit=0 · microvm 是退出码,而且 25 个事件类型里一半直接 return '' 没翻译。
做成独立模块而不是散在各页面,是因为画布时间线、助手栏、运行页要读同一个
故事。两套翻译的话同一次运行在不同地方讲得不一样,用户没法判断哪个是真的。
## 三条原则
- **不回写事件**:run 终态时对全部已落库事件算 manifest_hash,事后增删改会
对不上。翻译只在前端内存里发生。
- **成对事件合并成一行**:tool.start + tool.end 是同一件事的两个时刻,拆两行
等于把"查询数据"和"查完了"当成两件事。按 call_id 配对(并发调同名工具时
按名字会错位),独立 Tool 节点没有 call_id 则回退到名字。
- **节点是天然的分组单位**:一个图节点内部可能有好几轮模型和工具调用,
它们属于同一个步骤——这正是"简要思考节点"该有的结构。
## 用真实事件调出来的五个坑
从库里导出 5 类真实运行(带查询/带审批/带出具/失败/带思考)当 fixture,
构造的样本永远不会长成那样:
- **审批重复三次**。human.requested 没有 interrupt_id、run.interrupted 才有,
两个 key 算不到一起;而审批恢复后 LangGraph 重放节点又发一遍 human.requested。
改用两者共有的 node_id|mode|title 做键,三条收敛成一条。
- **节点重放让同名节点出现两次**。node.started 第二次来时复用原来那条。
- **"继续执行"说两遍**。run.resumed 和 run.started(resumed=true) 是同一件事。
- **标题退化成裸 id**。没起过名字的节点 label 等于 id(后端兜底),"in"/"h"/"out"
对用户毫无意义——退到节点类型的中文名。
- **空 preview 当详情显示**。{} 不是详情,是噪音。
## 刻意不做的
- 不显示 token 数和美元:那是账单视角,不是"它干了什么"
- 不依赖 canvas/nodeDefs:解码器是纯翻译层,画布只是消费者之一。
16 行的类型名映射重复得起,反向依赖会让运行页被迫加载整套节点定义
- 未知事件不静默吞掉:宁可显示一条原始的,也比让人觉得"什么都没发生"好
## 验证
scripts/check-decode.mjs 用真实 fixture 跑 21 条断言(转译借 vite dev server,
测的就是应用实际运行的那份)。守的是"审批只出现一次""没有裸 id""没有事件名
泄漏到标题"这类会悄悄退化的性质。
|
2026-09-17 16:25:33 +08:00 |
|