Files
crm-ai/bridge/src/experiments.js
T

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 };