0.1.11 ai修复与全屏

This commit is contained in:
liaibo
2026-01-10 10:35:21 +08:00
parent e74219c802
commit 0bcdc3e730
55 changed files with 6597 additions and 174 deletions
@@ -0,0 +1,170 @@
# Cloudflare 部署与性能优化方案(全盘)
> 目标:前端可通过 Cloudflare 域名访问;后端(Supabase / LightRAG / ingest_service / Redis 等)通过 Cloudflare Tunnel 或等价方式对外暴露;同时解决当前“页面切换卡顿”的体感问题,并为后续 OCR(MinerU)与 RAG 自动入库留出扩展空间。
## 0. 现状结论(基于当前代码扫描)
### 0.1 当前代码对 CloudflareEdge/Workers/Pages)的直接阻塞点
`wolai-frontend` 内存在大量 Node 内置依赖与本地文件系统读写(`fs/path/process.cwd()`),例如:
- `wolai-frontend/src/lib/mindmap-files.ts``wolai-frontend/src/app/api/mindmap/**`:读写 `public/documents` / `public/mindmaps` 下的本地文件
- `wolai-frontend/src/app/api/documents/{create,duplicate,copy-tree}`:本地文件写入/拷贝
- `wolai-frontend/src/lib/ai/onlineAiConfig.ts`:本地读取配置文件
- 部分 API 还使用 `crypto.randomUUID`
这些在 Cloudflare Pages/Workers 的运行时中**不可用或不推荐**(没有传统 Node 文件系统/进程工作目录概念),因此“把整个 Next.js(含 API Routes)直接部署到 Cloudflare 并保持当前行为”会遇到现实阻碍。
### 0.2 当前“页面切换卡”的高概率根因
在改造前,`/documents/[id]` 会把 `documents.content` 作为 Server Component props 直接带到前端编辑器(BlockNote)。当 `content` 较大时,会显著增加:
- 服务器到浏览器的 RSC Flight payload 体积
- 浏览器端 JSON 解析、反序列化与 hydration 负担
这类卡顿会在“切换不同页面”场景中放大。
> 已落地修复:`/documents/[id]` 不再直接携带 `documents.content`,改为客户端按需请求 `/api/documents/content` 后再加载编辑器内容(见下方“P0 优化已实现”)。
---
## 1. Cloudflare 部署拓扑:推荐的三种路径
### 路径 A(推荐优先):Cloudflare 仅做反代/加速,本地跑完整 Next(含 API)
**适用场景**:希望最少改代码、尽快可用;允许前端与 API 都跑在你本地/服务器(Docker),Cloudflare 只提供域名与隧道入口。
- Cloudflare Tunnel 将 `https://你的域名` 指向本地的 `wolai-frontend`Next server
- `/api/*` 也由同一 Next server 提供(无需跨域)
- Supabase/LightRAG 等服务同样通过 Tunnel 暴露(可用子域名或路径分流)
**优点**
- 几乎不需要重构(保留 `fs`/本地 mindmap 文件体系)
- cookie / session 同源,前端调用 `/api` 最简单
**缺点**
- 首屏与静态资源仍取决于你本地带宽/延迟(可通过 Cloudflare 缓存静态资源缓解)
- 需要你保证本地服务稳定在线
### 路径 BCloudflare 部署“纯静态前端”(SPA/静态 Next),API 全部回源到本地
**适用场景**:希望前端 CDN 化(加载快),后端服务仍由你本地/服务器提供;愿意做一定的前后端解耦与配置改造。
关键改造点:
- 前端不能依赖 Next Server Components 的“服务器取数”,而应改为客户端请求后端 API
- Next 的 `app/api/*` 要迁移到后端服务(或保留在本地 Next,但需要确保 Cloudflare Pages 的 `/api` 反代到本地)
- 所有本地文件读写(mindmap 文件、copy-tree)必须移动到“后端服务”中执行
**优点**
- 前端体验更好(静态资源全球 CDN)
- 后端仍可保留现有 Node 能力(fs、pdf 处理、脚本等)
**缺点**
- 需要统一“API Base URL / 反代规则 / CORS / cookie 域”
- 需要规划“哪些 API 放在本地 Next,哪些独立成后端服务”
### 路径 CCloudflare 运行 NextWorkers/Pages Functions+ 后端回源
**适用场景**:希望尽可能多逻辑在 Cloudflare 边缘,但愿意严格约束 Node 能力,重构较大。
现实结论:以当前代码结构(大量 `fs/path/process.cwd()`)来看,除非先做较大改造,否则不建议直接走该路径。
---
## 2. 性能优化路线图(按优先级)
### P0(已实现,立刻收益):文档内容按需加载,减小路由切换负担
已改动:
- 新增 `GET /api/documents/content?documentId=...`:仅返回 `documents.content`
- `/documents/[id]` 不再 `select content`,避免 RSC 携带大字段
- 编辑器内容在客户端加载:`DocumentContent``initialContent==null` 时拉取内容并展示“页面内容加载中...”
- `BlockNoteEditor``normalizedInitialContent` 变化时重新初始化(只会从 `null -> 实际内容` 触发一次)
预期收益:
- 切换页面时,RSC payload 更小、解析更快
- 为 Cloudflare 反代/跨网访问(带宽较差)场景奠定基础
### P1(强烈推荐,低风险高收益):侧边栏与文件树性能
1) 文件树虚拟列表
- `FileTree` 当前对 `rows` 直接 `map` 渲染,文档/附件数量上来后会明显卡顿
- 已有 `@tanstack/react-virtual` 用例(`PrivateTree`),可复用同一技术栈把 `FileTree` 也做虚拟化
2) `assetsByDoc` 构建从 O(n^2) 优化为 O(n)
- 当前实现每次 push 前会 `some()` 去重(n 较大时会变慢)
- 建议改为 `Map<docId, Map<assetKey, asset>>` 一次遍历构建
3) `/api/sidebar` 数据裁剪与拆分
- `fetchSidebarDataset``media_assets` 使用 `select("*")`,字段过多会拉大 payload
- 建议改为只取侧边栏需要字段;并进一步拆分:
- 文档树(documents
- 垃圾桶(trashed docs/assets
- 附件列表(只取当前展开节点/或按 docId 批量懒加载)
### P2(中期):文档内容存储与传输优化(大文档体验)
1) 内容分片/分页加载(可选)
-`documents.content` 进行“块级分页”(例如按顶层 block 分块),在编辑器侧逐步加载
2) 只拉取必要字段
- 页面切换只拉 metatitle/updated_at/options/stats),内容按需加载(已完成)
- backlinks / history 等面板可延迟加载或切换时懒加载
3) 缓存与压缩策略(配合 Cloudflare
- 对静态资源启用强缓存(Cloudflare 默认可做)
- 对“用户私有”接口设置 `Cache-Control: private`,避免被共享缓存污染
- 对“公开内容(public pages)”可用 `s-maxage` + `stale-while-revalidate`
---
## 3. Cloudflare 反代/穿透下的关键工程点(必须规划)
### 3.1 统一 API 入口与路径分流
建议以同一域名提供:
- `https://mnote.example.com/`:前端
- `https://mnote.example.com/api/*`:后端 API(本地 Next 或独立服务)
- `https://mnote.example.com/supabase/*`:如需(或直接使用 supabase 子域名)
- `https://mnote.example.com/lightrag/*`LightRAG 7777(建议仅内部/鉴权后暴露)
这样可以最大化减少 CORS 与 cookie 域问题。
### 3.2 上传链路必须“直传”
Cloudflare/反代链路对大文件上传很敏感:
- 建议前端改为“拿签名 URL 后直传到 Supabase Storage”(你已有 `/api/media/signed-url`
- 避免通过前端服务器中转大文件(会受 CF/反代限制、也会拖慢)
### 3.3 本地文件系统能力的迁移策略
当前 mindmap 文件存储在 `public/documents/**` 本地目录,这对 Cloudflare 部署不友好。
两条路:
- 保留“本地 Node 后端”(路径 A / B),让所有文件读写都在本地后端做
- 或迁移到 Supabase Storage(推荐长期):mindmap 变成一个可版本化的对象文件(支持软删/回收站/延迟清理)
---
## 4. 观测与量化(否则很难证明“不卡了”)
建议先落地最小可用的观测:
1) 前端导航耗时
-`router.push` 前后打点(`performance.mark`),记录到 console 或上报
2) 关键 API 延迟
- `/api/sidebar``/api/documents/content``/api/search/*`:记录开始/结束与 payload 大小
3) 体感指标
- 首次可交互(TTI
- 文档打开到“编辑器可输入”的时间
---
## 5. 下一步执行清单(建议按顺序)
1) 选择 Cloudflare 部署路径(A/B/C
2) P1`FileTree` 虚拟化 + `assetsByDoc` O(n) 化 + `/api/sidebar` select 裁剪
3) P2:公开页面缓存策略、上传直传、mindmap 存储迁移方案
+7
View File
@@ -110,6 +110,9 @@
**要做**
- [x] 拖拽默认移动;按修饰键(`Alt`)为复制(浏览器层面 dropEffect 已设置;Alt-copy 建议人工补测一次)
- [x] 起拖行在选择集中 → 拖整个选择集;否则仅拖当前行并先切为单选
- [ ] 拖拽悬停反馈(drop feedback):
- [ ] 悬停到“收起的 doc(文件夹)行”时,仅该行变灰
- [ ] 悬停到“展开的 doc(文件夹)行”时,该 doc 的可见子节点范围一起变灰(类似 VSCode Explorer
- [x] drop 目标:
- [x] drop 到 doc 行:贴入其下
- [x] drop 到 index/asset 行:等同贴入其所属 doc
@@ -134,6 +137,9 @@
- [ ] 复制 asset:生成新的存储对象(不是引用),新文件可独立下载(需要至少 1 个真实附件用例)
- [x] 拖拽移动 doc:已通过页面拖拽触发 `/api/documents/move` 并验证 200
- [x] 从系统拖拽文件到文件树:落点为目标页面(doc 行 / index 行 / asset 行),触发 `/api/media/upload` 上传并作为“真实文件”附件挂到该页面下
- [ ] 拖拽悬停反馈:收起 doc 行仅该行变灰;展开 doc 行则其可见范围一起变灰
- [ ] 从系统拖拽单个文件:只创建 1 份附件记录(不应出现两个同名文件)
- [ ] 从系统拖拽文件到“当前打开的页面”:主编辑区自动插入 1 个附件/媒体块(可在页面正文中看到)
- [ ] Alt+拖拽复制:逻辑已接入(`event.altKey`),需要人工补测一次(MCP 暂无法“按住 Alt 拖拽”)
---
@@ -158,3 +164,4 @@
**手工验收**
- [x] 多选后右键任一已选行 → 点击“删除到垃圾桶”,应删除整个选择集(而不是仅最后右键项)
- [ ] 多选包含「页面 + 附件」时:无论最后右键落在页面行还是附件行,“删除”都应按选择集执行
+144
View File
@@ -0,0 +1,144 @@
# 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. 刷新页面后仍存在(说明落盘成功)。
+294
View File
@@ -0,0 +1,294 @@
# Mindmap AI v2 实施计划(文档大纲 / 小结带引用 / 搜索补全)
> 目标:把当前“仅能对话并返回 Markdown 列表”的 AI,升级为**可读可用**的“文档驱动思维导图”能力:
> 1) PDF/Word/PPT 按目录与大纲生成思维导图,并且节点可跳转到对应页/幻灯片;
> 2) 基于文档内容生成小结/知识点,并附带可点击引用(页码/链接);
> 3) 支持在节点上“搜索/查询后补完内容”(RAG + 可选联网检索)。
---
## 0. 现状与问题
### 0.1 当前实现(代码落点)
- `wolai-frontend/src/components/editor/blocks/MindmapSidebar.tsx`:AI 面板目前是“本地 Ollama /api/chat 流式输出 + Markdown 解析为节点”。
- `services/ingest_service`:已具备 **Supabase -> MinerU -> LightRAG** 的自动入库链路(用于全文检索/RAG)。
- `services/rag_gateway`:封装了对 LightRAG 的 HTTP 调用(`/query``/query/data`),可作为统一 RAG 网关。
- `wolai-frontend/src/app/onlyoffice/page.tsx`OnlyOffice 文档查看/编辑入口(目前用于 docx/pptx/xlsx 等)。
- `wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`:节点超链接通过 `SET_NODE_HYPERLINK` 直接写入 URL。
### 0.2 为什么“离想象差很远”
当前 AI 只有“生成文本 -> 解析成节点”的能力,缺少:
- **与文档绑定**:没有把 PDF/Word/PPT 的结构(目录/标题层级)变成导图;
- **可验证的引用**:没有“这句话来自第几页/哪一段”,因此难以沉浸式阅读与回溯;
- **补全的检索依据**:没有把“补完节点”变成“基于检索结果 + 引用”输出,容易胡编;
- **工程化形态**:没有任务、进度、缓存、失败重试、可复用数据结构。
---
## 1. 用户故事与验收标准(以你的测试文件为准)
### 1.1 文档按目录/大纲生成导图(PDF/Word/PPT
**用户故事**
- 我上传(或选中)一个文档(PDF/Word/PPT),点击“按大纲生成思维导图”,自动生成多级节点。
- 我点击任意节点,可以跳转到该文档对应页(PDF)或对应位置(Word/PPT 至少能定位页/提示页码,理想是直接跳转)。
**验收**
-`wolai-frontend/test/卤化反应原理_1-9.pdf`
- 能生成至少 2 级结构(中心主题 -> 章节 -> 小节)。
- 至少章节级节点带可点击引用(页码/链接)。
- 点击节点后打开的 URL 包含 `#page=<n>`PDF 以浏览器内建查看器为准)。
### 1.2 小结/知识点生成(带链接引用)
**用户故事**
- 我选中“生成小结”,AI 输出“关键结论/反应机理/注意点”,每条结论附带引用(页码链接或文档片段来源)。
**验收**
-`wolai-frontend/test/卤化反应原理测试.pdf`
- 生成不少于 N(建议 8)条要点;
- 每条要点至少 1 个引用(页码/链接);
- 引用能点击打开对应 PDF 页。
### 1.3 搜索/查询后补完节点内容
**用户故事**
- 我选中一个/多个节点,输入“补完方向/问题”,AI 基于检索结果补充子节点或备注,附带引用。
**验收**
- 对同一 PDF 文档:
- “补完”后的内容至少包含 3 个子节点或 1 段结构化备注;
- 结论带引用(页码/链接);
- 不允许“无引用的长篇自由发挥”(默认强制引用)。
---
## 2. 总体方案(推荐架构)
核心原则:**先结构化,再生成**;**先检索证据,再写结论**;**引用是第一公民**。
### 2.1 三层能力拆分
1) **文档解析层(Document -> Outline/Chunks**
- 输出:`DocOutline`(层级标题 + 页码/位置)与 `DocChunks`(可引用文本块)。
2) **RAG/生成层(Outline/Chunks -> Mindmap/Summary/Expansion**
- 输出:思维导图节点树(含引用)、小结节点(含引用)、补全子节点(含引用)。
3) **渲染/交互层(Mindmap UI**
- 节点点击:打开引用的文档页/位置;支持“查看引用”“展开更多证据”。
### 2.2 数据来源优先级(PDF
按可靠性排序:
1) PDF 自带书签/目录(`pypdf` outline)→ **最靠谱**(本次 `卤化反应原理_1-9.pdf` 没有 outline
2) MinerU 解析结果(建议开启 `return_content_list`/`return_middle_json`)→ 可拿到更结构化的块,并可能带页信息
3) `pypdf` 分页提取文本 + 标题检测(规则 + LLM 辅助)→ 兜底方案
---
## 3. 关键数据结构(建议新增/统一)
### 3.1 引用结构 `NodeRef`
用于节点的“可点击跳转”和“可追溯引用”:
```ts
export type NodeRef = {
kind: "pdf" | "docx" | "pptx" | "url";
assetId?: string; // 优先用 assetId,避免 URL 过期
fileUrl?: string; // public URL 或 signed URL(兜底)
page?: number; // PDF 页码(1-based
slide?: number; // PPT 页码/幻灯片序号(1-based
title?: string; // 引用标题(例如“2.1 卤化机理”)
snippet?: string; // 可选:引用片段
};
```
### 3.2 节点存储方式
- **短链**(推荐):`node.data.refs: NodeRef[]`(自定义字段)
- **展示用超链接**:仍使用 `SET_NODE_HYPERLINK` 写入一个可点击 URL(例如 PDF `...#page=3`
- **引用详情**:写入 `node.data.note``node.data.data.note`(保持兼容)为 Markdown
- `- [p3] 证据片段...`
- `- [p5] ...`
说明:simple-mind-map 对 `data` 的自定义字段容忍度较高,但要确保序列化/反序列化后不丢字段。
---
## 4. 具体功能方案
### 4.1 “按目录/大纲生成思维导图”
#### 4.1.1 API(建议新增)
新增 Next Route(前端同域,避免跨域/鉴权麻烦):
- `POST /api/mindmap-ai/outline-to-mindmap`
- 入参:`{ assetId, documentId?, prefer: "bookmark" | "mineru" | "heuristic" }`
- 出参:`{ mindmapData, outline, refsSummary }`
后台实现可以优先走 `services/ingest_service` 或直接复用其逻辑(后续可沉到 `wolai-backend`)。
#### 4.1.2 解析策略(对测试 PDF 友好)
因为 `卤化反应原理_1-9.pdf` 没有书签:
1) 使用 `pypdf` 逐页提取文本(已有代码可参考 `services/ingest_service/app/services/auto_indexer.py`);
2) 对每页文本做标题候选抽取(规则:编号标题如 `1.` `1.1` `(一)` 等 + 行长/标点密度);
3) 用 LLM(可走 Ollama)把候选标题整理为层级结构,并返回 `{title, level, page}`
4) 转换为 mindmap:中心主题=文件名/第一页大标题,子节点=章节;每个章节节点写入 hyperlink `fileUrl#page=<page>`
#### 4.1.3 Word/PPT
阶段 1 先保证:
- Word/PPT 也能“生成大纲导图”,但跳转能力允许降级:
- 若 OnlyOffice 支持跳转 API:实现真实跳转;
- 若不支持:点击节点打开文档,并弹出“建议跳转页码/幻灯片序号”的提示(至少可用)。
(后续再把 Word/PPT 的“位置”提升为可跳转锚点)
---
### 4.2 “小结/知识点生成(带引用)”
#### 4.2.1 证据来源:优先 LightRAG,其次本地分页文本
优先走 `services/rag_gateway`
- `POST /rag/query`:拿到 `response + references`LightRAG 会返回引用列表)
- `POST /rag/graph`:拿结构化分块/引用(用于“点开看证据/更多片段”)
如果 LightRAG 返回引用信息不足以映射到页码,需要补齐“页码映射”:
- 方案 A(推荐):在入库时把“每页”作为 chunk,并把 `file_source + page` 写入可回传字段(需要改 ingest_service 装饰文本或分块入库方式)
- 方案 B(兜底):在本地用 `pypdf` 重新做“分页文本”,对引用片段做模糊匹配定位页码
#### 4.2.2 输出形态
在 Mindmap 里提供两种落地方式:
- 生成到“备注”(适合长文本 + 引用列表)
- 生成到“子节点”(每条要点一个子节点,子节点携带 `refs` 和 hyperlink
默认要求:**每条要点至少 1 个引用**(无引用则标记为“待核验”,并提示用户继续检索)。
---
### 4.3 “搜索/查询后补完节点内容”
#### 4.3.1 两种检索源(可配置)
1) **本地文档库检索**LightRAG(默认)
2) **联网检索**SearxNG(本仓库已存在 Docker 服务,可直接用)
##### 4.3.1.1 SearxNG 接入约定(基于仓库现状)
仓库已存在 `services/searxng-docker/.env`,其中包含:
- `SEARXNG_BASE_URL=http://127.0.0.1:8889`
- `SEARXNG_API_TOKEN=...`
建议接入方式:
- 前端**不要**直连 searxng(避免 token 暴露),通过 Next Route 代理:
- `POST /api/search/searxng`
- 入参:`{ q: string, count?: number, lang?: string }`
- 出参:`{ results: Array<{ title: string, url: string, snippet?: string, engine?: string }>} `
SearxNG 查询接口(推荐 JSON):
- `GET ${SEARXNG_BASE_URL}/search?q=<query>&format=json&language=zh-CN&categories=general&safesearch=1`
鉴权策略(需要实际跑通后确定,做成可配置):
- 方案 A:不加鉴权(本地内网服务)
- 方案 B:携带 token(例如 `X-API-Key` / `Authorization: Bearer` 之一;以你当前 searxng 配置为准)
> 注意:SearxNG 返回结果字段不同版本略有差异,后端代理层要做一次“结果归一化”和去重(按 url)。
#### 4.3.2 交互与输出
- 输入:用户选中节点 + “补完问题/方向”
- 系统 prompt 固定:强制输出结构化(Markdown 列表)+ 引用
- 输出策略:
- 子节点补全:把回答拆成 3~8 个子节点追加
- 备注补全:写入 note,并附引用列表
---
## 5. 前端 UI 改造建议(MindmapSidebar 的 AI 面板)
把当前“模型/地址/系统提示”改为“面向功能的工作流”,保留“高级设置”折叠:
1) **从文档生成**
- 选择文档资产(assetId)+ 生成按钮 + 进度(解析中/生成中/完成)
2) **生成小结**
- 可选:作用范围(整篇/选中节点对应章节)+ 要点数量 + 输出到(子节点/备注)
3) **补完节点**
- 输入框 + 检索源选择 + 输出到(子节点/备注)
同时在节点右键/工具栏增加:
- “打开引用”
- “查看引用(弹窗列出页码/片段)”
---
## 6. 工程落地步骤(里程碑)
> 建议按“先可用,再做强”的顺序推进。
### M1(1~2 天):PDF 大纲导图(可跳页)
- [ ] 新增 `/api/mindmap-ai/outline-to-mindmap`(仅支持 PDF
- [ ] 实现 “pypdf 分页文本 + 标题候选 + LLM 整理层级”
- [ ] MindmapSidebar 增加“从 PDF 生成导图”入口(基于 assetId)
- [ ] 节点写入 hyperlink`publicFileUrl#page=<n>`
- [ ] pw-tests:导入 `卤化反应原理_1-9.pdf`,断言生成节点数与 `#page=` 链接存在,并截图
### M2(2~3 天):小结生成(强制引用)
- [ ] 接入 `services/rag_gateway``/rag/query`(前端通过 Next Route 代理)
- [ ] 让小结输出“要点 + 引用”
- [ ] 引用映射到 `#page=`(先用本地分页匹配兜底)
- [ ] pw-tests:对 `卤化反应原理测试.pdf` 生成 N 条小结并截图
### M3(2~4 天):搜索补全节点(RAG)
- [ ] AI 面板增加“补完节点”模式
- [ ] 选中节点作为上下文,拼接检索 query:LightRAG(文档内)+ SearxNG(联网)
- [ ] 新增 `POST /api/search/searxng` 作为代理(隐藏 token + 统一返回格式)
- [ ] 将 searxng 的 `title/url/snippet` 作为“外部证据”,与 LightRAG 引用一起喂给模型生成
- [ ] 防胡编:无引用则提示“需要更多证据/换关键词”
- [ ] pw-tests:选中某节点补完,校验新增子节点与引用
### M4(可选):Word/PPT 真跳转
- [ ] 调研 OnlyOffice 是否支持跳页/跳幻灯片 API(不支持则维持降级)
- [ ] 若支持:在 `/onlyoffice` 页面接收 `page/slide` 参数并调用 DocsAPI 跳转
---
## 7. 风险与对策
- **PDF 无书签(当前测试文件就是)**:必须做“文本标题抽取 + LLM 结构化”的兜底。
- **页码引用难**:短期先用“本地分页匹配”;中期把“页级分块”纳入入库,引用直接带页码。
- **URL 过期/权限**:优先存 `assetId`,点击时再换取可用 URL(必要时扩展 `/api/media/signed-url` 支持 assetId)。
- **性能**:解析/生成尽量放到后端(Next Route 或 wolai-backend),前端只跑轻量 UI;长任务使用 job 表/轮询。
---
## 8. 与 BlockNote 的集成点(后续)
- Slash Menu 可加入口(例如 `/mind ai``/mind from pdf`),插入/更新块可用 BlockNote 的 `insertOrUpdateBlockForSlashMenu``editor.updateBlock`(见 BlockNote 官方文档的 Suggestion Menus 示例)。