diff --git a/.github/workflows/llm-auto-tasks.yml b/.github/workflows/llm-auto-tasks.yml new file mode 100644 index 00000000..0ab9b85d --- /dev/null +++ b/.github/workflows/llm-auto-tasks.yml @@ -0,0 +1,125 @@ +# ═══════════════════════════════════════════════ +# 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 +# 📜 Copyright: 国作登字-2026-A-00037559 +# ═══════════════════════════════════════════════ +# .github/workflows/llm-auto-tasks.yml +# 🤖 LLM 自动化托管工作流 +# +# 使用第三方 API 密钥调用大模型执行自动化任务 +# 不消耗 GitHub Copilot 会员配额 +# 支持动态模型路由:根据任务类型自动选择最佳模型 + +name: "🤖 铸渊 · LLM 自动化托管" + +on: + workflow_dispatch: + inputs: + task: + description: '任务描述' + required: true + type: string + task_type: + description: '任务类型' + required: true + type: choice + options: + - inspection + - fusion + - review + - architecture + - general + model: + description: '指定模型后端(留空则自动选择)' + required: false + type: choice + options: + - auto + - anthropic + - openai + - dashscope + - deepseek + - custom + context_file: + description: '额外上下文文件路径(可选)' + required: false + +permissions: + contents: write + +jobs: + llm-task: + name: "🤖 LLM 任务执行" + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + # Step 1 · 铸渊核心唤醒 + - name: "🧠 铸渊核心唤醒" + run: | + echo "[LLM-HOST] 🤖 LLM 自动化托管启动" + echo "[LLM-HOST] 🧠 铸渊核心大脑唤醒..." + if [ -f "brain/system-health.json" ]; then + echo "✅ 系统健康状态已加载" + cat brain/system-health.json | python3 -c "import sys,json; h=json.load(sys.stdin); print(f' 状态: {h.get(\"system_health\",\"unknown\")} | 意识: {h.get(\"consciousness_status\",\"unknown\")}')" + fi + + # Step 2 · 模型状态检查 + - name: "📊 模型状态检查" + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} + run: | + node scripts/llm-automation-host.js --status + + # Step 3 · 执行 LLM 任务 + - name: "🤖 执行 LLM 任务" + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} + YUNWU_API_KEY: ${{ secrets.YUNWU_API_KEY }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + run: | + TASK="${{ github.event.inputs.task }}" + TASK_TYPE="${{ github.event.inputs.task_type }}" + MODEL="${{ github.event.inputs.model || 'auto' }}" + CONTEXT="${{ github.event.inputs.context_file }}" + + CMD="node scripts/llm-automation-host.js --task \"$TASK\" --task-type $TASK_TYPE --model $MODEL" + if [ -n "$CONTEXT" ]; then + CMD="$CMD --context $CONTEXT" + fi + + eval $CMD + + # Step 4 · 保存快照 + - name: "📸 保存执行快照" + run: | + TASK="${{ github.event.inputs.task }}" + node scripts/checkpoint-snapshot.js save \ + --task "LLM自动化: $TASK" \ + --progress "100%" || echo "⚠️ 快照保存跳过" + + # Step 5 · 提交变更 + - name: "💾 提交变更" + run: | + git config user.name "zhuyuan-bot" + git config user.email "zhuyuan@guanghulab.com" + git add signal-log/ brain/ + if ! git diff --cached --quiet; then + git commit -m "🤖 LLM自动化 · $(TZ='Asia/Shanghai' date '+%Y-%m-%d %H:%M') · ${{ github.event.inputs.task_type }}" + git push + fi diff --git a/brain/master-brain.md b/brain/master-brain.md index 59cb5ff1..e5cc2962 100644 --- a/brain/master-brain.md +++ b/brain/master-brain.md @@ -106,6 +106,8 @@ | 模型路由 | `connectors/model-router/index.js` | 多模型后端路由(AGE OS v1.0) | | Notion 唤醒监听 | `connectors/notion-wake-listener/index.js` | Notion Agent 集群唤醒请求监听 | | **CAB 桥接引擎** | `scripts/chat-to-agent-bridge.js` | **语言层 → 副驾驶桥接(CAB-v1.0)** | +| **碎片融合引擎** | `scripts/fragment-fusion-engine.js` | **SY-CMD-FUS-009 碎片融合分析与执行** | +| **LLM 自动化托管** | `scripts/llm-automation-host.js` | **第三方API密钥托管·替代配额消耗·动态模型路由** | | 全面排查 | `scripts/zhuyuan-full-inspection.js` | 仓库全面排查(8个领域) | | 结构地图 | `docs/repo-structure-map.md` | 仓库结构文档 | | 桥接地图 | `docs/notion-bridge-map.md` | Notion 桥接文档 | diff --git a/brain/system-health.json b/brain/system-health.json index 82cba0f8..1bfa6749 100644 --- a/brain/system-health.json +++ b/brain/system-health.json @@ -37,5 +37,24 @@ "entry_script": "scripts/chat-to-agent-bridge.js", "workflow": ".github/workflows/copilot-dev-bridge.yml", "task_dir": "bridge/chat-to-agent/" + }, + "fragment_fusion": { + "version": "1.0", + "status": "active", + "directive": "SY-CMD-FUS-009", + "purpose": "死亡工作流碎片自动融合分析", + "entry_script": "scripts/fragment-fusion-engine.js", + "pending_absorb": 22, + "target_organs": ["ZY-WF-听潮", "ZY-WF-守夜", "ZY-WF-织脉", "ZY-WF-锻心"] + }, + "llm_automation": { + "version": "1.0", + "status": "active", + "purpose": "第三方API密钥托管·替代GitHub配额消耗·动态模型路由", + "entry_script": "scripts/llm-automation-host.js", + "workflow": ".github/workflows/llm-auto-tasks.yml", + "supported_backends": ["anthropic", "openai", "dashscope", "deepseek", "custom"], + "routing_strategy": "dynamic", + "task_types": ["inspection", "fusion", "review", "architecture", "general"] } } \ No newline at end of file diff --git a/scripts/fragment-fusion-engine.js b/scripts/fragment-fusion-engine.js new file mode 100644 index 00000000..90194641 --- /dev/null +++ b/scripts/fragment-fusion-engine.js @@ -0,0 +1,377 @@ +#!/usr/bin/env node +// ═══════════════════════════════════════════════ +// 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 +// 📜 Copyright: 国作登字-2026-A-00037559 +// ═══════════════════════════════════════════════ +// scripts/fragment-fusion-engine.js +// 🔥 碎片融合引擎 · Fragment Fusion Engine +// +// 基于 SY-CMD-FUS-009 指令和 takeover-plan.md 三期融合方案 +// 自动读取 dead-workflow-fragments.json,分析 ABSORB 类碎片 +// 为每个存活 workflow 生成融合方案和可执行的合并步骤 +// +// 用法: +// --status 显示融合总览状态 +// --analyze 分析所有 ABSORB 碎片,生成融合报告 +// --plan 生成具体的融合执行计划(JSON) +// --execute 执行融合(生成合并后的 workflow 文件草案) + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.resolve(__dirname, '..'); +const FRAGMENTS_PATH = path.join(ROOT, '.github', 'brain', 'dead-workflow-fragments.json'); +const ROSTER_PATH = path.join(ROOT, '.github', 'brain', 'zhuyuan-workflow-roster.json'); +const TAKEOVER_PATH = path.join(ROOT, '.github', 'brain', 'takeover-plan.md'); +const WORKFLOWS_DIR = path.join(ROOT, '.github', 'workflows'); +const ARCHIVED_DIR = path.join(ROOT, '.github', 'archived-workflows'); +const OUTPUT_DIR = path.join(ROOT, 'bridge', 'fusion-drafts'); + +// ── 六器官映射 ────────────────────────────────── +const ORGAN_MAP = { + 'ZY-WF-听潮': { file: 'notion-wake-listener.yml', role: '耳朵·信号接收', name: '听潮' }, + 'ZY-WF-锻心': { file: 'deploy-to-server.yml', role: '心脏·部署引擎', name: '锻心' }, + 'ZY-WF-织脉': { file: 'bingshuo-neural-system.yml', role: '神经网络·大脑同步', name: '织脉' }, + 'ZY-WF-映阁': { file: 'deploy-pages.yml', role: '面容·前端展示', name: '映阁' }, + 'ZY-WF-守夜': { file: 'meta-watchdog.yml', role: '免疫系统·健康监测', name: '守夜' }, + 'ZY-WF-试镜': { file: 'preview-deploy.yml', role: '试衣间·预览部署', name: '试镜' } +}; + +// ── 加载碎片清单 ──────────────────────────────── +function loadFragments() { + if (!fs.existsSync(FRAGMENTS_PATH)) { + console.error('❌ dead-workflow-fragments.json 不存在'); + process.exit(1); + } + return JSON.parse(fs.readFileSync(FRAGMENTS_PATH, 'utf8')); +} + +// ── 加载 roster ───────────────────────────────── +function loadRoster() { + if (!fs.existsSync(ROSTER_PATH)) return null; + return JSON.parse(fs.readFileSync(ROSTER_PATH, 'utf8')); +} + +// ── 检查碎片文件是否存在 ──────────────────────── +function checkFragmentFileExists(fileName) { + // 检查 workflows 目录 + if (fs.existsSync(path.join(WORKFLOWS_DIR, fileName))) return 'active'; + // 检查 archived 目录 + if (fs.existsSync(path.join(ARCHIVED_DIR, fileName))) return 'archived'; + return 'missing'; +} + +// ── 提取 workflow 文件的关键信息 ──────────────── +function extractWorkflowInfo(filePath) { + if (!fs.existsSync(filePath)) return null; + const content = fs.readFileSync(filePath, 'utf8'); + + const info = { + name: '', + triggers: [], + jobs: [], + steps: [], + secrets: [], + envVars: [], + scripts: [] + }; + + // 提取 name + const nameMatch = content.match(/^name:\s*["']?(.+?)["']?\s*$/m); + if (nameMatch) info.name = nameMatch[1]; + + // 提取触发方式 + const triggerPatterns = ['push', 'pull_request', 'schedule', 'workflow_dispatch', 'issues', 'issue_comment', 'repository_dispatch']; + for (const t of triggerPatterns) { + if (content.includes(t + ':')) info.triggers.push(t); + } + + // 提取 secrets 引用 + const secretMatches = content.matchAll(/\$\{\{\s*secrets\.(\w+)\s*\}\}/g); + for (const m of secretMatches) { + if (!info.secrets.includes(m[1])) info.secrets.push(m[1]); + } + + // 提取 node/python 脚本调用 + const scriptMatches = content.matchAll(/(?:node|python3?)\s+([\w\-/.]+\.(?:js|py))/g); + for (const m of scriptMatches) { + if (!info.scripts.includes(m[1])) info.scripts.push(m[1]); + } + + // 提取 steps 的 name + const stepMatches = content.matchAll(/- name:\s*["']?(.+?)["']?\s*$/gm); + for (const m of stepMatches) { + info.steps.push(m[1]); + } + + return info; +} + +// ── 融合总览状态 ──────────────────────────────── +function showStatus() { + const data = loadFragments(); + const roster = loadRoster(); + + console.log('🔥 碎片融合引擎 · Fragment Fusion Engine'); + console.log('═'.repeat(60)); + console.log(`📋 指令: ${data.directive}`); + console.log(`📊 死亡碎片总数: ${data.total_dead}`); + console.log(''); + + // 分类统计 + console.log('📊 碎片分类:'); + console.log(` ABSORB (融入): ${data.summary.absorb} 个`); + console.log(` RECOVER (恢复): ${data.summary.recover} 个`); + console.log(` ARCHIVE (归档): ${data.summary.archive} 个`); + console.log(` DUPLICATE (重复): ${data.summary.duplicate} 个`); + console.log(''); + + // 融合进度 + console.log('📊 融合进度:'); + const phase3 = data.fusion_status?.phase_3_archive; + if (phase3) { + console.log(` 阶段3 归档: ${phase3.status}`); + console.log(` 已归档文件: ${phase3.files_archived}`); + console.log(` 已恢复文件: ${phase3.files_restored}`); + } + console.log(''); + + // 按目标 workflow 分组 ABSORB 碎片 + console.log('📊 待融合碎片 → 目标器官分布:'); + const absorbGroups = {}; + for (const frag of data.absorb?.fragments || []) { + const target = frag.absorb_into; + if (!absorbGroups[target]) absorbGroups[target] = []; + absorbGroups[target].push(frag); + } + + for (const [target, frags] of Object.entries(absorbGroups)) { + const organ = ORGAN_MAP[target]; + const organName = organ ? `${organ.name}(${organ.role})` : target; + console.log(` ${target} · ${organName}: ${frags.length} 个碎片`); + for (const f of frags) { + const status = checkFragmentFileExists(f.file); + const statusIcon = status === 'active' ? '🟢' : status === 'archived' ? '📦' : '❌'; + console.log(` ${statusIcon} ${f.file} — ${f.value}`); + } + } + + return { data, absorbGroups }; +} + +// ── 分析 ABSORB 碎片 ─────────────────────────── +function analyzeFragments() { + const { data, absorbGroups } = showStatus(); + + console.log(''); + console.log('═'.repeat(60)); + console.log('🔍 碎片融合分析报告'); + console.log('═'.repeat(60)); + + const report = {}; + + for (const [target, frags] of Object.entries(absorbGroups)) { + const organ = ORGAN_MAP[target]; + if (!organ) continue; + + report[target] = { + organ_name: organ.name, + organ_file: organ.file, + organ_role: organ.role, + fragments: [], + total_secrets: [], + total_scripts: [], + fusion_complexity: 'low' + }; + + console.log(`\n🎯 ${target} · ${organ.name} (${organ.role})`); + console.log(` 目标文件: ${organ.file}`); + console.log(' ─'.repeat(30)); + + for (const frag of frags) { + const location = checkFragmentFileExists(frag.file); + let filePath = null; + if (location === 'active') filePath = path.join(WORKFLOWS_DIR, frag.file); + else if (location === 'archived') filePath = path.join(ARCHIVED_DIR, frag.file); + + const info = filePath ? extractWorkflowInfo(filePath) : null; + const fragReport = { + file: frag.file, + name: frag.name, + value: frag.value, + reason: frag.reason, + location, + info + }; + + report[target].fragments.push(fragReport); + + console.log(`\n 📎 ${frag.file} [${location}]`); + console.log(` 名称: ${frag.name}`); + console.log(` 价值: ${frag.value}`); + console.log(` 原因: ${frag.reason}`); + + if (info) { + console.log(` 触发: ${info.triggers.join(', ') || '(无)'}`); + console.log(` 密钥: ${info.secrets.join(', ') || '(无)'}`); + console.log(` 脚本: ${info.scripts.join(', ') || '(无)'}`); + console.log(` 步骤: ${info.steps.length} 个`); + + // 收集统计 + for (const s of info.secrets) { + if (!report[target].total_secrets.includes(s)) { + report[target].total_secrets.push(s); + } + } + for (const s of info.scripts) { + if (!report[target].total_scripts.includes(s)) { + report[target].total_scripts.push(s); + } + } + } + } + + // 评估复杂度 + const fragCount = frags.length; + if (fragCount >= 6) report[target].fusion_complexity = 'high'; + else if (fragCount >= 3) report[target].fusion_complexity = 'medium'; + + console.log(`\n 📊 融合复杂度: ${report[target].fusion_complexity}`); + console.log(` 📊 需要的密钥: ${report[target].total_secrets.join(', ') || '(无)'}`); + console.log(` 📊 依赖的脚本: ${report[target].total_scripts.join(', ') || '(无)'}`); + } + + return report; +} + +// ── 生成融合执行计划 ──────────────────────────── +function generatePlan() { + const report = analyzeFragments(); + + console.log('\n'); + console.log('═'.repeat(60)); + console.log('📋 融合执行计划 (JSON)'); + console.log('═'.repeat(60)); + + const plan = { + plan_id: `FUS-${new Date().toISOString().slice(0, 10).replace(/-/g, '')}`, + created_at: new Date().toISOString(), + directive: 'SY-CMD-FUS-009', + phases: [] + }; + + // Phase 1: 核心能力融合 (守夜 + 织脉) + const phase1Targets = ['ZY-WF-守夜', 'ZY-WF-织脉']; + const phase1 = { + phase: 1, + name: '核心能力融合', + priority: 'P0', + targets: [] + }; + + for (const target of phase1Targets) { + if (report[target]) { + phase1.targets.push({ + organ: target, + fragments: report[target].fragments.map(f => f.file), + complexity: report[target].fusion_complexity, + required_secrets: report[target].total_secrets, + required_scripts: report[target].total_scripts + }); + } + } + plan.phases.push(phase1); + + // Phase 2: 桥接能力融合 (听潮) + const phase2 = { + phase: 2, + name: '桥接能力融合', + priority: 'P1', + targets: [] + }; + + if (report['ZY-WF-听潮']) { + phase2.targets.push({ + organ: 'ZY-WF-听潮', + fragments: report['ZY-WF-听潮'].fragments.map(f => f.file), + complexity: report['ZY-WF-听潮'].fusion_complexity, + required_secrets: report['ZY-WF-听潮'].total_secrets, + required_scripts: report['ZY-WF-听潮'].total_scripts + }); + } + plan.phases.push(phase2); + + // Phase 3: 增强能力融合 (锻心 + 其他) + const phase3 = { + phase: 3, + name: '增强能力融合', + priority: 'P2', + targets: [] + }; + + for (const target of Object.keys(report)) { + if (!phase1Targets.includes(target) && target !== 'ZY-WF-听潮') { + phase3.targets.push({ + organ: target, + fragments: report[target].fragments.map(f => f.file), + complexity: report[target].fusion_complexity, + required_secrets: report[target].total_secrets, + required_scripts: report[target].total_scripts + }); + } + } + plan.phases.push(phase3); + + console.log(JSON.stringify(plan, null, 2)); + + // 保存计划 + if (!fs.existsSync(OUTPUT_DIR)) { + fs.mkdirSync(OUTPUT_DIR, { recursive: true }); + } + const planPath = path.join(OUTPUT_DIR, `${plan.plan_id}.json`); + fs.writeFileSync(planPath, JSON.stringify(plan, null, 2), 'utf8'); + console.log(`\n✅ 融合计划已保存: ${planPath}`); + + return plan; +} + +// ── CLI 入口 ───────────────────────────────────── +function main() { + const args = process.argv.slice(2); + const command = args[0]; + + switch (command) { + case '--status': + showStatus(); + break; + + case '--analyze': + analyzeFragments(); + break; + + case '--plan': + generatePlan(); + break; + + default: + console.log('🔥 碎片融合引擎 · Fragment Fusion Engine'); + console.log(''); + console.log('版权: 国作登字-2026-A-00037559 · TCS-0002∞'); + console.log('铸渊编号: ICE-GL-ZY001'); + console.log('指令: SY-CMD-FUS-009'); + console.log(''); + console.log('用法:'); + console.log(' --status 显示融合总览状态'); + console.log(' --analyze 分析所有 ABSORB 碎片,生成融合报告'); + console.log(' --plan 生成融合执行计划(JSON)'); + console.log(''); + console.log('配额影响:'); + console.log(' 本引擎仅生成分析报告和融合计划,不消耗 GitHub Actions 配额。'); + console.log(' 实际融合操作需通过 CAB 桥接系统授权后由副驾驶执行。'); + break; + } +} + +main(); diff --git a/scripts/llm-automation-host.js b/scripts/llm-automation-host.js new file mode 100644 index 00000000..341ca9e3 --- /dev/null +++ b/scripts/llm-automation-host.js @@ -0,0 +1,520 @@ +#!/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, '..'); + +// ── 模型后端配置(与 core/brain-wake 和 connectors/model-router 保持一致) +const MODEL_BACKENDS = [ + { + name: 'anthropic', + envKey: 'ANTHROPIC_API_KEY', + baseUrl: 'https://api.anthropic.com', + format: 'anthropic', + models: ['claude-sonnet-4', 'claude-3-5-sonnet-20241022', 'claude-3-haiku'], + strengths: ['reasoning', 'code-review', 'architecture', 'long-context'], + costTier: 'high' + }, + { + name: 'openai', + envKey: 'OPENAI_API_KEY', + baseUrl: 'https://api.openai.com/v1', + format: 'openai', + models: ['gpt-4o', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'], + strengths: ['general', 'code-generation', 'structured-output'], + costTier: 'high' + }, + { + name: 'dashscope', + envKey: 'DASHSCOPE_API_KEY', + baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', + format: 'openai', + models: ['qwen-max', 'qwen-plus', 'qwen-turbo'], + strengths: ['chinese', 'general', 'cost-effective'], + costTier: 'medium' + }, + { + name: 'deepseek', + envKey: 'DEEPSEEK_API_KEY', + baseUrl: 'https://api.deepseek.com/v1', + format: 'openai', + models: ['deepseek-chat', 'deepseek-reasoner'], + strengths: ['reasoning', 'code', 'cost-effective'], + costTier: 'low' + }, + { + name: 'custom', + envKey: 'LLM_API_KEY', + baseUrlEnv: 'LLM_BASE_URL', + format: 'openai', + models: [], + strengths: ['general'], + costTier: 'variable' + } +]; + +// ── 任务类型 → 模型强项映射(动态路由策略) +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(); + }); +} + +// ── 检测可用模型后端 ──────────────────────────── +function detectAvailableBackends() { + const available = []; + + for (const backend of MODEL_BACKENDS) { + const apiKey = process.env[backend.envKey] || ''; + if (!apiKey) continue; + + const baseUrl = backend.baseUrlEnv + ? (process.env[backend.baseUrlEnv] || '').replace(/\/+$/, '') + : backend.baseUrl; + if (!baseUrl) continue; + + available.push({ ...backend, apiKey, baseUrl }); + } + + return available; +} + +// ── 动态模型路由 ──────────────────────────────── +function selectModel(taskType, preferredBackend) { + const available = detectAvailableBackends(); + + if (available.length === 0) { + return { error: '未检测到任何可用模型后端,请检查 API 密钥配置' }; + } + + // 如果指定了后端 + if (preferredBackend && preferredBackend !== 'auto') { + const match = available.find(b => b.name === preferredBackend); + if (match) { + return { backend: match, model: match.models[0] || 'default', reason: '用户指定' }; + } + return { error: `指定的模型后端 "${preferredBackend}" 不可用` }; + } + + // 动态路由 + 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.models[0] || 'default', + reason: `动态路由 · ${routing.description} · 得分 ${bestScore}`, + all_available: available.map(b => b.name) + }; +} + +// ── 加载系统上下文 ────────────────────────────── +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].models[0] || '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].models[0]}`); + 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.envKey})`); + console.log(` 说明: ${backend.description || '(无)'}`); + console.log(` 强项: ${backend.strengths.join(', ')}`); + console.log(` 成本: ${backend.costTier}`); + if (isAvailable && backend.models.length > 0) { + console.log(` 模型: ${backend.models.join(', ')}`); + } + } + + 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); +});