/** * Evolution Evaluator — 进化系统的 LLM 智能评估器 * * 所有判断都由 Agent (LLM) 完成,无规则降级。 * * 基础设施: * 1. callEvaluator: JSON-in-JSON-out 的结构化推理 * 2. LRU 缓存: 相同输入不重复调用 * 3. Token 预算: 防止失控 * 4. 超时保护: 30s */ const path = require('path'); const crypto = require('crypto'); // ════════════════════════════════════════════════════════ // LLM 调用缓存 (进程内 LRU) // ════════════════════════════════════════════════════════ const _cache = new Map(); const CACHE_MAX_SIZE = 200; function cacheKey(systemContext, task) { return crypto.createHash('md5').update(systemContext + task).digest('hex'); } function getCached(key) { if (_cache.has(key)) { const val = _cache.get(key); _cache.delete(key); _cache.set(key, val); return val; } return null; } function setCache(key, val) { if (_cache.size >= CACHE_MAX_SIZE) { const oldest = _cache.keys().next().value; _cache.delete(oldest); } _cache.set(key, val); } // ════════════════════════════════════════════════════════ // Token 预算控制 // ════════════════════════════════════════════════════════ let _llmCallCount = 0; let _llmBudget = parseInt(process.env.EVOLUTION_LLM_BUDGET) || 500; function getLLMStats() { return { calls: _llmCallCount, budget: _llmBudget, remaining: _llmBudget - _llmCallCount }; } function resetLLMBudget(budget) { _llmCallCount = 0; if (budget !== undefined) _llmBudget = budget; } // ════════════════════════════════════════════════════════ // AgentManager 注入 — 四维正交链路 // ════════════════════════════════════════════════════════ let _agentManager = null; /** * 注入 AgentManager 实例 * 由 run-controller 在 Phase 开始时调用 */ function setAgentManager(manager) { _agentManager = manager; } /** * 从 LLM 输出中提取 JSON 对象 * 使用括号深度匹配,正确处理 JSON 内容中嵌套 code fence 的情况 */ function extractJSON(text) { let depth = 0, start = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '{') { if (start === -1) start = i; depth++; } else if (text[i] === '}') { depth--; if (depth === 0 && start !== -1) { try { return JSON.parse(text.slice(start, i + 1)); } catch { /* continue */ } start = -1; } } } return null; } /** * 核心 LLM 调用: 发送 prompt → 获取 JSON 响应 * * 通过 AgentManager.callAgent('evaluator') 走四维正交链路: * evaluator agent → kimi client → runtime-resolver → AcpProtocol * * 每次调用都会通过 _logSink 记录: * - prompt (完整输入) * - raw_output (LLM 原始输出) * - parsed (解析后的 JSON) */ async function callEvaluator(systemContext, task) { if (!_agentManager) { throw new Error('AgentManager 未注入,请先调用 setAgentManager()'); } const key = cacheKey(systemContext, task); const cached = getCached(key); if (cached) { _emit('llm_cache_hit', { key: key.slice(0, 8) }); return cached; } if (_llmCallCount >= _llmBudget) { throw new Error(`LLM budget exhausted: ${_llmCallCount}/${_llmBudget}`); } const fullPrompt = `${systemContext}\n\n---\n\n${task}\n\n---\n\n**重要**: 你的回复必须是一个合法的 JSON 对象,不要包含任何 markdown 标记或额外文字。直接输出 JSON。`; _emit('llm_request', { backend: 'evaluator', callIndex: _llmCallCount, systemContext: systemContext.slice(0, 500), task: task.slice(0, 1000), promptLength: fullPrompt.length, }); const startMs = Date.now(); const result = await _agentManager.callAgent('evaluator', fullPrompt); const durationMs = Date.now() - startMs; _llmCallCount++; const output = result.output || ''; _emit('llm_response', { backend: 'evaluator', callIndex: _llmCallCount - 1, durationMs, outputLength: output.length, rawOutput: output.slice(0, 3000), }); // 从输出中提取 JSON —— 用括号深度匹配,避免嵌套 code fence 截断问题 const parsed = extractJSON(output); if (!parsed) { throw new Error(`Failed to parse JSON from output (${output.length} chars): ${output.slice(0, 200)}`); } _emit('llm_parsed', { callIndex: _llmCallCount - 1, parsed, }); setCache(key, parsed); return parsed; } // ════════════════════════════════════════════════════════ // 日志 Sink — 由 run-controller 注入 // ════════════════════════════════════════════════════════ let _logSink = null; function setLogSink(sink) { _logSink = sink; } function _emit(event, data) { if (_logSink) _logSink(event, data); } // ════════════════════════════════════════════════════════ // 业务 API — 每个函数 = 一个 prompt 模板 // ════════════════════════════════════════════════════════ /** * 综合评估基因组的 6 维适应度 */ async function evaluateGenomeFitness(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 || {}); } return callEvaluator( `你是一个 Agent Skill 能力评估专家。评估 Skill 的 6 维认知能力。 6 维: mentalModels(因果推理), patternLibrary(模式识别), judgment(决策判断), toolFluency(工具使用), taste(品味标准), scarTissue(失败教训)。 每个维度打 0.0-1.0 分,评估内容的**质量和深度**,不是数条目。`, `Skill: ${genome.skill_name} ${JSON.stringify(dims, null, 2).slice(0, 4000)} 返回 JSON: { "dimensionScores": { "mentalModels": 0.7, "patternLibrary": 0.8, "judgment": 0.6, "toolFluency": 0.75, "taste": 0.5, "scarTissue": 0.65 }, "overall": 0.68, "strengths": ["..."], "weaknesses": ["..."] }`, ); } /** * Benchmark 探针评估: 判断基因组是否覆盖探针指定的能力 */ async function evaluateBenchmarkProbe(probe, genomeDimension) { return callEvaluator( `你是 Benchmark 评估专家。判断 Skill 的能力维度是否覆盖了探针测试场景。 探针类型: - trap: 陷阱规避 — Skill 是否知道要避开这个陷阱 - positive: 正向能力 — Skill 是否具备处理此场景的模式 - boundary: 边界识别 — Skill 是否意识到自己在此场景的能力边界 语义级覆盖判断,不是关键词匹配。`, `探针: ${JSON.stringify(probe)} Skill 能力维度: ${JSON.stringify(genomeDimension, null, 2).slice(0, 2000)} 返回 JSON: { "passed": true, "score": 0.85, "reasoning": "判断理由" }`, ); } /** * Arena 场景评估: 编译基因组为 prompt → 用 prompt 回答场景 → 评分 * * 这是进化的核心选择压力来源。 * 不需要文件系统,不需要 workDir,纯文本推理。 */ async function evaluateScenario(compiledPrompt, scenario, criteria) { // Step 1: 用编译后的 genome prompt 执行场景任务 const agentOutput = await callEvaluator(compiledPrompt, scenario); // Step 2: Judge 评估产出质量 return callEvaluator( `你是进化评估裁判。根据评估标准,评判一个 Agent 对场景任务的产出质量。 评估标准: ${JSON.stringify(criteria, null, 2)} 六维评分: - scarTissue: 是否规避了已知陷阱 (0-1) - patternLibrary: 是否识别了关键模式 (0-1) - judgment: 决策质量和优先级排序 (0-1) - toolFluency: 工具使用效率 (0-1) - taste: 输出的专业度和可操作性 (0-1) - mentalModels: 是否展现了因果推理 (0-1)`, `Agent 产出: ${JSON.stringify(agentOutput, null, 2).slice(0, 3000)} 返回 JSON: { "dimensionScores": { "mentalModels": 0.7, "patternLibrary": 0.8, "judgment": 0.6, "toolFluency": 0.75, "taste": 0.5, "scarTissue": 0.65 }, "overall": 0.68, "reasoning": "评判理由" }`, ); } /** * 心智模型深化 — 追问 WHY */ async function deepenMentalModels(currentModels, relatedFailures) { return callEvaluator( `你是系统思维专家。深化 Skill 的心智模型: 1. 因果链深挖: 追问 WHY 2. 跨领域类比: 寻找结构相似模式 3. 系统动力学: 分析连锁反应`, `当前心智模型: ${JSON.stringify(currentModels, null, 2)} 相关失败: ${JSON.stringify(relatedFailures, null, 2).slice(0, 2000)} 返回 JSON: { "deepenedChains": [{ "original": "...", "deeper": "...", "depth": 3 }], "newAnalogies": [{ "domain": "...", "insight": "..." }], "systemDynamics": [{ "pattern": "...", "cascade": "..." }] }`, ); } /** * Agent 智能变异: 为基因组生成新的知识片段 */ async function generateMutationKnowledge(genome, dimension) { const dimData = typeof genome[dimension] === 'string' ? JSON.parse(genome[dimension]) : (genome[dimension] || {}); return callEvaluator( `你是 Skill 知识生成专家。为指定的认知维度生成新的知识片段。 维度: ${dimension} 要求: 生成与当前知识互补的新条目(不是重复已有的)。`, `Skill: ${genome.skill_name} 当前 ${dimension} 内容: ${JSON.stringify(dimData, null, 2).slice(0, 2000)} 根据维度生成 1-3 条新知识。返回 JSON: { "newEntries": [<与当前维度结构一致的新条目>] }`, ); } /** * Benchmark 自动生成: 为指定 Skill 生成测试场景 */ async function generateBenchmarkScenarios(skillName, existingGenome) { const dims = {}; for (const key of ['mental_models', 'pattern_library', 'judgment', 'tool_fluency', 'taste', 'scar_tissue']) { const val = existingGenome[key]; dims[key] = typeof val === 'string' ? JSON.parse(val) : (val || {}); } return callEvaluator( `你是 Benchmark 设计专家。为一个 Agent Skill 设计测试场景和探针。 每个 Benchmark 包含: - scenario: 给 Agent 的文本场景描述(包含足够的上下文让 Agent 产出回答) - expectedBehaviors: 期望 Agent 展现的行为 - evaluationCriteria: 六维评估标准(每个维度如何打分) - probes: 探针数组 [{name, type(trap|positive|boundary), description}]`, `Skill: ${skillName} Skill 能力概况: ${JSON.stringify(dims, null, 2).slice(0, 3000)} 为此 Skill 生成 3 个难度递进的 Benchmark。返回 JSON: { "benchmarks": [{ "id": "bench-${skillName}-01", "scenario": "完整的场景描述...", "expectedBehaviors": ["..."], "evaluationCriteria": { "scarTissue": "...", "judgment": "..." }, "difficulty": 1, "probes": [{ "name": "...", "type": "trap", "description": "..." }] }] }`, ); } module.exports = { callEvaluator, setAgentManager, setLogSink, evaluateGenomeFitness, evaluateBenchmarkProbe, evaluateScenario, deepenMentalModels, generateMutationKnowledge, generateBenchmarkScenarios, getLLMStats, resetLLMBudget, };