ETA(Estimated Time of Arrival)系统是美团超脑的时间预估引擎,负责对外卖履约全链路各环节进行精准的时间预测。作为调度决策的关键输入和用户体验的核心保障,ETA系统需要在毫秒级延迟内完成复杂的时空计算,为千万级订单提供分钟级精度的时间预估。
本章将深入剖析ETA系统的技术架构,从多粒度时间建模到深度学习应用,从特征工程到在线推理优化,帮助你理解如何构建一个城市级的实时时间预估系统。我们还将探讨LLM/Agent如何增强ETA系统的异常识别能力和自适应学习能力。
完成本章学习后,你将能够:
ETA系统在美团超脑中扮演着”时间度量衡”的角色,其准确性直接影响用户体验、骑手效率和平台成本。一个精准的ETA系统需要达到以下目标:
核心价值体现:
挑战与复杂性:
时间预估的复杂性来源:
┌─────────────────────────────────────────────────────┐
│ 环境因素:天气、交通、节假日、区域特征 │
│ ↓ │
│ 行为因素:商家出餐速度、骑手经验、用户位置 │
│ ↓ │
│ 系统因素:并单策略、路径规划、调度决策 │
│ ↓ │
│ 不确定性:突发事件、地址错误、电梯等待 │
└─────────────────────────────────────────────────────┘
ETA系统采用分层架构设计,各层职责明确:
┌────────────────────────────────────────────────────────┐
│ 接入层(API Gateway) │
│ 职责:请求路由、参数校验、限流熔断、协议转换 │
└────────────────────────────────────────────────────────┘
│
┌────────────────────────────────────────────────────────┐
│ 服务编排层(Service Orchestration) │
│ 职责:多模型调度、策略选择、降级逻辑、结果融合 │
└────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 预测服务 │ │ 特征服务 │ │ 规则引擎 │
│ (Prediction) │ │ (Feature) │ │ (Rule) │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ · 模型推理 │ │ · 实时特征 │ │ · 业务规则 │
│ · 批量预测 │ │ · 离线特征 │ │ · 异常处理 │
│ · 结果缓存 │ │ · 特征回填 │ │ · 兜底策略 │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└────────────────────────────────────────────────────────┘
│
┌────────────────────────────────────────────────────────┐
│ 数据层(Data Layer) │
│ · 实时流数据(Kafka) · 离线数据(Hive/HBase) │
│ · 特征存储(Redis) · 模型仓库(HDFS) │
└────────────────────────────────────────────────────────┘
实时预测数据流:
用户下单 → 订单创建事件 → ETA请求组装 → 特征提取
↓ ↓
履约完成 ← 骑手调度 ← 时间预估 ← 模型推理
↓
时间回流 → 样本构建 → 模型更新 → 版本发布
核心接口定义:
1. 单点时间预估接口
POST /api/eta/predict
{
"order_id": "xxx",
"merchant_id": "xxx",
"user_location": {...},
"order_time": "2024-01-01 12:00:00",
"items": [...],
"context": {...}
}
2. 批量预估接口(供调度使用)
POST /api/eta/batch_predict
{
"requests": [
{"order_id": "001", "rider_id": "r1", ...},
{"order_id": "002", "rider_id": "r2", ...}
],
"strategy": "fast" // fast/accurate
}
3. 链路时间查询接口
GET /api/eta/breakdown/{order_id}
Response: {
"total_time": 35,
"breakdown": {
"merchant_prepare": 12,
"rider_arrive_merchant": 8,
"rider_pickup": 2,
"rider_deliver": 10,
"other": 3
}
}
ETA系统的性能评估需要多维度指标:
准确性指标:
| 准时率: | 预测-实际 | < 5分钟的订单占比,目标 > 95% |
系统性指标:
业务指标:
外卖履约是一个复杂的多阶段流程,准确的时间预估需要对每个环节进行精细建模:
订单履约全链路时间分解:
┌──────────────────────────────────────────────────────────┐
│ T_total = T_merchant + T_pickup + T_deliver + T_other │
└──────────────────────────────────────────────────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 商家阶段 │ │ 取餐阶段 │ │ 配送阶段 │
├─────────────┤ ├─────────────┤ ├─────────────┤
│·订单确认 │ │·骑手分配 │ │·取餐出发 │
│·备餐制作 │ │·前往商家 │ │·配送路线 │
│·打包装袋 │ │·到店等待 │ │·交付用户 │
│·等待取餐 │ │·取餐核验 │ │·确认完成 │
└─────────────┘ └─────────────┘ └─────────────┘
细粒度时间建模:
T_merchant(商家阶段)= {
T_confirm: 订单确认时间(5-30秒)
T_prepare: 备餐时间(5-30分钟,主要变量)
T_pack: 打包时间(30-120秒)
T_wait: 等待取餐(0-5分钟)
}
T_pickup(取餐阶段)= {
T_assign: 骑手分配时间(系统决策,<100ms)
T_arrive: 到店时间(2-15分钟)
T_indoor: 室内寻找时间(30秒-3分钟)
T_verify: 取餐核验(10-60秒)
}
T_deliver(配送阶段)= {
T_route: 骑行时间(5-20分钟)
T_building: 楼宇内时间(1-5分钟)
T_handover: 交付时间(30-120秒)
}
T_other(其他因素)= {
T_traffic: 交通延误(概率事件)
T_weather: 天气影响(雨天+20%)
T_elevator: 电梯等待(高层建筑+2-5分钟)
T_exception: 异常处理(地址错误、用户不在等)
}
不同场景需要不同粒度的时间预估:
1. 承诺时间(用户视角)
目标:给用户一个可信且稳定的预期
策略:P80分位数 + 缓冲时间
公式:T_promise = T_p80 + Buffer(context)
Buffer计算考虑因素:
- 天气状况:雨天+5分钟,雪天+10分钟
- 高峰时段:午高峰+3分钟,晚高峰+5分钟
- 区域特征:CBD+3分钟,大学城+2分钟
- 商家历史:新商家+3分钟,延误商家+5分钟
2. 调度时间(系统视角)
目标:为调度决策提供准确的时间成本
策略:期望值 + 方差考虑
公式:T_dispatch = E[T] + α * Var[T]^0.5
α参数动态调整:
- 运力充足:α = 0.5(激进策略)
- 运力紧张:α = 1.5(保守策略)
- 普通状态:α = 1.0(平衡策略)
3. 导航时间(骑手视角)
目标:给骑手实时更新的到达时间
策略:实时路况 + 个人因子
公式:T_nav = T_base * PersonalFactor * TrafficFactor
PersonalFactor计算:
- 历史速度:rider_speed / avg_speed
- 熟悉度:area_orders / 100(上限1.2)
- 疲劳度:1 + 0.1 * working_hours / 8
时间预估的核心挑战是处理不确定性:
不确定性来源分析:
┌────────────────────────────────────────┐
│ 可建模不确定性(70%) │
├────────────────────────────────────────┤
│ · 商家出餐速度波动(历史数据) │
│ · 交通拥堵模式(时段规律) │
│ · 天气影响(气象预报) │
│ · 骑手个体差异(个人画像) │
└────────────────────────────────────────┘
│
┌────────────────────────────────────────┐
│ 难建模不确定性(30%) │
├────────────────────────────────────────┤
│ · 突发交通事故 │
│ · 商家设备故障 │
│ · 用户临时修改地址 │
│ · 电梯故障/等待 │
└────────────────────────────────────────┘
概率分布建模:
# 使用混合高斯模型建模时间分布
T ~ GMM(k=3) = Σ(w_i * N(μ_i, σ_i²))
其中:
- 成分1:正常情况(权重0.7)
- 成分2:轻度延误(权重0.2)
- 成分3:严重延误(权重0.1)
# 贝叶斯更新
P(T|evidence) ∝ P(evidence|T) * P(T)
实时根据新信息更新时间分布
分位数回归: 不仅预测期望值,还预测整个分布:
分位数回归目标:
- P10:乐观估计(最快可能时间)
- P50:中位数(典型时间)
- P90:保守估计(考虑延误)
Loss = Σ_τ Σ_i ρ_τ(y_i - q_τ(x_i))
其中 ρ_τ(u) = u(τ - I(u<0))
时序依赖建模:
订单时间具有强时序依赖:
T_t = f(T_{t-1}, T_{t-2}, ..., Context_t)
使用LSTM捕获时序模式:
h_t = LSTM(x_t, h_{t-1})
T_t = Dense(h_t)
周期性模式编码:
- 小时周期:sin(2π*hour/24), cos(2π*hour/24)
- 星期周期:sin(2π*day/7), cos(2π*day/7)
- 月度周期:sin(2π*day/30), cos(2π*day/30)
时空特征是ETA预估的核心,需要从多个维度刻画时间和空间的关联:
空间特征体系:
1. 绝对位置特征
- 经纬度:(lng, lat)
- Geohash编码:不同精度的空间索引
- H3六边形网格:均匀的空间划分
- 行政区划:省/市/区/街道/社区
2. 相对位置特征
- 直线距离:haversine_distance(A, B)
- 路网距离:road_network_distance(A, B)
- 方向角度:bearing(A, B)
- 相对象限:quadrant(A, B)
3. 区域属性特征
- POI密度:餐厅/写字楼/住宅密度
- 路网密度:主干道/支路/小区道路
- 区域类型:CBD/住宅区/学校/医院
- 配送难度:楼层高度/是否有电梯/门禁
4. 时空交互特征
- 时段×区域:rush_hour × CBD
- 天气×距离:rain × distance
- 节假日×商圈:holiday × business_area
时间特征工程:
时间多尺度编码:
├── 绝对时间
│ ├── 年月日时分秒
│ ├── 星期几(0-6)
│ └── 是否节假日
├── 周期时间
│ ├── 日内周期(0-23时)
│ ├── 周内周期(周一到周日)
│ └── 月内周期(1-31日)
├── 相对时间
│ ├── 距离高峰时间差
│ ├── 距离节假日天数
│ └── 距离月初/月末天数
└── 统计时间
├── 过去1小时订单量
├── 过去10分钟延误率
└── 未来30分钟预测单量
用户、商家、骑手的历史行为蕴含丰富的时间模式:
商家行为序列:
特征维度:
1. 出餐速度序列
- 最近N单出餐时间:[t1, t2, ..., tn]
- 移动平均:MA(n) = mean(last_n_orders)
- 加权平均:WMA = Σ(w_i * t_i) / Σw_i
- 趋势特征:trend = t_n - t_1
2. 忙碌程度序列
- 同时在做订单数:concurrent_orders
- 厨师在岗数量:active_chefs
- 排队等待时间:queue_time
3. 异常事件序列
- 超时次数:timeout_count
- 取消次数:cancel_count
- 投诉次数:complaint_count
骑手行为序列:
特征维度:
1. 速度模式
- 历史平均速度:avg_speed_history
- 分时段速度:speed_by_hour
- 分区域速度:speed_by_area
- 负载速度:speed_by_load
2. 履约模式
- 准时率:on_time_rate
- 平均超时:avg_overtime
- 并单能力:multi_order_efficiency
3. 路径偏好
- 主路/小路偏好:main_road_preference
- 绕路倾向:detour_tendency
- 熟悉区域:familiar_areas
外部环境对时间有显著影响:
天气特征:
实时天气数据:
├── 降水
│ ├── 降水量(mm/h)
│ ├── 降水类型(雨/雪/冰雹)
│ └── 持续时间
├── 温度
│ ├── 实时温度
│ ├── 体感温度
│ └── 温差(日内最高-最低)
├── 风力
│ ├── 风速(m/s)
│ ├── 风向
│ └── 阵风等级
└── 能见度
├── 能见度距离
├── 空气质量(AQI)
└── 雾霾等级
影响量化:
- 小雨:速度降低10-15%
- 大雨:速度降低20-30%
- 雪天:速度降低30-50%
- 高温(>35°C):速度降低5-10%
- 低温(<0°C):速度降低10-15%
交通特征:
实时路况:
1. 拥堵指数
- 路段拥堵指数:congestion_index
- 区域拥堵指数:area_congestion
- 拥堵里程占比:congested_ratio
2. 事件影响
- 交通事故:accident_impact
- 道路施工:construction_impact
- 大型活动:event_impact
3. 公共交通
- 地铁运行状态
- 公交拥挤度
- 共享单车可用量
流式特征计算架构:
Kafka订单流
│
┌────────────┼────────────┐
▼ ▼ ▼
Flink作业1 Flink作业2 Flink作业3
(事件特征) (聚合特征) (窗口特征)
│ │ │
└────────────┼────────────┘
▼
特征写入Redis
│
▼
在线特征服务
特征更新策略:
# 实时特征更新
class RealtimeFeatureUpdater:
def update_merchant_features(self, order_event):
# 更新商家最近N单
self.redis.lpush(f"merchant:{merchant_id}:recent",
order_event.prepare_time)
self.redis.ltrim(f"merchant:{merchant_id}:recent", 0, 99)
# 更新移动平均
recent = self.redis.lrange(f"merchant:{merchant_id}:recent", 0, 9)
ma_10 = np.mean([float(x) for x in recent])
self.redis.set(f"merchant:{merchant_id}:ma_10", ma_10)
# 更新并发订单数
self.redis.hincrby(f"merchant:{merchant_id}:concurrent",
order_event.hour, 1)
# 批量特征计算
class BatchFeatureComputer:
def compute_daily_features(self):
# Spark作业计算T+1特征
df = spark.sql("""
SELECT merchant_id,
AVG(prepare_time) as avg_prepare_time,
STDDEV(prepare_time) as std_prepare_time,
PERCENTILE(prepare_time, 0.5) as median_prepare_time,
PERCENTILE(prepare_time, 0.9) as p90_prepare_time
FROM orders
WHERE dt = current_date - 1
GROUP BY merchant_id
""")
# 写入特征存储
df.write.mode("overwrite").saveAsTable("merchant_daily_features")
特征一致性保证:
训练与推理特征一致性方案:
1. 特征版本管理
- 每个特征带版本号
- 模型记录使用的特征版本
- 推理时校验版本匹配
2. 特征回填机制
- 离线特征定期回填到在线
- 保证时间窗口对齐
- 处理缺失值策略统一
3. 特征监控
- 分布偏移检测
- 异常值告警
- 覆盖率监控
DeepFM是ETA系统的核心模型,结合了因子分解机(FM)的二阶特征交互能力和深度神经网络(DNN)的高阶特征学习能力:
DeepFM架构图:
Input Features
│
┌─────────────┼─────────────┐
│ │
Embedding Layer Embedding Layer
│ │
┌─────┴─────┐ ┌─────┴─────┐
│ FM │ │ DNN │
│Component │ │Component │
└─────┬─────┘ └─────┬─────┘
│ │
FM Output DNN Output
│ │
└─────────┬─────────────────┘
│
Sigmoid/Linear
│
Prediction
FM组件:
y_FM = <w, x> + Σ_i Σ_j<V_i, V_j>x_i·x_j
DNN组件:
y_DNN = σ(W_L·a_L + b_L)
其中 a_l = σ(W_l·a_{l-1} + b_l)
最终输出:
y = sigmoid(y_FM + y_DNN)
特征嵌入层设计:
# 离散特征嵌入
merchant_emb = Embedding(num_merchants, emb_dim)(merchant_id)
rider_emb = Embedding(num_riders, emb_dim)(rider_id)
area_emb = Embedding(num_areas, emb_dim)(area_id)
# 连续特征处理
continuous_features = [
'distance', 'merchant_avg_time', 'rider_speed',
'temperature', 'rainfall', 'congestion_index'
]
cont_input = Dense(emb_dim, activation='relu')(continuous_features)
# 特征拼接
all_embeddings = Concatenate()([
merchant_emb, rider_emb, area_emb, cont_input
])
ETA系统采用多任务学习同时预测多个相关目标:
多任务学习架构:
Shared Bottom
│
┌────────────┼────────────┐
│ │ │
Task1 Tower Task2 Tower Task3 Tower
│ │ │
承诺时间 实际时间 超时概率
损失函数:
L = λ₁L_promise + λ₂L_actual + λ₃L_timeout
其中:
- L_promise: 用户承诺时间的MAE损失
- L_actual: 实际履约时间的MAE损失
- L_timeout: 超时概率的交叉熵损失
任务间关系建模:
class MultiTaskETA(nn.Module):
def __init__(self):
# 共享底层
self.shared_bottom = nn.Sequential(
nn.Linear(input_dim, 256),
nn.ReLU(),
nn.Linear(256, 128)
)
# 任务特定层
self.promise_tower = nn.Sequential(
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 1) # 承诺时间
)
self.actual_tower = nn.Sequential(
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 1) # 实际时间
)
self.timeout_tower = nn.Sequential(
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 1),
nn.Sigmoid() # 超时概率
)
引入注意力机制捕获关键特征和时序依赖:
自注意力层:
Attention(Q,K,V) = softmax(QK^T/√d_k)V
应用场景:
1. 商家订单序列注意力
- 关注相似菜品的历史出餐时间
- 关注相同时段的出餐模式
2. 骑手轨迹序列注意力
- 关注相似路线的历史用时
- 关注拥堵路段的时间影响
3. 跨模态注意力
- 文本地址 → 地理位置
- 天气状况 → 速度影响
单一模型难以应对所有场景,需要模型融合:
融合架构:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ DeepFM │ │ XGBoost │ │ LSTM │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└─────────────┼─────────────┘
│
┌──────▼──────┐
│ Stacking │
│ MetaModel │
└──────┬──────┘
│
Final Output
融合策略:
1. 加权平均:y = Σw_i * y_i
2. Stacking:meta_model(y1, y2, y3, features)
3. Blending:动态选择最优模型