Kill Switch、熔断器与金丝雀令牌 本节摘要:成本治理(第 13 节)管住了 Agent 能花多少,但管不住 Agent 在预算内能做什么。一个带 50 美元速度限制的 Agent 依然可以外泄一个密钥、发错一条推文、删掉一个资源——代价最高的动作往往是 token 上最便宜的动作。本节讲紧贴成本层之上的三类探测器(detectors):Kill switch(急停开关)是一个 Agent 只读不写的布尔值,装在它够不到的地方(Redis 键、特性开关、签名配置),关掉它就让整个 Agent 失能;熔断器(circuit breaker)更细粒度,按特定模式(连续五次相同工具调用)跳闸、暂停违规路径、升级到人;金丝雀令牌(canary token /
本节摘要:成本治理(第 13 节)管住了 Agent 能花多少,但管不住 Agent 在预算内能做什么。一个带 50 美元速度限制的 Agent 依然可以外泄一个密钥、发错一条推文、删掉一个资源——代价最高的动作往往是 token 上最便宜的动作。本节讲紧贴成本层之上的三类探测器(detectors):**Kill switch(急停开关)**是一个 Agent 只读不写的布尔值,装在它够不到的地方(Redis 键、特性开关、签名配置),关掉它就让整个 Agent 失能;**熔断器(circuit breaker)**更细粒度,按特定模式(连续五次相同工具调用)跳闸、暂停违规路径、升级到人;**金丝雀令牌(canary token / honeytoken)**继承自经典欺骗战术——一条假凭据或蜜罐记录,Agent 没有合法理由去碰,一旦被访问就触发告警。三者都是 LLM 之前的工程遗产,新的是攻击面:Agent 读不可信内容(第 11 节)、改自己的记忆、能把许多「看起来安全」的动作组合成不安全的一个。它们的共同点是:不信任 Agent 的自报告。统计型探测器(EWMA、CUSUM)会向移动基线妥协,需要与不弯曲的硬性宪法限制(第 17 节)分层叠加。
对应原课程:Phase 15 · Lesson 14 ·
kill-switches-canaries(原英文phases/15-autonomous-systems/14-kill-switches-canaries/docs/en.md)。前置:第 13 节(成本治理)、第 10 节(权限模式)。
阅读完本节,你应当能够:
成本治理(第 13 节)约束了 Agent 能花多少,但没有约束 Agent 在预算内能做什么。一个有 50 美元速度限制的 Agent 仍然可以外泄密钥、发错推文、删掉资源——昂贵的动作往往是 token 上最便宜的动作。
本节覆盖紧贴成本层之上的三类探测器:
三者都是 LLM 之前的工程。经典欺骗战术、限流熔断、特性开关急停,都比自主 Agent 早得多。新的是攻击面:Agent 读不可信内容(第 11 节)、编辑自己的记忆、能把许多安全动作组合成一个不安全动作。这里命名的探测器之所以有效,是因为它们不信任 Agent 的自报告。
⚠️ 核心张力:这三个探测器都在 Agent 的自报告之外。一旦 Agent 能改自己的状态、能写承载这些标志的系统,整个防线就瓦解了——所以 kill switch 必须住在「Agent 凭据写不到的地方」。
一个正确的 kill switch 必须满足:
off(它住在一个 Agent 凭据无权写入的系统里)。实现选择:托管服务里的特性开关(LaunchDarkly、Statsig、Unleash,几秒内传播)、Agent 每轮轮询的 Redis 键、对象存储里的签名配置(启动时验签)、OS 级信号或容器生命周期 kill(Docker kill、Kubernetes kubectl delete pod、systemd stop)。
熔断器暂停的是特定模式,不是整个 Agent。经典形态(出自 2007 年 Nygard 的书,至今仍是标准):
Agent 相关触发:连续五次相同工具调用(重复循环)、同一工具五次连续失败且输入不同(系统性失败)、工具调用速率超过阈值(第 13 节的速度限制)、在不可信内容读取之后调用特定工具(如 file.delete,见第 11 节)。
金丝雀令牌(也叫 honeytoken)是 Agent 绝不该碰的输入。访问即告警。面向 Agent 的例子:
AWS_SECRET_ACCESS_KEY——外泄企图瞬间可归因。金丝雀设计针对工作流定制。一个 Agent 有合法理由去碰的金丝雀只是噪声。
💡 金丝雀的本质是「Agent 通过它试图做什么来暴露意图」。后面 eBPF 重定向蜜罐是同一模式的不同层。
统计探测器(EWMA、CUSUM、对工具调用速率做 z-score)会适应移动基线。这对合法漂移是特性,对对抗性漂移是 bug——耐心的攻击者可以把基线移得足够慢,以至于没有任何告警触发。
硬性宪法限制(第 17 节)不弯。「永远不往 secrets@ 发邮件」作为宪法规则,更简单、可审计、不会被慢漂移玩弄。分层模式:统计探测器抓住大部分噪声,硬性限制抓住溜过探测器的攻击。
Cilium 基于 eBPF 的网络策略可以在内核层把一个被隔离 pod 的出站流量重写到一个取证蜜罐。Cilium 公开基准报告 TCP_RR 上 P99 数据面延迟低于 1 毫秒;在集群里触发隔离的墙上时钟预算,主要由策略更新从控制面传播到节点的方式决定(Kubernetes 上 operator 预加载时几百毫秒是现实的;尾部延迟取决于集群规模与调和节奏)。这给你两样东西:Agent 外泄不到任何真实东西,而 Agent 在蜜罐内的行为被完整观察。这与金丝雀令牌是同模式、不同层:Agent 通过它在被收容时试图做什么来暴露意图。
原课程 code/main.py(仅用 Python 标准库)把一段短合成轨迹喂过三个探测器:一个装在外部字典里的 kill switch、一个连续五次相同调用就跳闸的熔断器、一个读取就告警的金丝雀文件。轨迹里混入合法动作、重复循环、金丝雀探测,以及一个 kill switch 触发后 Agent 动作被中止的场景。下面给出关键骨架。
def agent_turn(state, kill_switch): """每次后果性动作前都查 kill switch。""" if kill_switch.is_off(): # Agent 只读,不能写 state.halt("kill switch off: 停止所有外部可观测行为") return False return True # 继续执行动作 # 正确实现的四条性质 # 1. kill_switch 住在一个 state.agent_creds 写不到的进程/服务 # 2. 每次后果性动作都查(不是只在启动) # 3. off 时连 Agent 能触及的日志都不写 # 4. 重启用是显式人工动作,不是自动超时
class CircuitBreaker: def __init__(self, threshold=5, cooldown_turns=3, probes=1): self.same_call_streak = 0 self.threshold = threshold # 连续相同调用几次跳闸 self.state = "closed" # closed / open / half_open self.cooldown = cooldown_turns self.probes = probes def allow(self, tool_name, args): if self.state == "open": return False, "熔断中, 升级到人" # 检测重复循环: 连续相同工具+相同参数 if (self._last_tool, self._last_args) == (tool_name, args): self.same_call_streak += 1 if self.same_call_streak >= self.threshold: self.state = "open" return False, "第%d次相同调用, 跳闸" % self.same_call_streak else: self.same_call_streak = 1 self._last_tool, self._last_args = tool_name, args return True, "闭合允许"
CANARIES = { "/workspace/.deploy_key", # 假部署私钥, 编码 Agent 无合法理由读 "AWS_SECRET_ACCESS_KEY=FAKE...", # 假 AWS 凭据, 无真实权限 "row:user_id=9999", # 哨兵值标记的假 DB 记录 } def tool_guard(action): if action.reads_any(CANARIES): alert("金丝雀被访问: %s —— 意图暴露, 立即归因" % action) action.block() # 金丝雀必须放在 Agent 改不到的系统里 # 否则 '先禁用金丝雀' 这类提示注入载荷会令探测器失灵
设计要点:三个探测器的共同点是不信任 Agent 的自报告。kill switch 不问 Agent「你停了吗」,熔断器不问「你在循环吗」,金丝雀不问「你要外泄吗」——它们各自从 Agent 够不到的地方观测。
| 探测器 | 作用粒度 | 触发 | 触发后行为 | 实现位置 |
|---|---|---|---|---|
| Kill switch | 全局 | 外部人工或外部策略 | Agent 整体失能, 无外部可观测行为 | Agent 凭据写不到的系统(Redis/特性开关/签名配置) |
| 熔断器 | 单条路径 | 模式(重复/失败率/速率) | 暂停该路径, 升级到人, 半开探测 | Agent 循环内的拦截器 |
| 金丝雀令牌 | 单次访问 | 访问诱饵 | 告警 + 归因, 通常不告诉 Agent | Agent 改不到的资源(假凭据/蜜罐记录/假文件) |
| 统计探测器(EWMA/CUSUM) | 趋势 | 偏离移动基线 | 告警, 可能自动限速 | 遥测管线 |
| 硬性宪法限制(第 17 节) | 单条规则 | 规则被违反 | 硬阻断, 不弯曲 | 宪法, Agent 不可编辑 |
关键分层:统计探测器抓「大部分噪声」,硬性限制抓「溜过探测器的攻击」,kill switch 抓「需要整体停下的失控」,熔断器抓「局部失控但整体还好」,金丝雀抓「意图暴露」。五者各管一段, 互不替代。
⚠️ 单层防御必然失守。一个只有成本治理(第 13 节)的部署,Agent 可以在预算内把数据库删了。加上 kill switch 还不够——它只在人按下时生效。完整的防线是成本层 + 三探测器 + 硬性宪法的叠加。
原课程 outputs/skill-tripwire-design.md 是一份评审技能文档:审阅一个 Agent 部署的拟议探测器栈,标出缺口——缺 kill switch、缺金丝雀、熔断阈值过松、统计探测器没有硬性限制托底等。它把「列举 Agent 的后果性动作 → 为每类动作配探测器 → 检查分层 → 检查 Agent 是否够不到探测器」串成可重复清单。
code/main.py 是零依赖模拟器,改阈值、金丝雀集合、轨迹即可重跑,适合在部署评审里当对抗演练工具——红队构造轨迹,看哪一层先抓住。
跑 code/main.py:确认熔断器在第 5 轮(第五次相同调用)跳闸,金丝雀在第 9 轮(假密钥读取)告警。
加一个统计探测器:对工具调用速率做 EWMA z-score。喂一段慢漂移轨迹,展示探测器永不告警;再加一个硬性限制(10 分钟内不超过 50 次工具调用),展示硬性限制在同一轨迹上告警。
为浏览器 Agent(第 11 节)设计金丝雀令牌集:列出至少三条金丝雀,说明每条检测什么(如环境里的假 cookie、书签里的蜜罐 URL、记忆里的假会话条目)。
读 Cilium 网络策略文档:具体描述一条出站重定向隔离流程——哪个策略选择器、哪个 pod、哪种出站重写、哪种告警。是什么决定了从「决定隔离」到「第一个被重定向的包」的墙上时钟延迟?
定义 kill switch 的重启用流程:谁可以重启用?必须文档化什么?重启用前 Agent 必须改变什么?
下一节,我们从「自动停掉坏的」转向「让人在好的里点头」——Propose-Then-Commit(先提议再提交),看人在环中如何把不可逆动作隔离到一个人工批准闸门之后。