25 KiB
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 - 状态:
.../agents/ai_writing/state.py(AIWritingState+UserInterventionPayload) - 注册:
offline-backend-20260512/backend/langgraph.json→assistant_id = "ai_writing" - 执行:与 lead agent 共用 RunManager + StreamBridge + checkpointer(
app/gateway/services.py里assistant_id=="ai_writing"分发到make_ai_writing_graph)
节点拓扑(实际):
sample_analyzer → intent_parser → researcher
→ [cond] pause_material | writer_draft
→ writer_outline → pause_outline
→ writer_draft → [cond] pause_section_help | pause_draft | END
→ editor → [cond] pause_draft | pause_review
→ pause_review → END | writer_draft
四个「阶段大脑」节点:
| 阶段 | 节点 | 文件 |
|---|---|---|
| 素材收集 | 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 / supplement_search / enable_skill_search |
outline_confirm |
大纲确认 | confirm / re_outline |
draft_confirm |
草稿确认 | to_editor / finalize / user_revise / custom_revise |
review_confirm |
编辑打回确认 | accept_review / force_finalize |
section_help |
素材不足求助 | supplement / loose / delete |
resume 载荷字段(
UserInterventionPayload):action、approved_material_ids、extra_keywords、user_query(自然语言补充检索)、edited_outline、outline_feedback、user_revision_notes、override_verdict、selected_issue_indices、section_decisions。 关键点:现有协议已经支持「自然语言 + 结构化 action」,这是对话改造的核心可复用资产。
REST(业务元数据,非图执行):app/gateway/routers/ai_writing.py,前缀 /api/ai-writing
(sessions CRUD、transcript 保存、article-types、后台挂起 /background + /progress 等)。
持久化:ai_writing_sessions 表(transcript 为 PortableLongText)
ai_writing_article_types(deerflow/persistence/...)。
配置:config.yaml → ai_writing(keyword_model、rank_model、max_materials、
use_builtin_agents.{researcher,outliner,writer,editor} 等)。
1.2 前端:open-canvas 子系统
- 主页面:
frontend-web/src/open-canvas/pages/AIWritingPage.tsx - 路由:
/page/canvas/ai-writing、/page/workspace/ai-writing - 状态机:
contexts/AIWritingContext.tsx(useReducer+ LangGraphuseStream) - 事件翻译/reducer:
hooks/useAIWritingStream.ts - 类型:
types/ai-writing.ts(WritingStatus、ProgressEvent、UserInterventionPayload、AIWritingRequest…) - 流式:LangGraph
useStream,assistantId: 'ai_writing',streamMode: ['values','updates','custom','messages-tuple'] - 要去掉的输入 UI(改造重点):
- 启动表单
components/ai-writing/AIWritingForm.tsx+WritingFormContext - 阶段暂停输入
components/ai-writing/InterventionCard.tsx(素材/大纲/草稿/审核四套内嵌表单)
- 启动表单
- 已存在的对话原型:
components/ai-writing/WritingSetupChat.tsx(用useThreadStream起一条 lead_agent 线程 +setup_writing工具审批卡回填表单)—— 这是「对话 → 结构化配置」的现成范式,可借鉴但不直接复用(它只管启动前配置)。
1.3 结论
- 状态机、流式、暂停/续跑协议、持久化都可复用,无需重写引擎。
- 改造本质 = 把「分散表单」换成「一个对话输入框 + 意图路由层」,
再把意图映射回现有的
action + payload(大部分场景), 少量新意图(如「对内容提问」「素材不足自动重搜」)需要新增协议/节点。
2. 目标与范围
2.1 必须满足
- 新页面/新路由,旧页面零改动(用户要求「初始的不用动」)。
- 底部单一对话输入框控制所有阶段交互,移除各阶段独立输入框。
- 强意图解析:准确识别用户自然语言意图并路由到正确动作,覆盖全流程 + 更多。
- 用户给修改意见后,若现有素材不足以满足要求 → 自动/建议重新检索素材再继续。
- 保留右侧草稿实时预览(可编辑)与时间线/进度展示。
2.2 明确不做(本期)
- 不改旧
AIWritingPage及其表单/干预卡组件。 - 不替换底层
ai_writinggraph 的四个阶段大脑逻辑(只加意图层与少量新协议)。 - 不引入新的状态管理库(继续
useReducer+useStream)。
3. 核心设计
3.1 总体思路:三层结构
┌─────────────────────────────────────────────────────────┐
│ 对话 UI 层(新) │
│ - 左:消息流(用户消息 + AI 阶段产物卡片 + 进度) │
│ - 底:统一智能输入框(唯一交互入口) │
│ - 右:草稿实时预览(复用 AIWritingDraftPanel) │
└───────────────┬─────────────────────────────────────────┘
│ 用户自然语言
▼
┌─────────────────────────────────────────────────────────┐
│ 意图解析层(新,核心) │
│ - 输入:用户文本 + 当前 WritingStatus + 上下文快照 │
│ - 输出:{ intent, action, payload, needResearch, ... } │
└───────────────┬─────────────────────────────────────────┘
│ 结构化 action + payload
▼
┌─────────────────────────────────────────────────────────┐
│ 执行层(复用现有) │
│ - 空闲态:startWriting(AIWritingRequest) │
│ - 暂停态:submitIntervention(UserInterventionPayload) │
│ - 运行中:stream.stop() → 续跑 / 新命令 │
│ - 提问态:旁路问答(不改写作状态) │
└─────────────────────────────────────────────────────────┘
设计原则:意图解析层是「翻译官」——把自由文本翻译成现有执行层已经懂的指令。
能复用 action + payload 的绝不新造协议;只有现有协议表达不了的才扩展。
3.2 意图分类体系(覆盖用户全流程 + 更多)
按「意图类别 → 触发时机(当前 status)→ 映射到的执行」组织。这是意图解析引擎的目标 schema。
A. 启动类(status = idle)
| 意图 | 说明 | 映射 |
|---|---|---|
start_writing |
用户描述要写什么 | 解析成 AIWritingRequest → startWriting() |
start_with_outline |
用户自带大纲 | AIWritingRequest.userOutline |
start_imitate |
样文仿写 | writingModeType=imitate + 样文 |
B. 素材类(status ≈ awaiting_material_confirm / 运行中)
| 意图 | 用户说法示例 | 映射 |
|---|---|---|
confirm_materials |
「素材可以,继续」 | material_confirm + confirm(可带勾选 ids) |
research_again |
「重新搜索素材」 | material_confirm + re_search |
add_material / add_keyword |
「补充关于 X 的素材」「加个关键词 Y」 | material_confirm + supplement_search,user_query=X/Y |
use_skill_search |
「用 XX 技能查」 | material_confirm + enable_skill_search,user_query=技能/来源 |
select_materials |
「只用第 1、3 条」 | confirm + approved_material_ids 子集 |
drop_materials |
「去掉第 2 条」 | 同上,反选后 approved_material_ids |
C. 大纲类(status ≈ awaiting_outline_confirm)
| 意图 | 示例 | 映射 |
|---|---|---|
confirm_outline |
「大纲 OK」 | outline_confirm + confirm |
reoutline |
「重写大纲」 | outline_confirm + re_outline(outline_feedback 可空) |
revise_outline |
「第二章拆成两节」「调整顺序」 | re_outline + outline_feedback=自然语言 |
edit_outline_manually |
用户直接给出新大纲文本 | 解析成 edited_outline |
D. 写作/草稿类(status ≈ awaiting_draft_confirm / writing_draft)
| 意图 | 示例 | 映射 |
|---|---|---|
to_editor |
「交给编辑审核」 | draft_confirm + to_editor |
finalize |
「就这样定稿」 | draft_confirm + finalize |
revise_draft |
「第三段太啰嗦,精简」「补个案例」 | draft_confirm + user_revise,user_revision_notes=意见 |
rewrite_section |
「重写第二章」 | user_revise + 定位信息(章节)写进 notes |
change_tone/style |
「更口语一点」 | user_revise + notes |
E. 编辑审核类(status ≈ awaiting_review_confirm)
| 意图 | 示例 | 映射 |
|---|---|---|
accept_review |
「按编辑意见改」 | review_confirm + accept_review(可带 selected_issue_indices) |
partial_accept_review |
「只接受第 1、2 条意见」 | accept_review + selected_issue_indices |
force_finalize |
「不改了,强制定稿」 | review_confirm + force_finalize |
add_review_opinion |
「你再补一条:检查数据准确性」 | 追加自定义审核意见(见 5.3 扩展) |
F. 素材不足求助类(status = awaiting_section_help)
| 意图 | 映射 |
|---|---|
section_supplement |
section_help + supplement(去重搜) |
section_loose |
section_help + loose(放宽续写) |
section_delete |
section_help + delete |
G. 问答类(新,任意 status,旁路,不改写作状态)
| 意图 | 示例 | 映射 |
|---|---|---|
ask_about_content |
「第二章讲了啥?」「为什么用这个论点?」 | 旁路问答(见 5.4) |
ask_meta |
「现在进行到哪一步?」「用了哪些素材?」 | 读当前 state 直接回答,不走图 |
H. 控制类(新/半新)
| 意图 | 映射 |
|---|---|
undo/back |
回退到上一暂停点(依赖 checkpoint 回滚,见 8.4) |
restart_stage |
重跑当前阶段 |
run_background |
挂起后台自动跑 → POST /sessions/{id}/background |
cancel |
stream.stop() |
unknown/clarify |
无法判定 → 反问澄清(对话追问,不误触发) |
覆盖「甚至更多」:G(问答)、H(控制)、以及 D/E 里「素材不足自动回退重搜」都是超出原表单能力的新增。
3.3 「素材不足自动重搜」的判定与回退(用户强调的重点)
触发场景:用户在大纲/草稿/审核阶段提出修改意见,但意见需要的信息现有素材里没有。
判定放在意图解析层输出一个 needResearch: boolean + researchQuery,判定来源二选一(推荐组合):
- 意图解析 LLM 直接判断:把「当前素材摘要(keywords + 每条 title/来源)」喂给意图解析
prompt,让它判断「用户这条意见能否用现有素材满足」。不能 →
needResearch=true并给出检索词。 - 执行阶段兜底:
writer_draft严格模式本就有blocked_sections→pause_section_help, 这是已有的素材不足闸门;对话层把它翻译成「素材不足,是否重新检索?」的对话追问。
回退动作:needResearch=true 时,先走 material_confirm + supplement_search(或新的
「带目标的重搜」协议,见 8.3)补素材,补完自动继续用户原本的修改意图(需要在对话层
记住「pending 修改意图」,重搜结束后再提交)。
4. 意图解析引擎:方案对比与推荐
| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| A. 后端 LLM 意图节点(推荐) | 新增 /api/ai-writing/intent/parse(或图内新节点),后端用 LLM 把 {text, status, contextSnapshot} → 结构化意图 JSON |
能吃到完整 state(素材/大纲/草稿);可判 needResearch;prompt/模型集中可控;离线内网可用配置模型 |
多一次网络往返(可接受,交互本就等待) |
| B. 前端调模型接口分类 | 前端直接调现有 /api/models 聊天做分类 |
少一层后端改动 | 前端难拿全后端 state 快照;prompt 分散;鉴权/模型选择复杂 |
| C. 规则 + LLM 混合 | 常见短句走关键词规则快路径,兜底 LLM | 快、省 token | 规则易漏、维护成本高;中文表达多样命中率低 |
推荐 A(后端 LLM 意图节点),并在其上叠加 C 的快路径做优化(如「确认/继续/定稿」等 高频短句先走轻量规则,未命中再调 LLM)。理由:
- 意图判定需要当前写作上下文(尤其
needResearch判定),后端天然持有 state/checkpoint。 - 与现有「配置助手
setup_writing」一脉相承,模型选择/离线部署策略统一。 - 输出 schema 稳定,前端只消费结构化结果,UI 简单。
4.1 意图解析输出契约(建议)
{
"intent": "revise_draft", // 见第 3.2 分类
"pausePoint": "draft_confirm", // 若需 resume;否则 null
"action": "user_revise", // 映射到现有 action;新意图可为新值
"payload": { // 直接可喂 submitIntervention 的字段
"userRevisionNotes": "第三段精简,并补一个 2024 年的案例"
},
"needResearch": true, // 素材是否不足
"researchQuery": "2024 行业案例", // needResearch 时的检索意图
"answer": null, // 问答类意图的直接回答(旁路)
"confidence": 0.86,
"clarify": null // 低置信时的反问话术
}
前端拿到后:
answer非空 → 直接在对话流渲染回答(问答旁路,不动写作状态)。clarify非空或confidence低 → 渲染反问,等用户澄清(防误触发)。needResearch=true→ 先重搜,缓存payload为 pending,重搜完再提交。- 否则 →
submitIntervention({action, ...payload})或startWriting(...)。
5. 前端改造方案
5.1 新页面与路由
- 新目录:
frontend-web/src/open-canvas/pages/AIWritingChatPage.tsx(或conversational/子目录)。 - 新路由:
/page/canvas/ai-writing-chat(在OpenCanvasRoutes.tsx注册;旧路由保留)。 - 侧边栏:
sidebar-menu.ts加一个入口(如「AI 写作(对话版)」),或先不加、内部灰度。
5.2 复用 vs 新建
| 复用(尽量不改) | 新建 |
|---|---|
AIWritingContext / useAIWritingStream(状态机 + 流式) |
统一对话输入框组件 WritingChatComposer |
AIWritingDraftPanel(右侧草稿预览) |
消息流容器 WritingChatThread(渲染阶段产物为消息卡片) |
types/ai-writing.ts(按需扩展) |
意图解析客户端 api/ai-writing-intent.ts |
ProgressEvent 事件模型 |
阶段产物 → 消息卡片的适配器 progressToMessage.ts |
| 会话持久化 REST | 「pending 意图」暂存逻辑(重搜后续跑) |
不复用:AIWritingForm / WritingFormContext / InterventionCard(这三个就是要去掉的表单)。
5.3 交互流程(对话版)
- 空闲:用户在底部输入「写一篇关于 X 的深度分析,2000 字」→ 意图解析
start_writing→ 组装AIWritingRequest→startWriting()。 - 运行中:
ProgressEvent(research_progress/outline_chunk/draft_chunk…) → 适配成消息流里的进度卡片/流式卡片(复用现有流式视图组件的展示部分)。 - 到达暂停点:不再弹
InterventionCard,而是在消息流里展示阶段产物卡片 (素材列表 / 大纲 / 草稿 / 审核结果)+ 一句提示「你可以说:确认继续 / 补充素材 / 改大纲…」。 用户下一句自然语言 → 意图解析 →submitIntervention。 - 问答:任意时刻用户提问 → 旁路回答,不打断写作状态。
- 素材不足:意图解析
needResearch或后端section_help→ 对话追问「素材不足,帮你重搜?」 → 确认后重搜 → 自动续跑 pending 意图。
5.4 问答旁路(新能力)
「对写作内容提问」不应改写作 state。两种实现:
- 轻量(推荐先做):意图解析节点直接带回
answer(后端能读 state,一次调用出答案)。 - 完整:新起一条只读问答线程(lead_agent,context 注入当前草稿/大纲作为背景),
类似
WritingSetupChat的useThreadStream模式,但只读不回填。
6. 后端改造方案
6.1 意图解析入口(二选一)
- 方案 A1(推荐,独立 REST):
app/gateway/routers/ai_writing.py新增POST /api/ai-writing/sessions/{id}/intent,入参{text},后端读该 session 的 checkpoint state(素材/大纲/草稿摘要)+ 用ai_writing配置模型解析,返回第 4.1 的契约。- 好处:不改图拓扑;前端拿到结果后再决定调
startWriting/submitIntervention。
- 好处:不改图拓扑;前端拿到结果后再决定调
- 方案 A2(图内节点):在图里加
conversation_router节点。改动大、状态耦合高,不推荐首期。
6.2 意图解析 prompt
新增 agents/ai_writing/prompts/intent_router_prompts.py(或复用/扩展 intent_parser.py):
- 输入:用户文本、
current_status、素材摘要(keywords + 每条 title/source)、大纲纲要、 草稿字数/章节标题、审核意见列表。 - 输出:严格 JSON(第 4.1 契约)。要求模型只在证据充分时给高 confidence,
否则给
clarify(防误触发是硬指标)。
6.3 「带目标的重搜」协议扩展(可选增强)
现有 supplement_search 用 user_query 追加检索。为支持「改稿意见 → 缺素材 → 重搜 → 自动续跑」,
建议在 material_confirm 的 resume 里新增可选字段 resume_intent(重搜完成后要自动执行的原始意图)。
- 前端也可纯前端实现(重搜完成事件回来后再自动提交缓存的 pending payload),首期优先前端实现,避免动后端协议。
6.4 追加自定义审核意见(add_review_opinion)
editor/pause_review 目前只接受「接受/强制定稿」。要支持「你再补一条审核意见」,
需在 review_confirm resume 里接受 extra_review_notes,editor 或 writer_draft
修订时并入。首期可先降级为 user_revise + notes(把用户的审核诉求当作改稿意见),
完整版再扩协议。
6.5 不需要改的部分
- 四个阶段大脑节点(researcher/writer_outline/writer_draft/editor)逻辑不动。
- 五个暂停点、checkpoint、StreamBridge、持久化不动。
7. 数据流时序(改稿 + 素材不足自动重搜)
用户: "第三段补个2024年的行业案例"
│
▼ POST /api/ai-writing/sessions/{id}/intent { text }
后端: 读 state(素材摘要) + LLM 解析
│ 返回 { intent:revise_draft, action:user_revise,
│ payload:{userRevisionNotes:...},
│ needResearch:true, researchQuery:"2024 行业案例" }
▼
前端: needResearch=true → 缓存 pending=payload
│ 对话追问/直接执行: submitIntervention(material_confirm, supplement_search, user_query="2024 行业案例")
▼ LangGraph resume → researcher 补素材 → materials_ready
前端: 收到新素材 → 自动提交 pending
│ submitIntervention(draft_confirm, user_revise, userRevisionNotes=...)
▼ writer_draft 用新素材改稿 → draft_chunk... → draft_ready → pause_draft
前端: 消息流展示新草稿 + "已按你的意见改写并补充了案例"
8. 关键实现细节与坑
- 暂停态识别:对话层必须知道「现在停在哪个 pause_point」才能正确映射 action。
AIWritingContext已有currentPause/status,直接读。 - 防误触发:低置信意图一律走
clarify反问,绝不猜着执行(尤其finalize/re_search这种代价大的)。 - pending 意图队列:素材不足重搜是「先插一段、再回原意图」,需要一个小状态机记住 pending, 并处理「重搜后用户又改主意」的情况(重搜完成前允许覆盖 pending)。
- 回退/undo(H 类):依赖 checkpoint 回滚,
runtime/runs/worker.py有rollback概念, 首期可不做或只做「重跑当前阶段」。 - 问答旁路不污染 transcript:问答消息可标记
kind:'qa',与写作进度事件区分持久化。 - 流式产物→消息卡片:复用现有
OutlineStreamingView/DraftStreamingPreview等的展示部分, 套进消息气泡即可,不必重写渲染。 - 离线内网:意图解析模型走
config.yaml → ai_writing里的配置模型;检索仍走技能/内网 ES, 不联网(与现有并行席位「禁 web_search」的离线策略一致)。
9. 分阶段实施路线图
阶段 0 — 骨架(不含意图)
- 新路由 + 新页面
AIWritingChatPage,复用AIWritingContext/useStream/DraftPanel。 - 底部对话框先只做「空闲
start_writing+ 暂停点固定按钮」跑通端到端(相当于把干预卡换成消息)。
阶段 1 — 意图解析(核心)
- 后端
POST /sessions/{id}/intent+ intent prompt,返回第 4.1 契约。 - 前端接入:自然语言 → 意图 →
startWriting/submitIntervention,覆盖 A~F 类意图。 - 加高频短句规则快路径 + 低置信
clarify反问。
阶段 2 — 问答旁路 + 素材不足自动重搜
- G 类问答(先用 intent 节点直接带
answer)。 needResearch+ pending 意图机制(前端实现重搜后自动续跑)。
阶段 3 — 增强协议(按需)
add_review_opinion扩review_confirm协议、resume_intent后端化、undo/回退。
阶段 4 — 打磨
- 消息卡片视觉、历史回看(复用 transcript)、后台挂起入口、灰度到侧边栏。
10. 关键文件清单(落地时对照)
新增(前端)
open-canvas/pages/AIWritingChatPage.tsx— 新页面open-canvas/components/ai-writing-chat/WritingChatComposer.tsx— 统一输入框open-canvas/components/ai-writing-chat/WritingChatThread.tsx— 消息流open-canvas/components/ai-writing-chat/progressToMessage.ts— 事件→消息适配open-canvas/api/ai-writing-intent.ts— 意图解析客户端- 路由注册:
open-canvas/OpenCanvasRoutes.tsx(+ 可选sidebar-menu.ts)
新增(后端)
app/gateway/routers/ai_writing.py— 加POST /sessions/{id}/intentagents/ai_writing/prompts/intent_router_prompts.py— 意图 prompt- (阶段 3)
state.py/nodes/*— 扩review_confirm、resume_intent
复用(尽量不改)
open-canvas/contexts/AIWritingContext.tsx、hooks/useAIWritingStream.tsopen-canvas/components/ai-writing/AIWritingDraftPanel.tsx及各流式展示组件open-canvas/types/ai-writing.ts(按需扩字段)agents/ai_writing/graph.py、四个阶段节点、五个暂停点
不动
- 旧
open-canvas/pages/AIWritingPage.tsx及AIWritingForm/WritingFormContext/InterventionCard
11. 待确认
- 意图解析引擎是否采用推荐的方案 A(后端 LLM 节点)?
- 新页面是独立入口(侧边栏新增)还是先内部灰度(仅直链)?
- 「素材不足自动重搜」首期是否接受前端实现 pending 续跑(不动后端 resume 协议)?
- 问答旁路首期是否接受意图节点直接带答案(不新起只读线程)?
以上默认按「A / 内部灰度 / 前端 pending / 意图节点带答案」推进,如需调整请指出。