移植自 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 带网关原话 / 正常返回)。
14 KiB
14 KiB
AgentOS 原子操作体系 v2 完整规划
状态: 规划中 | 创建: 2026-06-21 09:30 目标: 建立系统性、可持续迭代的原子操作开发与测试体系 角色: ghai 是项目负责人/决策者,Claw 是技术搭档/建议者
一、核心理念
1.1 我们不是在写代码,是在建立一套"自动化操作知识库"
每个平台的每个操作,都应该像一本字典里的词条:
- 有明确的"前提条件"(在什么页面/状态下才能做)
- 有确定的"执行方式"(用什么方法做)
- 有可验证的"结果"(做了之后页面变成什么样子)
1.2 不做"按键精灵",做"智能操作代理"
按键精灵: 盲按 → 不管结果 → 继续按下一个 智能代理: 检测当前状态 → 确认可以操作 → 执行 → 验证结果 → 记录
1.3 开发效率 > 代码量
一次录制 = 10个原子操作的新代码,比你手写10个函数快100倍。 但前提是:录制系统能捕获足够的信息(状态+操作+结果),并且你我能高效标注它。
二、总架构:三层 + 两工具 + 一视图
┌─────────────────────────────────────────────────────────┐
│ 看板 (Dashboard) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 操作流程图 │ │ 录制标注界面 │ │ 测试结果面板 │ │
│ │ (你的操作地图)│ │ (你的编辑器) │ │ (你的眼睛) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 工具层 │
│ ┌──────────────────────┐ ┌───────────────────────┐ │
│ │ 增强录制系统 │ │ 单步调试模式 │ │
│ │ (捕获三层状态+操作) │ │ (mc debug --step) │ │
│ │ (支持人工标注命名) │ │ (3秒出结果不等待) │ │
│ └──────────────────────┘ └───────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 引擎层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ PageState (统一状态模型) │ │
│ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │
│ │ │L1: │→│L2: │→│L3: │→│Action│→│验证 │ │ │
│ │ │页面│ │区域│ │元素│ │执行 │ │结果 │ │ │
│ │ └────┘ └────┘ └────┘ └────┘ └────┘ │ │
│ └──────────────────────────────────────────────────┘ │
│ 原子操作库 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 抖音 ops │ │ 小红书ops │ │ 未来平台 │ │
│ │ douyin/ │ │ xhs/ │ │ ... │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
三、PageState 状态模型(引擎层核心)
3.1 抖音页面状态定义
| 状态名 | 判断条件 | 可用操作 |
|---|---|---|
grid |
URL含jingxuan,card_count>0 | scroll_feed, open_video, search, goto_profile |
player_modal |
URL含modal_id,video>0 | like, collect, follow, comment, next_video, go_back |
player_full |
URL含/video/xxx | like, collect, follow, comment, go_back |
search |
URL含/search/ | click_result, scroll, go_back |
profile |
URL含/user/ | read_field, go_back |
3.2 小红书页面状态定义
| 状态名 | 判断条件 | 可用操作 |
|---|---|---|
grid |
URL=/explore, note-item>0 | scroll_feed, click_note, search, goto_profile |
note_detail |
URL=/explore/xxx | like, collect, comment, post_comment, follow, go_back |
search |
URL=/search_result | click_result, go_back |
profile |
URL=/user/profile/xxx | read_field, go_back |
3.3 状态转换规则
grid ──(open_video/click_note)──> player/note_detail
grid ──(search)──────────────────> search
grid ──(goto_profile)────────────> profile
player ──(go_back)───────────────> grid
player ──(next_video)────────────> player (不同视频)
search ──(click_result)──────────> player/note_detail
search ──(go_back)───────────────> grid
profile ──(go_back)──────────────> grid
四、增强录制系统(工具层)
4.1 录制数据的完整结构
{
"session": {
"platform": "douyin",
"account": "douyin_test",
"start_time": "2026-06-21T10:00:00",
"window_size": [702, 783]
},
"steps": [
{
"id": 1,
"label": "", // 你来命名(如"点开第一个视频")
"before": {
"url": "...",
"page_type": "grid",
"dom_snapshot": {
"video_count": 0,
"card_count": 20,
"has_search_bar": true,
"has_nav": true,
"author_id": null,
"is_self": false
},
"mouse_position": [434, 190],
"mouse_region": "feed_area",
"nearby_elements": [
{"tag": "A", "class": "cover mask ld", "text": "", "x": 375, "y": 292}
]
},
"action": {
"type": "click",
"x": 375,
"y": 292,
"target_tag": "A",
"target_class": "cover mask ld",
"target_text": "",
"details": "笔记封面点击,可能打开了笔记详情页"
},
"after": {
"url": "https://www.douyin.com/jingxuan?modal_id=xxx",
"page_type": "player_modal",
"dom_snapshot": {
"video_count": 1,
"card_count": 0,
"has_comment_list": true
},
"url_changed": true,
"dom_diff": "video元素从0变为1,进入了播放页"
}
}
]
}
4.2 录音交互流程
1. 你启动录制: mc record --account=douyin_test --platform=douyin
→ 浏览器打开,开始录制
2. 你做操作,按 `·` 标记每一步
→ 系统自动捕获 before/action/after
3. 录制结束按 Esc
→ 系统保存完整录制数据
4. 进入标注界面(Dashboard新页面)
→ 展示每一步的 before/action/after
→ 自动检测操作类型(点击/按键/滚动)
→ 自动建议原子操作名称(like/collect/comment)
→ 你可以修改名称和补充说明
→ 连续2步可以合并为1个原子操作(如:开评论(x) + 输入 + 发送 = post_comment)
5. 标注完成 → 系统自动生成原子操作代码模板
→ 你审核通过 → 代码入库
4.3 录制界面的设计要点
┌──────────────────────────────────────────────┐
│ 录制回放: douyin_test - 2026-06-21 10:00 │
├──────────────────────────────────────────────┤
│ │
│ Step 1: grid → player_modal │
│ ┌────────┐ ┌──────────┐ ┌──────────┐ │
│ │ BEFORE │→│ ACTION │→│ AFTER │ │
│ │ 首页 │ │ 双击卡片 │ │ 播放页 │ │
│ │ card=20│ │ A.cover │ │ video=1 │ │
│ └────────┘ └──────────┘ └──────────┘ │
│ 操作名: [open_video___] ← 你填或系统建议 │
│ 备注: [双击feed卡片进入首个视频___] │
│ │
│ Step 2: 播放页 → 播放页(已点赞) │
│ ┌────────┐ ┌──────────┐ ┌──────────┐ │
│ │ BEFORE │→│ ACTION │→│ AFTER │ │
│ │ 播放页 │ │ 按z键 │ │ 心变红 │ │
│ └────────┘ └──────────┘ └──────────┘ │
│ 操作名: [like_________] ← 系统建议: like │
│ 备注: [键盘z点赞_______________] │
│ │
│ [合并步骤1+2?] [导出为蓝图] [生成代码] │
└──────────────────────────────────────────────┘
五、单步调试模式(工具层)
5.1 命令设计
# 模式1: 启动交互式调试(浏览器保持打开)
mc debug --accounts=douyin_test --platform=douyin
→ 浏览器打开,保持运行
→ 等待命令
# 模式2: 执行单步操作
mc step open_video # 只执行这一个操作,返回结果+消耗时间
mc step like # 继续下一步
mc step collect # 3秒内出结果
# 模式3: 批量单步(自动但不跳过)
mc step open_video like collect follow open_comments post_comment
→ 每步执行后停顿1秒,显示结果,继续下一步
5.2 交互流程
> mc debug --accounts=douyin_test
✅ 浏览器已启动,当前状态: grid (首页)
等待命令...
> open_video
✅ open_video → video_detail (5.2s) 状态: grid → player_modal
验证: video=1 ✅ 可继续下一步
> like
✅ like → 👍 (1.1s) 状态: player_modal
验证: 通过✅
> collect
❌ collect → - (0.0s) 状态: player_modal
元素未找到: [data-e2e="video-player-collect"]
回退: JS搜索也失败,坐标兜底也失败
建议: 你在浏览器里点一下收藏,我看看实际DOM是什么
六、看板集成(展示层)
6.1 新增三个页面
| 页面 | 路径 | 功能 |
|---|---|---|
| 操作流程图 | /ops-flow |
SVG流程图,实时显示测试状态 |
| 录制标注 | /recorder |
回放录制,标注原子操作,合并步骤 |
| 测试控制台 | /debug |
单步测试,查看日志,状态监控 |
6.2 操作流程图功能
- 展示所有平台的操作状态机
- 绿色 = 已测试通过
- 橙黄色 = 部分通过/有问题
- 灰色 = 未测试
- 点击任意节点 → 显示该操作的详细信息
- 点击连线 → 显示状态转换条件
- 底部显示当前正在测试的"路径"和进度
七、实施计划(分阶段)
Phase 1: 基础设施(1-2天)
| 任务 | 说明 |
|---|---|
| 1.1 实现 PageInspector | L1: 页面类型, L2: 鼠标区域, L3: 元素详情 |
| 1.2 增强 Recorder | 录制时捕获完整三层状态 |
| 1.3 实现 mc debug | 浏览器保持打开,接受后续命令 |
Phase 2: 标注系统 + 流程图(1-2天)
| 任务 | 说明 |
|---|---|
| 2.1 录制标注页面 | 看板新页面,回放+标注+合并步骤 |
| 2.2 操作流程图页面 | SVG流程图 + 实时状态 |
| 2.3 测试控制台页面 | 单步调试 + 日志查看 |
Phase 3: 存量修复(1天)
| 任务 | 说明 |
|---|---|
| 3.1 录一次抖音全流程 | 你录制 → 我分析 |
| 3.2 修复 collect + open_comments | 基于录制数据修复 |
| 3.3 录一次小红书全流程 | 你录制 → 我分析 |
| 3.4 修复小红书全部操作 | 基于录制数据修复 |
Phase 4: 扩展(持续)
| 任务 | 说明 |
|---|---|
| 4.1 新增平台 | 每个平台遵循同样流程:录制 → 标注 → 测试 |
| 4.2 蓝图组合 | 基于稳定的原子操作,组合成养号/评论等蓝图 |
| 4.3 定时任务 | 每日自动化执行 |
八、当前迭代聚焦
本周目标(2026-06-21 ~ 06-27)
Week 1: 抖音全流程打通 → 小红书全流程打通 → 看板可查看流程图
今天目标(2026-06-21)
- 你先录一次抖音完整流程(约10步),我在录制过程中捕获完整状态
- 一起标注这些步骤,给每个操作命名
- 基于录制数据修复问题(collect + open_comments)
- 完成抖音流程图,所有路径从灰色变绿色
九、你需要的(而不是我想要的)
这个计划的核心目标只有一个:让你能高效地验证想法,而不是把时间花在猜代码上。
每一步都是:
- 你操作 → 我记录
- 你标注 → 我理解
- 你测试 → 我修复
- 你确认 → 下一个
你不是在写代码。你是在教会我你的平台操作方式。 我是你的"手"和"眼",你是"大脑"和"决策者"。