From 75258db0a2cc53248f7395738f55a3756985f7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E5=87=AF?= Date: Fri, 12 Jun 2026 00:38:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai-writing-右侧编辑器-滑词与段落菜单.md | 228 --------------- .../docs/ai-writing-用户自带大纲-测试用例.md | 274 ------------------ 2 files changed, 502 deletions(-) delete mode 100644 frontend-web/docs/ai-writing-右侧编辑器-滑词与段落菜单.md delete mode 100644 frontend-web/docs/ai-writing-用户自带大纲-测试用例.md diff --git a/frontend-web/docs/ai-writing-右侧编辑器-滑词与段落菜单.md b/frontend-web/docs/ai-writing-右侧编辑器-滑词与段落菜单.md deleted file mode 100644 index ea9c4d50..00000000 --- a/frontend-web/docs/ai-writing-右侧编辑器-滑词与段落菜单.md +++ /dev/null @@ -1,228 +0,0 @@ -# AI 写作 · 右侧文本编辑器(滑词 / 段落菜单) - -> 说明 AI 写作页、开放画布中文稿右侧编辑器的实现方式:**编辑器本体基于 Tiptap v3**;选中文本浮层(「滑词」)与段落悬停按钮为**自研 React 组件**,未使用 `@tiptap/extension-bubble-menu` 官方 BubbleMenu。 - ---- - -## 一、功能概览 - -| 交互 | 用户感知 | 组件 | 触发方式 | -|------|----------|------|----------| -| **滑词** | 拖选一段文字后,选区上方出现工具条(AI 改写、加粗、斜体、下划线、H1、复制) | `WritingBubbleMenu` | Tiptap `selectionUpdate` + 浏览器 `window.getSelection()` | -| **段落菜单** | 鼠标移入某段/标题/引用块时,段落左侧出现 ✨ 按钮,点击可 AI 改写整段 | `WritingParagraphMenu` | 编辑器 DOM `mousemove` + ProseMirror `posAtCoords` | -| **AI 改写面板** | 点击「AI 改写」后弹出润色/扩写等操作与预览 | `WritingAiMenu` / `WritingRewritePreview` | 由 `TextArtifactRenderer` + `useWritingRewrite` 串联 | - -画布线程页(`/page/canvas/:thread_id`)与 AI 写作页(`/page/workspace/ai-writing`)右侧草稿**共用同一套** `TextArtifactRenderer` 与菜单组件。 - ---- - -## 二、在 AI 写作中的挂载路径 - -```mermaid -flowchart TB - AIWritingPage --> CanvasResizableLayout - CanvasResizableLayout --> AIWritingDraftPanel - AIWritingDraftPanel --> DraftStreamingPreview - AIWritingDraftPanel --> TextArtifactRenderer - DraftStreamingPreview["DraftStreamingPreview
(流式只读 Markdown)"] - TextArtifactRenderer --> WritingBubbleMenu - TextArtifactRenderer --> WritingParagraphMenu - TextArtifactRenderer --> TiptapEditor - TextArtifactRenderer --> WritingAiMenu -``` - -| 阶段 | 右侧展示 | 滑词 / 段落菜单 | -|------|----------|-----------------| -| 草稿流式生成中(`writing_draft` / `revising` 且有 section 流) | `DraftStreamingPreview`(ReactMarkdown 只读) | **不可用** | -| 成稿可编辑(`isDraftEditable === true`) | `TextArtifactRenderer` + Tiptap | **可用** | -| 流式结束但尚未 editable | `TextArtifactRenderer` 且 `isStreaming={true}` | **不可用**(`editable: false`) | - -相关状态由 [CanvasUIContext.tsx](../src/open-canvas/contexts/CanvasUIContext.tsx) 的 `isDraftStreaming`、`isDraftEditable` 控制;注释中「禁用编辑/润色/滑词」即指生成完成前不进入可编辑渲染器。 - -入口文件: - -- [AIWritingDraftPanel.tsx](../src/open-canvas/components/ai-writing/AIWritingDraftPanel.tsx) — 决定流式预览 vs `TextArtifactRenderer` -- [TextArtifactRenderer.tsx](../src/open-canvas/components/artifact/TextArtifactRenderer.tsx) — 挂载编辑器与两套浮层菜单 - ---- - -## 三、技术栈:编辑器 vs 浮层 - -### 3.1 Tiptap 编辑器(有使用) - -| 包 | 用途 | -|----|------| -| `@tiptap/react` | `useEditor`、`EditorContent` | -| `@tiptap/starter-kit` | 段落、标题、加粗、斜体、下划线等 | -| `@tiptap/extension-placeholder` | 空文档占位符 | -| `tiptap-markdown` | Markdown ↔ 文档互转 | -| 自研 `AiHighlightExtension` | AI 改写前高亮选区(ProseMirror Decoration) | - -扩展注册见 [TiptapExtensions.ts](../src/open-canvas/editor/TiptapExtensions.ts);实例创建见 [useWritingEditor.ts](../src/open-canvas/hooks/useWritingEditor.ts)。 - -### 3.2 浮层菜单(自研,非 Tiptap 官方插件) - -| 包 | 是否用于滑词/段落菜单 | -|----|----------------------| -| `@tiptap/extension-bubble-menu` | **未使用**(`package.json` 中有依赖,代码未 import) | -| `@tiptap/react` 的 `` | **未使用** | - -两套菜单均采用: - -1. 监听 Tiptap / DOM 事件更新位置与选区; -2. `createPortal(..., document.body)` 挂到 `document.body`,避免被右侧滚动容器 `overflow` 裁切; -3. `position: fixed` + `getBoundingClientRect()` 定位。 - -命名上的 `WritingBubbleMenu` 仅表示「气泡式选区工具条」,实现上与 Tiptap 官方 Bubble Menu 扩展无关。 - ---- - -## 四、核心文件索引 - -| 文件 | 职责 | -|------|------| -| [TiptapEditor.tsx](../src/open-canvas/editor/TiptapEditor.tsx) | `EditorContent` 渲染容器 | -| [TiptapExtensions.ts](../src/open-canvas/editor/TiptapExtensions.ts) | StarterKit、Placeholder、Markdown、AiHighlight | -| [AiHighlightExtension.ts](../src/open-canvas/editor/AiHighlightExtension.ts) | `setAiHighlight` / `clearAiHighlight` 命令 | -| [useWritingEditor.ts](../src/open-canvas/hooks/useWritingEditor.ts) | 创建 editor、同步外部 markdown、editable | -| [WritingBubbleMenu.tsx](../src/open-canvas/components/artifact/WritingBubbleMenu.tsx) | **滑词** 选区浮层 | -| [WritingParagraphMenu.tsx](../src/open-canvas/components/artifact/WritingParagraphMenu.tsx) | **段落** 左侧 ✨ 按钮 | -| [WritingAiMenu.tsx](../src/open-canvas/components/artifact/WritingAiMenu.tsx) | AI 改写操作菜单 | -| [WritingRewritePreview.tsx](../src/open-canvas/components/artifact/WritingRewritePreview.tsx) | 改写结果预览 | -| [useWritingRewrite.ts](../src/open-canvas/hooks/useWritingRewrite.ts) | 调用润色 API、写回编辑器 | -| [apply-rewrite.ts](../src/open-canvas/utils/apply-rewrite.ts) | 将改写结果应用到 ProseMirror 选区 | -| [CanvasUIContext.tsx](../src/open-canvas/contexts/CanvasUIContext.tsx) | `writingSelection` 全局状态 | -| [selection.ts](../src/open-canvas/types/selection.ts) | `WritingSelection` 类型定义 | - ---- - -## 五、滑词:`WritingBubbleMenu` - -### 5.1 行为 - -- 用户在编辑器内拖选非空文本时显示浮层;选区为空或位于 `codeBlock` 内时隐藏。 -- 浮层按钮:`AI 改写`、`B` / `I` / `U`、`H1`、复制。 -- 按钮 `onMouseDown` 使用 `e.preventDefault()`,避免点击时编辑器失焦导致选区丢失。 - -### 5.2 实现要点 - -```text -editor.on("selectionUpdate") - → 读取 editor.state.selection (from, to, empty) - → window.getSelection() + Range.getBoundingClientRect() 算屏幕坐标 - → setWritingSelection({ from, to, text }) 写入 CanvasUIContext - → createPortal 渲染工具条,useLayoutEffect 做贴边 / 上方空间不足时翻到选区下方 -``` - -关键代码位置:[WritingBubbleMenu.tsx](../src/open-canvas/components/artifact/WritingBubbleMenu.tsx) 中 `useEffect` 监听 `selectionUpdate` 与 `blur`。 - -点击「AI 改写」时: - -1. `editor.commands.setAiHighlight(from, to)` 高亮选区; -2. 调用父组件传入的 `onAiRewrite()`,由 `TextArtifactRenderer` 打开 `WritingAiMenu`。 - ---- - -## 六、段落菜单:`WritingParagraphMenu` - -### 6.1 行为 - -- 鼠标在编辑器内移动时,检测光标下的**块级节点**; -- 仅对 `paragraph`、`heading`、`blockquote` 且文本非空时,在块左侧显示 ✨ 按钮; -- 鼠标离开编辑器或移出可改写块后,延迟约 **380ms** 隐藏(`HIDE_DELAY`),并在按钮与正文之间留有 hover 桥接区,避免移向按钮时闪烁消失。 - -### 6.2 实现要点 - -```text -editor.view.dom mousemove - → editor.view.posAtCoords({ left, top }) - → doc.resolve(pos).before(depth) 定位块起点 - → editor.view.nodeDOM(nodeStart) 取 DOM 矩形 - → createPortal 固定定位按钮在块左侧 (BTN_SIZE=24, BTN_GAP=6) -``` - -点击 ✨ 时: - -1. `setTextSelection` 选中整段 ProseMirror 范围(`nodeFrom` ~ `nodeTo`); -2. `setAiHighlight` + `setWritingSelection`; -3. 触发与滑词相同的 `onAiRewrite()` 流程。 - -关键代码位置:[WritingParagraphMenu.tsx](../src/open-canvas/components/artifact/WritingParagraphMenu.tsx)。 - ---- - -## 七、AI 改写数据流 - -```mermaid -sequenceDiagram - participant User - participant Menu as BubbleMenu / ParagraphMenu - participant Ctx as CanvasUIContext - participant Renderer as TextArtifactRenderer - participant API as rewrite API - - User->>Menu: 选中文本或点段落 ✨ - Menu->>Ctx: setWritingSelection - Menu->>Renderer: onAiRewrite() - Renderer->>Renderer: phase = menu, WritingAiMenu - User->>Renderer: 选择润色/扩写等 - Renderer->>API: useWritingRewrite.rewrite - API-->>Renderer: 流式结果 - Renderer->>Renderer: WritingRewritePreview - User->>Renderer: 接受改写 - Renderer->>Renderer: applyReplace / applyInsert -``` - -- 选区信息类型:`WritingSelection`(`from`、`to`、`text`),见 [selection.ts](../src/open-canvas/types/selection.ts)。 -- 与画布聊天侧选区 `CanvasTextSelection` 分离;后者用于快捷操作栏等,不用于 Tiptap 改写。 - ---- - -## 八、与顶部工具栏、目录栏的关系 - -同一 `TextArtifactRenderer` 编辑模式下还包含: - -| 组件 | 作用 | -|------|------| -| [WritingToolbar.tsx](../src/open-canvas/components/artifact/WritingToolbar.tsx) | 顶部固定格式栏、导出等 | -| [DraftOutlineSidebar.tsx](../src/open-canvas/components/artifact/DraftOutlineSidebar.tsx) | 左侧标题目录,点击滚动到对应 heading | - -滑词 / 段落菜单与工具栏互不替代:工具栏面向全局格式;浮层面向**当前选区或当前段落**的快捷操作与 AI 改写入口。 - ---- - -## 九、开发与改造建议 - -### 9.1 若要改用官方 Bubble Menu - -1. 在 [TiptapExtensions.ts](../src/open-canvas/editor/TiptapExtensions.ts) 或组件层引入 `@tiptap/react` 的 `BubbleMenu`(或注册 `@tiptap/extension-bubble-menu`); -2. 将 [WritingBubbleMenu.tsx](../src/open-canvas/components/artifact/WritingBubbleMenu.tsx) 内手写 `selectionUpdate` + Portal 逻辑替换为官方组件的 `shouldShow` / `tippyOptions`(v3 API 以官方文档为准); -3. **段落左侧按钮**仍建议保留自研 `WritingParagraphMenu`(官方 Bubble Menu 只覆盖选区,不覆盖 hover 段落场景)。 - -### 9.2 新增浮层按钮 - -- **滑词**:在 `WritingBubbleMenu` 的 `BubbleButton` 列表中增加,使用 `editor.chain().focus()....run()`。 -- **段落**:在 `WritingParagraphMenu` 的 `handleClick` 或拆出多按钮布局;注意 `REWRITABLE_TYPES` 与 hover 延迟逻辑。 - -### 9.3 流式阶段禁止交互 - -不要在 `DraftStreamingPreview` 中挂载 Tiptap 菜单:流式阶段 token 高频更新,挂载 `useWritingEditor` 易引发更新风暴(见 [DraftStreamingPreview.tsx](../src/open-canvas/components/ai-writing/DraftStreamingPreview.tsx) 文件头注释)。应等 `isDraftEditable` 后再走 `TextArtifactRenderer`。 - ---- - -## 十、相关文档 - -- [前端开发.md](./前端开发.md) §25.4 — Canvas / AI 写作路由与组件总表 -- [ai-writing-用户自带大纲-测试用例.md](./ai-writing-用户自带大纲-测试用例.md) — AI 写作业务流程测试(含右侧草稿层级校验) - ---- - -## 十一、依赖版本(摘录) - -以 [package.json](../package.json) 为准,编辑器相关主要为: - -- `@tiptap/core`、`@tiptap/react`、`@tiptap/starter-kit`:`^3.23.4` -- `@tiptap/extension-bubble-menu`:已安装但未接入滑词实现 -- `tiptap-markdown`:Markdown 扩展 - -升级 Tiptap 大版本前,请对照官方迁移指南,并重点回归:`WritingBubbleMenu` 选区坐标、`WritingParagraphMenu` 的 `posAtCoords`、以及 `AiHighlightExtension` 与 StarterKit 扩展名冲突(勿重复注册 `underline`)。 diff --git a/frontend-web/docs/ai-writing-用户自带大纲-测试用例.md b/frontend-web/docs/ai-writing-用户自带大纲-测试用例.md deleted file mode 100644 index da73fbeb..00000000 --- a/frontend-web/docs/ai-writing-用户自带大纲-测试用例.md +++ /dev/null @@ -1,274 +0,0 @@ -# AI 写作 ·「用户自带大纲」手动测试用例 - -> 功能:用户在 AI 写作表单粘贴已有大纲后,系统**完全按用户大纲生成正文**,兼容一/二/三级目录。 -> 入口页面:`http://localhost:5173/#/page/workspace/ai-writing`(或 `2026` 端口经 nginx) - ---- - -## 一、测试目标 - -验证三件事: -1. **识别**——用户粘贴的各种格式大纲,能被正确解析成 章节(##) → 小节(###) → 子要点(####) 三级结构。 -2. **保真**——生成的大纲标题、层级**与用户粘贴的完全一致**,不增、不删、不改写。 -3. **兼容**——只有一级 / 一+二级 / 一+二+三级 三种深度都能正确生成对应层级的正文。 - ---- - -## 二、前置条件 - -| 项 | 要求 | -|----|------| -| 服务 | 后端(Gateway 8001)+ 前端(Vite 5174/5173)均已启动,可正常发起一次普通 AI 写作 | -| 模型 | 写作模型已在表单可选、能正常输出(先用不带大纲的普通流程跑通一次作为基线) | -| 账号 | 已登录(或无鉴权模式) | -| 入口 | 进入 `/page/workspace/ai-writing`,能看到「写作配置选项」表单 | - ---- - -## 三、通用操作步骤 - -除非用例另有说明,每条用例都按以下步骤执行: - -1. 进入 AI 写作页面,点开**「已有大纲(可选)」** 旁的「我已有大纲,直接粘贴 ▾」。 -2. 在「写作意图」填一句话(如:`写一篇关于新能源汽车出口的分析文章`)。 -3. 把用例里的**「输入大纲」**整段粘贴进「已有大纲」输入框。 -4. 文章类型 / 目标字数 / 素材来源保持默认即可。 -5. 点击「开始 AI 写作」。 -6. 等待流程走到 **PAUSE-2「大纲确认」**,对照**「预期大纲结构」**核对左侧/卡片里的大纲。 -7. 点「确认」继续,等正文生成完,对照**「预期正文层级」**核对右侧草稿的标题层级。 - -> 关键校验点都在第 6 步(大纲确认卡)。正文层级(第 7 步)作为二次确认。 - ---- - -## 四、测试用例 - -### TC-01 Markdown 三级 + 文章标题 - -**输入大纲:** -``` -# 新能源汽车出口为何高速增长 -## 引言 -### 出口数据创新高 -#### 同比增幅 -#### 全球排名变化 -### 市场背景 -## 增长动因 -### 成本优势 -``` - -**预期大纲结构:** -- 文章标题 = `新能源汽车出口为何高速增长`(首个孤立 `#` 被识别为标题,不作为章节) -- 章节1 `引言` - - 小节 `出口数据创新高` → 子要点:`同比增幅`、`全球排名变化` - - 小节 `市场背景`(无子要点) -- 章节2 `增长动因` - - 小节 `成本优势` - -**预期正文层级:** `#` 文章标题 → `##` 引言/增长动因 → `###` 小节 → `####` 同比增幅/全球排名变化,各级都有正文。 - ---- - -### TC-02 中文编号三级(一、/(一)/ 1.) - -**输入大纲:** -``` -一、引言 -(一)背景 -(二)意义 -二、现状分析 -(一)市场数据 -1. 国内市场 -2. 海外市场 -``` - -**预期大纲结构:** -- 章节1 `引言` → 小节 `背景`、`意义` -- 章节2 `现状分析` → 小节 `市场数据` → 子要点 `国内市场`、`海外市场` -- 标题留空 → 用「写作意图」兜底(卡片标题约等于意图前 30 字) - -**校验重点:** `一、` > `(一)` > `1.` 的层级顺序正确(符合中文公文惯例)。 - ---- - -### TC-03 阿拉伯小数点编号(1. / 1.1 / 1.1.1) - -**输入大纲:** -``` -1. 引言 -1.1 研究背景 -1.2 研究意义 -2. 现状 -2.1 行业概况 -``` - -**预期大纲结构:** -- 章节1 `引言` → 小节 `研究背景`、`研究意义` -- 章节2 `现状` → 小节 `行业概况` - ---- - -### TC-04 缩进 bullet 表示层级 - -**输入大纲:** -``` -- 引言 - - 背景 - - 意义 -- 现状 - - 数据 - - 细节展开 -``` - -**预期大纲结构:** -- 章节1 `引言` → 小节 `背景`、`意义` -- 章节2 `现状` → 小节 `数据` → 子要点 `细节展开` - -**校验重点:** 缩进深度正确映射成层级。 - ---- - -### TC-05 只有一级目录(平铺 Markdown) - -**输入大纲:** -``` -## 引言 -## 现状 -## 对策 -## 结论 -``` - -**预期大纲结构:** 4 个章节 `引言/现状/对策/结论`,**均无小节**。 -**预期正文层级:** 每个章节 `##` 标题下是一整段连贯正文(不出现 `###`)。 - -**校验重点:** 一级目录场景不报错、不被强行拆出多余小节。 - ---- - -### TC-06 只有一级目录(中文编号) - -**输入大纲:** -``` -一、引言 -二、现状 -三、结论 -``` - -**预期大纲结构:** 3 个章节 `引言/现状/结论`,无小节。与 TC-05 等价,验证另一种一级写法。 - ---- - -### TC-07 纯文本平铺(无任何标记) - -**输入大纲:** -``` -引言 -现状 -对策 -总结 -``` - -**预期大纲结构:** 4 个章节 `引言/现状/对策/总结`(每行一个短标题 → 一级章节)。 - -**校验重点:** 无编号无缩进的短标题列表也能识别为一级大纲。 - ---- - -### TC-08 格式混乱 / 散文化(走 LLM 兜底) - -**输入大纲:** -``` -我想写一篇分析今年AI大模型发展趋势的文章,重点关注国内企业的布局。 -希望先讲背景,再讲现状,最后给出展望。 -``` - -**预期行为:** -- 确定性解析判定为「散文」→ 自动走 **LLM 归一兜底**,整理成一份结构化大纲。 -- 大纲确认卡能正常显示一份大纲(不报错、不卡死)。 -- 大纲内容应贴近原意(背景 / 现状 / 展望之类),**但不要求逐字一致**(这一条是兜底鲁棒性,不是保真用例)。 - -**校验重点:** 流程不崩、能产出可用大纲。 - ---- - -### TC-09 保真校验(标题原样保留) - -**输入大纲:** -``` -一、A股市场2024年回顾与展望 -(一)宏观经济(GDP/CPI)影响 -(二)流动性与利率 -二、行业轮动主线 -``` - -**预期大纲结构:** 标题文本**逐字保留**,仅编号被剥离: -- 章节1 `A股市场2024年回顾与展望` - - 小节 `宏观经济(GDP/CPI)影响`、`流动性与利率` -- 章节2 `行业轮动主线` - -**校验重点:** 标题里的中文括号、英文缩写、年份等**不被改写**。 - ---- - -### TC-10 大纲确认卡里再编辑 / 重新规划 - -**前置:** 先用 TC-02 的大纲跑到「大纲确认」卡。 - -**操作:** -1. 在确认卡里**手动改一个小节标题**(如把 `背景` 改成 `行业背景`)→ 点「确认」。 -2. 或点「重新规划」并填修改要求(如 `给"现状分析"再加一个小节`)。 - -**预期行为:** -- 手动改后确认:正文按改后的大纲生成。 -- 重新规划:基于**用户这份大纲**做编辑式调整(在原结构上加/改),而不是从零另起一份无关大纲。 - -**校验重点:** 自带大纲 → 确认 → 编辑 这条回路正常,不会丢掉用户原始结构。 - ---- - -### TC-11 回归:不填大纲(默认流程不受影响) - -**操作:** 「已有大纲」**留空**,只填写作意图,正常发起。 - -**预期行为:** 与功能上线前完全一致——系统自动检索素材、**自动规划大纲**(大纲由 AI 生成,非用户提供)。 - -**校验重点:** 增量功能不污染老流程。 - ---- - -### TC-12 素材关联(仍走检索) - -**前置:** 素材来源选「知识库」,用 TC-02 的大纲发起,等检索出素材并确认。 - -**预期行为:** -- 走到大纲时,用户的章节会被**关联上检索到的素材**(正文里能体现素材中的事实/数据)。 -- 关联**只填素材、不改大纲标题与结构**。 - -**校验重点:** 「自带大纲」与「检索素材」能共存——大纲是用户的,正文有素材支撑。 - ---- - -## 五、缺陷记录模板 - -| 用例 | 输入大纲(摘要) | 预期 | 实际 | 是否通过 | 备注 | -|------|------------------|------|------|----------|------| -| TC-01 | Markdown 三级 | 标题+2章节+子要点 | | ☐ | | -| TC-02 | 一、/(一)/1. | 中文三级 | | ☐ | | -| TC-03 | 1./1.1/1.1.1 | 阿拉伯三级 | | ☐ | | -| TC-04 | 缩进 bullet | 缩进三级 | | ☐ | | -| TC-05 | 平铺 ## | 4 章节无小节 | | ☐ | | -| TC-06 | 平铺 一、 | 3 章节无小节 | | ☐ | | -| TC-07 | 纯文本平铺 | 4 章节 | | ☐ | | -| TC-08 | 散文 | LLM 兜底产出大纲 | | ☐ | | -| TC-09 | 带括号/缩写标题 | 标题逐字保留 | | ☐ | | -| TC-10 | 确认卡编辑/重规划 | 在原大纲上调整 | | ☐ | | -| TC-11 | 留空 | 走自动规划 | | ☐ | | -| TC-12 | 检索+自带大纲 | 素材关联不改结构 | | ☐ | | - ---- - -## 六、备注 - -- TC-01~07、09 的解析逻辑已有**自动化单测**覆盖(`backend/tests/test_ai_writing_outline_parser.py`,19 个用例),本文档侧重**端到端真机表现**(流式展示、确认卡渲染、正文层级、素材关联)。 -- 若某条解析结果与预期不符,先看大纲确认卡里的**结构**是否正确(定位是解析问题还是正文生成问题)。 -- 相关后端实现:`packages/harness/deerflow/agents/ai_writing/outline_parser.py`(解析)、`nodes/writer_outline.py`(自带大纲分支 + 素材关联)。