Files
ChatLab/docs/cn/standard/chatlab-pull.md
T
2026-04-26 23:38:29 +08:00

334 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
outline: deep
---
# Pull 远程数据源协议
> v1
本文档定义第三方数据源暴露标准 HTTP 端点供 ChatLab 主动拉取数据的协议规范。这是 ChatLab 生态**推荐的第三方集成方式**。
::: tip 两种导入方式
- **[Push 模式](./chatlab-import.md)**:外部系统主动将数据推送到 ChatLab 的导入接口。适用于脚本集成、一次性文件导入。
- **Pull 模式**(本文档):第三方暴露标准 HTTP 端点,ChatLab 主动拉取数据。**推荐的第三方集成方式。**
:::
## 为什么 Pull 是推荐方案
- 第三方工具是数据生产者,天然适合暴露数据;ChatLab 是数据消费者/分析者,天然适合主动获取
- 用户只需在 ChatLab UI 中输入数据源地址,即可浏览、选择、同步——操作完全在 ChatLab 端完成
- Push 模式需要第三方实现 HTTP 客户端逻辑(批次管理、重试、游标维护),门槛更高
- Pull 协议定义的是**通用数据暴露标准**,不只服务 ChatLab,任何兼容工具都可以接入
**适用场景:**
- 外部采集端运行在远程设备上,只需暴露 HTTP 接口
- 用户希望在 ChatLab UI 上浏览可用对话、选择导入、点击"立即同步"
- 需要定时自动增量同步的长期运行场景
---
## 概述
Pull 模式的工作流程分为三个阶段:
```
1. 发现:ChatLab 获取数据源上的所有可用对话列表
2. 拉取:用户选择对话后,ChatLab 拉取历史消息
3. 同步:定时增量拉取新消息(可选 SSE 实时通知加速)
```
第三方数据源只需按本协议实现标准 HTTP 端点,ChatLab(以及未来任何兼容工具)即可自动完成发现、全量拉取和增量同步。
---
## 阶段一:发现可用对话
ChatLab 连接到远程数据源后,首先获取所有可拉取的对话列表。
### GET /sessions
```
GET {baseUrl}/sessions
Authorization: Bearer {token} ← 仅配置了 token 时携带
Accept: application/json
```
**可选参数:**
| 参数 | 类型 | 说明 |
| --------- | ------ | -------------------------------------------------------------------------- |
| `keyword` | string | 按对话名称模糊搜索。搜索语义由服务端定义,推荐按 `name` 模糊匹配,可选扩展到 `id` |
| `limit` | number | 返回条数限制。未传时默认返回全部;若服务端实现分页,建议设置合理上限 |
| `cursor` | string | 分页游标。仅在服务端支持分页发现时使用;`keyword` 变化后必须重新从第一页开始 |
**响应:**
```json
{
"sessions": [
{
"id": "xxx@chatroom",
"name": "产品讨论群",
"platform": "wechat",
"type": "group",
"messageCount": 58000,
"memberCount": 86,
"lastMessageAt": 1711468800
},
{
"id": "wxid_friend_a",
"name": "张三",
"platform": "wechat",
"type": "private",
"messageCount": 1200,
"memberCount": 2,
"lastMessageAt": 1711465200
}
],
"page": {
"hasMore": true,
"nextCursor": "eyJsYXN0TWVzc2FnZUF0IjoxNzExNDY1MjAwLCJpZCI6Ind4aWRfZnJpZW5kX2EifQ=="
}
}
```
| 字段 | 类型 | 必填 | 说明 |
| --------------- | ------ | ---- | ----------------------------------- |
| `id` | string | 是 | 对话在数据源中的唯一标识 |
| `name` | string | 是 | 对话名称(群名/联系人名) |
| `platform` | string | 是 | 平台标识(与 Push 模式相同) |
| `type` | string | 是 | `group` / `private` |
| `messageCount` | number | 否 | 消息总数(用于 ChatLab 展示预估量) |
| `memberCount` | number | 否 | 成员数 |
| `lastMessageAt` | number | 否 | 最新消息时间戳 |
`page` 为**可选增强字段**
| 字段 | 类型 | 必填 | 说明 |
| ------------ | ------- | ---- | ------------------------------------------------------------------ |
| `hasMore` | boolean | 否 | 是否还有下一页。仅在服务端支持分页发现时返回 |
| `nextCursor` | string | 否 | 下一页游标。`hasMore=true` 时应返回;客户端原样透传给下次请求 |
**兼容规则:**
- 旧版服务端可以继续只返回 `{ "sessions": [...] }`,不带 `page`
- ChatLab 客户端在响应中**未发现** `page` 字段时,应按“单次全量结果”处理
- 若响应中包含 `page`,客户端可根据产品交互选择手动“加载更多”或自动续拉
- ChatLab 当前推荐在 UI 中使用手动“加载更多”,按 `hasMore / nextCursor` 拉取后续页面
**分页一致性建议:**
- 服务端应保证分页顺序稳定,推荐使用固定排序(例如 `lastMessageAt desc, id asc`
- `cursor` 必须与当前查询条件绑定;只要 `keyword` 变化,旧 `cursor` 就应视为失效
- 不建议在 `/sessions` 发现接口中使用 `offset` 分页,避免在列表变化时出现重复或漏项
ChatLab 在 UI 中展示该列表,用户选择需要导入的对话。
---
## 阶段二:拉取对话数据
用户选定对话后,ChatLab 拉取指定对话的数据。
### GET /sessions/:id/messages
```
GET {baseUrl}/sessions/{sessionId}/messages?format=chatlab&since={timestamp}
Authorization: Bearer {token}
Accept: application/json, application/x-ndjson
```
| 参数 | 必填 | 说明 |
| ----------- | ---- | ------------------------------------------------------------------- |
| `sessionId` | 是 | 来自阶段一返回的对话 `id` |
| `format` | 是 | 固定为 `chatlab`,要求数据源返回 ChatLab 标准格式 |
| `since` | 否 | Unix 时间戳(秒级)。省略或为 `0` 时为全量拉取,大于 0 时为增量拉取 |
| `end` | 否 | Unix 时间戳(秒级)。指定时间范围上界,用于历史回填等分段拉取 |
| `limit` | 否 | 单次返回的最大消息数,用于分页 |
| `offset` | 否 | 分页偏移量,配合 `limit` 使用 |
### 数据携带规则
- **首次全量**`since` 为空或 0):**必须**包含 `chatlab` + `meta` + `members` + `messages`
- **增量同步**`since > 0`):**必须**包含 `messages``meta` / `members` **仅在发生实际变更时携带**,未变更时不得携带,以避免历史快照覆盖当前状态
- 无新数据时返回空 `messages` 数组
### 响应格式
响应为标准 [ChatLab Format](./chatlab-format.md)JSON 或 JSONL),并附带 `sync` 同步元信息。
```json
{
"chatlab": { "version": "0.0.2", "exportedAt": 1711468800 },
"meta": { "name": "产品讨论群", "platform": "wechat", "type": "group" },
"members": [ ... ],
"messages": [ ... ],
"sync": {
"hasMore": true,
"nextSince": 1711468800,
"nextOffset": 5000,
"watermark": 1711470000
}
}
```
### sync 同步元信息
| 字段 | 类型 | 说明 |
| ------------ | ------- | ------------------------------------------------------------------------------------------ |
| `hasMore` | boolean | 是否还有更多数据。为 `true` 时 ChatLab 自动续拉 |
| `nextSince` | number | 下一次请求建议使用的 `since` 值 |
| `nextOffset` | number | 下一次请求建议使用的 `offset` 值,用于分页续拉 |
| `watermark` | number | 本次拉取的快照上界时间戳,防止分页期间新数据写入导致的窗口漂移 |
**sync 块的必要性规则:**
| 数据源返回方式 | sync 块要求 | 说明 |
| -------------------------- | ----------- | ---------------------------------------------------------------- |
| 单次返回全部数据(不分页) | 可选 | ChatLab 视 `messages` 为完整结果 |
| 支持 `limit`/`offset` 分页 | **必须** | 至少包含 `hasMore`;若无法保证快照一致性,还必须包含 `watermark` |
::: warning 注意
若数据源支持分页但未返回 `sync` 块,ChatLab 不保证自动续拉和分页一致性——仅处理首次返回的数据。
:::
### 快照一致性要求
- 同一次分页拉取(通过 `limit`/`offset` 遍历)**应当**基于同一数据快照视图
- 若数据源无法保证快照一致性,**必须**返回 `sync.watermark`,供 ChatLab 固定拉取窗口上界
### 分批拉取策略
对于大量历史数据(如数万条消息),推荐两种分批方式:
1. **时间窗口分批**:通过 `since` + `end` 指定时间段,逐段拉取
2. **分页分批**:通过 `limit` + `offset` 做数据库级分页,结合 `sync` 块自动续拉
ChatLab 会自动处理分批场景,通过去重机制保证不重复写入。
---
## 阶段三:定时增量同步
ChatLab 按用户配置的间隔,定期对已订阅的对话执行增量拉取:
```
GET {baseUrl}/sessions/{sessionId}/messages?format=chatlab&since={lastPullAt}
```
远程数据源返回 `since` 之后的增量消息。ChatLab 通过内部导入管道处理(去重、meta/members 更新、FTS 索引等全部复用 Push 模式逻辑)。
---
## 可选:SSE 实时通知
除定时轮询外,远程数据源可**可选**实现 SSEServer-Sent Events)端点,用于**通知 ChatLab 有新数据可拉取**。
::: warning 重要
SSE 仅作为通知通道,不是数据同步主通道。ChatLab 不假设 SSE 事件可靠送达(网络断连、进程重启均可能丢失事件)。最终数据一致性始终由定时 Pull 保证。SSE 的作用是将增量同步延迟从"分钟级"降到"秒级"。
:::
### GET /push/messages
```
GET {baseUrl}/push/messages
Authorization: Bearer {token}
Accept: text/event-stream
```
**事件格式:**
```
event: message.new
data: {"eventId":"evt_001","sessionId":"xxx@chatroom","timestamp":1711468800}
```
| 字段 | 类型 | 必填 | 说明 |
| ------------------- | ------ | ---- | ------------------------------------------ |
| `eventId` | string | 是 | 事件唯一 ID,用于 ChatLab 去重已处理的通知 |
| `sessionId` | string | 是 | 有新消息的对话 ID |
| `timestamp` | number | 是 | 新消息的时间戳 |
| `platformMessageId` | string | 否 | 新消息的平台 ID(如可获取) |
ChatLab 接收到 SSE 事件后,**触发一次该 session 的增量拉取**(调用 `GET /sessions/:id/messages?format=chatlab&since={lastPullAt}`),而非直接将事件数据写入存储。
---
## 认证
远程数据源可选择是否要求认证。如果需要,使用 `Authorization: Bearer {token}` 机制。
::: tip SSE 认证
部分数据源额外支持 `?access_token=TOKEN` 查询参数方式传递 Token(SSE 长连接场景推荐此方式,因为 EventSource API 不支持自定义 Header)。ChatLab 在连接 SSE 时也支持查询参数传 Token。
:::
---
## 实现指南
### 最小实现(2 个端点)
只需实现以下两个端点即可接入 ChatLab:
| 端点 | 说明 |
| --------------------------------------------------- | --------------------- |
| `GET /sessions` | 返回对话列表 |
| `GET /sessions/:id/messages?format=chatlab&since=X` | 返回 ChatLab 格式数据 |
最小实现不需要分页、SSE 或复杂的 `sync` 块。ChatLab 会将响应中的 `messages` 视为完整数据。
### 增强实现
| 能力 | 说明 |
| -------------------------- | ------------------------------------------------------------ |
| `GET /push/messages` | SSE 实时通知(仅唤醒拉取,不传输完整数据) |
| 支持 `limit`/`offset` 分页 | 大量历史数据的分页拉取 |
| 支持 `end` 参数 | 时间窗口分段拉取 |
| 响应包含 `sync` 块 | 提供 `hasMore`/`nextSince`/`nextOffset`/`watermark` 续拉信息 |
### 数据格式
所有数据响应必须符合 [ChatLab 标准化格式规范](./chatlab-format.md)JSON 或 JSONL),包括 `chatlab``meta``members``messages` 四个标准块。
### 媒体文件
如果数据源的消息中包含媒体引用,`attachments` 中的 `filePath``dataUri` 可指向数据源的媒体服务端点。ChatLab 当前按"协议预留"处理,未来版本将支持从数据源拉取媒体文件。
---
## 示例场景
某采集端在手机上持续采集微信消息,暴露 `GET /sessions``GET /sessions/:id/messages` 两个端点。用户在 ChatLab 中操作:
```
1. 在 ChatLab 设置中添加远程数据源(输入采集端 URL + 可选 Token
2. ChatLab 调用 GET {baseUrl}/sessions
→ 展示 86 个群和 200 个私聊
3. 用户选择其中 5 个群导入
4. ChatLab 立即执行全量拉取:
GET {baseUrl}/sessions/{id}/messages?format=chatlab&since=0
→ 如有 sync.hasMore=true,自动续拉直到全部完成
5. 之后每小时自动增量同步:
GET {baseUrl}/sessions/{id}/messages?format=chatlab&since={lastPullAt}
6. 如果采集端实现了 SSE
收到 message.new 事件 → 立即触发增量拉取(不等定时器)
7. 用户可随时在 ChatLab UI 点击"立即同步"
```
---
## 相关文档
- [ChatLab API 文档](./chatlab-api.md) — 查询、导出和系统端点
- [Push 导入协议](./chatlab-import.md) — 外部系统主动推送数据到 ChatLab
- [ChatLab 标准化格式规范](./chatlab-format.md) — 数据交换格式定义