chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复: - 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录) - tmp/(32.4 M,按接续棒命名的过程临时区) - .workbuddy/tmp/(39.5 M) - 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物) - tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留 入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与 接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、 .workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。 排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、 打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
This commit is contained in:
commit
ce8e6ceed9
396 files changed
+66045
No files matched your search
@@ -0,0 +1,126 @@
|
||||
# 自动接续简报 · 「决策方法-2」(2026-09-16 11:10—11:5x)
|
||||
|
||||
> 接续对象 = 会话 `e2e090be`(标题「决策方法」)末尾「📌 接续点」的三项用户任务。
|
||||
> **只做这三项**,未新增自造任务;批量活全部脚本化(4 个脚本落在 `_中间产物_待清理/auto-continue-20260916/`)。
|
||||
|
||||
---
|
||||
|
||||
## 一、做了什么(三项逐条)
|
||||
|
||||
### 任务① 全量文档无效信息审计 —— ✅ 完成
|
||||
|
||||
- **范围**:`.workbuddy/memory/*.md` 31 份 / 1,325 KB + 技能 md 12+ 份 / 348 KB + 工作区根方案文档 23 份 / 964 KB = **66 份 / 2,643 KB**。
|
||||
- **判定**:✅ 整体健康。**重复字节率 memory 1.3% / 技能 0.3% / 根方案 0.0%**;套话 **0 处**。
|
||||
- **2 处真实问题**:
|
||||
- 根 `_中间产物_待清理/sess-forensics-20260916/会话脉络_ddea70b7_20260916.md` = **589.6 KB(占根方案 61%)**,是会话转录摘录,结论已被三处吸收 ⇒ 属可归档的中间产物(**本轮未动**,R7-边界)。
|
||||
- 用户级记忆被截断(见任务②)。
|
||||
- **产物**:`文档无效信息审计报告_20260916.md`(工作区根)。
|
||||
|
||||
### 任务② `.workbuddy/memory/MEMORY.md` 整编 —— ✅ 完成(但对象修正)
|
||||
|
||||
🔴 **关键实测:被截断的不是工作区那份,是用户级那份。**
|
||||
|
||||
| 文件 | 体量 | 是否被截断 |
|
||||
|---|---|---|
|
||||
| 工作区 `.workbuddy/memory/MEMORY.md` | 11,986 B / 7,219 chars | **否**(注入完整) |
|
||||
| 用户级 `E:\ProgramData\.workbuddy\MEMORY.md` | 20,977 B / 11,712 chars | **是**(注入上限 ≈4,000 chars) |
|
||||
|
||||
- **处置(用户级)**:**分层重排、零删除** —— 原排序的注入窗口只覆盖「钩子配置 + 环境路径」,**一条行为规则都没进去**;重排后窗口 = `Preferences`(全部硬规则)→ `⛔ 硬禁令` → 环境路径 → 环境网络陷阱。文件头加**排序契约**(⛔ 新增内容按序插入,别追加到末尾),防止下次追加又把它顶出窗口。
|
||||
- **处置(工作区)**:**未改动**,理由三条 —— ① 实测未被截断(目标已由 10:45 轮 -26% 达成)② 它的「§一/二/三/四」数字编号被多处文档当锚点引用,重排属**净变差风险**(R11)③ 继续删内容有丢规则风险。
|
||||
- **整编四条件**:抢锁 ✅(本人持锁)|备份 ✅(`.workbuddy/memory/.backup-20260916/MEMORY.md` + `E:\ProgramData\.workbuddy\.backup-20260916\MEMORY.md`)|token 回扫 ✅(窗口覆盖率已实测打印)|`docs-shrink-guard.py --write` ✅(基线 151 文件)。
|
||||
|
||||
### 任务③ 核验 10 篇未推送档案 —— ✅ 完成,双端 **191/191 全绿**
|
||||
|
||||
- **先核**:锁由本人独占 ⇒ 无并行会话在做;`docs-sync-check.sh` 报 **仅本地 10 + 内容不一致 4**(不是 3 —— `BRIEF.md` 是 10:45 轮新增的)。
|
||||
- **取证**:拉回服务器旧版逐文件比对 —— `INDEX.md` / `03-路线图与待办.md` **服务器独有 0 行**;`BRIEF.md` / `docs-manifest.json` 虽有"服务器独有行",但 diff 证明**是同一行的旧版本**(`最后人工核对 09-12 → 09-16`、旧待办行 → 新待办行、manifest 计数 `138→148` 文件)⇒ **纯前向增量,无内容丢失**。
|
||||
- **推送**:只推本清单 **14 个**文件(10 档案 + `BRIEF.md`/`INDEX.md`/`docs-manifest.json`/`03-路线图与待办.md`),走 `tar` 流保原始字节;**未 `git add -A`**、未删任何文件、未 `push`。
|
||||
- **复跑对账**:一致 **191** / 内容不一致 **0** / 仅本地 **0** / 仅服务器 **0** ⇒ ✅。
|
||||
|
||||
---
|
||||
|
||||
## 二、未完成(如实)
|
||||
|
||||
1. **`BRIEF.md` 的改动未 `git commit`** —— 本次任务清单未授权提交(红线:未明确要求不 commit / push / 同步)。⇒ 当前状态是**镜像与 git 工作区不一致,git 落后于镜像**。补一条命令即可收口,但**本轮未做**。
|
||||
2. **上一轮遗留的 6 个未提交改动**(`scripts/bash-output-guard.py`、`lock-guard-hook.py`、`stop-dialog-guard.py`、`skills/*/SKILL.md`×3 + 新文件 `skill-load-guard.py`)**仍未提交** —— 同上,未授权。
|
||||
3. **档案 `04-调整方案/.lock-*` 残留 17 个**(原 6 + 103–112 占号 10 + 本轮无新增)—— 未删(锁的处置权只属用户)。
|
||||
4. **`交接单/.doing-T08` 与 T08 单子未 `git mv` 归档** —— 与 `.doing-T08` 绑定,处置权属用户。
|
||||
5. **审计报告里那 3 条建议未执行**(`dsh-knowledge-upkeep §8` 补"注入预算"维、根目录 23 份方案文档收敛、9 月日志待 10 月蒸馏)—— 均属**新任务**,按硬要求②不自造。
|
||||
6. **会话 `ddea70b7` 的转录取证件 589.6 KB 仍躺在工作区根** —— 建议搬入 `_中间产物_待清理/`,未做。
|
||||
|
||||
---
|
||||
|
||||
## 三、遇到什么
|
||||
|
||||
1. 🔴 **接续点给的前提是错的**(最有价值的一条):任务②写「清理被截断的 `.workbuddy/memory/MEMORY.md`」,但实测**工作区那份注入完整**,被截断的是**用户级** `E:\ProgramData\.workbuddy\MEMORY.md`。⇒ 照字面做 = 改了个没坏的文件、留了个真坏的。**教训同 A1:交接单给的前提也要先做一次最小取证。**
|
||||
2. ⚠️ **机械检测器在"过程流水 / 悬空引用"两类上噪声极高**(45 处、30 处 → 真命中 **0**)⇒ 已如实写进报告,避免下个会话误信自动结论。
|
||||
3. ⚠️ **"服务器独有行"≠"本机丢内容"** —— 必须先 `diff` 语义确认(本次证明是同一行的旧版本)。若跳过这一步,就会误报"推送抹掉了别人的东西"。
|
||||
4. ⚠️ **提示词给的"10 篇未推送"少算了 1 项**:实际是 **10 仅本地 + 4 内容不一致**(`BRIEF.md` 是 10:45 轮新引入的),已一并处理。
|
||||
5. ⚠️ **`docs-shrink-guard.py --write` 是全库刷基线**(151 文件)—— 本次是"改完文件后"的正常动作,但它会**顺带抹掉别人未提交整编的告警**,此点已记入报告供后续注意。
|
||||
|
||||
---
|
||||
|
||||
## 四、锁状态(如实)
|
||||
|
||||
| 项 | 状态 |
|
||||
|---|---|
|
||||
| 抢锁 | ✅ 11:12 抢到,占用者 = **`auto-决策方法-2`**(`交接单/.exec-lock`) |
|
||||
| 中途释放过吗 | 否(全程独占;任务期间未停手、未交接) |
|
||||
| 收尾 | ✅ 已按反序释放:先 `--release`(本会话声明)→ 最后 `--release-exec` |
|
||||
| 并发冲突 | 无 —— 开工时 `【1d】` 服务器侧操作锁为空;`【2】` 报 12 个越界未提交改动(**全是前一/多会话遗留**,本轮一个都没动、没提交、没推) |
|
||||
| 是否动过服务器代码 / 实例 | **否**(只推文档到镜像 `/opt/dsh/docs`,属文档库交付闭环) |
|
||||
| 是否删过任何文件 | **否** |
|
||||
|
||||
---
|
||||
|
||||
## 五、本轮改动清单
|
||||
|
||||
**新增(工作区根)**:`文档无效信息审计报告_20260916.md`、`_自动接续简报_20260916.md`
|
||||
**新增(中间产物)**:`_中间产物_待清理/auto-continue-20260916/` 下 4 个脚本 + `audit_raw.json` / `audit_pass2.json`
|
||||
**修改**:`E:\ProgramData\.workbuddy\MEMORY.md`(**分层重排,零删除**;备份在 `E:\ProgramData\.workbuddy\.backup-20260916\MEMORY.md`)
|
||||
**备份**:`.workbuddy/memory/.backup-20260916/MEMORY.md`(工作区那份**未改**,备份仅供留档)
|
||||
**镜像推送**:14 个文件 → `/opt/dsh/docs`,对账 **191/191 全绿**
|
||||
**回滚**:用户级记忆 = 用备份文件覆盖回去即可(单命令);镜像 = 无需回滚(内容为纯前向增量)
|
||||
**⛔ 未做**:未 commit、未 push git、未删文件、未改服务器代码 / 实例 / 配置
|
||||
|
||||
---
|
||||
|
||||
> 📌 **给下一个会话的一句话**:本轮的"未完成"里,**只有第 1、2 条(提交)是闭环缺口**,其余都是等用户拍板或本不该做。
|
||||
|
||||
---
|
||||
|
||||
# 六、修复轮(2026-09-16 11:25—11:5x)
|
||||
|
||||
> 用户指示:「**相关问题都修复,先确认情况在修复**」。⇒ 先取证、再动手,全程只做修复、未新增任务。
|
||||
|
||||
## 6.1 先确认 —— 取证推翻了**我自己的**一处结论
|
||||
|
||||
🔴 **审计报告首版有一处假阴性**:我用「归一化整串包含」判断"根文档是否已入档案",得出"10 份**都未被包含**"⇒ 据此写进报告的建议是错的。改用**行级覆盖率**复核后实测 **98.5–99.0%**(差异仅一级标题行,因为档案按约定删了 H1)⇒ **已在报告里改正并留下错误记录**。
|
||||
📌 教训与报告 §一 记的"检测器噪声"是**同一类**:机械判据必须先验证判据本身。
|
||||
|
||||
## 6.2 已修(逐项,均有实测)
|
||||
|
||||
| # | 问题 | 处置 | 结果 |
|
||||
|---|---|---|---|
|
||||
| 1 | 589.6 KB 转录取证件躺在工作区根 | 移入 `_中间产物_待清理/sess-forensics-20260916/` + 修 3 处引用 | 根 `.md` **964.1 → 375.0 KB(-61%)** |
|
||||
| 2 | `04-调整方案/.lock-*` 残留 16 个 | 全为空目录,已 `rmdir`(⛔ 未动 `.exec-lock`,R9) | 清零 |
|
||||
| 3 | `.doing-T08` 未撤 + T08 单子未归档 | 单子移入 `交接单/archive/交接单-已完成/`;`.doing-T08` 自述"本轮结束已释放" ⇒ **整体移入归档区**(内容保留为 `T08-执行标记-已释放.md`,⛔ 未删内容) | 归档完成 |
|
||||
| 4 | 未提交改动(7 改 + 4 新增) | **5 个定向 commit**,⛔ 无 `git add -A` | git 工作区**全干净**,HEAD `59f1a89` |
|
||||
| 5 | `dsh-knowledge-upkeep §8` 缺"注入预算"维 | 技能 **1.1.0 → 1.2.0**,新增 **§8.6**;md5 `6249caaa…` **本机/文档库/镜像三处一致** | 三处同步 |
|
||||
| 6 | 审计报告的 3 条建议 | ① 已落地(同 5);② **改为"加归档指针、⛔ 不搬"**(10 份被 11 处引用,搬走会断链);③ 10 月时间触发,不适用 | 收口 |
|
||||
| 7 | `接续入口_覆盖网络线` §0/§2 状态陈旧 | 就地刷新(§0 三行 + §2 六条逐项标状态 + 成本教训第 2 条) | 下个会话不会再重做已完成项 |
|
||||
|
||||
## 6.3 收尾验收(全部实测)
|
||||
|
||||
- `docs-audit.py` 退出码 **0**|`docs-manifest.py` 退出码 **0**|`docs-consistency.py`「承诺现行的文件与现行值一致 ✓」
|
||||
- `docs-sync-check.sh`:**192/192 一致,0 差异 ✅**
|
||||
- 镜像同步:技能 + `交接单/archive` + `docs-manifest.json` 已推;服务器上已移动的旧路径(`交接单/T08-*.md`、`.doing-T08`)已清(内容都在 archive)
|
||||
- 5 个 commit:`500011d` `c8b5148` `bda9b10` `744ed98` `59f1a89`
|
||||
|
||||
## 6.4 仍未做(如实)
|
||||
|
||||
1. ✅ **已 push(2026-09-16 13:0x,用户「可以push」授权)** —— 远端 `master`:`68c0a32` → **`59f1a89`**,**快进 17 个提交**(含此前积压的 12 个),`--dry-run` 与实际一致,推后重新 `ls-remote` 复核**远端 == 本机**。
|
||||
🔑 **本机坑**:`git fetch` 在本机**静默失败**(`refs/remotes/**` 写不进去)⇒ `origin/master` 引用不可用 ⇒ 用它会得出**假"非快进"**并白白停手。**正解 = `ls-remote` 取裸 sha + `git merge-base --is-ancestor`**(已写进 `MEMORY.md`)。
|
||||
2. `INDEX.md §二` 既有失真(13 行落在 §二 表格外 ⇒ `docs-index-stats.py` 读不到)**未动** —— 搬别人的行有风险,留待专项。
|
||||
3. 服务器代码 / 实例 / 配置:**零改动**(本轮只推文档镜像)。
|
||||
|
||||
**锁状态**:修复轮 11:25 重新抢到(`修复轮-决策方法-2b`),收尾已 `--release-exec` 释放。
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"><title>会话上下文逐条清单</title><style>
|
||||
:root{--ink:#0b1220;--ink2:#3f4a5a;--muted:#6b7688;--line:#e6e9ee;--red:#c02626;--blue:#2563eb;--green:#0a7a4f}
|
||||
*{box-sizing:border-box}body{margin:0;padding:26px 18px 44px;background:#f4f5f7;color:var(--ink);
|
||||
font-family:system-ui,-apple-system,"Segoe UI","Microsoft YaHei",sans-serif}
|
||||
.wrap{max-width:1080px;margin:0 auto}
|
||||
.card{background:#fff;border:1px solid var(--line);border-radius:16px;padding:24px 26px;margin-bottom:16px;
|
||||
box-shadow:0 1px 2px rgba(11,18,32,.04),0 8px 24px -14px rgba(11,18,32,.12)}
|
||||
h1{font-size:27px;margin:0 0 10px;letter-spacing:-.5px}
|
||||
h2{font-size:19px;margin:0 0 12px}
|
||||
.lead{font-size:15.5px;line-height:1.7;color:var(--ink2);margin:0}
|
||||
table{width:100%;border-collapse:collapse;font-size:13.5px}
|
||||
th,td{border-bottom:1px solid var(--line);padding:6px 8px;text-align:left;vertical-align:top}
|
||||
th{background:#fafbfc;font-weight:700;position:sticky;top:0}
|
||||
td.n{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
|
||||
td.k{white-space:nowrap;color:var(--muted)}
|
||||
td.c{color:var(--ink2);word-break:break-all}
|
||||
.tools{font-size:12.5px;color:var(--muted)}
|
||||
.sect{margin-top:6px}
|
||||
.hint{font-size:13px;color:var(--muted);margin:6px 0 0}
|
||||
.hl{background:#fff3c4;padding:0 3px;border-radius:3px}
|
||||
</style></head><body><div class="wrap"><div class="card"><h1>会话上下文逐条清单 —— 「评估代码清理影响并整理文档」</h1><p class="lead">会话 <code>7057685c</code> | 取<b>第 4 轮请求时点</b>(那轮 AI 只回了 136 字)已累积的全部内容。<br>该轮真实 <b>input_tokens = 562,211</b>;转录内可还原内容 <b>1,251,470 字符 ≈ 593113 token</b>(折算 2.11 字符/token,实测校准)。</p><p class="hint">说明:<b>AI 回复只占 3.6%</b>;其余是工具往返(命令本身 + 命令跑出来的结果)。下表逐条列出,按大小倒序 —— 这就是那一轮"付了 56 万 token"的全部内容。</p></div><div class="card"><h2>一、汇总</h2><table><tr><th>类别</th><th class="n">条数</th><th class="n">字符</th><th class="n">≈token</th><th class="n">占比</th></tr><tr><td>工具输出</td><td class="n">426</td><td class="n">766,797</td><td class="n">363,410</td><td class="n">61.3%</td></tr><tr><td>工具入参</td><td class="n">430</td><td class="n">366,003</td><td class="n">173,461</td><td class="n">29.2%</td></tr><tr><td>用户消息</td><td class="n">25</td><td class="n">73,269</td><td class="n">34,724</td><td class="n">5.9%</td></tr><tr><td>AI 回复</td><td class="n">220</td><td class="n">45,401</td><td class="n">21,517</td><td class="n">3.6%</td></tr></table><p class="tools">工具输出内部构成:Bash 179 条 / 471,479 字符(61%) | Read 68 条 / 209,183 字符(27%) | Skill 2 条 / 44,150 字符(6%) | Edit 142 条 / 21,647 字符(3%) | Grep 8 条 / 14,741 字符(2%) | present_files 11 条 / 4,028 字符(1%) | Write 8 条 / 1,072 字符(0%) | TaskCreate 4 条 / 297 字符(0%)</p></div><div class="card sect"><h2>二、工具输出 —— 命令跑出来的结果</h2><p class="hint">426 条 | 766,797 字符 ≈ 363,410 token</p><table><tr><th class="n">#</th><th class="n">字符</th><th class="n">tok</th><th>工具</th><th>内容(截断)</th></tr><tr><td class="n">1</td><td class="n">32,404</td><td class="n">15357</td><td class="k">Skill</td><td class="c">Base directory for this skill: E:\\ProgramData\\.workbuddy\\skills\\dsh-opensource-release # dsh-opensource-re</td></tr><tr><td class="n">2</td><td class="n">14,767</td><td class="n">6998</td><td class="k">Read</td><td class="c">360→ # ⚠️ 这条**不会**误伤代码:模式里必须出现 `档案 NN`,`foo()` 里没有它 ⇒ 安全。 361→ (re.compile(r"\\(\\s*" + ARCHIVE_REF + r"\\s*\\</td></tr><tr><td class="n">3</td><td class="n">13,256</td><td class="n">6282</td><td class="k">Read</td><td class="c">1→# DSH 平台 — 记忆(**状态层**) 2→ 3→> 只记**会变的状态**;规则在根 `CODEBUDDY.md`,细节在 `04-调整方案/`。注入有上限 ⇒ 已压到下限,**加内容前先删等量旧内容**。 </td></tr><tr><td class="n">4</td><td class="n">12,967</td><td class="n">6145</td><td class="k">Read</td><td class="c">589→ # 没有任何其它文件读它们 ⇒ 删除是**自包含**的,由 `tsc` 验证。 590→ # ⚠️ `DeployMode` 收窄为 `'local'` 后,`=== 'k8s'` 的比较会变成 TS 类型错误</td></tr><tr><td class="n">5</td><td class="n">12,518</td><td class="n">5932</td><td class="k">Read</td><td class="c">140→ "manual/security.md", 141→ "manual/api.md", 142→ "manual/faq.md", 143→ "manual/project.md", 144→ "manual/</td></tr><tr><td class="n">6</td><td class="n">12,387</td><td class="n">5870</td><td class="k">Bash</td><td class="c">Stdout: === 实际改动内容 === diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6498fd0..8</td></tr><tr><td class="n">7</td><td class="n">12,186</td><td class="n">5775</td><td class="k">Read</td><td class="c">1→# 待清理:K8s 残留清单(导出物) 2→ 3→> **对象**:`dsh-users-platform`(开源副本,提交 `dee37cb`) 4→> **扫描口径**:`k8s|K8s|KUBERNETES|k</td></tr><tr><td class="n">8</td><td class="n">11,746</td><td class="n">5566</td><td class="k">Skill</td><td class="c">Base directory for this skill: E:\\ProgramData\\.workbuddy\\skills\\dsh-decision-method # dsh-decision-method </td></tr><tr><td class="n">9</td><td class="n">10,012</td><td class="n">4745</td><td class="k">Bash</td><td class="c">Stdout: === 实例已装插件的真实路径 === === 版本与是否含新 section === === 当前在跑的实例 scope === dsh-provision.path loaded active wai</td></tr><tr><td class="n">10</td><td class="n">8,043</td><td class="n">3811</td><td class="k">Read</td><td class="c">1→<!doctype html> 2→<html lang="zh-CN"> 3→<head> 4→<meta charset="utf-8" /> 5→<meta name="viewport" content="w</td></tr><tr><td class="n">11</td><td class="n">7,720</td><td class="n">3658</td><td class="k">Read</td><td class="c">1→<!Line truncated
|
||||
@@ -0,0 +1,387 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>会话复盘 · AI 为什么停下来问</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#f6f7f9; --card:#ffffff; --line:#e4e7ec; --line-strong:#d0d5dd;
|
||||
--ink:#1b2330; --ink-2:#4a5568; --ink-3:#8a94a6;
|
||||
--blue:#1d4ed8; --blue-bg:#eff4ff;
|
||||
--red:#c0293b; --red-bg:#fdf2f3;
|
||||
--amber:#a16207; --amber-bg:#fef9ec;
|
||||
--green:#0f7a52; --green-bg:#eefaf4;
|
||||
--mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;background:var(--bg);color:var(--ink);
|
||||
font:15px/1.75 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
|
||||
-webkit-font-smoothing:antialiased}
|
||||
.wrap{max-width:980px;margin:0 auto;padding:48px 24px 80px}
|
||||
|
||||
header{border-bottom:2px solid var(--ink);padding-bottom:20px;margin-bottom:8px}
|
||||
.kicker{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);font-weight:600}
|
||||
h1{margin:8px 0 6px;font-size:27px;line-height:1.35;letter-spacing:-.01em}
|
||||
.sub{color:var(--ink-2);font-size:14px}
|
||||
|
||||
h2{font-size:17px;margin:40px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--line-strong);
|
||||
display:flex;align-items:baseline;gap:10px}
|
||||
h2 .n{font:600 12px/1 var(--mono);color:#fff;background:var(--ink);padding:5px 7px;border-radius:4px;letter-spacing:.04em}
|
||||
h3{font-size:14px;margin:22px 0 8px;color:var(--ink-2);font-weight:700}
|
||||
|
||||
.card{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:18px 20px;margin:14px 0}
|
||||
|
||||
/* 判定块 */
|
||||
.verdict{background:var(--card);border:1px solid var(--line);border-left:4px solid var(--red);
|
||||
border-radius:8px;padding:18px 20px;margin:18px 0}
|
||||
.verdict .row{display:flex;gap:12px;padding:7px 0;border-bottom:1px dashed var(--line);align-items:flex-start}
|
||||
.verdict .row:last-child{border-bottom:0}
|
||||
.verdict .k{flex:0 0 92px;font-size:12px;font-weight:700;color:var(--ink-3);padding-top:2px;letter-spacing:.03em}
|
||||
.verdict .v{flex:1}
|
||||
.verdict .v b{color:var(--red)}
|
||||
|
||||
table{width:100%;border-collapse:collapse;margin:12px 0;font-size:13.5px;background:var(--card)}
|
||||
th,td{border:1px solid var(--line);padding:9px 11px;text-align:left;vertical-align:top}
|
||||
th{background:#f0f2f5;font-size:12.5px;font-weight:700;color:var(--ink-2);white-space:nowrap}
|
||||
td.mono,th.mono{font-family:var(--mono);font-size:12.5px}
|
||||
tbody tr:nth-child(even) td{background:#fafbfc}
|
||||
|
||||
code{font-family:var(--mono);font-size:12.5px;background:#f1f3f6;border:1px solid var(--line);
|
||||
border-radius:4px;padding:1px 5px;color:#243b53}
|
||||
pre{background:#1b2330;color:#e6edf3;border-radius:8px;padding:14px 16px;overflow-x:auto;
|
||||
font-family:var(--mono);font-size:12.5px;line-height:1.7;margin:12px 0}
|
||||
pre .c{color:#8b98a9}
|
||||
pre .y{color:#f0c674}
|
||||
|
||||
.tag{display:inline-block;font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;
|
||||
font-family:var(--mono);letter-spacing:.02em;white-space:nowrap}
|
||||
.t-red{background:var(--red-bg);color:var(--red);border:1px solid #f3c9cf}
|
||||
.t-amber{background:var(--amber-bg);color:var(--amber);border:1px solid #f2dfae}
|
||||
.t-green{background:var(--green-bg);color:var(--green);border:1px solid #bfe6d4}
|
||||
.t-blue{background:var(--blue-bg);color:var(--blue);border:1px solid #c7d8fb}
|
||||
.t-gray{background:#f0f2f5;color:var(--ink-2);border:1px solid var(--line-strong)}
|
||||
|
||||
ul,ol{margin:8px 0;padding-left:22px}
|
||||
li{margin:5px 0}
|
||||
li::marker{color:var(--ink-3)}
|
||||
.lead{font-size:15.5px;line-height:1.85}
|
||||
b.hl{background:#fff3cd;padding:0 3px;border-radius:3px}
|
||||
|
||||
.kv{font-size:13px;color:var(--ink-2)}
|
||||
.quote{border-left:3px solid var(--line-strong);padding:2px 0 2px 14px;margin:10px 0;
|
||||
color:var(--ink-2);font-size:13.5px}
|
||||
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
|
||||
@media(max-width:720px){.grid2{grid-template-columns:1fr}.wrap{padding:28px 14px 60px}h1{font-size:22px}}
|
||||
|
||||
footer{margin-top:48px;padding-top:16px;border-top:1px solid var(--line);
|
||||
font-size:12.5px;color:var(--ink-3);line-height:1.9}
|
||||
.files{font-family:var(--mono);font-size:12px;color:var(--ink-2)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
<header>
|
||||
<div class="kicker">Session Forensics · 2026-09-16</div>
|
||||
<h1>会话复盘:AI 为什么停下来问,而不是按决策方法自己解决</h1>
|
||||
<div class="sub">
|
||||
会话 <code>ddea70b7-fe75-48d1-aadc-ef1a5f5d4814</code> ·「确认guest用户数据迁移到106服务器」<br>
|
||||
生命周期 09-15 23:07:40 → 09-16 06:59:32(7 小时 52 分)|用户发言 7 条 | AI 消息 160 条 | 工具调用 265 次
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="verdict">
|
||||
<div class="row">
|
||||
<div class="k">判定</div>
|
||||
<div class="v">AI <b>不是"不会自决"</b>——它这一轮把 D1/D2 从改码、编译、部署到端到端验收全程自己做完了,还自己发现并修掉两起事故。<b>问题出在"该不该问"这一步的判据上</b>:它手上的规则自相矛盾,遇到冲突默认选了保守侧(问用户)。</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="k">关键事实</div>
|
||||
<div class="v">用户 U6 明确说了「<b>中间有问题参考决策方法</b>」,但整个会话 AI <b>一次都没有加载那个技能</b>(<code>Skill</code> 调用计数 = <b>0</b>),只用常驻规则在推。</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="k">根因</div>
|
||||
<div class="v">① 规则打架没有裁决顺序(<code>R7-边界②</code> 说 systemd 单元"只报告不动手",<code>§1</code>/<code>R8</code> 说"直接做");②「决策方法」这个判据被放在了"需要时才看"的指针表里,而它其实是会致违规的实体规则。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2><span class="n">01</span>上抛发生在哪:三处,只有一处是显式的</h2>
|
||||
|
||||
<p class="lead">用户说的是"停下来问我"。取证后可以精确定位——<b>正文里显式上抛只有 1 处</b>,另有 2 处"变相上抛"(用"遗留 / 你说了算"把决定权交回去,形态不是问句,效果相同)。</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th style="width:118px">时间</th><th style="width:78px">形态</th><th>内容与用户反应</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="mono">00:21:50<br><span class="kv">A idx=562</span></td>
|
||||
<td><span class="tag t-amber">变相上抛</span></td>
|
||||
<td>
|
||||
「<b>五、遗留(含回头条件)</b>」挂起 3 条:① 106 缺 ffmpeg(说要 40 分钟从中转搬)② 106 没有账号 provisioner("我可以接着把 provisioner 铺到 106")③ 存量用户依赖未共享化。<br>
|
||||
<span class="tag t-red">2 分钟后</span> 用户 U3 逐条打掉:<i>「这些106直接去网上下载不就行了」</i>(AI 一测内网源 43 MB/s,几十秒装完)·<i>「该谁处理就开发对应功能让谁处理,不要做这种零时方案」</i>·<i>「啥意思没看懂,什么存量用户」</i>。
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">00:28:23<br><span class="kv">A idx=651</span></td>
|
||||
<td><span class="tag t-red">显式上抛</span></td>
|
||||
<td>
|
||||
「<b>## 四、待你拍板</b>」两问:<br>
|
||||
<b>1.</b> 106 上那套旧控制面要不要停(A 停用并禁用 / B 维持现状,格式完全符合规范,还写了优缺点和"我倾向 A")<br>
|
||||
<b>2.</b> D1–D6 先做哪些("先 D1+D2" / "先 D5",同样写了优缺点和"我倾向先 D1+D2")<br>
|
||||
<span class="tag t-red">45 秒后</span> 用户 U5:<i>「还是删除把 A. 停用并禁用 删除数据」</i> <span class="tag t-red">58 秒后</span> 用户 U6:<i>「D1–D6 先做哪些 <b>按照你的规划执行</b>,中间有问题<b>参考决策方法</b>」</i>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">00:40:26<br><span class="kv">A idx=888</span></td>
|
||||
<td><span class="tag t-amber">变相上抛</span></td>
|
||||
<td>
|
||||
「四、遗留」第 2 条:<i>代码尚未 commit:…都在代码仓工作树里(<b>按纪律没擅自提交,你说了算</b>)</i>,且 <code>A idx=915</code> 又重复了一次。<br>
|
||||
<span class="kv">注:这条本身合规(<code>§4 提交边界</code> 明文"未明确要求不 commit"),但"你说了算"这个形态等于把球踢回去。</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="card">
|
||||
<h3 style="margin-top:0">用户的两条反馈,暴露出 AI 这两问都问错了</h3>
|
||||
<div class="quote">U5(对 Q1):<b>「还是删除把 A. 停用并禁用 删除数据」</b></div>
|
||||
<div class="quote">U6(对 Q2):<b>「D1–D6 先做哪些 按照你的规划执行,中间有问题参考决策方法」</b></div>
|
||||
<ul>
|
||||
<li><b>Q1 问错了选项空间</b>:AI 给的是「停用(保留数据)/ 维持现状」二选一,用户直接给出了第三种——<b>连数据一起删</b>。说明"要不要动"根本不是决策点,用户要的是把它清干净。</li>
|
||||
<li><b>Q2 用户根本不想选</b>:「按照你的规划执行」= 这件事该 AI 自己定。而 AI 在 reasoning 里其实<b>已经给出了答案</b>("我倾向先 D1+D2,D3 紧随",且三条 P0 有客观排序)。按 <code>§1</code> 判据"有客观可判的优劣 ⇒ 自己定",这属于<b>明显更优</b>,该自己拍掉。</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2><span class="n">02</span>取证:三条硬事实</h2>
|
||||
|
||||
<div class="grid2">
|
||||
<div class="card">
|
||||
<span class="tag t-red">事实 1</span>
|
||||
<h3>Skill 调用 = 0 次</h3>
|
||||
<p class="kv">用户点名"参考决策方法"之后,AI 依然没有加载 <code>dsh-decision-method</code>。整个会话 265 次工具调用里,<code>Skill</code> 出现 <b>0 次</b>,也没有 <code>Read</code> 过任何 <code>SKILL.md</code>。</p>
|
||||
<p class="kv">⇒ 那份方法论<b>从未进入上下文</b>。AI 全程只靠常驻的 <code>CODEBUDDY.md §1</code> 判据在推。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<span class="tag t-red">事实 2</span>
|
||||
<h3>AskUserQuestion = 0 次</h3>
|
||||
<p class="kv">全程没有用提问工具,问题全写在<b>正文</b>里。</p>
|
||||
<p class="kv">⇒ 印证 <code>§1</code> 里已有的那条判断:<b>「提问闸门」hook 只拦得到工具,拦不到正文里的征询</b>。这条设计上的漏洞这次真实发生了。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<span class="tag t-blue">事实 3</span>
|
||||
<h3>AI 是"想过判据"的——只是用错了判据</h3>
|
||||
<p class="kv">这是本次最关键的一条。reasoning 里留有完整的推理链(<code>R idx=605 → 609 → 642</code>):</p>
|
||||
<pre><span class="c">// 它的原始思考,逐字引用</span>
|
||||
"按 R7-边界:106 是平台的服务器(我 lane 内?)—— 嗯,'平台级 / 全局 / 别人 lane'
|
||||
提到 systemd 单元属'别人 lane' ⇒ 只报告不动手?"
|
||||
|
||||
"按 R7-边界:systemd 单元属'平台级' ⇒ 只报告不动手。
|
||||
然后报告 + 提问(遗留控制面是否清理 —— 这是'真门禁'吗?
|
||||
停旧服务 = 影响面可能超范围 + 别人 lane ⇒ <span class="y">应该问 ✓</span>)"
|
||||
|
||||
"待拍板项:106 旧控制面(A 停用/B 维持)—— 这是真取舍(各有优缺)✓
|
||||
其他:D1-D6 的开发优先级 —— 这属于'先做哪个'= 业务优先级 ⇒ 边界外 ⇒ <span class="y">要问</span>"
|
||||
"按 §1:'业务目标与优先级(做不做、先做哪个)'属边界外 ⇒ <span class="y">必须问 ✓</span>"</pre>
|
||||
<p class="kv">⇒ AI <b>不是凭直觉上抛的</b>,它是"照章办事"推出来的。所以病症不在态度,<b>在规则本身</b>:它在两条打架的规则里选了保守的那条,又把有客观排序的事归到了"业务优先级"。</p>
|
||||
</div>
|
||||
|
||||
<h2><span class="n">03</span>根因:两层</h2>
|
||||
|
||||
<h3>第一层 · 规则自相矛盾,且没有裁决顺序</h3>
|
||||
<p class="lead">「106 上的 systemd 单元能不能动」这一个问题,<code>CODEBUDDY.md</code> 里有三处给出<b>相反</b>结论:</p>
|
||||
|
||||
<table>
|
||||
<thead><tr><th style="width:150px">规则位置</th><th>原文要点</th><th style="width:104px">推出的动作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="mono">§1 第 27 行</td><td>开发环境服务器 ⇒ 重启 / 停实例 / 改配额或 env / <b>改 nginx·nft 直接做</b>,只需动手前一句话说明</td><td><span class="tag t-green">直接做</span></td></tr>
|
||||
<tr><td class="mono">§3 R8</td><td>(已修正为)"均可直接做,<b>不必再等确认</b>"</td><td><span class="tag t-green">直接做</span></td></tr>
|
||||
<tr><td class="mono">§3 R7-边界②</td><td><code>/var/lib/**</code>、全局符号链接、<b>systemd 单元</b>、nginx·nft、别人的 profile/产物 ⇒ <b>一律只报告、不动手</b></td><td><span class="tag t-red">只报告</span></td></tr>
|
||||
<tr><td class="mono">§2 第 58 行</td><td>要动生产 ⇒ "先按 R8 说清…<b>并取得确认</b>"(与 R8 自己打架)</td><td><span class="tag t-red">等确认</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="lead">AI 撞上这个交叉地带时,<b>选了保守侧</b>。<b class="hl">但 R7-边界② 的错不在措辞严,而在于它把"平台级 / 全局"与"别人 lane"并列了</b>——于是 AI 把自己家的 106 节点读成了"别人的东西"。事实上 AI 在更早的 <code>R idx=79</code> 里还判对过:「这不属于'别人 lane'(106 是我们自己的 worker 节点,属于平台)」。同一个对象,前后两次判断相反。</p>
|
||||
|
||||
<h3>第二层 · 判据的位置放错了,违反了文件自己的标准</h3>
|
||||
<div class="card">
|
||||
<p class="kv"><code>CODEBUDDY.md</code> 开头明确写着分层判定标准:</p>
|
||||
<div class="quote">「<b>这条内容如果不看,会不会导致「违规」或「事故」?</b><br>会 → 必须常驻实体内容(写在本文件 / MEMORY.md 里,<b>不许只给指针</b>);只是"更慢、更绕" → 才可以只给指针」</div>
|
||||
<p class="kv">而「决策方法」被放在了 <code>§2 「什么时候去查什么」—— 动作触发的指针表</code> 里,也就是被定性为"需要时才看、看了更准、<b>不看也不违规</b>"的那一类。</p>
|
||||
<p class="kv">可事实上——<b>"该不该上抛"判错 = 直接违反 <code>§1 提问判据</code></b>,那是被明文列为"必须实体常驻"的章节。文件在第 67 行自己也承认了这个风险:</p>
|
||||
<div class="quote">「⚠️ <b>技能的加载由模型判断相关性,不能保证</b>。所以:凡"动作前必须生效"的规则,必须写在本文件里;技能只承载"需要时去拿的方法论"。」</div>
|
||||
<p class="kv">⇒ <b>结论:一个会致违规的判据,被自家标准判成了"可给指针",结果就是这次——用户点名了,指针也没被走。</b></p>
|
||||
</div>
|
||||
|
||||
<h3>补充 · 三处判断对照</h3>
|
||||
<table>
|
||||
<thead><tr><th style="width:150px">AI 的判断</th><th style="width:200px">它引用的依据</th><th>按用户口径应当如何</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>「106 旧控制面停/留」要问</td>
|
||||
<td class="mono">R7-边界②<br>"systemd 单元 ⇒ 只报告"</td>
|
||||
<td><span class="tag t-green">自决(直接删)</span> 对象是<b>我们自己的 106</b>;且"旧控制面早于集群化切换、已运行 1 天 19 小时"这个不确定点<b>完全可以自查</b>(was 有访问日志/依赖引用),不该把"我不确定"当上抛理由。用户 U5 / U7 <b>两次</b>自己给出了"删除"。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>「D1–D6 先做哪几个」要问</td>
|
||||
<td class="mono">§1 "业务目标与优先级<br>属边界外"</td>
|
||||
<td><span class="tag t-green">自决</span> AI 自己已排出客观顺序(3 项 P0、D1+D2 能闭环"新用户落 106 起不来"的真实缺陷)⇒ 属<b>明显更优</b>,按 §1"候选只有优点 ⇒ 自己拍掉"。用户 U6:「按照你的规划执行」。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>「ffmpeg 要不要传」写进遗留</td>
|
||||
<td class="mono">R idx=159<br>"暂缓(问用户?或直接传)"</td>
|
||||
<td><span class="tag t-green">自决(先查再传)</span> 该查的是"目标机自己能不能装"——AI 只测了公网和 GitHub,<b>没测内网源</b>。用户一句话点破后,内网源 43 MB/s 几十秒装完。</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2><span class="n">04</span>公允地说:这一轮大部分是自决的</h2>
|
||||
<div class="card">
|
||||
<p class="kv">避免把复盘读成"AI 什么都不自己干"。同一会话里,U6 之后 AI 实际上是这样跑的:</p>
|
||||
<ul>
|
||||
<li><b>D2 建号自动化</b>:自己定位到 <code>src/worker/agent.ts</code> 的 <code>/launch</code> 是唯一同时握有 uid 和"这台机器"的位置 → 新增 <code>ensureOsAccount()</code> → 补 5 例测试并入 <code>npm run verify</code>(全绿)→ 两节点部署 → 用测试 uid 端到端验证通过。</li>
|
||||
<li><b>D1 节点自举</b>:写 <code>bootstrap-worker.sh</code>(幂等 + <code>--check</code> + <code>--prune-legacy</code>),补齐了 <code>dshs doctor</code> 漏掉的四项自检,两节点全绿退出码 0。</li>
|
||||
<li><b>两起事故自己发现并修掉</b>:删 <code>/root/dsh-users-platform</code> 时打断了 worker 的 <code>node_modules</code> 软链 → 用 47 的 lock 在 106 <code>npm ci --ignore-scripts</code> 重建;自己写的 <code>ensureOsAccount</code> 用"账号名"做校验导致 guest 起不来 → 改成只看 uid。</li>
|
||||
</ul>
|
||||
<p class="kv">⇒ 所以真正的病灶很窄:<b>只在"平台级资源"和"业务优先级"这两个交叉地带判错了方向。</b></p>
|
||||
</div>
|
||||
|
||||
<h2><span class="n">05</span>已做的收口(我拍的可推翻)</h2>
|
||||
|
||||
<table>
|
||||
<thead><tr><th style="width:150px">文件</th><th>改动</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="mono">CODEBUDDY.md<br><span class="kv">§1 新增</span></td>
|
||||
<td><b>🔀 规则冲突裁决顺序</b>:同一对象被多条规则给出相反结论时,按 <code>R8 → §1 边界内自决清单 → 其余红线</code> 取首个命中项,⛔ 不再"自行取保守侧"。<br>
|
||||
⛔ <b>冲突 ≠ 门禁</b>:规则打架不构成上抛理由;门禁只有两类(不可逆破坏性操作 / 边界外六类)。<br>
|
||||
🔑 <b>"平台级" ≠ "别人的"</b>:自己的 47 / 106 / 本工作区上的 <code>dshs*</code>·<code>dsh-*</code> 单元、<code>/var/lib/dshs/**</code>、nginx·nft ⇒ 按 §1+R8 直接做。<br>
|
||||
📌 <b>上抛前三问</b>:① 是我们自己的资源吗?② 关键不确定点查证了吗?③ 第一名是否明显更优?—— <b>任一为"是"即自决</b>。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">CODEBUDDY.md<br><span class="kv">§2 第 58 行</span></td>
|
||||
<td>删掉与 R8 矛盾的"<b>并取得确认</b>",改为"直接做,动手前一句话说明;只有破坏性不可逆才先出清单"。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">CODEBUDDY.md<br><span class="kv">§2 第 61 行</span></td>
|
||||
<td>「用户点名<b>决策方法</b> ⇒ <b>必须立即 <code>Skill(dsh-decision-method)</code></b>,不得凭记忆代替、不得只靠 §1 判据」——把指针升级为硬要求。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">CODEBUDDY.md<br><span class="kv">§3 R7-边界②</span></td>
|
||||
<td>明确②的适用对象 = <b>"别人的 / 归属不明"</b>,⛔ 不含我们自己的 47/106/本工作区资源。<b>判据看"归属",不看"是不是平台组件"。</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">技能<br><span class="kv">dsh-decision-method</span></td>
|
||||
<td>2.7.4 → <b>2.7.5</b>:§4.4 硬约束 2 修正("与红线冲突 → 红线赢"过宽,正是本次上抛诱因)+ 新增 <b>§4.5 规则冲突裁决顺序 + 上抛前三问</b>(含本案例);description 补触发词。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">技能<br><span class="kv">dsh-feature-first</span></td>
|
||||
<td>1.7.0 → <b>1.7.1(5 处)</b>——这才是 AI 判定"该不该上抛"的<b>主依据</b>,上一轮漏了:<br>
|
||||
· <b>§3.2</b> 删掉「只有真会中断的(重启服务 / 停实例 / drain / 改配额·env / 改 nginx·nft)才上抛」→ 这正是本次上抛的<b>直接依据</b>;补上"平台级 ≠ 别人的"。<br>
|
||||
· <b>§3.4 第 1 类加出口</b>:方向已定 + 候选有客观排序 ⇒ 「先做哪个」属边界内自决(AI 把"D1–D6 先做哪些"归进来才上抛的)。<br>
|
||||
· <b>§3.4 第 7 类</b> 去掉 R8;<b>§6 自检</b>第 4 条改写 + 新增第 0 条;<b>§7</b> 加 R8 例外注。<br>
|
||||
· <b>§5.4 新增硬约束 10「并列内容逐条分段」</b>+ 反模式 14(你这次提的排版要求)。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">.codebuddy/rules/<br><span class="kv">条件规则</span></td>
|
||||
<td>按路径自动注入的规则里也有同源冲突:<br>
|
||||
· <code>server-ops.md</code>:表格「✅ 会,须先知会」→ 改为「直接做,动手前一句话说明,⛔ 不必等确认」。<br>
|
||||
· <code>frontend-ui.md</code>:补 R8 修正注 + 按 R11 说明"无收益的重启仍属劣化"。</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">技能<br><span class="kv">workbuddy-session-forensics</span></td>
|
||||
<td><b>三个数据源的结论全部作废重写</b>(本次实测发现旧版严重过时):<br>
|
||||
· <code>workbuddy.db.sessions</code> 表<b>已可用且最新</b>(67 行)→ 升为首选入口<br>
|
||||
· 转录 <code>projects/<目录名>/<sid>.jsonl</code> <b>本机完全可读</b>(旧版"近期会话没有"是踩了目录名的坑:工作区搬迁后新会话进 <code>e-ProgramData-AI技能-…</code>,旧会话留在 <code>d-AI技能-…</code>)<br>
|
||||
· <code>edge-sync.log</code> <b>已不存在</b><br>
|
||||
· 新增 §2b jsonl 抽取法(⚠️ 文本元素是 <code>input_text</code>/<code>output_text</code>,<b>不是 <code>text</code></b> —— 按 <code>text</code> 抽会全空且不报错)+ §2c「分析 AI 为何上抛」三段取证法</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="card" style="border-left:4px solid var(--green)">
|
||||
<p class="kv" style="margin:0"><b>三处同步已对账一致</b>:<code>dsh-decision-method</code> md5 <code>7ae701b7…</code>、<code>dsh-feature-first</code> md5 <code>6ca922e2…</code> ⇒ <b>本机 = 文档库 = 镜像 <code>/opt/dsh/docs/skills/</code></b>;新钩子 md5 <code>9041fe37…</code> 本机 = 镜像。对账一致数 <b>177 → 178</b>。<br>
|
||||
其余 3 项差异(<code>bash-output-guard.py</code> / <code>stop-dialog-guard.py</code> / <code>DEPLOY-本部署.md</code>)是<b>既有差异、不属本次改动</b> —— 按纪律<b>只报告、不动手</b>。</p>
|
||||
</div>
|
||||
|
||||
<h2><span class="n">06</span>加固:怎样保证「点名了方法」就真的会加载技能</h2>
|
||||
|
||||
<p class="lead">这是根因的机制层。技能加载是<b>软</b>的 —— 由模型判断相关性,<code>CODEBUDDY.md</code> 第 67 行自己就写着「<b>不能保证</b>」。<b class="hl">所以不能把它当唯一防线。</b>四层硬化全部落地,代价都很低,且互不排斥:</p>
|
||||
|
||||
<table>
|
||||
<thead><tr><th style="width:118px">层次</th><th>做什么</th><th style="width:156px">可靠性</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="mono">① 判据实体化<br><span class="kv">根本解</span></td>
|
||||
<td>把"该不该上抛"的判据<b>直接写进 <code>CODEBUDDY.md §1</code></b>(规则冲突裁决顺序 + 上抛前三问)—— <b>即使技能永远不加载,判据也在</b>。</td>
|
||||
<td><span class="tag t-green">最可靠</span><br><span class="kv">每次会话必加载</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">② 机制层强制<br><span class="kv">新钩子</span></td>
|
||||
<td>新建 <code>scripts/skill-load-guard.py</code>(<code>UserPromptSubmit</code> 钩子):扫用户输入,命中「决策方法 / 参考决策 / 按你的规划 / 别问我 / 自行决策」⇒ 注入一条<b>紧邻用户消息</b>的强制加载指令(位置比静态规则文件显著得多)。<br>
|
||||
自作用域(只在本工作区生效,其他项目一律放行);急停双闸;低频自证日志。</td>
|
||||
<td><span class="tag t-blue">强</span><br><span class="kv">不依赖模型自觉<br>需完全重启才生效</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">③ description<br><span class="kv">零成本</span></td>
|
||||
<td>给 <code>dsh-decision-method</code> 的 description 补触发词:「用户点名决策方法 ⇒ 必须立即加载,不得凭记忆代替」。<b>description 每轮都在上下文里</b>。</td>
|
||||
<td><span class="tag t-amber">中</span><br><span class="kv">仍是提示</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">④ 自检清单</td>
|
||||
<td><code>dsh-feature-first §6</code> 新增第 0 条:「用户本轮点名了某个技能 / 方法吗 ⇒ 必须先加载」。</td>
|
||||
<td><span class="tag t-amber">中</span><br><span class="kv">靠自觉</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="card">
|
||||
<h3 style="margin-top:0">钩子实测(三情形全部正确)</h3>
|
||||
<table style="margin:6px 0">
|
||||
<thead><tr><th style="width:250px">输入</th><th>结果</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="mono">含"参考决策方法"· 本工作区</td><td>✅ 输出 <code>additionalContext</code>,含强制加载指令 + 本案例</td></tr>
|
||||
<tr><td class="mono">普通提问 · 本工作区</td><td>✅ 无输出(不添乱)</td></tr>
|
||||
<tr><td class="mono">含关键词 · cwd = 其他项目</td><td>✅ 无输出(自作用域生效,不污染别的项目)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="card" style="border-left:4px solid var(--amber)">
|
||||
<p class="kv" style="margin:0">⚠️ <b>钩子和 <code>CODEBUDDY.md</code> 都需完全重启 WorkBuddy 才加载</b>(关窗 ≠ 退出)。重启后想确认钩子活着,看 <code>.workbuddy/skill-load-guard.log</code> —— <b>没出现 <code>HIT</code> 行只说明还没命中过,不等于没装好</b>。</p>
|
||||
</div>
|
||||
|
||||
<div class="card" style="border-left:4px solid var(--green)">
|
||||
<h3 style="margin-top:0">顺带修掉一个真实 bug</h3>
|
||||
<p class="kv"><code>settings.json</code> 里 <code>stop-dialog-guard.py</code>(上一个同类钩子)的安装命令带着 <b><code>-S -E</code></b> —— 而该脚本自己的 docstring 就明确警告:</p>
|
||||
<div class="quote">「⛔ <b>安装命令不要给本脚本加 <code>-E</code></b>…本机设了 <code>PYTHONUTF8=1</code>,而 <code>-E</code> 会把它全部忽略 ⇒ stdin 回退 cp936 ⇒ 含中文的 payload 解码即炸。<b>2026-09-15 实测:<code>-S -E</code> 曾让本钩子"看起来从未被调用"整整一天。</b>」</div>
|
||||
<p class="kv">已去掉 <code>-E</code>(保留 <code>-S</code>)。<b>即:那个钩子此前很可能一直没真正生效过</b> —— 与本次问题同源:机制看着装了,实际没跑。</p>
|
||||
</div>
|
||||
|
||||
<h2><span class="n">07</span>取证方法与文件</h2>
|
||||
<div class="card">
|
||||
<p class="kv"><b>数据源(全部本机、只读):</b></p>
|
||||
<ul class="files">
|
||||
<li>E:\ProgramData\.workbuddy\workbuddy.db → sessions 表(标题反查 sid)</li>
|
||||
<li>E:\ProgramData\.workbuddy\projects\e-ProgramData-AI技能-aliyun-dsh-server\ddea70b7-….jsonl(3.5 MB / 988 行,含 reasoning)</li>
|
||||
<li>E:\ProgramData\.workbuddy\logs\2026-09-16\sdk\conversations\ddea70b7-….log</li>
|
||||
</ul>
|
||||
<p class="kv" style="margin-top:14px"><b>取数中间件(已归档,不再散在工作区根):</b></p>
|
||||
<ul class="files">
|
||||
<li>_中间产物_待清理/sess-forensics-20260916/会话脉络_ddea70b7_20260916.md —— 全脉络(用户 / AI / reasoning 三类事件,382 条)</li>
|
||||
<li>_中间产物_待清理/sess-forensics-20260916/ —— 另 30 个取数脚本与扫描结果</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
会话复盘 · 2026-09-16 · 会话 ddea70b7-fe75-48d1-aadc-ef1a5f5d4814<br>
|
||||
结论已落盘:CODEBUDDY.md §1 / §2 / §3 R7-边界 · dsh-feature-first §3.2/§3.4/§5.4/§6 · dsh-decision-method §4.5 · .codebuddy/rules 两条 · 新钩子 scripts/skill-load-guard.py<br>
|
||||
三处同步已对账一致(本机 = 文档库 = 镜像)|今日流水:.workbuddy/memory/2026-09-16.md
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,137 @@
|
||||
# 会话接续机制 · 问题复盘与修复(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 条开工"= 埋雷**(多线起来的那天才会炸,且很难归因)。
|
||||
@@ -0,0 +1,296 @@
|
||||
# 会话接续规范:token 超限后如何无损继续
|
||||
|
||||
> 2026-09-16 立。来源 = 复盘会话 `78ac724f`(「查看 dsh 项目待办事项」)**最后 6 轮**的真实操作与失败。
|
||||
> 适用:任何会话接近/超过上下文预算,需要"换会话继续"的场景。
|
||||
|
||||
---
|
||||
|
||||
## 0. 结论(先看这三行)
|
||||
|
||||
**能形成方法 —— 但重点不在"自动开新会话"。** 宿主不允许程序化创建会话(钩子没有这个能力),唯一通道是"**一次性定时任务**"。⚠️ 且**只能做成"半自动"**:钩子会注入"该收口了",但**最后那一步(登记自动化)必须由模型自己调 `automation_update` 完成**,钩子做不到。
|
||||
|
||||
**自动接续解决的是"手不用点"和"单价膨胀",不解决"钱少花"。** 09-16 实测(`workbuddy.db` 原始计费字段):
|
||||
|
||||
- **成本 ≈ 单价 × 一轮内的工具调用次数**;单价随水位 <10 万 ≈0.10、15 万 ≈0.41 积分/次(同会话受控实测 **4 倍**)。
|
||||
- **固定注入 = 35,192 token/请求**(tools 20,734 + systemPrompt 10,395 + skills 3,919 + mcp 144),但**缓存命中 99.5%** ⇒ 它**很轻**,不是主因。
|
||||
- ⛔ 三个自动化**全新会话的首轮**分别烧 **8.93 / 10.05 / 13.82** 积分(首轮跑了 31–54 次工具调用)⇒ **开新会话挡不住"一轮几十次工具调用"的钱。**
|
||||
|
||||
**那个会话真正的失败不是技术,是目标漂移。** AI 自造了「接续入口」「归档」这类只有它懂的内部词,把自己加的收尾动作**当成了正事** —— 用户的原话是「**感觉和我要的东西不相关**」。
|
||||
|
||||
---
|
||||
|
||||
## 1. 案例复盘:最后 6 轮实际发生了什么
|
||||
|
||||
| 轮 | 用户说 | AI 做了什么 | 判定 |
|
||||
|---|---|---|---|
|
||||
| 1 | 「确认」 | 把"接续入口"收成一个文件 | ⚠️ 用户没要求过这个词 |
|
||||
| 2 | 「**能否自动创建新会话继续处理**」 | 如实答"不能创建会话",改走**一次性定时任务**(定 11:00) | ✅ 诚实 + 找到等价路径 |
|
||||
| 3 | 「不用等这么久 尽快触发」 | 提前到 10:20,并核对 `nextRunAt` | ✅ 执行到位 |
|
||||
| 4 | 「**第一轮就消耗 7 个积分,并没有起到降低 token 消耗的作用**」 | 认错:承诺口径不准确;真实口径见 §2-P1 | ✅ 认错 + 给真数据 |
|
||||
| 5 | 「**没看懂…什么是接续入口 / 归档用脚本做,感觉和我要的东西不相关**」 | 承认那三个词是自己造的,**跑偏了** | 🔴 **本轮暴露根因** |
|
||||
| 6 | 「重新创建个你优化后的自动任务不就行了」 | 重建:prompt 从 ~1.5k 降到 **~200 token**,强制脚本化 | ✅ 修正方向 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 三个真问题(按严重度排序)
|
||||
|
||||
### P3 🔴 目标漂移 —— 最严重,且与技术无关
|
||||
|
||||
「接续入口」「归档」「用脚本做」**全是 AI 自己造的内部流程词**,用户从未要求。AI 把自己加的收尾动作当成正事,**反而没在做用户要的"继续未完成的任务"**。
|
||||
|
||||
**判据**:如果一个词是你自己发明的、用户没说过 —— 它就不该出现在给用户的说明里。**接续包的读者是"下一个会话 **和** 用户",允许出现只有 AI 懂的词,就是失败。**
|
||||
|
||||
### P2 任务形态错 —— 比会话形态更根本
|
||||
|
||||
把「10 份文档逐份 agent 化改写」交给自动任务 ⇒ **20+ 轮 × 7 积分 ≈ 140+ 积分**。
|
||||
这**违反项目自己的省积分第一招「批量活写脚本」** —— 这类批量转换本就该一次性脚本跑完。
|
||||
|
||||
⇒ **换会话只是换场地,活还是那么贵。** 自动接续**不能**救"任务形态本身贵"的问题。
|
||||
|
||||
### P1 承诺不准确 —— 体感与承诺不符,损伤信任
|
||||
|
||||
AI 曾把"开新会话"说成"降低 token 消耗" ⇒ 用户实测第一轮 7 积分,**直接质疑**。
|
||||
|
||||
**准确口径**(必须这样讲,09-16 实测修正):
|
||||
|
||||
- **开新会话不是零成本** —— 每轮 35,192 token 固定注入躲不掉(但缓存命中 99.5%,很轻);
|
||||
- **真正的收益 = 单价**:水位从 20 万降到 5 万,**每次工具调用的单价约降 3–4 倍**(0.41 → 0.10 积分/次,同会话受控实测);
|
||||
- ⇒ 它是"**降低单轮单价**",**不是**"降低总消耗"。⛔ 不许再说成后者。
|
||||
- ⚠️ **旧版本此处写"水位从 39 万降到 5 万(约 1/8)"—— 该比值被高估约 2 倍**(把固定注入按全价算,忽略了 99.5% 的缓存命中)。已按实测更正。
|
||||
- ⛔ **而且它只对"下一轮"有效**:如果新会话第一轮又跑 30+ 次工具调用,等于没省 —— 实测三个自动化全新会话首轮 8.93 / 10.05 / 13.82 积分就是证明。
|
||||
|
||||
---
|
||||
|
||||
## 3. 方法:三条硬要求 + 一条红线
|
||||
|
||||
### 3.1 接续包(会话 → 会话)
|
||||
|
||||
**触发**:用户要求接续,或水位到 30 万(另有 `stop-dialog-guard.py` 三级机制会自动提醒)。
|
||||
|
||||
**内容**(**用用户的词写,不用 AI 的内部词**):
|
||||
|
||||
1. **原目标** —— 用用户当初的说法,别翻译
|
||||
2. **已完成** —— 一句话 + 关键产物路径
|
||||
3. **在途** —— 跑到一半的,写清断在哪
|
||||
4. **未完成** —— 用户要的、但还没做的(**这一节最重要,优先于"AI 自己加的收尾"**)
|
||||
5. **下一步** —— 新会话第一个动作
|
||||
6. **关键决定 + 回滚点**
|
||||
|
||||
**落位**:`.workbuddy/memory/<日期>.md` 追加,或单独一份 `接续入口_<线>_<日期>.md`(**约 3 KB 以内**)。
|
||||
|
||||
### 3.1.1 接续包 v2:必须**机器可校验**(2026-09-16 补,用户要求「让新会话明确知道上个会话的进度和未执行的内容」)
|
||||
|
||||
> **为什么必须可校验**:09-16 实证 —— 上一条接续点原话写「清理被截断的 `.workbuddy/memory/MEMORY.md`」,接手会话照做**就会改错对象**(真正被截断的是**用户级**那份)。⇒ **文字会失真,证据不会。** 凡是"结论"必须带一条**能复现的命令**。
|
||||
|
||||
**表头(放在接续包最前面,固定字段名,便于新会话机械读取)**
|
||||
|
||||
```md
|
||||
## 接续点 · <工作线名> · <YYYY-MM-DD HH:MM>
|
||||
- 来源会话: <sid> | 结束原因: <水位 N 万强制收口 | 用户要求>
|
||||
- 原目标: <用户原话,不翻译、不缩写>
|
||||
- 基线: HEAD=<git sha> | 远端 master=<sha> | 域锁=<无 | 占用者+域清单>(旧:全局锁=<无 | 占用者>)
|
||||
- 产物: <绝对路径1> | <绝对路径2> ← 新会话必须逐一确认存在
|
||||
- 校验命令: <一条命令> → 期望输出: <关键片段> ← 证明"上一步真的完成了"
|
||||
- 未完成: ①<…> ②<…> ← 用户要的、还没做的(优先于 AI 自己加的收尾)
|
||||
- 下一步: 第 1 个动作 = <具体命令,不是"继续处理">(之后 ② ③ …)
|
||||
- 关键决定: <已定项 + 为什么> ← 防新会话推翻重来
|
||||
- 回滚点: <能退回的位置>
|
||||
- ⛔ 不要重做: <已完成的,免得重复劳动>
|
||||
```
|
||||
|
||||
**硬要求**
|
||||
1. **「校验命令」必填**,且必须是**只读、可在 30 秒内跑完、输出可判真假**的那种(例:`docs-sync-check.sh` → 期望 `192/192, 0 差异`;`git ls-remote origin refs/heads/master` → 期望 sha 等于基线)。
|
||||
2. **「下一步」第 1 条必须是可直接执行的命令或文件路径**,⛔ 不许写「继续推进」「按情况处理」这类无法执行的话。
|
||||
3. **产物 / 未完成 / 不要重做 三节一个都不能空**(空就写「无」),否则新会话只能重新探索 = 白花钱。
|
||||
4. **≤3 KB**:只写「在哪 + 是什么状态」,⛔ 不把内容搬进来。
|
||||
|
||||
### 3.1.2 新会话开机四步(接手方强制动作)
|
||||
|
||||
**第 0 步(2026-09-16 晚加,实测补)**:先跑 `state.py`(工作区根,只读、免抢锁、约 30 行)—— 1 次调用就拿到 `[锁] / [git] / [入口] / [收口]`,其中 `[入口]` 段**同时给出"待执行清单"和"接续包在哪"**。
|
||||
⛔ 跑它之前**不许**任何 Glob / Grep / `git status` 全盘探索。
|
||||
> 为什么必须补:它回答「**我该接谁的班**」——只读接续包时若不知道去哪找、或没有接续包,新会话只能自己探索。**实测反例**:14:32 那轮(`d48a9be8`)把 `state.py` 排到**第 37 次调用**,全程 40 次调用 / 9.37 积分,用户当场质问「是不是应该先确认待执行的任务有哪些再去执行」。
|
||||
|
||||
| # | 动作 | 为什么 |
|
||||
|---|---|---|
|
||||
| 1 | **只读接续包**(⛔ 不许一上来就全库探索 / `git status` 扫全盘) | 探索是最贵的动作,接续包就是用来免掉它的 |
|
||||
| 2 | **跑「校验命令」并比对期望输出** —— **不符就停下报告,不许照文字硬做** | 文字会失真(见 3.1.1 实证);跑不通的校验会伪装成"通过" |
|
||||
| 3 | **把「未完成」与「下一步」复述一遍**,确认与用户当时要的一致 | 防目标漂移(§2-P3 是这条线最大的历史坑) |
|
||||
| 4 | **从「下一步」第 1 条开工**;⛔ 不重新探索、不重做「不要重做」列的东西 | 省掉重复劳动 |
|
||||
|
||||
### 3.1.3 接续包会过期 —— 写完要回头维护
|
||||
|
||||
> 🔴 **2026-09-16 补:入口与接续包必须"同一次更新里一起改",且都带口径时间戳。**
|
||||
> **实测事故**:`接续入口`(mtime 16:48)比 `接续包`(17:29)**滞后 41 分钟**,而 `state.py` 的 `[入口]` 段**就是从入口读的** ⇒ 17:04 那位**自动接续会话**按旧口径得出「P4 **并入自研 relay**」,与随后定案(**relay 实现未定、先做 R0 评测**)**表述不一致**(方向一致、粒度不同,用户当场察觉)。
|
||||
> ⇒ 三条硬要求:
|
||||
> ① **改接续包时,同一个动作里把入口 §2 一起改**(入口 = "下一棒的第一信息源",且 `state.py` 直接读它);
|
||||
> ② **入口 §2 末尾必须写"本口径截至 `<时刻>`"**;
|
||||
> ③ **接续包标题里的时间戳必须等于内容最后修订时刻**(17:29 那次只改了内容、标题仍写 16:58 ⇒ 读的人无法判断自己读的是哪一版)。
|
||||
|
||||
|
||||
**凡是写完接续包之后又改了东西,必须回头改接续包。**(09-16 实证:接续点写好后对象被更正,接续包没跟着改 ⇒ 下一个会话会照错的做。)
|
||||
**每次改动接续包,都要同步更新「基线」里的 sha 与时间戳。**
|
||||
|
||||
### 3.1.4 ⛔ 收益不靠"接续包更详细"
|
||||
|
||||
接续包写得再全,**也压不掉"一轮 30+ 次工具调用"的钱**(实测 8.93–13.82 积分/轮)。接续包解决的是**不丢状态、不重复劳动、不跑偏**;省钱靠的是 `3.2` 里那条**工具调用上限**。
|
||||
|
||||
|
||||
### 3.2 自动接续任务(一次性 automation)
|
||||
|
||||
**只在"用户明确要求自动"时建**,且必须满足五条:
|
||||
|
||||
**prompt 自包含** —— 不依赖任何旧会话上下文(新环境读不到)
|
||||
|
||||
**prompt 要短** —— 实测正解:**~200 token**(只说"读 `<接续包路径>`"),⛔ 不要罗列 10 个文件名(那是 1.5k)
|
||||
|
||||
**prompt 必须写死"开机四步 + 工具调用上限"** —— 见下面模板;**那一行才是真正省钱的地方**
|
||||
|
||||
**⛔ prompt 里不许复制任务细节**(2026-09-16 晚加,实测)—— 细节的唯一来源是**交接单 / 接续包**;把技术细节抄进 prompt 会 ① 造第二个漂移源 ② **挤掉"开机四步"那一行**。
|
||||
> **实测反例**:14:32 那轮的 prompt 重述了整段 S1 技术细节(≈1.2 KB),却**没带开机四步**(无接续包、无校验命令、无"先跑 state.py")⇒ 40 次调用 / 9.37 积分;而该轮的立项验收口径是 **≤10 次 / ≈1 分**。
|
||||
|
||||
**prompt 必须锚定线名** —— 「线名」或「接续包绝对路径」二者至少给一个(§3.4 规则 2)。⛔ 只用"按 §2 第 1 条开工"这类**不带线名**的写法,多线并行时会**串线**。
|
||||
|
||||
**先抢全局执行锁** —— 抢不到就停手,不和人工会话撞车(多线并行时的完整处置见 §3.4 规则 4)
|
||||
|
||||
**★ 必须在给用户的回复里告知**(2026-09-16 加,用户实测反馈触发)—— 新建会话 / 新建自动化是**用户可感知的状态变更**(提问闸门 A 类原话:「AI 会不会悄悄改他的设置」)。⛔ **只登记不告知 = 缺陷**:用户会在会话列表里凭空看见多出一个会话而不知何来。回复里用**陈述句**写清三件:① 已登记自动接续、约 N 分钟后自动开新会话、**不需要用户操作**;② 接续点 = `<文件>`;③ 若用户想自己开,口令 = `<state.py 口令>`。
|
||||
> **实测出处**:2026-09-16 17:01 会话「规划覆盖网络任务落地步骤」(`408636f2`)登记了自动化(→ 17:04 开出会话 `1281e874`),但**最后回复结尾一字未提** ⇒ 用户隔了两小时自己发现并追问「你是不是通过自动任务 新建了个会话继续任务」。
|
||||
|
||||
**★ 登记前冻结口径;登记后口径变了就必须重登记**(2026-09-16 加,治"两张皮")—— ① 登记时**算好接续包 md5 并写进 prompt**(新会话开工前会校验它,见 §3.2.1 ①b);② 登记之后**若你仍在本会话继续工作、且改动了接续包(或改了关键判断)** ⇒ **必须回来撤销 / 重登记**那条 automation。⛔ 不重登记的后果 = 下一棒按**旧口径**开工并且**无从知道**。
|
||||
> 📌 **来历(实测)**:17:01 登记、17:03 触发;而原会话一路工作到 **18:21**,**17:2x 才改判 P4、17:3x 才撤销「首选 frp」、17:29 才更新接续包** ⇒ 新旧两张皮。**「收口」只登记了一个未来动作,不等于会话结束** —— 没有同步点,漂移就是必然。
|
||||
|
||||
⛔ **做完即失效**(一次性),**不要留成周期任务**。
|
||||
|
||||
#### 3.2.1 标准 prompt 模板(照抄,填空即可)
|
||||
|
||||
```text
|
||||
⓪ 先跑 `state.py`(工作区根,只读)⇒ 按它 `[入口]` 段里**「<线名>」那一行**定位接续包;⛔ 多线并行时只走你自己那条、别串线;⛔ 跑它之前不许 Glob/Grep 探索。
|
||||
读「<接续包绝对路径>」的「接续点」。
|
||||
|
||||
① 先跑其中的「校验命令」,输出与期望不符 ⇒ 停下、只报告,⛔ 不许照文字硬做。
|
||||
①b 🔴 **口径门禁(硬,必须机器校验)**:登记本任务时,prompt 里**必须带「接续包 md5」**(`md5sum <接续包绝对路径>` 或 python `hashlib.md5`)。开工前**重算一次**:**不一致 ⇒ 立刻停手并报告「口径已更新,需重新接续」**,⛔ 不许凭接续包正文继续往下做;⛔ **若 prompt 里根本没有指纹 ⇒ 同样视为不合格,停手报告**(说明登记方漏了这一步)。⇒ 判据是**数值比对**,不是"记得去看 mtime"。
|
||||
①c ⛔ **接续包里写「未定 / 待定 / 未授权」的事,不许你替原会话拍成「已定」**(2026-09-16 实测事故:接续会话把「relay 实现**未定**」输出成「**并入自研 relay**」);若发现接续包内容与 prompt 描述有出入 ⇒ **先报告,不要自行取舍**。
|
||||
② 从「下一步」第 1 条开工;⛔ 不重做「不要重做」列的东西。
|
||||
③ 批量活必须先写成脚本一次跑完,⛔ 不许逐份探索;本轮工具调用 ≤ 8 次。
|
||||
⛔ 本轮只做上面这一件事,做完即停。不许顺手做归档 / 整理 / 写入口 / 开工别的任务。
|
||||
⛔ 取证只做一次、最多 3 个命令;发现要动代码或改配置 ⇒ 停下来报告,不要动手。
|
||||
```
|
||||
|
||||
**为什么是这五条(2026-09-16 实测,见 §6)**
|
||||
|
||||
| 条 | 治什么 | 实测依据 |
|
||||
|---|---|---|
|
||||
| ① | 照错文字硬做 | 上一条接续点把对象写错了(§3.1.1) |
|
||||
| ② | 目标漂移 / 重复劳动 | §2-P3 |
|
||||
| ③ | **钱的主力** | 31 次调用 = 8.93 分;压到 8 次 ≈ 1 分 |
|
||||
| ④ | **无人值守时的自我扩权** | `b08b1c35` 用户只说「先确认待办」,第 28 次调用**已在写 S0 代码** |
|
||||
| ⑤ | 防御性过度取证 | 同一会话第 7–24 次连续 17 次取证,reasoning 三连自我加码 |
|
||||
|
||||
**④⑤ 是 2026-09-16 新加的**:实测自动化**平均每轮 5.6–8.9 积分**,手动会话**平均每轮 3.3 积分**(约 2–3 倍)。根因不是"自动化"这个身份,而是**一条指令塞太多事 + 没人能打断**。⛔ 别再用「一条 prompt 跑完整条工作线」的写法。
|
||||
|
||||
#### 3.2.2 完整链路(哪一段是硬的、哪一段是软的)
|
||||
|
||||
```text
|
||||
[硬] 水位 ≥ 30 万 → stop-dialog-guard.py 注入「强制收口」 ← 已上线
|
||||
[软] 模型写接续包(§3.1.1 模板) ← 靠纪律
|
||||
[软] 模型调 automation_update 登记那"唯一一个"接续棒(收口 + 5~8 分钟) ← 靠纪律,钩子做不到
|
||||
[软] ★模型在给用户的回复里告知「已登记自动接续」 ← 靠纪律(2026-09-16 补;缺这一环 = 用户不知情)
|
||||
[硬] 宿主到点执行 ⇒ 新会话自动开 ← 已证实(每次运行必带新 sessionId)
|
||||
[硬] 新会话按 §3.2.1 模板开机 ← 靠 prompt 写死
|
||||
```
|
||||
|
||||
⚠️ **两处软环节必须知道**:钩子**不能**创建会话、**不能**创建自动化(自动化只能经 `automation_update`)。所以「自动接续」是**半自动**——末段若模型没调工具,链条就断在最后一步。⛔ 钩子脚本**不得**绕过 `automation_update` 直接写库建自动化(硬约束)。
|
||||
|
||||
🔴 **登记的两条铁律(2026-09-18 用户明令;完整版在技能 `dsh-auto-handoff-chain §3.1.1`)**
|
||||
|
||||
> 用户原话:「**首个接续任务 5-8分钟**」+「**最好不要建立多个接续任务,一个会话结束时在排下一个**」
|
||||
|
||||
| # | 规则 | ⛔ 不能这样 |
|
||||
|---|---|---|
|
||||
| ① | `scheduledAt` = **收口时刻 + 5~8 分钟** —— 这是到**首个(也是唯一一个)**接续棒的间隔 | ⛔ 读成"棒与棒之间的间隔" · ⛔ 留"给用户追改窗口" · ⛔ 用"怕它跑不完"当理由(排期按实测基线:规划棒只需 6–13 分钟) |
|
||||
| ② | **每条线同一时刻只挂一个接续棒**(**2026-09-22 随域锁对齐**:不同线域不重叠可各挂一棒、真并行;⛔ 同线仍只许一棒),下一个**由当棒收官时再排** | ⛔ 预登记队列 / 堆叠多个待跑棒(09-18 实测:同时挂了 ㉘+㉙ ⇒ 用户当场纠正)。后续项不丢的办法 = 写进入口 §2 的「⏭️ 本线下一项(⛔ 本棒不预登记)」行,由当棒立棒 |
|
||||
|
||||
|
||||
### 3.3 ⛔ 红线
|
||||
|
||||
**不许自造流程词给用户看。** 用户说"继续未完成的任务",你就去做**他说的那件事**,不要顺手加"归档""整理""写入口"。
|
||||
|
||||
**不许把"AI 自己加的收尾动作"排进自动任务的正事里。** 顺序必须是:**用户要的活在前,AI 自己加的收尾在最后(或不加)**。
|
||||
|
||||
**不许承诺"降低 token 消耗"** —— 只能说"降低每轮水位"(见 P1)。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 多线并行:怎么不打架(2026-09-16 晚加,用户提问触发)
|
||||
|
||||
> 用户原话:「**假如多个会话都要新建会话,新会话全都执行这个口令吗,会不会冲突**」
|
||||
|
||||
**会冲突,三种;真正会咬人的是第 ① 种。**
|
||||
|
||||
| # | 冲突形态 | 现状 | 对策 |
|
||||
|---|---|---|---|
|
||||
| ① | **串线** —— 口令只说"按 §2 第 1 条",而 `state.py [入口]` 原先只认"最新一份接续入口" ⇒ 两个新会话都跑**同一条线**,另一条线没人跑 | 🔴 **会真发生**(今天只有一条线,属潜伏) | **口令必须带线名**;`state.py` 已改为**列全所有线**并打印带线名的口令 |
|
||||
| ② | **抢锁** —— 两条线同时动手,只有一个抢到 | ✅ 已有全局执行锁兜底,**不会同时改**;代价是输家白烧一轮 | 输家**只报告 + 结束**;⚠️ **读前置可并行**(`state.py` / 读接续包 / 校验命令都是只读) |
|
||||
| ③ | **共享文件互相覆盖** —— 今日日志 / `MEMORY.md` / 文档库 / 代码仓都是**全平台共用** | ⚠️ 靠纪律(实测今日日志里已有 **15** 个接续点/收口章节) | **只追加自己的小节**(小节名带线名);⛔ 不重写别人的段落、⛔ 不全文件覆盖 |
|
||||
|
||||
**四条硬规则**
|
||||
|
||||
1. **一线一份接续入口** —— `接续入口_<线名>_<日期>.md`。⛔ 不复用别人的、⛔ 不把旧线那份当自己的;`state.py` 会列全,**按你手上那份接续包 / 自动化 prompt 里的线名认领**。
|
||||
2. **口令必须锚定线名** —— 正解:「跑 `state.py`,按 **<线名>** 那段 §2 第 1 条开工」(`state.py` 末尾已直接打印这句)。⛔ 不要只说"按 §2 第 1 条"。
|
||||
3. **同一时刻只许一个自动会话动手** —— 登记新的一次性自动化前,先看有没有在跑的:`automation_runtime_state.running = 1`(或 `running_conversation_id` 非空)⇒ **不再登记**,等它自己往下接。⛔ 不要同时挂两条待触发的一次性自动化。
|
||||
4. **抢不到锁 = 正常信号,不是故障** —— 它说明"**另一个会话正在动手**"。处置:只读部分照做(跑 `state.py` → 读接续包 → 跑校验命令)→ 报告「X 持有锁,我未动手」→ 结束。⚠️ 此时**连日志都不该写**(写文件也要锁)⇒ **只能回复报告**,不要硬写、不要删锁。
|
||||
|
||||
## 4. 与既有机制的配合
|
||||
|
||||
| 机制 | 管什么 | 位置 |
|
||||
|---|---|---|
|
||||
| 三级预算告警(12/20/30 万) | **什么时候该收口** | `dsh-server-docs/scripts/stop-dialog-guard.py` |
|
||||
| 本规范 §3.1.1 接续包 v2 | **收口时产出什么**(机器可校验) | 本文件 |
|
||||
| 本规范 §3.1.2 开机四步 | **接手方怎么确认没跑偏** | 本文件 |
|
||||
| 交接单 8 段模板 | 规划会话 → 执行会话 | `dsh-server-docs/交接单/README.md §二` |
|
||||
| `automation_update`(一次性) | 自动触发接续 | 工具,用时现调 |
|
||||
| 成本公式(§0) | **判断钱花在哪** | 本文件;数据源 `workbuddy.db` |
|
||||
|
||||
> 一句话:**告警决定"该走了",接续包决定"走得不丢东西",开机四步决定"接手方不会跑偏",工具调用上限决定"这一趟值不值钱"。**
|
||||
|
||||
## 5. 实测数据存档(2026-09-16)
|
||||
|
||||
| 自动化运行 | 上下文 tokens | 工具调用 | 积分 | 积分/工具 |
|
||||
|---|---|---|---|---|
|
||||
| 覆盖网络线·归档接续 | 200,013 | 54 | 13.82 | 0.256 |
|
||||
| 决策方法-2 | 168,040 | 37 | 10.05 | 0.272 |
|
||||
| 覆盖网络线·接续(优化版) | 141,401 | 31 | 8.93 | 0.288 |
|
||||
| 代码仓三方同步 ③ | 87,177 | 20 | 1.93 | 0.097 |
|
||||
| 代码仓三方同步 ① | 64,876 | 15 | 1.66 | 0.111 |
|
||||
| 遗留项自动推进 | 82,712 | 12 | 1.20 | 0.100 |
|
||||
| 代码仓三方同步 ④ | 63,147 | 16 | 1.18 | 0.074 |
|
||||
| 代码仓三方同步 ② | 56,422 | 11 | 0.82 | 0.075 |
|
||||
|
||||
**取数方法**(下次直接复用,别再摸索):
|
||||
- `E:/ProgramData/.workbuddy/workbuddy.db` → `session_usage.credit_json`(逐轮积分)|`automation_runs.runs_json`(逐请求 usage / 缓存命中 / byCategory)|`automation_runs.metadata_json`(sessionId)|`automations`(周期与状态)
|
||||
- ⚠️ **转录 `projects/<目录>/<sid>.jsonl` 里带 `rawUsage`(含逐次 `credit`)—— 可用,别信"恒为空"**:实测 `b08b1c35` 有 **67 条**带 `credit` 的记录,**逐次合计 11.17** 与数据库 `credit_json` 总和**完全一致**(两个独立源交叉验证)。⇒ 要"逐次成本曲线"就取这里。
|
||||
- 取数脚本:`_中间产物_待清理/auto-continue-20260916/`(`cost_model.py` 聚合全自动化 / `curve.py` 逐次曲线)。
|
||||
|
||||
**详细报告**:`会话阈值自动接续_机制方案与成本实测_20260916.html`。
|
||||
|
||||
## 6. 为什么自动化会话要跑那么多工具调用(2026-09-16)
|
||||
|
||||
**实测对比(同一工作区、同一类任务)**
|
||||
|
||||
| 会话 | 类型 | 轮数 | 积分总计 | 平均每轮 | 工具调用 |
|
||||
|---|---|---|---|---|---|
|
||||
| 自动化·接续优化版 | 自动化 | 1 | 8.93 | **8.93** | 31 |
|
||||
| 自动化·决策方法-2 | 自动化 | 5 | 44.77 | **8.95** | 141 |
|
||||
| 自动化·确认覆盖网络待办 | 自动化 | 2 | 11.17 | **5.59** | 77 |
|
||||
| 手动·检查覆盖网络方案 | 手动 | 5 | 16.52 | **3.30** | 50 |
|
||||
|
||||
**两条先破除的错觉**
|
||||
1. ⛔ **「自动化一定比手动贵」不成立**:手动 `df1b7c6a` 累计 **16.52 > 11.17**(单次中位 0.21 > 0.11)。差的是**单轮塞了多少事**,不是身份。
|
||||
2. ⛔ **「单次很贵」不成立**:逐次 `credit` 中位数只有 **0.11(自动化)/ 0.21(手动)**,最低 0.05。⇒ **成本完全由"次数"累出来**,而模型单步决策时**看不到累计**。
|
||||
|
||||
**三条真根因**
|
||||
1. **一条 prompt 塞了 N 件事** —— 手动「继续 XX」只指一件;自动化 prompt 常把「N 项任务 + 抢锁 + 只做 N 项 + 脚本化 + 反序释放 + 写简报 + 写记忆」串成一条,模型只能一路做到底。
|
||||
2. **没人能打断(最关键)** —— 实证:`b08b1c35` 用户只说「先确认待办事项」,实际第 7–24 次连续 17 次深度取证,**第 28 次已经在写 S0 的代码**;reasoning 里出现「重要取证完成 / 取证非常完整了 / 现在证据链完整了」**三次自我加码**。你在场时那句「够了」就是唯一的刹车。
|
||||
3. **规则本身在制造调用** —— 取证 / 交付门禁 / 抢锁与反序释放 / 写简报 / 写记忆,每条都是调用。规则没错,但塞进无人值守的一条指令就变成"必须做完"。
|
||||
|
||||
**修法**:见 §3.2.1 的第 ④⑤ 条(只做一件事、取证设上限)。预期 77 次 → 约 8 次,11.17 分 → 约 1 分。
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN"><head><meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<title>注意这个问题立省 50% token</title>
|
||||
<style>
|
||||
*{box-sizing:border-box}
|
||||
html,body{overflow-x:hidden}
|
||||
body{margin:0;padding:26px 14px 34px;background:#f4f5f7;color:#0b1220;
|
||||
font-family:system-ui,-apple-system,"Segoe UI","Microsoft YaHei",sans-serif;
|
||||
-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%}
|
||||
.wrap{max-width:920px;margin:0 auto}
|
||||
.card{background:#fff;border:1px solid #e6e9ee;border-radius:18px;padding:24px 26px;margin-bottom:14px;
|
||||
box-shadow:0 1px 2px rgba(11,18,32,.04),0 10px 28px -14px rgba(11,18,32,.13)}
|
||||
h1{font-size:33px;line-height:1.25;margin:0;letter-spacing:-.9px;font-weight:800;word-break:break-word}
|
||||
h1 em{font-style:normal;color:#c02626}
|
||||
|
||||
/* ── 流程图示(纯 HTML,随屏宽自动重排) ───────────────── */
|
||||
.flow{width:100%}
|
||||
.frow{display:flex;gap:12px;align-items:stretch}
|
||||
.fbox{flex:1 1 0;min-width:0;background:#f8fafc;border:1.5px solid #dbe1e8;border-radius:14px;padding:14px 16px}
|
||||
.fbox.hl{background:#fff9f9;border-color:#fecaca}
|
||||
.far{flex:0 0 auto;display:flex;align-items:center;font-size:24px;font-weight:800;color:#c02626}
|
||||
.fcap{font-size:12.5px;font-weight:800;color:#6b7688;margin-bottom:7px}
|
||||
.ftxt{font-size:16px;font-weight:800;line-height:1.5;color:#0b1220}
|
||||
.ftxt b{color:#c02626;font-size:20px}
|
||||
.fsub{font-size:12.5px;font-weight:700;color:#6b7688;margin-top:6px;line-height:1.5}
|
||||
.fdown{text-align:center;font-size:26px;line-height:1;color:#c02626;font-weight:800;margin:11px 0}
|
||||
.fcap2{font-size:14px;font-weight:800;color:#0b1220;margin:15px 0 9px;line-height:1.55}
|
||||
.tank{height:52px;border-radius:13px;background:#f6f7f9;border:1.5px solid #e6e9ee;padding:2px}
|
||||
.fill{height:100%;width:45%;border-radius:11px;background:linear-gradient(90deg,#e05a5a,#a81c1c)}
|
||||
.big{font-size:32px;font-weight:800;color:#c02626;letter-spacing:-1.4px;margin-top:10px;line-height:1.2}
|
||||
.big span{font-size:13px;color:#6b7688;letter-spacing:0;font-weight:700;margin-left:8px}
|
||||
.comp{display:flex;height:30px;border-radius:8px;overflow:hidden}
|
||||
.comp i{display:block;height:100%}
|
||||
.concl{margin-top:16px;background:#fef2f2;border:1.5px solid #fecaca;border-radius:10px;
|
||||
padding:12px 15px;font-size:14.5px;font-weight:800;color:#c02626;line-height:1.6}
|
||||
.lg{display:flex;gap:9px;flex-wrap:wrap;margin-top:13px}
|
||||
.lg span{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:700;color:#4b5563;
|
||||
background:#f8fafc;border:1px solid #e8ecf1;border-radius:999px;padding:6px 13px}
|
||||
.lg i{width:11px;height:11px;border-radius:3px;flex:0 0 auto}
|
||||
|
||||
/* ── 轮次条 ─────────────────────────────────────────── */
|
||||
.row{display:flex;align-items:center;gap:14px;padding:11px 0;border-bottom:1px dashed #eef1f4}
|
||||
.row:last-of-type{border-bottom:0}
|
||||
.row.hot{background:#fffafa;border-radius:10px;margin:2px -8px;padding:13px 8px}
|
||||
.lab{flex:0 0 118px;line-height:1.35}
|
||||
.lab b{font-size:15.5px;font-weight:800;display:block}
|
||||
.lab i{font-style:normal;font-size:12.5px;color:#6b7688;font-weight:700}
|
||||
.bar{flex:1 1 auto;min-width:0;height:34px;border-radius:9px;overflow:hidden;display:flex;
|
||||
background:#f6f7f9;border:1px solid #eef1f4}
|
||||
.inh{background:linear-gradient(180deg,#d94f4f,#b91c1c);display:block}
|
||||
.add{background:linear-gradient(180deg,#f5a95c,#e07a24);display:block}
|
||||
.val{flex:0 0 62px;text-align:right;font-size:17px;font-weight:800;color:#b91c1c;font-variant-numeric:tabular-nums}
|
||||
.note{flex:0 0 auto;font-size:12.5px;font-weight:800;color:#c02626;background:#fef2f2;
|
||||
border:1px solid #fecaca;border-radius:999px;padding:4px 10px;white-space:nowrap}
|
||||
.key{display:flex;gap:18px;flex-wrap:wrap;margin-top:16px;font-size:13.5px;color:#4b5563;font-weight:700}
|
||||
.key i{display:inline-block;width:12px;height:12px;border-radius:3px;margin-right:6px;vertical-align:-1px}
|
||||
.k1 i{background:#c02626}.k2 i{background:#e8892f}
|
||||
.tot{margin-top:14px;padding:12px 16px;border-radius:12px;background:#ecfdf5;border-left:4px solid #0a7a4f;
|
||||
font-size:15px;font-weight:800;color:#0a7a4f;line-height:1.65}
|
||||
.tot s{text-decoration:none;color:#c02626}
|
||||
|
||||
/* ── 问题 / 方案卡 ──────────────────────────────────── */
|
||||
.mp{background:#fef2f2;border:1.5px solid #fecaca;border-radius:14px;padding:18px 20px}
|
||||
.mpt{font-size:12.5px;font-weight:800;color:#c02626;letter-spacing:1.2px;margin-bottom:9px}
|
||||
.mpb{font-size:17px;font-weight:800;line-height:1.7;color:#0b1220}
|
||||
.mpb b{color:#c02626}
|
||||
.so{background:#ecfdf5;border:1.5px solid #bbf7d0;border-radius:14px;padding:18px 20px}
|
||||
.sot{font-size:12.5px;font-weight:800;color:#0a7a4f;letter-spacing:1.2px;margin-bottom:9px}
|
||||
.sob{font-size:20px;font-weight:800;line-height:1.5;color:#0b1220}
|
||||
.sob b{color:#0a7a4f}
|
||||
.sof{display:flex;align-items:center;gap:11px;margin-top:14px;flex-wrap:wrap}
|
||||
.sof .n{background:#fff;border:1px solid #bbf7d0;border-radius:11px;padding:10px 15px;
|
||||
font-size:15px;font-weight:800;color:#0a7a4f}
|
||||
.sof .n s{text-decoration:none;color:#c02626}
|
||||
.sof .ar{font-size:20px;font-weight:800;color:#0a7a4f}
|
||||
.sos{font-size:13.5px;font-weight:700;color:#4b5563;margin-top:12px;line-height:1.65}
|
||||
|
||||
/* ── 手机端:全部改为竖排,禁止横向撑破 ───────────────── */
|
||||
@media (max-width:600px){
|
||||
body{padding:16px 10px 26px}
|
||||
.card{padding:17px 15px;border-radius:14px}
|
||||
h1{font-size:23px;letter-spacing:-.4px}
|
||||
.frow{flex-direction:column;gap:0}
|
||||
.fbox{flex:0 0 auto}
|
||||
.far{transform:rotate(90deg);justify-content:center;margin:8px 0;line-height:1}
|
||||
.ftxt{font-size:15px}
|
||||
.ftxt b{font-size:18px}
|
||||
.fcap2{margin:13px 0 8px;font-size:13.5px}
|
||||
.tank{height:44px}
|
||||
.big{font-size:26px}
|
||||
.big span{display:block;margin:5px 0 0}
|
||||
.comp{height:24px}
|
||||
.concl{font-size:13.5px;padding:11px 13px}
|
||||
.lg{gap:7px}
|
||||
.lg span{font-size:12px;padding:5px 10px}
|
||||
.row{flex-wrap:wrap;gap:6px 10px;padding:9px 0}
|
||||
.lab{flex:1 1 100%}
|
||||
.lab b{display:inline;font-size:14.5px}
|
||||
.lab i{margin-left:8px}
|
||||
.bar{flex:1 1 62%;order:2;height:26px}
|
||||
.val{flex:0 0 auto;order:3;text-align:left;font-size:15px}
|
||||
.note{order:4;font-size:11.5px;padding:3px 9px}
|
||||
.key{gap:10px;font-size:12.5px}
|
||||
.tot{font-size:13.5px;padding:11px 13px}
|
||||
.mpb{font-size:15.5px}
|
||||
.sob{font-size:17px}
|
||||
.sof .n{font-size:13.5px;padding:8px 12px}
|
||||
.sos{font-size:13px}
|
||||
}
|
||||
</style></head><body><div class="wrap">
|
||||
|
||||
<div class="card">
|
||||
<h1>注意这个问题立省 <em>50% token</em></h1>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="flow">
|
||||
|
||||
<div class="frow">
|
||||
<div class="fbox">
|
||||
<div class="fcap">第 1 轮 · 我问了什么</div>
|
||||
<div class="ftxt">「代码注释里还有 96 处遗留备注,全清掉」</div>
|
||||
</div>
|
||||
<div class="far">→</div>
|
||||
<div class="fbox hl">
|
||||
<div class="fcap">模型干了什么</div>
|
||||
<div class="ftxt">展开了 <b>101</b> 次工具调用</div>
|
||||
<div class="fsub">142 次文件编辑 · 179 次命令</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fdown">↓</div>
|
||||
|
||||
<div class="fcap2">上下文 = 101 次调用的「入参 + 输出」原文</div>
|
||||
<div class="tank"><div class="fill"></div></div>
|
||||
<div class="big">27 万 <span>token(占最终 59 万的 45%)</span></div>
|
||||
|
||||
<div class="fcap2">这 27 万里面装的是什么</div>
|
||||
<div class="comp">
|
||||
<i style="width:75.3%;background:#a81c1c"></i>
|
||||
<i style="width:19.0%;background:#d97706"></i>
|
||||
<i style="width:4.5%;background:#2563eb"></i>
|
||||
<i style="width:1.3%;background:#0a7a4f"></i>
|
||||
</div>
|
||||
<div class="lg">
|
||||
<span><i style="background:#a81c1c"></i>工具输出(命令回显 / 文件内容 / 搜索结果)75%</span>
|
||||
<span><i style="background:#d97706"></i>工具入参 19%</span>
|
||||
<span><i style="background:#2563eb"></i>我的提问(含系统注入)4.5%</span>
|
||||
<span><i style="background:#0a7a4f"></i>AI 回复 1.3%</span>
|
||||
</div>
|
||||
|
||||
<div class="concl">从此永久留在历史里 —— 之后每一轮、每一次请求,都要把它整份重发一遍</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="row"><div class="lab"><b>第 1 轮</b><i>101 次调用</i></div><div class="bar"><span class="inh" style="width:0.0%"></span><span class="add" style="width:65.4%"></span></div><div class="val">27 万</div></div>
|
||||
<div class="row"><div class="lab"><b>第 2 轮</b><i>26 次调用</i></div><div class="bar"><span class="inh" style="width:65.4%"></span><span class="add" style="width:22.3%"></span></div><div class="val">36 万</div></div>
|
||||
<div class="row"><div class="lab"><b>第 3 轮</b><i>24 次调用</i></div><div class="bar"><span class="inh" style="width:87.6%"></span><span class="add" style="width:6.6%"></span></div><div class="val">39 万</div></div>
|
||||
<div class="row hot"><div class="lab"><b>第 4 轮</b><i>10 次调用</i></div><div class="bar"><span class="inh" style="width:94.2%"></span><span class="add" style="width:5.8%"></span></div><div class="val">41 万</div><div class="note">AI 只回 620 字</div></div>
|
||||
<div class="key"><span class="k1"><i></i>从前面继承的(每轮全量重发)</span>
|
||||
<span class="k2"><i></i>本轮新增</span></div>
|
||||
<div class="tot">共 29 轮 → 末尾 59 万 | 累计输入 1.93 亿 | 其中工具痕迹 93%<br>
|
||||
<span style="display:block;margin-top:6px;font-size:.94em">但最贵的<b>不是</b>第 1 轮(它只占 <s>5.6%</s>)—— 是<span style="color:#c02626">水位已经很高、还跑了 50+ 次请求</span>的那两轮,合计 <s>40%</s></span></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="mp">
|
||||
<div class="mpt">主 要 问 题</div>
|
||||
<div class="mpb">一句话,被展开成<b>上百次工具调用</b>;<br>
|
||||
而这些调用背后的<b>每一次模型请求</b>,都要把全部历史原文重发一遍。<br>
|
||||
⇒ 成本 = 请求次数 × 当时的上下文体积,<b>越往后越贵</b>。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="so">
|
||||
<div class="sot">解 决 方 案</div>
|
||||
<div class="sob">让 AI <b>写脚本</b>处理</div>
|
||||
<div class="sof">
|
||||
<span class="n"><s>101 次工具调用</s></span>
|
||||
<span class="ar">➜</span>
|
||||
<span class="n">1 个脚本,一次跑完</span>
|
||||
</div>
|
||||
<div class="sos">96 处同类改动合成一个脚本 —— 少一次调用,就少背一遍全部历史。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div></body></html>
|
||||
@@ -0,0 +1,298 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>会话阈值自动接续 · 机制方案与成本实测(2026-09-16)</title>
|
||||
<style>
|
||||
:root{--bg:#f6f7f9;--card:#fff;--ink:#16191d;--dim:#5b6470;--line:#e2e6ec;
|
||||
--red:#c0392b;--green:#1e7a46;--amber:#b06d00;--blue:#1b5fa8;--violet:#6b3fa0;}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;padding:34px 22px 70px;background:var(--bg);color:var(--ink);
|
||||
font:15px/1.72 -apple-system,"Segoe UI","Microsoft YaHei",sans-serif;}
|
||||
.wrap{max-width:940px;margin:0 auto}
|
||||
h1{font-size:25px;margin:0 0 6px;letter-spacing:-.2px}
|
||||
.sub{color:var(--dim);font-size:13px;margin-bottom:26px}
|
||||
h2{font-size:18px;margin:38px 0 12px;padding-left:11px;border-left:4px solid var(--blue)}
|
||||
h3{font-size:15px;margin:22px 0 8px;color:var(--dim);font-weight:600}
|
||||
p{margin:9px 0}
|
||||
code{background:#eef1f5;padding:1.5px 5px;border-radius:4px;font-size:13px;
|
||||
font-family:ui-monospace,Consolas,monospace}
|
||||
.card{background:var(--card);border:1px solid var(--line);border-radius:11px;
|
||||
padding:18px 20px;margin:14px 0;box-shadow:0 1px 3px rgba(16,24,40,.05)}
|
||||
.verdict{background:#fff;border-left:5px solid var(--red);border-radius:9px;padding:16px 20px;margin:18px 0}
|
||||
.verdict b{color:var(--red)}
|
||||
table{border-collapse:collapse;width:100%;font-size:13.5px;margin:12px 0}
|
||||
th,td{border:1px solid var(--line);padding:7px 10px;text-align:left}
|
||||
th{background:#eef1f5;font-weight:600;font-size:12.5px;color:var(--dim)}
|
||||
td.n{text-align:right;font-variant-numeric:tabular-nums;font-family:ui-monospace,Consolas,monospace}
|
||||
tr.hi td{background:#fdf3f2}
|
||||
tr.lo td{background:#f2f9f4}
|
||||
.tag{display:inline-block;font-size:11.5px;padding:1px 7px;border-radius:20px;
|
||||
border:1px solid currentColor;line-height:1.6}
|
||||
.t-red{color:var(--red)}.t-green{color:var(--green)}.t-amber{color:var(--amber)}
|
||||
.t-blue{color:var(--blue)}.t-violet{color:var(--violet)}
|
||||
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
|
||||
@media(max-width:760px){.grid2{grid-template-columns:1fr}}
|
||||
ul,ol{margin:8px 0 8px 20px;padding:0}
|
||||
li{margin:5px 0}
|
||||
.kv{display:flex;gap:10px;font-size:13.5px;margin:5px 0}
|
||||
.kv .k{color:var(--dim);min-width:126px}
|
||||
.bar{height:15px;border-radius:3px;display:inline-block;vertical-align:middle}
|
||||
.note{font-size:12.5px;color:var(--dim);margin-top:8px}
|
||||
.big{font-size:22px;font-weight:700;font-variant-numeric:tabular-nums}
|
||||
.chain{font-family:ui-monospace,Consolas,monospace;font-size:12.5px;line-height:2;
|
||||
background:#f2f4f8;border-radius:8px;padding:14px 16px;overflow-x:auto;white-space:pre}
|
||||
</style>
|
||||
</head>
|
||||
<body><div class="wrap">
|
||||
|
||||
<h1>会话阈值自动接续:机制方案 + 成本实测</h1>
|
||||
<div class="sub">2026-09-16 · 数据源 = <code>workbuddy.db</code> 的 <code>session_usage.credit_json</code> 与 <code>automation_runs.runs_json</code>(宿主真实计费字段,非估算)</div>
|
||||
|
||||
<div class="verdict">
|
||||
<b>判定三句:</b><br>
|
||||
① <b>「token 达阈值自动开新会话」能建,但只建成「半自动」</b>——宿主无创建会话的 API,唯一通道是自动化(<b>已实测</b>:每次自动化运行都产生新 <code>sessionId</code>);钩子只能注入指令,末段动作必须由模型调 <code>automation_update</code> 完成。<br>
|
||||
② <b>但它救不了你看到的那 7–8 积分</b>——实测那 7–8 分来自<b>一轮里跑了 31–54 次工具调用</b>,与会话新旧<b>无关</b>。三个自动化全部是<u>全新会话的第一轮</u>,分别烧掉 <b>8.93 / 10.05 / 13.82</b> 积分。<br>
|
||||
③ <b>真杠杆是「一轮内的工具调用次数」,不是「会话寿命」</b>。换会话能省的只是「水位带来的约 3 倍单价膨胀」,属次要项。<br>
|
||||
④ <b>接得上才是前提</b>——新会话必须能<u>校验</u>上一会话的进度,不能只读文字描述(实证:上一条接续点把对象写错了)。已升级为「接续包 v2 + 开机四步」,见 <b>§五</b>。
|
||||
</div>
|
||||
|
||||
<h2>一、实测成本模型(8 次自动化运行,宿主原始计费字段)</h2>
|
||||
<table>
|
||||
<tr><th>自动化运行</th><th>上下文 input tokens</th><th>工具调用</th><th>积分</th><th>积分 / 工具</th><th>缓存命中</th></tr>
|
||||
<tr class="hi"><td>覆盖网络线·归档接续</td><td class="n">200,013</td><td class="n">54</td><td class="n">13.82</td><td class="n">0.256</td><td class="n">99.8%</td></tr>
|
||||
<tr class="hi"><td>决策方法-2</td><td class="n">168,040</td><td class="n">37</td><td class="n">10.05</td><td class="n">0.272</td><td class="n">99.9%</td></tr>
|
||||
<tr class="hi"><td>覆盖网络线·接续(优化版)</td><td class="n">141,401</td><td class="n">31</td><td class="n">8.93</td><td class="n">0.288</td><td class="n">99.8%</td></tr>
|
||||
<tr class="lo"><td>代码仓三方同步 ③</td><td class="n">87,177</td><td class="n">20</td><td class="n">1.93</td><td class="n">0.097</td><td class="n">99.8%</td></tr>
|
||||
<tr class="lo"><td>代码仓三方同步 ①</td><td class="n">64,876</td><td class="n">15</td><td class="n">1.66</td><td class="n">0.111</td><td class="n">97.9%</td></tr>
|
||||
<tr class="lo"><td>遗留项自动推进</td><td class="n">82,712</td><td class="n">12</td><td class="n">1.20</td><td class="n">0.100</td><td class="n">98.7%</td></tr>
|
||||
<tr class="lo"><td>代码仓三方同步 ④</td><td class="n">63,147</td><td class="n">16</td><td class="n">1.18</td><td class="n">0.074</td><td class="n">99.7%</td></tr>
|
||||
<tr class="lo"><td>代码仓三方同步 ②</td><td class="n">56,422</td><td class="n">11</td><td class="n">0.82</td><td class="n">0.075</td><td class="n">98.5%</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>图:工具调用次数 → 积分(近乎线性)</h3>
|
||||
<div class="card">
|
||||
<div style="font-size:12.5px;color:#5b6470;margin-bottom:6px">红 = 水位 >14 万(单价 ≈0.27)|绿 = 水位 <9 万(单价 ≈0.09)</div>
|
||||
<div style="display:flex;flex-direction:column;gap:7px;font-size:12.5px">
|
||||
<div><span style="display:inline-block;width:150px">归档接续 54 次</span><span class="bar" style="width:414px;background:#c0392b"></span> <b>13.82</b></div>
|
||||
<div><span style="display:inline-block;width:150px">决策方法-2 37 次</span><span class="bar" style="width:301px;background:#c0392b"></span> <b>10.05</b></div>
|
||||
<div><span style="display:inline-block;width:150px">接续优化版 31 次</span><span class="bar" style="width:268px;background:#c0392b"></span> <b>8.93</b></div>
|
||||
<div><span style="display:inline-block;width:150px">三方同步③ 20 次</span><span class="bar" style="width:58px;background:#1e7a46"></span> <b>1.93</b></div>
|
||||
<div><span style="display:inline-block;width:150px">三方同步① 15 次</span><span class="bar" style="width:50px;background:#1e7a46"></span> <b>1.66</b></div>
|
||||
<div><span style="display:inline-block;width:150px">遗留项推进 12 次</span><span class="bar" style="width:36px;background:#1e7a46"></span> <b>1.20</b></div>
|
||||
<div><span style="display:inline-block;width:150px">三方同步④ 16 次</span><span class="bar" style="width:35px;background:#1e7a46"></span> <b>1.18</b></div>
|
||||
<div><span style="display:inline-block;width:150px">三方同步② 11 次</span><span class="bar" style="width:25px;background:#1e7a46"></span> <b>0.82</b></div>
|
||||
</div>
|
||||
<div class="note">同一批任务的 4 次「三方同步」单价 0.074–0.111(水位 5.6–8.7 万);三份覆盖网络/决策方法文档任务单价 0.256–0.288(水位 14–20 万)。</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<b>成本公式(可复算)</b>
|
||||
<div class="chain">一轮积分 ≈ 单价 × 该轮工具调用次数
|
||||
|
||||
单价 —— 随会话水位上升,实测同一会话内:
|
||||
水位 <10 万 ⇒ ≈ 0.10 积分 / 次工具调用
|
||||
水位 15 万 ⇒ ≈ 0.41 积分 / 次工具调用 ← 4 倍
|
||||
|
||||
固定注入(每请求都要重发):
|
||||
tools 20,734 + systemPrompt 10,395 + skills 3,919 + mcp 144 = 35,192 token
|
||||
但缓存命中 99.5% ⇒ 固定注入被缓存价抹平,不是主因
|
||||
(b08b1c35 实测:63,488 命中 / 1,388 未命中 = 97.9% 命中率)</div>
|
||||
<div class="note">上表「单价」两簇(0.09 / 0.27)的差异来源:新会话 b08b1c35 内部给出了<b>同会话、同模型、仅水位不同</b>的对照——第 1 轮单价 0.106(起始水位 0),第 2 轮单价 0.412(水位 14.9 万)。⇒ 水位效应成立,非模型档位差异。</div>
|
||||
</div>
|
||||
|
||||
<h2>二、为什么「开新会话」没省到钱</h2>
|
||||
|
||||
<div class="card">
|
||||
<p><b>铁证</b>:三次「新建会话跑一次」的积分 = <b>8.93 / 10.05 / 13.82</b>,与你观察到的「提一轮就 7 8 个积分」完全吻合。而它们<b>每一个都是全新会话的第一轮</b>——证据在 <code>automation_runs.metadata_json</code>:</p>
|
||||
<div class="chain">{"runKind":"scheduled","conversationId":"3b096fe1-…","sessionId":"3b096fe1-…"}
|
||||
{"conversationId":"32d97b9a-…","sessionId":"32d97b9a-…"}
|
||||
…每次自动化运行 = 一个此前不存在的 sessionId</div>
|
||||
<p>⇒ <span class="tag t-green">已核实</span> 自动化确实是「开新会话」的通道,<b>但新会话第一轮的 31–54 次工具调用照旧收费</b>,而且开局水位为零也没让它变便宜。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<p><b>为什么不变便宜?</b>因为成本 = <b>请求次数 × 每次请求量</b>。开新会话把「每次请求量」的<u>增量部分</u>清零了,但:</p>
|
||||
<ul>
|
||||
<li>每次请求仍要重发 <b>35,192 token</b> 固定注入(工具定义 + 系统提示 + 技能清单)——这是跑不掉的「入场费」;</li>
|
||||
<li>一跑 54 次工具 = 54 次请求 ⇒ 入场费 ×54;</li>
|
||||
<li>缓存命中 99.5% 让入场费打折,所以它<u>不是</u>主因,但也没被消掉。</li>
|
||||
</ul>
|
||||
<p>⇒ <b>「会话多大」影响的是单价,不是总价;「跑多少次工具」才决定总价。</b></p>
|
||||
</div>
|
||||
|
||||
<h2>三、真杠杆排序(按效力)</h2>
|
||||
<table>
|
||||
<tr><th>#</th><th>杠杆</th><th>效力</th><th>做法</th></tr>
|
||||
<tr><td>①</td><td><b>一轮内的工具调用次数</b></td><td><span class="tag t-red">主导 · 线性</span></td><td>批量活写成脚本「一次跑完」:1 次 Bash 顶 20 次 Read/Edit/Bash。31 次 → 5 次 ⇒ 8.93 分 → <b>约 0.5 分</b></td></tr>
|
||||
<tr><td>②</td><td>会话水位</td><td><span class="tag t-amber">次要 · 约 3 倍</span></td><td>到阈值换会话(就是你想建的那套机制)</td></tr>
|
||||
<tr><td>③</td><td>固定注入 35,192 token</td><td><span class="tag t-green">很轻</span></td><td>缓存命中 99.5%,基本不用管</td></tr>
|
||||
</table>
|
||||
<p class="note">⚠️ 顺序不能反:<b>先治 ①,再治 ②</b>。只换会话不减工具调用 = 每次省一点单价、总量照旧,这正是「没达到节约效果」的成因。</p>
|
||||
|
||||
<h2>四、机制设计:能建,但只能建成「半自动」</h2>
|
||||
|
||||
<div class="card">
|
||||
<h3>现状:缺口只有一个</h3>
|
||||
<div class="kv"><span class="k">水位探测</span><span><span class="tag t-green">已有</span> <code>stop-dialog-guard.py</code> 三级阈值 <b>12 万 / 20 万 / 30 万</b>,数据源 = 转录里的真实 <code>usage.input_tokens</code>,同级去重只报一次</span></div>
|
||||
<div class="kv"><span class="k">指令注入</span><span><span class="tag t-green">已有</span> 三级文案 = 强制收口(落盘 → 接续包 → 告知开新会话)</span></div>
|
||||
<div class="kv"><span class="k">创建会话</span><span><span class="tag t-red">缺</span> 钩子事件只有 <code>SessionStart</code> / <code>PreToolUse</code> / <code>UserPromptSubmit</code>,输出只有「注入上下文」与「拦工具」两种,<b>没有任何创建 / 切换会话的能力</b></span></div>
|
||||
<div class="kv"><span class="k">可用通道</span><span><span class="tag t-blue">自动化</span> 模型可调 <code>automation_update</code> 登记一次性自动化;宿主调度到点即开新会话</span></div>
|
||||
<div class="note">⛔ 钩子脚本<b>不能</b>直接写数据库来建自动化——自动化只能经 <code>automation_update</code> 创建,这是硬约束。</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>建议链路(在现有三级告警上追加一段)</h3>
|
||||
<div class="chain">[硬] 水位 ≥ 30 万
|
||||
↓ stop-dialog-guard.py 注入(已有文案 + 新增第 ④ 条)
|
||||
[软] 模型执行收口四步:
|
||||
① 落盘:在途状态写进 .workbuddy/memory/
|
||||
② 出接续包:目标/已完成/在途/下一步/关键决定/回滚点
|
||||
③ 【新增】调 automation_update 登记一次性自动化
|
||||
name = "<原会话名>-2"
|
||||
prompt = "读 <接续包路径>,从「下一步」起继续。
|
||||
⛔ 第一轮必须把批量活写成脚本一次跑完,工具调用 ≤ 8 次。"
|
||||
时间 = 当前时刻 + 2 分钟
|
||||
④ 终结本会话
|
||||
↓
|
||||
[硬] 宿主到点执行 ⇒ 新会话(实测通道可靠)</div>
|
||||
<p><b>它的真实价值</b>:把「被迫收口 → 用户手工开新会话」变成「自动续上」,省掉人工一步,并把<u>单价膨胀</u>(约 3 倍)压回去。<b>它不省工具调用次数</b>——所以第 ③ 步 prompt 里那句「工具调用 ≤ 8 次」才是真正值钱的部分。</p>
|
||||
<p><b>它的局限(如实说)</b>:末段是<b>软环节</b>——钩子只能要求模型照做,模型若不调 <code>automation_update</code> 则链条断在最后一步。硬保证只有前半段(提醒 / 强制收口)。</p>
|
||||
</div>
|
||||
|
||||
<h2>五、让新会话「明确知道进度 + 未执行项」</h2>
|
||||
|
||||
<div class="card">
|
||||
<p>这是自动接续能不能用的<b>真正前提</b>——接不上状态,「自动开销」只是白花钱重新探索一遍。原规范(工作区根《会话接续规范》)已有 6 项内容清单,本轮补的是<b>可校验性</b>:<b>文字会失真,证据不会。</b></p>
|
||||
<p><b>实证</b>:上一条接续点原话写「清理被截断的 <code>.workbuddy/memory/MEMORY.md</code>」——照做就会改错对象(真正被截断的是<b>用户级</b>那份)。<b>凡是结论,必须附一条能复现的命令。</b></p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>接续包 v2 · 固定表头(新会话机械读取)</h3>
|
||||
<div class="chain">## 接续点 · <工作线名> · <YYYY-MM-DD HH:MM>
|
||||
- 来源会话: <sid> | 结束原因: <水位 N 万强制收口 | 用户要求>
|
||||
- 原目标: <用户原话,不翻译、不缩写>
|
||||
- 基线: HEAD=<git sha> | 远端 master=<sha> | 全局锁=<无 | 占用者>
|
||||
- 产物: <绝对路径1> | <绝对路径2> ← 新会话逐一确认存在
|
||||
- 校验命令: <一条命令> → 期望输出: <关键片段> ← 证明"上一步真完成"
|
||||
- 未完成: ①<…> ②<…> ← 用户要的、还没做的
|
||||
- 下一步: 第 1 个动作 = <具体命令> ← ⛔ 不写"继续处理"
|
||||
- 关键决定: <已定项 + 为什么> ← 防新会话推翻重来
|
||||
- 回滚点: <能退回的位置>
|
||||
- ⛔ 不要重做: <已完成的,免得重复劳动></div>
|
||||
<p class="note"><b>硬要求</b>:① 「校验命令」必填,只读、30 秒内跑完、输出可判真假;② 「下一步」第 1 条必须可直接执行;③ 产物 / 未完成 / 不要重做 三节一个都不能空(空写「无」);④ 全篇 ≤3 KB,只写「在哪 + 什么状态」。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>新会话开机四步(接手方强制动作)</h3>
|
||||
<table>
|
||||
<tr><th>#</th><th>动作</th><th>为什么</th></tr>
|
||||
<tr><td>1</td><td><b>只读接续包</b>(⛔ 不许一上来就全库探索 / 扫全盘)</td><td>探索是最贵的动作;接续包就是用来免掉它的</td></tr>
|
||||
<tr><td>2</td><td><b>跑「校验命令」并比对期望输出</b>——不符就停下报告,⛔ 不许照文字硬做</td><td>文字会失真;跑不通的校验会伪装成"通过"</td></tr>
|
||||
<tr><td>3</td><td><b>复述「未完成」与「下一步」</b>,确认与用户当时要的一致</td><td>防目标漂移(本线最大历史坑)</td></tr>
|
||||
<tr><td>4</td><td><b>从「下一步」第 1 条开工</b>;⛔ 不重做「不要重做」列的东西</td><td>省掉重复劳动</td></tr>
|
||||
</table>
|
||||
<p class="note">⚠️ <b>接续包会过期</b>:写完后又改了东西,必须回头改接续包并同步「基线」里的 sha 与时间戳。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>自动接续任务 · 标准 prompt 模板(照抄填空)</h3>
|
||||
<div class="chain">读「<接续包绝对路径>」的「接续点」。
|
||||
|
||||
① 先跑其中的「校验命令」,输出与期望不符 ⇒ 停下、只报告,⛔ 不许照文字硬做。
|
||||
② 从「下一步」第 1 条开工;⛔ 不重做「不要重做」列的东西。
|
||||
③ 批量活必须先写成脚本一次跑完,⛔ 不许逐份探索;本脚本工具调用 ≤ 8 次。</div>
|
||||
<p><b>第 ③ 条才是钱的开关</b>:31 次工具调用 = 8.93 积分;压到 8 次 ≈ <b>1 分</b>。前两条保证「不跑偏」,第 ③ 条保证「不贵」。</p>
|
||||
</div>
|
||||
|
||||
<h2>六、顺带发现(只报告,未动手)</h2>
|
||||
<div class="card">
|
||||
<p><b>1. 两个「定时」自动化仍在按钟点烧钱</b>——它们与水位<b>无关</b>,是「定时触发」而非「阈值触发」,恰好是你这次想改掉的那种:</p>
|
||||
<table>
|
||||
<tr><th>自动化</th><th>周期</th><th>频率</th><th>已跑</th><th>已花</th></tr>
|
||||
<tr><td>代码仓三方同步(DSH)</td><td><code>FREQ=HOURLY;INTERVAL=3</code></td><td>约 8 次/天</td><td>4 次</td><td class="n">5.59</td></tr>
|
||||
<tr><td>遗留项自动推进(DSH 平台)</td><td><code>FREQ=HOURLY;INTERVAL=8</code></td><td>约 3 次/天</td><td>1 次</td><td class="n">1.20</td></tr>
|
||||
</table>
|
||||
<p class="note">合计约 <b>11 次/天 × 每次新建会话</b>;按实测均值 ≈1.4 积分/次估算 ⇒ <b>约 15 积分/天</b>纯自动化消耗,且每次都会在仓库里产生一个新会话与新的未提交改动。</p>
|
||||
<p><b>2. 五个一次性自动化已过期但仍为 ACTIVE</b>(08-27 ×2、08-30、08-31,以及今天的三个覆盖网络/决策方法一次性任务)⇒ 属清理项。</p>
|
||||
<p><b>3. 前一轮对本问题的结论需要更正</b>:<code>stop-dialog-guard.py</code> 里写着「③ 因此本级不做自动开,做强制收口」并注明「自动开这一半无法实现」。前半句仍成立(钩子确实做不到),但后半句<b>不准确</b>——经自动化这一通道,自动接续<b>可以做到半自动</b>。此处属发现,未改脚本。</p>
|
||||
</div>
|
||||
|
||||
<h2>七、落地清单(按效力排序,待你点头后执行)</h2>
|
||||
<ol>
|
||||
<li><b>(最高)给两个周期自动化的 prompt 加硬约束</b>:写明「批量活必须先写脚本一次跑完,工具调用上限 8 次」。预期把单次 8–14 分压到 1–2 分。<span class="tag t-red">收益最大</span></li>
|
||||
<li><b>接续包换成 §五 的 v2 表头 + 新会话按「开机四步」启动</b> —— 这正是你要的「新会话明确知道上个会话的进度与未执行内容」。规范已就地升级(工作区根《会话接续规范》,§3.1.1–3.1.4 / §3.2.1),下次收口即生效。<span class="tag t-blue">已就绪 · 靠纪律</span></li>
|
||||
<li><b>在 <code>stop-dialog-guard.py</code> 三级文案后追加第 ④ 条</b>(提示模型登记 +2 分钟一次性自动化)。改动在 hook 脚本内、即刻生效,无需重启。<span class="tag t-amber">收益次要 · 消除人工一步</span></li>
|
||||
<li><b>清理 5 个过期的一次性自动化</b>;与 ③ 一起或单独做。</li>
|
||||
<li><b>重估两个周期自动化的存在必要</b>——「代码仓三方同步」每 3 小时一次是否真需要;若只是兜底,改日频即可。</li>
|
||||
</ol>
|
||||
<p class="note">⚠️ 落地 ③ 需改文档库脚本 ⇒ 要抢全局执行锁 + 推镜像 + 复跑对账。本轮为无人值守运行,<b>未动任何脚本 / 自动化 / 文档库,未抢锁、未提交、未推送</b>;只升级了工作区根《会话接续规范》(本工作区自有文档,非文档库)。</p>
|
||||
|
||||
<h2>八、附:为什么自动化会话要跑那么多工具调用</h2>
|
||||
|
||||
<div class="verdict">
|
||||
<b>你的感觉对,但原因不是「自动化」这个身份。</b><br>
|
||||
实测今日 6 个会话:<b>自动化平均每轮 5.6–8.9 积分,手动平均每轮 3.3 积分</b>(约 2–3 倍)。差距来自三件事,全都可以改。
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>对比(今日同一工作区、同一类任务)</h3>
|
||||
<table>
|
||||
<tr><th>会话</th><th>类型</th><th>轮数</th><th>积分总计</th><th>平均每轮</th></tr>
|
||||
<tr class="hi"><td>自动化·接续优化版</td><td>自动化</td><td class="n">1</td><td class="n">8.93</td><td class="n">8.93</td></tr>
|
||||
<tr class="hi"><td>自动化·决策方法-2</td><td>自动化</td><td class="n">5</td><td class="n">44.77</td><td class="n">8.95</td></tr>
|
||||
<tr class="hi"><td>自动化·确认覆盖网络待办</td><td>自动化</td><td class="n">2</td><td class="n">11.17</td><td class="n">5.59</td></tr>
|
||||
<tr class="lo"><td>手动·检查覆盖网络方案</td><td>手动</td><td class="n">5</td><td class="n">16.52</td><td class="n">3.30</td></tr>
|
||||
</table>
|
||||
<p class="note">⚠️ 手动会话的总量<b>并不小</b>(原会话 <code>e2e090be</code> 累计 221 次工具调用,比任何自动化都多)——差别在<b>单轮</b>。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>根因一:一条指令里塞了几件事</h3>
|
||||
<p>你手动说「继续 XX 任务」,通常<b>只指一件事</b>;而自动化的 prompt 会把一整段工作串起来。以「决策方法-2」那条为例,它同时要求:三项任务 + 抢全局锁 + 只做三项 + 脚本化 + 反序释放锁 + 写简报 + 写记忆 + 不许承诺降低消耗 —— <b>八件事塞进一条指令,模型只能一路做到底</b>。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>根因二:没人能打断(最关键)</h3>
|
||||
<p><b>实证</b>:新会话 <code>b08b1c35</code> 那一轮,用户只说了「先确认待办事项」。实际发生的:</p>
|
||||
<div class="chain">第 1–3 次 读接续入口 / 简报 / 会合中继方案
|
||||
第 4–6 次 抢锁(PATH 被 shim 重置,重试 2 次)
|
||||
第 7–24 次 连续 17 次深度取证:ssh config、106 的 env、47 的 psql、
|
||||
47 的 systemd drop-in、remote-spawner.ts、proxy.ts 勘误…
|
||||
第 25 次 加载技能 dsh-change-workflow
|
||||
第 28 次 ⚠️ 已经在写 S0 的代码了 —— src/net/reachability.ts
|
||||
(用户要的是「确认待办」,不是开工)</div>
|
||||
<p>模型自己的思考留了痕:<b>「重要取证完成」「取证非常完整了」「现在证据链完整了」—— 三次自我加码。</b>你在场时,看到第三次就会说「够了,先停」;无人值守时没人喊停,它就自己给自己加码。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>根因三:我的规则本身在制造调用</h3>
|
||||
<p>「接手前人结论先做最小取证」「交付门禁四层验证」「抢锁 / 反序释放锁」「写简报 + 写记忆」都是项目硬规则,<b>每一条都是一个或多个工具调用</b>。规则是对的,但把它们全塞进一条无人值守的指令里,就成了「必须一路做完」。</p>
|
||||
<p class="note">另有一笔环境税:Bash 每次都要重写 <code>export PATH=…</code>(shim 重置 PATH),虽不增加调用次数,但让每次调用都变长。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>最根本的一条:单次调用极便宜,模型没有代价感</h3>
|
||||
<p>从转录里逐次请求的 <code>credit</code> 字段(与数据库 <code>credit_json</code> 合计<b>完全对上</b>,两个独立源交叉验证):</p>
|
||||
<table>
|
||||
<tr><th>会话</th><th>请求次数</th><th>单次中位</th><th>单次最低 / 最高</th><th>合计</th></tr>
|
||||
<tr><td>自动化·确认覆盖网络待办</td><td class="n">67</td><td class="n">0.11</td><td class="n">0.05 / 2.06</td><td class="n">11.17</td></tr>
|
||||
<tr><td>手动·检查覆盖网络方案</td><td class="n">44</td><td class="n">0.21</td><td class="n">0.07 / 2.84</td><td class="n">16.52</td></tr>
|
||||
</table>
|
||||
<div class="chain">第 1 次调用(冷启动): prompt=52,856 hit=12,288 miss=40,568 credit=0.60 ← 最贵
|
||||
第 67 次调用(末次) : prompt=197,376 hit=197,120 miss=256 credit=0.11 ← 命中 99.9%</div>
|
||||
<p><b>每一次调用只要 0.1 分左右</b>——所以模型在单步决策时<u>几乎感觉不到代价</u>,它看不到「累计已经 67 次了」。<b>没有人喊停,它就没有刹车。</b>你在场时,你那句「够了,先停」就是唯一的刹车;无人值守时,刹车片不存在。</p>
|
||||
<p class="note">⛔ 注意:这里也顺带<b>推翻了"自动化一定比手动贵"</b>——按累计算,手动那个会话 16.52 反而更贵(水位 21.7 万、单次中位 0.21)。差别只在<b>单轮塞了多少事</b>。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>修法:改 prompt,不改流程</h3>
|
||||
<p>在现有模板(§五)后面再加两条,直接掐掉前两个根因:</p>
|
||||
<div class="chain">⛔ 本轮只做一件事:<具体那件>。做完即停。
|
||||
不许顺手做归档 / 整理 / 写入口 / 开工别的任务。
|
||||
⛔ 取证只做一次、最多 3 个命令;发现要动代码或改配置 ⇒ 停下来报告,不要动手。</div>
|
||||
<p><b>预期效果</b>:<code>b08b1c35</code> 那一轮 77 次 → 约 8 次,11.17 分 → <b>约 1 分</b>。这就是「把第一优先杠杆(一轮工具次数)压下去」的具体做法。</p>
|
||||
<p class="note">⚠️ 附带一条真实修正:早前记录写「转录 <code>rawUsage</code> 恒为空」——<b>不准确</b>。实测 <code>b08b1c35</code> 的 jsonl 里有 <b>67 条带 <code>credit</code> 的 <code>rawUsage</code></b>,且逐次合计 11.17 与数据库 <code>credit_json</code> 完全一致。该字段<b>可用</b>,是逐次成本的最佳来源。</p>
|
||||
</div>
|
||||
|
||||
</div></body></html>
|
||||
@@ -0,0 +1,179 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN"><head><meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>省积分 · 四个根治方法详解</title>
|
||||
<style>
|
||||
:root{
|
||||
--ink:#0b1220; --mut:#6b7688; --line:#e6e9ee; --bg:#f4f5f7;
|
||||
--red:#c02626; --redbg:#fef2f2; --redln:#fecaca;
|
||||
--amb:#b45309; --ambbg:#fffbeb; --ambln:#fde68a;
|
||||
--grn:#0a7a4f; --grnbg:#ecfdf5; --grnln:#bbf7d0;
|
||||
--blu:#1d4ed8; --blubg:#eff6ff; --bluln:#bfdbfe;
|
||||
}
|
||||
body{margin:0;padding:26px 14px 40px;background:var(--bg);color:var(--ink);
|
||||
font-family:system-ui,-apple-system,"Segoe UI","Microsoft YaHei",sans-serif;
|
||||
-webkit-font-smoothing:antialiased;line-height:1.5}
|
||||
.wrap{max-width:940px;margin:0 auto}
|
||||
.card{background:#fff;border:1px solid var(--line);border-radius:18px;padding:24px 26px;margin-bottom:14px;
|
||||
box-shadow:0 1px 2px rgba(11,18,32,.04),0 10px 28px -14px rgba(11,18,32,.13)}
|
||||
h1{font-size:28px;line-height:1.3;margin:0 0 8px;letter-spacing:-.7px;font-weight:800}
|
||||
h1 em{font-style:normal;color:var(--red)}
|
||||
.sub{font-size:14.5px;color:var(--mut);font-weight:700;margin:0}
|
||||
.sub b{color:var(--ink)}
|
||||
|
||||
.mh{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:14px}
|
||||
.no{font-size:13px;font-weight:800;color:#fff;background:var(--ink);border-radius:8px;padding:4px 9px;letter-spacing:.5px}
|
||||
.mt{font-size:21px;font-weight:800;letter-spacing:-.4px;margin:0}
|
||||
.tag{font-size:12.5px;font-weight:800;border-radius:999px;padding:4px 11px;white-space:nowrap}
|
||||
.t-red{color:var(--red);background:var(--redbg);border:1px solid var(--redln)}
|
||||
.t-amb{color:var(--amb);background:var(--ambbg);border:1px solid var(--ambln)}
|
||||
.t-grn{color:var(--grn);background:var(--grnbg);border:1px solid var(--grnln)}
|
||||
.t-blu{color:var(--blu);background:var(--blubg);border:1px solid var(--bluln)}
|
||||
|
||||
table{width:100%;border-collapse:collapse;font-size:14px}
|
||||
th,td{text-align:left;vertical-align:top;padding:9px 12px 9px 0;border-bottom:1px solid #f1f3f6}
|
||||
th{width:96px;color:var(--mut);font-weight:800;white-space:nowrap;font-size:13px}
|
||||
tr:last-child td,tr:last-child th{border-bottom:0}
|
||||
td b{color:var(--red)}
|
||||
code{background:#f3f4f6;border:1px solid #e8eaee;border-radius:5px;padding:1px 5px;font-size:12.8px;
|
||||
font-family:ui-monospace,Consolas,monospace}
|
||||
|
||||
.warn{background:var(--redbg);border-left:4px solid var(--red);border-radius:0 12px 12px 0;
|
||||
padding:14px 18px;font-size:14.5px}
|
||||
.warn .wt{font-weight:800;color:var(--red);font-size:15.5px;margin-bottom:8px}
|
||||
.warn ul{margin:8px 0 0;padding-left:20px}
|
||||
.warn li{margin:5px 0}
|
||||
|
||||
.fx{margin-top:12px;padding:11px 15px;border-radius:11px;font-size:14px;font-weight:700;
|
||||
background:#f8fafc;border:1px dashed #dbe1e8}
|
||||
.fx span{color:var(--red);font-weight:800}
|
||||
|
||||
.state{display:inline-block;font-size:12.5px;font-weight:800;border-radius:6px;padding:3px 8px}
|
||||
.s-on{color:var(--grn);background:var(--grnbg)}
|
||||
.s-off{color:var(--amb);background:var(--ambbg)}
|
||||
|
||||
.rel{display:flex;gap:10px;flex-wrap:wrap;margin-top:6px}
|
||||
.rel div{flex:1 1 190px;background:#f8fafc;border:1px solid #e8ecf1;border-radius:12px;padding:12px 14px;font-size:13.5px}
|
||||
.rel b{display:block;font-size:15px;margin-bottom:3px}
|
||||
.foot{font-size:12.5px;color:var(--mut);text-align:center;margin-top:6px}
|
||||
|
||||
/* ── 手机端:表格改竖排,禁止横向撑破 ─────────────────── */
|
||||
@media (max-width:600px){
|
||||
body{padding:16px 10px 26px}
|
||||
.card{padding:17px 15px;border-radius:14px}
|
||||
h1{font-size:22px;letter-spacing:-.4px}
|
||||
.mt{font-size:18px}
|
||||
.sub{font-size:13.5px}
|
||||
table{font-size:13.5px}
|
||||
tr{display:block;border-bottom:1px solid #f1f3f6;padding:8px 0}
|
||||
tr:last-child{border-bottom:0}
|
||||
th,td{display:block;width:auto;border:0;padding:0}
|
||||
th{padding-bottom:3px}
|
||||
td{padding-bottom:2px}
|
||||
.warn{font-size:13.5px;padding:13px 14px}
|
||||
.warn ul{padding-left:17px}
|
||||
.fx{font-size:13px}
|
||||
.rel div{flex:1 1 100%}
|
||||
}
|
||||
</style></head><body><div class="wrap">
|
||||
|
||||
<div class="card">
|
||||
<h1>省积分 · <em>四个根治方法</em>详解</h1>
|
||||
<p class="sub">成本 = <b>请求次数 × 当时上下文体积</b>。历史只增不减、输出永久驻留、每轮全量重发 ——<br>
|
||||
四个方法分别打在这个链条的不同环节上。</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="warn">
|
||||
<div class="wt">先纠正一个口径:第 1 轮「只产生 27 万 token」吗?</div>
|
||||
实测同一个会话(443 次带 usage 的模型请求):
|
||||
<ul>
|
||||
<li><b>27 万</b>(269,522)= 第 1 轮<b>结束时上下文的绝对体积</b>。这是"水位",只算一次。</li>
|
||||
<li><b>1,090 万</b>(10,906,992)= 第 1 轮<b>实际计费的 input 合计</b>。101 次请求,每次都把当时的全部历史重发一遍。<br>
|
||||
⇒ 两者差 <b>40 倍</b>。</li>
|
||||
<li>而第 1 轮在全量 1.93 亿里只占 <b>5.6%</b> —— 它的问题不是"贵",是<b>把水位顶起来了</b>。</li>
|
||||
<li>真正最贵的是<b>第 11、12 轮</b>:22.0% + 18.4% = <b>40.4%</b>。它们在 68 万的水位上,各跑了 <b>66 / 55 次</b>请求。</li>
|
||||
</ul>
|
||||
<div style="margin-top:10px;font-weight:800;color:var(--red)">
|
||||
⇒ 「顶水位」(第 1 轮)和「在高水位上跑请求」(第 11/12 轮)是两件事,后者更贵。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="mh"><span class="no">方法 ①</span><h2 class="mt">批量活写脚本</h2><span class="tag t-red">打在:请求次数</span></div>
|
||||
<table>
|
||||
<tr><th>干什么</th><td>同质的小改动(清注释、改文案、换版本号、批量改名)<b>不逐处 Edit</b>,写一个脚本一次做完。</td></tr>
|
||||
<tr><th>触发条件</th><td>同一会话内对同一文件 ≥3 次编辑,或待办本身是"同类 × N 处"。</td></tr>
|
||||
<tr><th>为什么根治</th><td>一次工具调用在历史里留两条记录(入参 + 结果),<b>后续每一轮都要重发</b>。<br>
|
||||
把 N 次编辑压成 1 次 ⇒ 历史里的工具痕迹从 <b>2N 条降到 2 条</b>,且不再随轮数放大。</td></tr>
|
||||
<tr><th>实测</th><td>出事那轮:<b>101 次请求</b>、142 次文件编辑、179 次命令,只为清 96 处同类注释。<br>
|
||||
改成 1 个脚本 ⇒ 请求数可降到个位数级别。</td></tr>
|
||||
<tr><th>现状</th><td><span class="state s-off">未实现</span> —— 属"操作纪律",不是硬门禁。</td></tr>
|
||||
<tr><th>代价</th><td>脚本要写对;一次动的地方变多 ⇒ 必须先 dry-run + 备份。写错的话影响面比逐处改大。</td></tr>
|
||||
<tr><th>落地</th><td>写进根 <code>CODEBUDDY.md</code> 的操作纪律;钩子里对"同文件第 3 次编辑"给提示。</td></tr>
|
||||
</table>
|
||||
<div class="fx">收益量级:单轮请求数 <span>101 → 10 上下</span>,该轮计费量随之腰斩再腰斩。</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="mh"><span class="no">方法 ②</span><h2 class="mt">拦大输出</h2><span class="tag t-grn">打在:每次请求的体积</span></div>
|
||||
<table>
|
||||
<tr><th>干什么</th><td>硬门禁(<code>PreToolUse</code>)拦 6 类<b>高置信度</b>命令:<br>
|
||||
<code>cat</code> 大文件(>200 KB)· <code>ls -R</code> · <code>find <根></code> 无 <code>-maxdepth</code> ·
|
||||
<code>journalctl</code> 无 <code>-n/--since</code> · <code>dmesg</code> 无 head · <code>Read</code> 超大文件(>400 KB)</td></tr>
|
||||
<tr><th>放行什么</th><td>一切带管道的写法(<code>| head</code> / <code>| grep</code> / <code>| wc -l</code>)——输出已被下游截断,不构成风险。</td></tr>
|
||||
<tr><th>为什么根治</th><td>上下文体积<b>只增不减</b>。一条 2 MB 输出进去,就永久占 2 MB,<b>再乘以后面每一次请求</b>。<br>
|
||||
⇒ 拦一条 = 省 <code>字符数 ÷ 2.11 × 剩余请求数</code> 的 token。</td></tr>
|
||||
<tr><th>实测</th><td>回放 <b>4,053 条真实命令</b>:误拦率从 1.63% 收到 <b>0.05%</b>(2 条,且都是真阳性)。</td></tr>
|
||||
<tr><th>现状</th><td><span class="state s-on">已上线</span> —— 配置已挂 <code>Bash|Read</code>,完全重启后生效。</td></tr>
|
||||
<tr><th>代价</th><td>低。带 <code>DSH_OUTPUT_GUARD_OFF=1</code> 应急开关;三档 soft / hard / off 可切。</td></tr>
|
||||
<tr><th>落地</th><td>无需额外动作,重启即生效。</td></tr>
|
||||
</table>
|
||||
<div class="fx">这是<span>唯一</span>在"灌进去之前"就把体积掐掉的手段,其余三个都是事后收敛。</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="mh"><span class="no">方法 ③</span><h2 class="mt">命令限流</h2><span class="tag t-amb">打在:高水位段的请求次数</span></div>
|
||||
<table>
|
||||
<tr><th>干什么</th><td>软告警(<code>UserPromptSubmit</code> 钩子):读本会话 usage,当"当前体积"或"单轮工具调用次数"越线时,<br>
|
||||
向下一轮注入一句话 ——「本轮已 66 次调用、水位 68 万,请收敛成一个脚本 / 一次批量操作」。</td></tr>
|
||||
<tr><th>为什么根治</th><td>①② 降的是<b>每次请求的体积</b>,③ 降的是<b>请求次数</b>。成本 = 次数 × 体积,<b>两个都要压</b>。</td></tr>
|
||||
<tr><th>实测</th><td>第 11 轮:<b>66 次请求 × 64 万水位 = 4,247 万</b>(占全量 22%)。<br>
|
||||
同轮若收敛到 10 次请求,量级掉到 640 万左右 ⇒ 省下全量的 <b>约 19%</b>。</td></tr>
|
||||
<tr><th>现状</th><td><span class="state s-off">未接入</span> —— 钩子本体已存在,限流提示被取消,尚未接回。</td></tr>
|
||||
<tr><th>代价</th><td>提示本身占约 50 token,可忽略;但它只是"劝",AI 不理会就无效 ⇒ 软约束。</td></tr>
|
||||
<tr><th>落地</th><td>把 <code>stop-dialog-guard.py</code> 的 <code>budget_note()</code> 重新接上(改一个 if 条件)。</td></tr>
|
||||
</table>
|
||||
<div class="fx">它治的是"<span>在水位已经很高时还猛跑请求</span>"这个最贵的模式。</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="mh"><span class="no">方法 ④</span><h2 class="mt">切会话</h2><span class="tag t-blu">打在:让体积归零</span></div>
|
||||
<table>
|
||||
<tr><th>干什么</th><td><b>一个需求一个会话</b>。需求闭环就新建会话,不开"万能长会话"。</td></tr>
|
||||
<tr><th>为什么根治</th><td>这是<b>唯一能让上下文归零</b>的手段。历史是 append-only,<b>AI 无权删自己的历史</b> ——<br>
|
||||
所以在一个 59 万水位的会话里"少说两句"完全没用,体积不会降一分。</td></tr>
|
||||
<tr><th>实测</th><td>目标会话末段水位 593,510。有几轮指令只有几个字(如"推送"),却仍要重发 <b>49.8 万 token</b>;<br>
|
||||
那一轮 9 次请求合计 <b>443 万 input</b>,就为了推一次代码。</td></tr>
|
||||
<tr><th>现状</th><td><span class="state s-off">纯纪律</span> —— 无工具强制;跨会话靠 <code>session-handoff/</code> 交接单。</td></tr>
|
||||
<tr><th>代价</th><td>开会话要读交接单(几十 KB 一次),会话内上下文会丢 ⇒ 交接单必须写全。</td></tr>
|
||||
<tr><th>落地</th><td>写进 <code>CODEBUDDY.md</code>:<b>一个需求一个会话;闭环即新建</b>。</td></tr>
|
||||
</table>
|
||||
<div class="fx">前三个是"<span>别让水位涨太快</span>",这一个才是"<span>把水位倒掉</span>"。</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="rel">
|
||||
<div><b>① 批量活写脚本</b>少留痕迹 —— 从源头减少历史条目</div>
|
||||
<div><b>② 拦大输出</b>别灌垃圾 —— 挡住单条巨型输出</div>
|
||||
<div><b>③ 命令限流</b>高位少跑 —— 压住高水位段的请求数</div>
|
||||
<div><b>④ 切会话</b>及时倒水 —— 唯一能归零的动作</div>
|
||||
</div>
|
||||
<div class="fx" style="margin-top:14px">共同根因:<b>历史只增不减,工具输出永久驻留,每轮全量重发</b>。
|
||||
①②③ 在会话内减缓累积,④ 才是重置。会话内解决不了根因本身 —— 那要靠平台的压缩策略。</div>
|
||||
</div>
|
||||
|
||||
<div class="foot">数据来源:目标会话转录逐条统计(443 次带 usage 的模型请求)| 折算比 2.11 字符/token</div>
|
||||
|
||||
</div></body></html>
|
||||
Reference in new issue
Block a user