Files
prop-ai-hr/scripts/verify-ask-memory-local.mjs

133 lines
5.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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.
#!/usr/bin/env node
import crypto from 'node:crypto';
const baseUrl = process.env.AIHR_BASE_URL || 'http://127.0.0.1:8080';
const phone = process.env.AIHR_VERIFY_PHONE || '13900000001';
const smsCode = process.env.AIHR_VERIFY_SMS_CODE || '123456';
const clientId = '428a8310cd442757ae699df5d894f051';
async function request(endpoint, { method = 'GET', body, token } = {}) {
const response = await fetch(`${baseUrl}${endpoint}`, {
method,
headers: {
clientid: clientId,
'Content-Language': 'zh_CN',
...(token ? { Authorization: `Bearer ${token}` } : {}),
...(body ? { 'Content-Type': 'application/json;charset=utf-8' } : {})
},
body: body ? JSON.stringify(body) : undefined,
signal: AbortSignal.timeout(30_000)
});
const text = await response.text();
let payload;
try {
payload = JSON.parse(text);
} catch {
throw new Error(`${endpoint} 返回了非 JSON 响应(HTTP ${response.status})`);
}
if (!response.ok || Number(payload.code) !== 200) {
throw new Error(`${endpoint} 失败:HTTP ${response.status} / ${payload.code} ${payload.msg || ''}`);
}
return payload.data;
}
function assert(condition, message) {
if (!condition) throw new Error(message);
}
await request(`/resource/sms/code?phonenumber=${encodeURIComponent(phone)}`);
const login = await request('/auth/mobile/sms-login', {
method: 'POST',
body: { phonenumber: phone, smsCode, tenantId: '000000' }
});
const token = login?.access_token;
assert(token, '手机号登录未返回 access_token');
const conversationId = crypto.randomUUID();
const first = await request('/api/knowledge/query', {
method: 'POST',
token,
body: {
queryText: '报修受理后第一步应该做什么?',
category: 'sop',
position: '生活顾问',
limit: 5,
conversationId,
contextVersion: 0
}
});
assert(first?.conversationId === conversationId, '首问没有返回同一 conversationId');
assert(Number(first?.contextVersion) === 1, '首问 contextVersion 未从 0 递增到 1');
assert(first?.intent === 'QA', `首问意图错误:${first?.intent}`);
assert(!first?.noEvidence, '首问未召回已授权的报修依据');
const followup = await request('/api/knowledge/query', {
method: 'POST',
token,
body: {
queryText: '把对应原文件给我',
category: 'sop',
position: '生活顾问',
limit: 5,
conversationId,
contextVersion: first.contextVersion
}
});
assert(followup?.conversationId === conversationId, '追问没有保持同一 conversationId');
assert(Number(followup?.contextVersion) === 2, '追问 contextVersion 未从 1 递增到 2');
assert(followup?.intent === 'FILE', `追问意图错误:${followup?.intent}`);
assert(String(followup?.rewrittenQuery || '').includes('报修'), '追问没有结合首问主题重写');
assert(Array.isArray(followup?.resources) && followup.resources.length > 0, '追问未返回授权原文件');
const resource = followup.resources[0];
assert(resource?.contentUrl?.startsWith('/api/knowledge/resources/'), '原文件没有使用受保护内容地址');
const download = await fetch(`${baseUrl}${resource.contentUrl}`, {
headers: { clientid: clientId, Authorization: `Bearer ${token}` },
signal: AbortSignal.timeout(30_000)
});
assert(download.ok, `原文件下载失败:HTTP ${download.status}`);
assert((await download.arrayBuffer()).byteLength > 0, '原文件下载内容为空');
const videoFollowup = await request('/api/knowledge/query', {
method: 'POST',
token,
body: {
queryText: '有对应操作视频吗?',
category: 'sop',
position: '生活顾问',
limit: 5,
conversationId,
contextVersion: followup.contextVersion
}
});
assert(Number(videoFollowup?.contextVersion) === 3, '视频追问 contextVersion 未递增到 3');
assert(videoFollowup?.intent === 'VIDEO', `视频追问意图错误:${videoFollowup?.intent}`);
assert(Array.isArray(videoFollowup?.resources) && videoFollowup.resources.length === 0, '无视频时返回了伪造资源');
assert(String(videoFollowup?.answer || '').includes('未找到'), '无视频时没有明确说明未找到');
const staleResponse = await fetch(`${baseUrl}/api/knowledge/query`, {
method: 'POST',
headers: {
clientid: clientId,
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify({
queryText: '再说一遍',
category: 'sop',
conversationId,
contextVersion: first.contextVersion
}),
signal: AbortSignal.timeout(30_000)
});
const stalePayload = await staleResponse.json();
assert(Number(stalePayload.code) === 409, `旧上下文版本未被拒绝:${stalePayload.code}`);
console.log('PASS 手机号真实登录');
console.log('PASS 首问召回有依据答案,contextVersion=1');
console.log(`PASS 追问识别 FILE 并重写为:${followup.rewrittenQuery}`);
console.log(`PASS 授权原文件下载:${resource.title}`);
console.log('PASS 视频追问无匹配时明确返回空资源');
console.log('PASS 旧 contextVersion 返回 409');