HowTo文档

如何在 XSwitch 中实现满意度评价

在语音客服的呼入和呼出场景中,常常需要客户对当前服务进行满意度评价,来收集客户对本次服务的满意度,进而对整体服务过程进行调整与优化。

XSwitch支持客户在与客服沟通完成后,客服主动挂断后,进行满意度评价。满意度评价大致流程如下:

  1. 客服主动挂断通话,客户通话通道转入myd路由。
  2. XSwitch向客户播放欢迎音,说明各个满意度等级对应的DTMF按键。(如满意请按1,不满意请按2)。
  3. 客户根据自身体验与受服务程度给出此次的满意度等级,通过DTMF按键传给XSwitch。
  4. XSwitch将客户评价的满意度等级进行存储。(存储方式有:存入数据库、给某个地址发送HTTP消息、ESL Event等)。

满意度路由

XSwitch已为您预设一条专门用作满意度评价的路由:

  • 名称:myd
  • 呼叫源:分机
  • 被叫字冠:myd
  • 目的地类型:系统
  • 内容:lua xui/scripts/grade_ivr.lua

XSwitch默认满意度评级为:

  • 1:满意
  • 2:一般
  • 3:不满意

呼入进行满意度评价

假设您的呼入路由为:

  • 名称:call-in
  • 呼叫源:分机
  • 被叫字冠:1
  • 最大号长:4
  • 目的地类型:本地分机

那么您拨打所有1开头的4位纯数字号码均会进入该路由,并桥接至同号码分机上。

您可在本路由的【动作】内添加以下两个动作即可:

动作1:

  • 名称:set
  • 模块参数:hangup_after_bridge=false

动作2:

  • 名称:set
  • 模块参数:transfer_after_bridge=myd:XML:context-1

呼出进行满意度评价

通过路由直接外呼

假设您的呼入路由为:

  • 名称:call-out
  • 呼叫源:分机
  • 被叫字冠:0
  • 最大号长:12
  • 目的地类型:网关

那么您拨打所有0开头的12位纯数字号码均会进入该路由,并通过网关桥接至对应号码。

您可在本路由的【动作】内添加以下两个动作即可:

动作1:

  • 名称:export
  • 模块参数:hangup_after_bridge=false

动作2:

  • 名称:export
  • 模块参数:nolocal:transfer_after_bridge=myd:XML:context-1

通过XCC接口呼出

为避免出现无效满意度评价出现,推荐您使用如下方式实现满意度评价:

XNode.Dial发起外呼 --> 客户应答 --> 客服与客户交互 --> XNode.Transfer将客户转移至myd路由 --> 客服自动挂机 --> 客户满意度评价 --> 获取满意度评价结果

其中XNode.Transfer示例如下:

{
  "jsonrpc": "2.0",
  "method": "XNode.Transfer",
  "id": "f6f8a912-3757-469c-a14f-bd512e51ad5b",
  "params": {
    "ctrl_uuid": "41f54dd7-dedb-452b-956f-86c911f62dc6",
    "uuid": "f6f8a912-3757-469c-a14f-bd512e51ad5b",
    "extension": "myd",
    "dialplan": "XML",
    "context": "context-1"
  }
}

其中uuid必须为客户UUID,iduuidv4格式uuid。

通过REST API呼出

如何使用REST API呼出参见REST API呼出,假设您使用如下消息进行呼出,则表示XSwitch先呼叫分机号为1000的客服,同时进行录音,再重路由到18666666666号码上。

{
  "autoAnswer": "true",
  "destNumber": "1000",
  "apps": [
    { "app": "set", "args": "RECORD_ANSWER_REQ=true" },
    { "app": "record_session", "args": "/tmp/test.wav" },
    { "app": "transfer", "args": "18666666666" }
  ]
}

18666666666号码会匹配到您预先设置好的路由(假设路由名称为call-out)。

您需在call-out路由内添加以下两个动作:

动作1:

  • 名称:export
  • 模块参数:hangup_after_bridge=false

动作2:

  • 名称:export
  • 模块参数:nolocal:transfer_after_bridge=myd:XML:context-1

完成以上配置后,在1000客服与18666666666客户沟通完成后,1000客服主动挂机后,18666666666客户将路由至myd路由进行满意度评价。

获取满意度评价结果

ESL Event

推荐使用XCC接收事件,在【对接】--【XCC】--【xcc】选择XCC-BINDINGS界面点击【添加】按钮,内容如下:

  • 域:XCC-BINDINGS
  • 键:CUSTOM:100
  • 值:grade::grade

配置完成后点击【重载】按钮。

这样您就可以在cn.xswitch.ctrl.event节点内收到满意度评价消息。

HTTP消息

修改myd路由中的内容为lua xui/scripts/grade_ivr.lua url http://192.168.31.98:9000/grade来实现将满意度评价通过HTTP消息推送至您指定的接口内。

数据库

修改myd路由中的内容为lua xui/scripts/grade_ivr.lua database来实现将满意度评价存储到数据库内。

若您想查询数据库内的满意度评价结果,请联系我们。

修改满意度评级

若您觉得我们预设的满意度评级不够精准,您也可以设置您自己的满意度评级,不过需进行以下步骤才可实现。

修改评级音频

您需新建一个满意度评级的提示音,并命名为grade_welcome.wav。将XSwitch容器内的/usr/local/freeswitch/sounds/zh/cn/link/xswitch/grade_welcome.wav替换为最新的grade_welcome.wav文件。

以上方式在XSwitch容器重启将失效,需重新替换grade_welcome.wav文件。

修改lua脚本

lua脚本见附件,其中收集用户DTMF按键信息的核心功能为:

local grade = session:playAndGetDigits(
		1, 1, 3, 5000, "#", sound_path .. "grade_welcome.wav",
		sound_path .. "grade_invalid.wav", "[1-3]", 5000)

若您修改后有效满意度评级为1-5,那么请修改为如下形式:

local grade = session:playAndGetDigits(
		1, 1, 3, 5000, "#", sound_path .. "grade_welcome.wav",
		sound_path .. "grade_invalid.wav", "[1-5]", 5000)

修改有效值校验逻辑,原始代码如下:

if grade == "invalid" or grade == "1" or grade == "2" or grade == "3" then

同样,修改后代码如下:

if grade == "invalid" or grade == "1" or grade == "2" or grade == "3" or grade == "4" or grade == "5" then

修改完成后,将新的grade_ivr.lua上传至XSwitch容器内/usr/local/freeswitch/xui/lua/xui/xui/scripts目录下。即刻生效。

永久化处理

上述两个章节,无论修改评级音频还是修改grade_ivr.lua均为将修改后文件上传至XSwitch容器内。在XSwitch容器重启后,文件将恢复成初始状态,之前修改无效。

因此您可通过在docker-compose.yml文件内的volumes下新增挂载配置项,将grade_welcome.wavgrade_ivr.lua挂载到XSwitch容器内的对应位置。这样,新文件将不再受XSwitch重启的影响。

若您有任何疑问,请及时联系我们。

附件

-- for test
local cur_dir = debug.getinfo(1).source;
cur_dir = string.gsub(debug.getinfo(1).source, "^@(.+/)[^/]+$", "%1")
if cur_dir:sub(1,1) == '/' then -- plain Lua
package.path = package.path .. ";/etc/xtra/?.lua"
package.path = package.path .. ";" .. cur_dir .. "?.lua"
package.path = package.path .. ";" .. cur_dir .. "vendor/?.lua"
end
local utils = require 'utils'
local config = require 'xtra_config'
local xdb = require 'xdb'

local config = require 'xtra_config'
local utils = require 'utils'
local xdb = require 'xdb'
local api = freeswitch.API()

local db_handle = xdb.new()
db_handle:connect(config.dsn)

if config.db_auto_connect then db_handle:connect(config.dsn) end
local sound_path = "xswitch/"
local post_type = argv[1]
local post_url = argv[2]
local result = {}
result.grade_level = 0
local curl = freeswitch.cURL()
local json = freeswitch.JSON()
session:answer()
session:streamFile("silence_stream://2000")

local grade = session:playAndGetDigits(
		1, 1, 3, 5000, "#", sound_path .. "grade_welcome.wav",
		sound_path .. "grade_invalid.wav", "[1-3]", 5000)

if grade == '' then
	grade = 'invalid'
end

local uuid = session:getVariable("uuid")
local caller_id_number = session:getVariable("caller_id_number")
local original_destination_number = session:getVariable("original_destination_number")
local cc_agent = session:getVariable("cc_agent")
local queue_name = session:getVariable("cc_queue")

if grade == "invalid" or grade == "1" or grade == "2" or grade == "3" then
	session:setVariable("grade", grade)
	local uuid = session:getVariable("uuid")
	if post_type and post_type == "database" then
		result.grade_level = grade
		result.uuid = uuid
		result.caller_id_number = caller_id_number
		result.destination_number = original_destination_number
		result.agent = cc_agent
		result.queue = queue_name
		db_handle:create_return_id('grades', result)
	elseif post_type and post_type == "url" then
		if post_url then
			result.grade_level = grade
			result.uuid = uuid
			result.destination_number = original_destination_number
			result.caller_id_number = caller_id_number
			result.agent = cc_agent
			result.queue = queue_name
			local ret = curl:post({url = post_url,
				headers = {['X-Tested-By'] = 'XUI-GRADE'},
				data = utils.json_encode(result),
				dataType = 'application/json'
			})
		end
	else
		local e = freeswitch.Event("CUSTOM", "grade::grade")
		e:addHeader("subclass", "grade")
		e:addHeader("grade_level", grade)
		e:addHeader("timestamp", os.date("%Y-%m-%d %H:%M:%S"))
		e:addHeader("uuid", uuid)
		e:addHeader("caller_id_number", caller_id_number)
		e:addHeader("original_destination_number", original_destination_number)
		e:addHeader("cc_agent", cc_agent)
		e:addHeader("cc_queue", queue_name)
		e:fire()
	end
end

session:streamFile(sound_path .. "grade_bye.wav")
session:streamFile("silence_stream://1000")
session:hangup()
如何在XSwitch中下载日志