会议室匹配与平替推荐 · 优化方案(讨论稿)
状态:方案讨论稿(待评审,短期不排期) | 范围:对话端会议预定 · 指定会议室匹配与占用平替
关联:会议室推荐算法逻辑、SA_空间预约_物理执行逻辑、实体检索策略方案、SA_会议预定_设计实现偏差问题说明
定位:本文针对"用户指定会议室"链路的匹配不准、占用不可判、无平替三个问题给出优化方案,并记录讨论中形成的顾虑与替代路径。本文为设计讨论稿,短期内不实施(核心顾虑见 §5)。
0. 一句话
现状用单一相似度分数 + 阈值去同时回答"是不是这一间 / 能不能订 / 换哪间"三个性质不同的问题,导致阈值怎么调都不对、占用判断做不出、平替缺失。本方案把三者拆开:目标识别交给 LLM 裁决节点,可用性交给带状态的后端接口,平替交给锚点父链的结构化查询。
1. 现状与根因
1.1 现状链路(Dify 会议预定)
1.2 三个已核实的事实
| # | 事实 | 出处 |
|---|---|---|
| 1 | "找到 / 没找到"由 resolve 的 score 判定(space_id_score_flag),不是由可用性结果判定 | 会议预定.yml:847-849, 1007 |
| 2 | 前端判定阈值 = 0.5(_DEFAULT_SPACE_ID_SCORE);Dify 发给后端的 scoreThreshold = 0.2 | 会议预定.yml:805, 765 |
| 3 | Dify 解析 resolve 响应**只读 spaceId 与 score**,无路径 / 父节点 / 容量 / 占用 | 会议预定.yml:843-849 |
阈值在 0.5 / 0.6 / 0.8 之间反复漂移,本身就是"调不准"的直接证据。
1.3 根因
一个标量分数被要求回答三个不同的问题:
| 问题 | 性质 | 真正需要什么 | 现状用什么 |
|---|---|---|---|
| 是不是用户要的那一间 | 离散语义判断 | 精确名录 / 结构化 key / 语义理解 | 连续相似度分数 + 阈值 |
| 这一间能不能订 | 时间相关查询 | 目标已确定 + 忙闲接口 | 空结果反推 |
| 换哪一间 | 结构化检索 | 锚点属性 + 邻居查询 | 无(缺失) |
由此派生三个直接后果:
- 阈值不可调:
803与804在向量空间近乎同分布;同一分数在"订 803"与"三楼那个大的"两种意图下含义相反;无 ground truth 可回归。 - 占用不可判:占用判断的前提是"目标已确定",而 score 只给排序列表、不给目标。top1 错,查的就是错房间的占用。
- 平替缺失(对应偏差文档 §3.3,P0):named path 用
locationIds硬过滤到解析出的房间,被占 → 后端返回空 → 直接"未能匹配",不做同规格二次检索。
2. 设计目标与约束
目标
- G1 准确识别用户真实目标;识别不出时明确收敛或追问,不假装识别成功
- G2 目标不可用时,给出就近、同规格的平替
- G3 不显著增加端到端延迟
约束
- C1 不能依赖 Exact 短路作为主干:
room_name经 LLM 槽位改写后,能精确命中名录是低概率事件 - C2 空间树层级不固定:园区 / 楼栋 / 楼层 / 虚拟分区(行政区、工位区等)/ … 每项目配置不同,层数无统一规范,无法在 slot 阶段按固定 schema 类型化抽取
- C3 空间是"控错 = 安全事故"的场景(检索方案 §二),不能瞎猜
3. 前置依赖:后端接口契约(共同前置)
⚠️ 本节不落地,主方案与降级方案都无法实施。 这是本方案的第一优先项,且与是否加 LLM 无关。
| 接口 | 现状 | 需要补充 |
|---|---|---|
/internal/spaces/resolve | 返回 spaceId + score | ① 可读渲染路径(如 园区A/3号楼/8楼/行政区/803) ② 父节点标识(parentId) ③ 结构化属性(capacity、capabilities) ④ 匹配类型(exact / fuzzy,可选) |
list_available_meeting_rooms | 只返回空闲房间 | ① 返回带 occupied 标记(或新增单点忙闲查询 spaceId → {exists, occupied}) ② locationIds 支持父节点级过滤(传楼层 / 分区 id,返回其下全部房间) |
为什么路径是硬要求:LLM 裁决节点拿到 5 个裸 spaceId 是无法判断哪个是 803 的,它必须看到渲染后的路径串才能理解"这间在 8 楼行政区"。降级方案(字符串匹配)同样依赖路径。
4. 主方案:LLM 裁决节点 + 父链平替
4.1 链路总览
4.2 LLM 裁决节点
输入:用户原话 + 候选集(每项含 spaceId / 渲染路径 / capacity / capabilities / score)+ 会议需求(人数、时间)
输出(结构化枚举,非自由文本):
| 字段 | 取值 | 说明 |
|---|---|---|
resolution | EXACT / AMBIGUOUS / NONE | 目标确定 / 多个近似需确认 / 无法定位 |
target_space_id | string | EXACT 时给出 |
candidates | string[] | AMBIGUOUS 时给出待确认项 |
reason | string | 便于回归与解释 |
分流:
EXACT→ 锁定目标,进入可用性判断AMBIGUOUS→ 追问("你指的是 8 楼行政区那间 803,还是 9 楼的 803?")NONE→ 走容量推荐 + 追问,不做占用判断(目标未定,占用问题不存在)
设计要点:用枚举而非自由文本,是为了可测、可回归、可解释——正好治现状"没法调"的病。
4.3 占用冲突与平替(父链阶梯)
触发条件统一为"锚点不可用",不止"被占用":
| 触发 | 现状行为 |
|---|---|
| 锚点房间不存在(短路未命中真实实体) | 空 → "未查询到" |
| 锚点房间被占用 | 空 → "未能匹配" |
| 锚点容量 / 设施不满足 | 空 → "未能匹配" |
平替逻辑:沿父链逐级上跳(本方案核心思路)
空间树层数不固定(C2),因此不预设"楼层"这一层级类型,而是沿锚点的真实父链向上找兄弟节点:
锚点 = 803(parent = 8楼/行政区)
├─ 1. 同一 parent 下的兄弟房间,容量 ≥ 需求,按 |容量 − 需求| 升序,取 Top3
├─ 2. 同 parent 无合适 → 上跳到 grandparent(8楼)下的兄弟,重复
├─ 3. 继续上跳(3号楼 → 园区A),重复
└─ 4. 到顶仍无 → 跨楼 / 跨园区按容量兜底;再无 → 如实告知优点:不依赖"楼层"语义,天然适配任意层深与虚拟分区(行政区、工位区等)。"同一父节点下的兄弟"即等价于"同层 / 同区",无需知道那层叫什么。
排序:容量适配度 |RoomCap − UserCap| 升序(沿用推荐算法 §2.2 思想),保证"大小相近"。
4.4 状态与话术
话术遵循智能体统一人设:不用「您好」、第一人称、去工程词。
| 场景 | 状态 | 示例话术 |
|---|---|---|
| 目标确定且空闲 | SUCCESS | "803 这个时段空着,帮你订?" |
| 目标确定但被占 | CONFLICT | "803 被占了,同层还有 805、806 空着,你看要哪间?" |
| 目标不明确 | AMBIGUOUS | "你指的是 8 楼那间 803 吗?" |
| 无法定位 | NONE | "没找到你说的房间,能说下楼层或大概位置吗?" |
5. 顾虑点与待决(重要)
本节是本方案短期不实施的主要原因,也是后续评审的重点。
| # | 顾虑 | 说明 | 可能应对 |
|---|---|---|---|
| 1 | 性能 | 新增 LLM 节点 = +1 次模型往返,端到端延迟上升 | 点名 + 短路命中时跳过 LLM,直接收敛 top1;LLM 仅在模糊 / 歧义时介入 |
| 2 | 依赖运营数据质量 | 路径可读性、父节点正确性取决于空间树与别名维护质量;检索方案 §四之二已警告空间树可能非标准结构 | 路径渲染容错;缺失时降级 |
| 3 | 占用状态的时间相关性 | "是否被占"取决于具体时间窗,属可用性层而非实体层,不应由 resolve 承担 | 状态从可用性接口取,resolve 只做实体解析 |
| 4 | LLM 稳定性 | 裁决节点输出是否稳定、歧义 margin 如何定 | 枚举输出 + 评测集回归(复用 SA_会议预定_测试方法 L3) |
| 5 | 平替找不到时 | 全父链走完仍无 → 需话术兜底 | 如实告知 + 引导换时间 |
| 6 | 是否值得加节点 | 收益(准确率)vs 成本(延迟、维护)未量化 | 建议先做 §3 后端契约 + 真实 query 分布统计 |
6. 附录:无 LLM 降级路径
若性能约束优先,可在不加 LLM 节点的前提下覆盖大部分场景(代价:纯模糊语义场景降级为追问):
| 子问题 | 无 LLM 解法 | 依赖 |
|---|---|---|
| 目标识别 | ① 匹配类型为 exact → 取 top1;② 分差 < margin → 追问;③ 路径含定位 key(楼层 / 区域字符串)→ 结构化过滤收敛 | resolve 返回路径 + 匹配类型 |
| 占用判断 | 纯 if occupied → 平替 | 可用性接口返回状态 |
| 平替 | 同 §4.3 父链阶梯(本就是结构化查询,不需要 LLM) | 父节点标识 + 父级过滤 |
结论:无 LLM 版能覆盖点名、定位泛指、占用平替,零额外延迟、无阈值调参;不能覆盖纯模糊语义("那个靠窗的大房间")——这类只能追问,或由 LLM 猜。
决策依据建议:抽一批真实 query,统计"含结构化 key"与"纯模糊无 key"的占比。前者占大头 → 无 LLM 版够用;纯模糊占大头 → 需 LLM。
7. 待确认清单
/internal/spaces/resolve响应完整字段是什么?是否已含路径 / 父节点 / 容量?- 后端能否提供单点忙闲查询,或让
list_available返回occupied标记? locationIds是否支持父节点级过滤(传楼层 / 分区 id 返回其下房间)?- 空间树的
parentId链路是否稳定可得? - 真实 query 中"有结构化 key / 纯模糊无 key"的分布?
