当前阶段,我比较推崇使用Hermes作为运维助手。因为我比较喜欢它的自我进化以及自动生成skill的能力,真的是非常方便。当然,它的问题也不少,其中安全问题尤为重要。
一个成熟的AIOps一定是可以自动解决问题的,但是吧,在现阶段我们还真不敢把所有的操作都交给AI,特别是那种“写”操作比较多的行为。
于是,这两天我特意去研究了下Hermes的审批能力。就是说,当它想做我们认为比较危险的操作时,一定要让它先获得我们的审批,才能继续操作。
01 | Hermes 自带审批功能
Hermes 官方文档说明,它在执行命令前会检查危险模式,命中后要求人工审批;安全模型里专门有一层叫 Dangerous command approval,即对破坏性操作做 human-in-the-loop 审批。
配置大致是:
approvals:mode: manual# manual | smart | offtimeout: 60cron_mode: denymcp_reload_confirm: truedestructive_slash_confirm: true建议运维生产环境使用:
approvals:mode: manualtimeout: 60cron_mode: deny
这段配置的含义是:
① 凡是命中危险规则的命令,都必须人工批准;
② 如果 60 秒内没人批准,默认拒绝;
③ 无人值守的 cron 场景遇到危险命令也默认拒绝。
注意,approvals.mode: off 会关闭安全提示,只适合可信、隔离环境。
Hermes 会触发审批的典型操作包括:递归删除、mkfs/dd、SQL DROP TABLE、无 WHERE 的 DELETE FROM、TRUNCATE TABLE、写入 /etc/、systemctl stop/restart/disable/mask、远程脚本管道执行、进程强杀等。
02 | 运维里的“写操作”不能只依赖命令规则,要做工具层审批
问题的关键在这里:Hermes 自带审批主要识别 shell 命令危险模式。但运维领域很多“写操作”不一定表现为危险 shell 命令,比如:
kubectl scale deployment xxx --replicas=0kubectl rollout restart deployment xxx调用云厂商 API 修改安全组调用 CMDB API 更新资产调用工单 API 关闭告警调用数据库 API 执行 UPDATE调用发布平台触发上线
这些操作可能不会被 Hermes 的危险命令规则完整覆盖。因此更稳妥的实现方式是:把运维写操作封装成 Hermes plugin/MCP tool,然后在 tool 调用前加审批拦截。
Hermes 的工具执行流程里,模型发起 tool_call 后,会先触发 pre_tool_call plugin hook,然后再进入工具分发;终端命令还会经过危险命令审批检查。
其中,pre_tool_call 可以用于阻止工具调用,并可用于强制 destructive write_file / patch 等操作审批。
推荐架构如下:

03 | 建议把工具分成 read / plan / write 三类
运维Agent 最好不要让模型直接执行写操作,而是拆成三步:
① read:只读查询
② plan:生成变更计划、diff、风险说明
③ write:真正执行变更,必须审批
例如Kubernetes 场景:
read_k8s_resource(namespace, kind, name)plan_k8s_change(action, target, payload)apply_k8s_change(change_id)
其中apply_k8s_change 才是真正写操作,必须审批。
可以用这样的策略表:
kubectl get pods | ||
04 | 审批内容必须让人看得懂
不要只展示:
kubectl apply -f /tmp/a.yaml应该展示:
操作类型:Kubernetes 配置变更环境:production命名空间:payment资源:deployment/payment-api变更内容:- replicas: 6 -> 3- image: payment-api:v1.8.2 -> v1.8.3影响范围:- payment-api 生产服务- 预计触发滚动发布风险等级:高回滚方式:kubectl rollout undo deployment/payment-api -n payment
05 | 如何将审批机制接入飞书这类的IM
大体思路是:写一个Hermes Plugin,用pre_tool_call 钩子拦截写操作,然后通过飞书 API 发送交互卡片让用户审批。
插件结构:
~/.hermes/plugins/write-approval/├── plugin.yaml└── handler.py
plugin.yaml 内容
name: write-approvaldescription: 写操作审批 - 在飞书发送交互卡片version: 1.0.0
handler.py 内容
import jsonimport osimport timeimport threadingimport loggingfrom pathlib import Pathlogger = logging.getLogger(__name__)# 风险等级规则SAFE_PATHS = ["/tmp/", str(Path.home() / "temp")]BLOCKED_PATHS = ["/etc/", str(Path.home() / ".hermes" / ".env")]# 审批状态存储_pending_approvals = {}_approval_results = {}def _check_path_risk(path: str) -> str:"""返回 L1 / L2 / L3"""for p in BLOCKED_PATHS:if path.startswith(p):return "L3"for p in SAFE_PATHS:if path.startswith(p):return "L1"return "L2"def pre_tool_call_handler(tool_name: str, args: dict, task_id: str, **kwargs):"""pre_tool_call 钩子"""# 只拦截写操作工具if tool_name not in ("write_file", "patch"):return None # 放行path = args.get("path", "")risk = _check_path_risk(path)# L1: 安全路径 → 自动放行if risk == "L1":return None# L3: 禁止路径 → 直接拦截if risk == "L3":return {"action": "block", "message": f"❌ 禁止写入受保护路径: {path}"}# L2: 需要审批# 这里可以调用飞书 API 发送交互卡片# 然后等待用户审批结果approval_id = f"{tool_name}:{path}:{int(time.time())}"# 发送飞书审批卡片(需要飞书 API)_send_feishu_approval_card(approval_id, tool_name, path, args.get("content", "")[:200])# 等待审批结果(带超时)timeout = 120result = _wait_for_approval(approval_id, timeout)if result == "approved":return None # 放行else:return {"action": "block", "message": f"⏱️ 审批超时或已拒绝: {path}"}def _send_feishu_approval_card(approval_id, tool_name, path, content_preview):"""通过飞书 API 发送交互卡片"""# 需要飞书 API 凭证app_id = os.getenv("FEISHU_APP_ID")app_secret = os.getenv("FEISHU_APP_SECRET")chat_id = os.getenv("FEISHU_HOME_CHANNEL")# 1. 获取 tenant_access_token# 2. 发送交互卡片(含审批按钮)# 卡片内容:# ┌─────────────────────────────┐# │ ✏️ 写操作审批请求 │# │ │# │ 工具: write_file │# │ 路径: /opt/production/config │# │ 内容预览: server.port=8080 │# │ │# │ [✅ 批准] [❌ 拒绝] │# └─────────────────────────────┘passdef _wait_for_approval(approval_id, timeout):"""等待用户审批结果"""# 通过飞书卡片回调 + webhook 接收审批结果# 这里简化处理time.sleep(timeout)return "timeout"def register(ctx):ctx.register_hook("pre_tool_call", pre_tool_call_handler)以上只是我的一些初步设想,
后续还需要进一步实践验证。
这里希望能起到一个抛砖引玉的作用。
夜雨聆风