Day 2 Development (2026-01-24): Backend Enhancements: - Implement SSE streaming in ProtocolAgentController using createStreamingService - Add data condensation via LLM in ProtocolOrchestrator.handleProtocolSync - Support stage editing without resetting progress - Add explicit JSON output format for each stage in system prompt - Create independent seed script for Protocol Agent (seed-protocol-agent.ts) Frontend Improvements: - Integrate useAIStream hook for typewriter effect in ChatArea - Add MarkdownContent component for basic Markdown rendering - Implement StageEditModal for editing stage data (scientific question, PICO, etc.) - Add edit button to StageCard (visible on hover) - Fix routing paths from /aia to /ai-qa - Enhance CSS with full-screen layout and Markdown styles New Documentation: - One-click protocol generation development plan (v1.1) - Editor selection evaluation (Novel vs BlockNote vs Tiptap) - Novel fork strategy for AI-native editing Technical Decisions: - Choose Novel (Fork) as protocol editor for AI-first design - Two-stage progressive generation: summary in chat, full protocol in editor - 10-day development plan for protocol generation feature Code Stats: - Backend: 3 files modified, 1 new file - Frontend: 9 files modified, 2 new files - Docs: 3 new files Status: Streaming and editable features working, protocol generation pending
6.1 KiB
临床研究方案编辑器选型评估与落地建议
评估对象: BlockNote vs. Tiptap vs. Others
业务场景: AI 驱动的一键生成临床研究方案 (Protocol)
技术栈: React 19, TypeScript, Ant Design X
1. 为什么 BlockNote (Tiptap Wrapper) 是最佳选择?
在 "AI + 文档编辑器" 领域,目前业界的主流标准其实就是 Tiptap。
- Vercel AI SDK 的官方示例使用的是 Tiptap (Novel)。
- Notion 风格的开源实现大多基于 Tiptap。
✅ BlockNote 的核心优势(针对你们的项目)
- 结构化数据 (JSON): BlockNote 默认保存为 JSON Blocks。这对于后端解析、存储、甚至未来做 "结构化数据提取"(比如从方案中提取入排标准存入数据库)非常有利。
- 对比: 纯 Markdown 编辑器(Milkdown)在处理复杂嵌套结构时,解析成本较高。
- Slash Command (/命令): 开箱即用的 "/" 菜单,非常适合集成 "AI 续写"、"插入统计公式"、"插入 CRF 表格" 等医疗特有指令。
- React 19 兼容性: 它是专为 React 设计的,集成到你们现在的 frontend-v2 没有任何阻碍。
2. 医疗科研场景的特殊挑战 (Risk Assessment)
虽然 BlockNote 很棒,但在临床研究方案这个垂直场景下,有两个潜在的大坑,你需要提前规划:
🚨 挑战 A:复杂的医学表格 (Schedule of Events)
临床方案中必须包含 "访视排期表" (Schedule of Events),这是一个极其复杂的二维表格(行是检查项目,列是访视时间点,中间是 X)。
- BlockNote 现状: 表格支持相对基础。
- Tiptap 能力: Tiptap 拥有非常强大的 Table 扩展(支持合并单元格、列宽调整)。
- 建议: 如果 BlockNote 的表格无法满足需求,你需要利用它 "基于 Tiptap" 的特性,混入 Tiptap 原生的 Table 插件,或者开发一个自定义的 React Component Block 来渲染复杂的访视表。
🚨 挑战 B:参考文献管理 (Citations)
研究方案必须引用文献(如 [1], [2]),且文末要有参考文献列表。
- 现状: 通用编辑器通常不自带引用管理。
- 建议: 这需要自定义开发。利用 BlockNote 的 Inline Content 扩展能力,创建一个 Citation 节点。点击 [1] 时,侧边栏(利用 Ant Design X)显示文献详情(从 PKB 知识库获取)。
3. 推荐的 AI 集成架构 (Streaming Architecture)
不要只把编辑器当文本框。在 React 19 + AI 场景下,建议采用以下模式:
3.1 影子文档模式 (Shadow Document)
当 AI 在生成方案时,不要直接操作用户的编辑器实例(会导致光标跳动、冲突)。
- 方案: 使用一个不可见的 "Shadow Editor" 接收流式数据,或者在当前编辑器中插入一个 "Ghost Block"(幽灵块,灰色文字),流式结束后转为正式内容。
3.2 交互式 AI 块 (Interactive AI Blocks)
利用 BlockNote 的自定义 Block 能力,不仅仅生成文本,而是生成交互组件。
- 场景: AI 觉得样本量太小,不仅生成文字建议,还生成一个 "样本量重算按钮" 插入到文档中。点击按钮,弹窗调用你们的 SSA (统计分析) 模块。
4. 最终选型结论与 Roadmap
🏆 最终推荐:BlockNote (Phase 1) -> Tiptap Custom (Phase 2)
这个路线是完全可行的,因为 BlockNote 就是 Tiptap 的一层优美的皮肤。
| 阶段 | 技术选型 | 关键任务 |
|---|---|---|
| Phase 1 (快速上线) | BlockNote (标准版) | 1. 快速实现文档读写 2. 实现 /ai 命令调用你们的 Protocol Agent 3. 实现 Markdown/Word 导出 |
| Phase 2 (专业增强) | BlockNote + 自定义 React Blocks | 1. 开发 CitationBlock (关联 PKB 模块) 2. 开发 ScheduleTableBlock (复杂表格) 3. 开发 ReviewComment (批注功能,复用 RVW 模块逻辑) |
| Phase 3 (极致定制) | Tiptap Headless (如果 BlockNote 限制太大) | 如果 BlockNote 的交互逻辑(如拖拽)严重干扰了医学文档的严谨性,此时可以剥离 BlockNote UI,回退到 Tiptap 核心重写 UI,数据模型无需迁移。 |
💡 另外一个强有力的竞争者:Novel (novel.sh)
- 简介: Novel 是一个开源的、基于 Tiptap 的、类似 Notion 的编辑器,专门为 AI 写作设计。
- 优势: 它已经把 "AI 自动补全" (Vercel AI SDK) 集成得非常好了。
- 建议: 你们可以看一眼 Novel 的源码。如果觉得 BlockNote 封装太深,可以直接 Fork Novel 进行修改。Novel 的代码结构可能更适合做深度定制。
5. 代码落地示例 (React 19 + BlockNote + AI Stream)
// components/ProtocolEditor/index.tsx
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
export default function ProtocolEditor({ aiStreamContent }) {
// 1. 初始化编辑器
const editor = useCreateBlockNote({
initialContent: [
{ type: "heading", content: "1. 研究背景" },
{ type: "paragraph", content: "在此输入背景..." }
],
});
// 2. 监听 AI 流式输出 (Hooks)
useEffect(() => {
if (aiStreamContent) {
// 实时将 AI 的内容插入到当前光标位置或指定 Block
editor.insertBlocks(
[{ type: "paragraph", content: aiStreamContent }],
editor.getTextCursorPosition().block,
"after"
);
}
}, [aiStreamContent]);
// 3. 自定义渲染 (集成 Ant Design X 风格)
return (
<div className="protocol-editor-container bg-white min-h-screen">
<BlockNoteView
editor={editor}
theme="light"
// 可以在这里覆盖默认的 Slash Menu,集成你们的 Protocol Agent
slashMenu={false}
>
{/* 自定义 Slash Menu 组件 */}
<MyCustomSlashMenu editor={editor} />
</BlockNoteView>
</div>
);
}