// src/brain/model-router.js // 任务型模型路由器 — 核心脑升级 // 职责:根据任务类型、上下文长度、成本、可用性选择最优模型 // // 路由表: // chat → 对话模型(deepseek-chat, 均衡性价比) // build → 代码模型(deepseek-chat, 代码能力强) // review → 推理模型(deepseek-reasoner, 逻辑分析强) // brain → 低温稳定模型(deepseek-chat + low temp, 输出可控) // long → 长上下文模型(moonshot-v1-128k / gemini-1.5-pro) 'use strict'; /** * 任务-模型路由映射表 * 每个任务类型定义: * preferred: 首选 { provider, model, temperature, max_tokens } * fallbacks: 降级列表(按优先级排序) * context_budget: 建议上下文窗口大小 */ const ROUTING_TABLE = { chat: { description: '普通对话 — 均衡性价比', preferred: { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.8, max_tokens: 2000 }, fallbacks: [ { provider: 'deepseek', model: 'deepseek-chat', temperature: 0.8, max_tokens: 2000 }, { provider: 'zhipu', model: 'glm-4-flash', temperature: 0.8, max_tokens: 2000 }, { provider: 'moonshot', model: 'moonshot-v1-8k', temperature: 0.8, max_tokens: 2000 }, ], context_budget: 32000, }, build: { description: '写代码 / 构建 — 代码能力优先', preferred: { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.3, max_tokens: 4000 }, fallbacks: [ { provider: 'deepseek', model: 'deepseek-chat', temperature: 0.3, max_tokens: 4000 }, { provider: 'yunwu', model: 'gpt-4o', temperature: 0.3, max_tokens: 4000 }, { provider: 'zhipu', model: 'glm-4', temperature: 0.3, max_tokens: 4000 }, ], context_budget: 64000, }, review: { description: '审查 / 分析 — 推理能力优先', preferred: { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.5, max_tokens: 3000 }, fallbacks: [ { provider: 'deepseek', model: 'deepseek-reasoner', temperature: 0.5, max_tokens: 3000 }, { provider: 'yunwu', model: 'gpt-4o', temperature: 0.5, max_tokens: 3000 }, { provider: 'zhipu', model: 'glm-4', temperature: 0.5, max_tokens: 3000 }, ], context_budget: 32000, }, brain: { description: '脑记忆整理 — 低温稳定输出', preferred: { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.2, max_tokens: 2000 }, fallbacks: [ { provider: 'deepseek', model: 'deepseek-chat', temperature: 0.2, max_tokens: 2000 }, { provider: 'zhipu', model: 'glm-4-flash', temperature: 0.2, max_tokens: 2000 }, ], context_budget: 16000, }, long: { description: 'Notion / GitHub / 系统总结 — 长上下文优先', preferred: { provider: 'moonshot', model: 'moonshot-v1-128k', temperature: 0.5, max_tokens: 4000 }, fallbacks: [ { provider: 'yunwu', model: 'gemini-1.5-pro', temperature: 0.5, max_tokens: 4000 }, { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.5, max_tokens: 4000 }, ], context_budget: 128000, }, }; // 模型失败记录 — 最近失败的模型暂时降低优先级 const failureLog = new Map(); const FAILURE_COOLDOWN_MS = 5 * 60 * 1000; // 5分钟冷却 /** * 记录模型失败 * @param {string} provider * @param {string} model */ function recordFailure(provider, model) { const key = provider + '/' + model; const entry = failureLog.get(key) || { count: 0, lastFail: 0 }; entry.count++; entry.lastFail = Date.now(); failureLog.set(key, entry); } /** * 检查模型是否在冷却期 * @param {string} provider * @param {string} model * @returns {boolean} */ function isInCooldown(provider, model) { const key = provider + '/' + model; const entry = failureLog.get(key); if (!entry) return false; if (Date.now() - entry.lastFail > FAILURE_COOLDOWN_MS) { failureLog.delete(key); return false; } return entry.count >= 3; // 5分钟内失败3次及以上才冷却 } /** * 根据任务模式选择最优模型 * @param {string} mode - 任务模式 (chat/build/review/brain/long) * @param {Object} opts * @param {number} opts.contextLength - 当前上下文 token 数 * @param {boolean} opts.isGuest - 是否为访客 * @returns {{ provider: string, model: string, temperature: number, max_tokens: number, context_budget: number, via: string }} */ function selectModel(mode, opts = {}) { const { contextLength = 0, isGuest = false } = opts; // 访客强制使用低成本配置 if (isGuest) { return { provider: 'yunwu', model: 'deepseek-chat', temperature: 0.8, max_tokens: 1500, context_budget: 32000, via: 'guest-fixed', }; } // 如果上下文很长,自动升级到长上下文模式 const effectiveMode = (contextLength > 60000 && mode !== 'long') ? 'long' : mode; const route = ROUTING_TABLE[effectiveMode] || ROUTING_TABLE.chat; // 尝试首选模型 if (!isInCooldown(route.preferred.provider, route.preferred.model)) { return { ...route.preferred, context_budget: route.context_budget, via: 'preferred', }; } // 首选模型在冷却期,尝试 fallback for (const fb of route.fallbacks) { if (!isInCooldown(fb.provider, fb.model)) { return { ...fb, context_budget: route.context_budget, via: 'fallback', }; } } // 所有模型都在冷却期,强制使用首选(宁可重试也不能无响应) return { ...route.preferred, context_budget: route.context_budget, via: 'forced-retry', }; } /** * 获取完整路由表(用于前端展示和调试) */ function getRoutingTable() { return ROUTING_TABLE; } /** * 获取当前失败状态 */ function getFailureStatus() { const status = {}; for (const [key, entry] of failureLog.entries()) { const inCooldown = Date.now() - entry.lastFail < FAILURE_COOLDOWN_MS && entry.count >= 3; status[key] = { failures: entry.count, lastFail: new Date(entry.lastFail).toISOString(), inCooldown, }; } return status; } module.exports = { selectModel, recordFailure, isInCooldown, getRoutingTable, getFailureStatus, ROUTING_TABLE, };