- 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
16 KiB
16 KiB
一键生成研究方案 - 开发计划
版本: 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例 │ │
│ │ │
├────────────────────────────────────────┴────────────────────┤
│ [输入消息...] [发送] │
└─────────────────────────────────────────────────────────────┘
三、研究方案结构
# 临床研究方案
## 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 模板控制样式
数据模型
-- 方案生成记录表(复用现有 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 设计
// 生成完整方案(流式)
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 增加依赖:
RUN apt-get update && apt-get install -y pandoc
RUN pip install pypandoc
Service 代码:
# 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-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
// 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 增强
// 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 / 快速验证 | 高频编辑场景 |
何时考虑上编辑器?
如果用户反馈以下问题,再考虑引入编辑器:
- "对话修改太慢,我想直接改某个字"
- "我需要频繁调整段落顺序"
- "我想同时看到多个章节并对比"
八、验收标准
功能验收
- 点击"一键生成",对话框流式输出完整方案
- 点击"导出 Word",下载格式正确的 docx 文件
- 输入"修改样本量",AI 只重新生成该章节
- Word 文档符合伦理委员会格式要求
性能指标
| 指标 | 目标 |
|---|---|
| 完整方案生成时间 | < 2 分钟 |
| Word 导出时间 | < 5 秒 |
| 章节修改响应时间 | < 30 秒 |
九、Word 模板设计
格式要求(伦理委员会标准)
字体:正文宋体小四,标题黑体三号
行距:1.5 倍
页边距:上下 2.54cm,左右 3.17cm
页眉:研究方案 + 版本号
页脚:页码
Reference Doc 制作
- 在 Word 中创建符合格式的模板文档
- 定义样式:标题1、标题2、正文、表格等
- 保存为
protocol_template.docx - 放置到
python-service/app/services/assets/
十、后续迭代
v1.1:编辑器增强(可选)
如果用户反馈需要更直接的编辑体验:
- 引入 Novel/Tiptap 编辑器
- 支持实时编辑 + AI 辅助
v1.2:模板库
- 不同研究类型的方案模板
- RCT、队列研究、病例对照等
v1.3:协作与审核
- 多人协作编辑
- 方案审核流程
- 与伦理系统对接
十一、参考文档
- 基于对话流的文档生成技术方案
- 编辑器选型深度评估(备选方案)
- Novel vs BlockNote 对比分析(备选方案)
文档更新记录:
- 2026-01-24 v1.0: 初始版本(技术选型:Tiptap/BlockNote)
- 2026-01-24 v1.1: 技术选型改为 Novel (Fork)
- 2026-01-25 v2.0: 重大调整:采用无编辑器方案,优先实现对话流生成 + Word 导出