# Mindmap AI Agent API 设计（读写思维导图，类似 CLI 工具）

## 目标

让 AI 不再“只输出一段文本”，而是像 CLI 一样：

1. **读**：获取当前页面中某个 Mindmap 的完整数据（包含节点、链接、引用）。
2. **改**：以结构化的 `ops`（操作序列）形式修改导图（新增/删除/改名/加链接/加引用/加备注）。
3. **可追溯**：每次 AI 修改都能记录“为什么这样改/引用来自哪里”（refs 作为第一公民）。
4. **安全**：必须校验登录态 + document 权限；只能操作该 document 下的 mindmap 文件。

## 现状（简述）

- 目前 Mindmap 的保存/加载已经存在（按文件树保存，支持同页多个独立 mindmap）。
- AI v2 的 `/api/mindmap-ai/outline-to-mindmap` 能把 PDF 转成 mindmapData（节点带 `hyperlink` 与 `refs`）。
- 仍缺少：AI 直接对“已有导图”做增量修改的能力（例如“补完此节点”“根据搜索扩展子节点”）。

## 设计原则

- **客户端只负责 UI/交互**：选择节点、展示进度、应用结果。
- **服务端负责权限与落盘**：验证用户、读写 mindmap JSON、生成 ops、应用 ops。
- **AI 输出必须是 ops**：避免 AI 直接吐一棵全量树造成覆盖/丢数据；也利于审计与回滚。
- **引用强制**：默认每个新增节点都要给 `refs`（至少页码/URL），没有引用则标记为 `待核验`。

## 核心数据结构

### 1) Mindmap 节点引用 `NodeRef`（已在实现中使用）

```ts
export type NodeRef = {
  kind: "pdf" | "docx" | "pptx" | "url";
  assetId?: string;
  fileUrl?: string;
  page?: number;   // 1-based
  slide?: number;  // 1-based
  title?: string;
  snippet?: string;
};
```

### 2) 操作协议 `MindmapOp`

> 说明：这里的 `uid` 指 simple-mind-map 节点 `data.uid`（当前实现已使用）。

```ts
export type MindmapOp =
  | { op: "addChild"; parentUid: string; node: { uid?: string; text: string; hyperlink?: string; refs?: NodeRef[]; note?: string } }
  | { op: "addSiblingAfter"; targetUid: string; node: { uid?: string; text: string; hyperlink?: string; refs?: NodeRef[]; note?: string } }
  | { op: "updateText"; uid: string; text: string }
  | { op: "setHyperlink"; uid: string; hyperlink: string | null }
  | { op: "setRefs"; uid: string; refs: NodeRef[] }
  | { op: "appendNote"; uid: string; markdown: string }
  | { op: "deleteNode"; uid: string };
```

### 3) 服务端应用 ops（纯 JSON 层）

- 以“树遍历 + uid 索引”应用变更，保证：
  - 不依赖浏览器端实例；
  - 不依赖 simple-mind-map 内部状态；
  - 可在服务端记录变更日志。

## API 设计（建议新增）

### 1) 读取导图

`GET /api/mindmap/[documentId]/[mindmapId]`

返回：

```json
{
  "mindmapId": "xxx",
  "documentId": "xxx",
  "data": { "data": { "text": "...", "uid": "..." }, "children": [] },
  "updatedAt": "2026-01-09T00:00:00.000Z"
}
```

### 2) 应用 ops（增量修改 + 自动保存）

`POST /api/mindmap/[documentId]/[mindmapId]/ops`

入参：

```json
{
  "ops": [ { "op": "addChild", "parentUid": "...", "node": { "text": "...", "hyperlink": "...", "refs": [] } } ],
  "actor": { "kind": "ai", "provider": "online", "model": "gemini-2.5-flash" },
  "reason": "补完该节点：……（可选）"
}
```

出参：

```json
{
  "ok": true,
  "applied": 7,
  "data": { "data": { "text": "...", "uid": "..." }, "children": [] }
}
```

### 3) AI 补完（服务端编排：检索 → 生成 ops → 应用 ops）

`POST /api/mindmap-ai/expand-node`

入参（建议）：

```json
{
  "documentId": "xxx",
  "mindmapId": "xxx",
  "targetUid": "xxx",
  "instruction": "请补完该节点，要求每条必须带引用",
  "sources": { "rag": true, "searxng": true }
}
```

输出：

```json
{
  "ok": true,
  "providerUsed": "online",
  "ops": [ ... ],
  "applied": 7
}
```

> 注意：该接口内部会调用 `/api/mindmap/[...]/ops` 做落盘，避免重复代码。

## 与 SearxNG / RAG 的关系

- 对“搜索补完”：服务端先走检索（LightRAG + 可选 SearxNG），把证据（标题/URL/snippet）整理成短上下文，再让在线 AI 输出 `MindmapOp[]`。
- 必须要求：每个新增节点都附带 refs（url 或 pdf page），否则标记为“待核验”并限制输出量。

## 验收建议（后续 pw-tests）

1. 选中某节点，点击“AI 补完”，等待生成。
2. 导图新增 ≥ 3 个子节点。
3. 每个新增节点包含可点击 `hyperlink` 或 `refs`。
4. 刷新页面后仍存在（说明落盘成功）。

