208 lines
6.3 KiB
JavaScript
208 lines
6.3 KiB
JavaScript
/**
|
||
* ═══════════════════════════════════════════════════════════
|
||
* 🧠 智能模型分流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
|
||
};
|