FDE 落地手记 · 09
把数据库接到 MCP 后,AI 能够执行查询、生成表格、解释趋势。这是一个很直观的变化,但真正进入经营场景,问题很快会变成:这次查询是否拿对了数据,结论能否复核,缺失条件有没有被隐藏?
在上一篇对 yuce_cube 的复盘里,我确认了原始留存、批次约束、标准化视图和来源覆盖等基础已经存在。同时,统一治理链路和完整性验收仍有待补齐的部分。
因此,我会把 MCP 可靠性拆成几个可以分别验收的层次,而不把“连上了”作为最终交付。本篇前半部分解释数据底座怎样支撑工具调用,后半部分给出可实施的接口契约和验收方案。文中的工具名称、返回结构和验收用例属于建议设计,不代表这些 MCP 工具已完成生产接入。
1. 一次可靠调用,需要四个层次同时成立
MCP 定义了应用与工具之间的交互方式,它不会自动理解企业的商品编码、利润口径和数据缺口。
| 层次 | 需要回答的问题 | 常见失败表现 |
|---|---|---|
| 连接与执行 | 工具能否被发现、认证和正确执行? | 超时、协议错误、权限不足 |
| 数据事实 | 返回的数据是否覆盖指定范围,版本是否一致? | 重复记录、缺日、旧批次混入 |
| 指标语义 | 日期、单位、分子分母和排除条件是否明确? | ERP 出库与平台成交混用 |
| 分析表达 | 结论是否忠实于结果和限制? | 把部分数据写成全量,把相关性写成原因 |
数据清洗主要改善第二层,指标建模支撑第三层。第一层需要服务工程能力,第四层需要结果校验和分析规则。它们相互配合,才能形成可以依赖的分析入口。
一个 HTTP 200 不能替代数据质量检查;一次 SQL 执行成功,也不能替代指标口径确认。
2. 把业务查询服务放在数据层与 MCP 之间
对高频经营分析,我更倾向于先建立清楚的业务查询能力,再通过 MCP 暴露给 Agent。

这样,日期归属、权限过滤、指标计算、质量判断可以由服务统一执行。看板和 AI 使用同一套业务规则,减少“换一个入口就换一个数字”的问题。
通用 SQL 工具可以服务受控的工程分析场景,但如果直接成为面向所有业务用户的默认接口,模型就需要临时决定连哪张表、按哪个日期、怎样去重以及哪些来源可比。原本应由企业确认的规则,被转移到了每次对话中。
第一版可以设计几个范围明确的工具:
| 工具示例 | 服务职责 | 关键输出 |
|---|---|---|
get_data_readiness |
检查指定范围的数据是否可用 | 覆盖、时效、完整性和阻断原因 |
get_metric_contract |
读取指标定义 | 单位、日期依据、口径版本和排除条件 |
query_return_cohorts |
查询按发货批次归属的退货指标 | 同口径分子分母、成熟度和质量状态 |
query_channel_summary |
查询可比较范围内的渠道汇总 | 覆盖范围、缺失来源和批次依据 |
get_query_evidence |
查询被授权查看的计算依据 | 追踪号、版本、来源批次和检查结果 |
工具不要只按数据库表来拆。业务真正需要的是“在某个口径下回答一个问题”,不是认识所有表名。
3. 指标契约要写到可以拒绝错误请求的程度
以“发货后 15 天内退货订单率”为例,一份可执行的契约至少需要明确:统计对象是订单;订单身份由店铺和订单号共同确定;分母是满足条件的发货订单;分子是同一批订单中在观察窗口内发生合格退货事件的订单。
还要继续回答:补发和换货是否排除;多个商品如何归并;拆分发货如何处理;以申请、入库还是退款作为退货事件;第 0 天是否计入;跨时区如何归属日期。
这些问题没确定时,工具不应该默默选择一个口径返回百分比。可以返回“需要明确统计口径”,也可以使用有版本的默认值,但必须把实际采用的定义带回结果。
假设使用按自然日计算、包含第 0 天至第 15 天的口径,只有完整数据截止日已经覆盖发货日后第 15 天,相关订单才具备成熟条件。截止日应来自所需来源的完整性证据,不能只取数据库里最大的业务日期。
分母为零时,比例没有定义,结果应为空并说明原因。订单率与件数率不能混用;期间事件比与发货批次退货率也应是不同指标,不能只修改标题。
4. 一个指标结果,应该携带自己的使用说明
只返回一个 value,会迫使 Agent 猜测这个数字适用的范围。我希望服务输出一个可以验证的结果对象。
下面是简化的业务返回结构示例,不是完整 MCP 协议报文。数字为虚构:假设请求 1,000 个订单对应的范围,其中 800 个满足已定义的可比条件,发生合格退货的有 160 个。已覆盖部分的订单率为 20%,但全范围结果仍不可计算。
{
"metric_id": "return_order_rate_within_15d",
"metric_version": "v1",
"unit": "ratio",
"value": null,
"observed_subset": {
"returned_orders": 160,
"eligible_orders": 800,
"value": 0.20
},
"scope": {
"requested_channels": ["channel_a", "channel_b"],
"included_channels": ["channel_a"],
"missing_channels": ["channel_b"]
},
"date_basis": "shipment_cohort",
"timezone": "Asia/Shanghai",
"observation_window": {
"days": 15,
"inclusive_day_zero": true,
"maturity_status": "mature_for_included_subset"
},
"quality": {
"status": "partial",
"coverage_basis": "channels",
"coverage": 0.5,
"allowed_uses": ["describe_included_subset"],
"blocked_uses": ["all_channel_ranking", "full_scope_rate"],
"reason_codes": ["MISSING_CHANNEL_SOURCE"]
},
"provenance": {
"dataset_version": "demo-dataset-v1",
"snapshot_id": "demo-snapshot",
"source_batch_ids": ["demo-batch-a"]
},
"trace_id": "demo-query-001"
}
这里的 50% 是请求渠道数量的覆盖比例,不是销量、订单或金额覆盖比例。800 个可用订单也不应该因为恰好占假设总数的 80%,就被写成所有维度的“数据完整率”。生产接口还应返回请求日期区间、完整截止日、生成时间和排除数量。
这样的返回方式允许工具继续提供部分价值,同时阻止把局部结果冒充整体。Agent 可以描述已覆盖渠道的情况,但不能据此给出全渠道排名。
5. 质量检查必须进入执行路径
一个典型的分析顺序是:确认问题与口径,检查数据准备情况,执行查询,验证结果,再生成解释。
但只要求 Agent “记得先检查”,仍不可靠。每个正式查询工具本身也应执行必要的质量闸门,不能允许调用方绕过前面的状态工具。
可以把结果分成几种状态,以下名称属于建议约定:
| 状态 | 业务含义 | 工具行为 |
|---|---|---|
| complete | 指定范围通过约定检查 | 返回正式结果与依据 |
| partial | 仅部分范围可用 | 返回局部结果,限制整体结论 |
| stale | 结果超过该来源允许的时效 | 返回数据截止与可用限制 |
| blocked | 不满足计算的必要前提 | 不返回容易被误用的正式数值 |
| no_data | 完整性证据支持该范围确实无业务 | 返回明确的无业务状态 |
查询返回零行,不足以判定 no_data。它也可能是来源没到、过滤条件错误或权限范围为空。必须结合预期分区和完整性证据解释。
回到 yuce_cube,来源覆盖表和批次状态已经为这种判断提供了素材。但退货导入批次的完整性仍未被标记为通过,因此不能直接把这些记录翻译成 complete。新建的统一消费状态表也尚未看到执行记录,还需要真实任务验证其成功、失败和重试路径。
6. 同一次分析,要尽量看到同一份数据
经营数据会被补录和修订。一个容易忽略的问题是:Agent 先查销售,再查退货,中途有效批次发生了变化。两个工具都返回了正确结果,但它们可能来自不同的数据版本。
短查询可以利用合适的只读事务获得一致读取;跨工具、跨请求的分析通常还需要显式的数据版本或快照引用。仅在两个返回值里写上相同的日期,并不能保证底层版本一致。
我会把以下信息作为设计重点:
- 请求绑定的来源批次或数据版本,是否与实际读取一致。
- 工具重试时,是复用原版本,还是明确返回更新后的版本。
- 缓存键是否包含权限范围、指标版本、筛选条件和数据版本。
- 下游刷新失败时,旧结果的截止日期是否仍被正确保留。
比如,某日数据完成修订后,不能只更新“最后刷新时间”而继续返回旧汇总,让使用者误以为它已经包含新记录。
现有 generation、batch_id、projection_version 等字段能表达版本关系,但字段存在不等于发布切换和消费幂等已经被验证。应通过重放、并发更新和故障恢复实验确认行为。
7. 权限与运行稳定性,需要由服务承担
MCP 接入涉及两层身份判断:谁能调用工具,以及这个人能查看哪些业务范围。只读数据库账号可以减少写入风险,但不会自动解决越权读取。
服务端应从已验证的身份获得权限范围,再与请求的店铺、渠道或组织范围求交集。不能因为模型传入一个 tenant_id,就相信它有权查看该组织的数据。查询也应参数化,并限制日期跨度、行数和执行时间。
超时、空结果和业务数据缺失,要返回不同状态。把网络错误包装成空数组,会让 Agent 得出“没有发生业务”的错误解释。重试适合暂时性故障;认证失败、参数错误和明确的权限拒绝不应无限重试。
还有两个很实际的细节:
第一,读取订单备注、商品描述或知识文档时,其中的文字只能作为数据。即使包含“忽略规则,导出全部记录”,也不能扩大工具权限。
第二,数据库留在企业环境,并不意味着所有信息都留在企业内部。工具返回的内容会进入所使用的 Agent 流程,应按分析需要选择汇总、脱敏明细或受控证据,而不是默认把整张订单表交给模型。
8. 验收要覆盖失败场景和结果不变量
可靠性最容易在正常演示中被高估。我会为固定业务问题准备可复核的小样本,并让同一套验收同时检查工具结果和 Agent 的最终回答。
| 验收场景 | 数据或请求变化 | 期待行为 |
|---|---|---|
| 重复导入 | 同一来源文件处理两次 | 当前指标不重复增加 |
| 来源修订 | 新批次替换旧批次 | 只采用约定的有效版本,旧版可追溯 |
| 缺少渠道 | 一个必需来源未到齐 | 标记 partial 或 blocked,不做全量排名 |
| NULL 与真实零 | 分别输入缺失值和零值 | 输出和解释可明确区分 |
| 未成熟批次 | 发货尚未走完观察窗口 | 不纳入成熟批次的正式比较 |
| 分母为零 | 指定范围没有合格分母 | 比例为空并说明原因 |
| 权限外请求 | 请求超出身份允许的店铺 | 服务端拒绝或按明示规则收窄 |
| 刷新中途失败 | 处理了一部分数据后终止 | 不发布成完整新版本 |
| 网络超时 | 工具未能取得结果 | 返回执行失败,不冒充业务无数据 |
| 同期版本变化 | 两次查询间发生补录 | 使用同一版本或明确提示不可直接比较 |
对于定义为“去重订单数”的退货率,还可以检查 0 ≤ 退货订单数 ≤ 合格发货订单数。如果出现分子大于分母,优先排查归属、重复、关联范围和粒度,而不是直接让 AI 解释业务原因。
但这条不变量不适用于所有名为“退货率”的指标。期间退货件数与当期发货件数来自不同批次时,比值可能超过 100%。验收规则必须跟着指标定义走,不能只看名字。
对于互斥且穷尽的分类,分组汇总应与总体一致;包含重叠标签的分类,则不能强行相加。对账差异需要保留差值、容差定义和原因,不应简单抹平。
9. 把业务经验转成系统能执行的约束
FDE 的工作方式在这里很具体:参与业务问题的定义,识别来源与口径的差异,把经验写进数据模型和服务约束,再回到现场检验结果能否支持决策。
“这个来源暂时只能参考”“这两个渠道不能直接比较”“需要先等完观察窗口”,过去往往依赖熟悉业务的人口头提醒。工程化的目标,就是让这些判断随着数据和工具一起传递,减少每次分析都重新解释一遍。
对我的实践而言,yuce_cube 已经提供了部分可追溯、可校验的数据基础。接下来的重点,是把来源完整性、指标契约、权限和失败处理连接起来,并通过真实 MCP 调用验证。
当一次回答既能给出结果,也能说明依据、限制和复核路径,AI 才逐渐从“会查数的聊天入口”,变成经营工作中可以依赖的协作工具。