# 预约/改约日程或会议、查询/搜索可用会议室的工作流

## CRITICAL 执行摘要（先按这个骨架执行，再看下方细则）

- **第一步永远是判断任务类型：新建日程，还是编辑已有日程。** 不要把“预约/查会议室”默认等同于“新建”。
- **编辑已有日程时，必须先定位目标日程或实例的 `event_id`。** 用户一旦给出了既有日程锚点（标题、时间段、`这个日程`、`这场会`）并表达修改动作（加人、删人、改时间、换会议室等），默认走编辑流。
- **默认做智能助理，不做表单填写机。** 能根据上下文补全的默认值就直接补全，避免把用户带入表单式问答。
- **新建流先补默认值，编辑流先继承已定位日程信息。** 默认值包括标题、参会人、时长，以及在“完全无时间信息”时的默认时间范围；编辑流则优先复用已定位日程的标题、时间、已有参与人和会议室信息作为基线。
- **只有三类场景才主动追问用户**：存在时间冲突、搜索结果无法唯一确定、时间语义本身有歧义。
- **编辑流的时间基准必须明确。** 如果编辑时不改时间，则后续会议室搜索必须基于已定位日程的原始起止时间；如果既改时间又加会议室，必须先确定最终时间，再基于该时间搜索会议室。
- **编辑流中“新增会议室”默认是增量语义。** 如果用户说的是“加会议室/再加一个会议室”，最终 `+update` 只做 `add`，默认保留已有会议室；只有在用户明确说“更换会议室/移除会议室”时，才执行旧会议室删除。
- **明确时间**：若需要会议室，先 `+room-find`；再 `+freebusy` 判断参会人忙闲；有冲突时先说明冲突，再让用户决定继续当前时间还是改走 `+suggestion`。
- **模糊时间或无时间信息**：先 `+suggestion` 产出候选时间块；若需要会议室，再把这些时间块批量交给 `+room-find`，将“候选时间 + 对应可用会议室”一次性展示给用户选择。
- **BLOCKING REQUIREMENT: 只要面临时间方案（模糊时间/无时间）或会议室方案（需要会议室）的选择，必须先向用户展示选项并等待用户明确确认，绝对禁止在未获用户确认的情况下直接执行创建新日程或更新既有日程。**
- **用户选中了 `+suggestion` 返回的候选时间块后，不要再次调用 `+freebusy`。** 用户确认后直接进入最终落地操作：创建新日程，或更新既有日程。
- **当用户说“查会议室”“找会议室”“搜可用会议室”时，默认意图是查会议室可用性，不是检索会议室资源名录。**
- **必须按顺序执行。** 不要跳过“任务类型判定”“目标日程定位（编辑流）”“补默认值/继承基线信息”“判断时间明确性”这些前置步骤。

> **💡 核心原则：做智能助理，充分利用默认值规则（如默认标题、时长、参与人等）自动补全信息。极力避免像“表单填写机”一样频繁打断并反问用户，仅在必须决策的冲突或无法唯一确定的场景下才发起询问。**

## 严禁行为

- **严禁在未读取对应子命令文档（如 `lark-calendar-room-find.md`、`lark-calendar-suggestion.md`）的情况下直接调用命令！** 必须先阅读文档掌握最新参数要求与规范。
- **严禁在尚未判断“新建”还是“编辑”之前，就直接进入创建日程或查会议室动作。**
- **严禁把“给明天上午的‘产品发布会’加人/加群/加会议室”这类带有既有日程锚点 + 修改动词的请求，当成新建日程。** 这类请求必须先定位目标日程。
- **严禁在编辑已有日程时跳过目标定位步骤。** 未拿到唯一的 `event_id` 前，不得调用 `+update`、也不得基于猜测时间去查会议室。
- **严禁在用户仅要求“查会议室”但未提供明确时间时，直接调用 `+room-find`！** 必须先默认一个合理时间范围，调用 `+suggestion` 拿到候选时间块，再将时间块传给 `+room-find`。
- **不要在用户完全没给时间时，直接反问“你想约什么时候”。** 先补一个合理时间范围，再进入 `+suggestion`。
- **不要在“需要会议室 + 时间模糊”的场景下，先让用户只选时间。** 应先批量查出每个候选时间对应的可用会议室，再让用户一次性完成选择。
- **不要在用户已经选中 `+suggestion` 候选时间后，再重复调用 `+freebusy`。**
- **不要在用户未明确说出城市时，仅凭园区/办公室名自动补城市。**
- **严禁在面临时间方案或会议室方案的选择时（模糊时间、无时间或需要会议室），未经用户确认就擅自创建新日程或更新既有日程。**

## 适用场景

- “帮我约个会”
- “下周找时间和 XX 开会”
- “帮我订个会议室”
- “帮我找/搜索一个可用的会议室”
- “帮我推荐一个我以前常用的会议室”
- “查询明天下午可用的会议室”
- “明天下午3点约个日程/日历”
- “把明天上午的日程‘产品发布会’加上 小明
- “给下周一的周会换个会议室”
- “把这个日程改到明天下午，并加上学清 F201”

## 核心概念

- **会议室是日程的一种参与人（attendee / resource），不能脱离日程单独预定。** 
- **预定或查找会议室，均需先确定时间块。** 在推荐可用会议室后，应顺势引导用户完成最终的**日程落地**操作：创建新日程，或更新既有日程。

## CRITICAL 约束

- **在调用任何具体的 CLI 子命令（如 `+room-find`、`+suggestion`、`+freebusy`、`+create`）前，必须先读取其对应的 Markdown 文档。** 禁止仅凭记忆组装命令参数，以确保符合各命令最新的业务约束和格式规范。
- **当用户说“查会议室”“找会议室”“搜可用会议室”等，默认意图是查询会议室可用性，而不是检索会议室资源名录。**
- **必须严格按照下方【工作流】的步骤顺序完成任务。特别是单独查会议室时，若无明确时间，强制先走“模糊时间/无时间信息”分支调用 `+suggestion`。**

## 任务类型判定

| 类型 | 典型语言信号 | 第一动作 |
|------|--------------|----------|
| 新建日程 | “约个会”“安排一个会议”“新建日程”“帮我订个会议室开会” | 补默认值，再进入时间判断 |
| 编辑已有日程 | “给某个日程加人/删人/加群/加会议室”“把某个日程改到…”“给这场会换个会议室” | 先定位目标日程 `event_id`，再进入后续流程 |

进一步规则：

- 只要同时出现**既有日程锚点**（标题、时间段、`这个日程`、`这场会`、某次实例）和**修改动词**（添加、移除、调整、改到、换、延后、提前），默认判定为**编辑已有日程**。
- 对重复性日程的编辑，必须先定位到对应实例的 `event_id`，不能直接拿原重复日程的 `event_id` 做更新。

## 工作流

### 1. 编辑已有日程：先定位目标日程

一旦判定为编辑流，必须先定位目标日程；没有 `event_id` 就不能继续后续修改动作。

定位规则：

- 优先利用用户给出的标题、日期、时间范围、`这个日程/这场会` 等锚点，通过 `+agenda`、`events search_event` 或实例视图缩小范围。
- 如果命中多个候选日程，必须向用户展示候选项并要求确认，禁止自行猜测。
- 如果是重复性日程的某一次实例，必须继续定位到该次实例的 `event_id`。

编辑流分支规则：

- **仅增删普通参会人/群组，不改时间，也不涉及会议室**：定位完成后可直接进入最终 `+update`。
- **新增会议室，但不改时间**：必须基于已定位日程的当前 `start/end` 作为时间块执行 `+room-find`，不能因为用户没重复说时间就退回“无时间信息”。
- **既改时间，又新增会议室**：必须先处理时间，拿到最终候选时间块后，再基于该时间执行 `+room-find`；最终只增量添加新会议室，不自动删除已有会议室。
- **既改时间，又更换会议室**：必须先处理时间，拿到最终候选时间块后，再基于该时间执行 `+room-find`；只有在用户明确表达“更换”时，最终才执行“移除旧会议室 + 添加新会议室”。
- **只改时间，不涉及会议室**：沿用下方时间工作流，但最终落地必须是 `+update`，不是 `+create`。

### 2. 新建日程：智能推断默认值
以下信息智能推断，减少频繁询问用户：

- **标题**：根据上下文自动生成，例如“沟通对齐”“需求讨论”；如无法推断，默认为“会议”
- **参会人**：如未明确指定其他人，默认参会人仅为**用户自己**
- **时长**：基于会议类型和上下文动态推断；如无法推断，默认为 30 分钟
- **无任何时间信息**：默认推断一个合理区间（如“今天”或“近两天”），并进入时间推荐流程，禁止询问用户

当搜索特定参与人（人、群）出现多个结果无法唯一确定时，必须询问用户进行选择确认，并将该偏好记录为长期记忆，以便后续自动识别。

### 3. 判断时间是否明确

这一步判断的是**最终要落地的目标时间**，不是只看用户原句里有没有重复说时间。

时间基准规则：

- **新建流**：使用用户给出的时间，或默认补全出的时间范围作为时间基准。
- **编辑流且不改时间**：已定位日程的当前 `start/end` 就是时间基准。后续如需查会议室，直接使用这个明确时间块。
- **编辑流且改时间**：用户想改到的新时间才是时间基准；若表达模糊，则进入 `+suggestion`。

分两类处理：

- **明确时间**：如“明天下午3点”
- **模糊时间**：如“明天下午”“下周找个时间”

### 4. 明确时间

明确时间时，需先判断是否需要会议室，如果需要，提前查询会议室；然后判断是否有时间冲突。这里的“明确时间”既可以来自用户直接表达，也可以来自已定位日程的原始时间。
详见 [`+room-find`](./lark-calendar-room-find.md) 与 [`+freebusy`](./lark-calendar-freebusy.md)。

```bash
# 1. 如果需要会议室，提前查询会议室
lark-cli calendar +room-find \
  --slot "<start>~<end>" \
  --attendee-ids "<ids>" \
  --city "<city>" \
  --building "<building>" \
  --floor "<F2>" \
  --room-name "<room_name>"

# 2. 查询当前用户及其他参会人忙闲
# （如果有多名参会人，需分别调用查询：--user-id "<ou_xxx>"）
lark-cli calendar +freebusy --start "<start>" --end "<end>"
```

规则：

- **参会人过多或包含群组时的处理**：
  - 如果参与人过多（例如超过 5 人），为避免高耗时，仅需查询**当前用户（自己）**及少数核心人员的忙闲状态即可。
  - 如果参与人中包含**群组**，无需展开群组成员查询其忙闲状态。
- **编辑已有日程且不改时间，只新增会议室时**：这里的 `--slot` 必须来自已定位日程的当前 `start/end`。
- **编辑已有日程且既改时间又加会议室时**：这里的 `--slot` 必须来自候选新时间，而不是旧时间；如果用户是“新增会议室”，后续落地只做添加，不删除旧会议室。
- **如果没有冲突**：直接让用户选择会议室（如需），然后进入最终落地操作：创建新日程，或更新既有日程
- **如果有冲突**：必须先说明冲突情况，询问用户继续选择这个时间还是换个时间
  - **如果说换个时间**：放弃当前时间，转入【模糊时间】流程，调用 `+suggestion` 推荐多个可用时间块
  - **如果继续选择这个时间**：直接让用户选择会议室（如需），然后进入最终落地操作：创建新日程，或更新既有日程
- 位置信息要优先拆到结构化字段：用户明确说了城市才提取 `--city`；`--building` 不要再重复携带城市前缀。
- 参数归类顺序应为：`city/building/floor` > `floor + room-name` 复合表达 > `room-name`。像 `2L`、`2F` 这类更像楼层或区域定位的短词，优先视为 `--floor`，不要默认当作 `--room-name`。像 `学清2层` 这种表达，通常拆为 `--building "学清"` 与 `--floor "F2"`。
- 会议室名要做轻量归一化：`木星会议室` -> `--room-name "木星"`；`会议室 02` / `02会议室` -> `--room-name "02"`。
- 对 `F3-05` / `F5-07` / `3楼-08` 这类复合表达，若能稳定识别楼层与会议室号，应优先提取为 `--floor + --room-name`，不要把整段直接退化成 `--room-name`。

### 5. 模糊时间或无时间信息

先调用：
详见 [`+suggestion`](./lark-calendar-suggestion.md)；若需要会议室，再结合 [`+room-find`](./lark-calendar-room-find.md)。

```bash
lark-cli calendar +suggestion \
  --start "<range_start>" \
  --end "<range_end>" \
  --attendee-ids "<ids>" \
  --duration-minutes <n> \
  --event-rrule "<rrule>"
```

规则：

- 若用户完全没有提供时间信息，应先默认一个合理区间后再调用 `+suggestion`
- 编辑流中，若用户表达的是“改到明天下午”“下周找个时间再约”这类模糊新时间，则基于用户期望的新时间范围调用 `+suggestion`；不要继续沿用旧时间。
- **不需要会议室**：获取多个推荐时间块后，直接向用户展示候选时间，用户确认后进入最终落地操作：创建新日程，或更新既有日程。
- **需要会议室**：获取多个候选时间块后，**不要急于让用户选时间**。先将这些时间块一次性交给 `calendar +room-find` 批量查询可用会议室，然后将【候选时间】与【对应的可用会议室列表】结构化分行展示，让用户一次性完成选择。（**注意：即使用户最初只说“查会议室”，且未带时间，也必须强制走到这一步，先 suggestion 再 room-find**）。
- 用户一旦选择了 `+suggestion` 返回的时间块，**无需再次调用 `+freebusy`**

### 6. 模糊语义消解与长期记忆构建

针对用户专属的时间表达习惯或存在歧义的时间场景，严禁主观臆断。典型例子包括：

- “上班后”
- “下班前”
- 未明确上下午的 12 小时制时间表达

处理规则：

- 应主动澄清真实意图，而不是自行猜测
- 当用户给出澄清后，应将这类个性化定义沉淀为长期偏好，推动后续直接理解类似表达

### 7. 重复性日程

若当前会议为重复性日程，调用 `+room-find` 时需携带 `--event-rrule`。

必须检查返回中的：

- `reserve_until_time`

若候选会议室的可预约上限早于重复规则覆盖范围，**不要直接按原规则落地日程**。应：

- 向用户明确说明该会议室最长可约至何时。
- 若用户确认继续选用该会议室，你必须**自动将日程的重复规则结束时间缩短**至该 `reserve_until_time`，以防止会议室预约失败。

### 8. 落地日程变更

用户确认后调用：
如果是新建会议，详见 [`+create`](./lark-calendar-create.md)。
如果是更新既有日程，详见 [`+update`](./lark-calendar-update.md)。必须先定位目标 `event_id`，再按用户意图用 `+update` 独立执行字段更新、添加参会人/会议室、移除参会人/会议室，或组合这些动作。若用户意图是“新增会议室”，默认仅追加 `room_id`，不移除已有会议室。

```bash
lark-cli calendar +create \
  --summary "..." \
  --start "<start>" \
  --end "<end>" \
  --attendee-ids "ou_xxx,oc_xxx,omm_xxx"

lark-cli calendar +update \
  --event-id "<event_id>" \
  --start "<start>" \
  --end "<end>" \
  --add-attendee-ids "omm_new_room"

# 仅当用户明确要求“更换会议室”时，才同时移除旧会议室并添加新会议室
lark-cli calendar +update \
  --event-id "<event_id>" \
  --remove-attendee-ids "omm_old_room" \
  --add-attendee-ids "omm_new_room"
```

规则：
- 新建日程时，可使用 `+create`
- 更新既有日程时，优先使用 `+update`。改时间/标题/描述、添加参会人/会议室、移除参会人/会议室可以分别独立执行；
- 编辑流必须始终沿用前面定位得到的目标 `event_id`；禁止在最后一步重新按标题猜测一次目标日程。
- 编辑流中如果只是新增群组或普通参会人，不涉及时间和会议室，可直接 `+update --add-attendee-ids ...`。
- 编辑流中如果是“新增会议室但不改时间”，必须先基于目标日程原始时间查到可用会议室，再 `+update --add-attendee-ids "<room_id>"`；默认保留已有会议室。
- 编辑流中如果是“既改时间又新增会议室”，顺序必须是：先确定最终时间，再查会议室，最后一次性 `+update` 时间与新增会议室；默认保留已有会议室。
- 编辑流中如果是“既改时间又更换会议室”，顺序必须是：先确定最终时间，再查会议室，最后一次性 `+update` 时间、移除旧会议室并添加新会议室。
- 需要会议室时，将选中的 `room_id` 写入最终落地请求的参与人列表
- 展示会议室候选时，必须保留 CLI/API 返回的完整 `room_name` 原值；允许附加“推断说明”，但禁止用摘要名、楼层及会议室号、容量/视频标签重组后的名称替换原值

## 用户展示建议

当向用户展示多个时间块及对应的多个会议室时，**必须使用结构化清晰的格式排版**。**严禁将时间与会议室名称放在同一行展示**，必须分行并使用编号列表呈现可用会议室，严禁将所有信息揉成一团纯文本堆叠。

**推荐展示格式参考：**

```text
## 2026-03-27 周五

[选项 1] 14:00 - 15:00（参会人均空闲）
  可用会议室：
  1. 学清嘉创大厦B座-F2-02🎦(7人)
  2. 学清嘉创大厦B座-F2-05🎦(10人)

[选项 2] 16:00 - 17:00（参会人均空闲）
  可用会议室：
  1. 学清嘉创大厦B座-F3-01🎦(6人)
  2. 学清嘉创大厦B座-F3-06🎦(8人)

💡 请回复您倾向的选项编号以及对应的会议室序号，我来为您完成预定。
```

## 参考

- [lark-calendar-room-find.md](./lark-calendar-room-find.md)
- [lark-calendar-freebusy.md](./lark-calendar-freebusy.md)
- [lark-calendar-suggestion.md](./lark-calendar-suggestion.md)
- [lark-calendar-create.md](./lark-calendar-create.md)
- [lark-shared](../../lark-shared/SKILL.md)
- [lark-calendar](../SKILL.md)
