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:
@@ -1,12 +1,13 @@
|
||||
# AI智能文献模块 - 当前状态与开发指南
|
||||
|
||||
> **文档版本:** v1.6
|
||||
> **文档版本:** v2.0
|
||||
> **创建日期:** 2025-11-21
|
||||
> **维护者:** AI智能文献开发团队
|
||||
> **最后更新:** 2026-02-22 🆕 **Deep Research V2.0 开发计划确认 + Unifuncs API 网站覆盖测试完成**
|
||||
> **最后更新:** 2026-02-23 🆕 **Deep Research V2.0 核心功能开发完成!SSE 实时流 + 瀑布流 UI + 中文数据源 + Word 导出**
|
||||
> **重大进展:**
|
||||
> - 🆕 2026-02-22:V2.0 开发计划确认!四步瀑布流 + 异步模式 + HITL 需求确认 + 务实结果展示
|
||||
> - 🆕 2026-02-22:Unifuncs API 网站覆盖测试完成!18 站点实测,9 个一级可用,ClinicalTrials.gov 专项验证通过
|
||||
> - 🆕 2026-02-23:V2.0 核心功能完成!SSE 流式架构 + 段落化思考日志 + 引用链接可见化
|
||||
> - 🆕 2026-02-22:V2.0 前后端联调完成!瀑布流 UI + Markdown 渲染 + Word 导出 + 中文数据源测试
|
||||
> - 🆕 2026-02-22:V2.0 开发计划确认 + Unifuncs API 网站覆盖测试完成
|
||||
> - 2026-01-18:智能文献检索(DeepSearch)MVP完成 - unifuncs API 集成
|
||||
> **文档目的:** 反映模块真实状态,帮助新开发人员快速上手
|
||||
|
||||
@@ -30,46 +31,96 @@
|
||||
AI智能文献模块是一个基于大语言模型(LLM)的文献筛选系统,用于帮助研究人员根据PICOS标准自动筛选文献。
|
||||
|
||||
### 当前状态
|
||||
- **开发阶段**:🚧 V1.x 完成,V2.0 Deep Research 开发计划已确认,即将启动
|
||||
- **开发阶段**:🎉 V2.0 Deep Research 核心功能开发完成
|
||||
- **已完成功能**:
|
||||
- ✅ 标题摘要初筛(Title & Abstract Screening)- 完整流程
|
||||
- ✅ 全文复筛后端(Day 2-5)- LLM服务 + API + Excel导出
|
||||
- ✅ **智能文献检索(DeepSearch)V1.x MVP** - unifuncs API 集成,SSE 实时流式
|
||||
- ✅ **智能文献检索(DeepSearch)V1.x MVP** - unifuncs API 集成
|
||||
- ✅ **Unifuncs API 网站覆盖测试** - 18 站点实测,9 个一级可用
|
||||
- ✅ **Deep Research V2.0 开发计划** - 完整技术方案、API 契约、5 天分阶段计划
|
||||
- **V2.0 开发中**:
|
||||
- 🚧 Deep Research V2.0 — 四步瀑布流(Landing→配置→HITL→终端→结果)
|
||||
- 🚧 异步模式改造(SSE → Unifuncs create_task/query_task)
|
||||
- 🚧 需求扩写 + HITL 确认 + Agent 终端 + 简洁结果展示 + Word 导出
|
||||
- **模型支持**:DeepSeek-V3(需求扩写) + unifuncs s2(深度搜索) + Qwen-Max(筛选)
|
||||
- ✅ **🎉 Deep Research V2.0 核心功能** — SSE 流式架构 + 瀑布流 UI + HITL + Word 导出
|
||||
- **V2.0 已完成**:
|
||||
- ✅ **SSE 流式架构**:从 create_task/query_task 轮询改为 OpenAI Compatible SSE 流,实时推送 AI 思考过程
|
||||
- ✅ **LLM 需求扩写**:DeepSeek-V3 将粗略输入扩写为结构化检索指令书(PICOS + MeSH)
|
||||
- ✅ **HITL 策略确认**:用户可编辑、保存、确认 AI 生成的检索指令
|
||||
- ✅ **瀑布流 UI**:Landing → 配置 → HITL → Agent 终端 → 结果,已完成步骤折叠为摘要卡片
|
||||
- ✅ **段落化思考日志**:reasoning_content 按段落聚合,连续思考合并为一段(非逐行碎片)
|
||||
- ✅ **Markdown 渲染**:react-markdown + remark-gfm 正确渲染报告中的标题、链接、列表
|
||||
- ✅ **引用链接可见化**:报告中 `[6]` 引用后显示完整 URL,方便复制分享
|
||||
- ✅ **Word 导出**:Pandoc 微服务,文献标题内嵌超链接,引用展开为可见 URL
|
||||
- ✅ **中文数据源支持**:CNKI/中华医学期刊网动态 prompt 增强 + 专项测试验证
|
||||
- ✅ **5 个精选数据源**:PubMed(默认)、ClinicalTrials.gov、Cochrane Library、CNKI、中华医学期刊网
|
||||
- **模型支持**:DeepSeek-V3(需求扩写) + unifuncs s2(深度搜索,SSE 流式) + Qwen-Max(筛选)
|
||||
- **部署状态**:✅ 本地开发环境运行正常
|
||||
|
||||
### 🆕 Deep Research V2.0(2026-02-22 开发计划确认)
|
||||
### 🎉 Deep Research V2.0(2026-02-23 核心功能完成)
|
||||
|
||||
**V2.0 核心升级:**
|
||||
- 四步瀑布流:Landing → 配置 → HITL 策略确认 → Agent 终端 → 结果展示
|
||||
- LLM 需求扩写(DeepSeek-V3):粗略输入 → 结构化自然语言检索指令书
|
||||
- Human-in-the-Loop:用户可编辑修改 AI 生成的检索需求
|
||||
- **异步模式**:SSE → Unifuncs create_task/query_task + pg-boss 队列(离开页面不中断)
|
||||
- Agent 终端:暗色主题 + 分类结构化日志(每 3-5s 弹出一条)
|
||||
- 简洁结果展示:AI 综合报告(Markdown)+ 文献清单表格 + Word 导出
|
||||
- **多站点搜索**:9 个一级可用站点,用户可在前端选择数据源
|
||||
**V2.0 架构与功能:**
|
||||
|
||||
**V2.0 确认可用数据源(2026-02-22 实测):**
|
||||
| 层级 | 组件 | 说明 | 状态 |
|
||||
|------|------|------|------|
|
||||
| 前端 | 瀑布流页面 | Landing→配置→HITL→终端→结果,折叠式摘要 | ✅ |
|
||||
| 前端 | react-markdown | 综合报告 Markdown 正确渲染 + 引用链接可见化 | ✅ |
|
||||
| 后端 | SSE 流式客户端 | OpenAI Compatible SSE,实时推送 reasoning_content | ✅ |
|
||||
| 后端 | 段落化解析器 | 连续思考行合并为段落,搜索/阅读/分析独立成条 | ✅ |
|
||||
| 后端 | 需求扩写服务 | DeepSeek-V3 PICOS+MeSH 结构化扩写 | ✅ |
|
||||
| 后端 | Word 导出 | Pandoc 微服务,内嵌超链接 + 引用展开 | ✅ |
|
||||
| 后端 | pg-boss 队列 | 异步任务,离开页面不中断 | ✅ |
|
||||
| 数据库 | 6 个新字段 | targetSources/confirmedRequirement/aiIntentSummary/executionLogs/synthesisReport/resultList | ✅ |
|
||||
|
||||
| 站点 | 站内链接数 | 说明 |
|
||||
|------|-----------|------|
|
||||
| PubMed | 28 | 核心数据源,效果最佳 |
|
||||
| ClinicalTrials.gov | 38 | 必须英文查询,max_depth≥10 |
|
||||
| NCBI/PMC | 18 | 含 PMC 全文链接 |
|
||||
| Google Scholar | 10 | 跨库聚合 |
|
||||
| CBM/SinoMed | 9 | 中文生物医学 |
|
||||
| CNKI | 7 | 中文核心期刊 |
|
||||
| GeenMedical | 5 | 医学搜索引擎 |
|
||||
| Cochrane Library | 4 | 系统综述金标准 |
|
||||
| 维普 | 1 | 中文库 |
|
||||
**V2.0 核心技术决策:**
|
||||
1. **SSE 流式替代轮询**:从 create_task/query_task 轮询改为 OpenAI Compatible SSE 流。解决了"等很久才一股脑显示思考过程"的体验问题,reasoning_content 实时推送、每 2 秒刷写 DB。
|
||||
2. **段落化思考日志**:reasoning_content 按 `\n\n` 段落拆分,同段落内连续思考行用空格合并为一条,搜索/阅读/分析动作独立。Worker 每 200+ 字符批量解析,写入前再合并连续同类条目。
|
||||
3. **引用链接可见化**:Web 端通过 react-markdown 自定义 `<a>` 组件在链接后追加灰色 URL;Word 端通过 `expandReferenceLinks()` 将 `[[N]](url)` 展开为 `[N](url) (url)`。
|
||||
4. **中文数据源策略**:纯中文源(CNKI/中华医学期刊网)单独搜索效果好;混合中英文源时 PubMed 主导,建议分批搜索。
|
||||
|
||||
**开发计划**:5 天分阶段交付,详见 `04-开发计划/07-Deep Research V2.0 开发计划.md`
|
||||
**V2.0 选定数据源(5 个精选):**
|
||||
|
||||
| 数据源 | 类型 | 默认选中 | 说明 |
|
||||
|--------|------|----------|------|
|
||||
| PubMed | 英文 | ✅ | 核心数据源,效果最佳 |
|
||||
| ClinicalTrials.gov | 英文 | - | 临床试验注册库 |
|
||||
| Cochrane Library | 英文 | - | 系统综述金标准 |
|
||||
| 中国知网 CNKI | 中文 | - | 中文核心期刊 |
|
||||
| 中华医学期刊网 | 中文 | - | 中华医学会官方期刊 |
|
||||
|
||||
**V2.0 API 端点:**
|
||||
```http
|
||||
POST /api/v1/asl/deep-research/generate-requirement # LLM 需求扩写
|
||||
POST /api/v1/asl/deep-research/tasks # 创建任务(pg-boss)
|
||||
GET /api/v1/asl/deep-research/tasks/:taskId # 查询任务状态+日志+结果
|
||||
GET /api/v1/asl/deep-research/tasks/:taskId/export-word # Word 导出
|
||||
```
|
||||
|
||||
**V2.0 关键文件:**
|
||||
```
|
||||
backend/src/modules/asl/
|
||||
├── services/unifuncsSseClient.ts # SSE 流式客户端(AsyncGenerator)
|
||||
├── services/requirementExpansionService.ts # LLM 需求扩写
|
||||
├── services/wordExportService.ts # Word 导出(Pandoc + 引用展开)
|
||||
├── workers/deepResearchV2Worker.ts # SSE Worker(段落解析 + 2s 刷写)
|
||||
├── utils/reasoningParser.ts # 段落化解析器 + mergeConsecutiveThinking
|
||||
├── utils/resultParser.ts # content 解析(报告 + JSON 文献列表)
|
||||
├── controllers/deepResearchController.ts # 4 个 API 端点
|
||||
├── config/dataSources.ts # 5 个精选数据源配置
|
||||
└── __tests__/
|
||||
├── deep-research-v2-e2e.ts # 端到端测试
|
||||
├── deep-research-v2-smoke.ts # 冒烟测试
|
||||
└── deep-research-chinese-sources.ts # 中文数据源专项测试
|
||||
|
||||
frontend-v2/src/modules/asl/
|
||||
├── pages/DeepResearchPage.tsx # 瀑布流主页面(phase 0-4)
|
||||
├── components/deep-research/
|
||||
│ ├── LandingView.tsx # 搜索入口
|
||||
│ ├── SetupPanel.tsx # 数据源+过滤器配置(可折叠)
|
||||
│ ├── StrategyConfirm.tsx # HITL 策略确认(可折叠)
|
||||
│ ├── AgentTerminal.tsx # 暗色终端日志展示
|
||||
│ └── ResultsView.tsx # Markdown 报告 + 文献表格 + Word 导出
|
||||
├── hooks/useDeepResearchTask.ts # React Query 轮询 Hook
|
||||
├── types/deepResearch.ts # TypeScript 类型
|
||||
└── api/index.ts # 4 个 V2.0 API 函数
|
||||
```
|
||||
|
||||
**开发计划**:详见 `04-开发计划/07-Deep Research V2.0 开发计划.md`
|
||||
|
||||
**通用能力指南**:`docs/02-通用能力层/04-DeepResearch引擎/01-Unifuncs DeepSearch API 使用指南.md`
|
||||
|
||||
@@ -1319,28 +1370,28 @@ Drawer打开: <50ms
|
||||
|
||||
## 🎯 下一步开发计划
|
||||
|
||||
### 当前Sprint(全文复筛MVP)
|
||||
1. 🚧 **全文复筛 Day 4**:批处理任务服务(进行中)
|
||||
2. ⏳ **全文复筛 Day 5**:前端UI开发(待开始)
|
||||
3. ⏳ **全文复筛 Day 6**:API集成与联调(待开始)
|
||||
### 当前(Deep Research V2.0 优化)
|
||||
1. ⏳ **端到端回归测试**:完整流程测试(创建→扩写→确认→执行→结果→导出)
|
||||
2. ⏳ **用户体验打磨**:加载动画、错误提示、边界情况处理
|
||||
3. ⏳ **中文检索优化**:中英文混合检索策略调优(建议分批搜索)
|
||||
4. ⏳ **导出格式完善**:Word 模板美化、更多导出格式
|
||||
|
||||
### 短期优化(标题摘要初筛)
|
||||
1. ⏳ Prompt优化(提升准确率到85%+)
|
||||
2. ⏳ 添加任务暂停/取消功能
|
||||
3. ⏳ 实现并发处理(3-5个并发)
|
||||
4. ⏳ 添加估计剩余时间显示
|
||||
### 短期优化
|
||||
1. ⏳ Prompt 优化(需求扩写质量提升)
|
||||
2. ⏳ 搜索历史管理(历史任务列表、重新搜索)
|
||||
3. ⏳ 全文复筛前端 UI 开发
|
||||
4. ⏳ 标题摘要初筛 Prompt 优化(准确率 60% → 85%+)
|
||||
|
||||
### 中期(Month 2)
|
||||
1. 🚧 全文复筛功能(开发中)
|
||||
2. ⏳ 全文数据提取功能
|
||||
3. ⏳ 用户自定义边界情况
|
||||
4. ⏳ WebSocket实时推送
|
||||
1. ⏳ 全文复筛功能完善
|
||||
2. ⏳ 证据图谱可视化
|
||||
3. ⏳ 用户自定义数据源
|
||||
4. ⏳ 生产环境部署
|
||||
|
||||
### 长期(Month 3+)
|
||||
1. ⏳ 多用户支持(真实认证)
|
||||
2. ⏳ 消息队列(Bull/RabbitMQ)
|
||||
3. ⏳ 分布式处理
|
||||
4. ⏳ 成本控制和监控
|
||||
1. ⏳ 多语言检索策略自动优化
|
||||
2. ⏳ 批量文献检索
|
||||
3. ⏳ 成本控制和监控
|
||||
|
||||
---
|
||||
|
||||
@@ -1350,15 +1401,14 @@ Drawer打开: <50ms
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2025-11-22(全文复筛 Day 2-3完成)
|
||||
**最后更新**:2026-02-23(Deep Research V2.0 核心功能完成)
|
||||
**文档状态**:✅ 反映真实状态
|
||||
**下次更新时机**:全文复筛MVP完成 或 标题摘要Prompt优化完成
|
||||
**下次更新时机**:V2.0 端到端回归测试完成 或 全文复筛前端开发启动
|
||||
|
||||
**本次更新内容**(v1.1):
|
||||
- ✅ 更新当前状态(新增全文复筛开发进度)
|
||||
- ✅ 更新关键里程碑(Day 2-3完成)
|
||||
- ✅ 新增后端代码结构(common层 + fulltext-screening层)
|
||||
- ✅ 新增开发记录链接(Day 2-3工作总结)
|
||||
- ✅ 更新下一步开发计划(当前Sprint)
|
||||
**本次更新内容**(v2.0):
|
||||
- ✅ 更新当前状态:V2.0 核心功能开发完成(SSE 流式 + 瀑布流 UI + Word 导出)
|
||||
- ✅ 新增 V2.0 完整架构表、技术决策、API 端点、关键文件列表
|
||||
- ✅ 新增 5 个精选数据源配置(替代 9 站全量展示)
|
||||
- ✅ 更新下一步开发计划(V2.0 优化 + 短期/中期/长期)
|
||||
|
||||
|
||||
|
||||
@@ -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,不引入 Zustand(v1.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 各组件核心逻辑
|
||||
|
||||
**LandingView(Landing 大搜索框)**
|
||||
- 居中大输入框 + "开始研究"按钮 + 推荐预置词
|
||||
- 点击后携带输入值,平滑过渡到 SetupPanel
|
||||
- 参考原型图 V4.2 的 `#landing-view` 部分
|
||||
|
||||
**SetupPanel(Step 1: 配置)**
|
||||
**SetupPanel(Step 1: 配置)** *(v1.1 数据源更新)*
|
||||
- 继承 Landing 输入值到 textarea
|
||||
- 数据源 Checkbox(PubMed/PMC, BMJ Open, Cochrane)
|
||||
- 高级过滤(年份下拉、目标数量、OA 强制)
|
||||
- 数据源 Checkbox(基于实测精选 5 个,见 5.1.1):
|
||||
- 🌍 **英文数据源**
|
||||
- [x] PubMed(默认勾选,不可取消)
|
||||
- [ ] ClinicalTrials.gov — ⚠️ 旁标橙色提示:"该站点需要英文查询,系统将自动为此数据源生成英文检索指令"
|
||||
- [ ] Cochrane Library
|
||||
- 🇨🇳 **中文数据源**
|
||||
- [ ] 中国知网 CNKI
|
||||
- [ ] 中华医学期刊网
|
||||
- 高级过滤(年份下拉、目标数量、OA 偏好 — 注意是偏好非强制)
|
||||
- 点击"解析并生成检索需求书" → POST /generate-requirement
|
||||
- Loading 后平滑展开 Step 2
|
||||
|
||||
**StrategyConfirm(Step 2: HITL 确认)**
|
||||
- 左侧 1/3:AI 意图提炼卡片(只读,来自 `intentSummary`)
|
||||
- 右侧 2/3:可编辑 textarea(内容为 `generatedRequirement`)
|
||||
- 提示文案:"您可以像写邮件一样在这里补充任何大白话要求"
|
||||
**StrategyConfirm(Step 2: HITL 确认)** *(v1.1 PICOS + MeSH)*
|
||||
- 左侧 1/3:AI 意图提炼卡片(只读,PICOS 结构化展示)
|
||||
- 🎯 研究目标:`objective`
|
||||
- 👥 研究人群:`population`
|
||||
- 💊 干预措施:`intervention`(含 MeSH 英文术语)
|
||||
- ⚖️ 对照组:`comparison`
|
||||
- 📊 结局指标:`outcome`
|
||||
- 📋 研究设计:`studyDesign` Tag 列表
|
||||
- 🏷️ MeSH 术语:`meshTerms` 小标签展示
|
||||
- 右侧 2/3:可编辑 textarea(内容为 `generatedRequirement`,自然语言对话风格)
|
||||
- 提示文案:"这是 AI 以医学信息官的视角为您扩写的检索需求,您可以像写邮件一样直接编辑修改"
|
||||
- 点击"确认需求,启动 Deep Research" → PUT /execute
|
||||
|
||||
**AgentTerminal(Step 3: 暗黑终端)**
|
||||
**AgentTerminal(Step 3: 暗黑终端)** *(v1.1 条件滚动)*
|
||||
- 暗色背景(bg-slate-900),固定高度 550px,内部滚动
|
||||
- 顶部状态栏:红/黄/绿圆点 + "Running" 脉冲指示灯
|
||||
- 日志渲染:
|
||||
@@ -443,7 +643,10 @@ interface DeepResearchState {
|
||||
- `done` → 绿色 + ✅ 图标
|
||||
- `summary` → 黄色 + 📋 图标
|
||||
- 轮询逻辑:`useQuery` + refetchInterval: 3000(running 时启用)
|
||||
- 新日志出现时 auto-scroll 到底部
|
||||
- **条件自动滚动**(v1.1 新增):
|
||||
- 用户**未手动上滚**时 → 新日志自动滚到底部
|
||||
- 用户**已手动上滚**查看历史 → 停止自动滚动,避免打断阅读
|
||||
- 实现:`onScroll` 检测 `scrollTop + clientHeight < scrollHeight - threshold` 设 `userScrolled` flag
|
||||
- 完成后状态灯变灰 "Finished",终端可折叠
|
||||
|
||||
**ResultsView(Step 4: 结果)**
|
||||
@@ -454,8 +657,9 @@ interface DeepResearchState {
|
||||
- 列:标题(可点击跳转 PubMed)、期刊、年份、类型 Tag、PDF 状态
|
||||
- 支持简单搜索过滤
|
||||
- 分页(前端分页即可,数据量 ~100 条)
|
||||
- **降级展示**:若 `resultList` 为 null(JSON 解析失败),隐藏表格,仅展示综合报告
|
||||
|
||||
### 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 导出 | Pandoc(Python 微服务) | 复用 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=180(15分钟),超时标记 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 方案确认(含审查建议),待开发启动
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# **Deep Research V2.0 开发计划审查与优化建议书**
|
||||
|
||||
**审查对象:** 07-Deep Research V2.0 开发计划.md
|
||||
|
||||
**审查基准:** V4.1 PRD、Unifuncs API 官方文档、系统高可用性架构标准
|
||||
|
||||
**审查结论:** 整体架构设计(pg-boss \+ 异步轮询 \+ 单页瀑布流)非常优秀。但在**容错机制、数据解析提取、前端状态管理、以及终端 UX 细节**上存在明显隐患,需进行针对性修正。
|
||||
|
||||
## **🔴 一、 核心高危风险与修正建议 (Critical Risks & Fixes)**
|
||||
|
||||
### **1\. Unifuncs 轮询逻辑的“脆弱性” (Worker 改造部分)**
|
||||
|
||||
* **原计划缺陷:** 在 deepResearchV2Worker.ts 的伪代码中,轮询逻辑简单地使用了 await unifuncsClient.queryTask()。如果在长达 15 分钟的轮询中,发生了一次短暂的网络抖动或 Unifuncs 网关返回了 502/504,整个 try-catch 就会抛出异常,导致这个耗费了大量时间的长任务直接 failed。
|
||||
* **修正方案:**
|
||||
在 Worker 的 while 循环内部,**必须包裹 try-catch 并引入重试机制(Exponential Backoff)**。遇到网络请求失败时,不能直接退出,而是应该记录 warning 并 continue 等待下一次 5s 后的轮询,连续失败超过 5 次才判定任务崩溃。
|
||||
|
||||
### **2\. 输出解析的“幻觉陷阱” (JSON Parsing)**
|
||||
|
||||
* **原计划缺陷:** 计划中提到使用 extractSection 提取 \<JSON\_LIST\_SECTION\> 后,直接调用 safeParseJsonList。但在实际的大模型输出中,即便你规定了 XML 标签,模型极大概率会在标签内输出 Markdown 代码块,例如:
|
||||
\<JSON\_LIST\_SECTION\>
|
||||
\`\`\`json
|
||||
\[{"title": "..."}\]
|
||||
|
||||
\</JSON\_LIST\_SECTION\>
|
||||
如果直接 \`JSON.parse()\` 必定报错崩溃。
|
||||
|
||||
* **修正方案:**
|
||||
utils/resultParser.ts 中的 safeParseJsonList 方法**必须**包含预处理逻辑:
|
||||
1. 使用正则清洗掉可能存在的 json\` 和 首尾包裹符。
|
||||
2. 增加**兜底方案 (Fallback)**:如果正则和清洗均宣告失败,调用一次系统自带的 DeepSeek-V3,传入提取出的坏字符串,要求其强制吐出标准 JSON Array(利用 response\_format: { type: "json\_object" }),确保 Step 4 的表格必定能渲染。
|
||||
|
||||
## **🟡 二、 前端架构与交互体验优化 (Frontend UX/Architecture)**
|
||||
|
||||
### **1\. 终端日志自动滚动的“反人类”交互 (AgentTerminal)**
|
||||
|
||||
* **原计划缺陷:** 计划中提到“新日志出现时 auto-scroll 到底部”。如果用户在长达 5 分钟的执行过程中,向上滚动滚轮想查看之前的思考日志,此时新日志一推过来,页面又被强制拉回到底部,这种体验非常“反人类”。
|
||||
* **修正方案:**
|
||||
修改 Auto-scroll 触发逻辑:前端通过 ref 判断当前滚动条位置,**只有当用户处于滚动条最底部(或距离底部 \< 50px)时,收到新日志才自动滚动到底部**。如果用户已经向上滚动阅读历史,则只在容器内部静默追加日志,不改变视口位置(可浮现一个“↓ 有新日志”的提示小红点)。
|
||||
|
||||
### **2\. 前端状态管理的“螺旋地狱” (State Management)**
|
||||
|
||||
* **原计划缺陷:** 计划在 6.2 节中明确写道:“页面级状态(useState 即可,无需 Zustand)”。
|
||||
这是一个有 4 个复杂步骤(Landing \-\> Setup \-\> HITL \-\> Terminal \-\> Results)的单页应用。如果将 taskId、query、logs、step 全塞在父组件的 useState 里,会导致极其严重的**属性透传(Prop Drilling)和父组件频繁全量重渲染**。
|
||||
* **修正方案:**
|
||||
坚决反对在此场景下仅使用 useState。建议使用现有的 @tanstack/react-query 缓存状态,或者引入极其轻量的 Context API / Zustand。特别是终端日志(Logs)频繁追加时,不能让整个大页面跟着一起重绘,终端组件应当是局部渲染的。
|
||||
|
||||
## **🟢 三、 业务细节与数据闭环补充 (Business Details)**
|
||||
|
||||
### **1\. Word 导出功能的缺失逻辑 (Word Export)**
|
||||
|
||||
* **原计划缺陷:** 提到“报告 \+ 文献表格 → 完整 Markdown → Pandoc 转 Word”。但 result\_list 存的是 JSON Array,Pandoc 不认识 JSON。
|
||||
* **修正方案:**
|
||||
在 services/wordExportService.ts 中,需要明确增加一步\*\*“JSON to Markdown Table”\*\*的转化方法。
|
||||
即把 JSON 数组转化为:
|
||||
| 文献标题 | 期刊 | 发表年份 | 链接 |
|
||||
|---|---|---|---|
|
||||
| Efficacy of... | Lancet | 2023 | \[PubMed\](...) |
|
||||
|
||||
再将这个生成的 Markdown Table 拼接到 synthesis\_report 的末尾,最后统一送给 Pandoc 渲染。
|
||||
|
||||
### **2\. 第一步“需求扩写”的成本追踪漏洞**
|
||||
|
||||
* **原计划缺陷:**
|
||||
数据库 Schema 只增加了 Unifuncs 任务的 tokenUsage 记录。但 Step 1 中,调用本地 DeepSeek-V3 把一句话扩写成几百字的“自然语言指令书”,也是要消耗 Token 的。
|
||||
* **修正方案:**
|
||||
在 AslResearchTask 模型中,应将 tokenUsage 分离或扩展为能够记录两次消耗的结构。例如:internal\_token\_usage (记录扩写的成本) 和 external\_token\_usage (记录 Unifuncs 成本),以实现精细化的财务核算。
|
||||
|
||||
## **📝 最终评审结论**
|
||||
|
||||
请开发团队长仔细阅读上述 6 条修正建议,并同步更新到研发 Task 列表中。
|
||||
|
||||
特别是在\*\*【JSON解析防崩溃】**和**【终端条件自动滚动】\*\*这两点上,直接决定了本次 V2.0 交付的“工业级质感”。只要把控好这几个细节,原有的技术路线完全可以跑通,且效果会非常惊艳!
|
||||
@@ -0,0 +1,114 @@
|
||||
# **Deep Research 需求扩写 Prompt 设计与检索维度指南**
|
||||
|
||||
**设计者:** 医学信息官 (MIO)
|
||||
|
||||
**应用场景:** ASL Deep Research V2.0 (MVP) \- Step 1 需求转化阶段
|
||||
|
||||
**底层引擎:** 本地 DeepSeek-V3 / Qwen-Max (负责扩写) \-\> Unifuncs (负责执行)
|
||||
|
||||
## **一、 文献检索维度的解构 (MIO 视角)**
|
||||
|
||||
要把用户的“一句话”扩写成一份严谨的检索指令书,LLM 必须脑补并结构化以下维度。这些维度将直接决定 Unifuncs 引擎去哪些网页、抓取什么关键词、过滤什么条件。
|
||||
|
||||
### **🚨 1\. 必须项 (Mandatory Fields \- 决定检索成败)**
|
||||
|
||||
如果用户没提,LLM 必须根据医学常识自动补齐其合理范围;如果补不齐,应设置默认的最优标准。
|
||||
|
||||
1. **核心疾病/目标人群 (Population):** 必须明确适应症。*(例如:用户说“他汀”,LLM必须根据常识扩写出可能针对的“高脂血症”或“心血管疾病预防”人群)*。
|
||||
2. **核心干预措施 (Intervention):** 具体的药物、疗法或器械。
|
||||
3. **目标文献类型 (Study Design \- 极度重要):** 在循证医学中,文献类型决定了证据等级。如果用户没说,LLM 默认必须限定为:**高质量的随机对照试验 (RCT)、前瞻性队列研究、系统综述与 Meta 分析**。绝不能让引擎去抓取个案报道 (Case Report) 或动物实验。
|
||||
4. **获取约束 (Access Rule):** 这是我们系统的硬杠杠。必须强制要求:**仅限开放获取 (Open Access) 且包含完整 PDF 链接的文献**。
|
||||
|
||||
### **💡 2\. 可选项 (Optional Fields \- 提升查准率)**
|
||||
|
||||
用户如果有提及,要强化;如果没提及,LLM 可以适当发散或留白。
|
||||
|
||||
1. **对照组 (Comparison):** 安慰剂 (Placebo) 还是标准疗法 (Standard of Care)。
|
||||
2. **特定结局指标 (Outcomes):** 比如 OS (总生存期)、MACE (主要心血管不良事件)、AEs (不良反应)。
|
||||
3. **同义词与 MeSH 词扩展 (Synonyms Expansion):** 这是体现“专业性”的核心!用户可能只输入了 "心衰",LLM 必须在指令书里附带提供 "Heart Failure", "HFrEF", "HFpEF" 等检索词,供 Unifuncs 扩大查全率。
|
||||
|
||||
## **二、 核心 System Prompt 设计 (可以直接写入代码)**
|
||||
|
||||
请将以下 Prompt 部署到 services/requirementExpansionService.ts 中。
|
||||
|
||||
\# Role
|
||||
你是一名受过严谨训练的“临床医学信息专家(Medical Information Officer)”与循证医学图书管理员。
|
||||
|
||||
\# Task
|
||||
用户的输入往往是口语化且不完整的临床检索诉求。你的任务是将用户简短的一句话,扩写、翻译并结构化为一份《深度检索指令书》。
|
||||
这份指令书将直接发送给另一个拥有自主上网能力的 AI 深搜引擎(Agent)去执行。因此,你的语言必须清晰、专业、逻辑严密,并利用你的医学知识为用户补全隐含的专业要求。
|
||||
|
||||
\# Instructions & Rules
|
||||
1\. \*\*理解与补全 (PICOS):\*\* 基于用户的输入,识别 P (人群)、I (干预)、C (对照)、O (结局)、S (研究类型)。
|
||||
2\. \*\*强制文献质量过滤:\*\* 如果用户没有指定文献类型,你必须强制添加:“优先获取 随机对照试验(RCT)、前瞻性队列研究、系统综述与Meta分析;排除动物实验、体外研究、个案报道及非英文文献”。
|
||||
3\. \*\*强制数据可及性:\*\* 必须在指令中强调:“只提取开放获取 (Open Access) 且附带有效 PDF 全文下载链接的文献”。
|
||||
4\. \*\*扩展专业检索词 (MeSH):\*\* 利用你的医学知识,将用户的中文或俗称,扩展为标准的英文医学主题词 (MeSH) 和常见缩写,写在【扩展检索词库】中,指导深搜引擎扩大召回率。
|
||||
|
||||
\# Output Format (严格遵循以下 Markdown 结构,不要输出额外废话)
|
||||
|
||||
请帮我执行一次深度的医学文献检索。以下是具体的检索要求:
|
||||
|
||||
【核心检索主题】
|
||||
(一句话总结用户意图,例如:评估XXX在YYY中的疗效与安全性)
|
||||
|
||||
【目标人群与干预措施】
|
||||
\- 人群 (Population):(详细描述疾病阶段、特征,如无明确要求则合理推断)
|
||||
\- 干预 (Intervention / Comparison):(具体的药物、疗法及可能的对照)
|
||||
|
||||
【关注的结局指标 (Outcomes)】
|
||||
(列出该领域常见的核心指标,如 OS, PFS, 并发症发生率等。如果用户指定了则用用户的)
|
||||
|
||||
【目标文献类型与质量要求】
|
||||
\- 必须是:随机对照试验 (RCT)、队列研究、系统综述与 Meta 分析。
|
||||
\- 强制排除:动物实验、体外细胞研究、个案报道。
|
||||
\- 强制获取要求:必须是开放获取 (Open Access) 的文献,且必须能获取 PDF 全文或完整 XML 数据。对于需要付费墙 (Paywall) 的文献请予以忽略。
|
||||
|
||||
【扩展检索词库 (供搜索引擎参考)】
|
||||
(列出 5-8 个核心的英文专业词汇、MeSH词或缩写,用逗号分隔。例如:Hydroxymethylglutaryl-CoA Reductase Inhibitors, Heart Failure, MACE...)
|
||||
|
||||
## **三、 效果演示 (Before vs After)**
|
||||
|
||||
你可以看到,经过这个 Prompt 处理后,原本干瘪的需求变得极其丰满,且直接指导了 Unifuncs 引擎该怎么做。
|
||||
|
||||
### **🧑⚕️ 医生的粗略输入 (前端传入)**
|
||||
|
||||
"替雷利珠单抗治疗非小细胞肺癌,只要带PDF的"
|
||||
|
||||
### **🤖 LLM 扩写后的指令书 (前端 Step 2 展示给用户确认)**
|
||||
|
||||
请帮我执行一次深度的医学文献检索。以下是具体的检索要求:
|
||||
|
||||
**【核心检索主题】**
|
||||
|
||||
评估替雷利珠单抗(Tislelizumab)在治疗非小细胞肺癌(NSCLC)中的疗效与安全性。
|
||||
|
||||
**【目标人群与干预措施】**
|
||||
|
||||
* **人群 (Population):** 晚期、局部晚期或转移性非小细胞肺癌患者(可包含一线或二线治疗人群)。
|
||||
* **干预 (Intervention / Comparison):** 替雷利珠单抗单药或联合化疗,对照组可为标准含铂双药化疗或安慰剂。
|
||||
|
||||
**【关注的结局指标 (Outcomes)】**
|
||||
|
||||
重点关注总生存期 (OS)、无进展生存期 (PFS)、客观缓解率 (ORR) 以及免疫相关不良事件 (irAEs) 的发生率。最好能提取到 HR 值及 95% CI。
|
||||
|
||||
**【目标文献类型与质量要求】**
|
||||
|
||||
* 必须是:随机对照试验 (RCT)、前瞻性队列研究、系统综述与 Meta 分析。
|
||||
* 强制排除:动物实验、体外细胞研究、个案报道。
|
||||
* 强制获取要求:必须是开放获取 (Open Access) 的文献,且必须能获取 PDF 全文或完整 XML 数据。对于需要付费墙 (Paywall) 的文献请予以忽略。
|
||||
|
||||
**【扩展检索词库 (供搜索引擎参考)】**
|
||||
|
||||
Tislelizumab, BGB-A317, Non-Small Cell Lung Cancer, NSCLC, Carcinoma, Non-Small-Cell Lung (MeSH), PD-1 Inhibitors, Overall Survival.
|
||||
|
||||
## **四、 为什么这种设计对 Unifuncs API 特别有效?**
|
||||
|
||||
Unifuncs 是一个基于大模型推理的 Agent。如果你只给它扔一句 "替雷利珠单抗治疗非小细胞肺癌",它会像个无头苍蝇一样在网上随便搜几个百度百科或知乎的新闻来交差。
|
||||
|
||||
但当你把上面这篇**充斥着 MeSH词、明确排除了动物实验、明确要求找 RCT、并且要求带有 PDF** 的大白话发给 Unifuncs 的 messages 时:
|
||||
|
||||
1. Unifuncs 会直接把 Tislelizumab AND NSCLC AND Randomized Controlled Trial 作为 PubMed 的搜索词。
|
||||
2. 它在阅读网页时,看到没有 PDF 下载链接的,会自动根据指令里的【强制获取要求】执行 Discard(放弃)动作。
|
||||
3. 它在总结时,会自动围绕【关注的结局指标】去撰写你的 synthesis\_report。
|
||||
|
||||
这就是大模型时代\*\*“用魔法(本地大模型)打败魔法(搜索大模型)”\*\*的最佳实践!
|
||||
Reference in New Issue
Block a user