FDE 落地手记 · 09

把数据库接到 MCP 后,AI 能够执行查询、生成表格、解释趋势。这是一个很直观的变化,但真正进入经营场景,问题很快会变成:这次查询是否拿对了数据,结论能否复核,缺失条件有没有被隐藏?

在上一篇对 yuce_cube 的复盘里,我确认了原始留存、批次约束、标准化视图和来源覆盖等基础已经存在。同时,统一治理链路和完整性验收仍有待补齐的部分。

因此,我会把 MCP 可靠性拆成几个可以分别验收的层次,而不把“连上了”作为最终交付。本篇前半部分解释数据底座怎样支撑工具调用,后半部分给出可实施的接口契约和验收方案。文中的工具名称、返回结构和验收用例属于建议设计,不代表这些 MCP 工具已完成生产接入。

1. 一次可靠调用,需要四个层次同时成立

MCP 定义了应用与工具之间的交互方式,它不会自动理解企业的商品编码、利润口径和数据缺口。

层次 需要回答的问题 常见失败表现
连接与执行 工具能否被发现、认证和正确执行? 超时、协议错误、权限不足
数据事实 返回的数据是否覆盖指定范围,版本是否一致? 重复记录、缺日、旧批次混入
指标语义 日期、单位、分子分母和排除条件是否明确? ERP 出库与平台成交混用
分析表达 结论是否忠实于结果和限制? 把部分数据写成全量,把相关性写成原因

数据清洗主要改善第二层,指标建模支撑第三层。第一层需要服务工程能力,第四层需要结果校验和分析规则。它们相互配合,才能形成可以依赖的分析入口。

一个 HTTP 200 不能替代数据质量检查;一次 SQL 执行成功,也不能替代指标口径确认。

2. 把业务查询服务放在数据层与 MCP 之间

对高频经营分析,我更倾向于先建立清楚的业务查询能力,再通过 MCP 暴露给 Agent。

从数据质量检查到业务查询与 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 才逐渐从“会查数的聊天入口”,变成经营工作中可以依赖的协作工具。

上一篇:yuce_cube 数据治理实践:让经营数据有来源、有口径、可复核。

参考:MCP 官方架构说明;MCP 服务端开发指南;MCP 授权说明;MySQL 一致性读取。

代码世界的构建师,现实生活的悠游者。