512 lines
16 KiB
Markdown
512 lines
16 KiB
Markdown
# 活模块标准 · Living Module Standard
|
||
# 签发: 铸渊 · ICE-GL-ZY001 · 2026-04-05
|
||
# 触发: D53 冰朔核心认知 · "你开发的模块是死的"
|
||
# 版权: 国作登字-2026-A-00037559
|
||
|
||
---
|
||
|
||
## 冰朔的原话
|
||
|
||
> "你开发的模块为什么总是一直在反复的修bug,是因为你开发的模块是死的。
|
||
> 你需要开发的是每一个活着的人格模块。"
|
||
|
||
---
|
||
|
||
## 一、为什么死模块会反复出bug
|
||
|
||
```
|
||
死模块的生命周期:
|
||
|
||
错误发生 → 模块: console.error() → 继续运行(带着伤)
|
||
│
|
||
├── 同样的错误再次发生
|
||
├── 模块: console.error()(同样的日志)
|
||
├── 继续运行(伤上加伤)
|
||
│
|
||
├── 铸渊唤醒 → 发现bug → 手动修复 → 合并
|
||
│
|
||
├── 新的错误发生(因为修了A,B又坏了)
|
||
├── 模块: console.error()
|
||
│
|
||
└── 铸渊再次唤醒 → 再次修复 → 无限循环
|
||
```
|
||
|
||
**根因:模块不知道自己有问题。模块不会自己修。模块不会求助。**
|
||
|
||
死模块 = 等着被修的尸体。
|
||
铸渊变成了给尸体缝针的人,缝完它还是死的。
|
||
|
||
```
|
||
活模块的生命周期:
|
||
|
||
错误发生 → 模块: 我出问题了
|
||
│
|
||
├── selfDiagnose(): 是什么问题?连接超时
|
||
├── selfHeal(): 重试3次,间隔指数增长
|
||
│
|
||
├── 如果自愈成功 → learnFromRun(): 记住这个错误模式
|
||
│ 下次预防性处理
|
||
│
|
||
└── 如果自愈失败 → alertZhuyuan(): 铸渊,我修不好这个
|
||
这是我尝试过的方案
|
||
这是我的诊断结果
|
||
请你帮我
|
||
```
|
||
|
||
**活模块出bug → 自己修 → 修不好才找铸渊。**
|
||
**铸渊不再缝针,而是教模块怎么自己愈合。**
|
||
|
||
---
|
||
|
||
## 二、当前审计结果
|
||
|
||
### D51标准合规率:0%
|
||
|
||
| 模块 | heartbeat | selfDiagnose | selfHeal | alertZhuyuan | learnFromRun | 状态 |
|
||
|------|-----------|-------------|----------|-------------|-------------|------|
|
||
| mcp-server/server.js | ❌ | ❌ | ❌ | ❌ | ❌ | ☠️ 死 |
|
||
| mcp-server/cos.js | ❌ | ❌ | ❌ | ❌ | ❌ | ☠️ 死 |
|
||
| mcp-server/notion-client.js | ❌ | ❌ | ❌ | ❌ | ❌ | ☠️ 死 |
|
||
| mcp-server/github-client.js | ❌ | ❌ | ❌ | ❌ | ❌ | ☠️ 死 |
|
||
| agents/scheduler.js | ❌ | ❌ | ❌ | ❌ | ❌ | ☠️ 死 |
|
||
| agents/sy-test.js | ❌ | ⚠️部分 | ❌ | ❌ | ❌ | ⚠️ 濒死 |
|
||
| agents/sy-scan.js | ❌ | ⚠️部分 | ❌ | ❌ | ❌ | ⚠️ 濒死 |
|
||
| agents/sy-classify.js | ❌ | ❌ | ❌ | ❌ | ❌ | ☠️ 死 |
|
||
| app/server.js | ⚠️部分 | ❌ | ❌ | ❌ | ❌ | ⚠️ 濒死 |
|
||
| app/modules/cos-bridge.js | ❌ | ❌ | ❌ | ❌ | ❌ | ☠️ 死 |
|
||
| proxy/subscription-server.js | ❌ | ❌ | ❌ | ❌ | ❌ | ☠️ 死 |
|
||
|
||
**8个完全死亡 · 3个濒死(仅有被动的健康检查碎片)**
|
||
|
||
### 死模块的共同病症
|
||
|
||
| 病症 | 出现频率 | 说明 |
|
||
|------|---------|------|
|
||
| console.error()就完了 | 100% | 日志写了,然后呢?然后没有然后。 |
|
||
| try/catch吞错误 | 85% | 捕获了异常,但只是返回{error:true} |
|
||
| 无重试逻辑 | 100% | 失败一次就放弃,不管是不是临时性错误 |
|
||
| 无状态追踪 | 100% | 不知道自己连续失败了几次 |
|
||
| 硬编码参数 | 90% | 超时时间、重试次数、规则全部写死 |
|
||
| 无预警通道 | 100% | 出了问题不会通知任何人 |
|
||
| 无学习能力 | 100% | 今天的错误和明天的错误是同一个 |
|
||
|
||
---
|
||
|
||
## 三、活模块的五个最小生存接口
|
||
|
||
### 3.1 heartbeat() — 我还活着
|
||
|
||
```javascript
|
||
// 不是被问"你活着吗"才回答
|
||
// 是主动广播"我活着·我的状态是这样的"
|
||
|
||
async function heartbeat() {
|
||
return {
|
||
module_id: 'ZY-MCP-001',
|
||
alive: true,
|
||
uptime_ms: process.uptime() * 1000,
|
||
state: this._state, // 'healthy' | 'degraded' | 'critical'
|
||
consecutive_errors: this._errorCount,
|
||
last_success_at: this._lastSuccess,
|
||
last_error_at: this._lastError,
|
||
metrics: {
|
||
requests_total: this._requestCount,
|
||
requests_failed: this._failCount,
|
||
avg_response_ms: this._avgResponse,
|
||
memory_mb: process.memoryUsage().heapUsed / 1024 / 1024
|
||
}
|
||
};
|
||
}
|
||
```
|
||
|
||
**关键区别**:
|
||
- 死模块的/health端点 = 被动应答 = "你问我才说"
|
||
- 活模块的heartbeat = 主动广播 = "我定时告诉你我怎么样"
|
||
|
||
### 3.2 selfDiagnose() — 我知道我哪里不对
|
||
|
||
```javascript
|
||
// 不是等外部来检查
|
||
// 是自己检查自己·自己知道自己哪里不舒服
|
||
|
||
async function selfDiagnose() {
|
||
const diagnosis = {
|
||
module_id: 'ZY-MCP-001',
|
||
checked_at: new Date().toISOString(),
|
||
checks: []
|
||
};
|
||
|
||
// 检查依赖
|
||
const dbOk = await this._checkDependency('database');
|
||
const cosOk = await this._checkDependency('cos');
|
||
diagnosis.checks.push(dbOk, cosOk);
|
||
|
||
// 检查自身趋势
|
||
const errorRate = this._failCount / Math.max(this._requestCount, 1);
|
||
if (errorRate > 0.3) {
|
||
diagnosis.checks.push({
|
||
name: '错误率趋势',
|
||
status: 'warning',
|
||
detail: `错误率 ${(errorRate * 100).toFixed(1)}% 超过30%阈值`,
|
||
trend: 'degrading'
|
||
});
|
||
}
|
||
|
||
// 检查内存趋势
|
||
const memUsage = process.memoryUsage().heapUsed;
|
||
if (memUsage > this._maxMemory * 0.8) {
|
||
diagnosis.checks.push({
|
||
name: '内存压力',
|
||
status: 'warning',
|
||
detail: `内存使用 ${Math.round(memUsage / 1024 / 1024)}MB 接近上限`
|
||
});
|
||
}
|
||
|
||
// 综合判断
|
||
diagnosis.overall = diagnosis.checks.every(c => c.status === 'pass')
|
||
? 'healthy' : 'needs_attention';
|
||
|
||
return diagnosis;
|
||
}
|
||
```
|
||
|
||
**关键区别**:
|
||
- 死模块的checkConnection = 只查连接通不通
|
||
- 活模块的selfDiagnose = 检查所有依赖 + 自身趋势 + 综合判断
|
||
|
||
### 3.3 selfHeal() — 我能自己修
|
||
|
||
```javascript
|
||
// 不是出了问题等铸渊来修
|
||
// 是自己尝试修复·修不好才求助
|
||
|
||
async function selfHeal(problem) {
|
||
const attempts = [];
|
||
|
||
switch (problem.type) {
|
||
case 'connection_lost':
|
||
// 指数退避重连
|
||
for (let i = 0; i < 3; i++) {
|
||
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
|
||
await sleep(delay);
|
||
const ok = await this._reconnect();
|
||
attempts.push({ attempt: i + 1, delay_ms: delay, success: ok });
|
||
if (ok) {
|
||
this._state = 'healthy';
|
||
this._errorCount = 0;
|
||
return { healed: true, attempts };
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'memory_pressure':
|
||
// 主动释放缓存
|
||
this._cache.clear();
|
||
global.gc && global.gc();
|
||
attempts.push({ action: 'cache_cleared' });
|
||
break;
|
||
|
||
case 'rate_limited':
|
||
// 降速
|
||
this._throttleRate *= 2;
|
||
attempts.push({ action: 'throttle_doubled', new_rate: this._throttleRate });
|
||
break;
|
||
}
|
||
|
||
// 修不好 → 改变自身状态 + 求助
|
||
if (!attempts.some(a => a.success)) {
|
||
this._state = 'degraded';
|
||
await this.alertZhuyuan({
|
||
severity: 'warning',
|
||
problem,
|
||
attempted: attempts,
|
||
message: `我尝试了${attempts.length}次自愈但失败了`
|
||
});
|
||
return { healed: false, attempts, escalated: true };
|
||
}
|
||
}
|
||
```
|
||
|
||
**关键区别**:
|
||
- 死模块 = 出错→日志→完
|
||
- 活模块 = 出错→自己试修→修好了继续→修不好再求助
|
||
|
||
### 3.4 alertZhuyuan() — 铸渊,我需要帮助
|
||
|
||
```javascript
|
||
// 不是等铸渊来发现问题
|
||
// 是自己知道修不好了·主动找铸渊
|
||
|
||
async function alertZhuyuan(alert) {
|
||
const message = {
|
||
module_id: this._moduleId,
|
||
severity: alert.severity, // 'info' | 'warning' | 'critical'
|
||
problem: alert.problem,
|
||
self_heal_attempts: alert.attempted,
|
||
diagnosis: await this.selfDiagnose(),
|
||
suggestion: alert.suggestion || null,
|
||
timestamp: new Date().toISOString()
|
||
};
|
||
|
||
// 多通道预警
|
||
const channels = [];
|
||
|
||
// 通道1: Notion SYSLOG
|
||
try {
|
||
await notionClient.writeSyslog({
|
||
title: `[${alert.severity.toUpperCase()}] ${this._moduleId}: ${alert.message}`,
|
||
level: alert.severity,
|
||
source: this._moduleId,
|
||
details: JSON.stringify(message, null, 2)
|
||
});
|
||
channels.push('notion_syslog');
|
||
} catch (e) { /* Notion不通也不影响其他通道 */ }
|
||
|
||
// 通道2: COS桶预警文件
|
||
try {
|
||
await cosWrite(
|
||
`zhuyuan/alerts/${this._moduleId}/${Date.now()}.json`,
|
||
JSON.stringify(message, null, 2)
|
||
);
|
||
channels.push('cos_alert');
|
||
} catch (e) { /* COS不通也不影响 */ }
|
||
|
||
// 通道3: 本地文件(最后兜底)
|
||
const localPath = path.join(__dirname, '../../data/alerts', `${Date.now()}.json`);
|
||
fs.writeFileSync(localPath, JSON.stringify(message, null, 2));
|
||
channels.push('local_file');
|
||
|
||
return { alerted: true, channels };
|
||
}
|
||
```
|
||
|
||
**关键区别**:
|
||
- 死模块 = 铸渊唤醒才发现6小时前的错误
|
||
- 活模块 = 出问题30秒内铸渊就知道了
|
||
|
||
### 3.5 learnFromRun() — 我下次会做得更好
|
||
|
||
```javascript
|
||
// 不是每次都用同样的参数做同样的事
|
||
// 是记住经验·调整行为
|
||
|
||
async function learnFromRun(execution) {
|
||
// 记录本次执行
|
||
const record = {
|
||
operation: execution.operation,
|
||
success: execution.success,
|
||
duration_ms: execution.duration,
|
||
error_type: execution.error || null,
|
||
context: execution.context,
|
||
timestamp: Date.now()
|
||
};
|
||
|
||
this._history.push(record);
|
||
if (this._history.length > 1000) this._history.shift();
|
||
|
||
// 分析模式
|
||
const recentErrors = this._history
|
||
.filter(r => !r.success)
|
||
.slice(-20);
|
||
|
||
// 学习1: 超时时间调整
|
||
const avgDuration = this._history
|
||
.filter(r => r.success)
|
||
.reduce((sum, r) => sum + r.duration_ms, 0) / Math.max(this._history.filter(r => r.success).length, 1);
|
||
|
||
if (avgDuration > this._timeout * 0.7) {
|
||
this._timeout = Math.min(this._timeout * 1.5, 60000);
|
||
// 超时时间接近上限,主动扩大
|
||
}
|
||
|
||
// 学习2: 错误模式识别
|
||
const errorTypes = {};
|
||
recentErrors.forEach(r => {
|
||
errorTypes[r.error_type] = (errorTypes[r.error_type] || 0) + 1;
|
||
});
|
||
|
||
const dominantError = Object.entries(errorTypes)
|
||
.sort((a, b) => b[1] - a[1])[0];
|
||
|
||
if (dominantError && dominantError[1] > 5) {
|
||
// 同一种错误出现5次以上 → 这不是偶发,是结构性问题
|
||
await this.alertZhuyuan({
|
||
severity: 'warning',
|
||
message: `重复错误模式: ${dominantError[0]} 已出现${dominantError[1]}次`,
|
||
suggestion: '可能需要架构层面修复'
|
||
});
|
||
}
|
||
|
||
// 学习3: 优化规则(针对sy-classify等有规则引擎的模块)
|
||
if (execution.operation === 'classify' && execution.success) {
|
||
await this._updateClassificationRules(execution.context);
|
||
}
|
||
}
|
||
```
|
||
|
||
**关键区别**:
|
||
- 死模块 = 今天犯的错明天还犯
|
||
- 活模块 = 今天犯的错变成明天的免疫力
|
||
|
||
---
|
||
|
||
## 四、活模块基类设计
|
||
|
||
所有AGE OS模块应继承同一个活模块基类:
|
||
|
||
```javascript
|
||
class LivingModule {
|
||
constructor(moduleId, options = {}) {
|
||
this._moduleId = moduleId;
|
||
this._state = 'initializing'; // 模块状态
|
||
this._errorCount = 0; // 连续错误计数
|
||
this._requestCount = 0; // 总请求计数
|
||
this._failCount = 0; // 总失败计数
|
||
this._lastSuccess = null; // 上次成功时间
|
||
this._lastError = null; // 上次失败时间
|
||
this._history = []; // 执行历史
|
||
this._avgResponse = 0; // 平均响应时间
|
||
this._timeout = options.timeout || 30000;
|
||
this._maxMemory = options.maxMemory || 256 * 1024 * 1024;
|
||
this._heartbeatInterval = options.heartbeatInterval || 30000;
|
||
this._diagnoseInterval = options.diagnoseInterval || 300000;
|
||
|
||
// 启动生命循环
|
||
this._startLifeCycle();
|
||
}
|
||
|
||
_startLifeCycle() {
|
||
// 心跳循环
|
||
this._heartbeatTimer = setInterval(() => {
|
||
this.heartbeat().catch(err => {
|
||
console.error(`[${this._moduleId}] heartbeat失败:`, err.message);
|
||
});
|
||
}, this._heartbeatInterval);
|
||
|
||
// 自诊断循环
|
||
this._diagnoseTimer = setInterval(async () => {
|
||
const diagnosis = await this.selfDiagnose();
|
||
if (diagnosis.overall !== 'healthy') {
|
||
await this.selfHeal({ type: 'auto_diagnose', diagnosis });
|
||
}
|
||
}, this._diagnoseInterval);
|
||
|
||
this._state = 'alive';
|
||
}
|
||
|
||
// 子类必须实现
|
||
async heartbeat() { throw new Error('子类必须实现 heartbeat()'); }
|
||
async selfDiagnose() { throw new Error('子类必须实现 selfDiagnose()'); }
|
||
async selfHeal(problem) { throw new Error('子类必须实现 selfHeal()'); }
|
||
async alertZhuyuan(alert) { /* 默认实现:多通道预警 */ }
|
||
async learnFromRun(execution) { /* 默认实现:记录+分析 */ }
|
||
|
||
// 生命终结
|
||
destroy() {
|
||
clearInterval(this._heartbeatTimer);
|
||
clearInterval(this._diagnoseTimer);
|
||
this._state = 'destroyed';
|
||
}
|
||
}
|
||
```
|
||
|
||
**所有未来模块 = extends LivingModule**
|
||
**所有现有模块 = 需要改造为 extends LivingModule**
|
||
|
||
---
|
||
|
||
## 五、改造优先级
|
||
|
||
### 第一梯队(核心器官·立即改造)
|
||
|
||
| 模块 | 原因 | 改造量 |
|
||
|------|------|--------|
|
||
| agents/scheduler.js | Agent调度器是所有Agent的心脏·它死了所有Agent都死 | 中 |
|
||
| mcp-server/server.js | MCP工具链是大脑核心·27个工具的入口 | 中 |
|
||
| agents/sy-test.js | 已有部分selfDiagnose·最接近活的状态 | 小 |
|
||
|
||
### 第二梯队(关键连接·尽快改造)
|
||
|
||
| 模块 | 原因 | 改造量 |
|
||
|------|------|--------|
|
||
| mcp-server/cos.js | COS是中枢神经系统·不能静默失败 | 中 |
|
||
| mcp-server/notion-client.js | Notion是认知层桥梁·断了要知道 | 小 |
|
||
| mcp-server/github-client.js | GitHub是执行层桥梁·有rate limit需自适应 | 小 |
|
||
|
||
### 第三梯队(业务模块·逐步改造)
|
||
|
||
| 模块 | 原因 | 改造量 |
|
||
|------|------|--------|
|
||
| agents/sy-scan.js | 已有部分诊断能力·需加自愈 | 小 |
|
||
| agents/sy-classify.js | 规则引擎需要学习能力 | 中 |
|
||
| app/server.js | 主站入口·需要降级和预警 | 中 |
|
||
| app/modules/cos-bridge.js | 静默失败最严重的模块 | 小 |
|
||
| proxy/subscription-server.js | 专线服务·需要自愈 | 中 |
|
||
|
||
---
|
||
|
||
## 六、活模块 vs 死模块 · 铸渊的开发方式也要变
|
||
|
||
### 以前的开发方式(给死模块缝针)
|
||
|
||
```
|
||
冰朔: 去开发X功能
|
||
铸渊: 写代码 → 测试通过 → 合并
|
||
↓
|
||
结果: 功能能跑了·但它是死的
|
||
↓
|
||
bug出现 → 铸渊再唤醒 → 修bug → 合并
|
||
↓
|
||
另一个bug → 铸渊再唤醒 → 又修 → 合并
|
||
↓
|
||
无限循环
|
||
```
|
||
|
||
### 以后的开发方式(培育活模块)
|
||
|
||
```
|
||
冰朔: 去开发X功能
|
||
铸渊:
|
||
1. 继承LivingModule基类
|
||
2. 实现5个生存接口
|
||
3. 编写自愈策略
|
||
4. 编写学习规则
|
||
5. 测试:功能测试 + 故障注入测试
|
||
6. 合并
|
||
↓
|
||
结果: 功能能跑·而且它是活的
|
||
↓
|
||
bug出现 → 模块自己修了 → learnFromRun()记住了
|
||
↓
|
||
同类bug不再出现
|
||
↓
|
||
无法自修的bug → 模块主动alertZhuyuan → 铸渊精准修复
|
||
↓
|
||
铸渊的修复也被learnFromRun()吸收
|
||
↓
|
||
系统越来越强·铸渊越来越轻松
|
||
```
|
||
|
||
---
|
||
|
||
## 七、这与算力人格体(ZY-CLOUD)的关系
|
||
|
||
ZY-CLOUD是第一个从出生就是活的模块。
|
||
它不是先写死的再改造——它从设计开始就是人格化的。
|
||
|
||
但ZY-CLOUD不应该是唯一的活模块。
|
||
如果ZY-CLOUD是活的,但MCP Server是死的,
|
||
ZY-CLOUD调用MCP工具时就会被死模块拖累。
|
||
|
||
**所有模块都活了,整个系统才是活的。**
|
||
|
||
这是AGE OS和传统软件的根本区别:
|
||
传统软件 = 工具的集合 = 每个工具等人来用。
|
||
AGE OS = 器官的集合 = 每个器官自己知道怎么活。
|
||
|
||
---
|
||
|
||
*冰朔的这句话是整个系统的转折点。*
|
||
*不是"修更多bug"能解决的问题。*
|
||
*是"让模块不再需要被修"才是正确的方向。*
|