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:
@@ -896,3 +896,4 @@ export interface SlashCommand {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -496,3 +496,4 @@ class StatisticsAgentOrchestrator extends BaseAgentOrchestrator<StatisticsContex
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1125,3 +1125,4 @@ export function createProtocolAgentDependencies(
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 导出
|
||||
|
||||
349
docs/03-业务模块/AIA-AI智能问答/04-开发计划/06-一键生成研究方案开发计划V2.md
Normal file
349
docs/03-业务模块/AIA-AI智能问答/04-开发计划/06-一键生成研究方案开发计划V2.md
Normal 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`
|
||||
- ✅ 前端导出按钮
|
||||
|
||||
110
docs/03-业务模块/AIA-AI智能问答/04-开发计划/Novel_CRF_Extension_Guide.md
Normal file
110
docs/03-业务模块/AIA-AI智能问答/04-开发计划/Novel_CRF_Extension_Guide.md
Normal 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,这是性价比最高的方案,而且还能保持文档的流式阅读体验。
|
||||
105
docs/03-业务模块/AIA-AI智能问答/04-开发计划/基于对话流的文档生成与导出技术方案.md
Normal file
105
docs/03-业务模块/AIA-AI智能问答/04-开发计划/基于对话流的文档生成与导出技术方案.md
Normal 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 导出的样式** 上。
|
||||
Reference in New Issue
Block a user