feat(asl): Add Deep Research V2.0 development plan and Unifuncs API site coverage testing

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>
This commit is contained in:
2026-02-22 22:44:41 +08:00
parent 3446909ff7
commit b06daecacd
12 changed files with 2662 additions and 27 deletions

View File

@@ -0,0 +1,215 @@
/**
* Unifuncs API — ClinicalTrials.gov 专项测试
*
* ClinicalTrials.gov 在上一轮测试中超时,本脚本用多种策略针对性测试
*
* 运行方式:
* cd backend
* npx tsx scripts/test-unifuncs-clinicaltrials.ts
*/
const API_KEY = 'sk-2fNwqUH73elGq0aDKJEM4ReqP7Ry0iqHo4OXyidDe2WpQ9XQ';
const BASE_URL = 'https://api.unifuncs.com/deepsearch/v1';
const POLL_INTERVAL = 10000;
const MAX_WAIT = 900000; // 15 分钟(上次 10 分钟超时,放宽到 15 分钟)
interface TestStrategy {
id: string;
name: string;
query: string;
domainScope: string[];
maxDepth: number;
introduction: string;
}
const STRATEGIES: TestStrategy[] = [
{
id: 'A',
name: '英文查询 + 限定 clinicaltrials.gov',
query: 'Statin therapy for cardiovascular disease prevention, randomized controlled trials, recent 5 years',
domainScope: ['https://clinicaltrials.gov/'],
maxDepth: 10,
introduction: 'You are a clinical research expert. Search ClinicalTrials.gov for relevant clinical trials. Return trial NCT numbers, titles, status, and links.',
},
{
id: 'B',
name: '英文查询 + 不限域名(观察是否自然覆盖 clinicaltrials.gov',
query: 'Find clinical trials on statins for cardiovascular disease prevention registered on ClinicalTrials.gov, list NCT numbers and trial details',
domainScope: [],
maxDepth: 10,
introduction: 'You are a clinical research expert. Focus on finding registered clinical trials from ClinicalTrials.gov. Return NCT numbers, trial titles, status, and direct links to clinicaltrials.gov.',
},
{
id: 'C',
name: '中文查询 + 限定 clinicaltrials.gov + 高深度',
query: '他汀类药物预防心血管疾病的临床试验,请在 ClinicalTrials.gov 上查找注册的 RCT返回 NCT 编号和试验详情',
domainScope: ['https://clinicaltrials.gov/'],
maxDepth: 15,
introduction: '你是临床试验检索专家。请在 ClinicalTrials.gov 上搜索相关临床试验,返回 NCT 编号、试验标题、状态、链接。',
},
{
id: 'D',
name: '简短英文查询 + 限定 clinicaltrials.gov + 低深度',
query: 'statin cardiovascular RCT',
domainScope: ['https://clinicaltrials.gov/'],
maxDepth: 5,
introduction: 'Search ClinicalTrials.gov for statin cardiovascular trials. Return NCT IDs and links.',
},
];
async function createTask(strategy: TestStrategy): Promise<{ taskId: string } | { error: string }> {
const payload: any = {
model: 's2',
messages: [{ role: 'user', content: strategy.query }],
introduction: strategy.introduction,
max_depth: strategy.maxDepth,
reference_style: 'link',
generate_summary: true,
};
if (strategy.domainScope.length > 0) {
payload.domain_scope = strategy.domainScope;
}
try {
const res = await fetch(`${BASE_URL}/create_task`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
const json = await res.json() as any;
if (json.code === 0 && json.data?.task_id) {
return { taskId: json.data.task_id };
}
return { error: `API error: ${json.message || JSON.stringify(json)}` };
} catch (err: any) {
return { error: `Request failed: ${err.message}` };
}
}
async function queryTask(taskId: string): Promise<any> {
const params = new URLSearchParams({ task_id: taskId });
const res = await fetch(`${BASE_URL}/query_task?${params.toString()}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` },
});
return res.json();
}
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
function extractLinks(content: string): { ctLinks: string[]; nctIds: string[]; otherLinks: string[] } {
const urlPattern = /https?:\/\/[^\s)\]"'>]+/gi;
const allUrls = [...new Set(content.match(urlPattern) || [])];
const ctLinks = allUrls.filter(u => u.includes('clinicaltrials.gov'));
const otherLinks = allUrls.filter(u => !u.includes('clinicaltrials.gov'));
const nctPattern = /NCT\d{6,}/gi;
const nctIds = [...new Set(content.match(nctPattern) || [])];
return { ctLinks, nctIds, otherLinks };
}
async function testStrategy(strategy: TestStrategy): Promise<void> {
const startTime = Date.now();
console.log(`\n${'━'.repeat(80)}`);
console.log(`策略 ${strategy.id}: ${strategy.name}`);
console.log(`查询: "${strategy.query}"`);
console.log(`domain_scope: ${strategy.domainScope.length > 0 ? strategy.domainScope.join(', ') : '不限'}`);
console.log(`max_depth: ${strategy.maxDepth}`);
console.log(`${'━'.repeat(80)}`);
const createResult = await createTask(strategy);
if ('error' in createResult) {
console.log(` ❌ 创建失败: ${createResult.error}`);
return;
}
console.log(` → task_id: ${createResult.taskId}`);
const deadline = Date.now() + MAX_WAIT;
let lastProgress = '';
while (Date.now() < deadline) {
await sleep(POLL_INTERVAL);
try {
const json = await queryTask(createResult.taskId) as any;
const data = json.data;
if (!data) continue;
const progress = data.progress ? `${data.progress.current}/${data.progress.total}` : '?';
const stats = data.statistics || {};
const statusLine = `${data.status} (${progress}) 搜索${stats.search_count || 0} 阅读${stats.read_count || 0} 迭代${stats.iterations || 0}`;
if (statusLine !== lastProgress) {
const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);
console.log(` [${elapsed}s] ${statusLine}`);
lastProgress = statusLine;
}
if (data.status === 'completed') {
const content = data.result?.content || '';
const reasoning = data.result?.reasoning_content || '';
const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);
const { ctLinks, nctIds, otherLinks } = extractLinks(content);
const { ctLinks: rCtLinks, nctIds: rNctIds } = extractLinks(reasoning);
console.log(`\n ✅ 完成 (${elapsed}s)`);
console.log(` ├─ 内容长度: ${content.length} 字符`);
console.log(` ├─ 推理长度: ${reasoning.length} 字符`);
console.log(` ├─ 搜索/阅读: ${stats.search_count || 0}/${stats.read_count || 0}`);
console.log(` ├─ ClinicalTrials 链接 (content): ${ctLinks.length}`);
console.log(` ├─ ClinicalTrials 链接 (reasoning): ${rCtLinks.length}`);
console.log(` ├─ NCT 编号 (content): ${nctIds.length} 个 → ${nctIds.slice(0, 10).join(', ')}${nctIds.length > 10 ? '...' : ''}`);
console.log(` ├─ NCT 编号 (reasoning): ${rNctIds.length}`);
console.log(` ├─ 其他链接: ${otherLinks.length}`);
console.log(` └─ Token: ${JSON.stringify(stats.token_usage || {})}`);
if (ctLinks.length > 0) {
console.log(`\n 📎 ClinicalTrials.gov 链接示例:`);
ctLinks.slice(0, 8).forEach((link, i) => console.log(` ${i + 1}. ${link}`));
}
if (ctLinks.length === 0 && nctIds.length === 0) {
console.log(`\n ⚠️ 内容预览 (前 500 字):`);
console.log(` ${content.slice(0, 500).replace(/\n/g, '\n ')}`);
}
return;
}
if (data.status === 'failed') {
console.log(` ❌ 失败: ${data.result?.content || '未知错误'}`);
return;
}
} catch (err: any) {
// 轮询网络错误,继续重试
}
}
console.log(` ⏰ 超时 (${MAX_WAIT / 1000}s)`);
}
async function main() {
console.log('╔════════════════════════════════════════════════════════════╗');
console.log('║ ClinicalTrials.gov 专项测试 — 4 种策略并行 ║');
console.log('╚════════════════════════════════════════════════════════════╝');
console.log(`超时: ${MAX_WAIT / 1000}s | 轮询: ${POLL_INTERVAL / 1000}s`);
// 并行执行所有策略
await Promise.all(STRATEGIES.map(s => testStrategy(s)));
console.log(`\n${'═'.repeat(80)}`);
console.log('所有策略测试完成!');
console.log(`${'═'.repeat(80)}`);
}
main().catch(err => {
console.error('脚本执行失败:', err);
process.exit(1);
});