fitness-calculator.js 2.35 KB
/**
 * Fitness Calculator — 6 维适应度评估
 *
 * 所有评估由 LLM Agent 完成,无规则降级。
 * evaluator.evaluateGenomeFitness 做语义级质量评估。
 */
const evolutionQueries = require('../queries').evolutionQueries;
const { evaluateGenomeFitness } = require('../evaluator');

const DEFAULT_WEIGHTS = {
  mentalModels: 0.10,
  patternLibrary: 0.20,
  judgment: 0.25,
  toolFluency: 0.15,
  taste: 0.15,
  scarTissue: 0.15,
};

/**
 * 评估并持久化单个基因组的适应度
 */
async function evaluateAndPersistFitness(genomeId, runId) {
  const genome = await evolutionQueries.getGenome(genomeId);
  if (!genome) throw new Error(`Genome ${genomeId} not found`);

  const result = await evaluateGenomeFitness(genome);
  const dimensionScores = result.dimensionScores || {};
  const overall = result.overall || Object.entries(dimensionScores).reduce(
    (sum, [key, score]) => sum + (score || 0) * (DEFAULT_WEIGHTS[key] || 0), 0,
  );

  await evolutionQueries.updateGenomeFitness(genomeId, dimensionScores, parseFloat(overall.toFixed(4)));

  await evolutionQueries.insertEvaluation({
    genomeId, runId,
    benchmarkId: null,
    generation: genome.generation,
    probeResults: null,
    dimensionScores,
    overallScore: parseFloat(overall.toFixed(4)),
    weights: DEFAULT_WEIGHTS,
    totalToolCalls: null,
    redundantCalls: null,
    executionTimeMs: null,
  });

  return { genomeId, dimensionScores, overall: parseFloat(overall.toFixed(4)) };
}

/**
 * 批量评估 run 中所有存活基因组
 */
async function evaluateRunFitness(runId) {
  const genomes = await evolutionQueries.getGenomesByRun(runId, { aliveOnly: true });
  console.log(`📊 适应度评估: ${genomes.length} 个基因组\n`);

  const results = [];
  for (const genome of genomes) {
    try {
      const result = await evaluateAndPersistFitness(genome.id, runId);
      results.push(result);
      console.log(`  ${genome.skill_name}: ${(result.overall * 100).toFixed(1)}%`);
    } catch (err) {
      console.error(`  ❌ ${genome.skill_name}: ${err.message}`);
    }
  }

  if (results.length > 0) {
    const best = results.reduce((max, r) => r.overall > max.overall ? r : max, results[0]);
    await evolutionQueries.updateRun(runId, { best_fitness: best.overall });
  }

  return results;
}

module.exports = {
  evaluateAndPersistFitness,
  evaluateRunFitness,
  DEFAULT_WEIGHTS,
};