39 lines
1.5 KiB
JavaScript
39 lines
1.5 KiB
JavaScript
'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 };
|