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>
26 KiB
26 KiB
Deep Research V2.0 开发计划
文档版本: v1.0
创建日期: 2026-02-22
维护者: 开发团队
前置文档: PRD V4.1 / 原型图 V4.2 / 技术设计 V4.1
预计工期: 5 天
核心理念: 单页瀑布流 + 自然语言需求扩写 + 异步执行 + 务实结果展示
1. 升级概述
1.1 V1.x → V2.0 变化总结
| 维度 | V1.x (当前) | V2.0 (目标) |
|---|---|---|
| 交互模式 | 单输入框 → 直接搜索 | 四步瀑布流 Landing → 配置 → HITL 确认 → 终端 → 结果 |
| 需求理解 | 用户原文直传 unifuncs | 内置 LLM 需求扩写 + 用户人工核验修改 |
| API 协议 | OpenAI 兼容(SSE 流式) | Unifuncs 异步模式(create_task + query_task 轮询) |
| 执行展示 | 混合文字流(打字机效果) | 暗黑终端 + 分类结构化日志(每 3-5s 弹出一条) |
| 结果展示 | PubMed 链接列表 | 综合报告(Markdown)+ 文献清单表格 + Word 导出 |
| 可靠性 | 离开页面任务丢失 | pg-boss 队列,离开页面任务继续,回来可恢复 |
1.2 设计决策记录
| 决策 | 选择 | 理由 |
|---|---|---|
| SSE vs 异步 | 异步模式 | Deep Research 任务 3-10 分钟,SSE 连接不稳定;异步模式用户可离开回来,可靠性远高于 SSE |
| 异步下的实时性 | Worker 5s 轮询 + 前端 3s 轮询 | 用户每 3-5s 看到一条新日志,对分钟级 Agent 任务来说体验自然,比逐字流更适合终端 UI |
| 结果展示复杂度 | 报告 + 表格,不做图表看板 | 研究人员要的是内容本身(综合报告 + 文献清单),图表是锦上添花非刚需,MVP 不做 |
| Word 导出 | 复用 Pandoc | Protocol Agent 已验证 Pandoc → Word 方案,零额外依赖 |
2. 系统数据流
┌──────────────────────────────────────────────────────────────────────┐
│ Step 1-2: 需求扩写(同步,本系统内部) │
│ │
│ 前端 Landing/Setup ──POST──→ Node.js ──LLMFactory──→ DeepSeek-V3 │
│ original_query "需求扩写Prompt" │
│ │
│ 返回:taskId + generatedRequirement(结构化自然语言检索指令书) │
│ 前端展示指令书,用户可编辑修改 │
└──────────────────────────────────────────────────────────────────────┘
↓ 用户确认
┌──────────────────────────────────────────────────────────────────────┐
│ Step 3: 异步执行(pg-boss + Unifuncs 异步 API) │
│ │
│ 前端 ──PUT──→ Node.js ──pg-boss push──→ Worker │
│ confirmed_requirement │
│ │
│ Worker: │
│ 1. POST unifuncs/v1/create_task(传入 confirmed_requirement) │
│ 2. 每 5s GET unifuncs/v1/query_task │
│ 3. 解析 reasoning_content → 增量日志写 DB (execution_logs) │
│ 4. 完成后解析 content → synthesis_report + result_list │
│ │
│ 前端每 3s GET /tasks/:id → 渲染 execution_logs 到暗黑终端 │
└──────────────────────────────────────────────────────────────────────┘
↓ status === 'completed'
┌──────────────────────────────────────────────────────────────────────┐
│ Step 4: 结果展示(读 DB 渲染) │
│ │
│ 终端折叠 → 白底结果区展开 │
│ ├── ✅ 完成横幅(一行 + 导出 Word 按钮) │
│ ├── 📄 AI 综合报告(synthesis_report → Markdown 渲染) │
│ └── 📋 文献清单表格(result_list → Ant Design Table) │
└──────────────────────────────────────────────────────────────────────┘
3. 数据库 Schema 变更
在现有 AslResearchTask 基础上新增 6 个字段,不删除任何现有字段(向后兼容)。
model AslResearchTask {
// ── 现有字段(保留不动)──────────────────────────
id String @id @default(uuid())
projectId String @map("project_id")
userId String @map("user_id")
query String // 原始粗略输入(Step 1)
filters Json? // 高级筛选配置
externalTaskId String? @map("external_task_id") // unifuncs task_id
status String @default("pending")
errorMessage String? @map("error_message")
resultCount Int? @map("result_count")
rawResult String? @map("raw_result") @db.Text
reasoningContent String? @map("reasoning_content") @db.Text
literatures Json?
tokenUsage Json? @map("token_usage")
searchCount Int? @map("search_count")
readCount Int? @map("read_count")
iterations Int?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
completedAt DateTime? @map("completed_at")
// ── V2.0 新增字段 ──────────────────────────────
targetSources Json? @map("target_sources") // 选中的数据源 ["pubmed.ncbi.nlm.nih.gov", ...]
confirmedRequirement String? @map("confirmed_requirement") @db.Text // 用户核验后的自然语言检索指令书
aiIntentSummary Json? @map("ai_intent_summary") // AI提炼的结构化摘要(左侧卡片用)
executionLogs Json? @map("execution_logs") // 终端日志数组 [{type, title, text, timestamp}]
synthesisReport String? @map("synthesis_report") @db.Text // AI综合报告(Markdown)
resultList Json? @map("result_list") // 结构化文献元数据列表
// ── 索引(保留现有)────────────────────────────
@@index([projectId], map: "idx_research_tasks_project_id")
@@index([userId], map: "idx_research_tasks_user_id")
@@index([status], map: "idx_research_tasks_status")
@@index([createdAt], map: "idx_research_tasks_created_at")
@@map("research_tasks")
@@schema("asl_schema")
}
Status 枚举扩展:
| 状态 | 含义 | 触发时机 |
|---|---|---|
draft |
需求已扩写,等待用户确认 | POST /generate-requirement |
pending |
用户已确认,等待 Worker 拾取 | PUT /tasks/:id/execute |
running |
Worker 已创建 unifuncs 任务,轮询中 | Worker 内部 |
completed |
unifuncs 完成,结果已解析入库 | Worker 内部 |
failed |
执行失败 | Worker 内部 |
迁移命令:
npx prisma migrate dev --name add_deep_research_v2_fields
4. API 契约
4.1 需求扩写(同步)
POST /api/v1/asl/research/generate-requirement
// 请求
{
originalQuery: string, // "他汀预防心血管疾病,要能下载PDF的"
targetSources: string[], // ["pubmed.ncbi.nlm.nih.gov", "bmjopen.bmj.com"]
filters: {
yearRange?: string, // "2010至今" | "过去5年" | "不限"
targetCount?: string, // "~100篇" | "全面检索"
requireOpenAccess?: boolean // true
}
}
// 响应
{
success: true,
data: {
taskId: "uuid", // 已创建DB记录(status=draft)
generatedRequirement: "请帮我执行一次深度的医学文献检索...", // LLM扩写结果
intentSummary: { // 结构化摘要
objective: "为Meta分析构建测试语料库",
intervention: "他汀类药物 (Statins)",
condition: "心血管疾病 (CVD)",
literatureStandard: "高质量临床研究,PDF全文可下载"
}
}
}
实现要点:
- 调用
LLMFactory.getAdapter('deepseek-v3')进行需求扩写 - System Prompt 要求 LLM 输出结构化自然语言指令(非布尔检索式)
- 同时创建 DB 记录,status =
draft
4.2 启动执行(进入异步队列)
PUT /api/v1/asl/research/tasks/:id/execute
// 请求
{
confirmedRequirement: string // 用户核验修改后的最终指令书
}
// 响应
{ success: true }
实现要点:
- 更新 DB 的
confirmed_requirement和target_sources jobQueue.push('asl_deep_research_v2', { taskId })推入 pg-boss- status 更新为
pending
4.3 任务状态与日志轮询
GET /api/v1/asl/research/tasks/:id
// 响应
{
success: true,
data: {
taskId: "uuid",
status: "running", // draft/pending/running/completed/failed
executionLogs: [ // 终端日志(增量)
{ type: "think", title: "任务理解", text: "已收到检索需求...", ts: "..." },
{ type: "action", title: "Search", text: "executing search across PubMed...", ts: "..." },
{ type: "done", title: "搜索轮次完成", text: "", ts: "..." },
],
progress: { current: 60, total: 100 },
// 仅 completed 时有:
synthesisReport: "## 研究背景\n他汀类药物...",
resultList: [
{ title: "...", authors: "...", journal: "...", year: 2010, type: "Meta-analysis", pmid: "...", doi: "...", pdfStatus: "OA" },
],
resultCount: 103,
errorMessage: null
}
}
4.4 Word 导出
GET /api/v1/asl/research/tasks/:id/export-word
- 读取 DB 的
synthesis_report(Markdown)和result_list(JSON) - 拼接为完整 Markdown(报告 + 文献清单表格)
- 调用 Pandoc 转 Word
- 返回
.docx文件流
4.5 路由汇总
| 方法 | 路径 | 说明 | 新增/改造 |
|---|---|---|---|
| POST | /research/generate-requirement |
需求扩写 | 新增 |
| PUT | /research/tasks/:id/execute |
启动执行 | 新增 |
| GET | /research/tasks/:id |
状态+日志+结果 | 改造 |
| GET | /research/tasks/:id/export-word |
Word 导出 | 新增 |
| POST | /research/stream |
V1 SSE(保留兼容) | 不动 |
| POST | /research/tasks |
V1 异步创建(保留) | 不动 |
5. 后台 Worker 逻辑
5.1 核心流程(伪代码)
// backend/src/modules/asl/workers/deepResearchV2Worker.ts
export async function processDeepResearchV2(job: Job) {
const { taskId } = job.data;
const task = await prisma.aslResearchTask.findUnique({ where: { id: taskId } });
// 1. 调用 Unifuncs 创建异步任务
const unifuncsPayload = {
model: "s2",
messages: [{
role: "user",
content: `请根据以下详细检索需求执行深度研究:\n${task.confirmedRequirement}`
}],
introduction: buildIntroduction(),
max_depth: 25,
domain_scope: task.targetSources || ["https://pubmed.ncbi.nlm.nih.gov/"],
domain_blacklist: ["wanfang.com", "cnki.net"],
output_prompt: buildOutputPrompt(),
reference_style: "link",
generate_summary: true,
};
const createRes = await unifuncsClient.createTask(unifuncsPayload);
const unifuncsTaskId = createRes.data.task_id;
await prisma.aslResearchTask.update({
where: { id: taskId },
data: { externalTaskId: unifuncsTaskId, status: 'running' }
});
// 2. 轮询 Unifuncs 直到完成
let previousReasoning = '';
const MAX_POLLS = 180; // 最多 15 分钟(180 × 5s)
for (let i = 0; i < MAX_POLLS; i++) {
await sleep(5000);
const queryRes = await unifuncsClient.queryTask(unifuncsTaskId);
const data = queryRes.data;
// 解析增量日志
const currentReasoning = data.result?.reasoning_content || '';
if (currentReasoning.length > previousReasoning.length) {
const increment = currentReasoning.slice(previousReasoning.length);
const newLogs = parseReasoningToLogs(increment);
await appendExecutionLogs(taskId, newLogs);
previousReasoning = currentReasoning;
}
// 同步进度
if (data.progress) {
// progress 信息可通过 executionLogs 的最后一条体现
}
// 检查完成
if (data.status === 'completed') {
const content = data.result?.content || '';
const report = extractSection(content, 'REPORT_SECTION');
const jsonList = extractSection(content, 'JSON_LIST_SECTION');
const parsedList = safeParseJsonList(jsonList);
await prisma.aslResearchTask.update({
where: { id: taskId },
data: {
status: 'completed',
rawResult: content,
reasoningContent: currentReasoning,
synthesisReport: report || content,
resultList: parsedList,
resultCount: parsedList?.length || 0,
tokenUsage: data.statistics?.token_usage,
searchCount: data.statistics?.search_count,
readCount: data.statistics?.read_count,
iterations: data.statistics?.iterations,
completedAt: new Date(),
}
});
return;
}
if (data.status === 'failed') {
throw new Error(data.result?.content || 'Unifuncs 任务失败');
}
}
throw new Error('任务超时(15分钟)');
}
5.2 日志解析逻辑
function parseReasoningToLogs(increment: string): LogEntry[] {
const logs: LogEntry[] = [];
const lines = increment.split('\n').filter(l => l.trim());
for (const line of lines) {
if (line.includes('搜索') || line.includes('search') || line.includes('Search')) {
logs.push({ type: 'action', title: 'Search', text: line.trim(), ts: new Date().toISOString() });
} else if (line.includes('阅读') || line.includes('read') || line.includes('Read')) {
logs.push({ type: 'action', title: 'Read', text: line.trim(), ts: new Date().toISOString() });
} else if (line.includes('完成') || line.includes('成功') || line.includes('OK')) {
logs.push({ type: 'done', title: '阶段完成', text: line.trim(), ts: new Date().toISOString() });
} else if (line.includes('汇总') || line.includes('总结') || line.includes('发现')) {
logs.push({ type: 'summary', title: '阶段总结', text: line.trim(), ts: new Date().toISOString() });
} else if (line.trim().length > 10) {
logs.push({ type: 'think', title: 'Thinking', text: line.trim(), ts: new Date().toISOString() });
}
}
return logs;
}
5.3 output_prompt 设计
function buildOutputPrompt(): string {
return `请严格按照以下格式输出结果:
<REPORT_SECTION>
[此处撰写深度综合研究报告,使用 Markdown 格式,包括:
- 研究背景与目的
- 核心发现与共识
- 分歧点与研究空白
- 参考文献列表(带编号和PubMed链接)]
</REPORT_SECTION>
<JSON_LIST_SECTION>
[此处输出文献元数据的严格 JSON 数组,每条包含:
{"title":"...", "authors":"...", "journal":"...", "year":2024, "type":"RCT|Meta-analysis|Cohort|SR", "pmid":"...", "doi":"...", "pdfStatus":"OA|Restricted", "url":"https://pubmed.ncbi.nlm.nih.gov/..."}]
</JSON_LIST_SECTION>`;
}
6. 前端组件设计
6.1 页面结构
frontend-v2/src/modules/asl/pages/
└── DeepResearchPage.tsx # V2.0 主页面(替代 ResearchSearch.tsx)
frontend-v2/src/modules/asl/components/
├── deep-research/
│ ├── LandingView.tsx # Landing 大搜索框
│ ├── SetupPanel.tsx # Step 1: 配置面板
│ ├── StrategyConfirm.tsx # Step 2: HITL 策略确认(左右分栏)
│ ├── AgentTerminal.tsx # Step 3: 暗黑执行终端
│ └── ResultsView.tsx # Step 4: 结果展示(报告+表格)
6.2 状态管理
// 页面级状态(useState 即可,无需 Zustand)
interface DeepResearchState {
currentStep: 'landing' | 'setup' | 'strategy' | 'terminal' | 'results';
taskId: string | null;
originalQuery: string;
generatedRequirement: string;
intentSummary: IntentSummary | null;
isGenerating: boolean; // 需求扩写中
}
6.3 各组件核心逻辑
LandingView(Landing 大搜索框)
- 居中大输入框 + "开始研究"按钮 + 推荐预置词
- 点击后携带输入值,平滑过渡到 SetupPanel
- 参考原型图 V4.2 的
#landing-view部分
SetupPanel(Step 1: 配置)
- 继承 Landing 输入值到 textarea
- 数据源 Checkbox(PubMed/PMC, BMJ Open, Cochrane)
- 高级过滤(年份下拉、目标数量、OA 强制)
- 点击"解析并生成检索需求书" → POST /generate-requirement
- Loading 后平滑展开 Step 2
StrategyConfirm(Step 2: HITL 确认)
- 左侧 1/3:AI 意图提炼卡片(只读,来自
intentSummary) - 右侧 2/3:可编辑 textarea(内容为
generatedRequirement) - 提示文案:"您可以像写邮件一样在这里补充任何大白话要求"
- 点击"确认需求,启动 Deep Research" → PUT /execute
AgentTerminal(Step 3: 暗黑终端)
- 暗色背景(bg-slate-900),固定高度 550px,内部滚动
- 顶部状态栏:红/黄/绿圆点 + "Running" 脉冲指示灯
- 日志渲染:
think→ 紫色 + 🧠 图标action→ 蓝色 + 💻 图标done→ 绿色 + ✅ 图标summary→ 黄色 + 📋 图标
- 轮询逻辑:
useQuery+ refetchInterval: 3000(running 时启用) - 新日志出现时 auto-scroll 到底部
- 完成后状态灯变灰 "Finished",终端可折叠
ResultsView(Step 4: 结果)
- 白色背景,与终端形成视觉分界
- 完成横幅(一行):文献数 + 耗时 + "导出 Word" 按钮
- AI 综合报告区:
react-markdown渲染synthesisReport,可折叠,默认展开 - 文献清单表格:Ant Design Table
- 列:标题(可点击跳转 PubMed)、期刊、年份、类型 Tag、PDF 状态
- 支持简单搜索过滤
- 分页(前端分页即可,数据量 ~100 条)
6.4 轮询 Hook
// hooks/useDeepResearchTask.ts
function useDeepResearchTask(taskId: string | null) {
return useQuery({
queryKey: ['deep-research-task', taskId],
queryFn: () => apiClient.get(`/api/v1/asl/research/tasks/${taskId}`),
enabled: !!taskId,
refetchInterval: (query) => {
const status = query.state.data?.data?.status;
return (status === 'pending' || status === 'running') ? 3000 : false;
},
});
}
7. 复用清单(不重复造轮子)
| 能力 | 来源 | 用法 |
|---|---|---|
| LLM 调用 | common/llm/LLMFactory |
DeepSeek-V3 需求扩写 |
| pg-boss 队列 | common/jobs/jobQueue |
Worker 注册与任务推送 |
| 日志服务 | common/logging/logger |
全程结构化日志 |
| 认证中间件 | common/auth/authenticate |
所有 API 路由 |
| Prisma 全局实例 | config/database |
数据库操作 |
| Word 导出 | Pandoc(Python 微服务) | 复用 Protocol Agent 验证的方案 |
| 前端 API Client | common/api/axios |
带认证的请求 |
| 前端布局 | ASLLayout.tsx |
左侧导航 |
8. 分阶段开发计划
Phase 1: 数据库 + 需求扩写(Day 1)
目标: 用户输入粗略想法 → AI 扩写为结构化指令书 → 用户可编辑修改
| 任务 | 文件 | 说明 |
|---|---|---|
| Schema 迁移 | prisma/schema.prisma |
新增 6 个字段,prisma migrate dev |
| 需求扩写 Prompt | services/requirementExpansionService.ts |
新建服务,调用 DeepSeek-V3 扩写 |
| 扩写 API | controllers/researchController.ts |
新增 POST /generate-requirement |
| 启动 API | controllers/researchController.ts |
新增 PUT /tasks/:id/execute |
| 状态 API 改造 | controllers/researchController.ts |
改造 GET /tasks/:id,返回新字段 |
| 路由注册 | routes/index.ts |
注册新端点 |
验收标准:
POST /generate-requirement返回扩写后的指令书PUT /tasks/:id/execute成功推入 pg-boss 队列GET /tasks/:id返回含新字段的完整数据
Phase 2: Worker 改造 — Unifuncs 异步模式(Day 2)
目标: Worker 使用 create_task + query_task 轮询,增量日志写入 DB
| 任务 | 文件 | 说明 |
|---|---|---|
| Unifuncs 异步客户端 | services/unifuncsAsyncClient.ts |
新建,封装 create_task / query_task |
| V2 Worker | workers/deepResearchV2Worker.ts |
新建,轮询 + 日志解析 + 结果切割 |
| 日志解析器 | utils/reasoningParser.ts |
新建,reasoning_content → 结构化日志 |
| 结果解析器 | utils/resultParser.ts |
新建,XML 标签切割报告与 JSON 列表 |
| Worker 注册 | workers/researchWorker.ts |
注册新 Worker asl_deep_research_v2 |
验收标准:
- Worker 成功调用 unifuncs create_task
- 轮询期间 execution_logs 持续增量更新
- 完成后 synthesis_report 和 result_list 正确入库
- 超时保护(15 分钟)和错误处理正常
Phase 3: 前端 — Landing + 配置 + HITL 确认(Day 3)
目标: 完成 Step 1-2 的前端交互,瀑布流渐进展开
| 任务 | 文件 | 说明 |
|---|---|---|
| 主页面骨架 | pages/DeepResearchPage.tsx |
新建,管理瀑布流状态 |
| Landing 组件 | components/deep-research/LandingView.tsx |
大搜索框 + 推荐预置词 |
| 配置面板 | components/deep-research/SetupPanel.tsx |
数据源 + 高级过滤 + 生成按钮 |
| HITL 确认 | components/deep-research/StrategyConfirm.tsx |
左右分栏 + 可编辑 textarea |
| API 函数 | api/index.ts |
新增 generateRequirement / executeTask |
| 路由注册 | pages/index.tsx |
新增 V2 路由 |
验收标准:
- Landing 输入 → Step 1 配置面板流畅过渡
- 点击"生成需求书" → Loading → Step 2 展开
- Step 2 左侧摘要卡片正确展示,右侧 textarea 可编辑
- 点击"启动 Deep Research" → 进入 Step 3
Phase 4: 前端 — 终端 + 结果展示(Day 4)
目标: 完成 Step 3-4,终端实时日志 + 结果报告/表格
| 任务 | 文件 | 说明 |
|---|---|---|
| 暗黑终端 | components/deep-research/AgentTerminal.tsx |
日志渲染 + auto-scroll + 状态灯 |
| 结果视图 | components/deep-research/ResultsView.tsx |
横幅 + 报告 + 文献表格 |
| 轮询 Hook | hooks/useDeepResearchTask.ts |
3s 轮询,running 时启用 |
| 终端样式 | CSS / Tailwind | 暗色主题 + 日志类型着色 |
验收标准:
- 终端日志按类型着色,新日志 auto-scroll
- 完成后终端折叠,结果区展开
- 综合报告 Markdown 渲染正确
- 文献清单表格展示(标题可点击跳转 PubMed)
- 全流程端到端联调通过
Phase 5: Word 导出 + 收尾(Day 5)
目标: Word 导出功能 + 全流程打磨 + 测试
| 任务 | 文件 | 说明 |
|---|---|---|
| Word 导出 API | controllers/researchController.ts |
GET /tasks/:id/export-word |
| Markdown 拼接 | services/wordExportService.ts |
报告 + 文献表格 → 完整 Markdown |
| Pandoc 调用 | 复用 Python 微服务 | Markdown → .docx |
| 前端导出按钮 | ResultsView.tsx |
下载 Word 文件 |
| 全流程测试 | 手动 + 脚本 | 端到端验证 |
| 文档更新 | 模块状态文档 | 更新 ASL 模块当前状态 |
验收标准:
- 点击"导出 Word" → 下载包含报告和文献清单的 .docx
- 全流程:Landing → 配置 → 扩写 → 确认 → 执行 → 日志 → 结果 → 导出
- 离开页面回来,能恢复查看正在执行/已完成的任务
- 错误情况处理(unifuncs 超时、API 报错、网络中断)
9. 验收标准总览
功能验收
- Landing 引导:用户输入粗略想法 → 进入配置
- 需求扩写:AI 自动扩写为结构化自然语言指令书
- HITL 核验:用户可直接编辑修改指令书
- 异步执行:pg-boss 队列,离开页面任务不中断
- 终端日志:每 3-5s 弹出一条结构化日志
- 综合报告:Markdown 渲染,内容来自 Unifuncs 输出
- 文献清单:表格展示,标题可跳转 PubMed
- Word 导出:一键导出报告 + 文献清单
非功能验收
- V1.x SSE 端点保留不动(向后兼容)
- 所有 API 经过 authenticate 中间件
- 日志使用
logger(非 console.log) - 无硬编码配置(API Key 来自环境变量)
- 数据库变更通过 Prisma migrate(非 db push)
10. 风险与应对
| 风险 | 概率 | 影响 | 应对措施 |
|---|---|---|---|
| Unifuncs 异步模式下 reasoning_content 不增量更新 | 低 | 终端日志为空 | 降级方案:只显示 progress.message |
| output_prompt XML 标签分割不可靠 | 中 | 报告和列表无法分离 | 降级方案:整体作为报告展示,文献从 PubMed 链接提取 |
| Unifuncs 长任务超时 | 低 | 任务失败 | MAX_POLLS=180(15分钟),超时标记 failed,用户可重试 |
| Pandoc Word 导出在 SAE 不可用 | 低 | 导出失败 | 降级方案:导出为 Markdown 文件 |
文档维护者: 开发团队
最后更新: 2026-02-22
文档状态: ✅ 方案确认,待开发启动