fix: 工作台任务补 workspace_scope=workspace,修复会话落「未分组任务」

根因:工作台直接写宿主 automations 表时漏了 workspace_scope 字段。
对比取证:走正规 automation 工具创建的任务该字段为 'workspace'(36 条)→ 会话 is_playground=0 → 正常进工作区分组;
工作台写的为 null(当天 5 条)→ is_playground=1 → 会话落「未分组任务」。
修复:server.js 两处 INSERT INTO automations(createOnceAutomation / /api/run)补该列并填 'workspace'。
实测:新任务 scope=workspace、新会话 is_playground=0;对照组仍为 1。
排除的假因:改 cwd 无效;登记 workspaces 表无效。
This commit is contained in:
maogeigei committed 2026-10-08 17:09:58 +08:00
1 parent a4b2cb1d4f
commit a3ca08eabd
2 files changed
+52 -6

No files matched your search

+43
View File
@@ -89,6 +89,27 @@
- 验证:只剩 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'`,否则会话一律落「未分组任务」。
## 素材库补采:批 65 收尾 + 批 66/67 完成(30 卡)
- 承 09-29 进度:批 65(细节专业 15 卡)已 apply 完成,细节专业 40→55。
@@ -213,6 +234,28 @@
- 批 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` 为准**。
@@ -404,9 +404,11 @@ function createOnceAutomation(name, prompt, skillsArr, connectorIdsArr) {
const ownerUserId = uidRow ? uidRow.user_id : '';
const modelRow = db.prepare(`SELECT model FROM sessions WHERE deleted_at IS NULL AND COALESCE(is_background_automation,0)=0 AND model IS NOT NULL AND model <> '' ORDER BY COALESCE(last_activity_at, updated_at) DESC LIMIT 1`).get();
const modelId = (modelRow && modelRow.model) ? modelRow.model : 'auto';
db.prepare(`INSERT INTO automations (id,name,prompt,status,schedule_type,scheduled_at,next_run_at,rrule,cwds,created_at,updated_at,skills_json,connector_ids_json,model_id,permission_mode,owner_user_id,owner_status)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
.run(id, name, prompt, 'ACTIVE', 'once', scheduledAt, nextRunAt, '', JSON.stringify([cwd]), now, now, JSON.stringify(Array.isArray(skillsArr) ? skillsArr : []), JSON.stringify(Array.isArray(connectorIdsArr) ? connectorIdsArr : []), modelId, 'fullAccess', ownerUserId, 'confirmed');
// 10-08 修复:必须带 workspace_scope='workspace'。走正规 automation 工具创建的任务都带该值(库里 36 条如此),
// 工作台直接写库时漏了它 ⇒ 会话被视为"自动生成的空间"、落进「未分组任务」。
db.prepare(`INSERT INTO automations (id,name,prompt,status,schedule_type,scheduled_at,next_run_at,rrule,cwds,created_at,updated_at,skills_json,connector_ids_json,model_id,permission_mode,owner_user_id,owner_status,workspace_scope)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
.run(id, name, prompt, 'ACTIVE', 'once', scheduledAt, nextRunAt, '', JSON.stringify([cwd]), now, now, JSON.stringify(Array.isArray(skillsArr) ? skillsArr : []), JSON.stringify(Array.isArray(connectorIdsArr) ? connectorIdsArr : []), modelId, 'fullAccess', ownerUserId, 'confirmed', 'workspace');
return { id };
} finally {
db.close();
@@ -874,9 +876,10 @@ const server = http.createServer((req, res) => {
// 无手动会话记录时兜底 'auto'(自动路由)。实证:调度接受 'auto' 与具体模型 ID('deepseek-v4-pro' 曾成功)
const modelRow = db.prepare(`SELECT model FROM sessions WHERE deleted_at IS NULL AND COALESCE(is_background_automation,0)=0 AND model IS NOT NULL AND model <> '' ORDER BY COALESCE(last_activity_at, updated_at) DESC LIMIT 1`).get();
const modelId = (modelRow && modelRow.model) ? modelRow.model : 'auto';
db.prepare(`INSERT INTO automations (id,name,prompt,status,schedule_type,scheduled_at,next_run_at,rrule,cwds,created_at,updated_at,skills_json,connector_ids_json,model_id,permission_mode,owner_user_id,owner_status)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
.run(id, name || '工作台任务', finalPrompt, 'ACTIVE', 'once', scheduledAt, nextRunAt, '', JSON.stringify([cwd]), now, now, JSON.stringify(skillsArr), JSON.stringify(connArr), modelId, 'fullAccess', ownerUserId, 'confirmed');
// 10-08 修复:与 createOnceAutomation 同理,必须带 workspace_scope='workspace',否则会话落「未分组任务」
db.prepare(`INSERT INTO automations (id,name,prompt,status,schedule_type,scheduled_at,next_run_at,rrule,cwds,created_at,updated_at,skills_json,connector_ids_json,model_id,permission_mode,owner_user_id,owner_status,workspace_scope)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
.run(id, name || '工作台任务', finalPrompt, 'ACTIVE', 'once', scheduledAt, nextRunAt, '', JSON.stringify([cwd]), now, now, JSON.stringify(skillsArr), JSON.stringify(connArr), modelId, 'fullAccess', ownerUserId, 'confirmed', 'workspace');
db.close();
log('已提交 AI 任务: ' + id + (name ? ' (' + name + ')' : ''));
return sendJSON(200, { ok: true, id, message: '任务已提交,请在左侧会话栏查看执行' });