人、机客户服务质量 - 实时透视分析

标签

PostgreSQL , 多输入流 , 人工客服 , 机器客服 , 服务质量 , 多维透视 , 实时透视 , 窗口查询 , 流式计算 , 流式SQL , 阅后即焚 , 上下文相关 , 流式窗口 , 到达时间错乱 , 窗口保持 , 可重算 , 幂等


背景

通常一个服务型的产品,面向很多用户时,都会提供多种服务渠道:

电话、WEB、人工客服。机器人客服。

如何从各个维度(问题分类、地区分类。。。。)了解问题的 : 解决率、解决时长柱状图、一次解决率(例如电话机器人、转人工、转机器人等,如果多次流转说明一次解决率太低)。

这个透视分析,可以作为检验客户服务质量,提升用户体验的一个重要参考。

以电商为例,一次问题,可能涉及:

1、多股数据流表:

电话、WEB、人工客服。机器人客服。

2、数据流的内容涉及:

卖家ID

买家ID

商品ID

小二ID

时间。

3、多个元数据表可能包括:

买家、买家、商品、问题、小二 属性、标签。

4、透视维度包括:

时间。

地区。

商品类别。

问题类别。

等。

5、透视指标可能包括:

解决率、解决时长柱状图、一次解决率(例如电话机器人、转人工、转机器人等,如果多次流转说明一次解决率太低)。

如何做到实时透视?可以使用PostgreSQL的流式处理(实时、或异步阅后即焚)功能,以及insert on conflict幂等操作的功能。

多股数据流的到达时间问题

通常在一个大型的企业中,业务会拆得很细,甚至对于客服这样的系统,人工处理和电话处理都属于两个业务线。

而数据流转往往通过MQ平台,也就是说不同业务线的数据流可能存在到达的时间差,或者消费的时间差。

为了透视客户服务质量相关数据,需要用到多个数据流,势必面临到达时间差的问题。

例如,对于某个CASE:

人工服务是9点发生的。

机器服务是9点01分发生的。

但是他们属于多个数据流,最后的到达时间反过来了。那么在统计一解率时,可能出现误差。

为了解决这个误差,需要做窗口保持的重复计算操作,做到实时统计结果可调整,可增量重算。(这个可以使用insert on conflict do update xx=exclude.xx where xx<>excluded.xx来实现)

demo

以一解率维度为例,其他维度可以参考模仿。

数据流如下:

会话流(多表) -> 流合并表 -> 转换表 -> (转换表+元数据表) 透视结果表

1、会话数据流

流1,机器人

create table tbl_robot (
  caseid int8,             -- 会话ID
  crt_time timestamp,      -- 消息时间
  message text,            -- 交互信息
  custom_id int8,          -- 消费者ID
  pro_id int,              -- 问题ID
  others text              -- 其他字段
);

流2,人工

create table tbl_human (
  caseid int8,             -- 会话ID
  crt_time timestamp,      -- 消息时间
  message text,            -- 交互信息
  custom_id int8,          -- 消费者ID
  xiao2_id  int8,          -- 小二ID
  pro_id int,              -- 问题ID
  others text              -- 其他字段
);

2、合并数据流表(这里可以使用分区表,便于维护)

create table tbl_session (
  caseid int8,             -- 会话ID
  crt_time timestamp,      -- 消息时间
  message text,            -- 交互信息
  custom_id int8,          -- 消费者ID
  xiao2_id  int8,          -- 小二ID
  pro_id int,              -- 问题ID
  stream_id int,           -- 流ID, 1 表示robot, 2 表示human
  others1 text,            -- 流1,其他字段
  others2 text             -- 流2,其他字段
);  

create index idx_tbl_session_1 on tbl_session (crt_time);
create index idx_tbl_session_2 on tbl_session (caseid, crt_time);

3、创建源头规则,自动将会话数据流,合并到合并数据流表单表。

通过规则,自动将数据合并到合并表。

create or replace rule r1 as on insert to tbl_robot do instead
  insert into tbl_session
    (caseid, crt_time, message, custom_id, pro_id, others1, stream_id)
    values (NEW.caseid, NEW.crt_time, NEW.message, NEW.custom_id, NEW.pro_id, NEW.others, 1);  

create or replace rule r1 as on insert to tbl_human do instead
  insert into tbl_session
    (caseid, crt_time, message, custom_id, pro_id, others2, xiao2_id, stream_id)
    values (NEW.caseid, NEW.crt_time, NEW.message, NEW.custom_id, NEW.pro_id, NEW.others, NEW.xiao2_id, 2);

4、元数据表

略。

5、会话状态转换表(批量可重算转换表)

打个会话会涉及多条记录,这个DEMO的目的是找到人工回复后,用户从机器人找答案,从而识别人工回复的效率。

也可以反之,求机器人回复的效率。

create table tbl_session_etl (
  caseid int8 primary key,   -- 会话ID
  s_crt_time timestamp,      -- 会话最开始的时间
  e_crt_time timestamp,      -- 会话最后一条记录的时间
  robot_to_human boolean,    -- 是否包含 从机器人切到人工
  human_to_robot boolean     -- 是否包含 从人工切到机器人
);

会话转换SQL,调度,可以重复执行。

窗口大小可调整,容忍不同数据流的到达时间差异。

select caseid, max(s_crt_time) s_crt_time, max(e_crt_time) e_crt_time,
       bool_or(lag=1 and stream_id=2) as robot_to_human,
       bool_or(lag=2 and stream_id=1) as human_to_robot
from
(
select caseid, min(crt_time) over w1 as s_crt_time, max(crt_time) over w1 as e_crt_time,
       (case when (row_number() over w1) = 1 then stream_id else lag(stream_id) over w1 end) as lag,
       stream_id
from tbl_session
where crt_time > now() - interval '10 min'           -- 10分钟内的会话数据, 可以自由调整这个窗口
window w1 as (partition by caseid order by crt_time)
) t
group by caseid;

合并写入,使用如下SQL,可以重复执行。

窗口大小可调整,容忍不同数据流的到达时间差异。

insert into tbl_session_etl (caseid, s_crt_time, e_crt_time, robot_to_human, human_to_robot)
select caseid, max(s_crt_time) s_crt_time, max(e_crt_time) e_crt_time,
       bool_or(lag=1 and stream_id=2) as robot_to_human,
       bool_or(lag=2 and stream_id=1) as human_to_robot
from
(
select caseid, min(crt_time) over w1 as s_crt_time, max(crt_time) over w1 as e_crt_time,
       (case when (row_number() over w1) = 1 then stream_id else lag(stream_id) over w1 end) as lag,
       stream_id
from tbl_session
where crt_time > now() - interval '10 min'             -- 10分钟内的会话数据, 可以自由调整这个窗口
window w1 as (partition by caseid order by crt_time)   -- 开窗查询
) t
group by caseid
on conflict (caseid)
do update set
  s_crt_time = excluded.s_crt_time,
  e_crt_time = excluded.e_crt_time,
  robot_to_human = excluded.robot_to_human,
  human_to_robot = excluded.human_to_robot
where       -- 当数据转换后的值,发送变化时,合并写入。
  tbl_session_etl.s_crt_time<>excluded.s_crt_time
or
  tbl_session_etl.e_crt_time<>excluded.e_crt_time
or
  tbl_session_etl.robot_to_human<>excluded.robot_to_human
or
  tbl_session_etl.human_to_robot<>excluded.human_to_robot
;

创建函数便于调用

create or replace function f_tbl_session_etl(interval) returns void as $$
insert into tbl_session_etl (caseid, s_crt_time, e_crt_time, robot_to_human, human_to_robot)
select caseid, max(s_crt_time) s_crt_time, max(e_crt_time) e_crt_time,
       bool_or(lag=1 and stream_id=2) as robot_to_human,
       bool_or(lag=2 and stream_id=1) as human_to_robot
from
(
select caseid, min(crt_time) over w1 as s_crt_time, max(crt_time) over w1 as e_crt_time,
       (case when (row_number() over w1) = 1 then stream_id else lag(stream_id) over w1 end) as lag,
       stream_id
from tbl_session
where crt_time > now() - $1             -- n分钟内的会话数据, 可以自由调整这个窗口
window w1 as (partition by caseid order by crt_time)   -- 开窗查询
) t
group by caseid
on conflict (caseid)
do update set
  s_crt_time = excluded.s_crt_time,
  e_crt_time = excluded.e_crt_time,
  robot_to_human = excluded.robot_to_human,
  human_to_robot = excluded.human_to_robot
where       -- 当数据转换后的值,发送变化时,合并写入。
  tbl_session_etl.s_crt_time<>excluded.s_crt_time
or
  tbl_session_etl.e_crt_time<>excluded.e_crt_time
or
  tbl_session_etl.robot_to_human<>excluded.robot_to_human
or
  tbl_session_etl.human_to_robot<>excluded.human_to_robot
;
$$ language sql strict;

调度方法如下,完成自动修正:

每10秒,统计10分钟内的数据。(不同流的到达时间差异,容忍度为10分钟。)

每小时,统计全天内的数据。(不同流的到达时间差异,容忍度为全天。)

6、会话统计表,统计一解率 (可选,如果不统计的话,就直接查询)

天维度表

create table tbl_session_stat_day (
  stat_dim text primary key,
  robot_to_human_cnt int8,
  human_to_robot_cnt int8
);

分钟维度表

create table tbl_session_stat_min (
  stat_dim text primary key,
  robot_to_human_cnt int8,
  human_to_robot_cnt int8
);

统计调度SQL,可以重复执行。

select to_char(s_crt_time, 'yyyymmdd') as stat_dim,
       sum(case when robot_to_human then 1 else 0 end) robot_to_human_cnt,
       sum(case when human_to_robot then 1 else 0 end) human_to_robot_cnt
from tbl_session_etl
group by 1;

分钟维度

select to_char(s_crt_time, 'yyyymmddhh24mi') as stat_dim,
       sum(case when robot_to_human then 1 else 0 end) robot_to_human_cnt,
       sum(case when human_to_robot then 1 else 0 end) human_to_robot_cnt
from tbl_session_etl
group by 1;

写入并合并,可以重复执行。

insert into tbl_session_stat_day
select to_char(s_crt_time, 'yyyymmdd') as stat_dim,
       sum(case when robot_to_human then 1 else 0 end) robot_to_human_cnt,
       sum(case when human_to_robot then 1 else 0 end) human_to_robot_cnt
from tbl_session_etl
group by 1
on conflict (stat_dim) do update
set
  robot_to_human_cnt = excluded.robot_to_human_cnt,
  human_to_robot_cnt = excluded.human_to_robot_cnt
where
  tbl_session_stat_day.robot_to_human_cnt <> excluded.robot_to_human_cnt
or
  tbl_session_stat_day.human_to_robot_cnt <> excluded.human_to_robot_cnt
;  

insert into tbl_session_stat_min
select to_char(s_crt_time, 'yyyymmddhh24mi') as stat_dim,
       sum(case when robot_to_human then 1 else 0 end) robot_to_human_cnt,
       sum(case when human_to_robot then 1 else 0 end) human_to_robot_cnt
from tbl_session_etl
group by 1
on conflict (stat_dim) do update
set
  robot_to_human_cnt = excluded.robot_to_human_cnt,
  human_to_robot_cnt = excluded.human_to_robot_cnt
where
  tbl_session_stat_min.robot_to_human_cnt <> excluded.robot_to_human_cnt
or
  tbl_session_stat_min.human_to_robot_cnt <> excluded.human_to_robot_cnt
;

创建函数便于调用

create or replace function f_tbl_session_stat_day() returns void as $$
insert into tbl_session_stat_day
select to_char(s_crt_time, 'yyyymmdd') as stat_dim,
       sum(case when robot_to_human then 1 else 0 end) robot_to_human_cnt,
       sum(case when human_to_robot then 1 else 0 end) human_to_robot_cnt
from tbl_session_etl
group by 1
on conflict (stat_dim) do update
set
  robot_to_human_cnt = excluded.robot_to_human_cnt,
  human_to_robot_cnt = excluded.human_to_robot_cnt
where
  tbl_session_stat_day.robot_to_human_cnt <> excluded.robot_to_human_cnt
or
  tbl_session_stat_day.human_to_robot_cnt <> excluded.human_to_robot_cnt
;
$$ language sql strict;  

create or replace function f_tbl_session_stat_min() returns void as $$
insert into tbl_session_stat_min
select to_char(s_crt_time, 'yyyymmddhh24mi') as stat_dim,
       sum(case when robot_to_human then 1 else 0 end) robot_to_human_cnt,
       sum(case when human_to_robot then 1 else 0 end) human_to_robot_cnt
from tbl_session_etl
group by 1
on conflict (stat_dim) do update
set
  robot_to_human_cnt = excluded.robot_to_human_cnt,
  human_to_robot_cnt = excluded.human_to_robot_cnt
where
  tbl_session_stat_min.robot_to_human_cnt <> excluded.robot_to_human_cnt
or
  tbl_session_stat_min.human_to_robot_cnt <> excluded.human_to_robot_cnt
;
$$ language sql strict;

性能压测

1、高并发写入会话信息

vi test.sql  

\set caseid1 random(1,1000000)
\set caseid2 random(1,1000000)
\set custom_id1 random(1,100000)
\set pro_id1 random(1,1000)
\set custom_id2 random(1,100000)
\set pro_id2 random(1,1000)
\set xiao2_id random(1,100)
insert into tbl_robot values (:caseid1, now(), 'test', :custom_id1, :pro_id1, 'test');
insert into tbl_human values (:caseid2, now(), 'test', :custom_id2, :xiao2_id, :pro_id2, 'test');
\sleep 500 us
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 32 -j 32 -T 120

单条写入,约17.6万行/s.

如果批量写入,可以做到100万+ 行/s

transaction type: ./test.sql
scaling factor: 1
query mode: prepared
number of clients: 32
number of threads: 32
duration: 120 s
number of transactions actually processed: 10655120
latency average = 0.360 ms
latency stddev = 0.466 ms
tps = 88792.101825 (including connections establishing)
tps = 88804.892722 (excluding connections establishing)
script statistics:
 - statement latencies in milliseconds:
         0.001  \set caseid1 random(1,1000000)
         0.001  \set caseid2 random(1,1000000)
         0.000  \set custom_id1 random(1,100000)
         0.000  \set pro_id1 random(1,1000)
         0.000  \set custom_id2 random(1,100000)
         0.000  \set pro_id2 random(1,1000)
         0.000  \set xiao2_id random(1,100)
         0.178  insert into tbl_robot values (:caseid1, now(), 'test', :custom_id1, :pro_id1, 'test');
         0.178  insert into tbl_human values (:caseid2, now(), 'test', :custom_id2, :xiao2_id, :pro_id2, 'test');

2、实时转换调度

同时开启写入,(写入速度14.2万行/s。)

psql  

select f_tbl_session_etl(interval '5 sec');  

\watch 1  

Sat 09 Dec 2017 07:05:42 PM CST (every 1s)  

 f_tbl_session_etl
-------------------  

(1 row)  

Time: 4515.817 ms (00:04.516)

处理最近71万行, 耗时4.5秒。处理速度约15.7万行/s。

3、实时统计调度

postgres=# select f_tbl_session_stat_day();
 f_tbl_session_stat_day
------------------------  

(1 row)  

Time: 926.839 ms
postgres=# select f_tbl_session_stat_min();
 f_tbl_session_stat_min
------------------------  

(1 row)  

Time: 1162.713 ms (00:01.163)

4、数据量

1.79亿。

postgres=# select count(*) from tbl_session;
   count
-----------
 179639156
(1 row)  

Time: 1635.908 ms (00:01.636)  

postgres=# select count(*) from tbl_session_etl;
  count
---------
 1000000
(1 row)  

Time: 47.540 ms

5、性能指标:

并发度 写入吞吐 写入延迟
32 17.6万行/s 0.178毫秒

1.79亿数据打散到全天写入的话,响应速度会更快。

并发度 转换吞吐 转换延迟
1 15.7万行/s 1秒
并发度 统计吞吐 统计延迟
1 1000000行 1秒

统计信息查询性能,毫秒级延迟

postgres=# select * from tbl_session_stat_day ;
 stat_dim | robot_to_human_cnt | human_to_robot_cnt
----------+--------------------+--------------------
 20171209 |              80160 |              80453
(1 row)  

Time: 6.476 ms  

postgres=# select * from tbl_session_stat_min;
   stat_dim   | robot_to_human_cnt | human_to_robot_cnt
--------------+--------------------+--------------------
 201712091758 |              56558 |              56531
 201712091800 |                  4 |                  4
 201712091759 |                509 |                501
 201712091757 |             236638 |             236657
 201712091802 |               7273 |               7177
 201712091817 |               8336 |               8358
 201712091812 |                  0 |                  0
 201712091814 |                 12 |                  8
 201712091815 |                127 |                144
 201712091813 |                  1 |                  1
 201712091816 |               1688 |               1761
 201712091905 |              56645 |              57046
 201712091904 |                411 |                391
 201712091906 |              23104 |              23015
 201712091902 |                  0 |                  1
(15 rows)  

Time: 6.695 ms

参考

《PostgreSQL 流式统计 - insert on conflict 实现 流式 UV(distinct), min, max, avg, sum, count ...》

《HTAP数据库 PostgreSQL 场景与性能测试之 27 - (OLTP) 物联网 - FEED日志, 流式处理 与 阅后即焚 (CTE)》

《经营、销售分析系统DB设计之PostgreSQL, Greenplum - 共享充电宝 案例实践》

时间: 2024-09-20 15:05:53

人、机客户服务质量 - 实时透视分析的相关文章

画像圈人 + 目标人群FEED行为透视

标签 PostgreSQL , Greenplum , HybridDB for PostgreSQL 背景 本文讲的是这样的场景:画像圈人和人群行为透视的结合. 数据量 标签数 5W+ 用户数 10W+ 用户行为数据 10亿+ 业务目标:根据标签圈出用户群体,对这些用户的行为进行透视分析. 第一步,圈人 第一步是圈出用户群体,如果这一步的数据量非常庞大,则可以参考如下文章: 1.<多字段,任意组合(0建模) - 毫秒级实时圈人> 2.<万亿级营销(圈人)迈入毫秒时代 - 实时推荐系统数

如何利用VR和AR提升客户服务质量 这四个例子或许可以帮到你

许多企业对客户服务都很重视,但大部分人追求的是获得更多的好评和尽可能少的差评.由于互联网时刻都在我们的指边,这使得糟糕的客户体验无所遁形,因为我们可以把自己的感受随时发布到网上,让所有人都看到. 在布鲁斯·威利斯主演的一部名为<机器代理人>(国内译名<未来战警>)的电影中,人们利用虚拟现实技术使得自己无需亲自出门,只需要将大脑接入网络,就可以用思维来控制的"机器代理人"来替自己做一切想做的事情.这样的技术是否能够帮助或者挽救正在不断下降的客户服务质量呢? 呃,是

《社会智能与综合集成系统》—第1章1.4节人—机结合智能的科学

1.4 人-机结合智能的科学 社会智能与综合集成系统 关于智能的研究,可以说有机器智能和人的智能两个方面.一个方面是研究应用计算机来实现智能的行为.社会的需要和科学技术的发展,呼唤着一个新学科的产生.19世纪以来数理逻辑.自动机理论.<控制论>.<信息论>.仿生学.心理学.计算机等科学技术的发展,为一个新学科的产生准备了思想.理论和物质基础.在这个背景下开始具有真正意义的机器智能的研究.在中国,大陆习惯应用"智能",而在台湾则习惯应用"智慧"

怎么提高客户服务质量

最近一段时间,我都在考虑怎么提高客户服务的质量.每次我看到其他客户服务质量优秀的企业时,我就会借鉴他们的经验并思考. 我最近研究了一家位于旧金山,名为One Medical的企业的客户服务,他们对人们预约医生的方式已经有所改变了.我对他们的客户服务进行了仔细的研究,总结出以下4点. 1. 从一开始就为客户提供简单的使用体验 如果你访问过One Medical的网站,你就会发现他们的网站设计的十分友好.你可以看到附近所有医生的时间表,并且轻松的进行预约,整个过程十分简单. 经验: 让你不认识的人访

ELK(ElasticSearch, Logstash, Kibana)搭建实时日志分析平台

ELK平台介绍 在搜索ELK资料的时候,发现这篇文章比较好,于是摘抄一小段: 以下内容来自:http://baidu.blog.51cto.com/71938/1676798 日志主要包括系统日志.应用程序日志和安全日志.系统运维和开发人员可以通过日志了解服务器软硬件信息.检查配置过程中的错误及错误发生的原因.经常分析日志可以了解服务器的负荷,性能安全性,从而及时采取措施纠正错误. 通常,日志被分散的储存不同的设备上.如果你管理数十上百台服务器,你还在使用依次登录每台机器的传统方法查阅日志.这样

我想将职员的打分表中的数据进行处理,具体就是将每个人对别人的打分进行分析,如果不合格,就将本人的得分减去5,然后把这个人的所得平均分写回打分汇总表怎样实现!

问题描述 我想将职员的打分表中的数据进行处理,具体就是将每个人对别人的打分进行分析,如果不合格,就将本人的得分减去5,然后把这个人的所得平均分写回打分汇总表怎样实现! 解决方案 解决方案二:usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web;usingSystem.Web.UI;usingSystem.Web.UI.WebControls;usingSystem.Web.Configuratio

实时股票分析系统的架构与算法

[编者的话]如果能在一台服务器上应用人工智能和机器学习算法处理每天的股票交易,而自己则在夏威夷的海滩上享受生活,那将是多么惬意呀.虽然股票 价格的变化受多种因素的影响,世上也没有免费的午餐,但是有些公司依然能够借助于开源的机器学习算法和数据分析平台得到"更好.更健康.更便宜的午餐". 本文搜集并整理了一些如何实现实时股票分析系统的资料,从架构和算法两个层面给出了一种可行的方案. 虽然股票交易市场一直在持续地变化,经济力量.新产品.竞争.全球性的事件.法规.甚至是Tweet都 有可能引起

联邦快递为客户的实时托运带来前所未有的方便体验

FedExCorp.旗下附属公司兼全球最具规模速递运输公司之一联邦快递日前宣布,优化其移动应用系统解决方案,推出一系列全新工具和拓展功能,为客户的实时托运带来前所未有的方便体验. 为持续满足全球托运用户的需要,"联邦快递移动网站"的覆盖范围已经扩展到206个国家和地区,包括30个亚太区市场,并支持25种语言.此外,联邦快递移动应用程序现已提供适合iPad及安卓用户使用的版本.上述的新增项目加强了联邦快递移动应用解决方案组合.目前,联邦快递的移动应用系统已适用于iPhone.iPod t

分布式实时日志分析解决方案ELK部署架构

一.概述 ELK已经成为目前最流行的集中式日志解决方案,它主要是由Beats.Logstash.Elasticsearch.Kibana等组件组成,来共同完成实时日志的收集,存储,展示等一站式的解决方案.本文将会介绍ELK常见的架构以及相关问题解决. Filebeat:Filebeat是一款轻量级,占用服务资源非常少的数据收集引擎,它是ELK家族的新成员,可以代替Logstash作为在应用服务器端的日志收集引擎,支持将收集到的数据输出到Kafka,Redis等队列. Logstash:数据收集引