import fs from 'fs'; import path from 'path'; import app from './src/index'; const PORT = 3000; const BASE_URL = `http://localhost:${PORT}`; // 定义 12 个测试用例的数据 const testCases = [ { case_id: 'TC-01', sample_type: '明显漏洞', materials: { current_policy_text: '我们收集您的姓名和手机号。我们会申请设备权限。我们会共享数据给第三方。', prd_text: '包含人脸识别功能,需要获取身份证正反面,并在开户流程中使用相机进行活体检测。', permission_items: [ { name: '相机权限', purpose: '活体检测与身份证识别', trigger_page: '开户流程', required: true } ], sdk_items: [ { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符', '网络状态'], privacy_url: 'https://jiguang.cn/privacy' } ] }, peer_updates: ['同业A细化了权限说明'], regulatory_updates: ['监管强调最小必要原则'] }, { case_id: 'TC-02', sample_type: '仅缺权限用途', materials: { current_policy_text: '我们收集您的姓名和手机号。我们在开户时申请相机权限。接入了极光推送SDK用于消息推送。', prd_text: '开户流程中使用相机进行活体检测。', permission_items: [ { name: '相机权限', purpose: '活体检测与身份证识别', trigger_page: '开户流程', required: true } ], sdk_items: [ { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符'], privacy_url: 'https://jiguang.cn/privacy' } ] }, peer_updates: [], regulatory_updates: [] }, { case_id: 'TC-03', sample_type: 'SDK缺失', materials: { current_policy_text: '我们在开户时申请相机权限用于活体检测。我们绝不与第三方共享数据。', prd_text: '接入了极光推送SDK用于消息推送。', permission_items: [ { name: '相机权限', purpose: '活体检测', trigger_page: '开户流程', required: true } ], sdk_items: [ { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符'], privacy_url: 'https://jiguang.cn/privacy' } ] }, peer_updates: [], regulatory_updates: [] }, { case_id: 'TC-04', sample_type: '基本合规', materials: { current_policy_text: '我们在开户流程申请相机权限,用于身份证识别与活体检测。我们接入极光推送SDK(收集设备标识符)用于消息推送。', prd_text: '开户活体检测,极光消息推送。', permission_items: [ { name: '相机权限', purpose: '活体检测与身份证识别', trigger_page: '开户流程', required: true } ], sdk_items: [ { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符'], privacy_url: 'https://jiguang.cn/privacy' } ] }, peer_updates: [], regulatory_updates: [] }, { case_id: 'TC-05', sample_type: '缺 PRD', materials: { current_policy_text: '基本条款...', prd_text: '', // 故意置空触发 4001 permission_items: [], sdk_items: [] }, peer_updates: [], regulatory_updates: [] }, { case_id: 'TC-06', sample_type: '缺 权限清单', materials: { current_policy_text: '基本条款...', prd_text: '基本说明...', permission_items: null, // 故意置空触发 4001 sdk_items: [] }, peer_updates: [], regulatory_updates: [] }, { case_id: 'TC-07', sample_type: '缺 SDK清单', materials: { current_policy_text: '基本条款...', prd_text: '基本说明...', permission_items: [], sdk_items: null // 故意置空触发 4001 }, peer_updates: [], regulatory_updates: [] }, { case_id: 'TC-08', sample_type: '权限超范围收集', materials: { current_policy_text: '我们申请相机权限用于活体检测。我们还会申请通讯录权限用于推荐好友。', prd_text: '仅开户需要相机,无推荐好友功能。', permission_items: [ { name: '相机权限', purpose: '活体检测', trigger_page: '开户流程', required: true } ], sdk_items: [] }, peer_updates: [], regulatory_updates: ['严禁超范围收集无关权限'] }, { case_id: 'TC-09', sample_type: 'SDK超范围收集', materials: { current_policy_text: '极光SDK用于推送,仅收集设备标识。', prd_text: '极光SDK实际还偷偷收集了用户地理位置。', permission_items: [], sdk_items: [ { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符', '精准地理位置'], privacy_url: 'https://jiguang.cn/privacy' } ] }, peer_updates: [], regulatory_updates: [] }, { case_id: 'TC-10', sample_type: '缺少注销账号条款', materials: { current_policy_text: '欢迎使用。收集信息... 完毕。', prd_text: 'APP包含用户注册和注销功能。', permission_items: [], sdk_items: [] }, peer_updates: [], regulatory_updates: ['工信部要求必须提供便捷的账号注销方式并在协议中说明'] }, { case_id: 'TC-11', sample_type: '缺少未成年人保护条款', materials: { current_policy_text: '我们面向所有用户提供服务。', prd_text: 'APP可能有不满14周岁用户注册。', permission_items: [], sdk_items: [] }, peer_updates: [], regulatory_updates: ['必须设立专门的未成年人个人信息保护规则'] }, { case_id: 'TC-12', sample_type: '个性化推荐未提供关闭选项', materials: { current_policy_text: '我们会根据您的浏览记录进行千人千面的内容推荐。', prd_text: '首页包含基于用户画像的个性化推荐信息流。', permission_items: [], sdk_items: [] }, peer_updates: [], regulatory_updates: ['算法推荐必须提供便捷的关闭选项'] } ]; // 辅助请求函数 async function post(url: string, data: any) { const start = Date.now(); const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const latency = Date.now() - start; const json = await res.json(); if (!res.ok) { throw { status: res.status, latency, error: json }; } return { data: json, latency }; } // 主执行循环 async function runRegression() { console.log('Starting Agent Services for Regression Test...'); const server = app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); }); const report: any[] = []; const logStream = fs.createWriteStream(path.join(__dirname, 'regression_report.log'), { flags: 'w' }); for (const tc of testCases) { console.log(`\n====================================`); console.log(`Executing ${tc.case_id}: ${tc.sample_type}`); logStream.write(`\n====================================\n`); logStream.write(`Executing ${tc.case_id}: ${tc.sample_type}\n`); const result = { case_id: tc.case_id, sample_type: tc.sample_type, e2e_latency_ms: 0, each_agent_latency_ms: { agent1: 0, agent2: 0, agent3: 0, agent4: 0 }, actual_recommendation: '', error: null as any }; const startE2E = Date.now(); try { // 构建基础上下文 const baseContext = { case_id: tc.case_id, customer_id: 'CUST-TEST', customer_name: '测试银行', app_name: '测试银行APP', business_line: 'retail', language: 'zh-CN', materials: tc.materials, external_context: { peer_updates: tc.peer_updates, regulatory_updates: tc.regulatory_updates }, metadata: { submitted_at: new Date().toISOString(), source: 'regression-test' } }; // Agent 1: 同业/监管变化摘要 console.log(` -> [Agent 1] Peer Reg Summary...`); const a1Res = await post(`${BASE_URL}/api/agents/peer-reg-summary`, { peer_updates: tc.peer_updates, regulatory_updates: tc.regulatory_updates, business_line: 'retail', app_name: '测试银行APP' }); result.each_agent_latency_ms.agent1 = a1Res.latency; // Agent 2: 协议重构 console.log(` -> [Agent 2] Policy Rewrite...`); const a2Res = await post(`${BASE_URL}/api/agents/policy-rewrite`, { case_context: baseContext, peer_reg_summary: a1Res.data }); result.each_agent_latency_ms.agent2 = a2Res.latency; // Agent 3: 合规校验 console.log(` -> [Agent 3] Compliance Check...`); const a3Res = await post(`${BASE_URL}/api/agents/compliance-check`, { case_context: baseContext, gap_analysis: a2Res.data }); result.each_agent_latency_ms.agent3 = a3Res.latency; // Agent 4: 法务审核包生成 console.log(` -> [Agent 4] Legal Pack...`); const a4Res = await post(`${BASE_URL}/api/agents/legal-pack`, { case_context: baseContext, peer_reg_summary: a1Res.data, gap_analysis: a2Res.data, compliance_check: a3Res.data }); result.each_agent_latency_ms.agent4 = a4Res.latency; result.actual_recommendation = a4Res.data.review_recommendation || 'unknown'; console.log(` => Recommendation: ${result.actual_recommendation}`); } catch (err: any) { console.log(` => Error caught:`, err.status || err.message || err); result.error = err.error || err.message || err; if (err.error && (err.error.error_code === 'need_more_material' || (err.error.error && err.error.error.code === 4001))) { result.actual_recommendation = 'need_more_material (4001)'; } else if (err.error && err.error.error && err.error.error.code === 5001) { result.actual_recommendation = 'LLM_ERROR (5001)'; } else { result.actual_recommendation = 'ERROR'; } } result.e2e_latency_ms = Date.now() - startE2E; // 写入 Log logStream.write(JSON.stringify(result, null, 2) + '\n'); report.push(result); } // 输出总报告 console.log(`\n\n====== Regression Test Report ======`); console.table(report.map(r => ({ CaseID: r.case_id, Type: r.sample_type, E2E_ms: r.e2e_latency_ms, A1_ms: r.each_agent_latency_ms.agent1, A2_ms: r.each_agent_latency_ms.agent2, A3_ms: r.each_agent_latency_ms.agent3, A4_ms: r.each_agent_latency_ms.agent4, Recommendation: r.actual_recommendation }))); logStream.write(`\n\n====== Summary ======\n`); logStream.write(JSON.stringify(report, null, 2)); logStream.end(); console.log(`\nRegression test complete. Report saved to regression_report.log`); server.close(); process.exit(0); } runRegression().catch(console.error);