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
|
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
|
a3058e7df8
|
复核套件骨架(后端):先把后续几个工作包要用的位置占好,各包只改自己的文件。节点类型加双盲复算 dual_check、反方审稿 challenge,执行器是占位(记一条 info 日志后跳过,产出 skipped 和原因),校验按普通节点走,叫法进 labels,暂不写进助手的节点说明。出具契约留一个通用口子:成果节点上游任何节点产出里的 issuance_gaps(字符串列表)收进缺口,前面加上「节点类型「节点名」:」,出具随之降档、不会升档;只认这个成果的上游,写坏的条目跳过。路由先登记 schedules、compare、bundle、inquiry、whatif 五个空前缀,挂在 /api/runs 下的四个排在 runs.router 之前,以后加固定路径不会被 /{run_id} 吃掉。定时任务的调度先放空的 start / stop,AGENTLAB_SCHEDULER 默认开、0 / false / off 关,写进设置;启动时开着才 start,测试环境默认关
|
2026-10-04 23:40:20 +08:00 |
|
ThearchyHelios
|
e7646532c1
|
数据目录阶段 1A:探查补读外键和唯一约束(读不到就跳过、不写键),新增 50 张表的合成景区库夹具
|
2026-10-03 09:30:50 +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 |
|