客户发"查订单",机器人要去订单系统查订单号;发"退款",要建退款工单。这些业务处理不是一个 if-else 能搞定的,是一条管道:接收消息 → 提取意图 → 调用业务接口 → 回发结果。这篇讲这条管道怎么搭,重点在业务处理层,不是消息接收本身。

底层用的是 Eyun 平台开放的企微 API,凭证在Eyun 平台首页开通,Webhook 收消息,message/sendText 发回复。

接收层:只做落库和投递

回调进来先落库,推队列,立即返回 200。队列里存的是消息 ID,处理线程取 ID 查消息体再处理。

@app.route("/wx-api/webhook/", methods=["POST"])
def webhook():
    delivery_id = request.headers.get("X-Eyun-Delivery")
    if redis.setnx(f"dedup:{delivery_id}", 1):
        redis.expire(f"dedup:{delivery_id}", 86400)
        msg_id = save_message(request.json)
        redis.lpush("process_queue", msg_id)
    return "ok"

消息体字段(fromUin、content、conversationId、msgType)在Eyun 开发文档里有完整说明。落库后处理逻辑和接收层解耦,处理慢了不会堵回调。

意图提取:关键词优先,模型兜底

业务处理的第一步是判断客户要干什么。先用关键词规则匹配——"订单"对应查询订单、"退款"对应退款流程、"发票"对应发票申请。规则匹配成本低、响应快,覆盖 70% 场景。

规则没命中的,塞给轻量模型做意图分类。模型输出预定义的意图标签和置信度,置信度低于 0.7 的转人工,不硬答。

业务处理:按意图调接口

意图确定后,按意图调对应业务接口。每个意图对应一个处理函数,输入是消息上下文,输出是回复内容。

def handle_query_order(appid, from_uin, content):
    order_no = extract_order_no(content)
    if not order_no:
        return "请提供订单号"
    order = order_api.query(order_no)
    if order["status"] == "shipped":
        return f"订单 {order_no} 已发货,快递单号 {order['tracking']}"
    return f"订单 {order_no} 状态:{order['status']}"

INTENT_HANDLERS = {
    "query_order": handle_query_order,
    "refund": handle_refund,
    "invoice": handle_invoice,
}

处理函数里调业务系统的接口,不直接调企微接口。需要查客户档案时,调 contact/getUserProfileDetail;需要打标签时调 label/updateLabel。

回发:结果格式化后发消息

处理结果回发时,简单文本用 sendText,带链接或详情用 sendRichText 发卡片。

def reply(appid, to_uin, text):
    requests.post(
        f"{BASE}/wx-api/api/message/sendText",
        headers=HEADERS,
        json={"appid": appid, "to": to_uin, "content": text}
    )

处理失败要回友好提示,不能把异常信息直接发给客户。比如订单系统超时,回"正在查询,请稍候",后台异步重试后再补发结果。

处理流水线的核心是解耦

接收、意图、业务、回发四层拆开,每层只做一件事。接收层不关心业务,业务层不关心消息怎么来的,回发层不关心业务逻辑。分层之后改意图规则不用动接收层,换业务接口不用动回发层,维护成本低很多。

Logo

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

更多推荐