摘要

设备客户来电报修,会描述设备故障现象。语音机器人识别故障类型,收集客户信息,自动创建售后工单,分配给售后工程师处理。本文结合一家小型设备厂售后报修系统的落地过程,讲清楚语音机器人怎么接住报修电话、怎么从客户口语描述里识别故障类型、怎么通过多轮对话把设备型号和联系人等槽位收齐、怎么自动建单并分派给合适的售后工程师。内容包括整体架构、故障分类的规则与模型配合、槽位收集的对话设计、工单字段映射、分派策略、异常兜底,以及上线后的效果数据和踩坑复盘。代码和表格都是工程里实际用过的简化版,适合正在做售后报修自动化、语音机器人、工单系统的开发和运营同学参考。

关键词

小型设备厂;语音机器人;售后报修;故障类型识别;工单自动创建;ASR语音转写;多轮对话;售后工单系统;意图识别

一、先说结论:这套系统解决什么问题

一句话概括:客户打报修电话进来,语音机器人在几轮对话内问清故障现象、设备型号、城市和联系人,系统判断故障类型,自动建好售后工单并分派给对应区域的工程师,识别不了的、客户着急的转人工内勤。

它解决的是四个具体痛点:

痛点过去做法自动化后
登记耗时长内勤边听边手写,单通3到5分钟机器人对话采集,内勤只处理转人工部分
故障类型靠经验新内勤容易分错,工程师白跑规则+模型双保险,低置信度转人工判断
分派不准翻通讯录找区域工程师,来回转按技能、区域、负载自动选人
夜间周末无人接客户留言,次日处理机器人接单,次日优先派

下面从业务场景开始,把技术实现一步步拆开。

二、业务场景:小型设备厂售后报修的真实情况

先交代背景。我们这家厂做的是小型包装机械和食品加工设备,全国几千家客户,售后团队十来个人。设备单价几万到几十万不等,客户以中小食品厂、包装厂为主。设备故障大致分几类:

  • 电气类:不启动、跳闸、报警代码、温控异常、电机不转。

  • 机械类:异响、卡料、传送带跑偏、轴承发热、漏油。

  • 工艺类:封口不牢、计量不准、温度上不去、压力不稳。

  • 操作类:客户不会调参数、不会换模具、误操作导致停机。

客户来电报修时,描述方式非常口语化。有人上来就说“你们那台机器又不转了”,有人会说“封口机封不牢,袋子一撕就开”,还有人直接报报警代码“显示E03”。售后内勤需要边听边判断:这是哪类故障、客户买的是哪款设备、在哪个城市、急不急、之前有没有修过。判断完再决定派哪个工程师、带什么配件。

过去的流程是:客户打电话 → 内勤接听 → 手写记录 → 挂机后录入Excel或简易工单系统 → 微信或电话通知工程师。问题很明显:高峰期电话占线,内勤忙起来记录不全,故障类型靠个人经验判断,新来的内勤容易分错。更麻烦的是晚上和周末,内勤下班后客户报修只能留言,第二天才能处理。

技术目标很明确:客户来电后,语音机器人先接住,通过几轮对话把故障现象、设备型号、联系人、地址问清楚,系统自动判断故障类型并创建工单,按规则分派给对应售后工程师。识别不了的、客户着急的,转人工内勤。整个链路要快,客户等不了机器人啰嗦半天。

三、整体架构:电话进来之后发生了什么

架构不复杂,小型设备厂不需要堆太多组件,关键是链路短、稳定、好维护。

text

客户来电
   │
   ▼
400电话线路 ──呼叫事件──▶ 接入服务(Webhook)
   │                          │
   ▼                          ▼
语音机器人(IVR+ASR+对话管理)  会话状态服务
   │                          │
   ▼                          ▼
故障分类服务 ──────────▶ 工单创建服务
   │                          │
   ▼                          ▼
槽位收集(多轮对话)        分派引擎
   │                          │
   ▼                          ▼
                  工单系统(API) ──▶ 售后工程师企微/短信通知

电话线路这块,我们用的是优音通信的400号码线路,提供呼叫事件推送和语音流对接能力,接入方式比较直接。呼叫事件里带主叫号码、通话开始时间、被叫号码等字段,这些是后续关联客户历史记录的基础。

语音机器人负责三件事:一是IVR导航,让客户按键选择“报修”“咨询”“投诉”等大类;二是ASR实时转写,把客户说的话变成文本;三是对话管理,按槽位逐项收集信息。故障分类服务在对话过程中持续跑,文本每增量一次就做一次轻量判断,等关键信息收齐后再做最终分类。工单创建服务拿到分类结果和槽位信息后调工单系统API建单,分派引擎按规则选工程师。

这里有个设计取舍:语音机器人是全语音交互,还是“语音+按键”混合?我们最终选了混合。纯语音对老年客户不友好,纯按键又收集不了故障描述。实际做法是:设备型号让客户按键选择或口述数字,故障现象让客户自由说,联系人电话直接用来电号码兜底,地址让客户说或者按键选省份。这样对话轮次控制在四到六轮,大部分客户能接受。

四、核心技术一:故障类型识别,规则打底、模型补语义

4.1 为什么小型设备厂适合规则先行

大型设备企业的故障类型可能有几百种,小型设备厂的故障类型相对集中。我们统计了历史工单,高频故障大概二十来种,覆盖了八成以上的报修。这种分布下,规则匹配的性价比很高:可解释、可快速调整、不需要标注大量数据。我们第一版完全用规则,上线两周就把高频故障的识别率做到了七成多。

故障分类的规则不是简单的关键词命中,而是“关键词+否定词+上下文”的组合。比如客户说“不是电机的问题,是封口不牢”,要能识别出否定结构,不能简单匹配“电机”就判成电气故障。我们用了简单的正则和关键词窗口,先把高频故障的典型表达覆盖住。

python

import re

FAULT_RULES = {
    "electrical_start": [r"不启动", r"启动不了", r"开不了机", r"没反应", r"跳闸"],
    "electrical_alarm": [r"报警", r"报错", r"显示[A-Z]\d+", r"代码[Ee]\d+"],
    "mechanical_noise": [r"异响", r"响声", r"噪音", r"咔咔", r"嗡嗡"],
    "mechanical_jam": [r"卡料", r"卡住", r"堵了", r"不下料"],
    "seal_issue": [r"封口不牢", r"封不严", r"一撕就开", r"封口(很|太)?差"],
    "temp_issue": [r"温度上不去", r"不加热", r"温控不准", r"温度不稳"],
    "weigh_issue": [r"计量不准", r"重量不对", r"称不准", r"下料不准"],
}

NEGATION_WORDS = ["不是", "没有", "并非", "排除"]

def rule_classify_fault(text: str) -> list[tuple[str, float]]:
    results = []
    for fault_type, patterns in FAULT_RULES.items():
        hits = 0
        for p in patterns:
            for m in re.finditer(p, text):
                start = max(0, m.start() - 6)
                window = text[start:m.start()]
                if any(neg in window for neg in NEGATION_WORDS):
                    continue
                hits += 1
        if hits > 0:
            conf = min(0.55 + 0.12 * hits, 0.85)
            results.append((fault_type, conf))
    results.sort(key=lambda x: x[1], reverse=True)
    return results

规则层还有一个实际作用:强信号快速建单。客户如果直接报出报警代码,比如“显示E03”,规则命中后可以直接触发预建单,同时继续对话收集其他信息。这样即使后续对话中断,工单也已经落下来了,内勤能看到一条待补全的记录。

4.2 模型补的是“说法不一样”的场景

规则覆盖不了的是表达变体。比如同样是不下料,客户可能说“料斗里面下不去”“螺杆不转”“送料电机没劲”,这些词规则没覆盖,但语义上属于同一类故障。我们用一个轻量的中文文本分类模型做补充,输入是ASR转写的前若干轮对话文本,输出故障类型概率。

训练数据来自历史工单的“故障描述”字段和对应的故障分类标签。样本量不大,几千条,做了类别平衡。模型没有追求高参数量,用的是一个小尺寸预训练模型微调,推理延迟控制在百毫秒级,不影响对话体验。阈值定在0.7,低于阈值的交给规则结果或者转人工。

4.3 融合决策

规则和模型的融合策略很简单:规则强命中(置信度≥0.85)直接用规则;规则和模型一致时取较高置信度;模型置信度≥0.75且规则没命中时用模型;其余情况标记“待确认”,工单照建但故障类型留空,由内勤补。

python

def classify_fault(text: str) -> dict:
    rule_results = rule_classify_fault(text)
    model_label, model_conf = model_predict_fault(text)

    if rule_results and rule_results[0][1] >= 0.85:
        return {"fault_type": rule_results[0][0], "confidence": rule_results[0][1], "source": "rule"}
    if rule_results and model_label == rule_results[0][0]:
        return {"fault_type": model_label, "confidence": max(rule_results[0][1], model_conf), "source": "fusion"}
    if model_conf >= 0.75:
        return {"fault_type": model_label, "confidence": model_conf, "source": "model"}
    if rule_results:
        return {"fault_type": rule_results[0][0], "confidence": rule_results[0][1], "source": "rule_low"}
    return {"fault_type": "unknown", "confidence": 0.0, "source": "fallback"}

五、核心技术二:客户信息采集,多轮对话怎么设计才不招人烦

故障类型是工单的核心字段,但光有故障类型不够,还得知道是哪台设备、谁在用、在哪儿、怎么联系。这些信息我们按槽位来收。

5.1 槽位定义

槽位是否必填收集方式兜底策略
设备型号是按键选择或口述数字来电号码关联历史工单
故障现象是自由说,ASR转写转人工补录
联系人是来电号码默认,可修改来电号码
联系电话是来电号码默认来电号码
所在城市是口述或按键选省份历史工单地址
紧急程度否按键选择(急/不急)默认一般

设备型号是最容易卡住的槽位。小型设备厂客户往往记不住型号,只说“就是那台封口机”。我们的做法是双通道:一是让客户按键选择大类(封口机、包装机、杀菌锅等),二是来电号码关联历史工单,如果这个号码之前报修过,直接带出设备型号让客户确认。两个通道都没有的,转人工。

故障现象槽位不设固定话术,让客户自由说,ASR实时转写。对话管理会判断客户说了几轮、有没有提到关键故障词,如果客户说了两轮还没说到点上,机器人会追问一句“是设备不启动、还是有异响、还是封口不牢?”给客户几个选项,降低表达门槛。

5.2 对话流程示例

text

机器人:您好,这里是XX设备售后报修,请问设备出了什么问题?
客户:那个封口机封不牢,袋子一撕就开。
机器人:好的,封口不牢。请问您是哪款设备?可以按1封口机,按2包装机,按3其他。
客户:1。
机器人:封口机。请问您在哪个城市?
客户:山东临沂。
机器人:好的。您的来电号码是138xxxx1234,联系人怎么称呼?
客户:我姓王。
机器人:王先生您好,故障已记录:封口机封口不牢,山东临沂,联系电话138xxxx1234。马上为您安排售后工程师,请稍候。

整个对话控制在五轮以内,大部分客户四十秒左右能完成。客户如果中途说“我着急,直接找人工”,对话管理直接切人工,不纠缠。

5.3 槽位提取的代码思路

槽位提取用的是规则+词典。城市用行政区划词典匹配,联系人用“我姓X”“我是X”这类模式,设备型号按键直接取值。故障现象不提取结构化字段,整段转写文本作为工单描述。

python

def extract_slots(dialog_history: list[dict]) -> dict:
    slots = {}
    full_text = " ".join([t["text"] for t in dialog_history if t["role"] == "customer"])

    # 城市匹配
    for city in CITY_DICT:
        if city in full_text:
            slots["city"] = city
            break

    # 联系人
    m = re.search(r"(我姓|我是|免贵姓)([\u4e00-\u9fa5])", full_text)
    if m:
        slots["contact_name"] = m.group(2) + "先生/女士"

    # 设备型号从按键结果取
    for t in dialog_history:
        if t.get("slot") == "device_model":
            slots["device_model"] = t["value"]

    return slots

六、核心技术三:工单自动创建与分派

6.1 工单字段映射

故障类型和槽位收齐后,工单创建服务按模板建单。不同故障类型的工单字段略有差异,比如电气类需要带报警代码,机械类需要带设备运行时长,工艺类需要带物料类型。我们做了一张映射表,放在配置里,售后主管可以自己改。

故障类型工单模板必填字段默认优先级建议技能标签
electrical_start电气维修单设备型号、城市、联系人、故障描述高电气
electrical_alarm电气维修单设备型号、报警代码、城市、联系人高电气
mechanical_noise机械维修单设备型号、城市、联系人、故障描述中机械
mechanical_jam机械维修单设备型号、城市、联系人、故障描述高机械
seal_issue工艺维修单设备型号、物料类型、城市、联系人中工艺
temp_issue工艺维修单设备型号、温度设定、城市、联系人中工艺
unknown通用报修单设备型号、城市、联系人、故障描述中待定

工单创建接口带上 source=voice_robot、call_id、fault_type、confidence、auto_created=true 等字段,方便后续统计自助报修完成率和分类准确率。

python

import requests

def create_service_ticket(call_info: dict, slots: dict, fault: dict) -> dict:
    payload = {
        "title": f"{slots.get('device_model', '设备')}-{fault['fault_type']}-{slots.get('city', '')}",
        "type": FAULT_TO_TICKET_TYPE.get(fault["fault_type"], "通用报修单"),
        "priority": FAULT_TO_PRIORITY.get(fault["fault_type"], "中"),
        "description": call_info["transcript_summary"],
        "device_model": slots.get("device_model"),
        "city": slots.get("city"),
        "contact_name": slots.get("contact_name"),
        "contact_phone": call_info["caller"],
        "call_id": call_info["call_id"],
        "fault_type": fault["fault_type"],
        "intent_confidence": fault["confidence"],
        "auto_created": True,
        "source": "voice_robot",
    }
    resp = requests.post(TICKET_API, json=payload, timeout=3)
    resp.raise_for_status()
    return resp.json()

超时设短一点,3秒足够。工单创建失败不能阻塞通话,失败就记一条待补偿任务,走异步重试。工单接口一定要做幂等,同一个 call_id 重复推送不能建出两张单,这个在呼叫事件重推的时候很容易出问题。

6.2 分派引擎:按技能、区域、负载三要素选人

建单之后是分派。小型设备厂售后工程师人数不多,十来个人,但每个人擅长的领域不一样:有人偏电气,有人偏机械,有人偏工艺调试。分派规则按优先级依次判断:

  1. 技能匹配:故障类型对应的技能标签,优先派给有该标签的工程师。

  2. 区域匹配:工程师负责的区域覆盖客户所在城市,跨区域派单成本高。

  3. 负载均衡:同等条件下选当前未关闭工单数少的工程师。

  4. 兜底:以上都不满足,派给售后主管人工分派。

这套规则用配置表维护,售后主管可以调整工程师的技能标签和负责区域,不需要改代码。

python

def assign_engineer(ticket: dict, engineers: list[dict]) -> str | None:
    candidates = [e for e in engineers if ticket["fault_type"] in e["skills"]]
    if not candidates:
        return None  # 兜底给主管

    regional = [e for e in candidates if ticket["city"] in e["regions"]]
    pool = regional if regional else candidates
    pool.sort(key=lambda e: e["open_ticket_count"])
    return pool[0]["engineer_id"]

分派完成后,工单系统给工程师发企业微信或短信通知,带工单链接和客户电话。工程师接单后状态回写,客户那边如果留了手机号,系统会发一条确认短信,告诉客户工单号和预计联系时间。

七、异常兜底:语音机器人不能硬扛所有情况

语音机器人最怕的是“客户说半天,机器人没听懂还不让转人工”。我们定了五个兜底原则:

原则一:两轮没听清就转人工。 同一个槽位连续追问两次还没拿到有效信息,直接切人工内勤,不耗客户时间。

原则二:客户说“转人工”立即转。 识别到“人工”“找个人”“客服”等词,不解释不挽留,直接切。

原则三:投诉类不进机器人。 IVR第一层按键如果客户选“投诉”,直接转人工,不走报修流程。投诉和报修混在一起处理容易激化情绪。

原则四:挂机后补全。 客户中途挂断的,已经收集到的槽位和转写文本照常建单,标记“待补全”,内勤看到后回拨补信息。不能因为客户挂了就丢掉这条报修。

原则五:故障类型低置信度不硬派。 置信度低于0.6的工单,故障类型留空,分派给售后主管人工判断,避免派错工程师白跑一趟。

八、上线效果与踩坑复盘

上线三个多月,数据如下:

指标上线前上线三个月后
语音机器人自助完成报修比例—约68%
工单自动创建覆盖率0约85%
故障类型分类准确率(人工抽检300通)—82%
售后内勤单通登记时间平均3到5分钟约1分钟(仅处理转人工部分)
夜间和周末报修响应次日处理机器人接单,次日优先派

低置信度转人工判断的比例从上线初期的四成左右降到一成五左右,说明模型在持续纠错回流中逐步收敛。

踩过的坑挑几个说:

坑一:客户口述设备型号根本不可靠。 一开始我们让客户直接说型号,结果十个人里有八个说不清,有人说“就是那个小封口机”,有人说“去年买的那个”。后来改成按键选大类+来电号码关联历史工单,槽位收集完成率从五成多提到八成五。

坑二:ASR把报警代码识别错。 客户说“E零三”,ASR转成“一零三”,规则匹配不上。后来在热词表里加了报警代码的读法变体,同时让客户按键输入数字,准确率才上来。热词表里还要放设备型号、常见故障词、配件名称,每周更新一次。

坑三:一个电话说多个故障。 客户说“封口不牢,而且有时候还跳闸”。单标签分类只能取一个,我们改成主故障+附注故障,主故障走分派,附注故障写进工单描述让工程师现场判断。

坑四:方言和背景噪音。 小型设备厂的客户很多在车间里打电话,机器噪音大,加上各地方言,ASR转写质量下滑。我们的做法是对话设计上尽量用短问句和选项式提问,减少长段自由说的依赖;同时IVR里保留按键通道,客户说不清可以按。

坑五:工程师不习惯看工单。 系统上线后工程师还是习惯等内勤打电话,工单挂在系统里没人接。后来我们把企业微信通知做成卡片式,点开就是工单详情和一键接单,同时在周会上盯了两周接单时效,习惯才慢慢扭过来。技术系统再好,流程不跟上也白搭。

九、常见问题FAQ

Q1:小型设备厂一定要上语音机器人吗?先人工接行不行?
行,但要看量。如果日均报修电话在二三十通以内,人工内勤加简易工单系统能撑住。超过这个量,高峰期占线和登记遗漏的问题会明显。建议先把工单系统和分派规则跑通,语音机器人可以后上,避免一上来摊子铺太大。

Q2:故障类型识别,规则和模型哪个先上?
先上规则。小型设备厂高频故障集中,规则两周能覆盖七成以上场景,可解释、好调整。模型用来补表达变体,等规则跑稳、积累了一批纠错数据再训练,效果更实在。

Q3:客户说不清设备型号怎么办?
双通道兜底:一是按键选设备大类,二是来电号码关联历史工单带出型号让客户确认。两个都没有的转人工。别指望客户能准确口述型号,这是实际踩过的坑。

Q4:ASR转写不准怎么优化?
先做热词表,把设备型号、报警代码读法、故障词、配件名挂进去;再考虑电话场景专用模型;车间噪音大的场景,对话设计上多用短问句和按键选项,减少对长段自由说的依赖。

Q5:工单分派错了怎么办?
分派规则里保留“售后主管人工改派”入口,工程师端提供“转派”按钮并记录原因。改派数据定期复盘,该调技能标签调标签,该调区域调区域。

Q6:怎么衡量这套系统有没有效果?
看四个指标:语音机器人自助完成报修比例、工单自动创建覆盖率、人工抽检故障分类准确率、内勤单通登记耗时。再补一个工程师首次响应时长,基本能说明问题。

十、小结

整套方案的核心就四件事:把语音变成文本,把文本变成故障标签,把标签和槽位变成工单,把工单分给对的人。技术上没有特别高深的东西,难在细节打磨——热词表、设备型号兜底、否定词处理、置信度阈值、转人工时机,每一项都需要结合业务反复调。

如果团队刚开始做,建议先上规则层和工单创建链路,把强信号场景跑通,快速拿到收益,再逐步引入模型和多轮对话优化。别一上来就追求端到端的智能对话,工单系统对接、字段映射、分派规则、异常兜底这些“脏活”才是决定项目能不能落地的关键。

代码示例里的函数都是简化版,实际工程里还要考虑并发、幂等、日志埋点、灰度开关这些。工单创建接口一定要做幂等,这个在呼叫事件重推的时候很容易出问题。

最后提一句数据回流。故障分类不是一锤子买卖,内勤的每一次改类型、工程师的每一次转派、客户的每一次挂断,都是模型和规则迭代的燃料。把这条链路打通,系统才会越用越准。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐