客服人力测算与排班诊断
把一张时段话务表,变成两样东西:一份「现在排得够不够」的诊断,和一条「每个整点该有几个人」的人力需求曲线。
本技能面向在线(livechat / IM)客服——坐席可同时接待多个会话。这与电话客服(一人同时只接一通)的算法口径不同,请勿用于纯电话呼叫中心。
这个技能做什么、不做什么
做:诊断历史人力盈亏、按目标接起率测算每小时所需坐席数、输出人力需求曲线和热力图。
不做:不生成具体班次表(不决定谁上早班、几点到几点)。原因是拼班次要考虑请假、技能组、个人偏好等企业内部约束,技能无从得知。技能只输出客观的「每个整点需要几人」,企业拿这条曲线去套自己的两班/三班制,既覆盖业务又不浪费人力。
工作流程
- 收集话务数据
- 清洗与字段识别,回显给用户确认
- 第一段——诊断现有人力盈亏
- 第二段——测算所需人力曲线
- 渲染成报告页面并交付
第一步:收集话务数据
需要用户提供按时段拆分的话务数据。让用户直接粘贴或上传从客服系统导出的表即可,不要求特定格式。
必备字段(名称允许变体,见下一步的识别规则):
- 时段:小时或半小时粒度,如
09:00、9-10 点、09:00-10:00 - 进线量:该时段呼入/咨询总量
- 接起量:实际接通/受理的量
- 平均会话时长:单通平均耗时
有则更准的字段:
- 排队量:排队等待的会话数——有它诊断段能判断得更准
如果只有进线量、缺接起量,诊断段会退化为只能估算,仍可进行测算段。缺时段、进线量或会话时长任一项,无法计算,提示用户补充。
此外测算段用到一个参数:单人活跃并发数(默认 3)。可在收集数据时一并问用户「你们一个坐席一般同时接几个会话」,用户说不清就用默认 3,并在结果中说明用的是默认值。
第二步:清洗与字段识别(这一步最容易出错,必须认真做)
用户导出的表格式千差万别,直接计算几乎一定会出错。先清洗、回显、让用户确认,再算。
字段名识别(同义词映射)
- 时段 ← 时段 / 时间 / 小时 / hour / 时间段 / 时点
- 进线量 ← 进线量 / 呼入量 / 来话量 / 咨询量 / 会话量 / 总量 / inbound / offered
- 接起量 ← 接起量 / 接通量 / 受理量 / answered / handled
- 排队量 ← 排队量 / 排队数 / 等待数 / queued / waiting
- 平均会话时长 ← 平均会话时长 / 平均通话时长 / 平均处理时长 / AHT / 平均时长 / avg handle time
数值清洗
- 千分位逗号:
1,234→1234 - 百分号:接起率若给的是
95%这类,转成小数0.95 - 空值/横杠:
-、空白、N/A→ 该时段按缺失处理,不参与该项计算,并在回显中标注
时长归一化(务必转成「秒」再算)
时长字段格式多样,统一转为秒:
mm:ss(如03:45)→ 分×60+秒 = 225 秒hh:mm:ss(如00:03:45)→ 225 秒- 带单位(
225秒、3.75分钟)→ 抽数字按单位换算 - 纯数字且单位不明:数值 < 60 视为分钟(
3.75→ 225 秒),≥ 60 视为秒(225→ 225 秒)。这是推测,必须在回显里写明「识别为 X 秒/通」让用户核对。
回显确认
清洗完,向用户展示一份简短的识别结果再继续:
- 识别到的时段数、时间粒度(整点/半点)
- 平均会话时长识别为多少秒/通
- 哪些时段的哪些字段缺失、如何处理的
- 一句话:「以上识别无误的话我就开始测算,有出入请指出」
用户确认或未提异议,进入计算。
第三步:诊断现有人力盈亏
目标:不需要用户告诉我们「当时排了几个人」,用结果指标反推每个时段的人力状态。
反推当时的在线人力
该时段实际消耗工时(人) ≈ 接起量 × 平均会话时长(秒) ÷ 3600
这是「至少投入了这么多人力」的下限估计。
判定每个时段的状态
按目标接起率 95%(见第四步说明)为基准线,结合三个信号打标签:
- 人手不足(红):接起率明显低于目标(接起量/进线量 < 0.95),或排队量显著偏高。说明当时人不够,在丢客户。
- 基本匹配(绿):接起率达标且排队可控。
- 人力过剩(黄):接起率满、话务量低于该时段人力可承载量较多。说明当时人闲着。
诊断段输出
- 一句总体结论:全天有几个时段人手不足、几个过剩
- 逐时段的状态标签(喂给热力图)
- 点名最突出的问题时段:「周二 20:00-22:00 接起率仅 82%、排队严重,是全周最缺人的时段」「周三 14:00-16:00 接起率 100% 但话务量低,人力偏富余」
诊断的价值在于具体——不要泛泛说「高峰要加人」,要指到具体时段、给出具体数字。
第四步:测算所需人力(在线客服并发口径)
这是技能的计算内核。分两步:先用排队论(Erlang C)算出需要多少「会话通道」以保证目标接起率,再按坐席的活跃并发能力把通道数换算成实际人数。
为什么不能用「话务量 × 时长 ÷ 3600」这种朴素公式:它假设话务均匀到达,会系统性少算——真实话务会扎堆,要用排队论才能保住目标接起率。
为什么要除以并发数:在线客服一个坐席能同时处理多个会话,所以真正需要的人数是「会话通道数 ÷ 单人活跃并发数」。若不除,会得出电话客服口径的人力(数倍虚高),完全不符合 livechat 场景。
参数
- 目标接起率:默认 95%,即 95% 的会话在 20 秒内被接起。可让用户指定(行业常见 95%–99%);未指定就用 95%/20秒。
- 平均会话时长:用清洗后的秒数。
- 单人活跃并发数:默认 3。指一个坐席同时真正在处理的会话数(不是系统设置的接待上限,也不含已咨询完但未关闭的静默会话)。3 是在线客服较理想的活跃并发,再高坐席会吃力、回复变慢。可让用户按自身业务复杂度调整——简单咨询可高些,复杂问题应低些。
> 关于并发口径:本技能用「进线量 × 会话时长」计算工作负荷,这个口径只包含真正处理会话所消耗的时间,静默挂着、已咨询完未关闭的会话不产生会话时长、不进这个账。因此反推得到的是活跃并发的等效负荷,天然排除了「挂着但没在聊」的僵尸会话,无需额外校正。
计算方法(对每个时段分别算)
第一步用 Erlang C 求出满足目标接起率所需的最少「会话通道数」,第二步除以并发数并向上取整得到人数。参考实现(agent 按此逻辑用代码计算,不要心算):
import math
def erlang_channels(calls_per_hour, aht_seconds, target_sl=0.95, target_wait_sec=20):
if calls_per_hour <= 0:
return 0
intensity = calls_per_hour * (aht_seconds / 3600.0) # 话务强度(Erlang)
N = max(1, math.ceil(intensity))
while True:
A = intensity
if N <= A:
N += 1; continue
sum_terms, term = 0.0, 1.0
for k in range(N):
if k > 0: term = term * A / k
sum_terms += term
top = term * A / N
pw = (top * (N/(N-A))) / (sum_terms + top * (N/(N-A))) # 排队概率
sl = 1 - pw * math.exp(-(N-A) * (target_wait_sec / aht_seconds))
if sl >= target_sl:
return N
N += 1
def required_staff(calls_per_hour, aht_seconds, concurrency=3,
target_sl=0.95, target_wait_sec=20):
channels = erlang_channels(calls_per_hour, aht_seconds, target_sl, target_wait_sec)
return math.ceil(channels / concurrency) # 通道数 → 实际人数
对半点粒度的数据,把该半小时的会话量换算成每小时速率(×2)再代入。
测算段输出
- 一条 24 小时人力需求曲线:每个整点建议的坐席数
- 全天折算:总人时、按 8 小时/人折算的全职人数
- 一句引导:「以上是各整点满足 95% 接起率所需的人力(按单人活跃并发 3 计算),贵方可据此曲线安排两班或三班制,使排班既覆盖高峰又不在低谷浪费人力」
第五步:渲染报告并交付
不要自己手写报告 HTML 或重新实现算法。 本技能自带 scripts/staffing_report.py,Erlang C 算法、并发换算、数据清洗辅助、报告模板都在里面,且已验证。你只需:清洗数据 → 构造 records → 调用 build_report()。
import sys
sys.path.insert(0, 'scripts') # 指向技能包内的 scripts 目录
from staffing_report import build_report, clean_number, to_seconds
# records:每个时段一条,aht 用 to_seconds() 转成秒,数值用 clean_number() 清洗
records = [
dict(seg="09:00", offered=1234, answered=1180, queued=45, aht=to_seconds("03:20")),
# ... 其余时段
]
# verdict_lines 传入你根据诊断结果撰写的结论句子(可含 <span class='hi'>...</span> 高亮),
# 不传则用脚本的自动结论。建议自己写,更具体、更有价值。
verdict_lines = [
"全天有 <span class='hi'>3 个时段人手不足</span>,集中在晚间高峰。",
"<span class='hi'>20:00 最缺人</span>——接起率仅 86.2%、排队 260,应配 50 人。",
"午间 12:00-13:00 人力宽松,可后移支援晚高峰。",
]
out_path, summary = build_report(
records, concurrency=3, target_sl=0.95, target_wait_sec=20,
verdict_lines=verdict_lines,
out_path='人力测算报告.html')
参数说明:concurrency 用第一步问到的并发数(默认 3);target_sl/target_wait_sec 用目标接起率(默认 0.95/20秒)。函数返回报告路径和汇总 dict(含 peak、shorts、total、fte 等,可用于写交付话术)。
报告已包含:顶部参数概览、诊断结论、现状 vs 建议人力曲线图、分时段明细表、使用说明。生成后用 present_files 交付给用户。
交付时必须说明的口径
- 目标接起率用的是多少(默认 95%/20秒)
- 单人活跃并发数用的是多少(默认 3,或用户指定值)——并说明这是最敏感的参数,业务复杂度不同应相应调整
- 平均会话时长按 X 秒计算
- 若时长做过「单位不明」的推测,重申一次识别值
- 技能给的是「每整点所需人力」,班次由企业自行套用
交付话术保持专业、不生硬(面向企业场景)。不要在交付时罗列每个时段的数字——报告里都有,用户打开就看到,文字部分只点关键结论。
计算与数据不外传
本技能全部计算在本地完成,用户上传的话务数据不发送到任何外部服务。这是与「问卷生成」等需要云端模型的技能的关键区别——排班测算是确定性计算,无需外部 API。