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:
2026-01-25 19:16:36 +08:00
parent 4d7d97ca19
commit 303dd78c54
332 changed files with 6204 additions and 617 deletions

View File

@@ -896,3 +896,4 @@ export interface SlashCommand {

View File

@@ -496,3 +496,4 @@ class StatisticsAgentOrchestrator extends BaseAgentOrchestrator<StatisticsContex
```

View File

@@ -1125,3 +1125,4 @@ export function createProtocolAgentDependencies(
```

View File

@@ -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 Context5 阶段数据)
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 1Fork Novel + 对接后端3天)
**目标**3天内跑通"编辑器 + AI 流式生成"
### Phase 1MVP 核心功能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 导出

View File

@@ -0,0 +1,349 @@
# 一键生成研究方案 - 开发计划 V2
> **版本**: 2.0
> **日期**: 2026-01-25
> **状态**: 规划中
> **前置依赖**: Pandoc Word 导出已完成 ✅
---
## 一、设计目标
### 1.1 核心理念
```
"关键要素就绪 → 一键生成方案"
```
**解耦设计**:方案生成功能**不依赖** AI 对话流程。无论用户通过 AI 对话收集要素,还是手动填写要素,只要必填项完成即可生成。
### 1.2 用户场景
| 场景 | 用户行为 | 系统响应 |
|------|---------|---------|
| **场景 A** | 用户通过 AI 对话完成 5 阶段 | 自动提示可生成,点击生成 |
| **场景 B** | 用户手动填写关键要素 | 校验通过后,直接点击生成 |
| **场景 C** | 用户对生成内容不满意 | 针对某章节"讨论与优化" |
### 1.3 必填要素校验
| 要素 | 是否必填 | 说明 |
|------|---------|------|
| 科学问题 | ✅ 必填 | 方案的核心目的 |
| PICO | ✅ 必填 | 研究框架基础 |
| 研究设计 | ✅ 必填 | 决定方案结构 |
| 观察指标 | ✅ 必填 | 结局评价依据 |
| 样本量 | ⚪ 可选 | 可后续补充 |
**校验规则**4/5 必填项完成 → 可生成基础方案
---
## 二、UI 架构设计
### 2.1 双阶段动态布局
```
┌─────────────────────────────────────────────────────────────┐
│ Protocol Agent Page │
├─────────────────────────────────────────────────────────────┤
│ │
│ 阶段 A: 要素收集期 │
│ ┌─────────────────────────┬───────────────────────────┐ │
│ │ ChatPanel │ ContextPanel │ │
│ │ 65% │ 35% │ │
│ │ │ ┌───────────────────┐ │ │
│ │ [AI 对话区域] │ │ 科学问题 [✓][✏️] │ │ │
│ │ │ │ PICO [✓][✏️] │ │ │
│ │ │ │ 研究设计 [✓][✏️] │ │ │
│ │ │ │ 样本量 [○] │ │ │
│ │ │ │ 观察指标 [✓][✏️] │ │ │
│ │ │ ├───────────────────┤ │ │
│ │ │ │ 进度: 80% (4/5) │ │ │
│ │ │ │ [✨ 一键生成方案] │ │ │
│ │ │ │ [📥 导出 Word] │ │ │
│ │ │ └───────────────────┘ │ │
│ └─────────────────────────┴───────────────────────────┘ │
│ ↕ 拖拽手柄 │
│ │
│ 阶段 B: 方案生成期 │
│ ┌───────────────────┬─────────────────────────────────┐ │
│ │ ChatPanel │ DocumentPanel │ │
│ │ 35% │ 65% │ │
│ │ │ ┌─────────────────────────┐ │ │
│ │ [正在撰写...] │ │ [复制] [导出 Word] │ │ │
│ │ 1. 研究背景 ✓ │ ├─────────────────────────┤ │ │
│ │ 2. 研究目的 ✓ │ │ │ │ │
│ │ 3. 研究设计 ⟳ │ │ ══ A4 文档预览 ══ │ │ │
│ │ │ │ │ │ │
│ │ [修改指令输入] │ │ 1. 研究背景 │ │ │
│ │ │ │ [讨论与优化] │ │ │
│ │ │ │ 内容... │ │ │
│ │ │ │ │ │ │
│ │ │ │ 2. 研究目的 │ │ │
│ │ │ │ [讨论与优化] │ │ │
│ │ │ │ 内容... │ │ │
│ │ │ │ │ │ │
│ └───────────────────┴─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 视图切换机制
```typescript
type ViewMode = 'context' | 'document';
// 切换条件
const shouldSwitchToDocument = (event: string) => {
return event === 'GENERATION_STARTED';
};
// 布局比例
const LAYOUT_RATIO = {
context: { chat: 65, right: 35 },
document: { chat: 35, right: 65 },
};
```
### 2.3 视图切换按钮
顶部 Header 区域添加视图切换:
```
[视图: 关键要素] [视图: 完整方案]
```
---
## 三、功能模块详细设计
### 3.1 ContextPanel (要素面板) - 已有,需增强
**现有功能**
- ✅ 五阶段卡片展示
- ✅ 完成状态编辑按钮
- ✅ 导出 Word 按钮
**需新增**
1. **一键生成按钮位置调整**:移到面板顶部醒目位置
2. **生成条件提示**:显示 `已完成 4/5 必填项,可生成方案`
3. **手动添加要素入口**:未完成阶段也显示 [ 添加] 按钮
### 3.2 DocumentPanel (文档面板) - 新增
**核心功能**
- A4 纸张预览效果(宋体、首行缩进、分节标题)
- 流式输出 + 打字机光标
- 分章节"讨论与优化"按钮
- 顶部工具栏:复制、导出 Word
**章节结构**
```markdown
# 研究方案标题
## 1. 研究背景 (Background)
## 2. 研究目的 (Objectives)
## 3. 研究设计 (Study Design)
## 4. 研究对象 (Subjects)
## 5. 样本量估算 (Sample Size)
## 6. 研究实施步骤 (Implementation)
## 7. 观察指标 (Endpoints)
## 8. 数据管理与质量控制
## 9. 统计分析计划
## 10. 伦理与知情同意
## 11. 研究时间表
## 12. 参考文献
```
### 3.3 "讨论与优化" 功能
**交互流程**
1. 用户点击某章节的 [讨论与优化] 按钮
2. 左侧 Chat 自动切换到"章节讨论模式"
3. AI 理解上下文,针对该章节进行对话
4. 用户可发送修改建议
5. AI 重新生成该章节内容
6. 右侧文档实时更新
**技术实现**
```typescript
interface SectionContext {
sectionId: string; // 如 'background', 'objectives'
sectionTitle: string; // 如 '研究背景'
currentContent: string; // 当前内容
conversationMode: 'section'; // 章节讨论模式
}
```
### 3.4 动态布局组件
```typescript
// ResizableSplitPane.tsx
interface ResizableSplitPaneProps {
defaultRatio: number; // 默认比例 (0-100)
minRatio: number; // 最小比例
maxRatio: number; // 最大比例
onRatioChange: (ratio: number) => void;
leftPanel: React.ReactNode;
rightPanel: React.ReactNode;
}
```
---
## 四、开发计划 (分阶段)
### Phase 1: 核心功能 MVP (5天)
| 天数 | 任务 | 交付物 |
|------|------|--------|
| Day 1 | 动态布局组件 | `ResizableSplitPane` + 视图切换按钮 |
| Day 2 | DocumentPanel 基础 | A4 预览 + 流式渲染 + 打字机效果 |
| Day 3 | 生成 API 对接 | 分章节流式生成 + 前后端联调 |
| Day 4 | 生成按钮 & 校验 | 必填项校验 + 双位置生成按钮 |
| Day 5 | 集成测试 & Bug Fix | 端到端测试 |
**Phase 1 交付标准**
- [x] 用户可从 ContextPanel 点击生成
- [x] 方案在 DocumentPanel 流式展示
- [x] 布局可根据阶段自动切换
- [x] Word 导出功能正常
### Phase 2: "讨论与优化" (3天)
| 天数 | 任务 | 交付物 |
|------|------|--------|
| Day 6 | 章节按钮 UI | 每个章节显示 [讨论与优化] 按钮 |
| Day 7 | 章节对话模式 | Chat 切换到章节讨论模式 + API |
| Day 8 | 章节重新生成 | 单章节流式更新 + 文档同步 |
**Phase 2 交付标准**
- [x] 用户点击章节按钮,左侧进入讨论模式
- [x] AI 理解当前章节上下文
- [x] 重新生成的内容替换原章节
### Phase 3: 体验优化 (2天)
| 天数 | 任务 | 交付物 |
|------|------|--------|
| Day 9 | 手动添加要素 | 未完成阶段的 [ 添加] 入口 |
| Day 10 | 拖拽 & 记忆 | 拖拽手柄 + localStorage 记忆比例 |
---
## 五、技术要点
### 5.1 流式渲染方案
```typescript
// 使用现有 useAIStream hook
const { streamingContent, isStreaming } = useAIStream({
onChunk: (chunk) => {
// 实时更新 DocumentPanel
updateDocumentContent(chunk);
},
onComplete: (fullContent) => {
// 保存完整方案
saveProtocol(fullContent);
},
});
```
### 5.2 章节解析
```typescript
// 将流式 Markdown 解析为章节
const parseMarkdownSections = (markdown: string): Section[] => {
const sections: Section[] = [];
const regex = /^## (\d+)\. (.+)$/gm;
// ... 解析逻辑
return sections;
};
```
### 5.3 状态管理
```typescript
interface ProtocolGenerationState {
viewMode: 'context' | 'document';
layoutRatio: number;
generationStatus: 'idle' | 'generating' | 'completed';
currentSection: string | null; // 当前讨论的章节
sections: Section[];
canGenerate: boolean; // 基于必填项校验
}
```
---
## 六、文件结构
```
frontend-v2/src/modules/aia/protocol-agent/
├── components/
│ ├── ProtocolAgentPage.tsx # 主页面 (修改)
│ ├── ChatPanel.tsx # 聊天面板 (已有)
│ ├── StatePanel.tsx # 要素面板 (修改)
│ ├── DocumentPanel.tsx # 文档面板 (新增)
│ ├── ResizableSplitPane.tsx # 可拖拽分栏 (新增)
│ ├── SectionRenderer.tsx # 章节渲染器 (新增)
│ └── ViewSwitcher.tsx # 视图切换器 (新增)
├── hooks/
│ ├── useProtocolGeneration.ts # 方案生成 hook (新增)
│ └── useLayoutRatio.ts # 布局比例 hook (新增)
├── styles/
│ ├── protocol-agent.css # 主样式 (修改)
│ └── document-panel.css # 文档面板样式 (新增)
└── types/
└── index.ts # 类型定义 (修改)
```
---
## 七、风险与应对
| 风险 | 影响 | 应对措施 |
|------|------|---------|
| 长文档流式渲染性能 | 卡顿 | 虚拟滚动 + 分片渲染 |
| 章节解析复杂性 | 解析错误 | 规范化 Markdown 模板 |
| 布局切换体验 | 突兀 | CSS transition 平滑过渡 |
---
## 八、验收标准
### MVP (Phase 1)
- [ ] 4/5 必填项完成后,生成按钮可点击
- [ ] 点击生成后,自动切换到文档视图 (35:65)
- [ ] 文档以 A4 纸样式流式展示
- [ ] 生成完成后可导出 Word
### 完整版 (Phase 1+2+3)
- [ ] 每个章节有"讨论与优化"按钮
- [ ] 章节可单独重新生成
- [ ] 未完成阶段可手动添加要素
- [ ] 布局比例可拖拽调整并记忆
---
## 九、附录
### A. 参考原型
- `AIclinicalresearch/docs/03-业务模块/AIA-AI智能问答/00-系统设计/研究方案一键生成.html`
### B. 相关文档
- `UI_Layout_Ratio_Analysis.md` - 布局比例分析
- `基于对话流的文档生成与导出技术方案.md` - 技术方案
### C. 已完成依赖
- ✅ Pandoc 系统安装 (3.8.3)
- ✅ pypandoc Python 包
- ✅ Python `/api/convert/docx` 端点
- ✅ Node.js `ProtocolExportService`
- ✅ 前端导出按钮

View File

@@ -0,0 +1,110 @@
# **基于 Novel (Tiptap) 的 CRF 表单扩展开发指南**
**目标**: 在编辑器中实现 "填空"、"单选"、"日期选择" 等 CRF 控件,并支持导出 Word。
## **1\. 自定义 CRF 节点开发 (Tiptap Extensions)**
我们需要开发一组 **Node Extensions**,让编辑器理解表单元素。
### **1.1 填空输入框 (UnderlineInput)**
// extensions/underline-input.tsx
import { Node, mergeAttributes } from '@tiptap/core';
import { ReactNodeViewRenderer, NodeViewWrapper } from '@tiptap/react';
export const UnderlineInput \= Node.create({
name: 'underlineInput',
group: 'inline',
inline: true,
atom: true, // 作为一个整体,不可分割
addAttributes() {
return {
placeholder: { default: '请输入...' },
value: { default: '' },
}
},
parseHTML() {
return \[{ tag: 'span\[data-type="input"\]' }\]
},
renderHTML({ HTMLAttributes }) {
return \['span', mergeAttributes(HTMLAttributes, { 'data-type': 'input' })\]
},
// React 组件渲染
addNodeView() {
return ReactNodeViewRenderer(({ node, updateAttributes }) \=\> {
return (
\<NodeViewWrapper as="span" className="inline-block mx-1"\>
\<input
className="border-b border-black outline-none px-1 w-24 bg-transparent text-center focus:border-blue-500"
placeholder={node.attrs.placeholder}
value={node.attrs.value}
onChange={(e) \=\> updateAttributes({ value: e.target.value })}
/\>
\</NodeViewWrapper\>
)
})
},
});
### **1.2 单选组 (RadioGroup)**
// extensions/radio-group.tsx
// 类似逻辑,渲染一组 radio buttons
## **2\. AI 生成与解析策略**
如何让 AI 生成这些控件?我们约定一套 **"占位符语法"**。
* **Prompt**: "生成一个性别选择项,包含男、女。"
* **AI Output**: 性别: {{radio:男,女}}
* **前端解析 (Replacement Logic)**:
// 在 useAIStream 的 onStream 更新中
const parseContent \= (text) \=\> {
// 正则替换
if (text.includes('{{input}}')) {
editor.chain().focus().insertContent({ type: 'underlineInput' }).run();
}
// ...
};
## **3\. Word 导出逻辑 (docx.js)**
针对自定义节点的导出映射。
// utils/export-docx.ts
import { TextRun, UnderlineType } from "docx";
export const transformNode \= (node) \=\> {
switch (node.type) {
// 导出填空框 \-\> 带下划线的空格
case 'underlineInput':
return new TextRun({
text: node.attrs.value || " ", // 有值填值,无值填空格
underline: {
type: UnderlineType.SINGLE,
},
});
// 导出复选框 \-\> 特殊字符
case 'taskItem':
const isChecked \= node.attrs.checked;
return new TextRun({
text: isChecked ? "☑ " : "☐ ", // Unicode 字符
font: "Arial Unicode MS", // 确保字体支持
});
// ... 其他节点
}
};
## **4\. 总结**
Novel (Tiptap) 完全有能力承载 CRF 的需求。
虽然这需要一些 **"Extension 开发"** 的工作量,但相比自己从头写一个 Form Builder这是性价比最高的方案而且还能保持文档的流式阅读体验。

View File

@@ -0,0 +1,105 @@
# **基于对话流的文档生成与导出技术方案**
**核心策略**: No-Editor (无编辑器模式)
**目标**: 在 Chat 界面完成方案生成与修改,后端直接合成 Word 下载。
## **1\. 业务流程 (User Flow)**
sequenceDiagram
participant User
participant ChatUI
participant Agent (Node.js)
participant PandocSvc (Python)
User-\>\>ChatUI: "生成完整方案"
ChatUI-\>\>Agent: POST /generate
Agent--\>\>ChatUI: Stream Markdown ("\# 1\. 研究背景...")
User-\>\>ChatUI: "把样本量部分改一下..."
ChatUI-\>\>Agent: POST /regenerate
Agent--\>\>ChatUI: Stream Updated Markdown
User-\>\>ChatUI: 点击 \[📥 导出 Word\]
ChatUI-\>\>Agent: POST /export/docx { markdown }
Agent-\>\>PandocSvc: Convert(markdown, reference.docx)
PandocSvc--\>\>Agent: Buffer (Binary)
Agent--\>\>ChatUI: Blob (Download)
## **2\. 核心技术实现**
### **2.1 Python 微服务Pandoc 转换器**
利用你们现有的 Python 微服务,集成 pypandoc。
**Dockerfile 增加依赖:**
RUN apt-get update && apt-get install \-y pandoc
**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) 来控制样式(字体、字号、页眉)
reference\_doc \= os.path.join(os.path.dirname(\_\_file\_\_), 'assets/style\_template.docx')
pypandoc.convert\_text(
markdown\_text,
'docx',
format='markdown',
outputfile=output\_path,
extra\_args=\[f'--reference-doc={reference\_doc}'\]
)
### **2.2 Node.js 后端:导出 API**
// backend/src/modules/aia/controllers/exportController.ts
export const exportToWord \= async (req, reply) \=\> {
const { markdown } \= req.body;
// 1\. 调用 Python 微服务
const response \= await pythonService.post('/convert/docx', { content: markdown }, { responseType: 'arraybuffer' });
// 2\. 返回文件流
reply.header('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
reply.header('Content-Disposition', 'attachment; filename="protocol.docx"');
return reply.send(response.data);
};
### **2.3 前端Chat 组件增强**
在 AIStreamChat 的消息组件中,增加导出按钮。
// frontend-v2/src/shared/components/Chat/MessageBubble.tsx
const MessageBubble \= ({ content, role }) \=\> {
const handleDownload \= async () \=\> {
const blob \= await api.post('/aia/export/docx', { markdown: content }, { responseType: 'blob' });
saveAs(blob, '研究方案.docx');
};
return (
\<div className="message-bubble"\>
\<Markdown\>{content}\</Markdown\>
{role \=== 'assistant' && (
\<div className="toolbar"\>
\<Button icon={\<DownloadIcon /\>} onClick={handleDownload}\>
导出 Word
\</Button\>
\</div\>
)}
\</div\>
);
};
## **3\. 方案优势总结**
1. **格式完美**:通过 Pandoc 的 Reference Doc可以保证导出的 Word 完全符合医院的格式要求(如宋体小四、行间距等),这是前端编辑器很难做到的。
2. **开发极快**:不需要处理 Tiptap 的状态管理、协同冲突、光标位置等复杂问题。
3. **符合直觉**:用户习惯在 Word 里做最后的精修。
**结论:** 这是一个非常务实且高效的决策。我们可以先不上编辑器,把精力花在 **AI 生成内容的质量****Word 导出的样式** 上。