# AIChat 授权功能体验改进方案

> 创建: 2026-06-14 | 状态: 待讨论

## 一、当前授权机制概览

### 1.1 核心流程

```
用户发送消息
  → Agentic Loop (最多 10 轮)
    → LLM 流式响应
    → 解析 tool_calls
    → 按风险等级分流:
        ├─ Low Risk  → 自动并行执行（join_all）
        ├─ Medium    → 暂停循环，等待人工审批
        └─ High      → 暂停循环，等待人工审批
    → 审批通过 → 执行工具 → try_continue_agent_loop 恢复循环
    → 审批拒绝 → tool_result="用户拒绝了此操作" → 恢复循环让 LLM 自行决策
```

### 1.2 工具风险分级矩阵

| 风险等级 | 工具 | 执行方式 |
|---------|------|---------|
| **Low** | `list_projects`, `list_tasks`, `list_ideas`, `list_trash`, `read_file`, `list_directory` | 自动执行，并行 `join_all` |
| Medium | `create_project`, `create_task`, `create_idea`, `update_project`, `update_task`, `bind_directory`, `write_file` | 需人工审批 |
| **High** | `delete_project`, `restore_project`, `purge_project`, `delete_task`, `run_workflow`, `run_command` | 需人工审批 |

### 1.3 已实现的亮点

- **审计留痕**：`ai_tool_executions` 表完整记录每次工具调用的状态、风险等级、决策者、请求时间和执行时间
- **断点恢复**：应用重启后通过 `restore_pending_approvals()` 从数据库重建内存中的待审批状态
- **审批卡片可读化**：`build_approval_reason()` 解析工具参数，拼接人类可读的审批理由
- **generating 状态 RAII 守护**：`GeneratingGuard` 确保无论正常退出还是 panic，`generating` 标志都能复位
- **数据变更联动刷新**：工具执行成功后自动 emit `df-data-changed` 事件，前端自动刷新列表
- **安全防护**：API Key 存 OS keyring、文件路径双层校验、write_file 覆盖前自动 .bak 备份 + 原子写

### 1.4 关键代码位置

| 模块 | 文件 | 职责 |
|------|------|------|
| 工具注册表 | `src-tauri/src/commands/ai/tool_registry.rs` | 工具定义 + 风险等级 + handler 同源 |
| 审批处理 | `src-tauri/src/commands/ai/audit.rs` | 工具调用审计 + pending 审批恢复 + 审批后状态回填 |
| Agentic 循环 | `src-tauri/src/commands/ai/agentic.rs` | 流式接收 → 工具执行 → 结果回传 LLM → 循环 |
| IPC 命令 | `src-tauri/src/commands/ai/commands.rs` | `ai_approve` / `ai_chat_send` / `ai_chat_stop` 等 |
| 审批卡片 UI | `src/components/ToolCard.vue` | 卡片渲染 + 参数展示 + 批准/拒绝按钮 |
| 卡片列表 | `src/components/ToolCardList.vue` | 多卡片折叠管理 |
| 聊天面板 | `src/components/AiChat.vue` | 消息列表 + 审批转发 + 输入区 |
| AI Store | `src/stores/ai.ts` | 模块级单例 state |
| 发送/审批 | `src/composables/ai/useAiSend.ts` | `approveToolCall` 乐观更新 + IPC 调用 |

---

## 二、用户体验痛点分析

### 痛点 1：逐个审批，无法批量操作

**现象**：当 AI 一次返回多个 Medium/High 工具调用时（比如同时创建 3 个任务 + 写 2 个文件），用户需要逐个点批准/拒绝。

**根因**：`process_tool_calls`（audit.rs）把每个待审批工具都推入 `pending_approvals` HashMap，前端为每个 pending 渲染独立的 ToolCard，每个卡片只有自己的"批准/拒绝"按钮。`ToolCardList.vue` 没有批量操作入口。

**影响**：高频交互场景下审批变成体力活，用户体验疲劳。

### 痛点 2：审批阻塞整个对话，期间无法输入

**现象**：审批等待期间 `generating=true`，用户虽然能上滑看历史，但无法发送新消息（`sendMessage` 会入队），也无法预知还有多少审批在排队。

**根因**：`ai_chat_send`（commands.rs:46）检查 `session.generating` 为 true 时拒绝新消息。审批等待期间 `generating` 保持 true（`agentic.rs` 的 `guard.disarm()` 保持 true 以便 `try_continue` 续生成）。

**影响**：用户处于"被动等待"状态，无法并行做其他事。

### 痛点 3：Medium 和 High 体验无差异

**现象**：两者都弹同样的审批卡片、同样的按钮，交互流程完全一致。

**根因**：`process_tool_calls`（audit.rs:103-120）中 Medium 和 High 走同一个 `pending_approvals.insert` 分支，区别仅在 `build_approval_reason` 的文案后缀（"请确认是否执行" vs "高风险，需人工批准"）。

**影响**：High 操作（如 `purge_project` 不可恢复）缺乏足够的警示力度，容易误操作。

### 痛点 4：write_file 审批信息不够直观

**现象**：`write_file` 是 Medium 风险，审批卡片只展示 path 和 content 参数。content 可能是几百行代码，在审批卡片里以 `formatArgValue` 截断到 300 字符展示。

**根因**：`ToolCard.vue` 的 `toolArgsEntries` → `displayArgValue` → `formatArgValue` 对超长值统一截断到 300 字符。

**影响**：用户无法看清要写入的完整内容，只能盲目批准。尤其覆盖已有文件时，用户不知道会改什么。

### 痛点 5：审批后无进度反馈

**现象**：点击"批准"后，卡片乐观置 `running`，但用户不知道：
- 工具正在执行还是已执行完等待 LLM 续生成
- 还有多少待审批在排队
- 整个 agentic 循环进行到第几轮

**根因**：前端没有全局的 agentic 循环进度指示器。`AiAgentRound` 事件虽然通知了轮次，但没有在 UI 上持久化展示。

**影响**：用户对系统状态缺乏掌控感，尤其在多轮工具调用时。

### 痛点 6：审批卡片可能被滚出视口

**现象**：当消息很多时，审批卡片可能被新消息推到上方滚出视口。

**根因**：`AiChat.vue` 的 `collapseInactive` 不会收起 `pending_approval` 卡片，但也不会自动滚动到 pending 卡片。没有全局徽标提醒。

**影响**：用户可能看不到待审批项，对话看起来"卡住了"但不知道在等什么。

---

## 三、改进方案

### P0 — 快速改善体感

#### 3.1 批量审批

**方案**：当同一轮有多个 pending 时，在 ToolCardList 顶部显示"全部批准(N) / 全部拒绝"按钮。

**改动范围**：
- `ToolCardList.vue`：新增批量操作栏，监听 toolCalls 中 pending_approval 数量
- `useAiSend.ts`：新增 `approveAll(rejectAll)` 方法，循环调用 `ai_approve`

**交互**：
```
┌─────────────────────────────────┐
│  ⏳ 3 项待审批                   │
│  [✓ 全部批准]  [✕ 全部拒绝]      │
├─────────────────────────────────┤
│  [工具卡片 1 - pending]          │
│  [工具卡片 2 - pending]          │
│  [工具卡片 3 - pending]          │
└─────────────────────────────────┘
```

#### 3.2 审批计数器 + 跳转

**方案**：在输入框上方或 header 显示 `⏳ 2 项待审批`，点击跳转到第一个 pending 卡片。

**改动范围**：
- `AiChat.vue`：header 区域增加审批徽标
- `ToolCardList.vue`：暴露 `scrollToFirstPending` 方法

**交互**：
```
┌──────────────────────────────────┐
│  🤖 助手          ⏳2   [+][×]   │  ← 徽标在 header
├──────────────────────────────────┤
│  ...消息列表...                    │
│  ┌─ 工具卡片 (pending) ──┐        │  ← 点击徽标滚动到此
│  │  创建任务：XXX         │        │
│  │  [批准] [拒绝]         │        │
│  └────────────────────────┘        │
├──────────────────────────────────┤
│  [输入框]                         │
└──────────────────────────────────┘
```

#### 3.3 write_file diff 预览

**方案**：write_file 审批时，如果文件已存在，展示前后对比 diff 而非裸 content。

**改动范围**：
- 后端 `tool_registry.rs`：write_file handler 在执行前读取旧文件内容，返回 diff 信息（或前端请求 diff）
- `ToolCard.vue`：pending_approval + name=write_file 时渲染 diff 视图

**交互**：
```
┌─ 写入文件 src/main.rs (待审批) ──────┐
│                                       │
│  - fn main() {                        │  ← 红色：删除行
│  -     println!("hello");             │
│  + fn main() {                        │  ← 绿色：新增行
│  +     println!("hello, world");      │
│  +     setup_logging();               │
│                                       │
│  ⚠ 覆盖已有文件 (23→45 行)            │
│  [查看完整内容] [批准] [拒绝]          │
└───────────────────────────────────────┘
```

---

### P1 — 增强控制力

#### 3.4 会话级授权 Session Trust

**方案**：引入会话级信任机制，替代全局宽松模式。用户在当前对话中一次性授权某目录的写/执行权限，后续该对话内同类操作自动放行。切换对话或新建对话时信任清空。

**配置项**：
```
当前会话信任目录:
  ✅ E:/wk-lab/devflow/src  (Write + Execute)
  ✅ E:/wk-lab/devflow/docs  (Write)
  + 添加目录...
```

**改动范围**：
- 前端：Settings 或对话 header 新增「信任管理」入口
- 后端：`AiSession` 增加 `trusted_dirs: HashSet<(PathBuf, TrustLevel)>`
- `audit.rs`：`process_tool_calls` 先查 session trust，命中则跳过 pending

**安全边界**：
- 仅纯读取操作（list_*/read_*/list_directory）保持自动放行
- 所有 create/update/bind/write/delete 操作默认需审批或 session-trust
- bind_directory 归类为修改操作
- 信任仅限当前会话内存，不持久化

#### 3.5 High 二次确认

**方案**：delete/purge/run_command 等高风险操作，批准后弹出二次确认。

**改动范围**：
- `ToolCard.vue`：High 风险 + approved=true 时，先弹 inline 确认（"确定要永久删除？此操作不可恢复"）
- 或用现有 `ConfirmDialog` 组件

**交互**：
```
第一次点击"批准":
  → 卡片内弹出确认提示
  → "确定要永久删除项目「XXX」？此操作不可恢复"
  → [确认删除] [取消]

第二次点击"确认删除":
  → 才真正执行 ai_approve
```

#### 3.6 审批超时（前端定时器）

**方案**：前端侧 5 分钟超时自动拒绝，避免对话永久卡住。超时策略独立于 Webhook 等外部动作路径。

**改动范围**：
- 前端：`useAiSend.ts` 在 pending 时启动 5min 定时器，超时自动调 `ai_approve(id, false)`
- 不改后端 AiSession 结构（远期 Webhook 走独立路径）

---

### P2 — 信息透明度

#### 3.7 Agentic 进度条

**方案**：在消息区域底部显示循环进度。

**改动范围**：
- `AiChat.vue`：底部增加进度指示条
- `useAiEvents.ts`：处理 `AiAgentRound` 事件时更新进度

**交互**：
```
┌──────────────────────────────────┐
│  ...消息列表...                    │
│                                   │
│  🔄 循环 3/10 · ⏳2待审批 · ✅5完成 │  ← 底部进度条
├──────────────────────────────────┤
│  [输入框]                         │
└──────────────────────────────────┘
```

#### 3.8 审计历史面板

**方案**：独立页面展示 `ai_tool_executions` 表的审计记录。

**改动范围**：
- 后端：新增 `list_tool_executions` IPC 命令（查询审计表）
- 前端：新增 `AuditLog.vue` 视图，表格展示历史记录

**展示字段**：
| 时间 | 工具 | 风险 | 状态 | 决策者 | 参数摘要 | 结果摘要 |
|------|------|------|------|--------|---------|----------|

---

## 四、实施优先级建议

| 优先级 | 改进项 | 预估工作量 | 用户价值 |
|--------|--------|-----------|---------|
| **P0** | 3.1 批量审批 | 0.5 天 | 🔥🔥🔥 |
| **P0** | 3.2 审批计数器 + 跳转 | 0.5 天 | 🔥🔥🔥 |
| **P0** | 3.3 write_file diff 预览 | 1 天 | 🔥🔥 |
| **P1** | 3.4 会话级授权 Session Trust | 1.5 天 | 🔥🔥🔥 |
| **P1** | 3.5 High 二次确认 | 0.5 天 | 🔥 |
| **P1** | 3.6 审批超时(前端5min) | 0.5 天 | 🔥 |
| **P2** | 3.7 Agentic 进度条 | 0.5 天 | 🔥🔥 |
| **P2** | 3.8 审计历史面板 | 1 天 | 🔥 |

**建议第一批落地**：P0 三项（批量审批 + 计数器 + diff 预览），总计约 2 天工作量，覆盖最高频的体验痛点。
