feat(keyboard): add clipboard voice command mode
Long-press mic runs ASR as an instruction over eligible clipboard text, with host-confirm recording UI, min-record gate, and light ASR prewarm. Bump CURRENT_PROJECT_VERSION to 53.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
# 剪贴板语音指令(Clipboard Voice Command)一页规划
|
||||
|
||||
> **文档状态**:产品规划(**决策已冻结**,未进入实现)
|
||||
> **文档状态**:产品与架构规划(**决策已冻结**;实现进行中)
|
||||
> **适用范围**:iOS 主 App + 键盘扩展(复用现有 Flow / ASR / 润色管线)
|
||||
> **分支**:`feature/clipboard-voice-command`
|
||||
> **创建日期**:2026-08-07
|
||||
> **修订日期**:2026-08-07(架构评审 + 状态机 + 过滤/手势 + 协议/Prompt 冻结)
|
||||
> **关联能力**:Flow 会话、PiP 保活、Polish Style Packs、字段指纹插入
|
||||
|
||||
---
|
||||
@@ -19,18 +21,33 @@
|
||||
|
||||
| 项 | 选择 |
|
||||
|----|------|
|
||||
| 入口 | **复用麦克风**:短按 = 听写;长按 = 剪贴板指令 |
|
||||
| 入口 | **复用麦克风,长按为新增状态**:短按 = 现有听写 toggle;长按 = 剪贴板指令 |
|
||||
| 长按录音 | 达标后 **立刻开录**;**松手结束本轮**并进入生成 |
|
||||
| 生成中麦克风 | **F1:锁定**,等结果上屏后再长按连续改写(不排队、不取消重录) |
|
||||
| 提示文案 | 有开场资格时改为:**「点击说话,长按处理剪贴板」** |
|
||||
| 开录时机 | 长按达标后 **立刻录音**,不插确认动画 |
|
||||
| 剪贴板读取 | **机会读**(键盘出现 / 回前台 / 触摸键盘)更新提示;长按开场再读一次并 **冻结快照**;不后台轮询 |
|
||||
| 开场资格时钟 | 复制后 **30 秒内** 可进入 |
|
||||
| 材料过滤 | R0–R6;过短 **有效长度 < 15**;见 §4 |
|
||||
| 长按达标 | **0.45s**;松手结束;滑出再松仍送生成(见 §4.1) |
|
||||
| 指令会话时钟 | 开场成功起 **30 秒**;每轮 **成功上屏** 刷新 30 秒;失败不刷新 |
|
||||
| 与 Flow/PiP | **独立子状态**:指令 30s ≠ Flow 无活动超时;小会话结束不退出 Flow |
|
||||
| 材料快照 | 开场瞬间冻结;会话中剪贴板变更不影响本会话 |
|
||||
| 首轮写入 | 空框写入;非空追加 |
|
||||
| 连续改写 | **干净 → 替换上轮产物**;**不干净 → 追加**(用户自删) |
|
||||
| 替换实现 | **不依赖**系统撤销 API(扩展无法调用宿主 Undo);用自记文本 + `deleteBackward` 尽力替换 |
|
||||
| 会话中短按 | **结束指令会话 → 听写** |
|
||||
| 换输入框 | **结束指令会话**(拿不准是否换框时,按不干净处理:只追加不删) |
|
||||
| 风格 | 共享基础润色契约;当前 Style Pack 作底色;**本轮指令优先** |
|
||||
| 翻译开关 | 指令模式 MVP **忽略** |
|
||||
| 预览 | 直接上屏,不强制确认 |
|
||||
| 翻译开关 | 指令模式 MVP **忽略**(语言需求由口述指令携带) |
|
||||
| 录音条 | **展示**指令 ASR 原文(仅 UI,不上屏到输入框) |
|
||||
| 语音历史 | **只记成功产物**;不记指令 ASR;快照不落盘 |
|
||||
| 密码 / 安全框 | **禁止**指令模式 |
|
||||
| 扩展 jetsam | **指令会话结束**(内存态丢弃,可接受) |
|
||||
| 失败 fallback | 指令模式 **禁止** raw ASR 插入输入框(与听写失败落原文相反) |
|
||||
| 协议 | **扩展 FlowCommand(A1)**:`utteranceMode` + `clipboardSnapshot`;**start 即携带**;见 §11 |
|
||||
| 快照上限 | wire 与送模型均为 **3000** 字(超出截断) |
|
||||
| Prompt | **独立** `ClipboardCommandPromptComposer`;材料 / 指令 / 上一版;风格 **B1 短偏置**;见 §11 |
|
||||
|
||||
---
|
||||
|
||||
@@ -38,57 +55,96 @@
|
||||
|
||||
```text
|
||||
复制文本
|
||||
→ 开场资格 30s 内且通过材料过滤
|
||||
→ 机会读发现开场资格(≤30s + 通过材料过滤)
|
||||
→ 提示:「点击说话,长按处理剪贴板」
|
||||
→ 长按 → 立刻录音,冻结 clipboardSnapshot,开启指令会话
|
||||
→ ASR(指令) + Snapshot(材料) + StylePack(底色) → LLM
|
||||
→ 空框写入 / 非空追加;记录本轮插入区间
|
||||
→(可选)会话未到期再长按连续改写:
|
||||
→ 长按达标 → 立刻录音,再读并冻结 clipboardSnapshot,开启指令会话
|
||||
→ 松手 → 停录;录音条曾展示指令 ASR
|
||||
→ ASR(指令) + Snapshot(材料) + StylePack(底色) → LLM(生成中 mic 锁定)
|
||||
→ 成功:空框写入 / 非空追加;记录本轮产物;刷新会话 30s;可写入语音历史(仅产物)
|
||||
→ 失败:不插入;不写历史;会话保持但不续期
|
||||
→(可选)出字后再长按连续改写:
|
||||
干净则删上轮产物再写新版;否则追加
|
||||
→ 短按 / 会话到期 / 切框 / 收起键盘 → 结束会话,丢弃快照
|
||||
→ 短按 / 会话到期 / 换输入框 / 收起键盘 / jetsam → 结束会话,丢弃快照
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 开场过滤(不开放指令入口)
|
||||
## 4. 开场过滤(可测规则,冻结)
|
||||
|
||||
命中任一条 → UI 保持「点击说话」,长按按 **听写** 处理:
|
||||
命中任一条 → 保持听写提示,**不进入指令模式**。
|
||||
|
||||
- 长度 **< 10** 字
|
||||
- 明显是数字(电话、订单号等)
|
||||
- 无意义字符 / 乱码 / 单符号重复
|
||||
- 验证码形态(短字母数字码等)
|
||||
- 非文本、安全输入框、无 Full Access 读不到剪贴板
|
||||
**计数:** trim 首尾空白后用 Swift `String.count`(扩展字形簇),下称「有效长度」。
|
||||
|
||||
| # | 规则 | 可测定义 |
|
||||
|---|------|----------|
|
||||
| R0 | 空 / 非文本 | trim 后为空 |
|
||||
| R1 | 整段像号码 | 去掉空白后匹配 `^[\d\-\+\(\)\s]+$` 且至少含 1 位数字(任意长度) |
|
||||
| R2 | 纯 emoji/符号 | 去掉空白后 **无** 字母、汉字、数字 |
|
||||
| R3 | 验证码形态 | 有效长度 4…8,整段仅 `[A-Za-z0-9]`,且 **同时含** 字母与数字 |
|
||||
| R4 | 过短 | 有效长度 **< 15**(定死) |
|
||||
| R5 | 单字符刷屏 | 去掉空白后长度 ≥ 15,不同字符种类 ≤ 2,且某一字符占比 ≥ 80% |
|
||||
| R6 | 运行时拒绝 | 安全输入框、无 Full Access、读剪贴板失败(含系统粘贴权限拒绝) |
|
||||
|
||||
**不做(MVP):** 用模型判断「有无意义」;因含 URL/邮箱整段拒绝;因过长拒绝入口。
|
||||
|
||||
**送模型截断(不影响入口):** 快照超过 **3000** 字则截断(与 wire 上限相同),prompt 可注明已截断。
|
||||
|
||||
**例:**
|
||||
|
||||
| 文本 | 结果 |
|
||||
|------|------|
|
||||
| `13812345678`、`+86 138-1234-5678` | 拒(R1) |
|
||||
| `😀😀😀`、`!!!` | 拒(R2) |
|
||||
| `A8f2K1` | 拒(R3) |
|
||||
| `周末吃饭吗`(<15) | 拒(R4) |
|
||||
| `啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊` | 拒(R5) |
|
||||
| `周末有空一起吃个饭吗?我想聊下项目进度。` | 过 |
|
||||
|
||||
首次读剪贴板触发系统粘贴权限且失败 → 无资格 + 弱提示。
|
||||
|
||||
---
|
||||
|
||||
## 4.1 长按阈值(冻结)
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| 长按达标 | **0.45s** |
|
||||
| 未达 0.45s 松手 | 视为 **短按** → 听写 toggle |
|
||||
| 达到 0.45s | 可选轻震 + **立刻**开指令录音 |
|
||||
| 达标后滑出按钮再松手 | MVP:**仍结束本轮并送去生成**(不做滑出取消) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 写入与「干净」判定
|
||||
|
||||
**可替换(干净)须同时满足:** 同一指令会话、字段指纹未变、有上轮插入记录、光标仍在上轮产物末尾、上轮文本未被用户改动。
|
||||
**可替换(干净)须同时满足:** 同一指令会话、能判定仍在同一输入框、有上轮插入记录、光标仍在上轮产物末尾、上轮文本未被用户改动。
|
||||
|
||||
**推荐执行顺序:** LLM 成功拿到新文本 → 再删旧 → 再插新;删后插失败则尽力写回旧文本。不确定时 **只追加或不插,绝不误删用户原文**。
|
||||
|
||||
**说明:** 用户仍可自行使用系统撤销(摇一摇等)作为逃生口;产品自动替换不依赖该能力。
|
||||
|
||||
---
|
||||
|
||||
## 6. 失败态与边界(优先级)
|
||||
|
||||
1. **不丢用户原文**(替换不确定 → 追加或不插)
|
||||
2. **不把指令 ASR 当听写上屏**(ASR/LLM 失败均不插入指令原文)
|
||||
3. **不静默滥用剪贴板**(无资格 = 听写;仅长按开场时读一次)
|
||||
2. **不把指令 ASR 写入输入框**(ASR/LLM 失败均不插入;禁用听写式 raw fallback)
|
||||
3. **不静默滥用剪贴板**(无资格不进指令;机会读 + 开场再读;不轮询)
|
||||
4. **软失败可重试但不续期**(没听清 / 生成失败:不插入、会话保持、时钟不刷新)
|
||||
5. **少打断**(弱提示,不弹模态)
|
||||
|
||||
| 场景 | 策略 |
|
||||
|------|------|
|
||||
| 剪贴板过期后长按 | 弱提示「剪贴板已过期」→ 听写 |
|
||||
| 无有效语音 / ASR 空 | 不调用 LLM;不插入;会话保持 |
|
||||
| 指令不可解析 | 提示说明要怎么处理;不插入 |
|
||||
| LLM 超时/拒绝/空结果 | 不插入;可重试;不刷新时钟 |
|
||||
| 字段切换 / 收起键盘 | 结束会话;已上屏保留 |
|
||||
| 开场资格过期 | **仅改回普通听写文案**;不必再弹「已过期」。此后长按不进指令 |
|
||||
| 无有效语音 / ASR 空 | 不调用 LLM;不插入;**麦克风上方文本提示**;会话保持(不续期) |
|
||||
| 指令不可解析 | 麦克风上方提示说明要怎么处理;不插入 |
|
||||
| LLM 超时/拒绝/空结果 | 不插入;麦克风上方提示可重试;不刷新时钟 |
|
||||
| 换输入框 / 收起键盘 / jetsam | 结束会话;已上屏保留 |
|
||||
| 会话中剪贴板被覆盖 | 本会话仍用旧快照 |
|
||||
| 密码框 | 禁止指令模式 |
|
||||
| 生成中再长按 | 忽略(mic 锁定),等上屏后再改写 |
|
||||
|
||||
**隐私:** 快照仅内存,会话结束丢弃;不写历史明文;不后台轮询剪贴板。
|
||||
**隐私:** 快照仅内存;会话结束丢弃;不写历史明文;不把指令 ASR 写入历史。
|
||||
|
||||
---
|
||||
|
||||
@@ -96,28 +152,171 @@
|
||||
|
||||
| 可复用 | 必须新建(实现期) |
|
||||
|--------|-------------------|
|
||||
| Flow 命令/结果桥、PiP、ASR、插入器、字段指纹 | 剪贴板开场资格与材料过滤 |
|
||||
| `PolishingService` / LLM 调用层、Style Pack 底色 | **新 prompt 契约**(指令模式;不可复用「ASR 绝不当命令」的听写 R6) |
|
||||
| 会话超时与冷启动 handoff | 指令会话状态、上轮插入区间、干净替换逻辑 |
|
||||
| Flow 命令/结果桥、PiP、ASR、插入器、字段指纹 | 长按手势层;机会读与 R0–R6 过滤 |
|
||||
| `PolishingService`(含 `systemPrompt` 覆盖) | `ClipboardCommandPromptComposer`;finalize 按 `utteranceMode` 分流 |
|
||||
| Flow 大会话保活 | 指令会话态(扩展);`FlowCommand` mode/snapshot/previousOutput;禁 raw 门禁 |
|
||||
|
||||
**非目标(本期):** 后台监听剪贴板、强制预览确认、独立「回复」大按钮、把指令模式混进现有 transcript-polish 路径。
|
||||
**两套会话(写死):**
|
||||
|
||||
- **Flow / PiP**:主 App 可响应录音(分钟级无活动等现有策略)
|
||||
- **指令会话**:一次「处理剪贴板」任务(30s 刷新规则如上)
|
||||
- 小会话结束 ≠ 退出 Flow;勿把指令 30s 接到 `FlowInactivityDuration`
|
||||
|
||||
**非目标(本期):** 后台轮询剪贴板、强制预览、独立回复大按钮、生成中排队/取消重录(F2/F3)、把指令模式混进现有 transcript-polish 路径、依赖宿主 Undo API。
|
||||
|
||||
---
|
||||
|
||||
## 8. 成功标准(验收)
|
||||
## 8. 状态机(冻结)
|
||||
|
||||
1. 复制合格文本 30s 内,提示变为「点击说话,长按处理剪贴板」。
|
||||
2. 长按后口述「委婉拒绝 / 精简 / 总结」等,结果直接进入当前输入框。
|
||||
3. 会话内再长按改写:未手改则替换上轮产物;手改则追加。
|
||||
4. 会话中短按恢复听写,且不会把上一段指令会话状态带到听写。
|
||||
5. 过短/数字/验证码等材料不会出现指令提示。
|
||||
6. ASR/LLM 失败时输入框不被指令原文污染。
|
||||
状态机回答三件事:**现在在哪、什么事件会跳转、哪些操作允许**。分两层,勿混用。
|
||||
|
||||
### 8.1 层 A — 手势(手指)
|
||||
|
||||
| 状态 | 含义 |
|
||||
|------|------|
|
||||
| 空闲 | 未按下 |
|
||||
| 按下待判定 | 已按下,尚未达到长按阈值(可能变成短按) |
|
||||
| 指令按住录音 | 长按已达标,正在录指令;**松手 → 停录** |
|
||||
| (听写录音) | 仍由现有 **短按 toggle** 进入/结束,不经本层长按路径 |
|
||||
|
||||
### 8.2 层 B — 剪贴板任务(生意)
|
||||
|
||||
| 状态 | 提示 / mic | 含义 |
|
||||
|------|------------|------|
|
||||
| **无资格** | 普通听写文案 | 材料不合格、已过期、安全框、无权限等 |
|
||||
| **有资格** | 「点击说话,长按处理剪贴板」 | 机会读通过:合格材料且复制后 ≤30s |
|
||||
| **指令录音中** | 录音条可展示指令 ASR | 长按已开场,快照已冻结 |
|
||||
| **生成中** | mic **锁定**(F1) | 转写 + LLM;不接受新的短按/长按开录 |
|
||||
| **可连续改写** | 可再长按;短按则退出 | 上一轮已结束(成功上屏或失败提示);会话 30s 未到期 |
|
||||
| **已结束** | — | 清快照与上轮产物记录;再评估有资格/无资格 |
|
||||
|
||||
### 8.3 主转移
|
||||
|
||||
```text
|
||||
无资格
|
||||
│ 机会读:合格且 ≤30s
|
||||
▼
|
||||
有资格 ◄─────────────────────────────────────────┐
|
||||
│ 长按达标(开场读并冻快照) │
|
||||
▼ │
|
||||
指令录音中 ──松手──► 生成中 │
|
||||
│ │
|
||||
┌───────────┼───────────┐ │
|
||||
▼ ▼ ▼ │
|
||||
成功上屏 失败(仅麦克风上方文本提示) │
|
||||
│ │ │
|
||||
└─────┬─────┘ │
|
||||
▼ │
|
||||
可连续改写 ──再长按───────────────────────┘
|
||||
│ (回到「指令录音中」,同一快照)
|
||||
│ 短按听写 / 会话30s到期 / 换输入框
|
||||
│ / 收起键盘 / jetsam
|
||||
▼
|
||||
已结束 ──► 机会读 → 有资格 或 无资格
|
||||
```
|
||||
|
||||
**资格过期:** 机会读发现超时 → **无资格**,提示改回普通文案即可,**不再**单独弹「剪贴板已过期」。
|
||||
|
||||
**生成失败:** 不插入、不写历史、不刷新会话 30s;落在 **可连续改写**(若会话未到期),麦克风上方文本提示;若墙钟已超过会话 30s → **已结束**。
|
||||
|
||||
**换框拿不准:** 不强制跳「已结束」时,按「不干净」只追加不删(见 §5);一旦能判定换框 → **已结束**。
|
||||
|
||||
### 8.4 事件 × 任务态(摘要)
|
||||
|
||||
| 当前任务态 | 短按 | 长按达标 | 松手 |
|
||||
|------------|------|----------|------|
|
||||
| 无资格 | 听写 toggle | 不进指令 | — |
|
||||
| 有资格 | 听写 toggle | → 指令录音中 | — |
|
||||
| 指令录音中 | — | — | → 生成中 |
|
||||
| 生成中 | 忽略 | 忽略 | — |
|
||||
| 可连续改写 | → 已结束,再听写 | → 指令录音中(同快照) | — |
|
||||
| 已结束 | 听写 | 视重新评估后的资格 | — |
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一步
|
||||
## 9. 成功标准(验收)
|
||||
|
||||
1. 补一页状态机(听写 / 开场资格 / 指令录音 / 生成 / 可连续改写 / 结束)。
|
||||
2. 定长按阈值与材料过滤的具体启发式。
|
||||
3. 设计指令模式 prompt 与听写 polish 的分流接口。
|
||||
4. 产品确认后进入实现(键盘提示 → 主 App 会话 → Shared prompt → 插入替换)。
|
||||
1. 复制合格文本后,经机会读,30s 内提示变为「点击说话,长按处理剪贴板」;过期后仅改回普通文案。
|
||||
2. 长按立刻录音,松手后生成;口述「委婉拒绝 / 精简 / 总结」等,结果直接进入当前输入框。
|
||||
3. 录音条可见指令 ASR;失败时输入框不被指令原文污染,麦克风上方有文本提示。
|
||||
4. 出字后再长按改写:未手改则替换上轮产物;手改或不干净则追加。
|
||||
5. 生成中无法再开下一轮录音(F1)。
|
||||
6. 会话中短按恢复听写;换输入框结束指令会话。
|
||||
7. 纯数字 / 纯 emoji / 验证码 / 有效长度<15 / 刷屏等不出现指令提示。
|
||||
8. 语音历史仅出现成功产物,无指令 ASR、无剪贴板快照明文。
|
||||
9. 长按 0.45s 开录;未达阈值松手走听写;滑出松手仍生成。
|
||||
|
||||
---
|
||||
|
||||
## 11. 协议与 Prompt(冻结)
|
||||
|
||||
### 11.1 FlowCommand 扩展(A1)
|
||||
|
||||
在现有 `start/stop/abort` 上增加(`protocolVersion` bump,保持旧字段可解码):
|
||||
|
||||
| 字段 | 时机 | 含义 |
|
||||
|------|------|------|
|
||||
| `utteranceMode` | start(必填语义) | `dictation`(默认/缺省)\| `clipboardCommand` |
|
||||
| `clipboardSnapshot` | **start 即带** | 开场冻结的材料;≤3000 字;仅 `clipboardCommand` |
|
||||
| `fieldContext` | 可仍在 stop 时带 | 插入指纹 / 空框判定等(沿用现状) |
|
||||
|
||||
```text
|
||||
长按达标 → 冻快照(≤3000)
|
||||
→ startRecording { mode: clipboardCommand, clipboardSnapshot }
|
||||
→ 松手 → stopRecording { fieldContext? }
|
||||
→ host finalize:mode=clipboardCommand → 指令 Composer
|
||||
失败 → error;rawText 可留作调试,ext 不得 insert raw
|
||||
```
|
||||
|
||||
连续改写 = 同一指令会话内多个 utterance;每轮 start **重复携带同一份**内存快照。
|
||||
「上一版产物」不进 FlowCommand,由扩展在成功上屏后记住,经 host 侧 prompt 用户区传入(见 11.2)——若上一版仅 ext 知道,则需在 stop/start 增加可选 `previousOutput`,或 finalize 前写入 App Group。
|
||||
|
||||
**推荐补字段(冻结):** start 或 stop 可选 `previousOutput: String?`(连续改写时由 ext 带上轮成功产物;首轮 nil)。与快照同属 utterance 自描述,避免旁路。
|
||||
|
||||
**FlowResult:** 回传 `utteranceMode`(或等效标记)。ext 对 `clipboardCommand`:**跳过**一切 raw ASR 上屏路径(含 `deliverRawFallbackIfAvailable`)。
|
||||
|
||||
### 11.2 Prompt 分流
|
||||
|
||||
| | 听写 | 剪贴板指令 |
|
||||
|--|------|------------|
|
||||
| Composer | `PolishPromptComposer`(含 R6) | **新建** `ClipboardCommandPromptComposer` |
|
||||
| 用户文本角色 | ASR = 待整理正文 | ASR = **指令**;快照 = **材料** |
|
||||
| 翻译开关 | 可生效 | **忽略**,强制非 translate |
|
||||
| 失败 | 可 fallback raw ASR | **禁止** raw 上屏 |
|
||||
| Style Pack | 完整听写人格装配 | **B1**:短语气偏置;指令优先 |
|
||||
|
||||
**System 契约(精神):** 按材料执行指令;只输出可直接发送的最终文本;无解释套话;不编造材料中没有的关键事实(除非指令要求语气发挥);Style Pack 为弱底色;口述指令覆盖风格。
|
||||
|
||||
**User 结构:**
|
||||
|
||||
```text
|
||||
【材料】
|
||||
{clipboardSnapshot}
|
||||
|
||||
【指令】
|
||||
{asrInstruction}
|
||||
|
||||
【上一版结果】 ← 仅连续改写且有 previousOutput
|
||||
{previousOutput}
|
||||
```
|
||||
|
||||
### 11.3 职责切分
|
||||
|
||||
| 职责 | 归属 |
|
||||
|------|------|
|
||||
| 机会读、R0–R6、资格 30s、hint、长按 0.45s | 扩展 |
|
||||
| 指令会话 30s、快照内存、上轮产物、干净替换 | 扩展 |
|
||||
| ASR、指令 LLM、结果 mode 标记 | 主 App |
|
||||
| 坚持不插 raw、插入/替换执行 | 扩展 |
|
||||
| Style Pack 短偏置读取 | 主 App(现有 store) |
|
||||
|
||||
主 App 对指令 utterance **尽量无会话态**(按包执行);指令会话态在扩展 → 与 jetsam=会话结束一致。
|
||||
|
||||
---
|
||||
|
||||
## 12. 下一步(可进入实现)
|
||||
|
||||
1. 实现 Shared:过滤纯函数 + 单测;`FlowCommand`/`FlowResult` 字段与兼容解码。
|
||||
2. 实现 `ClipboardCommandPromptComposer` + finalize 分流 + 禁 raw。
|
||||
3. 扩展:机会读 hint、长按 0.45s、指令会话态、插入/替换。
|
||||
4. 联调验收对照 §9。
|
||||
|
||||
Reference in New Issue
Block a user