feat: port quality, escalation, KB version, site-map KB, experiments and metrics

This commit is contained in:
wangliang
2026-10-23 11:41:09 +08:00
parent 81b49c9350
commit b778570045
33 changed files with 2446 additions and 344 deletions
+38
View File
@@ -0,0 +1,38 @@
'use strict';
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
// A/B 实验:按 conversationId 稳定分桶(旧 bridge config/experiments.js 精简版)。
function createExperimentManager(cfg = {}) {
const file = cfg.experimentsPath || path.resolve(__dirname, '..', 'config', 'experiments.json');
let experiments = [];
try {
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
experiments = Array.isArray(data.experiments) ? data.experiments : [];
} catch {
experiments = [];
}
function getVariant(convId, experimentId) {
const exp = experiments.find((e) => e.active && (!experimentId || e.id === experimentId));
if (!exp) return { experiment_id: null, variant: null, overriddenParams: null };
const hash = crypto.createHash('md5').update(`${convId}:${exp.id}`).digest('hex');
const bucket = parseInt(hash.slice(0, 8), 16) % 100;
let cumulative = 0;
for (const v of exp.variants || []) {
cumulative += v.weight || 50;
if (bucket < cumulative) return { experiment_id: exp.id, variant: v.name, overriddenParams: v.overriddenParams || null };
}
return { experiment_id: exp.id, variant: (exp.variants && exp.variants[0] && exp.variants[0].name) || null, overriddenParams: null };
}
function getActiveExperiments() {
return experiments.filter((e) => e.active);
}
return { getVariant, getActiveExperiments };
}
module.exports = { createExperimentManager };