feat(asl): Complete Deep Research V2.0 core development

Backend:
- Add SSE streaming client (unifuncsSseClient) replacing async polling
- Add paragraph-based reasoning parser with mergeConsecutiveThinking
- Add requirement expansion service (DeepSeek-V3 PICOS+MeSH)
- Add Word export service with Pandoc, inline hyperlinks, reference link expansion
- Add deep research V2 worker with 2s log flush and Chinese source prompt
- Add 5 curated data sources config (PubMed/ClinicalTrials/Cochrane/CNKI/MedJournals)
- Add 4 API endpoints (generate-requirement/tasks/task-status/export-word)
- Update Prisma schema with 6 new V2.0 fields on AslResearchTask
- Add DB migration for V2.0 fields
- Simplify ASL_DEEP_RESEARCH_EXPANSION prompt (remove strategy section)

Frontend:
- Add waterfall-flow DeepResearchPage (phase 0-4 progressive reveal)
- Add LandingView, SetupPanel, StrategyConfirm, AgentTerminal, ResultsView
- Add react-markdown + remark-gfm for report rendering
- Add custom link component showing visible URLs after references
- Add useDeepResearchTask polling hook
- Add deep research TypeScript types

Tests:
- Add E2E test, smoke test, and Chinese data source test scripts

Docs:
- Update ASL module status (v2.0 - core features complete)
- Update system status (v6.1 - ASL V2.0 milestone)
- Update Unifuncs DeepSearch API guide (v2.0 - SSE mode + Chinese source results)
- Update module auth specification (test script guidelines)
- Update V2.0 development plan

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-23 13:21:52 +08:00
parent b06daecacd
commit 8f06d4f929
39 changed files with 5605 additions and 417 deletions

View File

@@ -1,11 +1,12 @@
# Deep Research V2.0 开发计划
> **文档版本:** v1.0
> **文档版本:** v1.1
> **创建日期:** 2026-02-22
> **维护者:** 开发团队
> **前置文档:** PRD V4.1 / 原型图 V4.2 / 技术设计 V4.1
> **预计工期:** 5 天
> **核心理念:** 单页瀑布流 + 自然语言需求扩写 + 异步执行 + 务实结果展示
> **核心理念:** 单页瀑布流 + 自然语言需求扩写 + 异步执行 + 务实结果展示
> **v1.1 更新:** 融入审查建议Worker 重试、JSON 防崩溃、条件滚动、MeSH 扩展、Prompt 管理、数据源精简)
---
@@ -30,6 +31,9 @@
| 异步下的实时性 | **Worker 5s 轮询 + 前端 3s 轮询** | 用户每 3-5s 看到一条新日志,对分钟级 Agent 任务来说体验自然,比逐字流更适合终端 UI |
| 结果展示复杂度 | **报告 + 表格,不做图表看板** | 研究人员要的是内容本身(综合报告 + 文献清单图表是锦上添花非刚需MVP 不做 |
| Word 导出 | **复用 Pandoc** | Protocol Agent 已验证 Pandoc → Word 方案,零额外依赖 |
| 需求扩写 Prompt | **Prompt 管理服务(运营端可配)** | 使用 `ASL_DEEP_RESEARCH_EXPANSION`,运营管理端可在线调优,代码中写兜底 Fallback |
| 数据源范围 | **精简为 5 个3英文+2中文** | 基于 18 站实测结果精选PubMed 默认勾选ClinicalTrials 标记需英文查询 |
| 状态管理 | **React Query + useState** | 服务端状态用 useQuery 轮询(自带缓存+去重),页面步骤用 useState不引入 Zustand |
---
@@ -135,9 +139,82 @@ npx prisma migrate dev --name add_deep_research_v2_fields
---
## 4. API 契约
## 4. 需求扩写 Prompt 设计
### 4.1 需求扩写(同步)
### 4.1 设计原则
需求扩写 Prompt 通过 **Prompt 管理服务**`ASL_DEEP_RESEARCH_EXPANSION`)进行管理,运营端可在线调优,代码内置 Fallback 兜底。
| 原则 | 说明 |
|------|------|
| PICOS 结构化 | 引导 LLM 按 Population-Intervention-Comparison-Outcome-Study Design 拆解用户模糊需求 |
| MeSH 同义词扩展 | 自动补充专业 MeSH 术语(如 "他汀" → "Statins, Hydroxymethylglutaryl-CoA Reductase Inhibitors" |
| 默认高质量研究设计 | 若用户未指定,默认偏向 RCT、SR/MA、Cohort Study |
| 自然语言对话风格 | 输出"像资深医学信息官写给检索助手的一封邮件",方便 HITL 编辑 |
| 数据源感知 | Prompt 接收用户选择的数据源列表ClinicalTrials.gov 时自动生成英文指令段 |
| 不硬编码约束 | 不强制 Open Access / 不排除非英文文献 — 这些由用户在配置面板自主选择 |
### 4.2 Prompt 模板结构Fallback
```typescript
// backend/src/common/prompt/prompt.fallbacks.ts新增
const ASL_DEEP_RESEARCH_EXPANSION = `
你是一位经验丰富的医学信息官Medical Information Officer
擅长将研究者的模糊想法转化为精准的文献检索需求指令。
## 任务
根据用户输入的粗略研究想法,生成一份结构化的深度文献检索指令书。
## 输出规则
1. **自然语言风格**:像写邮件一样,口语化但专业,方便研究者阅读和编辑
2. **PICOS 拆解**:明确 Population / Intervention / Comparison / Outcome / Study Design
3. **MeSH 扩展**:为关键术语补充 MeSH 同义词(用括号标注英文 MeSH 术语)
4. **研究设计偏好**:若用户未指定,默认优先 RCT、Systematic Review/Meta-Analysis、Cohort Study
5. **数据源适配**:根据用户选择的数据源列表调整语言和策略
- 若包含 ClinicalTrials.gov → 追加一段英文检索指令
- 若包含中文数据源CNKI/中华医学期刊网)→ 保留中文检索关键词
6. **不得自行添加约束**:不要擅自限定"仅开放获取"或"仅英文文献"
## 用户输入
- 研究想法:{{originalQuery}}
- 选择的数据源:{{targetSources}}
- 时间范围:{{yearRange}}
- 目标数量:{{targetCount}}
## 输出格式
请同时输出两部分:
### Part 1: 自然语言检索指令书
(可编辑的完整检索需求描述)
### Part 2: 结构化摘要JSON
\`\`\`json
{
"objective": "...",
"population": "...",
"intervention": "...",
"comparison": "...",
"outcome": "...",
"studyDesign": ["RCT", "Meta-analysis", ...],
"meshTerms": ["term1", "term2", ...],
"condition": "..."
}
\`\`\`
`;
```
### 4.3 Prompt 管理集成
| 层级 | 说明 |
|------|------|
| **运营端** | Prompt 管理界面 → `ASL_DEEP_RESEARCH_EXPANSION` → 可在线编辑、版本管理、A/B 测试 |
| **代码 Fallback** | `prompt.fallbacks.ts` 写入默认模板,数据库无记录时自动使用 |
| **调用方式** | `promptService.getPrompt('ASL_DEEP_RESEARCH_EXPANSION')` → 填充变量 → LLMFactory 调用 |
---
## 5. API 契约
### 5.1 需求扩写(同步)
**POST /api/v1/asl/research/generate-requirement**
@@ -145,7 +222,7 @@ npx prisma migrate dev --name add_deep_research_v2_fields
// 请求
{
originalQuery: string, // "他汀预防心血管疾病要能下载PDF的"
targetSources: string[], // ["pubmed.ncbi.nlm.nih.gov", "bmjopen.bmj.com"]
targetSources: string[], // 从精选数据源列表中选择(见 5.1.1
filters: {
yearRange?: string, // "2010至今" | "过去5年" | "不限"
targetCount?: string, // "~100篇" | "全面检索"
@@ -159,22 +236,40 @@ npx prisma migrate dev --name add_deep_research_v2_fields
data: {
taskId: "uuid", // 已创建DB记录status=draft
generatedRequirement: "请帮我执行一次深度的医学文献检索...", // LLM扩写结果
intentSummary: { // 结构化摘要
intentSummary: { // PICOS + MeSH 结构化摘要
objective: "为Meta分析构建测试语料库",
intervention: "他汀类药物 (Statins)",
condition: "心血管疾病 (CVD)",
literatureStandard: "高质量临床研究PDF全文可下载"
population: "心血管疾病高危患者",
intervention: "他汀类药物 (Statins, HMG-CoA Reductase Inhibitors)",
comparison: "安慰剂/未使用他汀",
outcome: "主要不良心血管事件 (MACE) 发生率",
studyDesign: ["RCT", "Meta-analysis", "Cohort"],
meshTerms: ["Hydroxymethylglutaryl-CoA Reductase Inhibitors", "Cardiovascular Diseases", "Primary Prevention"],
condition: "心血管疾病 (CVD)"
}
}
}
```
**实现要点:**
- 调用 `LLMFactory.getAdapter('deepseek-v3')` 进行需求扩写
- System Prompt 要求 LLM 输出结构化自然语言指令(非布尔检索式)
- 同时创建 DB 记录status = `draft`
#### 5.1.1 精选数据源配置
### 4.2 启动执行(进入异步队列)
基于 Unifuncs API 18 站实测结果精选的 5 个数据源:
| 类别 | 数据源 | domain_scope 值 | 默认 | 备注 |
|------|--------|-----------------|------|------|
| 🌍 英文 | **PubMed** | `https://pubmed.ncbi.nlm.nih.gov/` | ✅ 默认勾选 | 一级可用,核心数据源 |
| 🌍 英文 | **ClinicalTrials.gov** | `https://clinicaltrials.gov/` | ☐ 可选 | ⚠️ 前端提示"需英文查询" |
| 🌍 英文 | **Cochrane Library** | `https://www.cochranelibrary.com/` | ☐ 可选 | 一级可用,系统综述金标准 |
| 🇨🇳 中文 | **中国知网 CNKI** | `https://www.cnki.net/` | ☐ 可选 | 二级可达,中文文献 |
| 🇨🇳 中文 | **中华医学期刊网** | `https://medjournals.cn/` | ☐ 可选 | 二级可达,中文文献 |
**实现要点:**
- 调用 `promptService.getPrompt('ASL_DEEP_RESEARCH_EXPANSION')` 获取 Prompt无数据库记录时走 Fallback
- 填充变量 `{{originalQuery}}``{{targetSources}}``{{yearRange}}``{{targetCount}}`
- 调用 `LLMFactory.getAdapter('deepseek-v3')` 执行扩写
- 解析 LLM 输出Part 1 → `generatedRequirement`Part 2 JSON → `intentSummary`
- 创建 DB 记录status = `draft`
### 5.2 启动执行(进入异步队列)
**PUT /api/v1/asl/research/tasks/:id/execute**
@@ -193,7 +288,7 @@ npx prisma migrate dev --name add_deep_research_v2_fields
- `jobQueue.push('asl_deep_research_v2', { taskId })` 推入 pg-boss
- status 更新为 `pending`
### 4.3 任务状态与日志轮询
### 5.3 任务状态与日志轮询
**GET /api/v1/asl/research/tasks/:id**
@@ -221,7 +316,7 @@ npx prisma migrate dev --name add_deep_research_v2_fields
}
```
### 4.4 Word 导出
### 5.4 Word 导出
**GET /api/v1/asl/research/tasks/:id/export-word**
@@ -230,7 +325,7 @@ npx prisma migrate dev --name add_deep_research_v2_fields
- 调用 Pandoc 转 Word
- 返回 `.docx` 文件流
### 4.5 路由汇总
### 5.5 路由汇总
| 方法 | 路径 | 说明 | 新增/改造 |
|------|------|------|----------|
@@ -243,9 +338,9 @@ npx prisma migrate dev --name add_deep_research_v2_fields
---
## 5. 后台 Worker 逻辑
## 6. 后台 Worker 逻辑
### 5.1 核心流程(伪代码)
### 6.1 核心流程(伪代码)
```typescript
// backend/src/modules/asl/workers/deepResearchV2Worker.ts
@@ -278,14 +373,33 @@ export async function processDeepResearchV2(job: Job) {
data: { externalTaskId: unifuncsTaskId, status: 'running' }
});
// 2. 轮询 Unifuncs 直到完成
// 2. 轮询 Unifuncs 直到完成(含指数退避重试)
let previousReasoning = '';
const MAX_POLLS = 180; // 最多 15 分钟180 × 5s
const MAX_POLLS = 180; // 最多 15 分钟180 × 5s
let consecutiveErrors = 0; // 连续错误计数
const MAX_CONSECUTIVE_ERRORS = 5;
for (let i = 0; i < MAX_POLLS; i++) {
await sleep(5000);
const queryRes = await unifuncsClient.queryTask(unifuncsTaskId);
const data = queryRes.data;
let data: any;
try {
const queryRes = await unifuncsClient.queryTask(unifuncsTaskId);
data = queryRes.data;
consecutiveErrors = 0; // 成功后重置
} catch (err) {
consecutiveErrors++;
logger.warn(`Unifuncs query_task 第 ${consecutiveErrors} 次失败`, { taskId, error: err.message });
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
throw new Error(`Unifuncs 连续 ${MAX_CONSECUTIVE_ERRORS} 次查询失败: ${err.message}`);
}
// 指数退避2s → 4s → 8s → 16s → 32s
const backoffMs = Math.min(2000 * Math.pow(2, consecutiveErrors - 1), 32000);
await sleep(backoffMs);
continue;
}
// 解析增量日志
const currentReasoning = data.result?.reasoning_content || '';
@@ -306,7 +420,7 @@ export async function processDeepResearchV2(job: Job) {
const content = data.result?.content || '';
const report = extractSection(content, 'REPORT_SECTION');
const jsonList = extractSection(content, 'JSON_LIST_SECTION');
const parsedList = safeParseJsonList(jsonList);
const parsedList = safeParseJsonList(jsonList); // 防崩溃 JSON 解析,见 6.4
await prisma.aslResearchTask.update({
where: { id: taskId },
@@ -336,7 +450,15 @@ export async function processDeepResearchV2(job: Job) {
}
```
### 5.2 日志解析逻辑
**v1.1 新增 — 轮询韧性设计:**
| 机制 | 策略 | 说明 |
|------|------|------|
| 瞬态失败重试 | 指数退避 2s → 4s → 8s → 16s → 32s | 网络抖动、Unifuncs 临时不可用时自动恢复 |
| 连续失败阈值 | `MAX_CONSECUTIVE_ERRORS = 5` | 连续 5 次查询全失败才标记任务 failed |
| 成功后重置 | `consecutiveErrors = 0` | 中间穿插成功不累计 |
### 6.2 日志解析逻辑
```typescript
function parseReasoningToLogs(increment: string): LogEntry[] {
@@ -360,7 +482,7 @@ function parseReasoningToLogs(increment: string): LogEntry[] {
}
```
### 5.3 output_prompt 设计
### 6.3 output_prompt 设计
```typescript
function buildOutputPrompt(): string {
@@ -381,11 +503,55 @@ function buildOutputPrompt(): string {
}
```
### 6.4 JSON 解析防崩溃v1.1 新增)
LLM 输出的 JSON 常携带 ` ```json ` 代码围栏或尾部逗号,直接 `JSON.parse` 会崩溃。
```typescript
function safeParseJsonList(raw: string | null): any[] | null {
if (!raw) return null;
// Step 1: 去除 Markdown 代码围栏
let cleaned = raw.replace(/```json\s*/gi, '').replace(/```\s*/g, '');
// Step 2: 去除尾部逗号(数组/对象末尾的 ,] 或 ,}
cleaned = cleaned.replace(/,\s*([}\]])/g, '$1');
// Step 3: 尝试解析
try {
const parsed = JSON.parse(cleaned);
return Array.isArray(parsed) ? parsed : [parsed];
} catch (e) {
logger.warn('JSON 解析失败,尝试正则提取', { error: e.message });
// Step 4: 降级 — 尝试逐行提取 JSON 对象
const objects: any[] = [];
const regex = /\{[^{}]*\}/g;
let match;
while ((match = regex.exec(cleaned)) !== null) {
try {
objects.push(JSON.parse(match[0]));
} catch { /* 跳过无法解析的单条 */ }
}
return objects.length > 0 ? objects : null;
}
}
```
**防崩溃策略总结:**
| 层级 | 处理 | 覆盖场景 |
|------|------|---------|
| L1 | 去除 ` ```json ` 围栏 | LLM 习惯性包裹代码块 |
| L2 | 去除尾部逗号 | `[{...}, {...},]``[{...}, {...}]` |
| L3 | 标准 JSON.parse | 正常路径 |
| L4 | 正则逐条提取 | JSON 结构被破坏但单条仍有效 |
| L5 | 返回 null | 彻底无法解析,前端降级展示报告 |
---
## 6. 前端组件设计
## 7. 前端组件设计
### 6.1 页面结构
### 7.1 页面结构
```
frontend-v2/src/modules/asl/pages/
@@ -400,10 +566,18 @@ frontend-v2/src/modules/asl/components/
│ └── ResultsView.tsx # Step 4: 结果展示(报告+表格)
```
### 6.2 状态管理
### 7.2 状态管理
**设计决策:** React Query + useState不引入 Zustandv1.1 确认)
| 状态类型 | 工具 | 理由 |
|---------|------|------|
| 服务端数据(任务状态、日志、结果) | `@tanstack/react-query` | 自带缓存、去重、条件轮询refetchInterval完美匹配轮询场景 |
| 页面步骤流转 | `useState` | 仅 5 个步骤的 FSM组件树内部流转无需全局状态 |
| 组件间共享(如 taskId | `props drilling` / `React.memo` | 组件层级仅 2-3 层prop 传递足够,不需要 Context/Zustand |
```typescript
// 页面级状态useState 即可,无需 Zustand
// 页面级状态useState 即可,不引入 Zustand
interface DeepResearchState {
currentStep: 'landing' | 'setup' | 'strategy' | 'terminal' | 'results';
taskId: string | null;
@@ -412,29 +586,55 @@ interface DeepResearchState {
intentSummary: IntentSummary | null;
isGenerating: boolean; // 需求扩写中
}
// PICOS + MeSH 结构化摘要v1.1 新增)
interface IntentSummary {
objective: string;
population: string;
intervention: string;
comparison: string;
outcome: string;
studyDesign: string[];
meshTerms: string[];
condition: string;
}
```
### 6.3 各组件核心逻辑
### 7.3 各组件核心逻辑
**LandingViewLanding 大搜索框)**
- 居中大输入框 + "开始研究"按钮 + 推荐预置词
- 点击后携带输入值,平滑过渡到 SetupPanel
- 参考原型图 V4.2 的 `#landing-view` 部分
**SetupPanelStep 1: 配置)**
**SetupPanelStep 1: 配置)** *(v1.1 数据源更新)*
- 继承 Landing 输入值到 textarea
- 数据源 CheckboxPubMed/PMC, BMJ Open, Cochrane
- 高级过滤年份下拉、目标数量、OA 强制)
- 数据源 Checkbox基于实测精选 5 个,见 5.1.1
- 🌍 **英文数据源**
- [x] PubMed默认勾选不可取消
- [ ] ClinicalTrials.gov — ⚠️ 旁标橙色提示:"该站点需要英文查询,系统将自动为此数据源生成英文检索指令"
- [ ] Cochrane Library
- 🇨🇳 **中文数据源**
- [ ] 中国知网 CNKI
- [ ] 中华医学期刊网
- 高级过滤年份下拉、目标数量、OA 偏好 — 注意是偏好非强制)
- 点击"解析并生成检索需求书" → POST /generate-requirement
- Loading 后平滑展开 Step 2
**StrategyConfirmStep 2: HITL 确认)**
- 左侧 1/3AI 意图提炼卡片(只读,来自 `intentSummary`
- 右侧 2/3可编辑 textarea内容为 `generatedRequirement`
- 提示文案:"您可以像写邮件一样在这里补充任何大白话要求"
**StrategyConfirmStep 2: HITL 确认)** *(v1.1 PICOS + MeSH)*
- 左侧 1/3AI 意图提炼卡片(只读,PICOS 结构化展示
- 🎯 研究目标:`objective`
- 👥 研究人群:`population`
- 💊 干预措施:`intervention`(含 MeSH 英文术语)
- ⚖️ 对照组:`comparison`
- 📊 结局指标:`outcome`
- 📋 研究设计:`studyDesign` Tag 列表
- 🏷️ MeSH 术语:`meshTerms` 小标签展示
- 右侧 2/3可编辑 textarea内容为 `generatedRequirement`,自然语言对话风格)
- 提示文案:"这是 AI 以医学信息官的视角为您扩写的检索需求,您可以像写邮件一样直接编辑修改"
- 点击"确认需求,启动 Deep Research" → PUT /execute
**AgentTerminalStep 3: 暗黑终端)**
**AgentTerminalStep 3: 暗黑终端)** *(v1.1 条件滚动)*
- 暗色背景bg-slate-900固定高度 550px内部滚动
- 顶部状态栏:红/黄/绿圆点 + "Running" 脉冲指示灯
- 日志渲染:
@@ -443,7 +643,10 @@ interface DeepResearchState {
- `done` → 绿色 + ✅ 图标
- `summary` → 黄色 + 📋 图标
- 轮询逻辑:`useQuery` + refetchInterval: 3000running 时启用)
- 新日志出现时 auto-scroll 到底部
- **条件自动滚动**v1.1 新增):
- 用户**未手动上滚**时 → 新日志自动滚到底部
- 用户**已手动上滚**查看历史 → 停止自动滚动,避免打断阅读
- 实现:`onScroll` 检测 `scrollTop + clientHeight < scrollHeight - threshold``userScrolled` flag
- 完成后状态灯变灰 "Finished",终端可折叠
**ResultsViewStep 4: 结果)**
@@ -454,8 +657,9 @@ interface DeepResearchState {
- 列:标题(可点击跳转 PubMed、期刊、年份、类型 Tag、PDF 状态
- 支持简单搜索过滤
- 分页(前端分页即可,数据量 ~100 条)
- **降级展示**:若 `resultList` 为 nullJSON 解析失败),隐藏表格,仅展示综合报告
### 6.4 轮询 Hook
### 7.4 轮询 Hook
```typescript
// hooks/useDeepResearchTask.ts
@@ -474,94 +678,105 @@ function useDeepResearchTask(taskId: string | null) {
---
## 7. 复用清单(不重复造轮子)
## 8. 复用清单(不重复造轮子)
| 能力 | 来源 | 用法 |
|------|------|------|
| LLM 调用 | `common/llm/LLMFactory` | DeepSeek-V3 需求扩写 |
| **Prompt 管理** | `common/prompt/promptService` | `ASL_DEEP_RESEARCH_EXPANSION` Prompt 获取(运营端可配 + 代码 Fallback |
| pg-boss 队列 | `common/jobs/jobQueue` | Worker 注册与任务推送 |
| 日志服务 | `common/logging/logger` | 全程结构化日志 |
| 认证中间件 | `common/auth/authenticate` | 所有 API 路由 |
| Prisma 全局实例 | `config/database` | 数据库操作 |
| Word 导出 | PandocPython 微服务) | 复用 Protocol Agent 验证的方案 |
| DeepResearch 引擎 | `common/deepresearch/` | Unifuncs API 封装create_task / query_task |
| 前端 API Client | `common/api/axios` | 带认证的请求 |
| 前端布局 | `ASLLayout.tsx` | 左侧导航 |
---
## 8. 分阶段开发计划
## 9. 分阶段开发计划
### Phase 1: 数据库 + 需求扩写Day 1
### Phase 1: 数据库 + Prompt 管理 + 需求扩写Day 1
**目标:** 用户输入粗略想法 → AI 扩写为结构化指令书 → 用户可编辑修改
**目标:** 用户输入粗略想法 → AI 按 PICOS 框架扩写为结构化指令书 → 用户可编辑修改
| 任务 | 文件 | 说明 |
|------|------|------|
| Schema 迁移 | `prisma/schema.prisma` | 新增 6 个字段,`prisma migrate dev` |
| 需求扩写 Prompt | `services/requirementExpansionService.ts` | 新建服务,调用 DeepSeek-V3 扩写 |
| **Prompt Fallback** | `common/prompt/prompt.fallbacks.ts` | 新增 `ASL_DEEP_RESEARCH_EXPANSION` 兜底模板PICOS + MeSH |
| 需求扩写服务 | `services/requirementExpansionService.ts` | 新建,调用 promptService → LLMFactory → 解析 Part 1/Part 2 输出 |
| 扩写 API | `controllers/researchController.ts` | 新增 `POST /generate-requirement` |
| 启动 API | `controllers/researchController.ts` | 新增 `PUT /tasks/:id/execute` |
| 状态 API 改造 | `controllers/researchController.ts` | 改造 `GET /tasks/:id`,返回新字段 |
| 状态 API 改造 | `controllers/researchController.ts` | 改造 `GET /tasks/:id`,返回新字段(含 PICOS intentSummary |
| **数据源配置** | `config/dataSources.ts` | 新建,定义 5 个精选数据源常量domain、label、默认状态、备注 |
| 路由注册 | `routes/index.ts` | 注册新端点 |
**验收标准:**
- [ ] `POST /generate-requirement` 返回扩写后的指令书
- [ ] `POST /generate-requirement` 返回 PICOS 结构化摘要 + 自然语言指令书
- [ ] Prompt 从数据库加载,无记录时自动走 Fallback
- [ ] `PUT /tasks/:id/execute` 成功推入 pg-boss 队列
- [ ] `GET /tasks/:id` 返回含新字段的完整数据
- [ ] 选择 ClinicalTrials.gov 时,扩写结果包含英文检索指令段
### Phase 2: Worker 改造 — Unifuncs 异步模式Day 2
**目标:** Worker 使用 create_task + query_task 轮询,增量日志写入 DB
**目标:** Worker 使用 create_task + query_task 轮询(含指数退避),增量日志写入 DB
| 任务 | 文件 | 说明 |
|------|------|------|
| Unifuncs 异步客户端 | `services/unifuncsAsyncClient.ts` | 新建,封装 create_task / query_task |
| V2 Worker | `workers/deepResearchV2Worker.ts` | 新建,轮询 + 日志解析 + 结果切割 |
| V2 Worker | `workers/deepResearchV2Worker.ts` | 新建,轮询 + 指数退避重试 + 日志解析 + 结果切割 |
| 日志解析器 | `utils/reasoningParser.ts` | 新建reasoning_content → 结构化日志 |
| 结果解析器 | `utils/resultParser.ts` | 新建XML 标签切割报告与 JSON 列表 |
| **结果解析器** | `utils/resultParser.ts` | 新建XML 标签切割 + `safeParseJsonList` 防崩溃解析 |
| Worker 注册 | `workers/researchWorker.ts` | 注册新 Worker `asl_deep_research_v2` |
**验收标准:**
- [ ] Worker 成功调用 unifuncs create_task
- [ ] 轮询期间 execution_logs 持续增量更新
- [ ] 完成后 synthesis_report 和 result_list 正确入库
- [ ] **韧性测试**:模拟单次 query_task 失败 → 指数退避后自动恢复
- [ ] **JSON 防崩溃**LLM 输出带 ` ```json ` 围栏 → safeParseJsonList 正确解析
- [ ] 超时保护15 分钟)和错误处理正常
### Phase 3: 前端 — Landing + 配置 + HITL 确认Day 3
**目标:** 完成 Step 1-2 的前端交互,瀑布流渐进展开
**目标:** 完成 Step 1-2 的前端交互,瀑布流渐进展开PICOS 结构化展示
| 任务 | 文件 | 说明 |
|------|------|------|
| 主页面骨架 | `pages/DeepResearchPage.tsx` | 新建,管理瀑布流状态 |
| 主页面骨架 | `pages/DeepResearchPage.tsx` | 新建,useState 管理瀑布流步骤 |
| Landing 组件 | `components/deep-research/LandingView.tsx` | 大搜索框 + 推荐预置词 |
| 配置面板 | `components/deep-research/SetupPanel.tsx` | 数据源 + 高级过滤 + 生成按钮 |
| HITL 确认 | `components/deep-research/StrategyConfirm.tsx` | 左右分栏 + 可编辑 textarea |
| **配置面板** | `components/deep-research/SetupPanel.tsx` | **精选 5 数据源 Checkbox**(含 ClinicalTrials.gov 英文提示)+ 高级过滤 |
| **HITL 确认** | `components/deep-research/StrategyConfirm.tsx` | 左**PICOS + MeSH 卡片** + 右侧可编辑 textarea |
| API 函数 | `api/index.ts` | 新增 generateRequirement / executeTask |
| 路由注册 | `pages/index.tsx` | 新增 V2 路由 |
**验收标准:**
- [ ] Landing 输入 → Step 1 配置面板流畅过渡
- [ ] 数据源显示 5 个选项PubMed 默认勾选ClinicalTrials.gov 标注英文提示
- [ ] 点击"生成需求书" → Loading → Step 2 展开
- [ ] Step 2 左侧摘要卡片正确展示,右侧 textarea 可编辑
- [ ] Step 2 左侧 PICOS 结构化摘要 + MeSH 术语标签正确展示
- [ ] Step 2 右侧 textarea 显示自然语言对话风格的检索指令书,可编辑
- [ ] 点击"启动 Deep Research" → 进入 Step 3
### Phase 4: 前端 — 终端 + 结果展示Day 4
**目标:** 完成 Step 3-4终端实时日志 + 结果报告/表格
**目标:** 完成 Step 3-4终端实时日志(条件滚动)+ 结果报告/表格(含降级)
| 任务 | 文件 | 说明 |
|------|------|------|
| 暗黑终端 | `components/deep-research/AgentTerminal.tsx` | 日志渲染 + auto-scroll + 状态灯 |
| 结果视图 | `components/deep-research/ResultsView.tsx` | 横幅 + 报告 + 文献表格 |
| 轮询 Hook | `hooks/useDeepResearchTask.ts` | 3s 轮询running 时启用 |
| **暗黑终端** | `components/deep-research/AgentTerminal.tsx` | 日志渲染 + **条件 auto-scroll** + 状态灯 |
| 结果视图 | `components/deep-research/ResultsView.tsx` | 横幅 + 报告 + 文献表格 + **降级展示** |
| 轮询 Hook | `hooks/useDeepResearchTask.ts` | React Query 3s 轮询running 时启用 |
| 终端样式 | CSS / Tailwind | 暗色主题 + 日志类型着色 |
**验收标准:**
- [ ] 终端日志按类型着色,新日志 auto-scroll
- [ ] 终端日志按类型着色,未手动滚动时 auto-scroll,手动上滚时暂停
- [ ] 完成后终端折叠,结果区展开
- [ ] 综合报告 Markdown 渲染正确
- [ ] 文献清单表格展示(标题可点击跳转 PubMed
- [ ] **降级验证**resultList 为 null 时,隐藏表格仅展示报告
- [ ] 全流程端到端联调通过
### Phase 5: Word 导出 + 收尾Day 5
@@ -585,7 +800,7 @@ function useDeepResearchTask(taskId: string | null) {
---
## 9. 验收标准总览
## 10. 验收标准总览
### 功能验收
@@ -608,17 +823,36 @@ function useDeepResearchTask(taskId: string | null) {
---
## 10. 风险与应对
## 11. 风险与应对
| 风险 | 概率 | 影响 | 应对措施 |
|------|------|------|---------|
| Unifuncs 异步模式下 reasoning_content 不增量更新 | 低 | 终端日志为空 | 降级方案:只显示 progress.message |
| output_prompt XML 标签分割不可靠 | 中 | 报告和列表无法分离 | 降级方案:整体作为报告展示,文献从 PubMed 链接提取 |
| **LLM 输出 JSON 格式不规范** | 中 | 文献列表解析失败 | `safeParseJsonList` 四层防崩溃(围栏清理 → 尾逗号 → 标准解析 → 正则逐条),见 6.4 |
| **Unifuncs query_task 瞬态失败** | 中 | 轮询中断 | 指数退避重试2s→32s连续 5 次失败才标记 failed见 6.1 |
| Unifuncs 长任务超时 | 低 | 任务失败 | MAX_POLLS=18015分钟超时标记 failed用户可重试 |
| **ClinicalTrials.gov 中文查询失败** | 高 | 临床试验检索无结果 | Prompt 自动为该数据源生成英文检索指令段,前端标注提示 |
| Pandoc Word 导出在 SAE 不可用 | 低 | 导出失败 | 降级方案:导出为 Markdown 文件 |
| **Prompt 管理服务不可用** | 低 | 需求扩写失败 | 代码内置 Fallback 模板,数据库无记录时自动使用 |
---
## 附录v1.1 更新变更记录
| 变更项 | 章节 | 说明 |
|--------|------|------|
| Prompt 管理集成 | §4新增 | 需求扩写 Prompt 通过 Prompt 管理服务配置,含 PICOS + MeSH 扩展 |
| 精选数据源 | §5.1.1(新增) | 基于 18 站实测精选 5 个数据源3英文+2中文 |
| 指数退避重试 | §6.1(更新) | Worker 轮询增加瞬态失败指数退避2s→32s |
| JSON 防崩溃 | §6.4(新增) | safeParseJsonList 四层解析策略 |
| PICOS 摘要 | §7.2, §7.3(更新) | IntentSummary 扩展为 PICOS + MeSH 结构 |
| 条件自动滚动 | §7.3(更新) | AgentTerminal 手动上滚时暂停 auto-scroll |
| 状态管理确认 | §1.2, §7.2(更新) | 确认 React Query + useState不引入 Zustand |
| 降级展示 | §7.3(更新) | ResultsView 在 resultList=null 时仅展示报告 |
---
**文档维护者:** 开发团队
**最后更新:** 2026-02-22
**文档状态:**方案确认,待开发启动
**文档状态:**v1.1 方案确认(含审查建议),待开发启动