Files
dsh_ai1net_server/docs/会话与接续/会话复盘_AI为何上抛_20260916.html
admin ce8e6ceed9 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)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

388 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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/&lt;目录名&gt;/&lt;sid&gt;.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>