新增 mcn-workshop/docs/会话任务链路-待优化项.md:4 项待优化(任务模型浮动/面板跳转入口/任务权限/旧会话归位)+红线+已修历史。 同步:项目 MEMORY.md 权威源表加该文档指针,工作台红线补 workspace_scope 一条。 理由:只写进当天日志的规矩换会话读不到,必须落到工作台文档与长期记忆。
279 lines
39 KiB
Markdown
279 lines
39 KiB
Markdown
# 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 同样命中 ⇒ 配置化改法稳定。两次自检排期均已删,会话留在目标工作区供用户肉眼确认。
|
||
|
||
## 提交(13:09)
|
||
- commit **`f77735b`**:目录改名 + 121 处替换 + 任务进度面板 + 会话归属配置化 + 前端轮询缺陷修复。提交前已查:仓库内**无 API key**(`grep -rl "ck_fzpjin5c0iyo"` 空)、新目录**未被 gitignore 误伤**(`git check-ignore` 无输出)。工作区干净(0 未提交)。
|
||
- ⚠️ **push 失败(非权限问题)**:`ssh work.alotbuy.com:222` 连接超时 —— 公司内网 git 服务器,当前网络不通。走 10800 代理也失败:Git Bash **没有 `connect` 工具**(`ProxyCommand` 用不了)。
|
||
- 待办:回到公司网络/VPN 后跑 `git push origin main`(本地提交已就位,领先 origin 1 个)。
|
||
|
||
### 🔴 新远程地址实测:能连通,但**两条历史无共同祖先**(已停手,未推送)
|
||
- 用户给新地址 `[email protected]:admin/mcn-short-video.git`(旧 `work.alotbuy.com:222` 超时)。实测 **能连通**:`ls-remote` 成功,远程 main = `e738dd1`(2026-10-07 10:48)。
|
||
- 🔴 **`git merge-base main FETCH_HEAD` 为空** ⇒ 无共同祖先;两边各 **253** 个提交,`rev-list --left-right --count` = **253 / 253**。
|
||
- 本地最早 2026-08-06「Full upload: MCNVideo AI project」,远程内容同源但 **hash 全不同** ⇒ 历史被重写过,是两条独立线。
|
||
- 远程有本地没有的提交(如 10-07 那条 gitignore 补充),说明远程不等于"落后于本地"。
|
||
- ⛔ **不能直接 push**:普通 push 必被拒(非 fast-forward);**强推会抹掉远程 253 个提交,不可逆**。
|
||
- 待用户定:① 推到远程**新分支**(安全,推荐)② 合并两条历史(`--allow-unrelated-histories`,冲突量极大)③ 强推覆盖(⛔ 需明确授权)④ 不推,等原地址恢复。
|
||
- ⚠️ 排查教训:换远程地址前必须先看 `merge-base`,⛔ 不能因为"地址能连通"就推。
|
||
|
||
## 合并两条历史并完成同步(15:4x)
|
||
|
||
- 合并 `admin/main`(两条独立历史、无共同祖先):冲突 **20 文件 47 块**。
|
||
- **40 块自动**:规则①一边为空 → 取非空(纯新增,取并集);规则②ours 含 `mcn-workshop` 且 theirs 含 `mcn-work-shop` → 取 ours(目录改名,本地最新)。
|
||
- **7 块人工**(全在 `MEMORY.md`):本地 v4 精简版覆盖远程 v3,远程的详细条目在 RUNBOOK/技能文档均有指针 ⇒ 取本地。
|
||
- ⚠️ 脚本坑:冲突标记正则**必须写 `\r?\n`**(工作区是 CRLF),第一版漏了导致 0 匹配。
|
||
- ⭐⭐ **合并的隐藏副作用(换线合并必查)**:远程那条线的工作台目录仍是旧名 ⇒ 合并后**旧目录 `mcn-work-shop` 被整个带回**,两套代码并存。
|
||
- 取证三步:① `diff -rq` 显示旧目录**零独有文件**;② `server.js` 报 2672 行差异,实为**行尾差异**(旧 CRLF / 新 LF),`diff --strip-trailing-cr` 后仅 **206 行**;③ 其中旧目录独有的 40 行**全是本地已升级掉的旧代码**(旧版凭据解析 / 旧状态接口 / 写死的会话目录)⇒ 远程无本地缺失的实质改动。
|
||
- 结论:安全删除,已 `git rm -r -f`(合并暂存态需 `-f`)。
|
||
- 推送:`git push admin main` = **快进** `e738dd1..67317c4`(合并后远程变成本地祖先,**不是强推**)。本地与 admin 已完全一致(`rev-list --left-right --count` = `0 / 0`)。
|
||
- ⚠️ 旧 origin(`work.alotbuy.com:222`)仍 **ahead 256**,网络不通未推。
|
||
|
||
## 旧远程清理(16:1x,用户:「旧的远程没用了」)
|
||
|
||
- 处理三步:① `git remote set-url origin` 指向 `[email protected]:admin/mcn-short-video.git`——**保留默认远程名 origin**,脚本与命令习惯不用改;② 删除冗余别名 `admin`(与 origin 同址);③ 删除随旧远程留下的本地分支 `admin-main`(= e738dd1,是 main 的祖先,零独有内容)。
|
||
- 验证:只剩 origin 一个远程;本地与 origin 完全一致(`0 / 0`)。
|
||
- 📌 **远程口径(本项目现行)**:`[email protected]:admin/mcn-short-video.git`(旧 `work.alotbuy.com:222` 已废弃)。
|
||
|
||
### 🔴🔴 再修正:后台任务会话**不进「空间」分组**(这才是真机制,前两轮都猜错了)
|
||
- 现象:cwd 已改成 `D:\AI技能\mcn-workshop`,会话仍落在「**未分组任务**」。
|
||
- 取证(sessions 表):`group_id` / `group_title` **全为 null**;`project_id` 也全 null ⇒ 分组不靠这三个字段。
|
||
- ⭐ 真因:**侧边栏是「空间区」与「任务区」两套**——
|
||
- **手动会话**(`is_background_automation` 为 0/null)→ 按 cwd 聚合成**空间分组**(如 mcn-short-video、vibe-video-analysis);
|
||
- **后台任务会话**(`is_background_automation=1`)→ 一律进**任务区**,与 cwd 无关。
|
||
- 工作台 `/api/run` 提交的任务会话**全是 bg=1** ⇒ **改 cwd 永远改不了它进哪个区**。
|
||
- 想让「mcn-workshop 空间」出现在侧边栏:**在 `D:\AI技能\mcn-workshop` 手动开一个会话**(手动会话按 cwd 聚合才会生成该空间);但后台任务仍会在任务区,属产品设计。
|
||
- ✅ 正解(已落地):看后台任务执行状态用**工作台右下角进度面板**,不必依赖侧边栏分区。
|
||
- ⚠️ `app.asar` 里 grep「未分组」= **0 次** ⇒ 该文案在别处生成(未继续逆向)。
|
||
|
||
### ✅✅ 真根因并修复:`automations.workspace_scope` 漏填(不是 cwd、不是工作区登记表)
|
||
- 逆向线索:`is_playground` 赋值处 = `typeof e.space?.autoGenerated=='boolean' ? +!!e.space.autoGenerated : null` ⇒ 「空间是自动生成的」才落未分组。
|
||
- 交叉表取证(决定性):
|
||
- 走**正规 automation 工具**创建的任务:**`workspace_scope='workspace'`(36 条)** ⇒ 会话 `is_playground=0` ⇒ 正常进工作区分组;
|
||
- **工作台直接写库**的任务(`createOnceAutomation` + `/api/run` 两处 INSERT):**`workspace_scope` 为 null(当天 5 条)** ⇒ 会话 `is_playground=1` ⇒ 落「未分组任务」。
|
||
- ❌ 排除的假因:改 cwd(无效)、把目录登记进 `workspaces` 表(无效,该表不是决定因素;登记行已保留,无害)。
|
||
- 🔧 修复:`server.js` 两处 `INSERT INTO automations` 增加 `workspace_scope` 列并填 `'workspace'`(`createOnceAutomation` 约 L407、`/api/run` 约 L879)。
|
||
- 实测:新任务 `scope=workspace`、新会话 **`is_playground=0`** ✅(对照组仍是 1)。
|
||
- 📌 **红线**:工作台凡直接写宿主 `automations` 表,必须带 `workspace_scope='workspace'`,否则会话一律落「未分组任务」。
|
||
|
||
## 会话任务链路审计(用户:「看看哪些需要调整」)
|
||
|
||
**已修**:4 个轮询点口径统一(此前只有榜单更新一处有容错)。
|
||
- 轮询点 = `app.js executeTask`(通用 AI 任务)/`data-pages` 的榜单更新、批量更新账号、视频解析。
|
||
- 原先 3 处是 `catch(e){continue}`,服务被回收时静默空转 15~35 分钟才超时 ⇒ 用户只看到按钮转圈。现均为「连续 3 次查询失败 → 提示服务断开 → 结束轮询」。
|
||
|
||
**已确认健康(无需改)**:
|
||
- 写宿主 `automations` 表**仅 2 处**(`createOnceAutomation` 约 L409、`/api/run` 约 L880),均已带 `workspace_scope`。
|
||
- `submit-review-tasks.cjs`、`check-run-status.cjs` 走 HTTP 接口/只读,不直接写库 ⇒ 自动被修复覆盖。
|
||
- 技能挂载(`skills_json`)、浏览器锁约束、会话清理(`SESSION_CWD_PATTERN='%mcn-work%'` 仍覆盖新 cwd)均正常。
|
||
|
||
**待用户定(各有权衡)**:
|
||
1、**任务模型浮动**:`model_id` 取 `sessions` 最近活跃**手动**会话的 model,与 `config.ai.cli.model`(deepseek-v4.1-flash)**无关** ⇒ 同一任务换次数可能换模型,产出不稳定。建议加 `taskModel` 配置(留空=跟随现状,填了=固定)。
|
||
2、**进度面板缺「打开会话」入口**:能看状态但跳不进会话。
|
||
3、**任务权限恒为 `fullAccess`**(可写文件/执行命令):是否收紧取决于任务类型。
|
||
4、**修复前创建的旧会话仍 playground=1**:改库可归位,但需重启 WorkBuddy 才生效。
|
||
|
||
## 素材库补采:批 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 去重。
|
||
|
||
**⭐⭐ 27 批 397 卡统一入库完成(2026-10-08 · `13_import_pending.py --run`)**
|
||
- **结果**:成功 27 批 [43–70 除 53] / 失败 0。**库内 631 → 1028,增量 397 == 暂存卡数**,完全对上(判据用库内增量,不用 SUMMARY 的 success 数)。问法完整性:无 std 0/无 sim 0/无 neg 0。
|
||
- 导入后 tag:反差 224 / 反常识 132 / 品质对比 109 / 价值观冲击 108 / 氛围沉浸 105 / 自嘲反差 85 / 细节专业 79 / 视觉冲击 76 / 感官沉浸 70 / 预见式服务 22 / 食材极致 9 / 难度极限 2。
|
||
- ⚠️ 注意:**库内 tag 数与本地 `5_tag_coverage.py` 统计不同**(例:感官沉浸本地 74 vs 库内 70)—— 库内按 spec 的单值 `tag`(每卡主标签),本地按卡片事件标签字段(含辅标签计数)。**两者口径不同,不要互相校验。**
|
||
|
||
**🔴 新踩坑(重要,会静默导致整批导入失败):spec.json 的 `card` 字段必须是「标题行」,不是卡片全文**
|
||
- **现象**:首次 `--run` 成功 13 批 [43–47,50–58],失败 14 批 [48,49,59–70],报错 `✗ spec 与 md 对不上:[整张卡片全文]`。
|
||
- **根因**:`2_import_batch.py` L40–48 的比对是
|
||
`cards={}` → `t = b.splitlines()[0].replace("### ","").strip()` → `cards[t] = b.strip()` → `miss = [s["card"] for s in spec if s["card"] not in cards]`
|
||
⚠️ **`cards` 是 dict,`in` 判的是 key(=标题行),不是 value** → 所以 **spec 的 `card` 必须等于「去 `### ` 前缀后的标题行」**,放全文必然 miss。
|
||
- **我犯的错**:`_tmp_bNN_spec.py` 用 `CARD_RE` 提整张卡塞进 `card`(且带 `### `)→ 批 66–70(及此前 59–65、48、49)全部中招。对比成功的批 58:`card` = `"20829-1|凌晨三点半看完球…"`(仅标题)。
|
||
- **修复**:对 14 批执行 `sp['card'] = blocks[i].splitlines()[0].replace('### ','').strip()` 后重跑 → 27/27 成功。
|
||
- ⭐ **更新铁律表述**(旧的「card 禁带 `### ` 前缀」不够精确,易误解成"全文去前缀"):**`card` = 标题行本身(去 `### `、strip),不含正文字段。**
|
||
- ⭐ **排查口诀**:报「spec 与 md 对不上」时,**不要读那一大坨报错文本**(它把整卡打印出来,看不出差异),直接复现 L40–48 的两行逻辑比对 `spec[0]['card']` 与 `cards` 的 key。
|
||
- ⚠️ **apply-pending 不会修 card**:`10_polish_batch.py` L412–413 只回写 `sp["std"]/["sim"]/["neg"]`,**不动 `card`**(标题润色前后不变,所以不修是对的)。**card 的正确性全在生成 spec 那一刻**,写错只能事后修。
|
||
|
||
**导入后的遗留项**
|
||
- 批 48 的 `sim=2`(少 1 条)**已随导入进库** → 按红线「改问法只能删+重导」,若要补须删该批 15 条重导。**未处理,待用户定夺。**
|
||
- 批 43 的 `neg=3`(且是关键词短语)—— **neg 不入索引**(RUNBOOK §步骤6),对召回无影响,可不处理。
|
||
- 回炉链路仍未完成:删旧条目(按 `polish_plan.json` 冻结的 526 id)+ 重导 + 检索终验。
|
||
|
||
**⭐ 方向性判断:「现在要不要测精准率/召回率」(2026-10-08 用户问,结论待执行)**
|
||
- **结论:分拆看,不要笼统测。**
|
||
| 指标 | 该不该测 | 理由 |
|
||
|:--|:--|:--|
|
||
| **召回率(反查法 R@1/R@3)** | ⚠️ **只做回归验证**,不做优化基线 | 有 ground truth(卡自身)、能隔离「问法」变量、成本 2 分钟。但历史基线已是 R@1 34/35,问法改写实测差异在噪声内 → **测了也难再提升**,价值只剩「验证 397 卡导入后检索没坏」。 |
|
||
| **精准率** | ❌ **暂不必测** | 需人工标注「top-N 每条是否相关」,1028 条库成本极高;且分数间隔极窄(top1~top10 仅差 0.047)→ 机器判不了优劣,只能人工。已知不是瓶颈。 |
|
||
| **覆盖率**(真实需求节点 → 能否找到素材) | ✅ **该测,且是当前唯一真瓶颈** | 3/5 真实节点零命中 = 素材覆盖不足。刚导入 397 卡(631→1028),覆盖率已变,**正是重新测的时机**。 |
|
||
- ⭐⭐ **根本前提(决定一切检索指标的传导性)**:**素材召回端到端触发率 ≈ 0**,且用户已裁定这是「流程不主动召回、知识库按需被取」的**设计意图**,`hybrid_search` 0 次 ≠ bug。
|
||
→ **推论:检索指标再好,也传导不到最终产出**(优化一个几乎不被调用的函数)。所以「测检索指标」的价值**只在回归验证**,不在效果优化。
|
||
- ⭐ **刚导入 397 卡后必做的不是指标测试,而是「入库正确性验证」**:确认新卡真能被检索到(不是精准率/召回率,是「导入成功了吗」)。
|
||
|
||
**⭐ 入库验证 + R@N 基线(2026-10-08 · 反查法,81 条样本)**
|
||
- **结论:R@1 = 81/81 = 100%**,27 批全部命中 → **397 卡导入成功,检索链路正常**。明细 `recall_cache/verify_import_1028.json`,脚本 `_tmp_verify_import.py`(可复用,支持 `--dry`)。
|
||
- 抽样:27 个暂存批 × 每批首/中/尾 = 81 条,覆盖 9 个标签。查询源 = **卡片正文「极致内容」**(不在索引里,索引只有 std/sim;neg 不入索引)→ 与 std 不同源,比 18b 的「从 standard_question 提取事件」更远一步。
|
||
- ⚠️ **这个 100% 不能当「召回质量好」的证据**:std 本就是由卡片正文改写而来,正文查自己属**同源查询,存在天花板效应**。**它只证明「入库成功 + 检索没坏」,不证明「问法有效」。** 与历史基线(18b 反查法 R@1 34/35 ≈ 97%)量级一致,属正常。真要判召回质量仍需异源测试(真实编导需求节点)。
|
||
|
||
**🔴 新踩坑(极具迷惑性,差点得出「召回率 0%」的错误结论)**
|
||
- **检索接口路径与参数名**:正确是 **`POST /knowledge-bases/{KB}/faq/search`**,body 用 **`query_text`**(不是 `query`),返回 **`r.get("data") or r.get("results")`(直接是列表,不是嵌套 `data.data`)**。
|
||
- 我写成了 `/faq/search` + `query` → **HTTP 404**。
|
||
- ⚠️ **坑的杀伤力**:404 被 `try/except` 静默吞掉 → 每条都记成「未命中」→ 输出 **R@1 = 0/81 = 0%**,看起来像「导入失败/检索全崩」。
|
||
- ⭐ **识别口诀**:**跑完先看耗时**。81 条检索正常约 14s;若「0 命中且耗时 1s」→ 一定是异常被吞了,不是真实结果。**批量检索脚本必须让异常显式暴露(打印 err),不要静默记为未命中。**
|
||
|
||
**新增踩坑**
|
||
- ⚠️ `_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` 为准**。
|