Skip to content

Push 导入协议 ​

v1

本文档定义外部数据源向 ChatLab 推送聊天数据的标准导入协议。覆盖首次全量导入、历史回填、周期性增量同步三类场景。

两种导入方式

  • Push 模式(本文档):外部系统主动将数据推送到 ChatLab 的导入接口。适用于脚本集成、一次性文件导入等场景。
  • Pull 模式:第三方暴露标准 HTTP 端点,ChatLab 主动拉取数据。推荐的第三方集成方式。

两种模式底层共用同一套导入逻辑(去重、meta/members 更新、会话索引维护),数据格式统一为 ChatLab Format。

设计原则 ​

  1. 统一入口:首次导入和增量导入使用同一个端点,调用方无需区分。
  2. 接口最小化:1 个导入接口 + 2 个查询接口覆盖全部 Push 场景。
  3. 双层幂等:请求级幂等(Idempotency-Key)+ 记录级去重(platformMessageId / 确定性 fallback key),承诺 at-least-once + deterministic dedupe。
  4. 同步优先:小批量导入同步返回 200 OK 和写入结果。
  5. 默认自动更新:meta 和 members 默认随导入请求自动更新,可通过 options 控制。

基础约定 ​

服务地址 ​

Base URL:http://<host>:<port>   (桌面端默认 127.0.0.1:3110)
Prefix:  /api/v1

认证 ​

所有请求必须携带 Bearer Token:

Authorization: Bearer <token>

Token 在 ChatLab 设置页面生成,格式为 clb_ + 64 字符 hex。

Content-Type ​

application/json    # 标准 JSON body(≤50MB)

接口总表 ​

方法路径说明
POST/api/v1/imports/:sessionId导入消息到指定会话(首次自动创建,后续追加)
GET/api/v1/sessions/:id查询会话状态(主要用于对账校验)
GET/api/v1/sessions列出所有会话(发现目标 Session)

查询接口的详细说明请参阅 ChatLab API 文档。


POST /api/v1/imports/:sessionId ​

唯一导入入口。 会话不存在时自动创建,已存在时追加数据并自动更新 meta/members。

Path 参数 ​

参数类型说明
sessionIdstringSession ID,由调用方生成并维护。同一聊天来源必须保持固定,跨批一致。生成策略见下方。

Session ID 生成策略:

优先级场景推荐格式示例
1(首选)有平台原始 ID{platform}_{originalId}whatsapp_112233445566、qq_123456789
2文件导入(有结构化 ID){platform}_{meta.groupId} 或 {platform}_{对方platformId}whatsapp_112233445566
3文件导入(无结构化标识)file_{SHA256(文件内容)[:16]}file_a1b2c3d4e5f6g7h8
4(兜底)一次性导入import_{UUID}import_550e8400-e29b-41d4-a716-446655440000

注意

  • 不建议使用文件路径作为 sessionId 的输入——文件重命名会导致 sessionId 变化
  • 同一聊天来源的首次导入和增量导入必须使用相同的 sessionId

请求 Header ​

Header必填说明
Authorization是Bearer <token>
Content-Type是application/json
Idempotency-Key建议当前批次的唯一标识,用于重试安全。建议格式:{sessionId}-{batchIndex}-{windowStart}

快速测试 ​

复制以下命令即可直接测试(将 YOUR_TOKEN 和端口替换为实际值):

bash
curl http://127.0.0.1:3110/api/v1/imports/group_abc123 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "chatlab": { "version": "0.0.2", "exportedAt": 1711468800, "generator": "test" },
  "meta": { "name": "产品讨论群", "platform": "whatsapp", "type": "group", "groupId": "112233445566" },
  "members": [
    { "platformId": "user_a", "accountName": "张三", "roles": [{ "id": "owner" }] }
  ],
  "messages": [
    { "platformMessageId": "msg_1001", "sender": "user_a", "timestamp": 1711468800, "type": 0, "content": "Hello" }
  ]
}'

成功时返回 "success": true 和写入统计;重复调用同一 platformMessageId 会被去重(duplicateCount 增加,writtenCount 不变)。


请求 Body(JSON 模式) ​

json
{
  "chatlab": {
    "version": "0.0.2",
    "exportedAt": 1711468800,
    "generator": "YourSystem/1.0"
  },
  "meta": {
    "name": "产品讨论群",
    "platform": "whatsapp",
    "type": "group",
    "groupId": "112233445566",
    "groupAvatar": "data:image/jpeg;base64,...",
    "ownerId": "user_owner"
  },
  "members": [
    {
      "platformId": "user_a",
      "accountName": "张三",
      "groupNickname": "产品",
      "avatar": "data:image/jpeg;base64,...",
      "roles": [{ "id": "owner" }]
    }
  ],
  "messages": [
    {
      "platformMessageId": "msg_1001",
      "sender": "user_a",
      "accountName": "张三",
      "groupNickname": "产品",
      "timestamp": 1711468800,
      "type": 0,
      "content": "Hello",
      "replyToMessageId": "msg_1000"
    }
  ],
  "options": {
    "metaUpdateMode": "patch",
    "memberUpdateMode": "upsert"
  }
}

options 对象(可选) ​

字段类型默认值可选值说明
metaUpdateModestringpatchpatch / nonepatch:非空字段覆盖更新;none:跳过更新
memberUpdateModestringupsertupsert / noneupsert:新增+更新;none:跳过更新

提示

回填历史数据时建议传 "metaUpdateMode": "none" 防止旧群名覆盖当前值。

各块携带规则 ​

块会话首次创建后续增量导入语义
chatlab必填可选首次用于创建会话;后续用于版本兼容判断
meta必填可选首次作为初始值;后续携带则非空字段自动覆盖更新
members必填可选首次写入全量成员;后续按 platformId 做 upsert
messages必填必填每批必须包含至少一条消息

Meta 自动更新规则:

  • 调用方传了且值不为空的字段 → 覆盖更新
  • 调用方未传或值为空的字段 → 保持原值不变
  • platform 和 type 不允许增量更新(会话创建后不变)

Members Upsert 规则:

  • 按 platformId 匹配:新 platformId → 插入为新成员;已存在的 → 更新非空字段
  • 不携带 members 时:消息中出现的未知 sender 仍会被自动创建为成员(仅含最小信息)

字段定义 ​

chatlab 对象 ​

字段类型必填说明
versionstring是格式版本号,当前为 "0.0.2"
exportedAtnumber是导出/生成时间(秒级 Unix 时间戳)
generatorstring否生成工具/系统名称

meta 对象 ​

字段类型首次必填说明
namestring是群名/会话名
platformstring是平台标识(见下方枚举)
typestring是会话类型:group(群聊)/ private(私聊)
groupIdstring否群的平台原始 ID
groupAvatarstring否群头像,base64 Data URL 或网络 URL
ownerIdstring否导出者/所有者的 platformId

平台标识枚举:

值平台
wechat微信
qqQQ
telegramTelegram
discordDiscord
whatsappWhatsApp
lineLINE
slackSlack
unknown未知/其他

提示

如果你的平台不在上述列表中,可使用小写英文标识(如 signal、matrix),ChatLab 会按 unknown 的分析策略处理。

members 数组元素 ​

字段类型必填说明
platformIdstring是成员在平台的唯一标识(QQ号、用户ID等)
accountNamestring建议账号名称(不随群变化的原始昵称)
groupNicknamestring否群内专属昵称
avatarstring否头像,base64 Data URL 或网络 URL
rolesarray否角色列表,见 角色定义

messages 数组元素 ​

字段类型必填说明
senderstring是发送者的 platformId 或保留标识(如 SYSTEM)
timestampnumber是消息时间戳,秒级 Unix 时间戳
typenumber是消息类型枚举(见 消息类型)
accountNamestring建议发送时的账号名称
groupNicknamestring否发送时的群昵称
contentstring|null否纯文本内容,非文本消息可为 null
platformMessageIdstring强烈建议消息的平台原始 ID,去重的首选依据
replyToMessageIdstring否回复目标消息的 platformMessageId

sender 字段规则:

  1. 必须是稳定的 platformId 或保留标识 SYSTEM
  2. 若 sender 对应的成员不在 members 中,ChatLab 自动补创建成员(仅含最小信息)
  3. SYSTEM 用于系统消息(入群/退群/公告等),不计入成员统计

成功响应 ​

json
{
  "success": true,
  "data": {
    "sessionId": "group_abc123",
    "created": false,
    "batch": {
      "receivedCount": 5000,
      "writtenCount": 4986,
      "duplicateCount": 14
    },
    "session": {
      "totalCount": 128640,
      "memberCount": 86,
      "firstTimestamp": 1609459200,
      "lastTimestamp": 1711468800
    },
    "updates": {
      "metaUpdated": true,
      "membersAdded": 3,
      "membersUpdated": 5
    }
  }
}
字段说明
createdtrue 表示本次请求触发了会话创建(首次导入)
batch.receivedCount本批收到的消息条数
batch.writtenCount实际写入的条数
batch.duplicateCount因去重跳过的条数
session.totalCount写入后会话的累计消息总数
session.memberCount会话的成员总数
session.firstTimestamp会话内最早消息时间戳
session.lastTimestamp会话内最新消息时间戳
updates.metaUpdated本次请求是否触发了 meta 更新
updates.membersAdded本次新增的成员数
updates.membersUpdated本次更新的成员数

去重语义 ​

去重在单个 Session 内生效,不跨会话。

优先级:

  1. 若消息提供了 platformMessageId,以此作为唯一键去重(高精度,推荐)。
  2. 若未提供 platformMessageId,退化为确定性 fallback key: timestamp + sender + type + normalizedContent + replyToMessageId。
层次机制适用范围精度
请求级幂等Idempotency-Key同一 HTTP 请求的重试精确
消息级去重(主键)platformMessageId跨批次、跨窗口的同一条消息精确
消息级去重(降级)上述字段生成的确定性 fallback key无 platformMessageId 时的兜底最大努力

注意

  • 同一 platformMessageId 的消息不会被重复写入,即使 content 不同(以首次写入为准)
  • 两个不同的 platformMessageId 即使其他字段完全相同,也会保留为两条消息
  • fallback 去重在"同一人、同一秒、相同类型、相同规范化内容和相同回复目标"时判定为重复,存在极小概率误判
  • 强烈建议外部数据源提供 platformMessageId,这是最可靠的去重依据

分批策略 ​

默认批次大小 ​

每批建议 5000 条消息。

约束值说明
JSON body 大小上限50MB超过返回 BODY_TOO_LARGE (413)
建议每批消息数5000 条兼顾性能和内存占用

分批原则 ​

  • 按时间顺序分批,从旧到新推送
  • 批次间允许时间重叠(建议重叠 5~10 分钟),依靠去重吸收
  • 每批独立请求,任意一批失败不影响其他批次
  • 第一批必须携带 chatlab + meta + members(触发会话创建)
  • 后续批次可仅携带 messages

游标维护(由调用方负责) ​

ChatLab 不为调用方维护游标。推荐结构:

json
{
  "sessionId": "group_abc123",
  "lastSyncedTimestamp": 1711468800,
  "lastSyncedMessageId": "msg_900000"
}

每批成功后,将响应中的 session.lastTimestamp 更新到游标。失败时保持游标不变,用相同的 Idempotency-Key 重试。

并发约束 ​

当前版本:同一用户数据目录同一时刻仅允许一个写入型导入任务。如果 Desktop、CLI、Web 或 Push API 已在向该数据目录导入,后续请求即使目标 sessionId 不同,也会收到 IMPORT_IN_PROGRESS (409) 错误。格式检测、导入分析等只读操作不受此限制。


标准调用流程 ​

首次全量导入(Bootstrap) ​

1. 准备全量聊天数据,按时间顺序切分为 N 批(每批 ≤5000 条)

2. 第一批请求:
   POST /api/v1/imports/group_abc123
   Body: { chatlab, meta, members, messages }
   → 响应 created: true,会话创建成功

3. 第 2~N 批请求:
   POST /api/v1/imports/group_abc123
   Body: { messages }
   Idempotency-Key: {sessionId}-{batchIndex}-{windowStart}

4. 每批成功后记录游标;失败用相同 Idempotency-Key 重试

5. 全部批次完成后对账:
   GET /api/v1/sessions/group_abc123
   → 校验 totalCount、firstTimestamp、lastTimestamp

历史回填(Backfill) ​

1. 确定需要补齐的时间区间 [start, end]
2. 按时间窗口拆分为多批
3. 依次调用 POST /api/v1/imports/:sessionId
   (建议设置 options.metaUpdateMode: "none" 防止旧群名覆盖)
4. 每批成功后更新本地游标
5. 可选:GET /api/v1/sessions/:id 对账

每日定时增量(Scheduled Sync) ​

1. 固定 Session ID + 定时器(如每小时/每天)
2. 从上次游标开始,向前重叠 5~10 分钟作为窗口起点
3. 拉取该时间窗口内的新消息
4. 如有新成员或群名变更,携带 meta/members
5. 调用 POST /api/v1/imports/:sessionId
6. 依靠去重吸收重叠数据
7. 更新本地游标

媒体附件(协议预留) ​

当前版本重点保证文本消息的导入稳定性。attachments 作为协议预留字段:调用方可在消息中携带该字段,ChatLab 当前接收但不承诺完整落库与渲染。

预留字段结构见 ChatLab 格式规范。


错误码与重试策略 ​

错误码HTTP 状态说明可重试
UNAUTHORIZED401Token 无效或缺失否
INVALID_FORMAT400Content-Type 不支持或请求体格式错误否
INVALID_PAYLOAD400必填字段缺失、类型错误或校验失败否
BODY_TOO_LARGE413JSON body 超过 50MB否
IMPORT_IN_PROGRESS409当前有其他导入正在执行是
IDEMPOTENCY_CONFLICT409相同幂等键但请求体不一致否
IDEMPOTENCY_PENDING409相同幂等键的首次请求仍在执行是
IMPORT_FAILED500导入过程内部错误是
SERVER_ERROR500服务内部错误是

重试策略建议:

最大重试次数:3
退避策略:指数退避 + 随机抖动
  第 1 次重试:5s + random(0, 1s)
  第 2 次重试:15s + random(0, 3s)
  第 3 次重试:45s + random(0, 5s)
重试时必须使用相同的 Idempotency-Key

版本与兼容性 ​

  • chatlab.version:0.0.2
  • API 路径前缀:/api/v1
  • 向后兼容:新增字段均为可选,不破坏旧调用方
  • 字段废弃策略:废弃字段先标记为 deprecated,保留两个版本后移除
  • 平台标识可扩展:platform 字段不限于预定义枚举,可传入任意小写字母标识

相关文档 ​