/** * Arena — 进化竞技场 * * 核心选择压力来源。 * 把基因组编译为 prompt → 用 prompt 在文本场景中推理 → Judge 评分。 * 不需要文件系统,不需要沙盒目录,纯 LLM 推理。 */ const evolutionQueries = require('../queries').evolutionQueries; const { evaluateScenario } = require('../evaluator'); // ════════════════════════════════════════════════════════ // Genome → Prompt 编译 // ════════════════════════════════════════════════════════ /** * 将 6 维基因组编译为自然语言 prompt * * 这个 prompt 代表基因组的"表型"——它如何在环境中表现。 */ function compileGenomeToPrompt(genome) { const dims = {}; for (const key of ['mental_models', 'pattern_library', 'judgment', 'tool_fluency', 'taste', 'scar_tissue']) { const val = genome[key]; dims[key] = typeof val === 'string' ? JSON.parse(val) : (val || {}); } const sections = []; // 心智模型 → 第一性原理 const mm = dims.mental_models; if (mm.ironLaws?.length) sections.push(`## 铁律\n${mm.ironLaws.map(l => `- ${typeof l === 'string' ? l : l.insight || JSON.stringify(l)}`).join('\n')}`); if (mm.causalChains?.length) sections.push(`## 因果推理\n${mm.causalChains.map(c => `- ${typeof c === 'string' ? c : c.model || JSON.stringify(c)}`).join('\n')}`); // 模式库 → 识别模式 const pl = dims.pattern_library; if (pl.problemPatterns?.length) sections.push(`## 问题识别模式\n${pl.problemPatterns.map(p => `- **${p.id || p.signature || ''}**: ${p.signature || p.solution || JSON.stringify(p)}`).join('\n')}`); if (pl.solutionPatterns?.length) sections.push(`## 解决方案模式\n${pl.solutionPatterns.map(p => `- **${p.id || p.name || ''}**: ${p.applicableWhen || JSON.stringify(p)}`).join('\n')}`); // 判断力 → 决策规则 const jd = dims.judgment; if (jd.decisionRules?.length) sections.push(`## 决策规则\n${jd.decisionRules.map(r => `- ${typeof r === 'string' ? r : r.decision || JSON.stringify(r)}`).join('\n')}`); if (jd.exclusions?.length) sections.push(`## 不适用场景\n${jd.exclusions.map(e => `- ${typeof e === 'string' ? e : JSON.stringify(e)}`).join('\n')}`); // 伤疤记忆 → 反模式 const st = dims.scar_tissue; if (st.antiPatterns?.length) sections.push(`## 反模式(必须避开)\n${st.antiPatterns.map(a => `- ❌ ${a.pattern || JSON.stringify(a)}`).join('\n')}`); if (st.failures?.length) sections.push(`## 历史教训\n${st.failures.map(f => `- ${f.lesson || f.preventionRule || JSON.stringify(f)}`).join('\n')}`); // 品味 → 质量标准 const ta = dims.taste; if (ta.qualitySignals?.length) sections.push(`## 质量标准\n${ta.qualitySignals.map(s => `- ${s.signal || (typeof s === 'string' ? s : JSON.stringify(s))}`).join('\n')}`); // 工具流利度 → 工具使用 const tf = dims.tool_fluency; if (tf.idioms?.length) sections.push(`## 工具使用惯用法\n${tf.idioms.map(i => `- ${i.step || ''}: ${i.tools?.join(', ') || JSON.stringify(i)}`).join('\n')}`); return `你是一个 ${genome.skill_name} 专家 Agent。以下是你的能力配置:\n\n${sections.join('\n\n')}\n\n请基于以上能力完成后续任务。`; } // ════════════════════════════════════════════════════════ // 竞技场评估 // ════════════════════════════════════════════════════════ /** * 在 Arena 中评估单个基因组 * * @param {object} genome - 基因组记录 * @param {object[]} scenarios - Benchmark 场景数组 * @returns {{ overall, scores }} */ async function evaluateGenomeInArena(genome, scenarios) { const prompt = compileGenomeToPrompt(genome); const scores = []; for (const scenario of scenarios) { try { const result = await evaluateScenario( prompt, scenario.scenario || scenario.description || '', scenario.evaluationCriteria || {}, ); scores.push({ scenarioId: scenario.id, overall: result.overall || 0, dimensionScores: result.dimensionScores || {}, reasoning: result.reasoning || '', }); } catch (err) { console.error(` ⚠️ Arena 场景 ${scenario.id} 评估失败: ${err.message}`); scores.push({ scenarioId: scenario.id, overall: 0, error: err.message }); } } const avgOverall = scores.length > 0 ? scores.reduce((sum, s) => sum + (s.overall || 0), 0) / scores.length : 0; return { genomeId: genome.id, overall: avgOverall, scores }; } /** * 评估 Run 中所有存活基因组 */ async function evaluateRunInArena(runId) { const genomes = await evolutionQueries.getGenomesByRun(runId, { aliveOnly: true }); if (genomes.length === 0) return { ranked: [] }; const skillName = genomes[0].skill_name; const benchmarks = await evolutionQueries.getBenchmarks(skillName); // 优先使用新的 scenario 字段,兼容旧的 description-only 格式 const scenarios = benchmarks.map(b => { const criteria = typeof b.evaluation_criteria === 'string' ? JSON.parse(b.evaluation_criteria) : (b.evaluation_criteria || {}); return { id: b.id, scenario: b.scenario || b.description || '', evaluationCriteria: criteria, }; }); if (scenarios.length === 0) { console.log(` ⚠️ ${skillName} 没有 Benchmark 场景,跳过 Arena`); return { ranked: [] }; } console.log(`🏟️ Arena: ${genomes.length} 基因组 × ${scenarios.length} 场景\n`); const results = []; for (const genome of genomes) { const result = await evaluateGenomeInArena(genome, scenarios); results.push(result); // 更新 overall_fitness await evolutionQueries.updateGenomeFitness(genome.id, {}, result.overall); console.log(` ${genome.skill_name}[${genome.id.slice(0, 6)}]: ${(result.overall * 100).toFixed(1)}%`); } // 按得分排序 results.sort((a, b) => b.overall - a.overall); return { ranked: results }; } module.exports = { compileGenomeToPrompt, evaluateGenomeInArena, evaluateRunInArena, };