#!/usr/bin/env node 'use strict'; // 离线回归评测:不调用 LLM,覆盖意图/注入/商品词/lead/格式/引用审计等确定性逻辑。 const path = require('path'); const fs = require('fs'); const { quickClassify } = require('../bridge/src/intent'); const { detectInjection } = require('../bridge/src/security'); const { translateProductQuery } = require('../bridge/src/product-query'); const { hasBuyingIntent, missingLeadContacts } = require('../bridge/src/lead-intent'); const { markdownToHtml } = require('../bridge/src/markdown'); const { auditAnswer } = require('../bridge/src/audit'); const ENTRIES = [ { title: 'Rings', category: 'jewelry/rings', url: 'https://www.yehwang.com/jewelry/rings', aliases: ['戒指', 'ringen'] }, { title: 'Earrings', category: 'jewelry/earrings', url: 'https://www.yehwang.com/jewelry/earrings', aliases: ['耳环'] }, { title: 'Necklaces', category: 'jewelry/necklaces', url: 'https://www.yehwang.com/jewelry/necklaces', aliases: ['项链'] }, ]; const cases = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../bridge/eval/golden.json'), 'utf8')); function run(c) { if (c.type === 'intent') return quickClassify(c.message); if (c.type === 'injection') return detectInjection(c.message).level; if (c.type === 'product_query') return translateProductQuery(c.message, ENTRIES).query; if (c.type === 'lead') { const missing = missingLeadContacts(c.slots || {}); return missing.join(','); } if (c.type === 'markdown') return markdownToHtml(c.message); if (c.type === 'audit') { const audit = auditAnswer({ finalContent: c.message, toolResults: [{ results: [{ source_urls: c.tool_urls || [] }] }], toolCalls: [], }); return String(audit.unverified_urls.length); } throw new Error(`unknown case type: ${c.type}`); } function check(c, actual) { if (c.type === 'lead') return actual === (c.expect_missing || []).join(','); if (c.type === 'markdown') { return (c.expect_contains || []).every((s) => actual.includes(s)) && (c.expect_not_contains || []).every((s) => !actual.includes(s)); } if (c.type === 'audit') return actual === String(c.expect_unverified); return actual === c.expect; } let failed = 0; for (const c of cases) { const actual = run(c); const ok = check(c, actual); if (!ok) failed += 1; console.log(`${ok ? 'PASS' : 'FAIL'} ${c.id}${ok ? '' : ` expected=${JSON.stringify(c.expect || c.expect_missing || c.expect_contains)} actual=${JSON.stringify(actual)}`}`); } console.log(`\n${cases.length - failed}/${cases.length} passed`); process.exit(failed ? 1 : 0);