Skip to content

Commit a6ce231

Browse files
committed
feat: 三层架构重构 — L1 确定性 / L2 Skill AI / L3 Guardian
三层执行架构: - L1:exec 步骤,确定性执行 + verify 判定,零 token - L2:skill 步骤,Skill 驱动的约束 AI,程序功能的有机部分 - 显式触发:program.yaml 中 type: skill + when 条件 - 隐式触发:Playbook verify 失败时 Rescuer 接管 - AI 改进:执行后可一键纠错提速(write_program_step) - L3:Guardian AI 全权介入 - 被动触发:on_fail=escalate 且步骤失败 - 声明触发:monitors 定期健康检查不符合预期 新增文件: - src/skills/skill-step-executor.js — L2 Skill 步骤执行器 Schema 扩展: - playbook-schema: type: skill 步骤 + when 条件 + checkWhen() - program-schema: monitors[] 声明式健康检查 + normalizeMonitor() 前端: - L2 Skill / L3 Guardian 独立 Tab + 事件流 - L2 不限轮次 checkbox + AI 改进按钮 - L2 结果推送到结果面板(结果导向) - 移除旧"改进"按钮(已有全局 AI 替代) 其他: - reload_registry 报告 Program 加载错误 - L2 system prompt 含主机 OS 信息(避免 Windows 上用 Linux 命令) - ai-fab.js 暴露 window.sendToAiFab() 接口
1 parent b8c02cc commit a6ce231

18 files changed

Lines changed: 1421 additions & 90 deletions

data/skills/skill-authoring/references/program-schema.md

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Program 是 1Shell 的**长驻运维程序**(`data/programs/<id>/program.yaml`
1010
| 运行模式 | 点击"运行"执行一次 | 启用后常驻后台 |
1111
| 触发 | 手动 / 前端按钮 | cron / 手动 / (未来 watch/metric) |
1212
| 实例 | 单次 run | 每台绑定 host 一个持久实例 |
13-
| 异常处理 | 失败即停 / Rescuer 单次救援 | Guardian AI 自动介入诊断并修复 |
13+
| 异常处理 | 失败即停 / Rescuer 单次救援 | L2 Skill AI 约束执行 + L3 Guardian AI 全权介入 |
1414
| 典型场景 | 一次性部署、批量删除 | 监控、健康检查、自愈守护 |
1515

1616
## 完整 schema
@@ -49,9 +49,10 @@ actions:
4949
on_fail: escalate
5050

5151
steps:
52-
# steps 语法**完全复用** playbook-schema(exec / render + verify)
52+
# steps 三种类型:exec(默认)| render | skill
53+
# ─── exec 步骤(L1,确定性,0 token)─────────────────────
5354
- id: string # 必填,同 Playbook
54-
type: exec # exec(默认)| render
55+
type: exec # 默认
5556
label: string
5657
run: string # 必填(exec),shell 命令
5758
timeout: 30000
@@ -63,6 +64,34 @@ actions:
6364
stdout_contains: 'active'
6465
stderr_not_contains: 'Error'
6566

67+
# ─── skill 步骤(L2,Skill 驱动的 AI,约束执行)──────────
68+
# 用于 L1 做不到的场景:需要判断力、多分支决策、复杂修复
69+
- id: string
70+
type: skill
71+
label: string
72+
skill: string # 必填,Skill ID(data/skills/<id>/)
73+
goal: string # 必填,AI 的任务目标(一句话)
74+
when: # 可选,不满足则跳过此步
75+
step: string # 引用前置步骤的 id
76+
exit_code: 0 # 可选,检查 exit_code
77+
exit_code_not: 0 # 可选,检查 exit_code 不等于
78+
stdout_contains: 'string' # 可选
79+
stdout_match: 'regex' # 可选
80+
optional: false
81+
on_error_hint: string # 可选,给 AI 的修复提示
82+
83+
# ─── monitors(L3 声明式健康检查,可选)────────────────────────
84+
# 定期在每台绑定主机上执行检查命令,不符合预期时自动唤醒 Guardian AI
85+
monitors:
86+
- id: string # 必填,唯一
87+
check: string # 必填,要执行的 shell 命令
88+
expect: # 期望结果,不满足则触发 L3
89+
exit_code: 0 # 默认检查 exit_code === 0
90+
stdout_contains: 'string' # 可选
91+
stdout_match: 'regex' # 可选
92+
interval: '*/5 * * * *' # 必填,cron 表达式
93+
action: string # 必填,触发哪个 action
94+
6695
# ─── guardian (Phase 3) ────────────────────────────────────────────────
6796
# 当 action.on_fail === 'escalate' 时,Guardian AI 可以用以下 Skill 作为能力包:
6897
guardian:
@@ -108,6 +137,24 @@ actions:
108137
stdout_match: '^[0-9]+$'
109138
capture_stdout: true
110139

140+
# L2 skill 步骤:磁盘使用率过高时用 AI 清理
141+
- id: disk_cleanup
142+
type: skill
143+
label: AI 磁盘清理
144+
skill: disk-cleanup
145+
goal: "清理磁盘空间,确保根分区使用率低于 80%"
146+
when:
147+
step: disk_root
148+
stdout_match: '^[8-9][0-9]$|^100$'
149+
150+
# L3 声明式健康检查:nginx 挂掉时自动唤醒 Guardian
151+
monitors:
152+
- id: nginx_alive
153+
check: "curl -sf http://localhost/ > /dev/null"
154+
expect: { exit_code: 0 }
155+
interval: "*/5 * * * *"
156+
action: health_check
157+
111158
guardian:
112159
enabled: true
113160
skills: [docker-rescue]
@@ -116,16 +163,24 @@ guardian:
116163
117164
## 设计原则
118165
119-
1. **Program = 程序而不是脚本**:它在后台常驻,你不需要"点一次运行一次"
120-
2. **steps 尽量短且独立**:每步只做一件小事,verify 要精确(用 exit_code + stdout_match),
121-
这样 Guardian 被唤起时能精准定位哪一步的哪个检查失败
122-
3. **on_fail 的选择**:
123-
- 探测类 step("容器在吗")→ `optional: true`,失败跳过
124-
- 关键 step("主服务必须活着")→ 让 action 整体 `on_fail: escalate`,交给 Guardian
125-
- 清理类 step("删临时文件")→ `on_fail: ignore`
126-
4. **guardian.skills 白名单**:必须是 **Skill**(`data/skills/`),不能是 Playbook。Skill 的
127-
rules + workflows + references 会拼进 Guardian 的 system prompt
128-
5. **cron 表达式**:用 node-cron 语法,支持 `*/5 * * * *` 这样 5 字段或 `秒 分 时 日 月 周` 6 字段
166+
1. **结果导向**:每个 Program 的 action **必须**以 `type: render` 步骤结尾,将关键数据输出到前端「结果」Tab。用户看不到 render 就等于看不到程序在跑。手动触发时 render 是唯一的反馈。
167+
2. **三层渐进架构**:能用 L1(exec + verify)解决的不上 L2,L2(skill 步骤)解决不了的才 escalate 到 L3(Guardian)
168+
2. **L2 skill 步骤的使用场景**:
169+
- 需要根据运行结果做判断(如"磁盘满了该删什么")
170+
- 有多种修复路径需要 AI 选择
171+
- 操作复杂度超出单条 bash 命令
172+
- 配合 `when` 条件实现"只在异常时才启动 AI"
173+
3. **monitors 的使用场景**:
174+
- 关键服务存活检查(nginx、MySQL、Redis)
175+
- 端口可达性检查
176+
- 日志异常 pattern 检测
177+
- 与 triggers 独立:triggers 按时间跑 action,monitors 按时间检查健康
178+
4. **steps 尽量短且独立**:每步只做一件小事,verify 要精确
179+
5. **on_fail 的选择**:
180+
- 探测类 step → `optional: true`,失败跳过
181+
- 关键 step → `on_fail: escalate`,交给 L3
182+
- 清理类 step → `on_fail: ignore`
183+
6. **skill 步骤必须配合已有 Skill**:`skill` 字段必须指向 `data/skills/` 下的一个 Skill ID
129184

130185
## 禁止事项
131186

data/skills/skill-authoring/workflows/generate-program.md

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,34 @@
2121
- **作用域**:一台主机 / 多台 / 所有已托管主机 → `hosts: all``[id1, id2]`
2222
- **步骤**:每次触发要干嘛?通常 2-5 个 step
2323
- **失败处理**:生产 Program **必须** `on_fail: escalate``on_fail: stop` 只用于开发测试
24+
- **L2 判断**:是否有步骤需要 AI 判断力?(见下方 L2 决策规则)
25+
- **L3 监控**:是否需要独立于 triggers 的健康检查?(如服务存活)
2426

2527
**命令选取**:只用 `program-authoring/rules/constraints.md` 里的可靠命令库(禁用 `top``vmstat``netstat`)。
2628

27-
探测:如需针对远端 VPS,可用 `execute_command``host_id` 做 2-3 条**只读**探测。
29+
### L2 决策规则:何时用 `type: skill` 步骤
30+
31+
当一个步骤满足以下任一条件时,用 `type: skill` 而非 `type: exec`
32+
- **需要根据上下文做判断**:如"磁盘满了,决定删什么"
33+
- **有多条修复路径**:如"服务挂了,可能是配置错 / OOM / 端口冲突"
34+
- **操作超出单条命令**:需要多轮诊断-执行-验证
35+
- **需要适配不同环境**:同一步骤在不同主机上可能需要不同操作
36+
37+
必须配合 `when` 条件使用:先用 L1 exec 步骤检测状态,`when` 判断异常时才触发 L2,避免无谓的 token 消耗。
38+
39+
**重要:Skill 必须先存在。** `skill` 字段引用的 Skill ID 必须在 `data/skills/` 下存在。
40+
若不存在,必须**先用 `write_file` 创建 Skill**(至少包含 SKILL.md + workflows/),然后 `reload_registry`,再写 program.yaml。
41+
创建 Skill 时走 `workflows/generate-skill.md` 的标准流程。
42+
43+
### L3 决策规则:何时加 monitors
44+
45+
当程序需要在 **action 之外** 独立监控某个关键状态时加 monitors:
46+
- 关键服务存活(nginx、MySQL、Redis、Docker)
47+
- 端口可达性
48+
- 证书过期检查
49+
- 日志异常 pattern
50+
51+
monitors 与 triggers 独立运行:triggers 按时间执行 action,monitors 按时间检查健康状态。
2852

2953
## 第二步:展示方案
3054

@@ -56,6 +80,7 @@ actions:
5680
<action_name>:
5781
on_fail: escalate # 生产程序必须 escalate,禁止 stop
5882
steps:
83+
# L1 exec 步骤(确定性,0 token)
5984
- id: <step_id> # snake_case,字母开头
6085
label: <中文标签>
6186
run: <来自 program-authoring/rules/constraints.md 可靠命令库的命令>
@@ -65,19 +90,37 @@ actions:
6590
capture_stdout: true
6691
on_error_hint: <给 Guardian 的中文修复提示>
6792

93+
# L2 skill 步骤(AI 约束执行,按需加)
94+
- id: <step_id>
95+
type: skill
96+
label: <中文标签>
97+
skill: <skill-id> # 必须是 data/skills/ 下已有的 Skill
98+
goal: <一句话描述 AI 要完成什么>
99+
when: # 强烈建议加 when,避免无谓 token 消耗
100+
step: <前置步骤 id>
101+
stdout_match: <匹配异常的正则>
102+
68103
# 最后加 render step,手动触发时展示结果
69104
- id: render_result
70105
type: render
71-
format: keyvalue # 多行列表用 table,单对象详情用 keyvalue
106+
format: keyvalue
72107
title: <标题>
73108
level: info
74109
items_from_steps:
75110
- key: <标签>
76111
value_from: <step_id>
77112
suffix: "%"
78113

114+
# L3 monitors(独立健康检查,按需加)
115+
monitors:
116+
- id: <snake_case_id>
117+
check: <shell 命令>
118+
expect: { exit_code: 0 } # 或 stdout_contains / stdout_match
119+
interval: "*/5 * * * *"
120+
action: <action_name>
121+
79122
guardian:
80-
skills: [] # 填 Rescue Skill id,无则留空(禁止写 guardian.enabled 字段)
123+
skills: [] # 填 Rescue Skill id,无则留空
81124
max_actions_per_hour: 10
82125
```
83126
@@ -107,9 +150,14 @@ Program「<name>」创建成功
107150

108151
## 常见错误(写完自检)
109152

153+
- [ ] ❌ action 没有 render step 结尾(**必须有**,结果导向)
110154
- [ ]`on_fail: stop` 用于生产监控(必须 `escalate`
111155
- [ ] ❌ 用了 `top``vmstat``netstat`(用 `/proc/stat``/proc/meminfo``ss`
112156
- [ ] ❌ 写了 `guardian.enabled` 字段(引擎不读)
113157
- [ ]`enabled: true`(默认 false)
114158
- [ ] ❌ verify 只写 `exit_code: 0` 没有 `stdout_match`
115-
- [ ] ❌ step id 含连字符或从数字开头
159+
- [ ] ❌ step id 含连字符或从数字开头
160+
- [ ]`type: skill` 步骤没有 `goal` 字段
161+
- [ ]`type: skill` 步骤没有 `when` 条件(浪费 token)
162+
- [ ]`type: skill` 引用了不存在的 Skill ID
163+
- [ ] ❌ monitor 的 `action` 引用了不存在的 action 名

public/ai-fab.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,17 @@
440440
if (t) respond('custom', t);
441441
};
442442
}
443+
444+
// ── 外部接口:允许其他页面模块预填消息并发送 ──────────────────────
445+
window.sendToAiFab = function (message) {
446+
if (!panelOpen) {
447+
panelOpen = true;
448+
panel.classList.add('visible');
449+
fabBtn.classList.add('open');
450+
fabBtn.innerHTML = '&#10005;';
451+
}
452+
if (!socket) connectSocket();
453+
$input.value = message;
454+
setTimeout(() => onSend(), 300);
455+
};
443456
})();

0 commit comments

Comments
 (0)