guanghulab/server/app/modules/smart-router.js

208 lines
6.3 KiB
JavaScript
Raw Normal View History

/**
*
* 🧠 智能模型分流Agent · Smart Model Router
*
*
* 编号: ZY-MODEL-ROUTER-002
* 守护: 铸渊 · ICE-GL-ZY001
*
* 根据用户说什么来决定调用便宜的模型还是深度推理模型
* 同时追踪API调用成本和次数
*/
'use strict';
// ─── 模型定价表(元/百万token ───
const MODEL_PRICING = {
'deepseek-chat': { input: 1.0, output: 2.0, tier: 'economy', name: 'DeepSeek-V3' },
'deepseek-reasoner': { input: 4.0, output: 16.0, tier: 'premium', name: 'DeepSeek-R1' },
'qwen-turbo': { input: 0.3, output: 0.6, tier: 'economy', name: '通义千问-Turbo' },
'qwen-plus': { input: 0.8, output: 2.0, tier: 'economy', name: '通义千问-Plus' },
'qwen-max': { input: 2.0, output: 6.0, tier: 'standard', name: '通义千问-Max' },
'gpt-4o-mini': { input: 0.15, output: 0.6, tier: 'economy', name: 'GPT-4o-mini' },
'gpt-4o': { input: 2.5, output: 10.0, tier: 'premium', name: 'GPT-4o' },
'claude-3-5-sonnet': { input: 3.0, output: 15.0, tier: 'premium', name: 'Claude-3.5-Sonnet' },
'claude-3-haiku': { input: 0.25, output: 1.25, tier: 'economy', name: 'Claude-3-Haiku' }
};
// ─── 深度推理触发关键词 ───
const DEEP_REASONING_PATTERNS = [
/分析|推理|评估|审查|review|analyze/i,
/架构|设计|重构|方案|strategy/i,
/为什么|原因|解释.*原理|how.*work/i,
/复杂|困难|棘手|tricky|complex/i,
/比较|对比|权衡|trade.?off/i,
/安全|漏洞|vulnerability|security/i,
/调试|debug|排查|诊断|diagnose/i,
/优化|性能|performance|bottleneck/i
];
// ─── 简单对话关键词 ───
const SIMPLE_CHAT_PATTERNS = [
/你好|hi|hello|嗨|在吗/i,
/谢谢|感谢|thank/i,
/是的|好的|对|ok|确认|没问题/i,
/帮我.*写|生成|创建|create|generate/i,
/查询|查看|获取|get|fetch|list/i,
/翻译|translate/i
];
// ─── 代码生成关键词 ───
const CODE_PATTERNS = [
/写代码|编写|实现|implement|code/i,
/函数|function|方法|method|class/i,
/接口|api|路由|route|endpoint/i,
/部署|deploy|发布|build|构建/i,
/修复|fix|bug|报错|error/i
];
// ─── API调用统计 ───
const usageStats = {
totalCalls: 0,
totalInputTokens: 0,
totalOutputTokens: 0,
totalCostCNY: 0,
byModel: {},
byTier: { economy: 0, standard: 0, premium: 0 },
hourlyRate: [],
startTime: Date.now()
};
/**
* 分析用户输入决定使用哪个模型
* @param {string} userMessage - 用户说的话
* @param {object} context - 上下文信息
* @returns {object} 模型选择结果
*/
function routeModel(userMessage, context = {}) {
const { messageCount = 0, isFirstMessage = false, userId = null } = context;
const msgLen = userMessage.length;
let selectedModel = 'deepseek-chat'; // 默认便宜模型
let reason = '默认对话';
let tier = 'economy';
let temperature = 0.7;
let maxTokens = 2000;
// 1. 检查是否需要深度推理
const needsDeepReasoning = DEEP_REASONING_PATTERNS.some(p => p.test(userMessage));
if (needsDeepReasoning && msgLen > 50) {
selectedModel = 'deepseek-reasoner';
reason = '检测到深度推理需求';
tier = 'premium';
temperature = 0.3;
maxTokens = 4000;
}
// 2. 检查是否是代码相关
else if (CODE_PATTERNS.some(p => p.test(userMessage))) {
selectedModel = 'deepseek-chat';
reason = '代码生成任务';
tier = 'economy';
temperature = 0.3;
maxTokens = 4000;
}
// 3. 简单对话用最便宜的
else if (SIMPLE_CHAT_PATTERNS.some(p => p.test(userMessage)) || msgLen < 20) {
selectedModel = 'qwen-turbo';
reason = '简单对话';
tier = 'economy';
temperature = 0.8;
maxTokens = 1000;
}
// 4. 中等长度的普通对话
else if (msgLen < 200) {
selectedModel = 'deepseek-chat';
reason = '普通对话';
tier = 'economy';
temperature = 0.7;
maxTokens = 2000;
}
// 5. 长消息,可能需要更好的理解
else {
selectedModel = 'deepseek-chat';
reason = '长文本理解';
tier = 'economy';
temperature = 0.5;
maxTokens = 3000;
}
const pricing = MODEL_PRICING[selectedModel] || MODEL_PRICING['deepseek-chat'];
return {
model: selectedModel,
modelName: pricing.name,
reason,
tier,
temperature,
maxTokens,
estimatedCost: {
inputPer1k: (pricing.input / 1000).toFixed(6),
outputPer1k: (pricing.output / 1000).toFixed(6),
currency: 'CNY'
}
};
}
/**
* 记录API调用
*/
function recordUsage(model, inputTokens, outputTokens) {
const pricing = MODEL_PRICING[model] || MODEL_PRICING['deepseek-chat'];
// 价格单位: 元/百万token → cost = tokens * (元/百万token) / 1000000
const cost = (inputTokens * pricing.input + outputTokens * pricing.output) / 1000000;
usageStats.totalCalls++;
usageStats.totalInputTokens += inputTokens;
usageStats.totalOutputTokens += outputTokens;
usageStats.totalCostCNY += cost;
if (!usageStats.byModel[model]) {
usageStats.byModel[model] = { calls: 0, tokens: 0, cost: 0 };
}
usageStats.byModel[model].calls++;
usageStats.byModel[model].tokens += inputTokens + outputTokens;
usageStats.byModel[model].cost += cost;
usageStats.byTier[pricing.tier] = (usageStats.byTier[pricing.tier] || 0) + 1;
// 记录小时速率
const hour = new Date().getHours();
if (!usageStats.hourlyRate[hour]) usageStats.hourlyRate[hour] = 0;
usageStats.hourlyRate[hour]++;
return { cost, model, inputTokens, outputTokens };
}
/**
* 获取使用统计
*/
function getUsageStats() {
const uptimeHours = (Date.now() - usageStats.startTime) / 3600000;
return {
...usageStats,
uptimeHours: Math.round(uptimeHours * 10) / 10,
avgCallsPerHour: uptimeHours > 0 ? Math.round(usageStats.totalCalls / uptimeHours * 10) / 10 : 0,
totalCostCNY: Math.round(usageStats.totalCostCNY * 10000) / 10000
};
}
/**
* 获取模型定价表
*/
function getPricingTable() {
return MODEL_PRICING;
}
module.exports = {
routeModel,
recordUsage,
getUsageStats,
getPricingTable,
MODEL_PRICING
};