Files
dsh_ai1net_server/docs/会话与接续/会话接续机制_问题复盘与修复_20260916.md
T

137 lines
10 KiB
Markdown
Raw Normal View History

# 会话接续机制 · 问题复盘与修复(2026-09-16)
> **结论一行**:机制本身(规范 §3.1.1 / §3.1.2 / §3.2.1)没错,**错在它没有被接上** —— ① 真正跑的那条自动化 prompt 没带"开机四步";② 硬环节的注入文案还停在"请用户开新会话",从不提 `automation_update`;③ 开机第一屏有可能喂过期事实。
> **触发**:用户 2026-09-16 14:41 原话「决策方法 之前建立的机制有问题,看看自动任务新建的会话对话记录」。
> **取证范围**:`~/.workbuddy/projects/e-ProgramData-AI技能-aliyun-dsh-server/{3814f5fb,e265f0cd,478eef8c,d48a9be8}.jsonl` + `workbuddy.db`(`automations` / `automation_runs` / `session_usage`)+ `state.py` 实跑 + `stop-dialog-guard.py` 源码。
---
## 一、今天的自动任务新建会话 —— 花了多少、干了什么
| 会话 | 自动化 | 轮 | 工具调用 | 积分 | 用户当场说了什么 |
|---|---|---|---|---|---|
| `3814f5fb` 归档接续 | `5d1dc22c` @10:20 | 2 | 65 | **21.34** | 「是否知道那个会话创建了**这个你**,是否有执行那个会话待处理的任务」 |
| `e265f0cd` 接续(优化版) | `4a3d815b` @10:45 | 1 | 32 | **8.93** | — |
| `478eef8c` 决策方法-2 | `218e5b11` @11:10 | 5 | 191 | **74.80** | 机制就是在这一条里定的(14 条用户发言) |
| `d48a9be8` S1 落地 | `d30f3cf7` @14:32 | 2 | 40 | **9.37** | 「**是不是应该先确认待执行的任务有哪些 再去执行**,不确定你搞清楚情况没有」 |
| **合计** | | | **328** | **114.44** | |
- 积分口径 = 转录 `rawUsage.credit` 逐次求和;与 `session_usage.credit_json` 三处**完全吻合**(`d48a9be8` 9.37 / `e265f0cd` 8.93 / `b08b1c35` 33.71),两个独立数据源对上 ⇒ 数字可信。
- 用户观察「自动任务新建的会话,提一轮就 7、8 个积分」**成立**:`e265f0cd` 单轮 8.93、`d48a9be8` 首轮 5.28。
- `d30f3cf7` 立项时的验收口径是**与基线 `b08b1c35`(77 次 / 11.17 分)对比,目标 ≤10 次 / ≈1 分**(见 `478eef8c` A72)。**实测 40 次 / 9.37 分 ⇒ 未达标。**
---
## 二、四个真问题(按严重度)
### D1 🔴 生成自动化 prompt 的那一步没接上机制 —— **主因**
规范 §3.2.1 要求 prompt **写死"开机四步 + 工具调用上限"**。实际跑在 `d48a9be8` 上的那条 prompt:
- ✅ 有「本轮只做一件事,做完即停」「取证最多 3 条」(§3.2.1 ④⑤)
- ❌ **没有**接续包路径、**没有**校验命令、**没有**「先跑 `state.py`」
- ❌ 反而把整段 S1 技术细节(≈1.2 KB)抄了进去 —— 而细节的单一来源本是 `交接单_覆盖网络落地执行_20260916.md`
⇒ 结果:新会话手里既没有"从哪接班",也没有"上限是几次",只能自己从零探索。**同一件事被写了两遍(prompt 与交接单),真源被架空。**
### D2 🔴 开机顺序反了 —— 用户看到的就是这一步
`d48a9be8` 的 40 次调用里:
- `[01]` 读 automation memory(不存在,首次运行)
- `[02]–[15]` 读交接单、读架构文档、抢锁、**10 次连续读码探索**
- `[16]–[36]` 改码 → build/test → scp → 重启 → 验收 → 写记忆 → 清临时文件
- **`[37]` 才第一次跑 `state.py`**;**`[39]` 才第一次读 `交接单/README.md §一`(待执行清单)**
⇒ 机制设计的第 0 步(1 次调用代替十几轮探索)被排到了**倒数第 4 步**。用户当场质问「是不是应该先确认待执行的任务有哪些再去执行」,AI 也在下一轮自认「顺序错了」。
⚠️ 补充:`state.py` **确实**能给出答案 —— 它的 `[入口]` 段 14:43 实跑就写着"3) ✅ S0 已完成 …… 5) 下一步 = 按交接单执行 S1"。**跑对了就不会有这一问。**
### D3 🔴 硬环节的注入文案停在旧版 —— 自动接续没有触发源
`scripts/stop-dialog-guard.py` 三级(≥30 万)注入原文(修复前):
```
③ 然后明确告知用户「请开新会话,接续点在 X」,**由用户开**(钩子无法自动创建会话)。
```
而规范 §3.2.2 画的链路里,第三段是 **[软] 模型调 `automation_update` 登记一次性任务(+2 分钟)**。
⇒ 钩子**从不要求**模型登记自动化 ⇒ 这条"软"环节连提示都没有,全凭模型自觉。今天 4 条自动化全是会话内**临场手写** prompt 的结果 —— 这正是 D1 的来源。
### D4 🟡 开机第一屏可能喂**已被推翻**的事实
`state.py` 的 `[收口]` 段是"今日日志原文摘录",机械取**最后一个含「接续/收口」的章节**。实测 14:43 输出里带着两条**当天已被勘误**的说法:
- 「两个『定时』自动化仍在按钟点烧钱」—— 实为 09-12 / 09-13 已软删除、早已停摆;
- 「转录里的 `rawUsage` 字段为空 `{}`」—— 实为有值(本次积分就是这么算出来的)。
另外 `state.py` 的 `[入口]` 文件名**写死**为覆盖网络线那一份 ⇒ 换工作线后会**静默展示旧线的待办**。
### D5 🟢 一次性 automation 的 `memory.md` 是死重量
宿主系统提示强制"先读 `automation memory.md`、收尾写回"。但一次性任务只跑一次 ⇒ 首轮**必然读不到**(`d48a9be8 [01]` 就是白跑一次),写回的那份**永不再被读**。已知设计面,无法改宿主,只能靠 prompt 一句"该文件不存在属正常"省掉一次调用。
---
## 三、已落地的修复(本轮,全部在我们自己的资源上,可推翻)
| # | 文件 | 改了什么 |
|---|---|---|
| F1 | `会话接续规范_20260916.md §3.1.2` | 新增**第 0 步**:先跑 `state.py`(1 次调用拿到 [锁]/[git]/[入口=待办+接续包位置]/[收口])⇒ 回答"我该接谁的班" |
| F2 | `会话接续规范_20260916.md §3.2` | 新增硬约束 **「⛔ prompt 里不许复制任务细节」**(会造第二漂移源 + 挤掉开机四步),附 `d48a9be8` 实测 |
| F3 | `会话接续规范_20260916.md §3.2.1` | 模板首行加 `⓪ 先跑 state.py` |
| F4 | `dsh-server-docs/scripts/stop-dialog-guard.py` | 三级注入 ③④ 改为:**登记一次性 automation(照 §3.2.1 模板)→ 做不到才让用户开** |
| F5 | `state.py` | ① `[收口]` 加"日志原文摘录、可能已被推翻、以 MEMORY.md 状态层为准"护栏;② `[入口]` 改为**自动取最新的 `接续入口_*.md`**,不再写死 |
✅ 验证:`stop-dialog-guard.py` `py_compile` 通过、新文案渲染正确;`state.py` 实跑通过(护栏行已出现在过期说法之前,`[入口]` 动态解析正常)。
---
## 四、重测结果(14:55 一次性自动化,**已跑完,达标**)
| 口径 | 失败轮 `d48a9be8`(14:32) | 重测 `d5398c7d`(14:55) |
|---|---|---|
| 工具调用 | **40 次** | **6 次** ✅(预算 ≤8、验收线 ≤10) |
| 积分 | **9.37** | **1.69** ⚠️(目标"≈1 分"未完全达到,但比失败轮省 **82%**) |
| 是否先跑 `state.py` | 第 **37** 次调用才跑 | **第 2 次**(第 1 次是宿主强制的 automation memory,文件不存在) |
| 是否先确认待执行清单 | 第 39 次才读到 | **首屏即由 `state.py` 的 `[入口]` 段给出**,并复述了"未完成/下一步" |
重测会话自报的两处可再省:① 用带 emoji 的完整标题串 grep 未命中、要用子串再 `tail`(多 1 次);② ⓪ 与 ① 是同一命令,本可合并(多 1 次)⇒ **理想路径 4 次**。
⇒ **机制已闭环**:改动只落"接不下班"这一侧,效果可归因。剩余可选项见 §四-2。
---
## 五、还没做 / 需要条件的
1. **prompt 生成仍未强约束**:现在靠"模型记得照模板写"。要彻底硬起来,需要一个 `gen-continuation-prompt.py <接续包>` 生成器 + 钩子文案里写死"照它的输出原文"(本轮未做,属新造工具)。
2. **`state.py` 的 `[入口]` 只覆盖"最新一份接续入口"**,多条工作线并行时仍会漏(当前只有一条线,够用)。
3. **D5 无法从我们这侧解决**(宿主行为),已记录,不列为待办。
4. **`state.py` 的 `[收口]` 护栏只是"提醒",不是"过滤"** —— 更彻底的做法是让它只摘"判据/结论"行、或与 `MEMORY.md` 状态层比对;本轮先用最低成本方式止血。
---
## 六、多线并行会不会冲突(2026-09-16 15:2x · 用户提问)
> 用户原话:「**假如多个会话都要新建会话,新会话全都执行这个口令吗,会不会冲突**」
**会 —— 三种形态,真正会咬人的是 ①。**
| # | 形态 | 机制现状 | 处置 |
|---|---|---|---|
| ① | **串线** —— 口令不带线名,而 `state.py [入口]` 原只取"最新一份接续入口" ⇒ 多个新会话都跑**同一条线**,另一条线没人跑 | 🔴 **真会发生**(今天只有一条线,属潜伏) | 口令**必带线名**;`[入口]` 已改为**列全各线** |
| ② | **抢锁** —— 同时动手只有一个抢到,输家"停手"白烧一轮 | ✅ 全局锁兜底,**不会同时改** | **读前置可并行**(都只读);输家**只报告** |
| ③ | **共享文件互覆** —— 日志 / `MEMORY.md` / 文档库 / 代码仓全平台共用 | ⚠️ 靠纪律(实测今日日志有 **15** 个接续点/收口章节) | **只追加自己的小节**(小节名带线名) |
**已修(3 处)**
- `state.py`:`[入口]` 列全所有 `接续入口_*.md`(多线时打 ⚠️"只走你自己那条");`[收口]` 追加"最近 3 个接续点标题";末尾**直接打印带线名的口令**。
- 规范 **新增 §3.4「多线并行:怎么不打架」**(三形态表 + 四条硬规则:一线一份接续入口 / 口令必锚线名 / 同一时刻只许一个自动会话动手 / 抢不到锁=正常信号只报告)。
- 规范 §3.2 硬要求 **四条 → 五条**(新增"prompt 必须锚定线名");§3.2.1 模板 ⓪ 改为"按 `[入口]` 里**「<线名>」那一行**定位接续包"。
**新口令(`state.py` 末行自动打印,直接抄给新会话)**
```
跑 `state.py`,按 覆盖网络线 那段 §2 第 1 条开工
```
⇒ 单线时它和旧口令等价;**多线时这一步就决定了新会话走哪条线**,不会串。
⛔ 反过来:`automation_update` 的 prompt 里**只写"按 §2 第 1 条开工"= 埋雷**(多线起来的那天才会炸,且很难归因)。