本文以 ops-agent 的真实构建过程为线索,完整讲解如何设计并实现一个"默认只读、高风险操作需授权、可回滚、全审计"的 Claude Code 运维 Agent 。所有代码均可在本仓库中对照阅读,文末附有改造清单,可复用到你自己的 Agent 场景。 0. 我们要造什么 一个运维 Agent ,满足四个硬要求: 默认只读 —— 查状态、看日志直接执行,无感通过。 高风险操作需授权 —— 写操作必须先出"变更单"(风险说明 + 回滚计划),经用户在会话中显式批准后才能执行。 可回滚 —— 执行前自动快照,一条命令回滚。 全审计 —— 批准、校验、执行、中止,每个事件都落日志。 最终形态(本仓库): ops-agent/ ├── .claude/ │ ├── agents/ops.md # Agent 定义:第一层防线( prompt 纪律) │ └── settings.json # PreToolUse hook:第二层防线(硬拦截) ├── core.py # 核心库:分类/审批戳/快照/回滚/审计(纯函数) ├── opsx # CLI 薄壳 + hook 入口 ├── config.yaml # 只读白名单 + R1/R2/R3 风险规则 ├── selftest.py # 内置自检( 23 项断言) ├── mcp_server.py # 演进产物:同一能力的 MCP server 形态 └── README.md 1. 设计阶段:先定权限模型,再写一行代码 这是整个项目最重要的一步。实现之前先回答这几个问题(当时的设计决策): 决策点 选择 理由 审批方式 会话内人工确认(变更单 → 弹窗批准) 最严格,无凭据外泄风险;比预存 API token 或全自动策略更贴合"谨慎"要求 快照方式 文件副本 + 状态抓取命令输出,存本地状态目录 简单、可靠、零依赖;数据库场景再扩展逻辑导出 目标环境 本机 macOS + 远程 Linux ( SSH )+ K8s ( kubeconfig )+ Docker 覆盖个人/小团队真实运维面 演进预留 core.py 写成纯函数库,不 import CLI 或 hook 为后续包装成 MCP server 铺路(后文第 8 节兑现) 然后是 风险分级模型 ,全系统的基石: R0 只读 : kubectl get 、 systemctl status 、 docker ps 、日志检索、git 只读族。直接放行。 R1 可逆低风险写 :服务重启、docker restart 。走轻量审批流程。 R2 高风险写 :部署、扩缩容、包安装、防火墙规则。走完整变更单流程。 R3 不可逆 : rm -rf 、 kubectl delete 、 docker rm 、格式化、 DROP 。 默认拒绝 ,需 --force + 用户显式确认。 三条派生原则,都是安全方向的默认值: 复合命令按 ; && || | 分段, 取最高风险级 。 不认识的命令 默认 R2 (宁高勿低,误放行代价远大于误拦截)。 白名单形状但带写标记( > 、 >> 、 tee 、 -exec 、 -delete )的, 不按只读放行 。 2. 仓库初始化 GitHub 建私有仓库后在本地初始化骨架。 .gitignore 排除构建产物;状态目录不进仓库( ~/.ops-agent/ 放审批戳、快照、审计,是运行时状态,且可能含环境信息)。 mkdir ops-agent && cd ops-agent git init # .claude/agents/ops.md 、.claude/settings.json 、core.py 、opsx 、config.yaml 、selftest.py 3. 第一层防线:Agent 定义 .claude/agents/ ops.md 是 Claude Code 的子代理定义。frontmatter 决定它何时被调度和能用哪些工具: --- name: ops description: 运维操作代理。默认只读;任何写操作必须按 R1/R2/R3 流程提交变更单并经用户确认后通过 opsx exec 执行。 tools: Bash, Read, Grep, Glob, AskUserQuestion --- 正文写 纪律 而非知识。三段最关键: 最高原则 (放在文件开头,优先级最高): 1. 默认只读:优先用只读命令查状态、看日志。写操作是例外,不是默认。 2. 所有写命令必须经 opsx exec 执行,禁止直接 Bash 执行写操作。hook 会拦截未授权命令。 3. 禁止拆分命令规避审批、禁止在复合命令里夹带未申报的命令段。 R1/R2 强制流程 (变更单五步): 输出变更单:目标环境、完整命令、风险说明、回滚计划、快照计划。 AskUserQuestion 请用户确认。 批准后先建审批戳: opsx approve ' ' 原子执行(快照参数随命令一次传入): opsx exec ' ' --snapshot-file --snapshot-cmd 'name:: ' 报告结果、snapshot_id 、回滚命令。 R3 流程 :默认拒绝;仅用户显式确认后,先 opsx approve ' ' --force 再 exec 。 经验 :prompt 纪律是"软"防线——它约束行为端正的 Agent ,但拦不住异常输出。所以必须有第二层。 4. 第二层防线:PreToolUse 硬拦截 .claude/settings.json 注册 hook ,对每次 Bash 工具调用先过闸门: { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/opsx\" hook", "timeout": 10 } ] } ] } } hook 从 stdin 读 JSON ,取 tool_input.command 分类, exit 0 放行、exit 2 拒绝 。两条铁律: fail-closed :任何异常(解析失败、空命令、core 报错)一律 return 2 拒绝,绝不静默放行。 提示语要指路:拒绝时告诉 Agent"请向用户提交变更单,经确认后用 opsx exec 执行"——拦截不是终点,是把行为引导回正确流程。 # opsx 的 hook 入口 try: data = json.load(sys.stdin) command = data.get("tool_input", {}).get("command", "") if not command.strip(): return 2 code, msg = core.check(command) ... return code except Exception as e: # hook 异常一律拒绝方向 print(f"[opsx] hook 异常,拒绝执行: {e}",


  • 情报分类:技术学习与提效
  • 分类依据:内容涉及技术、AI、软件工具或工程实践
  • 信息来源:服务器 / V2EX
  • 发布时间:2026/9/20 09:12:08