
1. 从一次深夜排障说起为什么能查到数据和查得可信是两回事凌晨两点值班群里弹出一条消息看板上的支付成功率掉了3个点但日志里查不到任何异常。我打开查询界面输入支付失败返回了八千多条记录时间跨度横跨三天字段里混杂着测试环境的脏数据、被重试机制吞掉的中间态、还有几个明显是上游埋点写错的枚举值。那一刻我意识到问题不在于查不到而在于查出来的东西根本没法信。这个场景几乎每个做数据平台的人都遇到过。我们花了大量精力把日志采集做稳、把存储做便宜、把查询做快却很少认真回答一个更根本的问题当一个人或者一个 AI Agent向系统提问时系统凭什么让提问者相信返回的结果是对的这次排障之后我们做了一次范式上的调整核心动作是给查询链路引入一层业务字典。这篇文章不聊虚的我想把这次升级的完整思路拆开讲清楚业务字典到底是什么、它怎么改变 AI 参与数据查询的方式、我们在落地时踩了哪些坑、以及为什么我认为可信这件事必须被显式建模而不能指望模型自己悟出来。如果你正在做可观测性平台、数据查询工具或者正在尝试把大模型接进内部数据系统这篇内容应该能给你一些直接能用的参考。关键词会围绕AI、业务字典、数据查询、SLS、DataAgent这几个点展开但重点始终落在怎么让查询结果可信这个实际问题上。2. 业务字典不是数据字典它解决的是语义对齐而非字段说明2.1 大多数人把业务字典理解错了一提到字典很多人的第一反应是数据字典——记录表结构、字段类型、注释、负责人。这东西当然有用但它解决的是这个字段叫什么而不是这个字段在业务上意味着什么。这两者之间的差距恰恰是排障时最要命的地方。举个具体的例子。日志里有个字段叫status数据字典会告诉你它是string类型、长度 32、允许为空。但业务字典要回答的是status0到底代表成功还是未开始status3是失败还是已退款在支付链路里status3可能同时出现在用户主动取消和风控拦截两种完全不同的场景中而这两种场景的排查方向截然相反。所以我对业务字典的定义是它是把机器可读的原始数据映射到人类业务语义的一层显式声明。它包含枚举值的业务含义、字段之间的业务约束、指标的计算口径、以及不同数据源之间的等价关系。2.2 业务字典的四个核心组成在落地时我们把业务字典拆成了四块内容每一块都对应一类具体的查询困惑组成模块解决的问题典型内容枚举映射值到含义的翻译status 码表、错误码分级、渠道编码口径定义指标怎么算成功率分母是否含重试、耗时是否含排队实体关系谁属于谁订单与支付单、请求与链路、用户与设备数据源边界哪份数据可信生产与测试隔离、采样与全量区分这四块里口径定义是最容易被忽略、也最容易引发争议的。我们曾经因为成功率的分母定义不一致导致两个团队对着同一份数据吵了一整天——一个把重试算进去一个不算。后来把口径写进业务字典查询时强制引用这类争论基本消失了。2.3 为什么业务字典必须显式存在有人会问这些东西让老员工记住不就行了为什么要专门建一层原因很简单隐式知识无法被 AI 使用也无法被审计。当查询是由人发起时老员工脑子里的经验确实能兜底但当查询由 AI Agent 发起时模型只能看到它被喂进去的上下文。如果业务语义只存在于人的脑子里模型就只能靠猜而猜出来的结果在排障场景下是灾难性的。更重要的是显式的业务字典让可信变得可验证。当一条查询结果返回时我们可以追溯它用了哪个口径、命中了哪些枚举映射、排除了哪些数据源。这种可追溯性是可信从感觉变成事实的关键一步。3. 当 AI 接入查询链路DataAgent 到底该拿到什么上下文3.1 裸接大模型的三种典型翻车我们最早尝试把大模型直接接到查询接口上让它根据自然语言生成查询语句。结果翻车翻得很整齐基本可以归为三类第一类是枚举幻觉。用户问昨天有多少支付失败模型自己编了一个statusfailed的条件但实际系统里根本没有这个值真实失败态是status in (3, 7, 9)。查询返回零结果模型还很自信地说昨天没有支付失败。第二类是口径漂移。同一个成功率模型这次用了含重试的口径下次用了不含的两次结果对不上用户直接失去信任。第三类是数据源越界。模型不知道测试环境和生产环境的区别把压测数据也算了进去指标直接失真。这三类问题的共同点是它们都不是模型能力问题而是上下文缺失问题。模型再强也不可能凭空知道一个内部系统的枚举约定。3.2 DataAgent 需要的不是更多数据而是更准的语义很多团队的第一反应是那就多喂点数据给模型。但实测下来喂更多原始数据只会让模型更困惑——它会在海量字段里迷失反而更容易编造。正确的做法是给 DataAgent 喂结构化的业务语义也就是前面说的业务字典。具体来说Agent 在生成查询前应该拿到这样一份上下文当前查询涉及哪些实体这些实体的枚举字段有哪些合法取值用户提到的指标其标准口径是什么可用的数据源有哪些各自的可信边界在哪历史相似查询是怎么写的用了什么条件这份上下文不需要很大但必须精准。我们实测发现把业务字典以结构化形式注入后枚举幻觉类错误下降了绝大部分口径漂移基本消失。3.3 一个具体的注入示例下面是我们实际使用的一段上下文注入结构用 JSON 描述方便 Agent 解析{ entity: payment_order, enum_fields: { status: { 0: 初始化, 1: 处理中, 3: 支付失败-渠道拒绝, 7: 支付失败-风控拦截, 9: 支付失败-超时, 10: 支付成功 } }, metric: { success_rate: { numerator: status10, denominator: status in (0,1,3,7,9,10), exclude: is_retrytrue, note: 分母不含重试单与财务口径一致 } }, data_source: { prod: 生产全量可信, test: 测试环境查询时须排除 } }这段结构看起来朴素但它把失败这个模糊词精确映射到了status in (3,7,9)把成功率的分母口径写死把数据源边界标清楚。Agent 拿到这份上下文后生成的查询语句质量是质的飞跃。提示业务字典的注入不要一次性全量塞进去而是根据用户 query 先做一轮实体识别只注入相关部分。全量注入会稀释注意力反而降低准确率。4. 可信查询的三道防线从生成到返回的完整校验链4.1 第一道防线生成阶段的约束校验Agent 生成查询语句后不能直接执行要先过一道约束校验。这道校验的核心是检查生成的语句是否违反了业务字典里的硬约束。我们实现的校验规则包括枚举字段的取值是否都在合法集合内、指标计算是否引用了标准口径、查询是否触碰了被标记为不可信的数据源。任何一条不通过就把错误信息回传给 Agent让它重新生成。这一步的价值在于把错误拦在执行之前。之前模型编造statusfailed时查询会正常执行并返回零结果用户看到的是没有失败这是最危险的——错误被伪装成了正确答案。有了约束校验这种语句根本执行不了Agent 会收到status 无 failed 取值合法值为 0/1/3/7/9/10的反馈然后自我修正。4.2 第二道防线执行阶段的边界控制查询真正执行时还有一层边界控制。这层主要处理数据源隔离和结果规模控制。数据源隔离很好理解生产查询只能命中生产数据源测试数据源在查询计划阶段就被排除。结果规模控制则是防止 Agent 生成一个全表扫描的查询把系统拖垮——我们设置了单次查询的扫描行数上限超限的查询会被拒绝并要求 Agent 收窄条件。这里有个实操细节值得说边界控制不要做成静默截断。早期我们试过对超限查询自动加limit结果用户拿到的是部分数据却以为是全量判断直接跑偏。后来改成显式报错让 Agent 和用户都知道查询被限制了反而更可信。4.3 第三道防线返回阶段的口径标注查询结果返回时我们强制附带一段口径说明。这段说明告诉使用者这个结果用了什么口径、排除了什么、数据源是什么、查询时间范围是什么。查询结果支付成功率 97.2% 口径分母不含重试单与财务口径一致 数据源生产全量 时间范围2024-XX-XX 00:00 至 24:00 排除条件is_retrytrue, envtest别小看这段标注。它让可信从一种主观感受变成了可核对的事实。用户看到结果时能立刻判断这个口径是不是自己要的。如果口径不对他可以明确要求换口径而不是对着一个数字猜来猜去。4.4 三道防线的协同关系这三道防线不是孤立的它们构成一条完整的链路防线阶段拦截的问题失败后的动作约束校验生成后枚举幻觉、口径漂移回传错误Agent 重生成边界控制执行时数据源越界、资源滥用拒绝执行要求收窄口径标注返回时口径不透明、误读附带说明供人工核对实测下来这条链路把不可信结果的漏出率压到了很低的水平。更重要的是它让整个查询过程变得可解释——每一步为什么这么做都有据可查。5. 落地过程中踩过的坑业务字典维护的四个现实难题5.1 字典谁来维护怎么保证不过期这是落地时第一个撞上的问题。业务字典听起来美好但谁来写、谁来更新如果靠人工维护用不了多久就会和实际系统脱节。我们的做法是分层维护枚举映射这类和代码强相关的内容从代码里的常量定义自动抽取代码变了字典跟着变口径定义这类偏业务的内容由数据团队维护但每次修改都要走评审实体关系从元数据系统同步。这样把必须人工维护的部分压到最小。即便如此仍然会有滞后。我们的兜底策略是查询时如果发现字典里的枚举值和实际数据对不上自动告警并记录。这条告警帮我们抓到过好几次字典过期的情况。5.2 口径冲突怎么裁决不同团队对同一个指标往往有不同口径这在业务字典建设时是绕不开的。我们的原则是一个指标只能有一个标准口径其他口径作为变体存在但必须显式命名。比如成功率是标准口径含重试成功率是变体。查询时默认用标准口径要用变体必须显式指定。这样既避免了默认口径的混乱又保留了灵活性。5.3 字典粒度怎么把握字典太粗起不到约束作用太细维护成本爆炸。我们的经验是只对会引发歧义的部分建字典。一个字段如果取值唯一、含义明确就不需要进字典只有当它存在多义、或者不同人理解不一致时才值得建。判断标准很简单如果这个字段曾经引发过一次以上的沟通成本它就值得进字典。5.4 怎么衡量字典有没有用我们用了两个指标一是查询返工率即用户对结果不满意、要求重新查询的比例二是口径争议次数即因为口径不一致引发的讨论。引入业务字典后这两个指标都有明显下降。这比任何主观评价都更有说服力。6. 从 SLS 到 DataAgent一次查询请求的完整生命周期6.1 请求进入意图识别与实体抽取用户或上游系统发来一个自然语言查询比如看下昨天支付成功率为什么掉了。请求进入后第一步是意图识别和实体抽取识别出这是一个归因类查询涉及实体支付订单指标成功率时间范围昨天。这一步的输出是一份结构化的查询意图而不是直接生成查询语句。把意图和语句生成分开是为了让业务字典的注入有的放矢。6.2 上下文组装精准注入业务字典根据抽取出的实体和指标从业务字典里取出相关部分组装成 Agent 的上下文。这一步的关键是只取相关的。查询涉及支付订单就只注入支付订单的枚举和口径不要把用户、商品那些无关的字典也塞进去。组装好的上下文里除了业务字典还会带上几条历史相似查询作为参考。历史查询的价值在于给 Agent 提供这个系统里大家习惯怎么写的范例进一步降低生成偏差。6.3 查询生成与校验Agent 与约束层的往返Agent 拿到上下文后生成查询语句交给约束层校验。校验不通过就回传错误Agent 修正后再次提交。这个往返通常一到两轮就能收敛。这里有个经验错误信息要具体。不要只说查询非法而要告诉 Agentstatus 字段无 failed 取值合法值为 0/1/3/7/9/10。具体的错误信息能让 Agent 一次修正到位模糊的错误信息会让它反复试错。6.4 执行与返回结果附带完整口径校验通过后查询在 SLS 上执行。执行结果返回时附带前面说的口径标注。整个链路的每一步都留痕方便事后审计。6.5 反馈闭环让字典越用越准每次查询结束后我们会收集用户反馈结果是否符合预期、口径是否正确。如果发现字典有问题就触发字典更新。这样业务字典不是一成不变的而是随着使用不断校准。这个闭环是整套方案能长期运转的关键。没有反馈闭环字典迟早会僵化有了闭环它就是一个活的、会自我修正的系统。7. 我对可信数据查询的一点个人理解做完这次升级我最大的体会是可信不是一个技术指标而是一种被设计出来的关系。它建立在查询系统和提问者之间的契约上——系统承诺用明确的口径、合法的枚举、可信的数据源来回答提问者则能核对这个承诺是否被兑现。AI 的加入让这件事变得更紧迫因为 AI 不会像人一样对模糊结果保持警惕它会自信地把错误答案呈现出来。所以当 AI 参与数据查询时业务字典和校验链路不是可选项而是必需品。最后分享一个我们内部一直在用的小原则任何一条查询结果如果无法说清它的口径和数据源就不应该被展示。这条原则听起来苛刻但它逼着我们把可信落到每一个细节上。踩过几次坑之后你会发现这种苛刻反而是最省事的做法——因为它把问题拦在了源头而不是等到用户拿着错误数据做了错误决策之后再去补救。