# v0.12 以来 HATT / delivcoal 更新总结

本文按问题线索整理 `v0.12` 以来的主要更新，不按版本流水账展开。重点回答三件事：

- 每个问题做过什么尝试，验证了什么，证伪了什么。
- 这些更新背后的原理和大致算法流程。
- 现在还缺什么。

## 1. HATT 在 delivcoal 里为什么失败

`v0.12` 之后的主问题是：HATT v2.0 在 capcoal 这类能力匹配问题里比较自然，但到 delivcoal 配送场景中会不稳定，常见表现是反复待命、探索效率低、best checkpoint 和 last checkpoint 差距大。

### 做过的尝试

- 加 decoder-only 信息路径消融：`raw`、`physics`、`taskattn`。
- 加资源和候选集合上下文：`resource`、`setctx`。
- 加 `no-idle` 变体，把 persistent Idle 屏蔽单独拿出来看。
- 加 finite idle timeout，把单次 Idle 最长等待限制到固定步数。
- 加 allocation diagnostics，统计 payload shortage / surplus、harmful idle、useful idle 等指标。
- 加特殊动作对照：clean no-idle、special action detached、split head、任务类型编码和 gate。

### 验证了什么

- HATT 的失败不只是“一次 Idle 卡太久”。有限 Idle 能把单次待命限制住，但 HATT 被唤醒后仍会继续选 Idle。
- Idle 确实是有害自由度。No-Idle 明显强于 Idle-25，说明当前策略没有合理利用 Idle。
- 特殊动作污染很关键。Explore / Idle / Resupply 和 Deliver 共用候选任务表示、共用联盟聚合时，会干扰配送任务关系建模。
- detached 之后 HATT best 明显改善，说明把特殊动作从配送联盟聚合中分出去是有效方向。

### 证伪了什么

- 单靠 finite idle timeout 不能修复 HATT。
- 单靠给 decoder 加 raw task row、物理量、资源阶段或候选集合上下文，还不能稳定解决 delivcoal。
- 单纯说“Idle 联盟上下文导致一切问题”也不够。清空 Idle 联盟上下文会降低 Idle 倾向，但差 checkpoint 仍可能继续 Idle。

### 剩下的问题

- 特殊动作 detached 能让 best 变强，但训练末期仍退化。
- MAPPO 在部分相同条件下仍比 HATT 更稳，说明 HATT 结构和 PPO 训练稳定性还有问题。
- 现在更像是找到了主要污染源，但还没形成最终稳定方法。

## 2. 探索为什么失败

`v0.14.0-v0.14.2` 把探索问题拆开看：到底是找不到任务，还是找到任务后不会配送。

### 做过的尝试

- sector 探索改成 agent-relative 表示：每个 agent 看到的是自己从当前位置出发会去的目标。
- agent 到达 sector 目标后重新决策。
- 发现新任务后打断旧探索动作，允许转去配送。
- 屏蔽地图边界处无效探索方向。
- 加 coverage 网格，记录地图哪些区域已经被搜索。
- Explore 观测加入候选路径的新覆盖面积、路径长度、全局未搜索比例。
- 加 all-visible 和 delivery-only 评估开关，把探索和配送拆开诊断。

### 验证了什么

- 原 sector 表示不够。它不能表达“当前 agent 走这个方向能搜到什么”。
- 直接前往最近未发现任务给了过强先验。改成真实方向探索后，任务明显更难。
- 到达探索目标后重新决策很关键，否则 agent 会继续执行已经没有意义的探索。
- coverage 是关键状态信息。HATT+coverage 的发现率、任务完成率、成功率都明显提高。
- RNN 单独不是主要解法。coverage 比 recurrence 更像是在补探索状态缺口。

### 证伪了什么

- “只要加记忆就能解决探索”不成立。
- “探索失败只是一个调度 bug”不完整。调度语义要修，但观测也缺信息。
- “找任务”和“配送”不能混在一起看。all-visible / delivery-only 结果显示，任务可见性变化会显著改变结论。

### 剩下的问题

- coverage 是近似网格，不是精确几何覆盖。
- HATT+coverage best 很好，但 last 仍会低于 best。
- 纯配送条件下仍有待命和补给调度问题。

## 3. Idle 是否应该被惩罚，是否需要预留空闲运力

`v0.14.4-v0.14.6` 进一步检查 Idle 的语义：agent 选 Idle 到底是错，还是有时应该主动保留空闲运力。

### 做过的尝试

- 在所有任务已知且禁止 Explore 的条件下，检查 agent 选择 Idle 时是否还能为未完成任务补足载荷。
- 把“可疑 Idle”从只看 Deliver 扩展到 Resupply：如果全局缺载荷、agent 空载且能补给，不应直接 Idle。
- 取消 Idle 额外时间惩罚，改成统一时间成本。
- 构造第二批任务延迟出现的场景，比较“多余 agent 原地待命”和“加入当前配送”。
- 改待命位置、加入短期服务承诺、调整卸货方式，检查 reserve 的物理收益来自哪里。

### 验证了什么

- 去掉 Idle 额外惩罚后，HATT best 明显改善。
- 但改善不是因为模型学会了合理 reserve。控制状态诊断里，HATT 仍不会稳定保留空闲运力。
- reserve 不是天然有价值。原地待命可能减少总路程，但不一定缩短完成时间。
- reserve 的收益依赖具体机制：等在哪里、是否有服务承诺、能耗预算和任务出现位置。

### 证伪了什么

- “HATT 差只是因为 Idle 被奖励函数惩罚”不成立。
- “应该鼓励长期空闲 reserve”没有被当前环境支持。
- “卸货方式导致载荷天然浪费”也被排除；按比例卸货和重新分配没有带来额外改善。

### 剩下的问题

- 如果以后要研究 reserve，需要先明确现实机制：待命位置、能耗、服务承诺、任务到达过程。
- 当前环境下不应把长期 Idle 当成显式目标。

## 4. 原文联盟重组机制是否有用

`v0.15` 开始重点转向原文动态联盟重组机制：智能体是否应该保持当前任务，还是在事件、周期或学习式 gate 下重新选择任务。

### 做过的尝试

- 复现多种重组方式：
  - 原有行为。
  - 每次都重选。
  - 固定周期重选。
  - 任务事件触发重选。
  - 任务紧迫度触发重选。
  - learned gate 学习何时保持或更换任务。
- 修复评估配置，保证 checkpoint eval 继承训练时的重组设置。
- 加显式 reconfiguration context，让 gate 看到新任务、任务完成、当前任务是否有效、距上次重选多久、episode 进度和紧迫度。
- 加 switch gate，让 gate 直接表示“离开当前任务”的概率。
- 加 gate override，用同一 checkpoint 做 force-open、force-closed、event、event_or_learned 干预。
- 加 hierarchical gate，把“是否重新决策”和“重新决策后选哪个任务”拆成两层动作。
- 保存 gate action，并在 PPO update 时重放，避免训练时重新采样 gate 导致 ratio 对不上 rollout 动作。
- 加 reconfiguration decision cost，筛选非强制状态下主动重选是否需要更稀疏。
- 加 gate metrics，统计 mandatory open rate 和 learned open rate。

这里的 hierarchical gate 仍然算 learned 方法。它不是像 event / periodic / urgency 那样用固定规则决定重组；它只是在建模上把 learned gate 拆成两步：先学“非强制状态下要不要重新决策”，再在开门后交给 HATT 选任务。mandatory open 是环境强制部分，不属于 learned；真正需要评估的是 learned open 是否真的带来收益。

### 验证了什么

- 简单 event 规则和原有 legacy 比当前 learned gate 更稳。
- learned gate 能减少“每次重选决策真的换任务”的比例，但没有换来更高成功率。
- 每次都重选会退化，容易频繁待命和少探索。
- 只加显式 context 不够。
- switch gate 更可解释，但成功率没有稳定提升。
- force-closed 很差，说明“保持当前任务”不是简单修复。
- hierarchical gate 是更干净的 learned 方法：gate action 可解释，PPO replay 对齐，也没有明显 Idle collapse。
- mandatory open 正常，说明强制重选链路执行没问题。
- learned open 偏保守，而且和成功率关系还不清楚。

### 证伪了什么

- “原文重组机制复现后自然提升”不成立。
- “重选越频繁越好”不成立。
- “给 gate 加事件信息就够了”不成立。
- “把 gate 改成可解释 switch 形式就够了”不成立。
- “cost0.05 单种子最好，所以就是最终方法”不成立；seed2 / seed3 没有复现更强优势。

### 剩下的问题

- hierarchical gate 现在只是更可解释、更正确的建模方式，还没有证明能提升成功率。
- learned gate 还没学会“什么时候主动重选真的有用”。
- event / legacy 仍是更稳的重组基线。
- 需要把 gate open 时刻和具体任务事件、任务完成、响应时间联系起来看。

## 5. 当前方法的大致算法流程

现在的主线可以用下面的流程理解。

### 观测

每个 agent 得到结构化观测：

- `ego`：自身位置、资源、当前任务、动作状态等。
- `others`：其他 agent 的状态。
- `tasks`：配送任务和特殊动作槽。
- `reconfiguration`：可选重组上下文，包括任务事件、当前任务有效性、时间间隔、episode 进度和 mandatory 标记。

### HATT 编码

HATT actor 不把所有东西压成一个扁平向量，而是按实体关系建模：

- agent-agent attention：看队友。
- task-agent attention：看每个任务和 agent 的匹配。
- coalition aggregation：看当前任务已有联盟成员。
- task decoder：对每个候选动作打分。

这套结构适合表达“哪个 agent 应该加入哪个任务联盟”。

### 特殊动作处理

Deliver 是真实配送任务，Explore / Idle / Resupply 是控制动作。后续结论说明，它们不应该无脑共享 Deliver 的联盟聚合。否则特殊动作会污染配送任务之间的关系。

### 探索 coverage

Explore 不再只是抽象方向，而是带有实际搜索收益：

- 这个方向能覆盖多少新区域。
- 要走多远。
- 全局还有多少区域没搜。

这样 HATT 能更早停止重复探索，把时间留给配送。

### hierarchical gate

如果启用 hierarchical reconfiguration：

1. 环境判断 agent 是否 active，需要重新请求动作。
2. actor 先采样 gate action：是否重新决策。
3. 如果 gate 关闭，保持当前任务。
4. 如果 gate 打开，再从 HATT task distribution 里选任务。
5. 当前任务完成、失效、全队停住等 mandatory 场景强制打开。
6. rollout 保存 gate action。
7. PPO update 时用保存的 gate action 重算 log prob。

这样做的好处是：gate 不再是从最终任务动作里猜出来的，而是 PPO 真正训练过的动作变量。

## 6. 现在还缺什么

- HATT+coverage 的训练稳定性仍未完全解决。best 可以很强，但 last / p90 仍会波动。
- learned reconfiguration 还没有超过 event / legacy。
- hierarchical gate 只证明了建模更清楚，没有证明最终效果更强。
- learned open rate 和成功率之间的关系还不明确。
- 纯配送场景里仍存在待命、补给和任务切换问题。
- reserve idle 的价值没有被当前环境自然支持，除非后续明确建模能耗、等待位置或服务承诺。
- 多 seed 证据还不够。很多判断已经有方向，但最终方法选择还需要更统一的统计口径。

## 7. gate 因果评估结论

同一 hierarchical checkpoint 只改变推理期 gate override 后，三 seed
best checkpoint 得到：

| 干预 | success | task completion | 联盟变化/100 alive steps |
| --- | ---: | ---: | ---: |
| event-only | 23.2% | 82.0% | 3.20 |
| event-or-learned | 18.5% | 80.9% | 3.66 |
| force-open | 19.8% | 80.1% | 4.04 |
| force-closed | 0% | 0% | 0 |

event-only 在三个 seed 上均优于 event-or-learned，说明当前 learned gate
的非事件开启总体有害。hierarchical gate 仍是语义和 PPO replay 更正确的
建模方式，但目前不能作为性能贡献；现阶段应使用 event 重组。

原 `gate_learned_open_rate` 只把 context[10] 视为 mandatory，却没有排除
event override 同时使用的 context[0/1/3]，因此 event-only 下仍会显示约
13%。该指标需要按完整 event trigger 修正，历史数值不能直接解释为真正的
off-event learned opening。

## 8. detached × coverage 组合结论

统一使用 sector、uniform idle cost 和 event reconfiguration 后：

| 方法 | best success | last success | best task completion |
| --- | ---: | ---: | ---: |
| plain | 37.5% | 22.4% | 86.7% |
| detached | 8.6% | 7.6% | 65.1% |
| coverage | 69.8% | 62.0% | 96.2% |
| detached + coverage | 60.2% | 60.9% | 94.3% |

coverage 是当前最强且跨 seed 可复现的组件。detached 在早期 nearest
配置下的收益不能迁移到多 sector 控制动作设置：它单独使用时明显退化，
加入 coverage 后也没有产生正交增益，并增加联盟切换。因此当前冻结的
delivcoal 主线是：

`HATT v2.0 + sector + coverage + uniform idle cost + event`

不加入 detached、learned gate、recurrence 或长期 reserve Idle。

需要特别注意：不同历史改进线曾在不同配置中分别验证，不能直接视为已经
组合验证的“当前方法”。下一项严格对照是同配置的 MAPPO coverage/event
三 seed；确认 HATT 优势后，再扩展 seed 4/5 和 capcoal、MRTA。

## 9. 一句话总结

`v0.12` 以来的工作逐步拆解了 delivcoal 的信息、探索、特殊动作和重组问题。
目前最可靠的性能来源是 coverage；event 是比 learned gate 更稳的重组方式；
detached 的早期收益不具备 sector 配置下的可迁移性。下一步必须用完全匹配的
MAPPO 基线确认 HATT 的结构优势，而不是继续叠加尚未稳定的组件。
