Files
HaHafeng 96290d2f76 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
2026-01-24 17:29:24 +08:00

847 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Protocol Agent 数据库设计
> 版本v1.0
> 创建日期2026-01-24
---
## 一、Schema规划
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 数据库Schema架构 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ agent_schema (通用Agent框架 - 可复用) │
│ ├── agent_configs Agent配置表 │
│ ├── agent_stages 阶段配置表 │
│ ├── agent_tools 工具注册表 │
│ ├── action_cards Action Card配置表 │
│ ├── reflexion_rules 反思规则表 │
│ ├── agent_sessions Agent会话表 │
│ └── agent_traces 追踪日志表 │
│ │
│ protocol_schema (研究方案Agent专用) │
│ ├── protocol_contexts 研究方案上下文表 │
│ ├── protocol_versions 版本历史表 │
│ └── protocol_exports 导出记录表 │
│ │
│ knowledge_schema (知识库 - Phase 2) │
│ ├── knowledge_docs 知识文档表 │
│ ├── knowledge_chunks 知识块表 │
│ └── knowledge_embeddings 向量嵌入表 │
│ │
│ aia_schema (已有 - 复用) │
│ ├── conversations 对话表 │
│ └── messages 消息表 │
│ │
│ capability_schema (已有 - 复用) │
│ └── prompt_templates Prompt模板表 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 二、agent_schema - 通用Agent框架表
### 2.1 agent_configs (Agent配置表)
```prisma
/// Agent配置表 - 定义每个Agent的基础信息
model AgentConfig {
id String @id @default(uuid())
agentId String @unique @map("agent_id")
agentName String @map("agent_name")
agentDescription String? @map("agent_description")
baseSystemPrompt String @map("base_system_prompt") @db.Text
// 全局配置
toneOfVoice String? @map("tone_of_voice")
globalKnowledge String[] @map("global_knowledge")
memoryStrategy String @default("full") @map("memory_strategy")
maxHistoryTurns Int @default(20) @map("max_history_turns")
// LLM配置
defaultModel String @default("deepseek-v3") @map("default_model")
temperature Float @default(0.7)
maxTokens Int @default(4096) @map("max_tokens")
// 状态
isActive Boolean @default(true) @map("is_active")
version Int @default(1)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// 关联
stages AgentStage[] @relation("AgentConfigStages")
tools AgentTool[] @relation("AgentConfigTools")
reflexionRules ReflexionRule[] @relation("AgentConfigReflexion")
@@index([agentId, isActive])
@@map("agent_configs")
@@schema("agent_schema")
}
```
**字段说明**
| 字段 | 类型 | 说明 |
|------|------|------|
| agentId | string | Agent唯一标识`protocol_agent` |
| baseSystemPrompt | text | 基础系统Prompt |
| toneOfVoice | string | 语气风格,如"专业但亲和" |
| memoryStrategy | string | 记忆策略full/sliding_window/summary |
| maxHistoryTurns | int | 最大保留对话轮数 |
### 2.2 agent_stages (阶段配置表)
```prisma
/// 阶段配置表 - 定义Agent的各个阶段
model AgentStage {
id String @id @default(uuid())
agentConfigId String @map("agent_config_id")
stageId String @map("stage_id")
stageName String @map("stage_name")
stageOrder Int @map("stage_order")
// Prompt配置
instruction String @db.Text
extractionSchema Json @map("extraction_schema")
completionCriteria String? @map("completion_criteria") @db.Text
// 流转配置
nextStageId String? @map("next_stage_id")
transitionMode String @default("user_confirm") @map("transition_mode")
transitionCondition Json? @map("transition_condition")
// 工具配置
availableToolIds String[] @map("available_tool_ids")
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
agentConfig AgentConfig @relation("AgentConfigStages", fields: [agentConfigId], references: [id], onDelete: Cascade)
actionCards ActionCard[] @relation("StageActionCards")
@@unique([agentConfigId, stageId])
@@index([agentConfigId, stageOrder])
@@map("agent_stages")
@@schema("agent_schema")
}
```
**extractionSchema 示例** (PICO阶段)
```json
{
"type": "object",
"properties": {
"P": {
"type": "object",
"properties": {
"value": { "type": "string", "description": "研究人群" },
"details": { "type": "string" },
"confidence": { "type": "number" }
}
},
"I": { ... },
"C": { ... },
"O": { ... }
}
}
```
### 2.3 agent_tools (工具注册表)
```prisma
/// 工具注册表 - 定义Agent可用的工具
model AgentTool {
id String @id @default(uuid())
agentConfigId String? @map("agent_config_id")
toolId String @unique @map("tool_id")
toolName String @map("tool_name")
toolDescription String @map("tool_description") @db.Text
toolType String @map("tool_type")
// Function定义
functionSchema Json? @map("function_schema")
// 执行配置
handlerType String @map("handler_type")
handlerConfig Json @map("handler_config")
// 权限和限制
requiresAuth Boolean @default(false) @map("requires_auth")
rateLimit Int? @map("rate_limit")
timeout Int @default(30000)
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
agentConfig AgentConfig? @relation("AgentConfigTools", fields: [agentConfigId], references: [id], onDelete: Cascade)
@@index([agentConfigId, isActive])
@@index([toolType])
@@map("agent_tools")
@@schema("agent_schema")
}
```
**toolType 枚举值**
- `internal`: 内部计算工具
- `external`: 外部API工具
- `deep_link`: 深度链接跳转
- `rag`: RAG知识检索
### 2.4 action_cards (Action Card配置表)
```prisma
/// Action Card配置表
model ActionCard {
id String @id @default(uuid())
stageId String @map("stage_id")
cardId String @map("card_id")
title String
description String? @db.Text
iconType String? @map("icon_type")
// 触发配置
triggerType String @map("trigger_type")
triggerCondition Json? @map("trigger_condition")
// Action配置
actionType String @map("action_type")
actionPayload Json @map("action_payload")
// 回调配置
onCompleteAction String? @map("on_complete_action")
resultMapping Json? @map("result_mapping")
displayOrder Int @default(0) @map("display_order")
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
stage AgentStage @relation("StageActionCards", fields: [stageId], references: [id], onDelete: Cascade)
@@unique([stageId, cardId])
@@map("action_cards")
@@schema("agent_schema")
}
```
**actionPayload 示例** (Deep Link)
```json
{
"targetAgent": "sample_calculator",
"targetPath": "/aia/sample-calculator",
"params": {
"studyType": "{{studyDesign.type}}",
"primaryEndpoint": "{{endpoints.primary[0].name}}"
},
"openMode": "modal"
}
```
### 2.5 reflexion_rules (反思规则表)
```prisma
/// Reflexion规则表
model ReflexionRule {
id String @id @default(uuid())
agentConfigId String @map("agent_config_id")
ruleId String @map("rule_id")
ruleName String @map("rule_name")
ruleDescription String? @map("rule_description") @db.Text
// 触发配置
triggerStageId String? @map("trigger_stage_id")
triggerTiming String @map("trigger_timing")
// 规则内容
ruleType String @map("rule_type")
promptTemplate String? @map("prompt_template") @db.Text
ruleLogic Json? @map("rule_logic")
// 失败处理
severity String @default("warning")
failureAction String @map("failure_action")
isActive Boolean @default(true) @map("is_active")
priority Int @default(0)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
agentConfig AgentConfig @relation("AgentConfigReflexion", fields: [agentConfigId], references: [id], onDelete: Cascade)
@@unique([agentConfigId, ruleId])
@@index([triggerStageId, triggerTiming])
@@map("reflexion_rules")
@@schema("agent_schema")
}
```
### 2.6 agent_sessions (Agent会话表)
```prisma
/// Agent会话表 - 扩展conversation的Agent状态
model AgentSession {
id String @id @default(uuid())
conversationId String @unique @map("conversation_id")
agentId String @map("agent_id")
userId String @map("user_id")
// 状态管理
currentStageId String @map("current_stage_id")
stageStatus String @default("in_progress") @map("stage_status")
// 元数据
sessionMetadata Json? @map("session_metadata")
// 生命周期
startedAt DateTime @default(now()) @map("started_at")
lastActiveAt DateTime @default(now()) @map("last_active_at")
completedAt DateTime? @map("completed_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([agentId, userId])
@@index([currentStageId])
@@index([lastActiveAt])
@@map("agent_sessions")
@@schema("agent_schema")
}
```
### 2.7 agent_traces (追踪日志表)
```prisma
/// Agent追踪日志表 - 可观测性
model AgentTrace {
id String @id @default(uuid())
conversationId String @map("conversation_id")
messageId String? @map("message_id")
traceId String @map("trace_id")
// 步骤信息
stepType String @map("step_type")
stepOrder Int @map("step_order")
// 输入输出
stepInput Json? @map("step_input")
stepOutput Json? @map("step_output")
// LLM信息
llmModel String? @map("llm_model")
llmPromptTokens Int? @map("llm_prompt_tokens")
llmCompletionTokens Int? @map("llm_completion_tokens")
// 性能
durationMs Int? @map("duration_ms")
// 状态
status String @default("success")
errorMessage String? @map("error_message") @db.Text
createdAt DateTime @default(now()) @map("created_at")
@@index([conversationId, traceId])
@@index([traceId, stepOrder])
@@index([createdAt])
@@index([stepType, status])
@@map("agent_traces")
@@schema("agent_schema")
}
```
---
## 三、protocol_schema - 研究方案专用表
### 3.1 protocol_contexts (研究方案上下文表)
```prisma
/// Protocol上下文表 - 研究方案的结构化数据存储
model ProtocolContext {
id String @id @default(uuid())
sessionId String @unique @map("session_id")
conversationId String @unique @map("conversation_id")
userId String @map("user_id")
// ===== Phase 1 核心字段 =====
/// 阶段1科学问题
scientificQuestion Json? @map("scientific_question") @db.JsonB
/// 阶段2PICO
pico Json? @db.JsonB
/// 阶段3研究设计
studyDesign Json? @map("study_design") @db.JsonB
/// 阶段4样本量
sampleSize Json? @map("sample_size") @db.JsonB
/// 阶段5终点指标
endpoints Json? @db.JsonB
// ===== Phase 2 扩展字段 =====
/// 入选标准
inclusionCriteria Json? @map("inclusion_criteria") @db.JsonB
/// 排除标准
exclusionCriteria Json? @map("exclusion_criteria") @db.JsonB
/// 统计分析计划
statisticalPlan Json? @map("statistical_plan") @db.JsonB
// ===== Phase 3 扩展字段 =====
/// 伦理考量
ethicsConsiderations Json? @map("ethics_considerations") @db.JsonB
/// 研究时间线
timeline Json? @db.JsonB
/// 预算估算
budget Json? @db.JsonB
// ===== 元数据 =====
/// 总体进度 0-100
overallProgress Float @default(0) @map("overall_progress")
/// 已完成阶段
completedStages String[] @map("completed_stages")
/// 版本号
version Int @default(1)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
versions ProtocolVersion[] @relation("ProtocolContextVersions")
@@index([userId])
@@index([conversationId])
@@map("protocol_contexts")
@@schema("protocol_schema")
}
```
**各字段JSON结构**
#### scientificQuestion
```json
{
"content": "探究新型降糖药X对2型糖尿病患者的降糖效果及安全性",
"clinicalBackground": "2型糖尿病患病率逐年上升...",
"researchGap": "目前缺乏针对中国人群的大规模临床研究",
"significance": "填补该领域空白,为临床用药提供依据",
"confidence": 0.95,
"sourceMessageIds": ["msg_123", "msg_125"],
"lastUpdated": "2026-01-24T10:30:00Z"
}
```
#### pico
```json
{
"P": {
"value": "2型糖尿病患者",
"details": "年龄18-70岁HbA1c 7.0-10.0%",
"confidence": 0.9
},
"I": {
"value": "新型降糖药X",
"details": "口服每日一次剂量10mg",
"confidence": 0.95
},
"C": {
"value": "二甲双胍",
"details": "口服每日两次剂量500mg",
"confidence": 0.88
},
"O": {
"value": "HbA1c变化",
"details": "治疗12周后HbA1c相对基线的变化值",
"confidence": 0.92
},
"sourceMessageIds": ["msg_130", "msg_132"],
"lastUpdated": "2026-01-24T11:00:00Z"
}
```
#### studyDesign
```json
{
"type": "RCT",
"phase": "III",
"blinding": "双盲",
"randomization": "中心随机",
"allocationRatio": "1:1",
"controlType": "阳性对照",
"multiCenter": true,
"centerCount": 20,
"duration": "12周治疗期 + 4周随访期",
"confidence": 0.9,
"sourceMessageIds": ["msg_140"],
"lastUpdated": "2026-01-24T11:30:00Z"
}
```
#### sampleSize
```json
{
"total": 500,
"perGroup": 250,
"groups": 2,
"calculationMethod": "优效性检验",
"assumptions": {
"alpha": 0.05,
"power": 0.8,
"effectSize": 0.5,
"dropoutRate": 0.15,
"standardDeviation": 1.2
},
"justification": "基于既往研究预期效应量0.5%...",
"toolUsed": "sample_calculator",
"confidence": 0.95,
"sourceMessageIds": ["msg_150"],
"lastUpdated": "2026-01-24T12:00:00Z"
}
```
#### endpoints
```json
{
"primary": [{
"name": "HbA1c变化值",
"definition": "治疗12周后HbA1c相对基线的变化",
"measurementMethod": "高效液相色谱法",
"timePoint": "第12周",
"analysisMethod": "ANCOVA"
}],
"secondary": [{
"name": "空腹血糖",
"definition": "...",
"measurementMethod": "...",
"timePoint": "第4、8、12周"
}],
"safety": [{
"name": "低血糖发生率",
"definition": "血糖<3.9mmol/L的事件"
}],
"exploratory": [],
"sourceMessageIds": ["msg_160", "msg_162"],
"lastUpdated": "2026-01-24T12:30:00Z"
}
```
### 3.2 protocol_versions (版本历史表)
```prisma
/// Protocol版本历史表 - 支持回溯
model ProtocolVersion {
id String @id @default(uuid())
contextId String @map("context_id")
version Int
changeType String @map("change_type")
changedFields String[] @map("changed_fields")
previousSnapshot Json @map("previous_snapshot") @db.JsonB
changeReason String? @map("change_reason")
changedBy String @map("changed_by")
createdAt DateTime @default(now()) @map("created_at")
context ProtocolContext @relation("ProtocolContextVersions", fields: [contextId], references: [id], onDelete: Cascade)
@@unique([contextId, version])
@@index([contextId, createdAt])
@@map("protocol_versions")
@@schema("protocol_schema")
}
```
### 3.3 protocol_generations (研究方案生成表)
```prisma
/// Protocol生成记录表 - 一键生成研究方案
model ProtocolGeneration {
id String @id @default(uuid())
contextId String @map("context_id")
userId String @map("user_id")
// 生成内容
generatedContent String @map("generated_content") @db.Text // 生成的研究方案全文
contentVersion Int @default(1) @map("content_version") // 版本号(重新生成时递增)
// 使用的Prompt和参数
promptUsed String @map("prompt_used") @db.Text
generationParams Json? @map("generation_params") @db.JsonB // 生成参数
// LLM调用信息
modelUsed String @map("model_used")
tokensUsed Int? @map("tokens_used")
durationMs Int? @map("duration_ms")
// 导出记录
wordFileKey String? @map("word_file_key") // Word文件OSS Key
pdfFileKey String? @map("pdf_file_key") // PDF文件OSS Key
lastExportedAt DateTime? @map("last_exported_at")
// 用户编辑(如有)
userEditedContent String? @map("user_edited_content") @db.Text
isEdited Boolean @default(false) @map("is_edited")
status String @default("completed") // generating, completed, failed
errorMessage String? @map("error_message")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([contextId])
@@index([userId, createdAt])
@@map("protocol_generations")
@@schema("protocol_schema")
}
```
**字段说明**
| 字段 | 类型 | 说明 |
|------|------|------|
| generatedContent | text | LLM生成的完整研究方案 |
| contentVersion | int | 版本号,重新生成时递增 |
| promptUsed | text | 使用的Prompt便于复现和调试 |
| userEditedContent | text | 用户编辑后的内容(可选) |
| wordFileKey | string | 导出的Word文件在OSS中的Key |
### 3.4 protocol_exports (导出记录表)
```prisma
/// Protocol导出记录表
model ProtocolExport {
id String @id @default(uuid())
contextId String @map("context_id")
userId String @map("user_id")
exportType String @map("export_type")
exportVersion Int @map("export_version")
fileKey String? @map("file_key")
status String @default("pending")
errorMessage String? @map("error_message")
createdAt DateTime @default(now()) @map("created_at")
completedAt DateTime? @map("completed_at")
@@index([contextId])
@@index([userId, createdAt])
@@map("protocol_exports")
@@schema("protocol_schema")
}
```
---
## 四、knowledge_schema - 知识库表 (Phase 2)
### 4.1 knowledge_docs (知识文档表)
```prisma
/// 专家知识文档表
model KnowledgeDoc {
id String @id @default(uuid())
agentId String? @map("agent_id")
docType String @map("doc_type")
title String
description String? @db.Text
content String @db.Text
source String?
author String?
publishDate DateTime? @map("publish_date")
tags String[]
status String @default("active")
isPublic Boolean @default(true) @map("is_public")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
chunks KnowledgeChunk[] @relation("DocChunks")
@@index([agentId, docType])
@@index([status, isPublic])
@@map("knowledge_docs")
@@schema("knowledge_schema")
}
```
### 4.2 knowledge_chunks (知识块表)
```prisma
/// 知识块表用于RAG检索
model KnowledgeChunk {
id String @id @default(uuid())
docId String @map("doc_id")
chunkIndex Int @map("chunk_index")
content String @db.Text
contentHash String @map("content_hash")
metadata Json? @db.JsonB
createdAt DateTime @default(now()) @map("created_at")
doc KnowledgeDoc @relation("DocChunks", fields: [docId], references: [id], onDelete: Cascade)
embedding KnowledgeEmbedding? @relation("ChunkEmbedding")
@@unique([docId, chunkIndex])
@@index([contentHash])
@@map("knowledge_chunks")
@@schema("knowledge_schema")
}
```
### 4.3 knowledge_embeddings (向量嵌入表)
```prisma
/// 向量嵌入表pgvector
model KnowledgeEmbedding {
id String @id @default(uuid())
chunkId String @unique @map("chunk_id")
embedding Unsupported("vector(1536)")
embeddingModel String @map("embedding_model")
createdAt DateTime @default(now()) @map("created_at")
chunk KnowledgeChunk @relation("ChunkEmbedding", fields: [chunkId], references: [id], onDelete: Cascade)
@@map("knowledge_embeddings")
@@schema("knowledge_schema")
}
```
---
## 五、数据库迁移计划
### 5.1 迁移步骤
```bash
# 1. 创建新Schema
CREATE SCHEMA IF NOT EXISTS agent_schema;
CREATE SCHEMA IF NOT EXISTS protocol_schema;
CREATE SCHEMA IF NOT EXISTS knowledge_schema;
# 2. 更新Prisma schema.prisma中的schemas配置
schemas = ["...", "agent_schema", "protocol_schema", "knowledge_schema"]
# 3. 生成迁移
npx prisma migrate dev --name add_agent_framework
# 4. 应用迁移
npx prisma migrate deploy
```
### 5.2 初始数据
```sql
-- 插入Protocol Agent配置
INSERT INTO agent_schema.agent_configs (
id, agent_id, agent_name, base_system_prompt,
memory_strategy, max_history_turns, default_model, is_active
) VALUES (
gen_random_uuid(),
'protocol_agent',
'研究方案制定助手',
'你是一位经验丰富的临床研究方法学专家,正在帮助研究者制定研究方案...',
'full',
20,
'deepseek-v3',
true
);
-- 插入阶段配置(示例:科学问题阶段)
INSERT INTO agent_schema.agent_stages (
id, agent_config_id, stage_id, stage_name, stage_order,
instruction, extraction_schema, next_stage_id, transition_mode
) VALUES (
gen_random_uuid(),
(SELECT id FROM agent_schema.agent_configs WHERE agent_id = 'protocol_agent'),
'scientific_question',
'科学问题澄清',
1,
'引导用户明确研究的核心科学问题...',
'{"type": "object", "properties": {...}}',
'pico',
'user_confirm'
);
```
---
## 六、ER关系图
```
agent_schema
┌───────────────────┐
│ AgentConfig │
│ (agent_configs) │
└────────┬──────────┘
┌────┴────┬─────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────────┐
│AgentStage│ │AgentTool│ │ReflexionRule│
└────┬────┘ └─────────┘ └─────────────┘
┌─────────────┐
│ ActionCard │
└─────────────┘
┌─────────────┐ ┌─────────────┐
│AgentSession │ ───────→│ AgentTrace │
└─────────────┘ └─────────────┘
│ conversationId
aia_schema
┌─────────────┐ ┌─────────────┐
│Conversation │────────<│ Message │
└─────────────┘ └─────────────┘
protocol_schema
┌─────────────────┐ ┌─────────────────┐
│ProtocolContext │────<│ProtocolVersion │
└─────────────────┘ └─────────────────┘
```