用户点开右下角的聊天气泡,却没人理:vibe 项目在线客服实时聊天实战
聊天气泡只是个按钮,真正的在线客服是一条会话生命周期:营业时间与排队规则、WebSocket 送达与断线重连、客服工作台、没人在线时的机器人兜底,以及上线前必须跑的五组测试。

用户点开右下角的聊天气泡,却没人理:先讲一个虚构的教训
先讲一个虚构的教学例子。陈默一个人用 AI 搭了个日程应用“云朵日程”,上线第三周,有个用户林小雨在付款时卡住了:点了支付没反应,订单状态一直转圈。她点开页面右下角那个很显眼的聊天气泡,写了句“你好,付款失败了,能帮忙看看吗”,然后等。等了 5 分钟,没人回;等了 40 分钟,还是没人回。气泡右上角一直显示“通常几分钟内回复”,可那是陈默随手抄来的文案,背后根本没有客服,也没有值班表——消息发出去之后,直接掉进了一个没人看的数据库表里。第二天林小雨退款走人,还在应用商店留了条一星评价:“客服形同虚设。”
这就是在线客服最常见的死法:入口做得像模像样,后面却没有一条完整的链路。聊天气泡只是个按钮,真正的在线客服是一整套东西:用户发消息之后谁接、排多久队、没人在线怎么办、断线了消息丢不丢、聊完的记录去哪了。只做了按钮没做链路,等于在门口挂了块“欢迎光临”的牌子,门后却没有店员。
本文讲的就是怎么给 vibe 项目做一套能真正跑起来的在线客服实时聊天:从会话的生命周期,到营业时间与排队规则,到消息怎么送达、断线怎么办,到客服工作台长什么样,再到没人在线时的兜底和上线前的测试清单。以下示例均为虚构的教学示例,应用名、人名与数字均为为讲解而构造的简化版本。

一、在线聊天不是“多个输入框”,它是一条会话生命周期
先把概念理清,不然写出来的东西迟早散架。在线聊天不是“页面上多个输入框加一个发送按钮”,它是一条会话从生到死的完整生命周期:用户发起会话、进入排队、被客服接起、双方来回聊、需要时转接给另一个人、问题解决后结束会话、用户打个分、记录归档。每一个环节都对应一个状态,状态之间只能按规定的方向走,不能乱跳。
状态机不用搞得很复杂,但必须显式存在。最小可用的状态只有六个:waiting(排队中)、active(进行中)、transferred(转接中)、resolved(已解决)、closed(已关闭)、abandoned(用户中途离开)。客服接起之前是 waiting,接起之后变 active;转接时先变 transferred,接手成功再回 active;问题解决后客服点“解决”,会话变 resolved,等用户确认或超时后自动变 closed;用户排了十分钟没等到人、直接关掉窗口,会话就变 abandoned。每个状态变化都要记一条时间戳:什么时候排队、等了多久、什么时候接起、聊了多久、什么时候结束。这些时间戳就是你以后优化客服的全部依据。
最容易被忽略的两个状态是 abandoned 和 resolved 之后的那一步。Abandoned 不是“用户走了就完了”,而是你要回头看的数据:每天有多少人排队排到一半走了,平均等了多久走的。如果 abandoned 的人平均只等了两分钟,说明入口有问题——可能用户点开发现要先填一大堆表单;如果平均等了十五分钟,说明人手不够。Resolved 之后也别急着关:先问用户一句“问题解决了吗”,给一个“已解决 / 未解决”的按钮。用户点“未解决”,会话要能一键重开,回到 waiting,而不是让用户重新描述一遍问题。重开时带上原来的会话记录,客服一眼就能看到前情。
还有一个细节:一个用户同时只能有一条进行中的会话。听起来是废话,但新手常踩:用户在手机上开了一个会话,又在电脑上开了一个,两个窗口各聊各的,客服在两边回出两套答案。做法是按用户 id 做唯一约束,同一用户新开会话时,先把旧的未关闭会话接回来,界面上直接回到原来的对话,而不是新建一条。未登录的游客用户没有用户 id,就用浏览器里存的一个匿名会话 id,同一个浏览器里也只保留一条。

二、先定规矩再写代码:营业时间、排队与并发上限
写代码之前,先把客服的规矩定下来,写在帮助文档里,也写进代码的配置里。规矩一共五条,缺一条,上线后都会有人来问你。
第一条是营业时间。一个人做的项目,不可能 24 小时在线,诚实写出来比假装在线强得多。营业时间要写三样东西:每周哪几天在线、每天几点到几点、节假日怎么处理。界面上的状态要跟着营业时间实时变:营业时间内显示“在线,平均 X 分钟内回复”,营业时间外显示“当前不在营业时间,我们会在明天 9:00 后回复”,并且给一个“留言转工单”的入口。千万别在半夜三点还显示“通常几分钟内回复”,用户等一夜,第二天带着火气来。
第二条是排队上限。排队不是无限排的,排队人数要设一个上限,比如同时排队不超过 20 人。超过上限时,界面要诚实地说“当前咨询人数较多,预计等待 30 分钟以上,您也可以留言,我们会邮件回复”,而不是让第 21 个人也排进去、等一个小时。排队位置要实时告诉用户“您前面还有 3 位”,这个数字每次变化都要推给用户,别让用户对着一个静止的页面干等。
第三条是客服同时接待的上限。一个客服同时聊几路,要设死。刚开始建议 3 路,也就是一个客服手里最多同时有 3 条 active 会话。超过 3 路,回复质量肉眼可见地下降:回错窗口、答非所问、把 A 用户的问题回给 B 用户。第 4 条会话进来时,系统要么让它继续排队,要么分给另一个在线客服,绝不能硬塞给已经满了的人。这个上限写进配置里,别写死在代码里,因为你招到第二个客服那天就要改。
第四条是自动回复。用户发第一条消息后,系统要在几秒内先回一句,告诉用户三件事:消息收到了、前面还有几位、大概等多久。这句自动回复不是客服亲手打的,是系统按规则发的,文案要写得像人话:“收到啦,前面还有 2 位,大概 5 分钟内有客服接起,您可以先描述一下遇到的问题。”自动回复还能顺手做一件事:让用户先选问题类型(付款问题、账号问题、功能建议、其他),选完直接把会话打上标签,分给对口的人。选类型这一步别搞超过四个选项,选项一多,用户还没聊上就先烦了。
第五条是转工单的规矩。不是所有问题都能在聊天里当场解决:要查账的、要退款的、要改数据的,都得转工单。规矩是:客服在聊天里点“转工单”,系统自动把整段聊天记录、用户信息、问题标签一起带过去,用户那边收到一条“已为您转交,工单号 #1234,处理后会邮件通知您”。用户不需要重新描述问题,这是转工单唯一不能妥协的地方。反过来,工单处理完了,也要回聊天里补一句结果,让用户在一个地方看到全过程。
| 规矩 | 建议值 | 没定会怎样 |
|---|---|---|
| 营业时间 | 写清星期、时段、节假日 | 半夜显示“几分钟内回复”,用户白等一夜 |
| 排队上限 | 同时排队不超过 20 人 | 第 50 个人排一小时,最后带着火气走 |
| 单客服并发 | 同时最多 3 路会话 | 回错窗口、答非所问 |
| 自动回复 | 几秒内确认收到 + 排队位置 | 用户不知道消息发出去了没有,连发五遍 |
| 转工单 | 记录、标签、用户信息自动带过去 | 用户换个地方重新描述一遍问题 |
三、消息怎么送达:长连接、断线重连与回执
在线聊天是实时功能,实时功能的第一性原理是:消息不能丢、顺序不能乱、状态要让双方都看得见。做不到这三点,界面做得再好看也是个摆设。
先说通道。浏览器里的实时聊天,标准做法是 WebSocket 长连接:用户打开聊天窗口时建一条连接,消息走这条连接双向推。别用轮询,也就是别让前端每两秒问一次服务器“有新消息吗”,用户量一上来,服务器会被空问拖垮。WebSocket 建连之后,第一件事是鉴权:连接建立时带上登录态,后端校验通过才允许订阅这个用户的会话频道。没鉴权的聊天频道等于把所有会话敞开给任何人看,千万别省这一步。
再说断线。手机切后台、进电梯、地铁过隧道,连接随时会断,断线是常态不是异常。前端要做自动重连:断开后先等 1 秒重连,失败再等 2 秒、4 秒、8 秒,最长间隔别超过 30 秒,每次重连都带上“上次收到的最后一条消息 id”。后端收到这个 id,就把这之后的新消息一次性补发给前端,这叫断线补发。没有断线补发,用户重连回来会发现中间少了几条消息,客服以为自己回过了,用户以为客服没理他,两边各说各话。
然后是回执,也就是消息边上那个小对勾。至少要做三级:已发送(消息到了服务器)、已送达(消息推到了对方设备)、已读(对方打开会话看到了)。回执不是装饰,它是双方的共同事实:用户看到“已读”就知道客服看见了,不会连发五遍“在吗”;客服看到“已读”就知道用户看见了解决方案,不会反复追问“看到了吗”。已读回执要节流:用户打开会话时发一次就行,别每条消息都回一次。还要给用户一个开关,有些人不想让对方知道自己已读,关掉之后只显示到“已送达”为止。
最后是顺序与去重。网络抖动的时候,同一条消息可能被发了两遍:用户点了发送没反应,又点了一次;或者重连补发时重复推了一条。去重的办法是每条消息带一个客户端生成的唯一 id,后端按这个 id 做幂等:同一个 id 的消息只存一次,重复的直接丢弃。顺序的办法是每条消息带一个服务端发号的递增序号,前端按序号排序显示,不按到达时间排。两条消息同时到达时,按序号排永远不会错,按到达时间排迟早会错一次,而错的那一次,恰好会是用户截图投诉的那一次。
// 错:发送失败就丢了,用户以为发出去了
async function send(text) {
await fetch('/api/chat/send', { method: 'POST', body: text });
}
// 对:每条消息带客户端唯一 id,失败可重试、重复可去重
async function send(text) {
const clientId = crypto.randomUUID(); // 同一条消息重试时 id 不变
const res = await fetch('/api/chat/send', {
method: 'POST',
body: JSON.stringify({ clientId, text }),
});
if (!res.ok) {
// 标记为“发送失败,点一下重试”,不要悄悄丢掉
markFailed(clientId);
}
}
离线消息是最后一道兜底。用户关掉页面之后,客服回的消息不能丢进黑洞:后端要把消息存进数据库,用户下次打开聊天窗口时,先从数据库拉取历史记录,再接上实时连接。更进一步,如果用户留了邮箱,客服回复后超过 10 分钟用户还没上线,系统可以发一封邮件提醒“客服回复了您的咨询”,邮件里只写“您有一条新回复”,别把聊天内容全文贴进邮件——邮件会被转发、会被存档,聊天内容只应该待在聊天记录里。
四、客服工作台:客服每天打开的那个页面,决定了回复质量
很多 vibe 项目只做了用户侧的聊天窗口,忘了客服自己也要一个工作台。结果就是客服每天在一个将就的后台里回消息:会话列表按时间乱排、找一条会话要翻三页、回消息要切五六个系统查订单。这种工作台回出来的消息,质量不可能高。
工作台至少要有四块东西。第一是会话列表,按“待接起、进行中、已解决”分组,待接起的按等待时间倒序排,等得最久的在最上面。每一行要一眼看到:用户是谁、问题类型标签、等了多久、最后一条消息是什么。千万别把列表做成按创建时间正序,等得最久的人沉在最底下,客服永远先回刚来的。
第二是客户上下文。客服点开一条会话,右边要自动带出这个用户的信息:注册时间、套餐、最近一笔订单、历史会话记录。客服不用开口问“您的订单号是多少”,系统已经帮他查好了。上下文里不要放敏感信息:密码、支付卡号、身份证号这类东西,客服界面上根本不应该出现。能不展示就不展示,这是原则,不是可选项。
第三是快捷回复。客服每天会反复打同一批话:“请问您的订单号是多少?”“请刷新页面再试一次。”“退款已提交,预计 3 个工作日到账。”这些话要存成快捷回复,支持按关键词搜索,一键插入。快捷回复的文案要定期review:哪条用得最多、哪条发出去之后用户追问最多。追问最多的那条,文案一定有问题,改文案比培训客服管用。
第四是内部备注和转接。客服之间要能互相留备注:“这个用户昨天来过,问题是发票抬头。”备注只有客服看得到,用户看不到。转接时,备注、标签、上下文要一起带给下一个人,接手的人不用重新问一遍。转接要让用户知道:“我把您转给负责付款问题的同事小王,他马上接手”,而不是悄悄换人,用户对着一个突然换了名字和语气的人发愣。
工作台还要有一个“值班”开关。客服上班点“开始值班”,系统才给他分会话;下班点“结束值班”,手里没聊完的会话要交接出去,不能留在自己名下过夜。没有值班开关,系统会在半夜把会话分给一个已经睡觉的人,用户等到天亮也没人回,而你还以为有人在值班。
五、用户侧体验:聊天气泡越小,细节越不能省
用户侧的聊天窗口通常只有右下角一个小气泡,地方小,细节就更不能省。先说入口:气泡要一直可见,别藏在三级菜单里;气泡上要有状态点,绿色是在线、灰色是不在营业时间,点开之前用户就知道现在有没有人。气泡上还可以挂一个未读红点,有新回复时显示数字,用户切到别的页面再回来,一眼就知道客服回了。
再说输入体验。输入框要支持多行、支持粘贴截图、支持发图片和文件。用户遇到问题时,最有用的往往是一张报错截图,而不是一段文字描述。图片上传要有限制:单张不超过 5MB,只接受图片格式,上传前在本地先压缩一下。文件也是:只接受常见的文档格式,上传后客服那边能直接预览,别让客服下载一个不知道是什么的东西。
“对方正在输入”这个小提示,别小看它。客服开始打字时,用户那边显示“客服正在输入…”,用户就知道有人在,不会因为等了 20 秒没回复就连发三条“在吗”。反过来也一样,用户打字时客服那边也显示,能避免两个人同时发消息、互相打断。正在输入的状态要节流:开始打字发一次,停下 5 秒发一次“停止输入”,别每个按键都发一次。
结束会话的体验也要设计。问题解决后,别让会话永远挂在那里:客服点“解决”后,用户那边显示“问题解决了吗?”加两个按钮“已解决”和“还有问题”。点“已解决”之后,顺手问一句打分:1 到 5 星,再加一个可选的文字框“有什么想对我们说的吗”。打分不是为了好看,是为了发现问题:连续三个 1 星都指向同一个功能,那个功能一定有问题。打分要自愿,关掉也能继续用,别搞成不打分就不让走的拦截。
最后是隐私。聊天记录里不要让用户发密码、验证码、银行卡号。输入框上方可以放一句轻提醒:“请不要在聊天中发送密码或验证码。”真有人发了,系统要能自动识别并打码,比如把连续的 6 位数字、银行卡号格式替换成星号,客服那边看到的是“****”,而不是明文。这条规则要在客服手册里也写一遍:客服绝不能主动向用户索要密码或验证码。
六、没人在线的时候:机器人先顶上,诚实比假装在线重要
一个人做的项目,大部分时间是没有客服在线的。这个现实不用藏,藏也藏不住。没人在线的时候,系统要做三件事:先让机器人顶上、再给用户留一个能找到人的口子、最后把承诺说清楚。
机器人顶上的第一层是常见问题。把用户最常问的十个问题做成快捷入口,点开直接给答案:“怎么退款?”“怎么换套餐?”“发票去哪开?”答案要写得具体,能一步步照着做,别只给一句“请查看帮助中心”。第二层是关键词匹配:用户发“退款”,机器人回退款流程的链接和要点;用户发“发票”,机器人回开发票的入口。这两层都答不上来,机器人要立刻承认:“这个问题我答不上来,已经帮您转给客服,客服会在营业时间回复您。”承认答不上来,比胡说八道强一百倍。
留资转工单是第二道口子。营业时间外,用户点开聊天看到的应该是:“现在是休息时间,客服会在明天 9:00 后回复。您可以先留言,留下邮箱,回复后我们会邮件通知您。”留言表单只要三样东西:问题描述、邮箱、问题类型。别让用户填十项信息,用户是来求助的,不是来填表的。留言提交后给一个工单号,用户凭工单号能查到处理进度。
排队太长的时候也要诚实。如果前面排了 20 个人、预计等 40 分钟,界面就要直接说“预计等待 40 分钟,您也可以留言,我们会邮件回复您”,把选择权交给用户。最糟糕的做法是让用户排着队、看着一个静止的“前面还有 19 位”干等 40 分钟。用户自己选留言,就不会觉得被晾着;用户自己选继续等,也有了心理准备。
七、聊天记录与隐私:存多久、谁能看、怎么删
聊天记录是用户跟你说过的最私人的话,存多久、谁能看、怎么删,这三件事要在上线前定好,写进隐私政策里。
存多久,建议默认 180 天。180 天够用来复盘和处理纠纷,又不至于把几年前的对话无限期留着。超过 180 天的记录要自动归档或删除,别让它在数据库里越积越多。删除要真删:数据库里的记录、备份里的记录、日志里可能残留的片段,都要有清理的办法。用户主动要求删除自己的聊天记录时,要在 30 天内处理完,并告诉用户“已删除”。
谁能看,原则是最小权限。只有处理这条会话的客服和管理员能看记录,别的客服看不到。客服离职或换岗,权限要立刻收回。记录的查看要记日志:谁、什么时候、看了哪条会话的记录。记日志不是不信任客服,而是出了问题能查清楚。客服的内部备注也一样,只有客服团队看得到,用户侧永远看不到。
敏感信息要自动打码。用户发了身份证号、银行卡号、密码、验证码,系统要在入库之前就识别出来、替换成星号,只存打码后的版本。识别规则不用很复杂:连续的 18 位数字、16 到 19 位银行卡号格式、6 位纯数字验证码,这三类先 cover 住。打码规则要定期复查,因为用户发敏感信息的方式永远比你想的多。
还有一个容易被忽略的点:聊天记录不要用来训练模型,也不要拿去给 AI 客服当语料,除非用户明确同意过。用户找客服是来解决问题的,不是来给你贡献训练数据的。这条要写进隐私政策,别藏在角落里。
八、上线前必须跑的测试清单
上线之前,把下面这些场景一个个跑一遍,别只测“能发消息”就上线。
第一组是正常流程:用户发起会话、排队、客服接起、来回聊三轮、客服点解决、用户点已解决并打分,全程走通。每一轮消息都要确认回执状态正确,顺序不错乱。
第二组是断线场景:用户聊到一半断网 30 秒再回来,消息不能丢、不能重复;客服聊到一半刷新页面,回来还能看到原来的会话;用户同时在手机和电脑上打开,会话是同一条,不是两条。
第三组是边界场景:排队人数达到上限,第 21 个人看到的是“留言”入口而不是无限排队;营业时间外打开聊天,看到的是留言表单而不是“几分钟内回复”;客服同时接了 3 路,第 4 路不会硬塞给他;用户发了一张 10MB 的图,上传被拒绝并给出明确提示。
第四组是转接与转工单:客服 A 转给客服 B,B 能看到完整上下文;转工单后用户收到工单号,工单处理完聊天里有回执;用户点“未解决”,会话能重开并且带着原来的记录。
第五组是隐私:用户发了一串 6 位数字验证码,入库后是打码的;没登录的游客看不到别人的会话;客服 B 打不开客服 A 正在处理的会话记录。
跑完这五组,再上线。上线第一周,每天看三个数:平均首次响应时间、排队放弃率、用户打分。这三个数就是你的客服健康度。首次响应时间超过 10 分钟,说明人手不够或者自动回复没写好;放弃率超过 20%,说明排队上限或营业时间有问题;打分连续走低,去看 1 星评价里反复提到的那个功能。
九、常见的反模式:这些坑,别人都踩过
反模式一:把聊天入口做成全站弹窗。用户刚打开首页,聊天气泡就自动弹出来挡住一半屏幕,关掉之后每刷新一次又弹一次。入口要安静地待在右下角,等用户需要的时候再打开。主动邀请可以有,但要克制:用户在同一个页面停留超过 60 秒、或者连续三次操作失败,再弹一次轻提示,而且同一天只弹一次。
反模式二:用聊天代替所有反馈入口。用户想提功能建议、想举报 bug、想商务合作,全被赶进同一个聊天窗口,客服每天被不属于他的问题淹没。聊天只接“需要马上解决的问题”,功能建议走反馈入口,商务合作走邮箱,各走各的口子。
反模式三:客服回复全是模板。用户说了三段话描述问题,客服回一句“亲,请问有什么可以帮您”,再回一句“亲,请提供订单号”。模板可以用,但要先看完用户说的话再回。用户已经把订单号写在第一条消息里了,客服还问“请问订单号是多少”,用户会立刻觉得对面根本没在看。
反模式四:上线之后没人看数据。聊天跑了三个月,没人知道平均等多久、多少人中途走了、哪些问题反复出现。数据不用多,就盯三个数:首次响应时间、放弃率、打分。每个月花半小时看一次,比加任何功能都管用。
在线客服这件事,说到底就一句话:用户点开聊天气泡的那一刻,他默认对面是有人在的。你要做的,就是让这个默认成立:有人接、接得快、接了能解决问题、解决不了也给个明确的说法。入口、排队、消息、转接、记录,链路上的每一环都做到位了,那个小小的气泡才真的算“在线客服”,而不是一个装饰品。
评论 (0)
相关文章

“3 分钟前”还是“2026-11-06T02:53:33.198Z”?一张可直接抄的阈值表、服务端只存 UTC 时间戳的铁律、前端分档刷新、夏令时与多语言边界,以及上线前必须跑的五组测试。

一分钱对不上,账就没法结:浮点数为什么算不准钱、只存整数分的铁律、只在最后关口舍入一次、折扣税费分摊与多币种的坑,以及上线前必须跑的七个测试用例。

2GB 视频传到 99% 断了不该从头再传:分片协议怎么定、前端怎么切怎么传、服务端怎么合、秒传怎么做、三层失败处理怎么做,附上线前检查清单。