
做智能客服一线这么多年最头疼的从来不是模型准确率不够而是用户情绪识别不准。之前我们上线过一套FAQ机器人意图识别准确率都做到85%了可满意度一直上不去。后来翻录音记录才发现用户早就冒火了机器人还在不紧不慢地背菜单。那次之后团队就重点研究AI Agent的情感计算核心就一句话让机器先读懂情绪再组织语言。这篇文章把我们从立项到上线的完整路径写出来包括技术选型、数据标注、模型训练、系统对接还有后面踩的一堆坑给做智能客服、对话式AI、体验优化的同行们一些参考。1. 项目概述与整体设计思路1.1 情感计算解决的“冷冰冰”问题传统客服机器人的主线是意图识别加FAQ检索用户说“我手机死机了”系统识别出“手机故障咨询”然后返回一篇标准解决方案。这套逻辑在用户情绪平稳时没问题可一旦用户处于愤怒、焦虑、失望的状态单纯按意图走就会显得特别冷血。用户说“你们这破路由器我又重启了还是不行垃圾产品”传统系统很可能直接自动回复“请您尝试重启路由器”这等于火上浇油。情感计算要解决的就是这个“冷冰冰”问题。它不是在现有人话术后面硬加一句“我理解您的心情”而是把情绪感知作为整个对话决策的输入信号。系统先判断题目的语义内容同时判断题目的情绪色彩和强度两者共同决定机器怎么回应。比如是优先道歉、优先解决问题还是直接转人工。这样用户感受到的不再是“答非所问”而是“这个机器人好像真的知道我着急”。我们当时做了一个很直观的对比实验同一批客服工单用传统意图机器人和加入了情感计算的新系统分别回复请用户打分。在情绪较为激烈的会话里新系统的满意度比旧系统高出将近两成。这说明在客户服务该场景下情感计算不是什么锦上添花而是直接决定服务体验的关键能力。1.2 从意图识别到“意图情绪”双通道建模整体设计上我们没有把情感计算做成一个游离的插件而是把它和意图识别放在同一个决策框架下。核心是双通道结构一条通道做意图识别一条通道做情绪识别最后把两个结果拼在一起交给下游的策略模块。比如用户说“你们这破路由器我又重启了还是不行”意图通道给出“网络故障咨询”情绪通道给出“愤怒0.85”策略模块就会决定不优先推说明书而是先道歉、共情再用最快的路径给排查步骤。语义如果对齐还不够情绪也要对齐。这个双通道结构的好处是解耦。意图模块和情绪模块可以独立迭代比如情绪模块有新数据需要更新不会影响意图识别。我们实践下来如果直接在一个模型里同时输出意图和情绪会出现梯度互相干扰的情况尤其是情绪标签样本不均衡的时候意图准确率会被拉低。分开做或者用多任务学习但控制好loss权重相对更稳。用户画像也会参与进来。同样一句“怎么又出问题了”老用户和新用户的容忍度不同历史投诉多的用户需要更谨慎地对待。所以我们在策略层会叠加一个用户维度高频投诉用户、高价值用户、首次咨询用户分别有不同的情绪升级阈值。这个不是技术模型的事而是业务规则的事但放在情感计算框架里一起考虑效果才完整。1.3 总体架构分层整个系统我们分成四层接入层、感知层、认知层、策略层。接入层负责和客服平台对接。不管是网页在线客服、微信小程序还是电话客服统一走一个消息网关转成内部标准格式。感知层做的是原始信号处理比如文本进来先做清洗和归一化语音进来先转文本、提取声学特征。认知层是核心包括意图识别、情绪识别、多轮上下文管理、用户画像查询。策略层则负责决策和生成比如从话术库挑选模板、判断是否转人工、生成最终回复。四层之间用消息队列解耦平时是异步处理但面向用户的消息走实时通道必须保证端到端延迟在可接受范围内。我们最初设计时犯过一个错把所有逻辑放在一个服务里结果一次模型推理超时就把整个会话阻塞了。后面改成独立部署情绪识别单独一个微服务意图识别单独一个微服务策略引擎再通过RPC调用它们反而更灵活。2. 关键技术选型与实现原理2.1 文本情感识别的模型选型文本情感识别是整个系统里最先要定的模块。我们最早试过情感词典打分搞了一套正向词、负向词、程度副词规则拼了半天覆盖率低而且完全处理不了反讽和口语变化。后来直接上预训练语言模型这里有个经验可以分享不要一上来就追最新的大模型先在通用中文预训练模型上加客服语料微调成本和效果都更可控。具体做法是用一个12层的中文预训练模型做基底在积累的客服对话数据上继续训练做六分类愤怒、焦虑、失望、满意、中性、其他。序列长度截断到128学习率设在2e-5batch size 32epoch跑3到5轮。拿验证集看F1值选最好的checkpoint。这里有个关键点分类标签不是越多越好我们一开始定了十几个情绪标签结果很多标签边界模糊标注一致性极差后来精简到六个模型才真正稳住。分类模型输出的不只是一个标签我们还额外加了一个情绪强度打分范围0到1。这个强度分是单独一个全连接头从隐藏层特征回归出来的和分类头共享同一个BERT。之所以要强度分是因为“有点烦”和“暴怒”虽然都可能被分到愤怒类但它们应该触发完全不同的服务策略。只有分类标签的话策略层很难精细调控。2.2 情绪分值阈值与标签体系阈值设计是个非常实际的问题。我们给每个情绪类别设置了两档阈值比如“愤怒”置信度大于等于0.7触发安抚话术“愤怒”置信度大于等于0.9触发优先转人工。这个阈值不是拍脑袋定的而是拿历史已完结的工单做样本请业务团队把需要人工介入的会话标出来然后画ROC曲线取约登指数最大值对应的阈值。最初我们的阈值取的是0.5结果误报率高得吓人大量其实只是语气不太好的中性对话被标成了愤怒导致安抚话术泛滥反而让用户觉得奇怪。后来我们把训练数据和线上分布对齐重新标了8000条会话经过两轮评估后不同情绪的阈值分别落在0.65到0.85之间。这个数字只能作为参考因为不同业务线、不同用户群体分布差异很大最好的办法还是自己积累数据做校准。情绪强度分也做了一层平滑处理。单轮的强度波动太剧烈比如用户打错字、网络卡顿导致对话暂停强度会突然跳变。我们会对最近三轮的情绪强度做指数移动平均权重按照时间衰减当前轮次占0.6上一轮占0.3再往前一轮占0.1这样既能反映趋势变化又不会因为一句突发的抱怨就立刻触发升级。2.3 多轮对话中的情绪累积与衰减单轮情绪识别只能反映“此刻”的状态但客户服务里的愤怒往往是累积出来的。用户可能一开始很平静连续两次没得到有效帮助后逐渐暴躁到最后破口大骂。如果只看每句话的瞬时情绪系统会漏掉这种趋势。所以我们做了一个多轮情绪轨迹模块。设计思路是维护一个会话级的情感状态每进来一轮新的消息就更新这个状态。公式上可以简化为当前轮情感得分和上轮历史状态按比例混合。我们用了滑动窗口加权保留最近五轮的识别结果当前轮权重最高越早的轮次权重越低。同时引入一个“触发事件检测”逻辑比如客服回复超时、错误传递信息、重复询问同一件事会直接给情绪状态加一个惩罚增量因为这类事件本身就会激怒用户。这里有个容易被忽略的细节用户情绪峰值往往不是第一句而是中间某句话。比如用户突然说“算了不问了我去投诉”这时即便是平静语气也一定要当最高优先级处理。所以我们额外保存会话内历史情绪最高分一旦超过阈值后续策略就保持高度谨慎而不是等新情绪识别出来再反应。2.4 语音情绪特征与多模态融合电话客服场景下情感计算还需要处理语音。语音里的情绪信息非常丰富语速变快、音量增大、停顿变多这些都是典型信号。我们参考了一些公开声学特征方案把基频、能量、语速、停顿占比和梅尔频谱等特征拼起来用一个小型CNN或者全连接网络做回归。这里不追求特别复杂的模型因为实际线上环境噪声很多声学特征本身不稳定。多模态融合上我们采用的是“文本为主、语音为辅”的加权策略。语音先转成文本同时抽取声学特征两个模态分别预测情绪标签和强度最后加权融合。文本权重我们设定为0.6语音权重0.4。原因是客服场景里如果语音转文本准确率高时语义信息的可解释性和稳定性比声学特征好得多。实测下来纯语音识别情绪在怒音、哭腔等场景有一定优势但误报也多加权融合后整体AUC最高。不过多模态融合也带来一个额外成本每次对话都需要并行调用两个模型时延压力会变大。我们优化时用了两个措施一是把语音特征抽取放到接入层缓存避免重复算二是把两个模型的推理放到同一个GPU服务上做batch推理整体时延能控制在200毫秒以内。3. 数据准备与模型训练实操3.1 数据标注规范与质量控制情感计算的数据标注比意图标注难很多。意图标注看的是用户“想做什么”相对客观情绪标注看的是用户“是什么状态”非常主观。我们一开始请了三个人同时标同一批数据三个人给的标签经常不一致尤其是“焦虑”和“失望”这两个类别边界很模糊。后面我们定了一套标注规范先把情绪分为正向、负向、中性三大类负向再细分为愤怒、焦虑、失望。标注时不仅看这条消息本身还要看前两轮上下文。比如用户说“哦”单独看是中性但如果前文客服一直没解决问题这个“哦”其实是失望。另外我们要求标注员额外标注一个“触发原因”比如“客服延迟回复”“重复提问”“推诿踢皮球”这些信息后面用于事件检测模块。质量把控上我们用Kappa一致性系数做指标要求标注一致性达到0.7以上。达不到的样本会组织复标并由一位有经验的人仲裁。这个过程非常耗人力但必须做因为情绪模型的性能上限是由标注质量决定的。我们总共清洗出来约5万条高质量会话其中负面情绪占比约30%覆盖了十余个客服业务场景。3.2 数据增强与类别不均衡处理客服数据里中性样本占了绝大多数真正愤怒、焦虑的样本很稀缺。类别不均衡会导致模型把几乎所有句子都预测成中性特别是情绪强度低的时候。我们没有简单做欠采样因为欠采样会丢掉大量上下文信息特别是多轮对话里前面几轮可能对判断后面的情绪很有用。我们主要做了三件事。第一是困难样本挖掘先拿初步模型跑一遍未标注数据把预测概率在0.4到0.6之间、置信度低的样本挑出来人工标注这批样本对模型最有价值。第二是回译将中文客服句子翻译成英文再翻译回来生成语义一致但表达不同的训练数据尤其适合扩充愤怒和焦虑类。第三是同音词和口语化表达替换比如“无语了”“麻了”“裂开”这类网络热词我们建了自己的客服表情词典定期更新进训练语料。损失函数方面我们把愤怒和焦虑两个类别的样本权重调高到1.5让模型在训练时更关注这些少数但关键的类别。实验对比发现调权重比简单改变阈值更有效因为直接调阈值只影响推理阶段而损失权重能让模型本身学习到更细的区分特征。3.3 模型训练与调参细节训练阶段我们采用五折交叉验证每一折都单独调学习率和batch size。由于团队有GPU资源限制我们先把序列长度限制在128超出部分截断。但注意有些用户的“前缀铺垫”其实很长影响情绪判断所以会在接入层做预处理把重复的客套话和无意义语气词删掉再送入模型。多任务学习我们试过但有一次踩了坑意图和情绪同时训练时因为情绪分类的样本数量远小于意图分类意图loss把情绪loss压得死死的。后来我们把两个任务放在同一个预训练模型上共享前几层参数但最后两个分类头各自接一个梯度缩放系数情绪头的梯度乘以1.2才算平衡下来。训练完成后我们导出了ONNX格式做加速推理。这里有一个小细节在导出前要关掉dropout和layernorm的训练模式否则一致性会有问题。上线前我们还会做模拟对话压力测试保证每次调用稳定在50毫秒到150毫秒之间。下面给一段基于Transformers库的训练核心代码不是完整工程但思路可以直接用from transformers import AutoTokenizer, AutoModelForSequenceClassification tokenizer AutoTokenizer.from_pretrained(chinese-roberta-wwm-ext) model AutoModelForSequenceClassification.from_pretrained( chinese-roberta-wwm-ext, num_labels6 ) inputs tokenizer( [你们这破路由器我又重启了还是不行], max_length128, truncationTrue, paddingTrue, return_tensorspt ) outputs model(**inputs) emotion_logits outputs.logits emotion_probs torch.nn.functional.softmax(emotion_logits, dim-1) print(emotion_probs)实际项目中我们会在情绪头后面再接一个回归头用于输出强度分训练时总loss是分类交叉熵和强度回归MSE的加权和。这个细节很重要不要漏掉。4. 系统集成与客户服务流程落地4.1 与现有客服平台对接模型训完只是第一步真正难的是把情感计算流畅地嵌入现有客服系统。我们当时的客服平台用标准Webhook收发消息所以定制了一层消息网关做格式转换。网关在收到用户消息后立刻转发到情绪识别服务并把识别结果存到会话缓存里。策略引擎在生成回复前需要先从缓存取情绪状态再决定话术。接口我们设计成返回一个标准结构方便下游使用{ session_id: S123456, message: 你们这破路由器我又重启了还是不行, intent: network_troubleshoot, emotion: { type: anger, confidence: 0.82, intensity: 0.9, trend: rising }, suggested_strategy: empathy_and_apology, priority: urgent }这里有个值得注意的点结构化输出里的confidence和intensity是两个不同概念。confidence是模型对该标签判断的置信度intensity是情绪本身的强弱。一个模型可能非常确信用户“有点烦躁”高置信度、低强度也可能不太确定用户“是不是已经暴怒”低置信度、高强度。策略层对这两个维度的处理方式完全不同一定不能混。时延方面我们要求端到端响应不超过500毫秒否则用户可以明显感觉到卡顿。为了做到这一点所有模型推理都在一个独立的推理集群上执行用GPU批量处理平时稳定在100毫秒左右。情绪识别结果还会缓存10分钟这样如果用户连发几条消息不需要重复推理直接复用上一轮状态。4.2 动态话术生成与策略路由有了情绪识别结果接下来最关键的是怎么决定“说什么”。我们维护了一个策略路由表根据情绪类型和强度路由到不同话术策略。比如“愤怒高强度”策略是“共情快速道歉转人工预备”“焦虑中强度”策略是“安抚明确给出下一步处理时间”“失望低强度”策略是“解释原因提供补偿方案”。共情话术不能做得太死板。我们最早的做法是模板拼接比如“很抱歉给您带来不好的体验我们会尽快处理。”这句话单独看没问题但如果上一轮已经说过一遍这轮再说用户会明显觉得机器在敷衍。所以我们为每个策略准备了至少十套不同表达并且根据对话轮次、用户历史偏好做替换。比如老用户就少一点客套话直接上报处理新用户需要更详细的解释。我们还加了一个“反机械共情”机制模型会统计最近三轮里相似共情话术出现的次数如果超过两次就强制切换到行动导向的表达比如“我现在立刻帮您查一下请稍等”。实测这个机制对抑制用户二次情绪升级非常有效。4.3 人工升级与安全兜底不是所有情形都适合让机器人继续聊。我们的升级规则是当情绪强度达到0.9且置信度大于0.7直接转人工当用户明确表达“找人工”“转人工”“投诉”不管情绪分多少立即转人工当多轮情绪趋势持续上升即使当前分还没到阈值也触发人工预备队列让人工客服能看到这个会话的实时情绪曲线。转人工并不是直接把用户丢过去而是需要在转接信息里携带完整的情绪轨迹。人工客服在接起会话前已经能看到用户从第一轮到现在情绪是怎么演变的、已经做过哪些安抚、问题有没有解决。我们内部叫“情感接力”实测接起后人工客服的平均处理时长缩短了15%因为不用再从头问一遍情况也更容易知道用户为什么生气。这里有一条重要的安全兜底经验情绪识别模块要设置降级开关。如果模型服务异常或者返回结果超时系统必须自动降级为标准意图模式绝不阻塞用户对话。宁可让机器人显得笨一点也不能让用户因为技术故障连话都说不了。4.4 A/B测试与效果评估情感计算上线前我们做了三周A/B测试。实验组接入情感计算对照组保留原来的意图机器人所有会话流量随机分配。主要看四个指标用户满意度CSAT、首解率、情绪转变率、转人工率。情绪转变率是指会话结束时用户的情绪类别相对第一轮是否从负向变为中性或正向。测下来实验组CSAT提升了8个百分点情绪转变率从40%提升到55%转人工率下降了12%。比较有意思的是首解率没有显著变化说明情感计算没有直接提升业务解决率但它显著改善了服务过程的体验用户在同等解决率下更愿意给好评也更少二次投诉。当然A/B测试里也出现过对照组和实验组相互干扰的情况比如一个用户同时开了两个会话分别进了不同组情绪会被另一个会话影响。所以我们在测试时做了用户粒度分流确保同一个用户始终在一个组里数据才干净。5. 踩坑实录与问题排查技巧5.1 情绪误判的典型案例我们先看一个非常经典的误判用户说“你家产品真行”这明显是反讽我们第一版模型直接识别成了“满意”然后给用户发了一个“感谢您的支持”的答复简直灾难。后来排查发现训练数据里反讽样本太少模型根本没有见过这种表达。解决方法是专门构造了一批反讽语料包括“真行”“真棒”“太厉害了”“你们最棒了”等常见说法并标注为“负向情绪”。还引入了一个小的讽刺检测模块如果文本里出现明确负向事件但搭配了正向形容词就提高负向情绪概率。类似的坑还有用户表达“呵呵”“您说的都对”这类敷衍话。它们从字面看是中性偏正的但实际是强烈不满。我们也是靠扩充训练数据才最终把这些表达接住。5.2 方言、口语和网络热词干扰客服场景里用户不会一本正经说书面语方言、缩略语、网络热词到处都是。我们遇到过用户说“我裂开了”模型识别成中性因为没有见过这个词。还有“栓Q”“YYDS”“无语子”等等随着网络语境变化层出不穷。单一静态模型根本跟不上。解决方案分成两步。第一步在接入层维护一个动态同义词和热词映射表比如“裂开”映射到“崩溃”“栓Q”映射到“无语”把新词先翻译成模型更容易理解的旧词。第二步每个月用最新的线上语料做一次增量训练不用全部重训只往情感分类头里加新样本大概每轮训练半小时成本不高。热词表不能盲目替换比如“牛”在不同语境下可以是夸赞也可以是反讽要结合上下文判断。5.3 多轮上下文丢失导致情绪漂移误会最开始我们只对单轮消息做情绪判断结果出现了一个典型问题用户前面抱怨了很久最后回了一个“嗯”系统判断情绪为中性于是自动开始推产品广告用户彻底爆发。这个“嗯”如果只看字面确实是中性但在多轮场景里它是用户压抑情绪下的敷衍回应本质上还是负向。我们后来把所有会话都保留最近五轮输入并且计算情绪趋势。如果用户当前情绪分虽然不高但之前三到五轮里出现过高强度负向情绪那策略层会仍然保持谨慎不主动切换营销话题。趋势上升、下降、持平三种状态分别对应不同的应对方式。这个改动上线后因为情绪漂移导致的投诉明显减少。5.4 过度共情导致问题解决效率下降共情本身是好东西但容易矫枉过正。有一版我们为了让用户感觉更“懂我”把安抚话术做了很多结果机器人绕来绕去就是不给出具体解决方案。用户体验是“这机器人光会安慰人完全不办实事”反而更生气。我们后来在策略层加了一条硬规则连续两轮以内必须出现行动导向的表达。比如第一轮可以共情第二轮就必须给出明确的处理步骤或时间预期。如果安抚话术在整段对话里的占比超过40%系统会自动削减强制加入问题排查或解决方案。这个调整非常实际直接让CSAT和首解率同时上升。5.5 数据隐私与合规提示做情感计算必然要碰用户对话数据这里特别提醒一下合规红线。我们设计系统时严格要求情绪的识别和存储尽量在内存中完成不落盘原始对话文本模型训练用到的数据必须经过脱敏处理去除手机号、姓名、地址等个人信息跨部门使用情绪标签时只能提供聚合统计结果不允许提供个人级别的情绪轨道。虽然这看起来只是工程细节但在实际项目里非常重要。如果一开始不考虑数据授权和隐私保护后续业务拓展会处处受限。提前把数据脱敏和访问审计做好后面再想加新场景就会顺利很多。6. 经验总结与扩展方向最后分享几条我个人的体感。情感计算在客户服务里最大的价值不是让机器人“更高级”而是让它“更像一个靠谱的服务人员”。一个靠谱的客服不会在用户发火时背产品说明书也不会只道歉不给方案。情感计算就是让机器人学会这个分寸感。如果现在要新起一个类似项目我会建议先小范围跑通“情绪识别策略路由”的闭环不要一上来就做很复杂的多模态。先让文本情绪识别在客服场景里真正用起来用一段时间收集线上反馈再逐步扩展。另外情绪标签体系一定要业务方参与制定技术团队自己闭门造车定义的情绪上线后经常和真实业务不匹配。这个系统后续还可以向三个方向扩展。第一是做智能质检不光看客服有没有说标准话术还可以分析客服的情绪是否匹配用户情绪比如用户已经很激动了客服还是用一种平淡冷漠的语气质检系统可以自动提醒。第二是做情感趋势预测基于用户多个会话的情绪轨迹预判哪些用户存在流失风险提前做关怀。第三是向语音呼叫中心深化把声学特征模型加进去之后客服系统就能在用户真正发火之前提前感知给人工坐席留出准备时间。我自己做得越久越觉得情感计算不是一个单纯的算法问题而是一个服务设计和产品体验的问题。技术归根到底是为人服务的能把用户那口气理顺整个客服体系的价值才算真正发挥出来。