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