meituan_system

第6章:ETA系统 - 全链路时间预估

章节概述

ETA(Estimated Time of Arrival)系统是美团超脑的时间预估引擎,负责对外卖履约全链路各环节进行精准的时间预测。作为调度决策的关键输入和用户体验的核心保障,ETA系统需要在毫秒级延迟内完成复杂的时空计算,为千万级订单提供分钟级精度的时间预估。

本章将深入剖析ETA系统的技术架构,从多粒度时间建模到深度学习应用,从特征工程到在线推理优化,帮助你理解如何构建一个城市级的实时时间预估系统。我们还将探讨LLM/Agent如何增强ETA系统的异常识别能力和自适应学习能力。

学习目标

完成本章学习后,你将能够:

  1. 理解ETA系统架构:掌握全链路时间预估的系统设计和模块划分
  2. 掌握时间建模方法:学会对复杂履约流程进行时间分解和建模
  3. 精通特征工程:理解时空特征、行为特征、环境特征的构建方法
  4. 应用深度学习:掌握DeepFM等模型在时间预估中的应用
  5. 优化在线推理:学会设计高性能的在线预测服务
  6. 系统集成能力:理解ETA与调度、定价等模块的协同机制

本章大纲

6.1 ETA系统架构全景

6.1.1 系统定位与价值

ETA系统在美团超脑中扮演着”时间度量衡”的角色,其准确性直接影响用户体验、骑手效率和平台成本。一个精准的ETA系统需要达到以下目标:

核心价值体现

  1. 用户端承诺:为用户提供可信的送达时间,减少焦虑和投诉
  2. 调度决策依据:为调度引擎提供准确的时间成本函数
  3. 骑手任务规划:帮助骑手合理安排取餐和送餐顺序
  4. 商家备餐指导:指导商家合理安排出餐时间
  5. 运营分析支撑:为运营提供履约质量分析的基础数据

挑战与复杂性

时间预估的复杂性来源:
┌─────────────────────────────────────────────────────┐
│  环境因素:天气、交通、节假日、区域特征              │
│  ↓                                                  │
│  行为因素:商家出餐速度、骑手经验、用户位置          │
│  ↓                                                  │
│  系统因素:并单策略、路径规划、调度决策              │
│  ↓                                                  │
│  不确定性:突发事件、地址错误、电梯等待              │
└─────────────────────────────────────────────────────┘

6.1.2 核心模块划分

ETA系统采用分层架构设计,各层职责明确:

┌────────────────────────────────────────────────────────┐
│                     接入层(API Gateway)               │
│  职责:请求路由、参数校验、限流熔断、协议转换          │
└────────────────────────────────────────────────────────┘
                              │
┌────────────────────────────────────────────────────────┐
│                    服务编排层(Service Orchestration)  │
│  职责:多模型调度、策略选择、降级逻辑、结果融合        │
└────────────────────────────────────────────────────────┘
                              │
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  预测服务    │     │  特征服务    │     │  规则引擎    │
│ (Prediction) │     │  (Feature)   │     │   (Rule)     │
├──────────────┤     ├──────────────┤     ├──────────────┤
│ · 模型推理   │     │ · 实时特征   │     │ · 业务规则   │
│ · 批量预测   │     │ · 离线特征   │     │ · 异常处理   │
│ · 结果缓存   │     │ · 特征回填   │     │ · 兜底策略   │
└──────────────┘     └──────────────┘     └──────────────┘
        │                     │                     │
└────────────────────────────────────────────────────────┘
                              │
┌────────────────────────────────────────────────────────┐
│                    数据层(Data Layer)                 │
│  · 实时流数据(Kafka)  · 离线数据(Hive/HBase)      │
│  · 特征存储(Redis)    · 模型仓库(HDFS)            │
└────────────────────────────────────────────────────────┘

6.1.3 数据流与接口设计

实时预测数据流

用户下单 → 订单创建事件 → 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
     }
   }

6.1.4 性能指标体系

ETA系统的性能评估需要多维度指标:

准确性指标

系统性指标

业务指标

6.2 全链路时间建模

6.2.1 履约流程分解

外卖履约是一个复杂的多阶段流程,准确的时间预估需要对每个环节进行精细建模:

订单履约全链路时间分解:
┌──────────────────────────────────────────────────────────┐
│  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:  异常处理(地址错误、用户不在等)
}

6.2.2 多粒度时间预估

不同场景需要不同粒度的时间预估:

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

6.2.3 不确定性建模

时间预估的核心挑战是处理不确定性:

不确定性来源分析

┌────────────────────────────────────────┐
│         可建模不确定性(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)
实时根据新信息更新时间分布

6.2.4 时间分布估计

分位数回归: 不仅预测期望值,还预测整个分布:

分位数回归目标:
- 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)

6.3 特征工程与数据处理

6.3.1 时空特征构建

时空特征是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分钟预测单量

6.3.2 行为序列特征

用户、商家、骑手的历史行为蕴含丰富的时间模式:

商家行为序列

特征维度:
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

6.3.3 环境上下文特征

外部环境对时间有显著影响:

天气特征

实时天气数据:
├── 降水
│   ├── 降水量(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. 公共交通
   - 地铁运行状态
   - 公交拥挤度
   - 共享单车可用量

6.3.4 特征实时计算

流式特征计算架构

                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. 特征监控
   - 分布偏移检测
   - 异常值告警
   - 覆盖率监控

6.4 深度学习模型设计

6.4.1 DeepFM架构详解

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
])

6.4.2 多任务学习框架

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()  # 超时概率
        )

6.4.3 注意力机制应用

引入注意力机制捕获关键特征和时序依赖:

自注意力层:
Attention(Q,K,V) = softmax(QK^T/√d_k)V

应用场景:
1. 商家订单序列注意力
   - 关注相似菜品的历史出餐时间
   - 关注相同时段的出餐模式

2. 骑手轨迹序列注意力
   - 关注相似路线的历史用时
   - 关注拥堵路段的时间影响

3. 跨模态注意力
   - 文本地址 → 地理位置
   - 天气状况 → 速度影响

6.4.4 模型融合策略

单一模型难以应对所有场景,需要模型融合:

融合架构:
┌──────────┐  ┌──────────┐  ┌──────────┐
│ DeepFM   │  │ XGBoost  │  │ LSTM     │
└────┬─────┘  └────┬─────┘  └────┬─────┘
     │             │             │
     └─────────────┼─────────────┘
                   │
            ┌──────▼──────┐
            │   Stacking  │
            │   MetaModel │
            └──────┬──────┘
                   │
              Final Output

融合策略:
1. 加权平均:y = Σw_i * y_i
2. Stacking:meta_model(y1, y2, y3, features)
3. Blending:动态选择最优模型

6.5 在线推理与优化

6.6 系统集成与接口设计

本章小结

练习题

常见陷阱与错误