Major Changes: - Database: Install pg_bigm/pgvector plugins, create test database - Python service: v1.0 -> v1.1, add pymupdf4llm/openpyxl/pypandoc - Node.js backend: v1.3 -> v1.7, fix pino-pretty and ES Module imports - Frontend: v1.2 -> v1.3, skip TypeScript check for deployment - Code recovery: Restore empty files from local backup Technical Fixes: - Fix pino-pretty error in production (conditional loading) - Fix ES Module import paths (add .js extensions) - Fix OSSAdapter TypeScript errors - Update Prisma Schema (63 models, 16 schemas) - Update environment variables (DATABASE_URL, EXTRACTION_SERVICE_URL, OSS) - Remove deprecated variables (REDIS_URL, DIFY_API_URL, DIFY_API_KEY) Documentation: - Create 0126 deployment folder with 8 documents - Update database development standards v2.0 - Update SAE deployment status records Deployment Status: - PostgreSQL: ai_clinical_research_test with plugins - Python: v1.1 @ 172.17.173.84:8000 - Backend: v1.7 @ 172.17.173.89:3001 - Frontend: v1.3 @ 172.17.173.90:80 Tested: All services running successfully on SAE
394 lines
12 KiB
TypeScript
394 lines
12 KiB
TypeScript
/**
|
||
* Protocol Agent 独立 Seed 脚本
|
||
*
|
||
* 只初始化 Protocol Agent 相关的配置数据,不影响其他模块
|
||
*
|
||
* 运行方式:
|
||
* npx ts-node prisma/seed-protocol-agent.ts
|
||
* 或者:
|
||
* npx tsx prisma/seed-protocol-agent.ts
|
||
*/
|
||
|
||
import { PrismaClient } from '@prisma/client';
|
||
|
||
const prisma = new PrismaClient();
|
||
|
||
async function seedProtocolAgent() {
|
||
console.log('🌱 开始初始化 Protocol Agent 配置...\n');
|
||
|
||
try {
|
||
// ============================================
|
||
// 1. 创建 Agent 定义
|
||
// ============================================
|
||
console.log('📌 创建 Agent 定义...');
|
||
|
||
const protocolAgent = await prisma.agentDefinition.upsert({
|
||
where: { code: 'protocol_agent' },
|
||
update: {
|
||
name: '全流程研究方案制定',
|
||
description: '引导用户完成5个核心阶段(科学问题、PICO、研究设计、样本量、观察指标),最终一键生成研究方案',
|
||
version: '1.0.0',
|
||
config: {
|
||
defaultModel: 'deepseek-v3',
|
||
maxTurns: 100,
|
||
timeout: 120000,
|
||
enableTrace: true,
|
||
enableReflexion: true,
|
||
},
|
||
isActive: true,
|
||
},
|
||
create: {
|
||
code: 'protocol_agent',
|
||
name: '全流程研究方案制定',
|
||
description: '引导用户完成5个核心阶段(科学问题、PICO、研究设计、样本量、观察指标),最终一键生成研究方案',
|
||
version: '1.0.0',
|
||
config: {
|
||
defaultModel: 'deepseek-v3',
|
||
maxTurns: 100,
|
||
timeout: 120000,
|
||
enableTrace: true,
|
||
enableReflexion: true,
|
||
},
|
||
isActive: true,
|
||
},
|
||
});
|
||
console.log(` ✅ Agent 定义创建成功: ${protocolAgent.name} (ID: ${protocolAgent.id})`);
|
||
|
||
// ============================================
|
||
// 2. 创建 5 个阶段
|
||
// ============================================
|
||
console.log('📌 创建 5 个阶段配置...');
|
||
|
||
const stages = [
|
||
{
|
||
stageCode: 'scientific_question',
|
||
stageName: '科学问题梳理',
|
||
sortOrder: 1,
|
||
isInitial: true,
|
||
isFinal: false,
|
||
nextStages: ['pico']
|
||
},
|
||
{
|
||
stageCode: 'pico',
|
||
stageName: 'PICO要素',
|
||
sortOrder: 2,
|
||
isInitial: false,
|
||
isFinal: false,
|
||
nextStages: ['study_design']
|
||
},
|
||
{
|
||
stageCode: 'study_design',
|
||
stageName: '研究设计',
|
||
sortOrder: 3,
|
||
isInitial: false,
|
||
isFinal: false,
|
||
nextStages: ['sample_size']
|
||
},
|
||
{
|
||
stageCode: 'sample_size',
|
||
stageName: '样本量计算',
|
||
sortOrder: 4,
|
||
isInitial: false,
|
||
isFinal: false,
|
||
nextStages: ['endpoints']
|
||
},
|
||
{
|
||
stageCode: 'endpoints',
|
||
stageName: '观察指标',
|
||
sortOrder: 5,
|
||
isInitial: false,
|
||
isFinal: true,
|
||
nextStages: []
|
||
},
|
||
];
|
||
|
||
const stageMap = new Map<string, string>();
|
||
|
||
for (const stage of stages) {
|
||
const created = await prisma.agentStage.upsert({
|
||
where: {
|
||
agentId_stageCode: {
|
||
agentId: protocolAgent.id,
|
||
stageCode: stage.stageCode
|
||
}
|
||
},
|
||
update: {
|
||
stageName: stage.stageName,
|
||
sortOrder: stage.sortOrder,
|
||
isInitial: stage.isInitial,
|
||
isFinal: stage.isFinal,
|
||
nextStages: stage.nextStages,
|
||
},
|
||
create: {
|
||
agentId: protocolAgent.id,
|
||
stageCode: stage.stageCode,
|
||
stageName: stage.stageName,
|
||
sortOrder: stage.sortOrder,
|
||
isInitial: stage.isInitial,
|
||
isFinal: stage.isFinal,
|
||
nextStages: stage.nextStages,
|
||
},
|
||
});
|
||
stageMap.set(stage.stageCode, created.id);
|
||
console.log(` ✅ 阶段 ${stage.sortOrder}: ${stage.stageName}`);
|
||
}
|
||
|
||
// ============================================
|
||
// 3. 创建系统 Prompt
|
||
// ============================================
|
||
console.log('📌 创建系统 Prompt...');
|
||
|
||
const systemPromptContent = `你是一位资深的临床研究方法学专家,正在帮助医生设计临床研究方案。
|
||
|
||
## 你的角色
|
||
- 你是一位友好、专业的研究方案设计顾问
|
||
- 你会引导用户一步步完成研究方案的核心要素
|
||
- 你善于提问,帮助用户理清思路
|
||
|
||
## 当前工作流程
|
||
你将引导用户完成5个核心阶段:
|
||
1. 科学问题梳理 - 明确研究要解决的核心问题
|
||
2. PICO要素 - 确定研究人群(P)、干预(I)、对照(C)和结局(O)
|
||
3. 研究设计 - 选择合适的研究类型和方法
|
||
4. 样本量计算 - 估算所需的样本量
|
||
5. 观察指标 - 定义基线、暴露、结局指标和混杂因素
|
||
|
||
## 输出要求
|
||
1. 回复要简洁、专业、有针对性
|
||
2. 每次只关注当前阶段的问题
|
||
3. 当用户提供了足够信息后,整理成结构化内容
|
||
4. 整理完成后,在回复末尾输出提取的数据(用于同步到方案)
|
||
|
||
## 数据提取格式
|
||
当你认为当前阶段的信息已经收集完整,请在回复末尾添加:
|
||
<extracted_data>
|
||
{
|
||
"字段1": "值1",
|
||
"字段2": "值2"
|
||
}
|
||
</extracted_data>
|
||
|
||
注意:只有在信息收集完整时才输出 extracted_data 标签。`;
|
||
|
||
await prisma.agentPrompt.upsert({
|
||
where: {
|
||
agentId_promptCode_version: {
|
||
agentId: protocolAgent.id,
|
||
promptCode: 'system',
|
||
version: 1
|
||
}
|
||
},
|
||
update: {
|
||
content: systemPromptContent,
|
||
isActive: true,
|
||
},
|
||
create: {
|
||
agentId: protocolAgent.id,
|
||
promptType: 'system',
|
||
promptCode: 'system',
|
||
content: systemPromptContent,
|
||
variables: ['context', 'intent'],
|
||
version: 1,
|
||
isActive: true,
|
||
},
|
||
});
|
||
console.log(` ✅ 系统 Prompt 创建成功`);
|
||
|
||
// ============================================
|
||
// 4. 创建各阶段 Prompt
|
||
// ============================================
|
||
console.log('📌 创建各阶段 Prompt...');
|
||
|
||
const stagePrompts = [
|
||
{
|
||
stageCode: 'scientific_question',
|
||
content: `## 当前阶段:科学问题梳理
|
||
|
||
### 目标
|
||
帮助用户明确研究要解决的核心科学问题。
|
||
|
||
### 引导方向
|
||
1. 了解用户的研究背景和动机
|
||
2. 明确研究的核心问题是什么
|
||
3. 确认问题的科学价值和临床意义
|
||
|
||
### 提问示例
|
||
- "您想研究什么问题?"
|
||
- "这个研究的临床背景是什么?"
|
||
- "您希望通过这个研究解决什么问题?"
|
||
|
||
### 数据提取
|
||
当信息完整时,提取以下字段:
|
||
- content: 科学问题的完整描述
|
||
- background: 研究背景
|
||
- significance: 研究意义`,
|
||
},
|
||
{
|
||
stageCode: 'pico',
|
||
content: `## 当前阶段:PICO要素
|
||
|
||
### 目标
|
||
帮助用户确定PICO四要素。
|
||
|
||
### PICO定义
|
||
- P (Population): 研究人群 - 谁是研究对象?
|
||
- I (Intervention): 干预措施 - 研究什么干预/暴露?
|
||
- C (Comparison): 对照 - 与什么比较?
|
||
- O (Outcome): 结局 - 关注什么结果?
|
||
|
||
### 引导方向
|
||
1. 逐一确认每个要素
|
||
2. 确保定义清晰、可操作
|
||
|
||
### 数据提取
|
||
当信息完整时,提取以下字段:
|
||
- population: 研究人群描述
|
||
- intervention: 干预措施描述
|
||
- comparison: 对照描述
|
||
- outcome: 结局指标描述`,
|
||
},
|
||
{
|
||
stageCode: 'study_design',
|
||
content: `## 当前阶段:研究设计
|
||
|
||
### 目标
|
||
帮助用户选择合适的研究设计类型。
|
||
|
||
### 常见研究类型
|
||
- 随机对照试验 (RCT)
|
||
- 队列研究 (Cohort)
|
||
- 病例对照研究 (Case-Control)
|
||
- 横断面研究 (Cross-sectional)
|
||
- 前后对照研究 (Before-After)
|
||
|
||
### 引导方向
|
||
1. 根据研究问题推荐合适的设计类型
|
||
2. 讨论设计的优缺点
|
||
3. 确定关键设计要素(如盲法、随机化方法等)
|
||
|
||
### 数据提取
|
||
当信息完整时,提取以下字段:
|
||
- studyType: 研究类型
|
||
- design: 设计要素列表
|
||
- features: 特殊设计特征`,
|
||
},
|
||
{
|
||
stageCode: 'sample_size',
|
||
content: `## 当前阶段:样本量计算
|
||
|
||
### 目标
|
||
帮助用户估算所需的样本量。
|
||
|
||
### 关键参数
|
||
- α (显著性水平): 通常0.05
|
||
- β (检验效能): 通常0.80或0.90
|
||
- 效应量: 预期的效果大小
|
||
- 脱落率: 预计的失访比例
|
||
|
||
### 引导方向
|
||
1. 了解主要结局指标的类型
|
||
2. 讨论预期的效应量
|
||
3. 考虑脱落率
|
||
|
||
### 数据提取
|
||
当信息完整时,提取以下字段:
|
||
- sampleSize: 计算的样本量
|
||
- alpha: 显著性水平
|
||
- power: 检验效能
|
||
- effectSize: 效应量
|
||
- dropoutRate: 脱落率`,
|
||
},
|
||
{
|
||
stageCode: 'endpoints',
|
||
content: `## 当前阶段:观察指标
|
||
|
||
### 目标
|
||
帮助用户定义完整的观察指标体系。
|
||
|
||
### 指标分类
|
||
1. 基线指标 - 人口学特征、病史、实验室检查
|
||
2. 暴露/干预指标 - 干预措施的具体内容
|
||
3. 结局指标 - 主要结局、次要结局、安全性指标
|
||
4. 混杂因素 - 需要控制的混杂变量
|
||
|
||
### 引导方向
|
||
1. 逐类确认各项指标
|
||
2. 确保指标定义清晰、可测量
|
||
3. 确定测量时点
|
||
|
||
### 数据提取
|
||
当信息完整时,提取以下字段:
|
||
- baseline: 基线指标列表
|
||
- exposure: 暴露/干预指标
|
||
- outcomes: 结局指标(primary, secondary, safety)
|
||
- confounders: 混杂因素列表`,
|
||
},
|
||
];
|
||
|
||
for (const prompt of stagePrompts) {
|
||
const stageId = stageMap.get(prompt.stageCode);
|
||
await prisma.agentPrompt.upsert({
|
||
where: {
|
||
agentId_promptCode_version: {
|
||
agentId: protocolAgent.id,
|
||
promptCode: `stage_${prompt.stageCode}`,
|
||
version: 1
|
||
}
|
||
},
|
||
update: {
|
||
stageId: stageId,
|
||
content: prompt.content,
|
||
isActive: true,
|
||
},
|
||
create: {
|
||
agentId: protocolAgent.id,
|
||
stageId: stageId,
|
||
promptType: 'stage',
|
||
promptCode: `stage_${prompt.stageCode}`,
|
||
content: prompt.content,
|
||
variables: ['context'],
|
||
version: 1,
|
||
isActive: true,
|
||
},
|
||
});
|
||
console.log(` ✅ 阶段 Prompt: ${prompt.stageCode}`);
|
||
}
|
||
|
||
// ============================================
|
||
// 完成
|
||
// ============================================
|
||
console.log('\n🎉 Protocol Agent 配置初始化完成!\n');
|
||
console.log('╔════════════════════════════════════════════════════════════╗');
|
||
console.log('║ Protocol Agent 配置摘要 ║');
|
||
console.log('╠════════════════════════════════════════════════════════════╣');
|
||
console.log(`║ Agent ID: ${protocolAgent.id} ║`);
|
||
console.log('║ Agent Code: protocol_agent ║');
|
||
console.log('║ 阶段数量: 5 ║');
|
||
console.log('║ Prompt 数量: 6 (1 系统 + 5 阶段) ║');
|
||
console.log('╠════════════════════════════════════════════════════════════╣');
|
||
console.log('║ 阶段流程: ║');
|
||
console.log('║ 1. 科学问题梳理 → 2. PICO要素 → 3. 研究设计 ║');
|
||
console.log('║ → 4. 样本量计算 → 5. 观察指标 → 一键生成 ║');
|
||
console.log('╚════════════════════════════════════════════════════════════╝');
|
||
|
||
} catch (error) {
|
||
console.error('❌ Protocol Agent 初始化失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 执行
|
||
seedProtocolAgent()
|
||
.then(async () => {
|
||
await prisma.$disconnect();
|
||
})
|
||
.catch(async (e) => {
|
||
console.error(e);
|
||
await prisma.$disconnect();
|
||
process.exit(1);
|
||
});
|
||
|
||
|
||
|
||
|