id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
| id |
title |
type |
domain |
stack |
tags |
level |
prerequisites |
est_hours |
related |
week |
| backend.mongodb.scenario.conversation-store |
AI 客服会话存储:海量对话记录的建模、查询、归档和同步 |
scenario |
backend |
| mongodb |
| postgresql |
| elasticsearch |
|
|
intermediate |
| backend.mongodb.document-model |
|
2 |
| backend.mongodb.interview |
| scenario.data-sync |
| scenario.customer-service-kb |
|
|
AI 客服会话存储:海量对话记录的建模、查询、归档和同步
业务背景与问题
AI 客服上线后,每天产生大量对话(用户消息、模型回复、工具调用记录、评分),原先存在 PostgreSQL 的一张 JSONB 大表中:
- 消息结构经常变化(新增工具调用、图片、引用来源),频繁改表;
- 坐席后台按租户查“待处理会话”越来越慢;
- 需要按关键词搜索历史对话;
- 数据只需要在线保留 180 天,之后归档。
约束
- 订单、退款等核心交易数据仍在 PostgreSQL,强一致。
- 对话数据允许最终一致的检索。
- 多租户隔离。
方案设计
- 职责划分:交易数据留在 PostgreSQL;对话记录迁移到 MongoDB(结构灵活、写入量大);全文 / 语义检索交给 Elasticsearch。
- 建模:
conversations:会话元数据 + 最近 N 条消息嵌入(列表页和详情首屏一次读取);
messages:完整消息单独集合,按 conversation_id + seq 引用(避免无限数组);
- 每个文档都带
tenant_id。
- 索引:
{tenant_id, status, updated_at: -1}(坐席列表,ESR)、{conversation_id, seq} 唯一(幂等写入和顺序读取)、messages.created_at 上的 TTL 索引或按月归档任务。
- 同步检索:变更流 → 消费者 → 写入 ES;保存 resume token,断线后续传;消费者幂等(以消息 id 为 ES 文档 id)。
- 一致性:转人工(改会话 + 建工单)用事务,或者把工单放在 PostgreSQL 并通过 outbox 模式保证一致。
关键代码
可运行的基础示例:examples/conversations。
取舍
| 选择 |
优点 |
缺点 |
| MongoDB 存对话 + PG 存交易(本方案) |
各取所长,结构灵活 |
多一种数据库要运维,跨库一致性要设计 |
| 全部留在 PostgreSQL JSONB |
技术栈简单,事务方便 |
写入量大时表膨胀,结构变化仍需维护索引 |
| 对话直接写 ES |
检索方便 |
ES 不适合作为主存储(更新代价、一致性) |
易错点
- 消息全部嵌入会话文档,长会话超过 16MB。
- 查询忘记带
tenant_id,越权读取其他租户数据。
- 变更流消费者没有保存 resume token,重启后丢事件或全量重放。
- 归档前没有确认合规要求(对话内容可能包含个人信息,需要脱敏或加密)。
面试怎么讲
“对话记录结构多变、写入量大,我把它从 PG 的 JSONB 表迁到了 MongoDB,交易数据留在 PG。会话文档嵌入最近 20 条消息,完整消息单独集合用 conversation_id+seq 唯一索引,既避免无限数组又保证幂等。坐席列表按 ESR 原则建复合索引。检索通过变更流同步到 ES,保存 resume token 断点续传。180 天后归档。”