ThearchyHelios
|
db328efd21
|
原子写和部署脚本的几处(复核 R-a11、R-a12、R-a13 追加写)
- R-a12 create_bytes:硬链接不了的文件系统(exFAT、一部分 SMB / FUSE,EPERM / ENOTSUP / EXDEV)不再首次启动就崩,退回 O_CREAT | O_EXCL 直接建(仍然只建一次,说明里写了写的那一会儿读的一方可能读到不完整);建成后 fsync 目录(同 raw_store)
- R-a13 file_write 的追加改回原地追加写(O_APPEND,core/atomic.append_bytes):不换文件(inode、权限、开着的句柄不变),原有内容不动,文件大了也不用整份读出来重写;取舍写在函数说明里——读的一方最多看到半截新内容。覆盖照旧先写临时文件再改名,并沿用原文件的权限
- R-a11 deploy.sh 改属主前认数据目录收紧:只认 agentlab.db、.secret_key、.agentlab-data 三样之一(不再认 uploads/ 这种普通目录名);.agentlab-data 是服务启动时(Settings.ensure_dirs)建的标记,只建一次不覆盖,库在 PostgreSQL、密钥由环境变量给的部署也认得出;老部署有 agentlab.db 或 .secret_key。deploy/README 写明这个文件别删
- 测试:模拟不能硬链接仍只建一次、权限 0600、不留临时文件;覆盖沿用原权限;追加不换 inode、权限不变;file_write 追加不换文件、覆盖照常;数据目录里有标记、重复启动不覆盖
|
2026-10-19 12:07:31 +08:00 |
|
ThearchyHelios
|
ba87a93ef7
|
bubblewrap 沙箱不再继承后端的环境变量(复核 R-a5):bwrap 参数最前面加 --clearenv,再按缺省(HOME、TMPDIR、PATH、LANG、PYTHONDONTWRITEBYTECODE)和调用方给的逐条 --setenv;bwrap 进程本身也只拿 PATH
以前被执行的代码看得到后端进程的全部环境变量:模型的 API Key,现在又多一条带密码的 AGENTLAB_DATABASE_URL。local、seatbelt 一直只给最小的一套。调用方给的变量以前插在最前面、会被缺省的盖掉,现在放在最后、能盖过缺省的,同另外两种沙箱。测试(本机没有 bwrap,测参数和进程环境的组装):--clearenv 在所有 --setenv 前面,AGENTLAB_* / *_API_KEY 的名字和值都不出现,bwrap 进程的环境只有 PATH
|
2026-10-19 12:07:31 +08:00 |
|
ThearchyHelios
|
0d9f1d9339
|
用户看得到的列表补决胜键(复核 R-a9):按名称、时间等可能并列的排序后面补上同一个模型的 id,并列时两种库排出来一样
- 实测 /api/evals/calibers、/api/schedules/trigger-options 按 Workflow.name 排序遇同名并列时 SQLite、PostgreSQL 顺序不同。用 AST 逐个过了一遍 order_by:最后一个排序键是 created_at / updated_at / last_active_at / at / name / 各种 *_at 之类、不唯一的列,又不带 DISTINCT、GROUP BY 的,补「, 模型.id」(降序补 id.desc()),共 104 处,分布在 api、apps、auth、blueprints、chat、data、engine、eval、ledger、look、memory、process、providers、scheduler(runs.py 里的三处在上一个提交里)
- 按唯一列(seq、version、唯一的 name)排的不动;按聚合、coalesce 之类表达式排的说明见 db/postgres
|
2026-10-19 12:07:06 +08:00 |
|
ThearchyHelios
|
7febf2bcdb
|
两种库行为再对齐一轮(复核 NUL、R-a4、R-a6、R-a7、R-a10)
- 编排者的决定:PostgreSQL 下文本列写入时去掉 NUL 字符(before_cursor_execute 钩子,只装在应用库引擎上,看所有字符串参数,没有 NUL 的原样交回),SQLite 不变;同 JSON 列的处理
- R-a4 应用库地址里的 sslmode= 换成 asyncpg 的 ssl=(取值同一套),sslrootcert 等证书参数、application_name 这类 asyncpg 不收的参数启动时就报错并说怎么写(证书用 PGSSLROOTCERT 等环境变量);断点库连接串换回 sslmode=
- R-a6 不分大小写的搜索换成 db/portable.contains_ci:SQLite 上同 ilike(lower LIKE lower),PostgreSQL 上模式带 COLLATE "default"(列是 COLLATE "C",列上的 ILIKE 只认 ASCII,É/é、Ж/ж、全角A/a 匹配不上);运行列表按名字、对象索引按注释两处都改;改正 db/postgres 里那句说明
- R-a7 JSON 守卫改成编译层:tests/jsonguard 挂在应用库编译器上(conftest,两种库、跑全量时一直生效),整个 JSON 值出现在 = / != / IN / IS DISTINCT FROM / 大小比较、SELECT DISTINCT、GROUP BY、ORDER BY、UNION(去重的几种)里就报错;按类型认,变量别名、跨行 order_by、select(Run.origin).distinct()、JSON 下标的比较都抓得到,测试拿这些反例证明,json_eq、IS NULL、UNION ALL 不受影响。原来的逐行正则测试删掉
- R-a10 json_eq 两种库同一语义、按 JSON 类型比:字符串只等于字符串,数只等于数(1 等于 1.0),True / False 只等于 JSON 的 true / false(SQLite 上用 json_type 分开,以前 1 能匹配 true);非标量 TypeError。json_text 两种库都是「字符串取本身、别的取 JSON 写法」(SQLite 上同 PostgreSQL 的 ->>),数 1 的 json_text 两边都是 '1'
- db/postgres 说明里写清 NULL 排序补写的范围(只认直接按列排的)
|
2026-10-19 12:06:31 +08:00 |
|
ThearchyHelios
|
0e997eb968
|
copy-to-postgres 在真实开发数据上核对不过的几处(复核 R-a1、R-a2、R-a3、R-a8、R-a13)
- R-a1 老库 NOT NULL 列为空:第一版补 schedules.trigger 时没带缺省值,存量定时任务这一列是 NULL,导入 PostgreSQL 撞 NOT NULL,连带触发记录、通知撞外键。迁移表里这一列改成带 DEFAULT '{}';新增 _NOT_NULL_BACKFILLS,启动时按模型的缺省值把已经补过、留着 NULL 的填上(SQLite 这边也填,两种库存的一样;只更新这一列,不碰 updated_at);导入时 _Fixer 遇到 NOT NULL、有缺省值的列的 NULL 也填缺省值,计入「转换」。迁移表里的 NOT NULL 列都过了一遍,只有这一列没带缺省值
- R-a2 孤儿行:导入前在快照上按模型外键扫(settle_orphans):外键是 SET NULL、列可空的置空,计入转换;CASCADE / 不可空的从快照里拿掉、不导入,反复扫到没有为止,报告里单列「孤儿行(SQLite 当时没强制外键)」的表和条数,不算核对失败
- R-a3 停服务后 agentlab.db-wal 还在:lifespan 收尾 dispose 应用库引擎(只加了这几行),SQLite 最后一个连接关掉时并回、删掉 -wal,PostgreSQL 的池子也正常关
- R-a8 自增主键的序列取「目标表 MAX、快照 sqlite_sequence 的计数器、序列现值」三者最大,删过的号不复用(召回按 source_changes.seq 当游标)
- R-a13 插不进的行的原因不再带「<class 'asyncpg.exceptions.…'>:」前缀(errors.first_line 两种写法都去);设了 AGENTLAB_DATABASE_URL 却没装 [postgres] 时,应用库引擎和断点库都直接说要装什么
- 测试:DROP COLUMN 再按第一版迁移补回造老表、关着外键写孤儿行,迁移填值、快照上置空和拿掉、导入 PostgreSQL 核对通过且重跑一样;NULL 落回模型缺省值;端到端里删掉最后一条变化,导过去新变化从 3 编起;起停一次服务后数据目录里没有 -wal / -shm(去掉 dispose 这条测试就挂)
- PostgreSQL 引擎上另挂了去掉文本参数里 NUL 的钩子(db/postgres.strip_nul,见下一个提交)
|
2026-10-19 12:06:31 +08:00 |
|
ThearchyHelios
|
f0c34d790d
|
守着 PostgreSQL 的 json 列:扫应用代码里对整个 JSON 列做 ==、!=、in_、DISTINCT、ORDER BY、GROUP BY 的写法(json 没有相等运算符,SQLite 不报、PostgreSQL 报错),一处都不许有
|
2026-10-19 10:28:18 +08:00 |
|
ThearchyHelios
|
5e6ba12678
|
断点库模块去掉没用上的 kind();test-postgres.sh 的注释照现在的连接池写法改
|
2026-10-19 10:27:17 +08:00 |
|
ThearchyHelios
|
b9cfc243e2
|
PostgreSQL 上文本按字节比大小、排序,同 SQLite:应用库建表时文本列带 COLLATE "C"(PostgreSQL 缺省按建库的区域设置比,en_US.UTF-8 下大小写混排、标点被忽略,排出来的先后和 key < '…' 的结果都跟 SQLite 不一样),不看建库时选了什么;驱动报错的第一句去掉 asyncpg 带的「<class '…'>: 」前缀
- 测试:大小写、标点、中文混在一起的排序和比较两种库一样;建表语句里带 COLLATE "C",SQLite 的不带
- README:和 SQLite 一致的那几条补上排序规则
|
2026-10-19 10:26:45 +08:00 |
|
ThearchyHelios
|
b2f6d8d7d2
|
README、部署说明补上用 PostgreSQL:AGENTLAB_DATABASE_URL、断点、连接池(含 PgBouncer)、和 SQLite 一致的那几条、从 SQLite 导入的步骤(copy-to-postgres,先停服务、先 --dry-run)、备份(pg_dump 加数据目录)、多台机器要把数据目录挂在共享存储的同一个路径上,以及实话:数据目录里的 SQLite 文件(记录表原地追加写、上传表格版本库的 flock)在网络文件系统上并发写不安全,眼下仍按单进程部署
- 依赖与锁文件:[postgres] 里装的是什么、为什么 langgraph-checkpoint 单独卡小版本、checkpoint-postgres 锁在 3.1.2;数据位置:PostgreSQL 时不再用 agentlab.db / checkpoints.db;开发与验证:scripts/test-postgres.sh
- environment.yml 带上 [postgres];.env.example 补应用库一节
- deploy.sh:数据目录里没有 agentlab.db(库在 PostgreSQL)时,认 .secret_key、uploads/ 也算 AgentLab 的数据目录,不再拒绝改属主
- 断点库的连接串不带只给 asyncpg 看的参数(prepared_statement_cache_size 之类,psycopg 不认);地址规范化、启动日志不带密码的测试
|
2026-10-19 10:13:58 +08:00 |
|
ThearchyHelios
|
3e922cbefb
|
新增 python -m app.db copy-to-postgres:把 SQLite 数据目录里的应用库和断点导入 PostgreSQL(db/to_postgres.py)
- 先停服务:来源旁边留着 agentlab.db-wal / checkpoints.db-wal(还有连接开着,或者上次是强杀),或者目标库上有 application_name=agentlab 的连接,就拒绝,什么都不写;强杀留下的 -wal 用 --assume-stopped 放过。查不到的(另一台机器上的服务)在说明里写明要自己保证
- 读快照:SQLite 备份接口拷到 --work-dir(缺省系统临时目录),在拷贝上跑一遍启动时的建表补列,老版本的库按当前表结构读;来源按读写打开、只许读(mode=ro 打开 WAL 库会留下它自己建的 -wal,下一次导入就当成服务还开着)
- 目标:同启动时的 create_schema 建表;按外键先后逐表、按主键分批 INSERT … ON CONFLICT DO NOTHING——目标上已有同一主键(或撞了别的唯一约束)的行一律不动,重跑只补没有的;整批撞了外键之类的约束就逐行重试,插不进的列出主键和原因;自增主键的序列接到导入的最大值后面
- 值:按模型的列类型读写(时间、布尔),JSON 原文照搬(SQL 的 NULL 和 JSON 的 null 分开、键的先后不变);PostgreSQL 不收的(NaN、NUL、整数列里的浮点和数字字符串、文本列里的非文本)换掉并计数打印
- 断点:用两个 saver 的公开接口逐条读出写入(含 pending writes,特殊通道和普通写入分开写,序号同来源),目标上已有的断点不重写;按线程核对断点和 pending writes 的条数
- 核对:每张表按主键数来源的行目标上有没有(导入前已有、这次补上分开数),逐表打印;有没导进去的退出码非零。--dry-run 只核对、不建表不写
- 测试:另起进程在 SQLite 数据目录里跑一次停在审批上的运行(事件、审批、待办、工件引用),导入新建的空库、再导一遍什么都不重复,再另起进程连这个 PostgreSQL、同一个数据目录批掉审批,运行从断点接着跑完、事件序号接着往下编、工件读得到、新的变化记录的序号接在导入的后面;dry-run 不写;目标上服务还连着就拒绝;来源、参数、值转换的单测
|
2026-10-19 10:07:06 +08:00 |
|
ThearchyHelios
|
f63d037b2c
|
PostgreSQL 上和 SQLite 一致的两处:知识库统计的 sum 转成整数(PostgreSQL 的 sum 交回 numeric,驱动给 Decimal);对象索引按注释搜索不分大小写(SQLite 的 LIKE 对 ASCII 本来就不分,PostgreSQL 分,改用 icontains)
|
2026-10-19 10:01:20 +08:00 |
|
ThearchyHelios
|
dead51ae56
|
数据目录里的文件一律先写临时文件再改名:新增 core/atomic(同目录、带随机后缀的临时文件,写完 os.replace;只该生成一次的用 os.link 建,已有就不动)
- 工件(内容寻址):以前共用 <id>.tmp,两个进程同时写同一份内容时后到的会截断先到的半成品、先到的改名又让后到的改名失败;现在各写各的临时文件
- 加密密钥 .secret_key、断言签名私钥:几个进程同时第一次启动时只建出一把,后到的读先到的那一把(以前各生成一把,各自加密、签名的东西对方认不得),文件从建出来起就是 0600
- 附件抽出文字的缓存:临时文件名带随机后缀
- 文件工具(file_write)的覆盖和追加、三种系统沙箱写进工作目录的代码和输入文件、seatbelt 的策略文件:先写临时文件再改名
- 上传表格的版本库、原件存档、记录表本来就是这样写的,不动
|
2026-10-19 10:01:20 +08:00 |
|
ThearchyHelios
|
2e7d6103ea
|
应用库可移植到 PostgreSQL:AGENTLAB_DATABASE_URL 设成 postgresql+asyncpg://… 时整个应用库用 PostgreSQL,断点用官方的 langgraph-checkpoint-postgres(同一个库,AGENTLAB_CHECKPOINT_URL 可另给);不设时照旧是数据目录里的 SQLite,行为不变
- 配置:database_url、checkpoint_url(启动时校验写法,只认 PostgreSQL)、连接池四项;启动日志写用的是哪种库(主机、库名、用户,不写密码)
- 引擎(db/base.make_engine):SQLite 照旧 WAL、外键、忙等;PostgreSQL 用连接池、探活、会话时区 UTC,装上方言调整(db/postgres.py):建表时 String(n) 是不限长的 VARCHAR(SQLite 从不查长度,导过来的存量也可能超长)、ORDER BY 里可空列的 NULL 位置照 SQLite 补写(升序在前、降序在后,窗口函数里同样);Integer 列在 PostgreSQL 上是 BIGINT(列挂到表上时换成 with_variant,绑定参数跟着列按 BIGINT 转换);JSON 列两边都存原文(PostgreSQL 的 json,不用 JSONB:JSONB 重排对象里的键,快照的列序、表的先后读回来就变了);JSON 里的 NaN 写成 null、NUL 去掉(PostgreSQL 不收)
- 迁移按方言:已有的列和索引用 inspector 认;PostgreSQL 上补列的类型取模型里这一列的、缺省值取迁移表里的(布尔 0/1 换成 false/true),ADD COLUMN IF NOT EXISTS;去重语句里的布尔按方言写;部分唯一索引补 postgresql_where;init_db 在 PostgreSQL 上拿咨询锁(几个进程同时启动不撞)
- 应用库上的 SQLite 专用写法改成按方言编译的表达式(db/portable.py):json_extract 的十几处(口径召回、出具、对账、个人应用、幂等键、访问控制、数据源变化通知、剖析任务、待办超时)换成 json_eq / json_flag / json_text,json_type 换成 json_is_string(两种库同一套带类型的比较);能力登记的 sqlite insert 换成按会话方言的 insert_ignore;能力使用次数的 json_each 按方言分支;附件「没绑记录」的条件不靠 json 的等号;按报错文字认唯一约束的地方换成 unique_violation(按表和列认,两种库的报错都认得)
- 断点(engine/checkpoints.py):PostgreSQL 时开 psycopg 连接池、在咨询锁里 setup;runner 只认 BaseCheckpointSaver,读断点仍只走 saver 的公开接口
- 依赖:新 extra postgres(asyncpg、psycopg[binary] 3、psycopg-pool、langgraph-checkpoint-postgres 3.1.2);langgraph-checkpoint 卡在 <4.3(断点格式就在这个包里,postgres 包新版要 4.3,不动已装的 4.2.0);锁文件补上新包
- 运行收尾:记 action.unknown、安排核实做完才把运行摘掉(以前先摘掉,等「不在跑了」再读事件的一方在库慢时读不到这一条)
- 测试:AGENTLAB_TEST_DATABASE_URL 时跑在 PostgreSQL 上(每个 xdist 进程建自己的库、按事件循环分的连接池 tests/pgpool.py),不设时盖掉环境里的数据库地址照旧用 SQLite;scripts/test-postgres.sh 起临时的 postgres:16 容器跑全量;sqlite_only 标记;另开连接改库的几个竞态测试、触发器测试改成两种库都能跑(tests/appdb.py);新增 test_db_portable 守着方言调整、JSON 键序、超长字符串、大整数、NULL 排序、时区、PostgreSQL 老库迁移
|
2026-10-19 09:52:09 +08:00 |
|
ThearchyHelios
|
c79f4e8bf7
|
合入两处修正(部署上一段问数据会话里查出):数据源的探查范围(introspect_scope)、定时维护(catalog_maintenance)不再被拼进连接串——以前设过它们(运行时按需加入范围也会自动写)的 MySQL / PostgreSQL / Oracle 数据源,驱动 connect() 收到不认识的参数,每一条查询都失败,加守卫测试扫 app/data 下所有 *_OPTION 常量;上游查库全失败时报告照实说没查到,不再照着上游文字写出引用不存在的 Q1… 的标记。验收:后端全量 14568 通过;文案检查;泄露扫描干净
|
2026-10-19 08:25:44 +08:00 |
|
ThearchyHelios
|
06286deefa
|
上游查库全失败时报告照实说没查到:报告节点写作前从上游 Agent 的调用记录里找出失败的平台查询(engine/nodes/report.failed_queries),写作提示里多一节「没有查到的数据」列出哪几次、原因第一行;一次成功的查询都没有时不许写单元格引用、照实写没查到和怎么排查,重写请求里同一句;记一条 report_queries_failed 提醒。以前写作者照着上游文字里的 SQL 和列名写出引用不存在的 Q1…,重写一次还是那样,答案里留着没填上的 ⟦?v:Q1…⟧
|
2026-10-19 08:23:29 +08:00 |
|
ThearchyHelios
|
0ca8243587
|
数据源的探查范围、定时维护不再被拼进连接串:data/engine._NON_DRIVER_OPTIONS 补上 introspect_scope、catalog_maintenance。以前漏在名单外,设过探查范围(运行时按需把范围外的表加进来时也会自动写)或定时维护的 MySQL / PostgreSQL / Oracle 数据源,驱动 connect() 收到不认识的关键字参数,每一条查询都失败;加守卫测试,把 app/data 下所有 *_OPTION 常量扫出来逐个核对都不进连接串
|
2026-10-19 08:08:51 +08:00 |
|
ThearchyHelios
|
3aae81918a
|
合入编排完善第 1 波(docs/design/orchestration-round):问数据整轮放到服务端跑(建图、发起运行、复核、按复核重来、落库都在后台任务里,和评测共用同一个驱动;页面只负责看,掉线、刷新、关掉再打开按序号接着看,停止走接口;同一段对话、同一轮的发起在一个事务里占住再写);子工作流的步骤和证据进父图(子图每一步进同一个运行的事件流,出口字段带出处,父图口径卡、报告跨子图追到原始数据,子图里的报告按全局 id 记);驳回出口(审批、动作确认、代码和工具审批、双盲裁决驳回后沿出口往下走,不接时行为不变);定时任务的事件触发(上传表格出新一期、上游工作流正式运行成功结束、数据源有要处理的变化;去抖、防环、途经驳回与应用补算缺省不认)。验收:后端全量 14562 通过;tsc、文案检查;相关的 15 项浏览器检查 5452 项全过;泄露扫描干净
|
2026-10-18 18:37:33 +08:00 |
|
ThearchyHelios
|
591db44afd
|
README:编排完善第 1 波的升级说明(问数据整轮放到服务端、子工作流的步骤和证据进父图、驳回出口、定时任务的事件触发);核心能力的人工介入补上驳回出口
|
2026-10-18 18:31:52 +08:00 |
|
ThearchyHelios
|
ea28f02eeb
|
子工作流里那一步结束了运行时各处同一个名字:新增 apps/resolve.end_label(终态事件上记的「子工作流 › x」),定时任务的 Webhook 不再在找不到节点时什么都不写、发布单不再写内部 id「sub/x」;补测试
|
2026-10-18 18:28:17 +08:00 |
|
ThearchyHelios
|
4ca7d0c768
|
驳回出口接上数据事件触发:正式运行收尾时把途经的驳回(_rejected_via)交给 outbox.stage_run_finished,workflow.finished 照发并带上 rejected_via,缺省不认途经驳回的触发不派;补一条走驳回出口的正式运行与批准走完的对照测试
|
2026-10-18 18:25:24 +08:00 |
|
ThearchyHelios
|
c0739dfd5d
|
合入 W1-c 数据事件触发:定时任务加「事件触发」(上传表格出新一期、上游工作流正式运行成功结束、数据源有要处理的变化),事件在发生处写进待派发事件、调度循环去抖派发;保存时与运行时防环;{{ event.* }} 模板变量;途经驳回、应用补算的缺省不认(可勾选);积压一拍处理完、过期的合成一条跳过;按期次分窗口;定时页可选「定时 / 事件触发」
|
2026-10-18 18:23:33 +08:00 |
|
ThearchyHelios
|
3d5fb19fbd
|
合入 W1-b 子工作流证据交回父图 + 驳回出口:子图每一步进同一个运行的事件流;子图出口字段带出处,父图口径卡、报告跨子图追到原始数据(运行时、证据面板、静态检查同一套,最多 3 层);报告目录收进子图里的查询和口径卡;审批、动作确认、代码和工具审批、双盲裁决可接「驳回」出口,驳回后沿出口往下走(不接时行为不变),动作确认走出口不发送;发布检查拦下走不到结束的驳回路径;实例页、通知写「被驳回,转到 X」;子图里的报告按全局 id 记、父图只认自己的报告
|
2026-10-18 18:23:15 +08:00 |
|
ThearchyHelios
|
a4b83abecc
|
合入 W1-a 问数据整轮放到服务端跑:一轮是一个后台任务(建图 → 发起运行 → 复核 → 按复核重来 → 落库都在服务端),和评测共用同一个驱动(chat/drive.TurnDriver,评测 Driver 是它的子类);/api/chat/turns 发起、状态、按序号回放的实时流、停止;chat_turn_jobs / chat_turn_events;服务端写对话那一行;服务重启时建图中的标中断、已发起运行的接着收尾;页面改成发起 + 播放,掉线、刷新、关掉再打开都能接着看;同一段对话、同一轮的发起在一个事务里占住再写;使用者照旧不能继续运行
|
2026-10-18 18:23:15 +08:00 |
|
ThearchyHelios
|
3e6032bdca
|
复核修正(前端):节点计数、并行数、「当前在跑」不把子工作流里的步骤另算;子图那一步写「子工作流 › 那一步」
- B5 run/trace:子工作流里的步骤(isSubStep:节点 id「子工作流节点 id/…」)算在子工作流那一步里——完成数不另算(以前运行详情
会写「7/3」),没有 run.started 时的分母也不算它们;Telemetry 的「当前「X」等 N 个」同一个规矩,子工作流和它里面那一步不算成
两个同时在跑
- B6 run/trace.stepLabel:图上没有的子图步骤接上子工作流的名字(「取数子流程 › 查订单」,和服务端终态事件的 label 同一个拼法,
名字取 node.started 记的);运行胶囊(RunHud)、泳道(RunTimeline)的失败、阻断说法和泳道名、记录页的失败横幅与航迹
(RunDetailView.labelOf)都用它,不再写「失败于「sub/x」」
检查脚本:check-decode 加两段(子工作流步骤的完成数、并行数、当前在跑;出错在子图里那一步时的名字)
|
2026-10-18 18:18:53 +08:00 |
|
ThearchyHelios
|
32dbfcfb52
|
复核修正(后端):子图里的报告不再被判「疑似被修改」、驳回出口的双盲裁决照样降档、可验证报告与推演按卡所在的层找产出、子图那一步写名字
- B1 子工作流里有报告撰写节点时:它的 report.checked 进了父运行(节点 id「sub/write」),报告文档却记子图里的 id「write」,
证据接口、可验证报告、质询拿两边一比判「疑似被修改」,评测读答案也说答案不能当证据。
- nodes/report:报告文档、产出工件记这次运行里的节点 id(ctx.run.global_id),和事件同一个;nodes/io 子图自己的出具契约
按同一个 id 核对,_issuance.report.node_id 同样写这次运行里的 id
- api/evidence:_reports 默认只认父图上的报告(子图的报告是子工作流那一步内部的东西,父图的答案是子工作流出口的值),
nested=True 连子图里的一起给;点名要子图报告的(?report=sub/write)照样点得开(_named_reports);按成果挑报告的兜底
不再挑中子图的报告。成果上没有 _issuance 时退回最后一条 issuance 事件,子图成果节点的那条不认
- bundle:出具档位(_tier)同样不认子图成果节点的 issuance,verifier.js 同一个认法;子图报告的文档在引用链上照常核对
- B2 双盲裁决被驳回、走驳回出口接到出具成果时:节点产出换成 exit_payload,出具缺口和 dual_check 台账条目跟着丢了,审核人驳回的
那组数照样完整出具(比选「两边都不对」还宽)。NodeRejected 加 exit_updates(节点在驳回之前已算出的产出字段、台账、用量),
compiler._rejected_exit 并进去;双盲复算驳回时照记缺口「指标「X」两路结果不一致,审核人驳回了裁决」和一条结论为驳回的
dual_check。核对过别的关卡:人工节点、工具调用审批、动作确认、代码执行审批都在产出缺口、台账之前就被驳回,没有可丢的
- B4 子图里的卡转抄同一张子图里的卡、按位置取格:可验证报告(bundle/build._outputs_of)和推演(api/whatif._sources)按这次
运行里的 id 查产出,子图卡上记的是子图里的 id,查不到就整张遮住、推演不了;父图有同名节点时还拿父图那个当依据。改成按卡
所在那一层的 id 查(engine/lineage.in_scope / scoped_id,证据面板 _InputMasks 同一个认法),转抄的卡认不出时的兜底同样换层
- B6 子图里的一步出错、被驳回后 Run.error_node_id 是「sub/x」:实例页(apps/resolve)、通知(notify/service)照终态事件上记的
名字写「子工作流 › x」(run_node_label),不露出内部 id
测试:test_subgraph_evidence(子图含报告:证据图按父图、评测读答案不报被改、子图报告点得开、可验证报告核对通过且不写子图的
档位、子图自己的契约照常;子图卡在可验证报告里不被遮、推演还原得出、父图同名节点不当依据;子图那一步的名字)、
test_dual_check_node(驳回出口接到出具成果照样降档、台账照记、证据面板认得)。每条都去掉修复回跑过,确实失败
|
2026-10-18 18:15:37 +08:00 |
|
ThearchyHelios
|
656a550408
|
数据事件触发按复核意见修正:途经驳回、去抖积压、source.changed 补写、按期次分窗口、补算缺省不认、示例出口字段同一套过滤
M1 途经驳回(编排者已定):outbox.stage_run_finished 收可选的 rejected_via(缺省 None 即没途经驳回),事件内容带 rejected 和
rejected_via,说法接「(途经驳回)」,模板有 {{ event.rejected }}(true / false)。触发条件缺省只认没途经驳回的,trigger 加
include_rejected(界面「也认途经驳回的」勾选,说明照实写)。runner._finalize 把 _rejected_via(state) 传进来那一下留给编排者
在合并时接(runner 的 _rejected_via 在 W1-b 的分支上);测试直接调 stage_run_finished 传参覆盖。
C1 去抖积压:以前一拍只走一个窗口,积压时后面的越等越久、全过了宽限,而且每个窗口各记一条跳过,原因还说调度没在跑。
现在 _drain 按发生时刻把等着的事件切成窗口,一拍里把到点的处理完(最多 100 个窗口、5000 条);排在前面已过补发宽限的合成
一条 skipped_misfire(合并了几次事件、几个窗口、时间跨度,上传表格另写合掉的期次),派给任务时已经晚于宽限才说「调度未运行」,
否则照实说积压;宽限内的照常发起(照重叠规则跳过的照实记)。
C3 source.changed:这一处确实没法和变化记录同一个事务(一次探查、剖析的变化分好几批落库,一批只触发一次要等任务结束才知道),
改成说实话:models_triggers 的说明写清哪两种同事务、哪一种不是。trigger_events 加 dedupe_key(source.changed:<任务号>,唯一),
emit_source_changed 按它去重;调度的一拍(刚起的服务第一拍就补,之后每 10 分钟)由 backfill_source_changes 按任务号补写:2 天内
测到、还开着的要处理级变化,任务已结束却没有事件的补一条。分组在 Python 里做,不用各库写法不同的 JSON 取值。
C4 按期次分窗口:triggers.window_key 让上传表格按期次各自成窗口,9 月、10 月连着启用各有各的触发;同一期的重传照旧合并。
两个窗口结束在同一刻时后一个挪 1 微秒,fire_at 不撞唯一约束。
C5 应用补算:workflow.finished 缺省不认 app_backfill 发起的(升版补算连发好几条、各是某一期的历史输入),trigger 加
include_backfill(界面「也认应用补算的」),选了「只认应用补算」时本来就认;说法写「途经驳回、应用补算的不算」。
C6 示例出口字段:triggers.outputs_of 一处过滤「_」开头的键,真派发、预览、立即运行共用;示例事件也按 matches 跳过缺省不认的
途经驳回、应用补算,取合条件的最近一次。
测试:新加 test_schedule_events_review.py(11 项,先写、先看着失败;去掉 matches 的驳回判断、把每拍窗口数改回 1 回跑,对应用例
各自失败),test_schedule_events_api.py 加一项「也认」两项经接口存取和说法;check-schedules 加「上游工作流的请求体」一段、纯函数
几条、触发历史里合并跳过那一行的夹具。
验证:后端全量 14479 通过;tsc、check-copy;check-schedules 447 项、check-runs 327 项通过(拿锁、--stacks 1)。
|
2026-10-18 18:09:20 +08:00 |
|
ThearchyHelios
|
d6318b639d
|
问数据页的复核修正(前端):使用者不给「继续运行」,两个标签页同时打开停在半路的一轮时后到的那个接着看
- 使用者(user 角色)续不了运行(服务端照 POST /api/runs/{id}/continue 的老规矩回 403):补救区不摆「继续运行」,服务重启打断的
那一轮说明里也不叫它去点,改说「如需整轮重来,请点击「重新运行本轮」」(lib/terms 的 CHAT_TURN_TEXT);重试本轮、重新运行本轮照常。
以前这个按钮对使用者也摆着,点了只弹一句 403。
- 两个标签页同时打开停在半路的一轮:都会请服务端接着收尾,服务端现在只起得来一次,后到的那个收到 409 chat_turn_busy 时不再说
「无法确认本轮的当前状态」,改成问一下这一轮现在怎样——在跑就从这一次尝试的第一条接着播,收了尾就照那一行摆。
- scripts/check-chat.mjs 新加两段:「使用者(user 角色)」(伪造 /api/auth/me 认出使用者:中断的、跑挂的都不给继续运行,一次续跑都
不发)、「两个标签页同时打开」(服务端只接着收尾一次,两边都收尾、答案一样、都不写库)。去掉修复回跑,这两段都会失败。
|
2026-10-18 18:07:03 +08:00 |
|
ThearchyHelios
|
85eee89799
|
问数据一轮的复核修正(后端):同时来的几次发起只起得来一次、事件 seq 不撞号、晚到的复核记得上也送得到、使用者不能继续运行、
入口的 history 只取这一轮之前的、只有问数据的对话能发起
A1 并发发起不原子(复核实测:同一对话同时两次新问都 202、两轮 seq 都是 0;同一轮同时 3 次重试 [500, 202, 202],同一轮建两次图、
停止只停了后一个;写 turn.attempt 撞号时任务已提交成排队却没有后台任务,这段对话从此一直「正忙」):
- 发起的核对和写入(这段对话忙不忙、下一轮排第几、轮次、任务、这一次尝试的第一条事件 turn.attempt)放进一个事务,第一句先占住
这段对话(hold_conversation:SQLite 上拿到写锁、PostgreSQL 上锁住这一行),同时来的几次一个接一个,后来的看得见先来的写下的;
重试、接着收尾的那一轮占住之后重读,任务按「不在排队、不在跑」条件更新、看 rowcount;写不进去整个事务回滚,什么都不留下。
继续运行要先续上那次运行(它用自己的会话写库,不能放进占着对话的事务),续上了却没排上队的把它取消;挂起旧运行的取消挪到
提交之后(只有排上队的那一次去取消)。页面打开时自动发的接着收尾占锁时不改活跃时间。
- conversations.new_turn 先记活跃时间再数 seq(第一句写就占住这段对话),老页面的开轮接口也不再撞号。
- 事件的 seq 由任务行上的计数器发(UPDATE … SET last_seq = last_seq + 1 RETURNING),同一轮同时追加的几处各拿不同的号。
- ChatTurnManager.start:同一轮在这个进程里还有后台任务活着就不起第二个、不覆盖登记;上一次的任务刚记完结局还没退出时等它退出再起。
A2 使用者(user 角色)现在能「继续运行」了:以前 POST /api/runs/{id}/continue 对它是 403(只放行停止),问数据这里照原说法回 403
role_required,运行不动;重试本轮、重新运行本轮照常。
A3 晚到的复核:先写库、发 review.late,再出登记、叫醒订阅方(以前反过来,开着的实时流先收到 stream.end 走掉);那一行的几处写入
(任务、晚到的复核、下一次尝试)先占住那一行再读改写(hold_turn),用量改成按增量记在库里那一份上(以前任务开始时拿一份拷贝、
之后整块盖回去,重试时晚到复核记的那笔会被盖掉);自动重来时换下来的那次运行交出去之后不再当这一次的运行记一遍。
A4 运行入口的 history 只取这一轮之前的:新的 conversations.run_history 照以前页面 historyOf 的规则(答案没有用说明、都没有的跳过、
每轮截 600 字、最多 6 轮),重试、重新运行较早的一轮时后面几轮不再进它的入口。
A5 只有问数据的对话(kind=chat)能发起一轮,别的种类 422 chat_turn_invalid。
测试:tests/test_chat_turns_races.py(asyncio.gather 并发两次新问、三次重试、两个标签页接着收尾、二十次并发追加事件;发起写到一半
出错什么都不留下;后台任务不起第二个;使用者继续运行 403 且说法同 /runs;开着的流收到 review.late;重试不丢晚到复核的用量;
history 只取之前的、截 600 字、跳过没答案的;重新运行较早的一轮不带后面几轮;只认问数据的对话)。先写测试,12 条都失败;去掉
占锁、计数器、不起第二个、先写库再叫醒、增量记用量、同一事务、按 seq 截这几处修复分别回跑,对应的测试都会失败。
|
2026-10-18 18:07:03 +08:00 |
|
ThearchyHelios
|
124131b393
|
问数据一轮:停止正好落在发起运行的那一下,也不会留下没人管的运行
发起运行(launch_run)是这一轮后台任务里的一步,停止就是取消这个任务:取消落在发起的半路上,运行可能已经建好、开始执行,这一轮
却还没记下它的 id——停止取消不到它,它照样跑、照样计费,那一行也不知道有过这次运行。现在发起那一下不让停止打断:等它发起完、
记下运行,再照常取消它,那一行收成已取消、meta.runId 是这次运行。
测试:停止落在发起的半路上(发起被拦住、放行之前先停),停下之后这次运行是已取消、记在这一轮上;去掉修复回跑会失败。顺手把
「运行中停止」那一条改成等任务上记下运行 id 再停(stage=run 在发起之前就记下了,满载跑全量时读到的 run_id 偶尔还是空的)。
|
2026-10-18 17:10:01 +08:00 |
|
ThearchyHelios
|
8b74c0d532
|
问数据页的状态:去掉播放改造后用不上的导入(parseServerTime)
一轮搬到服务端之后,运行开始时刻由服务端随 turn.attempt 给(started_ms),页面不再自己解析运行的 started_at。
|
2026-10-18 17:03:10 +08:00 |
|
ThearchyHelios
|
fa9725adf2
|
问数据一轮的收尾补漏:接着收尾不算占着对话,实时流没等到结局不再转圈,刷新后接着播的过程照实时的样子摆
- 一段对话里停在半路的几轮(老轮次、服务重启后停着的)要一起交给服务端收尾:resume 不建图、不发起运行,不再按「这段对话正忙」
拒掉第二轮(以前打开这样的对话,只有第一轮收得了尾)。测试先写、去掉修复回跑会失败。
- 实时流结束了却没见到这一轮的 end(服务端那一轮出了岔子、这一轮被删了或没权限了):回服务端问一次那一行,照它摆;问不到就
照实说「无法确认本轮的当前状态」,给「重新核对」。重新核对一轮还没有运行的:问服务端它在不在跑,在跑接着播。
- 刷新、关掉再打开后接着播的那一轮就是正在跑的一轮:建图的过程照实时的样子一直摆着,不当成要点开才取的历史轮次
(check-chat「刷新后接着看」一段多守这一条,去掉修复回跑会失败)。
|
2026-10-18 16:55:30 +08:00 |
|
ThearchyHelios
|
5ff3e1136d
|
问数据整轮放到服务端跑(前端):store/chat.ts 改成「发起 + 播放」,断线按 seq 重连补齐,刷新、关掉再打开接着看
为什么:一轮问数据搬到服务端之后(上一个提交),页面不该再推这一轮:建图那条流、发起运行、复核、按复核重来、落库都和页面
绑在一起,页面一断这一轮就停。现在页面只发起、只看,要不要运行、要不要重来、什么时候交付都照服务端说的。
做了什么:
- 新的 api/chatTurns.ts:发起、查询、停止(挂在 api.chatTurns),streamChatTurn 订阅这一轮的事件日志——先回放、再接实时;
断了按收到的最后一个 seq 自动重连(收到东西马上接着连,连不上按退避),45 秒什么都没收到(服务端 15 秒一拍心跳)当连接死了。
- store/chat.ts:plan / launch / settle / retry 换成发起(launchTurn)+ 播放(play)。收到的操作照原来的渲染逻辑画(画布边收
边长、过程步骤、思考、查看、用量);收到 run 就接上运行自己的实时流画过程(watch 只画过程,不再判结局);stage 说到了
哪一步(停在审批上立刻去拿待审批);turn.retry 照原来自动重来那样重置;end 照服务端落库的那一行摆结局(和刷新之后从库里
恢复的是同一份 restoreTurn)。「重试本轮」「重新运行本轮」「放宽步数」「跑一下」「继续运行」「停止」都走新接口。
- 页面不再写这一轮的结局(不再 PATCH 轮次、不再调 /copilot/review、不再发 POST /api/runs);失败的说法照服务端记的原因
(meta.cause)按 lib/explain 讲,老轮次记的 failure 照旧认。
- 刷新、关掉再打开:服务端还在跑的一轮(ConversationTurn.job)从这一次尝试的第一条事件接着播;库里停在半路、服务端又没在跑
的(老轮次、服务重启后停着的)请服务端接着收尾(mode=resume),不在页面上自己收尾。问数据页的显示不变。
- 页面上那份退路(MAX_ATTEMPTS、fillInput、retryInstruction、等复核的计时)去掉:决定只在服务端一处。后端对照测试改成守
「页面上不再有这几样」。
- scripts/check-chat.mjs:伪造的服务端照 backend/app/chat/job.py 的做法用原来那几份剧本把一轮跑完(生成、发起运行、等运行的剧本
放完、复核、拼那一行),原有各段照过;守落库的几处改成守「页面不 PATCH 这一轮」和照服务端那一行摆;新加「断线重连补齐」
「刷新、关掉再打开接着看」「停止(建图中也停得下)」几段,并核对发起、重试、放宽步数、继续运行、停止的请求体。
- 服务端顺带:turn.attempt 带上到目前为止的用量、从什么时候算起(补交付的到运行结束为止),turn.retry 带上换下来的那次运行
并进之后的用量(页面上的运行壳子里没有用量);接着收尾(resume)不要求运行还在,运行记录删了的照实收成「运行记录不见了」。
|
2026-10-18 16:49:26 +08:00 |
|
ThearchyHelios
|
38addf8f5d
|
驳回出口与子工作流证据(前端):画布多一个「驳回」出口、走了驳回出口的那一步画「被驳回」、子工作流里的步骤和证据有名字
接后端两个提交(docs/design/orchestration-round/00-总方案.md §2、§3):
- canvas/nodeDefs:会被人驳回的节点多一个「驳回」出口(id rejected,和后端 REJECT_HANDLE 同一个,中性色):人工节点三种
模式、双盲复算一律给;沙箱代码只在审批策略是每次审批时给;调用工具选了工具、审批策略不是从不审批时给(数据源的查询
只读,只在明说每次审批时给)。出口跟着配置算:改成不会被驳回时,studio 原有的出口同步把连在驳回出口上的线收掉;补充输入
改成审批时普通出口的线跟到「批准」
- run/trace:node.finished 带 outcome=rejected 时这一步收成 rejected(exited,记下转到哪几步),下一次执行开始时清掉;
子工作流里的步骤不另算一路并行
- run/derive:接了驳回出口的关卡按出口路由(驳回出口 / 其余是普通出口,同 compiler._make_router);走了驳回出口的算
「送出了数据」,下游照常排队、跑完,普通出口那一侧未到达;退回修改时两条出口都算走过;withSubSteps 把子工作流里的
步骤排在子工作流那一步后面(泳道、航迹的节点列表)
- run/decode:走了驳回出口的那一步状态 rejected、副标题写谁驳回、备注、转到哪一步;终态写「完成(途经驳回)」和途经的
那几步(run.finished 的 rejected_via),没有途经的一字不变;子工作流里的步骤名接上子工作流的名字
- run/useNodeView:回放时「走了驳回出口」只说被驳回的那一刻
- canvas/NodeCard、pages/runs/TracePane:走了驳回出口的节点卡写「被驳回,转到「X」」,航迹节点卡写出口和转到哪,关键时刻
多一条「「审批」被驳回,转到「X」」、结局写「运行完成(途经驳回)」
- run/EvidencePanel、types:输入来源写明经哪个子工作流的哪个出口字段;子工作流里的节点(「sub/…」,画布上没有)照证据接口
给的名字写(node_label、subgraph_label),悬停写 id;停在子工作流入口的写「子工作流的运行输入」
- lib/terms:REJECTED_TEXT 加出口、走出口那一步、途经驳回的说法;EVIDENCE_TEXT 加经子工作流的说法
检查脚本:check-decode(驳回出口的步骤、航迹、走过的边、跑到一半、批准走普通出口;子工作流步骤名)、check-run-states
(reject-exit:卡片状态与剪影、槽里写转到哪、驳回边走过批准边没走、运行成功)、check-studio(各类节点的出口、能连能画、
改配置收掉驳回线、改模式线跟过去)、check-evidence(subgraph:输入来源写明经哪个子工作流、子图节点写名字)
|
2026-10-18 16:30:39 +08:00 |
|
ThearchyHelios
|
35611beb7d
|
数据事件触发:去抖中的等待状态补一条断言(几次在等、窗口何时结束)
pending_for 给接口的 trigger_state:派给任务、还在去抖窗口里的条数和窗口结束的时刻。原来只在没有等待时断言过 due_at 为空,
这里在两条事件落进同一个窗口、还没到时刻时核对 waiting=2、due_at 是第一条发生之后 60 秒。
验证:后端全量 14467 通过(-n 8)。
|
2026-10-18 16:15:28 +08:00 |
|
ThearchyHelios
|
3f1eeff841
|
数据事件触发(界面):定时页新建时选「定时 / 事件触发」、事件那一节、列表与详情的等待状态、触发历史和运行详情的「由事件触发」
做了什么:
- 新建定时任务(目标是工作流时)先选怎么触发:定时 / 事件触发(TriggerEditor 的 TriggerModePicker);修改时只读地写出触发方式、
说明建好之后不能改。选了事件触发,频率那一节换成事件(EventSection):三种事件、对象下拉(能选哪些、不能选的原因由新接口
GET /api/schedules/trigger-options 给,界面照着画、选不了的置灰并写原因)、上传表格的期次(只认新增的一期)、上游工作流的
只认应用 / 只认来源和触发链上限的说明、去抖、时区;换事件种类时对象和过滤清掉。
- 预览按一次事件渲染输入(对象最近一次的事件,没有时照实说是示例值),不列触发时间;变量按事件种类取(api.schedules.eventVariables),
插得进 {{ event.* }};请求体带 trigger_kind、trigger,cron 交空串;修改时只交改了的触发条件。model.ts 加 TriggerForm 与
triggerBody / switchEvent,表单问题里事件触发不查频率、要求选对象;服务端 422 指 trigger 时落到事件那一节。
- 列表、详情:「事件触发」标签,频率处写触发说明;下次运行写在等什么(去抖中收到几次、何时合成一次触发;有事件等待派发;调度
没开时照实说不会派发)。触发历史:类型「事件」,写「由事件触发:…」、合并了几次、第几层;立即运行按最近一次事件发起的写
「按事件:…」并标示例值。运行详情页头写「由事件触发:…(定时任务「X」)」(RunDetailView 只多传一个 trigger 属性),立即运行
按事件发起的写「立即运行定时任务「X」,按事件:…」,不说成由事件触发;应用页印章同样不算。
- 文案进 lib/termsTrigger.ts;SCHEDULE_TEXT 只补触发类型「事件」和「还需处理」里的「触发事件」。立即运行按示例事件的说法
不再在服务端加「(示例值)」后缀,是不是示例值看 real。
为什么:总方案 §4 要求定时页能建事件触发的任务、列表 / 详情 / 触发历史 / 记录页照实显示;能不能选、为什么由服务端判,界面不放逻辑。
验证:tsc、check-copy;check-schedules 加纯函数一段(表单 ⇄ 请求体)和三段页面(新建的请求体、列表 / 详情 / 触发历史、运行详情
页头,含 390px 浅色),假接口加了触发选项、事件预览、事件变量、事件触发任务的夹具,写请求断言请求体;原有各段照过;check-runs 照过。
|
2026-10-18 16:11:55 +08:00 |
|
ThearchyHelios
|
3998e821dc
|
子工作流的证据交回父图(后端):子图每一步进同一个运行、台账和口径卡交回、出口字段带出处、父图跨子图往下追
调查时发现:run_subgraph 只设了节点 id 前缀(node_prefix)没用上,子图在父节点里用 ainvoke 跑,LangGraph 不把它的
custom 事件交给父图的流——子图里每一步在运行的事件里一条都没有,时间线看不到、封存也不含它查过的数。「事件本来就在同一个
运行里」此前并不成立(docs/design/orchestration-round/00-总方案.md §2)。
- engine/context + nodes/multi:子图拿到父运行的写入口(STREAM_KEY)和前缀(PREFIX_KEY),子图里的节点经它发事件,节点 id
「子工作流节点 id/…」,嵌套的一层层接上;工件按这次运行里的 id 记。子图里报的错(含被驳回)交回时节点 id 换成这一层的、
名字接上子工作流(runner 的 run.failed / run.cancelled 写「子工作流 › 那一步」),不再和父图里同名的节点混在一起
- nodes/multi:子图的台账条目加前缀并进父图 evidence(父图报告的目录据此收进子图里的查询和口径卡);子图口径卡的产出以
「子工作流节点 id/卡」进父图 nodes;出口每个字段按出口模板认出处(模板整串就是一个取值链、渲染出来正是那个值的才认,
同口径卡认输入的那一套),出自子图运行输入的按子工作流节点的「输入」模板再往父图追一层,追不到的记 subgraph_input
- engine/compiler:子工作流节点自己的 node.finished 不再重记交回来的台账条目(子图那几步自己的 node.finished 已经带着)
- nodes/metrics:父图口径卡读 nodes.<子图>.output.<字段>(或 assign_to 变量的字段)时接上那个字段的出处,另记 subgraph、
field;经子图出口转抄子图口径卡的指标照样带上不完整、存疑;cell(nodes.<子图>.output.<原样交出的查询结果>, …) 按台账
认出子图里的那份快照
- engine/lineage:静态认法跨子图往下追(出口模板 → 子图里接着认;子图的运行输入按「输入」模板回父图认;最多 3 层),
和运行时同一套;whole_ref 是两边共用的「整串一个取值链」判断。lint_process.load_subgraphs 往下读嵌套的子工作流,
subgraph_spec 给静态认法取图;没读好放进来的照旧停在子工作流节点
- nodes/report:报告收证据的范围加上上游子工作流交回来的(evidence_from 写了子工作流节点的一并算)
- nodes/io:指标出具的缺口里,停在子图里的说停在那个子工作流(和静态检查同一句)
- api/evidence:卡上记的节点 id 按卡所在那一层认(_scoped:子图里的卡写子图里的 id),转抄、输入、追不到的那几步一路给到
子图里的那一格;子图里的节点按 node.started 记的名字接成「子工作流 › 节点」(node_label、subgraph_label);双盲复算条目
里的卡 id 同样换成这次运行里的
- apps/resolve:实例进度不单列子图里的步骤
- 口径账本的 use_caliber 不改;比对读封存的查询,子图里的查询如今在封存范围里,照常认得出
测试:test_subgraph_evidence(子图每一步进运行、报错点名子图里那一步、实例进度、父图口径卡跨子图追到查询格并完整出具、
子图里追不到的父图降档、不是整串取值链的停在子工作流、运行输入往父图追、嵌套两层、静态检查与运行时一致(含两层和输入
模板)、父图报告引用子图里的查询和口径卡并一路点到那一格、封存与可验证报告、子图里的遮罩照父图的运行)。去掉「交写入口」
回跑,五条失败(快照不在封存范围里)。后端全量 14472 通过
|
2026-10-18 16:11:15 +08:00 |
|
ThearchyHelios
|
d18a496ef9
|
问数据整轮放到服务端跑(后端):一轮是一个后台任务,建图、运行、复核、按复核重来、落库都在服务端,页面断了也会跑完
为什么:一轮问数据以前是浏览器在推(store/chat.ts 的 plan / launch / settle):网络一抖建图那条流就断、建了一半的图丢掉;
发起运行、复核、重来、落库都要页面在,页面一关这一轮就停在 running,15 分钟后才被判成断了(总方案 §0 的调查结论)。
做了什么:
- 流程和评测共用一份:eval/answer.Driver 里的流程抽到 chat/drive.TurnDriver,差异写成子类给的几项(生成的请求、目录视图、
改图、入口、发起参数、停在审批上怎么办、复核等多久)。评测的 Driver 成了它的子类,行为和测试一字不变(test_eval_* 照跑)。
- chat/job.py:问数据的一轮是 core/job_runtime 上的后台任务(不占数据源的队,并发归「交互」类,第 2 波再给上限和优先级)。
停在审批上照旧等人(评测是取消);复核最多等 25 秒,晚到的那次用量照记(review.late);按复核自动重来一次;用户点的
重试本轮、重新运行、放宽步数、跑一下、继续运行、接着收尾都是同一轮里的一次尝试,上一次的结局归档进 meta.attempts。
- 事件日志(新表 chat_turn_events,models_chat.py,不进运行的封存范围):每一步按 seq 落库再叫醒订阅方,订阅方按 seq
回库里读——回放和实时是同一段代码;心跳一拍也回库里看一次,第 2 波多进程时别的进程写的最迟一拍送到。
- 接口 api/chat_turns.py:POST /api/chat/turns(202 带 turn_id)、GET /{id}、GET /{id}/stream?after=(SSE:先回放、再推
实时、带心跳、结束补 stream.end)、POST /{id}/cancel。挂 chat_scope,每个接口按这一轮所属的对话核对归属;访问密钥调不了。
- conversation_turns 那一行由服务端写(chat/record.py:状态、运行 id、答案、复核、meta 各项和页面以前写的一一对应);失败
只记原因(meta.cause),说法留在界面层按 lib/explain 讲。TurnOut 带这一轮的任务(job),列表判「还在跑」先看任务。
- 使用者的问数据运行规则(白名单、自己的记忆范围、遮罩、只读)抽到 chat/launch.user_chat_run,POST /api/runs 和服务端的
一轮共用,说法一字不变。
- 服务重启:还在建图的标中断(可重试本轮);已发起运行的接着等那次运行、照常复核收尾;和调度循环同一个开关,拷来的数据
目录启动时不替人收尾,页面打开对话时再请服务端接着收尾(mode=resume)。老轮次停在建图中的同样标中断。
- 删对话时先停掉它正在跑的几轮(取消各自的运行),任务和事件日志一并删。
测试:tests/test_chat_turns.py——一轮跑完不依赖页面、直接回答与反问、开流前被拒、实时流回放后接实时与按 seq 重连(补齐、
不重复)、思考合并、建图中与运行中停止、重启时建图中标中断与已发起运行的接着收尾、开关关着时等人来接、等审批后收尾、
按复核重来、用户重试归档、一段对话一次一轮、继续运行被拒照 /runs 的说法、删对话停掉这一轮、使用者的归属与遮罩只读、
访问密钥调不了、驳回与失败与发起被拒的原因、晚到的复核、跑一下与重新运行不重建。去掉几处关键实现回跑,相应的测试都会失败。
|
2026-10-18 16:05:53 +08:00 |
|
ThearchyHelios
|
d57cdcaffc
|
驳回出口(后端):审批、补充输入、编辑稿件、动作确认、代码和工具审批、双盲裁决接了「驳回」出口时沿它往下走
以前这六类关卡被驳回一律抛 NodeRejected,运行收成 cancelled + end_reason=rejected,没有「驳回之后走另一条路」
(docs/design/orchestration-round/00-总方案.md §3)。现在画布上多一个可选的「驳回」出口(sourceHandle="rejected",和
分支、审批同一套连线机制):
- engine/schema:GATE_TYPES(人工、调用工具、沙箱代码、双盲复算)、reject_targets(驳回出口连到哪几步)、approve_mode;
画布校验:别的节点接驳回出口报错(graph.reject_exit_invalid,条件分支的 case 标识可以叫 rejected,不算);审批同时设了
「驳回即终止运行」和驳回边的给一条提示(human.stop_on_reject_exit)
- engine/compiler:接了驳回出口的关卡按出口路由(驳回出口 / 其余是普通出口,连线没写 sourceHandle 的照样是普通出口);
NodeRejected 被接住时这一步正常结束:产出 NodeRejected.exit_payload()(rejected、decision、actor、note、kind、text,
审批另带 approved=false、「结果存为变量」照旧写 false),node.finished 带 outcome=rejected、哪种关卡、结论、谁、备注、
转到哪几步(to),trail 记 outcome=rejected;不重试、不被「出错时继续」接住,照旧
- engine/context:NodeRejected 加 stop(审批明说了驳回即终止的,驳回边照旧走不到)和 exit_payload;动作确认抛在发送之前,
走出口时什么都没发
- engine/runner:成功结束的运行途经驳回出口时,run.finished 带 rejected_via(在哪一步、谁、结论、转到哪),照实说「途经
驳回」;没有途经的终态事件一字不变
- engine/governance:发布检查各级都拦「驳回出口那条路走不到成果节点或结束」(reject_exit.no_end);退回上游再问一次的放过
- engine/layout:驳回出口排在普通出口下面
- apps/resolve:实例进度写「被驳回,转到「X」」(动作确认写「未确认(被驳回),转到…」,超时驳回写「超时驳回,转到…」);
实例状态(实例页头、列表、通知同一句)写「「x」被驳回,转到「y」」,走到另一个成果节点的照旧写那个成果节点;退回修改后
又批准了的按批准算;子工作流里的步骤不进实例进度
- 没接出口:行为一字不变(node.failed reason=rejected、运行 cancelled + rejected);审批没设驳回即终止、没接驳回边的老规矩
照旧。接了驳回边的审批,实例进度那一步的说法从「已驳回」改成「被驳回,转到「已退回」」(test_app_instance_page 跟着改)
另:engine/context 的 RunContext 加 prefix / global_id、事件经 stream_writer 发(子工作流把父运行的写入口交下来),
这一步本身不改变最外层的事件,下一个提交接上子工作流
测试:test_reject_exit(五类关卡接了出口各走一遍、批准走普通出口、超时驳回走出口、没接一字不变、驳回即终止优先、
重试和出错时继续不管驳回、退回修改再问一次、校验与发布检查、布局、实例进度);test_process_actions(动作确认走出口
不发送);test_dual_check_node(裁决走出口)
|
2026-10-18 15:56:29 +08:00 |
|
ThearchyHelios
|
a1ab3247f9
|
数据事件触发(后端):待派发事件表、调度循环派发与去抖、防环、{{ event.* }} 模板变量、接口收 trigger_kind / trigger
做了什么:
- 新表 trigger_events(待派发事件)、trigger_deliveries(一个事件派给一个定时任务、等去抖),放在 db/models_triggers.py,
models.py 末尾整模块导入。事件在发生处、和那件事同一个事务里写一行:上传表格按期累积的新一期启用(table_versions 的
publish_build / publish_snapshot 切指针那一下,移除、作废、回滚不算)、正式运行成功收尾(runner._finalize 写终态的事务里
加两行调用 outbox.stage_run_finished)、探查 / 剖析新记了要处理的变化(source_changes.notify_after_job,一个任务一条)。
发生处不发起运行:事务边界、重复与重启都在派发这边说清,也为第 2 波多进程做准备(认领靠条件 UPDATE)。
- scheduler/outbox.py:调度循环每一拍(fire_due 之后)派发:pending 事件条件 UPDATE 认领成 routed,同一事务里给合条件、启用着的
事件触发任务写 delivery;窗口从最早一条的发生时刻起算 debounce_s(缺省 60 秒),过了就合成一次触发,内容取最新的那条,
fire_at 取窗口结束的时刻(同一窗口永远同一个时刻,触发记录唯一约束兜住重复派发);照 fire_once 发起正式运行,ScheduleFire
kind=event、inputs 写 {event: 事件说明, inputs: 渲染出的输入};上一次没结束记 skipped_overlap;派发太晚(超出补发宽限)
记 skipped_misfire;任务停用、改了触发条件时等着的标 dropped。AGENTLAB_SCHEDULER 关着时没有这一拍,事件留着,接口照实说。
- scheduler/triggers.py:三种事件的触发条件(table.version 可选只认新增的一期;workflow.finished 可选只认某个应用 / 某种来源;
source.changed)、核对对象(表格是按期累积的上传表格、上游工作流已发布、应用在)、说法、{{ event.* }} 变量(table、version、
period、activated_at;workflow_id、run_id、outputs.<字段>;source、counts)、示例事件(预览、立即运行用对象最近一次真实的事件)、
防环:保存时查 workflow.finished 串成的环(停用的也算,A→B→A、自己触发自己都拒,422 写出经过的工作流),运行时按 origin 里的
触发深度兜底(超过 5 层记未能发起)。
- service:fire_once / _fire_workflow 接事件(模板按事件种类校验、渲染 {{ event.* }},运行来源 origin.trigger 带种类、对象、
事件 id、深度、经过的运行和一句说法);check_template / render_inputs / variables 加可选的 event 参数,cron 任务一字不变。
- api/schedules.py:收 trigger_kind(cron 缺省)/ trigger,event 时目标只能是工作流、cron 存空串、下一次触发为空;返回带
trigger_kind、trigger、trigger_text(也是 description_text)、trigger_state(几条事件在等、去抖到何时、调度开没开);触发
历史带 event;预览按示例事件渲染;变量清单按事件种类给;新增 GET /api/schedules/trigger-options(各事件能选的对象及不能选的
原因、来源、应用,界面不自己判);立即运行按示例事件发起;触发方式建好之后不能改。Webhook 照旧可配,通知写「事件触发」那一行。
- 应用页印章的来源(apps/resolve._from_text)照实写「由事件触发:…」。
为什么:总方案 §4 已拍板接这三种事件、不接口径发布,一套定时机制(X4);派发放在调度循环里而不是发生处,事务和重复都说得清。
测试:test_schedule_events.py(三种事件各触发一次、去抖合并与窗口外另起、重叠跳过、停用 / 改条件丢弃、调度关着时事件留着、
超出宽限、触发链深度与上限、模板校验与渲染、期次写法、通知、印章)、test_schedule_events_api.py(建、核对、防环、改、预览、
变量、立即运行、触发状态、可选对象);test_schedules_api.py 的约定键集合补上新加的几项。
|
2026-10-18 15:53:08 +08:00 |
|
ThearchyHelios
|
3f3e39e4d5
|
编排完善第 1 波骨架:定时任务加触发方式(trigger_kind:cron | event,trigger 写事件种类和对象)两列与迁移,存量行都是 cron(docs/design/orchestration-round/00-总方案.md §4)
|
2026-10-18 15:25:13 +08:00 |
|
ThearchyHelios
|
153e48a568
|
合入真实模型验收后的修正:口径标签逐个数判(报告引用了一条查询的几列时,每个数按它那一列投影后和口径的指标列比,判定仍只走 match_template / explain_sql_diff,投影只在去掉别的列不改变这个数时做;整题等价照拍板 L1 不放宽),差异说明在投影之后说得清(多查了几列、COALESCE、ROUND、按当天现算的日期、取不到参数、LEFT JOIN ON 里的日期条件);转抄口径卡指标的出处跨卡追到取数查询(对账、召回不再降档,发布前的静态检查同一套说法);认证队列按规范形分组;问题聚类的类 id 由成员定;use_caliber 的协议写清 card;评测「期望没有数据」只认数值格和叙述里的数、逐题比 SQL 比产出答案的那一格并按引用的列投影、判错时给配对上的那一格、估不出金额时不写 0(评分规则第 2 版);被取消、超时的模型调用也计一次;助手只给思考不给操作时问数据和评测都自动重来一次;零售样例题集的期望 SQL 去掉取整;目录两种计数照实说;自由形应用入口与岗位显示名。验收:后端全量 14435 通过;tsc、文案检查;相关浏览器检查(decode、stream、chat、evidence、ledger、evals、publish、inbox、verify、compare 等)全过;泄露扫描干净;在隔离服务上复验:自然问法里和口径写法一致的那一格标等价已证明,COALESCE、现算日期的照实标相关并说清差在哪
|
2026-10-17 13:36:08 +08:00 |
|
ThearchyHelios
|
3d9dc7239b
|
README:口径账本逐个数判与取数 SQL 别取整的建议;第 4 波升级说明补上最终验收后的修正(比对算法第 4 版、评分规则第 2 版可重评、只思考不出操作自动重来、未完成的调用也计次)
|
2026-10-17 13:35:39 +08:00 |
|
ThearchyHelios
|
9627953445
|
差异说明认得一边套了 ROUND:说「X 套了 ROUND(…, 2):保留 2 位小数,另一边没有取整」,只陈述写法、不断言值不同,判等价照旧不等价;零售样例题集的期望 SQL 去掉 ROUND(…, 2)(取整交给展示,评分本来按容差比数),评测逐题比 SQL 不再因为期望里的取整一律对不上
|
2026-10-17 13:31:25 +08:00 |
|
ThearchyHelios
|
eb42c12f05
|
合入 nr6/c:口径标签逐格判(引用了好几列的查询,每个数按它自己那一格投影后和口径的指标列比,判定仍只走 match_template;整题等价照拍板 L1 不放宽);差异说明在投影之后也说得清(COALESCE 套聚合、按当天现算的日期、取不到参数);评测逐题比 SQL 取和期望配上的那一格
|
2026-10-17 13:18:02 +08:00 |
|
ThearchyHelios
|
39b89fd36b
|
差异说明在投影之后也说得清:COALESCE 套聚合、按当天现算的日期、取不到参数
逐格投影之后两边只剩一列,差别常常就在这一列的写法上,以前照样整段兜底。只改说明,判定一个不变(这几种本来就不等价):
- _Explainer._coalesced:同一位置上一边是 COALESCE(聚合, 常量)(IFNULL 也读成它)、另一边就是那个聚合,说「这次的销售额
套了 COALESCE(…, 0):没有符合条件的行、或者 amount 都为空时这次是 0、口径是空」;参数那一列表结构里一定不为空时只说
「没有符合条件的行时」;分组时说「某一组的 amount 都为空时」,参数一定不为空时两边一样、不说。只认会得出空值的
SUM / AVG / MIN / MAX,COUNT 套 COALESCE 两边其实一样,不说成不同。列名从原文取(规范形式去掉了外层别名)。
- _Explainer._relative:同一列上一边按运行当天现算(CURRENT_DATE、now()、SQLite 的 date('now', …),_now_based)、另一边写定
的常量,说「这次对 paid_date 的条件是 …,按运行当天现算(换一天运行,范围随之改变);口径写定的是 …」,要求同 _rewrites。
- match_template:口径有入口字段取不到值、又没有缺省值时,以前拿空值渲染出「paid_date >= ''」去比,说明里就出现不存在的
写法;改成一律说取不到参数,这次在那里写的是现算日期的逐个说清(_now_slots 按列和比较的方向认,只是说明)。解析不了的
照旧只说解析不了。评测逐题比 SQL 用的是同一个函数,跟着说得清。
测试:tests/test_ledger_cells.py 新增三条(COALESCE 那一格仍然不等价、说明先说多查了几列再说 COALESCE;在虚构库上没有行时
两边确实不同,COUNT 套 COALESCE 不说成不同;按当天现算的日期逐格比时说清取不到参数、和写定日期的 SQL 直接比时说清一边现算
一边写定)。去掉这三处再跑,三条失败;判等价的语料和执行守门照旧全过。
|
2026-10-17 13:09:55 +08:00 |
|
ThearchyHelios
|
271bc6e782
|
口径标签逐格判:引用了好几列的查询,每个数按它自己那一格标
真实模型下问「上周的销售额是多少」,助手写 SUM(amount) AS 销售额, COUNT(*) AS 订单数, MIN / MAX(paid_date) AS 区间首日 /
末日,报告四列都引用了,口径「周销售」只取 SUM(amount)。按「全部引用列」投影后还是 4 列,对不上口径的 1 列,整条比、
四个数都标相关口径,说明整段兜底。
- ledger/match._cells / _cell(algo 4):整条(含按全部引用列投影)没证明、又引用了这条查询两列以上的,被引用的每一列单独
投影到那一列,和每份口径的每个指标读的那一列(card_reads;口径版本没有口径卡的按取数的每一列)逐个比,仍只走
match_template / explain_sql_diff,上次的投影规则照用:投影不了(分组列、DISTINCT……)、过了时限、不可比的格照用整条
查询的那一个候选。结果记在 queries[].cells {列名: {status, candidates}},另记 one_row(sql_semantics.one_row:不分组的
聚合,一定只出一行)。整条这一层的结论、候选一字不改;老工件没有 cells,照旧按整条读。
- ledger/labels:单元格按它那一格的结论标(_view);指标用了同一条查询的好几格时,只有那条查询一定只出一行才把逐格的
结论合起来用(多行的结果里每一列各自对得上,不等于每一行的几列一起对得上)。整题照拍板 L1 不变:每个数都证明来自
同一份口径才等价;一格证明了、别的格只是相关的整题不等价,原因写「有的数和相关口径不一致,不来自口径」(以前一律说
「这次的推理」)。出具入口照 ledger/issue 现有判法只在整题等价时给,没有放宽。
- 逐格证明了的口径各处都算上(labels.candidates_of):工件 meta 的 calibers(召回按旧版本反查)、ledger/uses 的使用记录、
召回时找原答案对应的那一格、E4;E1 另数各种结论各几格(cells),E2 的兜底比例按标签上给人看的那一份说明数。
- GET /runs/{id}/ledger 的 queries 带上逐格的 cells(遮罩同候选);证据片段的 ledger 键、轮次摘要的计数本来就按每个数的
标签,数据变了、形状不变。前端只补类型;check-ledger 加一段:同一条查询一格来自口径(正文挂小标记)、一格相关(差在哪
先说多查了几列),整题不等价不给出具。
- 评测逐题比 SQL(eval/score.sql_match)取和期望配上的那一格(score.matched_cells:评分时配上的格),没给时取逐格证明了
的那一格;结果带 column。runner、rescore 传它。
测试:tests/test_ledger_cells.py(同一条查询一列等价、一列不等价各自标对;两份口径只证明了一份标那一份;口径多个指标按列
配对;指标跨两格只在一行的查询上用逐格结论;分组列、DISTINCT 逐格也不投影;照真实例子造的虚构库真跑一遍:销售额标等价
已证明,订单数、首末日标相关,整题不等价、不给出具、不自动出具,证据片段的 ledger 键一致;评测取配上的那一格)。去掉逐格
再跑,10 条失败;去掉「一行才合起来用」、去掉评测取格,各 1 条失败。
|
2026-10-17 13:07:19 +08:00 |
|
ThearchyHelios
|
f520a88891
|
静态地认指标出处也跨卡往下追:转抄同一张图里另一张口径卡的指标,按那个指标的式子接着认(最多 MAX_COPY_DEPTH 层),受管级发布检查、应用检查和运行时、证据面板同一套说法;被抄的卡按 caliber_from 固定在别处版本的照旧停在那张卡
|
2026-10-17 12:31:13 +08:00 |
|
ThearchyHelios
|
124e1c0f49
|
评测逐题比 SQL 和账本同一个口径:传报告引用了哪几列(ledger/match.referenced_of),整条比不等价时按引用的列投影再比;「多查了几列」那一条不算差在哪个方面
|
2026-10-17 12:29:20 +08:00 |
|
ThearchyHelios
|
f8d89e780c
|
合入 nr6/b:评测「期望没有数据」只认数值格和叙述里的数、逐题 E1 比产出答案的那条查询、判错时给配对上的那一格、估不出金额时不写 0、没做完的模型调用也计一次、助手只给思考不给操作时自动重来一次(问数据与评测同一个决定)、目录两种计数照实说、自由形应用入口与岗位显示名;评分规则升到第 2 版
|
2026-10-17 12:28:04 +08:00 |
|
ThearchyHelios
|
94f028fcef
|
合入 nr6/a:认口径按答案引用的那几列投影后再判等价(判定仍只走现有的 match_template)、差异说明投影之后再说并认得 LEFT JOIN ON 里的日期条件、转抄口径卡指标的出处跨卡追到取数查询(对账、召回不再降档)、认证队列按规范形分组、问题聚类的类 id 由成员定、use_caliber 的协议写清 card
|
2026-10-17 12:28:04 +08:00 |
|