# Investibook 设计文档

> **版本**：1.3  
> **最后更新**：2026-03-03  
> **状态**：设计草稿

---

## 1. 产品概述

Investibook 是面向 TiDB Cloud 运维的 **AI 研究工作台**，采用类 NotebookLM 的三列布局，帮助用户调查告警事件并进行根因分析。

核心理念：**以单个告警事件为中心组织工作——在一个 Workspace 内聚合数据源（Sources）、与 AI Agent 对话（Chat）、生成可交付的分析产物（Artifacts）**。

```
┌──────────────┬──────────────────────┬──────────────────────┐
│              │                      │                      │
│   Sources    │        Chat          │      Studio          │
│  （左列）    │    （中间列）        │    （右列）          │
│              │                      │                      │
│ • 告警       │  ACP Agent 对话      │ • Insight Report     │
│ • MCP 连接器 │  SSE 实时            │ • Anomaly Report     │
│ • 集群       │  流式输出            │ • Notes              │
│   元数据     │  工具调用可视化      │ • Charts             │
│ • 用户信息   │                      │                      │
│              │   ┌──────────────┐   │                      │
│ ───────────  │   │ File Viewer  │   │                      │
│  Files 标签  │   └──────────────┘   │                      │
│ （虚拟文件树）│                      │                      │
│              │                      │                      │
└──────────────┴──────────────────────┴──────────────────────┘
```

---

## 2. 核心概念

### 2.1 Workspace（工作区）

Workspace 是用户工作的顶层容器，代表一次完整的调查会话。

| 属性 | 说明 |
|------|------|
| `id` | 唯一标识符 `ws-{timestamp}-{random}` |
| `name` | 用户可编辑的标题 |
| `description` | 可选描述 |
| `createdAt` / `updatedAt` | 时间戳 |
| `isShared` | 是否对外公开共享（非所有者只读访问） |
| `isOwner` | 当前认证用户是否拥有该 Workspace（服务端计算） |
| `isExample` | 标识预置样例工作区（含样本告警和报告） |

**持久化**：所有 Workspace 元数据和每个 Workspace 的数据均**服务端存储在 SQLite**（`data/db/investibook.db`），通过 REST API 访问。Workspace 按认证用户（`user_id`）隔离。浏览器只在 `localStorage` 中保存 Bearer 认证 token 和用户邮箱（键名 `investibook_token` / `investibook_email`）。

**代码位置**：`lib/workspaces.ts`（CRUD via REST）/ `app/page.tsx`（Workspace 列表页）/ `backend/main.py`（`_db_*` 辅助函数 + `/api/workspaces` 路由）

### 2.2 Source（数据源）

Source 代表连接到 Workspace 的一个数据源。所有从外部进入系统的信息都以 Source 的形式出现在左列。

```typescript
type SourceType = 'alert' | 'git_repo' | 'mcp_connector' | 'cluster_meta' | 'user_meta' | 'knowledge' | 'skill';
```

| Source 类型 | 图标 | 说明 | 添加方式 |
|-------------|------|------|----------|
| `alert` | 🚨 | Jira 告警 ID——驱动整个调查过程 | 用户手动添加 |
| `mcp_connector` | 🔌 | MCP 协议连接器（Clinic / Ops Portal / O11Y / Jira） | 系统默认（展示用） |
| `cluster_meta` | 🏗️ | 集群拓扑和元数据 | 后端自动生成（由 Alert 触发） |
| `user_meta` | 👤 | 用户上下文信息 | 系统默认（只读，展示用） |
| `knowledge` | 📚 | 领域知识库（运行手册、Wiki、文档） | 用户添加（即将推出） |
| `skill` | ⚡ | 可复用的分析技能 / 提示词模板 | 用户添加（即将推出） |
| `git_repo` | 📦 | Git 仓库 | 即将推出 |

> **默认系统源**：每个新 Workspace 自动注入 `user_meta`（只读）+ **5 个 MCP 连接器**：**Grafana Screenshot**、**Clinic**、**Ops Portal**、**O11Y**、**Jira**（均为展示用，实际 MCP 连接由 ACP Agent 子进程处理）。

**代码位置**：`lib/workspace.tsx`（`Source` 接口、`SOURCE_TYPE_META`、`createDefaultSources()`）

### 2.3 Artifact（产物）

Artifact 是由 AI 分析或用户操作生成的可视化结果。

```typescript
type ArtifactType = 'report' | 'chart' | 'summary' | 'timeline' | 'note';
```

| Artifact 类型 | 说明 | 渲染组件 |
|---------------|------|----------|
| `report` | Guardian Insight 报告 | `InsightReport/index.tsx` |
| `chart` | 单个指标图表 | `ChartCard/index.tsx` |
| `summary` | 文本摘要 | `InsightReport/SummaryCard.tsx` |
| `timeline` | 事件时间轴 | — |
| `note` | 用户笔记 | `StudioPanel/NoteModal.tsx` |

**代码位置**：`lib/workspace.tsx`（`Artifact` 接口）/ `components/StudioPanel/index.tsx`（列表与详情渲染）

---

## 3. Sources 与虚拟文件系统映射

Investibook 将远程诊断数据映射到**虚拟文件系统**，使 ACP Agent 能够像读取本地文件一样访问所有上下文。这是将 Sources（左列）与 Agent 对话能力（中间列）连接起来的核心机制。

### 3.1 映射结构

```
data/                                ← ACP Agent 的 cwd（工作目录）
├── .opencode/
│   └── opencode.json               ← ACP/MCP Server 配置
├── cluster_meta.json                ← [cluster_meta Source] 集群拓扑
├── diagnostic_package.json          ← [Jira Issue Source] S3 诊断包
├── insight.json                     ← [Jira Issue Source] S3 Insight 报告
├── resource_worker_insight.json     ← [mcp_connector Source] Resource Worker 分析
└── toolset/
    └── resource-worker-mock-mcp/    ← MCP Tool Server 实现
        ├── data/mock/               ← 模拟响应数据
        │   ├── analyze_alert_result.json
        │   ├── chat_question1_result.json
        │   ├── chat_question2_result.json
        │   └── chat_question3_result.json
        └── service/mcp/
            └── worker_server_mock.py  ← MCP Server（analyze_alert 等工具）
```

### 3.2 Source → 文件详细映射

| Source 类型 | 映射文件 | 数据内容 | 来源 |
|-------------|----------|----------|------|
| `alert` | `insight.json` | Guardian Insight 报告（根因分析、证据链、指标状态） | S3 历史报告 |
| `alert` | `diagnostic_package.json` | 诊断包（指标分类、异常检测、慢查询、事件） | S3 诊断文件 |
| `cluster_meta` | `cluster_meta.json` | 集群 ID、版本、拓扑、云厂商、地域 | S3 / Ops Portal API |
| `mcp_connector`（Resource Worker） | `resource_worker_insight.json` | 告警类型、集群 ID、异常检测结果 | MCP 工具调用结果 |
| `user_meta` | _（内存中，不持久化到文件）_ | 用户偏好和上下文 | 前端注入 |

### 3.3 前端文件树

左列 Sources 面板有两个标签：**Sources** 和 **Files**。Files 标签**动态从后端获取**真实文件树（`GET /api/workspace/{id}/files`）并通过 `FileTree` 组件渲染。每个文件节点根据扩展名显示 emoji 图标（📄 JSON、🖼️ 图片、📝 Markdown、⚙️ YAML、🔒 lock 文件）及文件大小。

点击文件会在中间列下半区的 Monaco 编辑器（只读）中打开，用户可直接查看 Agent 正在读取的原始数据。目录节点可折叠，顶层目录默认展开。

**代码位置**：`components/SourcesPanel/index.tsx`（请求与状态）/ `components/SourcesPanel/FileTree.tsx`（递归树渲染）/ `components/StudioPanel/FileViewer.tsx`（Monaco 查看器）

**文件树数据流**：
```
SourcesPanel 挂载
  └─ GET /api/workspace/{id}/files
       → WorkspaceFile[]（name, path, type, children?, size?）
       → 传入 <FileTree> 渲染

用户点击文件
  └─ setOpenFilePath(path)    ← WorkspaceContext
       → ChatPanel 渲染 <FileViewer path={path}>
            GET /api/workspace/{id}/file-content?path=...
            → Monaco Editor（只读）
```

---

## 4. ACP Chat——Agent 通信协议

### 4.1 架构

```
浏览器                           后端（FastAPI）                  ACP Agent（子进程）
  │                                    │                                    │
  │  GET /api/chat/stream?message=...  │                                    │
  │ ──────────────────────────────────>│                                    │
  │                                    │  spawn / reuse subprocess          │
  │                                    │ ──────────────────────────────────>│
  │                                    │  ACP JSON-RPC over stdio           │
  │                                    │  conn.prompt(session_id, message)  │
  │                                    │ ──────────────────────────────────>│
  │                                    │                                    │
  │                                    │  session_update 回调：             │
  │                                    │  AgentMessageChunk / ThoughtChunk  │
  │                                    │  ToolCallStart / ToolCallProgress  │
  │                                    │<──────────────────────────────────│
  │                                    │                                    │
  │  SSE: data: {"type":"text",...}    │                                    │
  │<──────────────────────────────────│                                    │
  │  SSE: data: {"type":"tool",...}    │                                    │
  │<──────────────────────────────────│                                    │
  │  SSE: data: {"type":"complete"}    │                                    │
  │<──────────────────────────────────│                                    │
```

### 4.2 SSE 事件类型

| 事件 `type` | 含义 | 关键字段 |
|-------------|------|----------|
| `session_created` | 会话已建立 | `session_id` |
| `text` | Agent 文本输出（流式） | `text` |
| `thought` | Agent 推理过程 | `text` |
| `tool` | 工具调用（开始 / 进行中 / 完成） | `id`, `title`, `status`, `input`, `output` |
| `complete` | 本轮结束 | `session_id` |
| `error` | 发生错误 | `error` |

**前端类型定义**：`components/ChatPanel/types.ts`（`SSEEvent` 联合类型）  
**后端事件生成**：`backend/main.py`（`WebACPClient.session_update()`）

### 4.3 ACP 会话管理

```
                      ┌────────────────────────────────┐
                      │        ACPSession               │
                      │  • frontend_id（workspace 范围）│
                      │  • agent_type（opencode/claude）│
                      │  • acp_session_id               │
                      │  • conn（ClientSideConnection） │
                      │  • proc（subprocess）            │
                      │  • client（WebACPClient）        │
                      │  • _initialized                 │
                      └────────────────────────────────┘
                                    │
                      ┌─────────────┴─────────────┐
                      │  sessions: Dict[str, ACPSession] │
                      │  （全局会话池）                  │
                      └──────────────────────────────────┘
```

- 每个 Workspace 映射到一个 `session_id`；首次交互时创建 ACP 子进程
- Agent 的 `cwd` 设置为 `data/` 目录，可直接访问所有映射文件
- System Prompt 在会话初始化时发送，提供 TiDB Guardian Insight 领域知识
- 切换 Agent：通过环境变量 `ACP_AGENT_TYPE` 选择 `opencode` 或 `claude`

### 4.4 WebACPClient 能力

| 方法 | 功能 |
|------|------|
| `request_permission` | 自动批准 Agent 权限请求（allow_once / allow_always） |
| `read_text_file` | 读取本地文件系统文件（Agent 读取 `data/` 下的诊断文件） |
| `write_text_file` | 写入文件（Agent 可生成中间分析结果） |
| `session_update` | 接收 Agent 事件，转换为扁平 SSE 推送给前端 |

---

## 5. 告警 Source 添加流程（核心交互）

这是系统中最关键的用户流——添加 Jira Issue Source 会自动拉取相关数据并生成报告。

### 5.1 目标交互流程

```
用户操作                            系统行为                            UI 变化
────────                            ────────                            ──────
1. 点击"+ Add Sources"             打开 AddSourceModal                 弹窗出现
   输入告警 ID（如 O11Y-1956041）
   点击"Add"

2. Issue Source 添加成功           Source 出现在左列列表               🚨 O11Y-1956041
                                   Issue Source 卡片开始加载            ⏳ Spinner 动画

3.                                 后端异步处理：                       Alert 卡片继续转
                                   ├─ 通过告警 ID 查询 S3
                                   ├─ 下载 insight.json
                                   ├─ 下载 diagnostic_package.json
                                   ├─ 下载 cluster_meta.json
                                   └─ 写入 data/ 目录

4.                                 后端处理完成，通知前端：             Spinner 消失 ✓
                                   ├─ cluster_meta Source 自动添加     🏗️ Cluster Meta 在左列
                                   ├─ Insight Report Artifact 已添加   📊 右列出现报告
                                   └─ 文件树已更新                      Files 标签显示新文件

5. 用户点击 Report Artifact        Studio 进入详情视图                  右列渲染完整报告

6. 用户在 Chat 中提问              Agent 读取 data/ 文件               流式分析输出
                                   调用 MCP Tool（analyze_alert）       ToolCall 状态可视化
```

### 5.2 序列图

```
  用户           前端                  后端               S3/外部
   │              │                   │                  │
   │  添加 Alert  │                   │                  │
   │─────────────>│                   │                  │
   │              │                   │                  │
   │              │  POST /api/sources/prepare           │
   │              │  {alertId, workspaceId}               │
   │              │──────────────────>│                  │
   │              │                   │                  │
   │              │  202 Accepted     │                  │
   │              │  (taskId)         │                  │
   │              │<──────────────────│                  │
   │              │                   │                  │
   │  Alert 卡片  │                   │  GET insight.json │
   │  显示 Spinner│                   │─────────────────>│
   │              │                   │  GET diagnostic_  │
   │              │                   │  package.json     │
   │              │                   │─────────────────>│
   │              │                   │  GET cluster_     │
   │              │                   │  meta.json        │
   │              │                   │─────────────────>│
   │              │                   │<─────────────────│
   │              │                   │                  │
   │              │                   │  写入 data/ 目录  │
   │              │                   │  构建文件映射     │
   │              │                   │                  │
   │              │  SSE / 轮询       │                  │
   │              │  状态更新         │                  │
   │              │<──────────────────│                  │
   │              │                   │                  │
   │  Spinner     │                   │                  │
   │  消失        │                   │                  │
   │  右列出现    │                   │                  │
   │  报告        │                   │                  │
   │              │                   │                  │
```

### 5.3 前端 Alert Loading 状态设计

| 状态 | UI 表现 | 触发条件 |
|------|---------|----------|
| `loading` | Spinner + "Preparing data…" | 刚添加，后端正在获取数据 |
| `ready` | 绿色 ✓ + 正常展示 | 后端数据已就绪 |
| `error` | 红色 ⚠ + 错误信息 | S3 下载失败或超时 |

### 5.4 后端 Source 准备 API 设计（计划中）

```
POST /api/sources/prepare
请求：
{
  "alert_id": "O11Y-1956041",
  "workspace_id": "ws-1234567890-abc"
}

响应：202 Accepted
{
  "task_id": "task-xxxxx",
  "status": "processing"
}

GET /api/sources/prepare/{task_id}
响应：
{
  "status": "completed",           // "processing" | "completed" | "error"
  "files": [
    {"path": "insight.json", "type": "insight_report", "size": 48000},
    {"path": "diagnostic_package.json", "type": "diagnostic", "size": 125000},
    {"path": "cluster_meta.json", "type": "cluster_meta", "size": 2000}
  ],
  "cluster_meta": {
    "cluster_id": "10355969091959802668",
    "tidb_version": "v8.5.4",
    "topology": "2-tidb / 6-tikv / 3-pd / 2-tiflash"
  },
  "error": null
}
```

---

## 6. Studio Artifact 系统

### 6.1 报告类型与渲染

#### Guardian Insight Report

完整的根因分析报告，数据来自 `insight.json`。

**数据结构**（`lib/types.ts` — `InsightData`）：

```
InsightData
├── direct_cause
│   ├── summary          — 一行根因摘要
│   ├── evidences[]      — 证据链（指标/SQL/事件）
│   └── jira_issue_related_metrics_name
├── detail
│   ├── cluster_topology — 集群拓扑描述
│   ├── workload[]       — 工作负载指标（Metric[]）
│   ├── resource[]       — 资源指标（Metric[]）
│   ├── latency[]        — 延迟指标（Metric[]）
│   └── events[]         — 事件列表
└── metadata
    ├── analysis_uuid
    ├── jira_issue_id
    ├── cluster_id
    ├── time_range_start_ts / time_range_end_ts
    └── created_at
```

**渲染组件**（`components/InsightReport/`）：

| 组件 | 功能 |
|------|------|
| `index.tsx` | 报告主框架（Header + Accordion 分组） |
| `SummaryCard.tsx` | 根因摘要卡（直接原因 + 证据链） |
| `MetricCard.tsx` | 单个指标分析卡（状态标签、时序 / 空间 / 容量分析） |
| `MetricChart.tsx` | 指标图表（基于 `metrics-chart` 库） |
| `EventsCard.tsx` | 事件列表 |

**图表联动**：使用 `metrics-chart` 库的 `SyncChartPointer` 实现多图表联动交互。

#### Anomaly Report

异常检测详情，数据来自 `diagnostic_package.json`。

**数据结构**（`lib/diagnosticTypes.ts` — `DiagnosticPackage`）：

```
DiagnosticPackage
├── metadata           — 分析元数据（UUID/集群/时间范围）
├── metrics[]          — 分类指标
│   └── MetricCategory
│       └── Metric → Profile → Anomaly[]  — 三级嵌套异常数据
├── top_slow_queries[] — Top N 慢查询
├── top_cpu_time_sqls  — Top N CPU 密集型 SQL
└── events[]           — 系统事件
```

**渲染组件**：`components/AnomaliesPanel/index.tsx`（异常分面展示，Z-score 严重度分级）

### 6.2 Studio Action Cards（计划中）

右列顶部的快捷操作卡片，当前为"Coming Soon"状态：

| 操作 | 说明 | 触发方式 |
|------|------|----------|
| 📊 Report | 从 Sources 生成深度分析报告 | 触发 Agent 分析 → 创建 Artifact |
| ⚙️ Operation | 创建 Runbook / 运维操作指南 | Agent 生成 → 可执行步骤 |
| 📝 Notes | 记录调查笔记 | 打开 NoteModal 手动编辑 |

---

## 7. MCP（Model Context Protocol）集成

### 7.1 MCP Connector Sources

前端展示 5 个 MCP Connector 作为系统 Sources（仅展示用）：

| 连接器 | 说明 | 实际 MCP Server |
|--------|------|-----------------|
| Grafana Screenshot | 抓取 Grafana 仪表盘截图 | backend 中配置 |
| Clinic | TiDB Clinic 诊断服务，查询指标/事件/慢查询/Top SQL | backend 中配置 |
| Ops Portal | TiDB Cloud 运维门户，查询租户和集群信息 | backend 中配置 |
| O11Y | 监控指标和仪表盘 | backend 中配置 |
| Jira | Jira 问题跟踪与项目管理 | backend 中配置 |

这些 Sources 仅在前端提供**上下文感知**。实际 MCP 连接由后端 ACP Agent 子进程建立。

### 7.2 Resource Worker MCP（Mock）

用于本地开发和集成测试的 MCP Tool Server：

| 工具 | 功能 |
|------|------|
| `analyze_alert` | 分析告警相关指标（Mock：返回静态 JSON） |
| `chat` | 基于分析结果的问答（Mock：返回预设回复） |

**数据目录**：`data/toolset/resource-worker-mock-mcp/data/mock/`

**配置**：ACP Agent 通过 `.opencode/opencode.json` 中的 `mcpServers` 发现并连接。

---

## 8. 完整数据流概览

```
┌─────────────────────────── Investibook Workspace ───────────────────────────────┐
│                                                                                  │
│  ┌── Sources（左）──┐   ┌──── Chat（中）────────┐   ┌──── Studio（右）────┐    │
│  │                  │   │                       │   │                     │     │
│  │  🚨 Alert        │   │   用户消息             │   │  📊 Insight Report  │     │
│  │  ├ insight.json ─┼───┼─> ACP Agent 读取 ────┼───┼─> Report Artifact   │     │
│  │  ├ diagnostic_pkg─┼───┼─> data/ 文件         │   │                     │     │
│  │  └ （S3 下载）    │   │                       │   │  📈 Anomaly Report  │     │
│  │                  │   │   Agent 调用 MCP Tool  │   │                     │     │
│  │  🏗️ Cluster Meta─┼───┼─> cluster_meta.json  │   │  📝 Notes           │     │
│  │  （自动生成）     │   │                       │   │                     │     │
│  │                  │   │   SSE Stream：         │   │                     │     │
│  │  🔌 Clinic       │   │   • text（markdown）   │   │                     │     │
│  │  🔌 Ops Portal   │   │   • thought（推理）    │   │                     │     │
│  │  🔌 O11Y         │   │   • tool（调用）       │   │                     │     │
│  │  🔌 Grafana SS   │   │                       │   │                     │     │
│  │  🔌 Jira         │   │   ┌── File Viewer ──┐ │   │                     │     │
│  │                  │   │   │ Monaco Editor    │ │   │                     │     │
│  │  👤 User Meta    │   │   │ （只读）          │ │   │                     │     │
│  │  （只读）        │   │   └─────────────────┘ │   │                     │     │
│  └──────────────────┘   └───────────────────────┘   └─────────────────────┘     │
│                                                                                  │
│  ┌─── data/（虚拟文件系统 / ACP Agent cwd）─────────────────────────────────┐   │
│  │  insight.json │ diagnostic_package.json │ cluster_meta.json │ ...         │   │
│  └──────────────────────────────────────────────────────────────────────────┘   │
│                                                                                  │
└──────────────────────────────────────────────────────────────────────────────────┘
```

---

## 9. 告警全自动流程（计划实现）

### 9.1 设计目标

用户在左列添加 Jira Issue Source 时：

1. **前端立即反馈**：Issue Source 卡片出现并显示旋转 Spinner
2. **后端自动处理**：根据告警 ID 从 S3 下载历史报告、诊断文件和 Cluster Meta
3. **自动生成 Sources**：Cluster Meta Source 自动加入左列
4. **自动生成 Artifacts**：Insight Report 和 Anomaly Report Artifact 出现在右列
5. **Spinner 停止**：所有文件就绪，Alert 卡片状态变为 ✓ Ready

### 9.2 Source 状态转换

```
     添加告警
         │
         ▼
   ┌───────────┐    后端处理中         ┌───────────┐
   │  loading   │ ─────────────────── │  loading   │
   │  （转圈）   │                    │  （转圈）   │
   └───────────┘                      └───────────┘
         │                                   │
         │  后端成功                          │  后端失败
         ▼                                   ▼
   ┌───────────┐                       ┌────────────┐
   │   ready   │                       │   error    │
   │   (✓)    │                        │   (⚠)     │
   └───────────┘                       └────────────┘
         │
         │  自动触发：
         │  ├─ 添加 cluster_meta Source
         │  ├─ 添加 Report Artifact
         │  ├─ 添加 Anomaly Artifact
         │  └─ 更新文件树
         ▼
   Workspace 已就绪，可开始对话
```

---

## 10. 用户与认证系统

### 10.1 概述

Investibook 使用轻量级**纯邮件登录**方式。用户通过提供邮箱地址进行认证；后端在首次登录时自动创建用户并返回 Bearer token。所有 Workspace 数据按认证用户隔离（多租户）。

```
浏览器                                    后端（FastAPI / SQLite）
  │                                              │
  │  POST /api/auth/login {email}                │
  │─────────────────────────────────────────────>│
  │                                              │  users: get-or-create by email
  │                                              │  user_sessions: INSERT new token
  │  {token, user: {id, email}}                  │
  │<─────────────────────────────────────────────│
  │                                              │
  │  localStorage.setItem('investibook_token')   │
  │  后续每次请求：                               │
  │    Authorization: Bearer <token>             │
  │─────────────────────────────────────────────>│
  │                                              │  _db_get_user_by_token → user row
```

### 10.2 数据库 Schema（SQLite — `data/db/investibook.db`）

```sql
users                -- id, email, created_at
user_sessions        -- token (PK), user_id, created_at    ← Bearer token 存储
workspaces           -- id, user_id, name, description, is_example, is_shared, created_at, updated_at
workspace_sessions   -- workspace_id (PK), session_id (ACP), updated_at
workspace_sources    -- workspace_id (PK), data (JSON blob)
workspace_artifacts  -- workspace_id (PK), data (JSON blob)
workspace_messages   -- workspace_id (PK), data (JSON blob)
```

### 10.3 Auth API 端点

| 方法 | 路径 | 说明 |
|------|------|------|
| `POST` | `/api/auth/login` | 邮件登录——get-or-create 用户，返回 Bearer token |
| `GET` | `/api/auth/me` | 返回当前用户 `{id, email}` |
| `POST` | `/api/auth/logout` | 从 `user_sessions` 删除 session token |

**客户端辅助函数**：`lib/auth.ts` — `loginWithEmail()`、`authHeaders()`、`getToken()`、`getEmail()`、`clearAuth()`、`fetchWithAuth()`

**`fetchWithAuth` 行为**：所有 API 请求都通过此函数，自动注入 `Authorization: Bearer` 头。当收到 `401 Unauthorized` 响应时，自动清除本地凭据并跳转到 `/login` 页面。

**未来扩展**：`get_current_user` FastAPI 依赖项设计为可替换为 OAuth2/OIDC introspection，无需修改任何端点逻辑。

### 10.4 多租户隔离

所有 Workspace 查询按 `user_id` 过滤。`require_workspace_owner()` 中间件在 Workspace 属于不同用户时返回 **403**。`is_example` Workspace 也按用户隔离。

### 10.5 Workspace 只读共享

Workspace 所有者可以将 Workspace **分享为公开只读链接**。非所有者打开链接后可查看全部对话历史和产物，但无任何写操作权限。

```
所有者开启共享
  └─ PUT /api/workspaces/{id}/share  {is_shared: true}
       → workspaces.is_shared = 1（SQLite）

任意人打开 /workspace/{id}
  └─ GET /api/workspaces/{id}  →  {is_owner: false, is_shared: true, ...}
       → 前端：workspace.isOwner = false
            ChatPanel: readOnly={true}（输入框禁用）
            SourcesPanel: 标题点击禁用，共享控件隐藏
            StudioPanel: 禁止添加/删除 Artifact
```

**共享 API**：

| 方法 | 路径 | 说明 |
|------|------|------|
| `PUT` | `/api/workspaces/{id}/share` | 设置 `is_shared`（`{"is_shared": bool}`） |

**UI 行为**（由 `isOwner` prop 控制）：

| UI 元素 | 所有者 | 非所有者（只读） |
|---------|--------|----------------|
| Workspace 标题 | 点击可编辑 | 只读文本 |
| 共享切换按钮 | 可见（青绿色图标 + 文字） | 隐藏 |
| 复制链接按钮 | 共享开启后显示 | 不适用 |
| "+ Add Sources" | 可见 | 隐藏 |
| Chat 输入框 | 可用 | 禁用（显示只读标识） |

**共享链接格式**：`{origin}/workspace/{workspace_id}`

**客户端辅助函数**：`lib/workspaces.ts` 中的 `setWorkspaceShared(id, isShared)`

---

## 11. 状态管理

### 11.1 WorkspaceProvider（React Context）

```typescript
interface WorkspaceContextType {
  workspaceId: string | undefined;

  // Sources
  sources: Source[];
  addSource: (source) => Source;
  removeSource: (id) => void;
  selectedSourceId: string | null;
  setSelectedSourceId: (id) => void;

  // MCP Connector 激活状态（UI 中哪些连接器已开启）
  activeMcpConnectorIds: string[];
  toggleMcpConnector: (id: string) => void;

  // Artifacts
  artifacts: Artifact[];
  addArtifact: (artifact) => Artifact;
  updateArtifact: (id, updates) => void;
  removeArtifact: (id) => void;
  selectedArtifactId: string | null;
  setSelectedArtifactId: (id) => void;

  // 视图状态
  studioView: 'list' | 'detail';
  setStudioView: (view) => void;

  // 文件查看器
  openFilePath: string | null;
  setOpenFilePath: (path) => void;
}
```

**关键行为**：
- 新 Workspace 自动注入系统 Sources（`createDefaultSources()`：user_meta + **5 个 MCP 连接器**：Grafana Screenshot、Clinic、Ops Portal、O11Y、Jira）
- 挂载时，Sources 和 Artifacts 从后端 API **加载**（`GET /api/workspace/{id}/sources` / `artifacts`）。后端返回空时，用默认数据（或样例 Workspace 使用样例数据）填充
- Sources 和 Artifacts 的变更会**持久化到后端**（`PUT /api/workspace/{id}/sources` / `artifacts`）
- 系统 Sources（`id` 前缀 `default-*` / `mcp-*`）在加载时始终重新注入，防止数据过期
- `activeMcpConnectorIds` 仅存储在 `localStorage`（`workspace_{id}_active_mcp_connectors`），不持久化到后端

**已弃用的系统 Source**：`mcp-devops` 已被 `mcp-ops-portal` 替代；加载时自动过滤掉（`deprecatedMcpIds`）。

### 11.2 后端持久化 Schema

| API 端点 | SQLite 表 | 内容 |
|----------|----------|------|
| `GET/PUT /api/workspace/{id}/sources` | `workspace_sources` | `Source[]` JSON blob |
| `GET/PUT /api/workspace/{id}/artifacts` | `workspace_artifacts` | `Artifact[]` JSON blob |
| `GET/PUT /api/workspace/{id}/messages` | `workspace_messages` | `Message[]` JSON blob |
| `GET/PUT /api/workspace/{id}/session` | `workspace_sessions` | ACP `session_id` 字符串 |

### 11.3 对话历史持久化

对话消息**按 Workspace** 持久化（不按 ACP session），因此在 Pod 重启和 ACP 会话断开后历史仍然保留。

```
ChatPanel 挂载
  └─ loadMessagesFromAPI(workspaceId)
       GET /api/workspace/{id}/messages → Message[]
       （为空时回退到 WELCOME_MESSAGE）

每轮对话结束后
  └─ saveMessagesToAPI(workspaceId, messages)
       PUT /api/workspace/{id}/messages
```

### 11.4 浏览器 localStorage

| 键名 | 内容 |
|------|------|
| `investibook_token` | Bearer 认证 token |
| `investibook_email` | 已登录用户邮箱 |
| `workspace_{id}_active_mcp_connectors` | 该 Workspace 已激活 MCP 连接器 ID 的 JSON 数组（仅 UI 切换状态，不持久化到后端） |

---

## 12. 部署架构

```
              ┌─────────────┐
              │   Ingress    │
              │  (ALB/K8s)  │
              └──────┬───────┘
                     │
          ┌──────────┴──────────┐
          │                     │
    ┌─────┴──────┐        ┌─────┴─────┐
    │ Frontend   │        │ Backend   │
    │ Next.js    │        │ FastAPI   │
    │ :3000      │        │ :8000     │
    └────────────┘        └─────┬─────┘
                                │
                    ┌───────────┴───────────┐
                    │                       │
              ┌─────┴───────┐        ┌──────┴─────┐
              │ ACP Agent   │        │ MCP Server │
              │ （子进程）   │        │ （stdio）  │
              │ OpenCode /  │        │ Resource   │
              │ Claude      │        │ Worker     │
              └─────────────┘        └────────────┘
```

**环境变量配置**：

| 环境变量 | 说明 | 默认值 |
|---------|------|--------|
| `NEXT_PUBLIC_API_URL` | 前端访问后端的 URL | _（空，使用相对路径）_ |
| `ACP_AGENT_TYPE` | Agent 类型 | `opencode` |
| `ACP_AGENT_BIN` | Agent 二进制路径 | _（自动通过 `which` 检测）_ |
| `ACP_MODEL` | LLM 模型 | _（Agent 默认）_ |

**本地开发**：在 `.env.local` 中设置 `NEXT_PUBLIC_API_URL=http://localhost:8000`，前端直连后端（CORS 已允许 localhost:3000/3001）  
**生产环境**：不设置 `NEXT_PUBLIC_API_URL`，前端使用相对路径，由 Ingress 转发

---

## 13. 关键技术选型

| 层次 | 技术 | 用途 |
|------|------|------|
| 前端框架 | Next.js 16（App Router） | 页面路由 + Client Components |
| UI 库 | `@tidbcloud/uikit`（Mantine） | 主要组件库 |
| 补充 UI | Ant Design | 特定场景（Button） |
| 代码编辑器 | Monaco Editor | File Viewer（只读） |
| 图表 | `metrics-chart` | 指标时序图表 + 联动交互 |
| 日期处理 | dayjs（UTC 插件） | 时间格式化 |
| 后端框架 | FastAPI | REST API + SSE |
| Agent 协议 | ACP（Agent Client Protocol） | Agent 通信（stdio JSON-RPC） |
| 工具协议 | MCP（Model Context Protocol） | Agent 工具 |
| 流式传输 | SSE（Server-Sent Events） | 实时事件推送 |
| 认证存储 | `localStorage`（`investibook_token`） | API 调用的 Bearer token |
| 持久化 | SQLite（服务端） | Workspace、Source、Artifact、消息、会话 |

---

## 14. 未来路线图

| 功能 | 优先级 | 说明 |
|------|--------|------|
| Jira Issue Source 自动拉取 | P0 | 添加 Issue 后自动从 S3 下载文件 + Spinner |
| Cluster Meta 自动注入 | P0 | Alert 数据就绪后自动添加为 Source |
| Report Artifact 自动生成 | P0 | 下载完成后自动创建 Report + Anomaly Artifact |
| Source 状态指示器 | P1 | 三态（loading/ready/error）+ UI Spinner |
| Knowledge Source | P1 | 用户可上传运行手册、Wiki、操作指南作为 AI 上下文 |
| Skill Source | P1 | 可复用提示词模板 / 分析骨架，按 Workspace 存储 |
| Studio Action Cards | P1 | 一键生成 Report/Operation |
| OAuth / SSO | P2 | 将纯邮件登录替换为 OAuth2（OIDC）—— `get_current_user` 依赖项已就绪 |
| Git Repo Source | P2 | 接入代码仓库作为上下文 |
| MCP Connector 实时连接 | P2 | 前端可配置 MCP 连接参数 |
| 多 Workspace 协作 | P3 | 团队共享 Workspace |
| 导出功能 | P3 | 将 Report 导出为 PDF/Markdown |
