09-01 调度机制修复落地+并发方案A+全链路复测通过
- 调度根因修复:工作台/api/run 显式写 next_run_at=now+5s(17列),scheduledAt=now+5s 带秒 - 新增 自动化任务调度机制.md(根因/硬规则/状态机/并发上限≈3/排查命令) - 并发方案A(浏览器锁):/api/run 对浏览器类任务自动注入锁约束;浏览器搜索抖音账号操作规范.md 新增5.6并发互斥 - 端口 8899→8900 可传参(8899被小米MiPCAudio.exe系统服务占用且杀后复活) - cdp-test-junxi.mjs 支持 --port 参数 - SKILL.md 工作台段落同步修正(旧认知 scheduled_at=now 已废弃) - 俊希全流程 TC-01~11 复测全通过(验收表更新):并行解析6条+AI写脚本会话链路完整跑通
This commit is contained in:
1 parent
e906e6616a
commit
b2e472bf27
12 files changed
+265
-49
No files matched your search
@@ -8,8 +8,8 @@ description: >
|
||||
触发场景:写脚本、创作短视频、生成脚本、拆解对标视频、分析爆款视频、
|
||||
诊断脚本质量、设计账号人设、选题策划、生成分镜、广告植入设计、生成创作回顾。
|
||||
version: "1.0"
|
||||
updated_at: "2026-08-31"
|
||||
last_change: "MCP安装引导升级为首次触发检查(必做动作):会话首次交互(问功能/发起任务无论问什么)先检测 myai-mcp-production 可用性(可用工具列表或 mcp.json),未装轻量提醒可跳过;环境检查系统分流:mcp.json 路径 Win %USERPROFILE%\\.workbuddy\\mcp.json / mac ~/.workbuddy/mcp.json;同步 MCP_工具调用规范.md 检测方法/部署预检(npm缓存权限仅macOS高发,Win跳过)+ 帮助文档 MCP服务说明"
|
||||
updated_at: "2026-09-01"
|
||||
last_change: "工作台AI任务触发改为 scheduled_at=now+5s+显式写next_run_at(09-01调度机制实测定稿,替代08-31 scheduled_at=now旧认知);新增 自动化任务调度机制.md(根因/硬规则/状态机/并发上限≈3/排查命令);同步MCP安装引导(08-31)"
|
||||
deployment: "source"
|
||||
agent_created: true
|
||||
---
|
||||
@@ -70,7 +70,7 @@ agent_created: true
|
||||
- **🧪 AI写作复盘(#/reviews)**:评分维度列表 + 复盘详情(评分条/问题清单/报告)
|
||||
- **📁 产出浏览(#/files)**:默认读桌面 `MCNSkill项目/`,支持 ⚙️ 设置添加其他项目地址;md/json 在线预览
|
||||
4. **数据边界**:数据库读工作台自有副本 `mcn-work-shop/mcn-plugin.db`(由 dsh 库复制而来,与 dsh 原库 `~/.dsh/mcn-plugin.db` 完全隔离,两边各访问各的互不影响);周榜读 `D:\dshworkspace\抖音榜单\*.json` 文件
|
||||
5. **AI 任务触发(Automation 方案)**:各数据页「导入/创作/解析/改写/提炼/复盘」按钮点击后 → 工作台后端 `POST /api/run` → 向 `~/.workbuddy/workbuddy.db` 的 `automations` 表写入一条**一次性任务**(`schedule_type='once'`、`scheduled_at=now`、`cwds=["D:\\AgentSkill\\mcn-workshop"]`)→ WorkBuddy 运行时自动消费执行(创建 AI 会话)→ **会话归入 `mcn-workshop` 空间分组**(左侧会话栏按 cwd 分组,可在该分组查看任务执行过程与结果)。参考 dsh 插件 `dsh-plugin-mcn` 的 `/mcn/api/creative` 模式(固定会话 followup 触发);前端 5 秒防连点
|
||||
5. **AI 任务触发(Automation 方案)**:各数据页「导入/创作/解析/改写/提炼/复盘」按钮点击后 → 工作台后端 `POST /api/run` → 向 `~/.workbuddy/workbuddy.db` 的 `automations` 表写入一条**一次性任务**(`schedule_type='once'`、**`scheduled_at=now+5s`(带秒)且显式写 `next_run_at=now+5000`**、`cwds=["D:\\AgentSkill\\mcn-workshop"]`)→ WorkBuddy 客户端调度器按 `next_run_at` 扫描拾取(周期≤30s)→ 创建 AI 会话执行 → **会话归入 `mcn-workshop` 空间分组**(左侧会话栏按 cwd 分组,可在该分组查看任务执行过程与结果)。参考 dsh 插件 `dsh-plugin-mcn` 的 `/mcn/api/creative` 模式(固定会话 followup 触发);前端 5 秒防连点。**调度机制/状态机/并发上限/排查命令详见同目录 `自动化任务调度机制.md`(09-01 实测定稿,勿再按旧认知 `scheduled_at=now` 直写——会因 next_run_at 为空而卡死)**
|
||||
6. 本功能仅 V1.0 源技能环境提供,**不同步 dsh/Lite 副本**
|
||||
7. **改完必重开页面(硬性约定)**:对工作台任何页面/代码改动完成后,**最后一步必须用 present_files 重新打开对应页面**(带 hash 直接定位,如 `http://localhost:8899/#/accounts`),让用户在 WorkBuddy 内置浏览器立即看到最新效果。原因:内置浏览器无 CDP 调试端口(9222/9224 等均无响应),无法模拟 F5;重开页面是唯一可靠刷新方式。此约定对工作台所有 UI 改动强制生效
|
||||
|
||||
|
||||
@@ -2,16 +2,19 @@
|
||||
// 覆盖测试用例 TC-09 的界面交互环节(真实模拟用户点击,非仅文字步骤)
|
||||
//
|
||||
// 前置:
|
||||
// 1. 工作台服务运行中(node server.js,端口 8899)
|
||||
// 1. 工作台服务运行中(node server.js,端口 8899 默认;若 8899 被小米占用则用 8900)
|
||||
// 2. 浏览器 9223 调试实例已启动(browser-harness 调试实例)
|
||||
// 3. 账号列表(hot_accounts)含「俊希」
|
||||
// 用法:
|
||||
// node cdp-test-junxi.mjs # 只验证到「选题生成」,不触发创作任务(安全模式)
|
||||
// node cdp-test-junxi.mjs --confirm # 选题生成后继续点「确认执行」,真实触发创作任务
|
||||
// node cdp-test-junxi.mjs --port 8900 # 指定工作台端口(默认 8899)
|
||||
// 说明:脚本在 9223 实例新开标签页操作,不干扰用户已打开的页面;结束后自动关闭新开标签页。
|
||||
|
||||
const argPort = process.argv.indexOf('--port');
|
||||
const PORT = argPort > -1 && process.argv[argPort + 1] ? process.argv[argPort + 1] : '8899';
|
||||
const CONFIRM = process.argv.includes('--confirm');
|
||||
const BASE = 'http://localhost:8899';
|
||||
const BASE = 'http://localhost:' + PORT;
|
||||
|
||||
// 1) 连接 9223 调试实例
|
||||
const list = await fetch('http://localhost:9223/json').then((r) => r.json());
|
||||
@@ -30,10 +33,10 @@ await send('Page.enable'); await send('Runtime.enable');
|
||||
// 2) 新开标签页打开账号列表(不干扰现有页面)
|
||||
const { targetId } = await send('Target.createTarget', { url: BASE + '/#/accounts' });
|
||||
await sleep(2500);
|
||||
let targetPage = list2 => list2.find((t) => t.type === 'page' && t.url.includes('localhost:8899') && t.id === targetId);
|
||||
let targetPage = list2 => list2.find((t) => t.type === 'page' && t.url.includes('localhost:' + PORT) && t.id === targetId);
|
||||
let pages = await fetch('http://localhost:9223/json').then((r) => r.json());
|
||||
let tp = targetPage(pages);
|
||||
if (!tp) { console.log('ERR: 新开标签页未找到 8899 页面'); process.exit(1); }
|
||||
if (!tp) { console.log('ERR: 新开标签页未找到 ' + PORT + ' 页面'); process.exit(1); }
|
||||
// 切换 WS 到新页面
|
||||
const ws2 = new WebSocket(tp.webSocketDebuggerUrl);
|
||||
let id2 = 0; const pending2 = new Map();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// MCN工作台 - 本地 Web 服务(零依赖 Node 实现)
|
||||
// 启动: node server.js → http://localhost:8899
|
||||
// 启动: node server.js [端口] → http://localhost:8899(默认;可传参指定端口,如 node server.js 8900)
|
||||
// 功能: 技能导航 + 产出内容浏览(默认读桌面 MCNSkill项目)+ dsh 数据功能页(账号/视频/脚本/复盘/周榜,只读)
|
||||
'use strict';
|
||||
|
||||
@@ -10,7 +10,9 @@ const os = require('os');
|
||||
const { URL } = require('url');
|
||||
const dsh = require('./dsh-data');
|
||||
|
||||
const PORT_BASE = 8899;
|
||||
// 09-01 端口改为可传参(默认8899):本机 8899 可能被小米 PC 管家 MiPCAudio.exe 系统服务占用(0.0.0.0 监听且自动复活),
|
||||
// 此时 127.0.0.1:8899 绑定不生效 → 用 `node server.js 8900` 显式换端口
|
||||
const PORT_BASE = parseInt(process.argv[2], 10) || 8899;
|
||||
const ROOT = __dirname;
|
||||
const PUBLIC_DIR = path.join(ROOT, 'public');
|
||||
const CONFIG_FILE = path.join(ROOT, 'config.json');
|
||||
@@ -361,19 +363,35 @@ const server = http.createServer((req, res) => {
|
||||
try {
|
||||
const { prompt, name } = JSON.parse(body || '{}');
|
||||
if (!prompt || typeof prompt !== 'string' || !prompt.trim()) return sendErr(400, 'prompt 不能为空');
|
||||
// 09-01 并发方案A:浏览器类任务(账号信息/视频列表/采集/导入)自动注入浏览器锁约束——
|
||||
// 多个 AI 会话任务并行共享同一 9223 Chrome 实例,必须互斥使用,否则标签页互相导航抢占
|
||||
const BROWSER_HINT = /账号信息|视频列表|保存并分析|采集|导入账号|网页采集|浏览器/;
|
||||
const BROWSER_LOCK = 'D:\\\\AgentSkill\\\\mcn-workshop\\\\.browser-lock';
|
||||
let finalPrompt = prompt.trim();
|
||||
if (BROWSER_HINT.test(finalPrompt)) {
|
||||
finalPrompt += `
|
||||
|
||||
【浏览器锁约束(必须遵守)】本任务需要操作浏览器(browser-harness / 9223 Chrome)。多个 AI 会话任务可能并行,浏览器是共享单实例,必须互斥使用:
|
||||
1. 执行任何浏览器操作前,先检查锁文件:${BROWSER_LOCK} 是否存在(bash: ls)
|
||||
2. 锁存在 → 等待 10 秒后重试,最多重试 18 次(约 3 分钟);若锁文件修改时间已超过 10 分钟视为死锁,可删除后抢占
|
||||
3. 拿到锁(bash: echo <任务名+时间戳> > ${BROWSER_LOCK})→ 才可操作浏览器
|
||||
4. 浏览器操作全部完成后(无论成功失败)必须删除锁文件(bash: rm -f ${BROWSER_LOCK})
|
||||
5. 若确认本任务实际不需要浏览器(如数据已齐),忽略本条约束,直接跳过`;
|
||||
}
|
||||
const { DatabaseSync } = require('node:sqlite');
|
||||
const db = new DatabaseSync(process.env.WORKBUDDY_DB || path.join(os.homedir(), '.workbuddy', 'workbuddy.db'));
|
||||
const now = Date.now();
|
||||
const id = 'automation-' + now;
|
||||
const cwd = 'D:\\AgentSkill\\mcn-workshop'; // 工作台触发的会话归入 mcn-workshop 空间分组
|
||||
const d = new Date();
|
||||
const d = new Date(Date.now() + 5 * 1000); // 未来 5 秒(08-31 根因:客户端只对未来 scheduledAt 补算 next_run_at;写 now=过去时间→不补算→调度器扫不到→卡死)
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
const scheduledAt = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
const scheduledAt = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
||||
const nextRunAt = Date.now() + 5 * 1000; // 08-31 实测:客户端调度器按 next_run_at 扫描;缺此列=null=永不拾取(工作台任务卡死根因)
|
||||
const uidRow = db.prepare("SELECT user_id FROM sessions WHERE user_id IS NOT NULL AND user_id <> '' ORDER BY created_at DESC LIMIT 1").get();
|
||||
const ownerUserId = uidRow ? uidRow.user_id : '';
|
||||
db.prepare(`INSERT INTO automations (id,name,prompt,status,schedule_type,scheduled_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.trim(), 'ACTIVE', 'once', scheduledAt, '', JSON.stringify([cwd]), now, now, '[]', '[]', 'deepseek-v4-flash', 'fullAccess', ownerUserId, 'confirmed');
|
||||
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, '[]', '[]', 'deepseek-v4-flash', 'fullAccess', ownerUserId, 'confirmed');
|
||||
db.close();
|
||||
log('已提交 AI 任务: ' + id + (name ? ' (' + name + ')' : ''));
|
||||
return sendJSON(200, { ok: true, id, message: '任务已提交,请在左侧会话栏查看执行' });
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# 自动化任务调度机制(09-01 定稿,实测验证)
|
||||
|
||||
> 本文档固化 08-31~09-01 多次调试理清的客户端自动化调度机制,是工作台 `/api/run` 链路唯一权威说明。
|
||||
> 结论均来自本机实测(automations / automation_runs / sessions 三表 + 调度器实际行为),非推测。
|
||||
|
||||
## 一、完整链路
|
||||
|
||||
```
|
||||
工作台按钮 → POST /api/run → 直写 ~/.workbuddy/workbuddy.db automations 表
|
||||
→ 客户端调度器按 next_run_at 扫描(周期 ≈30s)
|
||||
→ 命中 → automation_runs 写 QUEUED → 建会话(sessions 表, is_background_automation=1)
|
||||
→ IN_PROGRESS(meta 含 conversationId/sessionId)→ 执行 → ACCEPTED(含 resultState/resultEvidence)
|
||||
```
|
||||
|
||||
1 任务 = 1 会话,一一对应。
|
||||
|
||||
## 二、根因(必须牢记)
|
||||
|
||||
| 现象 | 根因 |
|
||||
|---|---|
|
||||
| 工作台任务全卡死、永不执行 | **调度器按 `next_run_at` 列扫描**。工作台直写 SQLite 时若缺/空该列 → 永不拾取(08-31 P2「去掉 next_run_at 用 scheduled_at」是错误判断,09-01 已纠正) |
|
||||
| `scheduled_at` 写过去时间 → 卡死 | 客户端只对**未来** scheduledAt 自动补算 next_run_at;过去/太近时间 → 不补算 → 扫不到 |
|
||||
| 误以为「调度器停摆」 | 调度器从未停摆(33022 秒级准点执行过);停摆假象 = next_run_at 没填 |
|
||||
|
||||
## 三、硬规则(实测阈值)
|
||||
|
||||
1. **scheduled_at 必须未来**,且留足余量:**+60s 稳、+17s 失败**(update 往返有几秒延迟,越近越险)
|
||||
2. **next_run_at 必须显式写入**(工作台链路):`Date.now() + 5000`(+5s 即可,因为这是直写、无补算依赖)
|
||||
3. **立即执行参数不存在**:argv.json(仅 IDE 渲染)、settings.json(仅插件/sandbox/claw)、无 workbuddy CLI,客户端无并发数/扫描周期配置项 → 无法通过参数控制
|
||||
4. 实际开始时间 = 写库时刻 + 5s(next_run_at)+ 扫描周期(≤30s)≈ **5~35s**(工作台按钮链路已是最快路径)
|
||||
|
||||
## 四、并发与排队(09-01 实测)
|
||||
|
||||
- **客户端后台自动化并发上限 ≈ 3**:同秒触发 5 任务 → 3 个立即并行(各建会话)、2 个排队
|
||||
- 排队标记:automation_runs.metadata_json 含 `queuedPosition`(1、2、…)
|
||||
- 并行是任务层面天然支持的,无需任何配置;超出 3 个自动排队
|
||||
|
||||
## 五、automation_runs 状态机
|
||||
|
||||
```
|
||||
QUEUED(排队, meta.queuedPosition)
|
||||
→ IN_PROGRESS(建会话, meta.conversationId/sessionId)
|
||||
→ ACCEPTED(完成, meta.resultState=delivered|side_effect_only|partial_delivered, resultEvidence=assistant_output|external_action|local_file_mutation|none)
|
||||
```
|
||||
另有 PENDING_REVIEW(待人工确认,工作台轮询时显示「待确认」)。
|
||||
|
||||
表结构:`thread_id / automation_id / status / read_at / thread_title / source_cwd / runs_json / result_success / metadata_json / created_at / updated_at`
|
||||
|
||||
## 六、排查命令(Windows / Git Bash)
|
||||
|
||||
```bash
|
||||
cd "C:/Users/maidou/.workbuddy"
|
||||
"D:/miniconda3/python.exe" -c "
|
||||
import sqlite3
|
||||
db = sqlite3.connect('workbuddy.db'); db.row_factory = sqlite3.Row
|
||||
for r in db.execute(\"SELECT id,name,scheduled_at,next_run_at,last_run_at,status FROM automations WHERE deleted_at IS NULL ORDER BY created_at DESC LIMIT 10\"):
|
||||
print(dict(r))
|
||||
print('---runs---')
|
||||
for r in db.execute(\"SELECT automation_id,status,metadata_json FROM automation_runs ORDER BY created_at DESC LIMIT 10\"):
|
||||
print(dict(r))
|
||||
"
|
||||
```
|
||||
|
||||
判定要点:
|
||||
- 任务有 `next_run_at` 且 < now → 应已被拾取(看 automation_runs)
|
||||
- `last_run_at` 仍 None + runs 无记录 → next_run_at 没补算(scheduled_at 太近/过去)
|
||||
- runs 有 QUEUED → 在排队(并发 >3);有 IN_PROGRESS → 正在跑(看 sessions working)
|
||||
- 会话:`SELECT id,title,status,created_at FROM sessions WHERE is_background_automation=1`
|
||||
|
||||
## 七、服务维护
|
||||
|
||||
- 启动:`node server.js`(零依赖,端口 8899 自动避让)
|
||||
- 改 server.js 后必须重启才生效(Windows:`taskkill /PID <pid> /F` 后重启)
|
||||
- 后台运行:`node server.js > /tmp/mcn-workshop.log 2>&1 &`
|
||||
@@ -122,14 +122,18 @@
|
||||
|
||||
> **关键定义**:本节「底色」一律指**整卡背景色**(深色渐变底或浅色渐变底)。卡片块、文字、标签、强调色等**内部元素不随账号风格变**,统一按所选色系的「配色规范」表执行:**深色系(深色长图风)**= 浅米卡片 + 深色文字 + 珊瑚红强调;**浅色系(浅色甜宠风)**= 白卡 + 粉边 + 深粉文字 + 粉强调。同一账号系列卡片必须固定同一色系,保证系列视觉语言一致。
|
||||
|
||||
### ① 判定账号风格(读设定「色彩体系」+ 赛道/类型)
|
||||
以账号设定.md 的「视听风格/色彩体系」模块真实色值为第一依据(主色/强调色),赛道与账号类型辅助判定,归入下表风格。
|
||||
### ① 判定账号风格(调性 + 色值 + 赛道,三层判定)
|
||||
1. **第一依据 = 账号实际内容调性**(读设定 md 的「账号核心定位」情绪锚点 + 「情绪结构」五段 + 「视听风格」氛围):**决定底色走深色系还是浅色系**。情绪锚点含「明快/欢乐/爽文/解压/暖心/积极治愈/宠/甜」→ 浅色系;含「深沉/催泪/怀旧/忧郁/厚重/故事感/悲情」→ 深色系。
|
||||
2. **第二依据 = 视听风格/色彩体系真实色值**(主色/强调色),作为具体底色取色的色相来源。
|
||||
3. **第三依据 = 赛道/类型**,仅作初筛归入下表风格,**不作为色系终判**。
|
||||
|
||||
> ⚠️ **赛道标签有歧义时必须二次判定(反例:俊希)**:「温情/亲子/治愈」中的「治愈」是**双义**标签——深沉催泪型治愈走深色系,明快欢乐型治愈走浅色系。凡赛道含「治愈/亲子/温情」等暧昧词,**必须回读情绪锚点判定明快 vs 深沉**,禁止机械套下表默认色。俊希=「满级小孩哥给妈妈做饭」明快欢乐治愈爽文 → 浅色系(暖橙 `#F5D0A4→#DE9A5E`),非下表深焦糖深色。
|
||||
|
||||
### ② 风格 → 背景色映射(示例,可扩展)
|
||||
| 账号风格(色彩体系/赛道特征) | 背景底色(深色渐变) |
|
||||
|----------------------------|--------------------|
|
||||
| 暖怀旧/旧时光(剧情·怀旧,如旧梦留声机) | 深棕红 `#2B0F0A→#1A0805` |
|
||||
| 温情/亲子/治愈(亲子Vlog) | 深焦糖 `#2B1A10→#1A0F08` |
|
||||
| 温情/亲子/治愈(亲子Vlog) | 调性分叉:**深沉催泪型 → 深焦糖 `#2B1A10→#1A0F08`**;**明快欢乐型(如俊希)→ 浅色系**(暖橙 `#F5D0A4→#DE9A5E`,提亮降饱和) |
|
||||
| 科技/数码/冷淡(数码科技/商业) | 深灰蓝 `#0E1626→#080D16` |
|
||||
| 清新/田园/三农(三农/户外) | 深墨绿 `#0F1F18→#08130E` |
|
||||
| 潮流/都市/时尚(潮流风尚/颜值) | 深紫黑 `#17101F→#0C0810` |
|
||||
@@ -141,6 +145,7 @@
|
||||
**浅色系推导(明快/甜美/治愈调性)**:取账号色彩体系主色 → 提亮+降饱和至中浅明度(约 65-90% 亮度,如王微斯:主粉 → 玫瑰金 `#D6A3A9→#9A5563`)→ 得浅色系渐变底;正文文字用主色加深版(`#7B2D5E` 档)。**背景上直接书写的文字(模块标题/装饰线/注释/版权)随背景深浅提亮保可读**:背景越深文字越亮(如玫瑰金底上模块标题 `#FDE8EC`、装饰线/注释 `#EAC6CE`、斜体/版权 `#DDBCC4`)。
|
||||
|
||||
### ③ 强规则
|
||||
- **调性优先于赛道标签**:色系二选(深/浅)以账号实际内容调性(情绪锚点)为最终判据,赛道标签只作初筛;赛道含「治愈/温情/亲子」等暧昧词时必须二次判定,禁止机械套默认色
|
||||
- **禁止**默认沿用模板深棕红底;同一账号每次生成的卡背景色保持一致(系列一致性 = 同账号一致,非跨账号一致)
|
||||
- **底色决定色系**:选深色系则内部元素按「配色规范·深色系」执行,选浅色系则按「配色规范·浅色系」执行;同一色系内部元素色一律固定,不随账号再改
|
||||
- 模块「色彩体系」色块:必须用设定文件中账号的真实色值(模板 5 色块仅示例)
|
||||
|
||||
@@ -256,6 +256,38 @@ PY
|
||||
4. **用完不关闭**:任务完成后保留标签页(留给后续任务复用),下次任务直接导航到目标地址即可
|
||||
5. **多任务串行**:多个刷新/导入任务共用同一 9223 Chrome 实例,前一个任务的抖音标签页直接导航复用,无需重新打开
|
||||
|
||||
### 5.6 并发互斥(浏览器锁,09-01 新增,多 AI 会话并行时强制)
|
||||
|
||||
**背景**:工作台多个 AI 会话任务可并行(客户端并发上限≈3),但浏览器是**共享单实例**(9223 Chrome)。两个任务同时操作会互相导航标签页、丢失对方数据,因此**浏览器操作必须互斥(一次只有一个任务在用浏览器)**。非浏览器操作(解析/提炼/复盘/写脚本,走 MCP/文件)不受影响,可全并行。
|
||||
|
||||
**锁规则(每个需要浏览器的任务必须遵守)**:
|
||||
|
||||
1. **锁文件**:`D:\AgentSkill\mcn-workshop\.browser-lock`(工作台 `/api/run` 对浏览器类任务已自动注入本约束到 prompt,AI 会话执行时遵守即可)
|
||||
2. **操作前**:检查锁文件是否存在(`ls`)→ 存在则等 10 秒重试(最多 18 次 ≈ 3 分钟)
|
||||
3. **死锁保护**:锁文件修改时间超过 10 分钟 → 视为死锁,可删除后抢占
|
||||
4. **拿锁**:`echo "<任务名> <时间戳>" > D:\AgentSkill\mcn-workshop\.browser-lock` → 才操作浏览器
|
||||
5. **释放**:浏览器操作全部完成后(无论成败)`rm -f D:\AgentSkill\mcn-workshop\.browser-lock`
|
||||
6. **串行协调**:多个任务抢锁 = 先到先用;拿到锁的任务完成后释放,下一个任务继续;标签页复用规则(5.5)仅在自己持有锁期间适用
|
||||
|
||||
```bash
|
||||
# 拿锁(互斥,带重试)
|
||||
for i in $(seq 1 18); do
|
||||
if [ ! -f "D:/AgentSkill/mcn-workshop/.browser-lock" ]; then
|
||||
echo "$(date '+%H:%M:%S') ${任务名}" > "D:/AgentSkill/mcn-workshop/.browser-lock"
|
||||
break
|
||||
fi
|
||||
# 死锁保护:锁超过 10 分钟强制抢占
|
||||
if [ -f "D:/AgentSkill/mcn-workshop/.browser-lock" ] && [ $(($(date +%s) - $(stat -c %Y "D:/AgentSkill/mcn-workshop/.browser-lock" 2>/dev/null || echo 0))) -gt 600 ]; then
|
||||
rm -f "D:/AgentSkill/mcn-workshop/.browser-lock"
|
||||
continue
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
# ... 浏览器操作 ...
|
||||
# 释放锁(无论成败)
|
||||
rm -f "D:/AgentSkill/mcn-workshop/.browser-lock"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 检查已打开标签页
|
||||
"$BH" <<'PY'
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|---|---------|------|---------|---------|
|
||||
| 6 | 本地接口(`/mcn/api/...`)调用规范未沉淀,每次现查 | 无统一接口调用规范文件 | 新建 `references/接口调用/本地接口调用规范.md`,记录接口基地址判定(`$env:DSH_WEB_URL` → 3080/3081 兜底)、接口清单、请求构造方式(Python 直连优先) | 已沉淀为本规则 |
|
||||
| 7 | 生成人设卡前未先判定账号类型就套模板 | 账号类型判定是模板选择前置 | 严格执行 5.0 判定:从筛选出的 TOP 视频 analysis「人设」字段提取主角名称 → 全部不同=群像型 / 同一主角=固定主角+轮换搭档 / 全固定=单一达人型 | 已沉淀为人设卡生成方法 5.0 |
|
||||
| 8 | 生成设定卡时机械套赛道标签配色,把「明快欢乐型亲子(俊希)」误配成「深焦糖深色系」 | 「温情/亲子/治愈」赛道标签**双义**(深沉催泪型 vs 明快欢乐型),未做调性二次判定就套下表默认深色 | 色系二选以账号实际内容调性(情绪锚点:明快/欢乐/爽文/暖心 → 浅色系;深沉/催泪/怀旧/悲情 → 深色系)为最终判据,赛道标签只作初筛;赛道含「治愈/亲子/温情」等暧昧词时**必须回读情绪锚点判定明快 vs 深沉** | 已沉淀为 06_生成账号设定卡片.md「调性优先于赛道标签」规则 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user