refactor(asl): ASL frontend architecture refactoring with left navigation
- feat: Create ASLLayout component with 7-module left navigation - feat: Implement Title Screening Settings page with optimized PICOS layout - feat: Add placeholder pages for Workbench and Results - fix: Fix nested routing structure for React Router v6 - fix: Resolve Spin component warning in MainLayout - fix: Add QueryClientProvider to App.tsx - style: Optimize PICOS form layout (P+I left, C+O+S right) - style: Align Inclusion/Exclusion criteria side-by-side - docs: Add architecture refactoring and routing fix reports Ref: Week 2 Frontend Development Scope: ASL module MVP - Title Abstract Screening
This commit is contained in:
359
backend/src/scripts/test-closeai.ts
Normal file
359
backend/src/scripts/test-closeai.ts
Normal file
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* CloseAI集成测试脚本
|
||||
*
|
||||
* 测试通过CloseAI代理访问GPT-5和Claude-4.5模型
|
||||
*
|
||||
* 运行方式:
|
||||
* ```bash
|
||||
* cd backend
|
||||
* npx tsx src/scripts/test-closeai.ts
|
||||
* ```
|
||||
*
|
||||
* 环境变量要求:
|
||||
* - CLOSEAI_API_KEY: CloseAI API密钥
|
||||
* - CLOSEAI_OPENAI_BASE_URL: OpenAI端点
|
||||
* - CLOSEAI_CLAUDE_BASE_URL: Claude端点
|
||||
*
|
||||
* 参考文档:docs/02-通用能力层/01-LLM大模型网关/03-CloseAI集成指南.md
|
||||
*/
|
||||
|
||||
import { LLMFactory } from '../common/llm/adapters/LLMFactory.js';
|
||||
import { config } from '../config/env.js';
|
||||
|
||||
/**
|
||||
* 测试配置验证
|
||||
*/
|
||||
function validateConfig() {
|
||||
console.log('🔍 验证环境配置...\n');
|
||||
|
||||
const checks = [
|
||||
{
|
||||
name: 'CLOSEAI_API_KEY',
|
||||
value: config.closeaiApiKey,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'CLOSEAI_OPENAI_BASE_URL',
|
||||
value: config.closeaiOpenaiBaseUrl,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'CLOSEAI_CLAUDE_BASE_URL',
|
||||
value: config.closeaiClaudeBaseUrl,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
let allValid = true;
|
||||
|
||||
for (const check of checks) {
|
||||
const status = check.value ? '✅' : '❌';
|
||||
console.log(`${status} ${check.name}: ${check.value ? '已配置' : '未配置'}`);
|
||||
|
||||
if (check.required && !check.value) {
|
||||
allValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
if (!allValid) {
|
||||
throw new Error('环境配置不完整,请检查 .env 文件');
|
||||
}
|
||||
|
||||
console.log('✅ 环境配置验证通过\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试GPT-5-Pro
|
||||
*/
|
||||
async function testGPT5() {
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('1️⃣ 测试 GPT-5-Pro');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
try {
|
||||
const gpt5 = LLMFactory.getAdapter('gpt-5');
|
||||
|
||||
console.log('📤 发送测试请求...');
|
||||
console.log('提示词: "你好,请用一句话介绍你自己。"\n');
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
const response = await gpt5.chat([
|
||||
{
|
||||
role: 'user',
|
||||
content: '你好,请用一句话介绍你自己。',
|
||||
},
|
||||
]);
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
console.log('📥 收到响应:\n');
|
||||
console.log(`模型: ${response.model}`);
|
||||
console.log(`内容: ${response.content}`);
|
||||
console.log(`耗时: ${duration}ms`);
|
||||
|
||||
if (response.usage) {
|
||||
console.log(`Token使用: ${response.usage.totalTokens} (输入: ${response.usage.promptTokens}, 输出: ${response.usage.completionTokens})`);
|
||||
}
|
||||
|
||||
console.log('\n✅ GPT-5测试通过\n');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('\n❌ GPT-5测试失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试Claude-4.5-Sonnet
|
||||
*/
|
||||
async function testClaude() {
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('2️⃣ 测试 Claude-4.5-Sonnet');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
try {
|
||||
const claude = LLMFactory.getAdapter('claude-4.5');
|
||||
|
||||
console.log('📤 发送测试请求...');
|
||||
console.log('提示词: "你好,请用一句话介绍你自己。"\n');
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
const response = await claude.chat([
|
||||
{
|
||||
role: 'user',
|
||||
content: '你好,请用一句话介绍你自己。',
|
||||
},
|
||||
]);
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
console.log('📥 收到响应:\n');
|
||||
console.log(`模型: ${response.model}`);
|
||||
console.log(`内容: ${response.content}`);
|
||||
console.log(`耗时: ${duration}ms`);
|
||||
|
||||
if (response.usage) {
|
||||
console.log(`Token使用: ${response.usage.totalTokens} (输入: ${response.usage.promptTokens}, 输出: ${response.usage.completionTokens})`);
|
||||
}
|
||||
|
||||
console.log('\n✅ Claude测试通过\n');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('\n❌ Claude测试失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试文献筛选场景(实际应用)
|
||||
*/
|
||||
async function testLiteratureScreening() {
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('3️⃣ 测试文献筛选场景(双模型对比)');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
const testLiterature = {
|
||||
title: 'Deep learning in medical imaging: A systematic review',
|
||||
abstract: 'Background: Deep learning has shown remarkable performance in various medical imaging tasks. Methods: We systematically reviewed 150 studies on deep learning applications in radiology, pathology, and ophthalmology. Results: Deep learning models achieved high accuracy (>90%) in most tasks. Conclusion: Deep learning is a promising tool for medical image analysis.',
|
||||
};
|
||||
|
||||
const picoPrompt = `
|
||||
请根据以下PICO标准,判断这篇文献是否应该纳入系统评价:
|
||||
|
||||
**PICO标准:**
|
||||
- Population: 成年患者
|
||||
- Intervention: 深度学习模型
|
||||
- Comparison: 传统机器学习方法
|
||||
- Outcome: 诊断准确率
|
||||
|
||||
**文献信息:**
|
||||
标题:${testLiterature.title}
|
||||
摘要:${testLiterature.abstract}
|
||||
|
||||
请输出JSON格式:
|
||||
{
|
||||
"decision": "include/exclude/uncertain",
|
||||
"reason": "判断理由",
|
||||
"confidence": 0.0-1.0
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
console.log('📤 使用DeepSeek和GPT-5进行双模型对比筛选...\n');
|
||||
|
||||
// 并行调用两个模型
|
||||
const [deepseekAdapter, gpt5Adapter] = [
|
||||
LLMFactory.getAdapter('deepseek-v3'),
|
||||
LLMFactory.getAdapter('gpt-5'),
|
||||
];
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
const [deepseekResponse, gpt5Response] = await Promise.all([
|
||||
deepseekAdapter.chat([{ role: 'user', content: picoPrompt }]),
|
||||
gpt5Adapter.chat([{ role: 'user', content: picoPrompt }]),
|
||||
]);
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
console.log('📥 DeepSeek响应:');
|
||||
console.log(deepseekResponse.content);
|
||||
console.log('');
|
||||
|
||||
console.log('📥 GPT-5响应:');
|
||||
console.log(gpt5Response.content);
|
||||
console.log('');
|
||||
|
||||
console.log(`⏱️ 总耗时: ${duration}ms(并行)`);
|
||||
console.log(`💰 总Token: ${(deepseekResponse.usage?.totalTokens || 0) + (gpt5Response.usage?.totalTokens || 0)}`);
|
||||
|
||||
// 尝试解析JSON结果(简单验证)
|
||||
try {
|
||||
const deepseekDecision = JSON.parse(deepseekResponse.content);
|
||||
const gpt5Decision = JSON.parse(gpt5Response.content);
|
||||
|
||||
console.log('\n✅ 双模型筛选结果:');
|
||||
console.log(`DeepSeek决策: ${deepseekDecision.decision} (置信度: ${deepseekDecision.confidence})`);
|
||||
console.log(`GPT-5决策: ${gpt5Decision.decision} (置信度: ${gpt5Decision.confidence})`);
|
||||
|
||||
if (deepseekDecision.decision === gpt5Decision.decision) {
|
||||
console.log('✅ 两个模型一致,共识度高');
|
||||
} else {
|
||||
console.log('⚠️ 两个模型不一致,建议人工复核或启用第三方仲裁(Claude)');
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.log('⚠️ JSON解析失败(测试环境,实际应用需要优化提示词)');
|
||||
}
|
||||
|
||||
console.log('\n✅ 文献筛选场景测试通过\n');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('\n❌ 文献筛选场景测试失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试流式调用(可选)
|
||||
*/
|
||||
async function testStreamMode() {
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('4️⃣ 测试流式调用(GPT-5)');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
try {
|
||||
const gpt5 = LLMFactory.getAdapter('gpt-5');
|
||||
|
||||
console.log('📤 发送流式请求...');
|
||||
console.log('提示词: "请写一首关于人工智能的短诗(4行)"\n');
|
||||
console.log('📥 流式响应:\n');
|
||||
|
||||
const startTime = Date.now();
|
||||
let fullContent = '';
|
||||
let chunkCount = 0;
|
||||
|
||||
for await (const chunk of gpt5.chatStream([
|
||||
{
|
||||
role: 'user',
|
||||
content: '请写一首关于人工智能的短诗(4行)',
|
||||
},
|
||||
])) {
|
||||
if (chunk.content) {
|
||||
process.stdout.write(chunk.content);
|
||||
fullContent += chunk.content;
|
||||
chunkCount++;
|
||||
}
|
||||
|
||||
if (chunk.done) {
|
||||
const duration = Date.now() - startTime;
|
||||
console.log('\n');
|
||||
console.log(`\n⏱️ 耗时: ${duration}ms`);
|
||||
console.log(`📦 Chunk数: ${chunkCount}`);
|
||||
console.log(`📝 总字符数: ${fullContent.length}`);
|
||||
|
||||
if (chunk.usage) {
|
||||
console.log(`💰 Token使用: ${chunk.usage.totalTokens}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ 流式调用测试通过\n');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('\n❌ 流式调用测试失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主测试函数
|
||||
*/
|
||||
async function main() {
|
||||
console.log('╔═══════════════════════════════════════════════════╗');
|
||||
console.log('║ 🧪 CloseAI集成测试 ║');
|
||||
console.log('║ 测试GPT-5和Claude-4.5通过CloseAI代理访问 ║');
|
||||
console.log('╚═══════════════════════════════════════════════════╝\n');
|
||||
|
||||
try {
|
||||
// 验证配置
|
||||
validateConfig();
|
||||
|
||||
// 测试结果
|
||||
const results = {
|
||||
gpt5: false,
|
||||
claude: false,
|
||||
literatureScreening: false,
|
||||
stream: false,
|
||||
};
|
||||
|
||||
// 1. 测试GPT-5
|
||||
results.gpt5 = await testGPT5();
|
||||
|
||||
// 2. 测试Claude-4.5
|
||||
results.claude = await testClaude();
|
||||
|
||||
// 3. 测试文献筛选场景
|
||||
results.literatureScreening = await testLiteratureScreening();
|
||||
|
||||
// 4. 测试流式调用(可选)
|
||||
results.stream = await testStreamMode();
|
||||
|
||||
// 总结
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('📊 测试总结');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
const allPassed = Object.values(results).every((r) => r === true);
|
||||
|
||||
console.log(`GPT-5测试: ${results.gpt5 ? '✅ 通过' : '❌ 失败'}`);
|
||||
console.log(`Claude测试: ${results.claude ? '✅ 通过' : '❌ 失败'}`);
|
||||
console.log(`文献筛选场景: ${results.literatureScreening ? '✅ 通过' : '❌ 失败'}`);
|
||||
console.log(`流式调用测试: ${results.stream ? '✅ 通过' : '❌ 失败'}`);
|
||||
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
if (allPassed) {
|
||||
console.log('🎉 所有测试通过!CloseAI集成成功!');
|
||||
console.log('\n✅ 可以在ASL模块中使用GPT-5和Claude-4.5进行双模型对比筛选');
|
||||
console.log('✅ 支持三模型共识仲裁(DeepSeek + GPT-5 + Claude)');
|
||||
console.log('✅ 支持流式调用,适用于实时响应场景\n');
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.error('⚠️ 部分测试失败,请检查配置和网络连接\n');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 测试执行失败:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
main();
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user