移植自 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 带网关原话 / 正常返回)。
15 KiB
15 KiB
type, domain, nature, tags, status, created, updated, version, supersedes
| type | domain | nature | tags | status | created | updated | version | supersedes | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| concept | system | architecture |
|
draft | 2026-06-16 | 2026-06-16 | 1.0.0 |
AgentOS 联邦指挥台 — 操作架构设计 v1.0
版本: 1.0.0-draft | 2026-06-16 设计目标: 创建世界级的多机智能体编排系统,可扩展为大规模计算机军团
一、设计哲学
核心原则
- 指挥台只负责聚合和分发,不直接执行 — 所有操作在目标机器上运行
- 每台机器是自治的智能体 — 离线也能独立运行,在线时接受指挥台调度
- 操作即状态机 — 每个操作都有明确的声明周期,可追踪、可重试、可审计
- 先检后动 — 操作前检查资源状态,保证不会破坏正在运行的任务
- 双模交互 — Dashboard(图形)和 CLI(命令行)完全对等,同一套底层
参考架构
| 来源 | 借鉴点 |
|---|---|
| Selenium Grid Hub-Node | 中央调度 + 分布式执行节点 |
| Kubernetes Job/Pod | 声明式任务 + 状态机 + 日志聚合 |
| Apache Airflow DAG | 操作依赖管理 + 重试 + 告警 |
| Erlang/Elixir Actor | 每台机器 = 一个 Actor,通过消息通信 |
| Unix Pipeline | 每个操作 = 一个进程,stdin/stdout/stderr |
二、系统架构:三层模型
┌──────────────────────────────────────────────────────────────────┐
│ Layer 1: Command Center │
│ (Dashboard Web UI + CLI) │
│ │
│ 功能: 聚合展示 + 命令输入 + 日志查看 + 状态监控 │
│ 位置: chengzigedeAir (master) │
│ 也支持: 任何机器浏览器打开看板,或本地 CLI 直接执行 │
└──────────────────────┬───────────────────────────────────────────┘
│ HTTP / WebSocket / SSE
┌──────────────────────┴───────────────────────────────────────────┐
│ Layer 2: Orchestration Bus │
│ │
│ - federation API (REST): /api/federation/* │
│ - event stream (SSE): /api/events │
│ - remote exec (SSH): /api/federation/exec │
│ - log aggregation: 每台机器推送日志到中心存储 │
└──────────────────────┬───────────────────────────────────────────┘
│ SSH / HTTP / Tailscale
┌────────────────┼────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Machine A │ │ Machine B │ │ Machine C │
│ chengzige │ │ 5kecheng │ │ 7kecheng │
├────────────┤ ├────────────┤ ├────────────┤
│ Layer 3a │ │ Layer 3a │ │ Layer 3a │
│ Agent Daemon│ │ Agent D. │ │ Agent D. │
│ (guardd) │ │ (guardd) │ │ (guardd) │
├────────────┤ ├────────────┤ ├────────────┤
│ Layer 3b │ │ Layer 3b │ │ Layer 3b │
│ Camoufox │ │ Camoufox │ │ Camoufox │
│ Browser │ │ Browser │ │ Browser │
└────────────┘ └────────────┘ └────────────┘
每台机器上的 Agent Daemon (guardd)
每台机器运行一个常驻后台进程 guardd,职责:
| 职责 | 说明 |
|---|---|
| 心跳上报 | 每 300s 写一次 heartbeat.json,含 CPU/内存/磁盘/任务状态 |
| 操作队列 | 接收指挥台下发的操作,排队执行 |
| 资源锁 | 管理浏览器实例/账号的使用状态,防止冲突 |
| 日志推送 | 操作日志实时推送到中心日志存储 |
| 状态归零 | 操作完成后清理浏览器进程、还原环境 |
三、操作状态机
所有操作的通用生命周期
┌──────────┐
│ pending │ ← 用户下发操作,等待调度
└────┬─────┘
↓
┌──────────┐
┌──────→│ checking │ ← 检查目标机器状态、资源可用性
│ └────┬─────┘
│ ↓
│ ┌──────────┐
│ │preparing │ ← 关闭旧浏览器、清理缓存、锁资源
│ └────┬─────┘
│ ↓
│ ┌──────────┐
│ ┌─→│ running │ ← 操作执行中(浏览器打开/脚本运行)
│ │ └────┬─────┘
│ │ ↓
│ │ ┌──────────┐ ┌──────────┐
│ │ │completed │ 或 │ failed │ ← 成功完成或出错
│ │ └────┬─────┘ └────┬─────┘
│ │ ↓ ↓
│ │ ┌──────────┐ ┌──────────┐
│ │ │ cleaning │ │ cleaning │ ← 清理资源、释放锁
│ │ └────┬─────┘ └────┬─────┘
│ │ ↓ ↓
│ │ ┌──────────┐ ┌──────────┐
│ └──│ done │ │ error │
│ └──────────┘ └──────────┘
│
└── 任何阶段都可被"取消"或"超时"
超时后进入 cleaning → error
状态转换规则
| 转换 | 触发条件 | 动作 |
|---|---|---|
| pending → checking | 调度器获取执行权 | 读取目标机器心跳、检查是否有其他任务在跑 |
| checking → preparing | 资源检查通过 | 发送 prepare 命令到目标机器 |
| checking → pending | 资源忙 | 重新排队,等待重试 |
| checking → failed | 目标机器离线 | 标记失败,通知用户 |
| preparing → running | 准备完成 | 执行具体操作 |
| running → completed | 正常结束 | 收集结果 |
| running → failed | 异常退出 | 收集错误信息 |
| * → cleaning | 任何非 done 状态的完成 | 执行清理脚本 |
| cleaning → done | 清理完成 | 更新最终状态 |
| * → failed | 超时(默认 30min) | 强行终止,清理 |
四、资源管理
资源类型
| 资源 | 粒度 | 冲突规则 |
|---|---|---|
| 浏览器实例 | 每机器 1 个 Camoufox | 全局互斥——同一台机器不能开两个浏览器 |
| 账号登录态 | 每个 account_id | 同一账号不能同时被两个操作使用 |
| 身份指纹 | 每个 identity_dir | 同一身份不能同时被两个浏览器使用 |
| 键盘/鼠标 | 每机器独占 | 远程操作时锁定本地输入(可选) |
资源锁实现
# 每台机器上的 guardd 维护的锁表
locks = {
"browser": {"held_by": "task_001", "acquired_at": "2026-06-16T18:00:00"},
"account:douyin_01": {"held_by": "task_001", "acquired_at": "..."},
"identity:douyin_01_camo": {"held_by": "task_001", "acquired_at": "..."},
}
状态归零 (Pre-flight Reset)
每个操作执行前,目标机器必须执行:
- 检查是否有残留浏览器进程 —
pkill -f camoufox(如果有, kill) - 检查是否有残留的 Playwright 进程 —
pkill -f playwright - 检查账号登录锁 — 清除该账号的锁
- 清理临时文件 — 删除上次操作的临时缓存
- 重置代理状态 — 恢复默认代理配置
- 确认资源可用 — 返回"已就绪"状态
五、操作类型全表
| 操作 | 类型 | 机器 | 状态管理 | 并发 | 超时 |
|---|---|---|---|---|---|
| 账号登录 | A 本机浏览器 | assigned_machine | 锁 browser + 锁 account | 1 账号/次 | 120s |
| 养号执行 | A 本机浏览器 | assigned_machine | 锁 browser + 锁 identity | 1 身份/次 | 60min |
| 信息采集 | B 远程脚本 | 任意在线机器 | 只读,无需锁 | 并发 | 60s |
| 内容发布 | A 本机浏览器 | assigned_machine | 锁 browser + 锁 account | 1 账号/次 | 300s |
| 环境同步 | B 远程脚本 | 指定机器 | 互斥(同一机器不能同时同步) | 1 台/次 | 120s |
| 对账检查 | B 远程脚本 | 指定机器 | 只读,无需锁 | 并发 | 60s |
| 远程Shell | B 远程脚本 | 指定机器 | 无锁 | 并发 | 30s |
| 状态监控 | C 聚合 | 所有机器 | 只读 | 并发 | 10s |
六、日志与审计
日志结构
每个操作产生一条结构化日志:
{
"operation_id": "op_20260616_001",
"type": "account_login",
"target_machine": "5kechengdeAir",
"target_account": "douyin_01",
"status": "completed",
"states": [
{"state": "pending", "time": "18:00:00"},
{"state": "checking", "time": "18:00:01"},
{"state": "preparing", "time": "18:00:02"},
{"state": "running", "time": "18:00:05"},
{"state": "completed","time": "18:01:15"},
{"state": "cleaning", "time": "18:01:16"},
{"state": "done", "time": "18:01:17"}
],
"output": "login successful, cookie saved",
"error": null,
"triggered_by": "dashboard_user",
"duration_sec": 77
}
日志聚合
- 每台机器的 guardd 实时推送日志到中心
/api/logs/push - Dashboard 提供日志查看器: 按机器/操作/时间/状态过滤
- 日志保留 30 天,自动归档
七、并发与互斥策略
| 场景 | 策略 |
|---|---|
| 用户点「养号」时机器已在养号 | 拒绝,提示"5kecheng 正在养号中,是否停止当前任务?" |
| 用户点「登录」时浏览器已打开 | 先关闭浏览器,再重新打开 |
| 两台机器同时给同一账号发命令 | 不可能——账号只属于一台机器(ORACLE.yaml) |
| 用户从 Dashboard 和 CLI 同时操作 | 共享同一资源锁表,后到者等待或拒绝 |
| 机器离线时下发操作 | 缓存到队列,机器上线后自动执行(可选) |
八、安全与锁定
远程操作时的机器锁定
当通过 Dashboard 远程控制 5kechengdeAir 执行浏览器操作时:
| 安全措施 | 实现 |
|---|---|
| 键盘锁定 | 操作开始时,5kecheng 本地键盘暂时屏蔽(防止误触) |
| 屏幕提示 | 5kecheng 显示器显示"正在被远程控制"的提示 |
| 强制停止 | 任何时候按 Ctrl+C 或点击「停止」终止操作 |
| 超时自动释放 | 30min 无活动自动关闭浏览器并释放锁 |
注意
键盘锁定是进阶功能。初期先实现资源锁+状态机,锁定后续再做。
九、Dashboard 落地计划
Phase 1: 状态感知 (优先级最高)
- 三台机器心跳检测 (guardd 已有)
- 机器状态条: 每个视图顶部显示三台机器在线/离线/忙碌
- 账号级别的忙碌状态 (是否正在被操作)
Phase 2: 操作路由 (当前)
accountLogin()加 owner_machine 判断 + 远程执行clearCookies()加 owner_machine 判断deleteAccount()加 owner_machine 判断- 按钮状态: 远程账号显示"需在 XX 机器操作"或"远程执行"
Phase 3: 状态机引擎
- 统一的操作队列:
pending → checking → running → done - 资源锁: 账号级别和浏览器级别
- 操作前的 state reset (清理旧进程)
- 操作日志存储 + 查看器
Phase 4: 高级功能
- 键盘锁定 / 屏幕提示
- 离线队列 / 上线自动执行
- 操作 DAG (先后依赖)
- 多用户协作
附录: 参考的原始架构文档
本文设计参考了以下原有系统文档中的思想:
| 文档 | 关键借鉴 |
|---|---|
architecture/federated-multi-machine-architecture.md |
7大协同子系统(心跳/事件/任务/加密/知识/升级/传输)、guardd 7模块、三层数据架构 |
archive/CORE-ARCHITECTURE.md |
目录职责边界、agent-local/agent-sync 分离原则、软链接映射、技能分类 |
dashboard-v4-design.md |
cross_machine/data/ WPRA 写入规范、9个内置插件规范、机器感知设计、24h TTL 过期策略 |
COMMAND-CENTER-PLAN.md (99_system/) |
命名空间分层(agentos matrix/ave/crawl/fleet/serve) |
cross_machine/ARCHITECTURE-v2.md (04_memory/) |
WPRA 写分区·读聚合模型、Git 冲突消除方案 |
本次设计新增的概念
| 概念 | 来源 | 说明 |
|---|---|---|
| 操作状态机 | 新建 | pending→checking→preparing→running→completed→cleaning→done 完整生命周期 |
| 资源锁 | 新建 | 浏览器级/账号级/身份级互斥,防止冲突操作 |
| 状态归零 | 新建 | 操作前自动清理残留进程、释放锁、重置环境 |
| 操作路由 | 新建 | 根据 owner_machine 将操作路由到对应机器执行 |
| 双模对等 | 新建 | Dashboard 和 CLI 共享同一套底层 API |
十一、关键设计决策记录
| 决策 | 选项 | 选择 | 理由 |
|---|---|---|---|
| 操作同步还是异步? | 同步等待 / 异步队列 | 混合: 短操作(<10s)同步,长操作异步 | 登录等短操作需要实时反馈 |
| 远程执行通道 | SSH / HTTP / WebSocket | SSH (现有) + HTTP API (未来) | SSH 已打通,HTTP 需要额外服务 |
| 状态存储 | Redis / SQLite / 文件 | 文件 (现有 heartbeats/目录) | 零依赖,随 Git 同步 |
| 浏览器管理 | 每机器 1 个 / 多实例 | 1 个 Camoufox,轮流使用 | 指纹一致性,避免被检测 |
| CLI 与 Dashboard 关系 | 主从 / 对等 | 对等—同一套底层 API | CLI 无网络时也能用 |