feat(iit): Phase 1.5 AI对话集成REDCap真实数据完成

- feat: ChatService集成DeepSeek-V3实现AI对话(390行)
- feat: SessionMemory实现上下文记忆(最近3轮对话,170行)
- feat: 意图识别支持REDCap数据查询(关键词匹配)
- feat: REDCap数据注入LLM(queryRedcapRecord, countRedcapRecords, getProjectInfo)
- feat: 解决LLM幻觉问题(基于真实数据回答,明确system prompt)
- feat: 即时反馈(正在查询...提示)
- test: REDCap查询测试通过(test0102项目,10条记录,ID 7患者详情)
- docs: 创建Phase1.5开发完成记录(313行)
- docs: 更新Phase1.5开发计划(标记完成)
- docs: 更新MVP开发任务清单(Phase 1.5完成)
- docs: 更新模块当前状态(60%完成度)
- docs: 更新系统总体设计文档(v2.6)
- chore: 删除测试脚本(test-redcap-query-for-ai.ts, check-env-config.ts)
- chore: 移除REDCap测试环境变量(REDCAP_TEST_*)

技术亮点:
- AI基于REDCap真实数据对话,不编造信息
- 从数据库读取项目配置,不使用环境变量
- 企业微信端测试通过,用户体验良好

测试通过:
-  查询项目记录总数(10条)
-  查询特定患者详情(ID 7)
-  项目信息查询
-  上下文记忆(3轮对话)
-  即时反馈提示

影响范围:IIT Manager Agent模块
This commit is contained in:
2026-01-03 22:48:10 +08:00
parent 4794640f5d
commit b47079b387
158 changed files with 1273 additions and 110 deletions

View File

@@ -0,0 +1,249 @@
/**
* REDCap查询测试脚本从数据库读取配置
*
* 目的测试基础的REDCap查询功能验证数据格式
* 数据来源:从数据库 iit_schema.projects 表读取项目配置
*
* 运行方式:
* ```bash
* cd backend
* npm run tsx src/modules/iit-manager/test-redcap-query-from-db.ts
* ```
*/
import { RedcapAdapter } from './adapters/RedcapAdapter.js';
import { PrismaClient } from '@prisma/client';
import { logger } from '../../common/logging/index.js';
import dotenv from 'dotenv';
dotenv.config();
const prisma = new PrismaClient();
async function main() {
console.log('='.repeat(70));
console.log('🧪 REDCap查询测试为AI对话准备');
console.log('='.repeat(70));
console.log();
try {
// =============================================
// 1. 从数据库读取test0102项目配置
// =============================================
console.log('📋 从数据库读取项目配置...');
const project = await prisma.iitProject.findFirst({
where: {
OR: [
{ redcapProjectId: '16' },
{ name: { contains: 'test0102' } }
],
status: 'active'
}
});
if (!project) {
console.log('❌ 未找到test0102项目PID: 16');
console.log();
console.log('💡 可能的原因:');
console.log(' 1. 项目未在数据库中');
console.log(' 2. 项目状态不是active');
console.log(' 3. redcapProjectId不是"16"');
console.log();
console.log('📝 解决方法:');
console.log(' 请检查数据库 iit_schema.projects 表');
console.log(' 或联系管理员添加test0102项目配置');
console.log();
process.exit(1);
}
console.log('✅ 项目配置读取成功');
console.log();
console.log('📋 项目信息:');
console.log(` 数据库ID: ${project.id}`);
console.log(` 项目名称: ${project.name}`);
console.log(` REDCap项目ID: ${project.redcapProjectId}`);
console.log(` REDCap URL: ${project.redcapUrl}`);
console.log(` API Token: ${project.redcapApiToken.substring(0, 8)}***`);
console.log(` 状态: ${project.status}`);
console.log();
// =============================================
// 2. 创建RedcapAdapter实例
// =============================================
console.log('📦 创建RedcapAdapter实例...');
const redcap = new RedcapAdapter(
project.redcapUrl,
project.redcapApiToken
);
console.log('✅ Adapter创建成功\n');
// =============================================
// 3. 测试连接
// =============================================
console.log('🔌 测试API连接...');
try {
const isConnected = await redcap.testConnection();
if (isConnected) {
console.log('✅ API连接成功\n');
} else {
console.log('❌ API连接失败');
console.log(' 请检查:');
console.log(' 1. REDCap服务是否启动');
console.log(` 2. URL是否正确: ${project.redcapUrl}`);
console.log(' 3. API Token是否有效');
console.log();
process.exit(1);
}
} catch (error: any) {
console.error('❌ 连接测试失败:', error.message);
process.exit(1);
}
// =============================================
// 4. 查询所有记录获取记录ID列表
// =============================================
console.log('📊 测试1: 查询所有记录获取记录ID列表');
console.log('-'.repeat(70));
const allRecords = await redcap.exportRecords({
fields: ['record_id']
});
// 提取唯一记录ID
const uniqueRecordIds = Array.from(
new Set(allRecords.map((r) => r.record_id || r.record))
);
console.log(`✅ 查询成功`);
console.log(` 总记录数: ${uniqueRecordIds.length}`);
console.log(` 记录ID列表: ${uniqueRecordIds.join(', ')}`);
console.log();
if (uniqueRecordIds.length === 0) {
console.log('⚠️ 项目中没有记录请先在REDCap中创建测试数据');
console.log(' 建议在test0102项目中添加几条测试记录');
console.log();
process.exit(0);
}
// =============================================
// 5. 查询特定记录的详细信息
// =============================================
const testRecordId = uniqueRecordIds[0]; // 使用第一条记录做测试
console.log(`📋 测试2: 查询特定记录的详细信息 (ID: ${testRecordId})`);
console.log('-'.repeat(70));
const specificRecord = await redcap.exportRecords({
records: [testRecordId]
});
if (specificRecord.length > 0) {
console.log('✅ 查询成功');
console.log(` 记录数: ${specificRecord.length}`);
console.log();
console.log('📄 第一条记录的数据结构:');
const firstRecord = specificRecord[0];
const fields = Object.keys(firstRecord);
console.log(`${fields.length} 个字段:`);
fields.slice(0, 10).forEach((field, index) => {
const value = firstRecord[field];
const displayValue = value === '' ? '(空值)' : value;
console.log(` ${(index + 1).toString().padStart(2, ' ')}. ${field.padEnd(30)} = ${displayValue}`);
});
if (fields.length > 10) {
console.log(` ... (还有 ${fields.length - 10} 个字段)`);
}
console.log();
} else {
console.log('❌ 未找到记录');
console.log();
}
// =============================================
// 6. 模拟AI对话场景的查询
// =============================================
console.log('🤖 测试3: 模拟AI对话场景');
console.log('-'.repeat(70));
console.log();
// 场景1: 用户问"有多少条记录?"
console.log('【场景1】用户问"我们系统中已经有几条记录了?"');
const countResult = {
projectName: project.name,
totalRecords: uniqueRecordIds.length,
recordIds: uniqueRecordIds
};
console.log('💾 AI需要的数据:');
console.log(JSON.stringify(countResult, null, 2));
console.log();
console.log('🤖 AI应该回答:');
console.log(` "您好根据REDCap系统记录当前项目${project.name}已有 **${uniqueRecordIds.length}条** 患者数据记录。"`);
console.log();
// 场景2: 用户问特定记录的信息
const demoRecordId = uniqueRecordIds.includes('7') ? '7' : uniqueRecordIds[0];
console.log(`【场景2】用户问"了解Redcap中记录为ID ${demoRecordId}的信息"`);
const recordDetailResult = await redcap.exportRecords({
records: [demoRecordId]
});
console.log('💾 AI需要的数据:');
console.log(JSON.stringify({
projectName: project.name,
recordId: demoRecordId,
data: recordDetailResult[0]
}, null, 2));
console.log();
// 场景3: 用户问"项目名称"
console.log('【场景3】用户问"咱们当前的项目名称是什么?"');
console.log('💾 AI需要的数据:');
console.log(JSON.stringify({
projectName: project.name,
redcapProjectId: project.redcapProjectId,
recordCount: uniqueRecordIds.length,
lastSync: project.lastSyncAt
}, null, 2));
console.log();
console.log('🤖 AI应该回答:');
console.log(` "您好!当前项目名称为 **${project.name}**。如需查看完整方案或项目详情请登录REDCap系统或查阅项目文档。"`);
console.log();
// =============================================
// 测试总结
// =============================================
console.log('='.repeat(70));
console.log('✅ 所有测试完成REDCap查询功能正常');
console.log('='.repeat(70));
console.log();
console.log('📝 测试总结:');
console.log(` 1. ✅ 项目配置从数据库读取成功`);
console.log(` 2. ✅ API连接正常`);
console.log(` 3. ✅ 可以查询所有记录 (${uniqueRecordIds.length} 条)`);
console.log(` 4. ✅ 可以查询特定记录`);
console.log(` 5. ✅ 数据格式符合AI对话需求`);
console.log();
console.log('🚀 下一步:');
console.log(' 将查询功能集成到ChatService让AI能够基于真实数据回答问题');
console.log();
} catch (error: any) {
console.error('❌ 测试失败:', error.message);
console.error(' 错误详情:', error);
process.exit(1);
} finally {
await prisma.$disconnect();
}
process.exit(0);
}
// 执行测试
main().catch((error) => {
console.error('💥 测试脚本执行失败:', error);
process.exit(1);
});