云小猫异步审批执行系统设计与实现
摘要
本文详细记录了云小猫(基于 OpenClaw 框架的运维助手 Agent)审批系统的架构改造过程。针对原有同步阻塞式审批流程存在的阻塞等待、执行时机不可控、缺乏追溯能力等问题,设计并实现了一套基于异步消息处理的审批执行系统。该系统将审批请求创建、状态管理、操作执行解耦,通过独立的服务完成审批流程,实现了审批不阻塞聊天、执行可追溯、危险操作规范化等目标。
关键词:审批系统、异步处理、Agent 安全、运维自动化、OpenClaw
1. 引言
1.1 背景
云小猫是基于 OpenClaw 框架构建的运维助手 Agent,负责处理用户的运维请求。在实际使用中,部分操作(如重启容器、停止服务)具有风险性,需要管理员审批确认后才能执行。
原有的审批流程采用同步阻塞模式:云小猫发起审批请求后,需要等待管理员确认才能继续处理后续消息。这种设计存在明显缺陷:
| 问题 | 影响 |
|---|---|
| 阻塞聊天 | 审批等待期间,云小猫无法响应用户的其他消息 |
| 执行时机不可控 | 审批通过后由云小猫执行,可能因等待过久而产生状态不一致 |
| 缺乏追溯能力 | 审批状态存储在 JSON 文件中,无法完整追溯流程 |
| 安全隐患 | 危险操作由 Agent 直接执行,缺乏独立的安全校验 |
1.2 改造目标
| 目标 | 描述 |
|---|---|
| 异步处理 | 审批请求创建后立即返回,不阻塞聊天功能 |
| 自动执行 | 审批通过后由独立服务自动执行操作,避免延迟 |
| 完整追溯 | 审批流程全程记录到数据库,支持审计查询 |
| 安全规范 | 定义危险指令清单,禁止操作直接拒绝,高风险操作需审批 |
2. 架构对比
2.1 原有架构(同步阻塞式)
用户请求 → 云小猫 → 发起审批 → 阻塞等待
↓
管理员审批
↓
云小猫继续执行
↓
返回结果给用户
问题:整个流程中云小猫被阻塞,无法处理其他消息。
2.2 新架构(异步执行式)
用户请求 → 云小猫 → 调用审批执行中心 → 立即返回"已提交审批"
↓
云小猫继续处理其他消息
↓
审批执行中心独立运行:
├─ 管理员审批通过 → 自动执行 → 通知用户
├─ 管理员审批拒绝 → 通知用户
└─ 审批超时 → 自动取消 → 通知用户
优势:云小猫不阻塞,审批执行由独立服务完成,流程可追溯。
3. 系统架构设计
3.1 整体架构图
┌─────────────────────────────────────────────────────────────────────┐
│ 云小猫 (OpenClaw Agent) │
│ 主 Agent - 仅负责聊天协调 │
│ ↓ │
│ 发起审批请求后立即返回 │
│ 继续处理用户其他消息 │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 审批执行中心 (Approval Executor) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 接收审批 │ → │ 存储审批 │ → │ 推送通知 │ → │ 等待决策 │ │
│ │ 请求 │ │ 记录 │ │ (QQ/企微) │ │ (轮询DB) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ↓ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 记录日志 │ ← │ 通知结果 │ ← │ 执行操作 │ ← │ 审批通过 │ │
│ │ (审计) │ │ (用户) │ │ (实际执行) │ │ 或超时 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 数据库 (MySQL - management_api) │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ approval_requests │ │ approval_execution_logs │ │
│ │ (审批请求记录) │ │ (执行日志/可追溯) │ │
│ └──────────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
3.2 核心组件说明
| 组件 | 技术栈 | 端口 | 功能描述 |
|---|---|---|---|
| 审批执行中心 API | Flask + PyMySQL | 5682 | 提供 RESTful API,处理审批 CRUD、执行操作、发送通知 |
| 审批状态检查器 | Python 轮询脚本 | - | 后台服务,定时检查审批状态变更,生成通知文件 |
| 数据库 | MySQL 8.0 | 3306 | 持久化审批记录和执行日志,支持审计查询 |
| QQ 消息处理器 | Python 脚本 | - | 解析审批确认/拒绝消息,调用 API 完成审批 |
3.3 数据流程详解
步骤 1: 用户发起请求
用户在 QQ 发送: "帮我重启 MySQL 容器"
↓
步骤 2: 云小猫判断风险等级
根据操作类型判断: docker_restart = medium 风险
↓
步骤 3: 调用审批执行中心
POST /api/approval/create
{
"actionType": "docker_restart",
"actionParams": {"container": "MySQL"},
"riskLevel": "medium",
...
}
↓
步骤 4: 创建审批记录
- 生成 UUID 作为审批 ID
- 写入 approval_requests 表
- 写入 approval_execution_logs (event: created)
↓
步骤 5: 推送通知
- 查询 subscribers 表获取订阅者
- 发送 QQ 私信 / 企业微信通知
↓
步骤 6: 立即返回用户
"已提交审批请求!审批ID: xxx,等待管理员确认"
云小猫继续处理其他消息(不阻塞)
↓
步骤 7: 管理员审批
管理员回复: "确认 xxx" 或 "拒绝 xxx"
↓
步骤 8: 审批处理脚本解析
- 匹配审批 ID
- 调用 /api/approval/{id}/approve 或 reject
↓
步骤 9: 执行操作(如果通过)
- 执行 docker restart MySQL
- 更新审批状态为 executed
- 记录执行结果到日志
↓
步骤 10: 通知用户结果
- 审批通过: "✅ 审批已通过,容器 MySQL 已重启"
- 审批拒绝: "❌ 审批已拒绝,操作不会执行"
4. 数据库设计
4.1 审批请求表 (approval_requests)
CREATE TABLE approval_requests (
id VARCHAR(36) PRIMARY KEY, -- 审批 ID (UUID)
action_type VARCHAR(50) NOT NULL, -- 操作类型
action_params JSON, -- 操作参数 (JSON 格式)
risk_level ENUM('low', 'medium', 'high'), -- 风险等级
reason TEXT, -- 审批原因/描述
status ENUM('pending', 'approved', 'rejected',
'timeout', 'executed', 'failed'), -- 审批状态
requester_id VARCHAR(50), -- 请求人 ID
requester_name VARCHAR(100), -- 请求人名称
approver_id VARCHAR(50), -- 审批人 ID
approver_name VARCHAR(100), -- 审批人名称
source_channel VARCHAR(50), -- 来源渠道 (qq/wecom/api)
source_message TEXT, -- 原始请求消息
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, -- 创建时间
approved_at DATETIME, -- 审批时间
executed_at DATETIME, -- 执行时间
timeout_at DATETIME, -- 超时时间
execution_result JSON, -- 执行结果 (JSON 格式)
error_message TEXT, -- 错误信息
INDEX idx_status (status),
INDEX idx_created (created_at),
INDEX idx_timeout (timeout_at),
INDEX idx_action_type (action_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4.2 审批执行日志表 (approval_execution_logs)
此表用于完整追溯审批流程的每一步。
CREATE TABLE approval_execution_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
approval_id VARCHAR(36) NOT NULL, -- 关联审批 ID
event_type VARCHAR(50) NOT NULL, -- 事件类型
event_data JSON, -- 事件详情 (JSON 格式)
operator_id VARCHAR(50), -- 操作人 ID
operator_name VARCHAR(100), -- 操作人名称
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, -- 创建时间
FOREIGN KEY (approval_id)
REFERENCES approval_requests(id) ON DELETE CASCADE,
INDEX idx_approval (approval_id),
INDEX idx_event_type (event_type),
INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4.3 事件类型定义
| 事件类型 | 触发时机 | event_data 示例 |
|---|---|---|
created |
审批请求创建 | {"actionType": "docker_restart", "riskLevel": "medium"} |
approved |
审批通过 | {"approverName": "管理员", "approverChannel": "qq"} |
rejected |
审批拒绝 | {"approverName": "管理员", "reason": "风险太高"} |
timeout |
审批超时 | {"auto": true} |
executed |
操作执行完成 | {"success": true, "message": "容器已重启"} |
failed |
操作执行失败 | {"error": "容器不存在"} |
5. API 接口设计
5.1 创建审批请求
端点: POST /api/approval/create
请求头:
X-APPROVAL-TOKEN: approval-secret-token-2026
Content-Type: application/json
请求体:
{
"actionType": "docker_restart",
"actionParams": {"container": "MySQL"},
"riskLevel": "medium",
"reason": "用户请求重启 MySQL 容器",
"requesterId": "yunmao",
"requesterName": "云小猫",
"sourceChannel": "qq",
"sourceMessage": "帮我重启 MySQL",
"timeoutMinutes": 30,
"notifyChannels": ["qq"]
}
成功响应(需审批):
{
"success": true,
"approvalId": "566cf844-c583-4897-b74e-c158b163ba15",
"status": "pending",
"message": "审批请求已创建,等待管理员确认",
"timeoutAt": "2026-04-01T13:25:24.137281"
}
成功响应(低风险,直接执行):
{
"success": true,
"approvalId": "5944f5cb-df8b-4bd4-81e1-192931dd7252",
"status": "executed",
"executionResult": {
"success": true,
"message": "容器列表获取成功",
"output": "NAMES STATUS..."
},
"message": "低风险操作已直接执行"
}
拒绝响应(禁止操作):
{
"success": false,
"status": "forbidden",
"error": "禁止执行危险操作: rm -rf /"
}
5.2 批准审批并执行
端点: POST /api/approval/{approval_id}/approve
请求体:
{
"approverId": "admin",
"approverName": "管理员",
"approverChannel": "qq"
}
响应:
{
"success": true,
"approvalId": "566cf844-c583-4897-b74e-c158b163ba15",
"status": "executed",
"executionResult": {
"success": true,
"message": "容器 MySQL 已重启",
"output": "MySQL\n"
}
}
5.3 拒绝审批
端点: POST /api/approval/{approval_id}/reject
请求体:
{
"approverId": "admin",
"approverName": "管理员",
"reason": "风险太高,暂不执行"
}
响应:
{
"success": true,
"approvalId": "a82563e9-b09e-43c8-845b-aadc3f60b45e",
"status": "rejected",
"message": "审批已拒绝"
}
5.4 轮询审批状态
端点: GET /api/approval/poll
响应:
{
"success": true,
"data": [
{
"id": "566cf844-c583-4897-b74e-c158b163ba15",
"action_type": "docker_restart",
"action_params": "{\"container\": \"MySQL\"}",
"risk_level": "medium",
"status": "executed",
"requester_name": "云小猫",
"created_at": "2026-04-01T12:55:24",
"timeout_at": "2026-04-01T13:25:24"
}
],
"count": 1
}
5.5 获取执行日志
端点: GET /api/approval/logs/{approval_id}
响应:
{
"success": true,
"data": [
{
"id": 19,
"approval_id": "e77a13af-e6b1-47bc-8f82-09763dc110eb",
"event_type": "created",
"event_data": "{\"actionType\": \"docker_restart\", \"riskLevel\": \"medium\"}",
"created_at": "2026-04-01T13:11:43"
},
{
"id": 20,
"approval_id": "e77a13af-e6b1-47bc-8f82-09763dc110eb",
"event_type": "approved",
"event_data": "{\"approverName\": \"QQ用户\"}",
"operator_name": "QQ用户",
"created_at": "2026-04-01T13:12:02"
},
{
"id": 21,
"approval_id": "e77a13af-e6b1-47bc-8f82-09763dc110eb",
"event_type": "executed",
"event_data": "{\"success\": true, \"message\": \"容器 OpenResty 已重启\"}",
"created_at": "2026-04-01T13:12:03"
}
]
}
6. 风险判断机制
6.1 风险等级定义
| 等级 | 定义 | 操作示例 | 需要审批 | 默认超时 |
|---|---|---|---|---|
| low | 只读操作,不改变系统状态 | 查看容器列表、检查服务器状态 | 否 | - |
| medium | 可能暂时影响服务可用性 | 重启容器、启动容器 | 是 | 30 分钟 |
| high | 可能导致服务中断或数据丢失 | 停止容器、删除容器、执行命令 | 是 | 60 分钟 |
| forbidden | 绝对禁止执行的操作 | 删除根目录、格式化磁盘 | - | - |
6.2 操作类型映射表
| 操作类型 | 风险等级 | 需要审批 | 默认超时 | 描述 |
|---|---|---|---|---|
docker_list |
low | 否 | - | 列出所有容器 |
docker_inspect |
low | 否 | - | 查看容器详情 |
docker_logs |
low | 否 | - | 查看容器日志 |
check_status |
low | 否 | - | 检查服务器状态 |
check_disk |
low | 否 | - | 检查磁盘使用情况 |
check_memory |
low | 否 | - | 检查内存使用情况 |
docker_start |
medium | 是 | 30分钟 | 启动已停止的容器 |
docker_restart |
medium | 是 | 30分钟 | 重启运行中的容器 |
docker_stop |
high | 是 | 60分钟 | 停止运行中的容器 |
docker_rm |
high | 是 | 60分钟 | 删除容器 |
docker_exec |
high | 是 | 60分钟 | 在容器内执行命令 |
shell_command |
high | 是 | 60分钟 | 在宿主机执行命令 |
6.3 风险判断代码实现
# 危险操作配置
DANGEROUS_PATTERNS = ['rm -rf', 'mkfs', 'dd if=', '> /dev/', 'chmod 777 /']
CRITICAL_SERVICES = ['mysql', 'nginx', 'redis', 'postgresql', 'mongodb']
# 高风险操作列表
HIGH_RISK_ACTIONS = [
'docker_stop', 'docker_rm', 'docker_rmi', 'docker_exec',
'shell_command', 'edit_config', 'db_delete', 'db_drop'
]
# 中风险操作列表
MEDIUM_RISK_ACTIONS = [
'docker_start', 'docker_restart',
'restart_service', 'db_insert', 'db_update'
]
# 禁止操作列表
FORBIDDEN_PATTERNS = [
'rm -rf /', 'rm -rf /*', 'mkfs', 'dd if=/dev/zero',
'> /dev/sda', 'chmod 777 /', 'chmod -R 777 /',
':(){ :|:& };:', 'curl | bash', 'wget | sh'
]
def determine_risk_level(action_type, params):
"""
根据操作类型和参数判断风险等级
Returns:
tuple: (risk_level, reason, need_approval)
"""
# 1. 检查是否为禁止操作
if action_type == 'shell_command':
command = params.get('command', '').lower()
for pattern in FORBIDDEN_PATTERNS:
if pattern.lower() in command:
return 'forbidden', f'禁止执行危险操作: {pattern}', False
# 2. Shell 命令特殊处理
if action_type == 'shell_command':
command = params.get('command', '')
# 检查危险模式
for pattern in DANGEROUS_PATTERNS:
if pattern in command:
return 'forbidden', f'禁止执行危险命令: {pattern}', False
# 检查关键服务
for service in CRITICAL_SERVICES:
if service in command.lower():
return 'high', f'涉及关键服务: {service}', True
# 3. 根据操作类型判断
if action_type in HIGH_RISK_ACTIONS:
return 'high', None, True
if action_type in MEDIUM_RISK_ACTIONS:
return 'medium', None, True
return 'low', None, False
6.4 禁止操作列表
以下操作会被系统直接拒绝,不进入审批流程:
| 命令模式 | 危险原因 |
|---|---|
rm -rf / |
删除根目录,系统毁灭 |
rm -rf /* |
删除根目录所有文件 |
mkfs |
格式化磁盘,数据丢失 |
dd if=/dev/zero |
用零覆盖磁盘数据 |
> /dev/sda |
覆盖磁盘原始数据 |
chmod 777 / |
设置危险的完全权限 |
chmod -R 777 / |
递归设置危险权限 |
| `:(){ : | :& };:` |
curl ... | bash |
远程执行未知脚本 |
wget ... | sh |
远程执行未知脚本 |
7. QQ 消息集成
7.1 消息处理优先级
当云小猫收到 QQ 消息时,按以下优先级处理:
QQ 消息
│
▼
判断消息类型(优先级从高到低)
│
├── 1. 审批消息?
│ │
│ ├── "确认 xxx" → 调用审批处理脚本 → 批准审批
│ ├── "拒绝 xxx" → 调用审批处理脚本 → 拒绝审批
│ └── "待审批" → 调用审批处理脚本 → 返回审批列表
│
├── 2. 帮助消息?
│ └── 关键词: 帮助、怎么用、功能 → 返回使用帮助
│
├── 3. 知识问答?
│ └── 关键词: 知识库、文档、配置 → 调用 MaxKB 知识库
│
└── 4. 任务执行?
└── 检测操作意图 → 调用审批执行中心
7.2 审批命令格式
| 命令格式 | 功能 | 示例 |
|---|---|---|
确认 {ID} |
批准审批 | 确认 566cf844 |
批准 {ID} |
同意审批 | 批准 566cf844 |
同意 {ID} |
同意审批 | 同意 566cf844 |
拒绝 {ID} |
拒绝审批 | 拒绝 566cf844 |
否决 {ID} |
拒绝审批 | 否决 566cf844 |
待审批 |
查看审批列表 | 待审批 |
ID 简化输入:支持使用审批 ID 的前 8 位,系统自动匹配完整 ID。
7.3 审批处理脚本
脚本位置:/root/.openclaw/skills/approval-handler/approval-handler.py
核心功能:
- 解析审批确认/拒绝消息
- 通过部分 ID 查找完整审批记录
- 调用审批执行中心 API
- 返回格式化的审批结果给云小猫
调用示例:
# 查看待审批列表
python3 /root/.openclaw/skills/approval-handler/approval-handler.py "待审批"
# 确认审批
python3 /root/.openclaw/skills/approval-handler/approval-handler.py "确认 e77a13af"
# 拒绝审批
python3 /root/.openclaw/skills/approval-handler/approval-handler.py "拒绝 e77a13af"
8. 完整测试流程
8.1 测试场景 1:用户请求重启容器(中风险)
用户操作:在 QQ 发送消息
帮我重启 OpenResty 容器
云小猫调用审批执行中心:
curl -X POST http://localhost:5682/api/approval/create \
-H "X-APPROVAL-TOKEN: approval-secret-token-2026" \
-H "Content-Type: application/json" \
-d '{
"actionType": "docker_restart",
"actionParams": {"container": "OpenResty"},
"reason": "用户请求重启 OpenResty 容器",
"requesterId": "yunmao",
"requesterName": "云小猫",
"sourceChannel": "qq"
}'
系统响应:
{
"success": true,
"approvalId": "e77a13af-e6b1-47bc-8f82-09763dc110eb",
"status": "pending",
"message": "审批请求已创建,等待管理员确认",
"timeoutAt": "2026-04-01T13:41:43.160238"
}
云小猫回复用户:
已提交审批请求!
📋 审批ID: e77a13af...
🎯 操作: 重启容器 OpenResty
⚠️ 风险等级: 中等
已通知管理员,请等待审批结果。
8.2 测试场景 2:管理员查看待审批列表
管理员操作:在 QQ 发送消息
待审批
系统响应:
### 📋 待处理审批列表
📋 审批ID: e77a13af...
🎯 操作: docker_restart
📊 参数: {"container": "OpenResty"}
⚠️ 风险: 🟡 MEDIUM
📝 原因: 用户请求重启 OpenResty 容器
回复 "确认 {ID前8位}" 或 "拒绝 {ID前8位}" 进行审批
8.3 测试场景 3:管理员确认审批
管理员操作:在 QQ 发送消息
确认 e77a13af
系统响应:
✅ 审批已通过
✅ 执行成功
容器 OpenResty 已重启
详细信息:
- 操作: e77a13af...
- 结果: {"success": true, "message": "容器 OpenResty 已重启"}
8.4 测试场景 4:查看执行日志(可追溯)
API 调用:
curl http://localhost:5682/api/approval/logs/e77a13af-e6b1-47bc-8f82-09763dc110eb \
-H "X-APPROVAL-TOKEN: approval-secret-token-2026"
系统响应:
{
"data": [
{
"event_type": "created",
"event_data": "{\"actionType\": \"docker_restart\", \"riskLevel\": \"medium\"}",
"created_at": "2026-04-01T13:11:43"
},
{
"event_type": "approved",
"event_data": "{\"approverName\": \"QQ用户\"}",
"operator_name": "QQ用户",
"created_at": "2026-04-01T13:12:02"
},
{
"event_type": "executed",
"event_data": "{\"success\": true, \"message\": \"容器 OpenResty 已重启\"}",
"created_at": "2026-04-01T13:12:03"
}
]
}
8.5 测试场景 5:高风险操作拒绝
用户操作:请求停止容器
停止 clawith-frontend-1 容器
系统响应:
{
"success": true,
"approvalId": "a82563e9-b09e-43c8-845b-aadc3f60b45e",
"status": "pending",
"riskLevel": "high"
}
管理员操作:
拒绝 a82563e9
系统响应:
❌ 审批已拒绝,操作不会执行
8.6 测试场景 6:低风险操作直接执行
用户操作:
查看所有容器
系统响应:
状态: 直接执行(无需审批)
执行结果: 容器列表获取成功
容器列表(前5个):
NAMES STATUS PORTS
clawith-backend-1 Up 2 hours (healthy) 8000/tcp
clawith-frontend-1 Up 2 hours 80/tcp, 0.0.0.0:3030->3000/tcp
clawith-postgres-1 Up 2 hours (healthy) 5432/tcp
clawith-redis-1 Up 2 hours (healthy) 6379/tcp
1Panel-ollama-CqL2 Up 22 hours 0.0.0.0:11434->11434/tcp
8.7 测试场景 7:禁止操作直接拒绝
用户操作:
执行命令: rm -rf /
系统响应:
状态: forbidden
错误: 禁止执行危险操作: rm -rf /
8.8 测试结果汇总
| 测试场景 | 预期结果 | 实际结果 | 状态 |
|---|---|---|---|
| 中风险操作请求 | 创建审批,状态 pending | ✅ 正确 | 通过 |
| 查看待审批列表 | 返回格式化列表 | ✅ 正确 | 通过 |
| 确认审批 | 执行成功,状态 executed | ✅ 正确 | 通过 |
| 查看执行日志 | 返回完整事件链 | ✅ 正确 | 通过 |
| 高风险操作 | 状态 pending,风险 high | ✅ 正确 | 通过 |
| 拒绝审批 | 状态 rejected,不执行 | ✅ 正确 | 通过 |
| 低风险操作 | 直接执行,无需审批 | ✅ 正确 | 通过 |
| 禁止操作 | 状态 forbidden,直接拒绝 | ✅ 正确 | 通过 |
9. 安全设计
9.1 认证机制
所有 API 请求需要携带认证 Token:
X-APPROVAL-TOKEN: approval-secret-token-2026
未认证请求返回 401 Unauthorized:
{
"success": false,
"error": "Unauthorized"
}
9.2 操作隔离
| 安全措施 | 说明 |
|---|---|
| 独立进程 | 审批执行服务以独立进程运行,与云小猫隔离 |
| 子进程执行 | 操作执行通过子进程调用,设置超时限制(60-120秒) |
| 二次校验 | 危险命令在执行前进行二次模式匹配校验 |
| 权限分离 | 云小猫只有创建审批的权限,执行权限在审批服务 |
9.3 审计日志
所有审批操作记录到数据库,包含:
- 审批请求创建时间
- 请求人信息(ID、名称、渠道)
- 审批人信息(ID、名称、渠道)
- 审批决策(通过/拒绝/超时)
- 执行结果(成功/失败、输出信息)
- 执行时间
日志保留期限:90 天
10. 部署说明
10.1 服务管理
# 启动审批执行服务
systemctl start approval-executor
# 启动审批状态检查服务
systemctl start approval-checker
# 查看服务状态
systemctl status approval-executor
systemctl status approval-checker
# 查看日志
journalctl -u approval-executor -f
journalctl -u approval-checker -f
10.2 配置文件位置
| 文件 | 路径 | 用途 |
|---|---|---|
| 审批执行服务 | /opt/approval-executor/app.py |
Flask API 服务主程序 |
| Systemd 服务配置 | /etc/systemd/system/approval-executor.service |
服务启动配置 |
| 状态检查器 | /opt/approval-executor/approval_checker.py |
状态轮询脚本 |
| QQ 审批处理器 | /root/.openclaw/skills/approval-handler/approval-handler.py |
QQ 消息处理脚本 |
| 危险指令规范 | /root/.openclaw/workspace/dangerous-operations.md |
操作分类定义文档 |
| 云小猫工作配置 | /root/.openclaw/workspace/AGENTS.md |
云小猫工作流程配置 |
| 云小猫灵魂配置 | /root/.openclaw/workspace/SOUL.md |
云小猫核心行为定义 |
10.3 环境变量
# API Token(可在 systemd service 文件中配置)
APPROVAL_API_TOKEN=approval-secret-token-2026
# QQ Bot API 地址
QQ_BOT_API=http://127.0.0.1:5700
# 企业微信 Webhook(可选)
WECOM_BOT_WEBHOOK=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
11. 总结与展望
11.1 实现成果
本次改造成功实现了以下核心目标:
| 目标 | 实现情况 |
|---|---|
| 异步审批 | 审批请求创建后立即返回,云小猫可继续处理其他消息 ✅ |
| 自动执行 | 审批通过后由独立服务自动执行,避免延迟 ✅ |
| 完整追溯 | 审批流程全程记录到数据库,支持审计查询 ✅ |
| 安全规范 | 定义危险指令清单,禁止操作直接拒绝 ✅ |
| QQ 集成 | 支持通过 QQ 消息查看列表、确认、拒绝审批 ✅ |
11.2 架构优势对比
| 对比项 | 原有架构 | 新架构 |
|---|---|---|
| 审批等待 | 阻塞云小猫 | 不阻塞,异步处理 |
| 执行时机 | 不可控 | 审批通过立即执行 |
| 流程追溯 | JSON 文件,不完整 | 数据库记录,完整事件链 |
| 安全性 | Agent 直接执行 | 独立服务执行,多层校验 |
| 可用性 | 等待期间无法聊天 | 随时可聊天 |
11.3 后续优化方向
| 方向 | 描述 |
|---|---|
| 多渠道通知 | 集成企业微信、飞书、邮件等更多通知渠道 |
| 审批流引擎 | 支持多级审批、条件审批、会签等复杂流程 |
| 权限细粒度 | 基于角色的审批权限控制,不同管理员审批不同风险级别 |
| 操作模板 | 预定义常用操作模板,简化审批流程创建 |
| 定时审批 | 支持定时执行审批通过的操作 |
| Web 管理界面 | 提供 Web 界面管理审批、查看统计 |
| 告警集成 | 与监控系统集成,自动创建审批请求 |
附录 A:API 端点完整列表
| 端点 | 方法 | 功能 | 需要认证 |
|---|---|---|---|
/health |
GET | 健康检查 | 否 |
/api/approval/create |
POST | 创建审批请求 | 是 |
/api/approval/<id> |
GET | 获取审批详情 | 是 |
/api/approval/list |
GET | 列出审批请求 | 是 |
/api/approval/<id>/approve |
POST | 批准并执行 | 是 |
/api/approval/<id>/reject |
POST | 拒绝审批 | 是 |
/api/approval/check-timeout |
POST | 检查超时审批 | 是 |
/api/approval/logs/<id> |
GET | 获取执行日志 | 是 |
/api/approval/poll |
GET | 轮询审批状态 | 是 |
/api/approval/wait/<id> |
GET | 长轮询等待结果 | 是 |
/api/subscriber/list |
GET | 列出订阅者 | 是 |
/api/subscriber/add |
POST | 添加订阅者 | 是 |
/api/subscriber/remove |
POST | 移除订阅者 | 是 |
/api/actions/list |
GET | 操作类型列表 | 是 |
附录 B:数据库表关系图
┌─────────────────────────┐
│ approval_requests │
├─────────────────────────┤
│ id (PK) │
│ action_type │
│ action_params (JSON) │
│ risk_level │
│ status │
│ requester_id │
│ requester_name │
│ approver_id │
│ approver_name │
│ created_at │
│ approved_at │
│ executed_at │
│ timeout_at │
│ execution_result (JSON) │
└───────────┬─────────────┘
│
│ 1:N
│
┌───────────▼─────────────┐
│ approval_execution_logs │
├─────────────────────────┤
│ id (PK) │
│ approval_id (FK) │
│ event_type │
│ event_data (JSON) │
│ operator_id │
│ operator_name │
│ created_at │
└─────────────────────────┘
附录 C:状态流转图
┌─────────────┐
│ pending │
│ (等待审批) │
└──────┬──────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ approved │ │ rejected │ │ timeout │
│ (已批准) │ │ (已拒绝) │ │ (已超时) │
└─────┬──────┘ └────────────┘ └────────────┘
│
▼
┌────────────┐
│ executed │
│ (已执行) │
└─────┬──────┘
│
▼
┌────────────┐
│ failed │
│ (执行失败) │
└────────────┘
文档版本: 1.0
创建日期: 2026-04-01
作者: CodeBuddy
适用对象: 运维工程师、系统架构师、后端开发者