Files
MediaCrawler/mac-agent-os-main/99_system/architecture/COMMAND_CONDUIT_ARCHITECTURE.md
T
butubb 2112c1a870
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
feat(monitor): 抖音 Web 接口客户端 —— 绕开爬虫子进程,直接发 HTTP
移植自 mac-agent-os 的 mediacrawler_adapter:不起子进程、不开页面,用浏览器里那份
登录态直接调抖音 Web 接口。产物键名照抄 store/douyin,所以 ingest 那条链路一个字不用改。

**目前能用的(真环境实测,非推断)**:

    profile/other : 200, 7075 字节  —— 博主主页指标(粉丝/获赞/作品数/昵称)
    aweme/detail  : 200, 45425 字节 —— 单条作品详情(含点赞/评论/收藏/分享)

**目前不能用的:作品列表 `aweme/post`。** 两个互相独立的原因:
  1. 这个接口被抖音单独升级成了真校验:不带 x-tt-argus 回 403「Uifid Not Found」,
     带上 dummy 值回 200 + **空 body**。也就是说「头在不在」骗得过,「真校验」过不了。
     同一套头打 profile/other 和 aweme/detail 都是通的 —— 抖音是挑着接口加保护的,
     挑中的恰好是「批量拉作品列表」这个最敏感的动作。
  2. 改走页面截获也不行:CDP 浏览器打开博主主页会落到「验证码中间页」(当天大量探测的
     代价,过几小时要重测)。

  所以现在的边界是:**已知作品的指标刷新能做,自动发现新作品做不了**。

**排查中控住变量后得到的两条事实**(都写进注释了):
  · `Accept` / `Accept-Language` / `Referer` 才是主页接口能返回真数据的原因 —— 只有
    UA+client hints+Cookie 时是 200 但仅 121 字节的空壳,补上这三个头变 7074 字节。
    (我先前猜的 sec-ch-ua 不是关键。)
  · 因此 UA 与 client hints 必须**成套地取自同一个浏览器**,所以 BrowserIdentity 一次
    从 CDP 取齐 cookie + UA + hints,而不是各自写死。

「200 + 空 body 必须当场报错」也是刻意写死的:放过去它会在下游变成「这个博主没作品」,
把一次失败伪装成一条正常结果 —— 爬虫那条路正是这么栽的,还被翻译成「账号被封」。

测试 +11:cookie 解析、请求头成套性(含 uifid 缺失/回退)、产物键名与 store 对齐、
以及 _get 的三条失败路径(空 body / 403 带网关原话 / 正常返回)。
2026-10-10 17:12:25 +08:00

8.1 KiB
Raw Blame History

AgentOS 命令传导架构

版本: 1.0 | 最后更新: 2026-06-28 本文档描述从 L5 前端到 L0 浏览器的命令传导全链路设计与实际实现


一、五层传导架构

L5: 前端视图 (views/*.js)
  │ POST /api/ops/run {type, accounts, params}
  │
L4: API 路由层 (routes/ops.py)
  │ → api_ops_run() → CommandBus.dispatch()
  │
L3: 命令分发层 (CommandBus)
  │ dispatch():
  │   1. ORACLE 合规检查 (account→machine映射)
  │   2. CMD_REGISTRY 查模板 / 硬编码
  │   3. 按机器分组 + 模板渲染
  │   4. ThreadPoolExecutor 并行分发
  │
  ├──→ ▸ 新路径(优先): /scheduler/submit
  │     发送 {command_line, cmd_type, accounts, priority, interval, params}
  │        → guardd Scheduler → PriorityQueue → SlotManager
  │        → Executor._build_cmd() 仅加执行环境前缀
  │
  ├──→ ▸ 旧路径(降级): /task (v7)
  │     发送 {cmd: "cd scripts && python -m ..."}
  │        → guardd _start_task() → subprocess.Popen
  │
  └──→ ▸ 兜底路径: subprocess(本地) / SSH nohup(远程)
           nurture: nurture_runner.sh / 其他: 直接 Python

L2: 调度执行器 (Scheduler + Executor)
  │ Scheduler.run_cycle() 每15秒:
  │   → _check_all_active_tasks()   检查3个 slot
  │   → _schedule_all_slots()       分配新任务
  │   → Executor.execute(task)
  │     → _build_cmd(task):
  │         ▸ 优先用 command_line (L3已渲染好) → 仅加 cd+python 前缀
  │         ▸ 兜底: 自行硬编码构建 (向后兼容)
  │     → 子进程执行 → 日志解析 → 进度更新

L1: 平台原子操作 (douyin_ops.py / xhs_ops.py)
  │ → Camoufox 浏览器执行

L0: Camoufox 0.4.11 + Playwright 1.58.0

二、核心设计原则

2.1 三层降级设计

不是两条平行的命令生成路径,而是三层降级保护:

层级 路径 命令由谁构建 执行管理 可用条件
新路径 ⭐ /scheduler/submit L3渲染cmd_line, L2只加前缀 guardd调度器(15s+3slot) guardd v2.3.0+
旧路径 /task L3渲染完整shell命令 guardd直接subprocess guardd可用
兜底 subprocess/SSH L3渲染+执行环境 直接执行 任何时候

正常流中只有单一路径(新路径),每次降级都是因为上层不可用。

2.2 命令生成责任分离

层 职责 产出
L3 CommandBus 操作意图→CLI命令字符串 mc run --accounts=A --blueprints=B --rounds=3
L2 Executor 加执行环境前缀 cd scripts && PYTHONPATH=... python3 -m mc run ...
L2 Executor.兜底 command_line为空时重新构建 同上格式的完整命令

2.3 任务拆解

interact 类型的多账号任务在 L3 被拆解为 per-account 子任务(command_bus.py:1062-1087行),直接投递到 guardd /scheduler/submit。每条子任务有独立 task_id 和状态追踪。


三、CMD_REGISTRY 注册表

3.1 注册表结构

CMD_REGISTRY = {
    "nurture": {
        # 无 template — dispatch() 硬编码(因--mix,--interval特殊参数)
        "defaults": {"blueprint": "douyin_daily", "rounds": 10},
        "auto_blueprint": False,
    },
    "collect": {
        "template": "mc run --accounts={ids} --blueprints={blueprint} --rounds={rounds}",
        "defaults": {"rounds": 1},
        "auto_blueprint": True,
        "blueprint_map": {"douyin": "douyin_read_profile", "xiaohongshu": "xiaohongshu_read_profile"},
    },
    "login": {
        "template": "mc smart-login {ids}",
        "single_account": True,
    },
    "logout": {
        "template": "mc run --accounts={ids} --blueprints=douyin_daily --rounds=1",
    },
    "comment": {
        "template": "mc task comment --account={ids} --url={url} --direction={direction}",
        "required_params": ["url"],
    },
    "like": {
        "template": "mc run --accounts={ids} --blueprints=douyin_daily --rounds=1",
    },
    "interact": {
        "template": "mc run --accounts={ids} --blueprints={blueprint} --rounds=1 "
                    "--url={url} --direction={direction} --corpus={corpus}",
        "defaults": {"blueprint": "interact_comment", "direction": "", "corpus": ""},
        "required_params": ["url"],
    },
}

3.2 nurture 的特殊处理

nurture 类型不经过 CMD_REGISTRY 模板渲染,dispatch() 对其做硬编码(command_bus.py:965-978行),原因是:

  1. 需要按平台分组(douyin/xiaohongshu 走不同的默认蓝图)
  2. 有固定参数 --mix --interval=45-90
  3. 支持通过 params 传递自定义 interval、stagger 等参数

四、任务生命周期

pending → scheduled → queued → preflight → running → completed
                                            ↓          → failed
                                            ↓          → cancelled
                                            ↓          → skipped
                                            → paused (可恢复)
                                            → waiting_dep (依赖等待)

终端状态: completed / failed / cancelled / skipped 活跃状态: running / queued / preflight / paused / waiting_dep


五、调度引擎架构 (guardd)

5.1 组件关系

HTTP Server (:9090)
  ├── POST /scheduler/submit → api_scheduler_submit()
  ├── GET  /scheduler/tasks  → api_scheduler_status()
  ├── POST /task/{id}/stop   → _stop_task()
  ├── POST /task/pause       → scheduler.pause_task()
  ├── POST /task/resume      → scheduler.resume_task()
  ├── POST /queue/reorder    → scheduler.reorder_queue()
  ├── GET  /accounts/status  → AccountMonitor
  └── GET  /accounts/profiles → profiles.json

Scheduler (15秒循环)
  ├── PriorityQueue (heapq, P0/P1/P2)
  ├── BrowserSlotManager (3 slot)
  ├── Executor (子进程+日志解析)
  └── TaskStore (内存+SQLite双写)

HeartbeatReporter (每轮上报)
  ├── slot使用率
  ├── 账号登录状态
  ├── 任务统计
  └── 浏览器健康

ScheduleBridge (60秒检查)
  └── schedule.yaml → guardd scheduler

5.2 优先级策略

任务类型 优先级 说明
interact, comment P0 用户主动交互,最高优先
nurture, collect P1 日常养号/采集
scheduled tasks P1 定时任务
logout, login P2 低优先

六、已知维护注意事项

6.1 executor._build_cmd() 兜底代码

executor.py:148-189行的 _build_cmd() 有独立于 CMD_REGISTRY 的命令构建逻辑。此代码正常流不会触发(因 command_line 始终存在),仅作为向后兼容的安全网保留。修改 dispatch() 中的命令格式时,应同步检查此处的兜底是否一致。

6.2 comment 的潜在不一致

来源 comment 命令格式
CMD_REGISTRY template mc task comment --account=... --url=...
executor._build_cmd() 兜底 mc run --accounts=... --blueprints=... --rounds=1 --url=...

正常流走 CMD_REGISTRY,但如果兜底被触发(command_line 为空),会导致执行完全不同的命令。

6.3 routes/matrix.py 旧路由状态

路由 状态 说明
/nurture/start 已废弃·走CommandBus 保留兼容,内部调 CommandBus
/nurture/preview 只读·保留 排期预览,无执行操作
/task/run 已废弃·走CommandBus 保留兼容,内部调 CommandBus
/batch-run 已改造·走CommandBus 2026-06-28 改造完成
/blueprints/{name}/execute 保留 蓝图单步执行
/accounts/{id}/login 保留 小红书走SMS/抖音走CommandBus

6.4 命令轮询的迁移

旧版 CommandBus 有 _start_poll_guard() 轮询线程(每15秒检查各机状态)。v4.3.0 后此逻辑已迁移到 guardd 的 Scheduler.run_cycle()(15秒循环),CommandBus 中的 _start_poll_guard() 已空桩(command_bus.py:1264-1266行)。