feat(aia): Protocol Agent MVP complete with one-click generation and Word export
- Add one-click research protocol generation with streaming output - Implement Word document export via Pandoc integration - Add dynamic dual-panel layout with resizable split pane - Implement collapsible content for StatePanel stages - Add conversation history management with title auto-update - Fix scroll behavior, markdown rendering, and UI layout issues - Simplify conversation creation logic for reliability
This commit is contained in:
@@ -1,57 +1,80 @@
|
||||
# 一键生成研究方案 - 开发计划
|
||||
|
||||
> **版本**: v1.1
|
||||
> **版本**: v2.0
|
||||
> **创建日期**: 2026-01-24
|
||||
> **最后更新**: 2026-01-24
|
||||
> **最后更新**: 2026-01-25
|
||||
> **负责人**: AI Assistant
|
||||
> **状态**: 待开发
|
||||
> **状态**: 开发中
|
||||
|
||||
---
|
||||
|
||||
## 一、功能概述
|
||||
|
||||
### 目标
|
||||
基于 Protocol Agent 收集的 5 个核心要素,一键生成完整的临床研究方案文档,支持在线编辑和 Word 导出。
|
||||
基于 Protocol Agent 收集的 5 个核心要素,一键生成完整的临床研究方案文档,支持对话式修改和 Word 导出。
|
||||
|
||||
### 核心价值
|
||||
- 将 5-10 小时的方案撰写工作缩短至 30 分钟
|
||||
- AI 生成 + 人工编辑,保证专业性和个性化
|
||||
- AI 生成 + 对话式修改 + Word 精修,保证专业性和个性化
|
||||
- 输出符合伦理委员会审查要求的标准文档
|
||||
|
||||
### 技术策略:No-Editor First(无编辑器优先)
|
||||
|
||||
**核心洞察**:
|
||||
> 用户最终会在 Word 中精修,我们的价值是 **"AI 生成高质量初稿"**,而不是 **"提供一个编辑器"**。
|
||||
|
||||
---
|
||||
|
||||
## 二、交互设计:两阶段渐进式生成
|
||||
## 二、交互设计:对话流生成
|
||||
|
||||
### 第一阶段:对话框生成摘要
|
||||
### 用户流程
|
||||
|
||||
```
|
||||
用户点击"一键生成研究方案"
|
||||
用户完成 5 阶段要素收集
|
||||
↓
|
||||
AI 在对话框中流式输出研究方案摘要(约500字)
|
||||
点击"一键生成研究方案"
|
||||
↓
|
||||
用户确认摘要 → 进入第二阶段
|
||||
用户不满意 → 在对话中继续调整要素
|
||||
AI 在对话框中流式输出完整方案(Markdown)
|
||||
↓
|
||||
用户对话修改:"把样本量部分改一下..."
|
||||
↓
|
||||
AI 流式输出修改后的章节
|
||||
↓
|
||||
用户满意 → 点击"导出 Word"
|
||||
↓
|
||||
下载符合伦理委员会格式的 Word 文档
|
||||
```
|
||||
|
||||
**摘要内容**:
|
||||
- 研究题目
|
||||
- 研究目的(主要/次要)
|
||||
- 研究设计概述
|
||||
- 样本量结论
|
||||
- 主要结局指标
|
||||
|
||||
### 第二阶段:方案编辑器生成完整方案
|
||||
### 交互示意
|
||||
|
||||
```
|
||||
用户点击"生成完整方案"
|
||||
↓
|
||||
跳转到方案编辑器页面
|
||||
↓
|
||||
流式生成完整研究方案(5000-8000字)
|
||||
↓
|
||||
用户在线编辑 / AI协作润色
|
||||
↓
|
||||
导出 Word 文档
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ← 返回 全流程研究方案制定 │
|
||||
├────────────────────────────────────────┬────────────────────┤
|
||||
│ │ 📋 研究方案状态 │
|
||||
│ [AI] 根据您确认的信息,我已生成研究方案: │ │
|
||||
│ │ ✅ 科学问题 │
|
||||
│ # 1. 研究题目 │ ✅ PICO要素 │
|
||||
│ 糖尿病患者使用二甲双胍与格列美脲... │ ✅ 研究设计 │
|
||||
│ │ ✅ 样本量 │
|
||||
│ # 2. 研究背景 │ ✅ 观察指标 │
|
||||
│ 2型糖尿病是全球性公共卫生问题... │ │
|
||||
│ │ ──────────────── │
|
||||
│ ... │ │
|
||||
│ │ [📥 导出 Word] │
|
||||
│ ┌────────────────────────────────┐ │ [🔄 重新生成] │
|
||||
│ │ 📥 导出Word │ 🔄 修改本章节 │ │ │
|
||||
│ └────────────────────────────────┘ │ │
|
||||
│ │ │
|
||||
│ [用户] 把样本量从200改成300,并说明原因 │ │
|
||||
│ │ │
|
||||
│ [AI] 好的,我已修改样本量部分: │ │
|
||||
│ ## 6. 样本量估算 │ │
|
||||
│ 根据前期预实验数据...样本量调整为300例 │ │
|
||||
│ │ │
|
||||
├────────────────────────────────────────┴────────────────────┤
|
||||
│ [输入消息...] [发送] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
@@ -79,229 +102,344 @@ AI 在对话框中流式输出研究方案摘要(约500字)
|
||||
|
||||
---
|
||||
|
||||
## 四、方案编辑器设计
|
||||
## 四、技术方案
|
||||
|
||||
### 布局结构
|
||||
### 架构概览
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ ← 返回 📄 研究方案编辑器 [自动保存✓] [导出Word] [发布] │
|
||||
├──────────┬─────────────────────────────────────┬───────────────┤
|
||||
│ 📑 大纲 │ 📝 编辑区 │ 🤖 AI助手 │
|
||||
│ │ │ │
|
||||
│ 可点击 │ Notion 风格分块编辑 │ 选中文本后: │
|
||||
│ 快速跳转 │ 支持 Markdown + 富文本 │ - /ai 润色 │
|
||||
│ │ Slash 命令 (/) │ - /ai 扩写 │
|
||||
│ │ 拖拽排序章节 │ - /ai 精简 │
|
||||
└──────────┴─────────────────────────────────────┴───────────────┘
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ 前端 │ │ Node.js │ │ Python │
|
||||
│ ChatArea │ ──▶ │ Backend │ ──▶ │ Service │
|
||||
│ │ │ │ │ (Pandoc) │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
│ │ │
|
||||
│ 流式输出 │ 调用 LLM │ Markdown→Word
|
||||
│ Markdown │ 生成方案 │ 格式转换
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 用户下载 Word │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 核心功能
|
||||
|
||||
| 功能 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| **Slash 命令** | 输入 / 唤起菜单,支持 /ai 调用生成 | P0 |
|
||||
| **分块编辑** | 每个章节独立编辑,支持拖拽排序 | P0 |
|
||||
| **大纲导航** | 左侧目录,点击跳转 | P0 |
|
||||
| **自动保存** | 每30秒 + 失焦时保存 | P0 |
|
||||
| **导出Word** | Tiptap JSON → docx | P0 |
|
||||
| **AI润色** | 选中文本,/ai polish 优化 | P1 |
|
||||
| **AI扩写** | 选中章节,/ai expand 补充 | P1 |
|
||||
| **Ghost Text** | AI 生成时显示幽灵文字预览 | P1 |
|
||||
| **版本历史** | 查看修改记录,回滚 | P2 |
|
||||
|
||||
---
|
||||
|
||||
## 五、技术方案
|
||||
|
||||
### 技术选型:Novel (Fork)
|
||||
|
||||
**选型结论**:Fork [Novel](https://github.com/steven-tey/novel) 源码,而非 npm 包引入。
|
||||
|
||||
**选择 Novel 的理由**:
|
||||
|
||||
| 因素 | Novel 优势 |
|
||||
|------|-----------|
|
||||
| **AI 原生** | 专为 AI 写作设计,已处理流式生成的 UX 细节(Ghost Text、光标锁定) |
|
||||
| **标准 Tiptap** | 直接暴露 Tiptap 配置,可插入交互组件(样本量计算器、引用卡片) |
|
||||
| **可控性** | 源码在手,100% 可定制 UI 和逻辑 |
|
||||
| **对接成本** | 替换 useCompletion → useAIStream 即可对接现有后端 |
|
||||
|
||||
**Fork 策略**:
|
||||
### 技术栈
|
||||
|
||||
```
|
||||
不要 npm install novel
|
||||
|
||||
将 novel/packages/core/src 复制到:
|
||||
frontend-v2/src/shared/components/ProtocolEditor/
|
||||
|
||||
目录结构:
|
||||
├── index.tsx # 主组件
|
||||
├── extensions/ # Tiptap 扩展
|
||||
│ ├── ai-autocomplete.ts # 替换为 useAIStream
|
||||
│ ├── slash-command.tsx # 保留 Slash 菜单
|
||||
│ ├── citation.ts # Phase 2: 文献引用
|
||||
│ └── medical-table.ts # Phase 2: 复杂表格
|
||||
├── components/ # UI 组件
|
||||
│ ├── EditorContent.tsx
|
||||
│ ├── SlashMenu.tsx
|
||||
│ └── BubbleMenu.tsx
|
||||
└── styles/ # 样式(处理 Tailwind 冲突)
|
||||
└── editor.css
|
||||
```
|
||||
|
||||
**Tailwind CSS 冲突处理**:
|
||||
|
||||
```css
|
||||
/* 方案:CSS 命名空间隔离 */
|
||||
.novel-editor-scope {
|
||||
/* Novel 的 Tailwind 样式限制在此作用域 */
|
||||
}
|
||||
```
|
||||
|
||||
### 技术栈总览
|
||||
|
||||
```
|
||||
前端编辑器:Novel (Fork) - 基于 Tiptap/ProseMirror
|
||||
├── 优点:AI 原生、Notion 风格、源码可控
|
||||
├── 核心:Slash 命令、Ghost Text、拖拽排序
|
||||
前端:复用现有 ChatArea + useAIStream
|
||||
├── 增加"导出 Word"按钮
|
||||
├── 增加"修改本章节"交互
|
||||
│
|
||||
AI 调用:复用现有 useAIStream Hook
|
||||
├── 替换 Novel 的 useCompletion (Vercel AI SDK)
|
||||
├── 对接 /api/v1/aia/protocol-agent/generate
|
||||
后端 (Node.js):
|
||||
├── POST /api/v1/aia/protocol-agent/generate/full # 生成完整方案
|
||||
├── POST /api/v1/aia/protocol-agent/regenerate # 重新生成指定章节
|
||||
├── POST /api/v1/aia/protocol-agent/export/docx # 导出 Word
|
||||
│
|
||||
文档导出:docx.js 或 @tiptap-pro/extension-export-docx
|
||||
│
|
||||
数据存储:PostgreSQL (protocol_generations 表)
|
||||
后端 (Python):
|
||||
├── Pandoc 集成
|
||||
├── Reference Doc 模板控制样式
|
||||
```
|
||||
|
||||
### 数据模型
|
||||
|
||||
```sql
|
||||
-- 方案生成记录表
|
||||
CREATE TABLE protocol_generations (
|
||||
id UUID PRIMARY KEY,
|
||||
conversation_id UUID REFERENCES conversations(id),
|
||||
user_id UUID REFERENCES users(id),
|
||||
|
||||
-- 内容
|
||||
summary TEXT, -- 摘要(第一阶段)
|
||||
full_content JSONB, -- 完整方案(Tiptap JSON,标准格式)
|
||||
|
||||
-- 状态
|
||||
status VARCHAR(20), -- draft | generating | completed
|
||||
version INT DEFAULT 1,
|
||||
|
||||
-- 元数据
|
||||
word_file_url TEXT, -- 导出的Word文件URL
|
||||
created_at TIMESTAMP,
|
||||
updated_at TIMESTAMP
|
||||
);
|
||||
-- 方案生成记录表(复用现有 protocol_generations)
|
||||
ALTER TABLE protocol_schema.protocol_generations ADD COLUMN IF NOT EXISTS
|
||||
sections JSONB; -- 分章节存储,支持局部修改
|
||||
|
||||
-- sections 结构示例
|
||||
{
|
||||
"title": "糖尿病患者使用二甲双胍...",
|
||||
"background": "2型糖尿病是全球性...",
|
||||
"objectives": "...",
|
||||
"design": "...",
|
||||
"subjects": "...",
|
||||
"sample_size": "...",
|
||||
"implementation": "...",
|
||||
"endpoints": "...",
|
||||
"data_management": "...",
|
||||
"safety": "...",
|
||||
"statistics": "...",
|
||||
"ethics": "...",
|
||||
"timeline": "...",
|
||||
"references": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### API 设计
|
||||
|
||||
```typescript
|
||||
// 第一阶段:生成摘要(流式)
|
||||
POST /api/v1/aia/protocol-agent/generate/summary
|
||||
Request: { conversationId: string }
|
||||
Response: SSE 流式输出摘要
|
||||
|
||||
// 第二阶段:生成完整方案(流式)
|
||||
// 生成完整方案(流式)
|
||||
POST /api/v1/aia/protocol-agent/generate/full
|
||||
Request: { conversationId: string }
|
||||
Response: SSE 流式输出完整方案
|
||||
Response: SSE 流式输出完整 Markdown
|
||||
|
||||
// 保存编辑
|
||||
PUT /api/v1/aia/protocol-agent/generation/:id
|
||||
Request: { content: TiptapJSON }
|
||||
|
||||
// 导出Word
|
||||
POST /api/v1/aia/protocol-agent/generation/:id/export
|
||||
Response: { downloadUrl: string }
|
||||
|
||||
// AI编辑(润色/扩写)
|
||||
POST /api/v1/aia/protocol-agent/generation/:id/ai-edit
|
||||
// 重新生成指定章节(流式)
|
||||
POST /api/v1/aia/protocol-agent/regenerate
|
||||
Request: {
|
||||
action: 'polish' | 'expand' | 'simplify',
|
||||
selectedText: string,
|
||||
context: string
|
||||
conversationId: string,
|
||||
section: 'sample_size' | 'background' | ...,
|
||||
instruction: "把样本量从200改成300"
|
||||
}
|
||||
Response: SSE 流式输出
|
||||
Response: SSE 流式输出该章节
|
||||
|
||||
// 导出 Word
|
||||
POST /api/v1/aia/protocol-agent/export/docx
|
||||
Request: { conversationId: string }
|
||||
Response: Binary (application/vnd.openxmlformats-officedocument.wordprocessingml.document)
|
||||
|
||||
// 获取当前方案内容
|
||||
GET /api/v1/aia/protocol-agent/generation/:conversationId
|
||||
Response: { sections: {...}, fullMarkdown: "...", version: 3 }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、核心实现
|
||||
|
||||
### 5.1 Python 微服务:Pandoc 转换器
|
||||
|
||||
**Dockerfile 增加依赖:**
|
||||
```dockerfile
|
||||
RUN apt-get update && apt-get install -y pandoc
|
||||
RUN pip install pypandoc
|
||||
```
|
||||
|
||||
**Service 代码:**
|
||||
```python
|
||||
# python-service/app/services/doc_service.py
|
||||
import pypandoc
|
||||
import os
|
||||
|
||||
def convert_md_to_docx(markdown_text: str, output_path: str):
|
||||
# 使用参考文档控制样式(字体、字号、页眉)
|
||||
reference_doc = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'assets/protocol_template.docx'
|
||||
)
|
||||
|
||||
pypandoc.convert_text(
|
||||
markdown_text,
|
||||
'docx',
|
||||
format='markdown',
|
||||
outputfile=output_path,
|
||||
extra_args=[f'--reference-doc={reference_doc}']
|
||||
)
|
||||
return output_path
|
||||
```
|
||||
|
||||
**API 端点:**
|
||||
```python
|
||||
# python-service/app/routers/doc_router.py
|
||||
from fastapi import APIRouter, Response
|
||||
from ..services.doc_service import convert_md_to_docx
|
||||
import tempfile
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/convert/docx")
|
||||
async def convert_to_docx(request: dict):
|
||||
markdown = request.get("content", "")
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.docx', delete=False) as f:
|
||||
output_path = f.name
|
||||
|
||||
convert_md_to_docx(markdown, output_path)
|
||||
|
||||
with open(output_path, 'rb') as f:
|
||||
content = f.read()
|
||||
|
||||
return Response(
|
||||
content=content,
|
||||
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
)
|
||||
```
|
||||
|
||||
### 5.2 Node.js 后端:导出 API
|
||||
|
||||
```typescript
|
||||
// backend/src/modules/agent/protocol/controllers/ProtocolGenerateController.ts
|
||||
|
||||
export class ProtocolGenerateController {
|
||||
|
||||
// 生成完整方案
|
||||
async generateFull(request: FastifyRequest, reply: FastifyReply) {
|
||||
const { conversationId } = request.body as { conversationId: string };
|
||||
|
||||
// 获取 Protocol Context(5 阶段数据)
|
||||
const context = await this.contextService.getContext(conversationId);
|
||||
|
||||
// 构建生成 Prompt
|
||||
const prompt = this.buildGeneratePrompt(context);
|
||||
|
||||
// 流式生成
|
||||
const streamingService = createStreamingService(reply, {
|
||||
onChunk: async (chunk) => {
|
||||
// 保存到数据库
|
||||
await this.saveChunk(conversationId, chunk);
|
||||
}
|
||||
});
|
||||
|
||||
await streamingService.streamGenerate([
|
||||
{ role: 'system', content: this.getSystemPrompt() },
|
||||
{ role: 'user', content: prompt }
|
||||
]);
|
||||
}
|
||||
|
||||
// 重新生成指定章节
|
||||
async regenerateSection(request: FastifyRequest, reply: FastifyReply) {
|
||||
const { conversationId, section, instruction } = request.body;
|
||||
|
||||
const context = await this.contextService.getContext(conversationId);
|
||||
const currentGeneration = await this.getGeneration(conversationId);
|
||||
|
||||
const prompt = `
|
||||
请根据以下指令修改"${section}"章节:
|
||||
|
||||
用户指令:${instruction}
|
||||
|
||||
当前章节内容:
|
||||
${currentGeneration.sections[section]}
|
||||
|
||||
研究背景信息:
|
||||
${JSON.stringify(context)}
|
||||
|
||||
请输出修改后的完整章节内容(Markdown 格式):
|
||||
`;
|
||||
|
||||
// 流式输出修改后的章节
|
||||
const streamingService = createStreamingService(reply);
|
||||
await streamingService.streamGenerate([
|
||||
{ role: 'system', content: '你是临床研究方案撰写专家...' },
|
||||
{ role: 'user', content: prompt }
|
||||
]);
|
||||
}
|
||||
|
||||
// 导出 Word
|
||||
async exportDocx(request: FastifyRequest, reply: FastifyReply) {
|
||||
const { conversationId } = request.body as { conversationId: string };
|
||||
|
||||
// 获取完整 Markdown
|
||||
const generation = await this.getGeneration(conversationId);
|
||||
const markdown = this.sectionsToMarkdown(generation.sections);
|
||||
|
||||
// 调用 Python 微服务转换
|
||||
const response = await axios.post(
|
||||
`${PYTHON_SERVICE_URL}/convert/docx`,
|
||||
{ content: markdown },
|
||||
{ responseType: 'arraybuffer' }
|
||||
);
|
||||
|
||||
reply.header('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
|
||||
reply.header('Content-Disposition', 'attachment; filename="research_protocol.docx"');
|
||||
return reply.send(response.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 前端:ChatArea 增强
|
||||
|
||||
```tsx
|
||||
// frontend-v2/src/modules/aia/protocol-agent/components/ChatArea.tsx
|
||||
|
||||
// 在消息气泡中增加导出按钮
|
||||
const ProtocolMessageActions = ({ content, conversationId }) => {
|
||||
const [exporting, setExporting] = useState(false);
|
||||
|
||||
const handleExport = async () => {
|
||||
setExporting(true);
|
||||
try {
|
||||
const response = await fetch('/api/v1/aia/protocol-agent/export/docx', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ conversationId })
|
||||
});
|
||||
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = '研究方案.docx';
|
||||
a.click();
|
||||
} finally {
|
||||
setExporting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="protocol-message-actions">
|
||||
<Button
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={handleExport}
|
||||
loading={exporting}
|
||||
>
|
||||
导出 Word
|
||||
</Button>
|
||||
<Button icon={<EditOutlined />}>
|
||||
修改章节
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、开发计划
|
||||
|
||||
### Phase 1:Fork Novel + 对接后端(3天)
|
||||
|
||||
**目标**:3天内跑通"编辑器 + AI 流式生成"
|
||||
### Phase 1:MVP 核心功能(2 天)
|
||||
|
||||
| 天数 | 任务 | 交付物 |
|
||||
|------|------|--------|
|
||||
| Day 1 | Fork Novel 源码 + 项目集成 | 编辑器基础渲染、Slash 菜单可用 |
|
||||
| Day 2 | 替换 AI 调用 → useAIStream | /ai 命令可调用后端生成 |
|
||||
| Day 3 | 摘要生成 API + 编辑器页面路由 | 完整的"对话→摘要→编辑器"流程 |
|
||||
| Day 1 | Python 微服务集成 Pandoc | `/convert/docx` API 可用 |
|
||||
| Day 1 | Node.js 导出 API | `/export/docx` 端点完成 |
|
||||
| Day 2 | 前端导出按钮 | ChatArea 增加导出功能 |
|
||||
| Day 2 | 完整方案生成 Prompt | 生成质量优化 |
|
||||
|
||||
**Phase 1 交付**:
|
||||
- ✅ Notion 风格编辑器可用
|
||||
- ✅ /ai 命令可调用 Protocol Agent
|
||||
- ✅ 支持 Markdown 导出
|
||||
- ✅ 一键生成完整研究方案(Markdown)
|
||||
- ✅ 导出符合格式的 Word 文档
|
||||
- ✅ 基础对话式修改
|
||||
|
||||
### Phase 2:完整方案生成 + Word 导出(3天)
|
||||
### Phase 2:分章节修改(2 天)
|
||||
|
||||
| 天数 | 任务 | 交付物 |
|
||||
|------|------|--------|
|
||||
| Day 4 | 完整方案生成 API(流式) | 编辑器中流式显示完整方案 |
|
||||
| Day 5 | 自动保存 + 版本管理 | 数据库存储、草稿恢复 |
|
||||
| Day 6 | Word 导出功能 | docx 文件下载 |
|
||||
| Day 3 | 分章节存储模型 | sections JSONB 字段 |
|
||||
| Day 3 | 章节重新生成 API | `/regenerate` 端点 |
|
||||
| Day 4 | 前端章节选择器 | 点击章节触发修改对话 |
|
||||
| Day 4 | 版本历史 | 修改记录存储 |
|
||||
|
||||
**Phase 2 交付**:
|
||||
- ✅ 完整方案流式生成
|
||||
- ✅ 自动保存
|
||||
- ✅ Word 导出
|
||||
- ✅ 分章节存储和修改
|
||||
- ✅ 智能识别修改章节
|
||||
- ✅ 版本历史追踪
|
||||
|
||||
### Phase 3:医疗特性增强(4天)
|
||||
### Phase 3:体验优化(1 天)
|
||||
|
||||
| 天数 | 任务 | 交付物 |
|
||||
|------|------|--------|
|
||||
| Day 7 | 集成 Tiptap Table 扩展 | 复杂表格支持(访视排期表) |
|
||||
| Day 8 | 开发 CitationBlock | 文献引用组件(对接 PKB) |
|
||||
| Day 9 | AI 润色/扩写优化 | 选中文本 AI 编辑体验 |
|
||||
| Day 10 | 测试 + UI 美化 | 完整功能测试 |
|
||||
|
||||
**Phase 3 交付**:
|
||||
- ✅ 复杂表格支持
|
||||
- ✅ 文献引用功能
|
||||
- ✅ AI 协作编辑完善
|
||||
| Day 5 | Word 模板优化 | 符合伦理委员会格式 |
|
||||
| Day 5 | UI 美化 | 导出进度、预览 |
|
||||
| Day 5 | 测试与修复 | 完整功能测试 |
|
||||
|
||||
---
|
||||
|
||||
## 七、风险与依赖
|
||||
## 七、与编辑器方案对比
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| Tailwind CSS 冲突 | 样式混乱 | CSS 命名空间隔离 |
|
||||
| Novel 源码维护成本 | 后续升级困难 | 代码量小(~2000行),可独立维护 |
|
||||
| LLM 生成质量不稳定 | 方案内容不专业 | 优化 Prompt + 人工模板 |
|
||||
| 长文本生成超时 | 用户等待过久 | 分章节流式生成 |
|
||||
| Word 导出格式问题 | 格式错乱 | 预设 Word 模板 |
|
||||
| 维度 | 无编辑器方案(当前) | 编辑器方案(备选) |
|
||||
|------|---------------------|-------------------|
|
||||
| **开发周期** | 5 天 | 10 天 |
|
||||
| **维护成本** | 低(复用现有组件) | 高(Fork Novel) |
|
||||
| **格式控制** | ⭐⭐⭐⭐⭐ Pandoc 精确控制 | ⭐⭐⭐ 需自己处理 |
|
||||
| **修改体验** | 对话式(稍慢) | 直接编辑(更快) |
|
||||
| **用户习惯** | 符合(Word 精修) | 需适应新界面 |
|
||||
| **适用场景** | MVP / 快速验证 | 高频编辑场景 |
|
||||
|
||||
### 依赖项
|
||||
### 何时考虑上编辑器?
|
||||
|
||||
- [x] Protocol Agent 5阶段数据收集(已完成)
|
||||
- [x] StreamingService 流式输出(已完成)
|
||||
- [x] useAIStream Hook(已完成)
|
||||
- [ ] Novel 源码 Fork(待执行)
|
||||
- [ ] docx.js 导出功能(待开发)
|
||||
|
||||
### 兜底方案
|
||||
|
||||
如果 Fork 的 Novel 代码难以维护:
|
||||
- 可回退到 **Tiptap Headless**
|
||||
- 用 Ant Design 重写 UI
|
||||
- **数据模型 (Tiptap JSON) 完全兼容**,用户数据不丢失
|
||||
如果用户反馈以下问题,再考虑引入编辑器:
|
||||
1. "对话修改太慢,我想直接改某个字"
|
||||
2. "我需要频繁调整段落顺序"
|
||||
3. "我想同时看到多个章节并对比"
|
||||
|
||||
---
|
||||
|
||||
@@ -309,43 +447,69 @@ Response: SSE 流式输出
|
||||
|
||||
### 功能验收
|
||||
|
||||
- [ ] 点击"一键生成",对话框流式输出摘要
|
||||
- [ ] 点击"生成完整方案",跳转编辑器并流式生成
|
||||
- [ ] 编辑器支持 Slash 命令 (/)
|
||||
- [ ] 编辑器支持章节拖拽排序
|
||||
- [ ] 选中文本可调用 AI 润色/扩写
|
||||
- [ ] 可导出标准格式的 Word 文档
|
||||
- [ ] 支持复杂表格编辑
|
||||
- [ ] 点击"一键生成",对话框流式输出完整方案
|
||||
- [ ] 点击"导出 Word",下载格式正确的 docx 文件
|
||||
- [ ] 输入"修改样本量",AI 只重新生成该章节
|
||||
- [ ] Word 文档符合伦理委员会格式要求
|
||||
|
||||
### 性能指标
|
||||
|
||||
| 指标 | 目标 |
|
||||
|------|------|
|
||||
| 摘要生成时间 | < 30秒 |
|
||||
| 完整方案生成时间 | < 3分钟 |
|
||||
| 自动保存延迟 | < 1秒 |
|
||||
| Word导出时间 | < 5秒 |
|
||||
| 完整方案生成时间 | < 2 分钟 |
|
||||
| Word 导出时间 | < 5 秒 |
|
||||
| 章节修改响应时间 | < 30 秒 |
|
||||
|
||||
---
|
||||
|
||||
## 九、后续迭代
|
||||
## 九、Word 模板设计
|
||||
|
||||
- **v1.1**: 方案模板库(不同研究类型)
|
||||
- **v1.2**: 多人协作编辑
|
||||
- **v1.3**: 方案审核流程
|
||||
- **v1.4**: 与伦理系统对接
|
||||
### 格式要求(伦理委员会标准)
|
||||
|
||||
```
|
||||
字体:正文宋体小四,标题黑体三号
|
||||
行距:1.5 倍
|
||||
页边距:上下 2.54cm,左右 3.17cm
|
||||
页眉:研究方案 + 版本号
|
||||
页脚:页码
|
||||
```
|
||||
|
||||
### Reference Doc 制作
|
||||
|
||||
1. 在 Word 中创建符合格式的模板文档
|
||||
2. 定义样式:标题1、标题2、正文、表格等
|
||||
3. 保存为 `protocol_template.docx`
|
||||
4. 放置到 `python-service/app/services/assets/`
|
||||
|
||||
---
|
||||
|
||||
## 十、参考文档
|
||||
## 十、后续迭代
|
||||
|
||||
- [Novel GitHub](https://github.com/steven-tey/novel)
|
||||
- [Tiptap 官方文档](https://tiptap.dev/)
|
||||
- [编辑器选型深度评估](./编辑器选型深度评估与落地建议.md)
|
||||
- [Novel vs BlockNote 对比分析](./Novel_vs_BlockNote_深度对比分析.md)
|
||||
### v1.1:编辑器增强(可选)
|
||||
如果用户反馈需要更直接的编辑体验:
|
||||
- 引入 Novel/Tiptap 编辑器
|
||||
- 支持实时编辑 + AI 辅助
|
||||
|
||||
### v1.2:模板库
|
||||
- 不同研究类型的方案模板
|
||||
- RCT、队列研究、病例对照等
|
||||
|
||||
### v1.3:协作与审核
|
||||
- 多人协作编辑
|
||||
- 方案审核流程
|
||||
- 与伦理系统对接
|
||||
|
||||
---
|
||||
|
||||
## 十一、参考文档
|
||||
|
||||
- [基于对话流的文档生成技术方案](./基于对话流的文档生成与导出技术方案.md)
|
||||
- [编辑器选型深度评估](./编辑器选型深度评估与落地建议.md)(备选方案)
|
||||
- [Novel vs BlockNote 对比分析](./Novel_vs_BlockNote_深度对比分析.md)(备选方案)
|
||||
|
||||
---
|
||||
|
||||
**文档更新记录**:
|
||||
- 2026-01-24 v1.0: 初始版本(技术选型:Tiptap/BlockNote)
|
||||
- 2026-01-24 v1.1: 技术选型改为 **Novel (Fork)**,更新开发计划
|
||||
- 2026-01-24 v1.1: 技术选型改为 Novel (Fork)
|
||||
- 2026-01-25 v2.0: **重大调整:采用无编辑器方案**,优先实现对话流生成 + Word 导出
|
||||
|
||||
Reference in New Issue
Block a user