限制智能体权限后,还要审计工具输出
作者用受限工具替代 SSH 管理服务器,却发现工具会把手动终止容器误判为内存不足,并报告不存在的重启、建议无效命令。连续运行审计表明,工具正常执行并不代表返回的事实可靠。
AI 深度解读
把 Claude 的 SSH 权限收回来,再给它几个范围受限的工具,听起来已经稳妥。HomeButler 作者运行了六个月后发现,权限只解决了一半问题:工具依然可能把猜测包装成事实,让 agent 据此行动。
这篇文章最有用的地方,是把「代码正常运行」和「输出值得相信」拆开了。作者在 Raspberry Pi 上做了十一天持续运行观察,发现误报、残留数据、凭据泄露和字段语义混乱。它们没有导致崩溃,却足以误导下一步决策。
工具没报错,为什么 agent 还是会判断错
作者此前不愿给 Claude 家用服务器的 SSH 权限,选择提供结构化、范围受限的工具,避免 agent 拿着 shell 执行 rm -rf。这个方向没有变,变的是他对工具输出的要求。
在发布 HomeButler 1.0 前,作者把它放到 Raspberry Pi 上,运行三个应用、一个 watch 服务,并每天执行 report --json,持续十一天。他检查的重点是每一句输出是否属实。
第一天就发现三个问题:手动执行 docker kill 后,工具把容器退出认成 OOM,还给出 confidence: high;它记录了一次没发生的重启;它推荐了一条不存在的命令。
问题出在程序替证据补了故事。它看到退出码 137 就推断内存不足,看到时间戳字段就填入启动信息。人知道自己十分钟前杀过容器,agent 却未必拥有这段上下文。
作者的修复大多是删除推断:只有 Docker 真正发出 oom 事件才报告 OOM,只有实际发生启动才提供启动时间。
工具能确认的事实,才应该进入事实字段。
「没有变化」和「没查出来」必须分开
更隐蔽的问题出现在平静的一天:系统没有显著变化,report --json 却表示无法比较。原来,工具把「比较完成且没有变化」与「无法进行比较」归进了同一类。
对 agent 来说,这两种状态会触发不同动作:前者可以结束检查,后者可能需要继续调查。空列表返回 null 也有类似问题,脚本可能把它理解成数据缺失。
作者用下面的约定区分两种结果:
skipped the comparison could not be made
[] the comparison was made and found nothing
要看清的是第二行:[] 表示检查已经完成,结果为空。它不能同时承担「没检查」的含义。
操作结果也需要同样严格。作者清理演示机器时运行 install purge filebrowser,工具回答 Completely removed,但数据库和设置仍留在两个 Docker volumes 中。此前测试机器上也有残留,只是他相信了完成提示,没有发现。
修复后,有残留的回答会在 Removed, except: 下列出 volume 名称;真正全部删除时,仍使用原来的成功回答。这样,agent 才有依据报告剩余工作,而不会把一句过度承诺转述给用户。
只读权限管不住泄露,版本号也要管语义
作者随后加入 homebutler mcp --max-risk read,让 agent 只能查看机器。但其中的 docker_top 会返回容器的完整命令行。如果启动参数里有 --token=...,凭据就跟着只读结果一起交给了 agent。
「不会修改机器」和「不会暴露敏感信息」,是两项独立承诺。
作者把 docker_top 改为只返回进程名,不返回参数,并要求每个只读工具的文档明确说明会返回什么。权限标签描述的是操作范围,返回内容还需要单独审查。
真实交付链路也暴露了测试遗漏:watch 发出的告警,在 Discord、Slack 等所有 provider 上,时间都显示为 0001-01-01 00:00:00,直到作者给 Pi 接上真实 webhook 才被发现。
这些经历进一步影响了版本约定。agent 会根据 gone、new、replaced 分支处理;如果词义悄悄改变,即使 prompt 没动,行为也会变化。因此,HomeButler 1.0 承诺:agent 据以分支的工具、字段和词语,在 2.0 前不改变。golden test 会逐字段核对每个工具的回答,发现漂移就让构建失败。
先修正输出,再冻结接口。否则,稳定下来的可能就是错误承诺。
我的判断
我认同作者把输出当成决策接口来维护。对 AI 工具而言,字段名、空值、成功提示都参与控制流程,不能只按「人看着能懂」来设计。限制执行权限之后,仍要检查 agent 收到了什么证据。
不过,这篇提供的是作者的故障记录和工程经验,没有对照数据,不能据此证明窄工具与 shell 哪个总体更安全。十一天、三个应用的持续运行也只覆盖了有限场景,原文没有交代更完整的测试范围。
「只说机器报告的内容」适合约束事实字段,但故障诊断仍可能需要推断。我的取舍是把观察结果和诊断建议分开,让调用方知道哪些能直接引用,哪些还要验证。逐字段 golden test 有助于守住约定,却不能独自证明约定正确;错误输出同样可以被测试固定下来。
能拿来做什么
- 给日常 AI 编程工具检查一次返回值,把成功且为空、无法检查、执行失败分成可区分的状态,尤其检查
null、[]和成功提示是否混用。 - 为清理、卸载等操作返回实际删除项与残留项。只有证据支持全部完成时,才让 agent 向用户报告全部完成。
- 审查只读工具的返回内容,重点看进程参数是否带有 token 等凭据,并在工具说明中写清暴露范围。
- 如果要试用这个单一 Go binary,可以按下面的命令安装和查看报告。原文说明它没有 daemon、没有 database;最后一行限制操作风险为只读,返回内容仍需审查。
brew install Higangssh/homebutler/homebutler
homebutler report
homebutler mcp --max-risk read # for your agent