基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
  • 知识卡片碎片时间过考点
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
    • AI 热点每日 AI 资讯与动向
AI 工作台
简历
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-11-04 11:34

预约 Agent 如何避免并发重复订位

dev.to · AI#Agent#并发控制#数据一致性
Editor brief · 编辑速览

文章将查询空闲时段与持久化预约明确区分,要求预约系统在统一冲突规则下成功提交后,语音 Agent 才能确认。讨论覆盖并发请求、重复工具调用、响应丢失,以及状态模型、数据库约束和审计凭据。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

AI 语音助手查到房源有空,向来电者确认时间,日历里也出现了预约,看起来流程已经跑通。但如果两通电话同时选择同一时段,或者写入成功后网络超时,两个人都可能听到「预约成功」。

作者讨论的就是这段容易被演示掩盖的风险:只有负责预约的系统按冲突规则持久化接受了请求,AI 才能说「已确认」。 对接日历、开发工具调用或编排 AI 工作流的程序员,都能从中拿走一套明确的接口与验收思路。

查到有空,为什么不能直接确认?

Google Calendar 的 FreeBusy 和 Microsoft Graph 的 getSchedule 都能查询忙闲,但查询不会替用户占住时段。读到空闲和写入预约,是两次操作。

问题出在两次操作之间:请求 A 查到空闲,请求 B 也查到空闲,随后两边分别写入。如果写入端没有共同遵守的冲突规则,两次查询都正确,最终预约仍然可能冲突。

用户选定时间后再查一次,可以缩短这个窗口。作者提到 Cronofy 推荐这种做法,但二次查询依然不能让读取和写入成为原子操作。两次最终检查仍可能同时通过。

而且,冲突不一定等于「同一时间出现两个预约」。有些房源允许同时接待两组客户,有些需要前后缓冲时间。一次看房还可能占用房源、经纪人、钥匙保管人等多个资源。

因此,先明确谁拥有预约的最终决定权,再定义资源、时间区间和容量。日历可能只是预约系统的镜像,CRM 负责联系人信息;它们分别写入成功,不代表整个预约已经成立。

时间也必须明确。保存确定的时间点、房源当地时区和时长,确认时复述当地日期、时间与时区。遇到「周六九点」这样的表达,应先澄清;夏令时切换还会让某些当地时间消失或重复。

防重复请求和防抢占,要分别解决

作者把两个经常混在一起的问题拆开了。

幂等性解决的是:同一次预约因为工具重复调用、网络重试或任务重跑,不能生成多条记录。并发控制解决的是:不同预约争抢同一资源时,必须按容量规则决定谁能成功。

稳定的 request_key 应绑定用户选定的具体预约意图,通过随机标识生成并保存,重试时继续使用。用户换了房源或时间,才建立新意图、生成新键。

Google Calendar 支持客户端指定 event ID,可用于避免写入成功、响应丢失后重复创建;重复 ID 对应的错误包含 409。但不同 ID 仍可能创建同一时间的事件,因此它不能代替容量控制。

如果团队自己掌握关系数据库,作者建议把约束放到提交位置。下面的示例分别限制请求键重复,以及同一房源、经纪人、开始时间上的有效预约:

CREATE TABLE viewing_booking (
  booking_id uuid PRIMARY KEY,
  request_key text NOT NULL UNIQUE,
  property_id text NOT NULL,
  agent_id text NOT NULL,
  starts_at timestamptz NOT NULL,
  ends_at timestamptz NOT NULL,
  status text NOT NULL CHECK (status IN
    ('held', 'confirmed', 'cancelled', 'expired')),
  provider_event_id text,
  created_at timestamptz NOT NULL DEFAULT now(),
  CHECK (ends_at > starts_at)
);

CREATE UNIQUE INDEX one_live_booking_per_property_agent_start
  ON viewing_booking (property_id, agent_id, starts_at)
  WHERE status IN ('held', 'confirmed');

-- 按房源规则选择精确时段约束或区间重叠约束,
-- 不要直接照搬这个键。

要看清索引里的 starts_at:它只限制开始时刻相同的预约,挡不住开始时间不同、实际区间重叠的预约。需要限制区间重叠时,应考虑 PostgreSQL 的范围与排斥约束,或覆盖实际区间的 Serializable 事务,并处理事务失败后的重试。允许容量为两组时,还需要并发安全的容量计数或预订模型。

唯一请求键防止重复办理,资源约束防止不同请求争抢成功。

外部日历又多一层困难:本地数据库、日历和 CRM 通常不能放进同一个数据库事务。作者建议保存持久化工作流记录,记录请求、调用与结果;外部成功而本地保存失败时,先按请求键或事件标识核对,不能直接重建。

超时以后,AI 应该承诺什么?

作者用状态机约束承诺:OFFERED 只是候选快照,SELECTED 是用户选定,COMMITTING 是正在提交,只有 CONFIRMED 才允许确认预约。

最关键的是 UNKNOWN。

超时只说明调用方没拿到结果,日历可能已经保存。此时应保留原请求键,查找对应预约并核对结果。找到就返回已有确认;能权威确认不存在时,才以同一个键重试;仍无法确定,就返回 pending_reconciliation,告知正在核查,并提供查询编号或后续联系渠道。

不能因为沉默就换键重试,也不能自动换个时间再订一次。

工具应返回明确状态,而不是「看起来可以」。作者给出的回执示例包含预约身份、时间和策略版本:

{
  "request_key": "req_01J...",
  "status": "confirmed",
  "property_ref": "listing_7842",
  "starts_at": "2026-11-07T10:30:00+01:00",
  "time_zone": "Europe/Paris",
  "ends_at": "2026-11-07T11:00:00+01:00",
  "booking_id": "book_91f...",
  "provider_event_id": "evt_7c2...",
  "policy_version": "showing-policy-12",
  "committed_at": "2026-11-04T09:18:11Z"
}

要注意 status:模型只能根据它组织措辞,不能从其他字段猜测成功。这些值都是示意数据。conflict 应触发刷新候选时间,rejected 应区分业务拒绝和技术失败;pending_reconciliation 则必须阻止再次发起新的预约。

原文的 Node 风格接口草图通过 scheduler.commit 提交,传入 requestKey、propertyId、agentId、startsAt、timeZone 和 expectedPolicyVersion。真正的幂等和并发保证必须由其实现提供;把查询与无保护的插入包进一个 JavaScript 函数,并不会获得这些保证。

取消和改期也要有提交结果。默认应先提交新预约,再释放旧预约;新预约结果未知时,先核对,避免两个时段都丢失。若业务选择先释放,也应明确记录这项取舍。

我的判断

这篇最有用的部分,是把对话质量、可用时间查询和事务正确性分开评估。提示词可以要求模型只调用一次,却无法协调不同电话、不同进程同时发来的请求。

不过,它提供的是设计论证,不是经过实测的可靠性报告。Retell 社区案例描述过流式超时后出现 5 次相同工具调用并发到达端点,但单个公开报告不能推导整个产品或行业的故障率。Forrester 的 2026 年从业者访谈也不是预约正确性的基准测试。

作者在 2026 年 11 月 4 日提出了 12 项合成验收用例,但明确没有运行真实代理、日历或 CRM。所给原文也没有逐项列出这 12 项,不能当成现成完整测试表。

数据库方案同样有前提:所有预约入口都必须受同一套规则约束。人工或其他渠道若能绕过预约服务直接写日历,本地约束就无法保证全局容量。短期锁、二次查询和供应商保证各有适用范围,文章没有给出对照数据,不能据此宣称某种方案在所有系统里最好。

隐私部分也应落实到数据设计:音频、转写、联系方式、预约记录分别设置保留期限,日历描述尽量只放必要信息。房源事实来自批准的数据源,涉及适配性、资格或专业判断的问题交给人工。

能拿来做什么

  • 给 Node 预约接口定义固定状态,并让前端和语音回复共同遵守:只有 confirmed 展示成功,pending_reconciliation 展示核查状态与查询入口。
  • 把 request_key 保存为预约意图的一部分,让工具重发、网络重试和后台重跑复用它,同时为资源容量建立独立的提交约束。
  • 在测试环境同时发送重复请求与竞争请求,并注入「外部已保存、响应丢失」故障,核对转写、工具请求、回执、日历和 CRM 是否一致。
  • 使用虚构房源和联系人验证取消、改期、时区与策略变更,检查日志能否证明交易结果,同时避免复制完整通话内容。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
运维智能体如何逐步获得生产操作权限
下一篇
业务写入后超时,Agent 应先查明结果

资讯速览

dev.to · AI · 2026-11-04 11:34

AI 评分 8.0 / 10

相关标签

#Agent#并发控制#数据一致性

继续阅读

  • Python 模型级联:先用便宜模型再升级
  • 运维智能体如何逐步获得生产操作权限
  • 业务写入后超时,Agent 应先查明结果
  • 用 Whisper 与 Pyannote 构建多人转写
  • 让数据库代理先理解业务口径