乐于分享
好东西不私藏

AIOps探索:AI 不能“想改就改”,Hermes 写操作审批实践

AIOps探索:AI 不能“想改就改”,Hermes 写操作审批实践
↑↑↑ 点击关注,分享IT技术|职场晋升技巧|AI工具

当前阶段,我比较推崇使用Hermes作为运维助手。因为我比较喜欢它的自我进化以及自动生成skill的能力,真的是非常方便。当然,它的问题也不少,其中安全问题尤为重要。

一个成熟的AIOps一定是可以自动解决问题的,但是吧,在现阶段我们还真不敢把所有的操作都交给AI,特别是那种“写”操作比较多的行为。

于是,这两天我特意去研究了下Hermes的审批能力。就是说,当它想做我们认为比较危险的操作时,一定要让它先获得我们的审批,才能继续操作。

01 | Hermes 自带审批功能

Hermes 官方文档说明,它在执行命令前会检查危险模式,命中后要求人工审批;安全模型里专门有一层叫 Dangerous command approval,即对破坏性操作做 human-in-the-loop 审批。

配置大致是:

approvals:  mode: manual      # manual | smart | off  timeout: 60  cron_mode: deny  mcp_reload_confirm: true  destructive_slash_confirm: true

建议运维生产环境使用:

approvals:  mode: manual  timeout: 60  cron_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 = 120    result = _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)

以上只是我的一些初步设想,

后续还需要进一步实践验证。

这里希望能起到一个抛砖引玉的作用。 

··············  END  ··············
哈喽,我是阿铭,《跟阿铭学Linux》作者,曾就职于腾讯,有着19年的IT从业经验,现全职做IT类职业培训:运维、k8s、大模型。日常分享运维、AI、大模型相关技术以及职场相关,欢迎围观。

相关学习资料