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>
216 lines
8.4 KiB
TypeScript
216 lines
8.4 KiB
TypeScript
/**
|
||
* 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);
|
||
});
|