投诉用户需要安抚还是直接解决?闪电智能 Voice Agent 的动态服务策略设计
用户说:“别再解释规则了,我只想知道这笔重复扣款什么时候退。”
这时再追加一段长篇安抚,往往会让投诉升级。可系统如果跳过确认,直接说“会尽快处理”,又可能把未核实的订单、时效和权限说成了承诺。真正要解决的不是“先共情还是先解决”,而是:这通电话现在是否具备安全推进的条件;不具备时,该把哪些事实交给人工。
我不建议把这个判断交给“用户情绪类型”或音高、音量之类的特征。对投诉场景更可靠的输入,是用户明确提出的要求、已确认的问题对象、工具执行结果、重复解释次数和风险状态。闪电智能 Voice Agent 可以据此切换表达方式,但不能据此给用户贴上“难沟通”“情绪化”或人格标签。
本文用一组不含真实客户数据的合成事件,演示投诉路由、转人工上下文和测试口径。它只验证规则边界,不证明真实客服中的满意度、投诉率或处理时长。
目录
- 先把投诉路由放回 Voice Agent 链路
- 安抚不是独立状态,推进也不是默认答案
- 哪些事实可以驱动策略切换
- 一个可运行的投诉路由器
- 转人工时到底该交什么
- 如何用合成事件验证规则
- 指标、边界与两个常见问题
- 参考资料
先把投诉路由放回 Voice Agent 链路
投诉路由不该只发生在大模型生成话术的那一刻。它至少跨过五个位置:ASR 或文本输入确认用户说了什么,业务查询确认对象和状态,策略层决定澄清、推进还是交接,工具层执行可授权的动作,最后由 TTS 或文本把下一步说清楚。
当前表达 + 已确认字段 + 工具结果 + 风险标记
↓
投诉策略路由
↓ ↓ ↓
澄清 推进 转人工
↓ ↓ ↓
补齐事实 返回可核验结果 生成交接载荷
这里最容易混淆的地方是:承接语属于表达层,能不能继续自动处理属于策略层。系统可以先说“我已确认你在问重复扣款”,但不能因为这句话说得足够温和,就忽略支付争议、身份核验或工具失败。
安抚不是独立状态,推进也不是默认答案
投诉电话里的“承接”应该很短,只负责告诉用户系统抓住了当前问题。之后的动作取决于证据,而不是句子里的情绪强度。
| 路由动作 | 进入条件 | 系统应做什么 | 不该做什么 |
|---|---|---|---|
| 澄清 | 订单、时间、诉求或问题对象不一致 | 复述一项待确认事实,再提一个必要问题 | 继续播报固定流程 |
| 简短承接后收集 | 对象基本明确,但还缺执行所需字段 | 说明缺什么、为什么需要 | 用泛泛道歉代替收集 |
| 推进 | 问题已确认、工具可执行、没有风险标记 | 返回工具结果或明确下一步 | 许诺系统没有权限保证的结果 |
| 转人工 | 用户明确要求人工、高风险、重复解释未解决、流程被阻塞 | 说明转交原因并附带已确认上下文 | 让用户从头再说一遍 |
例如,“你根本没听懂”不是给用户贴情绪标签的理由,而是一个澄清信号;“我要投诉到平台”也不是单纯提高安抚强度的提示,而是需要升级处理的显式意图。两者的系统动作不同。
哪些事实可以驱动策略切换
下面这组字段只描述当前会话和业务状态。它们不推断人格,也不保存“好说话”之类的长期标签。
| 字段 | 例子 | 用途 |
|---|---|---|
issue_confirmed | 订单号、业务类型和争议点已一致 | 决定能否进入工具查询或处理 |
explicit_human_request | “转人工”“找主管” | 立即交接,不与默认策略竞争 |
high_risk | 身份、资金、重大权益或合规风险 | 立即交接,由业务规则提供来源 |
repeated_question_count | 同一已确认问题已解释两次仍未解决 | 避免机器人第三次重复同一套说法 |
blocked_action_count | 工具无权限、查询失败或流程无法继续 | 区分“再问一个字段”与“系统已卡住” |
user_denied_understanding | 用户明确否认系统理解 | 回到澄清,不沿用旧答案 |
reply_preference | “直接说结果” | 只影响回答长度和顺序,不能绕过风险规则 |
repeated_question_count >= 2、blocked_action_count >= 1 是下面 Demo 的测试门槛,不是生产阈值。真实项目应由业务、客服和合规团队根据失败样本、人工负载和错误成本共同确认,并保留规则版本。
一个可运行的投诉路由器
最小版本常常只有一条 if high_risk: handoff。它能挡住明显风险,却解释不了“系统一直没解决”“用户已要求主管”或“工具刚刚失败”这些更常见的投诉升级路径。下面的示例把这些事实留在事件中,输出动作、理由和表达模式。
from dataclasses import dataclass
from typing import Literal
Action = Literal["handoff", "clarify", "advance", "acknowledge_collect"]
@dataclass(frozen=True)
class ComplaintEvent:
conversation_id: str
issue_id: str | None
explicit_human_request: bool = False
escalation_requested: bool = False
high_risk: bool = False
issue_confirmed: bool = False
user_denied_understanding: bool = False
repeated_question_count: int = 0
blocked_action_count: int = 0
can_execute: bool = False
wants_result: bool = True
reply_preference: Literal["answer_first", "normal"] = "normal"
last_tool_result: str | None = None
risk_flags: tuple[str, ...] = ()
@dataclass(frozen=True)
class Decision:
action: Action
reason: str
reply_mode: Literal["answer_first", "brief"]
def choose_decision(event: ComplaintEvent) -> Decision:
reply_mode = "answer_first" if event.reply_preference == "answer_first" else "brief"
if event.explicit_human_request or event.escalation_requested:
return Decision("handoff", "explicit_escalation", reply_mode)
if event.high_risk:
return Decision("handoff", "high_risk", reply_mode)
if event.repeated_question_count >= 2:
return Decision("handoff", "repeated_unresolved_question", reply_mode)
if event.blocked_action_count >= 1:
return Decision("handoff", "blocked_business_action", reply_mode)
if not event.issue_confirmed or event.user_denied_understanding:
return Decision("clarify", "need_fact_confirmation", reply_mode)
if event.can_execute and event.wants_result:
return Decision("advance", "safe_to_execute", reply_mode)
return Decision("acknowledge_collect", "missing_execution_input", reply_mode)
这段代码刻意把 reply_preference 限制在 reply_mode:用户要求“别绕弯子”时,系统可以先报工具结果,但不能因此跳过资金争议或人工请求。把偏好写进 if high_risk 的前面,是投诉策略里很危险的顺序错误。
转人工时到底该交什么
“已为您转人工”不是一次完成的交接。若人工只拿到一句“客户不满”,用户仍要重复描述问题,系统前面的澄清也就白做了。
交接载荷至少应保留以下最小信息:会话 ID、已确认的问题对象、路由原因、风险标记、最近一次工具结果、用户明确的表达偏好,以及规则版本。不要默认塞入原始音频、人格标签或与当前投诉无关的历史判断。
POLICY_VERSION = "complaint-policy-demo-v1"
def build_handoff_payload(event: ComplaintEvent, decision: Decision) -> dict[str, object]:
if decision.action != "handoff":
raise ValueError("handoff payload requires a handoff decision")
return {
"conversation_id": event.conversation_id,
"issue_id": event.issue_id,
"issue_confirmed": event.issue_confirmed,
"handoff_reason": decision.reason,
"risk_flags": list(event.risk_flags),
"last_tool_result": event.last_tool_result,
"reply_preference": event.reply_preference,
"policy_version": POLICY_VERSION,
}
生产接入还需要把 issue_id 映射为允许人工查看的订单或工单字段,并按数据权限做脱敏。本文不提供支付、身份或 CRM 的真实接口,也不把这份字典当成完整的生产 Schema。
如何用合成事件验证规则
项目中的 examples/complaint_policy.py 与 tests/test_complaint_policy.py 不依赖第三方库。测试覆盖七条最小边界:明确要求人工、高风险、重复追问、工具阻塞、事实未确认、安全推进,以及交接载荷包含原因与规则版本。
cd CSDN-VASI-投诉动态策略
python3 -m unittest discover -v
测试不模拟真实电话音频,也不测满意度;它验证的是一条更基础的约束:同一组事件进入同一版规则时,路由结果应稳定、可解释、可回归。
指标、边界与两个常见问题
没有真实业务数据时,先把指标定义清楚,不要给出“投诉下降”之类的结论。可以从以下四个口径开始记录:
| 指标 | 计算口径 | 用来发现什么 |
|---|---|---|
| 重复解释率 | 同一已确认问题进入第三次澄清的会话数 / 已确认投诉会话数 | 路由是否把用户困在重复话术中 |
| 工具阻塞转交率 | 工具阻塞后成功生成交接载荷的会话数 / 工具阻塞会话数 | 流程卡住时是否仍能完整交接 |
| 交接字段完整率 | 必填字段齐全的交接载荷数 / 全部交接载荷数 | 人工是否需要重新追问 |
| 错误推进率 | 实际应交接却进入 advance 的会话数 / 审核样本数 | 风险规则是否过松 |
这些只是评测设计,不是本文的真实测量结果。若要上线,应先用脱敏历史样本和人工复核构建评测集,再在灰度环境观察错误推进和人工接管负担。
用户已经很生气,系统是不是应该立刻转人工?
不能只凭语速、音量或一句情绪化表达做判断。用户明确要求人工、高风险争议、重复解释未解决、业务动作被阻塞,才是本示例中的可审计交接条件。业务方可以增加其他条件,但要能解释它来自什么事实。
用户说“不要解释,直接告诉我结果”,系统还能要求补充信息吗?
可以,但要把缺什么和为什么说清楚。answer_first 只改变表达顺序:有可验证结果就先报结果;没有足够字段或没有执行权限时,仍需澄清或交接。
投诉策略的难点不在于找一句更会安抚人的话。它在于系统能否承认自己现在不能继续,并把已经确认的事实完整交给下一位处理者。
参考资料
- NIST AI Risk Management Framework:风险管理与可追溯治理的通用参考。
- Python
unittest文档:本文测试命令使用的标准库测试框架。
火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。
更多推荐
所有评论(0)