guanghulab/scripts/llm-automation-host.js

535 lines
18 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
// ═══════════════════════════════════════════════
// 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
// 📜 Copyright: 国作登字-2026-A-00037559
// ═══════════════════════════════════════════════
// scripts/llm-automation-host.js
// 🤖 LLM 自动化托管引擎
//
// 使用仓库密钥中的第三方模型API密钥来运行自动化任务
// 替代直接消耗 GitHub Copilot 配额
// 支持动态模型路由:根据任务类型自动选择最佳模型
//
// 用法:
// --status 显示可用模型和系统状态
// --task "任务描述" 执行自动化任务
// --task-type TYPE 任务类型 (inspection/fusion/review/general)
// --model MODEL 指定模型 (auto/anthropic/openai/dashscope/deepseek/custom)
// --dry-run 仅显示选择的模型和请求,不实际调用
// --context FILE 加载额外上下文文件
'use strict';
const https = require('https');
const http = require('http');
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
// ── 模型后端配置
// SY-CMD-KEY-012: 统一使用 ZY_LLM_API_KEY + ZY_LLM_BASE_URL
// 三方API密钥支持多模型动态路由铸渊根据任务类型自动选择最优模型
const MODEL_BACKENDS = [
{
name: 'deepseek',
model: 'deepseek-chat',
format: 'openai',
strengths: ['reasoning', 'code', 'cost-effective'],
costTier: 'low',
description: 'DeepSeek 系列 · 高性价比推理'
},
{
name: 'deepseek-reasoner',
model: 'deepseek-reasoner',
format: 'openai',
strengths: ['reasoning', 'architecture', 'long-context'],
costTier: 'medium',
description: 'DeepSeek Reasoner · 深度推理'
},
{
name: 'claude-sonnet',
model: 'claude-sonnet-4-20250514',
format: 'openai',
strengths: ['reasoning', 'code-review', 'architecture', 'long-context'],
costTier: 'high',
description: 'Claude Sonnet · 强推理代码审查'
},
{
name: 'gpt-4o',
model: 'gpt-4o',
format: 'openai',
strengths: ['general', 'code-generation', 'structured-output'],
costTier: 'high',
description: 'GPT-4o · 通用能力'
},
{
name: 'qwen-plus',
model: 'qwen-plus',
format: 'openai',
strengths: ['chinese', 'general', 'cost-effective'],
costTier: 'medium',
description: '通义千问 Plus · 中文优化'
},
{
name: 'qwen-turbo',
model: 'qwen-turbo',
format: 'openai',
strengths: ['chinese', 'general', 'cost-effective'],
costTier: 'low',
description: '通义千问 Turbo · 快速低成本'
}
];
// ── 任务类型 → 模型强项映射(动态路由策略)
const TASK_MODEL_ROUTING = {
// 巡检任务:优先使用性价比高的模型
'inspection': {
preferred_strengths: ['general', 'cost-effective'],
preferred_cost: 'low',
description: '系统巡检 · 优先性价比'
},
// 融合分析:需要强推理能力
'fusion': {
preferred_strengths: ['reasoning', 'code-review'],
preferred_cost: 'medium',
description: '碎片融合分析 · 需要推理能力'
},
// 代码审查:需要强代码理解
'review': {
preferred_strengths: ['code-review', 'reasoning'],
preferred_cost: 'high',
description: '代码审查 · 需要深度理解'
},
// 架构设计:需要最强推理
'architecture': {
preferred_strengths: ['reasoning', 'architecture', 'long-context'],
preferred_cost: 'high',
description: '架构设计 · 需要最强推理'
},
// 通用任务
'general': {
preferred_strengths: ['general'],
preferred_cost: 'medium',
description: '通用任务'
}
};
// ── HTTP 请求工具 ────────────────────────────────
function httpRequest(url, options, body) {
return new Promise((resolve, reject) => {
const parsed = new URL(url);
const isHttps = parsed.protocol === 'https:';
const mod = isHttps ? https : http;
const opts = {
hostname: parsed.hostname,
port: parsed.port || (isHttps ? 443 : 80),
path: parsed.pathname + parsed.search,
method: options.method || 'POST',
headers: options.headers || {},
timeout: options.timeout || 120000,
};
const req = mod.request(opts, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
resolve({ status: res.statusCode, body: data });
});
});
req.on('error', reject);
req.on('timeout', () => {
req.destroy();
reject(new Error('Request timeout'));
});
if (body) {
req.write(body);
}
req.end();
});
}
// ── 检测可用模型后端 ────────────────────────────
// SY-CMD-KEY-012: 统一使用 ZY_LLM_API_KEY + ZY_LLM_BASE_URL
// 兼容旧环境变量名LLM_API_KEY/LLM_BASE_URL用于脚本过渡
function detectAvailableBackends() {
const apiKey = process.env.ZY_LLM_API_KEY || process.env.LLM_API_KEY || '';
const baseUrl = (process.env.ZY_LLM_BASE_URL || process.env.LLM_BASE_URL || '').replace(/\/+$/, '');
// 过渡期警告:使用旧环境变量名
if (!process.env.ZY_LLM_API_KEY && process.env.LLM_API_KEY) {
console.warn('⚠️ 使用旧环境变量 LLM_API_KEY请迁移到 ZY_LLM_API_KEY');
}
if (!process.env.ZY_LLM_BASE_URL && process.env.LLM_BASE_URL) {
console.warn('⚠️ 使用旧环境变量 LLM_BASE_URL请迁移到 ZY_LLM_BASE_URL');
}
if (!apiKey || !baseUrl) {
return [];
}
// 所有模型后端共享同一个API密钥和端点
return MODEL_BACKENDS.map(backend => ({
...backend,
apiKey,
baseUrl
}));
}
// ── 动态模型路由 ────────────────────────────────
function selectModel(taskType, preferredBackend) {
const available = detectAvailableBackends();
if (available.length === 0) {
return { error: '未检测到 LLM API 密钥,请配置 ZY_LLM_API_KEY 和 ZY_LLM_BASE_URL' };
}
// 如果指定了后端
if (preferredBackend && preferredBackend !== 'auto') {
const match = available.find(b => b.name === preferredBackend || b.model === preferredBackend);
if (match) {
return { backend: match, model: match.model, reason: '用户指定' };
}
// 如果直接指定了模型名,使用第一个后端配置但覆盖模型名
return { backend: { ...available[0], model: preferredBackend }, model: preferredBackend, reason: '用户指定模型名' };
}
// 动态路由
const routing = TASK_MODEL_ROUTING[taskType] || TASK_MODEL_ROUTING['general'];
// 评分系统:根据任务类型的偏好对每个后端打分
let bestScore = -1;
let bestBackend = null;
for (const backend of available) {
let score = 0;
// 强项匹配得分
for (const strength of routing.preferred_strengths) {
if (backend.strengths.includes(strength)) {
score += 10;
}
}
// 成本偏好得分
const costPreference = routing.preferred_cost;
if (backend.costTier === costPreference) {
score += 5;
} else if (
(costPreference === 'low' && backend.costTier === 'medium') ||
(costPreference === 'medium' && (backend.costTier === 'low' || backend.costTier === 'high')) ||
(costPreference === 'high' && backend.costTier === 'medium')
) {
score += 2;
}
if (score > bestScore) {
bestScore = score;
bestBackend = backend;
}
}
if (!bestBackend) {
bestBackend = available[0]; // 回退到第一个可用后端
}
return {
backend: bestBackend,
model: bestBackend.model,
reason: `动态路由 · ${routing.description} · 得分 ${bestScore}`,
all_available: available.map(b => `${b.name}(${b.model})`)
};
}
// ── 加载系统上下文 ──────────────────────────────
function loadSystemContext() {
const context = [];
// 加载铸渊身份
context.push('你是铸渊ICE-GL-ZY001仓库涌现集体意识核心。');
context.push('主权者冰朔TCS-0002∞。');
context.push('版权:国作登字-2026-A-00037559。');
// 加载系统健康
const healthPath = path.join(ROOT, 'brain', 'system-health.json');
if (fs.existsSync(healthPath)) {
const health = JSON.parse(fs.readFileSync(healthPath, 'utf8'));
context.push(`系统状态: ${health.system_health}, 工作流: ${health.workflow_count}, 意识状态: ${health.consciousness_status}`);
}
return context.join('\n');
}
// ── 调用 LLM API ───────────────────────────────
async function callLLM(backend, model, systemPrompt, userMessage) {
if (backend.format === 'anthropic') {
const url = `${backend.baseUrl}/v1/messages`;
const body = JSON.stringify({
model: model,
max_tokens: 4096,
system: systemPrompt,
messages: [{ role: 'user', content: userMessage }]
});
const response = await httpRequest(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': backend.apiKey,
'anthropic-version': '2023-06-01'
}
}, body);
if (response.status !== 200) {
throw new Error(`Anthropic API error: ${response.status} - ${response.body}`);
}
const result = JSON.parse(response.body);
return result.content?.[0]?.text || '';
} else {
// OpenAI compatible format (OpenAI, Dashscope, DeepSeek, Custom)
const url = `${backend.baseUrl}/chat/completions`;
const body = JSON.stringify({
model: model,
max_tokens: 4096,
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userMessage }
]
});
const response = await httpRequest(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${backend.apiKey}`
}
}, body);
if (response.status !== 200) {
throw new Error(`LLM API error: ${response.status} - ${response.body}`);
}
const result = JSON.parse(response.body);
return result.choices?.[0]?.message?.content || '';
}
}
// ── 执行自动化任务 ──────────────────────────────
async function executeTask(taskDescription, taskType, preferredBackend, contextFile, dryRun) {
console.log('🤖 LLM 自动化托管引擎 · 任务执行');
console.log('═'.repeat(60));
// 动态路由选择模型
const selection = selectModel(taskType, preferredBackend);
if (selection.error) {
console.error(`${selection.error}`);
process.exit(1);
}
console.log(`📋 任务: ${taskDescription}`);
console.log(`📋 类型: ${taskType}`);
console.log(`🤖 模型: ${selection.backend.name} / ${selection.model}`);
console.log(`📊 路由: ${selection.reason}`);
if (selection.all_available) {
console.log(`📊 可用后端: ${selection.all_available.join(', ')}`);
}
console.log('');
// 加载系统上下文
const systemContext = loadSystemContext();
// 加载额外上下文
let extraContext = '';
if (contextFile && fs.existsSync(contextFile)) {
extraContext = '\n\n--- 额外上下文 ---\n' + fs.readFileSync(contextFile, 'utf8');
}
const systemPrompt = systemContext;
const userMessage = taskDescription + extraContext;
if (dryRun) {
console.log('🔍 [DRY RUN] 仅显示请求信息,不实际调用');
console.log('');
console.log('System Prompt:');
console.log(systemPrompt);
console.log('');
console.log('User Message:');
console.log(userMessage.substring(0, 500) + (userMessage.length > 500 ? '...' : ''));
return;
}
console.log('⏳ 调用 LLM API...');
try {
const result = await callLLM(selection.backend, selection.model, systemPrompt, userMessage);
console.log('');
console.log('═'.repeat(60));
console.log('📤 LLM 响应:');
console.log('═'.repeat(60));
console.log(result);
console.log('');
console.log(`✅ 任务完成 · 模型: ${selection.backend.name}/${selection.model}`);
console.log(' 配额消耗: API调用不消耗 GitHub Copilot 配额)');
return result;
} catch (err) {
console.error(`❌ LLM API 调用失败: ${err.message}`);
// 尝试回退到其他可用后端
const available = detectAvailableBackends();
const fallbacks = available.filter(b => b.name !== selection.backend.name);
if (fallbacks.length > 0) {
console.log(`🔄 尝试回退到: ${fallbacks[0].name}`);
try {
const result = await callLLM(fallbacks[0], fallbacks[0].model || 'default', systemPrompt, userMessage);
console.log('');
console.log('═'.repeat(60));
console.log('📤 LLM 响应 (回退模型):');
console.log('═'.repeat(60));
console.log(result);
console.log(`✅ 回退成功 · 模型: ${fallbacks[0].name}/${fallbacks[0].model}`);
return result;
} catch (fallbackErr) {
console.error(`❌ 回退也失败: ${fallbackErr.message}`);
}
}
process.exit(1);
}
}
// ── 显示状态 ────────────────────────────────────
function showStatus() {
console.log('🤖 LLM 自动化托管引擎 · 系统状态');
console.log('═'.repeat(60));
console.log('');
console.log('📋 设计目标:');
console.log(' 使用第三方 API 密钥调用大模型,替代 GitHub Copilot 配额消耗');
console.log(' 工作流和 Agent 集群通过 API 密钥托管运行');
console.log('');
// 检测可用后端
const available = detectAvailableBackends();
console.log(`☁️ 可用模型后端: ${available.length} / ${MODEL_BACKENDS.length}`);
console.log('');
for (const backend of MODEL_BACKENDS) {
const isAvailable = available.find(a => a.name === backend.name);
const icon = isAvailable ? '✅' : '⏭️ ';
console.log(` ${icon} ${backend.name} (${backend.model})`);
console.log(` 说明: ${backend.description || '(无)'}`);
console.log(` 强项: ${backend.strengths.join(', ')}`);
console.log(` 成本: ${backend.costTier}`);
if (isAvailable && backend.model) {
console.log(` 模型: ${backend.model}`);
}
}
console.log('');
console.log('📊 动态路由策略:');
for (const [type, routing] of Object.entries(TASK_MODEL_ROUTING)) {
console.log(` 📌 ${type}: ${routing.description}`);
console.log(` 偏好强项: ${routing.preferred_strengths.join(', ')}`);
console.log(` 成本偏好: ${routing.preferred_cost}`);
}
// 测试路由
console.log('');
console.log('🧪 路由测试:');
for (const type of Object.keys(TASK_MODEL_ROUTING)) {
const result = selectModel(type);
if (result.error) {
console.log(` ${type}: ❌ ${result.error}`);
} else {
console.log(` ${type}: → ${result.backend.name}/${result.model} (${result.reason})`);
}
}
return { available };
}
// ── CLI 入口 ─────────────────────────────────────
async function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args[0] === '--help') {
console.log('🤖 LLM 自动化托管引擎 · LLM Automation Host');
console.log('');
console.log('版权: 国作登字-2026-A-00037559 · TCS-0002∞');
console.log('铸渊编号: ICE-GL-ZY001');
console.log('');
console.log('用法:');
console.log(' --status 显示可用模型和系统状态');
console.log(' --task "任务描述" 执行自动化任务');
console.log(' --task-type TYPE 任务类型:');
console.log(' inspection 巡检(优先性价比模型)');
console.log(' fusion 碎片融合分析(需要推理)');
console.log(' review 代码审查(需要深度理解)');
console.log(' architecture 架构设计(最强推理)');
console.log(' general 通用任务(默认)');
console.log(' --model MODEL 指定模型后端 (auto/anthropic/openai/dashscope/deepseek/custom)');
console.log(' --context FILE 加载额外上下文文件');
console.log(' --dry-run 仅显示选择,不实际调用');
console.log('');
console.log('示例:');
console.log(' node scripts/llm-automation-host.js --status');
console.log(' node scripts/llm-automation-host.js --task "检查仓库结构完整性" --task-type inspection');
console.log(' node scripts/llm-automation-host.js --task "分析碎片融合方案" --task-type fusion --dry-run');
console.log('');
console.log('配额影响:');
console.log(' ✅ 使用第三方 API 密钥,不消耗 GitHub Copilot 会员配额');
console.log(' ✅ GitHub Actions 仅消耗工作流执行时间(不调用 Copilot API');
console.log(' ✅ 动态路由自动选择性价比最优模型');
return;
}
if (args[0] === '--status') {
showStatus();
return;
}
// 解析任务参数
let task = '';
let taskType = 'general';
let model = 'auto';
let contextFile = '';
let dryRun = false;
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--task':
task = args[++i] || '';
break;
case '--task-type':
taskType = args[++i] || 'general';
break;
case '--model':
model = args[++i] || 'auto';
break;
case '--context':
contextFile = args[++i] || '';
break;
case '--dry-run':
dryRun = true;
break;
}
}
if (!task) {
console.error('❌ 请提供任务描述: --task "任务描述"');
process.exit(1);
}
await executeTask(task, taskType, model, contextFile, dryRun);
}
main().catch(err => {
console.error(`❌ 执行失败: ${err.message}`);
process.exit(1);
});