refactor(asl): ASL frontend architecture refactoring with left navigation
- feat: Create ASLLayout component with 7-module left navigation - feat: Implement Title Screening Settings page with optimized PICOS layout - feat: Add placeholder pages for Workbench and Results - fix: Fix nested routing structure for React Router v6 - fix: Resolve Spin component warning in MainLayout - fix: Add QueryClientProvider to App.tsx - style: Optimize PICOS form layout (P+I left, C+O+S right) - style: Align Inclusion/Exclusion criteria side-by-side - docs: Add architecture refactoring and routing fix reports Ref: Week 2 Frontend Development Scope: ASL module MVP - Title Abstract Screening
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
# AI智能文献模块 - 数据库设计
|
||||
|
||||
> **文档版本:** v1.0
|
||||
> **文档版本:** v2.0
|
||||
> **创建日期:** 2025-10-29
|
||||
> **维护者:** AI智能文献开发团队
|
||||
> **最后更新:** 2025-10-29
|
||||
> **最后更新:** 2025-11-18
|
||||
> **更新说明:** 基于实际实现代码更新,采用 asl_schema 隔离架构
|
||||
|
||||
---
|
||||
|
||||
@@ -11,140 +12,385 @@
|
||||
|
||||
本文档描述AI智能文献模块的数据库设计,包括数据表结构、关系设计、索引设计等。
|
||||
|
||||
**技术栈**:
|
||||
- 数据库:PostgreSQL 16+
|
||||
- ORM:Prisma
|
||||
- Schema隔离:`asl_schema`
|
||||
- 关联用户表:`platform_schema.users`
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Schema架构
|
||||
|
||||
ASL模块使用独立的 `asl_schema` 进行数据隔离,确保模块独立性和数据安全。
|
||||
|
||||
```
|
||||
platform_schema
|
||||
└── users (用户表)
|
||||
↓
|
||||
asl_schema
|
||||
├── screening_projects (筛选项目)
|
||||
├── literatures (文献条目)
|
||||
├── screening_results (筛选结果)
|
||||
└── screening_tasks (筛选任务)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 核心数据表
|
||||
|
||||
### 1. 文献筛选项目表 (literature_screening_projects)
|
||||
### 1. 筛选项目表 (screening_projects)
|
||||
|
||||
```sql
|
||||
CREATE TABLE literature_screening_projects (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
project_name VARCHAR(255) NOT NULL,
|
||||
protocol_id UUID, -- 研究方案ID(未来关联)
|
||||
**Prisma模型名**: `AslScreeningProject`
|
||||
**表名**: `asl_schema.screening_projects`
|
||||
|
||||
```prisma
|
||||
model AslScreeningProject {
|
||||
id String @id @default(uuid())
|
||||
userId String @map("user_id")
|
||||
user User @relation("AslProjects", fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
-- PICO标准
|
||||
pico_criteria JSONB, -- PICO结构化数据
|
||||
projectName String @map("project_name")
|
||||
|
||||
-- 筛选标准
|
||||
inclusion_criteria TEXT,
|
||||
exclusion_criteria TEXT,
|
||||
// PICO标准
|
||||
picoCriteria Json @map("pico_criteria")
|
||||
// 结构: { population, intervention, comparison, outcome, studyDesign }
|
||||
|
||||
-- 状态
|
||||
status VARCHAR(50) DEFAULT 'draft', -- draft, screening, completed
|
||||
// 筛选标准
|
||||
inclusionCriteria String @map("inclusion_criteria") @db.Text
|
||||
exclusionCriteria String @map("exclusion_criteria") @db.Text
|
||||
|
||||
-- 筛选配置
|
||||
screening_config JSONB, -- 筛选配置(双模型选择等)
|
||||
// 状态
|
||||
status String @default("draft")
|
||||
// 可选值: draft, screening, completed
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
// 筛选配置
|
||||
screeningConfig Json? @map("screening_config")
|
||||
// 结构: { models: ["deepseek-chat", "qwen-max"], temperature: 0 }
|
||||
|
||||
// 关联
|
||||
literatures AslLiterature[]
|
||||
screeningTasks AslScreeningTask[]
|
||||
screeningResults AslScreeningResult[]
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("screening_projects")
|
||||
@@schema("asl_schema")
|
||||
@@index([userId])
|
||||
@@index([status])
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 文献条目表 (literature_items)
|
||||
|
||||
**SQL表结构**:
|
||||
```sql
|
||||
CREATE TABLE literature_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES literature_screening_projects(id) ON DELETE CASCADE,
|
||||
CREATE TABLE asl_schema.screening_projects (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
project_name TEXT NOT NULL,
|
||||
pico_criteria JSONB NOT NULL,
|
||||
inclusion_criteria TEXT NOT NULL,
|
||||
exclusion_criteria TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'draft',
|
||||
screening_config JSONB,
|
||||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_user FOREIGN KEY (user_id)
|
||||
REFERENCES platform_schema.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_screening_projects_user_id ON asl_schema.screening_projects(user_id);
|
||||
CREATE INDEX idx_screening_projects_status ON asl_schema.screening_projects(status);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 文献条目表 (literatures)
|
||||
|
||||
**Prisma模型名**: `AslLiterature`
|
||||
**表名**: `asl_schema.literatures`
|
||||
|
||||
```prisma
|
||||
model AslLiterature {
|
||||
id String @id @default(uuid())
|
||||
projectId String @map("project_id")
|
||||
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
|
||||
-- 文献基本信息
|
||||
pmid VARCHAR(50),
|
||||
title TEXT,
|
||||
// 文献基本信息
|
||||
pmid String?
|
||||
title String @db.Text
|
||||
abstract String @db.Text
|
||||
authors String?
|
||||
journal String?
|
||||
publicationYear Int? @map("publication_year")
|
||||
doi String?
|
||||
|
||||
// 云原生存储字段(V1.0 阶段使用,MVP阶段预留)
|
||||
pdfUrl String? @map("pdf_url") // PDF访问URL
|
||||
pdfOssKey String? @map("pdf_oss_key") // OSS存储Key(用于删除)
|
||||
pdfFileSize Int? @map("pdf_file_size") // 文件大小(字节)
|
||||
|
||||
// 关联
|
||||
screeningResults AslScreeningResult[]
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("literatures")
|
||||
@@schema("asl_schema")
|
||||
@@index([projectId])
|
||||
@@index([doi])
|
||||
@@unique([projectId, pmid]) // 同一项目中PMID唯一
|
||||
}
|
||||
```
|
||||
|
||||
**SQL表结构**:
|
||||
```sql
|
||||
CREATE TABLE asl_schema.literatures (
|
||||
id TEXT PRIMARY KEY,
|
||||
project_id TEXT NOT NULL,
|
||||
pmid TEXT,
|
||||
title TEXT NOT NULL,
|
||||
abstract TEXT NOT NULL,
|
||||
authors TEXT,
|
||||
journal VARCHAR(255),
|
||||
journal TEXT,
|
||||
publication_year INTEGER,
|
||||
doi VARCHAR(255),
|
||||
abstract TEXT,
|
||||
|
||||
-- 文件信息
|
||||
full_text_file_path VARCHAR(500),
|
||||
full_text_status VARCHAR(50), -- not_required, pending, downloaded, uploaded, failed
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
UNIQUE(project_id, pmid)
|
||||
doi TEXT,
|
||||
pdf_url TEXT,
|
||||
pdf_oss_key TEXT,
|
||||
pdf_file_size INTEGER,
|
||||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_project FOREIGN KEY (project_id)
|
||||
REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE,
|
||||
CONSTRAINT unique_project_pmid UNIQUE (project_id, pmid)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_literatures_project_id ON asl_schema.literatures(project_id);
|
||||
CREATE INDEX idx_literatures_doi ON asl_schema.literatures(doi);
|
||||
```
|
||||
|
||||
### 3. 标题摘要初筛结果表 (title_abstract_screening_results)
|
||||
---
|
||||
|
||||
### 3. 筛选结果表 (screening_results)
|
||||
|
||||
**Prisma模型名**: `AslScreeningResult`
|
||||
**表名**: `asl_schema.screening_results`
|
||||
|
||||
**设计亮点**:支持双模型(DeepSeek + Qwen)并行验证,包含完整的判断、证据和冲突检测。
|
||||
|
||||
```prisma
|
||||
model AslScreeningResult {
|
||||
id String @id @default(uuid())
|
||||
projectId String @map("project_id")
|
||||
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
literatureId String @map("literature_id")
|
||||
literature AslLiterature @relation(fields: [literatureId], references: [id], onDelete: Cascade)
|
||||
|
||||
// DeepSeek模型判断
|
||||
dsModelName String @map("ds_model_name") // "deepseek-chat"
|
||||
dsPJudgment String? @map("ds_p_judgment") // "match" | "partial" | "mismatch"
|
||||
dsIJudgment String? @map("ds_i_judgment")
|
||||
dsCJudgment String? @map("ds_c_judgment")
|
||||
dsSJudgment String? @map("ds_s_judgment")
|
||||
dsConclusion String? @map("ds_conclusion") // "include" | "exclude" | "uncertain"
|
||||
dsConfidence Float? @map("ds_confidence") // 0-1
|
||||
|
||||
// DeepSeek模型证据
|
||||
dsPEvidence String? @map("ds_p_evidence") @db.Text
|
||||
dsIEvidence String? @map("ds_i_evidence") @db.Text
|
||||
dsCEvidence String? @map("ds_c_evidence") @db.Text
|
||||
dsSEvidence String? @map("ds_s_evidence") @db.Text
|
||||
dsReason String? @map("ds_reason") @db.Text
|
||||
|
||||
// Qwen模型判断
|
||||
qwenModelName String @map("qwen_model_name") // "qwen-max"
|
||||
qwenPJudgment String? @map("qwen_p_judgment")
|
||||
qwenIJudgment String? @map("qwen_i_judgment")
|
||||
qwenCJudgment String? @map("qwen_c_judgment")
|
||||
qwenSJudgment String? @map("qwen_s_judgment")
|
||||
qwenConclusion String? @map("qwen_conclusion")
|
||||
qwenConfidence Float? @map("qwen_confidence")
|
||||
|
||||
// Qwen模型证据
|
||||
qwenPEvidence String? @map("qwen_p_evidence") @db.Text
|
||||
qwenIEvidence String? @map("qwen_i_evidence") @db.Text
|
||||
qwenCEvidence String? @map("qwen_c_evidence") @db.Text
|
||||
qwenSEvidence String? @map("qwen_s_evidence") @db.Text
|
||||
qwenReason String? @map("qwen_reason") @db.Text
|
||||
|
||||
// 冲突状态
|
||||
conflictStatus String @default("none") @map("conflict_status")
|
||||
// 可选值: none, conflict, resolved
|
||||
conflictFields Json? @map("conflict_fields")
|
||||
// 示例: ["P", "I", "conclusion"]
|
||||
|
||||
// 最终决策
|
||||
finalDecision String? @map("final_decision") // "include" | "exclude" | "pending"
|
||||
finalDecisionBy String? @map("final_decision_by") // userId
|
||||
finalDecisionAt DateTime? @map("final_decision_at")
|
||||
exclusionReason String? @map("exclusion_reason") @db.Text
|
||||
|
||||
// AI处理状态
|
||||
aiProcessingStatus String @default("pending") @map("ai_processing_status")
|
||||
// 可选值: pending, processing, completed, failed
|
||||
aiProcessedAt DateTime? @map("ai_processed_at")
|
||||
aiErrorMessage String? @map("ai_error_message") @db.Text
|
||||
|
||||
// 可追溯信息
|
||||
promptVersion String @default("v1.0.0") @map("prompt_version")
|
||||
rawOutput Json? @map("raw_output") // 原始LLM输出(备份)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("screening_results")
|
||||
@@schema("asl_schema")
|
||||
@@index([projectId])
|
||||
@@index([literatureId])
|
||||
@@index([conflictStatus])
|
||||
@@index([finalDecision])
|
||||
@@unique([projectId, literatureId]) // 一篇文献在一个项目中只有一个筛选结果
|
||||
}
|
||||
```
|
||||
|
||||
**SQL表结构**(简化版):
|
||||
```sql
|
||||
CREATE TABLE title_abstract_screening_results (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES literature_screening_projects(id) ON DELETE CASCADE,
|
||||
literature_item_id UUID NOT NULL REFERENCES literature_items(id) ON DELETE CASCADE,
|
||||
CREATE TABLE asl_schema.screening_results (
|
||||
id TEXT PRIMARY KEY,
|
||||
project_id TEXT NOT NULL,
|
||||
literature_id TEXT NOT NULL,
|
||||
|
||||
-- DS模型判断
|
||||
ds_p_judgment VARCHAR(10), -- ✓, ✗, ?
|
||||
ds_i_judgment VARCHAR(10),
|
||||
ds_c_judgment VARCHAR(10),
|
||||
ds_s_judgment VARCHAR(10),
|
||||
ds_conclusion VARCHAR(20), -- include, exclude
|
||||
|
||||
-- DS模型证据
|
||||
-- DeepSeek判断
|
||||
ds_model_name TEXT NOT NULL,
|
||||
ds_p_judgment TEXT,
|
||||
ds_i_judgment TEXT,
|
||||
ds_c_judgment TEXT,
|
||||
ds_s_judgment TEXT,
|
||||
ds_conclusion TEXT,
|
||||
ds_confidence DOUBLE PRECISION,
|
||||
ds_p_evidence TEXT,
|
||||
ds_i_evidence TEXT,
|
||||
ds_c_evidence TEXT,
|
||||
ds_s_evidence TEXT,
|
||||
ds_reason TEXT,
|
||||
|
||||
-- Q3模型判断
|
||||
q3_p_judgment VARCHAR(10),
|
||||
q3_i_judgment VARCHAR(10),
|
||||
q3_c_judgment VARCHAR(10),
|
||||
q3_s_judgment VARCHAR(10),
|
||||
q3_conclusion VARCHAR(20),
|
||||
|
||||
-- Q3模型证据
|
||||
q3_p_evidence TEXT,
|
||||
q3_i_evidence TEXT,
|
||||
q3_c_evidence TEXT,
|
||||
q3_s_evidence TEXT,
|
||||
-- Qwen判断
|
||||
qwen_model_name TEXT NOT NULL,
|
||||
qwen_p_judgment TEXT,
|
||||
qwen_i_judgment TEXT,
|
||||
qwen_c_judgment TEXT,
|
||||
qwen_s_judgment TEXT,
|
||||
qwen_conclusion TEXT,
|
||||
qwen_confidence DOUBLE PRECISION,
|
||||
qwen_p_evidence TEXT,
|
||||
qwen_i_evidence TEXT,
|
||||
qwen_c_evidence TEXT,
|
||||
qwen_s_evidence TEXT,
|
||||
qwen_reason TEXT,
|
||||
|
||||
-- 冲突状态
|
||||
conflict_status VARCHAR(20) DEFAULT 'none', -- none, conflict, resolved
|
||||
conflict_status TEXT NOT NULL DEFAULT 'none',
|
||||
conflict_fields JSONB,
|
||||
|
||||
-- 最终决策
|
||||
final_decision VARCHAR(20), -- include, exclude, pending
|
||||
final_decision_by UUID REFERENCES users(id),
|
||||
final_decision_at TIMESTAMP,
|
||||
final_decision TEXT,
|
||||
final_decision_by TEXT,
|
||||
final_decision_at TIMESTAMP(3),
|
||||
exclusion_reason TEXT,
|
||||
|
||||
-- AI处理状态
|
||||
ai_processing_status VARCHAR(50) DEFAULT 'pending', -- pending, processing, completed, failed
|
||||
ai_processed_at TIMESTAMP,
|
||||
ai_processing_status TEXT NOT NULL DEFAULT 'pending',
|
||||
ai_processed_at TIMESTAMP(3),
|
||||
ai_error_message TEXT,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
-- 可追溯信息
|
||||
prompt_version TEXT NOT NULL DEFAULT 'v1.0.0',
|
||||
raw_output JSONB,
|
||||
|
||||
UNIQUE(project_id, literature_item_id)
|
||||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT fk_project_result FOREIGN KEY (project_id)
|
||||
REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_literature FOREIGN KEY (literature_id)
|
||||
REFERENCES asl_schema.literatures(id) ON DELETE CASCADE,
|
||||
CONSTRAINT unique_project_literature UNIQUE (project_id, literature_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_screening_results_project_id ON asl_schema.screening_results(project_id);
|
||||
CREATE INDEX idx_screening_results_literature_id ON asl_schema.screening_results(literature_id);
|
||||
CREATE INDEX idx_screening_results_conflict_status ON asl_schema.screening_results(conflict_status);
|
||||
CREATE INDEX idx_screening_results_final_decision ON asl_schema.screening_results(final_decision);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 筛选任务表 (screening_tasks)
|
||||
|
||||
**Prisma模型名**: `AslScreeningTask`
|
||||
**表名**: `asl_schema.screening_tasks`
|
||||
|
||||
```prisma
|
||||
model AslScreeningTask {
|
||||
id String @id @default(uuid())
|
||||
projectId String @map("project_id")
|
||||
project AslScreeningProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||
|
||||
taskType String @map("task_type") // "title_abstract" | "full_text"
|
||||
status String @default("pending")
|
||||
// 可选值: pending, running, completed, failed
|
||||
|
||||
// 进度统计
|
||||
totalItems Int @map("total_items")
|
||||
processedItems Int @default(0) @map("processed_items")
|
||||
successItems Int @default(0) @map("success_items")
|
||||
failedItems Int @default(0) @map("failed_items")
|
||||
conflictItems Int @default(0) @map("conflict_items")
|
||||
|
||||
// 时间信息
|
||||
startedAt DateTime? @map("started_at")
|
||||
completedAt DateTime? @map("completed_at")
|
||||
estimatedEndAt DateTime? @map("estimated_end_at")
|
||||
|
||||
// 错误信息
|
||||
errorMessage String? @map("error_message") @db.Text
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("screening_tasks")
|
||||
@@schema("asl_schema")
|
||||
@@index([projectId])
|
||||
@@index([status])
|
||||
}
|
||||
```
|
||||
|
||||
**SQL表结构**:
|
||||
```sql
|
||||
CREATE TABLE screening_tasks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES literature_screening_projects(id) ON DELETE CASCADE,
|
||||
|
||||
task_type VARCHAR(50) NOT NULL, -- title_abstract, full_text
|
||||
status VARCHAR(50) DEFAULT 'pending', -- pending, running, completed, failed
|
||||
|
||||
total_items INTEGER,
|
||||
processed_items INTEGER DEFAULT 0,
|
||||
success_items INTEGER DEFAULT 0,
|
||||
failed_items INTEGER DEFAULT 0,
|
||||
|
||||
started_at TIMESTAMP,
|
||||
completed_at TIMESTAMP,
|
||||
CREATE TABLE asl_schema.screening_tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
project_id TEXT NOT NULL,
|
||||
task_type TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
total_items INTEGER NOT NULL,
|
||||
processed_items INTEGER NOT NULL DEFAULT 0,
|
||||
success_items INTEGER NOT NULL DEFAULT 0,
|
||||
failed_items INTEGER NOT NULL DEFAULT 0,
|
||||
conflict_items INTEGER NOT NULL DEFAULT 0,
|
||||
started_at TIMESTAMP(3),
|
||||
completed_at TIMESTAMP(3),
|
||||
estimated_end_at TIMESTAMP(3),
|
||||
error_message TEXT,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_project_task FOREIGN KEY (project_id)
|
||||
REFERENCES asl_schema.screening_projects(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_screening_tasks_project_id ON asl_schema.screening_tasks(project_id);
|
||||
CREATE INDEX idx_screening_tasks_status ON asl_schema.screening_tasks(status);
|
||||
```
|
||||
|
||||
---
|
||||
@@ -152,51 +398,129 @@ CREATE TABLE screening_tasks (
|
||||
## 📊 数据关系图
|
||||
|
||||
```
|
||||
literature_screening_projects (1) ──< (N) literature_items
|
||||
literature_screening_projects (1) ──< (N) title_abstract_screening_results
|
||||
literature_items (1) ──< (1) title_abstract_screening_results
|
||||
literature_screening_projects (1) ──< (N) screening_tasks
|
||||
platform_schema.users (1)
|
||||
↓
|
||||
asl_schema.screening_projects (N)
|
||||
├─→ literatures (N)
|
||||
│ └─→ screening_results (1)
|
||||
├─→ screening_results (N)
|
||||
└─→ screening_tasks (N)
|
||||
```
|
||||
|
||||
**关系说明**:
|
||||
- 一个用户可以有多个筛选项目(1:N)
|
||||
- 一个项目可以有多个文献(1:N)
|
||||
- 一篇文献对应一个筛选结果(1:1)
|
||||
- 一个项目可以有多个筛选任务(1:N)
|
||||
- 使用级联删除保证数据一致性
|
||||
|
||||
---
|
||||
|
||||
## 🔍 索引设计汇总
|
||||
|
||||
| 表名 | 索引字段 | 索引类型 | 说明 |
|
||||
|------|---------|---------|------|
|
||||
| screening_projects | user_id | B-tree | 用户项目查询 |
|
||||
| screening_projects | status | B-tree | 状态筛选 |
|
||||
| literatures | project_id | B-tree | 项目文献查询 |
|
||||
| literatures | doi | B-tree | DOI查重 |
|
||||
| literatures | (project_id, pmid) | Unique | 防止重复导入 |
|
||||
| screening_results | project_id | B-tree | 项目结果查询 |
|
||||
| screening_results | literature_id | B-tree | 文献结果查询 |
|
||||
| screening_results | conflict_status | B-tree | 冲突筛选 |
|
||||
| screening_results | final_decision | B-tree | 决策筛选 |
|
||||
| screening_results | (project_id, literature_id) | Unique | 唯一性约束 |
|
||||
| screening_tasks | project_id | B-tree | 项目任务查询 |
|
||||
| screening_tasks | status | B-tree | 任务状态筛选 |
|
||||
|
||||
**索引总数**: 12个
|
||||
**唯一约束**: 3个
|
||||
|
||||
---
|
||||
|
||||
## 💾 数据字典
|
||||
|
||||
### PICO标准 (picoCriteria JSON)
|
||||
```json
|
||||
{
|
||||
"population": "研究人群,如:2型糖尿病成人患者",
|
||||
"intervention": "干预措施,如:SGLT2抑制剂",
|
||||
"comparison": "对照,如:安慰剂或常规疗法",
|
||||
"outcome": "结局指标,如:心血管结局",
|
||||
"studyDesign": "研究设计,如:随机对照试验 (RCT)"
|
||||
}
|
||||
```
|
||||
|
||||
### 筛选配置 (screeningConfig JSON)
|
||||
```json
|
||||
{
|
||||
"models": ["deepseek-chat", "qwen-max"],
|
||||
"temperature": 0,
|
||||
"maxRetries": 3
|
||||
}
|
||||
```
|
||||
|
||||
### 冲突字段 (conflictFields JSON)
|
||||
```json
|
||||
["P", "I", "C", "S", "conclusion"]
|
||||
```
|
||||
|
||||
### 原始输出 (rawOutput JSON)
|
||||
```json
|
||||
{
|
||||
"deepseek": { "判断": {...}, "证据": {...} },
|
||||
"qwen": { "判断": {...}, "证据": {...} }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 索引设计
|
||||
## 🔒 数据安全
|
||||
|
||||
```sql
|
||||
-- 文献条目表索引
|
||||
CREATE INDEX idx_literature_items_project_id ON literature_items(project_id);
|
||||
CREATE INDEX idx_literature_items_pmid ON literature_items(pmid);
|
||||
### Schema隔离
|
||||
- 使用 `asl_schema` 与其他模块数据隔离
|
||||
- 用户表在 `platform_schema`,统一管理
|
||||
|
||||
-- 筛选结果表索引
|
||||
CREATE INDEX idx_screening_results_project_id ON title_abstract_screening_results(project_id);
|
||||
CREATE INDEX idx_screening_results_item_id ON title_abstract_screening_results(literature_item_id);
|
||||
CREATE INDEX idx_screening_results_conflict ON title_abstract_screening_results(conflict_status);
|
||||
CREATE INDEX idx_screening_results_decision ON title_abstract_screening_results(final_decision);
|
||||
### 级联删除
|
||||
- 删除用户 → 自动删除所有筛选项目及关联数据
|
||||
- 删除项目 → 自动删除文献、结果、任务
|
||||
- 删除文献 → 自动删除筛选结果
|
||||
|
||||
-- 任务表索引
|
||||
CREATE INDEX idx_screening_tasks_project_id ON screening_tasks(project_id);
|
||||
CREATE INDEX idx_screening_tasks_status ON screening_tasks(status);
|
||||
```
|
||||
### 唯一性约束
|
||||
- 同一项目中PMID唯一(允许无PMID)
|
||||
- 同一项目中一篇文献只有一个筛选结果
|
||||
|
||||
---
|
||||
|
||||
## ⏳ 待完善内容
|
||||
## 📈 数据量预估
|
||||
|
||||
后续将补充:
|
||||
- 全文复筛相关表结构
|
||||
- 数据提取相关表结构
|
||||
- 数据迁移方案
|
||||
- 数据字典
|
||||
| 项目规模 | 文献数 | 筛选结果 | 存储空间 |
|
||||
|---------|--------|---------|----------|
|
||||
| 小型 | 100-500 | 100-500 | < 10 MB |
|
||||
| 中型 | 500-2000 | 500-2000 | 10-50 MB |
|
||||
| 大型 | 2000-5000 | 2000-5000 | 50-200 MB |
|
||||
| 超大型 | 5000+ | 5000+ | 200 MB+ |
|
||||
|
||||
**单条记录大小估算**:
|
||||
- 文献条目:~2-5 KB
|
||||
- 筛选结果:~5-10 KB(含双模型判断和证据)
|
||||
|
||||
---
|
||||
|
||||
**文档版本:** v1.0
|
||||
**最后更新:** 2025-10-29
|
||||
|
||||
|
||||
|
||||
|
||||
## ⏳ 后续规划
|
||||
|
||||
### Phase 2 (全文复筛)
|
||||
- [ ] 添加全文复筛结果表
|
||||
- [ ] PDF文件元数据表
|
||||
- [ ] 全文解析结果表
|
||||
|
||||
### Phase 3 (数据提取)
|
||||
- [ ] 数据提取模板表
|
||||
- [ ] 提取结果表
|
||||
- [ ] 质量评估表
|
||||
|
||||
---
|
||||
|
||||
**文档版本:** v2.0
|
||||
**最后更新:** 2025-11-18
|
||||
**维护者:** AI智能文献开发团队
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# AI智能文献模块 - API设计规范
|
||||
|
||||
> **文档版本:** v1.0
|
||||
> **文档版本:** v2.0
|
||||
> **创建日期:** 2025-10-29
|
||||
> **维护者:** AI智能文献开发团队
|
||||
> **最后更新:** 2025-10-29
|
||||
> **最后更新:** 2025-11-18
|
||||
> **更新说明:** 基于实际实现代码更新,所有接口已测试验证
|
||||
|
||||
---
|
||||
|
||||
@@ -11,204 +12,542 @@
|
||||
|
||||
本文档描述AI智能文献模块的API设计规范,包括接口定义、请求响应格式、错误处理等。
|
||||
|
||||
**API基础信息**:
|
||||
- **Base URL**: `http://localhost:3001` (开发环境)
|
||||
- **API前缀**: `/api/v1/asl`
|
||||
- **协议**: HTTP/HTTPS
|
||||
- **数据格式**: JSON
|
||||
- **认证方式**: JWT Token (测试阶段支持默认用户)
|
||||
|
||||
---
|
||||
|
||||
## 🔌 API设计原则
|
||||
|
||||
1. **RESTful设计**: 遵循RESTful API设计规范
|
||||
2. **统一响应格式**: 统一的成功/错误响应结构
|
||||
3. **分页支持**: 列表接口支持分页
|
||||
4. **版本控制**: API版本化管理
|
||||
5. **认证授权**: 所有接口需要JWT认证
|
||||
2. **统一响应格式**: `{ success: boolean, data?: any, error?: string }`
|
||||
3. **分页支持**: 列表接口支持分页参数
|
||||
4. **版本控制**: API版本化管理 (`/api/v1/...`)
|
||||
5. **错误处理**: 统一的HTTP状态码和错误消息
|
||||
6. **模块化路由**: `/api/v1/asl/...` 独立路由空间
|
||||
|
||||
---
|
||||
|
||||
## 📡 核心API接口
|
||||
|
||||
### 1. 项目管理
|
||||
### 1. 项目管理 (Projects)
|
||||
|
||||
#### 创建筛选项目
|
||||
```
|
||||
POST /api/literature/projects
|
||||
Request Body:
|
||||
#### 1.1 创建筛选项目
|
||||
|
||||
**接口**: `POST /api/v1/asl/projects`
|
||||
**认证**: 需要 (测试阶段默认用户ID)
|
||||
**说明**: 创建一个新的文献筛选项目
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"projectName": "string",
|
||||
"picoCriteria": {...},
|
||||
"inclusionCriteria": "string",
|
||||
"exclusionCriteria": "string"
|
||||
"projectName": "SGLT2抑制剂系统综述",
|
||||
"picoCriteria": {
|
||||
"population": "2型糖尿病成人患者",
|
||||
"intervention": "SGLT2抑制剂",
|
||||
"comparison": "安慰剂或常规降糖疗法",
|
||||
"outcome": "心血管结局",
|
||||
"studyDesign": "随机对照试验 (RCT)"
|
||||
},
|
||||
"inclusionCriteria": "英文文献,RCT研究,2010年后发表",
|
||||
"exclusionCriteria": "病例报告,综述,动物实验",
|
||||
"screeningConfig": {
|
||||
"models": ["deepseek-chat", "qwen-max"],
|
||||
"temperature": 0
|
||||
}
|
||||
}
|
||||
Response:
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "uuid",
|
||||
"projectName": "string",
|
||||
"id": "d67f0b9a-b035-4804-aca1-0bd672c27d81",
|
||||
"userId": "asl-test-user-001",
|
||||
"projectName": "SGLT2抑制剂系统综述",
|
||||
"picoCriteria": {
|
||||
"population": "2型糖尿病成人患者",
|
||||
"intervention": "SGLT2抑制剂",
|
||||
"comparison": "安慰剂或常规降糖疗法",
|
||||
"outcome": "心血管结局",
|
||||
"studyDesign": "随机对照试验 (RCT)"
|
||||
},
|
||||
"inclusionCriteria": "英文文献,RCT研究,2010年后发表",
|
||||
"exclusionCriteria": "病例报告,综述,动物实验",
|
||||
"status": "draft",
|
||||
"screeningConfig": {
|
||||
"models": ["deepseek-chat", "qwen-max"],
|
||||
"temperature": 0
|
||||
},
|
||||
"createdAt": "2025-11-18T07:30:00.000Z",
|
||||
"updatedAt": "2025-11-18T07:30:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/v1/asl/projects \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"projectName": "测试项目",
|
||||
"picoCriteria": {
|
||||
"population": "成人患者",
|
||||
"intervention": "药物A",
|
||||
"comparison": "安慰剂",
|
||||
"outcome": "主要结局",
|
||||
"studyDesign": "RCT"
|
||||
},
|
||||
"inclusionCriteria": "英文文献",
|
||||
"exclusionCriteria": "综述"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 1.2 获取项目列表
|
||||
|
||||
**接口**: `GET /api/v1/asl/projects`
|
||||
**认证**: 需要
|
||||
**说明**: 获取当前用户的所有筛选项目
|
||||
|
||||
**查询参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "d67f0b9a-b035-4804-aca1-0bd672c27d81",
|
||||
"userId": "asl-test-user-001",
|
||||
"projectName": "SGLT2抑制剂系统综述",
|
||||
"picoCriteria": {...},
|
||||
"status": "screening",
|
||||
"createdAt": "2025-11-18T07:30:00.000Z",
|
||||
"updatedAt": "2025-11-18T07:35:00.000Z",
|
||||
"_count": {
|
||||
"literatures": 3,
|
||||
"screeningResults": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl http://localhost:3001/api/v1/asl/projects
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 1.3 获取项目详情
|
||||
|
||||
**接口**: `GET /api/v1/asl/projects/:projectId`
|
||||
**认证**: 需要
|
||||
**说明**: 获取指定项目的详细信息
|
||||
|
||||
**路径参数**:
|
||||
- `projectId`: 项目ID (UUID)
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "d67f0b9a-b035-4804-aca1-0bd672c27d81",
|
||||
"userId": "asl-test-user-001",
|
||||
"projectName": "SGLT2抑制剂系统综述",
|
||||
"picoCriteria": {
|
||||
"population": "2型糖尿病成人患者",
|
||||
"intervention": "SGLT2抑制剂",
|
||||
"comparison": "安慰剂或常规降糖疗法",
|
||||
"outcome": "心血管结局",
|
||||
"studyDesign": "随机对照试验 (RCT)"
|
||||
},
|
||||
"inclusionCriteria": "英文文献,RCT研究,2010年后发表",
|
||||
"exclusionCriteria": "病例报告,综述,动物实验",
|
||||
"status": "screening",
|
||||
"screeningConfig": {
|
||||
"models": ["deepseek-chat", "qwen-max"],
|
||||
"temperature": 0
|
||||
},
|
||||
"createdAt": "2025-11-18T07:30:00.000Z",
|
||||
"updatedAt": "2025-11-18T07:35:00.000Z",
|
||||
"_count": {
|
||||
"literatures": 3,
|
||||
"screeningResults": 3,
|
||||
"screeningTasks": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl http://localhost:3001/api/v1/asl/projects/{projectId}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 1.4 更新项目
|
||||
|
||||
**接口**: `PUT /api/v1/asl/projects/:projectId`
|
||||
**认证**: 需要
|
||||
**说明**: 更新项目信息
|
||||
|
||||
**路径参数**:
|
||||
- `projectId`: 项目ID (UUID)
|
||||
|
||||
**请求体**(支持部分更新):
|
||||
```json
|
||||
{
|
||||
"projectName": "更新后的项目名称",
|
||||
"status": "screening",
|
||||
"inclusionCriteria": "更新后的纳入标准"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "d67f0b9a-b035-4804-aca1-0bd672c27d81",
|
||||
"projectName": "更新后的项目名称",
|
||||
"status": "screening",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 获取项目列表
|
||||
```
|
||||
GET /api/literature/projects?page=1&pageSize=10
|
||||
```
|
||||
|
||||
#### 获取项目详情
|
||||
```
|
||||
GET /api/literature/projects/:projectId
|
||||
```
|
||||
|
||||
#### 更新项目
|
||||
```
|
||||
PUT /api/literature/projects/:projectId
|
||||
```
|
||||
|
||||
#### 删除项目
|
||||
```
|
||||
DELETE /api/literature/projects/:projectId
|
||||
```
|
||||
|
||||
### 2. 文献管理
|
||||
|
||||
#### 导入文献(Excel)
|
||||
```
|
||||
POST /api/literature/projects/:projectId/items/import
|
||||
Content-Type: multipart/form-data
|
||||
Body: file (Excel文件)
|
||||
Response:
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"importedCount": 100,
|
||||
"items": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 获取文献列表
|
||||
```
|
||||
GET /api/literature/projects/:projectId/items?page=1&pageSize=50
|
||||
```
|
||||
|
||||
#### 获取文献详情
|
||||
```
|
||||
GET /api/literature/projects/:projectId/items/:itemId
|
||||
```
|
||||
|
||||
### 3. 标题摘要初筛
|
||||
|
||||
#### 启动筛选任务
|
||||
```
|
||||
POST /api/literature/projects/:projectId/screening/start
|
||||
Request Body:
|
||||
{
|
||||
"screeningType": "title_abstract",
|
||||
"modelConfig": {
|
||||
"ds": true,
|
||||
"q3": true
|
||||
}
|
||||
}
|
||||
Response:
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"taskId": "uuid",
|
||||
"status": "running"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 获取筛选结果
|
||||
```
|
||||
GET /api/literature/projects/:projectId/screening/results
|
||||
Query Params:
|
||||
- page: 页码
|
||||
- pageSize: 每页数量
|
||||
- conflictOnly: 只显示冲突项
|
||||
- decision: include/exclude/pending
|
||||
```
|
||||
|
||||
#### 更新最终决策
|
||||
```
|
||||
PUT /api/literature/projects/:projectId/screening/results/:resultId
|
||||
Request Body:
|
||||
{
|
||||
"finalDecision": "include", // include/exclude
|
||||
"exclusionReason": "string" // 排除时必填
|
||||
}
|
||||
```
|
||||
|
||||
#### 批量更新决策
|
||||
```
|
||||
POST /api/literature/projects/:projectId/screening/results/batch-update
|
||||
Request Body:
|
||||
{
|
||||
"itemIds": ["uuid1", "uuid2", ...],
|
||||
"finalDecision": "include",
|
||||
"exclusionReason": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 任务管理
|
||||
|
||||
#### 获取任务状态
|
||||
```
|
||||
GET /api/literature/projects/:projectId/tasks/:taskId
|
||||
Response:
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"id": "uuid",
|
||||
"status": "running", // pending/running/completed/failed
|
||||
"totalItems": 100,
|
||||
"processedItems": 45,
|
||||
"progress": 45
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 任务进度流式推送(SSE)
|
||||
```
|
||||
GET /api/literature/projects/:projectId/tasks/:taskId/progress
|
||||
Accept: text/event-stream
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl -X PUT http://localhost:3001/api/v1/asl/projects/{projectId} \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "screening"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 响应格式规范
|
||||
#### 1.5 删除项目
|
||||
|
||||
### 成功响应
|
||||
**接口**: `DELETE /api/v1/asl/projects/:projectId`
|
||||
**认证**: 需要
|
||||
**说明**: 删除项目及所有关联数据(级联删除)
|
||||
|
||||
**路径参数**:
|
||||
- `projectId`: 项目ID (UUID)
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {...}
|
||||
"success": true,
|
||||
"message": "Project deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl -X DELETE http://localhost:3001/api/v1/asl/projects/{projectId}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 文献管理 (Literatures)
|
||||
|
||||
#### 2.1 导入文献(JSON格式)
|
||||
|
||||
**接口**: `POST /api/v1/asl/literatures/import`
|
||||
**认证**: 需要
|
||||
**说明**: 批量导入文献(JSON格式)
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "错误描述",
|
||||
"error": {
|
||||
"code": "ERROR_CODE",
|
||||
"details": "..."
|
||||
"projectId": "d67f0b9a-b035-4804-aca1-0bd672c27d81",
|
||||
"literatures": [
|
||||
{
|
||||
"pmid": "12345678",
|
||||
"title": "Efficacy of SGLT2 inhibitors in type 2 diabetes",
|
||||
"abstract": "Background: SGLT2 inhibitors are a new class...",
|
||||
"authors": "Smith J, Jones A, Brown B",
|
||||
"journal": "New England Journal of Medicine",
|
||||
"publicationYear": 2020,
|
||||
"doi": "10.1056/NEJMoa1234567"
|
||||
},
|
||||
{
|
||||
"title": "Another study on SGLT2 inhibitors",
|
||||
"abstract": "Objective: To evaluate...",
|
||||
"authors": "Johnson M",
|
||||
"journal": "The Lancet",
|
||||
"publicationYear": 2019
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"importedCount": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 分页响应
|
||||
**字段说明**:
|
||||
- `pmid`: PubMed ID (可选)
|
||||
- `title`: 文献标题 (必填)
|
||||
- `abstract`: 摘要 (必填)
|
||||
- `authors`: 作者 (可选)
|
||||
- `journal`: 期刊 (可选)
|
||||
- `publicationYear`: 发表年份 (可选)
|
||||
- `doi`: DOI (可选)
|
||||
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/v1/asl/literatures/import \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"projectId": "{projectId}",
|
||||
"literatures": [
|
||||
{
|
||||
"title": "测试文献",
|
||||
"abstract": "这是测试摘要"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2.2 导入文献(Excel文件)
|
||||
|
||||
**接口**: `POST /api/v1/asl/literatures/import-excel`
|
||||
**认证**: 需要
|
||||
**说明**: 从Excel文件批量导入文献
|
||||
|
||||
**请求类型**: `multipart/form-data`
|
||||
|
||||
**表单字段**:
|
||||
- `file`: Excel文件 (.xlsx)
|
||||
- `projectId`: 项目ID
|
||||
|
||||
**Excel格式要求**:
|
||||
| 列名(中英文均可) | 必填 | 说明 |
|
||||
|------------------|------|------|
|
||||
| PMID / pmid / PMID编号 | 否 | PubMed ID |
|
||||
| Title / title / 标题 | 是 | 文献标题 |
|
||||
| Abstract / abstract / 摘要 | 是 | 摘要 |
|
||||
| Authors / authors / 作者 | 否 | 作者 |
|
||||
| Journal / journal / 期刊 | 否 | 期刊名称 |
|
||||
| Year / year / 年份 | 否 | 发表年份 |
|
||||
| DOI / doi | 否 | DOI |
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"success": true,
|
||||
"data": {
|
||||
"items": [...],
|
||||
"importedCount": 15,
|
||||
"totalRows": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/v1/asl/literatures/import-excel \
|
||||
-F "file=@literatures.xlsx" \
|
||||
-F "projectId={projectId}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2.3 获取文献列表
|
||||
|
||||
**接口**: `GET /api/v1/asl/projects/:projectId/literatures`
|
||||
**认证**: 需要
|
||||
**说明**: 获取项目的文献列表(支持分页)
|
||||
|
||||
**路径参数**:
|
||||
- `projectId`: 项目ID (UUID)
|
||||
|
||||
**查询参数**:
|
||||
- `page`: 页码(默认: 1)
|
||||
- `limit`: 每页数量(默认: 50,最大: 100)
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"literatures": [
|
||||
{
|
||||
"id": "lit-uuid-001",
|
||||
"projectId": "d67f0b9a-b035-4804-aca1-0bd672c27d81",
|
||||
"pmid": "12345678",
|
||||
"title": "Efficacy of SGLT2 inhibitors...",
|
||||
"abstract": "Background: SGLT2 inhibitors...",
|
||||
"authors": "Smith J, Jones A",
|
||||
"journal": "NEJM",
|
||||
"publicationYear": 2020,
|
||||
"doi": "10.1056/NEJMoa1234567",
|
||||
"createdAt": "2025-11-18T07:32:00.000Z",
|
||||
"screeningResults": [
|
||||
{
|
||||
"conflictStatus": "none",
|
||||
"finalDecision": "include"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"pageSize": 20,
|
||||
"limit": 50,
|
||||
"total": 3,
|
||||
"totalPages": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl "http://localhost:3001/api/v1/asl/projects/{projectId}/literatures?page=1&limit=50"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2.4 删除文献
|
||||
|
||||
**接口**: `DELETE /api/v1/asl/literatures/:literatureId`
|
||||
**认证**: 需要
|
||||
**说明**: 删除指定文献(级联删除筛选结果)
|
||||
|
||||
**路径参数**:
|
||||
- `literatureId`: 文献ID (UUID)
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Literature deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl -X DELETE http://localhost:3001/api/v1/asl/literatures/{literatureId}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 筛选任务管理 (Screening Tasks)
|
||||
|
||||
> **注意**: 以下接口为待实现功能(Week 2计划)
|
||||
|
||||
#### 3.1 启动筛选任务
|
||||
|
||||
**接口**: `POST /api/v1/asl/projects/:projectId/screening/start`
|
||||
**认证**: 需要
|
||||
**说明**: 启动AI筛选任务(异步执行)
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"taskType": "title_abstract",
|
||||
"models": ["deepseek-chat", "qwen-max"],
|
||||
"concurrency": 3
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"taskId": "task-uuid-001",
|
||||
"status": "running",
|
||||
"totalItems": 100,
|
||||
"startedAt": "2025-11-18T08:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3.2 获取筛选进度
|
||||
|
||||
**接口**: `GET /api/v1/asl/tasks/:taskId/progress`
|
||||
**认证**: 需要
|
||||
**说明**: 获取筛选任务进度
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"taskId": "task-uuid-001",
|
||||
"status": "running",
|
||||
"totalItems": 100,
|
||||
"processedItems": 45,
|
||||
"successItems": 40,
|
||||
"failedItems": 2,
|
||||
"conflictItems": 3,
|
||||
"progress": 45,
|
||||
"estimatedEndAt": "2025-11-18T08:15:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3.3 获取筛选结果
|
||||
|
||||
**接口**: `GET /api/v1/asl/projects/:projectId/results`
|
||||
**认证**: 需要
|
||||
**说明**: 获取筛选结果列表
|
||||
|
||||
**查询参数**:
|
||||
- `page`: 页码(默认: 1)
|
||||
- `limit`: 每页数量(默认: 50)
|
||||
- `conflictOnly`: 只显示冲突项(布尔值)
|
||||
- `finalDecision`: 筛选决策(include / exclude / pending)
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"results": [
|
||||
{
|
||||
"id": "result-uuid-001",
|
||||
"literatureId": "lit-uuid-001",
|
||||
"literature": {
|
||||
"title": "...",
|
||||
"abstract": "..."
|
||||
},
|
||||
"dsConclusion": "include",
|
||||
"qwenConclusion": "include",
|
||||
"conflictStatus": "none",
|
||||
"finalDecision": "include",
|
||||
"createdAt": "2025-11-18T08:05:00.000Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"limit": 50,
|
||||
"total": 100,
|
||||
"totalPages": 5
|
||||
"totalPages": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,23 +555,277 @@ Accept: text/event-stream
|
||||
|
||||
---
|
||||
|
||||
## ⏳ 待完善内容
|
||||
#### 3.4 审核冲突文献
|
||||
|
||||
后续将补充:
|
||||
- 完整的API文档(所有接口详细说明)
|
||||
- 请求/响应示例
|
||||
- 错误码定义
|
||||
- 接口测试用例
|
||||
**接口**: `POST /api/v1/asl/results/review`
|
||||
**认证**: 需要
|
||||
**说明**: 批量审核冲突文献
|
||||
|
||||
**请求体**:
|
||||
```json
|
||||
{
|
||||
"projectId": "d67f0b9a-b035-4804-aca1-0bd672c27d81",
|
||||
"reviews": [
|
||||
{
|
||||
"resultId": "result-uuid-001",
|
||||
"finalDecision": "include"
|
||||
},
|
||||
{
|
||||
"resultId": "result-uuid-002",
|
||||
"finalDecision": "exclude",
|
||||
"exclusionReason": "不符合PICO标准中的干预措施"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"reviewedCount": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**文档版本:** v1.0
|
||||
**最后更新:** 2025-10-29
|
||||
## 📋 响应格式规范
|
||||
|
||||
### 1. 成功响应
|
||||
|
||||
**格式**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
// 响应数据
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**HTTP状态码**:
|
||||
- `200` - 成功(GET、PUT)
|
||||
- `201` - 创建成功(POST)
|
||||
|
||||
---
|
||||
|
||||
### 2. 错误响应
|
||||
|
||||
**格式**:
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "错误描述"
|
||||
}
|
||||
```
|
||||
|
||||
或(详细错误):
|
||||
```json
|
||||
{
|
||||
"error": "Missing required fields"
|
||||
}
|
||||
```
|
||||
|
||||
**常见HTTP状态码**:
|
||||
- `400` - 请求参数错误
|
||||
- `401` - 未授权
|
||||
- `403` - 无权限
|
||||
- `404` - 资源不存在
|
||||
- `500` - 服务器内部错误
|
||||
|
||||
**错误示例**:
|
||||
```json
|
||||
// 400 - 参数错误
|
||||
{
|
||||
"error": "Missing required fields"
|
||||
}
|
||||
|
||||
// 404 - 资源不存在
|
||||
{
|
||||
"error": "Project not found"
|
||||
}
|
||||
|
||||
// 500 - 服务器错误
|
||||
{
|
||||
"error": "Failed to create project"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 分页响应
|
||||
|
||||
**格式**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"items": [...], // 或 literatures、results 等
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"limit": 50,
|
||||
"total": 150,
|
||||
"totalPages": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**分页参数**:
|
||||
- `page`: 当前页码(从1开始)
|
||||
- `limit`: 每页数量
|
||||
- `total`: 总记录数
|
||||
- `totalPages`: 总页数
|
||||
|
||||
---
|
||||
|
||||
## 🔐 认证授权
|
||||
|
||||
### 当前状态(测试模式)
|
||||
|
||||
**测试用户**:
|
||||
- **用户ID**: `asl-test-user-001`
|
||||
- **邮箱**: `asl-test@example.com`
|
||||
- **权限**: 完全访问
|
||||
|
||||
**实现方式**:
|
||||
- 优先从JWT中获取`userId`
|
||||
- JWT不存在时使用默认测试用户ID
|
||||
|
||||
### 生产环境(待实现)
|
||||
|
||||
**认证流程**:
|
||||
1. 用户登录获取JWT Token
|
||||
2. 请求头携带Token: `Authorization: Bearer {token}`
|
||||
3. 中间件验证Token并提取`userId`
|
||||
4. 控制器使用`userId`查询用户数据
|
||||
|
||||
**中间件示例**:
|
||||
```typescript
|
||||
// 待实现
|
||||
fastify.addHook('preHandler', async (request, reply) => {
|
||||
const token = request.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) {
|
||||
return reply.status(401).send({ error: 'Unauthorized' });
|
||||
}
|
||||
const userId = await verifyJWT(token);
|
||||
(request as any).userId = userId;
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 API测试
|
||||
|
||||
### 快速测试脚本
|
||||
|
||||
**测试所有API**:
|
||||
```bash
|
||||
cd AIclinicalresearch/backend
|
||||
npx tsx scripts/test-asl-api.ts
|
||||
```
|
||||
|
||||
**测试结果**:
|
||||
```
|
||||
🚀 开始测试 ASL 模块 API...
|
||||
📍 测试 1/7: 健康检查 ✅
|
||||
📍 测试 2/7: 创建筛选项目 ✅
|
||||
📍 测试 3/7: 获取项目列表 ✅
|
||||
📍 测试 4/7: 获取项目详情 ✅
|
||||
📍 测试 5/7: 导入文献 ✅
|
||||
📍 测试 6/7: 获取文献列表 ✅
|
||||
📍 测试 7/7: 更新项目 ✅
|
||||
═══════════════════════════════════
|
||||
🎉 所有测试通过!(7/7 - 100%)
|
||||
═══════════════════════════════════
|
||||
```
|
||||
|
||||
### Postman集合
|
||||
|
||||
**导入说明**:
|
||||
1. 创建新的Collection: `ASL API`
|
||||
2. 设置环境变量:
|
||||
- `base_url`: `http://localhost:3001`
|
||||
- `api_prefix`: `/api/v1/asl`
|
||||
3. 导入下方接口
|
||||
|
||||
**示例请求** (Postman):
|
||||
```
|
||||
POST {{base_url}}{{api_prefix}}/projects
|
||||
Headers:
|
||||
Content-Type: application/json
|
||||
Body (raw JSON):
|
||||
{
|
||||
"projectName": "测试项目",
|
||||
"picoCriteria": {...},
|
||||
"inclusionCriteria": "英文文献",
|
||||
"exclusionCriteria": "综述"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 性能指标
|
||||
|
||||
### 响应时间目标
|
||||
|
||||
| 接口类型 | 目标响应时间 | 说明 |
|
||||
|---------|-------------|------|
|
||||
| 单个查询 | < 100ms | 项目详情、文献详情 |
|
||||
| 列表查询 | < 200ms | 项目列表、文献列表 |
|
||||
| 创建/更新 | < 300ms | 创建项目、更新项目 |
|
||||
| 批量导入 | < 2s | 导入100篇文献 |
|
||||
| LLM筛选 | 4-6s/篇 | 双模型并行筛选 |
|
||||
|
||||
### 并发能力
|
||||
|
||||
- **API服务器**: 支持100+并发请求
|
||||
- **LLM筛选**: 并发数为3(可配置)
|
||||
- **数据库连接池**: 17个连接
|
||||
|
||||
---
|
||||
|
||||
## 🔄 版本历史
|
||||
|
||||
### v2.0 (2025-11-18)
|
||||
- ✅ 实现10个核心API端点
|
||||
- ✅ 完成项目管理功能
|
||||
- ✅ 完成文献管理功能
|
||||
- ✅ 添加测试脚本和文档
|
||||
- ✅ 所有接口测试通过
|
||||
|
||||
### v1.0 (2025-10-29)
|
||||
- 初始API设计规范
|
||||
- 定义接口结构
|
||||
|
||||
---
|
||||
|
||||
## ⏳ 后续规划
|
||||
|
||||
### Week 2
|
||||
- [ ] 实现筛选任务API (3个接口)
|
||||
- [ ] 实现冲突审核API (2个接口)
|
||||
- [ ] 添加SSE进度推送
|
||||
- [ ] 集成异步任务队列
|
||||
|
||||
### Week 3-4
|
||||
- [ ] 添加JWT认证中间件
|
||||
- [ ] 实现权限控制
|
||||
- [ ] 添加API限流
|
||||
- [ ] 完善错误处理
|
||||
|
||||
---
|
||||
|
||||
**文档版本:** v2.0
|
||||
**最后更新:** 2025-11-18
|
||||
**维护者:** AI智能文献开发团队
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [数据库设计文档](./01-数据库设计.md)
|
||||
- [API测试报告](../../../backend/ASL-API-测试报告.md)
|
||||
- [Week 1完成报告](../05-开发记录/2025-11-18-Week1完成报告.md)
|
||||
|
||||
@@ -949,3 +949,5 @@ async function selectSimilarExamples(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
851
docs/03-业务模块/ASL-AI智能文献/02-技术设计/07-智能Prompt生成模块开发计划.md
Normal file
851
docs/03-业务模块/ASL-AI智能文献/02-技术设计/07-智能Prompt生成模块开发计划.md
Normal file
@@ -0,0 +1,851 @@
|
||||
# 智能Prompt生成模块 - 开发计划
|
||||
|
||||
**版本**: v1.0
|
||||
**日期**: 2025-11-18
|
||||
**原则**: 简单、直接、可执行
|
||||
|
||||
---
|
||||
|
||||
## 核心目标
|
||||
|
||||
**解决问题**: 消除AI与人类对边界情况的理解差异
|
||||
|
||||
**核心流程**:
|
||||
```
|
||||
用户输入PICOS → AI理解分析 → 生成Prompt → 用户修改 → 开始筛选
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MVP阶段(必做)
|
||||
|
||||
### 功能范围
|
||||
|
||||
#### 1. 用户输入 ✅
|
||||
|
||||
**前端表单**:
|
||||
```typescript
|
||||
{
|
||||
pico: {
|
||||
population: string; // 研究人群
|
||||
intervention: string; // 干预措施
|
||||
comparison: string; // 对照
|
||||
outcome: string; // 结局指标
|
||||
studyDesign: string; // 研究设计
|
||||
},
|
||||
inclusionCriteria: string; // 纳入标准
|
||||
exclusionCriteria: string; // 排除标准
|
||||
}
|
||||
```
|
||||
|
||||
**实现**: 一个表单页面,7个输入框
|
||||
|
||||
---
|
||||
|
||||
#### 2. AI理解与分析 🆕
|
||||
|
||||
**输入**: 用户的PICOS + 纳排标准
|
||||
|
||||
**输出**:
|
||||
```typescript
|
||||
{
|
||||
understanding: {
|
||||
mustInclude: string[]; // 必须纳入的要素(3-5条)
|
||||
mustExclude: string[]; // 必须排除的要素(3-5条)
|
||||
ambiguities: [ // 模糊的边界情况(5-8个)
|
||||
{
|
||||
id: number;
|
||||
question: string; // "如果研究人群是欧美但RCT质量高?"
|
||||
aiSuggestion: 'include' | 'exclude' | 'uncertain';
|
||||
reason: string; // AI的建议理由
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**API**:
|
||||
```
|
||||
POST /api/v1/asl/analyze-picos
|
||||
```
|
||||
|
||||
**实现**: 调用LLM分析用户输入
|
||||
|
||||
---
|
||||
|
||||
#### 3. 用户确认界面 🆕
|
||||
|
||||
**显示**:
|
||||
- ✅ 必须纳入(可勾选/取消)
|
||||
- ❌ 必须排除(可勾选/取消)
|
||||
- 🤔 边界情况(逐个确认:纳入/排除/不确定)
|
||||
|
||||
**实现**: Modal对话框,分三个区域
|
||||
|
||||
---
|
||||
|
||||
#### 4. 自动生成Prompt 🆕
|
||||
|
||||
**输入**: 用户确认后的规则
|
||||
|
||||
**输出**: 完整的筛选Prompt
|
||||
|
||||
**关键**: 将用户确认的边界规则注入到Prompt中
|
||||
|
||||
```
|
||||
## 特殊规则(基于您的确认)
|
||||
|
||||
1. 地域要求:优先亚洲人群,但欧美高质量RCT也可纳入
|
||||
2. 研究类型:排除综述,但2020年后Meta分析可纳入
|
||||
3. 对照类型:安慰剂对照,或另一种标准药物也可接受
|
||||
...
|
||||
```
|
||||
|
||||
**API**:
|
||||
```
|
||||
POST /api/v1/asl/generate-prompt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 5. Prompt编辑器 🆕
|
||||
|
||||
**功能**:
|
||||
- 显示生成的Prompt
|
||||
- 支持用户编辑
|
||||
- 保存并使用
|
||||
|
||||
**实现**: 简单的Textarea + 保存按钮
|
||||
|
||||
---
|
||||
|
||||
#### 6. 筛选结果增强 ⭐ **重要**
|
||||
|
||||
**当前问题**: 只显示最终决策(include/exclude/pending)
|
||||
|
||||
**改进**: 显示**两个模型的完整理由**
|
||||
|
||||
```typescript
|
||||
{
|
||||
literatureId: string;
|
||||
finalDecision: 'include' | 'exclude' | 'pending';
|
||||
|
||||
// ⭐ 新增:两个模型的详细结果
|
||||
model1: {
|
||||
modelName: 'DeepSeek-V3';
|
||||
conclusion: 'exclude';
|
||||
confidence: 0.92;
|
||||
judgment: { P: 'match', I: 'match', C: 'mismatch', S: 'match' };
|
||||
reason: '虽然P、I、S维度匹配,但对照组为另一种药物而非安慰剂...' // ⭐ 关键
|
||||
},
|
||||
model2: {
|
||||
modelName: 'Qwen-Max';
|
||||
conclusion: 'include';
|
||||
confidence: 0.85;
|
||||
judgment: { P: 'match', I: 'match', C: 'partial', S: 'match' };
|
||||
reason: '研究人群和干预措施匹配,对照组虽非安慰剂但有对比意义...' // ⭐ 关键
|
||||
},
|
||||
|
||||
hasConflict: true; // 两个模型判断不一致
|
||||
conflictFields: ['conclusion', 'C'];
|
||||
}
|
||||
```
|
||||
|
||||
**前端显示**:
|
||||
```jsx
|
||||
<Card title="筛选结果">
|
||||
<Alert type={finalDecision === 'pending' ? 'warning' : 'success'}>
|
||||
最终决策: {finalDecision}
|
||||
</Alert>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Card title="🤖 DeepSeek-V3" type="inner">
|
||||
<Tag color={model1.conclusion === 'include' ? 'green' : 'red'}>
|
||||
{model1.conclusion}
|
||||
</Tag>
|
||||
<Statistic title="置信度" value={model1.confidence} />
|
||||
<Divider />
|
||||
<h4>判断理由:</h4>
|
||||
<p>{model1.reason}</p> {/* ⭐ 显示理由 */}
|
||||
<Collapse>
|
||||
<Panel header="PICO维度详情">
|
||||
P: {model1.judgment.P}<br/>
|
||||
I: {model1.judgment.I}<br/>
|
||||
C: {model1.judgment.C}<br/>
|
||||
S: {model1.judgment.S}
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Card title="🤖 Qwen-Max" type="inner">
|
||||
{/* 同上 */}
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{hasConflict && (
|
||||
<Alert type="warning" showIcon>
|
||||
⚠️ 两个模型判断不一致,建议人工复核
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* ⭐ 人工复核按钮 */}
|
||||
<Button type="primary" onClick={handleManualReview}>
|
||||
人工复核此文献
|
||||
</Button>
|
||||
</Card>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### MVP开发清单
|
||||
|
||||
**Week 1: 后端**
|
||||
|
||||
| 任务 | 估时 | 优先级 |
|
||||
|------|------|--------|
|
||||
| API: 分析PICOS | 2天 | P0 |
|
||||
| API: 生成Prompt | 1天 | P0 |
|
||||
| 增强筛选结果结构 | 0.5天 | P0 |
|
||||
| 测试 | 0.5天 | P0 |
|
||||
|
||||
**Week 2: 前端**
|
||||
|
||||
| 任务 | 估时 | 优先级 |
|
||||
|------|------|--------|
|
||||
| PICOS输入表单 | 0.5天 | P0 |
|
||||
| 用户确认界面 | 1.5天 | P0 |
|
||||
| Prompt编辑器 | 0.5天 | P0 |
|
||||
| 结果展示增强 | 1天 | P0 |
|
||||
| 测试与调优 | 0.5天 | P0 |
|
||||
|
||||
**总计**: 2周(10个工作日)
|
||||
|
||||
---
|
||||
|
||||
## 2.0阶段(可选功能)
|
||||
|
||||
### 功能1: Few-shot自动学习 🔮
|
||||
|
||||
**触发场景**: 用户纠正AI判断后
|
||||
|
||||
**流程**:
|
||||
```
|
||||
1. AI判断: Exclude
|
||||
2. 用户纠正: 应该是Include
|
||||
3. 用户说明理由: "虽然是欧美人群,但RCT质量高"
|
||||
↓
|
||||
4. 系统记录案例
|
||||
↓
|
||||
5. 下次筛选时,将此案例作为Few-shot示例加入Prompt
|
||||
```
|
||||
|
||||
**数据结构**:
|
||||
```typescript
|
||||
{
|
||||
caseId: string;
|
||||
literature: {
|
||||
title: string;
|
||||
abstract: string;
|
||||
},
|
||||
aiDecision: 'exclude';
|
||||
userDecision: 'include';
|
||||
userReason: '虽然是欧美人群,但RCT质量高';
|
||||
picoCriteria: {...}; // 当时的PICOS
|
||||
createdAt: Date;
|
||||
}
|
||||
```
|
||||
|
||||
**Prompt增强**:
|
||||
```
|
||||
## 参考案例(Few-shot示例)
|
||||
|
||||
以下是您之前纠正的案例,请参考:
|
||||
|
||||
案例1:
|
||||
标题: TICA-CLOP STUDY...
|
||||
AI判断: Exclude(因为北非人群)
|
||||
您的决策: Include
|
||||
您的理由: 虽然是北非人群,但RCT质量高,方法有参考价值
|
||||
→ 启示: 地域要求可以灵活,如果研究质量高
|
||||
|
||||
案例2:
|
||||
...
|
||||
```
|
||||
|
||||
**实现复杂度**: 中等(需要案例库管理)
|
||||
|
||||
---
|
||||
|
||||
### 功能2: 测试模式 🧪
|
||||
|
||||
**使用场景**: 用户想先测试10篇文献,训练AI理解
|
||||
|
||||
**流程**:
|
||||
```
|
||||
1. 用户上传10篇测试文献(5篇纳入 + 5篇排除)
|
||||
↓
|
||||
2. 用户逐篇标注: Include/Exclude + 理由
|
||||
↓
|
||||
3. AI学习用户的判断模式
|
||||
↓
|
||||
4. 生成定制化Prompt
|
||||
↓
|
||||
5. 用于正式筛选
|
||||
```
|
||||
|
||||
**界面**:
|
||||
```jsx
|
||||
<TestMode>
|
||||
<Upload>上传10篇测试文献(Excel/JSON)</Upload>
|
||||
|
||||
<Table>
|
||||
{testCases.map(lit => (
|
||||
<Row>
|
||||
<td>{lit.title}</td>
|
||||
<td>
|
||||
<Radio.Group>
|
||||
<Radio value="include">纳入</Radio>
|
||||
<Radio value="exclude">排除</Radio>
|
||||
</Radio.Group>
|
||||
</td>
|
||||
<td>
|
||||
<Input.TextArea placeholder="请说明理由" />
|
||||
</td>
|
||||
</Row>
|
||||
))}
|
||||
</Table>
|
||||
|
||||
<Button onClick={analyzeTestCases}>
|
||||
分析我的判断模式
|
||||
</Button>
|
||||
</TestMode>
|
||||
```
|
||||
|
||||
**AI分析**:
|
||||
```
|
||||
用户的判断模式分析:
|
||||
|
||||
1. 地域灵活性:
|
||||
- 案例1(北非RCT)→ 纳入
|
||||
- 案例3(欧洲队列)→ 排除
|
||||
→ 结论: 只要是RCT就可接受非亚洲人群
|
||||
|
||||
2. 研究类型:
|
||||
- 案例2(Meta分析)→ 纳入
|
||||
- 案例5(传统综述)→ 排除
|
||||
→ 结论: Meta分析可接受,传统综述排除
|
||||
|
||||
3. 时间要求:
|
||||
- 案例4(2019年发表)→ 排除
|
||||
→ 结论: 严格执行2020年后要求
|
||||
```
|
||||
|
||||
**实现复杂度**: 高(需要模式识别)
|
||||
|
||||
---
|
||||
|
||||
### 功能3: Prompt模板库 📚
|
||||
|
||||
**功能**:
|
||||
- 保存用户生成的Prompt为模板
|
||||
- 下次可以直接复用
|
||||
- 可以分享给团队成员
|
||||
|
||||
**实现复杂度**: 低
|
||||
|
||||
---
|
||||
|
||||
### 2.0开发清单
|
||||
|
||||
| 功能 | 估时 | 优先级 | 依赖 |
|
||||
|------|------|--------|------|
|
||||
| Few-shot学习 | 3天 | P1 | MVP完成 |
|
||||
| 测试模式 | 5天 | P2 | MVP完成 |
|
||||
| Prompt模板库 | 2天 | P1 | MVP完成 |
|
||||
|
||||
**总计**: 2周
|
||||
|
||||
---
|
||||
|
||||
## 技术实现细节
|
||||
|
||||
### 1. AI分析PICOS的Prompt
|
||||
|
||||
```typescript
|
||||
const analyzePrompt = `
|
||||
你是医学文献筛选专家。用户提供了PICOS标准和纳排标准,请分析并生成:
|
||||
|
||||
【用户输入】
|
||||
人群: ${population}
|
||||
干预: ${intervention}
|
||||
对照: ${comparison}
|
||||
结局: ${outcome}
|
||||
设计: ${studyDesign}
|
||||
|
||||
纳入标准:
|
||||
${inclusionCriteria}
|
||||
|
||||
排除标准:
|
||||
${exclusionCriteria}
|
||||
|
||||
【分析任务】
|
||||
1. 提取必须纳入的核心要素(3-5条)
|
||||
2. 提取必须排除的要素(3-5条)
|
||||
3. 识别模糊的边界情况(5-8个),每个边界情况包括:
|
||||
- 具体问题描述
|
||||
- 你的建议(include/exclude/uncertain)
|
||||
- 建议理由
|
||||
|
||||
【输出格式】
|
||||
严格JSON格式:
|
||||
{
|
||||
"mustInclude": ["要素1", "要素2", ...],
|
||||
"mustExclude": ["要素1", "要素2", ...],
|
||||
"ambiguities": [
|
||||
{
|
||||
"id": 1,
|
||||
"question": "如果研究人群是欧美但RCT质量高?",
|
||||
"aiSuggestion": "exclude",
|
||||
"reason": "用户明确要求'亚洲人群',其他地域不符合"
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
`;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 生成Prompt的核心逻辑
|
||||
|
||||
```typescript
|
||||
function generateCustomPrompt(
|
||||
pico: PicoCriteria,
|
||||
inclusionCriteria: string,
|
||||
exclusionCriteria: string,
|
||||
userConfirmedRules: BoundaryRule[]
|
||||
): string {
|
||||
|
||||
// 基础Prompt(从标准模板开始)
|
||||
let prompt = getStandardPromptTemplate();
|
||||
|
||||
// 注入用户确认的边界规则
|
||||
const boundaryRulesSection = `
|
||||
## ⭐ 特殊边界规则(基于您的确认)
|
||||
|
||||
${userConfirmedRules.map((rule, index) => `
|
||||
${index + 1}. ${rule.category}:
|
||||
- 标准规则: ${rule.standardRule}
|
||||
- 您的确认: ${rule.userDecision === 'include' ? '✅ 可以纳入' : '❌ 必须排除'}
|
||||
- 具体情况: ${rule.situation}
|
||||
`).join('\n')}
|
||||
|
||||
⚠️ 请严格遵守以上特殊规则,这些是用户明确确认的判断标准。
|
||||
`;
|
||||
|
||||
// 将边界规则插入到Prompt的合适位置
|
||||
prompt = prompt.replace(
|
||||
'## 筛选任务',
|
||||
boundaryRulesSection + '\n\n## 筛选任务'
|
||||
);
|
||||
|
||||
return prompt;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 数据库设计
|
||||
|
||||
**新表: prompt_configurations**
|
||||
|
||||
```sql
|
||||
CREATE TABLE asl_schema.prompt_configurations (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
project_id UUID NOT NULL,
|
||||
|
||||
-- 用户输入
|
||||
pico_criteria JSONB NOT NULL,
|
||||
inclusion_criteria TEXT NOT NULL,
|
||||
exclusion_criteria TEXT NOT NULL,
|
||||
|
||||
-- AI分析结果
|
||||
ai_understanding JSONB NOT NULL, -- mustInclude, mustExclude, ambiguities
|
||||
|
||||
-- 用户确认
|
||||
user_confirmed_rules JSONB NOT NULL, -- 用户确认后的边界规则
|
||||
|
||||
-- 生成的Prompt
|
||||
generated_prompt TEXT NOT NULL,
|
||||
final_prompt TEXT NOT NULL, -- 用户编辑后的最终版本
|
||||
|
||||
-- 元数据
|
||||
version VARCHAR(20) DEFAULT 'v1.0',
|
||||
is_template BOOLEAN DEFAULT false,
|
||||
template_name VARCHAR(100),
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
**新表: few_shot_cases**(2.0阶段)
|
||||
|
||||
```sql
|
||||
CREATE TABLE asl_schema.few_shot_cases (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
project_id UUID NOT NULL,
|
||||
|
||||
-- 文献信息
|
||||
literature_id UUID NOT NULL,
|
||||
literature_title TEXT NOT NULL,
|
||||
literature_abstract TEXT NOT NULL,
|
||||
|
||||
-- AI判断
|
||||
ai_decision VARCHAR(20) NOT NULL, -- include/exclude
|
||||
ai_reason TEXT NOT NULL,
|
||||
|
||||
-- 用户纠正
|
||||
user_decision VARCHAR(20) NOT NULL,
|
||||
user_reason TEXT NOT NULL,
|
||||
|
||||
-- PICOS上下文
|
||||
pico_criteria JSONB NOT NULL,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API设计
|
||||
|
||||
### MVP阶段
|
||||
|
||||
#### 1. 分析PICOS
|
||||
|
||||
```
|
||||
POST /api/v1/asl/prompt/analyze
|
||||
|
||||
Request:
|
||||
{
|
||||
"projectId": "uuid",
|
||||
"pico": {
|
||||
"population": "...",
|
||||
"intervention": "...",
|
||||
"comparison": "...",
|
||||
"outcome": "...",
|
||||
"studyDesign": "..."
|
||||
},
|
||||
"inclusionCriteria": "...",
|
||||
"exclusionCriteria": "..."
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"configId": "uuid", // 保存的配置ID
|
||||
"understanding": {
|
||||
"mustInclude": ["要素1", "要素2"],
|
||||
"mustExclude": ["要素1", "要素2"],
|
||||
"ambiguities": [
|
||||
{
|
||||
"id": 1,
|
||||
"question": "...",
|
||||
"aiSuggestion": "exclude",
|
||||
"reason": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2. 确认边界规则
|
||||
|
||||
```
|
||||
POST /api/v1/asl/prompt/confirm-rules
|
||||
|
||||
Request:
|
||||
{
|
||||
"configId": "uuid",
|
||||
"confirmedRules": [
|
||||
{
|
||||
"ambiguityId": 1,
|
||||
"userDecision": "include", // include/exclude/uncertain
|
||||
"userNote": "虽然不是亚洲人群,但RCT质量高" // 可选
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"generatedPrompt": "完整的Prompt文本..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3. 保存最终Prompt
|
||||
|
||||
```
|
||||
POST /api/v1/asl/prompt/save
|
||||
|
||||
Request:
|
||||
{
|
||||
"configId": "uuid",
|
||||
"finalPrompt": "用户编辑后的Prompt...",
|
||||
"saveAsTemplate": false,
|
||||
"templateName": "" // 如果保存为模板
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"configId": "uuid",
|
||||
"promptVersion": "v1.0.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 4. 使用自定义Prompt筛选
|
||||
|
||||
```
|
||||
POST /api/v1/asl/screen/literature
|
||||
|
||||
Request:
|
||||
{
|
||||
"projectId": "uuid",
|
||||
"literatureId": "uuid",
|
||||
"configId": "uuid", // 使用哪个Prompt配置
|
||||
"models": ["deepseek-chat", "qwen-max"]
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"literatureId": "uuid",
|
||||
"finalDecision": "pending",
|
||||
|
||||
// ⭐ 关键:两个模型的详细结果
|
||||
"model1": {
|
||||
"modelName": "DeepSeek-V3",
|
||||
"conclusion": "exclude",
|
||||
"confidence": 0.92,
|
||||
"judgment": {...},
|
||||
"evidence": {...},
|
||||
"reason": "完整的排除理由..." // ⭐
|
||||
},
|
||||
"model2": {
|
||||
"modelName": "Qwen-Max",
|
||||
"conclusion": "include",
|
||||
"confidence": 0.85,
|
||||
"judgment": {...},
|
||||
"evidence": {...},
|
||||
"reason": "完整的纳入理由..." // ⭐
|
||||
},
|
||||
|
||||
"hasConflict": true,
|
||||
"conflictFields": ["conclusion"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.0阶段(可选)
|
||||
|
||||
#### 5. 提交Few-shot案例
|
||||
|
||||
```
|
||||
POST /api/v1/asl/prompt/add-few-shot
|
||||
|
||||
Request:
|
||||
{
|
||||
"configId": "uuid",
|
||||
"literatureId": "uuid",
|
||||
"aiDecision": "exclude",
|
||||
"aiReason": "...",
|
||||
"userDecision": "include",
|
||||
"userReason": "虽然是欧美人群,但..."
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"caseId": "uuid",
|
||||
"totalCases": 3 // 已有多少个Few-shot案例
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6. 基于Few-shot重新生成Prompt
|
||||
|
||||
```
|
||||
POST /api/v1/asl/prompt/regenerate-with-few-shot
|
||||
|
||||
Request:
|
||||
{
|
||||
"configId": "uuid"
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"updatedPrompt": "包含Few-shot示例的新Prompt...",
|
||||
"fewShotCasesUsed": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 测试计划
|
||||
|
||||
### MVP测试
|
||||
|
||||
**测试数据**: 卒中研究(已有5篇)
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. **场景1: 正常流程**
|
||||
- 输入PICOS → AI分析 → 用户确认 → 生成Prompt → 筛选
|
||||
- 验证:两个模型的理由是否完整显示
|
||||
|
||||
2. **场景2: 边界情况确认**
|
||||
- 用户确认"欧美RCT可纳入" → 验证Prompt中是否包含此规则
|
||||
- 验证:实际筛选时是否遵守此规则
|
||||
|
||||
3. **场景3: 用户编辑Prompt**
|
||||
- 用户修改生成的Prompt → 验证修改是否生效
|
||||
|
||||
4. **场景4: 模型冲突**
|
||||
- 验证:两个模型判断不一致时,理由是否清晰展示
|
||||
|
||||
**测试指标**:
|
||||
- Prompt生成准确率: >90%
|
||||
- 用户满意度: >80%
|
||||
- 理由展示完整性: 100%
|
||||
|
||||
---
|
||||
|
||||
### 2.0测试
|
||||
|
||||
**测试场景**:
|
||||
|
||||
1. **Few-shot学习**
|
||||
- 用户纠正3个案例 → 验证Prompt中是否包含这些案例
|
||||
- 验证:新的筛选是否改进
|
||||
|
||||
2. **测试模式**
|
||||
- 用户标注10篇 → AI分析模式 → 生成Prompt
|
||||
- 验证:生成的Prompt是否符合用户偏好
|
||||
|
||||
---
|
||||
|
||||
## 成功标准
|
||||
|
||||
### MVP阶段
|
||||
|
||||
| 指标 | 目标 |
|
||||
|------|------|
|
||||
| Prompt生成准确率 | >90% |
|
||||
| 用户完成配置时间 | <5分钟 |
|
||||
| 理由展示完整性 | 100% |
|
||||
| 模型冲突识别率 | 100% |
|
||||
| 用户满意度 | >80% |
|
||||
|
||||
### 2.0阶段
|
||||
|
||||
| 指标 | 目标 |
|
||||
|------|------|
|
||||
| Few-shot改进准确率 | +15% |
|
||||
| 测试模式匹配度 | >85% |
|
||||
| Prompt模板复用率 | >60% |
|
||||
|
||||
---
|
||||
|
||||
## 风险与应对
|
||||
|
||||
### 风险1: LLM生成的边界问题质量不稳定
|
||||
|
||||
**应对**:
|
||||
- 使用Few-shot Prompt
|
||||
- 人工审核常见边界情况
|
||||
- 提供默认边界问题库
|
||||
|
||||
### 风险2: 用户不愿意花时间确认
|
||||
|
||||
**应对**:
|
||||
- 只显示5个高优先级问题
|
||||
- 其他使用AI默认建议
|
||||
- 提供"快速模式"(跳过确认)
|
||||
|
||||
### 风险3: 两个模型理由过长,难以对比
|
||||
|
||||
**应对**:
|
||||
- 提取理由关键句(前100字)
|
||||
- 提供展开/收起按钮
|
||||
- 高亮冲突点
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
### MVP核心(必做)
|
||||
|
||||
1. ✅ PICOS输入表单
|
||||
2. ✅ AI分析与边界问题生成
|
||||
3. ✅ 用户确认界面
|
||||
4. ✅ 自动生成Prompt
|
||||
5. ✅ Prompt编辑器
|
||||
6. ✅ **显示两个模型的完整理由** ⭐
|
||||
|
||||
**开发时间**: 2周
|
||||
|
||||
---
|
||||
|
||||
### 2.0扩展(可选)
|
||||
|
||||
1. 🔮 Few-shot自动学习
|
||||
2. 🧪 测试模式
|
||||
3. 📚 Prompt模板库
|
||||
|
||||
**开发时间**: 2周
|
||||
|
||||
---
|
||||
|
||||
**原则**: MVP先做到简单可用,2.0再做智能化
|
||||
|
||||
**下一步**: 开始MVP阶段开发
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: v1.0
|
||||
**作者**: AI Assistant
|
||||
**审核**: [待用户确认]
|
||||
**日期**: 2025-11-18
|
||||
|
||||
|
||||
Reference in New Issue
Block a user