# AI 写作「对话驱动写作台」实现方案(可落地版) > 需求:**复制**现有 AI 写作代码,新增一个独立页面(**原页面/原路由零改动**)。在新页面里 > **大改交互流程**——去掉「素材收集 / 大纲规划 / 作家写作 / 编辑审核」四个阶段干预卡里的**输入框**, > 只**保留原来的确认按钮**;在页面**底部新增一个统一的多行智能输入框**,具备**强意图理解**能力: > 重新搜索、新增检索词、添加检索素材、重新规划大纲、按要求改大纲、提修改意见、审核意见、 > 素材不足继续搜集、对写作内容提问……**覆盖现有全部交互甚至更多**。 > > 交互节奏不变:**一步一确认**——进行到某个专家阶段时,交互就针对该专家(阶段作用域); > 用户可以**点原来的确认按钮**,也可以**在底部输入框里发「继续下一步」的意图**推进。 > 意图**拿不准时**主动向用户**发起协助/澄清**,绝不盲目触发高代价动作(定稿 / 重搜等)。 本文是**实现指引**,落地时按第 9 节路线图分阶段推进。文中所有文件路径、符号名均对齐当前代码。 --- ## 1. 现状梳理(改造地基) 现有 AI 写作是一套**独立 LangGraph 子系统**(不是 lead_agent),前后端都已成型、可复用。 ### 1.1 后端 `ai_writing` graph - 图工厂:`offline-backend-20260512/backend/packages/harness/deerflow/agents/ai_writing/graph.py` (`make_ai_writing_graph`,registered as `assistant_id="ai_writing"`) - 状态:`.../ai_writing/state.py`(`AIWritingState`) - 四个「阶段大脑」节点: | 阶段 | 节点 | 文件 | |------|------|------| | 素材收集 | `researcher` | `nodes/researcher.py` | | 大纲规划 | `writer_outline` | `nodes/writer_outline.py` | | 作家写作 | `writer_draft` | `nodes/writer_draft.py` | | 编辑审核 | `editor` | `nodes/editor.py` | - **五个暂停点**(`interrupt()`,`Command(resume=payload)` 续跑)——这是对话改造的核心可复用资产: | pause_point | 语义 | 现有 action | |-------------|------|-------------| | `material_confirm` | 素材确认 | `confirm` / `re_search` / `enable_skill_search` | | `outline_confirm` | 大纲确认 | `confirm` / `re_outline` | | `draft_confirm` | 草稿确认 | `to_editor` / `finalize` / `user_revise` | | `review_confirm` | 编辑打回确认 | `accept_review` / `force_finalize` | | `section_help` | 素材不足求助 | `section_help`(+`sectionDecisions`) | - resume 载荷(`UserInterventionPayload`,`types/ai-writing.ts`)字段:`action`、`approvedMaterialIds`、 `userQuery`(自然语言补充检索)、`editedOutline`、`outlineFeedback`、`userRevisionNotes`、 `overrideVerdict`、`selectedIssueIndices`、`sectionDecisions`。 **协议已同时支持「自然语言 + 结构化 action」**——这是对话层能复用的关键。 > 结论:**状态机 / 流式 / 暂停·续跑协议 / 持久化都可复用**,无需重写引擎。改造本质是 > 「把分散在干预卡里的输入框,换成一个底部智能输入框 + 意图路由层」,再把意图翻译回**现有 > `action + payload`**;仅少量新意图(对内容提问、素材不足自动回退重搜、追加审核意见)需新增能力。 ### 1.2 前端 `open-canvas` 子系统 - 主页面:`frontend-web/src/open-canvas/pages/AIWritingPage.tsx` - 路由注册:`open-canvas/routes/OpenCanvasRoutes.tsx`(`/page/canvas/ai-writing/*`)、 `pages/WorkspaceRoutes.tsx`(`/page/workspace/ai-writing/*`)、`pages/NotebookAIWritingPage.tsx` - 状态机 + 流式:`contexts/AIWritingContext.tsx`(`useReducer` + LangGraph `useStream`,`assistantId:'ai_writing'`) - 事件翻译 / reducer:`hooks/useAIWritingStream.ts` - 类型:`types/ai-writing.ts` - **左栏面板**:`components/ai-writing/AIWritingPanel.tsx`(时间线 + 干预卡 + 状态栏;idle 时挂启动表单) - **要改造的输入 UI**(本次重点): - 干预卡 `components/ai-writing/InterventionCard.tsx`:内含 `MaterialConfirmCard`(素材:有 `MaterialsToolbar` 输入框)、 `OutlineConfirmCard`(大纲:有 feedback textarea)、 `DraftConfirmCard`(草稿:有「修改意见」textarea)、 `ReviewConfirmCard`(审核:有「补充修改指令」textarea)、 `SectionHelpCard`(素材不足:按钮组)。 **每张卡里都既有输入框又有确认/动作按钮**——本次要**去掉输入框、保留按钮**。 - 启动表单 `components/ai-writing/AIWritingForm.tsx`(idle 阶段的配置表单)——本次由底部输入框接管启动。 - **已存在的对话原型**(可借鉴):`components/ai-writing/WritingSetupChat.tsx` (`useThreadStream` 起 lead_agent 线程 + `setup_writing` 审批卡回填表单;仅管启动前配置,不直接复用)。 - 底部输入框可直接复用 UI 组件:`@/components/ai-elements/prompt-input` (`PromptInput` / `PromptInputTextarea` / `PromptInputSubmit` / `PromptInputFooter`,见 `WritingSetupChat`)。 --- ## 2. 目标与范围 ### 2.1 必须满足 1. **复制式新增**:新目录 / 新路由 / 新侧边栏入口,**旧页面、旧路由、旧组件零改动**(「初始的不用动」)。 2. 页面**底部单一多行智能输入框**接管全流程交互;四个阶段干预卡里的**输入框全部移除**。 3. **保留确认按钮**:每个阶段到达暂停点时,消息流里仍渲染**产物卡 + 原来的确认/动作按钮** (素材「确认素材」、大纲「确认大纲」、草稿「让编辑审核 / 确认定稿」、审核「按编辑意见修改 / 强制定稿」、 素材不足的三选一按钮)。用户**点按钮**或**在底部输入框发自然语言意图**二选一均可推进。 4. **强意图理解**:把用户自由文本准确路由到正确动作,**覆盖现有全部交互 + 更多**(见 3.2)。 5. **阶段作用域**:意图解析必须知道「当前停在哪个专家阶段」,把同一句话按当前阶段翻译成对应 action。 6. **素材不足自动回退重搜**:修改意见需要的信息现有素材没有时,先重搜补素材、补完自动继续原意图。 7. **拿不准就协助**:低置信意图**不猜着执行**,走澄清追问(clarify)或用户协助。 8. 保留右侧草稿实时预览(可编辑)与时间线 / 进度展示。 ### 2.2 明确不做(本期) - 不改旧 `AIWritingPage` 及其 `AIWritingForm` / `InterventionCard`(这两个是要在**副本**里改的)。 - 不替换 `ai_writing` graph 四个阶段大脑逻辑,不动五个暂停点 / checkpoint / 持久化。 - 不引新状态管理库(继续 `useReducer` + `useStream`)。 ### 2.3 复制策略(关键决策) 「复制一份」= **复制会被大改的表现层组件**(页面 + 干预卡 + 面板),**复用不改的引擎层**: | 复制(新建副本,大改) | 复用(原样,不改;仅对 `types` 做**追加式**扩展) | |------------------------|--------------------------------------------------| | 新页面 `AIWritingChatPage.tsx` | `contexts/AIWritingContext.tsx`(状态机 + 流式) | | 新面板 `WritingChatPanel.tsx`(消息流 + 底部输入框) | `hooks/useAIWritingStream.ts`(事件 → state) | | 新消息卡 `chat-cards/*`(产物卡+确认按钮,**无输入框**) | `components/ai-writing/AIWritingDraftPanel.tsx`(右侧草稿预览) | | 新意图客户端 `api/ai-writing-intent.ts` | 各流式展示子组件(`MaterialsCard`/`OutlineCard`/`ReviewCard`/`SkillStepsCard`…) | | (可选)新 `contexts/WritingChatContext.tsx` 包一层「pending 意图 / 澄清态」 | `types/ai-writing.ts`(**只加字段不改旧字段**,保证旧页面不受影响) | > **不复用、也不改** 的三个「输入框组件」:`AIWritingForm` / `InterventionCard` / `WritingFormContext`—— > 旧页面继续用它们,新页面完全不引用它们。这样「初始的不用动」得到硬保证。 > > 为什么复用 `AIWritingContext` 而不复制它?它**不含任何输入框 UI**,只是「状态机 + 与后端 graph 的 > 流式桥」,复用它**不会改动旧页面的任何呈现**(旧页面照常在自己的 Provider 实例里跑)。复用能显著 > 降低维护成本、避免两套引擎漂移。若要绝对物理隔离,也可整目录复制 `AIWritingContext` + > `useAIWritingStream` 到新目录——**推荐先复用**,仅当后续需要给对话态加大量图内新协议时再分叉。 --- ## 3. 核心设计 ### 3.1 三层结构 ``` ┌──────────────────────────────────────────────────────────────┐ │ 对话 UI 层(新) WritingChatPanel │ │ - 消息流:用户消息 + AI 阶段产物卡(含【保留的确认按钮】)+ 进度 │ │ - 底部:统一多行智能输入框(唯一自由输入入口) │ │ - 右侧:草稿实时预览(复用 AIWritingDraftPanel) │ └───────────────┬──────────────────────────────────────────────┘ │ 用户自然语言(+ 当前 status/pausePoint 上下文) ▼ ┌──────────────────────────────────────────────────────────────┐ │ 意图解析层(新,核心) POST /api/ai-writing/sessions/{id}/intent │ │ 输出:{ intent, pausePoint, action, payload, │ │ needResearch, researchQuery, answer, confidence, clarify }│ └───────────────┬──────────────────────────────────────────────┘ │ 结构化 action + payload ▼ ┌──────────────────────────────────────────────────────────────┐ │ 执行层(复用现有 AIWritingContext) │ │ idle → startWriting(AIWritingRequest) │ │ 暂停 → submitIntervention(UserInterventionPayload) │ │ 问答 → 旁路回答(不改写作状态) │ │ 素材不足 → 先重搜、缓存 pending、补完自动续跑 │ └──────────────────────────────────────────────────────────────┘ ``` **设计原则**:意图解析层是「翻译官」——把自由文本翻译成**执行层已经懂的指令**。能复用 `action + payload` 的绝不新造协议;确认按钮点击**直接走执行层**(不经意图层,零延迟、零误判)。 ### 3.2 意图分类体系(覆盖全流程 + 更多) 按「意图 → 触发时机(当前 status/pausePoint)→ 映射执行」组织,即意图解析引擎的目标 schema。 **「甚至更多」** = G(问答)、H(控制)、以及 B/C/D/E 里的「素材不足自动回退重搜」。 #### A. 启动类(status = `idle`) | 意图 | 用户说法示例 | 映射 | |------|-------------|------| | `start_writing` | 「写一篇关于新能源出口的深度分析,2000 字」 | 解析成 `AIWritingRequest` → `startWriting()` | | `start_with_outline` | 「按这个大纲写:…」 | `AIWritingRequest.userOutline` | | `start_imitate` | 「仿照这篇样文写…」 | `writingModeType='imitate'` + `sampleText` | #### B. 素材类(pausePoint = `material_confirm`) | 意图 | 示例 | 映射 | |------|------|------| | `confirm_materials` | 「素材可以,继续 / 下一步」 | `{action:'confirm', approvedMaterialIds}` | | `research_again` | 「重新搜索素材」 | `{action:'re_search'}`(无 userQuery=覆盖式重检) | | `add_material`/`add_keyword` | 「再补点关于 X 的素材」「加个关键词 Y」 | `{action:'re_search', userQuery:'X/Y'}`(追加) | | `select_materials` | 「只用第 1、3 条」 | `{action:'confirm', approvedMaterialIds:[…]}` | | `drop_materials` | 「去掉第 2 条」 | 同上(反选后的子集) | #### C. 大纲类(pausePoint = `outline_confirm`) | 意图 | 示例 | 映射 | |------|------|------| | `confirm_outline` | 「大纲 OK,开始写」 | `{action:'confirm'}` | | `reoutline` | 「重写大纲」 | `{action:'re_outline'}`(feedback 可空) | | `revise_outline` | 「第二章拆成两节」「调整顺序」 | `{action:'re_outline', outlineFeedback:自然语言}` | #### D. 写作 / 草稿类(pausePoint = `draft_confirm`) | 意图 | 示例 | 映射 | |------|------|------| | `to_editor` | 「交给编辑审核」 | `{action:'to_editor'}` | | `finalize` | 「就这样定稿」 | `{action:'finalize'}` **(高代价:低置信必澄清)** | | `revise_draft` | 「第三段太啰嗦,精简」「补个 2024 案例」 | `{action:'user_revise', userRevisionNotes:意见}` | | `rewrite_section` | 「重写第二章」 | `user_revise` + 章节定位写进 notes | | `change_tone/style` | 「更口语一点 / 更正式」 | `user_revise` + notes | | `apply_review`(若已有审核结果) | 「按编辑意见改」「只接受第 1、2 条」 | `{action:'user_revise', selectedIssueIndices:[…]}` | #### E. 编辑审核类(pausePoint = `review_confirm`) | 意图 | 示例 | 映射 | |------|------|------| | `accept_review` | 「按编辑意见改」 | `{action:'accept_review', selectedIssueIndices}` | | `partial_accept_review` | 「只接受第 1、2 条」 | `accept_review` + `selectedIssueIndices` 子集 | | `add_review_opinion` | 「再补一条:核查数据准确性」 | `accept_review` + `userRevisionNotes`(并入补充指令,现协议已支持) | | `force_finalize` | 「不改了,强制定稿」 | `{action:'force_finalize', overrideVerdict:true}` **(高代价:必澄清)** | #### F. 素材不足求助类(pausePoint = `section_help`) | 意图 | 映射 | |------|------| | `section_supplement` | `{action:'section_help', sectionDecisions:[…'supplement']}` | | `section_loose` | `sectionDecisions:[…'loose']` | | `section_delete` | `sectionDecisions:[…'delete']` | #### G. 问答类(**新**;任意 status;旁路,不改写作状态) | 意图 | 示例 | 映射 | |------|------|------| | `ask_about_content` | 「第二章讲了啥?」「为什么用这个论点?」 | 意图节点直接带回 `answer`,对话流渲染(见 5.4) | | `ask_meta` | 「现在到哪步了?」「用了哪些素材?」 | 读当前 state 直接回答,不走图 | #### H. 控制类(**新 / 半新**) | 意图 | 映射 | |------|------| | `next_step`(通用「继续/下一步」) | 按**当前 pausePoint** 映射到该阶段的默认确认(material/outline→`confirm`,draft→`to_editor`…;等价点确认按钮) | | `restart_stage` | 重跑当前阶段(复用 `resumeWriting()` 空 command 续跑) | | `run_background` | 转后台自动跑:`suspendAIWritingToBackground()` | | `cancel` | `pauseWriting()`(`stream.stop()`) | | `unknown/clarify` | 无法判定 → 反问澄清 / 用户协助(**防误触发硬指标**) | ### 3.3 「素材不足自动重搜」判定与回退(用户强调点) 触发:用户在**大纲/草稿/审核**阶段提出修改意见,但意见需要的信息现有素材里没有。 判定来源(组合): 1. **意图节点直接判**:把「素材摘要(keywords + 每条 title/source)」喂给意图 prompt,让它判断 「这条意见能否用现有素材满足」;不能 → `needResearch=true` + `researchQuery`。 2. **执行阶段兜底**:`writer_draft` 严格模式已有 `blocked_sections` → `section_help` 暂停点, 对话层把它翻译成「素材不足,是否重搜?」的追问。 回退动作(**首期纯前端实现,不动后端协议**):`needResearch=true` 时,前端把用户的修改 `payload` 缓存为 `pendingIntent`,先 `submitIntervention({action:'re_search', userQuery:researchQuery})` 补素材;监听到新一轮 `material_confirm`(素材追加完成)后,**自动提交** `pendingIntent`。 需处理「重搜期间用户又改主意」→ 允许覆盖 pending。 --- ## 4. 意图解析引擎:方案与契约 **采用方案 A:后端 LLM 意图节点(独立 REST)**,叠加高频短句**规则快路径**优化。 理由:`needResearch` 判定需要**完整写作上下文**(素材/大纲/草稿),后端天然持有 checkpoint state; 与 `setup_writing` 一脉相承,离线模型选择策略统一;输出 schema 稳定、前端只消费。 ### 4.1 后端入口 `app/gateway/routers/ai_writing.py` 新增: ``` POST /api/ai-writing/sessions/{session_id}/intent Body: { text: string, status: string, pausePoint?: string } Resp: IntentResult(见 4.3) ``` 实现:读该 session 的 checkpoint state(素材摘要 / 大纲纲要 / 草稿字数·章节 / 审核意见)→ 用 `ai_writing` 配置模型(复用 `intent_parser.py` 同套 `create_chat_model` + `parse_json` + 模型容错 `_model_fallback`)解析 → 返回结构化契约。**不改图拓扑**。 新增 prompt:`agents/ai_writing/prompts/intent_router_prompts.py` - 输入:用户文本、`current_status`/`pause_point`、素材摘要、大纲纲要、草稿字数+章节标题、审核意见列表。 - 输出:严格 JSON(4.3 契约)。硬要求:**只在证据充分时给高 confidence,否则给 `clarify`**; `finalize`/`force_finalize`/`re_search` 等高代价意图**必须**高置信才不澄清。 ### 4.2 前端快路径(先跑规则,未命中再调后端) `api/ai-writing-intent.ts` 里对**当前 pausePoint** 做少量确定性短语命中,直接产出 payload,省一次往返: - 「继续/下一步/确认/可以/ok/好的」→ 当前阶段默认 `confirm`(draft 阶段=`to_editor`)。 - 「重新搜索/重搜」(material)→ `re_search`。「重写大纲」(outline)→ `re_outline`。 - 「定稿/强制定稿」→ **不进快路径**(高代价,一律走后端 + 可能澄清)。 - 命中即执行;未命中 → 调后端 `intent` 接口。 ### 4.3 意图解析输出契约 ```jsonc { "intent": "revise_draft", // 见 3.2 分类 "pausePoint": "draft_confirm", // 需要 resume 时;否则 null "action": "user_revise", // 映射到现有 action "payload": { // 直接可喂 submitIntervention 的字段(camelCase) "userRevisionNotes": "第三段精简,并补一个 2024 年的案例" }, "needResearch": true, // 素材是否不足 "researchQuery": "2024 行业案例", // needResearch 时的检索意图 "answer": null, // 问答类意图的直接回答(旁路) "confidence": 0.86, "clarify": null // 低置信时的反问话术(非空则不执行、只追问) } ``` 前端消费顺序: 1. `answer` 非空 → 对话流渲染回答(问答旁路,不动写作状态)。 2. `clarify` 非空 **或** `confidence < 阈值(建议 0.6)` → 渲染反问气泡,等用户澄清(**防误触发**)。 3. `needResearch=true` → 缓存 `pendingIntent=payload`,先重搜,收到新素材后自动提交。 4. 否则 → `idle` 走 `startWriting(...)`;暂停态走 `submitIntervention({action, ...payload})`。 --- ## 5. 前端改造方案 ### 5.1 新页面与路由(复制式) - 新页面:`open-canvas/pages/AIWritingChatPage.tsx` (骨架照抄 `AIWritingPage.tsx` 的 Provider 包裹与左右分栏;左栏换成 `WritingChatPanel`,右栏仍 `AIWritingDraftPanel`;idle 不再挂 `AIWritingForm`,改由底部输入框启动)。 - 路由注册(**新增,不动旧行**): - `open-canvas/routes/OpenCanvasRoutes.tsx` 加 `path="ai-writing-chat/*"`。 - 侧边栏 `core/page-layout/sidebar-menu.ts`:在 `qa-research` 旁新增 `{ id:"qa-research-chat", label:"AI写作(对话版)", icon:FileText, path:"/page/canvas/ai-writing-chat" }`。 (或先不加、内部灰度直链验证。) ### 5.2 新面板 `WritingChatPanel.tsx`(左栏主体) 职责 = 消息流容器 + 底部输入框,替代 `AIWritingPanel` 里的「时间线 + InterventionCard + 状态栏」。 复用 `useAIWriting()` 读 state / 调 `startWriting` / `submitIntervention` / `pauseWriting` / `suspendAIWritingToBackground`。 消息流渲染(把 `ProgressEvent` / 阶段产物适配成消息卡,复用现有展示子组件): - 运行中:`research_progress`/`outline_chunk`/`draft_chunk`/`review_chunk` → 进度 / 流式卡 (复用 `SkillStepsCard`/`OutlineStreamingView`/`DraftGeneratingView`/`ReviewStreamingView` 的展示部分)。 - 到达暂停点:渲染 **产物卡 + 保留的确认按钮**(见 5.3),不再有输入框。 - 用户消息、AI 澄清追问、问答回答:普通气泡。 底部输入框:直接复用 `@/components/ai-elements/prompt-input`(`PromptInput`+`PromptInputTextarea`+ `PromptInputSubmit`),Enter 发送、Shift+Enter 换行;发送时读当前 `state.status`/`currentPause?.pausePoint` 交给意图路由(见 5.5)。运行中(非暂停点)可禁用发送或转「排队/提示当前在生成」。 ### 5.3 新消息卡 `chat-cards/*`(**保留按钮、去掉输入框**) 把 `InterventionCard.tsx` 的五张卡各复制一份到 `open-canvas/components/ai-writing-chat/chat-cards/`, **删掉其中的 textarea / MaterialsToolbar 输入部分,保留产物展示 + 确认/动作按钮**: | 新卡 | 展示(复用) | 保留的按钮 | 删除的输入 | |------|-------------|-----------|-----------| | `MaterialConfirmChatCard` | `MaterialsCard`(勾选保留)+ `SkillStepsCard` | 「确认素材,规划大纲」「重新搜索」 | `MaterialsToolbar` 的 userQuery 输入框 | | `OutlineConfirmChatCard` | `OutlineCard`(可点标题微调保留) | 「确认大纲,开始写作」 | feedback textarea + 发送 | | `DraftConfirmChatCard` | `ReviewCard`(若有审核,勾选保留) | 「按编辑审核意见修改」「让编辑继续审核」「确认定稿」 | 「修改意见」textarea | | `ReviewConfirmChatCard` | `ReviewCard`(勾选保留) | 「按编辑意见修改」「强制定稿」 | 「补充修改指令」textarea | | `SectionHelpChatCard` | 章节 + 三选一按钮组 | 三选一 + 「提交并继续」 | (本就无自由输入,原样保留) | > 勾选态(素材/审核意见)**保留**——它是「点按钮」路径的一部分,不算自由输入框。用户想用自然语言 > 「只用第 1、3 条」也能走底部输入框(意图层产出 `approvedMaterialIds`/`selectedIssueIndices`)。 > 按钮点击 **直接调 `submitIntervention`**,与旧卡完全一致,零改动风险。 ### 5.4 问答旁路(新能力) 「对写作内容提问」不改写作 state。首期**轻量实现**:意图节点检出 `ask_*` 时读 state 直接带回 `answer`, 前端在消息流渲染为普通 AI 气泡即可(一次调用出答案)。完整版(后续)可另起只读 lead_agent 线程注入 草稿/大纲作背景,类似 `WritingSetupChat` 的 `useThreadStream`。 ### 5.5 交互流程(对话版)串起来 1. **空闲**:底部输入「写一篇关于 X 的深度分析,2000 字」→ 意图 `start_writing` → 组 `AIWritingRequest` → `startWriting()`。(缺关键字段时先 `clarify` 追问字数/读者/类型。) 2. **运行中**:进度/流式卡渲染。 3. **到暂停点**:渲染产物卡 + 按钮 + 一句提示「你可以点上面的按钮,或直接说:确认继续 / 补充素材 / 改大纲…」。 - **点按钮** → 直接 `submitIntervention`(旧逻辑)。 - **发文字** → `resolveIntent(text, status, pausePoint)`(先快路径、后后端)→ 按 4.3 消费。 4. **问答**:任意时刻提问 → 旁路 `answer`,不打断写作。 5. **素材不足**:`needResearch` 或 `section_help` → 追问/执行「重搜 → 自动续跑 pending 意图」。 6. **拿不准**:`clarify`/低置信 → 反问气泡,不执行。 ### 5.6 pending 意图 & 澄清态(新增本地状态) 在 `WritingChatPanel`(或新 `WritingChatContext`)维护: - `pendingIntent: UserInterventionPayload | null` —— 素材不足重搜完成后自动提交。 - `awaitingClarify: { originalText: string } | null` —— 澄清态,用户下一句与上一句拼接再解析。 - pending 生命周期:设置 → 提交 `re_search` → 监听 reducer 里新一轮 `awaiting_material_confirm` (`state.status` 从 `researching` 回到 `awaiting_material_confirm`)→ 自动 `submitIntervention(pending)` → 清空。 --- ## 6. 后端改造方案 ### 6.1 意图解析入口(新增,唯一后端改动主体) - `app/gateway/routers/ai_writing.py`:新增 `POST /sessions/{id}/intent`(见 4.1)。 读 checkpoint state 用 `app.state.checkpointer` + `make_ai_writing_graph().aget_state(...)` (参照 `ai_writing_job_executor.py` 的读法)取素材/大纲/草稿/审核摘要。 - `agents/ai_writing/prompts/intent_router_prompts.py`:新增意图 prompt(见 4.1)。 - 复用 `nodes/intent_parser.py` 的模型创建 / `parse_json` / `_model_fallback` 容错。 ### 6.2 不需要改的部分 - 四个阶段大脑节点、五个暂停点、checkpoint、StreamBridge、持久化、sessions/transcript REST——**全不动**。 - 「素材不足自动续跑」「问答旁路」首期都在**前端 / 意图节点**实现,**不扩 resume 协议**。 ### 6.3 可选增强(阶段 3,按需) - `add_review_opinion` 完整版:`review_confirm` resume 增字段 `extra_review_notes`(首期已用现有 `userRevisionNotes` 降级承载,够用)。 - `resume_intent` 后端化(把「重搜完自动续跑」搬到后端,减少前端状态机复杂度)。 - undo / 回退到上一暂停点(依赖 checkpoint 回滚)。 --- ## 7. 数据流时序(改稿 + 素材不足自动重搜) ``` 用户(draft_confirm 暂停): "第三段补个2024年的行业案例" │ 底部输入框 → resolveIntent(text, status='awaiting_draft_confirm', pausePoint='draft_confirm') ▼ POST /api/ai-writing/sessions/{id}/intent { text, status, pausePoint } 后端: 读 state(素材摘要) + LLM 解析 │ 返回 { intent:revise_draft, action:user_revise, │ payload:{userRevisionNotes:"第三段…补2024案例"}, │ needResearch:true, researchQuery:"2024 行业案例", confidence:0.9 } ▼ 前端: needResearch=true → pendingIntent = payload │ submitIntervention({action:'re_search', userQuery:"2024 行业案例"}) ▼ LangGraph resume → researcher 追加素材 → status 回到 awaiting_material_confirm 前端: 监听到新一轮素材确认 → 自动提交 pendingIntent │ submitIntervention({action:'user_revise', userRevisionNotes:"第三段…补2024案例"}) ▼ writer_draft 用新素材改稿 → draft_chunk… → draft_ready → draft_confirm 前端: 消息流展示新草稿卡 + "已按你的意见改写并补充了 2024 案例" ``` --- ## 8. 关键实现细节与坑 1. **阶段作用域**:`resolveIntent` 必须带 `state.currentPause?.pausePoint` + `state.status`—— 同一句「重新来」在 material 是重搜、在 outline 是重写大纲。`AIWritingContext` 已有这两个字段,直接读。 2. **确认按钮零改动**:新卡的按钮 `onClick` 原样调 `submitIntervention(payload)`,与旧卡逐字一致, 风险最低;**只删输入框**。 3. **防误触发**:低置信(<0.6)或 `clarify` 非空一律追问;`finalize`/`force_finalize`/`re_search` 即使命中也要求高置信,否则澄清。绝不盲目定稿/覆盖重搜。 4. **pending 意图队列**:素材不足是「先插重搜、再回原意图」,用 5.6 的小状态机;重搜完成前允许覆盖 pending。 5. **问答不污染 transcript**:问答/澄清消息在前端标记 `kind:'qa'|'clarify'`,与写作进度事件区分( `AIWritingContext` 的 transcript 只存 progressEvents/completedInterventions,问答气泡是纯前端消息, 默认不入 transcript,避免历史回看错乱)。 6. **流式产物→消息卡**:复用 `OutlineStreamingView`/`DraftGeneratingView`/`ReviewStreamingView`/ `SkillStepsCard` 的**展示部分**,套进消息容器,不重写渲染。 7. **右侧草稿预览**:`AIWritingDraftPanel` + `DraftSync` 原样复用(`DraftSync` 依据 status/markdown 同步右侧画布)。 8. **离线内网**:意图模型走 `config.yaml → ai_writing` 配置模型;检索仍走技能/内网 ES,不联网。 9. **模型选择透传**:`submitIntervention` 的 resume 已把 `state.request?.modelName` 一并回传(见 `AIWritingContext`),新页面沿用即可。 10. **旧页面隔离验证**:改完后回归旧 `/page/canvas/ai-writing` 与 `/page/workspace/ai-writing`、 笔记本嵌入页,确认零变化(新代码不 import 旧的 `AIWritingForm`/`InterventionCard`/`WritingFormContext`)。 --- ## 9. 分阶段实施路线图 **阶段 0 — 骨架(不含意图)** - 复制 `AIWritingChatPage` + 注册新路由 + 侧边栏入口;复用 `AIWritingContext`/`useStream`/`DraftPanel`。 - 复制五张 `chat-cards`(删输入框、留按钮);底部输入框先只做 idle 直发 `start_writing` (文本原样当 `userIntent`,其余字段取默认)+ 暂停点靠按钮推进。端到端跑通。 **阶段 1 — 意图解析(核心)** - 后端 `POST /sessions/{id}/intent` + `intent_router_prompts.py`,返回 4.3 契约。 - 前端 `api/ai-writing-intent.ts`(快路径 + 后端)接入 `WritingChatPanel`,覆盖 A~F 类意图。 - 低置信 `clarify` 澄清态(5.6)。 **阶段 2 — 问答旁路 + 素材不足自动重搜** - G 类问答(意图节点直接带 `answer`)。 - `needResearch` + pending 意图机制(前端实现重搜后自动续跑,5.6)。 **阶段 3 — 增强(按需)** - `add_review_opinion` 扩协议、`resume_intent` 后端化、undo/回退、完整问答只读线程。 **阶段 4 — 打磨** - 消息卡视觉、历史回看(transcript)、后台挂起入口、灰度到侧边栏。 --- ## 10. 关键文件清单(落地对照) ### 新增(前端) - `open-canvas/pages/AIWritingChatPage.tsx` — 新页面(复制 `AIWritingPage` 骨架改造) - `open-canvas/components/ai-writing-chat/WritingChatPanel.tsx` — 消息流 + 底部输入框 - `open-canvas/components/ai-writing-chat/chat-cards/{MaterialConfirmChatCard,OutlineConfirmChatCard,DraftConfirmChatCard,ReviewConfirmChatCard,SectionHelpChatCard}.tsx` — 保留按钮、去掉输入框 - `open-canvas/components/ai-writing-chat/progressToMessage.ts` — `ProgressEvent` → 消息卡适配 - `open-canvas/api/ai-writing-intent.ts` — 意图解析客户端(快路径 + `POST …/intent`) - (可选)`open-canvas/contexts/WritingChatContext.tsx` — pending 意图 / 澄清态 ### 修改(前端,仅追加,不动旧逻辑) - `open-canvas/routes/OpenCanvasRoutes.tsx` — 加一条 `ai-writing-chat/*` 路由 - `core/page-layout/sidebar-menu.ts` — 加一个侧边栏入口(可选) - `open-canvas/types/ai-writing.ts` — **仅追加** `IntentResult` 等新类型,不改旧字段 ### 新增(后端) - `app/gateway/routers/ai_writing.py` — 加 `POST /sessions/{id}/intent` - `agents/ai_writing/prompts/intent_router_prompts.py` — 意图 prompt - `tests/test_ai_writing_intent.py` — 意图接口单测(TDD 强制) ### 复用(不改) - `open-canvas/contexts/AIWritingContext.tsx`、`hooks/useAIWritingStream.ts` - `open-canvas/components/ai-writing/{AIWritingDraftPanel,DraftSync,MaterialsCard,OutlineCard,ReviewCard,SkillStepsCard,OutlineStreamingView,DraftGeneratingView,ReviewStreamingView}.tsx` - `agents/ai_writing/graph.py`、四个阶段节点、五个暂停点、`nodes/intent_parser.py` ### 绝不动(保证「初始的不用动」) - `open-canvas/pages/AIWritingPage.tsx` 及其引用的 `components/ai-writing/{AIWritingForm,InterventionCard}.tsx`、`contexts/WritingFormContext.tsx` - 旧路由 `/page/canvas/ai-writing`、`/page/workspace/ai-writing`、笔记本嵌入页 --- ## 11. 待确认(默认取值已给出,可直接推进) 1. 意图引擎:**后端 LLM 节点(方案 A)+ 前端短句快路径** —— 默认采用。 2. `AIWritingContext` / `useAIWritingStream`:**复用**(不物理复制)——默认采用;需绝对隔离可改为复制。 3. 新页面入口:**侧边栏新增「AI写作(对话版)」** —— 默认加;若要先灰度可只留直链。 4. 「素材不足自动重搜」:**首期前端 pending 续跑**(不动后端 resume 协议)——默认采用。 5. 问答旁路:**首期意图节点直接带 `answer`** —— 默认采用。 > 以上默认按「A + 复用引擎 + 侧边栏入口 + 前端 pending + 意图节点带答案」推进,如需调整请指出。