/** * 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(); 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(); });