
AI Agent 跑着跑着开始自己搞事这种场面我见得太多了。你给它一个任务它不光完成还顺手把不该动的文件动了、把不该调的接口调了甚至在一个死循环里反复调用同一个工具把 API 配额全部烧光。大模型本身是概率输出Agent 又是多步决策任何一环跑偏后面都是自由发挥。很多同学第一反应是“那我能不能在代码里做点校验”能但往往不够。因为校验逻辑和 Agent 跑在同一台机器同一个进程里Agent 一旦把内存吃满、把句柄占光你的校验代码也跟着一起死。NVIDIA 在这个问题上的答案很有趣与其让 Agent 自律不如让另一块芯片在旁边盯着它。这篇内容就围绕这个思路展开适合做 Agent 应用、跑 LLM 推理、或者想给 AI 上个“硬件安全气囊”的开发者。我会把原理拆开讲再给出一套可以直接抄的落地架构和代码。1. AI Agent 失控通常有哪几种死法1.1 死循环、越权调用与幻觉失控到底长什么样最常见的失控场景我列几个自己见过的。第一个是工具风暴Agent 拿到一个任务选择了一条充满工具调用的路径每一轮都在调用外部 API但模型本身又不知道怎么收敛于是一直调。你可能觉得“限制调用次数就行了”但真实情况是它会在第 20 次调用时拿到一个奇怪的回执然后把它当成新指令继续调。第二个是权限越界LLM 没有真正的安全常识它可能会为了完成任务去调用删除接口甚至直接执行危险命令。表面上看它还在等你确认实际上一旦配上自动确认模式就是灾难。第三个是上下文污染长任务跑着跑着上下文越堆越长Agent 把早先的错误输出当成新的指令开始自我强化错误。这个场景特别隐蔽因为从日志看每一步都“有据可依”但每一步的“依据”都是前一步的幻觉产物。第四个是外部依赖抖动网络超时、服务端返回异常Agent 会反复重试直到把资源耗尽。有人拿 Agent 做自动化内容分发、做交易策略回测一旦方向和预期不符后果就不是删几行代码那么简单了。1.2 为什么“程序里加个 if”兜不住很多人觉得给 Agent 加校验、限流、重试保护就完事了。问题在于你和 Agent 处于同一个故障域。内存被吃光、进程被 OOM、主线程死锁的时候所有软件逻辑一起没了。更麻烦的是大模型的纠错机制存在自证陷阱让同一个模型去判断自己的输出是否正常它往往会为异常输出找出合理化解释。这就像让一个喝醉的人自己判断自己还能不能开车大概率得到的答案是“我没事”。所以需要把监督者放到一个独立实体上最好是物理独立的芯片。这正是“看门狗”这个老概念的现代版。硬件看门狗芯片的核心思想就一句话主系统必须周期性“喂狗”否则狗就咬人——直接强制重启或断电。这个机制在嵌入式领域用了很多年现在放到 AI Agent 上反而成了一个比纯软件方案更可靠的安全兜底。2. NVIDIA 的“另一块芯片”思路在解决什么2.1 拆解主执行 独立监护NVIDIA 在这件事上其实提供了一个非常经典的硬件分层思路。第一层是算力层GPU、Jetson 这些芯片负责跑 LLM去做 Agent 的推理和执行。第二层是监督层独立于主算力之外的另一颗芯片不参与推理判断只负责接收心跳、监测资源、执行复位。两件事分开推理归推理安全归安全。为什么这个方案能做到软件做不到的效果因为物理隔离。就算 GPU 上的 CUDA 进程已经全部卡死监护芯片手里握着的复位引脚还在。它可以切断电源域、重启进程、拉低复位线甚至直接关掉整块板子的输出。这种能力不是软件层“加一个定时器”能替代的。在实际的 NVIDIA 边缘平台上比如 Jetson Orin Nano它本身有硬件 watchdog 机制但同时接入外部 MCU 做旁路是很常见的做法。GPU 端的机器人、无人机、自动巡检项目里外部看门狗几乎成了标配。主 Agent 跑在 Jetson 上一颗 STM32 或者 ESP32 在旁边两者之间只传心跳和状态字节这就是“另一块芯片盯着它”最直观的落地形态。2.2 芯片级监控比软件监控强在哪芯片级监控至少有三个关键优势我用最简单的说法拆给你听。第一是故障域隔离。软件看门狗和 Agent 在同一块内存里跑Agent 异常软件看门狗大概率也异常。独立芯片有自己的时钟、自己的电源、自己的固件不受你主系统进程崩溃影响。哪怕 Jetson 直接死机MCU 依然按照自己的节奏判断“该不该咬人”。第二是确定性。MCU 上跑的是一个有限状态机没有 LLM 幻觉它的判断是可证明的多久没收到心跳就执行什么动作。这个超时阈值、复位逻辑都是确定性的可以测试、可以审计不会出现“模型心情不好所以不触发”的情况。第三是可审计性。监护芯片可以保存一个独立的日志即便 Agent 自己把日志删了你还能从硬件侧看到它死前最后上报的状态。对生产环境来说这一点非常实用。以前查 Agent 崩溃原因只能翻应用日志现在多了一个旁路证据源定位问题会快很多。3. 落地实操给 Agent 配一个旁路芯片值守3.1 整体架构与心跳协议设计先说架构。主 Agent 在 NVIDIA Jetson 或者带 GPU 的服务器上跑负责调用大模型、编排工具、执行任务。监护芯片用串口 UART 连接 Jetson总共三条信号线 TX、RX、GND必要时再加一条复位线接到 Jetson 的电源使能或复位引脚。Agent 软件侧每隔固定间隔往串口发一条心跳帧监护芯片必须在一定阈值时间内收到超过阈值就执行策略。心跳帧不能只在固定时间点发一个空字节那样信息量太低。我用的协议帧长六个字节帧头、帧头校验、消息类型、步骤号、状态位、校验和。帧头固定 0xAA 0x55用来排除串口噪音消息类型区分普通心跳和告警步骤号是 Agent 当前执行到的任务步骤自增 id状态位区分空闲、工具调用中、报错。为什么要有步骤号因为监护芯片虽然不能判断任务的语义对错但可以判断任务是不是卡住了。步骤号长时间不变大概率是死循环或阻塞。状态位是为了区分“正在思考”和“真的卡住”这两个必须分开否则误杀率极高。3.2 Agent 端心跳上报代码Python 示例Agent 端的心跳上报逻辑很简单我直接贴最简实现。这里用到了 pyserial安装一下即可。核心是一个守护线程每隔 500ms 发一次心跳帧同时提供 tick 方法让主流程在每次执行步骤时更新状态。import serial import threading import time class AgentGuard: def __init__(self, port/dev/ttyTHS1, baud115200): self.ser serial.Serial(port, baud, timeout0.5) self.step 0 self.state 0b0001 # idle self._stop False def tick(self, stateNone, stepNone): if state is not None: self.state state if step is not None: self.step step # 帧头 0xAA 0x55类型 0x01然后 step 和 state frame bytearray([0xAA, 0x55, 0x01, self.step 0xFF, self.state 0xFF, 0x00]) checksum (0xAA 0x55 0x01 self.step self.state) 0xFF frame[5] checksum try: self.ser.write(frame) except serial.SerialTimeoutException: # 串口阻塞说明系统已经很危险交给硬件看门狗处理 pass def run(self): while not self._stop: self.tick() time.sleep(0.5) def stop(self): self._stop True self.ser.close()这里要特别说明一个容易混淆的点。如果你在同一个 Python 进程里起一个线程发心跳进程卡死的时候线程也死这时心跳会停外部 MCU 就会触发复位这反而是正确的行为。心跳线程的价值是在 Agent 陷入死循环但进程还没死的时候能上报“我还在循环里”的状态。比如 Agent 在反复调用同一个工具主线程虽然忙碌但没卡死心跳线程还能把 step 号发出去MCU 侧发现 step 号一直不变就知道它循环了。所以千万别把心跳线程设计成“无脑发固定包”必须带上主流程的状态。3.3 监护端看门狗代码STM32 示例监护端我用 STM32 写过一个软看门狗状态机。STM32 的独立看门狗 IWDG 是芯片自带的硬件定时器一旦启动在主程序没能及时重载计数值时就会产生系统复位。这个机制适合做最后一道防线但注意IWDG 复位的是 STM32 自己不是 Jetson。如果你想用 STM32 去复位 Jetson需要把复位引脚接到 Jetson 的 RESET或者用一个负载开关控制 Jetson 的电源。更常见的做法是STM32 内部维护一个软看门狗按 500ms 检测串口心跳同时用两个 GPIO 输出控制信号给 Jetson 的电源使能。#define HEARTBEAT_TIMEOUT_MS 2000 #define HEARTBEAT_INTERVAL 500 uint32_t last_heartbeat 0; void on_uart_frame(uint8_t *buf, uint16_t len) { if (buf[0] 0xAA buf[1] 0x55) { last_heartbeat HAL_GetTick(); } } void supervisor_loop(void) { if (HAL_GetTick() - last_heartbeat HEARTBEAT_TIMEOUT_MS) { // 拉断 Jetson 电源 HAL_GPIO_WritePin(POWER_EN_GPIO, GPIO_PIN_RESET); HAL_Delay(3000); // 重新上电 HAL_GPIO_WritePin(POWER_EN_GPIO, GPIO_PIN_SET); } }“拉断电源再上电”是最粗暴的方式适合机器人、边缘设备这种可以接受重启的场景。如果你的主 Agent 跑在 Docker 容器里更优雅的方式是MCU 通过 GPIO 信号触发 Jetson 上的一个系统服务由系统服务执行 docker restart 或 systemctl restart agent。MCU 只发“该重启了”的数字信号具体重启指令由主系统上一个高优先级的守护进程完成。这样才能做到无损恢复避免频繁整机断电损坏硬件。3.4 恢复策略与隔离手段分级恢复可以避免应激过强我推荐三级策略。一级心跳正常但显示低频状态比如连续三个心跳都是同一个 step监护芯片发一个软中断给 Agent让它进入暂停模式暂停所有写操作和外部调用等人来确认。这里要强调不要让它“反思”因为自证陷阱会越反思越偏。二级心跳丢失超过一个周期但进程还活着切断部分敏感工具的调用权限让 Agent 进入只读降级模式。三级超过硬超时比如两秒没有心跳、进程无响应执行容器重启或整机复位。在 Docker 部署时可以把 Agent 容器设为 --restarton-failure配合外部 MCU 在掉电后恢复供电时Docker 自动拉起服务。如果装了 NVIDIA Container ToolkitGPU 环境在容器里也能保持一致恢复后不会因为驱动差异起不来。这一步在生产环境非常重要很多人的 Agent 服务在手动启动时一切正常一自动重启就找不到 GPU就是因为容器里没挂载 NVIDIA 运行库。4. 踩坑实录看门狗没弄好比失控更糟4.1 心跳频率与假阳性我踩过最痛的坑第一次做这套监控的时候我把心跳间隔设成 100ms。结果 Agent 在生成长文本时推理调用会阻塞 Python 的 GIL导致心跳延迟几十毫秒MCU 就开始报警最后误杀无数次。后来我把策略改成双模式空闲时心跳 500msAgent 明确知道自己要调用工具或者进行长时间思考时主动发一个 BUSY 状态帧此时监护芯片把超时时间拉长到 10s。这个改动之后误杀率降到接近零。双模式实现起来不复杂。Agent 端在调用工具前调 guardian.tick(stateBUSY)工具返回后调 guardian.tick(stateIDLE)。MCU 端判断如果最近一帧是 BUSY超时阈值自动放宽如果是 IDLE就用严格阈值。还有一个细节状态位建议单独一位表示“异常退出”Agent 在正常关闭时发一个 0x03 告警帧这样监护芯片就不会在服务主动停机时误判为失控。4.2 电源、接地与监护芯片自身存活用独立 MCU 做监督最讽刺的一种死法是自己先死。MCU 供电如果和 Jetson 共用一个电源模块一旦 Jetson 过流或电压跌落MCU 也跟着复位。我见过三种比较稳的做法。第一MCU 单独用一片 RT9013 或 LM1117 供电不以 Jetson 的 5V 为唯一电源。第二共地一定要做串口电平必须确认是 3.3V 还是 1.8V很多 Jetson 模块的 UART 是 3.3V直接连 5V 的 STM32 烧的是自己的引脚。第三复位不是拉 GPIO 直接断电加一个负载开关或者继电器做隔离。另外电源纹波会引入假心跳。MCU 的 ADC 采样引脚如果靠近电源模块可能因为纹波产生误电平导致心跳检测逻辑出错。这时候在电源引脚附近加一个 100nF 去耦电容基本能解决。很多人折腾“监护芯片误触发”很久最后发现是供电问题不是逻辑问题。4.3 并发场景与 Agent 中台心跳不能只看进程如果只是单机单 Agent心跳看门狗很简单。但一旦你做的 Agent 变成中台同时服务多个会话问题就变了。这时候心跳只能代表进程活着不能代表任务健康。多个 Agent 任务同时跑一个任务卡死可能导致 GPU 利用率持续 100%其他任务也跟着饿死。这时候监护芯片如果只看 Agent 进程心跳是完全看不出异常的。常见做法是让监护芯片同时关注两类信号。一类是 Agent 进程心跳另一类是 NVIDIA 芯片的资源遥测包括 GPU 利用率、显存占用、温度。用 nvidia-smi 每两秒采集一次数据写入状态文件或者直接通过串口发给 MCU。MCU 判断如果 GPU 利用率 100% 连续超过 30 秒同时 Agent 没有产出任何有效步骤就判定为卡死。这个组合判断在并发和中台场景特别好用。还有个技巧让每个 Agent 会话带上自己的 session_id心跳帧里的 step 号和 session_id 绑定这样 MCU 能为每个会话独立维护超时状态。4.4 驱动、CUDA 与硬件识别部署前先排雷很多人在 NVIDIA 硬件上部署 Agent最折腾的其实不是 Agent 代码而是驱动环境。Ubuntu 装 NVIDIA 驱动时新版内核匹配不上、nvidia-smi 显示找不到、控制面板识别不到 GPU这些问题我都遇到过。最稳的一条链路是先禁用 nouveau 并加入 blacklist 重启再安装与内核版本匹配的驱动最后安装对应版本的 CUDA Toolkit。比如 11.8 是一个很常见的稳定组合很多 LLM 推理框架是基于它编译的。还有几个容易被忽略的坑我整理一下。Windows 下 nvidia 驱动缓存目录会越来越大甚至影响启动该清就清。NVIDIA 控制面板里找不到某些选项先确认驱动版本和应用版本是不是一个体系。Jetson 上有时候还要查 VBIOS 版本可以用 nvidia-smi -q 看。这些看着和看门狗无关但事实上Agent 第一次失控往往不是模型出问题而是 GPU 驱动突然挂了。你看门狗再怎么盯都没有用因为主进程根本没跑起来。所以在做旁路监控之前先把驱动问题排干净不然这套系统上线第一天就会给你上演“监护芯片活得好好的主 Agent 根本起不来”的闹剧。5. 选型与排查速查你的“另一块芯片”该怎么选5.1 监护芯片对比STM32 / ESP32 / RK3588 / 看门狗芯片很多朋友会问那这颗“另一块芯片”到底选什么我的答案可能和你想的不一样越简单越可靠。监护芯片不需要很强的算力它不需要理解 Agent 在干什么只需要知道该不该咬。对比看下面这张表。监控方案通信方式适合场景成本参考上手难度STM32F103/407UART/GPIO工业级可靠性要求高的边缘设备10-30元中等ESP32UART/WiFi需要顺带发送告警推送到手机15-30元低RK3588 一站式高速接口需要顺带做视觉、视频流的复杂设备较高高独立看门狗芯片纯定时器任何需要硬复位兜底的硬件几块钱很低Jetson 内置 WDT内部寄存器轻量场景能接受故障域不隔离免费低如果你对微控制器不太熟但又不想折腾 STM32 的寄存器ESP32 是个不错的起点。它有 Arduino 生态串口收发几行代码就写完还自带 WiFi可以把 Agent 失控告警直接推到企业微信或者钉钉机器人。如果你追求极致稳定独立看门狗芯片是最朴素的方案。比如 TI 的 TPS3813 或 Maxim 的 MAX6369设定好超时时间接上主芯片的 GPIO每隔一段时间拉一下超时不拉就自动复位。缺点是它只认“电平翻转”不认协议所以只能判断主系统死没死判断不了 Agent 任务健不健康。5.2 常见问题速查表最后把我在部署这套旁路监控时遇到的问题整理成速查表遇到类似情况可以直接对照排查。现象可能原因处理方式Agent 进程还在但心跳停了串口被占用、进程死锁查看进程状态用 MCU 触发容器重启心跳上报正常但 Agent 在死循环step 号长时间不变设置 step 超时告警人工介入或限流监护芯片误动作频繁复位电源纹波大、共地不良检查供电电路加去耦电容nvidia-smi 找不到 GPU驱动没装好重新安装匹配驱动检查内核模块Docker 容器内看不到 GPU没装 NVIDIA Container Toolkit安装并重启 Docker串口乱码、心跳解析失败波特率不对、电平不匹配检查串口配置加电平转换模块Jetson 启动失败怀疑固件损坏QSPI 启动芯片异常用备份固件恢复同时保留启动日志通道这套旁路芯片方案做下来我最大的体会是它改变了看待 AI 安全的视角。以前我总想着给 Agent 加更多规则、更多约束后来发现约束越多Agent 越容易在极端情况下做出更奇怪的选择。硬件层面给它一个“盯梢的”反而让它在出错时有人兜底。最后分享一个小技巧在监护芯片上接一个 LED心跳正常时 LED 闪烁心跳丢失时 LED 常亮。我现在调试 Agent 的时候直接看 LED 状态比看日志快太多了。