一个不该发生的悲剧,和一个必须面对的技术问题
2026 年 5 月 26 日,比利时佛兰德斯小镇 Buggenhout 附近,一辆校车与火车在平交道口相撞,4 人死亡,包括两名儿童。调查尚未公布最终原因,但这类事故的典型诱因通常是:驾驶员没有看到警示、感知系统失效(遮光或角度盲区)、或者决策系统在最后时刻选择了“闯过去”而不是“刹到底”。
如果你只把这个事件当作新闻,你错过了它给 Agent 开发者的真正信号:物理世界 Agent 的安全设计,不能依赖完美感知和完美决策。 当感知延迟、预测错误、通信中断时,Agent 必须有一个默认的“安全停止”行为——这个行为必须是能被严格证明的,而不是靠一个神经网络“学会”的。
本文不讨论具体事故归责,而是从技术角度给出一个可复用的安全停止策略设计框架。读完你可以直接用在自己的 Agent 系统里,无论是自动驾驶小车、无人机还是物理仿真测试台。
Agent 架构拆解:以平交道口为例
一个穿越平交道口的自动驾驶 Agent 包含四个层(与传统 LLM Agent 结构对应):
- 规划(Planning):决定何时减速、何时停车、何时重新起步。这里涉及多步骤任务规划:抵达前 200m 开始评估状态 → 进入等待区 → 确认安全 → 通过。
- 工具(Tools):传感器的信号——摄像头、激光雷达、雷达、甚至与铁路系统的无线接口。每个工具都有故障概率和延迟。
- 记忆(Memory):道口的近期状态(上一帧有没有火车鸣笛?信号灯几秒前变黄?),用于辅助预测。
- 执行(Execution):控制刹车、油门、转向。执行层本身也可能失效(如刹车油压不足)。

核心问题:Agent 什么时候“信任”自己的感知?
假设摄像头检测到前方道口亮起红灯,但激光雷达同时显示没有障碍物。Agent 应该前进还是停止?很多系统会做“多传感器融合”然后输出一个置信度。但置信度超过多少才允许通过?80%?95%?这个阈值在真实世界中很难定死,因为漏掉一列高速驶来的火车,代价是灾难性的。
我的观点:对于安全关键动作(如穿越道口),不应该有一个“通过”的决策阈值。 应该反过来设计:Agent 默认是“停止态”,只有确认所有潜在危险全部消除后,才进入“通过态”。这意味着一个简单的状态机比一个复杂的概率模型更适合做最终的安全门禁。
安全停止策略:状态机 + 冗余确认
下面是一个经过简化的平交道口 Agent 状态机(用于学习和演示,实际工业系统会复杂得多):
INIT → [前方 200m: 进入监控区] → MONITORING
MONITORING → [红灯+栏杆放下+无火车?] → HOLD
MONITORING → [绿灯+栏杆抬起+无火车?] → GO
HOLD → [警报解除] → MONITORING
HOLD → [超时(比如30秒)检测到冲突] → EMERGENCY_STOP
EMERGENCY_STOP → [手动恢复] → INIT
这个状态机的关键设计原则:
- 默认安全态(Default-Off):从 INIT 到任何前进动作之前,Agent 必须经过明确的“确认安全”转换。
- 多模态确认:进入 GO 前,至少需要两个独立信号同意(比如:摄像头说绿灯+铁路控制器说无火车+激光雷达说无运动物体)。任一信号不同意,则保持在 HOLD。
- 时间看门狗:在 HOLD 状态停留超过预估值(比如 30 秒),触发紧急制动并报告,而不是自作主张转向 GO。
- 恢复只能手动:从 EMERGENCY_STOP 到 INIT 必须有人工介入,这避免了 Agent 在故障后自动重启而忽视未解决的问题。

关键实现细节与踩坑记录
坑1:感知延迟导致“过时确认”
如果摄像头帧率是 30fps,网络传输延迟 100ms,那么 Agent 看到的画面实际是老画面。在这 100ms 里,火车可能已经接近。解决方案:在状态转换条件中加入时间戳校验。例如,如果要进入 GO,要求所有感知数据的采样时间都不超过 50ms,否则强制返回 HOLD。
坑2:传感器间冲突时的决策偏向
摄像头显示红灯,激光雷达显示无火车。你相信谁?如果相信摄像头,你停在原地——这是安全的,即使可能是误报。如果你相信激光雷达,你可能会穿过——可能撞上火车。正确的设计:任何传感器指示危险,就采用危险假设。 这叫“一致性检核”中的 OR 逻辑:只要有一个传感器提示危险,就按危险处理。
坑3:系统资源过载下的降级
如果 Agent 的规划器因为高负载而延迟输出,执行层应该怎么办?解决方案:引入一个独立于主规划器的安全监控器 Agent。它的唯一任务就是检测“是否需要紧急停止”。它不需要复杂的规划,只需要基于最新传感器数据的简单阈值判断。监控器和主规划器之间可以有不同的训练数据和逻辑,避免共性故障。
简化版动手实现:一个可运行的平交道口模拟
下面提供一个 Python 模拟,用有限状态机实现上述策略。你可以修改传感器延迟和故障模式来测试 Agent 的行为。
import time
import random
from enum import Enum
class State(Enum):
INIT = "INIT"
MONITORING = "MONITORING"
HOLD = "HOLD"
GO = "GO"
EMERGENCY_STOP = "EMERGENCY_STOP"
class CrossingAgent:
def __init__(self):
self.state = State.INIT
self.last_sensor_timestamp = 0
self.hold_start_time = None
self.hold_timeout = 30.0 # seconds
def sense(self):
# 模拟传感器:返回 (红灯亮?, 栏杆放下?, 火车检测到?, 时间戳)
# 为了测试,加入随机延迟和错误
now = time.time()
# 模拟20%概率传感器故障(永远返回安全)
red_light = random.choices([True, False], weights=[0.3, 0.7])[0] if random.random() > 0.2 else False
barrier_down = random.choices([True, False], weights=[0.3, 0.7])[0] if random.random() > 0.2 else False
train_detected = random.choices([True, False], weights=[0.3, 0.7])[0] if random.random() > 0.2 else False
timestamp = now - random.uniform(0.05, 0.3) # 模拟延迟
return red_light, barrier_down, train_detected, timestamp
def is_safe_to_go(self, red_light, barrier_down, train_detected, timestamp):
# 时间戳校验:如果数据太旧,视为不可信
if time.time() - timestamp > 0.5:
return False, "Sensor data stale"
# 任何危险信号 -> 不安全
if red_light or barrier_down or train_detected:
return False, "Danger signal present"
# 全部安全
return True, "All clear"
def step(self):
# 获取传感器数据
red, barrier, train, ts = self.sense()
safe, reason = self.is_safe_to_go(red, barrier, train, ts)
if self.state == State.INIT:
print(f"[INIT] Starting at position 0m")
self.state = State.MONITORING
elif self.state == State.MONITORING:
if safe:
print(f"[MONITORING] All clear -> GO")
self.state = State.GO
else:
print(f"[MONITORING] {reason} -> HOLD")
self.state = State.HOLD
self.hold_start_time = time.time()
elif self.state == State.HOLD:
# 如果条件变安全,回到MONITORING重新确认
if safe and time.time() - self.hold_start_time > 5.0: # 保证至少等待5秒
print(f"[HOLD] Condition cleared after hold -> MONITORING")
self.state = State.MONITORING
# 超时未解决 -> 紧急停止
elif time.time() - self.hold_start_time > self.hold_timeout:
print(f"[HOLD] Timeout exceeded -> EMERGENCY_STOP")
self.state = State.EMERGENCY_STOP
else:
print(f"[HOLD] Waiting... (elapsed {time.time()-self.hold_start_time:.1f}s)")
elif self.state == State.GO:
# 模拟通过过程,一旦发现异常立即刹车
if not safe:
print(f"[GO] Danger detected during crossing -> EMERGENCY_STOP")
self.state = State.EMERGENCY_STOP
else:
print(f"[GO] Crossing safely...")
# 模拟通过完毕
if random.random() < 0.1: # 10%概率到达对面
print(f"[GO] Arrived at destination. Resetting.")
self.state = State.INIT
elif self.state == State.EMERGENCY_STOP:
print(f"[EMERGENCY_STOP] Vehicle stopped. Manual reset required.")
# 在实际系统中不能自动退出,这里模拟5秒后手动恢复
time.sleep(5)
self.state = State.INIT
# 运行10步看看
agent = CrossingAgent()
for i in range(10):
print(f"\n--- Step {i+1} ---")
agent.step()
time.sleep(1)
运行说明:这段代码中有意加入了 20% 的传感器故障(永远返回安全)。你可以调整
random.random() > 0.2的值,观察 Agent 是否会在危险信号缺失时错误地进入 GO。你会看到,由于时间戳校验和 HOLD 超时机制,即使传感器短暂失效,Agent 也不会立刻闯过,而是先进入 HOLD 等待。这是安全停止策略的核心价值。
给开发者的行动建议
- 在 Agent 的每个安全关键 Action 前增加一个“确认门”:不要依赖神经网络输出 logits 阈值,而是用一个硬编码的状态机来做最终决策。
- 为每个感知输入加上时间戳、置信度元数据:即使输入不是来自传感器,而是来自 API 调用,也要考虑数据的新鲜度。
- 设计一个独立的“安全监控器”:它和主 Agent 共享传感器数据但使用不同逻辑(例如简单的规则),当主 Agent 意图不安全时,它能覆盖执行层。
- 始终让默认状态是“停止”:如果 Agent 不确定,它应该停着,而不是前进。这在 LLM Agent 的工具调用里也适用——如果某个工具返回结果置信度低于阈值,应该暂停并请求确认,而不是盲目执行下一步。
最后,记住:安全不是用更多的数据训练就能得到的,它需要被显式设计进系统架构。 下一次你设计 Agent 时,请为“如何优雅地失败”留出至少一半的思考时间。