# 听记发言人识别与标注 (speaker-correct)

> 触发条件：用户提供了一篇钉钉听记（URL 或 ID），表达了识别/标注/替换发言人的意图。即使用户只说"帮我标注发言人"也应触发本技能。

## 执行流程

### Step 1：查同时段日程，获取参会人名单

1. 从听记元数据获取录音**开始时间**（精确到分钟）。
2. 调用日历接口查询该时间点前后 30 分钟内的日程：`dws calendar event list --start <T-30min> --end <T+30min> --format json`
3. 若匹配到日程，提取参会人名单（姓名/花名），输出：
   > 根据录音时间，匹配到日程「{日程名称}」，参会人为：{姓名1}、{姓名2}、{姓名3}...
   > 未识别的发言人大概率来自以上名单。
4. 未匹配到日程 → 跳过，直接进入 Step 2。

### Step 2：展示发言人状态表格

列出所有发言人及其当前标注状态：

| # | 当前标注 | 状态 |
|---|---------|------|
| 1 | 张三 | 已识别 |
| 2 | 发言人 2 | 未标注 |
| 3 | 发言人 3 | 未标注 |
| 4 | 发言人 4 | 未标注，发言较少 |

### Step 3：让用户选择识别方式

```
你希望如何识别未标注的发言人？

A. 听音识人 — 为每位未识别发言人裁剪一段音频片段，你听后辨认是谁
B. 手动设置 — 直接告诉我对应关系，如「发言人2:王五, 发言人3:赵六」
C. 智能匹配 — 提供参会人名单或其他信息，我来尝试推断
```

**等待用户选择后再执行后续操作，不提前切片。**

---

#### 选项 A：听音识人

为每位未标注发言人裁剪一段代表性音频片段。

**片段选择策略：**
1. 语义质量过滤：排除口吃/重复词段落、文本 < 8 字、纯单字回应、句意不完整段落
2. 综合评分排序：`score = 时长(秒) x 语义完整度系数`，取最高分段落，截取 `min(段落时长, 10秒)`，不足 5 秒的跳过
3. 兜底（发言较少）：合并该发言人所有 >= 1 秒的片段；合并后仍不足 3 秒则放弃，告知用户建议手动标注

切片完成后告知用户可以听取，等待用户回复对应关系。

#### 选项 B：手动设置

等待用户输入对应关系（如「发言人2:王五, 发言人3:赵六」），收到后进入 Step 4 批量替换。

#### 选项 C：智能匹配

若 Step 1 已获取参会人名单，结合发言内容特征尝试推断；否则请用户补充名单后推断。

---

### Step 4：通讯录查询 + 执行替换

确认对应关系后，**必须先查询通讯录获取 dingUid**：

1. 调用 `dws contact user search --query "<姓名>" --format json`
2. 从返回结果提取 `userId`（长整型数字，即 dingUid）
3. 根据匹配结果执行替换：

| 情况 | 处理 |
|------|------|
| 唯一匹配（如 userId=123456789） | `dws minutes speaker replace --id <taskUuid> --from "发言人X" --to "<姓名>" --target-uid 123456789 --format json` |
| 多个匹配 | 列出候选（姓名 + 部门 + userId）让用户选择后执行 |
| 无匹配 | 提示通讯录未找到，执行不带 `--target-uid` 的替换：`dws minutes speaker replace --id <taskUuid> --from "发言人X" --to "<姓名>" --format json` |

### Step 5：追问

替换成功后：
> 还有其他发言人需要我帮你识别和替换吗？

---

## 注意事项

- **不透出执行过程**：不向用户展示时间戳、ffmpeg 命令、评分逻辑等内部细节
- **不做身份画像分析**：不提供语言风格、决策权等文字画像
- **串行处理**：逐条执行裁剪命令，不使用 shell `&` 并行
- **全已识别情况**：若所有发言人均已识别，直接告知「所有发言人已识别，无需修改」
- **通讯录查询必跑**：替换前必须调用 `dws contact user search` 获取 userId，严禁跳过

## 异常处理

| 情况 | 处理方式 |
|------|----------|
| 无法获取原始音频 | 告知权限不足或链接失效，建议用户本地提供或选择手动设置 |
| 未匹配到日程 | 跳过参会人提示，直接展示发言人表格 |
| 通讯录查不到目标人 | 执行不带 `--target-uid` 的替换，告知用户仅修改显示名 |
| 未找到未识别发言人 | 告知用户所有发言人均已识别 |
| speaker replace 报权限错误 | 告知用户需听记创建者/协作者权限，引导联系原作者 |
