大语言模型(LLM)调度代理无视元数据,毁了高管评审

Towards AI - Medium 2026-08-18T01:24:58.271496

我们把公司会议室的日历管理交给了一个AI代理。一个被忽略的元数据字段,直接引发了一场混乱。

我原本信任这个自主AI代理来处理公司日历,结果它在高管团队面前让我们工程团队丢尽了脸。周二早上9点,产品副总裁带着七位外部合作方走进A会议室,准备开季度战略评审会。结果看到两个技术员站在梯子上,天花板吊顶被拆了下来,正跨过梯架穿放cat6网线。这间会议室三周前就被标记为不可用,可AI代理还是把一场十二人的高管会议直接排进了维护窗口期。

这次重复预订让我们浪费了两个小时的高管准备时间,一次供应商会议被迫改期,还带来了一场难熬的事后复盘。问题根源在于,这个调度代理的工具调用架构存在一个根本性缺陷:模型忽略了关键的事件标签,只看时间戳,并根据片面的假设做判断,结果在真实环境中出了大乱子。

事故经过:一间关闭的会议室,两场会议撞在一起

这次故障的导火索简单得让人掉以轻心。三周前,设施团队在日历上创建了一个占用块,把A会议室预留给网络布线升级。这个占用块持续四小时,从早上8点到中午12点,并且在设施管理系统中明确标记为“设施封闭”。

一位高级经理通过我们内部的Slack机器人,要求找一个9点可供八人使用的安静空间。这个代理随即评估了日历:它扫描了目标时间窗口,选中了A会议室,然后执行写操作,确认了预订。当八位高管走进这片施工区时,维护团队已经开工四十分钟了。

在日历界面上,维护事件明明显示为一个醒目的红色色块,标题写着“设施维护”。但AI代理看到的不是红色色块——它看到的是原始JSON数据,并错误地套用了自己的选择逻辑。

拆解故障:只看时间,不看类型

我的初步调查集中在代理的工具模式和API集成日志上。

这个代理用的是一种函数调用架构,靠内部 REST API 查询日历事件。收到查找会议室的请求后,模型执行了一次 get_available_rooms 函数调用。该函数的参数结构接收 start_timeend_time 两个参数,用来过滤已有的日历条目。数据库查询返回的是代表这条维护时段的原始事件对象,payload 里带 start_timeend_timeattendeesevent_type 这几个字段。

{
  "event_id": "maint_90812",
  "start_time": "2026-07-14T08:00:00Z",
  "end_time": "2026-07-14T12:00:00Z",
  "attendees": [],
  "event_type": "facility_closure",
  "summary": "HVAC and Cabling Maintenance"
}

模型解析了 JSON 响应,取出 start_timeend_time 的值,接着检查 attendees 数组。问题就出在这里:这条维护时段是自动化脚本创建的,没有填写任何个人用户的邮箱地址,所以 attendees 数组完全是空的。模型把空的 attendees 数组当成了「未确认」或「仅供参考」的条目,而不是一次硬性的物理占用。它只看了时间字段,算了算时间密度,却漏掉了 event_type 标签——这个标签明明标注着 facility_closure(设施关闭)。最终代理得出结论:该时段没有人类用户被标记为忙碌,所以会议室是空的。

各系统组件的故障点

工具查询模式(Tool Query Schema)
- 预期系统逻辑:按时间范围过滤事件,并校验事件状态标志
- 实际执行路径:仅查询 start_time 和 end_time 参数
- 根本故障机制:函数参数中遗漏了 event_type 字段要求

上下文解析器(Context Parser)
- 预期系统逻辑:将完整的 JSON 元数据负载传递给模型上下文
- 实际执行路径:剥离自定义标签以尽量降低上下文令牌用量
- 根本故障机制:在提示词组装前删除了 facility_closure 标签

重叠评估器(Overlap Evaluator)
- 预期系统逻辑:若任何事件占用目标时间段,则阻止预订
- 实际执行路径:仅检查人类与会者的日程是否被占用
- 根本故障机制:将空的与会者数组视为物理可用时段

提交处理器(Commit Handler)
- 预期系统逻辑:在执行数据库写入前强制硬约束检查
- 实际执行路径:直接基于模型输出执行日历写入调用
- 根本故障机制:LLM 输出与 API 写入之间零确定性校验

我们的提示词上下文清理器是第二个故障点。为了节省上下文窗口令牌并降低 API 延迟,一个上游中间件服务在将日历负载传入模型上下文窗口之前,会移除其中的元数据属性。这个清理器删除了 event_type 和 facility_closure 等自定义键值对,同时保留了基本的时间结构。于是模型收到的是一个被截断的负载,只有时间戳和一个空的与会者数组。它完全无法在结构上看出房间实际上不可用。下游的 API 执行层在预订时没有验证房间状态。系统的假设是:只要 LLM 生成了带有效时间参数的工具调用,就说明模型已经成功验证了所有业务逻辑约束。这个假设被证明错得离谱。

根本原因分析:令牌优化与系统准确性的取舍

核心原因直接指向一个激进的令牌优化策略,该策略将成本置于正确性之上。

为了优化系统延迟,我们尝试压缩日历载荷的 schema,把 token 占用削减了 30%。但这样一来,元数据键从上下文窗口中被剥离,系统状态变成了代理在基于残缺信息运行。模型只能对截断的 JSON 结构做统计模式匹配,而不再依据显式的业务逻辑规则作判断。

当显式的元数据标签从上下文里被移除后,AI 代理根本推断不出那些隐含的边界条件。代理收到一个时间窗口,里面的事件没有与会者,而提示词指令告诉它要检查日程冲突。它没有发现冲突,因为没有任何用户日程受影响。于是它把设施维护时间段当成了普通空档。模型非常自信地执行了任务,生成了成功响应,最后把八位高管送进了一间布满灰尘、电线悬垂的屋子。

修复方案:Schema 强制约束与硬校验

修复这个 bug,需要同时调整上下文序列化管线和 API 授权架构。

我先改掉了上下文清理服务,让它保留所有业务元数据标签。像 facility_closure(设施关闭)、out_of_office(不在办公室)、equipment_maintenance(设备维护)这类自定义属性,现在会显式保留在传给模型的载荷中。

工具 schema 定义现在也明确把 event_type 列为评估日历可用性时的必填参数:

```json
{
"name": "check_room_availability",
"description": "Evaluates room availability including facility closure flags",
"parameters": {
"type": "object",
"properties": {
"room_id": {
"type": "string"
},

当代理尝试预订房间时,一个 Python 中间件脚本会先截获它的输出。该脚本会执行一次强制的 SQL 检查,确认没有 event_type = 'facility_closure' 的事件与请求的时间段重叠。一旦校验脚本发现冲突,就会自动阻止数据库写入,并向代理上下文返回错误代码。模型会自行重新评估其他可用房间,无需用户介入。

只要有 API 调用可能改动生产数据,就永远不要依赖 LLM 来强制实施严格的逻辑约束。必须在模型输出和运营基础设施之间设置确定性的安全护栏。

免责声明:内容仅供参考,不构成专业建议。

本文最初发表于 Medium 的 Towards AI 专栏,读者可在此继续讨论和回应这个故事。

查看原文