你的 Agent 读不懂的生产日志
你把事故响应 agent 接入了 Splunk。你在系统提示里给了它查询语法,给了它执行 SPL 的工具,还有一个新鲜的 API token。第一次真正处理告警时,它拉了错的日志,总结了错的服务,信誓旦旦地报了错的客户。集成做得完美无缺,agent 却一文不值。
你忘了什么。十五年的日志惯例、没文档的字段名、跨越三次重组从 ERR 漂移到 error 再到 ERROR 的告警级别字符串、把 customer_id 在认证服务里变成 cust_id_v2_actual、在计费服务里变成 tenant.user.id 的团队特定后缀——这些东西没有一条出现在 prompt 里。你给了 agent 对 API 的访问权,但你没有给它访问那些让 API 变得有用的机构知识的权力。
这种失败的形状比 Splunk 大得多。任何把查询语言暴露给 agent、而底层语料是团队手工塑造了十年的工具,都会撞上这堵墙。Agent 拿到了动词,没拿到名词。
有访问权,不等于能用上
"agent 能调这个工具"和"agent 知道该问什么问题"之间有一个相变。大多数生产集成只走到第一步,然后假设第二步是顺带免费的。
不是。典型 MCP 服务器上的工具文档只发布名字、描述、输入 schema。这足够让模型知道怎么格式化调用,但不够让它知道该问什么。Schema 告诉你 query 是字符串,它不告诉你索引里有哪些字段、哪些字段填得稳、哪些字段在 2021 年就废弃了但仍出现在 30% 的记录里,也不告诉你哪个团队拥有那条"级别 4 表示警告——除了在遗留 ingestion 管道里它表示致命"的惯例。
这正好是一半的集成。你交付的那一半是 API 契约——调用形式、参数类型、响应格式。你跳过的那一半是语料契约:数据里实际有什么、它的字段是什么意思、一个可被回答的问题长什么样。
Text-to-SQL 团队多年前就撞过这堵墙,并给它起了名字。即使最强的模型,在真实企业 schema 上的复杂查询准确率也会跌到 77% 以下,绝大多数失败都可以追溯到缺失的语义上下文:列的含义、业务定义、合法的过滤值、那种社交意义上存在但在外键图里不存在的关系。在同一个底层数据库上,加一层显式编码这些上下文的语义层,可以把准确率拉过 99%。模型没变聪明。是语料变得可读了。
日志比 SQL 更糟糕。SQL 的 schema 至少有带类型的列。日志是带嵌入式 JSON 的自由文本——是某个人 2019 年半结构化了一下就忘了收尾的东西。
隐性知识藏在你的技术栈里
走进真实事故时的 on-call 频道,看会发生什么。一个资深工程师 8 秒钟就敲出一条 Splunk 查询。里面有四个你在任何文档里都没见过的字段名。它对一张你根本不知道存在的查找表做 join。它根据一个魔法字符串过滤——那是一个已废弃服务的名字,数据管道为了向后兼容还在给行打这个标签。
那条查询是十年模式匹配的产物。这个工程师是通过写 4000 条查询、被骂错、慢慢累积出"我被问到的问题"和"在这套语料上真正回答它的 SPL"之间的私人映射,才学会的。没人把那张映射写下来。它存在于他和另外三个人的脑子里,其中两个已经离职了。
Meta 上个月发了一份内部分析,讲他们尝试把一个 AI 编码助手从事故模式匹配扩展到更广的开发任务时发生了什么。第一类任务系统能跑,因为模式已经被标注过——事故报告、根因、修复 diff。第二类任务它失败了,因为没人标注过那些惯例。两种配置模式对同一个逻辑操作用了不同的字段名。废弃的枚举值必须留着,因为序列化兼容性悄悄地依赖它们。团队找到了五十多个非显然的隐含中间命名惯例,公司里没有任何文档描述过它们。
你的日志看起来就是这样。不是糟糕工程的产物,而是你团队曾承受过的每一次发布压力沉积下来的地质层。Agent 看不见地质。它看到的是一个平面,并假设手册里写的字段就是数据里填充的字段。
工具表面里应该有什么
如果你接受"访问权只是集成的一半",那另一半也有名字:语料契约。它是工具表面的一等公民,和调用形式并列。
一个有用的日志查询工具的语料契约,包含模型不然就得猜的那些东西:
- 可被查询的 schema 描述。 不是正式的索引定义。是被标注过的那一份——哪些字段存在,哪些填得稳,哪些是稀疏的、为什么,哪些在不同服务里意思不同,哪些是遗留下来的化石。标注本身才是价值所在。
- 样例问题库。 二十到五十对"自然语言问题"和"在这套语料上真正回答它的 SPL 查询"。这就是把 text-to-SQL 准确率拉上去的同一个把戏:在上下文里给模型本地的方言示例,而不是让它去发明一个。挑那些故意触发字段名陷阱的例子。
- 魔法字符串词表。 服务名、环境标签、级别值、租户标识符——这些在数据里以原始字符串形式出现的东西。Agent 不会从"生产"猜出 "prod-us-east-1-legacy"。告诉它。
- 不在索引里的查找和 join。 如果工程师每次都跨表查
cmdb_hosts.csv拿到团队 owner,那次查找就是答案的一部分。把它做成一个可调用的工具,或者直接内联进去。 - 反模式。 "不要用
host字段做服务归属问题的过滤,那字段 2022 年就被弃用了——用service.owner。" 负面例子很便宜,效果非常好。
这里的模式,和 Splunk 现在为自己的 LLM 集成所固化的模式是同一个:get_splunk_fields 拿到某个 source type 的字段发现,get_splunk_lookups 暴露查找表,以及在工具定义里加入对每个索引的描述,详细写明它的字段和值。微软的 Azure Copilot 可观测性 agent 也明确警告:调查的准确率取决于你的应用是否发出了完整的遥测、保留了关联字段、附带了足 够的服务上下文。各家厂商正在收敛到同一个答案:工具表面必须发布"数据叫什么",而不只是"怎么查它"。
没人想做的那次审计
试试这个练习。挑出你 on-call 轮值里事故中最常被问的十个问题。对每一个,让一位资深工程师把他会跑的那条查询逐字写下来。然后让一位初级工程师只用你写下来的文档和 wiki 写出同一条查询。
那个 gap,就是隐性知识债。也几乎正好,就是 agent 会掉进去的那个 gap。
发现会让人不舒服。你会发现真正装着客户 ID 的那个字段上个季度改过了,仪表板还能用,只是因为有人加了一个 coalesce。你会发现"errors"在一个服务里指异常,在另一个服务里指失败的健康检查。你会发现资深工程师做 join 时用的那张查找表,是某人家目录里的一个 CSV,从一个 2023 年退役的系统里手动导出来,需要谁记起来时才刷新一次。
你大部分可观测性栈,跑在这种知识上。仪表板能用,是因为造它们的人记得那些 workaround。Runbook 能用,是因为写它们的人就是读它们的人。这些东西没有一样能熬过一个把 schema 文档当真的全新模型。
你不可能在这种语料之上直接交付一个 agent,而不先把隐含的东西显式化。审计就是工作本身。Agent 只是把它逼出来的那股力。
