资讯动态

数仓Agent实践:基于MCP+Skill实现全链路口径查询系统

发布时间:2026/9/15 2:41:53 来源:尧图企业网站定制
做了三年数仓我发现自己干得最多的一件事不是写ETL而是回答同一类问题GMV到底含不含退款日活是去重还是不去重你邮件里说的支付金额口径怎么和运营报表对不上。这些问题背后就是数仓里最磨人的一个词——口径。后来我动了用Agent来做全链路口径查询的念头架构上选了Agent MCP Skill这套组合Agent负责理解问题和编排流程MCP把数仓的元数据、指标字典、口径文档暴露成标准工具接口Skill把怎么查口径、怎么比口径、怎么回答口径沉淀成领域技能包。跑通之后我们组每周被口头轰炸的场景明显少了。这篇文章把设计思路、落地细节和踩坑记录完整写出来给正在做数仓Agent、BI智能问答或者刚接触MCP和Skill开发的同学一个可以照着搭的参考。1. 为什么数仓Agent的第一个落地场景我选了口径查询1.1 数仓团队每天被同一个问题轰炸先还原一个我特别熟悉的场景。某个下午运营同学在群里问最近30天活跃用户怎么算的我打开检索工具翻了半天文档把指标定义复制进去又在IM上解释了三遍它和日活的区别。没过两小时同样的口径问题被换了个措辞又问了一遍。这种事情在数据团队是常态指标字典散落在语雀、Excel、代码注释和多套数仓建模文档里有的口径甚至是几个老同事脑子里的潜规则。我们当时统计过核心指标大概有两百多个加上维度和派生指标超过八百个。其中用户数这类高热度指标不同部门能给出四五种说法注册用户数、下单用户数、支付用户数、活跃用户数、去重设备数。每种说法对应不同的来源表、不同的过滤条件、不同的统计窗口。业务方要的是到底按哪个口径算数据团队要的是别再让我人工翻文档了。这就是我决定做口径查询系统的直接动机。1.2 为什么不先做自然语言生成SQL很多团队一提到数仓Agent第一个想做的都是让业务直接问自然语言Agent帮忙生成SQL跑数。这个方向看着香落地却非常容易翻车模型对表结构不熟悉时会编造不存在的字段名多表join的语义稍微绕一点生成的SQL就是错的再加上数据权限和成本控制根本不敢让一个不可靠的Agent直接去调度生产查询引擎。口径查询这个场景天然更适合做Agent的第一个切入口。它本质上是检索 阅读理解 对比溯源而不是生成可执行代码。这类任务只读、低风险、有标准答案答案还能回溯到具体文档、具体字段、具体负责人。就算Agent答错了业务拿着我们给出的来源文档去核对马上能定位问题。对比之下SQL生成答错一个过滤条件可能就是一次错误的数据发布事故。先做口径查询能用可控的风险把Agent链路完整历练一遍后面再往SQL生成上延伸底盘也是稳的。1.3 口径查询带给团队的额外红利这个项目上线后还有个很多人没想到的副作用为了给Agent喂数据我们被迫把所有口径文档标准化了。以前文档里同一句话能写出七八种写法现在统一成了指标名称、口径定义、计算公式、统计粒度、来源表、变更记录的结构化条目。Agent吃掉的是结构化知识团队获得的是一份随时可查的在线口径手册。新同事入职不用再花三天啃历史文档大促前核对口径也不用拉上三个老员工开电话会。这套元数据沉淀下来BI报表、数据治理、离线任务调度都能复用属于一次建设、多处受益。所以我说口径查询不是一个小工具它是数仓知识资产化的起点。2. 三层各管哪一段Agent、MCP、Skill的职责边界2.1 先给没接触过MCP的同学补个底聊架构之前先花一小段说清楚MCP到底是个啥。MCP全称Model Context Protocol是Anthropic在2024年底开源的一套模型上下文协议目的很简单把大模型和外部工具、数据源之间的连接方式标准化。以前接一个工具就要写一套胶水代码Agent框架换一个又要重新适配一遍MCP把工具调用、资源读取、提示词模板统一成了JSON-RPC 2.0接口用类似AI界的USB-C的方式让同一个MCP Server可以被任意支持MCP的客户端调用。有人问MCP和写个REST API让Agent调有什么区别区别在于标准协议带来的生态价值。用MCP工具的描述格式、入参校验、资源定位都是标准化的官方SDK直接帮你处理了协议细节还能在MCP Inspector这类调试工具里直观地查看工具调用过程。对我们数仓团队来说最实际的好处是这套元数据服务做出来未来接Claude、接其他模型、接IDE插件都能复用不用因为换前端重写一遍对接逻辑。2.2 我的职责切分方式这套架构里三个角色我这么定义Agent是大脑负责理解用户意图、拆解任务、决定调什么工具、把多步结果拼装成最终答案MCP是标准的接口层负责把数仓里的表结构、字段注释、指标字典、口径文档、血缘关系全部暴露成Agent可调用的工具面Skill是领域技能包负责把怎么查口径才准这件事固化成可复用的流程和规则。打个比方Agent是司机MCP是车上统一的标准化接口Skill则是司机手里那本针对特定路线打磨过的导航手册。接口决定了你能接什么资源手册决定了你在复杂路况下怎么开才不出错。口径查询的难点不在于能不能搜到文档而在于能不能基于业务规则把散落的信息比对清楚这正好是Skill发挥价值的地方。2.3 MCP和Skill的边界辨析很多刚接触这套架构的同学会问MCP里也有prompts原语Skill和它到底有什么区别我踩过这个混淆的坑现在用一句话划清MCP管能查到什么Skill管怎么查得准。MCP Server里如果注册一个prompt它更像是某个标准问答场景的模板而Skill是一个完整的领域操作方法集包含触发条件、执行步骤、检查清单、兜底策略和示例甚至可以带上脚本和参考文档。举个具体例子MCP工具get_metric_detail负责把某个指标的详细信息拉回来这是能查到什么。但当搜索到多个同名指标时应该先对比版本号再输出当用户只说了用户数这种模糊概念时必须先追问而不是直接猜这类业务规则放进Skill最合适。它们不是代码逻辑而是领域知识单独改Skill包不用动MCP服务反过来也一样。我把三层之间的关系做成一张表开发时对照着看特别清晰层次职责典型内容谁维护更新频率Agent意图理解、任务编排、答案生成主提示词、路由器、反思校验逻辑应用开发随迭代MCP数据与能力接口标准化指标搜索、表结构、血缘查询等工具数仓/平台数据更新时Skill领域查询方法与业务规则匹配规则、澄清策略、答案模板数仓/业务分析口径变更时3. 元数据MCP Server设计把散落的数仓资产变成统一工具面3.1 先盘点我们到底有什么可查动手写MCP Server之前我先花了一周做资产盘点。数仓里能支撑口径查询的数据源大概分四类。第一类是指标字典也就是口径主数据包含指标唯一标识、名称、别名、业务定义、计算公式、统计粒度、默认维度、负责人、版本状态。第二类是数仓元数据来自Hive MetaStore和调度平台的表清单包含表名、表注释、字段名、字段类型、字段注释、分层信息ODS/DWD/DWS/ADS。第三类是血缘关系来自数据治理平台能追踪表与表、字段与字段之间的上下游。第四类是口径文档散落在语雀、内部wiki和历史变更邮件里这部分格式最乱有的写得像散文有的嵌在表格里需要做一次清洗和抽取。盘点完发现一个很关键的问题要是把原始数据直接交给Agent上下文根本装不下。所以我在MCP Server前面加了一层元数据索引服务把原始内容预先消化成结构化条目MCP工具返回的是加工后的摘要而不是一整个原始文档。这一步是整个架构里性价比最高的设计。3.2 工具面设计我最终给MCP Server设计了七个核心工具全部围绕查得全、查得准展开工具名作用关键入参返回内容search_metric_candidates关键词搜索候选指标keywords, biz_domain, top_k指标名、口径摘要、来源表、状态get_metric_detail获取单个指标完整定义metric_id定义、公式、粒度、维度、变更记录compare_metric_variants对比多个指标口径差异metric_ids字段级差异对照表get_table_schema获取表及字段信息table_name字段名、类型、注释、分层get_model_lineage获取表/字段血缘entity_name, entity_type上下游表、字段映射search_dimension_dict查询维度字典keyword维度定义、枚举值、来源get_metric_change_log查询指标口径变更历史metric_id版本、生效时间、变更说明每个工具都用JSON Schema声明入参这是MCP协议的标准做法。以最常用的指标搜索为例{ name: search_metric_candidates, description: 根据关键词搜索数仓候选指标返回指标名称、业务口径摘要、所属域、来源表。回答口径、定义、怎么算、如何统计等问题时优先调用。支持中文名、英文缩写、别名。, inputSchema: { type: object, properties: { keywords: { type: string, description: 指标关键词如 GMV、日活、支付金额 }, biz_domain: { type: string, enum: [交易, 用户, 内容, 广告], description: 可选业务域过滤 }, top_k: { type: integer, default: 5, description: 返回条数上限 } }, required: [keywords] } }服务本身我用FastMCP的Python SDK写原理上是把上面的工具定义注册进来函数体里去查索引服务。想造轮子的同学注意MCP SDK已经帮你处理了JSON-RPC的传输细节不需要自己写协议层workdir就是开发者体验最顺的一条路。3.3 工具描述怎么写才不会被模型用错这一小节必须单独拿出来说因为工具描述直接决定Agent会不会调错工具。大模型选函数靠的就是工具名和描述文本描述写得不清楚Agent就会瞎猜。我踩过的例子是第一次上线时get_table_schema的描述只写了获取表结构结果用户问某个字段的含义Agent居然也调它返回了一堆字段名根本答不到点子上。我的写法有两个要点。第一description里把所有可能的同义词和典型问法都埋进去比如指标搜索的描述里加上口径、定义、怎么算、如何统计、DAU、GMV这些词让模型在语义匹配时更容易选中。第二命名统一采用动词加名词的形式search_*表示搜索、get_*表示取详情、compare_*表示对比模型对这类命名模式的学习成本很低。每次改动工具描述后我都会拿着一个固定的评估问题集跑一遍回归看工具调用准确率有没有变化。3.4 服务落地细节元数据索引服务的构建我做成了一套离线流程每天凌晨从MetaStore、指标管理平台、文档系统做一次全量拉取加增量的更新清洗后写入一个向量索引加倒排索引的混合检索服务。之所以做两套索引是因为口径查询既有精确匹配需求比如gmv_30d这种指标ID又有语义近似需求比如最近三十天的成交额要找GMV。精确匹配走倒排索引兜底语义近似走向量召回两个结果做一次分数合并Top-K返回给MCP工具。MCP Server本身是独立进程通过stdio和Agent端通信。我在启动脚本里加了日志落盘和指标上报每个工具调用的耗时、入参、返回条数都记下来。刚开始调试时这些日志帮了大忙很多Agent行为问题不看调用记录根本定位不到。4. Skill包拆解口径查询的领域知识怎么沉淀成可复用技能4.1 Skill的标准目录结构Skill本质上是一个可以被动态加载的能力包。我参照目前社区主流的Agent Skill规范把口径查询技能包组织成下面这个结构metric-lookup-skill/ ├── SKILL.md ├── references/ │ ├── metric-classification.md │ ├── alias-map.csv │ └── ambiguity-policy.md ├── prompts/ │ ├── compare-variants.md │ └── clarify-question.md └── examples/ ├── gmv-vs-pay-amount.md └── dau-query.mdSKILL.md是整个包的入口头部用YAML frontmatter写元信息正文写执行方法。我维护的版本简化出来是这样--- name: metric-lookup description: 数仓指标口径查询技能。当用户询问指标定义、计算逻辑、口径差异、指标来源、历史口径时触发。典型问题词含口径、定义、怎么算、如何统计、区别、DAU、GMV、转化率。 version: 1.3.0 ---description字段是最重要的因为Agent要凭这段文字决定要不要激活这个技能。我把触发条件写得非常具体宁长勿短再加一条不是此类问题不要调用的反向说明能有效减少误触发。4.2 口径匹配规则笔画了一堆结构真正起作用的还是匹配规则的细节。我把口径匹配分成四档精确匹配、别名匹配、语义匹配、模糊存疑。精确匹配针对指标ID和唯一名称比如搜gmv_30d直接命中。别名匹配靠别名表alias-map.csv里维护了日活、DAU、活跃用户、活跃设备数这类关系。语义匹配走向量召回比如业务方问这个月搞了多少交易能搜到交易域相关的GMV、支付金额、订单数。最后一档模糊存疑最容易被忽略当检索结果的置信度很低或者存在多个候选口径时Skill里的规则要求Agent必须向用户澄清而不是随便挑一个答。有个小技巧值得分享业务方言和黑话一定要收集进别名表。我们组收集了钱、流水、盘子、单量、人头这类口语词匹配效果立刻上了一个档次。这个过程没有捷径就是每天看线上对话记录发现没匹配上的用户原话就补进去。4.3 版本与多口径对比口径最大的坑是同一个名字不同时间有不同算法。我们曾经有个转化率指标2024年上半年还是按点击口径算下半年加了有效点击的过滤条件这两个版本的报表在过渡期必然对不上。所以Skill里专门规定了版本处理策略回答任何指标先查变更记录命中多版本时要区分当前口径和历史口径输出绝不能把旧口径当成现状。多指标对比也是高频需求。compare_metric_variants工具返回字段级差异后Skill里的compare-variants.md提示词会引导Agent组织成差异点清单 影响说明的形式。比如GMV和支付金额差异可能出在时间窗口支付时间还是下单时间、退款是否剔除、是否包含未支付订单三个维度Agent要把这三个维度的对比逐项列清楚并给出每项的来源依据。4.4 Skill的触发与加载Skill加载方式有两种路线常驻上下文和按需注入。常驻的好处是Agent随时知道有这门技能坏处是吃token口径技能文档量一大主提示词很快就超限。我最终选择了按需注入在Agent前面放一个轻量的意图分类器判断用户问题是否命中口径查询意图命中后才把SKILL.md正文和必要的references注入系统提示词。这个分类器我用的是一个小模型加关键词规则兜底没有增加多少推理延迟但主上下文空间省了一大截。按需注入有个前提分类器漏判怎么办。我的兜底方案是在Agent主提示词里留下一行如果你发现用户问题包含口径、指标定义等含义但你没有获得metric-lookup技能请调用一个名为query_skill_requirements的MCP工具获取技能说明。这样既省了token又不至于漏掉真正的口径问题。5. 全链路跑通实录三类真实问答从提问到出答案5.1 GMV和支付金额为什么对不上这是上线后最典型的场景。业务同学原话取数组给我的GMV和支付金额上周开始对不上了帮我看看这俩指标口径啥区别。Agent的完整处理过程如下第一步意图分类器判定命中metric-lookup注入Skill。第二步Agent调用search_metric_candidates(keywordsGMV)返回三个候选其中gmv_30d状态为启用口径摘要写着支付成功订单金额按支付时间剔除退款。第三步再调用search_metric_candidates(keywords支付金额)返回pay_flow_amount摘要写着支付流水成功金额保留退款前已支付成功的流水记录。第四步Agent对两个指标分别调get_metric_detail拿到了各自的统计粒度和变更记录发现gmv_30d在2024年6月做过一次版本更新新增了剔除退款条件而pay_flow_amount从未剔除退款。最终Agent输出的答案是一个对比表逐项给出差异和来源文档链接。业务方看到退款是否剔除这一行立刻明白了对不上的原因。整个过程从提问到出答案不到一分钟以前靠人工至少要倒腾半天。5.2 查一下用户数——先澄清再回答用户只丢一句查一下用户数这是整个系统里最容易翻车的输入。因为用户数在我们的指标字典里能命中至少六个候选注册用户数、下单用户数、支付用户数、活跃用户数、新增用户数、设备去重数。如果不澄清直接答命中哪个都算运气。Skill里的ambiguity-policy.md明确要求当候选指标超过三个且用户没有给出时间范围、业务场景等限定条件时Agent必须主动追问把候选列表简化给用户选择。实测里Agent会这样回复您说的用户数我查到有这几个口径请确认是哪一个1. 注册用户数2. 活跃用户数按日去重3. 支付用户数4. 新增用户数。也可以告诉我这次用数的业务场景我帮您判断。用户点选之后Agent再查详情整个过程丝滑。刚开始我担心多轮交互增加使用成本实际上用户反馈说终于有个工具会问清楚再干活了比闷头给一个错误答案好得多。5.3 口径确认后生成预检SQL口径查询系统跑稳之后我们把它和SQL生成串了起来但做了严格限制Agent只有在口径完全明确且拿到来源表结构之后才允许生成查询SQL而且生成的SQL必须先走预检流程不能直接打生产集群。举例用户确认要看2024年12月每日活跃用户数口径明确是按user_id去重来源表dws_user_active_di过滤is_active_flag1。Agent调用get_table_schema(dws_user_active_di)确认字段确实叫user_id、active_date、is_active_flag然后生成类似下面的预检SQLSELECT active_date AS stat_date, COUNT(DISTINCT user_id) AS dau FROM dws.dws_user_active_di WHERE active_date BETWEEN 2024-12-01 AND 2024-12-31 AND is_active_flag 1 GROUP BY active_date ORDER BY active_date;这条SQL会先去一个只读的备库上试跑返回行数和样例数据给用户确认用户点头之后才进入正式查询链路。这一步把SQL生成的风险控制在了可接受范围内也让口径查询从回答问题自然延伸到了辅助取数。6. 上线一个月踩过的坑上下文爆炸、工具误调、口径编造6.1 坑一工具返回内容太长Agent开始发疯第一次联调时get_metric_detail返回的是完整指标定义包含一大段历史变更描述和一堆枚举值。有个问题需要对比三个指标三个详情一次性灌进来主上下文直接被撑爆Agent开始输出前言不搭后语的内容甚至把指标A的关系误套到指标B上。我后来在MCP工具返回层做了两层裁剪默认只返回摘要字段完整详情通过一个detail_level参数控制返回列表类结果时统一限制条数并明确提示如需更多结果请缩小范围或翻页。改完之后同样的测试集再跑错误率显著下降。6.2 坑二工具描述含糊Agent调错工具上线第二周出现了几次奇葩调用用户问某个表的上游是啥Agent竟然先调了search_metric_candidates因为它觉得上游和指标搜索有关。问题出在get_model_lineage的描述没写清楚血缘、上下游、来源表、依赖关系这些同义表达。我把所有工具的描述文案重新过了一遍给每个工具补上正面示例和反例然后在评估集里加了工具路由准确率这个指标。现在每次改描述都要跑一遍回归工具路由准确率稳定在96%以上才准上线。6.3 坑三文档里没有答案Agent开始编口径最严重的一个坑是幻觉。有一次用户问一个很偏门的指标索引里根本没有这个指标Agent搜了一通没找到居然根据其他指标的名称结构推测了一个口径还煞有介事地附了个不存在的文档链接。这个问题直接动摇了系统的可信度我马上下决心做了三层兜底。第一层Skill里写死规则索引中没有匹配结果时必须明确回答未找到该指标并给出近似建议禁止推测。第二层答案里引用的所有来源必须带doc_id或表名Agent输出前要做一次引用校验引用的内容必须能在检索结果里找到。第三层系统对低置信度答案自动标记需人工确认并转给值班的数据同学复核。这三层加上之后再也没出现过编造口径的情况。6.4 坑四口径文档漂移同步任务静默失败元数据索引服务的同步任务跑了三周后有一次凌晨拉取口径文档时因为源文档格式变动解析脚本抛了异常但任务重试机制把它当成功处理了。结果当天白天Agent返回的数据半天是新的、半天是旧的对业务造成了误导。这个坑让我养成了两个习惯同步任务必须做条数对比告警今天的索引量比昨天少超过5%就要发通知同时每周跑一次抽样核对随机抽几个指标回源比对口径摘要和原文是否一致。数据类应用数据质量永远比模型聪明程度更关键。这套系统跑了接近两个月我最大的感受不是模型变聪明了而是口径这个原本模糊的东西第一次变成了可查询、可追溯、可对比的数据资产。后续我打算做两件事一是把血缘查询接得更深让Agent能直接回答这个指标对应的底表字段是哪个二是把口径变更做成主动通知指标口径一旦有调整自动推送给引用这个指标的报表负责人。如果你也在搭类似的数仓Agent我的建议是从一套不超过五十个核心指标的种子字典开始先跑通链路再扩规模别一上来就吃成个胖子。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价