Completed: - Unifuncs DeepSearch API site coverage test (18 medical sites, 9 tier-1 available) - ClinicalTrials.gov dedicated test (4 strategies, English query + depth>=10 works best) - Deep Research V2.0 development plan (5-day phased delivery) - DeepResearch engine capability guide (docs/02-common-capability/) - Test scripts: test-unifuncs-site-coverage.ts, test-unifuncs-clinicaltrials.ts Key findings: - Tier-1 sites: PubMed(28), ClinicalTrials(38), NCBI(18), Scholar(10), Cochrane(4), CNKI(7), SinoMed(9), GeenMedical(5), VIP(1) - Paid databases (WoS/Embase/Scopus/Ovid) cannot be accessed (no credential support) - ClinicalTrials.gov requires English queries with max_depth>=10 Updated: ASL module status doc, system status doc, common capability list Co-authored-by: Cursor <cursoragent@cursor.com>
6.9 KiB
技术设计文档 (TDD):ASL - 智能文献检索 (Deep Research) MVP 版
文档版本: v4.4-Tech (MVP 自然语言确认版)
核心架构: LLM Requirement Expansion (需求扩写) + Postgres-Only (pg-boss) + Unifuncs Async API
🏗️ 1. 系统数据流向 (Data Flow)
MVP 版本的架构最大程度降低了状态维护的成本,充分利用 Unifuncs 原生支持自然语言查询的能力。
- Client 发送原始简短自然语言 -> Node.js 调用 DeepSeek-V3 -> 返回结构化、扩写后的自然语言检索需求(Search Requirements)(非布尔检索式)。
- Client 展示该检索需求(普通文本/Markdown),允许用户直接进行文字修改与补充 -> 用户点击执行,发送修改后的 Confirmed Requirement -> Node.js 创建 pg-boss 任务。
- Worker 启动 -> 将用户确认的自然语言需求直接作为 content 传给 Unifuncs 创建任务 -> 每 5 秒轮询一次 Unifuncs -> 增量日志写入 PostgreSQL。
- Client 每 3 秒轮询 Node.js 获取日志 -> 渲染 Terminal -> 任务完成,渲染结果。
🗄️ 2. 数据库设计 (Prisma)
极简表结构,去掉了复杂的澄清记录,只保留原始问题和最终执行的自然语言需求。
model AslResearchTask {
id String @id @default(uuid())
user_id String
// Step 1 & 2
original_query String @db.Text
target_sources Json // 选中的数据源,如 ["pubmed.ncbi.nlm.nih.gov"]
filters Json // 高级过滤条件
confirmed_requirement String? @db.Text // 核心字段:用户复核并修改后的自然语言检索需求
// Step 3
status String // 'draft', 'pending', 'running', 'completed', 'failed'
unifuncs_task_id String? // 外部API的ID
execution_logs Json? // 终端日志 [{type: 'log', text: '...'}, ...]
// Step 4
result_list Json?
synthesis_report String? @db.Text
created_at DateTime @default(now())
updated_at DateTime @updatedAt
}
🔌 3. 核心 API 契约
3.1 检索需求扩写接口 (同步, 无状态)
- POST /api/v1/asl/research/generate-requirement
- 处理: 拦截用户简短输入,拼装 System Prompt,调用系统内置 LLMFactory(DeepSeek-V3)。
- System Prompt 示例: “你是一个医学检索辅助专家。请将用户简短的研究意图,扩写并梳理成一份条理清晰的自然语言检索需求说明。内容包括:1. 核心检索主题;2. 建议包含的专业关键词(中英文);3. 目标人群及干预措施限定;4. 文献类型建议(如 RCT)。输出纯文本,方便用户二次编辑。”
- 同时: 创建数据库记录(状态为 draft)。
- 返回: { taskId: "uuid", generatedRequirement: "研究主题:他汀类药物...\n目标人群:...\n检索要求:..." }
3.2 任务启动接口 (进入异步队列)
- PUT /api/v1/asl/research/tasks/:id/execute
- 请求体: { confirmedRequirement: string }
- 处理: 1. 更新 AslResearchTask 的 confirmed_requirement 字段。
2. jobQueue.createJob('deep-research-worker', { taskId: id })
3. 更新状态为 pending。 - 返回: { success: true }
3.3 任务状态与日志轮询接口
- GET /api/v1/asl/research/tasks/:id
- 返回: 包含 status, execution_logs, 以及(若完成)synthesis_report 和 result_list。
⚙️ 4. 后台 Worker 逻辑 (Unifuncs 集成)
使用平台现有的 pg-boss 机制。
// backend/src/modules/asl/workers/DeepResearchWorker.ts
export async function processDeepResearch(job: Job) {
const taskId = job.data.taskId;
const task = await prisma.aslResearchTask.findUnique({ where: { id: taskId } });
// 1. 发起 Unifuncs 创建任务请求
const unifuncsPayload = {
model: "s2",
// 💡 核心变更:直接将用户确认的、详细的自然语言需求传给 Unifuncs,由 Unifuncs 自己去理解和拆解检索词
messages: [{
role: "user",
content: `请根据以下详细检索需求执行深度研究:\n${task.confirmed_requirement}`
}],
introduction: "你是一名资深的循证医学研究员。请严格遵循用户的检索需求,在指定数据库中执行详尽的深度检索。",
max_depth: 25,
domain_scope: task.target_sources,
// 强制输出格式以分离报告与文献JSON
output_prompt: `
<REPORT_SECTION>
[撰写综合报告]
</REPORT_SECTION>
<JSON_LIST_SECTION>
[输出严格的文献JSON数组]
</JSON_LIST_SECTION>
`,
reference_style: "link"
};
const createRes = await unifuncsClient.createTask(unifuncsPayload);
const unifuncsId = createRes.data.task_id;
await prisma.aslResearchTask.update({
where: { id: taskId },
data: { unifuncs_task_id: unifuncsId, status: 'running' }
});
// 2. 轮询 Unifuncs 状态 (防无限死循环,设置最大重试次数)
let isCompleted = false;
let maxRetries = 120; // 假设每5秒查一次,最多查10分钟
while (!isCompleted && maxRetries > 0) {
await sleep(5000);
const queryRes = await unifuncsClient.queryTask(unifuncsId);
// 解析增量日志 reasoning\_content
const logs \= parseReasoningToLogs(queryRes.data.result?.reasoning\_content);
// 更新数据库日志 (覆盖或追加)
await prisma.aslResearchTask.update({
where: { id: taskId },
data: { execution\_logs: logs }
});
if (queryRes.data.status \=== 'completed') {
isCompleted \= true;
// 解析提取 \<REPORT\_SECTION\> 和 \<JSON\_LIST\_SECTION\>
const report \= extractReport(queryRes.data.result.content);
const list \= extractJsonList(queryRes.data.result.content);
await prisma.aslResearchTask.update({
where: { id: taskId },
data: { status: 'completed', synthesis\_report: report, result\_list: list }
});
}
maxRetries--;
}
}
🛡️ 5. 技术优势
- 零学习成本:去除了对医生来说晦涩难懂的“布尔逻辑检索式”,整个确认过程完全采用大白话(自然语言),用户审查和修改都极其自然。
- 充分发挥 API 威力:将“拆解关键词、发起搜索、阅读网页”的复杂动作全部下放给专业的 Unifuncs 引擎,本系统架构只做轻量级的“需求扩写”和“流式日志透传”,代码稳定性极高。
- 极速上线:前端页面仅需一个大文本框渲染扩写后的要求,没有任何复杂的 UI 组件开销,是名副其实的 MVP 最优解。