数据分析 API · 简版设计(2 端点)

一套给 Agent 使用的数据分析 API:Agent 把用户口语转成端点 + 参数,数据团队负责实现端点、从真实库取数。 本文只讲 API 本身 + 每个参数 + Agent 如何匹配参数 + 数据团队如何实现。 约定:前缀 /pos/ai/v1;时间用 13 位毫秒时间戳(UTC+4);金额单位 AED。 设计沿用现网契约 2026-06-10-aigent-AI对接说明(2).md §1.7 agent-bi 的「一个接口搞定 取数/趋势/环比」思路,再把客单价、维度拆分也收进同一取数端点。


1. 端点总览(2 个)

端点用途谁调
POST /metrics:query统一取数:汇总 / 趋势 / 同比 / 客单价 / 维度拆分,全靠参数区分Agent 主力
GET /metrics/capabilities能力发现:这家店支持哪些指标/维度Agent 取数前先问

为什么是 2 个不是 1 个:取数类(原 query/report/dimensions)都是"给我数据",可由参数合并;而 capabilities 是"问这家店有什么能力",响应结构不同、且要在取数调用做门控,性质不同,单独保留。

端点收敛原则(与营销侧一致):端点选择 + 参数匹配由消费侧确定性纯函数完成 —— 不进 prompt、不靠 LLM 选,所以端点数与 prompt 长度/准确性解耦(实测数据专家 prompt 出现端点名 0 次)。只合真冗余(query/compare/trend/report/agent-bi → 一个 metrics:query),读端点性质不同的不强合。

危险写(导出报表)走平台共享写端点POST /marketing:preview(kind=EXPORT)→commit(与营销活动/会员的危险写同一对,kind 由消费侧确定性设置)。轻量取数无需 preview→commit,故本表不含。


2. 公共约定

2.1 响应外壳(两个端点统一)

{
  "code": 0,            // 0=成功,非 0 看 message
  "message": "ok",
  "data": { ... },      // 各端点的返回体
  "meta": {
    "currency": "AED",
    "data_basis": "DAILY_REPORT"   // 数据来源:DAILY_REPORT(日报直取) / ORDER_AGGREGATED(订单聚合) / NONE(无数据)
  }
}

data_basis=NONE 表示这家店这段时间无数据 —— Agent 应如实告知"暂无数据",不要把空当 0 汇报

2.2 时间

  • 一律 13 位毫秒时间戳,UTC+4。
  • 区间 date_from/date_to 含头含尾(如查 6 月,from=6/1 00:00,to=6/30 23:59:59)。

2.3 12 个指标(metrics[] 的取值)

key含义类型
income净收入(= 营业额 − 退款)金额
success_amount成功交易额(= 营业额/流水金额
order_count订单总数笔数
success_count成功笔数笔数
refund_amount退款额金额
refund_count退款笔数笔数
fail_amount失败额金额
fail_count失败笔数笔数
settle_amount总结算额金额
refund_rate退款率比率(0–1)
success_rate成功率比率(0–1)
avg_ticket客单价金额

3. 端点详解

3.1 POST /metrics:query —— 统一取数

营业额、订单量、退款、客单价、趋势、同比、分渠道占比……全用这一个端点,靠参数区分要哪种数据。

请求参数

参数类型必填说明取值
store_idstring门店 ID
metricsstring[]要查哪些指标,可多选见 §2.3(含 avg_ticket
aggregationstring区间内怎么合并,默认 SUMSUM 求和 / AVG 日均 / MAX 单日最高 / MIN 单日最低
time_granularitystring出数颗粒,不填=整段汇总成 1 行DAY 按天 / MONTH 按月
date_fromnumber区间起(毫秒)
date_tonumber区间止(毫秒)
comparestring是否要同比,默认 NONENONE / WOW 周环比 / MOM 月环比 / YOY 年同比
dimensionstring是否按维度拆分,不填=不拆CHANNEL 渠道 / PAYMENT 支付 / ORDER_SOURCE 来源 / MEMBER 会员

参数怎么组合 = 拿到哪种数据(这就是合并的核心)

你要的metricstime_granularitycomparedimension
上周营业额(一个数)["success_amount"]不填NONE不填
近 7 天每天营业额(趋势)["success_amount"]DAYNONE不填
本周营业额 + 比上周(同比)["success_amount"]不填WOW不填
客单价["avg_ticket"]不填NONE不填
分渠道营业额占比["success_amount"]不填NONECHANNEL

响应 data

① 不带 dimension(普通取数/趋势)

字段类型说明
seriesarray数据行;time_granularity 不填时 1 行,填了多行
series[].periodstring周期标签,如 2026-06-10(汇总行可为空)
series[].valuesobject每个指标一个值 {value, prev, pct}
series[].values.<m>.valuenumber本期值
series[].values.<m>.prevnumber|null对比期值(不带 compare 时 null)
series[].values.<m>.pctnumber|null变化率(不带 compare 时 null)

② 带 dimension(维度拆分)

字段类型说明
breakdownarray各维度切片
breakdown[].dimension_valuestring维度值,如 DINING_ROOM/PAY_BY_CARD
breakdown[].valuenumber该切片的指标值
breakdown[].sharenumber占比 0–1

示例 A · 「上周每天的营业额」

// 请求
{ "store_id":"B177", "metrics":["success_amount"],
  "time_granularity":"DAY", "date_from":1717977600000, "date_to":1718582399000 }
// 响应 data
{ "series":[
  {"period":"2026-06-09","values":{"success_amount":{"value":2167,"prev":null,"pct":null}}},
  {"period":"2026-06-10","values":{"success_amount":{"value":0,"prev":null,"pct":null}}}
  /* …共 7 天… */
]}

示例 B · 「分渠道看营业额」

// 请求
{ "store_id":"B177", "metrics":["success_amount"], "dimension":"CHANNEL",
  "date_from":1717977600000, "date_to":1718582399000 }
// 响应 data
{ "breakdown":[
  {"dimension_value":"DINING_ROOM","value":1860,"share":0.86},
  {"dimension_value":"STORE_DELIVERY","value":307,"share":0.14}
]}

三点约束

  1. avg_ticket(客单价)只有 263 家接入日报的门店有;其它店该指标 available:false不要用 income÷订单数现算
  2. dimension=ORDER_SOURCE(来源)只有笔数没有金额,传金额类 metric 时按笔数口径返回。
  3. dimensiontime_granularity 一般不同时用(要么看趋势、要么看拆分)。

3.2 GET /metrics/capabilities —— 能力发现

Agent 取数前先问:这家店支持哪些指标/维度,避免对没数据的店瞎查。

请求参数store_id(query 串)

响应 data

字段类型说明
metricsarray支持的指标 {key, label, available}
dimensionsarray支持的维度 {dimension, available}
limitsobject{query_max_days:400} 区间上限
currencystringAED

4. Agent 如何调用与匹配参数

Agent 把用户一句话拆成几件事,分别填到 /metrics:query 的参数里:

用户说的匹配到的参数规则
「上周」「6 月」「近 7 天」date_from / date_to把时间词解析成两个毫秒时间戳
营业额」「每天」「趋势」time_granularity=DAY;「按月」→ MONTH;不说 → 不填要不要逐日/逐月
「营业额」「订单量」「退款率」「客单价」metrics[]见下面"模糊词"表
「日均」「最高」aggregation=AVG/MAX不说 → SUM
「比上周」「同比」compare=WOW/YOY不说 → NONE
「分渠道」「各支付方式」dimension=CHANNEL/PAYMENT出现维度词就填这个参数

模糊词 → 指标(关键):「营业额」这种词不强行只给一个数 —— Agent 取一个主指标先回答,同时在数据卡列出相关指标。

用户词主指标
营业额 / 销售额 / 成交额 / 流水success_amount
净收入 / 利润income
营收 / 收入 / 赚了多少(模糊)success_amount(商家口语"收入"多指流水)
订单 / 单量order_count
客单价avg_ticket

完整例子:用户问「上周的日营业额」

  1. 「上周」→ date_from/to = 上周一~周日
  2. 「日…」→ time_granularity=DAY
  3. 「营业额」→ 主指标 success_amount
  4. 无"日均/比较/分渠道" → aggregation=SUMcompare=NONEdimension 不填 → 调 POST /metrics:query,得到 7 天逐日营业额。

5. 数据团队如何实现

合并成一个取数端点后,数据团队在 /metrics:query 的实现里按参数分支取数,对应真实库表(以 ai-Recs 全表分析为准):

参数情形 / 指标真实来源实现口径
success_amountreport_daily_statistics.turnover直取(营业额=流水)
incometurnover − refund派生(首选;如确认有 actual_income_no_tips 列再切换)
order_count / success_countreport_daily_statistics.order_quantity直取(近似)
refund_amount / refund_raterefund / refund÷turnover直取 / 派生
refund_count / fail_count / success_ratemain_order.order_status(refunded/blankout/cancel/payed)订单表聚合(日报无笔数级)
avg_ticketreport_daily_statistics.average_order_price直取(仅 263 日报店)
time_granularity=DAY/MONTH按日/月分组多行序列;不填则整段聚合 1 行
compare再拉一份上期同口径每个指标补 prev/pct
dimension=CHANNEL/PAYMENT/MEMBERdining_room/togo/store_deliverypay_by_*member_*预聚合列直取,算占比
dimension=ORDER_SOURCEorder_source_by_near/h5/shop只有笔数

实现要点

  1. 三端点收一端:现网 agent-bi(§1.7) 已合并取数/趋势/环比;本方案再把 客单价(源 average_order_price)和维度拆分(源分渠道/支付列)接进同一端点,即可让 /metrics:query 一口气覆盖原 query/report/dimensions。这些源 ai-Recs 已确认在真实日报表里存在。
  2. 覆盖度:日报只覆盖 263/5001 店。日报店走 report_daily_statisticsdata_basis=DAILY_REPORT);其余店从 main_order/daily_record 按日聚合(ORDER_AGGREGATED);都没有 → NONE不要返回 0
  3. capabilities 要如实:哪些指标/维度这家店真有数据,就在 capabilities 里标 available,让 Agent 据此决定查不查。

6. 待数据团队确认(实现前先核实)

列(report_daily_statistics用途未确认前回退
actual_income_no_tips / tips / vatincome 精确口径(是否扣小费/税)income 用 turnover − refund
customer_number 是否有/填充率客流量指标暂不提供该指标
member_* 各列填充率会员维拆分填充率低 → available:false