Files
AIclinicalresearch/backend/test-tool-c-day3.mjs
HaHafeng 98d862dbd4 feat(aia): Complete AIA V2.0 and sync all changes
AIA V2.0 Major Updates:
- Add StreamingService with OpenAI Compatible format (backend/common/streaming)
- Upgrade Chat component V2 with Ant Design X deep integration
- Implement 12 intelligent agents (5 phases: topic/design/review/data/writing)
- Create AgentHub with 100% prototype V11 restoration
- Create ChatWorkspace with fullscreen immersive experience
- Add ThinkingBlock for deep thinking display
- Add useAIStream Hook for stream handling
- Add ConversationList for conversation management

Backend (~1300 lines):
- common/streaming: OpenAI adapter and streaming service
- modules/aia: 12 agents config, conversation service, attachment service
- Unified API routes to /api/v1 (RVW, PKB, AIA modules)
- Update authentication and permission helpers

Frontend (~3500 lines):
- modules/aia: AgentHub + ChatWorkspace + AgentCard components
- shared/Chat: AIStreamChat, ThinkingBlock, useAIStream, useConversations
- Update all modules API endpoints to v1
- Modern design with theme colors (blue/yellow/teal/purple)

Documentation (~2500 lines):
- AIA module status and development guide
- Universal capabilities catalog (11 services)
- Quick reference card
- System overview updates
- All module documentation synchronization

Other Updates:
- DC Tool C: Python operations and frontend components
- IIT Manager: session memory and wechat service
- PKB/RVW/ASL: API route updates
- Docker configs and deployment scripts
- Database migrations and scripts
- Test files and documentation

Tested: AIA streaming verified, authentication working, core features functional
Status: AIA V2.0 completed (85%), all changes synchronized
2026-01-14 19:19:00 +08:00

395 lines
11 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Tool C Day 3 测试脚本
*
* 测试内容:
* 1. 10个Few-shot示例场景测试
* 2. AI自我修正机制测试重试
* 3. 端到端测试
*
* 前提:
* - 需要先创建一个Session使用Day 2的upload接口
* - 需要Python服务运行端口8000
* - 需要后端服务运行端口3000
*
* 执行方式node test-tool-c-day3.mjs
*/
import axios from 'axios';
import FormData from 'form-data';
import * as XLSX from 'xlsx';
const BASE_URL = 'http://localhost:3000';
const API_PREFIX = '/api/v1/dc/tool-c';
let testSessionId = null;
// ==================== 辅助函数 ====================
function printSection(title) {
console.log('\n' + '='.repeat(70));
console.log(` ${title}`);
console.log('='.repeat(70) + '\n');
}
function printSuccess(message) {
console.log('✅ ' + message);
}
function printError(message) {
console.log('❌ ' + message);
}
function printInfo(message) {
console.log(' ' + message);
}
// ==================== 准备测试Session ====================
async function createTestSession() {
printSection('准备创建测试Session');
try {
// 创建测试Excel数据
const testData = [
{ patient_id: 'P001', name: '张三', age: 25, gender: '男', diagnosis: '感冒', sbp: 120, dbp: 80, weight: 70, height: 175, BMI: '', creatinine: '>100', check_date: '2024-01-01' },
{ patient_id: 'P002', name: '李四', age: 65, gender: '女', diagnosis: '高血压', sbp: 150, dbp: 95, weight: 65, height: 160, BMI: '', creatinine: '<0.1', check_date: '2024-01-05' },
{ patient_id: 'P003', name: '王五', age: 45, gender: '男', diagnosis: '糖尿病', sbp: 135, dbp: 85, weight: 80, height: 170, BMI: '', creatinine: '85', check_date: '2024-01-03' },
{ patient_id: 'P004', name: '赵六', age: 70, gender: '女', diagnosis: '冠心病', sbp: 160, dbp: 100, weight: 60, height: 155, BMI: '', creatinine: '120', check_date: '2024-01-10' },
{ patient_id: 'P005', name: '钱七', age: 35, gender: '男', diagnosis: '胃炎', sbp: 110, dbp: 70, weight: 75, height: 180, BMI: '', creatinine: '-', check_date: '2024-01-08' },
{ patient_id: 'P003', name: '王五', age: 45, gender: '男', diagnosis: '糖尿病', sbp: 138, dbp: 88, weight: 82, height: 170, BMI: '', creatinine: '88', check_date: '2024-01-12' }, // 重复ID日期更新
];
// 生成Excel
const ws = XLSX.utils.json_to_sheet(testData);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const excelBuffer = XLSX.write(wb, { type: 'buffer', bookType: 'xlsx' });
// 上传创建Session
const form = new FormData();
form.append('file', excelBuffer, {
filename: 'test-medical-data-day3.xlsx',
contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const response = await axios.post(
`${BASE_URL}${API_PREFIX}/sessions/upload`,
form,
{ headers: form.getHeaders(), timeout: 10000 }
);
if (response.data.success) {
testSessionId = response.data.data.sessionId;
printSuccess(`Session创建成功: ${testSessionId}`);
printInfo(`数据: ${testData.length}行 x ${Object.keys(testData[0]).length}`);
return true;
} else {
printError('Session创建失败');
return false;
}
} catch (error) {
printError('Session创建异常: ' + error.message);
return false;
}
}
// ==================== AI测试函数 ====================
async function testAIGenerate(testName, userMessage, shouldSucceed = true) {
printSection(`测试: ${testName}`);
try {
printInfo(`用户需求: ${userMessage}`);
const response = await axios.post(
`${BASE_URL}${API_PREFIX}/ai/generate`,
{
sessionId: testSessionId,
message: userMessage
},
{ timeout: 30000 } // AI调用可能需要较长时间
);
if (response.data.success) {
printSuccess('AI生成代码成功');
console.log('\n生成的代码:');
console.log('```python');
console.log(response.data.data.code);
console.log('```\n');
console.log('解释:', response.data.data.explanation);
console.log('MessageID:', response.data.data.messageId);
return { success: true, data: response.data.data };
} else {
if (shouldSucceed) {
printError('AI生成失败: ' + response.data.error);
} else {
printInfo('预期失败: ' + response.data.error);
}
return { success: false, error: response.data.error };
}
} catch (error) {
printError('AI生成异常: ' + (error.response?.data?.error || error.message));
return { success: false, error: error.message };
}
}
async function testAIProcess(testName, userMessage) {
printSection(`测试(一步到位): ${testName}`);
try {
printInfo(`用户需求: ${userMessage}`);
const response = await axios.post(
`${BASE_URL}${API_PREFIX}/ai/process`,
{
sessionId: testSessionId,
message: userMessage,
maxRetries: 3
},
{ timeout: 60000 } // 带重试可能需要更长时间
);
if (response.data.success) {
printSuccess(`执行成功${response.data.data.retryCount > 0 ? `(重试${response.data.data.retryCount}次)` : ''}`);
console.log('\n生成的代码:');
console.log('```python');
console.log(response.data.data.code);
console.log('```\n');
console.log('解释:', response.data.data.explanation);
if (response.data.data.executeResult.success) {
printSuccess('代码执行成功');
console.log('数据预览前5行:');
console.log(JSON.stringify(response.data.data.executeResult.newDataPreview?.slice(0, 5), null, 2));
}
return { success: true, data: response.data.data };
} else {
printError('处理失败: ' + response.data.error);
return { success: false, error: response.data.error };
}
} catch (error) {
printError('处理异常: ' + (error.response?.data?.error || error.message));
return { success: false, error: error.message };
}
}
// ==================== 主测试函数 ====================
async function runAllTests() {
console.log('\n' + '🚀'.repeat(35));
console.log(' Tool C Day 3 测试 - AI代码生成');
console.log('🚀'.repeat(35));
const results = {};
try {
// 0. 准备测试Session
const sessionCreated = await createTestSession();
if (!sessionCreated) {
printError('测试Session创建失败无法继续');
return;
}
await new Promise(resolve => setTimeout(resolve, 2000));
// ==================== 10个Few-shot示例测试 ====================
// 测试1: 统一缺失值标记
let result = await testAIProcess(
'示例1: 统一缺失值标记',
'把所有代表缺失的符号(-、不详、NA、N/A统一替换为标准空值'
);
results['示例1-缺失值'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试2: 数值列清洗
result = await testAIProcess(
'示例2: 数值列清洗',
'把creatinine列里的非数字符号去掉<0.1按0.05处理,转为数值类型'
);
results['示例2-数值清洗'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试3: 分类变量编码
result = await testAIProcess(
'示例3: 分类变量编码',
'把gender列转为数字男=1女=0'
);
results['示例3-编码'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试4: 连续变量分箱
result = await testAIProcess(
'示例4: 连续变量分箱',
'把age列按18岁、60岁分为未成年、成年、老年三组'
);
results['示例4-分箱'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试5: BMI计算
result = await testAIProcess(
'示例5: BMI计算',
'根据weight和height计算BMI并标记BMI≥28为肥胖'
);
results['示例5-BMI'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试6: 条件筛选
result = await testAIProcess(
'示例6: 条件筛选',
'筛选出年龄≥60岁、且sbp≥140的患者'
);
results['示例6-筛选'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试7: 智能去重
result = await testAIProcess(
'示例7: 智能去重',
'按patient_id去重保留check_date最新的记录'
);
results['示例7-去重'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试8: 中位数填补(简化版,跳过多重插补)
result = await testAIProcess(
'示例8: 缺失值填补',
'用age列的中位数填补age列的缺失值'
);
results['示例8-填补'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试9: 统计汇总
result = await testAIProcess(
'示例9: 统计汇总',
'按diagnosis分组统计每个诊断的平均年龄和患者数量'
);
results['示例9-统计'] = result.success;
await new Promise(resolve => setTimeout(resolve, 3000));
// 测试10: 复杂计算
result = await testAIProcess(
'示例10: 复杂计算',
'根据sbp判断血压分类正常(<140)、高血压I级(140-159)、高血压II级(≥160)'
);
results['示例10-分类'] = result.success;
// ==================== 对话历史测试 ====================
printSection('测试: 获取对话历史');
try {
const historyResponse = await axios.get(
`${BASE_URL}${API_PREFIX}/ai/history/${testSessionId}?limit=5`
);
if (historyResponse.data.success) {
printSuccess(`获取历史成功: ${historyResponse.data.data.count}`);
results['对话历史'] = true;
} else {
printError('获取历史失败');
results['对话历史'] = false;
}
} catch (error) {
printError('获取历史异常: ' + error.message);
results['对话历史'] = false;
}
} catch (error) {
printError('测试过程中发生异常: ' + error.message);
console.error(error);
}
// 汇总结果
printSection('测试结果汇总');
let passed = 0;
let total = 0;
for (const [testName, result] of Object.entries(results)) {
total++;
if (result) {
passed++;
console.log(`${testName.padEnd(20)}: ✅ 通过`);
} else {
console.log(`${testName.padEnd(20)}: ❌ 失败`);
}
}
console.log('\n' + '-'.repeat(70));
console.log(`总计: ${passed}/${total} 通过 (${((passed/total)*100).toFixed(1)}%)`);
console.log('-'.repeat(70));
if (passed === total) {
console.log('\n🎉 所有测试通过Day 3 AI功能完成\n');
} else if (passed >= total * 0.7) {
console.log(`\n⚠️ 有 ${total - passed} 个测试失败但通过率≥70%,基本可用\n`);
} else {
console.log(`\n❌ 通过率过低,需要调试\n`);
}
}
// 执行测试
runAllTests()
.then(() => {
console.log('测试完成');
process.exit(0);
})
.catch((error) => {
console.error('测试失败:', error);
process.exit(1);
});