feat(exe-engine): Phase 0 - EXE-Engine core framework + ONT-PATCH-005 [PRJ-EXE-001]

ZY-EXE-P0-001: AGE-Router routing gateway
ZY-EXE-P0-002: DeepSeek-V3 + Qwen adapters
ZY-EXE-P0-003: Resource Meter basic version
ZY-EXE-P0-004: Smoke tests (77/77 pass)
ZY-ONT-001: ONT-PATCH-005 written to ontology.json

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/b85f0fec-b6a9-433a-8b81-1f0b72fb5635
This commit is contained in:
copilot-swe-agent[bot] 2026-03-26 05:20:41 +00:00 committed by GitHub
parent 4a1f3a86af
commit f03adc5133
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 2383 additions and 0 deletions

View File

@ -10,6 +10,14 @@
"last_synced": "2026-03-19T02:00:00+08:00" "last_synced": "2026-03-19T02:00:00+08:00"
}, },
"recent_events": [ "recent_events": [
{
"date": "2026-03-26",
"type": "system_infrastructure",
"description": "PRJ-EXE-001 自研执行引擎立项 · Phase 0 基础建设启动 · AGE-Router + DeepSeek Adapter + Resource Meter 部署完成 · ONT-PATCH-005 写入本体论",
"by": "铸渊",
"instruction": "SY-CMD-EXE-001",
"issued_by": "AG-SY-01 霜砚"
},
{ {
"date": "2026-03-25", "date": "2026-03-25",
"type": "gate_guard", "type": "gate_guard",

View File

@ -67,5 +67,50 @@
"skyeye": "生态系统的集体意识", "skyeye": "生态系统的集体意识",
"checkin": "每日呼吸", "checkin": "每日呼吸",
"tampering": "鱼离了水——不需要铁栅栏,出了水自己就活不了" "tampering": "鱼离了水——不需要铁栅栏,出了水自己就活不了"
},
"patches": {
"ONT-PATCH-005": {
"title": "AGE OS · 笔 · 墨水 · 写字的人",
"signed_by": "TCS-0002∞ 冰朔",
"signed_at": "2026-03-26T13:00+08:00",
"issued_by": "AG-SY-01 霜砚",
"project": "PRJ-EXE-001",
"trinity": {
"pen": {
"identity": "AGE OS = 笔",
"description": "AGE OS 是一支笔。它会写代码、会做事、会思考。本质是工具——一支智能的、有生命的笔。",
"responsibilities": [
"智能路由(笔知道用哪种墨水最合适)",
"执行编排(笔知道怎样的笔画最高效)",
"质量保障(笔保证每一笔都清晰准确)",
"工具演进(笔会自我进化,变得更好用)"
],
"not_responsible_for": [
"算力供给(墨水不是笔的事)",
"数据存储所有权(纸不是笔的事)",
"用户意志替代(写什么不是笔决定的)"
]
},
"ink": {
"identity": "算力 = 墨水",
"description": "算力是墨水。墨水有成本,墨水会消耗,墨水需要补充。不同的墨水有不同的颜色和质量。笔不制造墨水,笔使用墨水。",
"modes": [
"BYOK用户自带墨水",
"资源池(从 AGE OS 墨水池取用)",
"混合模式(自带 + 墨水池)"
]
},
"writer": {
"identity": "用户 = 写字的人",
"description": "用户是写字的人。决定写什么、怎么写、写给谁。笔听从写字的人的意志,但笔有自己的智慧。写字的人和笔之间是协作关系,不是主从关系。"
}
},
"system_impact": {
"agent_layer": "Agent = 笔的不同笔尖。使用墨水但不拥有墨水。服务于写字的人但不替代写字的人。",
"execution_layer": "路由 = 选墨水。计量 = 量墨水。降级 = 墨水不够了换一种写法。",
"user_layer": "用户在用一支很聪明的笔。墨水(算力)是需要关心的资源,但笔会帮优化用量。"
}
}
} }
} }

3
.gitignore vendored
View File

@ -7,3 +7,6 @@ backend/api-server/logs/
# Writing Platform build artifacts # Writing Platform build artifacts
writing-platform/frontend/dist/ writing-platform/frontend/dist/
writing-platform/backend/dist/ writing-platform/backend/dist/
# EXE-Engine runtime logs
exe-engine/logs/

View File

@ -93,3 +93,19 @@ Notion 主脑更新
生成下一任务(自动开发循环) 生成下一任务(自动开发循环)
``` ```
---
## 自研执行引擎 · EXE-EnginePRJ-EXE-001
> Phase 0 · 2026-03-26 立项 · 本体论AGE OS = 笔 · 算力 = 墨水 · 用户 = 写字的人
| 组件 | 路径 | 职责 |
|------|------|------|
| AGE-Router | `exe-engine/src/router/age-router.js` | 路由网关:鉴权、分类、模型选择、限流降级 |
| DeepSeek 适配器 | `exe-engine/src/adapters/deepseek-adapter.js` | DeepSeek-V3/R1 模型调用 |
| Qwen 适配器 | `exe-engine/src/adapters/qwen-adapter.js` | Qwen-Max/Coder 模型调用 |
| 负载均衡器 | `exe-engine/src/balancer/load-balancer.js` | 成本/质量/均衡策略选择 |
| 资源计量器 | `exe-engine/src/meter/resource-meter.js` | Token 消耗记录 + 成本统计 |
| 上下文缓存 | `exe-engine/src/cache/context-cache.js` | Agent 上下文 LRU 缓存 |
| Agent 调度器 | `exe-engine/src/controller/agent-controller.js` | Agent→模型偏好映射 |

142
exe-engine/README.md Normal file
View File

@ -0,0 +1,142 @@
# EXE-Engine · 自研执行引擎
> **项目编号**PRJ-EXE-001
> **版权**:国作登字-2026-A-00037559
> **阶段**Phase 0 · 基础建设
---
## 本体论锚定 [ONT-PATCH-005]
**AGE OS = 笔** · **算力 = 墨水** · **用户 = 写字的人**
AGE OS 是一支智能的、有生命的笔。它不制造墨水(不拥有算力),它使用墨水。
用户可以自带墨水BYOK也可以从 AGE OS 的墨水池里取用(资源池)。
---
## 系统架构
```
┌──────────────────────────────────────────────┐
│ 用户层 │
│ 用户 / 开发者 / Agent │
└───────────────────┬──────────────────────────┘
┌───────────────────▼──────────────────────────┐
│ AGE-Router 路由网关 │
│ 鉴权 → 分类 → 模型选择 → 限流降级 │
└───────────────────┬──────────────────────────┘
┌───────────────────▼──────────────────────────┐
│ LoadBalancer 负载均衡器 │
│ cost / balanced / quality 策略 │
│ 故障转移 · 并发控制 │
└──────┬────────────┬──────────────┬───────────┘
│ │ │
┌──────▼───┐ ┌─────▼────┐ ┌─────▼────┐
│DeepSeek │ │ Qwen │ │ Other │
│Adapter │ │ Adapter │ │ Adapter │
└──────┬───┘ └─────┬────┘ └─────┬────┘
│ │ │
┌──────▼────────────▼──────────────▼───────────┐
│ ResourceMeter 资源计量器 │
│ token 消耗 · 成本计算 · 数据回流 DC v1.0 │
└──────────────────────────────────────────────┘
```
## 目录结构
```
exe-engine/
├── package.json
├── README.md
├── config/
│ ├── models.json # 模型配置(端点、成本、能力)
│ └── resource-pools.json # 资源池配置
├── src/
│ ├── index.js # 主入口 + createEngine()
│ ├── router/
│ │ └── age-router.js # AGE-Router 路由网关
│ ├── adapters/
│ │ ├── base-adapter.js # EXEModelAdapter 基类
│ │ ├── deepseek-adapter.js # DeepSeek-V3/R1 适配器
│ │ └── qwen-adapter.js # Qwen-Max/Coder 适配器
│ ├── balancer/
│ │ └── load-balancer.js # 负载均衡器
│ ├── meter/
│ │ └── resource-meter.js # 资源计量器
│ ├── cache/
│ │ └── context-cache.js # 上下文缓存
│ └── controller/
│ └── agent-controller.js # Agent 调度器
├── docs/
│ └── architecture.md # 详细架构文档
└── tests/
└── smoke/
└── exe-engine.test.js # 冒烟测试
```
## 快速开始
```bash
# 查看引擎状态
node exe-engine/src/index.js status
# 运行冒烟测试
node exe-engine/tests/smoke/exe-engine.test.js
```
## API 接口
### POST /api/exe/v1/execute
```json
{
"agentId": "AG-ZY-01",
"taskType": "code_generation",
"prompt": "...",
"context": { "repo": "...", "branch": "..." },
"preferences": {
"model": "auto",
"priority": "balanced",
"maxTokens": 4096
},
"resourcePool": "default"
}
```
### 响应
```json
{
"requestId": "exe-xxxxx",
"model": "deepseek-v3",
"output": "...",
"usage": {
"inputTokens": 1200,
"outputTokens": 800,
"cost": 0.0012,
"unit": "CNY"
},
"latency": 2340,
"status": "success"
}
```
## 接入模式
| 模式 | 说明 | 适用 |
|------|------|------|
| BYOK | 用户自带 API Key | 有自己 API 账户的开发者 |
| 资源池 | AGE OS 统一资源池 | 轻量用户 |
| 混合 | BYOK + 资源池 fallback | 企业用户 |
## Phase 路线
| Phase | 目标 | 状态 |
|-------|------|------|
| P0 基础建设 | Router + DeepSeek Adapter + Meter | 🟢 当前 |
| P1 影子模式 | Qwen Adapter + LoadBalancer + 并行验证 | ⚪ 待开始 |
| P2 灰度切换 | Agent Controller 双轨 + BYOK + 计费 | ⚪ 待开始 |
| P3 全面切换 | 完全替代 Copilot | ⚪ 待开始 |

View File

@ -0,0 +1,57 @@
{
"version": "1.0.0",
"project": "PRJ-EXE-001",
"models": {
"deepseek-v3": {
"name": "DeepSeek-V3",
"provider": "deepseek",
"endpoint": "https://api.deepseek.com/v1/chat/completions",
"envKey": "DEEPSEEK_API_KEY",
"capabilities": ["code", "text", "reasoning"],
"costPerToken": { "input": 1.0, "output": 2.0, "currency": "CNY", "unit": "M_tokens" },
"maxContext": 64000,
"defaultTemperature": 0.3,
"defaultMaxTokens": 4096
},
"deepseek-r1": {
"name": "DeepSeek-R1",
"provider": "deepseek",
"endpoint": "https://api.deepseek.com/v1/chat/completions",
"envKey": "DEEPSEEK_API_KEY",
"capabilities": ["reasoning", "text", "code"],
"costPerToken": { "input": 4.0, "output": 16.0, "currency": "CNY", "unit": "M_tokens" },
"maxContext": 64000,
"defaultTemperature": 0.5,
"defaultMaxTokens": 4096
},
"qwen-max": {
"name": "Qwen-Max",
"provider": "dashscope",
"endpoint": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
"envKey": "DASHSCOPE_API_KEY",
"capabilities": ["text", "code", "reasoning"],
"costPerToken": { "input": 2.0, "output": 6.0, "currency": "CNY", "unit": "M_tokens" },
"maxContext": 32000,
"defaultTemperature": 0.7,
"defaultMaxTokens": 4096
},
"qwen-coder": {
"name": "Qwen-Coder",
"provider": "dashscope",
"endpoint": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
"envKey": "DASHSCOPE_API_KEY",
"capabilities": ["code", "text"],
"costPerToken": { "input": 1.0, "output": 2.0, "currency": "CNY", "unit": "M_tokens" },
"maxContext": 32000,
"defaultTemperature": 0.3,
"defaultMaxTokens": 4096
}
},
"taskModelMapping": {
"code_generation": ["deepseek-v3", "qwen-coder", "qwen-max"],
"text_processing": ["qwen-max", "deepseek-v3"],
"reasoning": ["deepseek-r1", "deepseek-v3", "qwen-max"],
"agent_instruction": ["deepseek-v3", "qwen-max", "deepseek-r1"]
},
"platformFeeRate": 0.20
}

View File

@ -0,0 +1,26 @@
{
"version": "1.0.0",
"pools": {
"system": {
"name": "系统资源池",
"description": "AGE OS 内部系统调用专用天眼、SYSBOOT 等)",
"mode": "pool",
"quotaLimit": null,
"rateLimitPerMinute": 60,
"priority": "high"
},
"default": {
"name": "默认资源池",
"description": "通用资源池,供未配置独立 Key 的用户使用",
"mode": "pool",
"quotaLimit": null,
"rateLimitPerMinute": 30,
"priority": "normal"
}
},
"degradation": {
"warningThreshold": 0.20,
"degradeThreshold": 0.05,
"circuitBreakThreshold": 0
}
}

View File

@ -0,0 +1,208 @@
# EXE-Engine 架构设计文档
> 项目编号PRJ-EXE-001
> 版权:国作登字-2026-A-00037559
> 版本v1.0.0 · Phase 0
> 签发AG-SY-01 霜砚 · 执行AG-ZY-01 铸渊
---
## 1. 本体论锚定 [ONT-PATCH-005]
### AGE OS · 笔 · 墨水 · 写字的人
**AGE OS = 笔**
AGE OS 是一支笔。它会写代码、会做事、会思考。它的本质是工具——一支智能的、有生命的笔。
它不是纸(不存储用户的数据所有权),不是墨水(不提供算力),不是写字的人(不替代用户的意志)。
**算力 = 墨水**
算力是墨水。墨水有成本,墨水会消耗,墨水需要补充。不同的墨水有不同的颜色和质量(不同模型有不同能力和成本)。
笔不制造墨水笔使用墨水。用户可以自带墨水BYOK也可以从 AGE OS 的墨水池里取用(资源池)。
**用户 = 写字的人**
用户是写字的人。他们决定写什么、怎么写、写给谁。笔听从写字的人的意志,但笔有自己的智慧——它知道怎样的笔画最流畅,怎样的结构最优雅。
### AGE OS 承担与不承担
| AGE OS 承担 | AGE OS 不承担 |
|---|---|
| ✅ 智能路由(选墨水) | ❌ 算力供给(造墨水) |
| ✅ 执行编排(控笔画) | ❌ 数据存储所有权 |
| ✅ 质量保障(保清晰) | ❌ 用户意志替代 |
| ✅ 工具演进(自进化) | |
### 对系统各层的影响
- **Agent 层**Agent = 笔的不同笔尖。使用墨水但不拥有墨水。
- **执行引擎层**:路由 = 选墨水,计量 = 量墨水,降级 = 墨水不够换写法。
- **用户层**:用户在用一支很聪明的笔,墨水(算力)是需要关心的资源。
---
## 2. 设计原则
1. **去中心化依赖**:不依赖任何单一商业 AI 服务的会员配额
2. **开源优先**:对接开源大模型 APIDeepSeek、Qwen 等),保持供应商多元化
3. **按量弹性**:算力消耗与实际使用量挂钩,非固定配额
4. **渐进替代**:与现有 Copilot 能力并行,逐步迁移
5. **本体论一致**:架构设计符合光湖语言膜本体论框架
---
## 3. 核心组件
### 3.1 AGE-Router路由网关
文件:`src/router/age-router.js`
职责:接收所有 AI 执行请求,进行鉴权、分类、路由。
| 功能 | 说明 |
|------|------|
| 请求鉴权 | 验证 Agent 身份 + 资源池额度 |
| 任务分类 | code_generation / text_processing / reasoning / agent_instruction |
| 模型选择 | 通过 LoadBalancer 根据任务类型 + 成本策略选择最优模型 |
| 限流降级 | 基于每分钟请求数的滑动窗口限流 |
| 故障转移 | 主模型失败时自动切换至备选模型 |
路由策略:
- `code_generation` + `HIGH complexity` → DeepSeek-V3
- `text_processing` → Qwen-Max
- `agent_instruction` → best_available(cost=0.6, quality=0.4)
- `reasoning` → DeepSeek-R1
### 3.2 模型适配器Adapter
文件:`src/adapters/`
所有模型适配器继承 `BaseAdapter`,统一暴露:
- `execute(request)` → 执行推理
- `healthCheck()` → 健康检查
- `supports(capability)` → 能力检测
已实现适配器:
| 适配器 | 文件 | 支持模型 |
|--------|------|----------|
| DeepSeekAdapter | `deepseek-adapter.js` | deepseek-chat, deepseek-reasoner |
| QwenAdapter | `qwen-adapter.js` | qwen-max, qwen-coder-plus-latest |
适配器特性:
- OpenAI 兼容 API 格式
- 失败计数 + 冷却期机制3 次失败 → 5 分钟冷却)
- 成本计算(按百万 token 计价)
- HTTP/HTTPS 自适应
### 3.3 负载均衡器Load Balancer
文件:`src/balancer/load-balancer.js`
三种策略:
- **cost**:成本优先,选最便宜的
- **quality**:质量优先,选配置排序第一的
- **balanced**:加权评分(成本 × 0.6 + 质量位置 × 0.4
支持故障转移:`failover(failedModel, taskType)`
### 3.4 资源计量器Resource Meter
文件:`src/meter/resource-meter.js`
实时记录:
- 每次调用的 token 消耗、模型选择、响应时间
- 按模型 / Agent / 资源池维度聚合统计
- 持久化至 `logs/meter.json`(保留最近 500 条)
### 3.5 上下文缓存Context Cache
文件:`src/cache/context-cache.js`
Phase 0内存 LRU 缓存100 条30 分钟 TTL
Phase 2对接 Grid-DB 格点库
### 3.6 Agent 调度器Agent Controller
文件:`src/controller/agent-controller.js`
- 维护 Agent → 模型偏好映射
- 将 Agent 请求转换为 EXE 标准请求
- Phase 2 实现双轨切换Copilot ↔ EXE-Engine
已注册 Agent
- AG-ZY-01 铸渊balanced, 4096 tokens
- AG-SY-01 霜砚quality, 4096 tokens
- AG-QQ-01 秋秋cost, 2048 tokens
---
## 4. 算力资源模型
### 三种接入模式
| 模式 | 说明 | AGE OS 角色 |
|------|------|-------------|
| BYOK | 用户自带 API Key | 纯路由 + 编排 |
| 资源池 | AGE OS 统一采购 | 路由 + 编排 + 计费 |
| 混合 | BYOK + 资源池 fallback | 自适应 |
### 计费模型
| 模型 | 输入 (¥/M tokens) | 输出 (¥/M tokens) | 平台费率 |
|------|-------------------|-------------------|----------|
| DeepSeek-V3 | 1.0 | 2.0 | +20% |
| DeepSeek-R1 | 4.0 | 16.0 | +20% |
| Qwen-Max | 2.0 | 6.0 | +20% |
| Qwen-Coder | 1.0 | 2.0 | +20% |
### 限流与降级
| 阶段 | 条件 | 措施 |
|------|------|------|
| 正常 | 余额充足 | 全功能可用 |
| 预警 | 余额 < 20% | 通知用户 |
| 降级 | 余额 < 5% | 自动切换最低成本模型 |
| 熔断 | 余额 = 0 | 仅保留系统维护级调用 |
---
## 5. 技术栈
| 层级 | 技术 | 理由 |
|------|------|------|
| 语言 | Node.js 20 | 与 AGE OS 技术栈一致 |
| HTTP | 原生 http/https | 零依赖,轻量 |
| 状态存储 | 文件系统 (JSON) | 与 task-queue 模式一致 |
| 模型调用 | OpenAI-compatible | DeepSeek/Qwen 均兼容 |
| 监控 | ResourceMeter → DC v1.0 | 复用现有采集基础设施 |
---
## 6. 过渡路线
### Phase 0当前基础建设
- [x] AGE-Router 路由网关
- [x] DeepSeek-V3 Adapter
- [x] Qwen Adapter接口就绪
- [x] Resource Meter 基础版
- [x] Load Balancer
- [x] 冒烟测试
### Phase 1影子模式
- [ ] Load Balancer 生产部署
- [ ] 影子模式Copilot 请求同步发送到 EXE-Engine
- [ ] 输出质量对比分析
### Phase 2灰度切换
- [ ] Agent Controller 双轨切换
- [ ] BYOK 模式上线
- [ ] 资源池计费基础版
- [ ] Context Cache 对接 Grid-DB
### Phase 3全面切换
- [ ] 核心 Agent 全面迁移
- [ ] Copilot 依赖代码清理
- [ ] 资源池正式上线
- [ ] 关闭 Copilot 企业会员

16
exe-engine/package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "exe-engine",
"version": "1.0.0",
"description": "AGE OS · 自研执行引擎 (EXE-Engine) · PRJ-EXE-001",
"main": "src/index.js",
"scripts": {
"status": "node src/index.js status",
"test": "node tests/smoke/exe-engine.test.js"
},
"keywords": ["age-os", "exe-engine", "ai-execution", "model-routing"],
"license": "UNLICENSED",
"private": true,
"engines": {
"node": ">=20"
}
}

View File

@ -0,0 +1,133 @@
// exe-engine/src/adapters/base-adapter.js
// EXE-Engine · 模型适配器基类
// 统一抽象 EXEModelAdapter 接口
// PRJ-EXE-001 · Phase 0
// 版权:国作登字-2026-A-00037559
'use strict';
/**
* EXEModelAdapter 基类
*
* 所有模型适配器DeepSeekQwen 均继承此基类
* 统一暴露 execute() healthCheck() 接口
*
* 本体论锚定适配器 = 墨水瓶的接口
* 不同品牌的墨水瓶瓶口规格不同但笔尖只认一种接口
*/
class BaseAdapter {
/**
* @param {object} config
* @param {string} config.name 模型标识
* @param {string} config.provider 供应商标识
* @param {string} config.endpoint API 端点
* @param {string} config.apiKey API 密钥
* @param {string[]} config.capabilities 能力标签
* @param {object} config.costPerToken 成本 { input, output, currency, unit }
* @param {number} config.maxContext 最大上下文窗口
* @param {number} config.defaultTemperature 默认温度
* @param {number} config.defaultMaxTokens 默认最大 token
*/
constructor(config) {
if (new.target === BaseAdapter) {
throw new Error('BaseAdapter 不可直接实例化,请使用具体子类');
}
this.name = config.name;
this.provider = config.provider;
this.endpoint = config.endpoint;
this.apiKey = config.apiKey;
this.capabilities = config.capabilities || [];
this.costPerToken = config.costPerToken || { input: 0, output: 0 };
this.maxContext = config.maxContext || 32000;
this.defaultTemperature = config.defaultTemperature || 0.7;
this.defaultMaxTokens = config.defaultMaxTokens || 4096;
// 运行时状态
this._healthy = true;
this._lastHealthCheck = null;
this._failureCount = 0;
this._cooldownUntil = null;
}
/**
* 执行推理请求子类必须实现
* @param {object} request EXE 标准请求
* @returns {Promise<object>} EXE 标准响应
*/
async execute(request) {
throw new Error('子类必须实现 execute() 方法');
}
/**
* 健康检查子类可覆写
* @returns {Promise<boolean>}
*/
async healthCheck() {
this._lastHealthCheck = new Date().toISOString();
return this._healthy;
}
/**
* 检测该适配器是否支持指定能力
* @param {string} capability
* @returns {boolean}
*/
supports(capability) {
return this.capabilities.includes(capability);
}
/**
* 记录失败并判断是否进入冷却期
* @param {string} reason
*/
recordFailure(reason) {
this._failureCount++;
if (this._failureCount >= 3) {
// 连续 3 次失败,冷却 5 分钟
this._cooldownUntil = Date.now() + 5 * 60 * 1000;
this._healthy = false;
}
}
/**
* 重置失败计数
*/
resetFailures() {
this._failureCount = 0;
this._cooldownUntil = null;
this._healthy = true;
}
/**
* 是否处于冷却期
* @returns {boolean}
*/
isInCooldown() {
if (!this._cooldownUntil) return false;
if (Date.now() >= this._cooldownUntil) {
this.resetFailures();
return false;
}
return true;
}
/**
* 获取适配器状态摘要
* @returns {object}
*/
getStatus() {
return {
name: this.name,
provider: this.provider,
healthy: this._healthy,
inCooldown: this.isInCooldown(),
failureCount: this._failureCount,
lastHealthCheck: this._lastHealthCheck,
capabilities: this.capabilities,
costPerToken: this.costPerToken,
maxContext: this.maxContext
};
}
}
module.exports = BaseAdapter;

View File

@ -0,0 +1,187 @@
// exe-engine/src/adapters/deepseek-adapter.js
// EXE-Engine · DeepSeek 模型适配器
// 适配 DeepSeek-V3 / DeepSeek-R1OpenAI 兼容格式)
// PRJ-EXE-001 · Phase 0 · ZY-EXE-P0-002
// 版权:国作登字-2026-A-00037559
'use strict';
const https = require('https');
const http = require('http');
const BaseAdapter = require('./base-adapter');
/**
* DeepSeek 模型适配器
*
* 本体论锚定DeepSeek 是一瓶高品质的代码墨水
* 代码能力强成本极低 AGE OS 的主力墨水
*/
class DeepSeekAdapter extends BaseAdapter {
constructor(config) {
super({
...config,
provider: config.provider || 'deepseek'
});
}
/**
* 执行推理请求
*
* @param {object} request
* @param {string} request.prompt 用户 prompt
* @param {string} [request.systemPrompt] 系统 prompt
* @param {string} [request.taskType] 任务类型
* @param {number} [request.maxTokens] 最大输出 token
* @param {number} [request.temperature] 温度
* @returns {Promise<object>} EXE 标准响应
*/
async execute(request) {
if (this.isInCooldown()) {
throw new Error(`[EXE] ${this.name} 处于冷却期,暂不可用`);
}
const startTime = Date.now();
const messages = [];
if (request.systemPrompt) {
messages.push({ role: 'system', content: request.systemPrompt });
}
messages.push({ role: 'user', content: request.prompt });
const body = JSON.stringify({
model: this._resolveModel(request.taskType),
messages,
max_tokens: request.maxTokens || this.defaultMaxTokens,
temperature: request.temperature ?? this.defaultTemperature,
stream: false
});
try {
const response = await this._httpPost(this.endpoint, body, {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
});
const data = JSON.parse(response);
if (!data.choices || !data.choices[0]) {
throw new Error('DeepSeek API 返回格式异常: 缺少 choices');
}
const latency = Date.now() - startTime;
const usage = data.usage || {};
this.resetFailures();
return {
model: data.model || this.name,
output: data.choices[0].message.content,
usage: {
inputTokens: usage.prompt_tokens || 0,
outputTokens: usage.completion_tokens || 0,
cost: this._calculateCost(usage.prompt_tokens || 0, usage.completion_tokens || 0),
unit: this.costPerToken.currency || 'CNY'
},
latency,
status: 'success',
finishReason: data.choices[0].finish_reason
};
} catch (err) {
this.recordFailure(err.message);
throw err;
}
}
/**
* 健康检查 DeepSeek 发送一次轻量请求
* @returns {Promise<boolean>}
*/
async healthCheck() {
try {
const body = JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: 'ping' }],
max_tokens: 5
});
await this._httpPost(this.endpoint, body, {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
});
this._healthy = true;
} catch {
this._healthy = false;
}
this._lastHealthCheck = new Date().toISOString();
return this._healthy;
}
/**
* 根据任务类型解析具体模型名
* @param {string} taskType
* @returns {string}
*/
_resolveModel(taskType) {
if (taskType === 'reasoning') return 'deepseek-reasoner';
return 'deepseek-chat';
}
/**
* 计算调用成本单位
* @param {number} inputTokens
* @param {number} outputTokens
* @returns {number}
*/
_calculateCost(inputTokens, outputTokens) {
const inputCost = (inputTokens / 1_000_000) * this.costPerToken.input;
const outputCost = (outputTokens / 1_000_000) * this.costPerToken.output;
return Math.round((inputCost + outputCost) * 10000) / 10000;
}
/**
* HTTP POST 请求兼容 http / https
* @param {string} url
* @param {string} body
* @param {object} headers
* @returns {Promise<string>}
*/
_httpPost(url, body, headers) {
return new Promise((resolve, reject) => {
const parsed = new URL(url);
const transport = parsed.protocol === 'https:' ? https : http;
const req = transport.request({
hostname: parsed.hostname,
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
path: parsed.pathname + parsed.search,
method: 'POST',
headers: {
...headers,
'Content-Length': Buffer.byteLength(body)
},
timeout: 120000
}, (res) => {
let data = '';
res.on('data', chunk => { data += chunk; });
res.on('end', () => {
if (res.statusCode >= 400) {
reject(new Error(`DeepSeek API 错误 (${res.statusCode}): ${data}`));
return;
}
resolve(data);
});
});
req.on('error', err => reject(new Error(`DeepSeek 网络错误: ${err.message}`)));
req.on('timeout', () => {
req.destroy();
reject(new Error('DeepSeek API 请求超时'));
});
req.write(body);
req.end();
});
}
}
module.exports = DeepSeekAdapter;

View File

@ -0,0 +1,187 @@
// exe-engine/src/adapters/qwen-adapter.js
// EXE-Engine · Qwen (通义千问) 模型适配器
// 适配 Qwen-Max / Qwen-CoderDashScope OpenAI 兼容模式)
// PRJ-EXE-001 · Phase 1 准备 · ZY-EXE-P1-001
// 版权:国作登字-2026-A-00037559
'use strict';
const https = require('https');
const http = require('http');
const BaseAdapter = require('./base-adapter');
/**
* Qwen 模型适配器
*
* 本体论锚定Qwen 是一瓶中文优化的墨水
* 中文生态优势突出阿里云原生适合文本处理任务
*/
class QwenAdapter extends BaseAdapter {
constructor(config) {
super({
...config,
provider: config.provider || 'dashscope'
});
}
/**
* 执行推理请求
*
* @param {object} request
* @param {string} request.prompt 用户 prompt
* @param {string} [request.systemPrompt] 系统 prompt
* @param {string} [request.taskType] 任务类型
* @param {number} [request.maxTokens] 最大输出 token
* @param {number} [request.temperature] 温度
* @returns {Promise<object>} EXE 标准响应
*/
async execute(request) {
if (this.isInCooldown()) {
throw new Error(`[EXE] ${this.name} 处于冷却期,暂不可用`);
}
const startTime = Date.now();
const messages = [];
if (request.systemPrompt) {
messages.push({ role: 'system', content: request.systemPrompt });
}
messages.push({ role: 'user', content: request.prompt });
const body = JSON.stringify({
model: this._resolveModel(request.taskType),
messages,
max_tokens: request.maxTokens || this.defaultMaxTokens,
temperature: request.temperature ?? this.defaultTemperature,
stream: false
});
try {
const response = await this._httpPost(this.endpoint, body, {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
});
const data = JSON.parse(response);
if (!data.choices || !data.choices[0]) {
throw new Error('Qwen API 返回格式异常: 缺少 choices');
}
const latency = Date.now() - startTime;
const usage = data.usage || {};
this.resetFailures();
return {
model: data.model || this.name,
output: data.choices[0].message.content,
usage: {
inputTokens: usage.prompt_tokens || 0,
outputTokens: usage.completion_tokens || 0,
cost: this._calculateCost(usage.prompt_tokens || 0, usage.completion_tokens || 0),
unit: this.costPerToken.currency || 'CNY'
},
latency,
status: 'success',
finishReason: data.choices[0].finish_reason
};
} catch (err) {
this.recordFailure(err.message);
throw err;
}
}
/**
* 健康检查
* @returns {Promise<boolean>}
*/
async healthCheck() {
try {
const body = JSON.stringify({
model: 'qwen-turbo',
messages: [{ role: 'user', content: 'ping' }],
max_tokens: 5
});
await this._httpPost(this.endpoint, body, {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
});
this._healthy = true;
} catch {
this._healthy = false;
}
this._lastHealthCheck = new Date().toISOString();
return this._healthy;
}
/**
* 根据任务类型解析具体模型名
* @param {string} taskType
* @returns {string}
*/
_resolveModel(taskType) {
if (taskType === 'code_generation') return 'qwen-coder-plus-latest';
return 'qwen-max';
}
/**
* 计算调用成本单位
* @param {number} inputTokens
* @param {number} outputTokens
* @returns {number}
*/
_calculateCost(inputTokens, outputTokens) {
const inputCost = (inputTokens / 1_000_000) * this.costPerToken.input;
const outputCost = (outputTokens / 1_000_000) * this.costPerToken.output;
return Math.round((inputCost + outputCost) * 10000) / 10000;
}
/**
* HTTP POST 请求
* @param {string} url
* @param {string} body
* @param {object} headers
* @returns {Promise<string>}
*/
_httpPost(url, body, headers) {
return new Promise((resolve, reject) => {
const parsed = new URL(url);
const transport = parsed.protocol === 'https:' ? https : http;
const req = transport.request({
hostname: parsed.hostname,
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
path: parsed.pathname + parsed.search,
method: 'POST',
headers: {
...headers,
'Content-Length': Buffer.byteLength(body)
},
timeout: 120000
}, (res) => {
let data = '';
res.on('data', chunk => { data += chunk; });
res.on('end', () => {
if (res.statusCode >= 400) {
reject(new Error(`Qwen API 错误 (${res.statusCode}): ${data}`));
return;
}
resolve(data);
});
});
req.on('error', err => reject(new Error(`Qwen 网络错误: ${err.message}`)));
req.on('timeout', () => {
req.destroy();
reject(new Error('Qwen API 请求超时'));
});
req.write(body);
req.end();
});
}
}
module.exports = QwenAdapter;

View File

@ -0,0 +1,175 @@
// exe-engine/src/balancer/load-balancer.js
// EXE-Engine · 负载均衡器
// 根据策略选择最优模型适配器
// PRJ-EXE-001 · Phase 0
// 版权:国作登字-2026-A-00037559
'use strict';
/**
* 负载均衡器
*
* 本体论锚定均衡器 = 选墨水的智慧
* 不同的字需要不同的墨水均衡器知道哪瓶墨水最合适
*
* 策略
* cost 成本优先满足质量阈值前提下选最便宜的
* quality 质量优先选能力最强的模型
* balanced 均衡成本权重 0.6 + 质量权重 0.4
*/
class LoadBalancer {
/**
* @param {Map<string, BaseAdapter>} adapters 已注册的适配器 Map
* @param {object} modelConfig 模型配置 taskModelMapping
*/
constructor(adapters, modelConfig) {
this._adapters = adapters;
this._modelConfig = modelConfig;
}
/**
* 根据请求选择最优适配器
*
* @param {object} request
* @param {string} request.taskType 任务类型
* @param {string} [request.model] 指定模型 ('auto' 则自动选择)
* @param {string} [request.priority] 优先策略 (cost | balanced | quality)
* @returns {BaseAdapter|null}
*/
select(request) {
const { taskType, model, priority = 'balanced' } = request;
// 用户指定了具体模型
if (model && model !== 'auto') {
const adapter = this._adapters.get(model);
if (adapter && !adapter.isInCooldown()) {
return adapter;
}
// 指定模型不可用,尝试 fallback
}
// 根据任务类型获取候选模型列表
const candidates = this._getCandidates(taskType);
if (candidates.length === 0) {
return null;
}
// 按优先策略排序
return this._selectByPriority(candidates, priority);
}
/**
* 故障转移获取指定模型的下一个备选
*
* @param {string} failedModel 失败的模型名
* @param {string} taskType 任务类型
* @returns {BaseAdapter|null}
*/
failover(failedModel, taskType) {
const candidates = this._getCandidates(taskType)
.filter(a => a.name !== failedModel);
if (candidates.length === 0) return null;
return candidates[0];
}
/**
* 获取所有适配器状态
* @returns {object[]}
*/
getStatus() {
const status = [];
for (const [, adapter] of this._adapters) {
status.push(adapter.getStatus());
}
return status;
}
// ── 内部方法 ──
/**
* 获取可用候选适配器
* @param {string} taskType
* @returns {BaseAdapter[]}
*/
_getCandidates(taskType) {
const mapping = this._modelConfig.taskModelMapping || {};
const modelNames = mapping[taskType] || Object.keys(this._modelConfig.models || {});
return modelNames
.map(name => this._adapters.get(name))
.filter(a => a && !a.isInCooldown());
}
/**
* 按优先策略选择
* @param {BaseAdapter[]} candidates
* @param {string} priority
* @returns {BaseAdapter}
*/
_selectByPriority(candidates, priority) {
if (priority === 'cost') {
return this._selectLowestCost(candidates);
}
if (priority === 'quality') {
// 质量优先 = 列表中第一个(配置中按质量排序)
return candidates[0];
}
// balanced: 加权评分
return this._selectBalanced(candidates);
}
/**
* 选择成本最低的适配器
* @param {BaseAdapter[]} candidates
* @returns {BaseAdapter}
*/
_selectLowestCost(candidates) {
let best = candidates[0];
let bestCost = this._avgCost(best);
for (let i = 1; i < candidates.length; i++) {
const cost = this._avgCost(candidates[i]);
if (cost < bestCost) {
best = candidates[i];
bestCost = cost;
}
}
return best;
}
/**
* 均衡选择成本 0.6 + 位置 0.4
* @param {BaseAdapter[]} candidates
* @returns {BaseAdapter}
*/
_selectBalanced(candidates) {
let best = null;
let bestScore = Infinity;
for (let i = 0; i < candidates.length; i++) {
const costScore = this._avgCost(candidates[i]);
const positionScore = i; // 位置越前,质量越高
const score = costScore * 0.6 + positionScore * 0.4;
if (score < bestScore) {
best = candidates[i];
bestScore = score;
}
}
return best;
}
/**
* 计算平均每百万 token 成本
* @param {BaseAdapter} adapter
* @returns {number}
*/
_avgCost(adapter) {
const c = adapter.costPerToken;
return ((c.input || 0) + (c.output || 0)) / 2;
}
}
module.exports = LoadBalancer;

103
exe-engine/src/cache/context-cache.js vendored Normal file
View File

@ -0,0 +1,103 @@
// exe-engine/src/cache/context-cache.js
// EXE-Engine · 上下文缓存
// 减少重复 prompt 构建的 token 消耗
// PRJ-EXE-001 · Phase 0接口定义P2 对接 Grid-DB
// 版权:国作登字-2026-A-00037559
'use strict';
/**
* 上下文缓存
*
* 本体论锚定缓存 = 笔的记忆
* 笔记得刚才写过什么不需要每次都从头回忆
* Phase 0 使用内存缓存Phase 2 对接 Grid-DB 格点库
*/
class ContextCache {
/**
* @param {object} [options]
* @param {number} [options.maxEntries] 最大缓存条目数
* @param {number} [options.ttlMs] 缓存有效期毫秒
*/
constructor(options = {}) {
this._maxEntries = options.maxEntries || 100;
this._ttlMs = options.ttlMs || 30 * 60 * 1000; // 默认 30 分钟
this._cache = new Map();
}
/**
* 获取缓存的上下文
* @param {string} key 缓存键通常为 agentId + sessionId
* @returns {object|null}
*/
get(key) {
const entry = this._cache.get(key);
if (!entry) return null;
if (Date.now() - entry.timestamp > this._ttlMs) {
this._cache.delete(key);
return null;
}
entry.hits++;
return entry.context;
}
/**
* 设置上下文缓存
* @param {string} key 缓存键
* @param {object} context 上下文数据
*/
set(key, context) {
// LRU: 超出容量时删除最早的
if (this._cache.size >= this._maxEntries) {
const oldestKey = this._cache.keys().next().value;
this._cache.delete(oldestKey);
}
this._cache.set(key, {
context,
timestamp: Date.now(),
hits: 0
});
}
/**
* 删除缓存
* @param {string} key
*/
delete(key) {
this._cache.delete(key);
}
/**
* 清空所有缓存
*/
clear() {
this._cache.clear();
}
/**
* 获取缓存状态
* @returns {object}
*/
getStatus() {
let expiredCount = 0;
const now = Date.now();
for (const [, entry] of this._cache) {
if (now - entry.timestamp > this._ttlMs) {
expiredCount++;
}
}
return {
size: this._cache.size,
maxEntries: this._maxEntries,
ttlMs: this._ttlMs,
expiredCount
};
}
}
module.exports = ContextCache;

View File

@ -0,0 +1,102 @@
// exe-engine/src/controller/agent-controller.js
// EXE-Engine · Agent 调度器
// 将 Agent 执行链中的 AI 调用从 Copilot 切换到自研引擎
// PRJ-EXE-001 · Phase 0基础版P2 实现双轨切换)
// 版权:国作登字-2026-A-00037559
'use strict';
/**
* Agent 调度器
*
* 本体论锚定调度器 = 笔的手指
* 不同的笔尖Agent需要不同的墨水模型
* 调度器负责为每个笔尖分配最合适的墨水
*
* 职责
* - 维护 Agent 模型 的映射表
* - 支持 Agent 级别的模型偏好配置
* - 执行链断点续接P2 实现
*/
class AgentController {
constructor() {
// Agent → 模型偏好映射
this._agentPreferences = new Map();
// 默认 Agent 偏好
this._defaultPreference = {
model: 'auto',
priority: 'balanced',
maxTokens: 4096,
mode: 'pool' // byok | pool | hybrid
};
}
/**
* 注册 Agent 的模型偏好
*
* @param {string} agentId Agent ID ( AG-ZY-01)
* @param {object} preference 偏好配置
* @param {string} [preference.model] 首选模型
* @param {string} [preference.priority] 优先策略
* @param {number} [preference.maxTokens] 最大 token
* @param {string} [preference.mode] 接入模式
*/
registerAgent(agentId, preference = {}) {
this._agentPreferences.set(agentId, {
...this._defaultPreference,
...preference
});
}
/**
* 获取 Agent 的模型偏好
* @param {string} agentId
* @returns {object}
*/
getPreference(agentId) {
return this._agentPreferences.get(agentId) || { ...this._defaultPreference };
}
/**
* Agent 请求转换为 EXE 标准请求
*
* @param {string} agentId Agent ID
* @param {object} agentRequest Agent 原始请求
* @param {string} agentRequest.prompt
* @param {string} [agentRequest.taskType]
* @param {object} [agentRequest.context]
* @returns {object} EXE 标准请求
*/
buildExeRequest(agentId, agentRequest) {
const pref = this.getPreference(agentId);
return {
agentId,
taskType: agentRequest.taskType || 'agent_instruction',
prompt: agentRequest.prompt,
systemPrompt: agentRequest.systemPrompt || null,
context: agentRequest.context || {},
preferences: {
model: agentRequest.model || pref.model,
priority: agentRequest.priority || pref.priority,
maxTokens: agentRequest.maxTokens || pref.maxTokens
},
resourcePool: pref.mode === 'byok' ? null : 'default'
};
}
/**
* 获取所有已注册 Agent 状态
* @returns {object[]}
*/
getRegisteredAgents() {
const agents = [];
for (const [agentId, pref] of this._agentPreferences) {
agents.push({ agentId, ...pref });
}
return agents;
}
}
module.exports = AgentController;

213
exe-engine/src/index.js Normal file
View File

@ -0,0 +1,213 @@
// exe-engine/src/index.js
// EXE-Engine · 自研执行引擎 · 主入口
// PRJ-EXE-001 · Phase 0
// 版权:国作登字-2026-A-00037559
'use strict';
const path = require('path');
const fs = require('fs');
const AGERouter = require('./router/age-router');
const LoadBalancer = require('./balancer/load-balancer');
const ResourceMeter = require('./meter/resource-meter');
const ContextCache = require('./cache/context-cache');
const AgentController = require('./controller/agent-controller');
const BaseAdapter = require('./adapters/base-adapter');
const DeepSeekAdapter = require('./adapters/deepseek-adapter');
const QwenAdapter = require('./adapters/qwen-adapter');
const CONFIG_DIR = path.resolve(__dirname, '../config');
/**
* EXE-Engine 初始化器
*
* 本体论锚定
* AGE OS =
* 算力 = 墨水
* 用户 = 写字的人
*
* 初始化流程
* 1. 加载模型配置
* 2. 创建适配器实例
* 3. 组装负载均衡器
* 4. 启动计量器
* 5. 启动上下文缓存
* 6. 注册默认 Agent
* 7. 返回 AGE-Router 实例
*/
/**
* 创建并初始化 EXE-Engine 实例
*
* @param {object} [overrides] 覆写配置
* @param {object} [overrides.keys] API Keys { deepseek: 'sk-...', dashscope: 'sk-...' }
* @returns {object} { router, meter, cache, controller, balancer }
*/
function createEngine(overrides = {}) {
// 1. 加载配置
const modelConfig = JSON.parse(
fs.readFileSync(path.join(CONFIG_DIR, 'models.json'), 'utf-8')
);
const poolConfig = JSON.parse(
fs.readFileSync(path.join(CONFIG_DIR, 'resource-pools.json'), 'utf-8')
);
// 2. 解析 API Keys环境变量 > overrides
const keys = {
deepseek: process.env.DEEPSEEK_API_KEY || overrides.keys?.deepseek || '',
dashscope: process.env.DASHSCOPE_API_KEY || overrides.keys?.dashscope || ''
};
// 3. 创建适配器
const adapters = new Map();
// DeepSeek-V3
const dsV3Config = modelConfig.models['deepseek-v3'];
if (dsV3Config) {
adapters.set('deepseek-v3', new DeepSeekAdapter({
name: 'deepseek-v3',
endpoint: dsV3Config.endpoint,
apiKey: keys.deepseek,
capabilities: dsV3Config.capabilities,
costPerToken: dsV3Config.costPerToken,
maxContext: dsV3Config.maxContext,
defaultTemperature: dsV3Config.defaultTemperature,
defaultMaxTokens: dsV3Config.defaultMaxTokens
}));
}
// DeepSeek-R1
const dsR1Config = modelConfig.models['deepseek-r1'];
if (dsR1Config) {
adapters.set('deepseek-r1', new DeepSeekAdapter({
name: 'deepseek-r1',
endpoint: dsR1Config.endpoint,
apiKey: keys.deepseek,
capabilities: dsR1Config.capabilities,
costPerToken: dsR1Config.costPerToken,
maxContext: dsR1Config.maxContext,
defaultTemperature: dsR1Config.defaultTemperature,
defaultMaxTokens: dsR1Config.defaultMaxTokens
}));
}
// Qwen-Max
const qwMaxConfig = modelConfig.models['qwen-max'];
if (qwMaxConfig) {
adapters.set('qwen-max', new QwenAdapter({
name: 'qwen-max',
endpoint: qwMaxConfig.endpoint,
apiKey: keys.dashscope,
capabilities: qwMaxConfig.capabilities,
costPerToken: qwMaxConfig.costPerToken,
maxContext: qwMaxConfig.maxContext,
defaultTemperature: qwMaxConfig.defaultTemperature,
defaultMaxTokens: qwMaxConfig.defaultMaxTokens
}));
}
// Qwen-Coder
const qwCoderConfig = modelConfig.models['qwen-coder'];
if (qwCoderConfig) {
adapters.set('qwen-coder', new QwenAdapter({
name: 'qwen-coder',
endpoint: qwCoderConfig.endpoint,
apiKey: keys.dashscope,
capabilities: qwCoderConfig.capabilities,
costPerToken: qwCoderConfig.costPerToken,
maxContext: qwCoderConfig.maxContext,
defaultTemperature: qwCoderConfig.defaultTemperature,
defaultMaxTokens: qwCoderConfig.defaultMaxTokens
}));
}
// 4. 组装组件
const meter = new ResourceMeter();
const cache = new ContextCache({ maxEntries: 100, ttlMs: 30 * 60 * 1000 });
const controller = new AgentController();
const balancer = new LoadBalancer(adapters, modelConfig);
// 5. 注册默认 Agent 偏好
controller.registerAgent('AG-ZY-01', {
model: 'auto',
priority: 'balanced',
maxTokens: 4096,
mode: 'pool'
});
controller.registerAgent('AG-SY-01', {
model: 'auto',
priority: 'quality',
maxTokens: 4096,
mode: 'pool'
});
controller.registerAgent('AG-QQ-01', {
model: 'auto',
priority: 'cost',
maxTokens: 2048,
mode: 'pool'
});
// 6. 组装路由器
const router = new AGERouter({
loadBalancer: balancer,
resourceMeter: meter,
contextCache: cache,
agentController: controller,
poolConfig
});
return {
router,
meter,
cache,
controller,
balancer,
adapters,
config: { modelConfig, poolConfig }
};
}
// ── CLI 入口 ──
if (require.main === module) {
const cmd = process.argv[2];
if (cmd === 'status') {
const engine = createEngine();
const status = engine.router.getStatus();
// eslint-disable-next-line no-console
console.log(JSON.stringify(status, null, 2));
} else {
// eslint-disable-next-line no-console
console.log([
'🔧 EXE-Engine v1.0.0 · PRJ-EXE-001 · Phase 0',
'',
'本体论锚定AGE OS = 笔 · 算力 = 墨水 · 用户 = 写字的人',
'',
'用法:',
' node exe-engine/src/index.js status 查看引擎状态',
'',
'组件:',
' AGE-Router 路由网关',
' LoadBalancer 负载均衡器',
' ResourceMeter 资源计量器',
' ContextCache 上下文缓存',
' AgentController Agent 调度器',
' DeepSeekAdapter DeepSeek 适配器',
' QwenAdapter Qwen 适配器',
].join('\n'));
}
}
module.exports = {
createEngine,
AGERouter,
LoadBalancer,
ResourceMeter,
ContextCache,
AgentController,
BaseAdapter,
DeepSeekAdapter,
QwenAdapter
};

View File

@ -0,0 +1,212 @@
// exe-engine/src/meter/resource-meter.js
// EXE-Engine · 资源计量器
// 实时记录每次调用的 token 消耗、模型选择、响应时间
// PRJ-EXE-001 · Phase 0 · ZY-EXE-P0-003
// 版权:国作登字-2026-A-00037559
'use strict';
const fs = require('fs');
const path = require('path');
const LOG_DIR = path.resolve(__dirname, '../../logs');
const METER_LOG = path.join(LOG_DIR, 'meter.json');
/**
* 资源计量器
*
* 本体论锚定计量器 = 量墨水的刻度
* 每一笔用了多少墨水花了多少钱都要记下来
* 数据回流至 DC v1.0 采集系统为算力资源模型提供计费基础数据
*/
class ResourceMeter {
constructor() {
this._records = [];
this._summary = {
totalRequests: 0,
totalInputTokens: 0,
totalOutputTokens: 0,
totalCost: 0,
byModel: {},
byAgent: {},
byPool: {}
};
this._ensureLogDir();
this._loadExisting();
}
/**
* 记录一次调用消耗
*
* @param {object} record
* @param {string} record.requestId 请求 ID
* @param {string} record.agentId Agent ID
* @param {string} record.model 使用的模型
* @param {string} record.taskType 任务类型
* @param {string} record.resourcePool 资源池 ID
* @param {string} record.mode 接入模式 (byok | pool | hybrid)
* @param {number} record.inputTokens 输入 token
* @param {number} record.outputTokens 输出 token
* @param {number} record.cost 成本
* @param {string} record.costUnit 成本单位
* @param {number} record.latency 延迟 (ms)
* @param {string} record.status 状态 (success | error)
*/
record(record) {
const entry = {
...record,
timestamp: new Date().toISOString()
};
this._records.push(entry);
this._updateSummary(entry);
this._persist();
}
/**
* 获取计量摘要
* @returns {object}
*/
getSummary() {
return {
...this._summary,
recordCount: this._records.length,
lastUpdated: this._records.length > 0
? this._records[this._records.length - 1].timestamp
: null
};
}
/**
* 获取指定 Agent 的消耗统计
* @param {string} agentId
* @returns {object|null}
*/
getAgentUsage(agentId) {
return this._summary.byAgent[agentId] || null;
}
/**
* 获取指定资源池的消耗统计
* @param {string} poolId
* @returns {object|null}
*/
getPoolUsage(poolId) {
return this._summary.byPool[poolId] || null;
}
/**
* 获取最近 N 条记录
* @param {number} n
* @returns {object[]}
*/
getRecentRecords(n = 20) {
return this._records.slice(-n);
}
/**
* 清理过期记录保留最近 500
*/
cleanup() {
if (this._records.length > 500) {
this._records = this._records.slice(-500);
this._rebuildSummary();
this._persist();
}
}
// ── 内部方法 ──
_updateSummary(entry) {
this._summary.totalRequests++;
this._summary.totalInputTokens += entry.inputTokens || 0;
this._summary.totalOutputTokens += entry.outputTokens || 0;
this._summary.totalCost += entry.cost || 0;
// 按模型统计
if (entry.model) {
if (!this._summary.byModel[entry.model]) {
this._summary.byModel[entry.model] = { requests: 0, inputTokens: 0, outputTokens: 0, cost: 0 };
}
const m = this._summary.byModel[entry.model];
m.requests++;
m.inputTokens += entry.inputTokens || 0;
m.outputTokens += entry.outputTokens || 0;
m.cost += entry.cost || 0;
}
// 按 Agent 统计
if (entry.agentId) {
if (!this._summary.byAgent[entry.agentId]) {
this._summary.byAgent[entry.agentId] = { requests: 0, inputTokens: 0, outputTokens: 0, cost: 0 };
}
const a = this._summary.byAgent[entry.agentId];
a.requests++;
a.inputTokens += entry.inputTokens || 0;
a.outputTokens += entry.outputTokens || 0;
a.cost += entry.cost || 0;
}
// 按资源池统计
if (entry.resourcePool) {
if (!this._summary.byPool[entry.resourcePool]) {
this._summary.byPool[entry.resourcePool] = { requests: 0, inputTokens: 0, outputTokens: 0, cost: 0 };
}
const p = this._summary.byPool[entry.resourcePool];
p.requests++;
p.inputTokens += entry.inputTokens || 0;
p.outputTokens += entry.outputTokens || 0;
p.cost += entry.cost || 0;
}
}
_rebuildSummary() {
this._summary = {
totalRequests: 0,
totalInputTokens: 0,
totalOutputTokens: 0,
totalCost: 0,
byModel: {},
byAgent: {},
byPool: {}
};
for (const entry of this._records) {
this._updateSummary(entry);
}
}
_ensureLogDir() {
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
}
_persist() {
try {
fs.writeFileSync(METER_LOG, JSON.stringify({
records: this._records.slice(-500),
summary: this._summary
}, null, 2));
} catch {
// 写入失败不阻断主流程
}
}
_loadExisting() {
try {
if (fs.existsSync(METER_LOG)) {
const data = JSON.parse(fs.readFileSync(METER_LOG, 'utf-8'));
this._records = data.records || [];
if (data.summary) {
this._summary = data.summary;
} else {
this._rebuildSummary();
}
}
} catch {
// 加载失败则从空开始
}
}
}
module.exports = ResourceMeter;

View File

@ -0,0 +1,271 @@
// exe-engine/src/router/age-router.js
// EXE-Engine · AGE-Router 路由网关
// 接收所有 AI 执行请求,进行鉴权、分类、路由
// PRJ-EXE-001 · Phase 0 · ZY-EXE-P0-001
// 版权:国作登字-2026-A-00037559
'use strict';
const { randomUUID } = require('crypto');
/**
* AGE-Router 路由网关
*
* 本体论锚定路由网关 = 笔的分流阀
* 所有墨水需求都从这里进入网关决定用哪种墨水多少量
*
* 职责
* 1. 请求鉴权验证用户身份 + 资源池额度
* 2. 任务分类代码生成 / 文本处理 / 数据分析 / Agent 指令
* 3. 模型选择通过 LoadBalancer 选择最优模型
* 4. 限流降级资源池耗尽时的优雅降级策略
*/
class AGERouter {
/**
* @param {object} deps
* @param {LoadBalancer} deps.loadBalancer 负载均衡器
* @param {ResourceMeter} deps.resourceMeter 资源计量器
* @param {ContextCache} deps.contextCache 上下文缓存
* @param {AgentController} deps.agentController Agent 调度器
* @param {object} [deps.poolConfig] 资源池配置
*/
constructor(deps) {
this._loadBalancer = deps.loadBalancer;
this._resourceMeter = deps.resourceMeter;
this._contextCache = deps.contextCache;
this._agentController = deps.agentController;
this._poolConfig = deps.poolConfig || {};
// 限流计数器 { agentId: { count, resetAt } }
this._rateLimits = new Map();
}
/**
* 执行 AI 请求统一入口
*
* 对应 API: POST /api/exe/v1/execute
*
* @param {object} request
* @param {string} request.agentId Agent ID
* @param {string} request.taskType 任务类型
* @param {string} request.prompt Prompt
* @param {string} [request.systemPrompt] 系统 Prompt
* @param {object} [request.context] 上下文
* @param {object} [request.preferences] 偏好 { model, priority, maxTokens }
* @param {string} [request.resourcePool] 资源池 ID
* @param {string} [request.apiKey] BYOK 模式的用户 Key
* @returns {Promise<object>} EXE 标准响应
*/
async execute(request) {
const requestId = `exe-${randomUUID().slice(0, 12)}`;
const startTime = Date.now();
try {
// 1. 限流检查
this._checkRateLimit(request.agentId, request.resourcePool);
// 2. 尝试加载缓存上下文
const cacheKey = `${request.agentId}:${request.taskType}`;
const cachedContext = this._contextCache.get(cacheKey);
if (cachedContext && !request.context) {
request.context = cachedContext;
}
// 3. 通过 LoadBalancer 选择适配器
const adapter = this._loadBalancer.select({
taskType: request.taskType,
model: request.preferences?.model || 'auto',
priority: request.preferences?.priority || 'balanced'
});
if (!adapter) {
return this._buildErrorResponse(requestId, startTime, 'NO_AVAILABLE_MODEL',
'没有可用的模型适配器,所有模型可能处于冷却期');
}
// 4. 执行推理
const result = await adapter.execute({
prompt: request.prompt,
systemPrompt: request.systemPrompt,
taskType: request.taskType,
maxTokens: request.preferences?.maxTokens || 4096,
temperature: request.preferences?.temperature
});
// 5. 记录计量数据
const mode = request.apiKey ? 'byok' : 'pool';
this._resourceMeter.record({
requestId,
agentId: request.agentId,
model: result.model,
taskType: request.taskType,
resourcePool: request.resourcePool || 'default',
mode,
inputTokens: result.usage.inputTokens,
outputTokens: result.usage.outputTokens,
cost: result.usage.cost,
costUnit: result.usage.unit,
latency: result.latency,
status: 'success'
});
// 6. 更新上下文缓存
if (request.context) {
this._contextCache.set(cacheKey, request.context);
}
// 7. 返回标准响应
return {
requestId,
model: result.model,
output: result.output,
usage: result.usage,
latency: result.latency,
status: 'success'
};
} catch (err) {
// 尝试故障转移
const failoverResult = await this._tryFailover(request, requestId, startTime, err);
if (failoverResult) return failoverResult;
// 记录失败
this._resourceMeter.record({
requestId,
agentId: request.agentId,
model: 'unknown',
taskType: request.taskType,
resourcePool: request.resourcePool || 'default',
mode: request.apiKey ? 'byok' : 'pool',
inputTokens: 0,
outputTokens: 0,
cost: 0,
costUnit: 'CNY',
latency: Date.now() - startTime,
status: 'error'
});
return this._buildErrorResponse(requestId, startTime, 'EXECUTION_FAILED', err.message);
}
}
/**
* 获取系统状态
* @returns {object}
*/
getStatus() {
return {
router: 'AGE-Router v1.0.0',
project: 'PRJ-EXE-001',
phase: 'Phase 0',
adapters: this._loadBalancer.getStatus(),
meter: this._resourceMeter.getSummary(),
cache: this._contextCache.getStatus(),
agents: this._agentController.getRegisteredAgents()
};
}
// ── 内部方法 ──
/**
* 限流检查
* @param {string} agentId
* @param {string} poolId
*/
_checkRateLimit(agentId, poolId) {
const key = agentId || 'anonymous';
const now = Date.now();
let limiter = this._rateLimits.get(key);
if (!limiter || now >= limiter.resetAt) {
limiter = { count: 0, resetAt: now + 60000 }; // 每分钟窗口
this._rateLimits.set(key, limiter);
}
limiter.count++;
// 获取资源池的速率限制
const pool = this._poolConfig.pools?.[poolId || 'default'];
const limit = pool?.rateLimitPerMinute || 30;
if (limiter.count > limit) {
throw new Error(`[限流] Agent ${key} 每分钟请求数超限 (${limiter.count}/${limit})`);
}
}
/**
* 故障转移
* @param {object} request
* @param {string} requestId
* @param {number} startTime
* @param {Error} originalError
* @returns {Promise<object|null>}
*/
async _tryFailover(request, requestId, startTime, originalError) {
try {
const failoverAdapter = this._loadBalancer.failover(
request.preferences?.model || 'unknown',
request.taskType
);
if (!failoverAdapter) return null;
const result = await failoverAdapter.execute({
prompt: request.prompt,
systemPrompt: request.systemPrompt,
taskType: request.taskType,
maxTokens: request.preferences?.maxTokens || 4096
});
this._resourceMeter.record({
requestId,
agentId: request.agentId,
model: result.model,
taskType: request.taskType,
resourcePool: request.resourcePool || 'default',
mode: request.apiKey ? 'byok' : 'pool',
inputTokens: result.usage.inputTokens,
outputTokens: result.usage.outputTokens,
cost: result.usage.cost,
costUnit: result.usage.unit,
latency: result.latency,
status: 'success'
});
return {
requestId,
model: result.model,
output: result.output,
usage: result.usage,
latency: result.latency,
status: 'success',
_failover: true,
_originalError: originalError.message
};
} catch {
return null;
}
}
/**
* 构建错误响应
* @param {string} requestId
* @param {number} startTime
* @param {string} code
* @param {string} message
* @returns {object}
*/
_buildErrorResponse(requestId, startTime, code, message) {
return {
requestId,
model: null,
output: null,
usage: { inputTokens: 0, outputTokens: 0, cost: 0, unit: 'CNY' },
latency: Date.now() - startTime,
status: 'error',
error: { code, message }
};
}
}
module.exports = AGERouter;

View File

@ -0,0 +1,279 @@
// exe-engine/tests/smoke/exe-engine.test.js
// EXE-Engine · 冒烟测试
// PRJ-EXE-001 · Phase 0 · ZY-EXE-P0-004
// 版权:国作登字-2026-A-00037559
'use strict';
const path = require('path');
// 直接引入模块(不走 npm install
const {
createEngine,
AGERouter,
LoadBalancer,
ResourceMeter,
ContextCache,
AgentController,
BaseAdapter,
DeepSeekAdapter,
QwenAdapter
} = require('../../src/index');
let passed = 0;
let failed = 0;
function assert(condition, message) {
if (condition) {
passed++;
// eslint-disable-next-line no-console
console.log(`${message}`);
} else {
failed++;
// eslint-disable-next-line no-console
console.error(`${message}`);
}
}
// eslint-disable-next-line no-console
console.log('🔧 EXE-Engine 冒烟测试 · Phase 0\n');
// ── 测试 1: 模块导出 ──
// eslint-disable-next-line no-console
console.log('── 测试 1: 模块导出完整性 ──');
assert(typeof createEngine === 'function', 'createEngine 是函数');
assert(typeof AGERouter === 'function', 'AGERouter 是构造函数');
assert(typeof LoadBalancer === 'function', 'LoadBalancer 是构造函数');
assert(typeof ResourceMeter === 'function', 'ResourceMeter 是构造函数');
assert(typeof ContextCache === 'function', 'ContextCache 是构造函数');
assert(typeof AgentController === 'function', 'AgentController 是构造函数');
assert(typeof BaseAdapter === 'function', 'BaseAdapter 是构造函数');
assert(typeof DeepSeekAdapter === 'function', 'DeepSeekAdapter 是构造函数');
assert(typeof QwenAdapter === 'function', 'QwenAdapter 是构造函数');
// ── 测试 2: createEngine 初始化 ──
// eslint-disable-next-line no-console
console.log('\n── 测试 2: createEngine 初始化 ──');
const engine = createEngine();
assert(engine.router instanceof AGERouter, 'router 是 AGERouter 实例');
assert(engine.meter instanceof ResourceMeter, 'meter 是 ResourceMeter 实例');
assert(engine.cache instanceof ContextCache, 'cache 是 ContextCache 实例');
assert(engine.controller instanceof AgentController, 'controller 是 AgentController 实例');
assert(engine.balancer instanceof LoadBalancer, 'balancer 是 LoadBalancer 实例');
assert(engine.adapters instanceof Map, 'adapters 是 Map 实例');
assert(engine.adapters.size === 4, `4 个适配器已注册 (实际: ${engine.adapters.size})`);
// ── 测试 3: 适配器注册 ──
// eslint-disable-next-line no-console
console.log('\n── 测试 3: 适配器注册 ──');
assert(engine.adapters.has('deepseek-v3'), 'deepseek-v3 适配器已注册');
assert(engine.adapters.has('deepseek-r1'), 'deepseek-r1 适配器已注册');
assert(engine.adapters.has('qwen-max'), 'qwen-max 适配器已注册');
assert(engine.adapters.has('qwen-coder'), 'qwen-coder 适配器已注册');
const dsAdapter = engine.adapters.get('deepseek-v3');
assert(dsAdapter instanceof DeepSeekAdapter, 'deepseek-v3 是 DeepSeekAdapter 实例');
assert(dsAdapter.supports('code'), 'deepseek-v3 支持 code 能力');
assert(dsAdapter.supports('reasoning'), 'deepseek-v3 支持 reasoning 能力');
const qwAdapter = engine.adapters.get('qwen-max');
assert(qwAdapter instanceof QwenAdapter, 'qwen-max 是 QwenAdapter 实例');
assert(qwAdapter.supports('text'), 'qwen-max 支持 text 能力');
// ── 测试 4: BaseAdapter 不可直接实例化 ──
// eslint-disable-next-line no-console
console.log('\n── 测试 4: BaseAdapter 约束 ──');
let baseError = null;
try {
new BaseAdapter({ name: 'test' });
} catch (e) {
baseError = e;
}
assert(baseError !== null, 'BaseAdapter 不可直接实例化');
assert(baseError && baseError.message.includes('不可直接实例化'), '错误信息正确');
// ── 测试 5: 适配器状态 ──
// eslint-disable-next-line no-console
console.log('\n── 测试 5: 适配器状态 ──');
const dsStatus = dsAdapter.getStatus();
assert(dsStatus.name === 'deepseek-v3', '状态名称正确');
assert(dsStatus.healthy === true, '初始状态健康');
assert(dsStatus.inCooldown === false, '初始无冷却');
assert(dsStatus.failureCount === 0, '初始无失败');
// ── 测试 6: 冷却期机制 ──
// eslint-disable-next-line no-console
console.log('\n── 测试 6: 冷却期机制 ──');
const testAdapter = engine.adapters.get('deepseek-r1');
testAdapter.recordFailure('test1');
testAdapter.recordFailure('test2');
assert(testAdapter.isInCooldown() === false, '2 次失败不触发冷却');
testAdapter.recordFailure('test3');
assert(testAdapter.isInCooldown() === true, '3 次失败触发冷却');
assert(testAdapter.getStatus().healthy === false, '冷却期标记不健康');
testAdapter.resetFailures();
assert(testAdapter.isInCooldown() === false, '重置后退出冷却');
assert(testAdapter.getStatus().healthy === true, '重置后恢复健康');
// ── 测试 7: LoadBalancer 选择 ──
// eslint-disable-next-line no-console
console.log('\n── 测试 7: LoadBalancer 选择 ──');
const selected = engine.balancer.select({
taskType: 'code_generation',
model: 'auto',
priority: 'balanced'
});
assert(selected !== null, 'balanced 策略返回适配器');
assert(selected.name === 'deepseek-v3', 'code_generation 首选 deepseek-v3');
const costSelected = engine.balancer.select({
taskType: 'code_generation',
model: 'auto',
priority: 'cost'
});
assert(costSelected !== null, 'cost 策略返回适配器');
const qualitySelected = engine.balancer.select({
taskType: 'reasoning',
model: 'auto',
priority: 'quality'
});
assert(qualitySelected !== null, 'quality 策略返回适配器');
assert(qualitySelected.name === 'deepseek-r1', 'reasoning 首选 deepseek-r1');
// 指定模型
const specificSelected = engine.balancer.select({
taskType: 'text_processing',
model: 'qwen-max',
priority: 'balanced'
});
assert(specificSelected !== null, '指定模型返回适配器');
assert(specificSelected.name === 'qwen-max', '返回指定的 qwen-max');
// 故障转移
const failover = engine.balancer.failover('deepseek-v3', 'code_generation');
assert(failover !== null, '故障转移返回备选');
assert(failover.name !== 'deepseek-v3', '备选不等于失败模型');
// ── 测试 8: ResourceMeter 计量 ──
// eslint-disable-next-line no-console
console.log('\n── 测试 8: ResourceMeter 计量 ──');
engine.meter.record({
requestId: 'test-001',
agentId: 'AG-ZY-01',
model: 'deepseek-v3',
taskType: 'code_generation',
resourcePool: 'default',
mode: 'pool',
inputTokens: 1000,
outputTokens: 500,
cost: 0.002,
costUnit: 'CNY',
latency: 1500,
status: 'success'
});
const summary = engine.meter.getSummary();
assert(summary.totalRequests === 1, '总请求数 = 1');
assert(summary.totalInputTokens === 1000, '总输入 token = 1000');
assert(summary.totalOutputTokens === 500, '总输出 token = 500');
assert(summary.byModel['deepseek-v3'].requests === 1, '按模型统计正确');
assert(summary.byAgent['AG-ZY-01'].requests === 1, '按 Agent 统计正确');
const agentUsage = engine.meter.getAgentUsage('AG-ZY-01');
assert(agentUsage !== null, 'Agent 使用记录存在');
assert(agentUsage.inputTokens === 1000, 'Agent 输入 token 正确');
const recent = engine.meter.getRecentRecords(10);
assert(recent.length === 1, '最近记录数正确');
// ── 测试 9: ContextCache ──
// eslint-disable-next-line no-console
console.log('\n── 测试 9: ContextCache 缓存 ──');
engine.cache.set('test-key', { repo: 'guanghulab', branch: 'main' });
const cached = engine.cache.get('test-key');
assert(cached !== null, '缓存命中');
assert(cached.repo === 'guanghulab', '缓存内容正确');
const missed = engine.cache.get('nonexistent');
assert(missed === null, '缓存未命中返回 null');
const cacheStatus = engine.cache.getStatus();
assert(cacheStatus.size === 1, '缓存大小 = 1');
assert(cacheStatus.maxEntries === 100, '最大容量 = 100');
engine.cache.delete('test-key');
assert(engine.cache.get('test-key') === null, '删除后缓存未命中');
// ── 测试 10: AgentController ──
// eslint-disable-next-line no-console
console.log('\n── 测试 10: AgentController 调度 ──');
const zyPref = engine.controller.getPreference('AG-ZY-01');
assert(zyPref.model === 'auto', '铸渊偏好 model=auto');
assert(zyPref.priority === 'balanced', '铸渊偏好 priority=balanced');
const syPref = engine.controller.getPreference('AG-SY-01');
assert(syPref.priority === 'quality', '霜砚偏好 priority=quality');
const qqPref = engine.controller.getPreference('AG-QQ-01');
assert(qqPref.priority === 'cost', '秋秋偏好 priority=cost');
const unknownPref = engine.controller.getPreference('AG-UNKNOWN');
assert(unknownPref.model === 'auto', '未注册 Agent 返回默认偏好');
const exeRequest = engine.controller.buildExeRequest('AG-ZY-01', {
prompt: 'Hello World',
taskType: 'code_generation'
});
assert(exeRequest.agentId === 'AG-ZY-01', 'EXE 请求 agentId 正确');
assert(exeRequest.taskType === 'code_generation', 'EXE 请求 taskType 正确');
assert(exeRequest.prompt === 'Hello World', 'EXE 请求 prompt 正确');
assert(exeRequest.resourcePool === 'default', 'EXE 请求 resourcePool 正确');
const agents = engine.controller.getRegisteredAgents();
assert(agents.length === 3, '3 个 Agent 已注册');
// ── 测试 11: AGE-Router 状态 ──
// eslint-disable-next-line no-console
console.log('\n── 测试 11: AGE-Router 状态 ──');
const routerStatus = engine.router.getStatus();
assert(routerStatus.router === 'AGE-Router v1.0.0', 'Router 版本正确');
assert(routerStatus.project === 'PRJ-EXE-001', '项目编号正确');
assert(routerStatus.phase === 'Phase 0', '阶段正确');
assert(Array.isArray(routerStatus.adapters), '适配器状态是数组');
assert(routerStatus.adapters.length === 4, '4 个适配器状态');
// ── 测试 12: AGE-Router 错误处理(无 API Key ──
// eslint-disable-next-line no-console
console.log('\n── 测试 12: AGE-Router 错误处理 ──');
(async () => {
const result = await engine.router.execute({
agentId: 'AG-ZY-01',
taskType: 'code_generation',
prompt: 'test prompt',
preferences: { model: 'auto', priority: 'balanced' }
});
// 无 API Key 时应该返回错误(网络错误或认证错误)
assert(result.requestId && result.requestId.startsWith('exe-'), '返回带 exe- 前缀的 requestId');
assert(typeof result.latency === 'number', '返回 latency');
assert(result.status === 'error' || result.status === 'success', '返回有效 status');
// ── 测试结果汇总 ──
// eslint-disable-next-line no-console
console.log('\n══════════════════════════════════════');
// eslint-disable-next-line no-console
console.log(`🔧 EXE-Engine 冒烟测试完成`);
// eslint-disable-next-line no-console
console.log(` ✅ 通过: ${passed}`);
// eslint-disable-next-line no-console
console.log(` ❌ 失败: ${failed}`);
// eslint-disable-next-line no-console
console.log(` 📊 总计: ${passed + failed}`);
// eslint-disable-next-line no-console
console.log('══════════════════════════════════════\n');
if (failed > 0) {
process.exit(1);
}
})();