# ADR-001: CA / VNS 平级并行架构 ## 修改记录 | 日期 | 变更 | |------|------| | 2026-07-18 | 修正 status(已替换→已采纳)、补充交叉引用、ADR-001++ 表格化 | ## ADR-001++ | 状态 | 项 | 说明 | 验证 | |------|-----|------|------| | ✅ | deep 参数校验 | deep + n_days=None 直接报错 | `search.py:311-312` | | ✅ | k→n_days 重命名 | 天数参数统一为 n_days | 全仓库确认 | | ✅ | 增益阈值早退 | 连续 3 次改善 < 1.0% 停止 | `search.py:210-224` | | ✅ | 去重标准化 | frozenset 分组去重,先排序后保留 | `search.py:130-140` | | 🚧 | 建议量化指标 | avg_daily_hours 等指标辅助选天数 | 未实现 | | 🚧 | 快慢模式基线对齐 | fast 达 VNS 70-80%,deep 追上限 | 骨架就绪无对齐逻辑 | | ⏸ | 策略模式重构 | 三层解耦,等 4-5 种模式时落地 | 完全未引入 | | ⏸ | 多目标返回 Top-3 | deep 模式输出多方案 | 只返回单最优 | ## 状态 **已采纳**。 废弃 SA + VNS 串行流水线,改用 CA / VNS 平级并行。 > 原串行方案:SA 全参数搜索(遍历 6 种聚类方法 × k 值)找到最优分组,再将每组初始解传递给 VNS 精炼。实验数据表明 SA 的评分不能代表 VNS 的潜力——SA(k=1)=955.4 而 VNS(k=1)=620.3(理论最优),串行让低精度算法替高精度算法做决策,埋没了 VNS 的最优性。 ## 一、设计原则 ### 双引擎平级并行 CA(压缩退火)和 VNS(变邻域搜索)是**平级选项**,而非串行流水线。 | 引擎 | 位置 | 定位 | 精度 | 速度 | |------|------|------|------|------| | **CASolver** | [`engine/ca.py`](../structure/backend.md#六求解引擎层-engine) | 快速预览 | 距最优 6-10%(与 GA/ACO 同级) | 秒级 | | **VNSSolver** | [`engine/vns.py`](../structure/backend.md#六求解引擎层-engine) | 深度优化 | 可达理论最优 | 分钟级 | CA 的精度上限决定了它不适合替 VNS 做决策。CA 的价值在于快——秒级出方案让用户预览行程结构;VNS 的价值在于准——追求极致精度。两者面向不同场景,没有上下级关系。 ### n_days 状态闸门 天数参数是整个流程的状态闸门(完整流程见 [`backend.md §九 数据流图`](../structure/backend.md#九数据流图)): ``` 未指定 n_days ──→ ca_suggest()(CA 快速建议) 指定 n_days ──→ cluster_and_solve(mode="fast"/"deep") mode="deep" + 无 n_days ──→ 直接报错 ``` ## 二、关键设计决策 ### 2.1 早退机制 从"连续无改善计数"改为**增益阈值式**([源码 `search.py:210-224`](../structure/backend.md#六求解引擎层-engine)): - 改善 $\ge$ `early_stop_gain_threshold`(默认 1.0%)→ 有意义,重置计数器 - 改善 < 1.0% → 算无效,`worse_count += 1` - 连续 3 次无效 → 停止该聚类方法的搜索 - 参数托管在 `CA_DEFAULT_PARAMS` 中 ### 2.2 去重逻辑 [`_deduplicate()`](../structure/backend.md#六求解引擎层-engine) 基于 `frozenset(frozenset(g) for g in groups)` 去重。 **先去重后排序 → 先排序后去重**:不同聚类方法可能产出相同分组方案,但 CA 有随机性,靠后运行的方法成本可能更低。因此改为**先按 cost 排序**,再**去重保留第一个**(即成本最低的),确保每个分组下保留最优解,而非方法注册表顺序优先。 ### 2.3 命名规范 | 旧命名 | 新命名 | 说明 | |--------|--------|------| | SASolver | CASolver | 正名为压缩退火,不是标准 SA | | k | n_days | 天数语义,不叫"聚类数" | | min_clusters / max_clusters | min_days(默认上限 n_spots) | 对应天数范围,已无 max_days 参数 | | best_k | best_days | 最优天数 |