feat: port quality, escalation, KB version, site-map KB, experiments and metrics
This commit is contained in:
@@ -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 };
|
||||
Reference in New Issue
Block a user