feat(aihr): harden finance RAG retrieval and evidence gates

This commit is contained in:
key
2026-08-03 22:34:18 +08:00
parent 99e8c509d5
commit 721839c847
22 changed files with 1306 additions and 153 deletions
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env node
import { run } from './evaluate-knowledge-quality.mjs';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
const baseUrl = process.env.AIHR_BASE_URL || 'https://wygj-api.localhost';
const clientid = '428a8310cd442757ae699df5d894f051';
const dataset = new URL('../tests/fixtures/data_quality/retrieval-finance-regression.json', import.meta.url);
function localUniqueActivePhone() {
const sql = `
select min(s.person_phone)
from aihr_org_snapshot s
where s.tenant_id = '000000'
and lower(coalesce(s.employment_status, 'active')) in ('active', '在职')
and s.person_phone regexp '^[0-9]{11}$'
and (select count(distinct p.ext_party_id) from aihr_org_snapshot p
where p.tenant_id = s.tenant_id and p.person_phone = s.person_phone
and lower(coalesce(p.employment_status, 'active')) in ('active', '在职')) = 1
and (select count(distinct e.person_phone) from aihr_org_snapshot e
where e.tenant_id = s.tenant_id and e.ext_party_id = s.ext_party_id
and lower(coalesce(e.employment_status, 'active')) in ('active', '在职')) = 1`;
return execFileSync('docker', [
'exec', 'wygj-mysql', 'mysql', '-uroot', '-proot', '--default-character-set=utf8mb4',
'ry-vue', '-Nse', sql
], { encoding: 'utf8' }).trim();
}
const phone = process.env.AIHR_VERIFY_PHONE || localUniqueActivePhone();
if (!phone) throw new Error('no bidirectionally unique active local employee is available');
execFileSync('docker', [
'exec', 'wygj-redis', 'redis-cli', '-a', 'ruoyi123', 'del',
`{global:rate_limit:/resource/sms/code:${phone}}:value`,
`global:rate_limit:/resource/sms/code:${phone}`,
`{global:rate_limit:/resource/sms/code:${phone}}:permits`
], { stdio: 'ignore' });
if (new URL(baseUrl).hostname.endsWith('.localhost')) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}
async function api(endpoint, init = {}) {
const response = await fetch(`${baseUrl.replace(/\/$/, '')}${endpoint}`, {
...init,
headers: { clientid, ...(init.headers || {}) },
signal: AbortSignal.timeout(30_000)
});
const payload = await response.json();
if (!response.ok || Number(payload.code) !== 200) {
throw new Error(`${endpoint} failed: HTTP ${response.status}, code ${payload.code}, ${payload.msg || ''}`);
}
return payload.data;
}
await api(`/resource/sms/code?phonenumber=${encodeURIComponent(phone)}`);
const login = await api('/auth/mobile/sms-login', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
phonenumber: phone,
smsCode: process.env.AIHR_VERIFY_SMS_CODE || '123456',
tenantId: '000000'
})
});
if (!login?.access_token) throw new Error('mobile login returned no access token');
const result = await run({
dataset: fileURLToPath(dataset),
baseUrl,
token: login.access_token,
clientid,
k: 20
});
console.log(JSON.stringify(result, null, 2));
if (result.thresholdPassed === false) process.exitCode = 2;