Files
Hua.Todo/docs/project/研发工单-v1.3.0/02-语音通话与语音控制.md
T
ShaoHua 9223ceca50 feat(mcp): 新增 MCP 服务基础设施,重构规则文件序号,新增 v1.3.0 工单文档
- 规则重组:全局/ 下 8 个规则合并为 6 个(01+02→01,05+06→04),序号顺延

- 新增项目规则 05-多入口功能同步规范(UI/语音入口覆盖检查)

- 新增 MCP 服务基础设施:Mcp/ 目录(DI 注册、端点扩展、动态工具描述符)、单元测试

- v1.3.0 工单文档:03 系列(会议任务拆分)、04(富文本描述与附件管理)

- MCP 接口与前端集成指南:docs/manual/08、09
2026-06-16 01:15:40 +08:00

454 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 研发工单 v1.3.0 - 02 语音控制与 AI 辅助
---
## 一、目标与范围
### 1.1 目标
实现语音控制 Todo 待办项能力与 AI 辅助任务拆分功能,通过平台原生 STT/TTS 实现语音输入输出,通过 LLM 意图解析处理自然语言指令,离线降级到规则匹配,通过 LLM 提供 AI 拆分建议。
### 1.2 范围
**包含**
- 语音输入(STT):平台原生语音识别 → 文字
- 语音播报(TTS):执行结果语音反馈
- 语音指令解析与执行(CRUD + 子任务操作)
- 指令歧义处理(目标不唯一时返回候选列表)
- AI 辅助任务拆分(LLM 生成子任务建议,用户确认后批量创建)
**不包含**
- 语音通话功能(场景不明确,本期不做)
- 前端语音 UI 设计(仅提供能力层与 API)
- 第三方语音服务集成(使用平台原生能力)
---
## 二、前置条件
| 条件 | 说明 |
|---|---|
| Hua.Todo v1.2.0 | 已完成,提供基础业务能力 |
| 各平台原生 STT/TTS | Windows`Windows.Media.SpeechRecognition`/`SpeechSynthesis`)、Android`SpeechRecognizer`/`TextToSpeech`)、iOS/macOS`SFSpeechRecognizer`/`AVSpeechSynthesizer`)、LinuxWebKitGTK Web Speech API / `vosk` 离线模型) |
| LLM API | 在线模式意图解析 + AI 拆分共用;API Key 在 Host 端管理 |
| 工单 01(可选) | MCP 服务就绪后,语音指令与 AI 拆分也可通过 MCP 暴露 |
---
## 三、架构设计
### 3.1 整体链路
```
平台 STT(语音→文字)
IVoiceInputServiceCore 接口,各平台实现)
↓ 回调文字到前端
WebView → POST /api/voice/command { text: "帮我把那个开会的删了吧" }
Host API → IVoiceIntentParser(双策略)
├─ 在线:LlmIntentParser(调 LLM,输出结构化意图+参数)
└─ 离线:RuleIntentParser(关键词规则匹配,覆盖高频指令)
意图 + 参数 → 判断歧义
├─ 无歧义 → 调用 TaskService 执行 → TTS 播报结果
├─ 有歧义 → 返回候选列表 → 前端展示 → 用户确认 → 再执行
└─ UNKNOWN → TTS 播报"没听懂,请再说一次"
```
### 3.2 STT/TTS 分层策略
遵循与全局快捷键相同的平台分离模式(接口+平台目录):
| 层 | 职责 | 位置 |
|---|---|---|
| `IVoiceInputService` / `IVoiceOutputService` | 接口定义 | `src/Hua.Todo.Core/Services/` |
| MAUI 平台实现 | 各平台原生 STT/TTS | `src/Hua.Todo.Maui/Platforms/{Windows,Android,macOS,iOS}/VoiceInputService.cs` |
| Avalonia 平台实现 | Linux 桌面 STT/TTS | `src/Hua.Todo.Avalonia/Services/Platforms/VoiceInputService.cs` |
| Web 降级方案 | 浏览器 Web Speech API | 前端 `voiceInput.ts` |
### 3.3 意图解析双策略(A+C 方案)
采用 **LLM 主解析 + 规则降级** 的混合方案:
#### 策略 A:在线 — LLM 意图解析(LlmIntentParser
在线模式下,将用户原始文字发送给 LLM,通过 system prompt 约束输出为结构化 JSON
```
System Prompt(精简版):
你是 Hua.Todo 的语音指令解析器。根据用户输入,输出以下 JSON 格式:
{
"intent": "CREATE|UPDATE|DELETE|COMPLETE|UNCOMPLETE|QUERY|ADD_SUBTASK|AI_BREAKDOWN|UNKNOWN",
"params": { ... },
"confidence": 0.0-1.0
}
意图说明:
- CREATE: 创建任务,params: { title, priority? }
- UPDATE: 修改任务,params: { targetTitle, newTitle }
- DELETE: 删除任务,params: { targetTitle }
- COMPLETE: 完成任务,params: { targetTitle }
- UNCOMPLETE: 取消完成,params: { targetTitle }
- QUERY: 查询任务,params: { filter? }
- ADD_SUBTASK: 添加子任务,params: { parentTitle, subTitle }
- AI_BREAKDOWN: AI辅助拆分,params: { targetTitle }
- UNKNOWN: 无法识别
只输出 JSON,不要解释。
```
**示例调用**
```
输入:"帮我把那个开会的任务删了吧"
LLM 输出:
{
"intent": "DELETE",
"params": { "targetTitle": "开会" },
"confidence": 0.95
}
```
```
输入:"这个项目太大了,帮我拆一下"
LLM 输出:
{
"intent": "AI_BREAKDOWN",
"params": { "targetTitle": "这个项目" },
"confidence": 0.88
}
```
**优势**
- 无需维护同义词表,LLM 天然理解各种自然语言表述
- 扩展新意图只需修改 prompt,不改代码
- 歧义场景 LLM 也能判断(如 confidence < 阈值时触发确认)
#### 策略 C:离线 — 规则匹配降级(RuleIntentParser
离线/嵌入式模式下,降级到关键词规则匹配,只覆盖高频指令:
| 意图 | 匹配规则 | 示例 |
|---|---|---|
| `CREATE` | 正则:`/^(创建|新增|添加|加)\s*(任务|待办)?\s*(.+)/` | "创建任务 开会" |
| `DELETE` | 正则:`/^(删除|删掉|移除)\s*(任务|待办)?\s*(.+)/` | "删除任务 开会" |
| `COMPLETE` | 正则:`/^(完成|做完)\s*(任务|待办)?\s*(.+)/` | "完成任务 开会" |
| `UNCOMPLETE` | 正则:`/^(取消完成|重新打开|恢复)\s*(.+)/` | "取消完成 开会" |
| `UPDATE` | 正则:`/^(修改|更新|编辑)\s*(.+?)\s*(改为|改成|改成)\s*(.+)/` | "修改任务 开会 改为 团队会议" |
| `QUERY` | 正则:`/^(查询|查看|列出|显示)\s*(.+)/` | "查询未完成任务" |
| `ADD_SUBTASK` | 正则:`/^(给|为)\s*(.+?)\s*(添加|加)\s*(子任务|子项)?\s*(.+)/` | "给任务开会添加子任务 准备PPT" |
| `AI_BREAKDOWN` | 正则:`/^(帮我拆分|AI拆分|智能拆分)\s*(.+)/` | "帮我拆分 开会" |
**降级策略**
- 离线模式下 `AI_BREAKDOWN` 意图不可用(需要 LLM),TTS 播报"离线模式不支持 AI 拆分"
- 规则匹配失败时返回 `UNKNOWN`
#### 策略切换逻辑
```csharp
/// <summary>语音意图解析器接口</summary>
public interface IVoiceIntentParser
{
/// <summary>解析语音指令文本,返回意图与参数</summary>
Task<VoiceIntentResult> ParseAsync(string text, CancellationToken ct = default);
}
/// <summary>双策略解析器:在线走 LLM,离线走规则</summary>
public class HybridVoiceIntentParser : IVoiceIntentParser
{
private readonly LlmIntentParser _llmParser;
private readonly RuleIntentParser _ruleParser;
private readonly IConnectivityService _connectivity;
public async Task<VoiceIntentResult> ParseAsync(string text, CancellationToken ct)
{
if (_connectivity.IsOnline)
{
try
{
var result = await _llmParser.ParseAsync(text, ct);
if (result.Intent != VoiceIntent.UNKNOWN)
return result;
}
catch (Exception)
{
// LLM 调用失败,降级到规则
}
}
return _ruleParser.Parse(text);
}
}
```
### 3.4 语音指令处理完整流程
```
原始文字 → HybridVoiceIntentParser
├─ 在线:LlmIntentParserLLM 结构化输出)
└─ 离线/降级:RuleIntentParser(关键词正则匹配)
意图 + 参数 + confidence
├─ confidence >= 阈值 且 目标唯一 → 执行 → TTS 播报
├─ 目标匹配多个 → 返回候选列表 → 用户确认 → 执行
├─ confidence < 阈值 → TTS "您是要...吗?" → 用户确认
└─ UNKNOWN → TTS "没听懂,请再说一次"
```
---
## 四、需求规格
### 4.1 语音输入(STT
各平台通过原生 API 将语音转为文字,通过 `IVoiceInputService` 接口统一暴露:
```csharp
/// <summary>语音输入服务接口,各平台实现原生 STT</summary>
public interface IVoiceInputService
{
/// <summary>是否正在监听</summary>
bool IsListening { get; }
/// <summary>开始语音识别,识别结果通过回调返回</summary>
Task StartListeningAsync(Action<string> onResult, Action<string>? onError = null);
/// <summary>停止语音识别</summary>
Task StopListeningAsync();
}
```
### 4.2 语音播报(TTS
```csharp
/// <summary>语音输出服务接口,各平台实现原生 TTS</summary>
public interface IVoiceOutputService
{
/// <summary>是否正在播报</summary>
bool IsSpeaking { get; }
/// <summary>播报文本</summary>
Task SpeakAsync(string text);
/// <summary>停止播报</summary>
Task StopSpeakingAsync();
}
```
### 4.3 语音指令解析
#### 意图定义
| 意图 (Intent) | 参数 | 说明 |
|---|---|---|
| `CREATE` | `title`(必填)、`priority`(可选:High/Medium/Low | 创建任务 |
| `UPDATE` | `targetTitle`(必填)、`newTitle`(必填) | 修改任务标题 |
| `DELETE` | `targetTitle`(必填) | 删除任务 |
| `COMPLETE` | `targetTitle`(必填) | 标记完成 |
| `UNCOMPLETE` | `targetTitle`(必填) | 取消完成 |
| `QUERY` | `filter`(可选:如"未完成"、"高优先级" | 查询任务 |
| `ADD_SUBTASK` | `parentTitle`(必填)、`subTitle`(必填) | 添加子任务 |
| `AI_BREAKDOWN` | `targetTitle`(必填) | AI 辅助拆分(仅在线) |
| `UNKNOWN` | 无 | 无法识别 |
#### 歧义处理规则
当指令中 `targetTitle` 匹配到多个 Todo 待办项时:
1. **不直接执行**,返回歧义结果(包含匹配的候选列表)
2. 前端展示候选项,用户点选或语音确认后再执行
3. 匹配规则:标题完全匹配优先,包含匹配次之
#### 置信度阈值
- LLM 返回 `confidence >= 0.8`:直接执行
- `0.5 <= confidence < 0.8`:确认后执行(TTS "您是要...吗?"
- `confidence < 0.5`:按 UNKNOWN 处理
### 4.4 语音指令 API
```
POST /api/voice/command
请求体:
{
"text": "帮我把那个开会的删了吧" // STT 识别后的原始文字
}
响应体:
{
"intent": "DELETE", // 解析出的意图
"params": { // 提取的参数
"targetTitle": "开会"
},
"confidence": 0.95, // 置信度
"result": { // 执行结果
"success": true,
"message": "已删除任务:开会",
"data": null
}
}
```
歧义响应:
```json
{
"intent": "COMPLETE",
"params": { "targetTitle": "开会" },
"confidence": 0.92,
"result": {
"success": false,
"message": "找到多个匹配的任务,请确认",
"ambiguity": true,
"candidates": [
{ "id": 1, "title": "开会" },
{ "id": 5, "title": "开会讨论方案" }
]
}
}
```
用户确认后二次请求:
```
POST /api/voice/command/confirm
请求体:
{
"intent": "COMPLETE",
"targetId": 5 // 用户选择的候选 ID
}
```
### 4.5 AI 辅助任务拆分
```
POST /api/voice/ai-breakdown
请求体:
{
"taskId": 5 // 要拆分的目标任务 ID
}
响应体:
{
"suggestions": [ // LLM 生成的子任务建议
{ "title": "准备会议议程", "priority": "Medium" },
{ "title": "发送会议邀请", "priority": "High" },
{ "title": "预定会议室", "priority": "Low" }
]
}
```
用户确认后批量创建:
```
POST /api/voice/ai-breakdown/confirm
请求体:
{
"parentTaskId": 5,
"subTasks": [ // 用户勾选后的子任务列表
{ "title": "准备会议议程", "priority": "Medium" },
{ "title": "发送会议邀请", "priority": "High" }
]
}
```
**关键约束**
- LLM 调用在 Host 端执行,API Key 不暴露到客户端
- 意图解析与 AI 拆分共用同一 LLM 基础设施
- 建议≠直接执行,必须用户确认后才创建子任务
- 离线模式下 AI 拆分不可用,TTS 播报"离线模式不支持 AI 拆分"
- MCP 服务就绪后,`aiBreakdown` 可同步暴露为 MCP 工具
---
## 五、验收标准
| 验收项 | 验证方法 | 预期结果 |
|---|---|---|
| STT 语音输入 | 说话后检查回调文字 | 文字识别正确 |
| TTS 语音播报 | 执行指令后检查播报 | 播报内容与执行结果一致 |
| 创建任务(在线) | "帮我把开会的任务加上" | LLM 解析为 CREATE,成功创建 |
| 创建任务(离线降级) | "创建任务 测试" | 规则匹配为 CREATE,成功创建 |
| 创建带优先级任务 | "建一个高优先级任务 紧急" | 成功创建高优先级任务 |
| 完成任务指令 | "把那个开会的事标完成" | LLM 解析为 COMPLETE,执行成功 |
| 取消完成指令 | "取消完成 测试" | 任务状态恢复为未完成 |
| 删除任务指令 | "帮我把测试删了" | LLM 解析为 DELETE,删除成功 |
| 更新任务指令 | "把测试改成验收" | 任务标题更新 |
| 查询任务指令 | "有哪些没做完的" | LLM 解析为 QUERY,返回列表 |
| 添加子任务指令 | "给开会加个子任务 准备PPT" | 子任务创建成功 |
| 歧义处理 | 多个任务含"开会"时说"完成开会" | 返回候选列表,不直接执行 |
| AI 拆分建议 | "帮我拆分 开会" | 返回子任务建议列表 |
| AI 拆分确认 | 用户勾选后确认 | 仅创建勾选的子任务 |
| 离线 AI 拆分 | 离线时说"帮我拆分" | 播报"离线模式不支持 AI 拆分" |
| 无法识别指令 | 说出无关内容 | 播报"没听懂,请再说一次" |
| LLM 降级 | 断网时使用语音 | 自动降级到规则匹配,基本 CRUD 可用 |
---
## 六、Touch List
| 文件路径 | 修改类型 | 说明 |
|---|---|---|
| `src/Hua.Todo.Core/Services/IVoiceInputService.cs` | 新增 | STT 接口定义 |
| `src/Hua.Todo.Core/Services/IVoiceOutputService.cs` | 新增 | TTS 接口定义 |
| `src/Hua.Todo.Core/Services/IVoiceIntentParser.cs` | 新增 | 意图解析器接口 |
| `src/Hua.Todo.Application/Voice/LlmIntentParser.cs` | 新增 | LLM 意图解析器(在线策略) |
| `src/Hua.Todo.Application/Voice/RuleIntentParser.cs` | 新增 | 规则匹配解析器(离线降级策略) |
| `src/Hua.Todo.Application/Voice/HybridVoiceIntentParser.cs` | 新增 | 双策略解析器(在线走 LLM,离线走规则) |
| `src/Hua.Todo.Application/Voice/VoiceCommandIntent.cs` | 新增 | 意图枚举定义 |
| `src/Hua.Todo.Application/Voice/VoiceCommandExecutor.cs` | 新增 | 意图执行器(分发到 TaskService |
| `src/Hua.Todo.Application/Voice/VoiceController.cs` | 新增 | 语音指令 API 端点(`/api/voice/command``/api/voice/ai-breakdown` |
| `src/Hua.Todo.Application/Voice/AiBreakdownService.cs` | 新增 | AI 辅助拆分服务(调用 LLM 生成建议) |
| `src/Hua.Todo.Application/Voice/LlmClientService.cs` | 新增 | LLM 调用封装(意图解析 + AI 拆分共用) |
| `src/Hua.Todo.Application/Voice/Models/` | 新增 | 语音相关 DTOVoiceIntentResult、VoiceCommandRequest 等) |
| `src/Hua.Todo.Application/Voice/VoiceServiceCollectionExtensions.cs` | 新增 | 语音服务 DI 注册 |
| `src/Hua.Todo.Maui/Platforms/Windows/VoiceInputService.cs` | 新增 | Windows STT 实现 |
| `src/Hua.Todo.Maui/Platforms/Windows/VoiceOutputService.cs` | 新增 | Windows TTS 实现 |
| `src/Hua.Todo.Maui/Platforms/Android/VoiceInputService.cs` | 新增 | Android STT 实现 |
| `src/Hua.Todo.Maui/Platforms/Android/VoiceOutputService.cs` | 新增 | Android TTS 实现 |
| `src/Hua.Todo.Maui/Platforms/MacCatalyst/VoiceInputService.cs` | 新增 | macOS STT 实现 |
| `src/Hua.Todo.Maui/Platforms/MacCatalyst/VoiceOutputService.cs` | 新增 | macOS TTS 实现 |
| `src/Hua.Todo.Maui/Platforms/iOS/VoiceInputService.cs` | 新增 | iOS STT 实现 |
| `src/Hua.Todo.Maui/Platforms/iOS/VoiceOutputService.cs` | 新增 | iOS TTS 实现 |
| `src/Hua.Todo.Avalonia/Services/Platforms/VoiceInputService.cs` | 新增 | Linux STT 实现 |
| `src/Hua.Todo.Avalonia/Services/Platforms/VoiceOutputService.cs` | 新增 | Linux TTS 实现 |
| `src/Hua.Todo.Web/src/api/voice.ts` | 新增 | 前端语音 API 模块 |
| `src/Hua.Todo.Web/src/composables/useVoiceInput.ts` | 新增 | 前端语音输入组合式函数(含 Web Speech API 降级) |
---
**工单编号**02
**标题**:语音控制与 AI 辅助
**版本**v1.3.0
**修订日期**2026-06-16
---
## 七、与其他工单的语音入口衔接
### 7.1 背景
Hua.Todo 目前有两个功能入口:**UI 入口**(WebView / 前端界面)和**语音控制入口**(本工单产出)。后续每项新增功能在需求阶段都必须确认这两个入口的覆盖情况。
### 7.2 工单 03(会议任务拆分)的语音入口
工单 03 的会议功能需要通过语音控制入口提供以下指令:
| 语音指令 | 意图 | 参数 | 说明 |
|---|---|---|---|
| "新建会议 项目评审" | `CREATE_MEETING` | `title`(必填)、`priority`(可选) | 创建会议类型任务(TaskType.Meeting |
| "记录会议内容" | `RECORD_MEETING` | `targetTitle`(必填) | 开始录制当前会议 |
| "停止录制" | `STOP_RECORDING` | 无 | 停止录制,触发转写 |
| "帮我拆分这个会议" | `AI_BREAKDOWN` | `targetTitle`(必填,目标为会议类型) | 对会议内容进行 AI 拆分(复用 AI_BREAKDOWN 意图,参数携带 TaskType=Meeting |
| "查看会议待办项" | `QUERY` | `filter="会议"` | 查询所有会议类型任务 |
**关键点**
- `AI_BREAKDOWN` 意图需扩展以区分普通任务拆分和会议拆分(通过 `TaskType` 字段),会议拆分的 prompt 侧重"提取行动项"
- `CREATE_MEETING``CREATE` 意图的子类型,创建时字段 `taskType = Meeting`,需要 LLM intent parser 的 prompt 增加说明
- 录音控制(`RECORD_MEETING` / `STOP_RECORDING`)是新意图,对应前端 MediaRecorder 的启动/停止
### 7.3 语音入口覆盖检查表
后续每个新增功能/工单,智能体必须在需求讨论阶段输出以下检查表:
| 检查项 | 状态 | 备注 |
|---|---|---|
| UI 入口是否已规划 | ✅ / ❌ | 描述 UI 入口 |
| 语音控制入口是否已规划 | ✅ / ❌ | 描述对应的语音指令与意图 |
| 当前不可覆盖的入口 | 说明原因 | 如:语音控制暂不支持 XX 操作(选项过多/需可视化交互) |
此规则已固化为项目规范,详见 [.trae/rules/项目/05-多入口功能同步规范.md](../../../.trae/rules/项目/05-多入口功能同步规范.md)。