在语音里收一笔卡付款:智能体必须暂时不再当那个界面。
呼叫中心行业花了二十年建起来的每一项「降范围」手法,靠的都是把某个听者从音频通路里挪走——而一个语音智能体并不是这通电话上的一个听者,它就是这通电话,这正是那套标准做法搬不过来的原因。这件事做错了,一个被念出来的卡号会同时落在一份转写稿、一段发给推理厂商的模型提示词、一组工具调用参数、一条追踪 span、一个评测夹具与一份 CRM 摘要里:六份副本、六套留存制度,而其中至少两份装着 PCI DSS 明说授权之后永远不得存储的数据。行得通的那份设计接受一件团队普遍抗拒的事——在付款这一步的时长里,智能体把话筒交给某个确定性的东西,然后等一个令牌回来。
为什么那套标准降范围做法搬不过来。
针对人工坐席的成熟方案是 DTMF 抑制:来电者用键盘输入卡号,电话通路上的一个组件截住按键音,而抵达坐席耳机、屏幕与录音机的是平音或星号。整个过程中这位人类一直在线——安抚客户、回答问题、看着一个打码的字段慢慢填满——而持卡人数据从不进入坐席环境。厂商把这一招宣传成「把呼叫中心从 SAQ D 往 SAQ A 拉」的那一步,而 PCI 安全标准委员会自己关于电话支付的指引也把抑制视作一项正当的范围缩减手法,附带一条提醒:适用哪一档取决于具体实现,而不取决于产品标签。
这套做法有一条隐藏前提:你要排除掉的那个听者,并不是在驱动这段对话的那个东西。剥掉一个人听那十六位数字的能力,通话照常进行。剥掉一个模型的,你剥掉的是它的输入。一个级联式语音智能体对这通电话的全部感知就是转写稿;一个语音到语音模型的全部感知就是音频本身。在采集数字的那段时间里,没有哪个「让智能体也在座」的座位,能不把持卡人数据放进智能体的上下文。
然后数一数副本,因为这才是让那些已经合规运营了多年呼叫中心的人吃惊的部分。在一套智能体栈里,一个被念出来的卡号会产出:
# artefacts created by one spoken PAN, and who holds them 1. call recording your storage, your retention rule 2. ASR transcript often the speech vendor's too 3. model context/prompt the inference provider's request logs 4. tool-call arguments your orchestrator, your app logs 5. trace span your observability vendor, 30–90d 6. CRM summary + eval set indefinite, and copied onward # PCI DSS: PAN must be unreadable wherever stored; # CVV/CVC and PIN must not be stored AT ALL post-auth. # Rows 2-6 did not exist in the human-agent design.
第 3 行值得单独一句话。把一个卡号发给第三方模型厂商,是把持卡人数据披露给一个几乎肯定不在你 PCI 范围内、没有被评估过、而且可能为了滥用监控而留存请求体的服务。在响应之后再打什么码都救不了;数据在请求发出的那一刻就已经走了。录音之下那五件产物,正是录音、同意与脱敏笼统点过名的那几件——而本页讲的是那个它们不只是麻烦、而是直接判你不合格的具体情形。
有一个说法能在设计评审里省下大量争论:就付款这一步而言,请把模型当成一个恰好待在你栈内部的不可信第三方。你不会为了决定下一句说什么,就把裸的 PAN 与 CVV 灌给一个未经评估的外部厂商。而一个天真的语音付款流程干的正是这件事,并且那家厂商的名字就写在你的推理账单上。
三种架构,以及你真正在做的那笔取舍。
能把持卡人数据挡在模型之外的形状恰好只有三种,而它们的区别在于让来电者付出多少代价,而不在于合规程度。请按来电者体验与完成率来挑;三种都能降范围。
- 在媒体层抑制,智能体留在通话里。SBC 或媒体服务器截住 DTMF,向每一个下游消费者——录音机、ASR 输入、模型音频输入——转发平音,并通过一条独立通道把数字送给支付服务。智能体只收到字段级事件:
pan_captured(last4, brand, luhn_ok)、expiry_captured、cvv_captured。这是唯一一种让对话保持连续的形状,智能体能继续解说、能处理「等等,卡拿错了」,而来电者从不觉得自己被转走了。它也是工程量最大的一种,因为抑制必须覆盖模型的音频或转写通路,而那条流恰恰是你电话厂商那套 DTMF 打码产品设计时压根没听说过的。 - 转给一个确定性的支付模块,再把通话接回来。智能体把通话转给一套安全 IVR 或支付应用,由它采集、令牌化、授权,并返回一个结果码;智能体拿着
{status, token, last4}续上。这是最容易认证、也最容易推理的一种,因为支付通路里压根没有模型。代价是在一段正跑得好的对话中间来一次交接、升级与温转接里那一整套状态丢失问题,以及一位在模块里失败的来电者落在了智能体帮不上他的地方。 - 带外链接,智能体等一个 webhook。发一条短信或往 App 里推一个链接,来电者在一个托管页面上付款,智能体等着并确认。这是可得的最佳降范围——与支付相关的任何东西从不进入语音通路——而它对没有智能手机的来电者会失败、给智能体添出一分钟需要填满的空气死寂,并且要求一位客户去点一条在来电途中发来的链接,而这恰恰是你自己风控团队天天警告的那种欺诈形状。请把它当作预约付款或后续付款的首选、以及一条兜底路径,而不是呼入通话上的主路径。
这三种都不会做的事,是让模型「就在内存里、就一秒钟」地握着那些数字。没有那种状态。上下文窗口就是一个请求体,它默认会被记在某个地方,而合规问题问的不是你有没有打算留它。
一个能判定你手上是哪种形状的设计评审问题:点名那个最先看到数字的确切组件,然后把每一条离开它的流都追一遍。如果其中有一条抵达了 ASR、模型、编排器或追踪器,那你手上不是一套抑制架构——而是一项作用在录音上的打码功能,而录音是那六份副本里最不重要的一份。
工具签名就是那件合规制品。
这是今天就能改的那一件具体事情,而且它能在代码评审里被核对,而不必等审计。一个模型可以拿卡号去调的工具,就是一个其参数将会出现在你追踪存储、提示词日志、被重放的评测夹具里,并最终出现在某张工单截图里的工具。请让这类数据在模型的词汇里根本无法被表达。
# wrong — PAN and CVV are now model-visible and trace-visible charge_card(pan: str, expiry: str, cvv: str, amount: int) # right — the model can only ask for collection and spend a token start_card_collection(amount: int, currency: str) -> { session_id, prompt_played: true } get_collection_status(session_id) -> { state: "waiting" | "captured" | "failed", fields_done: ["pan", "expiry"], last4: "4242", brand: "visa", failure: "luhn" | "timeout" | "caller_abandoned" } authorize(session_id, amount, currency, idempotency_key) -> { status, token, auth_code, decline_reason } # the model never receives a PAN, CVV, expiry or full track data. # everything it can say about the card is last4 + brand.
这个接口有三处性质是承重的。模型驱动的是流程、从不是数据,所以它的全部推理——重试、卡拿错了、把拒付原因温和地念回去——都发生在令牌与状态码之上。那个状态查询是对字段完成度的轮询、而不是一串数字的流,所以智能体能解说进度(「长号码我收到了,现在报有效期」)而从未拿到过那个号码。而 authorize 收一个幂等键,因为一个语音智能体在发出授权之后掉了线、恢复时又重试一遍,就是那个再普通不过的重复扣款 bug、只是中间多了个模型;道理与幂等与重试里的一样。
然后加上那条反面测试。一个单元测试,往你工具层的每一个参数里塞一个 PAN 形状的字符串,并断言它在序列化之前就被拒掉;再加一项 CI 检查,去你录下的追踪与评测夹具里 grep 通过 Luhn 校验的数字串。第二项会找出第一项本该拦住的那处泄漏,而它是本页价值最高的单项检查。
来电者会把号码念出来,而那才是真正的危险。
上面这些处理的都是键盘。真正咬人的失败模式是:一位被要求输入卡号的来电者会改成把它念出来——因为他们一直都这么干、因为人工坐席以前是接受的、也因为你的智能体听起来像个人。请把它当作必然而不是边角情形:每一条语音优先的流程里都会有一部分来电者开始念数字,而他们一开口,你在 DTMF 层建起的每一项控制都被一条你没做任何埋点的路径绕过了。
缓解措施必须摆在你能摆到的、流水线上最早的位置,而这个次序就是全部要点:
- 在识别之前抑制,不是在之后。一个作用在成品转写稿上的脱敏器,意味着那些数字曾存在于某个缓冲区、曾被发给一家语音厂商,并且多半就在那家厂商的请求日志里。如果你的 ASR 跑在你自己的边界内,一个作用在部分假设上的数字串检测器可以掐断那条流。如果它跑在厂商那边,你就需要把检测器放在音频那一侧——靠能量与节奏去检测一段被念出来的数字串是粗糙的,但它在对的位置上。
- 一旦检出,失败拦截:把这一轮丢掉。不要转写它、不要发给模型、不要记日志。暂停录音缓冲、丢弃那条 span,并让智能体用一句确定性的、非模型生成的台词重新提示:「卡片信息我没法通过语音接收——麻烦您在键盘上输入。」要常量字符串,不要提示词指令,理由见在通话里披露这是个智能体。
- 把这个检测器当作一项控制,而不是那项控制。它有漏报,每一次漏报都是一次潜在的合规事件,而一位带着停顿和自我更正念数字的来电者,在部分假设上确实很难抓。它降低发生率;真正让这个发生率变得可承受的,是 STEP 2 里的架构。
- 在提示词设计上先手拦住它。智能体应当在来电者有机会开始念之前就说出「用您的键盘」,并且绝不要在那一轮问一个开放式问题。降低「念出卡号」事件最便宜的办法,是一句不招惹它的话。
- 持续扫描每一个落地点,而不只在上线时扫一次。一个 Luhn 校验通过的字符串检测器,按计划跑遍转写稿、追踪、评测集与 CRM 备注。它会在上线后一个月内找到东西,而找到就是目的;这就是智能体追踪中的 PII 脱敏里那套落地点侧的纪律。
请留意那处让这件事比人工情形更难的不对称,并且值得对最终签字的那个人直说:一位听到了卡号的人类坐席可以被训练成不把它记下来,而录音可以被他按下的一个按钮停掉。一个模型没法被指示去「把输入收回来当没收到」,而那些副本是由基础设施做出来的、不是由某个人的选择做出来的。「已经告诉智能体不要这样」在这里不是一项控制——它过不了引用监视器那里的防篡改与调解两项检验。
别丢掉那些被键盘排除掉的人。
一条只走键盘的付款路径是一个干净的合规故事,而它会无声地甩掉一批很具体的人:用转盘电话或按键行为不良话机的来电者、任何走中继服务打进来的人、那些在时间压力下「找到十二个小键」本身就是难处的运动或视觉障碍者、在车里开着免提的人,以及任何设备发出的 DTMF 对端根本收不到的人。这些正是无障碍语音智能体里显示在通话其他各处都失败得最惨的那几个人群,而在这里失败是终局的——他们付不了款。
- 付款永远要留一条人工路径,并且在那条路上保留 DTMF 打码。这就是那个不体面的后果:把语音通道自动化,并不让你把呼叫中心那套打码基础设施退役,因为无障碍兜底要走它。请给两套都留预算。
- 把带外链接当作一个明示的替代选项,而不是一种失败状态。在一次键盘失败之后说「我也可以给您发一条安全链接的短信」读起来是在帮忙;在三次之后说读起来就是一条死路。而且请在同一句话里说出商户名,因为你正在请一个人去信任一条在呼入通话途中发来的链接。
- 绝不要把同一种方式重试三遍。键盘试两次,然后换形状:链接、回拨,或人工。第三次一模一样的尝试正是放弃率集中的地方;而与一次失败的订单查询不同,一笔被放弃的付款是丢掉的收入,后面还挂着一通客服电话。
- 先把「已存卡」这个情形做掉,因为它把问题整个拿走。对一位回头客,一张在档令牌加上一次绑定到动作、而不是绑定到这通电话的升级认证,能跳过整整这一页。那个绑定正是语音智能体的来电者核验里的论点,而付款这一步正是它回本的地方。
把它运营起来:五个数字,以及那一次真能找出缺陷的复盘。
付款是这通电话里唯一一个「智能体的对话类指标几乎什么都说明不了」的环节。请把它当作自己的一条漏斗来跟踪,并且把合规面埋点埋得跟转化面一样细。
- 付款环节完成率,按方式与话机类别分段。键盘、链接、转接、人工。手机与固话之间的差距通常大到足以改变你先提供哪条路径。
- 每千通电话的「念出卡号」检出事件数。看方向,并且把零当作检测器坏了、而不是通道干净。一次提示词改动之后出现的台阶式变化,是你能拿到的最快信号,说明某处措辞变化把人请去念号码了。
- 从首次付款提示到授权的耗时。这里的长尾是那些在键盘上挣扎的人,而 STEP 5 里那些无障碍失败会先以一条延迟分布的形式显形、之后才以一句投诉的形式显形。
- 每次扫描在每个落地点找到的 PAN 形状字符串数。转写稿、追踪、提示词日志、评测夹具、CRM 备注。目标是零,而这个指标的全部价值都在那些非零的月份里。
- 智能体未经人工就处理掉的拒付数。拒付是你最容易设计不足的那个常见结局,而智能体需要针对每个拒付原因的确定性话术——把一条原始的收单机构报文念给来电者听,既帮不上忙、偶尔还是一次披露。
然后跑一次没有任何仪表盘能替代的复盘:取十段录下来的付款环节,逐一手工追过你这套栈产出的每一件产物,一个个存储打开来看。这不是一次策略评审——这是一次搜索。你要找的是那份没人设计过的副本;而在一套由电话厂商、语音厂商、模型厂商、编排器与追踪器拼起来的栈里,通常恰好有一份。
只做一件事的话:改工具签名。把任何能接受卡号的工具换成 start_card_collection / get_collection_status / authorize(session_id, …),让 PAN 在模型能吐出的任何东西里都无法被表达;然后去你现有的追踪里 grep 通过 Luhn 校验的数字串,看看旧签名已经让你付了多少代价。本页其余内容都是有前置周期的架构工作;而这一对只要一个下午,并且它关掉的正是日后最难找的那几份副本。相关:语音中的工具调用与状态讲周边的轮次设计,购物与结账智能体讲同一个付款环节在文字通道里的样子,而智能体支付讲令牌化的、由智能体发起的付款正往哪儿走。