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:
@@ -121,6 +121,8 @@ ORDER BY ordinal_position;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -134,6 +134,8 @@ runMigration()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ COMMENT ON COLUMN "dc_schema"."dc_tool_c_sessions"."column_mapping" IS '列名
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -95,6 +95,8 @@ COMMENT ON COLUMN dc_schema.dc_tool_c_sessions.expires_at IS '过期时间(创
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -66,3 +66,5 @@ USING gin (metadata jsonb_path_ops);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -33,3 +33,5 @@ USING gin (tags);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ generator client {
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
schemas = ["admin_schema", "aia_schema", "asl_schema", "capability_schema", "common_schema", "dc_schema", "ekb_schema", "iit_schema", "pkb_schema", "platform_schema", "public", "rvw_schema", "ssa_schema", "st_schema"]
|
||||
schemas = ["admin_schema", "agent_schema", "aia_schema", "asl_schema", "capability_schema", "common_schema", "dc_schema", "ekb_schema", "iit_schema", "pkb_schema", "platform_schema", "protocol_schema", "public", "rvw_schema", "ssa_schema", "st_schema"]
|
||||
}
|
||||
|
||||
/// 应用缓存表 - Postgres-Only架构
|
||||
@@ -1393,3 +1393,303 @@ model EkbChunk {
|
||||
@@map("ekb_chunk")
|
||||
@@schema("ekb_schema")
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Agent Framework Schema (agent_schema)
|
||||
// 通用Agent框架 - 可复用于多种Agent类型
|
||||
// ============================================================
|
||||
|
||||
/// Agent定义表 - 存储Agent的基本配置
|
||||
model AgentDefinition {
|
||||
id String @id @default(uuid())
|
||||
code String @unique /// 唯一标识: protocol_agent, stat_agent
|
||||
name String /// 显示名称
|
||||
description String? /// 描述
|
||||
version String @default("1.0.0") /// 版本号
|
||||
|
||||
/// Agent配置
|
||||
config Json? @db.JsonB /// 全局配置 { defaultModel, maxTurns, timeout }
|
||||
|
||||
/// 状态
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
|
||||
/// 关联
|
||||
stages AgentStage[]
|
||||
prompts AgentPrompt[]
|
||||
sessions AgentSession[]
|
||||
reflexionRules ReflexionRule[]
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@index([code], map: "idx_agent_def_code")
|
||||
@@index([isActive], map: "idx_agent_def_active")
|
||||
@@map("agent_definitions")
|
||||
@@schema("agent_schema")
|
||||
}
|
||||
|
||||
/// Agent阶段配置表 - 定义Agent的工作流阶段
|
||||
model AgentStage {
|
||||
id String @id @default(uuid())
|
||||
agentId String @map("agent_id") /// 关联的Agent
|
||||
|
||||
/// 阶段标识
|
||||
stageCode String @map("stage_code") /// 阶段代码: scientific_question, pico
|
||||
stageName String @map("stage_name") /// 阶段名称: 科学问题梳理
|
||||
sortOrder Int @map("sort_order") /// 排序顺序
|
||||
|
||||
/// 阶段配置
|
||||
config Json? @db.JsonB /// 阶段特定配置
|
||||
|
||||
/// 状态机配置
|
||||
nextStages String[] @map("next_stages") /// 可转换的下一阶段列表
|
||||
isInitial Boolean @default(false) @map("is_initial") /// 是否为起始阶段
|
||||
isFinal Boolean @default(false) @map("is_final") /// 是否为结束阶段
|
||||
|
||||
/// 关联
|
||||
agent AgentDefinition @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
||||
prompts AgentPrompt[]
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@unique([agentId, stageCode], map: "unique_agent_stage")
|
||||
@@index([agentId], map: "idx_agent_stage_agent")
|
||||
@@index([sortOrder], map: "idx_agent_stage_order")
|
||||
@@map("agent_stages")
|
||||
@@schema("agent_schema")
|
||||
}
|
||||
|
||||
/// Agent Prompt模板表 - 存储各阶段的Prompt
|
||||
model AgentPrompt {
|
||||
id String @id @default(uuid())
|
||||
agentId String @map("agent_id") /// 关联的Agent
|
||||
stageId String? @map("stage_id") /// 关联的阶段(可选,null表示通用Prompt)
|
||||
|
||||
/// Prompt标识
|
||||
promptType String @map("prompt_type") /// system, stage, extraction, reflexion
|
||||
promptCode String @map("prompt_code") /// 唯一代码
|
||||
|
||||
/// Prompt内容
|
||||
content String @db.Text /// Prompt模板内容(支持变量)
|
||||
variables String[] /// 预期变量列表
|
||||
|
||||
/// 版本控制
|
||||
version Int @default(1) /// 版本号
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
|
||||
/// 关联
|
||||
agent AgentDefinition @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
||||
stage AgentStage? @relation(fields: [stageId], references: [id], onDelete: SetNull)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@unique([agentId, promptCode, version], map: "unique_agent_prompt_version")
|
||||
@@index([agentId, promptType], map: "idx_agent_prompt_type")
|
||||
@@index([stageId], map: "idx_agent_prompt_stage")
|
||||
@@map("agent_prompts")
|
||||
@@schema("agent_schema")
|
||||
}
|
||||
|
||||
/// Agent会话表 - 存储Agent的运行时会话状态
|
||||
model AgentSession {
|
||||
id String @id @default(uuid())
|
||||
agentId String @map("agent_id") /// 关联的Agent定义
|
||||
conversationId String @map("conversation_id") /// 关联的对话ID(aia_schema.conversations)
|
||||
userId String @map("user_id") /// 用户ID
|
||||
|
||||
/// 当前状态
|
||||
currentStage String @map("current_stage") /// 当前阶段代码
|
||||
status String @default("active") /// active, completed, paused, error
|
||||
|
||||
/// 上下文数据(具体Agent的上下文存储在对应schema)
|
||||
contextRef String? @map("context_ref") /// 上下文引用ID(如protocol_schema.protocol_contexts.id)
|
||||
|
||||
/// 统计信息
|
||||
turnCount Int @default(0) @map("turn_count") /// 对话轮数
|
||||
totalTokens Int @default(0) @map("total_tokens") /// 总Token数
|
||||
|
||||
/// 关联
|
||||
agent AgentDefinition @relation(fields: [agentId], references: [id])
|
||||
traces AgentTrace[]
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@unique([conversationId], map: "unique_agent_session_conv")
|
||||
@@index([agentId], map: "idx_agent_session_agent")
|
||||
@@index([userId], map: "idx_agent_session_user")
|
||||
@@index([status], map: "idx_agent_session_status")
|
||||
@@map("agent_sessions")
|
||||
@@schema("agent_schema")
|
||||
}
|
||||
|
||||
/// Agent执行追踪表 - 记录每一步的执行详情
|
||||
model AgentTrace {
|
||||
id String @id @default(uuid())
|
||||
sessionId String @map("session_id") /// 关联的会话
|
||||
|
||||
/// 追踪信息
|
||||
traceId String @map("trace_id") /// 请求追踪ID(用于关联日志)
|
||||
stepIndex Int @map("step_index") /// 步骤序号
|
||||
stepType String @map("step_type") /// query, plan, execute, reflect, tool_call
|
||||
|
||||
/// 输入输出
|
||||
input Json? @db.JsonB /// 步骤输入
|
||||
output Json? @db.JsonB /// 步骤输出
|
||||
|
||||
/// 执行信息
|
||||
stageCode String? @map("stage_code") /// 执行时的阶段
|
||||
modelUsed String? @map("model_used") /// 使用的模型
|
||||
tokensUsed Int? @map("tokens_used") /// 消耗的Token
|
||||
durationMs Int? @map("duration_ms") /// 执行时长(毫秒)
|
||||
|
||||
/// 错误信息
|
||||
errorType String? @map("error_type") /// 错误类型
|
||||
errorMsg String? @map("error_msg") /// 错误信息
|
||||
|
||||
/// 关联
|
||||
session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([sessionId, stepIndex], map: "idx_agent_trace_session_step")
|
||||
@@index([traceId], map: "idx_agent_trace_trace_id")
|
||||
@@index([stepType], map: "idx_agent_trace_step_type")
|
||||
@@map("agent_traces")
|
||||
@@schema("agent_schema")
|
||||
}
|
||||
|
||||
/// Reflexion规则表 - 定义质量检查规则
|
||||
model ReflexionRule {
|
||||
id String @id @default(uuid())
|
||||
agentId String @map("agent_id") /// 关联的Agent
|
||||
|
||||
/// 规则标识
|
||||
ruleCode String @map("rule_code") /// 规则代码
|
||||
ruleName String @map("rule_name") /// 规则名称
|
||||
|
||||
/// 触发条件
|
||||
triggerStage String? @map("trigger_stage") /// 触发阶段(null表示全局)
|
||||
triggerTiming String @map("trigger_timing") /// on_sync, on_stage_complete, on_generate
|
||||
|
||||
/// 规则类型
|
||||
ruleType String @map("rule_type") /// rule_based, prompt_based
|
||||
|
||||
/// 规则内容
|
||||
conditions Json? @db.JsonB /// 规则条件(rule_based时使用)
|
||||
promptTemplate String? @map("prompt_template") @db.Text /// Prompt模板(prompt_based时使用)
|
||||
|
||||
/// 行为配置
|
||||
severity String @default("warning") /// error, warning, info
|
||||
failureAction String @default("warn") @map("failure_action") /// block, warn, log
|
||||
|
||||
/// 状态
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
|
||||
/// 关联
|
||||
agent AgentDefinition @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@unique([agentId, ruleCode], map: "unique_agent_rule")
|
||||
@@index([agentId, triggerStage], map: "idx_reflexion_rule_agent_stage")
|
||||
@@index([isActive], map: "idx_reflexion_rule_active")
|
||||
@@map("reflexion_rules")
|
||||
@@schema("agent_schema")
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Protocol Agent Schema (protocol_schema)
|
||||
// Protocol Agent专用 - 研究方案制定
|
||||
// ============================================================
|
||||
|
||||
/// Protocol Context表 - 存储研究方案的核心上下文数据
|
||||
model ProtocolContext {
|
||||
id String @id @default(uuid())
|
||||
conversationId String @unique @map("conversation_id") /// 关联的对话ID
|
||||
userId String @map("user_id")
|
||||
|
||||
/// 当前状态
|
||||
currentStage String @default("scientific_question") @map("current_stage")
|
||||
status String @default("in_progress") /// in_progress, completed, abandoned
|
||||
|
||||
/// ===== 5个核心阶段数据 =====
|
||||
|
||||
/// 阶段1: 科学问题
|
||||
scientificQuestion Json? @map("scientific_question") @db.JsonB
|
||||
/// { content, background, significance, confirmed, confirmedAt }
|
||||
|
||||
/// 阶段2: PICO
|
||||
pico Json? @db.JsonB
|
||||
/// { P: {value, details}, I: {}, C: {}, O: {}, confirmed, confirmedAt }
|
||||
|
||||
/// 阶段3: 研究设计
|
||||
studyDesign Json? @map("study_design") @db.JsonB
|
||||
/// { type, blinding, randomization, duration, multiCenter, confirmed }
|
||||
|
||||
/// 阶段4: 样本量
|
||||
sampleSize Json? @map("sample_size") @db.JsonB
|
||||
/// { total, perGroup, alpha, power, effectSize, dropoutRate, justification, confirmed }
|
||||
|
||||
/// 阶段5: 观察指标(终点指标)
|
||||
endpoints Json? @db.JsonB
|
||||
/// { primary: [{name, definition, method, timePoint}], secondary: [], safety: [], confirmed }
|
||||
|
||||
/// ===== 元数据 =====
|
||||
completedStages String[] @default([]) @map("completed_stages") /// 已完成的阶段列表
|
||||
lastActiveAt DateTime @default(now()) @map("last_active_at")
|
||||
|
||||
/// 关联
|
||||
generations ProtocolGeneration[]
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@index([userId], map: "idx_protocol_context_user")
|
||||
@@index([status], map: "idx_protocol_context_status")
|
||||
@@index([currentStage], map: "idx_protocol_context_stage")
|
||||
@@map("protocol_contexts")
|
||||
@@schema("protocol_schema")
|
||||
}
|
||||
|
||||
/// Protocol生成记录表 - 存储一键生成的研究方案
|
||||
model ProtocolGeneration {
|
||||
id String @id @default(uuid())
|
||||
contextId String @map("context_id") /// 关联的Context
|
||||
userId String @map("user_id")
|
||||
|
||||
/// 生成内容
|
||||
generatedContent String @map("generated_content") @db.Text /// 生成的研究方案全文(Markdown)
|
||||
contentVersion Int @default(1) @map("content_version") /// 版本号
|
||||
|
||||
/// 使用的Prompt
|
||||
promptUsed String @map("prompt_used") @db.Text /// 实际使用的Prompt
|
||||
|
||||
/// 生成参数
|
||||
modelUsed String @map("model_used") /// 使用的模型
|
||||
tokensUsed Int? @map("tokens_used") /// 消耗的Token
|
||||
durationMs Int? @map("duration_ms") /// 生成耗时(毫秒)
|
||||
|
||||
/// 导出记录
|
||||
wordFileKey String? @map("word_file_key") /// Word文件OSS Key
|
||||
lastExportedAt DateTime? @map("last_exported_at")
|
||||
|
||||
/// 状态
|
||||
status String @default("completed") /// generating, completed, failed
|
||||
errorMessage String? @map("error_message")
|
||||
|
||||
/// 关联
|
||||
context ProtocolContext @relation(fields: [contextId], references: [id], onDelete: Cascade)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@index([contextId], map: "idx_protocol_gen_context")
|
||||
@@index([userId, createdAt], map: "idx_protocol_gen_user_time")
|
||||
@@map("protocol_generations")
|
||||
@@schema("protocol_schema")
|
||||
}
|
||||
|
||||
560
backend/prisma/seeds/protocol-agent-seed.ts
Normal file
560
backend/prisma/seeds/protocol-agent-seed.ts
Normal 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();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user