Files
mcn-short-video/.workbuddy/memory/2026-10-08.md
T
maogeigei f77735b206 工作台:目录改名 mcn-workshop + 任务进度可见 + 会话归属配置化
1. 目录 mcn-work-shop -> mcn-workshop(空间分组名取会话 cwd 的目录名,只改字符串无效,必须真改名)
2. 全仓替换 mcn-work-shop -> mcn-workshop:37 文件 121 处;历史日志按沿革句规矩保留当时目录名
3. 任务进度可见:/api/run/status 产出 已运行时长/工具调用/最近动作/停滞判定(读会话日志尾部),前端新增右下角常驻面板,四处任务入口接入
4. 会话归属目录配置化:新增 config.sessionCwd(留空=工作台自身目录),现指向 D://AI技能//mcn-workshop,使任务显示为命名分组而非「未分组任务」
5. 修前端轮询静默缺陷:连续 3 次查询失败即提示服务断开(原逻辑静默空转到 15 分钟超时,用户零感知)
2026-10-08 13:09:06 +08:00

166 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# 2026-10-08
## 会话技能加载 + 项目记忆去重
- 按用户指令加载 `session-mechanism`:四类必读已读(`00-动手前必过.md` / `rules.md` / `manifest.md` / `01-文档索引.md`)+ SKILL.md 第一屏(触发句、第 0 步加载门槛、十条禁令、自决策白名单、现状地图)。
- 内化判据:**边界内自决策**(技术选型/实现路径/命名/调参/部署/排查/版本/兼容降级/文档技术内容);**只提报三类**(功能语义分叉/红线门禁/超边界:花钱·对外承诺·要凭据)。
- 项目 `MEMORY.md` 去重重组为 v4(17,841 → 17,315 B):新增 §0 两条指针(工作台 AI 通路 `docs/AI链路-本地CLI接入.md`、技能侧 WeKnora 调用);§2.1 补 09-29 AI 通路四条硬红线+两段流程模型来源;§4.2/§4.3 降级为「存根+硬红线」;清历史流水。
### 未决
- ⚠️ `MEMORY.md` 体积仍会被注入截断(阈值约 10–12 KB)。解法二选一:① 维持单文件(细节自动可见,但注入截断)② 拆专题文件(不截断,但细节失去自动注入)。**无客观优劣 ⇒ 待用户定**。
## 工作台「更新榜单数据」按钮排查(功能正常,是可见性问题)
- 现象:用户点击后「没有会话执行」。
- 取证(四处读数):宿主库 `automations` 有该 once 任务(ACTIVE、`next_run_at` 已过期、`model=hy4-preview`、`skills=["mcn-data-insight"]`);`automation_runs.status=IN_PROGRESS` + `automation_runtime_state.running=1`(⚠️ `running_conversation_id=null` 是常态,不代表没起会话);`sessions` 已建后台会话 `8a085794…`(bg=1,10:07:23);会话 jsonl 283 KB 且 mtime 持续更新(10:14),工具调用 30 Bash/14 Read/4 Write ⇒ **任务真的在跑**。`/api/dsh/ranking-update` 返回 `files=104`、`lastUpdateAt` 10:13:51 ⇒ 数据已在落盘。
- 真因两条(都与抓取逻辑无关):① 会话 `cwd = …\mcn-work-shop` ⇒ 落在侧边栏 **mcn-work-shop 空间分组**,不在用户当前空间,需切组才可见(这是设计,非 bug);② 工作台服务作为会话后台任务**被回收** ⇒ 前端轮询 `/api/run/status` 持续抛错。
- 修复:`public/data-pages.js` `updateRankingData()` 轮询加失败计数 —— 连续 3 次查询失败即提示「工作台服务已断开,任务仍在后台执行」并结束轮询(原逻辑静默 `continue` 到 15 分钟超时,用户零感知)。
- ⚠️ **排查方法论沉淀**:判「后台任务有没有在执行」的正确取證链 = `automations` → `automation_runs` → `automation_runtime_state` → `sessions` → 会话 jsonl 的 mtime/大小。⛔ 别只看 `running_conversation_id`(常为 null,会误判成没执行)。
## 工作台目录统一改名 `mcn-work-shop` → `mcn-workshop`
- 动因(用户):「分组已改为 mcn-workshop,需要全部替换;执行会话任务时需要按分组名称显示在会话列表」。
🔑 **机制**:WorkBuddy 侧边栏空间分组名 = 会话 `cwd` 的 **basename** ⇒ **只改字符串无效,必须真改名目录**。
- 执行五步:① 停服务释放句柄(否则 rename 必 `WinError 5`)→ ② `mv mcn-work-shop mcn-workshop`(成功)→ ③ 临时脚本全仓替换 **`mcn-work-shop`→`mcn-workshop`:37 文件 / 121 处**;⛔ 按**沿革句规矩跳过** `.workbuddy/memory/YYYY-MM-DD.md`(16 个历史日志保留当时目录名史实)→ ④ 修 2 处被替换改坏语义的注释(`server.js` L1265 / L1269,原「旧 mcn-workshop/新建 mcn-workshop」被换成同词,已补回三代路径)→ ⑤ 重启验证:`app.js`/`data-pages.js` 200,`data-pages.js` 含 `pollFail` 修复。
- ⚠️ **事实澄清(易踩)**:`D:\AI技能\mcn-workshop` **不是工作台**,是 09-01 路径拼接 bug 留下的**垃圾目录**(3 个 0 字节文件 + `outputs`)。工作台本体一直在仓库内 `…\V1.0\mcn-workshop`。sessions 表取证:该垃圾目录下**零会话**;未删除会话中仅 1 条 mcn-work 相关,cwd 仍是旧 `…\mcn-work-shop`。
- 净效果:新建后台会话 cwd = `…\V1.0\mcn-workshop` ⇒ 侧边栏分组显示 **`mcn-workshop`** ✅
- 残留:`project/…/subskills/mcn-data-insight/scripts/__pycache__/*.pyc`(二进制缓存含旧串,会自行重生成,无需处理)。
### 追加澄清(用户真实意图,⛔ 别再理解成"目录改名")
- 用户要的不是改目录名,而是:**工作台提交 WorkBuddy 定时任务后,产生的 AI 会话应归属哪个「空间分组」**。
- 机制:分组名 = 会话 `cwd` 的**目录名**;工作台侧由 `server.js` 的 `SESSION_CWD`(当前 = 工作台自身目录)决定 ⇒ **改目录名只是让分组名变成 `mcn-workshop` 的手段**,不是目的本身。
- ⚠️ **存量不掉头**:10:07 那条榜单更新任务 cwd 仍是旧 `…\mcn-work-shop` ⇒ 显示在**旧**分组;改名后**新建**的任务才落到 `mcn-workshop`。清理用的 `SESSION_CWD_PATTERN='%mcn-work%'` 覆盖新旧三代,不受影响。
- 待用户定(功能语义分叉):① 维持工作台专属分组 ② 归入用户当前主项目分组 ③ 自定义独立分组(需目录真实存在且在 workspaces 登记,08-27 即此法)。可选项:把 `SESSION_CWD` 提升为 `config.json` 可配字段,换分组只改配置不动代码。
## 后台任务进度可见(用户:「我需要看到 才知道执行是否正常」)
- 真需求:不是分组放哪,而是**在工作台内直接看到执行状态**,判断在跑还是卡住、做了什么。
- 后端 `server.js` `/api/run/status` 增强:automation → 关联它拉起的后台会话(`sessions.is_background_automation=1` 且创建时间落在任务创建 −15s~+300s)→ 读该会话 jsonl **尾部 256 KB** → 新增字段
`elapsedMs` / `sessionId` / `sessionTitle` / `lastAction`(最近一段文本,截断 160 字)/ `toolCalls`(按工具名计数)/ `updatedAgoMs` / `stalled`(>90 s 无新输出)/ `logBytes`。
- ⭐ 会话日志路径换算(可复用):cwd → projects 目录名 = **盘符小写 + 分隔符全转 `-`**(`escapeCwdForProjects()`);基目录 `<workbuddy>/projects/<转义名>/<sessionId>.jsonl`。
- ⚠️ `stalled` 只在 `state=running` 时有意义(已完成任务日志当然不再写入,会恒 true)。
- 前端新增 `window.TaskProgress`(实现在 `public/app.js`,样式 `style.css` 的 `.tp-*`):右下角常驻面板 = 状态/已运行时长/工具调用/最近动作/停滞警告。**四处轮询点全接入**:`app.js executeTask`、`data-pages.js` 的榜单更新/批量更新账号/视频解析。终态保留 8 秒自动收起,运行超时则保留供继续观察。
- 验证:三文件 `node --check` 全过;服务起来后 `app.js` 含 6 处、`data-pages.js` 含 9 处 `TaskProgress`,`style.css` 含 `.tp-panel`;状态接口返回完整进度字段。
### ✅ 分组归属定案:就用 `mcn-workshop` 文件夹对应的分组(已实测)
- 用户拍板:任务会话归到 **`mcn-workshop` 文件夹对应的那个分组**。
- 实测(提交轻量自检任务取证):新会话 `cwd = …\V1.0\mcn-workshop` ✅;对照 10:07 那条旧榜单任务仍是 `…\mcn-work-shop`(**两者在侧边栏分属不同分组**,属正常存量,不用管)。
- 结论:**不需要再改代码** —— `SESSION_CWD = ROOT`,目录改名后自动生效。自检任务已删排期。
- ⚠️ 认知要点:侧边栏分组按 **完整 cwd** 聚合,不是按文件夹名合并 ⇒ 同名不同路径会是两个分组。
### ⚠️ 更正(12:2x):分组靠 cwd **命中已打开的工作区**,光改目录名不够
- 上一条只对了一半:目录改名后会话 cwd 是**仓库内** `…\V1.0\mcn-workshop`,**不是用户的工作区** ⇒ 仍显示在「**未分组任务**」区域(用户实测反馈)。
- 用户纠正:`D:\AI技能\mcn-workshop` **才是那个工作区**。我此前仅凭目录里只有 3 个 0 字节垃圾文件就判它"是垃圾目录"——**没查登记表就下结论**,违反「判状态先问程序自己」。
- 🔑 **真机制**:侧边栏分组 = 会话 cwd **命中已打开过的工作区**才显示为命名分组;没命中 ⇒ 落「未分组任务」。
- 取证:`workspaces` 表仅 7 行且全是 `C:\Users\maidou\WorkBuddy\<时间戳>`,**不含**任何 `D:\AI技能\*` ⇒ 分组依据不是这张表,而是按会话 cwd 对已打开工作区的聚合。
- 修法(配置化,⛔ 不写死):`load-config.js` 增 `sessionCwd`(默认 `''` = 用 ROOT)→ `server.js` `SESSION_CWD = String(CFG.sessionCwd||'').trim() || ROOT` → `config.json` 设 `"sessionCwd": "D:\\AI技能\\mcn-workshop"`。**换分组只改配置,不动代码。**
- 实测:新会话 cwd = `D:/AI技能/mcn-workshop` ✅;任务在该工作区执行正常(done,结果"成功",73 秒)。对照 12:23 那条仍在仓库内路径 ⇒ 两组并存,旧的会随过期清理消失。
- ✅ 13:06 **二次模拟请求复验一致**(用户指示:不测真实功能,只发模拟请求看分组):写入 `cwds=["D:\\AI技能\\mcn-workshop"]`,会话 cwd 同样命中 ⇒ 配置化改法稳定。两次自检排期均已删,会话留在目标工作区供用户肉眼确认。
## 素材库补采:批 65 收尾 + 批 66/67 完成(30 卡)
- 承 09-29 进度:批 65(细节专业 15 卡)已 apply 完成,细节专业 40→55。
- **批 66(细节专业 15 卡)**:取材 31748 姜乘澜底妆 / 17511 笑笑易 / 19014 俊希做菜 / 24821 烟道逃生 / 25090 姜乘澜画眼线。✅ **本批 5 条原生即教程/实操题材**,与细节专业天然契合(不像批 65 从非专业题材硬提)。apply:接受 15/退回 0,问法改写 15 张平均 +32 字。细节专业 55→70。
- **批 67(自嘲反差 15 卡)**:取材 26484 路之坑爹感恩宴 / 31816 直男五合一洗护 / 31970 李普不离谱 / 21500 桃气小周买饭 / 26411 老弟受难日记。含 1 张**元层面自黑**卡(31970 全家崩溃时推销泡面)。apply:接受 15/退回 0,问法改写 15 张平均 +12 字。自嘲反差 56→71。
- 两批均走完整链路:`写 md → _tmp_bNN_spec.py 生成 spec → meta.json → build-pending → cat PROMPT_polish_v3.md + build-pending 组装 prompt → 派 doubao-seed-2-1-pro → _wNN.py 校验落盘 → apply-pending`。
### 本轮新踩坑
- ⚠️ **MCP `myai-mcp-production` 登录态会过期**:批 66 拉第 4 条时报「认证失败」。解法:先 `auth_status` 确认 → `feishu_login` 重授权(拉起浏览器)→ 重试即通。**连续拉多条详情时中途要留意**。
- ⚠️ **spec 脚本里别夹英文单词**:批 66 的 neg 句误写「等摊主 processing」,批量产出会污染语料。写完 spec 脚本前先自查。
### 当前缺口(修正后口径,2026-10-08 10:50)
| 标签 | 现有 | 缺口 | 候选 |
|:--|--:|--:|--:|
| 食材极致 | 8 | 92 | ⛔ 0 |
| 预见式服务 | 21 | 79 | ⛔ 2 |
| 感官沉浸 | 59 | 41 | 14 |
| 细节专业 | 70 | 30 | 22 |
| 自嘲反差 | 71 | 29 | 34 |
| 视觉冲击 | 76 | 24 | 10 |
达标:品质对比 110 / 氛围沉浸 106 / 反差 217 / 反常识 128 / 价值观冲击 108。
- ⛔ **卡点未解**:`食材极致`(缺口 92)与 `预见式服务`(缺口 79)候选池近乎空,占剩余缺口一半以上。需换关键词或换捞法才能推进。
- 暂存批累计 24 批(43–52、54–67,缺 53)。
## 补采续跑:批 68(感官沉浸 15 卡)/批 69(细节专业 9 卡)
**批 68 · 感官沉浸(33289 子杭自驾318 ×6 / 31773 萝卜乔乔英国留学回国 ×6 / 30090 姐弟双向送礼 ×3)**
- apply 结果:**接受 15/退回 0,问法改写 15 张,平均 +19 字**。感官沉浸 59 → 74。
- ⭐ **两条来源 0 卡,已在 md 写明判据**:`28012`(电焊工试戏)男主嘴配的「滋滋」是**谐音梗的音效**而非真实声音质感,事件主角是"身在曹营心在焊" → 归 `反常识`/`自嘲反差`;`28415`(男朋友的算计)踩碎眼镜/摔筷子/拍桌的动静是**发疯甩锅的附产品** → 归 `反差`/`价值观冲击`。
- 感官通道分布齐全(冷/缺氧失眠/冰雹砸车/撕羊腿/热水澡/久坐腰酸 · 热瓶/苦到脸垮/中药味/红米肠/赶机喘/长途疲惫 · 中暑虚脱/猛灌水/红糖蛋滋啦)。
**批 69 · 细节专业(28328 海鲜蒸汽 ×4 / 37316 巧克力棒手作 ×5)= 9 卡**
- apply 结果:**接受 9/退回 0,问法改写 9 张,平均 +20 字**。细节专业 70 → 79。
- ⭐ **本批 3 条来源 0 卡,坚持不凑数**(沿用批 56 口径):
- `20461`(机票骗局)—— 骗子的"专业"是**话术与骗术设计**(伪装客服、真退票取信、连环索要卡号),不落在"这一下换普通人来做就做不到位"的操作细节上 → `反差`/`反常识`。**这是细节专业最容易误判的一类:话术专业 ≠ 操作专业。**
- `24820`(烟道脱困)—— 主角是**作死翻车与体力消耗**,片尾"专业人士、专业场地、专业操作"是**反讽** → `自嘲反差`/`难度极限`;且同账号同题材 `24821` 已在批 66 取过两卡,本条脱困手法同构,不重复入库。
- `22163`(轮椅游渔岛)—— 倒拖轮椅过软沙滩属**体力付出** → `难度极限`;其余为情侣互坑 → `反差`/`自嘲反差`。
- ⭐ **取批命中率下降的信号**:细节专业候选池看着有 22 条,但主队列取 5 条只有 2 条可用(命中率 40%)。后续该标签每批卡数会低于 15,属正常,不要为了凑 15 张硬塞。
**新增踩坑与复用做法**
- ✅ **`_wNN.py` 用 sed 复用**(`sed -e 's/_raw68/_raw69/g' -e 's/pending_0068/pending_0069/g' -e 's/^N = 15/N = 9/' _w68.py > _w69.py`)—— 卡数变化时记得同步改 `N`。本轮连续 3 批(67/68/69)一次通过,`bad: []`。
- ✅ **超长详情自动落盘**:`33289` 返回 88,150 字符超限 → 工具落盘到 `D:\.workbuddy\projects\d-AI技能-mcn-short-video\<sessionId>\tool-results\*.txt`,用 Read 直接读(该文件只有 117 行,一次读完)。
- ⚠️ 派模型改用「让 Agent 自己 Read prompt 文件 → 自己 Write 落盘 `_rawNN.txt`」——比让模型把 JSON 回吐到对话再手工落盘更省上下文,连续 3 批稳定。
**当前缺口(批 69 后)**:自嘲反差 29(候选 34)|感官沉浸 26(候选 14)|视觉冲击 24(候选 10)|细节专业 21(候选 22)。食材极致 92/预见式服务 79 仍挂起(用户 2026-10-08 决策:先不处理)。
## 批 70(自嘲反差 15 卡)· 本轮第 5 批 → 复核点
**结果**:接受 15/退回 0,问法改写 15 张,平均 **+27 字**(前几批 +19/+20,本批问法增厚更充分)。自嘲反差 71→**86**(缺口 14)。
**来源**:35376 唐轩乌龙×6 | 35301 丁浩普信男×6 | 28146 妈妈硬核推理×3。**5 条视频里 2 条 0 卡**(33593、21923)。
**⭐ 自嘲反差口径新增形态「姿态过载」**(本批确立):
- 定义:主角自己把一件小事升格到远超其体量的规格,且全程一本正经(妈妈把女儿回家晚做成刑侦审讯:北风4级/100米/11分37秒 vs 6点12分39秒)。
- 判据仍落回一句话:**笑点落在主角自己身上**。妈妈是主角、推理的荒谬感由她自己一本正经地制造 → 收。
- ⛔ 反向凡尔赛揭晓(语数英 100/98/100 + 兑现平板)→ 笑点在预期被颠覆 → `反常识`;儿子 O 型嘴石化 → 笑点落在旁观者受击 → `反差`(世界参差)。均不收。
**两条 0 卡判据(写入 meta)**:
- **33593(张开父子的下场)**:家族护短复仇爽剧,爽点落在反派父子被制裁上,主角阵营全程降维打击,无一处笑点落在自己身上 → `反差`/`价值观冲击`。其「悲痛段落硬塞洗面奶口播」虽属元层面自黑,但批 67 已收同构卡(C1 全家崩溃时推销泡面)→ 不重复入库。
- **21923(消防设施瘫痪的代价)**:真实事件改编的沉重社会悲剧(烟花引火→消防栓被锁→接头拧不上→母亲丧生→儿子一夜白头),全片无笑点 → `价值观冲击`。
- ⭐ **通用判据沉淀**:**沉重社会悲剧类一律不进自嘲反差**;**元层面自黑要查是否已收同构卡**(批 67 已占一类)。
**其他弃卡判据**:35376「你那算盘珠子都崩我脸上了」是吐槽他人 → `反差`;「我没吃过我妈包的包子」与「我妈不会做饭」是同一场乌龙的两端 → 分作「立 flag」与「揭盅」两卡,不合并(同批 67 B2/B3/B4 细分做法)。35301 防晒植入段属商业植入;丽丽怒骂并入 B4 不单列。
**本轮 5 批(66–70)合计 69 卡**:66=15 | 67=15(自嘲反差)| 68=15(感官沉浸)| 69=9(细节专业,不凑数)| 70=15(自嘲反差)。
**当前缺口(批 70 后)**:感官沉浸 **26**(候选 14)→ 视觉冲击 24(10)→ 细节专业 21(22)→ 自嘲反差 14(34)。食材极致 92/预见式服务 79 挂起。
**体检**:`5_tag_coverage.py` 卡片总数 1017(源文件 69 个),12 维无自造标签;⛔ 零覆盖仅 `难度极限`(用户决策不处理),⚠️ 偏少仅 `食材极致`(8,挂起)。
**复核抽查**:批 70 卡 6(哭着嚼坚果上供)润色生效——台词前置 + 破折号拍点(「我以后再也不养小仓鼠了,我对不起你」——唐轩把相框平放在桌上…),锁定 4 字段零改动,无编造。spec 回写 sim=3/neg=2、tag 单一、src 3 个,全部符合。
**⭐ 入库状态核查(用户问「素材都同步到 wekonra 了吗」→ 答案:没有,两层原因)**
- **流程层**:按暂存模式,补采只落 `batches/pending_import/`,统一入库必须跑 `13_import_pending.py --run`。截至批 70,**27 批 397 卡全部堆在暂存区,一条都没进库**(`13_import_pending.py --help` 盘点:27 批/397 卡/异常 0)。`batches/*.md` 41 批(早期跑过 `2_import_batch.py` 的)是唯一进过库的部分。
- **环境层**:`WeKnora-app` 容器 **Exited (127)**,最后一条日志停在 **2026-09-29 23:53**(health 200)→ 31607 端口不通,连"库里现有多少条"都查不了。同项目其余容器(frontend 24719 / docreader / postgres / redis)都在跑。**整个 10-08 的补采从未连过库。**
- **部署信息**:镜像 `wechatopenai/weknora-app:latest`,restart `unless-stopped`,compose 工作目录标签 `/home/maidou/weknora`(WSL 路径,本机 `docker ps -a` 可见)。拉起命令 `docker start WeKnora-app`;若仍 127 需查 compose 的 command/entrypoint。
- **回炉链路同样未完成**:「删旧条目 + 重导 + 检索终验」都还没做;回炉批 31–33 也未跑完。
- ⚠️ 排查口诀补充:**问「同步了吗」时先查两处**——① `13_import_pending.py --help` 看暂存盘点 ② `docker ps -a` 看 WeKnora-app 是否 Up。两者都要看,只查一处会误判。
**⭐ 规则核对:入库前必须补全 question(用户 2026-10-08 追问「是否遵循」→ 核查 27 批结论)**
- **规则依据(能查到的最强出处)**:RUNBOOK §步骤 6「写 spec.json(检索问法)」=流水线必经步骤;**真正的动机在 RUNBOOK 红线**:「追加相似问不重建索引(`POST /faq/entries/{id}/similar-questions` 返回 200 但向量分数按位不变)→ **要改问法只能删 + 重导**」。所以「入库前补全」不是形式要求,而是**事后补的代价=整批删+重导**。
- ⚠️ **未找到该规则的原始对话出处**:`conversation_search` 两次 0 命中,本地 memory / RUNBOOK 也没有「入库前要补全 question」这句原话。**已按「规范 + 红线」等价认定,未擅自当作既有明文规则引用。**
- **核查结论(27 批全量)**:全部有 spec.json、`std` 全部非空 → **形式上 100% 遵循**。但有 **2 批偏离现行规范**:
| 批 | 偏离 | 范围 |
|:--|:--|:--|
| **43** | `neg=3` 条(现行应为 2),且内容是**关键词短语**(「白酒带货 / 餐厅推荐 / 商务礼仪培训」)不是问句 | 15/15 卡 |
| **48** | `sim=2` 条(现行应为 3) | 15/15 卡 |
其余 25 批:sim=3(批 47 起)或 sim=6(批 44/45/46,合旧规范)/neg=2,全部合规。
- ⚠️ **文档与执行脱节(待用户裁定)**:RUNBOOK §步骤 6 明文写 **sim「口语问法 4–6 条」**,但**批 47 起实际统一为 3 条**(`_wNN.py` 校验也写死 `!= 3`)。要么改文档、要么把 sim 补回 ≥4 条。
- ⭐ **核查脚本口诀**:`glob('batches/pending_import/batch_*.spec.json')` 逐批 `Counter(len(x['sim']))` + `Counter(len(x['neg']))` 打分布表,一眼看出条数偏离;**别用 md 的 ```text 计数 //2 算卡数**(会算成一半,以 spec 长度为准)。
**⭐ Docker WeKnora 启动失败排查(2026-10-08 · 已修复)**
- **根因(不是应用崩溃,是容器 init 前的 bind mount 失败)**:`docker inspect WeKnora-app` 的 `.State.Error` 给出原文——
`OCI runtime create failed: runc create failed: ... error mounting "/run/desktop/mnt/host/wsl/docker-desktop-bind-mounts/Ubuntu-24.04/4f4eee8c…" to rootfs at "/app/config/config.yaml": not a directory: Are you trying to mount a directory onto a file (or vice-versa)?`
- **链路**:compose 写 `./config/config.yaml:/app/config/config.yaml`(文件→文件,写法正确,源文件 `/home/maidou/weknora/config/config.yaml` 确实存在且是 6545 B 的文件)→ 但 **Docker Desktop 的 bind-mount 代理层**(WSL 9P 中转,路径被哈希成 `4f4eee8c…`)把源**识别成了目录** → 挂载失败 → 容器连 init 都没进 → **Exit 127**(entrypoint `./scripts/docker-entrypoint.sh` 根本没执行)。
- **时间线**:StartedAt 09-28 01:45,FinishedAt **10-08 02:51** —— 正常跑了 10 天,今天凌晨有一次重启尝试(Docker Desktop/WSL 层变动)时代理层状态不对而失败。restart `unless-stopped` 不会救「启动失败」,所以一直停在 Exited。
- ✅ **修复:`docker start WeKnora-app` 一次即恢复**(`Up (healthy)`,31607 通)→ 属**代理层临时状态问题**,重试可解。复发时的升级路径:重启 Docker Desktop → `docker compose up -d` 重建容器(会换新的代理哈希)。治本方向:单文件 bind mount 跨 WSL 本就脆弱,可改 `docker cp` 或 volume。
- ⭐ **排查口诀:容器 Exited(127) 先看 `docker inspect --format '{{.State.Error}}'`**,不要只看 `docker logs`(日志只到上次正常运行,看不出启动失败原因)。本次日志最后一条停在 09-29 23:53 health 200,完全看不出问题,是 inspect 的 State.Error 直接给出根因。
**库内实测(服务恢复后)**
- FAQ 总数 **631**(分页抖动,去重拉取 624)。**问法 100% 齐全**:无 std 0 / 无 sim 0 / 无 neg 0。
- sim 条数分布 `{5:312, 6:222, 3:46, 2:30, 4:14}`、neg `{2:542, 3:82}` —— 历史口径混着(旧 5/6 条、现 3 条、少量 2 条)。
- 库内 tag_name 分布:反差 172/反常识 117/价值观冲击 93/视觉冲击 67/自嘲反差 44/细节专业 32/感官沉浸 26/氛围沉浸 24/品质对比 19/预见式服务 19/食材极致 9/难度极限 2 → **与本地统计一致,即已入库 = 早期 41 批;暂存 27 批 397 卡确实未进。**
- ⚠️ **接口结构坑**:FAQ 列表返回是 **`data.data`**(`{data:{total,page,page_size,data:[...]}}`)两层嵌套,只取一层会得到空列表;条目标签字段是 **`tag_name`**(不是 `tag`/`tags`);`page_size=total` 一次拉全会返回空 → 必须分页 100 逐页 + 按 id 去重。
**新增踩坑**
- ⚠️ `_tmp_bNN_spec.py` 里卡片名提取必须 **`.replace("###","")`**:`head.split("|")[0].strip()` 会带 `### ` 前缀 → `assert name in Q` 报 `AssertionError: ### 吃包子吃出丧母感`。批 70 踩到一次,加 replace 后通过。
- ✅ prompt 组装字节数核对:模板 7,683 + 载荷 34,647 = 42,330(`wc -c` 可验),比批 68 记录的 15,772 大是因当时统计口径不同,**以 `wc -c` 为准**。