docs: 删除 7 份已完结的执行计划,把教训与数据提出来

## 为什么删

执行计划在实施完结后**会变成误导性读物**:它记录的是"当时打算做什么",
而读者会读成"现在是什么"。

这在本仓已经造成真实误读 —— `prod-migration-checklist.md` 写着
「状态:HologramAgent 已停机」,那是一次性迁移(2026-10-04)当场的状态,
但照着读会以为服务现在还停着。

## 删了 7 份,删前提取三类内容

| 删掉的文档 | 行数 |
|---|---|
| `memory-restructure-plan.md` | 168 |
| `memory-blockflow-repair-plan.md` | 450 |
| `memory-usability-plan.md` | 309 |
| `scene-memory-fix-plan.md` | 271 |
| `toolcall-parallel-execution-plan.md` | 578 |
| `prod-migration-checklist.md` | 157 |
| `entities-retirement-plan.md` | 118 |

**提取去向**:

1. **教训 → 新建 `engineering-lessons.md`**

   这些是它们唯一还有长期价值的部分。汇总成一篇,并按形态归类:

   - 判据类(5 条):结构判据不能替代质量判据 / 测「接线之后」≠测「有没有接线」/
     验证通过≠架构正确 / fixture 照臆测字段编 / 先改代码再写测试
   - 接线类(3 条):机制≠接线 / 依赖图是唯一保证顺序的地方 / 单数改了复数漏了
   - 权限与生命周期(3 条):「接上了」有三层 / 以调用方名字为键的表要清理 /
     提权口子:让调用方声明自己是谁
   - 流程类(3 条):长任务纪律 / 数字要现场取 / 计划不进仓库

   文末点出它们共同的形态:**某种"看起来对了"的信号与"真的对了"之间存在缺口。**

2. **实测数据 → 并入 `toolcall-contract-and-sequence-design.md` 附录 A**

   那是新旧内核的对比压测(288 条输入 × 两版),核心结论值得留:

   ```
   场景        旧版中位/工具数    新版中位/工具数
   slowbatch8  0.220s / 0 个     0.409s / 8 个
   ```

   ★ **旧版"更快"是假的** —— 它的 `openai.lua` 缺 `stream_index` 透传,
   多个分片并到槽 0 ⇒ 每个工具报参数错误而一个都没真跑。
   这是「只看耗时会被骗」的典型。

   另附 stage 锁告警的完整归因(不是内核缺陷,成因在插件进程侧运行时)。

3. **未完成的项 → `future-plan.md` §六**

   尤其 `entities-retirement-plan` 那条:方案是"三表全退",但 2026-10-31
   实测生产库 `entities 1292 / relations 978 / sentences 66` 行**仍在用**。
   这个事实不能随文档一起消失。

## 保留两份(它们不是计划,是依据)

- `legacy-table-retirement.md` —— 被 **4 处生产代码注释**引用(`graph.go` ×2、
  `commit_blocks_test.go`、`probe_prod_test.go`),是退役判断的依据
- `interrupt-subsystem-split-plan.md` —— 探测事实被 `interrupt_invariant_test.go`
  引用;拆分本身确实未实施

## 索引重写

`docs/zh/README.md` 现在是「四类文档」的规则说明:

| 类型 | 回答什么 | 命名 |
|---|---|---|
| 设计文档 | 为什么这样设计 | `*-design.md` |
| 操作手册 | 怎么做 | `*-runbook.md` |
| 教训 | 踩过什么坑 | `engineering-lessons.md` |
| 未做登记 | 还剩什么没做 | `future-plan.md` |

并明确写「**不放执行计划**」及例外条件。

## 顺带修掉的引用

删除后 4 处引用指向不存在的文件:

- `deploy-runbook.md` → 改指 `toolcall-contract-and-sequence-design.md`
- SDK 仓 `guide/stream-tool-call-index.md` / `guide/parallel-tool-declaration.md`
  → 同样改指(这两个在 SDK 仓,随后单独提交)

## 验证

- docs/zh 27 篇,索引里列的全部存在、无遗漏
- 全仓无指向已删文档的引用(代码 + 文档)
- 代码块全部配平
- go build 通过
This commit is contained in:
JianFeeeee
2026-10-31 08:50:39 +08:00
parent 4963d70215
commit a402bfd777
12 changed files with 334 additions and 2134 deletions

View File

@ -780,3 +780,60 @@ if ret, err := parent.ExecuteTool(name, args); err == nil {
**存量插件不需改一行、不需重编**(新增方法/字段由**插件调用、内核实现**,
不调就不受影响——这是 1.1.0 / 1.2.0 / 1.3.0 三次跃迁共同的结论)。
---
## 附录 A:更新前后压测结果(2026-09-27)
> 原本在执行计划文档里。计划随实施完结删除,这些**数据**有长期价值,故并入本文。
方法:两版内核(旧 `85e3d66` / 新 `d3eaff4`)在**隔离 netns** 内各起一个实例,
共用同一个 mock LLM(保证 LLM 行为完全一致,消除变量),驱动脚本
`scripts/kernel-stress/cmp.py`。规模 3 = 24 连接 × 12 输入 = 288 条。
### A.1 批内工具调用(核心差异)
| 场景 | 旧版 中位/工具数 | 新版 中位/工具数 |
|---|---|---|
| `!slowbatch2` | 0.221s / **0 个** | 0.380s / **2 个** |
| `!slowbatch4` | 0.222s / **0 个** | 0.399s / **4 个** |
| `!slowbatch8` | 0.220s / **0 个** | 0.409s / **8 个** |
★ **旧版"更快"是假的**:它的 `openai.lua` 缺 `stream_index` 透传,多个分片
并到槽 0、argsRaw 混拼 ⇒ 每个工具报「参数不是合法 JSON」而**一个都没真跑**。
这正是「只看耗时会被骗」的典型 —— 所以 `cmp.py` 每轮都记录**处理数**
并把它当作通过条件之一。
### A.2 并发 vs 强制串行(新版内部对照)
`!slowbatchN`(全部 ParallelSafe ⇒ 并发)vs `!serialbatchN`(混入
`knowledge_create` ⇒ 整批降级串行):
| N | 并发 | 强制串行 | 加速 |
|---|---|---|---|
| 2 | 0.378s | 0.532s | 1.41× |
| 4 | 0.389s | 0.864s | 2.22× |
| 8 | 0.409s | 1.585s | **3.88×** |
### A.3 关于 stage 锁告警(不是内核缺陷)
压测中出现过 5 次 `stage.lock` 告警。**不要当内核缺陷去修**。链路:
1. SDK 生成的模板在每个 stage handler 入口**自动**调
`callCoreVoid("stage.lock", nil)`(跨进程写锁,内核仲裁)
2. `lock.go` —— 锁**不可重入**:`held && owner == plugin` 即报错
3. 所以只要同一次 `before_toolcall` 被触发两次且首次未释放,就会命中
已排查并排除:QQ 的 `beforeToolcall` 函数体里只有 `ctx.Lock()`(SDK **数据**锁,
与 proc stage 锁是两把锁)与纯本地调用,**无任何再次触发 stage 的路径**。
⇒ 成因在**插件进程侧的运行时**(编译进 `plugin.bin`),不在插件业务代码里。
生产 `plugin.bin` 是独立构建产物、**不随 homed 部署** —— 要修需改 SDK 模板
并重编该二进制。
**内核这边的行为是正确的**:插件持锁失败时内核强制释放("锁仲裁回内核"
的自愈机制),目的是避免后续插件死锁;错误收进 `ctx.Errors` 而不中断流程,
所以那一轮 212 秒正常跑完。
⇒ 5 次告警全部有惊无险。**唯一风险**是:哪天自愈逻辑变动,就会变成真死锁。