- 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
516 lines
16 KiB
Markdown
516 lines
16 KiB
Markdown
# 一键生成研究方案 - 开发计划
|
||
|
||
> **版本**: v2.0
|
||
> **创建日期**: 2026-01-24
|
||
> **最后更新**: 2026-01-25
|
||
> **负责人**: AI Assistant
|
||
> **状态**: 开发中
|
||
|
||
---
|
||
|
||
## 一、功能概述
|
||
|
||
### 目标
|
||
基于 Protocol Agent 收集的 5 个核心要素,一键生成完整的临床研究方案文档,支持对话式修改和 Word 导出。
|
||
|
||
### 核心价值
|
||
- 将 5-10 小时的方案撰写工作缩短至 30 分钟
|
||
- AI 生成 + 对话式修改 + Word 精修,保证专业性和个性化
|
||
- 输出符合伦理委员会审查要求的标准文档
|
||
|
||
### 技术策略:No-Editor First(无编辑器优先)
|
||
|
||
**核心洞察**:
|
||
> 用户最终会在 Word 中精修,我们的价值是 **"AI 生成高质量初稿"**,而不是 **"提供一个编辑器"**。
|
||
|
||
---
|
||
|
||
## 二、交互设计:对话流生成
|
||
|
||
### 用户流程
|
||
|
||
```
|
||
用户完成 5 阶段要素收集
|
||
↓
|
||
点击"一键生成研究方案"
|
||
↓
|
||
AI 在对话框中流式输出完整方案(Markdown)
|
||
↓
|
||
用户对话修改:"把样本量部分改一下..."
|
||
↓
|
||
AI 流式输出修改后的章节
|
||
↓
|
||
用户满意 → 点击"导出 Word"
|
||
↓
|
||
下载符合伦理委员会格式的 Word 文档
|
||
```
|
||
|
||
### 交互示意
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ ← 返回 全流程研究方案制定 │
|
||
├────────────────────────────────────────┬────────────────────┤
|
||
│ │ 📋 研究方案状态 │
|
||
│ [AI] 根据您确认的信息,我已生成研究方案: │ │
|
||
│ │ ✅ 科学问题 │
|
||
│ # 1. 研究题目 │ ✅ PICO要素 │
|
||
│ 糖尿病患者使用二甲双胍与格列美脲... │ ✅ 研究设计 │
|
||
│ │ ✅ 样本量 │
|
||
│ # 2. 研究背景 │ ✅ 观察指标 │
|
||
│ 2型糖尿病是全球性公共卫生问题... │ │
|
||
│ │ ──────────────── │
|
||
│ ... │ │
|
||
│ │ [📥 导出 Word] │
|
||
│ ┌────────────────────────────────┐ │ [🔄 重新生成] │
|
||
│ │ 📥 导出Word │ 🔄 修改本章节 │ │ │
|
||
│ └────────────────────────────────┘ │ │
|
||
│ │ │
|
||
│ [用户] 把样本量从200改成300,并说明原因 │ │
|
||
│ │ │
|
||
│ [AI] 好的,我已修改样本量部分: │ │
|
||
│ ## 6. 样本量估算 │ │
|
||
│ 根据前期预实验数据...样本量调整为300例 │ │
|
||
│ │ │
|
||
├────────────────────────────────────────┴────────────────────┤
|
||
│ [输入消息...] [发送] │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 三、研究方案结构
|
||
|
||
```markdown
|
||
# 临床研究方案
|
||
|
||
## 1. 研究题目
|
||
## 2. 研究背景与立题依据
|
||
## 3. 研究目的
|
||
## 4. 研究设计
|
||
## 5. 研究对象(纳入/排除标准)
|
||
## 6. 样本量估算
|
||
## 7. 研究实施步骤与技术路线
|
||
## 8. 观察指标
|
||
## 9. 数据管理与质量控制
|
||
## 10. 安全性评价
|
||
## 11. 统计分析计划
|
||
## 12. 伦理与知情同意
|
||
## 13. 研究时间表
|
||
## 14. 参考文献
|
||
```
|
||
|
||
---
|
||
|
||
## 四、技术方案
|
||
|
||
### 架构概览
|
||
|
||
```
|
||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||
│ 前端 │ │ Node.js │ │ Python │
|
||
│ ChatArea │ ──▶ │ Backend │ ──▶ │ Service │
|
||
│ │ │ │ │ (Pandoc) │
|
||
└─────────────┘ └─────────────┘ └─────────────┘
|
||
│ │ │
|
||
│ 流式输出 │ 调用 LLM │ Markdown→Word
|
||
│ Markdown │ 生成方案 │ 格式转换
|
||
▼ ▼ ▼
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ 用户下载 Word │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 技术栈
|
||
|
||
```
|
||
前端:复用现有 ChatArea + useAIStream
|
||
├── 增加"导出 Word"按钮
|
||
├── 增加"修改本章节"交互
|
||
│
|
||
后端 (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
|
||
│
|
||
后端 (Python):
|
||
├── Pandoc 集成
|
||
├── Reference Doc 模板控制样式
|
||
```
|
||
|
||
### 数据模型
|
||
|
||
```sql
|
||
-- 方案生成记录表(复用现有 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/full
|
||
Request: { conversationId: string }
|
||
Response: SSE 流式输出完整 Markdown
|
||
|
||
// 重新生成指定章节(流式)
|
||
POST /api/v1/aia/protocol-agent/regenerate
|
||
Request: {
|
||
conversationId: string,
|
||
section: 'sample_size' | 'background' | ...,
|
||
instruction: "把样本量从200改成300"
|
||
}
|
||
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:MVP 核心功能(2 天)
|
||
|
||
| 天数 | 任务 | 交付物 |
|
||
|------|------|--------|
|
||
| Day 1 | Python 微服务集成 Pandoc | `/convert/docx` API 可用 |
|
||
| Day 1 | Node.js 导出 API | `/export/docx` 端点完成 |
|
||
| Day 2 | 前端导出按钮 | ChatArea 增加导出功能 |
|
||
| Day 2 | 完整方案生成 Prompt | 生成质量优化 |
|
||
|
||
**Phase 1 交付**:
|
||
- ✅ 一键生成完整研究方案(Markdown)
|
||
- ✅ 导出符合格式的 Word 文档
|
||
- ✅ 基础对话式修改
|
||
|
||
### Phase 2:分章节修改(2 天)
|
||
|
||
| 天数 | 任务 | 交付物 |
|
||
|------|------|--------|
|
||
| Day 3 | 分章节存储模型 | sections JSONB 字段 |
|
||
| Day 3 | 章节重新生成 API | `/regenerate` 端点 |
|
||
| Day 4 | 前端章节选择器 | 点击章节触发修改对话 |
|
||
| Day 4 | 版本历史 | 修改记录存储 |
|
||
|
||
**Phase 2 交付**:
|
||
- ✅ 分章节存储和修改
|
||
- ✅ 智能识别修改章节
|
||
- ✅ 版本历史追踪
|
||
|
||
### Phase 3:体验优化(1 天)
|
||
|
||
| 天数 | 任务 | 交付物 |
|
||
|------|------|--------|
|
||
| Day 5 | Word 模板优化 | 符合伦理委员会格式 |
|
||
| Day 5 | UI 美化 | 导出进度、预览 |
|
||
| Day 5 | 测试与修复 | 完整功能测试 |
|
||
|
||
---
|
||
|
||
## 七、与编辑器方案对比
|
||
|
||
| 维度 | 无编辑器方案(当前) | 编辑器方案(备选) |
|
||
|------|---------------------|-------------------|
|
||
| **开发周期** | 5 天 | 10 天 |
|
||
| **维护成本** | 低(复用现有组件) | 高(Fork Novel) |
|
||
| **格式控制** | ⭐⭐⭐⭐⭐ Pandoc 精确控制 | ⭐⭐⭐ 需自己处理 |
|
||
| **修改体验** | 对话式(稍慢) | 直接编辑(更快) |
|
||
| **用户习惯** | 符合(Word 精修) | 需适应新界面 |
|
||
| **适用场景** | MVP / 快速验证 | 高频编辑场景 |
|
||
|
||
### 何时考虑上编辑器?
|
||
|
||
如果用户反馈以下问题,再考虑引入编辑器:
|
||
1. "对话修改太慢,我想直接改某个字"
|
||
2. "我需要频繁调整段落顺序"
|
||
3. "我想同时看到多个章节并对比"
|
||
|
||
---
|
||
|
||
## 八、验收标准
|
||
|
||
### 功能验收
|
||
|
||
- [ ] 点击"一键生成",对话框流式输出完整方案
|
||
- [ ] 点击"导出 Word",下载格式正确的 docx 文件
|
||
- [ ] 输入"修改样本量",AI 只重新生成该章节
|
||
- [ ] Word 文档符合伦理委员会格式要求
|
||
|
||
### 性能指标
|
||
|
||
| 指标 | 目标 |
|
||
|------|------|
|
||
| 完整方案生成时间 | < 2 分钟 |
|
||
| Word 导出时间 | < 5 秒 |
|
||
| 章节修改响应时间 | < 30 秒 |
|
||
|
||
---
|
||
|
||
## 九、Word 模板设计
|
||
|
||
### 格式要求(伦理委员会标准)
|
||
|
||
```
|
||
字体:正文宋体小四,标题黑体三号
|
||
行距:1.5 倍
|
||
页边距:上下 2.54cm,左右 3.17cm
|
||
页眉:研究方案 + 版本号
|
||
页脚:页码
|
||
```
|
||
|
||
### Reference Doc 制作
|
||
|
||
1. 在 Word 中创建符合格式的模板文档
|
||
2. 定义样式:标题1、标题2、正文、表格等
|
||
3. 保存为 `protocol_template.docx`
|
||
4. 放置到 `python-service/app/services/assets/`
|
||
|
||
---
|
||
|
||
## 十、后续迭代
|
||
|
||
### 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-25 v2.0: **重大调整:采用无编辑器方案**,优先实现对话流生成 + Word 导出
|