限时福利领取


基于飞书开放平台搭建AI客服质检智能体的架构设计与实战

关键词:飞书aily、AI客服质检、飞书开放平台、NLP、BERT、Celery、Redis、多租户


1. 背景痛点:传统客服质检的三大瓶颈

过去两年,我先后帮三家 SaaS 公司做客服系统改造,发现质检环节几乎清一色“人拉肩扛”:

  1. 实时性差
    人工抽检最快也要 T+1 天出报告,遇到 618、双 11 大促,投诉量暴涨,质检滞后直接导致高危对话漏检。某头部电商统计:72 小时内未拦截的辱骂对话,后续投诉率提升 4.2 倍。

  2. 规则僵化
    正则+关键词库维护成本极高,且无法识别“谐音”“拼音首字母”等变形文本。一次“SB→**”的简单绕过,就让差评率飙升 1.8%。

  3. 人力成本高
    以 200 人客服团队为例,质检员配比 1:10,每月仅工资就 30 万+;若想把抽检率从 5% 提升到 30%,人力几乎线性增长,ROI 肉眼可见地往下掉。

数据说话:引入 AI 自动质检后,同样 200 席客服,抽检率可拉到 100%,高危对话拦截时效从 24h 缩短到 30s,质检团队缩减至 5 人,综合成本下降 65%。这也是我们决定用飞书自建“AI 质检智能体”的最直接动因。


2. 技术选型:为什么最终敲定飞书 aily?

在正式编码前,我们花了两周对比飞书、企微、钉钉同一纬度 API,结论如下:

维度 飞书 企微 钉钉
事件推送 长连接+HTTP 合并 仅长连接 仅 HTTP
消息卡片 支持交互按钮、下拉、表单 仅按钮 仅按钮
审批流集成 官方组件,直接拖 需自建 需自建
开放程度 几乎全量开放 部分收费 部分收费

飞书 aily 带来的三大优势:

  1. 事件推送机制
    飞书支持“事件聚合+长连接”双通道,高峰期也不会丢消息;对质检这种“一条不能少”的场景极度友好。

  2. 消息卡片灵活性
    质检结果需要运营人员二次确认,飞书卡片可以一键“通过/驳回/标星”,省去跳转 H5 的割裂感。

  3. 审批流集成
    高危对话需要法务、质检、客服主管三方会签,飞书官方审批组件 2 行代码即可嵌入,无需额外开发。


3. 核心实现:从 0 到 1 的代码级拆解

3.1 飞书机器人权限配置(含 OAuth2.0 时序图)

先创建“自建应用”,开以下权限:

  • im:message
  • im:message.readonly
  • contact:user.basic

OAuth2.0 流程手绘一张,方便后面查日志:

oauth

3.2 Python 事件订阅 + 异常重试

飞书事件回调必须做“签名验证+解密”,下面给可直接跑的最小可运行示例(符合 PEP8):

# -*- coding: utf-8 -*-
import json
import time
import hmac
import hashlib
import requests
from flask import Flask, request
from feishu import decrypt_encrypted_key  # 官方 SDK

app = Flask(__name__)
APP_ID = "cli_xxx"
APP_SECRET = "xxx"
VERIFICATION_TOKEN = "xxx"
ENCRYPT_KEY = "xxx"

def refresh_tenant_access_token():
    """获取 tenant_access_token,带 3 次重试"""
    for i in range(3):
        try:
            r = requests.post(
                "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/",
                json={"app_id": APP_ID, "app_secret": APP_SECRET},
                timeout=5
            )
            r.raise_for_status()
            return r.json()["tenant_access_token"]
        except Exception as e:
            if i == 2:
                raise e
            time.sleep(2 ** i)

@app.route("/webhook", methods=["POST"])
def webhook():
    # 1. 验签
    header_sign = request.headers.get("X-Lark-Signature", "")
    body = request.data
    calc_sign = hmac.new(
        VERIFICATION_TOKEN.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    if header_sign != calc_sign:
        return "signature error", 403

    # 2. 解密
    plain_dict = decrypt_encrypted_key(ENCRYPT_KEY, body)
    event = plain_dict.get("event", {})
    if event.get("type") == "im.message.receive_v1":
        # 3. 投递异步任务
        from tasks import qc_task
        qc_task.delay(event)
    return "", 200

3.3 质检模型集成:BERT + 规则引擎双擎

架构图先奉上,方便大家一眼看懂数据流:

arch

核心代码片段(只保留骨干):

# qc_model.py
from transformers import BertTokenizer, BertForSequenceClassification
import torch
import re

KEYWORDS = re.compile(r"(sb|nc|md)")
tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")
model = BertForSequenceClassification.from_pretrained("./ckpt/bert-qc")

def predict(text: str):
    """返回 dict:{"emotion": "negative", "score": 0.92, "hit_rule": True}"""
    # 1. 规则快速拦截
    hit_rule = bool(KEYWORDS.search(text))

    # 2. BERT 情感打分
    inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=128)
    with torch.no_grad():
        logits = model(**inputs).logits
        score = torch.softmax(logits, dim=1)[0][1].item()  # 1=negative
    return {"emotion": "negative" if score > 0.8 else "neutral",
            "score": score,
            "hit_rule": hit_rule}

4. 性能优化:高并发场景三板斧

4.1 异步处理:Celery + RabbitMQ

安装省略,直接贴 tasks.py

from celery import Celery
from qc_model import predict
from feishu import reply_card

app = Celery("qc", broker="pyamqp://guest@localhost//")

@app.task(bind=True, max_retries=3)
def qc_task(self, event):
    try:
        text = event["message"]["content"]
        result = predict(text)
        if result["hit_rule"] or result["score"] > 0.8:
            reply_card(event["open_id"], "高危对话已拦截,等待人工复核")
    except Exception as exc:
        raise self.retry(exc=exc, countdown=2 ** self.request.retries)

4.2 对话缓存:Redis 管道批量写

为防止重复质检,我们对 message_id 做 1h 缓存:

import redis
r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
pipe = r.pipeline()
for mid in msg_id_list:
    pipe.setex(mid, 3600, "1")
pipe.execute()

4.3 规避飞书 API 限流

飞书默认“应用级 100 次/10 秒”。经验值:

  • 把“发卡片”与“回消息”分离成两个不同应用,配额翻倍;
  • 调用前 sleep(0.1) 均匀打散;
  • 对 429 响应做指数退避,最大 5 次。

5. 避坑指南:上线前必须踩的坑

  1. 敏感词过滤误判
    客户原话“这款口红真”,关键词“炸”被误杀。解决:引入白名单+拼音相似度,白名单可放 Redis Set,实时更新。

  2. user_access_token 刷新
    飞书 token 有效期 2h,必须在过期前 5min 刷新;否则用户端会收到“机器人无响应”提示。建议用 apscheduler 单线程轮询刷新,写库时加悲观锁,防止并发刷新。

  3. 模型冷启动降级
    刚发布时 GPU 节点未就绪,质检耗时会从 200ms 涨到 2s。方案:

    • 预热脚本:批量跑 100 条历史对话;
    • 降级开关:超时 500ms 直接走规则引擎,记录日志后人工回补。

6. 结语 & 开放讨论

整套代码跑通后,我们的质检覆盖率从 5% 提到 100%,高危对话拦截时效缩短到 30s,团队规模缩小 70%,真正让 AI 成为“生产力”。

但新问题来了:
如果要把这套智能体 SaaS 化,卖给多家品牌,如何在数据层、模型层、策略层做“多租户隔离”,既保证各家的敏感词库/评分阈值互不干扰,又能共享底层 GPU 算力?

欢迎大家一起留言探讨,也许你的思路就是我们下一版迭代的核心需求。

限时福利领取


更多推荐