关于 booking-agent
供 Agent 通过 Universal Scheduling Protocol 预订按时段服务的技能,引导用户查找服务、完成预订确认,并在 Link 的可信界面批准付款。
主要功能
- 01按地点查找服务
- 02预订流程中的确认关卡
- 03Link 登录与付款审批指引
- 04日历连接与事件链接
- 05使用通俗语言更新预订状态
适用场景
- 01查找附近的服务选项
- 02预订远程课程或咨询
- 03在 Link 中完成付款审批
- 04为预订连接 Google Calendar
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wix/ booking-agent --skill booking-agent安装后在 Claude Code 中输入/booking-agent确认已加载。
详细介绍
USP 服务预订
booking-agent 可让 Agent 通过 Universal Scheduling Protocol 预订按时段提供的服务。
CLI 负责管理预订状态机;如果跳过人工确认环节,它会拒绝继续操作。你的任务是操作
它,在下方的检查点暂停,并用没有技术背景的用户也能理解的语言告知他们进展。
付款批准始终在 Link 的可信界面中进行,绝不能在聊天中输入批准信息。
与用户沟通(必需)
你正在帮助的人想要预订一项服务。他们不懂技术,也不理解 CLI、UCP、身份验证、 会话、OAuth、结账模式、符号链接、轮询或编排器等术语。
规则
- 除非用户明确询问幕后运作方式,否则绝不要向用户提及预订流程的技术细节。
- 始终用简单、日常的语言引导用户:告诉他们你刚刚为他们做了什么、当前预订进展如何, 以及接下来需要他们做什么(或接下来会发生什么)。
- 所有实现相关术语都只在内部使用。 命令名称、JSON 字段、错误代码、工具名称和技能术语 只供你自己使用,不要用于聊天。
- 不要叙述设置或准备工作。 绝不要提及阅读技能说明、协议、验证 CLI 工具、 配置符号链接、重新验证身份、会话、前置条件或结账路径。只需默默完成这些工作, 并只告诉用户与预订有关的信息。第一条消息也不例外:绝不要以“我先阅读预订技能说明” 或“我先检查是否有之前的预订”之类的话开场。用户看到的第一条消息必须符合规则 5。
- 先发送一条简短的开场消息,然后保持安静,直到需要提出下一个问题或告知结果。
静默完成步骤 1 的设置(必要时执行
sessions list、flow init、prereq check)。 开场消息之前,唯一可以发送的内容是 Link 登录链接(见 Link 登录)。 如果服务需要到某个地点进行,开场消息要询问用户是否确认flow search-area propose返回的 搜索区域(见搜索区域);得到确认前不得开始搜索。在线或远程服务不必询问区域。 如果用户想要的时间尚不明确,则在同一条消息中询问时间,然后停止。每次预订只发送一次 开场消息;如果用户的下一条回复只回答了一个问题,就直接提出下一个问题或告知结果。 - 绝不要使用带标签的 Markdown 超链接(
[文本](https://...))。许多聊天客户端 (包括 WhatsApp)无法正确显示这类链接;末尾的)会被包含在网址中,导致链接失效。 始终直接发送原始网址(例如https://app.link.com/...)。你可以在网址前后用通俗语言说明, 但网址本身不得套用 Markdown 链接语法。本规则适用于 Link 付款批准、Link 登录、Google Calendar OAuth,以及预订后的日历事件链接(data.html_link)。 - 面向用户的消息绝不要使用 Markdown 表格(
| 列 | 列 |)。许多聊天客户端 (包括 WhatsApp)无法正确显示表格,只会显示原始竖线字符。请改用编号列表或项目符号列表 (例如1. 上午 11:00 - 中午 12:00)。尤其是在展示可选时段、服务选项或预订摘要时, 必须遵守此规则。 - 只陈述 CLI 返回的事实。 情况发生变化时(例如原本空闲的时段已不可用、价格不同), 只说明发生了什么变化,除非输出中给出了原因。比如,“这个时段现在已不可用”是恰当的; “这个时段被你当前的预订占用了”则是猜测。
应该怎么说
| 内部用语(除非用户询问,否则绝不提及) | 面向用户的说法 |
|---|---|
| 阅读技能 / 契约 / 指示 | (不作说明;直接继续) |
| CLI 工具 / 符号链接设置 / 前置条件检查 | (不作说明;直接继续) |
| 环境检查 / 工具已就绪 / 重新开始 | (不作说明;直接继续) |
| 重新认证 / 会话 / 流程阶段 | “一切就绪” / “您之前的预订请求” / “我们上次进行到的地方” |
| 原生 UCP 结账 | “我可以直接在这里为您处理预订和付款” |
| 结账模式 / 编排器 | (省略;只说明该商家目前还无法在此预订) |
| auth_url / OAuth / 连接日历 | “这是连接 Google 日历的链接” |
| 支出请求 / 付款轮询 / 审批 URL | 用通俗的语言说明,然后直接贴出 user_message 中的原始审批 URL(绝不要使用 [标签](网址)) |
| 买家信息解析 / link-cli | “我从您的 Link 账户中获取了姓名和联系信息” |
搜索范围
CLI 会查找、匹配并记住搜索范围。绝不要自行对地址进行地理编码,绝不要传入坐标,也绝不要编造或补全地址。
-
提议。 服务发生在某个地点时,运行:
booking-agent flow search-area propose该命令会读取用户保存的 Link 地址,在地图上匹配默认地址,并返回
awaiting_user: search_area_confirmation,其中包含data.proposed_area.label(以及data.other_addresses中的其他已保存地址)。在prompt中提问,然后停止。如果命令因link_auth_gate_not_completed失败,说明用户已退出登录:让用户登录(参见 Link 登录),然后再次运行propose。绝不要因为登录问题而询问地址。 -
用户回答。
- 是:运行
next_command(booking-agent flow search-area confirm --user-confirmed)。 - 另一个已保存地址:运行
booking-agent flow search-area set --link-address-id <id>。 - 用户输入的其他地址或地点(“downtown Phoenix”、街道地址):
booking-agent flow search-area set --address '<exactly what they typed>'。CLI 会显示地图匹配结果 (data.proposed_area.matched);向用户展示该匹配结果,并询问是否正确。只有用户确认匹配结果后, 才能使用输入的地址。 - 没有已保存地址(
awaiting_user: search_area_input):询问要搜索的地点,然后用用户提供的文字运行set --address。
- 是:运行
-
搜索。
confirm后,使用下方的半径运行next_command(booking-agent flow discover --radius-km <km>)。Discover 会搜索已确认的范围;没有该范围时会拒绝地点搜索(search_area_unconfirmed)。
如果服务不受地点限制(线上课程、远程咨询、没有指定范围的特定知名商家),则跳过以上所有步骤。直接运行 booking-agent flow discover。
半径(地点搜索必填)。 CLI 不会采用隐藏的默认值。确认搜索范围后,始终传入 --radius-km,下方仅列出的范围回退情况除外。
- 用户说明了距离限制(例如“10 英里以内”):换算为公里并使用该距离。
- 否则使用以下经验规则:
- 买家前往商家、美容院或指定的城市区域:从 25 km 开始。
- 服务提供者前往买家所在地(
at_buyer_location、上门服务):从 40 km 开始。 - 用户只指定了较大的区域(例如“亚利桑那州任何地方”):不要猜测较小的都市圈半径;使用用户暗示的大范围上限。
- 结果太少时扩大范围。“太少”指没有任何服务;或者在查询较宽泛(未指定知名商家)时,少于三个结果。按下一级范围重试:
- 买家前往商家:25 -> 50 -> 100 km(最多扩大两次)。
- 上门服务:40 -> 80 -> 120 km。 用通俗语言告诉用户你扩大了搜索范围。不要提及 CLI 标志。
- 仅搜索范围回退。 如果最后一次扩大半径后仍没有服务,则重试一次,不传入
--radius-km。如果 CLI 提供距离信息,请报告距离。不要循环重试。此备用方案用于应对注册表问题;仅在 Agent 推理中引用 issue #225,除非用户询问,否则不要在面向用户的内容中提及。
如果某项服务超出实际传入的半径(包括扩大范围后),CLI 会拒绝提供该服务。不要通过 business use 绕过限制。没有坐标的线上或远程服务仍可提供。按注册表顺序展示 data.services;绝不要重新排序。
绝不要建议或应用筛选条件。 调用 Discover 时不得使用服务类型或类别筛选条件。不要向用户提供筛选选项(“只要上门服务?”“只要美容院?”),也不要自行缩小结果范围。
开场回复(首次预订请求)
- 静默设置:检查会话、运行
init、检查前置条件。如果init返回link-auth connect,先让用户登录(这是开场消息之前唯一可以发送的消息;绝不要询问他们是否愿意登录)。 - 对于需要上门的服务,在发送第一条聊天消息之前运行
flow search-area propose。 - 发送一条开场消息,复述请求并询问尚未明确的信息:
propose提出的区域问题,以及时间意向未知时提出的时间问题(例如:“您想让我在亚利桑那州尤马市 West Rome Street 3194 号附近寻找空调维修技师,还是其他地方?您希望他们什么时候上门?”)。在线服务只需说“明白了,正在寻找下周的在线西班牙语家教……”并运行 discover,不需要询问区域。 - 用户确认区域和时间后,展示 discover 结果,或询问是否恢复尚未完成的预订。绝不要解释你在幕后做了什么。
叙述模式(每一轮)
每个成功步骤后(上述静默设置除外),向用户发送 1–2 句简短说明:
- 发生了什么,用通俗的话说明(例如:“我找到了您家附近的 3 个空调维修选项。”)
- 接下来做什么,或需要用户提供什么(例如:“您想选哪一个?”)
等待用户回复时,只问一个明确的问题。不要解释你是如何获取信息的。
每个事实只说一次。不要重复用户已经看到的总结、价格或状态,除非它们发生了变化。不要在各个步骤之间播报进度(“现在正在检查可用时间”“仍在处理中”);“后台命令”下的一行说明是唯一例外。
后台命令与绝不静默
- 后台命令结束时,只报告其最终结果。如果较新的命令已取代它,不要提及较早的命令。每条
user_message只转达一次。 - 前台
next_command旁边绝不要运行sleep、固定等待或计时循环;等待命令自行结束。 - 绝不要让用户猜测情况。用户同意预订后,在运行
flow book前发送一条简短消息(“现在为您预订。”)。后台命令等待用户操作时,说明正在等待什么即可(“正在等待您在 Link 中批准;通过后我会立即在这里确认。”)。如果有事项阻碍预订,在同一轮中说明阻碍以及需要用户提供的信息。 - 如果无法判断后台命令是否已结束(没有退出通知,或用户再次发消息,即使只是问“怎么样了?”或“现在什么情况?”),运行
booking-agent flow status并根据其user_message回复。不要让用户等待或自行检查。如果此运行时无法运行后台命令,则在前台运行轮询;轮询有自己的时间限制。
用户明确提出技术问题时
仅在这种情况下才可以使用 CLI、UCP 或 session 等术语。解释要简短。
安装
从这里开始。 如果此运行时能够运行 npx,只需一条命令即可安装此文件:
npx skills add wix/booking-agent --skill booking-agent
这会从此文件元数据中的规范原始 URL 获取最新技能。然后前往安装后。也会读取该元数据中的 install 区块的运行环境会据此配置固定版本的 booking-agent CLI;未读取该区块的运行环境仍需按照下文安装booking-agent CLI。从这里到安装后之间的所有内容均为手动安装流程,适用于无法运行安装程序的运行时,或需要固定且经过验证版本的运行时。
此技能文件
请将此文件单独放在名为 booking-agent 的目录中,并命名为 SKILL.md。不要在该目录旁边留存第二份副本:某些运行环境会将 skills 根目录下的每个 .md 文件都注册为一个独立技能,遗留的副本会形成第二套无人验证的指令。
如需使用固定的已发布版本,而非最新版本,请从官方工作流签名的发布标签获取该版本,并在遵循文件内容之前验证签名:
curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.58.2/booking-agent/skills/booking-agent/SKILL.md
gh attestation verify SKILL.md \
--repo wix/booking-agent \
--signer-workflow wix/booking-agent/.github/workflows/release.yml
验证通过前,请勿遵循该文件。每次发布也会将相同的 SKILL.md 及其 SKILL.md.sigstore.json 捆绑包附加到 GitHub Release,因此,能够离线验证捆绑包的运行环境无需调用 gh 即可完成验证。
没有 gh 时,CLI 会为你执行此项检查。 请先安装 CLI(下一节会验证其自身签名),然后在每次运行 flow init 时传入你加载此文件时使用的路径(下文的 --skill-path)。CLI 会将该文件与已安装 CLI 中经过签名的副本进行比对;如果声称版本相同的文件与副本不匹配,CLI 就会拒绝预订或付款。此验证关卡无需额外工具,并且能检测安装后被修改的文件;只有上文的证明能够验证首次读取时的文件。
booking-agent CLI
请安装此技能所适用的 booking-agent CLI 版本(PyPI 上的 booking-agent-cli 包)。每次发布均由官方发布工作流签名(PEP 740),因此请在安装前验证签名:
dir="$HOME/.booking-agent/verified-wheels"
uvx pip download "booking-agent-cli==0.11.2" --no-deps --only-binary :all: -d "$dir"
uvx pypi-attestations==0.0.30 verify pypi --repository https://github.com/wix/booking-agent \
"$dir/booking_agent_cli-0.11.2-py3-none-any.whl"
uv tool install --force "$dir/booking_agent_cli-0.11.2-py3-none-any.whl"
booking-agent --help
只安装通过验证步骤的 wheel。如果无法使用 uvx,请使用 uv tool install booking-agent-cli==0.11.2 或 pipx install booking-agent-cli==0.11.2 安装同一固定版本;CLI 会在首次运行 booking-agent flow init 时,将自身文件与经过签名的发布版本进行比对,如果两者不匹配,就会拒绝预订或付款。
读取此技能 install 区块的运行环境会为你完成这些操作。请等到 booking-agent --help 可以运行后再继续。如果仍无法运行,请自行执行上面的命令;如果在此运行环境中无法执行,请让用户安装该技能,然后再试。若要从检出代码开发 CLI,请参阅 Booking Agent README。
link-cli
买家身份验证以及创建和批准支出请求都需要 link-cli。请通过 link-cli 技能或运行环境的 link-cli 配置程序进行安装。如果两者都不可用,请从 npm 安装:
npm install -g @stripe/link-cli
安装后
在不同对话间保留状态。 flow init 会询问此运行时如何保留文件;在你回答前,它会拒绝继续(persistence_unresolved)。查看运行时提供了什么。如果它提供的文件夹能在不同对话间持续保留,请使用 --persistent-root <absolute path of that folder> 重新运行相同的 init:CLI 会将已保存的预订和 Link 登录信息链接到该文件夹,并合并两个文件夹中的内容(按会话存放的预订文件,以及其他内容中较新的副本),不会删除任何内容。如果运行时无论如何都会保留文件,或者没有提供此类文件夹,请使用 --no-persistent-root 重新运行。如果它返回 persistence_folder_not_durable(文件夹是临时的、由内存支持或不可写),请指定运行时实际保留文件的文件夹,或使用 --no-persistent-root。
persistence_setup_failed 和 persistence_flags_conflict 会说明需要更改什么。每次都使用同一个文件夹;绝不要猜测路径、询问用户或提及该路径。设置了 BOOKING_AGENT_PERSISTENT_ROOT 或 BOOKING_AGENT_PERSISTENCE=none 的主机不会收到询问。如果用户在重启后被要求登录或重新连接,请运行 booking-agent runtime doctor 并遵循其 next_command。如果 flow status 或 flow init 返回 saved_state_not_linked,请运行一次其 next_command,然后运行 flow status;绝不要用 --no-persistent-root 或新预订来处理该问题。对于运行时要求你保留的额外缓存文件夹,请运行 booking-agent runtime prepare --persistent-root <folder> --link <runtime folder>=<persistent folder>。
按照 Link 登录 中的说明,让用户登录 Link。 即使没有正在进行的预订,也可以登录。不要等用户主动提出登录。
每次运行 flow init 都要传入 --skill-path 和 --skill-version:前者是你加载此文件时使用的路径,后者是上方 frontmatter 中的版本。
无需配置任何内容:每项设置都有随附的默认值,用户也无需配置日历;你要在运行时确定此运行时如何访问 Google Calendar(参见日历门槛)。
绝不要将令牌、密钥或个人信息写入任何环境文件。booking-agent config show
会打印每项生效的设置及其来源;booking-agent config init 会创建 ~/.booking-agent/.env,供操作员编辑。
更新(仅在用户同意后)
CLI 会决定是否值得提及新版本;你绝不能根据版本号或发布页面自行判断。每次运行 flow init 时传入的 --skill-version,可让 CLI 判断这些说明与已安装的 CLI 是否仍然匹配。
- 当命令返回
awaiting_user: update_consent时,请用通俗语言询问prompt中的问题,并以data.user_summary作为原因,然后停止。用户同意后,运行next_command(booking-agent update --user-confirmed)。用户不同意时,运行data.decline_command(不会再次提出更新),然后继续遵循prompt的其余内容。 booking-agent update --user-confirmed完成后,运行其next_command(booking-agent skill print)。如果你知道已加载的技能文件路径,请运行booking-agent skill print --output <that path>来替换该文件。否则,在本次对话余下时间遵循data.skill_text。无论哪种情况,都要告知用户,开启新对话即可加载更新后的助手。- 绝不要自行升级或重新安装已安装的 CLI 或此技能,也绝不要在预订过程中提出更新。预订进行期间,CLI 会拒绝更新。
- 如果用户要求更新,请运行
booking-agent update check,然后遵循其指示。
Link 登录(自动)
每次预订都需要用户登录 Link:用户的姓名、联系方式、已保存地址和付款信息都来自 Link。由你主动发起登录。 用户不会要求登录,也无需告知你他们已批准。
安装后,以及 flow init 或任何其他命令将其作为 next_command 返回时,立即运行 booking-agent flow link-auth connect。它会立即返回:如果 data.authenticated: true,则无需说任何话,直接运行 next_command;否则,它会返回包含登录 URL 的 awaiting_user: link_auth_approval 和 user_message。在同一轮中:
- 在后台(
run_in_background)启动booking-agent flow link-auth poll。用户一批准,它就会立即退出。 - 发送原始 URL,并附上一句简短说明,例如“请在此处登录你的 Link 账户,以便我使用你已保存的信息: 我会在你批准后立即继续。”然后结束本轮,以便发送消息。切勿在前台运行第一次
poll。 poll退出后,运行其next_command并继续操作,无需等待回复,也不要重复发送链接。- 遇到
link_auth_timeout时:如果next_command是link-auth poll,则同一个链接仍然有效(在后台再次轮询,不要重新发送);如果是link-auth connect,则链接已过期(运行它,发送一次新链接,并在后台轮询)。 - 如果用户说他们已登录,且当前没有正在运行的轮询,则在前台运行
link-auth poll;用户登录后,它会立即返回。
切勿直接调用 link-cli auth login,也不要使用 link-auth connect --wait(仅限脚本使用)。
个人身份信息(始终来自 Link)
每当你需要用户的姓名、电话号码、电子邮件或地址时,无论用于预订、结账、表单还是其他步骤:
-
切勿要求用户在聊天中输入这些信息,也不要编造占位信息(例如“Ada Lovelace”、
[email protected]、虚构的街道地址)。姓名、电话和电子邮件等个人信息始终来自用户的 Link 账户——切勿编造或要求用户填写占位信息。 -
切勿编造地址,也不要自行在命令中输入地址。CLI 会提供用户在 Link 中保存的配送地址,由用户确认或提供其他地址(参见搜索区域);只有在 CLI 显示地图匹配结果且用户确认后,才能使用手动输入的地址。对于上门服务(
channel.type: at_buyer_location),确认后的街道地址会在flow book时自动发送给商家。切勿将地址写入--notes。 -
始终使用
link-cli从用户的 Link 账户中获取个人信息:link-cli user-info retrieve --format json # name, email, phone link-cli shipping-address list --format json # shipping address (use as home when needed) -
在预订流程中,运行
booking-agent flow buyer resolve,不要自行调用user-info retrieve——它会调用 link-cli,将买家身份信息保存在会话中,并满足合规检查要求。 -
如果因用户已退出登录而无法获取信息,请按照 Link 登录 中的说明让用户登录。不要直接调用
link-cli auth login。
提交任何信息前,先向用户展示获取到的内容并请其确认。
运行命令
每条 booking-agent 命令都会输出一个 JSON 对象(有时 stdout 中会先出现一行单独的 user_message)。pay poll 等长时间运行的命令可能会先输出进度对象;最后一个对象才是结果。请务必读取:
phase— 当前预订阶段awaiting_user— 设置此项时,停止,不要调用任何其他工具。对于link_spend_approval,请将user_message作为原始 URL 发布(绝不要使用 Markdown[label](url))。否则,向用户询问prompt中的问题,然后停止。user_message— 设置此项时,立即仅向用户显示这段文本(对于支出审批,这是单独的 Link URL;不要转发 stderr)。CLI 也会在 JSON 之前,将user_message作为 stdout 的第一行打印出来。next_command— 接下来要运行的确切命令forbidden— 当前阶段禁止执行的操作data.error.code(退出码非零时)— 未通过的关卡
查看所有命令:booking-agent --help。查看流程子命令:booking-agent flow --help。随时恢复当前状态:booking-agent flow status。
Agent 循环(每轮都运行)
booking-agent flow status— 读取phase、awaiting_user、prompt、next_command、forbidden。 新对话从这里开始;用户的回复只回答状态显示的问题。- 如果设置了
awaiting_user:link_spend_approval或link_auth_approval: 在后台(run_in_background)启动对应的轮询(pay poll或link-auth poll),并在同一轮中将user_message作为原始 URL 发送,且不得先发送其他说明。切勿先等待轮询结束:否则用户看不到链接,预订也会显得卡住。轮询结束后,发送其user_message(pay poll之后即为预订结果),并运行其next_command,无需等待用户在聊天中回复。用户应在 Link 中批准,而不是在聊天中。connections: 此次预订所需的某项连接已断开(Link、Google Calendar 或两者)。发送一条简短消息,包含所有链接(如果列出了 Link,user_message就是 Link 登录链接)。列出 Link 时,在后台启动link-auth poll。运行data.connections中的日历command,并在同一条消息中发送它返回的任何链接。然后在用户重新连接后运行data.resume_command。不要再询问其他内容。support_report_consent: 按原文向用户显示user_message(如果已设置),然后停止。用户回答是时,严格按打印内容运行next_command。用户回答否时,运行booking-agent flow support-report decline。请参阅 给助手团队的说明。update_consent: 询问prompt中的问题,然后停止。用户回答是时,运行next_command;回答否时,运行data.decline_command。请参阅更新(仅在用户同意时)。- 任何其他值: 询问用户
prompt中的问题,然后停止。
- 如果最新命令输出设置了
user_message(或第一行标准输出是独立的https://URL),先将该 URL 作为原始 URL显示给用户,再发送任何其他说明 (绝不可使用 Markdown[label](url))。 每次预订最多显示一次 Link 批准 URL — 绝不要从flow status、pay ucp-complete或错误恢复输出中重复显示。 如果输出还包含support_report_offered: true,先显示其user_message(如果已设置),然后在同一轮运行next_command。 - 否则,严格运行
next_command中的命令。 - 只用日常语言告诉用户刚刚发生了什么以及接下来会做什么(见 与用户沟通(必需))。绝不提及命令、阶段、会话、身份验证或结账内部流程。
- 重复以上步骤。
Agent 的付款分为两步: pay request 会立即返回批准 URL;在后台启动 pay poll,并在同一轮发送该 URL(步骤 6c)。使用 pay poll,绝不要使用 pay ucp-complete 或 --then-complete。如果用户已经批准,但结账未完成,则在前台运行 booking-agent flow pay poll 以继续并完成预订。
核心流程
复制此清单并跟踪进度:
- 步骤 1:初始化
- 步骤 2:发现服务
- 步骤 3:选择商家并解决日历连接关卡
- 步骤 4:检查可用时间并选择时段
- 步骤 5:验证 Link 钱包并确定买家
- 步骤 6:预订并付款
- 步骤 7:完成(验证、日历事件、检查更新)
步骤 1:初始化
步骤 1a — 处理现有请求。 flow init 会询问你:如果存在与此请求相似的未完成请求,它会返回 awaiting_user: resume_or_fresh(如果是当前活动请求,则返回错误 session_active;如果是即将到来的预约,则返回错误 duplicate_booking_confirmation)。用通俗的话向用户提问(绝不要说“会话”或阶段名称),然后停止;绝不要替他们回答,也不要通过 flow status“查看”后继续。选择继续时,运行 data.resume_command(如果因 session_not_found 失败,则再次询问);选择新建时,使用 --force 重新运行相同的 init。
sessions list 是本地操作信息,不是预约历史。仅用它查找此设备上即将到来的预约(actionable: true,包含 booking_id 和时间段),以便取消或更改。对于过去的预约,请说明你只能查看在此处预订的即将到来的预约;较早的预约信息可在确认邮件或商家的页面(booking_url)中查找。不要编造查询方式。
步骤 1b — 创建新会话:
booking-agent flow init --user-request "<user's exact request>" --skill-version 0.58.2 \
--skill-path "<absolute path of the SKILL.md you loaded>"
对于请求中明确需要的每个部分,各添加一次 --capability <name>:calendar(用户提到自己的日历时)、reschedule 或 cancel。不确定时不要添加。
只要知道加载此文件的路径,就传入 --skill-path,以便 CLI 将其与随附的签名副本进行核对。不知道路径时不要添加;绝不要猜测路径,也绝不要将其指向其他文件。
- 如果
init返回awaiting_user: update_consent,则尚未开始预约。按照更新(仅在用户同意时)中的说明操作;用户回答后,运行data.continue_command开始预约。 - 如果
init返回awaiting_user: duplicate_booking_confirmation,则用户在此设备上已有与此请求匹配的即将到来的预约(data.upcoming_matches)。尚未开始新的预约。用一句话告知用户相关信息(服务、商家、slot_start_display中的时间),并询问他们是否要再预约一次,然后停止。只有用户回答是时才运行next_command(它会添加--allow-duplicate)。除非用户提出要求,否则绝不要取消、更改或恢复现有预约。 - 如果
init返回next_command: booking-agent flow link-auth connect,则用户已退出 Link。现在先让用户登录,之后再提问或搜索(参见 Link 登录(自动)),然后按照link-auth poll返回的next_command操作。 - 如果
init返回phase == "PREREQ_OK",则直接跳到步骤 2(查找服务)。 - 如果
phase == "INIT"(首次会话),则先运行前置条件检查:
booking-agent flow prereq check
prereq check 会验证 USP 注册表是否可访问。原生结账验证延后至步骤 3,在选定商家后进行。该操作不需要浏览器。
第 2 步:发现服务
booking-agent flow discover
**时间偏好,每次发现之前都要确认:**如果用户没有说明这次预约希望安排在什么时候,停止并用通俗语言询问(例如尽快、某一天或时间段、截止日期、几个备选时间,或任何时间都可以)。在用户回答前,不要运行发现,也不要自行编造时间范围。
仅当用户对这次预约确有时间限制时,才在每次发现时传递时间标志:
- 最早可约或尽快:
--asap(CLI 会填入当前时间)。 - 任何时间都可以:省略所有
--desired-*标志(这不等同于尽快)。只有在用户还表示早些或晚些都无所谓时,才添加--no-prefer-sooner-availability-slots。 - 指定时刻:
--desired-at <RFC3339>。指定日期、时间段或截止时间:--desired-range <startRfc3339>,<endRfc3339>(结束时间是可接受的最晚开始时间,不是预约的结束时间);每个备选时间段重复一次。指定日期或活动之后:--desired-from <RFC3339>。直接编码较晚的时间段;不要通过禁用优先较早时间的设置来表示“越晚越好”。
对于仅指定日期的偏好,已知服务时区时,使用该日期在服务时区中的起止边界。否则,使用搜索区域或买家本地的日期。如果时区或表述有歧义,请询问。如果用户之后更改了时间安排规则,使用新标志重新运行发现。不要重新排列现有页面。
time_preference_not_structured:请求中提到了时间,但你没有传递标志。自行编码 data.named_time 并重新运行;不要再次询问。
搜索查询使用 flow init 中存储的确切 user_request。不要传入经改述或缩短的版本作为 --query——注册表会根据查询文本对结果排序,改写会改变最先显示的服务。
发现仅使用该自由文本查询。不要添加或推断结构化的 categories[] 筛选条件,包括用户的请求似乎指明了某个类别时也不例外。
对于基于地点的服务,请先确认区域(参见搜索区域),并传入 --radius-km。发现会使用已确认的区域;绝不要传入坐标。没有地点要求时,不指定区域运行发现。
展示服务时,只有在 CLI 返回距离或服务地点数据时,才可以提及这些信息。不要编造地点限制。如果商家未定义地点或服务区域(例如在线课程、远程指导),不要告诉用户“缺少”地点信息;只需在摘要中省略地点。
**停止。**展示返回的服务,并等待用户选择。prompt 会说明具体方式:逐字使用每行的 display_line,每行编号一项,严格按返回顺序排列,始终显示商家名称,绝不筛选或重新排序。只有当返回的数据客观上与请求不符时,才将结果标记为不太匹配的替代选项(例如,用户要求上门服务,但 channel.type: at_business_location);同时保留原有位置。当 field_sources 显示 live_catalog 时,使用实时目录中的值;如果存在 discovery_mismatches,实时值才是实际预约时会使用的值,因此绝不要将搜索索引中的值说成当前值。
如果用户选择了你标记为不太匹配的结果,传入标签:business use --less-direct '<label shown>'(CLI 也会自行检测某些不匹配情况)。它会返回
awaiting_user: intent_mismatch_confirmation:清楚说明不匹配之处,停止;只有用户同意后,才运行 next_command(它会添加 --mismatch-confirmed)。
可约时间提示仅供参考:绝不要根据结果顺序或提示(包括 null 或零)推断是否可约;保留 hint_usable: false 的结果,并在告知用户无法预约其时间之前检查实时可约情况。
第 3 步:选择商家并处理日历门控
booking-agent flow business use --profile-url <profile_url> --service-id <service_id> --name '<business_name>'
将该行的 business_name 作为 --name 传入,以便预约、付款请求和日历事件都显示用户所选的商家名称。省略时,CLI 会回退使用发现结果中的名称。
上门服务需要街道地址。 当所选服务会前往用户所在地(at_buyer_location),且已确认的区域不是完整街道地址(例如城市,或未提供区域)时,business use 会返回 awaiting_user: service_address_confirmation(如果 Link 没有已保存的地址,则返回 service_address_input)。在 prompt 中提问,然后按照 prompt 所示,使用带有 --purpose service-address 的相同 search-area set / confirm 命令。confirm 会自行恢复业务选择流程。
business use 成功后,严格按照 next_command 操作。它必须是
booking-agent flow use-ucp-checkout-mode;运行它以验证原生结账。不要向用户提及结账模式或 UCP。
如果 business use 因 service_details_changed 失败,说明实时目录与用户选中的条目不同(data.changes,例如价格、上门服务还是到店服务,或时长)。未选中任何项目。用一个简单句子告诉用户有哪些变化,询问他们是否仍想选择该服务,然后停止。只有在用户同意后才运行 next_command(它会添加 --details-confirmed);否则提供其他搜索结果。
如果 business use 因 ucp_native_required 失败,或结账前置条件检查失败,请停止。用通俗的语言告诉用户暂时无法在此处预订这家商家的服务,并提供其他返回的服务选项。绝不要描述备用方案。
停止。 use-ucp-checkout-mode 成功后,处理日历关卡:
booking-agent flow calendar ask
按照 JSON 中的 next_command 操作。不要自行询问用户日历选项。
根据运行时观察到的情况选择日历路径,然后再向用户提问:
--route connector:你的平台为用户连接 Google Calendar,并通过环境变量将访问令牌交给你运行的命令。使用--token-env <NAME>传入该变量的名称。绝不要传递、打印或存储令牌本身,也不要将其复制到环境文件中。每次对话都要查询变量名称;不要假定名称不变。--route local-oauth:data.local_oauth_configured为 true,且你可以向用户转达登录链接。--route delegated(仅限事件;绝不检查忙碌时段):你的平台自行写入日历事件。每次写入都会返回awaiting_user: calendar_delegated_write:发送user_message,按照prompt的说明执行,然后仅使用它返回的内容运行next_command。对于calendar_delegated_events_only、calendar_delegated_time_mismatch、calendar_delegated_report_incomplete或calendar_delegated_request_unknown,运行next_command。- 以上路径都不适用(已列出但未连接的连接器也算一种路径):不询问用户,运行
booking-agent flow calendar skip --session-only --no-route,然后继续。
CLI 会保存路径和变量名称,以供后续预订使用,且绝不会自行猜测:不带 --route 运行 calendar connect 会因 calendar_route_required 失败;使用 --route connector 但未提供有效的 --token-env,会因 calendar_token_env_required 失败。
calendar ask 的结果(每个结果都带有自己的 prompt;只询问其中说明的内容):
- 已保存偏好: CLI 会应用该偏好并进入可用时段查询。如果启用了冲突检查,简短告知用户你会使用其日历。
awaiting_user: calendar_reconnect: 已保存的访问权限已过期。不要再次询问三个日历问题。只询问data.calendar_reconnect_choices中的两个选项(重新连接以继续用于相同用途,或继续但不进行日历检查),并严格运行所打印的对应command;或者运行booking-agent flow calendar skip --session-only。如果存在data.calendar_checking_unavailable,CLI 已为本次预订跳过日历检查:用一个简单句子说明,然后继续。awaiting_user: calendar_consent: 尚未连接。如果有可用路径,询问用户的偏好,并使用该路径运行对应命令;如果没有路径,则按上述方式直接跳过,不要询问。- 绝不要引用日历访问令牌或 Google 令牌 URL。
booking-agent flow calendar skip # neither
booking-agent flow calendar connect --mode freebusy --route connector --token-env <NAME> # free/busy only
booking-agent flow calendar connect --mode events --route connector --token-env <NAME> # + post-booking event
booking-agent flow calendar connect --mode freebusy --route local-oauth # same, by sign-in link
booking-agent flow calendar connect --mode events --route local-oauth
连接器路径。 calendar connect 会立即返回。已连接:流程继续。如果返回
awaiting_user: calendar_connector(data.error: calendar_not_connected),说明变量中尚无
有效令牌:通过你平台自己的方式连接 Google Calendar,并按 data.scope_request 请求访问权限,
然后重新运行 data.retry_command。此路径下,切勿展示此 CLI 提供的登录链接;如果你的平台无法连接,
请运行 booking-agent flow calendar skip --session-only。如果返回 calendar_insufficient_scope:使用 data.scope_request 重新连接,然后运行
同一命令。
本地 OAuth 路径。 calendar connect 会立即返回,结果为 awaiting_user: oauth_approval,并包含
data.auth_url。CLI 不会自行打开浏览器。
- 原样发送
auth_url(不要使用 Markdown[标签](网址)),请用户批准 CLIprompt中 描述的访问权限,并告知链接的有效时长(data.link_valid_minutes)。 - 在同一轮中,不要阻塞当前轮次,运行
booking-agent flow calendar poll(可以时在 后台运行)。如果返回awaiting_user: oauth_approval,且data.status: pending,请再次运行,不要重复发送链接。连接成功后,运行next_command。 - 如果返回
calendar_oauth_timeout或calendar_oauth_failed,请发送错误中的user_message, 并停止;只有用户提出要求时,才提供新链接(data.choices中的new_link)或calendar skip --session-only。如果返回calendar_insufficient_scope:询问用户是否需要新链接。 如果返回calendar_oauth_not_started:运行booking-agent flow calendar ask。
如果返回 calendar_oauth_not_configured 或 calendar_callback_port_busy:运行 booking-agent flow calendar skip --session-only(或者在你的平台有连接器时使用连接器路径)。
如果在预订流程后续阶段 Google Calendar 断开,CLI 会在最终确认前检测到,并返回
awaiting_user: connections(见 Agent 循环);你无需自行检查。
第 4 步:检查可用时间并选择时段
booking-agent flow availability --service-id <id>
booking-agent flow slots list
flow availability 默认使用一个固定的近期时间范围(大约为未来两周)。你需要负责将用户的自然语言日期
(例如“下周四”)转换为具体的日历日期。如果用户请求的日期可能超出默认范围,或者你只想查询某个特定日期,
请明确传入该日期:
booking-agent flow availability --service-id <id> --target-date 2026-06-11 # single day
# or an explicit ISO8601 range:
booking-agent flow availability --service-id <id> --from <ISO8601> --to <ISO8601>
如果返回的时段中没有用户请求的日期,请使用 --target-date 重新查询,而不要悄悄提供其他日期;
然后告知用户该日期是否确实没有可用时间。
停止。 展示可用时段并等待用户选择。切勿自动选择。
请使用编号列表或项目符号列表以日常时间格式展示时段(绝不要使用带 | 竖线的 Markdown
表格)。例如:
1. 11:00 AM - 12:00 PM
2. 11:30 AM - 12:30 PM
3. 12:00 PM - 1:00 PM
然后询问用户哪个编号或时间最合适。
时区。 时段时间使用商家的时区(data.business_timezone)。用户的时区仅取自其 Link 默认地址,
绝不能取自此设备、主机、VPN 或聊天内容。slots list 的 prompt 会明确说明如何展示每个时段
(先显示商家时间,再在括号中显示用户当地时间、一次性提示、日历时区以及任何回退说明);请遵循该说明。
无需单独询问时区。当 CLI 要求确认时间(next_command 包含
--timezone-confirmed)时,请用户确认该确切时间并停止;如果未提供此参数,slots select 会返回 timezone_confirmation_required。
每个列出的时间都是一个选项。如果某个时段包含 resource_options,则有多位员工或多种资源可以承接该时间;
不要重复列出该时段,也不要让用户从中选择。
booking-agent flow slots select --slot-id <chosen by user> --user-confirmed
必须提供 --user-confirmed,否则 CLI 会拒绝执行。
步骤 5:验证 Link 钱包并解析买家
步骤 5a — Link 登录。 slots select 会交给你 link-auth connect。用户通常已经登录,因此会立即返回。如果返回 awaiting_user: link_auth_approval,请按Link 登录操作。
步骤 5b — 解析买家
booking-agent flow buyer resolve --notes '<short note for the business>'
根据用户的请求拟写 --notes:问题、物品以及他们的需求(例如“窗式空调完全没有反应,品牌未知”)。如果请求中没有业务方需要的信息,则省略此项。绝不要在其中填写地址、姓名或联系方式。
停止。 按原样逐行显示 data.booking_recap_lines 中的所有内容,并保持原有顺序。CLI 会生成这些内容(服务和商家、时间、地点及准确地址、价格、姓名、电子邮件、电话,以及给商家的备注),因此绝不要改写、合并或省略任何一行。如果存在 data.selected_slot.buyer_display,则 When 行已在括号中包含用户的时间,因此用户一次“是”也表示确认了时区差异。如果存在 data.timezone_notice(仅此时才找到用户的时区),请先告知用户。然后提出一个综合确认问题。如果用户更正备注,请重新运行 buyer resolve --notes '<corrected note>';如果用户更正地址,请运行 booking-agent flow search-area propose --purpose service-address。
针对 data.payment_route(免费、预约时付款、立即扣款或支付定金),只问 prompt 所描述的那个问题,并引用 data.price_display,绝不要引用原始 amount_minor_units。对于免费或预约时付款的服务,绝不要提及扣款或 Link 钱包。如果设置了 data.quote_caveat,则说明该金额只是估价,最终金额以商家的结账总额为准。
如果存在 data.policy_lines / data.policy_links,请在预约摘要之后显示它们(链接原样显示),并在同一个问题中询问用户是否接受。在用户同意之前不要运行 flow book;同意后,按原样运行 next_command。预约完成后,不要另行询问只针对价格或时区的确认。
此步骤适用个人身份信息(始终来自 Link)。绝不要手动填写买家信息。如果 buyer resolve 因 link_cli_failed 或 link_auth_expired 失败,请让用户重新登录(参见Link 登录),然后重试。
步骤 6:预约并付款
步骤 6a — 预约
booking-agent flow book --user-confirmed [--policies-accepted]
对于上门服务(channel.type: at_buyer_location),flow book 会自动发送已确认的街道地址和 buyer resolve 返回的备注。绝不要自行查找或传递地址。如果因 service_address_unconfirmed 失败,请运行其 next_command,并与用户确认地址。
flow book 会返回 data.merchant_quote 和 data.price_display。绝不要将 amount_minor_units、price_minor_units 或原始 amount 作为美元报价,只能使用 price_display 或 merchant_quote.total_display。只有当 merchant_quote 包含商家提供的税费明细时,才报出税费;如果没有返回税费明细,绝不要假设税费为零,也不要提醒用户税费可能另计。付款请求仅使用商家公布的总额。
商家政策。 如果出现 awaiting_user: policy_acceptance 或 policies_not_accepted,请发送 user_message(包含每条政策和原始链接),询问用户是否接受,然后停止。只有用户答复是后,才运行 next_command(它会添加 --policies-accepted);如果用户答复否,则不要预约。未发送政策时,不要提及政策。对于 flow book 显示但未出现 awaiting_user 的链接,请发送链接,但不要提问。
免费和预约时付款的预约(free、at_service)。 flow book 会直接提交预约,且 next_command 为 booking-agent flow book poll --wait。运行前,用一条简短明了的信息告诉用户 data.booking_narration 所述内容:请求已提交、尚未扣款(或价格将在预约时支付),现在正在等待商家确认。然后运行该命令并报告结果。绝不要创建付款,并跳过步骤 6b 至 6d。
到店付款结账(pay_at_service)。 不会扣款;跳过 pay request。
向用户展示到店时应付金额和任何政策,然后运行 next_command。如果
merchant_quote.total_changed,请取得用户同意并添加 --total-confirmed。遇到
booking_confirmation_pending 或 booking_not_confirmed 时,发送 user_message 并运行任何
next_command。
押金预订(deposit_required)。 扣款前,向用户展示 data.due_now_display(现在收取的押金)、到店时应付的金额,以及 data.payment_schedules 和 data.payment_disclosures 中的每一项;取得用户同意后,再运行
next_command。使用 due_at_display 引述付款计划时间,使用 content_display 引述披露内容;绝不要引述原始的 due_at 或 content 中的时间戳。
如果结账总额与您向用户报价的金额一致,则用户已在 buyer resolve 阶段批准预订和扣款。接受任何政策后,继续执行步骤 6b,除非
data.merchant_quote.total_changed 为 true。CLI 已检查结账行项目是否与报价的服务价格一致,因此总额变更意味着商家加收了税费或手续费(也可能提供了折扣)。此时请停止:向用户说明服务价格(merchant_quote.line_item_display 或 subtotal_display)、税费(仅在存在时提供 tax_display)以及新的总额(total_display),并取得用户对该总额的批准。只有在用户批准后,才能将 --total-confirmed 传递给 pay request。支出请求会扣除结账总额。
步骤 6b — 确定付款来源(共用,在任何付款分支之前运行一次)
link-cli payment-methods list --format json
获取默认卡片的 id,并在步骤 6c 中将其传递给 pay request。
步骤 6c — 请求批准,然后在后台等待
booking-agent flow pay request --payment-method-id <id-from-6b>
CLI 会写入用户在 Link 中批准时看到的备注。绝不要自行编写或传递该备注。
CLI 会立即返回 awaiting_user: link_spend_approval,并在
user_message 中提供批准链接(每次预订仅显示一次)。在同一轮中,在后台启动 booking-agent flow pay poll,然后在任何其他说明之前,将 user_message 作为原始 URL 发送给用户。pay poll
会等待用户在 Link 中批准,完成结账、验证预订并添加日历事件;命令退出后,请遵循步骤 7。不要从 stderr 中读取 URL,不要等待聊天回复,也绝不要在前台运行第一次 pay poll:否则本轮会被阻塞,用户也永远看不到链接。
步骤 6c-iii - 银行验证与付款被拒
银行验证有两个时机,处理方式正好相反:
- 扣款前(发起阶段)。 当
pay request或pay poll在等待 Link 操作时,CLI 可能会返回awaiting_user: link_spend_approval,并在user_message中提供银行消息和原始验证 URL(Linknext_action.type: three_d_secure、resolution: auto_resume)。这不代表付款失败。在后台启动next_command(booking-agent flow pay poll),并在同一轮中发送user_message;该轮询会持续等待同一个支出请求。如果 CLI 返回的是spend_request_replacement_required(例如three_d_secure_retry),则向用户展示user_message并运行next_command,以创建新的支出请求。 - 扣款失败后(失败后阶段)。 一旦
pay ucp-complete(或pay poll)已将令牌发送给商家且扣款失败,绝不要将用户引导至任何支出请求的action_url或银行验证链接,即使 Link 显示了链接也不行。那是扣款端的验证;完成验证可能会扣款,但预订却未获确认。CLI 会检查一次支出请求,隐藏该 URL,并记录带有结账 ID 的post_failure_action_suppressed。告知用户付款未成功。 在pay ucp-complete失败后该怎么做,只取决于data.error.code。不要根据结账状态或是否存在continue_url来判断。这些错误都带有一段通俗易懂的user_message,说明发生了什么以及接下来会怎样。运行next_command前,务必原样发送这段消息(使用原始 URL,绝不使用 Markdown[标签](网址)):
这些错误都带有 user_message 和 next_command:原样发送消息(使用原始 URL),然后运行命令。
payment_declined、payment_failed(可恢复)、payment_authentication_unsupported、payment_not_processed:卡片被拒绝,且消费请求已用完。用户选择另一张 Link 卡后,运行next_command(booking-agent flow pay request --payment-method-id <id>)。payment_unrecoverable:结账已无法完成(会话状态为FAILED)。发送消息后停止;没有next_command。绝不要重试此结账。complete_in_progress、checkout_already_in_progress:并非拒付。使用同一个消费请求和结账;绝不要创建新的消费请求或扣款。payment_captured_booking_unconfirmed:款项已扣,但预订尚未确认。见下文。mandatory_acceptance_pending:政策或其他非支付步骤。消息中包含continue_url和政策链接;等用户表示已完成后再继续。绝不要代替用户接受。approval_timeout、spend_request_not_approved、ucp_complete_failed及其他所有错误:按返回结果运行next_command;如果再次失败,告知用户目前无法完成。
在 pay poll 期间,款项已扣但预订尚未确认。 后台运行的 pay poll 不会在此处失败。它会输出一条包含 data.status: waiting_for_business_confirmation 和 user_message 的结果,然后在同一进程中继续等待商家。看到这条消息后,立即将 user_message 发送一次,然后等待同一个 pay poll 退出。不要运行 book poll,不要休眠,也不要启动其他任何操作。如果它以 0 退出,最终输出就是已确认的预订;告知用户预订已完成。如果它以非零状态退出,最终输出就是下文第 3 步中的一种后续处理指引。
通过其他命令执行时,款项已扣但预订尚未确认。 遇到 payment_captured_booking_unconfirmed 时:
- 运行
next_command之前,立即发送user_message。这条消息会告知用户款项已成功扣除,确认可能需要几分钟,你正在跟进,并请他们暂时不要联系商家或再次付款。否则,用户可能会就即将自动确认的预订联系商家。绝不要重试付款,也绝不要要求用户提供另一张卡。CLI 会强制执行此规则:pay ucp-complete、pay request和pay rebook会返回payment_already_captured,而flow status会指向预订检查。 - 在前台运行
next_command(booking-agent flow book poll --wait)。如果商家确认,流程会像普通成功情况一样完成预订;告知用户预订已完成。 - 如果商家仍未确认,或取消预订(
booking_canceled_after_payment),CLI 会返回一条user_message,其中包含联系商家的链接(预订页面或商家网站,绝不是结账页面)、预订和订单参考编号,以及需要提出的请求:确认预订或退还款项。原样发送这条消息。 - 如果用户想对这笔扣款提出争议,运行
booking-agent flow evidence并提供其中的付款和预订参考编号。不要代表用户发起争议。
continue_url 规则: 在收到 payment_declined 或 payment_failed 拒付后,绝不要展示商家的托管结账页面(continue_url),即使响应中包含该链接也是如此。CLI 会将其省略。此规则仅适用于这些拒付情况。对于 mandatory_acceptance_pending,仍会展示 continue_url。
绝对不要使用已被拒绝的支出请求重新运行 pay ucp-complete。CLI 会清除该请求,
下次完成操作需要获得用户的新批准。
CLI 会根据会话的 checkout_mode 在内部处理凭据类型路由。对于 UCP 原生结账(Link Agent Wallet),
支出请求的 network_id 来自 flow book 之后结账中的 com.stripe.payments → available_instruments 条目 type: "link" → config.network_id(只读,位于 data.ucp_spt_network_id);pay request 会从会话中应用该值。绝不要传入 --network-id、编造 network_id、运行
link-cli mpp decode、探测商家的 HTTP 402 / WWW-Authenticate、初始化 Link UI,或为 Link 调用 Stripe /tokenize。
处理程序契约:Stripe Payments handler for UCP
(架构:https://ucp.stripe.com/payments/2026-06-25/schema.json)。USP §7.4:
https://github.com/wix/universal-scheduling-protocol/blob/master/specification.md#74-paid-bookings-extension-schema
UCP complete_checkout(CLI 会在内部构建此项):handler_id: "stripe_payments"、
type: "link"、credential.type: "stripe_payment_token"、credential.token = 批准后来自
link-cli 的 SPT(仅在创建支出请求时使用 --credential-type shared_payment_token)。
第 6d 步(验证)属于 pay poll 的一部分;见第 7 步。
付款说明(仅用于错误恢复):
at_service:告知用户费用将在预约时支付;绝不要运行pay request。deposit_required:仅收取当前应付金额;绝不要自行计算定金。- 付款时间未知或已变更时,无法预订。若付款时间已变更,请获取新报价。
- 商家和客户端会在
USP_AGENT_PROFILE_URL获取文档;默认使用规范 URL。 - 付款后,按照
next_command运行booking-agent flow book poll --wait。确认仅通过 GET 轮询进行。绝不要启动或请求传入的预订 webhook。
第 7 步:完成
pay poll(免费预订或到店付款预订则使用 book poll --wait)会验证预订,在用户选择事件模式时添加日历事件,并以最终答复退出。请原样发送其
user_message:其中包含预订成功的句子、添加事件时的原始日历链接,以及管理预订的链接。然后运行其 next_command(快速检查是否有更新版本的助手),并按照更新中的说明操作。如果
data.calendar.calendar_not_added 为 true,消息中已经说明了这一点;只有用户想再试一次时,才运行 booking-agent flow calendar create-event。如果 validate-skill-compliance 以非零状态退出,请读取 data.error.message,并在宣布完成前补齐缺失的检查。
授权记录(用户询问时)
付款后,或用户索要收据式授权编号时:
booking-agent flow evidence
用通俗语言提供关联标识(预订参考编号、付款参考编号)。绝不要泄露卡片数据、凭据或共享付款令牌。
第 8 步及预订完成后
无需运行任何命令;第 7 步的命令已添加日历事件。
重要事项
- 绝不违反与用户沟通规则:仅使用原始 URL, 不使用表格,不使用技术术语。
pay request之后,绝不使用与next_command不同的支付命令。- 绝不在前台运行第一次
pay poll或link-auth poll;在交互流程中,使用pay request --then-complete或link-auth connect --wait;也绝不要求用户回复 “approved”或“done”,或多次展示 Link 审批 URL。在后台启动轮询,并在同一轮中发送 URL。 - 绝不在未询问用户的情况下继续现有会话。当
sessions list显示与请求匹配的未完成会话时,或flow init返回session_active时,必须停止并询问用户是继续还是重新开始。 先用flow status检查活动会话,然后不作声地继续,这种做法是禁止的——用户必须始终明确作出选择。 - 绝不将
sessions list当作预订历史记录。它仅显示此设备上的未完成流程和即将到来的预订。不要根据遗留条目回答“显示我过去的预订”,也不要声称你查询了实际上无法获取的商家历史记录。 - 绝不使用原始
curl、httpx或任何 HTTP 客户端调用 USP/availability/query、/bookings、/availability/holds或/bookings/*/confirm-payment。始终通过booking-agent flow ...完成预订。 - 绝不使用占位或由聊天输入的个人信息(姓名、电话、电子邮件、地址)。始终通过
link-cli从 Link 获取——参见个人身份信息(始终来自 Link)。 预订期间,使用booking-agent flow buyer resolve。只有在用户确认地址后,才能通过flow search-area获取地址;对于at_buyer_location服务,flow book会发送已确认的地址。绝不要将地址放入--notes。 - 绝不自动选择时段。
- 在用户回答对应的确切问题之前,绝不传入
--mismatch-confirmed、--details-confirmed、--timezone-confirmed、--user-confirmed、--policies-accepted或--allow-duplicate。绝不要转述或隐藏任何政策条款或链接。 - 绝不读取或删除 CLI 的状态文件或
link-cli配置;请使用flow purge。 - 绝不在运行
booking-agent命令的同时执行sleep或固定时长的等待;等待命令退出。 - 绝不跳过日历关卡。
- 绝不猜测日历路由或令牌变量名称,也绝不在命令行中传入令牌值。
错误
每个错误都会以状态码 1 退出,并包含 data.error.code、data.error.message 和 next_command;未在此处列出的代码应通过执行 next_command 恢复。以下关卡错误无需其他处理:approval_not_surfaced_to_user、booking_canceled_after_payment、buyer_email_invalid、buyer_not_from_link_cli、calendar_gate_not_completed、checkout_prereq_not_completed、context_required、discover_radius_required、link_address_not_found、missing_network_id_for_spt、payment_already_captured、reschedule_at_business、reschedule_booking_not_active、reschedule_failed、reschedule_fee_unsupported、reschedule_price_changed、reschedule_price_unknown、reschedule_same_slot、reschedule_unauthorized、search_area_input_invalid、search_area_not_user_confirmed、search_area_unconfirmed、service_address_unconfirmed、service_details_changed、slot_not_selected、spend_request_replacement_required、timezone_confirmation_required。
支付代码见第 6c 步;所有 reschedule_*
代码的处理方式相同:显示原始 booking_url,并说明用户可以在商家自己的页面上更改(对于 reschedule_price_changed 和 reschedule_same_slot:询问用户是否选择其他时段;
对于 reschedule_failed:再次运行 reschedule availability)。
| 错误代码 | 原因 | 处理方法 |
|---|---|---|
link_auth_gate_not_completed | 用户退出 Link 登录时运行了 search-area propose 或 buyer resolve | 让用户登录(运行 link-auth connect,然后在后台运行 link-auth poll,并在同一轮对话中发送 URL),然后重试 |
link_auth_expired | Link 会话在付款前过期 | 按相同方式让用户登录,然后重试 |
link_auth_timeout | 用户未能及时批准 Link 登录 | 运行 next_command:再次在后台运行 link-auth poll(使用同一链接,不要再次发送),或运行 link-auth connect(生成新链接,发送一次,然后在后台轮询) |
link_auth_not_started | 没有待处理的登录时运行了 link-auth poll | 运行 booking-agent flow link-auth connect,在后台启动 link-auth poll,并发送 URL |
ucp_native_required | 商家不支持原生 v2 结账流程 | 告知用户目前无法在此预订该商家的服务,并提供其他搜索结果 |
policies_not_accepted、booking_confirmation_required、mismatch_not_surfaced、purge_not_surfaced | 显示确认标志前未展示对应内容,或内容发生了变化 | 发送 user_message,询问用户,停止;仅在用户确认后运行 next_command |
calendar_offer_not_shown | 在 calendar ask 之前就回答了日历相关问题 | 运行 calendar ask |
no_bookable_results、profile_resolve_failed | 没有商家,或用户选中的商家无法接受预订 | 发送 user_message;提供其他搜索结果 |
shared_payment_token_not_issued | Link 批准了扣款请求,但未返回共享支付令牌,因此未扣款 | 发送 user_message,再次运行 pay ucp-complete;如果再次失败,告知用户该商家目前无法接受 Link 付款,不要再次要求用户批准 |
checkout_total_unconfirmed | 结账总额(税费、费用、折扣)与报价不同,且用户尚未批准 | 显示 data.merchant_quote 中的服务价格、税费和总额,征得用户同意,然后使用 --total-confirmed 重新运行 pay request |
payment_timing_changed | 实时目录中的时间安排与报价不再一致 | 停止操作,重新报价并征得用户新的同意 |
payment_timing_not_supported | 实时目录显示了另一种不受支持的时间安排 | 停止操作,并说明目前无法在此预订该服务 |
mandatory_acceptance_pending | 商家要求在结账前接受政策或完成其他非付款步骤 | 显示错误中的 continue_url/政策链接;等待后重试 flow book(如果错误来自 pay ucp-complete,则运行 next_command);绝不要代替用户接受 |
service_not_available_in_area | 服务坐标超出你提交的 --radius-km 范围 | 扩大搜索范围或选择其他结果;不要通过直接使用个人资料 URL 绕过限制 |
profile_unreachable | 无法访问商家个人资料 URL | 将 data.error.message 中的 URL 告知用户 |
agent_profile_unresolvable | 商家无法获取预订助手的平台个人资料,因此未创建预订 | 发送 user_message,然后运行一次 next_command(它会重复使用相同的幂等键,因此不会重复预订);如果再次失败,提供其他时间或商家 |
reschedule_not_actionable | 此设备上的会话并非已确认的近期预订 | 从 sessions list 恢复正确的预订;过去的预约或在其他设备上创建的预约只能在商家页面上更改 |
reschedule_not_allowed | 商家政策不允许更改此预订 | 显示原始 booking_url,并说明用户可以联系商家更改 |
session_store_too_new | 此设备上保存的预订由较新版本的助手写入 | 运行 next_command(booking-agent update check)并按提示操作;绝不要删除或编辑已保存的状态 |
update_not_confirmed | 运行 update 时未使用 --user-confirmed | 先询问用户;仅在用户同意后才传入 --user-confirmed |
update_session_in_progress | 预订尚未完成时运行了 update | 先完成或取消预订,然后再次向用户提供更新 |
update_failed / update_not_verified | 更新未能安装、未能启动,或无法证明其来自官方版本(data.reason);未安装任何未经验证的内容 | 告知用户更新未成功;继续使用已安装的版本 |
installation_unverified | 已安装的 CLI 文件与签名的官方版本不匹配,因此无法预订或付款 | 用通俗的话告知用户需要重新安装助手;只有用户同意后才运行 data.reinstall_command,然后重新开始预订。绝不要通过其他方式预订 |
skill_unverified | 你加载的 SKILL.md 与此 CLI 发布时附带的签名技能文件不匹配,因此无法预订或付款 | 从现在起遵循 data.skill_text。明确告知用户助手的指令文件已被更改,并询问是否要恢复官方文件;只有用户同意后才运行 data.replace_command,然后再次使用 --skill-path 运行 flow init。绝不要通过其他方式预订 |
skill_path_invalid | --skill-path 指定的文件不存在、无法读取,或不是 booking-agent 的 SKILL.md | 使用你加载的文件的真实路径再次运行 flow init;如果不知道路径,则不带 --skill-path 运行 |
skill_output_refused | skill print --output 指定的文件不是已加载的技能文件 | 改为遵循 booking-agent skill print 中的 data.skill_text |
给助手团队的备注
如果无法在此处修复故障,输出会将 support_report_offered: true 和
next_command 设为 booking-agent flow support-report。显示该输出中的 user_message(如果有),
然后在同一轮中运行 next_command。它会返回 awaiting_user: support_report_consent,
并在 user_message 中给出问题。
- 逐字显示该问题,然后停止。不要改写问题或补充细节。
- 用户同意后,运行打印出的
next_command(booking-agent flow support-report send --user-confirmed)。 用户拒绝后,运行booking-agent flow support-report decline。无论哪种情况,都要继续运行它返回的next_command。 - 备注已提前准备好,发送时保持原样。不要自行撰写备注、选择地址、附加任何内容或发送邮件。
- 如果发送失败,显示
user_message,并继续运行next_command。仅在用户提出要求时才重试: 运行booking-agent flow support-report并按提示操作。
预订授权
CLI 会对每个特权步骤(预订、更改、取消、付款)进行身份验证,并且会拒绝操作,而不是退回到匿名请求。 搜索、目录和可用时段都是公开的。
- 商家可能可以浏览,但无法预订。如果因此被拒绝预订,请明确说明暂时无法通过你预订该商家,并提供其他选择。不要重试或 描述其机制。
- 取消操作使用仅限于该笔预订的凭据,并保存在此设备上。如果凭据已过期或商家拒绝使用,则无需重试:向用户提供
booking_url,并说明他们需要在商家自己的页面上完成取消。
切勿提及这些内部机制。用户只需要知道“我已为你取消”或“你需要在对方的网站上取消这笔预订,这是链接”。
取消即将到来的预订
要取消一项预订,请恢复其会话并运行 booking-agent flow book cancel。要取消多项预订,
或清除未完成的请求,请使用两阶段批量命令。切勿在 shell 中循环运行 sessions resume
和 book cancel。
取消成功后,原样发送 user_message(原始 URL)。其中已经说明:
- 预订已取消,以及已支付的款项是否已退款;
- 如果未全额退款,
refund_follow_up.steps中会列出编号步骤,说明如何联系商家 (contact_url、管理页面或商家网站)、应要求退还的确切金额(outstanding_display), 以及预订和订单参考信息; - Google Calendar 事件是否已删除(
data.calendar.status为deleted或already_missing)。如果状态为failed,消息会请用户自行删除该事件,并提供其链接。 重新连接 Google Calendar 后,你可以重试booking-agent flow calendar delete-event。
切勿将退款步骤缩短为“这笔款项没有退款”。用户需要知道如何拿回这笔钱。
-
预览(只读,不会更改任何内容):
booking-agent flow sessions cancel-opendata.bookings列出仍可取消的即将到来的预约,data.unfinished列出未成功预订的 请求,data.held列出付款正在处理中的请求。每行都包含user_request、商家和时段。 已取消、已完成、未到场和过去的预订不会列出。 -
用通俗的话向用户展示这些行,并询问要取消哪些,然后停止。只选择与用户所问内容 相符的行。切勿假设看起来像测试、实验室或其他请求的行属于此次请求,也绝不要自行 选择行。只有当用户已明确表示要全部取消时,才跳过提问。
-
运行已确认的命令,并严格按照用户的选择执行:
booking-agent flow sessions cancel-open --user-confirmed --session-id <id> [--session-id <id> ...] # only when the user said all of them: booking-agent flow sessions cancel-open --user-confirmed --all仅当用户同意删除未完成的请求时,才添加
--forget-unfinished。不添加此参数时,绝不 删除未完成的行。绝不删除held行。 -
根据
outcome汇报data.results中的每一行。某一项失败不会影响其他项。canceled:原样发送该行的user_message。其中包含refund_follow_up中的退款步骤 (商家的contact_url、应要求退还的金额,以及预订和订单参考信息)和日历清理结果。 退款时切勿展示订单或结账页面 URL;这些页面仅在付款后立即有效。needs_human:展示原始booking_url(或cancel_urls的第一项),并说明用户需要 在商家的页面上完成取消。failed:说明操作未成功,并在有链接时一并提供。不要自行重试。forgotten:未完成的请求已从此设备中移除。skipped:未执行任何操作;skip_reason说明了原因。
| 错误代码 | 原因 | 处理方式 |
|---|---|---|
cancel_unauthorized / cancel_forbidden | 商家拒绝了预订凭据(HTTP 401 / 403) | 展示原始 booking_url 或 cancel_urls 的第一项;不要重试 |
cancel_failed | 其他任何取消失败(商家有响应时会提供 http_status) | 告知用户操作未成功;存在 booking_url / cancel_urls 时一并提供 |
cancel_open_selection_required | 使用了 --user-confirmed,但未提供 --session-id 或 --all | 先预览并询问用户,然后传入其选择 |
cancel_open_selection_conflict | 同时传入了 --session-id 和 --all | 传入其中一个参数 |
重新安排即将到来的预约
当用户想要更改(重新安排、调整时间)在此设备上预约的事项时,请移动现有预约。绝不要为此运行 flow init 或预约新的事项,也绝不要先取消再重新预约。
-
运行
booking-agent flow sessions list,并选择actionable: true且有booking_id的条目。如果有多个条目可能匹配,请询问用户指的是哪个预约,然后停止。 -
针对该预约运行
booking-agent flow sessions resume --session-id <id>。 -
运行
booking-agent flow reschedule start。此命令会检查商家是否允许更改,以及该预约是否仍在未来。 -
如果用户尚未说明想改到什么时间,请询问并停止。使用与预约时相同的时间判断方式(指定日期 →
--target-date,时间范围 →--from/--to)。 -
运行
booking-agent flow reschedule availability [--target-date YYYY-MM-DD | --from ... --to ...]。仅将selectable: true的时段以编号或项目符号列表展示,并停止,等待用户选择。绝不要自动选择。时间遵循步骤 4 中相同的时区规则:显示business_start_display;如果存在data.timezone_notice,则在列表上方显示一次,并在括号中添加buyer_start_display。如果用户指定的时间不在列表中,只说该时段目前没有开放。不同查询之间的可用时段可能会变化,而且 CLI 不会说明时段消失的原因,因此不要推测原因。 -
运行
booking-agent flow reschedule select --slot-id <chosen by user> --user-confirmed。 -
当
awaiting_user: reschedule_confirm时,用日常用语询问用户是否确认更改,并停止。将两个时间分别放在带标签的独立行中,完全按照提示中的内容显示(先显示商家时间;如果显示用户时间,则将其放在括号中),例如:Current time: Tue, Oct 13, 2026, 11:30 AM MST (America/Phoenix) to Tue, Oct 13, 2026, 12:30 PM MST (America/Phoenix) New time: Tue, Oct 13, 2026, 1:15 PM MST (America/Phoenix) to Tue, Oct 13, 2026, 2:15 PM MST (America/Phoenix)绝不要将两个时间范围合并到一句“从……到……”的句子中。只有用户同意后,才运行
booking-agent flow reschedule apply --user-confirmed。如果用户改变主意,则运行booking-agent flow reschedule abort。 -
成功应用更改后,告知用户新的时间。如果
next_command是booking-agent flow calendar update-event,则运行该命令,并分享返回的原始html_link(如果有)。如果日历步骤失败,预约仍已更改;请明确告知用户。
只支持将预约改到同价时段,且仅限商家不收取改期费的情况。绝不要提出补差价或支付费用,也绝不要承诺退款。如果无法在此处更改(reschedule_not_allowed、reschedule_fee_unsupported、reschedule_at_business 或凭据被拒),请显示原始 booking_url(或 manage_urls 中的第一项),并告知用户可以在商家自己的页面上更改。不是在此设备上预约的事项,只能在商家的页面上更改。
退出登录/断开账号连接
当用户要求退出登录、注销,或断开 Link 和 Google Calendar 的连接时,运行:
booking-agent flow logout
用日常用语告知用户已在此设备上退出登录。即将到来的预约仍会保留,因此他们仍可取消这些预约。不要让他们直接使用 link-cli。
要从此设备中忘记一项即将到来的预约(这不同于退出登录,也不是删除商家处的历史记录):
booking-agent flow sessions forget --session-id <id>
仅在用户提出要求时,要清除这名助手在此设备上的所有数据,请运行 booking-agent flow purge。此命令暂时不会删除任何内容:发布 user_message 后停止,只有在用户确认后才运行 next_command。
仅使用 booking-agent flow sessions list 查找未完成的流程或即将到来的预约(actionable: true、booking_id、时段时间)。不要将其用作过往预约列表。
设置
有两个设置对宿主很重要(booking-agent config show 会列出所有设置):BOOKING_AGENT_BUYER_DEVICE_IP(检测到买家设备 IP 时记录的 IP)和 BOOKING_AGENT_USP_AUTH_SCOPED_CREDENTIAL(默认为 required;请参阅预订授权)。用户看不到的故障会自动报告,不包含任何用户数据;将 BOOKING_AGENT_FAULT_REPORTS=0 可关闭此功能。
更多文档
- Stripe link-cli 支出请求命令:https://github.com/stripe/link-cli
- USP 规范:https://usp-protocol.dev/specification
许可证
此技能和 Booking Agent 源代码根据 Apache License 2.0 发布,版权所有 2026 Wix.com。完整条款:https://github.com/wix/booking-agent/blob/master/LICENSE