benchmark-collector 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,8 @@
9
9
  - **零闪烁采集** — 使用 CDP 直接截图 + Runtime.evaluate,不触发 Playwright 内部钩子
10
10
  - **iframe 深度采集** — 通过 CDP frame targeting 采集页面内所有 iframe(含跨域)的 DOM 和可交互元素,支持 ERP 等多 iframe 页面
11
11
  - **Cookie 自动注入** — 从本地 Chrome 提取并解密 Cookie,保留登录态(支持 macOS / Windows / Linux)
12
- - **对话采集** — 支持采集客服系统中的对话消息
12
+ - **对话采集** — 支持 DOM 检测和 API 拦截两种模式采集客服对话消息,内置平台预设选择器自动匹配,消息自动去重
13
+ - **平台预设** — 内置常见客服平台(抖店、拼多多等)的对话容器选择器,无需手动指定
13
14
  - **长时间录制优化** — 写入节流、原子保存、CDP 自动清理,支持数小时连续采集
14
15
  - **增量保存** — 每步操作实时保存,进程异常退出也不丢数据
15
16
  - **跨平台** — 支持 macOS / Windows / Linux
@@ -31,14 +32,20 @@ npm install benchmark-collector
31
32
  ### CLI 方式
32
33
 
33
34
  ```bash
34
- # 基本用法(自动提取本地 Chrome Cookie 保留登录态)
35
+ # 基本用法(自动注入本地 Chrome Cookie 保留登录态)
35
36
  benchmark-collect --task "处理退款" --url "https://mms.pinduoduo.com"
36
37
 
37
- # 采集客服对话(指定对话容器选择器)
38
- benchmark-collect --task "客服对话" --url "https://im.jinritemai.com/pc_seller_v2/main/workspace" --chat-selector ".msg-list"
38
+ # 采集抖店客服对话(已内置预设,无需指定 --chat-selector)
39
+ benchmark-collect --task "客服对话" --url "https://im.jinritemai.com/pc_seller_v2/main/workspace" --cookie
39
40
 
40
- # 跳过 Cookie 注入(未登录状态)
41
- benchmark-collect --task "测试" --url "https://www.example.com" --no-cookie
41
+ # 采集拼多多客服对话(已内置预设,无需指定 --chat-selector)
42
+ benchmark-collect --task "客服对话" --url "https://mms.pinduoduo.com/chat-merchant/index.html" --cookie
43
+
44
+ # 手动指定对话容器选择器(覆盖平台预设)
45
+ benchmark-collect --task "客服对话" --url "https://example.com/chat" --chat-selector ".my-chat-list"
46
+
47
+ # 使用 API 拦截模式采集对话(适用于动态加载的 IM 系统)
48
+ benchmark-collect --task "客服对话" --url "https://example.com" --chat-api "im/message/list"
42
49
 
43
50
  # 开启 Trace 录制(⚠️ 长时间采集可能产生大文件)
44
51
  benchmark-collect --task "测试" --url "https://www.example.com" --trace
@@ -50,15 +57,39 @@ benchmark-collect --task "测试" --url "https://www.example.com" --trace
50
57
  |------|------|--------|
51
58
  | `--task` | 任务名称,用于目录命名 | `未命名任务` |
52
59
  | `--url` | 起始 URL | `https://www.example.com` |
53
- | `--chat-selector` | 对话容器的 CSS 选择器 | 自动检测 |
54
- | `--no-cookie` | 跳过本地 Chrome Cookie 注入 | 默认注入 |
60
+ | `--chat-selector` | 对话容器的 CSS 选择器(已内置常见平台预设,通常无需手动指定) | 自动匹配或自动检测 |
61
+ | `--chat-api` | API 拦截模式的 URL 匹配模式 | |
62
+ | `--cookie` | 从本地 Chrome 提取 Cookie 并注入 | 默认关闭 |
55
63
  | `--trace` | 开启 Playwright Trace 录制 | 默认关闭 |
56
64
 
57
- 启动后浏览器会自动打开,**手动操作页面**,所有操作会被实时录制。操作完成后按 `Ctrl+C` 结束采集。
65
+ ### 平台预设选择器
66
+
67
+ 以下平台已内置对话容器选择器,使用时无需手动指定 `--chat-selector`:
68
+
69
+ | 平台 | URL 特征 | 预设选择器 |
70
+ |------|----------|------------|
71
+ | 抖店(飞鸽 IM) | `im.jinritemai.com` | `.messageList` |
72
+ | 拼多多(商家后台) | `mms.pinduoduo.com` | `#msgListContainer` |
73
+
74
+ > 如需新增平台预设,编辑 `src/collector.ts` 中的 `PLATFORM_CHAT_SELECTORS` 映射表即可。
75
+
76
+ ### 对话采集模式
77
+
78
+ 工具支持两种对话采集模式:
79
+
80
+ 1. **DOM 检测模式**(默认):从页面 DOM 中提取对话消息
81
+ - 优先使用 `--chat-selector` 或平台预设选择器精确定位
82
+ - 未指定时自动评分检测最佳对话容器
83
+ - 内置消息去重:过滤重复内容、时间戳噪声、加载提示等
84
+
85
+ 2. **API 拦截模式**(`--chat-api`):拦截 IM 系统的网络请求,从接口返回数据中提取消息
86
+ - 适用于消息通过 API 动态加载的 IM 系统
87
+ - 支持自动分页和消息累积
88
+ - 消息按时间排序并自动去重
58
89
 
59
90
  ### Cookie 自动注入
60
91
 
61
- 工具默认会从本地 Chrome 浏览器提取目标站点的 Cookie 并注入,实现免登录采集。
92
+ 使用 `--cookie` 参数时,工具会从本地 Chrome 浏览器提取目标站点的 Cookie 并注入,实现免登录采集。
62
93
 
63
94
  - **macOS**: 通过 Keychain 获取加密密钥,支持 Chrome 127+ 新版加密格式
64
95
  - **Windows**: 通过 DPAPI + AES-256-GCM 解密
@@ -74,9 +105,10 @@ benchmark-collect --task "测试" --url "https://www.example.com" --trace
74
105
  import { BenchmarkCollector } from 'benchmark-collector';
75
106
 
76
107
  const collector = new BenchmarkCollector('处理退款', 'https://mms.pinduoduo.com', 'output', {
77
- chatSelector: '.msg-list', // 可选:对话选择器
78
- noCookie: false, // 可选:跳过 Cookie 注入
79
- trace: false, // 可选:开启 Trace
108
+ chatSelector: '.custom-chat', // 可选:覆盖平台预设选择器
109
+ chatApiPattern: 'im/msg/list', // 可选:API 拦截模式
110
+ cookie: true, // 可选:注入本地 Chrome Cookie
111
+ trace: false, // 可选:开启 Trace 录制
80
112
  });
81
113
  await collector.start();
82
114
  ```
@@ -90,6 +122,7 @@ output/处理退款_2026-03-22T10-00-00/
90
122
  ├── session.json # 完整会话记录(元数据 + 所有步骤)
91
123
  ├── recorded-script.js # 可直接 node 执行的 Playwright 回放脚本
92
124
  ├── trace.zip # Playwright Trace 文件(仅 --trace 模式)
125
+ ├── history-{id}.json # API 拦截模式的累积对话记录
93
126
  └── steps/
94
127
  ├── 0000_clean.png # 纯净截图
95
128
  ├── 0000_som.png # SoM 标注截图
@@ -107,11 +140,12 @@ output/处理退款_2026-03-22T10-00-00/
107
140
  | `session.json` | 会话元数据、每步的 action / selector / timestamp / URL | 训练数据主文件 |
108
141
  | `recorded-script.js` | Codegen 原始代码片段(干净的操作序列) | 算法训练 action 数据、集成到测试框架 |
109
142
  | `trace.zip` | Playwright Trace(仅 `--trace` 模式) | `npx playwright show-trace trace.zip` 可视化回放 |
143
+ | `history-{id}.json` | API 拦截模式累积的对话消息 | 客服对话训练数据(API 模式) |
110
144
  | `*_clean.png` | 纯净页面截图 | 视觉模型输入 |
111
145
  | `*_som.png` | SoM 标注截图(彩色编号框标记可交互元素) | 视觉 grounding 训练 |
112
146
  | `*_elements.json` | 可交互元素列表(tag / role / text / bbox / attributes) | 结构化 grounding 数据 |
113
147
  | `*_dom.html` | 页面 DOM 快照(含 iframe 内容) | DOM-based agent 训练 |
114
- | `*_conversation.json` | 对话消息(角色 / 内容 / 时间戳) | 客服对话训练数据 |
148
+ | `*_conversation.json` | 对话消息(角色 / 内容 / 时间戳 / 附件) | 客服对话训练数据(DOM 模式) |
115
149
 
116
150
  ## 回放与调试
117
151
 
@@ -13,6 +13,7 @@ function hasFlag(name) {
13
13
  const task = getArg('task', '未命名任务');
14
14
  const url = getArg('url', 'https://www.example.com');
15
15
  const chatSelector = getArg('chat-selector', '');
16
+ const chatApi = getArg('chat-api', '');
16
17
  const cookie = hasFlag('cookie');
17
18
  const trace = hasFlag('trace');
18
19
  console.log(`Benchmark 数据采集器`);
@@ -20,12 +21,15 @@ console.log(` 任务: ${task}`);
20
21
  console.log(` URL: ${url}`);
21
22
  if (chatSelector)
22
23
  console.log(` 对话选择器: ${chatSelector}`);
24
+ if (chatApi)
25
+ console.log(` 对话接口: ${chatApi}`);
23
26
  if (cookie)
24
27
  console.log(` Cookie: 自动从本地 Chrome 提取并注入`);
25
28
  if (trace)
26
29
  console.log(` Trace: 开启(⚠️ 长时间采集可能产生大文件)`);
27
30
  const collector = new collector_1.BenchmarkCollector(task, url, 'output', {
28
31
  chatSelector: chatSelector || undefined,
32
+ chatApiPattern: chatApi || undefined,
29
33
  cookie,
30
34
  trace,
31
35
  });
@@ -0,0 +1,115 @@
1
+ /**
2
+ * 通过拦截网络 API 响应来采集对话数据。
3
+ *
4
+ * 适用场景:
5
+ * - DOM 检测不准确时,直接从接口获取结构化数据
6
+ * - 接口分页加载(如 拼多多 plateau/chat/list),需要累积多次响应
7
+ * - 数据比 DOM 更完整(含历史消息、被折叠的消息等)
8
+ *
9
+ * 自动翻页:
10
+ * 检测到首次请求后,自动读取 start_msg_id 对应字段,
11
+ * 用响应中最早消息的 msg_id 作为下一页的 start_msg_id,
12
+ * 循环拉取直到没有更多消息。
13
+ *
14
+ * 使用方式:
15
+ * --chat-api "plateau/chat/list" 匹配 URL 包含该字符串的响应
16
+ * --chat-api "/api/im/messages" 支持多种 IM 接口
17
+ */
18
+ import { ChatMessage } from './types';
19
+ /** 捕获的请求模板(用于重放翻页) */
20
+ export interface CapturedRequest {
21
+ url: string;
22
+ method: string;
23
+ headers: Record<string, string>;
24
+ postData?: string;
25
+ /** 会话标识(如 data.list.with.id) */
26
+ conversationId?: string;
27
+ }
28
+ /** processResponse 的返回结果 */
29
+ export interface ProcessResult {
30
+ /** 本次响应新增的消息数 */
31
+ newMessageCount: number;
32
+ /** 本次响应的消息总数(含已有重复) */
33
+ responseMessageCount: number;
34
+ /** 最早消息的 ID(用作下一页 start_msg_id) */
35
+ oldestMsgId?: string;
36
+ /** 是否还有更多历史消息(newCount > 0 且非全部重复) */
37
+ hasMore: boolean;
38
+ }
39
+ /** API 对话采集器 */
40
+ export declare class ApiConversationCapture {
41
+ /** URL 匹配模式 */
42
+ private pattern;
43
+ /** 已采集的消息(按 ID 去重) */
44
+ private messageMap;
45
+ /** 原始响应缓存(用于调试) */
46
+ private rawResponses;
47
+ /** 变更回调 */
48
+ private onChange?;
49
+ /** 捕获的请求模板(首次匹配的请求,用于翻页重放) */
50
+ capturedRequest?: CapturedRequest;
51
+ /** 当前正在自动翻页 */
52
+ private _paginating;
53
+ constructor(pattern: string, onChange?: () => void);
54
+ /** 检查 URL 是否匹配 */
55
+ matches(url: string): boolean;
56
+ get isPaginating(): boolean;
57
+ set isPaginating(v: boolean);
58
+ /** 保存请求模板 */
59
+ saveRequestTemplate(req: CapturedRequest): void;
60
+ /** 处理一个 API 响应体,返回翻页信息 */
61
+ processResponse(url: string, body: string): ProcessResult;
62
+ /**
63
+ * 构建翻页请求的 postData。
64
+ * 在原始请求体中替换 start_msg_id 为新的 cursor。
65
+ */
66
+ buildPageRequestBody(oldestMsgId: string): string | undefined;
67
+ /** 递归替换分页 cursor 字段 */
68
+ private replacePageCursor;
69
+ /** 获取累积的完整对话(按时间排序,去重后) */
70
+ getMessages(): ChatMessage[];
71
+ /** 获取消息数量 */
72
+ get messageCount(): number;
73
+ /** 获取诊断信息 */
74
+ getDiagnostics(): {
75
+ pattern: string;
76
+ responseCount: number;
77
+ messageCount: number;
78
+ conversationId: string | undefined;
79
+ lastResponseTime: number | null;
80
+ };
81
+ /** 检测响应中是否有 has_more 字段 */
82
+ private detectHasMore;
83
+ /** 在原始 JSON 中找到消息数组(未经解析的原始对象),用于提取 pre_msg_id 等 */
84
+ private findRawMessageArray;
85
+ /**
86
+ * 递归搜索 JSON 中的消息数组。
87
+ * 支持常见 IM 接口返回格式:
88
+ * { data: { list: [...] } }
89
+ * { result: { messages: [...] } }
90
+ * { data: { chat_list: [...] } }
91
+ * { messages: [...] }
92
+ * [...] (直接数组)
93
+ */
94
+ private extractMessages;
95
+ /** 尝试将数组解析为消息列表 */
96
+ private tryParseMessageArray;
97
+ /** 检查一个对象是否像消息 */
98
+ private looksLikeMessage;
99
+ /** 解析单条消息 */
100
+ private parseOneMessage;
101
+ /** 提取消息文本内容 */
102
+ private extractContent;
103
+ /** 提取角色 */
104
+ private extractRole;
105
+ /** 提取时间 */
106
+ private extractTime;
107
+ /** 从 API 消息对象中提取媒体附件 */
108
+ private extractMediaFromApi;
109
+ }
110
+ /**
111
+ * 从请求体中提取会话 ID。
112
+ * 支持:data.list.with.id(拼多多), conversation_id, session_id 等
113
+ */
114
+ export declare function extractConversationId(postData: string): string | undefined;
115
+ //# sourceMappingURL=api-conversation-capture.d.ts.map