Files

387 lines
28 KiB
HTML
Raw Permalink Normal View History

<!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>