ThearchyHelios
|
2714890d0d
|
文档重新梳理:README 精简成介绍和索引,细节按主题拆进 docs/zh-CN 与 docs/en(17 篇一一对应),英文文档不含中文
README.zh-CN.md / README.md 只留:这是什么、为什么用它、主要功能(按领域一行一条,链到文档)、快速开始、部署、文档索引、
开发与测试、许可(原文不动)。原来的每一节按读者和任务分到 17 篇主题文档:ask-data、workflows、evidence、
data-sources(新增:接入数据库、只读保证、上传表格、跨库合并、遮罩列)、data-catalog、tools(新增:工具、审批档位、
能力登记、端点、网络规则——它们管所有工作流,不只应用)、apps、evaluation-and-ledger、schedules(含通知)、deployment、
configuration、postgresql-and-scaling、identity-and-security、architecture、development、upgrade-notes(按时间倒序,
每波先写要做的事)、faq。删掉的只有模块名(api/copilot_gen.py、chat/drive.py、data/schema_view.py)、重复的话和
「以前…现在…」的叙述;操作步骤、命令、配置名、限制、权限边界都有归宿。中文是原文,英文按新的中文写;英文里界面文字
只写英文名,开头注明界面目前只有简体中文,中文占位符换成英文占位符,中文数字的例子改成不依赖中文的说法。
数据剖析可以同时剖析 2–3 张表(baf23998)收进 data-catalog。
新增 scripts/check-docs.py:英文文档(README.md、docs/en/**)不许有中日韩字符和全角符号(代码块也算),
README、docs/**、deploy/README.md 的相对链接和锚点都要通,docs/zh-CN 与 docs/en 文件一一对应、标题层级相同。
中文提示里指向 README 章节的地方改指新文档:cluster/guard.py、cluster/version.py、db/base.py 两处报错,
data/schema_view.py 文档串,.env.example 三处,scripts/prod.sh、scripts/test-postgres.sh,terms.ts 两处、
DataSourcesTab.tsx、check-manage.mjs 注释,deploy/README.md 三处链接。deploy/agentlab.env 未动。
验收:check-docs 0 问题(英文文档 18 个 0 处中文,相对链接 369 条 0 断链,18 对标题层级一致);相关后端测试
117 通过、7 跳过;tsc -b、check-copy 通过。
|
2026-10-21 10:23:26 +08:00 |
|
ThearchyHelios
|
aadc8d0115
|
中文 README 移到 README.zh-CN.md(内容不改,顶上加语言切换),中文提示里指向 README 章节的地方改指中文版
git mv 保留历史;顶上一行「[English](README.md) | 简体中文」,英文版随后单独提交。
改指 README.zh-CN.md 的:报错文字(cluster/guard.py、cluster/version.py、db/base.py 两处)、.env.example 三处注释、
scripts/prod.sh 用法说明、scripts/test-postgres.sh 注释、「口径同 README「哪些角色看得到原值」」的四处代码注释
(terms.ts 两处、DataSourcesTab.tsx、check-manage.mjs),deploy/README.md 里三处「仓库 README」写成指向
../README.zh-CN.md 对应章节的链接。
没改的:deploy/deploy.sh 四处「README 的常见问题」「README「用 PostgreSQL」」和 deploy/agentlab.env 117 行——离线包里和
deploy.sh 放在一起的 README.md 就是 deploy/README.md,那几句指的正是它的常见问题、用 PostgreSQL 两节;
deploy/README.md 第 16 行文件清单说的是它自己;version.py、runner.py 文档串里「以前 README 写…」是旧事的叙述。
验收:相关后端测试 117 通过、7 跳过;tsc -b、check-copy 通过。
|
2026-10-21 08:13:58 +08:00 |
|
ThearchyHelios
|
27237623c5
|
合入 nr8/b 的 45513c90(复核 R-b7–R-b15 和 R-b12 的尾巴:租约按库的时钟、隔离用计入休眠的钟、PostgreSQL 单进程墙钟跳变不再隔离自己、Fenced 只对绑定世代的执行抛、隔离后等不到重新登记的运行照实收尾、续租任务不退出、换身份后收拾旧身份、quota.take 交回日期、check_pgbouncer 同一主机端口(本机回环规范化)也拒绝、copy-to-postgres 抽出 checkpoint_conninfo 加 --direct-url、版本指纹算进 requirements.lock、run_events 重号记进设置);没有冲突,README、db/base.py、db/to_postgres.py 自动合并:to_postgres.main 里 nr8/b 的 --direct-url 和这边连不上时一句话报错的分支都在
|
2026-10-19 15:14:25 +08:00 |
|
ThearchyHelios
|
45513c90b7
|
R-b12 的尾巴:check_pgbouncer 判同一个主机端口时把主机名规范化——localhost、127.x.x.x、::1 都算本机回环,IP 按地址比(::ffff:10.0.0.5 和 10.0.0.5 相同),解析不成 IP 的按字面(小写)比、不查 DNS。补测试:localhost 对 127.0.0.1、127.0.0.1 对 ::1、IPv4 映射地址同端口都拒绝,本机另一个端口、名字不同的放行
|
2026-10-19 15:09:10 +08:00 |
|
ThearchyHelios
|
68c1f4143e
|
复核 R-b13(版本拦截与零碎):preflight 报错列出全部活着的、版本不同的进程,各标明心跳在走还是停着、等租约过期(以前只点名先看到心跳的那个,人停掉它再起还会被另一个拦下);源码指纹把依赖锁文件 requirements.lock 的内容也算进去,只升了依赖也算版本不同,锁文件取不到时换成几个关键包的版本,Docker 镜像里留一份锁文件;存量库 run_events 撞了号时把结果记一次(settings 表 cluster:run_events_seq_duplicates,记下撞号的那几处,最多 200 处),之后启动不再整表 GROUP BY、只按索引核对那几处还撞不撞,都处理掉了才再数一遍、建唯一索引、删掉记录。隔离后换身份那几条日志写出新旧完整身份已在 R-b9 那一笔里改了。README「升级」一段跟着补一句。补测试:一个心跳在走、一个心跳停着,报错两个都列出、各标状态;锁文件变了指纹跟着变、取不到时用包版本;撞号第一次整表数、第二次只核对不扫、处理掉之后建上索引并删掉记录
|
2026-10-19 14:45:54 +08:00 |
|
ThearchyHelios
|
d55ae68e7d
|
复核 R-b12(直连地址其实还是 PgBouncer、搬断点走了事务模式):check_pgbouncer 除了没给直连地址,直连地址和应用库地址连的是同一个主机端口(规范化之后比,端口不写按 5432)也拒绝启动,报错说清直连要绕过 PgBouncer;copy-to-postgres 搬断点抽成 checkpoint_conninfo:给了断点库地址用它,否则进目标库、给了直连地址就走直连,命令行加 --direct-url,不给而目标就是应用库时用 AGENTLAB_DATABASE_DIRECT_URL。补测试:同一个主机端口(大小写、用户、参数不同)拒绝、同主机另一个端口放行;搬断点缺省走直连、单给的断点库地址优先、目标是别的库不套用应用库的直连地址
|
2026-10-19 14:42:33 +08:00 |
|
ThearchyHelios
|
5318c33f57
|
合入 nr8/b 的 ac8b194c(复核 R-b1–R-b6、交互留底的运行名额、先占额度再出具、直连地址 AGENTLAB_DATABASE_DIRECT_URL 和 AGENTLAB_DB_PGBOUNCER、不许新旧版本混跑、prod.sh 和 Docker 入口问 python -m app.cluster workers、镜像带构建号);没有冲突,README、config.py、db/base.py 自动合并
|
2026-10-19 14:01:07 +08:00 |
|
ThearchyHelios
|
93cd32bc97
|
启动时等库:PostgreSQL 还连不上(和库的容器一起起、库还在初始化)时按退避重试(1、2、4、5、5… 秒),每次照实打一条「应用库还没就绪」,最多等 AGENTLAB_DB_STARTUP_TIMEOUT(缺省 90)秒再把最后一次的错抛出去;密码不对、库不存在这种等也没用的(SQLSTATE 28、3D、42 类)立刻抛;SQLite 不等。新增 python -m app.ops(部署脚本在容器里以镜像用户调的几件小事,只用标准库):内置 PostgreSQL 的密码文件和 pgpass(0600,已有就沿用,密码不打印)、数据目录用哪种库的状态(sqlite / builtin / migrating)、SQLite 库的一致备份(备份接口,含 -wal 里的)和只留最近 N 份。copy-to-postgres 连不上目标库、库报错时一句话说清楚、退出码 1,不再甩一屏调用栈
|
2026-10-19 13:43:32 +08:00 |
|
ThearchyHelios
|
35ab097b45
|
复核 R-b1(卡住但没死的进程被当成没了):续租约挪到单独的任务、专用连接(不走连接池,不被同一拍别的活拖住);进程间请求另起任务办,不在心跳里等收尾;新增 cluster/fence 自我隔离——离上次续上租约超过一个租约时长,或者这段执行属于上一次隔离之前,执行在写事件、写断点、开审批、发外部写入、后台任务写库之前就停手(Fenced 是 BaseException,节点里的 except Exception 拦不住),续租时发现续晚了就隔离:停掉手上的运行和后台任务、不写终态,换一个身份重新登记;运行收尾先占住那一行、属主不是自己就不写;别的进程按租约过期再多等一个租约时长才算不在了(两边中间留一段);run_events 的 (run_id, seq) 改成唯一索引(存量库先数重复,有就照实记日志、这一次不建,不让启动失败)。补测试:卡住期间被接手之后不盖终态、不写事件、序号不重;被接手的那次在别处接着跑完只结束一次、后面那一步只跑一次;收尾按属主写;撞号被库拒绝;多进程端到端加 6b、6c(真 SIGSTOP / SIGCONT)
|
2026-10-19 13:15:56 +08:00 |
|
ThearchyHelios
|
75ab3678cb
|
代码里拦住新旧版本混跑(用户拍板):新增 cluster/version——代码版本(包版本 + 构建号 AGENTLAB_BUILD 或 git 提交 + app/ 下源码指纹,比较按源码指纹)、库结构版本(各表各列加迁移清单的哈希),进程登记时一并记下(cluster_workers 两列,add-only 迁移)。PostgreSQL 时补列之前先看一眼(preflight):库里还有活着的、版本不同的进程,心跳在走就拒绝启动、照实写明是哪几个进程什么版本、要先停掉再升级,心跳停着的(强杀留下的)等租约过期再起;登记那一下在事务级咨询锁里再查一遍,两个不同版本同时启动只有先登记上的起得来。Docker 镜像带上构建号;README 写明升级步骤。补测试:版本不同的活进程被点名拒绝、同版本照常加入、过期的不算、只比源码指纹;PostgreSQL 上心跳在走的拒绝、心跳停着的等过期、登记时再拦一次
|
2026-10-19 13:04:07 +08:00 |
|
ThearchyHelios
|
d68ca9774f
|
加直连地址 AGENTLAB_DATABASE_DIRECT_URL 和 PgBouncer 事务模式开关 AGENTLAB_DB_PGBOUNCER(用户拍板):依赖会话状态的连接——收发进程间通知的那一条(LISTEN)、跨进程的锁(会话级咨询锁,另建一个直连的小池子)、断点库(缺省)——走直连地址,普通查询走 AGENTLAB_DATABASE_URL,不设就全用同一个;开关打开时普通地址不用客户端连接池、关掉 SQLAlchemy 和 asyncpg 的预备语句缓存、预备语句用随机名字(照 SQLAlchemy 文档),事务级咨询锁照常成立;开关打开却没给直连地址时拒绝启动。README 写三种部署(直连、PgBouncer 会话模式、事务模式加直连地址)和要配的 server_reset_query,照实说没在真的 PgBouncer 上跑过。补测试:地址解析和规范化、断点库缺省走直连、事务模式关掉的几样、LISTEN 和跨进程锁走直连、事务模式没给直连地址拒绝启动
|
2026-10-19 13:01:21 +08:00 |
|
ThearchyHelios
|
4e3623885b
|
合入 W2-a 的复核修复(nr8/a:导入的 NOT NULL 补值与孤儿行、收尾 dispose、sslmode 换算、PostgreSQL 写入去 NUL、不分大小写搜索 contains_ci、json_eq 两库同语义、JSON 守卫、用户可见列表补决胜键、bubblewrap --clearenv、部署脚本与原子写的几处、README 连接数预算)
# Conflicts:
# backend/app/api/schedules.py
# backend/app/main.py
|
2026-10-19 12:21:38 +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
|
87f0d62f9b
|
多进程协调(W2-b)第一步:新增 app/cluster——工作进程在库里登记、心跳续租约(属主随进程,进程没了它认领的行一起到期),调度当班的租约,发给属主进程的停止 / 记事件请求,PostgreSQL 的 LISTEN/NOTIFY 叫醒,跨进程的互斥锁(咨询锁),SQLite 单进程守卫(第二个进程拒绝启动);运行、问数据一轮、起草 / 剖析 / 探查 / 评测 / 影子起草、文档、发布单记 owner,启动整理改成只接手属主不在了的行,当班进程隔一会儿再接手一次;运行的并发名额改成库里按交互 / 应用 / 定时 / 系统四类发(各有全站上限和优先级,设置里可调),替掉进程内的信号量;同源队记在库里;运行停止、记事件跨进程交给属主;运行事件、问数据事件跨进程转发(库里按 seq 追,不落库的流式增量和保活心跳只转给在看的进程);每日花费账先占住那一行再读写;访问密钥限流、登录失败计数在 PostgreSQL 时记在库里;登录设置、对外地址、向量模型、能力快照、数据源连接池、应用页面缓存跨进程失效;上传版本库、记录表、按需探查、出具、召回、补算、按需视图、个人刷新、定时发起的锁换成跨进程的锁
|
2026-10-19 11:40:33 +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
|
3f3e39e4d5
|
编排完善第 1 波骨架:定时任务加触发方式(trigger_kind:cron | event,trigger 写事件种类和对象)两列与迁移,存量行都是 cron(docs/design/orchestration-round/00-总方案.md §4)
|
2026-10-18 15:25:13 +08:00 |
|
ThearchyHelios
|
91cc150f17
|
下一轮第 3 波的契约骨架:工作流版本加表的结构版本;定时的非工作流目标按种类交给各自的模块(评测题集 app/eval/scheduled、数据源维护 app/data/source_maintenance,先放照实记「还不能发起」的占位)、record_fire 共用;分享链接、数据源变化记录、口径账本按答案出具与对账的路由先登记好权限
|
2026-10-16 15:51:33 +08:00 |
|
ThearchyHelios
|
898e95a3ef
|
下一轮第 2 波的契约骨架:用户加 kind(服务账号)、剖析任务加上传表格的快照 id、工件按种类的索引;新模型文件 models_sources、models_eval(整模块导入);评测、访问密钥的路由模块和口径账本按运行读的路由先登记好权限(评测只给搭建者,访问密钥登录即可,账本按运行读的和证据同一道门)
|
2026-10-16 13:07:40 +08:00 |
|
ThearchyHelios
|
4819b21269
|
下一轮第 1 波的契约骨架:运行加 end_reason(人工驳回 / 手动停止),定时任务加目标种类和 id、触发记录加评测 id(定时只有一套),运行来源登记 eval、ledger_issue、reconcile、recall(都只能由服务端写);迁移只加不改
|
2026-10-16 09:43:08 +08:00 |
|
ThearchyHelios
|
7088f4557e
|
普通用户建应用的契约骨架(docs/design/normal-user-apps.md 第五部分):蓝图、蓝图留痕、发布单、文件、记录表的模型和迁移,应用的 scope / owner / blueprint_id;运行来源加 trial、personal_app;应用描述的固定版本可以没有版本号(试运行派生的草稿)或改钉蓝图(个人应用),视图来源加 exploratory,组件加 hidden,表单加文件控件;agentlab.blueprint/1 与 bp_* 操作、agentlab.template/1 与关节类型的定义;实例化、试运行、发布单、个人应用、记录表、文件、白话层的接口签名;蓝图、模板、文件、个人应用、蓝图助手的路由桩(501);前端约定 typesBlueprint.ts、接口桩、新建应用与蓝图页的占位路由、termsBlueprint.ts
|
2026-10-15 09:49:45 +08:00 |
|
ThearchyHelios
|
bff3062a97
|
集成收尾:前端约定写入后端实际返回的形状(应用详情只对 manage 给草稿、补算计划与结果、按名字的钩子行、能力字段审阅),去掉多种字段名都认的兼容解析;钩子按(应用, 名字)、应用助手对话按(应用, 建立人)加唯一索引,迁移前去重,插入改在保存点里、撞上就改用已有的那一行;口径卡固定的版本也须已发布且未退役,记级别之前有正式运行凭据的老版本启动时补记为已发布;证据面板和应用视图按列名遮端点快照(同一个函数),不再整张表遮掉;待办计数带上最新一条的时间,新待办通知不再漏;非管理员的搭建者不出模型接入的改动按钮
|
2026-10-14 22:08:33 +08:00 |
|
ThearchyHelios
|
04ffce252a
|
集成:冻结文件按各包请求一次补齐——对话表加建立人(开登录后按人隔离对话)及迁移和索引,钩子回执、投递记录的状态注释;前端约定补待办的节点名与运行类别、生效记录的幂等键等字段、发布结果的级别与哈希、视图来源的种类、各组件数据形状的说明、钩子、助手的澄清问题与应用润色的请求和结束事件
|
2026-10-14 20:06:27 +08:00 |
|
ThearchyHelios
|
220fd0e365
|
应用平台契约骨架:新表(身份、能力与端点、应用、待办与生效记录、通知)与迁移(补列、多列索引),运行来源与新事件类型,「人」的唯一入口 api/deps.py(关登录时行为不变),应用描述与表单的形状、受信组件目录 v1,各包跨包函数的签名桩,新路由登记,发布门禁的扩展钩子 EXTRA_LINTS;前端接口约定 typesApp.ts、各接口文件、SchemaForm / TaskCard 的 props 与最简实现、应用相关路由与占位页
|
2026-10-14 17:53:40 +08:00 |
|
ThearchyHelios
|
d3bd80211a
|
数据目录闭环第一步(后端 A)复核修正:没收下的有效记录条件不算失败,在起草任务逐表的 log 里记一句,接口看得到(以前只进服务端日志)——没通过校验的写原因(「有效记录条件里不能有子查询,模型提议的这一条没有收下:……」),剖析核对排除 0% 或 100% 的写数到了多少行;只在请模型写表级的项的那段里记,一张表最多 10 句。CatalogDraftTask 加 log 列(存量任务回填 NULL,接口给空列表)
|
2026-10-14 10:00:10 +08:00 |
|
ThearchyHelios
|
7c3484adba
|
数据目录闭环第一步(后端 A):起草遗留——宽表逐段落盘、按比例止损、块进度、停止后的说法、同步起草与任务互斥
- 宽表逐段落盘:每写出一段(表级那段、每个列分段)就把这张表到这时拿到的内容并入目录(catalog_jobs.write_part,这一次合并不删任何东西);整张表写完(ok、partial 都一样)再按完整覆盖的规则收尾一次,只删已经不在表结构里的列的模型项(write_draft 对 partial 也照删)。停止、服务重启时已经写进去的段留着;写过段的表照旧记 cancelled,model_error 写「已停止:已写进目录 N 段,剩下 M 段没起草」(中断写「已中断:…」)。新增、更新、删除的计数是各次写入的合计
- CatalogDraftTask 加 parts_written(已写进目录的段数)、chunks_total / chunks_done(这张表实际分成的块数和已结束的块数,段被缩小的上限分开、拆成两半后会变多;不用模型时为 null),升级时补列
- 止损按比例:这次起草最近 10 次调用有 7 次以上没写出来时,之后的段不再拆小、每块只问一次(不补问漏掉的列、截断后不再补剩下的、空壳不再原样再问,拆出来还没问的块也不再问),直到比例降下去;去掉会被偶尔一次成功清零的「连续 2 段」「接连超时」两条。结构化输出只给空壳的,这次起草还没换纯文本问过的照旧问一次;额度被拒、结构化输出不支持、网络错误这些通信上的重来照旧
- 同一张表接连 3 个原始段都一项没写出来:剩下没开始的段跳过不问,原因写「这张表接连 3 段都没写出来,剩下的 N 段已跳过,可以换一个起草模型后只重试这张表」
- 原因里「已拆小重试仍没写出」只跟在真拆过的那几段后面说:说到的段只有一部分拆过时写「其中第 3 段已拆小重试仍没写出」
- 目录清单的 last_draft 加 blank_columns_from(最近一次结束于 ok / partial 的用模型起草记下的留空列数;之后有起草写进了新内容时为 null)和 parts_written
- 同步起草和后台任务双向互斥:同步起草进行期间同一个源建任务、重试、导入(ensure_idle)、再开一次同步起草都 409 catalog_draft_busy(进程内登记)
|
2026-10-14 08:31:35 +08:00 |
|
ThearchyHelios
|
975dde1146
|
起草引擎按验收修正:列分段均分、超时拆小、段大小随模型减半、每段 8 次封顶、提醒、超时与没写出来的计数、模型留空的列、不插队、原因写清哪几段哪几列
- 均分:超过 40 列的表,列分段数是列数除以 40 向上取整,各段列数平均(相差不超过 1),不再留下 2 到 8 列的尾段(实测
会思考的模型常对这种小段只给空壳);表级那段、窄表拼段、parts_total 不变。
- 拆不开的段(不到 10 列)只给空壳:再问一次,结构化输出换纯文本,已经走纯文本的原样再问,仍是空壳才记没写出来。
- 超时当作这一段太大:不再原样重试同样大小的段,按额度用完的办法拆小再问;网络错误、429、5xx 仍原样退避重试。
- 段大小随模型调整:一次起草里额度用完或超时累计 2 次,之后新开始的段列数上限减半(40 → 20 → 10,最低 10),新开始
的段超过上限就先按上限均分开(多表的段按表重新拼);任务记当前的上限 part_columns。
- 封顶:一段(连同它拆出来的、补问的、重试的)最多调用 8 次,到了次数还没写完的记没写出来、原因写清。止损照旧。
- 提醒:最近 6 次调用有 4 次以上没写出来(额度用完、超时、空壳、出错),任务的 notice 写一句提醒(次数、原因,建议在
「设置 → 偏好设置」的「起草模型」换一个模型、减少表或停止);之后最近 6 次里少于 2 次就清掉。
- 用量:进度回调 usage 换成 call_done(CallReport:用量、没写出来的原因种类、当前的提醒和列数上限),任务的用量加
timeouts、failed_calls;超时的调用 token 照实记 0。
- 留空的列:请模型写了、模型也列出了,中文名和含义却都没给(或者给的都没通过校验)的列记在 ModelOutcome.blank,逐表
记 blank_columns 和最多 5 个列名 blank_sample,任务上是合计,目录清单的 last_draft 也带列数;表的状态不因此变。已确认
的、库注释已经说清的列不算。补问时把漏的列列出来、字段却留空的,算留空,不再记成「漏了这些列」。
- 公平排队:宽表的表级那段写完,它的列分段排到已经排着的段后面(先来先服务),不再插到队首让窄表一直等。
- 原因:几段因为同一个原因没写出来时说「这几段」,同一段拆出来的几块仍是「这一段」;只有真拆小过才补「已拆小重试仍
没写出」(截断后只补剩下的不算);每段写上列的范围,例如「第 5 段(列名 到 列名,共 33 列)」,窄表写出是哪几列。
- 库:catalog_draft_jobs 加 timeouts、failed_calls、part_columns、notice,catalog_draft_tasks 加 blank_columns、
blank_sample,旧库启动时补列(存量回填 0 和空)。假模型加 hang(头几次调用一直不回,用来模拟超时)。
|
2026-10-13 20:30:02 +08:00 |
|
ThearchyHelios
|
0eaaf71346
|
运行记下选项快照和来源:runs 加 options(JSON,默认 {})和 schedule_id(可空,建索引)两列。后面的核查设置要在发起时定下、整次运行沿用,审批停下期间改了设置,恢复和重放仍按发起时的(同 approval_default 的道理);定时任务要能找回自己发起的运行。RunManager.start 收 options、schedule_id 并落库,选项先深拷一份,调用方之后改自己的 dict 不影响快照;选项非空时写进每一段的 run.started(进封存范围),为空时 run.started 和以前逐字相同;恢复、继续运行照运行记录沿用,节点从 RunContext.extra 的 run_options 读同一份拷贝。RunOut 带 schedule_id,GET /api/runs 可按 schedule_id 筛,launch_run 原样往下交。旧库走 _COLUMN_MIGRATIONS 补列,存量运行回填 {} 和 NULL;create_all 不碰已有的表,新列的索引另由 _INDEX_MIGRATIONS 补建,索引名和新库 create_all 建出来的相同
|
2026-10-05 00:07:07 +08:00 |
|
ThearchyHelios
|
bfa391e344
|
数据目录阶段 4A 发布与运行:发布时记下模板 SQL 用到的表的目录版本,正式运行时目录有变化就发提醒事件、不拦运行,运行详情和时间线显示提醒,版本历史显示发布时的目录版本及之后的变化
|
2026-10-03 12:11:14 +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
|
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
|
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
|
5928cc052e
|
三道只配不管的护栏开始干活:运行时限、模型超时、清单核对
显示成生效却没生效,比明说没有更危险——它让人以为有东西在兜底。
1. max_run_seconds 只存在于配置和设置接口里,没有任何代码执行它:一个挂住的
模型或工具能把运行和它占着的并发名额一直拖着。现在 runner 用 asyncio.timeout
强制执行,按"一段执行"计(等人工审批时 _drive 已结束,不算);超时判 failed、
说清是哪道上限,可以接着跑。别处抛的 TimeoutError 不冒充这道上限(看
Timeout.expired())。
2. 模型调用从来没设过超时:ModelSpec.timeout 从没被赋值,那条传参分支一次都没
执行过,所有调用走 SDK 默认的 10 分钟外加重试。新增 model_timeout_seconds
(默认 300 秒)作为缺省,节点显式配的仍优先;已确认 ChatAnthropic / ChatOpenAI
都接这个参数。
3. manifest 哈希只写不验,而且是在 run.finished 落库之前算的——清单里恰恰少了
宣布"跑完了、成果是什么"的那条,改成果清单照样对得上。现在终态事件落库之后
再封存,并记下封到哪一条(manifest_seq,新列);新增 verify_manifest 与
GET /api/runs/{id}/verify,运行详情页加了核对按钮。封存之后追加的事件不算篡改。
老运行没有 manifest_seq,按当时的口径还原范围——开发库里 300 条带哈希的历史
运行全部核对通过(成功 / 失败 / 取消各类都有),没有一条误报。
设置页「运行环境」补上单次执行时限和模型调用超时两项,README 加了对应的常见问题。
顺带修了一个被 _seal 放大的老问题:_drive 的收尾被打断时(关停取消、库连接出错)
漏掉 _tasks.pop,留下的任务让下一次 shutdown 的 gather 在别的事件循环里抛
"future belongs to a different loop"——测试里表现为每八九轮一次、一出就是几十个
teardown error。pop 移进内层 finally,shutdown 只等未结束的任务并清空。修后全量
连跑 12 轮 12 轮全绿。
test_guardrails.py 8 例:超时中止(挂住的节点 1 秒后被停下)、缺省模型超时、封存覆盖
run.finished、改成果被戳穿、事后追加不误报、老运行按旧口径核、中断态未封存、接口
404。414 → 422 passed。
|
2026-09-25 15:59:32 +08:00 |
|
ThearchyHelios
|
ed83aa2fa4
|
并行支路一条失败时,运行不再永久卡在 running
两条并行支路一条失败,LangGraph 会取消另一条;那一条若正在写库(节点收尾写
工件最常见),整个运行就可能永远停在 running,占着的并发名额也不还。修审批时
加的并行驳回用例约五次卡一次,才把它翻出来。
根因在两层,都是"取消打进进行中的数据库清理":
1. aiosqlite 0.22 的 stop() 每调一次都往工作线程队列里放一个"关闭并退出"
请求,线程处理完第一个就退出,之后的请求没人处理,等它的一方永远挂着。
close() 的 finally 会调 stop(),SQLAlchemy 优雅关闭被取消后退回的强制
terminate 也会直接调——同一连接上两者一碰就卡死。并发 close 两次,10 次卡
10 次。现在同一条连接的 stop() 只真正执行一次,之后的调用拿同一个 future。
2. 卡死解开后露出第二层:SQLAlchemy 的 _close_connection 记完日志会把
CancelledError 原样抛出,跳过了把连接记录清空的那一行,已经关掉的连接留在
池里,下一个取到它的人拿到 "no active connection"。主引擎开 pool_pre_ping:
探活失败按断开处理、换新连接(aiosqlite 方言把这句判为断开,已核对源码)。
验证:test_aiosqlite_close.py 4 例是确定性的(并发 close、close 撞上强制
stop、被取消后的 close、重复 close),去掉补丁 3 例超时失败。复现脚本同一场景
连跑两批各 40 轮:修前 12 轮内必卡,修后 80 轮 0 卡、0 次坏连接抛给调用方;
SQLAlchemy 在被取消时仍会记一条"Exception terminating connection"日志,那是
它自愈过程的记录,不影响结果。334 → 338 passed。
|
2026-09-25 15:20:46 +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
|
6a46e4b55c
|
大文件传得进来了:分批算向量、上传转后台、边读边数大小
压测量出来的三个问题,10MB 文本(切 7914 段):
上传耗时 150s(本地哈希) 换成 Qwen3-Embedding-4B 要 40 分钟以上
而这 150 秒是压在一个 HTTP 请求里同步跑完的
浏览器和反向代理的默认超时都在 60 秒上下,也就是 5MB 以上的文件用户必然
看到超时——而后端其实还在跑,跑完文档也确实入了库。界面显示失败、数据其实
成功,比直接拒绝还糟。
**分批算向量。** 原来 ingest 是 embed_texts(pieces) 一次全发。本地哈希只是慢,
换成远端模型就是必挂:近八千段一次请求塞不下(OpenAI 单次上限 2048)。
reindex 那边早就分批了,ingest 这条一直漏着——而我上一轮刚把"换真模型"做成
设置页里点两下的事。
**上传转后台。** create_document / process_document 拆开,上传立刻返回
status=processing,切块和算向量在后台跑,进度写回文档行,前端轮询显示
"正在切块并算向量… 4288/7914 段"。实测上传返回 0.07 秒。
进度这件事返工过一次:先用另一个 session 单独写,结果整个过程界面上都是空的、
只在结束时闪一下——SQLite 只允许一个写者,主事务开着的时候那次写被堵住了。
改成主事务自己按批提交,顺手把进度写进去。
失败要写回 failed,否则永远停在"处理中"转圈。服务重启时也把残留的
processing 收尾成 failed——和 runner 对 running 的 run 是同一个道理。
**边读边数大小。** 原来是 raw = await file.read() 读完再判断,传个 1GB 的
文件内存在报 413 之前就吃掉了——那个检查拦的是"入库",不是"占内存"。改成
按块读、超了立刻停。上限从写死的 10MB 变成配置项,默认 50MB。
端到端验过:10MB 上传返回 0.07s,后台进度 768→1472→…→7914 实时可见,
最终 ready / 7914 段。
|
2026-09-20 09:47:02 +08:00 |
|
ThearchyHelios
|
913380f738
|
倒排索引取代全表重算;PDF/Word 终于传得进来
**倒排**:以前每次检索都 select(Chunk) 全量载入,再在 Python 里重建一遍 BM25
——不只是扫表,是对每一段重新分词、重新统计词频,每次查询都来一遍。几百段
无感,上万段就是秒级,而那正是知识库开始有用的规模。
没用 SQLite 的 FTS5。方案里写了实施前先验证,验了:trigram 分词器要求查询
至少三个字符,中文的「商家」「订单」「权限」这些两字词一个都召不回。照搬会
让召回大幅倒退。现有分词器本来就做中文单字 + 二元组,自己存一张 chunk_terms
既保住召回,也不把知识库锁死在 SQLite 上。
快是次要的,一致才是前提——所以先写了"倒排必须和全表扫给出同一个名次"的
测试。它当场抓到两个真问题:
1. BM25 被算了两次:倒排按全集统计(N、avg_len、df)算一次,hybrid_rank
又在候选子集上重建一次,而子集的 df 和平均长度是另一套数。改成把倒排
算好的分传下去,不再重算。
2. 原来的 BM25.score 遍历的是**带重复**的查询词表:「导出订单最多多少行」
里「多」出现两次,贡献就加两次。倒排用 set() 去重,顶部那条差了 2.32。
改成按查询词频加权。
还有一处是排序确定性:分数并列时稳定排序保留输入顺序,而两条路径喂进去的
顺序天生不同——同一个查询会因为走了哪条路给出不同名次。两边都按 id 定序。
倒排跟着 ingest 建、跟着删文档清、跟着 reindex 重建;没建倒排的片段(存量
数据升级上来就是这样)退回全表扫——宁可慢,也不要因为索引没建好就说"没搜到"。
**解析**:upload 以前只收 UTF-8,PDF 传上去直接 415,而 mime 字段记了却从不
参与判断。现在认 PDF / DOCX / HTML。解析库走可选依赖 [docs](几十兆,只用
纯文本的人不该为此付出安装时间),HTML 用已在依赖里的 bs4。
几处是实际验出来的,不是想出来的:DOCX 要连表格一起取(合同和口径说明的关键
信息常常整个在表里);PDF 每行拖着排版填充空格,不清掉会混进切块和词频统计
(实测 416 字里只有 68 字是正文);扫描件提不出文字要明说,否则悄悄存一份
空文档,用户以为传成功了却永远搜不到。
PDF/DOCX 两条路径在临时 venv 里用真文件验过(含中文、表格、扫描件、坏文件),
没有动开发环境。
|
2026-09-19 09:44:09 +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
|
2df591d05f
|
工件库:内容寻址存储、节点输出与工具快照落盘、运行清单哈希
事件流里的输出是给画布看的截断预览,出具溯源需要完整证据。
新增 data/artifacts/ 内容寻址工件库(sha256 即地址,取回时复验哈希,
篡改即报错);节点完整输出与工具调用快照(query+结果集)自动落盘,
事件只携带工件 id。运行终态对全部事件计算清单哈希存入 Run,
事后对事件流的增删改会与它对不上。
graph_hash 剔除 viewport 后计算——挪画布不改变图的身份。
Run/Workflow/WorkflowVersion/Approval 预置分级与治理列,
init_db 附带 SQLite 轻量列迁移。
|
2026-09-14 10:10:20 +08:00 |
|
ThearchyHelios
|
964eeaa829
|
AgentLab:可视化 Agent 编排实验台
在画布上拖拽编排 agent 工作流,并实时看着它执行。
后端 FastAPI + LangGraph:把画布 JSON 编译成 StateGraph,15 种节点
(模型/Agent/多Agent、工具/沙箱、分支/循环/子图、记忆/检索/整形、
人工介入/结构校验)。选 LangGraph 是因为可视化编排真正难的是暂停、
恢复和回放,而 checkpointer + interrupt() 正好把这几件事做在了底层。
前端 React + React Flow:节点元数据驱动属性面板与连接桩,运行时
节点高亮、边流动画、卡片内流式 token 全部由同一份事件流驱动。
关键实现:
- Agent 内工具审批用 @task 缓存模型调用,interrupt 重放时不重复计费
- 沙箱保持只读根文件系统,代码经 exec+base64 写入(put_archive 会被
只读容器拒绝),断网、非 root、内存/CPU/超时限额
- 检索为向量与 BM25 混合,本地哈希 embedding 零配置可用
- 分支条件走 AST 白名单求值器;HTTP 工具逐 IP 拦截内网与元数据地址
- Mock provider 让无 API Key 也能跑通完整链路
7 个内置模板全部冒烟通过,含真实模型端到端验证。
|
2026-09-13 17:27:00 +08:00 |
|