> 2026-09-30：已按 2026-09-29 后端交付合同完成插件接入。本文保留为需求交接历史，当前前端实现与验证范围见 [统一目录对接](speaker-directory-integration-2026-09-30.md)。

# 已识别说话人：统一目录、总数、新增提醒与跨端同步——后端交接

日期：2026-09-28

范围：录音板块的「已识别说话人」。不涉及聊天已读、消息通知或 AI Token 用量。

**结论：已标记说话人的出现天数、最近发声时间已接入，不需要重新开发一套统计。当前主要缺统一目录查询和目录级查看进度。建议优先提供统一摘要、统一分页列表、查看确认三个能力；接口路径和字段名均为建议，可扩展现有接口实现。**

核对依据：当前插件实现、3100 预览的本轮实际结果，以及本地 `jotmo-audio` 源码。源码存在不代表所有线上环境已部署；下文单独列出需要确认的项目。旧版 `recognized-speaker-presence-api-handoff-2026-09-27.md` 中「只缺统计／尚未部署」属于当时记录，不能继续当作本轮需求状态。

## 1. 要达到的用户体验

- 录音页入口直接显示「已识别说话人 N」。多个原始识别身份标记成同一人后只计一人；同名不同人不能合并。
- 自上次打开目录后有真正新增时，第二行显示「新识别 M 个」。打开目录并成功展示后清除；同账号其他设备随后也应同步清除。
- 「经常出现」：出现天数降序，同天数按最近发声时间降序。
- 「最近出现」：最近发声时间降序，同时间按出现天数降序。
- 已确认是本人的身份在全部／已标记列表置顶；搜索仍尊重搜索条件，未标记筛选不强行插入本人。
- 首屏即可得到正确全局排序，不需要前端把几千人全部读完后再排序。后续返回立即展示缓存，只检查更新。

这里的总数是**当前目录合并口径下的可展示身份数**：没有可靠绑定关系的匿名候选仍分别计数，不能承诺已经识别出真实世界全部不重复的人。

## 2. 已有能力与需要确认的项目

| 能力 | 当前证据与状态 | 本轮处理 |
| --- | --- | --- |
| 已标记身份及绑定账号 | 插件使用 `POST /api/v1/audio/get-speaker-ls`，按绑定账号或独立 Speaker 归组 | 复用，不另造身份系统 |
| 已标记人的出现天数、最近发声时间 | 插件使用 `POST /api/v1/audio/speaker-presence/list`；本轮预览已显示全历史统计；本地聚合已处理同一绑定用户的多 Speaker 日期并集 | 已有，不重复提为新缺口 |
| 未标记候选及统计 | `POST /api/v1/audio/unmarked-speakers/list` 已有分页、日期统计、候选标识及投影状态 | 扩展排序／搜索／统一目录能力 |
| 未标记轻量总数 | 本地后端已注册 `POST /api/v1/audio/unmarked-speakers/count`，含 `total_count/state/published_version/updated_at/retry_after_ms` | 请确认部署和灰度；插件当前尚未专门接入这个 count 路由，这部分是前端可补接，不是要求从零开发 |
| 已标记者关联的原始识别身份详情 | 插件已对接 `/speaker-presence/detail`，支持 `speaker_id/expected_version`；本次核对的本地后端路由未找到该 detail 注册 | 请后端确认实际分支、部署环境及契约，不直接认定线上不存在，也不默认已验收 |
| 本人判断、声纹状态及录入 | 当前可从绑定账号判定本人，已有 `/voiceprint/my` 及录入流程 | 置顶和引导本身不需要新接口；统一分页时需把本人信息随目录返回 |

已有候选 ID 跨请求稳定，也已有部分聚类成员信息。**真正缺少的是整个目录跨设备可用的统一身份，以及标记、合并、拆分、重聚类时对「是否新增、是否已查看」的继承规则**，不是完全没有候选 ID。

## 3. 当前前端临时方案及体验边界

1. 总数暂由「已标记人去重数＋未标记候选数」拼接。它们不保证来自同一个后端快照，标记与重建交错时可能短暂不一致。
2. 新增提醒保存在当前浏览器源、当前账号下。首次完整读取静默建立基准；换设备不能同步查看状态。
3. 前端无法准确识别「旧候选消失＋新身份出现」究竟是合并还是新发现，因此保守抑制这次新增；同时发生合并和真正新增时可能漏报。
4. 全局排序、搜索目前依赖读取全部元数据；最多 40 页／2,000 个未标记候选是**插件当前的保护上限，不是后端产品上限**。超过上限只展示部分结果，不能保证全局排序与搜索完整。
5. 已实现入口／列表共享缓存、分页复用和限流退避。本轮真实账号完整读到 1,990 个目录身份，再次进入能直接显示；但首次补齐仍慢，不能靠缓存解决首次正确排序。
6. 现有后端已有 `scan_truncated`、整理中数量等信息；插件适配层也应完整透传，不能将已有字段未接入误列为后端缺失。

## 4. P0：本轮优先补齐的能力

### A. 统一轻量摘要

建议能力名：`speaker-directory/summary`。

一次读取返回账号级、同快照的：

| 字段示意 | 含义 |
| --- | --- |
| `total_count`、`marked_count`、`unmarked_count` | 当前可展示目录去重数；三个字段必须同快照且口径一致 |
| `unseen_count` | 自目录查看基准之后的新增身份数，按当前合并结果计数，不是「总数之差」 |
| `snapshot_version`、`updated_at` | 与列表绑定的快照版本及更新时间 |
| `state`、`coverage` | `fresh/stale/building/failed`；数据是否完整，不能把截断结果称为全量 |
| `change_cursor` | 这个快照覆盖到的变化边界，供查看确认和后续增量使用 |
| `retry_after_ms` | 整理中、失败或限流时的重试建议 |

要求：入口统计不遍历名单、不逐人查询、不在每次读取时同步扫描全部录音。无可用快照时返回明确的未知值／状态，不能用 `0` 冒充空目录。存在旧快照时可返回旧数值并明确标记为过期。

可复用现有未标记 count 与已标记 presence 投影，但需要后端给出两者一致的合并结果；单纯把两个不同时点的接口相加不能替代统一摘要。

### B. 已标记＋未标记的统一分页、全局排序和搜索

建议能力名：`speaker-directory/list`。

请求支持：`filter=all|marked|unmarked`、`sort=frequent|recent`、`query`、`limit`、`cursor`，以及与摘要一致的 `snapshot_version` 或等效机制。

每行至少包含：稳定的账号内 `person_key`、类型、展示名称／编号、头像引用、`is_self`、`day_count`、`last_seen_at`、详情定位引用。稳定身份 ID 不是授权凭据；任何详情／标记请求仍需验证当前登录账号。

关键规则：

- 先按实际身份合并，再排序分页。全部列表必须混排，而不是「已标记排完再排未标记」。
- frequent：`day_count DESC, last_seen_at DESC, person_key ASC`；recent：`last_seen_at DESC, day_count DESC, person_key ASC`。本人按前述筛选规则优先，仍只出现一次。
- 当前未标记固定排序在同天数时先比较发声总时长，不符合本次「同天数看最近时间」规则；不能只给现有排序改一个名字。
- `last_seen_at` 统一指最后一个有效发声片段的时间，不能混成文件结束、身份修改或入库时间；天数继承已确认的录音自然日去重口径。
- 游标绑定登录账号、快照、筛选、搜索、排序；换排序不复用旧游标。快照失效时明确返回失效状态，不静默拼接新旧结果。
- 超过 2,000／10,000 身份也可持续分页；只加载第一屏就能得到该筛选下正确的第一屏排序。搜索应覆盖完整目录，而非前端已加载部分。
- 只有声纹授权、通讯录联系人、从未在录音出现的空身份，不应自动算作已识别的人；保留旧空身份如有产品需要，请返回明确类别并单列口径。证据不足／处理中是否进总数也必须与可展示列表一致，不能计数却无法查看。

### C. 目录级「新增」及跨端查看确认

建议能力名：`speaker-directory/seen`。确认请求携带**已成功展示快照的** `through_cursor`，不使用客户端时间。

- 「已查看」表示用户已打开该目录，不代表逐人读过每一张卡片，更不是录音消息已读。
- 为匹配「点进去提示就去掉」，第一屏成功展示后，可确认该目录快照覆盖到的新增事件；后续分页无需全部滚到底。不能拿一个新摘要的游标去确认仍在显示的旧快照。
- 同账号各端共享，确认幂等、只向前推进；旧设备较晚提交旧游标不能覆盖新游标。
- 用户打开快照 C 后，新身份 D 才到达：确认 C 不能清除 D。加载失败、离线、账号切换后的迟到响应都不能推进查看边界。
- 发布此能力时设定账号级首次基准，不把已有几年历史全部报成新增；后续换设备或重装不重新初始化。

新增必须依据服务端首次发现／进入目录的事件序号，而不是录音发生的 `first_seen_at`：今天上传旧录音发现新对象，应按今天发现处理。已知人再次讲话、改名、重新转写、引用过期不算新增。

### D. 身份继承：为上述统计提供正确依据

可内部维护事件和映射，不强制将全部关系暴露给前端。需明确：

- 未标记 A、B 标记成同一人 P：目录只剩 P，计 1；标记动作本身不新增一人。
- A、B 都未查看时合成 P，待查看数按 1；已查看身份的纯合并不能重新提醒。
- 已查看身份拆分或技术重聚类，继承其已知／查看状态；不能因换 ID 把历史全变成新增。若确实发现之前未覆盖的新对象，由后端明确产生发现事件。
- 合并与真正新增 X 同时发生，仍正确提示 X；不能因为总数没增加就漏报。
- 同昵称但无绑定关系不能合并；同一绑定用户的多个 Speaker 或多个候选归属同一正式人，按确认关系合并。
- 删除、恢复、取消标记、重新绑定、撤销合并需有明确继承规则。已看过对象的恢复默认不当新发现；有效名单和统计随录音删除／恢复更新。

## 5. P1：更及时的更新与更稳的服务保障

1. **变化检查／增量读取**：摘要支持条件版本检查，或提供 `changes(since_cursor)` 返回新增／更新／移除以及新版本。版本未变无需下发完整名单。若使用推送，只推账号级「目录变了」通知即可，不需推音频或 ASR 原文。
2. **可解释的退避**：请在 429 返回明确错误码、`Retry-After` 或 `retry_after_ms`。区分限额耗尽、旧路径计算锁占用、基础设施异常；前端能保留内容并按服务端建议重试。
3. **确认轻量投影已覆盖目标环境**：本地源码存在新投影读取和旧同步读取两条路径，限频不同。请确认灰度、部署版本及 count/list 的一致性；不要单纯取消限流。前端发生过 429，尚未取得服务端日志，不能断定线上一定走哪条路径。
4. **原始身份详情核验**：确认 `/speaker-presence/detail` 对应分支及部署，返回与列表一致的版本、全历史关联编号、统计、分页或明确覆盖范围；不要为已有接口重复开发另一条同义接口。

本人置顶、无本人时显示引导、按钮布局和空态都可由前端完成。后端只需可靠的账号绑定及声纹状态；可选在摘要中返回 `self_person_key/self_state` 减少额外请求，不是首期必需的新模块。

## 6. 迁移和客户端配合

- 当前插件的 `personKey/identityKey/candidateRef` 有本机密钥封装，不能直接作为各端通用 ID。后端提供账号内稳定身份，插件 Host 负责适配；前端不上传本机 HMAC 并假定后端能还原。
- 本机新增记录不是权威历史。最小迁移方案是后端统一一次性静默基准，之后各端共用后端游标；不要每台新设备把全量旧人重新报新。
- 如果必须保留上线前每台设备尚未查看的提醒，需要另定可验证映射和幂等迁移流程，不能悄悄以客户端列表覆盖后端查看状态。
- 后端统一查询落地后，插件移除「为全局排序扫描全部名单」的降级路径，改用服务端分页；保留同账号缓存、请求合并、失效保护和退避。
- 同步透传已有版本、截断、状态与退避字段，并接入已部署的轻量 count；这是客户端对接工作，不全部算成后端新增需求。

## 7. 联调验收清单

| 场景 | 预期 |
| --- | --- |
| 3 个候选标记为同一人 | 总数相应合并为 1；摘要和列表一致 |
| 多轨道同一天有讲话 | 人级出现 1 天，最近时间取最后有效片段 |
| 同天数但发声时长不同 | frequent 仍按最近时间作为第二排序条件 |
| 本人不在普通排序第一页 | 全部／已标记第一页仍置顶本人，后续页不重复 |
| 超过 2,000／10,000 身份 | 首屏全局排序正确，跨页无漏项／重复，搜索可命中后部数据 |
| 同一个人又说了几天、改名 | 天数／时间更新，不增加新增人数 |
| 合并两个未查看候选 | 新增人数由 2 变 1，不额外产生第三个新人 |
| 已查看人重聚类＋真正新增同时发生 | 旧人不重报，新人不漏报 |
| 今天导入上月录音发现新对象 | 按发现事件计新增，不因录音日期旧而漏报 |
| A 端打开目录，B 端检查 | 两端查看状态一致，无需重新加载全部身份 |
| A 确认旧快照时又来了新人 | 新人提示保留；重复确认安全，旧确认不回退状态 |
| 初始化／换设备／重装 | 历史不全部报新，不重置其他端已查看状态 |
| 构建中、截断、429、服务故障 | 不把未知显示为 0；保留旧快照并明确状态，按建议重试 |
| 伪造其他账号身份或游标 | 拒绝访问；缓存、计数、详情和查看状态均账号隔离 |

以上均可使用合成身份／录音夹具验收，不应为测试擅自修改真实用户的说话人归属或声纹。

## 8. 请后端回复的交付信息

1. 哪些能力可直接扩展现有接口，哪些需要新接口；最终请求／响应示例和字段口径。
2. P0（三个读取／确认能力及其身份继承规则）的排期，以及 P1 的拆分计划。
3. 已部署环境、版本或分支、灰度范围；特别是 count 和 presence/detail 的当前状态。
4. 身份合并／拆分／首次基准规则、快照和游标失效方式、限流退避约定。
5. 可用的合成测试账号与验收结果，避免将「本地有代码」等同「插件已能在线调用」。

### 核对入口（仓库相对路径）

- 插件：`src/client/recognized-speaker-directory.ts`、`recognized-speaker-tracker.ts`、`recognized-speaker-order.ts`、`ArkmeRecognizedSpeakersSurface.tsx`。
- 插件适配：`src/services/unmarked-speaker-service.ts`、`src/services/recording-service.ts`。
- Audio：`gin/api/router.go`、`gin/api/audio_unmarked_speaker.go`、`gin/api/audio_speaker_presence.go`。
- Audio 数据口径：`internal/audio/unmarked_speaker_candidates.go`、`internal/audio/speaker_presence_stats.go`、`internal/audio/repository.go`。
