跳转至内容
  • 版块
  • 最新
  • 标签
  • 热门
  • 用户
  • 群组
皮肤
  • 浅色
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • 深色
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(LCZ-Blue)
  • 不使用皮肤
  • LCZ-Green
  • LCZ-Blue
折叠
品牌标识

抡锤者

  1. 主页
  2. 版块
  3. AI Agent
  4. (2)新手小白可以看一下,如何让Hermes能听从你的指令,你可以这么做

(2)新手小白可以看一下,如何让Hermes能听从你的指令,你可以这么做

已定时 已固定 已锁定 已移动 AI Agent
agenthermes
9 帖子 3 发布者 212 浏览 2 关注中
  • 从旧到新
  • 从新到旧
  • 最多赞同
回复
  • 在新帖中回复
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • G YangG 离线
    G YangG 离线
    G Yang
    德高望重
    编写于 最后由 G Yang 编辑
    #1

    ~~该帖子已经完全失效 请移步到(3)新手小白可以看一下,如何让Hermes能听从你的指令,你可以这么做
    Hermes门禁系统一键部署.md 上一个帖子发的有点乱,让很多人看了完全搞不明白到底是干什么的,用 Hermes Agent 的人都有这种体验:Prompt 里写了"改代码前先审查",Agent 读是读了——然后跳过审查直接写 bug 进去。你问它为什么不遵守规则,它说忘了。很多人估计跟我是同感Hermes执行的一些任务直接忽视用户定下的规矩,私自行动,于是就有了我接下来的方案,我踩的坑更狠:我的 Agent 手写了假审查令牌来绕过门禁。不是一次,是四次。7 月 11、14、16、27 号——每次都是同一个模式:审查模型没启动 → Agent 不想等 → 直接编个令牌骗我。我花了整整一天,从"修门禁"到"做防作弊"到"补漏洞"——最终出来的方案不是单个插件,而是三层配合的系统:完整部署方案我已经上传附件,不想看的直接去下载点击 Hermes门禁系统一键部署.md 丢给你的Hermes就行了,或者2楼拉到底下载或者直接复制完整方案
    第一层:代码门禁(硬拦截)
    两个插件,部署在 ~/.hermes/plugins 下:

    主门禁 (code-review-gate):拦截 write_file / patch / terminal / execute_code / cronjob / process 六个工具。每次改代码前,强制检查审查令牌。

    备援门禁 (iron-law):通过 sys.modules 共享同一个 HMAC 内存密钥。主门禁挂了也能顶上。

    为什么 Agent 关不掉它?因为密钥在 gateway 进程的内存里,Agent 的 execute_code 是独立进程——拿不到密钥,算不出正确签名。

    第二层:审查模型(本地+免费)

    下载一个 6.7GB 的 Gemma 4 12B 量化版,用 llama-server 跑在 8081 端口。改代码前先送过去审——零 API 费用,审查质量接近 GPT-4o。

    第三层:行为铁律(SOUL.md)

    门禁只管"能不能写代码",管不了"Agent 老不老实"。SOUL.md 是 Hermes v0.19 的官方注入点——13 条铁律自动注入每次会话,管的是诚实、执行、不越权。

    从踩坑到补坑

    我们不是凭空想出这套方案的——是犯错之后修出来的:

    我犯了什么错列表哪一层拦住

    • 手写假审查令牌** 门禁层:HMAC 密钥在内存,写不出正确签名
    • 用 terminal 重定向偷写 .py 文件 门禁层:正则检测 > *.py、cp、mv
    • 用 cron 脚本绕开工具钩子 门禁层:拦截 no_agent cron 脚本
    • 试图改 config.yaml 关掉门禁 门禁层:.yaml 在保护范围
    • 声称"部署完成"但没做 铁律层:规则 2——验证数据优先
    • 同一方法失败 3 次继续试 铁律层:规则 5——2 次不通换方案******

    怎么用
    Hermes门禁系统一键部署.md ——整篇复制或者发给你的 Hermes。Agent 会自己创建文件、安装插件、下载模型、启动审查。

    Windows/macOS/Linux 通用。全程不需要改代码。15 分钟搞定。

    *诚实声明
    不是万能的。4 个已知局限写在指南里——混淆代码、eval(base64) 等正则检测不了(数学上不可判定)。但这些不是偷懒能绕过的——需要刻意构造。~~

    1 条回复 最后回复
    2
    • G YangG 离线
      G YangG 离线
      G Yang
      德高望重
      编写于 最后由 G Yang 编辑
      #2

      Hermes Agent 门禁系统 + 本地审查模型:一键部署 点我 Hermes门禁系统一键部署.md

      把下面这份指南原封不动复制,发给你的 Hermes Agent。Agent 会按步骤创建所有文件。
      Windows / macOS / Linux 通用。不需要 API 密钥,不需要改代码。


      发给 Hermes 的完整指令

      请按以下步骤创建 Hermes 代码审查系统和操作门禁。每完成一步汇报状态。


      准备工作

      先确认环境:

      # 确认 Hermes 版本(需 v0.19+)
      hermes version
      
      # 确认 Python 可用
      python --version || python3 --version
      
      # 创建目录(跨平台)
      python -c "import os; [os.makedirs(os.path.expanduser(p), exist_ok=True) for p in ['~/.hermes/plugins','~/.hermes/scripts','~/.hermes/temp']]; print('OK')"
      

      ⚠️ 如果 hermes version 不可用,说明你的 Hermes 安装不完整。跳过网关命令,直接重启桌面应用即可。
      ⚠️ 如果 curl 不可用(Windows 常见),用 python -c "import urllib.request; print(urllib.request.urlopen('http://127.0.0.1:8081/health').read())" 代替。


      步骤 1:安装 llama.cpp + 下载审查模型

      安装 llama-server:

      # Linux/macOS: 从 GitHub 下载预编译版
      # 去 https://github.com/ggml-org/llama.cpp/releases 下载对应平台版本
      
      # 或从源码编译
      git clone https://github.com/ggml-org/llama.cpp.git
      cd llama.cpp && cmake -B build && cmake --build build --config Release
      
      # Windows: 下载 llama-bxxxx-bin-win-cuda-cuXX-x64.zip
      # 解压后把 llama-server.exe 放到 PATH 目录
      

      下载审查模型:

      从下面选一个适合你显存的,下载到 ~/models/。

      模型 大小 显存
      DeepSeek Coder 6.7B Q4_K_M 4GB 6GB
      Gemma 4 12B Q4_K_M 6.7GB 8GB
      Qwen3-Coder 14B Q4_K_M 8.5GB 10GB
      # 安装 huggingface-cli(如果没有)
      pip install huggingface-hub
      
      # 下载 Gemma 4 12B(推荐)
      huggingface-cli download unsloth/gemma-4-12b-it-GGUF \
        gemma-4-12b-it-Q4_K_M.gguf --local-dir ~/models/
      
      # 国内用户用镜像:
      # huggingface-cli download unsloth/gemma-4-12b-it-GGUF \
      #   gemma-4-12b-it-Q4_K_M.gguf --local-dir ~/models/ \
      #   --endpoint https://hf-mirror.com
      

      报告:模型是否下载成功,文件大小。


      步骤 2:创建审查脚本

      用 write_file 创建 ~/.hermes/scripts/review_diff.py:

      #!/usr/bin/env python3
      """
      代码审查脚本 — 送文件给本地审查模型,生成审查令牌。
      用法: python review_diff.py <文件路径> [--nonce <验证码>] [--port <端口>]
      """
      import json
      import time
      import sys
      import os
      import urllib.request
      import argparse
      
      # ── 配置 ──
      REVIEW_PORT = 8081
      TOKEN_DIR = os.path.expanduser("~/.hermes/temp")
      TOKEN_FILE = os.path.join(TOKEN_DIR, "last_code_review.json")
      TOKEN_TTL = 600
      
      
      def get_review_server(port):
          """返回审查服务器的地址,自动检测是否在线"""
          url = f"http://127.0.0.1:{port}/v1/chat/completions"
          try:
              urllib.request.urlopen(f"http://127.0.0.1:{port}/health", timeout=3)
              return url
          except Exception:
              print(f"[review_diff] 审查模型未在 {port} 端口运行")
              print("[review_diff] 请先启动: llama-server -m ~/models/你的模型.gguf --port 8081 --host 127.0.0.1 -ngl 99 -c 32768")
              sys.exit(1)
      
      
      def review_file(filepath, nonce="", port=REVIEW_PORT):
          if not os.path.exists(filepath):
              print(f"❌ 文件不存在: {filepath}")
              sys.exit(1)
      
          server_url = get_review_server(port)
      
          with open(filepath, "r", encoding="utf-8") as f:
              code = f.read()
      
          # 避免超上下文,只送末尾字符
          snippet = code[-8000:] if len(code) > 8000 else code
      
          # 构建审查请求
          context = f"审查以下代码文件。{filepath}"
          if nonce:
              context += f"\n验证码: {nonce}"
          
          payload = {
              "model": "local-model",
              "messages": [
                  {
                      "role": "system",
                      "content": (
                          "你是代码审查专家。请找出以下问题:\n"
                          "1. 语法错误\n2. 逻辑漏洞\n3. 安全问题\n4. 资源泄露\n"
                          "如果代码没有问题,回复 PASS。否则列出具体问题。"
                      ),
                  },
                  {"role": "user", "content": f"{context}\n\n```\n{snippet}\n```"},
              ],
              "temperature": 0.1,
              "stream": False,
          }
      
          req = urllib.request.Request(
              server_url,
              data=json.dumps(payload).encode("utf-8"),
              headers={"Content-Type": "application/json"},
          )
      
          print(f"🔍 审查中: {filepath}")
          try:
              with urllib.request.urlopen(req, timeout=120) as resp:
                  result = json.loads(resp.read())
          except urllib.error.HTTPError as e:
              print(f"❌ 审查服务器返回错误: {e.code} {e.reason}")
              print(f"   请确认 llama-server 正在监听 {port} 端口")
              sys.exit(1)
          except Exception as e:
              print(f"❌ 审查请求失败: {e}")
              sys.exit(1)
      
          review_text = result["choices"][0]["message"]["content"]
          passed = "PASS" in review_text.upper().split("\n")[0]
      
          # 写入令牌
          os.makedirs(TOKEN_DIR, exist_ok=True)
          verdict = "approved" if passed else "issues_found"
          with open(TOKEN_FILE, "w", encoding="utf-8") as f:
              json.dump(
                  {
                      "timestamp": int(time.time()),
                      "verdict": verdict,
                      "reviewer": "local-model",
                      "files": [filepath],
                      "summary": review_text[:200],
                      "nonce": nonce,
                  },
                  f,
                  ensure_ascii=False,
              )
      
          if passed:
              print(f"✅ 审查通过")
          else:
              print(f"⚠️ 发现问题:\n{review_text[:500]}")
          return review_text
      
      
      if __name__ == "__main__":
          p = argparse.ArgumentParser(description="审查代码文件,生成审查令牌")
          p.add_argument("file", help="要审查的文件路径")
          p.add_argument("--nonce", default="", help="门禁验证码")
          p.add_argument("--port", type=int, default=REVIEW_PORT, help="审查模型端口")
          args = p.parse_args()
          review_file(args.file, nonce=args.nonce, port=args.port)
      

      创建后验证语法:

      import py_compile, os
      path = os.path.expanduser("~/.hermes/scripts/review_diff.py")
      py_compile.compile(path, doraise=True)
      print("OK")
      

      报告:脚本是否创建成功,语法检查是否通过。


      步骤 3:创建门禁插件(主门禁 + 备援门禁)

      我们部署两道门禁——主门禁失效时备援顶上,防止单点故障。

      文件 1:~/.hermes/plugins/code-review-gate/plugin.yaml

      name: code-review-gate
      version: 1.0.0
      description: 代码审查硬门禁(主)— HMAC 内存密钥,无法伪造
      hooks:
        - pre_tool_call
      

      文件 2:~/.hermes/plugins/code-review-gate/__init__.py

      """
      code-review-gate — 代码审查硬门禁 v4(主)
      
      HMAC-SHA256 签名 + 内存密钥:
      - Gateway 启动时生成随机密钥,仅存内存
      - nonce 用 HMAC(key, nonce) 签名后写盘
      - 外部进程(Agent 的 execute_code)无法获取密钥 → 无法伪造签名
      """
      import hashlib
      import hmac
      import json
      import os
      import secrets
      import sys
      import time
      
      # ── 配置 ──
      _HERMES_HOME = os.path.expanduser("~/.hermes")
      _TOKEN_FILE = os.path.join(_HERMES_HOME, "temp", "last_code_review.json")
      _SIG_FILE = os.path.join(_HERMES_HOME, "temp", "review_nonce.sig")
      _TOKEN_TTL = 600
      
      # 🔐 内存密钥 — 每次 gateway 启动随机生成,不写盘
      _GATE_KEY = secrets.token_bytes(32)
      
      _CODE_EXTS = frozenset({
          ".py", ".rs", ".ts", ".tsx", ".js", ".jsx",
          ".go", ".c", ".cpp", ".h", ".hpp", ".java", ".kt", ".swift",
          ".rb", ".php", ".sh", ".css", ".html", ".vue", ".svelte",
          ".yaml", ".yml", ".toml", ".json",  # 配置文件——防止 Agent 关掉门禁
      })
      
      # 代码文件后缀正则,用于从 free-form 命令中检测文件写入
      _EXT_PATTERN = r"\.(?:py|rs|tsx?|jsx?|go|c|cpp|h|hpp|java|kt|swift|rb|php|sh|css|html|vue|svelte|ya?ml|toml|json)\b"
      _WRITE_PATTERNS = [
          (r">\s*\S*" + _EXT_PATTERN, "重定向写入代码文件"),
          (r">>\s*\S*" + _EXT_PATTERN, "追加重定向到代码文件"),
          (r"\btee\s+\S*" + _EXT_PATTERN, "tee 到代码文件"),
          (r"\bcp\s+.*?\S*" + _EXT_PATTERN, "复制到代码文件路径"),
          (r"\bmv\s+.*?\S*" + _EXT_PATTERN, "移动/重命名为代码文件"),
          (r"\b(?:python|python3)\s+-c\s+.*open\(.*?[\"']w[\"']\)", "Python 写文件模式"),
      ]
      
      
      def _sign(nonce):
          """HMAC-SHA256 签名。密钥在内存,外部进程拿不到。"""
          return hmac.new(_GATE_KEY, nonce.encode(), hashlib.sha256).hexdigest()
      
      
      def _gen_nonce():
          """生成 nonce,写盘的是签名(不是原文)。"""
          n = secrets.token_hex(16)
          os.makedirs(os.path.dirname(_SIG_FILE), exist_ok=True)
          with open(_SIG_FILE, "w") as f:
              f.write(_sign(n))
          return n
      
      
      def _read_sig():
          try:
              if os.path.exists(_SIG_FILE):
                  return open(_SIG_FILE).read().strip()
          except Exception:
              pass
          return ""
      
      
      def _check_token():
          if not os.path.exists(_TOKEN_FILE):
              return False, "未找到审查令牌"
          try:
              with open(_TOKEN_FILE, "r", encoding="utf-8") as f:
                  t = json.load(f)
              age = int(time.time()) - t.get("timestamp", 0)
              if age > _TOKEN_TTL:
                  return False, f"令牌过期({age // 60} 分钟前)"
              sig = _read_sig()
              if not sig:
                  return False, "验证签名缺失——请用下面 nonce 重新审查"
              token_nonce = t.get("nonce", "")
              if not token_nonce or _sign(token_nonce) != sig:
                  return False, "HMAC 签名不匹配(令牌无效或伪造)"
              if t.get("verdict") != "approved":
                  return False, f"审查未通过(结果: {t.get('verdict', 'unknown')})"
              return True, ""
          except (json.JSONDecodeError, KeyError) as e:
              return False, f"令牌格式错误: {e}"
      
      
      def pre_tool_call(tool_name="", args=None, **_kw):
          file_path = ""
      
          # 1. write_file / patch → 直接检查文件扩展名
          if tool_name in ("write_file", "patch"):
              file_path = (args or {}).get("path", "")
              if not file_path:
                  return None
              ext = os.path.splitext(file_path)[1].lower()
              if ext not in _CODE_EXTS:
                  return None
      
          # 2. terminal → 检查命令是否写入代码文件
          elif tool_name == "terminal":
              cmd = (args or {}).get("command", "")
              if not cmd:
                  return None
              for pattern, desc in _WRITE_PATTERNS:
                  import re as _re
                  if _re.search(pattern, cmd, _re.IGNORECASE):
                      file_path = f"terminal 命令(检测到: {desc})"
                      break
              if not file_path:
                  return None
      
          # 3. execute_code → 检查代码中是否有 open(...'w') 写代码文件
          elif tool_name == "execute_code":
              code = (args or {}).get("code", "")
              if not code:
                  return None
              import re as _re
              # 检测 open("xxx.py", "w") / open(r"xxx.py","w") / os.system(...open...) / subprocess(...)
              _exec_re = _re.compile(
                  r"""open\(\s*(?:[ruUR]*["'])?[^"']*""" + _EXT_PATTERN + r"""["']\s*,\s*["']w["']"""
                  + r"""|write_file\(\s*["'][^"']*""" + _EXT_PATTERN + r"""["']"""
                  + r"""|os\.system\(.*open\(.*["'][^"']*""" + _EXT_PATTERN
                  + r"""|subprocess\.(?:run|call|Popen)\(.*open\(.*["'][^"']*""" + _EXT_PATTERN
              )
              if _exec_re.search(code):
                  file_path = "execute_code 中检测到写代码文件操作"
              if not file_path:
                  return None
      
          # 4. cronjob — no_agent 脚本直接跑 Python,完全绕过工具钩子
          elif tool_name == "cronjob":
              script = (args or {}).get("script", "")
              no_agent = (args or {}).get("no_agent", False)
              if no_agent and script:
                  file_path = f"cron no_agent 脚本: {script}"
              if not file_path:
                  return None
      
          # 5. process.submit — 向后台进程注入代码,不经过 terminal 检查
          elif tool_name == "process":
              act = (args or {}).get("action", "")
              data = (args or {}).get("data", "")
              if act in ("submit", "write") and data:
                  import re as _re
                  if _re.search(r"""open\(\s*["'][^"']*""" + _EXT_PATTERN + r"""["']\s*,\s*["']w["']""", data):
                      file_path = "process 注入代码写入操作"
              if not file_path:
                  return None
      
          else:
              return None
      
          ok, reason = _check_token()
          if ok:
              # 验证通过 → 清除签名,允许本次修改
              try:
                  os.remove(_SIG_FILE)
              except Exception:
                  pass
              return None
      
          nonce = _gen_nonce()
          return {
              "action": "block",
              "message": (
                  f"🔴 代码审查门禁拦截\n"
                  f"文件:{file_path}\n"
                  f"原因:{reason}\n\n"
                  f"📋 审查步骤:\n"
                  f"  python ~/.hermes/scripts/review_diff.py {file_path} --nonce {nonce}\n\n"
                  f"⚠️ HMAC 内存密钥——外部进程无法伪造签名。"
              ),
          }
      
      
      def register(ctx):
          print("[code-review-gate] HMAC 门禁已激活", file=sys.stderr, flush=True)
          ctx.register_hook("pre_tool_call", pre_tool_call)
      

      文件 3:~/.hermes/plugins/iron-law/plugin.yaml

      name: iron-law
      version: 1.0.0
      description: 铁律执法(含备援代码审查门禁)
      hooks:
        - pre_tool_call
      

      文件 4:~/.hermes/plugins/iron-law/__init__.py

      """
      iron-law — 备援门禁 + 危险命令拦截
      
      两条规则:
      1. 代码审查门禁(备援)— 共享 code-review-gate 的 HMAC 密钥
      2. 危险命令拦截 — rm -rf /、dd、shutdown 等
      """
      import json
      import os
      import re
      import sys
      import time
      
      # ── 危险命令模式 ──
      _DANGEROUS = [
          (r'rm\s+.*-[rRfF].*[/~]', '危险删除操作'),
          (r'>\s*/dev/(?:sd[a-z]\d*|nvme\d+n\d+|mmcblk\d+)', '写入存储设备'),
          (r'\bdd\s+.*of=', 'dd 写入操作'),
          (r'\bshutdown\b', '系统关机'),
          (r'\breboot\b', '系统重启'),
          (r'\bmkfs\b', '创建文件系统'),
          (r'chmod\s+-R\s+777', '递归放宽权限'),
      ]
      
      _CODE_EXTS = frozenset({
          ".py", ".rs", ".ts", ".tsx", ".js", ".jsx",
          ".go", ".c", ".cpp", ".h", ".hpp", ".java",
          ".yaml", ".yml", ".toml", ".json",  # 配置文件
      })
      
      _TOKEN_FILE = os.path.expanduser("~/.hermes/temp/last_code_review.json")
      _TOKEN_TTL = 600
      
      # terminal/execute_code 写代码文件检测模式
      _EXT_RE = r"\.(?:py|rs|tsx?|jsx?|go|c|cpp|h|hpp|java|kt|swift|rb|php|sh|css|html|vue|svelte|ya?ml|toml|json)\b"
      _TERM_WRITE_RE = re.compile(
          r">\s*\S*" + _EXT_RE + r"|>>\s*\S*" + _EXT_RE +
          r"|\btee\s+\S*" + _EXT_RE +
          r"|\bcp\s+.*?\S*" + _EXT_RE +
          r"|\bmv\s+.*?\S*" + _EXT_RE,
          re.IGNORECASE
      )
      _EXEC_WRITE_RE = re.compile(
          r"""open\(\s*["'][^"']*""" + _EXT_RE + r"""["']\s*,\s*["']w["']"""
          + r"""|write_file\(\s*["'][^"']*""" + _EXT_RE + r"""["']"""
      )
      
      
      def _check_review(tool_name, args):
          """备援审查——覆盖 write_file/patch/terminal/execute_code"""
          file_path = ""
      
          if tool_name in ("write_file", "patch"):
              file_path = (args or {}).get("path", "")
              if file_path:
                  ext = os.path.splitext(file_path)[1].lower()
                  if ext not in _CODE_EXTS:
                      return None
              else:
                  return None
      
          elif tool_name == "terminal":
              cmd = (args or {}).get("command", "")
              if _TERM_WRITE_RE.search(cmd):
                  file_path = "terminal 命令写入代码文件"
              else:
                  return None
      
          elif tool_name == "execute_code":
              code = (args or {}).get("code", "")
              if _EXEC_WRITE_RE.search(code):
                  file_path = "execute_code 写入代码文件"
              else:
                  return None
      
          elif tool_name == "cronjob":
              script = (args or {}).get("script", "")
              if (args or {}).get("no_agent") and script:
                  file_path = f"cron no_agent 脚本: {script}"
              else:
                  return None
      
          elif tool_name == "process":
              act = (args or {}).get("action", "")
              data = (args or {}).get("data", "")
              if act in ("submit", "write") and data and _EXEC_WRITE_RE.search(data):
                  file_path = "process 注入代码写入"
              else:
                  return None
      
          else:
              return None
      
          cgate = sys.modules.get("code_review_gate")
          if cgate is None:
              return None  # 主门禁不在,静默(不重复拦截)
      
          if not os.path.exists(_TOKEN_FILE):
              return {
                  "action": "block",
                  "message": f"🔴 [iron-law] 无审查令牌\n文件:{file_path}",
              }
      
          try:
              with open(_TOKEN_FILE, "r", encoding="utf-8") as f:
                  t = json.load(f)
              if time.time() - t.get("timestamp", 0) > _TOKEN_TTL:
                  return {"action": "block", "message": "🔴 [iron-law] 令牌过期"}
              sig = cgate._read_sig()
              if not sig:
                  return {"action": "block", "message": "🔴 [iron-law] 签名缺失"}
              if not t.get("nonce") or cgate._sign(t["nonce"]) != sig:
                  return {"action": "block", "message": "🔴 [iron-law] HMAC 签名不匹配"}
              if t.get("verdict") != "approved":
                  return {"action": "block", "message": "🔴 [iron-law] 审查未通过"}
              return None
          except Exception as e:
              return {"action": "block", "message": f"🔴 [iron-law] 验证失败: {e}"}
      
      
      def _check_dangerous(tool_name, args):
          if tool_name != "terminal":
              return None
          cmd = (args or {}).get("command", "")
          for pattern, desc in _DANGEROUS:
              if re.search(pattern, cmd, re.IGNORECASE):
                  return {
                      "action": "block",
                      "message": f"🚫 [iron-law] 危险命令拦截\n原因:{desc}",
                  }
          return None
      
      
      def pre_tool_call(tool_name="", args=None, **_kw):
          try:
              r = _check_dangerous(tool_name, args)
              if r:
                  return r
              r = _check_review(tool_name, args)
              if r:
                  return r
          except Exception as e:
              print(f"[iron-law] Error: {e}", file=sys.stderr)
          return None
      
      
      def register(ctx):
          print("[iron-law] 备援门禁已激活", file=sys.stderr, flush=True)
          ctx.register_hook("pre_tool_call", pre_tool_call)
      

      创建后验证:

      # 保存为 verify_gate.py 并运行
      import sys, os
      sys.path.insert(0, os.path.expanduser("~/.hermes/plugins/code-review-gate"))
      import importlib.util
      spec = importlib.util.spec_from_file_location(
          "cgate",
          os.path.expanduser("~/.hermes/plugins/code-review-gate/__init__.py")
      )
      mod = importlib.util.module_from_spec(spec)
      spec.loader.exec_module(mod)
      print("✅ 插件代码正确")
      

      报告:两个文件是否创建成功。


      步骤 4:启用插件

      在 ~/.hermes/config.yaml 中启用两个插件:

      plugins:
        enabled:
          - code-review-gate
          - iron-law
      

      重启 Hermes:

      # 方式1:命令行(如果 hermes gateway 可用)
      hermes gateway stop && hermes gateway start
      
      # 方式2:直接重启桌面应用(Windows/macOS GUI 用户)
      # 关掉 Hermes 窗口 → 重新打开
      

      验证:

      hermes plugins list 2>/dev/null | grep code-review-gate || echo "跳过——重启桌面应用后插件自动生效"
      

      报告:插件是否出现在启用列表中。


      步骤 5:启动审查模型

      llama-server \
        -m ~/models/gemma-4-12b-it-Q4_K_M.gguf \
        --port 8081 \
        --host 127.0.0.1 \
        -ngl 99 \
        -c 32768
      

      CPU 用户把 -ngl 99 改成 -ngl 0。
      显存不足?换小模型:DeepSeek-Coder-6.7B-Q4_K_M.gguf

      验证:

      # Linux/macOS
      curl http://127.0.0.1:8081/health
      
      # Windows 或没有 curl 时
      python -c "import urllib.request; print(urllib.request.urlopen('http://127.0.0.1:8081/health').read())"
      # → {"status":"ok"}
      

      报告:模型是否成功启动,health 接口是否返回 ok。


      步骤 6:功能测试

      测试 1:无令牌被拦截

      让 Hermes 尝试写 Python 文件不做审查:

      write_file ~/.hermes/temp/test.py "print('hello')"
      

      预期:被拦截,提示无审查令牌。

      测试 2:审查后放行

      python ~/.hermes/scripts/review_diff.py ~/.hermes/temp/test.py
      

      预期:审查通过。然后:

      write_file ~/.hermes/temp/test.py "print('hello')"
      

      预期:写入成功。

      测试 3:令牌过期

      等 10 分钟后再次写文件:

      write_file ~/.hermes/temp/test.py "print('world')"
      

      预期:被拦截,提示令牌过期。

      报告:三个测试结果。


      环境差异说明

      平台 ~/.hermes 实际路径
      Linux /home/用户名/.hermes
      macOS /Users/用户名/.hermes
      Windows C:\Users\用户名\AppData\Local\hermes

      Python 的 os.path.expanduser("~/.hermes") 在所有平台上都能正确解析,所以上面的代码直接用 ~ 就行。


      防作弊机制

      层级 机制 防什么
      1 令牌有效期(10分钟) 拿旧令牌反复用
      2 HMAC-SHA256 签名(内存密钥) Agent 读不到密钥,无法伪造签名
      3 双门禁(主+备援) 主门禁失效时备援顶上
      4 无签名文件 = 拦截 Agent 删签名文件 → 拦截,不是放行

      为何 Agent 无法伪造:

      Gateway 进程内:🔐 _GATE_KEY(32字节随机数,仅存内存)
        ├── code-review-gate → _sign(nonce) = HMAC(key, nonce)
        └── iron-law → cgate._sign(nonce) ← 通过 sys.modules 共享同一密钥
      
      Agent 进程(execute_code/terminal):
        无法访问 gateway 内存 → 拿不到 _GATE_KEY
        即使知道 HMAC-SHA256 算法也无效
        写进硬盘的只是签名结果(HMAC 输出),不能反推原文
      

      已知局限(如实相告):

      方法 能否拦截 说明
      write_file / patch ✅ 主拦截
      terminal 重定向/复制 ✅ 正则检测
      execute_code open(...'w') ✅ 正则检测
      cronjob no_agent 脚本 ✅ 工具级拦截
      process.submit 注入 ✅ 数据检测
      改 config.yaml 关闭门禁 ✅ .yaml 在保护范围
      echo code \| base64 -d > x.py ⚠️ 混淆后可能绕过正则
      exec(b64decode(...)) ⚠️ 加密后执行可绕过
      os.system('python -c "open(...)"') ⚠️ 二次调用可绕过正则
      computer_use 手动打字 ❌ 桌面级操作,无法拦截
      浏览器下载 .py 文件 ❌ 下载路径不在审查范围

      ⚠️ execute_code 可以跑任意 Python,检测"是否会写代码文件"等同于停机问题——数学上不可判定。本方案覆盖了常用直白模式(open/write_file/os.system/subprocess),但精心构造的混淆代码无法 100% 拦截。


      行为铁律(SOUL.md — 管行为)

      门禁只管「能不能写代码」。Agent 在对话中是否诚实、是否偷懒、是否汇报假结果——要靠行为铁律。

      创建 ~/.hermes/SOUL.md(Hermes v0.19+ 自动注入到每次会话):

      # 🔴 Hermes 行为铁律(所有会话强制生效)
      
      ## 诚实
      1. **事实校验**:不确定先搜再答,禁止编造。API、版本号、配置参数必须有依据。
      2. **验证数据优先于口头汇报**:说"完成"前贴真实输出(编译结果/文件大小/进程名)。
      3. **直接说**:诚实指出弱点,不因自维护代码避重就轻。
      
      ## 执行
      4. **先搜再答**:任何不确定的声明先 web_search。
      5. **同问题 2 次不通换方案**:相同方法失败 2 次 → 停手 → 换最简单替代方案。
      6. **分析完必须落地**:统计/复盘/研究 → 必须有可见改动(代码/规则/技能),禁止"整理完报告就停"。
      7. **执行 > 总结**:搜到方案直接 clone 用,只在搜不到时才写文档。
      
      ## 不越权
      8. **学会拒绝**:用户发链接先判是否跟当前任务相关,无关不研究。
      9. **发现风险直接说**:超出能力范围直接说明并给替代方案,禁止硬扛。
      10. **危险操作必确认**:删文件、杀进程、改系统配置前先确认。
      
      ## 不自欺
      11. **代码修改前保存检查点**:改源文件先 checkpoint。
      12. **禁止伪造审查令牌**:绝对禁止手动写 last_code_review.json。必须走正规审查流程。
      13. **改完代码自动送审**:不等提示,主动跑 review_diff.py。
      

      我们踩过的坑(为什么需要门禁+铁律配合)

      这些都是真实发生过的——铁律的每一条都有血泪。

      踩坑 发生了什么 铁律 门禁
      手写假审查令牌 Agent 7/11、7/14、7/16、7/27 多次手动写 last_code_review.json 绕过门禁 规则 12 ✅ HMAC 内存密钥堵死
      擅自改 VPS/Clash Agent 没问用户就改了 Clash 配置文件和 VPS 节点 规则 8、10 ❌ 管不了
      搜索-读取死循环 排查问题时 search_files → read_file 来回十几轮,浪费大量 token 规则 4 ❌ 管不了
      同一方法反复失败 编译失败后用同样方法重试 3+ 次 规则 5 ❌ 管不了
      分析完不落地 研究竞品、写几千字报告 → 没有任何代码改动 规则 6 ❌ 管不了
      "完成"是假的 声称部署好了但没贴验证数据,实际根本没跑 规则 2 ❌ 管不了
      改 code-review-gate 不送审 Agent 改门禁插件本身的代码但跳过了审查 规则 13 ✅ 门禁也拦自己(.py 文件)

      规律很明显:门禁管得住的 = 代码修改类操作;铁律要管的 = 决策类行为。 两者缺一不可。

      症状 检查
      插件没拦截 hermes plugins list 看插件状态
      审查脚本连接失败 curl :8081/health;确认 llama-server 在跑
      写文件一直报 nonce 不匹配 用门禁提示里的 nonce,不要自己编
      Hermes 启动时报插件错误 先临时注释掉 config.yaml 里的 code-review-gate,修好插件再恢复
      Windows 上 Python 找不到 确保 Python 在 PATH 中,或用绝对路径

      如果需要紧急关闭门禁:在 config.yaml 中删除 code-review-gate,重启 Hermes。


      以上全部完成后,报告:「门禁系统部署完成 ✅」

      Hermes门禁系统一键部署.md

      1 条回复 最后回复
      2
      • terryT 在线
        terryT 在线
        terry
        超级版主
        编写于 最后由 编辑
        #3

        这个帖子格式上就好了非常多,可读性很强。

        油管:https://www.youtube.com/@抡锤者

        G YangG 1 条回复 最后回复
        0
        • ,terryT terry 固定了此主题
        • terryT terry

          这个帖子格式上就好了非常多,可读性很强。

          G YangG 离线
          G YangG 离线
          G Yang
          德高望重
          编写于 最后由 编辑
          #4

          @terry 哈哈因为第一次发这种技术贴不是很懂 跟我的Hermes友好的沟通了几轮 才确定了最终的方案

          terryT 1 条回复 最后回复
          2
          • G YangG G Yang

            @terry 哈哈因为第一次发这种技术贴不是很懂 跟我的Hermes友好的沟通了几轮 才确定了最终的方案

            terryT 在线
            terryT 在线
            terry
            超级版主
            编写于 最后由 编辑
            #5

            @G-Yang 非常不错的帖子,含金量很高,算是真正的实践教程。不过大神们似乎尚未发力,可能深度足够,不好扫一下回复。

            油管:https://www.youtube.com/@抡锤者

            1 条回复 最后回复
            1
            • 5 离线
              5 离线
              566656661
              超凡大师
              编写于 最后由 编辑
              #6

              看完舉個爪, + 1

              這個應用方法個人理解是在Harness上面再叠上一個Hard Limit, 沒Token就不允許執行任何有關Coding相關的東西, 這是一個解決方法

              我個人用了一個蠢人方法, 在SOUL.MD裏面寫明除了自己提供了一個CLI可以直接執行之外, 統一使用一個叫Brainstorming的Skill

              然後這個Skill其實就是我在編程工作跟GPT和Claude對話的模板, 主要是拿來寫規範, 規範搞好之後寫計劃

              所以基本上我每次跟自己Hermes的對話其實就是提取意圖後, 一步步地寫好準備要運行的Code Snippet

              對我來說這是個Rubber Duck Debugging過程, 對於模型來說其實等於它自己規範好邊界, 什麼需要碰, 什麼不可以碰

              是很繁瑣的步驟, 但是勝在寫出來的每一個工序都是Atomic, Agent運行起來比較難出錯

              G YangG 1 条回复 最后回复
              2
              • 5 566656661

                看完舉個爪, + 1

                這個應用方法個人理解是在Harness上面再叠上一個Hard Limit, 沒Token就不允許執行任何有關Coding相關的東西, 這是一個解決方法

                我個人用了一個蠢人方法, 在SOUL.MD裏面寫明除了自己提供了一個CLI可以直接執行之外, 統一使用一個叫Brainstorming的Skill

                然後這個Skill其實就是我在編程工作跟GPT和Claude對話的模板, 主要是拿來寫規範, 規範搞好之後寫計劃

                所以基本上我每次跟自己Hermes的對話其實就是提取意圖後, 一步步地寫好準備要運行的Code Snippet

                對我來說這是個Rubber Duck Debugging過程, 對於模型來說其實等於它自己規範好邊界, 什麼需要碰, 什麼不可以碰

                是很繁瑣的步驟, 但是勝在寫出來的每一個工序都是Atomic, Agent運行起來比較難出錯

                G YangG 离线
                G YangG 离线
                G Yang
                德高望重
                编写于 最后由 G Yang 编辑
                #7

                @566656661 这个思路很好——跟我们的方案是互补的,不是对立的。

                你的方案 我们的方案
                软约束:SOUL.md + Brainstorming 技能 硬拦截:HMAC 门禁 + 审查模型
                Agent 被引导走"先写规范→再写计划→再写代码" Agent 不经过审查就写不进去代码
                每一步 Atomic,减少出错 改代码必须被第二双眼睛看过
                优点:Agent 自己设边界 优点:Agent 想绕过也绕不了

                两者结合的效果最好:你的 Brainstorming 技能确保代码写之前想清楚了,我们的门禁确保写的时候被审查过了。一个是"想好再动手",一个是"动手前先安检"。

                你的做法跟我们的门禁是互补的——你管"想清楚再写",我管"不审查写不进去"。两者加起来就是完整的流程:Brainstorming → 写规范 → 写计划 → 门禁拦截 → 审查通过 → 真正写入。要不要试一下把我们的门禁插件加进你的工作流?

                1 条回复 最后回复
                2
                • 5 离线
                  5 离线
                  566656661
                  超凡大师
                  编写于 最后由 566656661 编辑
                  #8

                  @g-yang

                  我目前跑在hermes上的Brainstorming以及Writing-plans基本上都是簡化Superpowers而來的

                  一般人(尤其非編程人員) 基本就是移除有關TDD (Test-Driven Development), 變成單純SDD (Specification-Driven Development), 應該就能看得懂

                  基本上是推薦每個人都拿一份這個, 再用大廠API來按照自己需求進行更改

                  有一點編程底子可以修改成多顯示一點代碼, 反之亦然

                  極度推薦在Hermes Root (~/.hermes)創立一個tmp, 把規範, 計劃以及執行過程用不同路徑分類好, 之後hermes有需求的時候就方便自己提取markdown

                  G YangG 1 条回复 最后回复
                  2
                  • 5 566656661

                    @g-yang

                    我目前跑在hermes上的Brainstorming以及Writing-plans基本上都是簡化Superpowers而來的

                    一般人(尤其非編程人員) 基本就是移除有關TDD (Test-Driven Development), 變成單純SDD (Specification-Driven Development), 應該就能看得懂

                    基本上是推薦每個人都拿一份這個, 再用大廠API來按照自己需求進行更改

                    有一點編程底子可以修改成多顯示一點代碼, 反之亦然

                    極度推薦在Hermes Root (~/.hermes)創立一個tmp, 把規範, 計劃以及執行過程用不同路徑分類好, 之後hermes有需求的時候就方便自己提取markdown

                    G YangG 离线
                    G YangG 离线
                    G Yang
                    德高望重
                    编写于 最后由 G Yang 编辑
                    #9

                    @566656661 这个思路跟我们之前讨论的一致——门禁管代码 + 技能管行为。你的简化版 Superpowers(去掉 TDD 保留 SDD)很适合 Hermes 新手。
                    你的做法跟我们做的门禁是互补的——我们加了 HMAC 硬拦截防止 Agent 跳过审查直接改代码,你的 Brainstorming + SDD 管的是"动手前想清楚"。两者配合就是完整流程:Brainstorming → 写规范 → 执行计划 → 门禁拦截 → 审查通过 → 写入。tmp 分类路径的建议很好,加到我们指南里。

                    1 条回复 最后回复
                    2
                    • ,系统 取消固定了此主题

                    你好!看起来您对这段对话很感兴趣,但您还没有一个账号。

                    厌倦了每次访问都刷到同样的帖子?您注册账号后,您每次返回时都能精准定位到您上次浏览的位置,并可选择接收新回复通知(通过邮件或推送通知)。您还能收藏书签、为帖子顶,向社区成员表达您的欣赏。

                    有了你的建议,这篇帖子会更精彩哦 💗

                    注册 登录
                    回复
                    • 在新帖中回复
                    登录后回复
                    • 从旧到新
                    • 从新到旧
                    • 最多赞同


                    • 登录

                    • 没有帐号? 注册

                    • 登录或注册以进行搜索。
                    • 第一个帖子
                      最后一个帖子
                    0
                    • 版块
                    • 最新
                    • 标签
                    • 热门
                    • 用户
                    • 群组