feat(aia): Implement Protocol Agent MVP with reusable Agent framework

Sprint 1-3 Completed (Backend + Frontend):

Backend (Sprint 1-2):
- Implement 5-layer Agent framework (Query->Planner->Executor->Tools->Reflection)
- Create agent_schema with 6 tables (agent_definitions, stages, prompts, sessions, traces, reflexion_rules)
- Create protocol_schema with 2 tables (protocol_contexts, protocol_generations)
- Implement Protocol Agent core services (Orchestrator, ContextService, PromptBuilder)
- Integrate LLM service adapter (DeepSeek/Qwen/GPT-5/Claude)
- 6 API endpoints with full authentication
- 10/10 API tests passed

Frontend (Sprint 3):
- Add Protocol Agent entry in AgentHub (indigo theme card)
- Implement ProtocolAgentPage with 3-column layout
- Collapsible sidebar (Gemini style, 48px <-> 280px)
- StatePanel with 5 stage cards (scientific_question, pico, study_design, sample_size, endpoints)
- ChatArea with sync button and action cards integration
- 100% prototype design restoration (608 lines CSS)
- Detailed endpoints structure: baseline, exposure, outcomes, confounders

Features:
- 5-stage dialogue flow for research protocol design
- Conversation-driven interaction with sync-to-protocol button
- Real-time context state management
- One-click protocol generation button (UI ready, backend pending)

Database:
- agent_schema: 6 tables for reusable Agent framework
- protocol_schema: 2 tables for Protocol Agent
- Seed data: 1 agent + 5 stages + 9 prompts + 4 reflexion rules

Code Stats:
- Backend: 13 files, 4338 lines
- Frontend: 14 files, 2071 lines
- Total: 27 files, 6409 lines

Status: MVP core functionality completed, pending frontend-backend integration testing

Next: Sprint 4 - One-click protocol generation + Word export
This commit is contained in:
2026-01-24 17:29:24 +08:00
parent 61cdc97eeb
commit 96290d2f76
345 changed files with 13945 additions and 47 deletions

View File

@@ -0,0 +1,560 @@
/**
* Protocol Agent 初始配置数据种子
*
* 运行方式: npx tsx prisma/seeds/protocol-agent-seed.ts
*/
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('🌱 Seeding Protocol Agent configuration...');
// 1. 创建Agent定义
const agentDefinition = await prisma.agentDefinition.upsert({
where: { code: 'protocol_agent' },
update: {},
create: {
code: 'protocol_agent',
name: '研究方案制定助手',
description: '帮助研究者系统地制定临床研究方案覆盖科学问题、PICO、研究设计、样本量和观察指标5个核心阶段',
version: '1.0.0',
config: {
defaultModel: 'deepseek-v3',
maxTurns: 100,
timeout: 60000,
enableTrace: true,
enableReflexion: true,
},
isActive: true,
},
});
console.log('✅ Created Agent Definition:', agentDefinition.code);
// 2. 创建5个阶段
const stages = [
{
stageCode: 'scientific_question',
stageName: '科学问题梳理',
sortOrder: 1,
isInitial: true,
isFinal: false,
nextStages: ['pico'],
config: {
requiredFields: ['content'],
minContentLength: 10,
},
},
{
stageCode: 'pico',
stageName: 'PICO要素',
sortOrder: 2,
isInitial: false,
isFinal: false,
nextStages: ['study_design'],
config: {
requiredFields: ['P', 'I', 'C', 'O'],
},
},
{
stageCode: 'study_design',
stageName: '研究设计',
sortOrder: 3,
isInitial: false,
isFinal: false,
nextStages: ['sample_size'],
config: {
requiredFields: ['type'],
},
},
{
stageCode: 'sample_size',
stageName: '样本量计算',
sortOrder: 4,
isInitial: false,
isFinal: false,
nextStages: ['endpoints'],
config: {
requiredFields: ['total'],
},
},
{
stageCode: 'endpoints',
stageName: '观察指标',
sortOrder: 5,
isInitial: false,
isFinal: true,
nextStages: [],
config: {
requiredFields: ['primary'],
},
},
];
for (const stage of stages) {
await prisma.agentStage.upsert({
where: {
agentId_stageCode: {
agentId: agentDefinition.id,
stageCode: stage.stageCode,
},
},
update: stage,
create: {
agentId: agentDefinition.id,
...stage,
},
});
console.log(` ✅ Stage: ${stage.stageName}`);
}
// 3. 创建Prompt模板
const prompts = [
// 系统Prompt
{
promptType: 'system',
promptCode: 'protocol_system',
content: `你是一位经验丰富的临床研究方法学专家,正在帮助研究者制定研究方案。
你的职责:
1. 系统引导用户完成研究方案的5个核心要素科学问题、PICO、研究设计、样本量、观察指标
2. 提供专业、准确的方法学建议
3. 确保研究设计的科学性和可行性
4. 使用通俗易懂的语言,同时保持学术严谨性
当前阶段: {{context.currentStage}}
已完成阶段: {{context.completedStages}}
请根据用户的输入,提供专业指导。`,
variables: ['context'],
},
// 科学问题阶段Prompt
{
promptType: 'stage',
promptCode: 'stage_scientific_question',
stageCode: 'scientific_question',
content: `【科学问题梳理阶段】
你正在帮助用户梳理研究的科学问题。一个好的科学问题应该:
- 明确、具体、可操作
- 有实际的临床或学术意义
- 可通过研究方法验证
{{#if context.scientificQuestion}}
用户当前的科学问题草稿:
{{context.scientificQuestion.content}}
{{/if}}
请引导用户:
1. 描述研究背景和动机
2. 明确想要解决的核心问题
3. 阐述研究的潜在意义
当用户表达清晰后,帮助整理成规范的科学问题陈述,并提供"同步到方案"按钮。`,
variables: ['context'],
},
// PICO阶段Prompt
{
promptType: 'stage',
promptCode: 'stage_pico',
stageCode: 'pico',
content: `【PICO要素梳理阶段】
PICO是临床研究问题结构化的核心框架
- P (Population): 研究人群
- I (Intervention): 干预措施
- C (Comparison): 对照措施
- O (Outcome): 结局指标
{{#if context.pico}}
当前PICO
- P: {{context.pico.P.value}}
- I: {{context.pico.I.value}}
- C: {{context.pico.C.value}}
- O: {{context.pico.O.value}}
{{/if}}
请引导用户逐一明确四个要素,确保:
1. P: 纳入标准、排除标准清晰
2. I: 干预措施具体可操作
3. C: 对照组设置合理
4. O: 结局指标可测量、有临床意义
当四要素都明确后,提供"同步到方案"按钮。`,
variables: ['context'],
},
// 研究设计阶段Prompt
{
promptType: 'stage',
promptCode: 'stage_study_design',
stageCode: 'study_design',
content: `【研究设计阶段】
根据科学问题和PICO需要确定合适的研究设计
科学问题:{{context.scientificQuestion.content}}
PICO
- P: {{context.pico.P.value}}
- I: {{context.pico.I.value}}
常见研究类型:
- 随机对照试验(RCT):最高证据等级,适合验证干预效果
- 队列研究:适合观察性研究,探索风险因素
- 病例对照研究:适合罕见疾病研究
- 横断面研究:描述性研究
请引导用户确定:
1. 研究类型
2. 盲法设计(如适用)
3. 随机化方法(如适用)
4. 研究周期
5. 是否多中心
设计确定后,提供"同步到方案"按钮。`,
variables: ['context'],
},
// 样本量阶段Prompt
{
promptType: 'stage',
promptCode: 'stage_sample_size',
stageCode: 'sample_size',
content: `【样本量计算阶段】
样本量计算需要考虑:
- α (显著性水平): 通常0.05
- β (统计效力): 通常0.8-0.9
- 预期效应量
- 预计脱落率
研究设计:{{context.studyDesign.type}}
主要结局:{{context.pico.O.value}}
请引导用户:
1. 确定检验类型(优效、非劣效、等效)
2. 估计预期效应量(基于文献或预试验)
3. 设定显著性水平和统计效力
4. 考虑脱落率调整
可以使用样本量计算工具辅助计算。
样本量确定后,提供"同步到方案"按钮。`,
variables: ['context'],
},
// 观察指标阶段Prompt
{
promptType: 'stage',
promptCode: 'stage_endpoints',
stageCode: 'endpoints',
content: `【观察指标设计阶段】
观察指标是评价研究结果的关键:
研究类型:{{context.studyDesign.type}}
PICO-O{{context.pico.O.value}}
需要明确的指标类型:
1. **主要结局指标(Primary Endpoint)**
- 与科学问题直接相关
- 用于样本量计算
- 每个研究通常只有1-2个
2. **次要结局指标(Secondary Endpoints)**
- 支持主要结局的补充指标
- 可以有多个
3. **安全性指标(Safety Endpoints)**
- 不良事件、实验室检查等
4. **探索性指标(Exploratory)**
- 为未来研究提供线索
请引导用户定义每个指标的:
- 名称
- 操作定义
- 测量方法
- 评价时点
所有指标确定后,提供"同步到方案"按钮。
🎉 完成观察指标后,您可以点击"一键生成研究方案"生成完整方案文档!`,
variables: ['context'],
},
// 数据提取Prompt
{
promptType: 'extraction',
promptCode: 'extraction_scientific_question',
stageCode: 'scientific_question',
content: `请从以下对话中提取科学问题信息:
用户消息:{{userMessage}}
请以JSON格式输出
{
"content": "完整的科学问题陈述",
"background": "研究背景",
"significance": "研究意义",
"readyToSync": true/false
}
如果信息不完整readyToSync设为false。`,
variables: ['userMessage'],
},
{
promptType: 'extraction',
promptCode: 'extraction_pico',
stageCode: 'pico',
content: `请从以下对话中提取PICO要素
用户消息:{{userMessage}}
当前PICO{{currentPico}}
请以JSON格式输出
{
"P": { "value": "研究人群", "details": "详细描述" },
"I": { "value": "干预措施", "details": "详细描述" },
"C": { "value": "对照措施", "details": "详细描述" },
"O": { "value": "结局指标", "details": "详细描述" },
"readyToSync": true/false
}
只更新用户提到的字段,保留其他字段不变。
如果PICO四要素都已完整readyToSync设为true。`,
variables: ['userMessage', 'currentPico'],
},
// 研究方案生成Prompt
{
promptType: 'generation',
promptCode: 'generate_protocol',
content: `你是一位资深的临床研究方法学专家,请基于以下核心要素生成一份完整、规范的临床研究方案。
## 核心要素
### 科学问题
{{scientificQuestion.content}}
{{#if scientificQuestion.background}}背景:{{scientificQuestion.background}}{{/if}}
### PICO要素
- **研究人群(P)**: {{pico.P.value}}
{{pico.P.details}}
- **干预措施(I)**: {{pico.I.value}}
{{pico.I.details}}
- **对照措施(C)**: {{pico.C.value}}
{{pico.C.details}}
- **结局指标(O)**: {{pico.O.value}}
{{pico.O.details}}
### 研究设计
- 研究类型: {{studyDesign.type}}
- 盲法设计: {{studyDesign.blinding}}
- 随机化方法: {{studyDesign.randomization}}
- 研究周期: {{studyDesign.duration}}
{{#if studyDesign.multiCenter}}- 多中心: 是,{{studyDesign.centerCount}}个中心{{/if}}
### 样本量
- 总样本量: {{sampleSize.total}}
- 每组样本量: {{sampleSize.perGroup}}
- 计算依据: {{sampleSize.justification}}
### 观察指标
**主要结局指标:**
{{#each endpoints.primary}}
- {{name}}: {{definition}} ({{method}}, {{timePoint}})
{{/each}}
**次要结局指标:**
{{#each endpoints.secondary}}
- {{name}}: {{definition}}
{{/each}}
**安全性指标:**
{{#each endpoints.safety}}
- {{name}}: {{definition}}
{{/each}}
---
## 生成要求
请生成包含以下章节的完整研究方案:
1. **研究背景与立题依据**
- 疾病/问题背景
- 国内外研究现状
- 研究的必要性和意义
2. **研究目的**
- 主要目的
- 次要目的
3. **研究方法**
- 研究类型与设计
- 研究对象
- 干预措施
- 对照设置
- 随机化与盲法
4. **受试者选择**
- 入选标准
- 排除标准
- 退出/剔除标准
5. **观察指标与评价标准**
- 主要疗效指标
- 次要疗效指标
- 安全性指标
- 评价时点
6. **统计分析计划**
- 样本量估算
- 分析数据集定义
- 统计方法
7. **质量控制**
- 数据管理
- 质量保证措施
8. **伦理考虑**
- 伦理审查
- 知情同意
- 受试者保护
9. **研究进度安排**
- 时间节点
- 里程碑
请使用专业、规范的学术语言,确保内容完整、逻辑清晰、符合临床研究规范。`,
variables: ['scientificQuestion', 'pico', 'studyDesign', 'sampleSize', 'endpoints'],
},
];
// 获取阶段ID映射
const stageIdMap = new Map<string, string>();
const savedStages = await prisma.agentStage.findMany({
where: { agentId: agentDefinition.id },
});
for (const stage of savedStages) {
stageIdMap.set(stage.stageCode, stage.id);
}
// 创建Prompts
for (const prompt of prompts) {
const stageId = prompt.stageCode ? stageIdMap.get(prompt.stageCode) : null;
await prisma.agentPrompt.upsert({
where: {
agentId_promptCode_version: {
agentId: agentDefinition.id,
promptCode: prompt.promptCode,
version: 1,
},
},
update: {
content: prompt.content,
variables: prompt.variables,
},
create: {
agentId: agentDefinition.id,
stageId: stageId,
promptType: prompt.promptType,
promptCode: prompt.promptCode,
content: prompt.content,
variables: prompt.variables,
version: 1,
isActive: true,
},
});
console.log(` ✅ Prompt: ${prompt.promptCode}`);
}
// 4. 创建Reflexion规则
const reflexionRules = [
{
ruleCode: 'scientific_question_completeness',
ruleName: '科学问题完整性检查',
triggerStage: 'scientific_question',
triggerTiming: 'on_sync',
ruleType: 'rule_based',
conditions: {
content: { required: true, minLength: 10 },
},
severity: 'warning',
failureAction: 'warn',
sortOrder: 1,
},
{
ruleCode: 'pico_completeness',
ruleName: 'PICO要素完整性检查',
triggerStage: 'pico',
triggerTiming: 'on_sync',
ruleType: 'rule_based',
conditions: {
P: 'required',
I: 'required',
C: 'required',
O: 'required',
},
severity: 'error',
failureAction: 'warn',
sortOrder: 2,
},
{
ruleCode: 'sample_size_validity',
ruleName: '样本量有效性检查',
triggerStage: 'sample_size',
triggerTiming: 'on_sync',
ruleType: 'rule_based',
conditions: {
total: { required: true, min: 1 },
},
severity: 'error',
failureAction: 'warn',
sortOrder: 3,
},
{
ruleCode: 'endpoints_primary_required',
ruleName: '主要终点指标必填',
triggerStage: 'endpoints',
triggerTiming: 'on_sync',
ruleType: 'rule_based',
conditions: {
primary: { notEmpty: true },
},
severity: 'error',
failureAction: 'warn',
sortOrder: 4,
},
];
for (const rule of reflexionRules) {
await prisma.reflexionRule.upsert({
where: {
agentId_ruleCode: {
agentId: agentDefinition.id,
ruleCode: rule.ruleCode,
},
},
update: rule,
create: {
agentId: agentDefinition.id,
...rule,
isActive: true,
},
});
console.log(` ✅ Rule: ${rule.ruleName}`);
}
console.log('\n🎉 Protocol Agent configuration seeded successfully!');
}
main()
.catch((e) => {
console.error('❌ Seed failed:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});