Files
dsh_ai1net_server/交付物/唤醒-会话自建后台任务方案评估-20260930.md
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

14 KiB
Raw Permalink Blame History

唤醒方案评估 · 会话自建后台任务(2026-09-30)

状态:✅ 已实测闭合(4 组对照实验)|日期:2026-09-30 13:0x |会话:6ecf6d98 一句话结论:会话自己起的「一次性后台任务」是一个可用的唤醒原语 —— run_in_background 任务完成即唤醒本会话,与 stdout 输出无关,静默也唤醒,300 秒时长不被回收;⛔ 但「常驻循环 + 周期 echo」不行(不完成 ⇒ 永不唤醒)。 本文件只写实测与方案,⛔ 不写法规、⛔ 不写合规。


§0 本棒范围

项 内容
本次要评的 会话自建后台任务能否当"唤醒本会话"的通道;粒度能不能到 5 分钟
明确不评 gateway 通道(用户 13:0x 指令:「不考虑gateway ,专门做 会话创建后台任务的方案评估测试」)
前置事实(已完成,另案) 本机 gateway 直连那条线已测通:.workbuddy/collab/wake-session.py → 现查网关(三判据 + 按 live.sessionId 精确匹配)→ POST /reply ⇒ 对本会话 HTTP 200 {"data":{"delivered":true}}。结论与判据另见 §8

§1 机制:为什么它能唤醒

依据不是推断,是工具契约原文(run_in_background 的说明):

「Once backgrounded you will receive a task_id; you do NOT need to poll — when the command finishes you will be automatically notified via a <task-notification> message in your next turn.」

⇒ 任务完成 ⇒ 宿主往本会话投一条 <task-notification> ⇒ 本会话开新的一轮。这就是用户记忆里「后台任务监控程序安装,装好后会话继续处理」的那条机制,逐字对上。


§2 对照实验(4 组 · 两组变量)

变量:① 有没有 stdout 输出 ② 时长。全部是一次性任务(⛔ 不是常驻)。

组 task_id 命令 stdout 时长 完成时刻(任务侧写盘)
A d5R89w sleep 18; 写状态; echo "BGTEST-A …" 有 18 s A-done 12:57:13
B KdwR4k sleep 36; 写状态 0 字节 36 s B-done 12:57:31
C 8chtue sleep 20;echo TICK-C1; sleep 20;echo TICK-C2; sleep 20;写状态 中途有 2 行 60 s C-done 12:58:58
D hixCxx sleep 300; 写状态 0 字节 300 s D-done 13:02:55

取证方法(两条线独立,⛔ 不靠"我觉得被叫醒了"):

  • 任务侧:每条任务自己往 tmp/_bgt_state.txt 追加带时间戳的 *-done ⇒ 证明任务真的跑完了(可以把"任务被杀"与"没被唤醒"分开判);
  • 会话侧:sessions.last_activity_at / updated_at + 我是否在零用户输入下开了新的一轮。

2.1 实测时间线

时刻 任务侧事件 会话侧
12:57:08 (A 完成触发的回合开启) ✅ last_activity_at=12:57:08
12:57:13 A-done(有声) —
12:57:31 B-done(全静默) ✅ 独立唤醒(零输入)
12:58:05 — (我上一轮结束)
12:58:16 C-t1(中途输出) ❌ last_activity_at 未动
12:58:37 C-t2(中途输出) ❌ last_activity_at 未动
12:58:58 C-done(静默完成) ✅ 唤醒
13:02:55 D-done(300 s 静默) ✅ 唤醒(updated_at 13:02:59 > 13:02:55)
— 残留检查 ✅ ps 无 sleep 残留进程

§3 结论(三条,均为实测)

# 结论 证据
1 🔴 唤醒的触发 = 「任务完成」这一个事件,与 stdout 输出无关 A(有声)与 B(0 字节)都被唤醒;C 的两次中途输出都没有唤醒,只有完成才唤醒
2 ✅ 静默任务完全安全 B / D 两条全静默任务各只产生 1 条通知,无输出洪泛
3 ✅ 长时长不会被沙箱回收 D 从 12:57:55 跑到 13:02:55,整 300 秒跑满,照常唤醒

3.1 ✅ 同时纠正一条旧记忆的因果(重要)

旧记法:「后台任务每轮输出都会唤醒宿主会话 ⇒ 会话永不空闲 ⇒ 看着像卡」。 实测不成立(C 组:中途输出不唤醒)。真因是另一件:

持续输出 ⇒ 转录体积涨 ⇒ 推过 10 MiB ⇒ 宿主 diagnostic-log dropped ⇒ 界面不再显示 ⇒ "看着像卡"

⇒ 所以毒药是「输出的量」,不是「被唤醒」;一次性 + 静默两者都干净。10 MiB 轮转已由既有实物确认(同一目录下 fe146dd9-*.log / .log.1 / .log.2 三代各 ≈10 MiB)。


§4 方案设计(P-1 … P-4)

方案 做法 粒度 代价 判定
P-1 单发脉冲 起 1 条 sleep N(静默)一次性任务 任意(N 自定,实测到 300 s) 0 新会话;跑完即退、无残留进程 ✅ 推荐的基础原语
P-2 批量预排 一轮里同时起 K 条错峰静默任务(sleep N、sleep 2N…) 覆盖 K 个周期 0 新会话;K 个 sleeping 进程在跑 ✅ 适合"这一小时每 5 分钟叫一次"这种有限窗口
P-3 链式续棒 每次被唤醒后,会话再起下一条 任意 0 新会话;每周期消耗一轮 AI;链路依赖会话/宿主活着 ✅ 省进程,但要接受"每棒一轮 AI"
P-4 常驻循环 + 周期 echo while true; do sleep 300; echo tick; done — 常驻进程 ⛔ 已实测不可行:永不"完成" ⇒ 永不唤醒(结论 1 的直接推论)

共同代价(P-1/P-2/P-3 都有):生命周期绑在这个会话上 —— 会话关闭 / 宿主重启 ⇒ 任务与链一起没(对比:自动化是宿主侧持久排期,不受会话影响)。


§5 与既有约束的对账

约束 本方案 判定
T1(⛔ 垫片/桥不得放进会话后台任务;长跑走一次性自动化或独立进程) 本方案不是把垫片放进去;它是用一次性任务自身当脉冲,无长跑 ✅ 不触 T1;⚠️ 但 T1 的文字需要补一条脚注(禁的是"常驻 + 持续输出",不是"会话起后台任务"本身)
T3(不占资源、不改它) ⛔ 不绑端口、⛔ 不改配置/插件/令牌、⛔ 不 kill 宿主 ✅ 全部满足
T7(可静默、可回滚、无残留) 跑完即退;实测 ps 无残留 ✅ 满足
09-30 铁律(手机接入类 ⛔ 不得影响 WorkBuddy 本身运行) 纯本机、单向依赖:任务被 kill ⇒ 只是"不唤醒",宿主无感 ✅ 降级方向正确(关掉自己)
09-29 坑(常驻后台任务 ⇒ 卡死) 本方案不常驻;即使要覆盖多周期也走 P-2 预排 ✅ 规避
rrule 硬白名单(自动化最小 1 小时) 本方案完全不碰 rrule ✅ 绕开了"5 分钟做不到"那堵墙

§6 边界与未测项(⛔ 不要当成已经验过)

  1. 只能唤醒"自己这个会话" —— 跨会话投递仍需 gateway reply(另案)。
  2. 唤醒载荷是 <task-notification>,不带自定义指令 —— 要带信息只能让任务 echo(那就进转录、会涨体积)。
  3. 未测:更长的时长(>300 s)、更高频/更大量的中途输出是否最终会唤醒、会话关闭后任务的存活边界、宿主重启后残留如何。
  4. ⚠️ 本会话的转录日志自 12:26 起已冻结(10,485,723 B,mtime 恒 12:26:19)—— 实验前就已如此。 ⇒ 讨论"规模/体积"时不能再用这个文件当度量,要换度量。
  5. ⛔ 本棒没有留下任何在跑的脉冲链(实验任务全部已退出;⛔ 未创建自动化)——要不要真的把 5 分钟脉冲挂上去,属"会影响你桌面体验"的事,由用户拍板。

§7 图

7.1 泳道图:一次脉冲跨哪几层

 会话(6ecf6d98)        工具运行器            宿主事件循环            UI
 ────────────────       ──────────────       ─────────────────      ──────────
 ① 起任务 ───────────►  spawn `sleep N`
 (run_in_background)        │
                              │ N 秒静默运行(不动会话 ↔ 不涨转录)
                              ▼
                        任务 exit ──────────► ② 生成 task-notification
                                              (★ 全部唤醒都发生在这一格)
                                                    │
 ④ 开新一轮 ◄───────────────────────────────────────┘
 (这就是"被唤醒")                                 │
    │                                              ▼
    ├─► P-1/P-3:再起下一条脉冲                ③ 写入 sessions.updated_at
    └─► 无人接棒 ⇒ 链自然停(天然 fail-safe)

 ★ 判据:能唤醒 = ②那一格发生 ⇒ 只认「完成」;①→③ 之间任何 stdout 都【不】触发它

7.2 事故链图:09-29「看着像卡」的真实成因(本次已纠正)

 ① 会话里起【常驻】后台任务
        │
        ▼
 ② 它【持续】往 stdout 吐内容            ← 🔴 毒药在这里(量),不在"唤醒"
        │
        ▼
 ③ 每段输出都进转录 ⇒ 转录体积持续上涨
        │
        ▼
 ④ 越过 10 MiB ⇒ 宿主 diagnostic-log dropped(droppedLines 上万)
        │
        ▼
 ⑤ 界面不再显示新内容 ⇒ 用户看到"卡死"
        │
 ✗ 旧记法把 ② 写成"每轮输出都会【唤醒】宿主会话" ⇒ ❌ 本次 C 组实测推翻
   (中途输出【没有】产生任何唤醒;唤醒只认"完成")

 本该在场而缺席的防线:
   · 缺「输出量闸门」:常驻任务必须完全静默(stdout 全重定向)—— 这条当时没落地
   · 缺「体积看门狗」:转录接近 10 MiB 就该告警/轮转 —— 本会话日志 12:26 冻结,至今无人报

§8 索引与出处

是什么 在哪
本报告 交付物/唤醒-会话自建后台任务方案评估-20260930.md
gateway 直连唤醒器(另案,已测通) .workbuddy/collab/wake-session.py
gateway 侧方案分析(含 §11「5 分钟自动任务做不到」) 交付物/唤醒的解决方式-官方文档核对与方案分析-20260930.md
「唤醒为什么断」的原始定案 交付物/查唤醒为什么断-结论-20260930.md
架构定稿 v3(T1–T7 + 验收判据 V7) 交付物/手机App经覆盖网络操作WorkBuddy-架构定稿v3-20260929.md §0.5
实验原始痕迹(任务侧时间线) tmp/_bgt_state.txt(⛔ tmp 不入库)
本次读数 sessions 行 ・ logs/2026-09-30/sdk/conversations/6ecf6d98-*.log(已冻结)

§9 追问(13:0x):能不能"持续"?—— 能,但要把两件事分开

用户两问:①「后台任务不能持续运行吗」②「一次性任务完成后继续创建,这样就能持续?」

9.1 🔴 核心区分:「持续运行」≠「持续唤醒」

维度 能不能 依据
持续运行(进程一直活着) ✅ 能 E1 实测:上一轮起的常驻静默心跳循环,跨过本轮的结束与下一次唤醒仍在跳(hb 13:08:31 / 13:08:52 / 13:09:12,20 s 一次)⇒ 后台任务进程不随本轮结束被杀
持续唤醒(每次都被叫起) ⛔ 常驻做不到 同一实验:13:08:33→13:09:12 之间 E1 吐过 hb 13:08:52,而 sessions.updated_at 没动 ⇒ 常驻静默也不唤醒(与 §3 结论 1 一致)
自举续命(任务自己续自己) ⛔ 做不到 ①任务内 nohup … & ⇒ detached spawn 活不过工具调用边界(既有实测:日志 0 字节即死)②任务内循环 ⇒ 不"完成" ⇒ 不唤醒
持久化兜底(计划任务/服务) ⛔ 封死 沙箱回收子进程 + 内置程序黑名单(wsl/wslconfig/wmic/sc/reg/schtasks)⇒ 本机唯一可行的常驻起法 = 会话后台任务 + stdout 全重定向

⇒ 想让"常驻"也能唤醒,只有一条路:它在循环里主动投递(调 gateway reply,见另案 .workbuddy/collab/wake-session.py)。常驻负责"活着",reply 负责"叫醒" —— 两条线在这里汇合。

9.2 ✅ 链式续棒(P-3)实测成立

棒 谁起的 fires 是否唤醒
第 1 棒 SAAMf7 我(13:08 轮) pulse 60s fired 13:09:12 ✅ updated_at 13:09:15
第 2 棒 BV7ZIm 🔴 我在"被第 1 棒唤醒的那一轮"里起的 pulse 60s fired 13:10:31 ✅ updated_at 13:10:34

⇒ 「完成 → 被唤醒 → 再创建下一棒 → 下一棒照样唤醒」闭环成立(第 2 棒的"起棒人"就是被第 1 棒叫醒的那个会话)。 ⚠️ 代价要认下来:每棒消耗一轮 AI(5 分钟节奏 = 288 轮/天);想省这个成本 ⇒ 改 P-2 一轮预排 K 条错峰(换成 K 个 sleeping 进程)。链断即停既是缺点也是安全特性(没人接就自然停)。

9.3 新增可复用工具

.workbuddy/collab/wake-pulse.sh <秒数>(默认 300)—— 一次性静默脉冲;三条硬约束写死在脚本头:⛔ 不写 stdout/⛔ 不常驻/⛔ 任务内不自我续棒。实测与内联写法等效(13:09:12 / 13:10:31 两次均由它触发)。

9.4 收尾(T7)

⛔ 演示到第 2 棒为止即停,没有让链继续跑;E1 已 TaskStop(runtime 1m18s);ps 复查全部实验进程已退出、无残留;心跳文件停在 3 行未再增长。