用途
会议日程优化(Conference Schedule Optimization)把“该参加哪些场次”当作一个约束优化问题来处理,而不是列一张愿望清单。活动是变量,时间段是它们的取值域;硬约束定义什么是可行的,加权目标定义什么是好的。最终输出是一份最优可行方案,并诚实说明它无法决定的部分以及为了得高分而放弃了什么。
主要能力
硬约束与软目标分离
硬约束(可行性,绝不违反):任意两个选择在时间上不重叠;不同房间的相邻选择之间至少间隔房间间步行时间(来自配置);容量受限的场次只有在参会者能现实地获得座位时才计入;来自偏好档案的硬约束(必参加场次强制纳入,禁排/保留空闲块强制排除)。违反硬约束的方案不是更差的方案,而是根本不算方案——先过滤,再优化。
软目标(质量,最大化):加权求和。
四项相互冲突的加权目标
方案得分 = w_interest·兴趣 + w_breadth·广度 + w_pacing·节奏 + w_serendipity·偶遇性,权重来自偏好档案:
- 兴趣:所选场次的参会者兴趣之和(主题亲和度 × 区域),即“看我关心的内容”项。
- 广度:跨不同主题的覆盖度(同一主题第 4 场收益递减),即“不要钻牛角尖”项。
- 节奏:奖励连续空闲时间、惩罚碎片化——一个真正 90 分钟的休息优于六个零散的 15 分钟间隙,即使场次数量相同;同时惩罚偏离偏好时段和过多步行。
- 偶遇性:奖励刻意留白和偶尔纳入离群/探索性选择,使方案不是一台最大化塞满的兴趣机器。
最大化连续空闲时间
把空闲时间建模为块,奖励最长的连续块,把真正的休息当作目标中的一等收益,而不是剩余空间。六个不相邻、各带 15 分钟间隙的场次,比同样六场但聚在一起并留出真正休息的一天更糟——人需要连续恢复、走廊交谈和吃饭。
暴露无法打破的冲突
当两个(或多个)场次重叠、得分在 epsilon 内接近、且偏好档案无法排序时,在 unresolved_conflicts 中标记两个候选及模型无法打破平局的原因,而不是抛硬币并隐藏。当档案能打破平局时,选择并在 alternatives 中记录落选者及 why_not。
求解方法
默认(无求解器依赖):贪心种子(按时间顺序为每个时段选最佳可行项)加局部搜索——在保持可行的前提下做交换/插入/删除以改进加权目标。快速、透明,对几百场活动的议程足够好。resources/schedule.py 仅用 numpy/pandas/networkx 实现。
精确(可选):安装 pulp/ortools 且需要最优解时,可形式化为 ILP 求解。记录实际运行的方法到 method 字段。
报告权衡(Goodhart 诚实原则)
无论权重如何,方案都会牺牲权重低估的东西。在 tradeoffs_note 中说明,例如“偏向兴趣,因此第 2 天密集且休息短”或“节奏/广度加权意味着你兴趣最高的那场被放弃以换取覆盖度”。命名这种替代效应,是优化代理指标却不假装它就是目标的解药。
使用方式
工作流:
- 加载 events.json、affinities.json、profile.json;从配置加载房间、步行矩阵和每日时间边界。
- 强制纳入档案中的必参加场次;强制排除禁排/保留空闲块。
- 构建可行性:每个时段中不重叠、满足步行时间、可入座(容量)的场次集合。
- 为参会者给每个场次打分(亲和度 × 区域兴趣、深度/形式匹配)。
- 贪心播种一个可行方案;运行局部搜索以最大化用户加权目标。
- 检测顶级候选在 epsilon 内打平且档案无法排序的重叠 → unresolved_conflicts(不自动选择)。
- 计算 free_blocks(奖励最长连续块)和 objective_breakdown。
- 写出 schedule.json + schedule.md(逐日)+ conflicts.md(决策)+ rationale.md(选择、目标分解、权衡说明)。返回 schedule.json 路径。
输出契约(schedule.json)字段包括:generated_on、method、selections(含 day/start/end/event_id/room/score/why/alternatives)、free_blocks(含 purpose:break | buffer | travel | meal)、objective_breakdown(interest/breadth/pacing/serendipity/total/weights)、unresolved_conflicts(含 candidates/why_unbreakable/needs)、constraints_applied(travel_time/capacity/hard_constraints)、tradeoffs_note。
适用场景
- 参会者面对多轨道、多房间、容量受限工作坊的会议议程,需要一份真正可执行的个人日程。
- 需要在兴趣、主题广度、休息节奏和留白之间做明确权衡取舍的场合。
- 需要把无法自动决定的冲突交回给人、而非被静默处理的决策场景。
限制与护栏
- 权重属于用户:从档案读取 objective_weights,绝不自行发明或覆盖;警惕编译进优化器的默认权重向量。
- 暴露冲突而非隐藏:epsilon 内打平且档案无法排序 ⇒ 写入 unresolved_conflicts,不做选择。
- 保护连续空闲时间:奖励最长连续空闲块,把真正休息当作一等目标项。
- Goodhart:在 tradeoffs_note 中说明加权牺牲了什么。
- 硬约束不可侵犯:优化前先过滤到可行;必参加强制纳入,禁排强制排除。
- 不静默截断、不重新引导、不擅自执行:记录任何候选上限;不修改档案;仅在明确指示时导出到日历。
相关技能
- conf-preference-elicitation:提供区域、轴偏好、权重和硬约束,为本优化提供参数。
- conf-theme-clustering:提供兴趣项和广度项所用的亲和度。
- decision-matrix:把暴露的冲突候选结构化呈现给人类的方式。
- expected-value:用于权衡容量有风险的工作坊(可能满员)与有保证的替代项。
- systems-thinking-leverage:激发 tradeoffs_note 的 Goodhart/替代效应视角。
示例
示例 1:暴露的冲突。第 2 天 11:10–11:30 有两场得分 0.81 和 0.79 的演讲——一场关于 Agents,一场关于 Retrieval,都接近参会者兴趣,且档案未将 Agents 排在 Retrieval 之上。优化器不做选择,而是写入一条 unresolved_conflicts 条目,列出两者及“均在 epsilon 内;档案未对这些主题排序”,needs: your decision。
示例 2:同等兴趣下节奏胜过密集。两个可行方案在兴趣上打平。方案 A:六场演讲,整个下午间隙 15–20 分钟。方案 B:五场演讲加一个连续的 80 分钟下午块。在参会者偏节奏的权重下,方案 B 得分更高——长休息是被建模的收益而非浪费空间——tradeoffs_note 记录放弃一场较低兴趣的演讲以换取休息,这正是此人要求的取舍。