/** * 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 { 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 { 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 { 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); });