首页 / 博客 / AI·大模型接入

微信接入通义千问做智能客服(国产大模型)

分类:AI·大模型接入 · 标签:微信、通义千问、智能客服

前言

随着大语言模型技术走向成熟,越来越多的企业和个人开发者开始思考:能否把 AI 直接接进微信,让用户在聊天框里就能得到智能回复,省去跳转 App、打开网页的麻烦?

通义千问(Qwen)是阿里云推出的国产大语言模型,支持对话、摘要、代码生成等多种能力,且对中文语境的理解相当出色。它提供了标准的 HTTP API,可以方便地嵌入到各类后端服务中。微信侧则需要一个能稳定收发消息的机制——只要把两端打通,就能构建一套"用户在微信提问 → 后端转发给通义千问 → 把答案送回微信"的闭环。

本文从零搭建这套流程:先讲清楚整体架构,再逐步展示消息收发、AI 调用、上下文管理三个核心模块的实现代码,最后给出几条生产建议。全程使用 Python,示例代码聚焦逻辑,可直接参考改造。

值得注意的是,这套方案并不依赖企业微信或公众号体系,而是直接对接个人微信账号。这意味着部署成本极低——不需要营业执照、不需要申请资质,只要有一个微信账号和一台能对外提供 HTTP 服务的服务器,就能把整套智能客服系统跑起来,非常适合小团队或独立开发者快速验证业务场景。


一、整体架构

1.1 数据流

用户(微信)
   │  发消息
   ▼
微信 HTTP 接口层(回调接收 + 消息发送)
   │  提取文本
   ▼
会话管理模块(维护 per-user 上下文)
   │  拼装 messages[]
   ▼
通义千问 API(DashScope)
   │  返回 AI 回复
   ▼
微信 HTTP 接口层(postText 发回用户)
   │
   ▼
用户(微信)收到回复

三个模块职责分离:消息层只负责收发;会话层维护对话历史;AI 层调用通义千问。这样任何一端替换(换其他大模型、换其他 IM)都只需改单层。

模块解耦的好处在实际维护中尤为明显。假设你后续想把通义千问换成 DeepSeek 或 Moonshot,只需修改 AI 层的调用逻辑,消息收发和上下文管理完全不用动。同样,如果未来需要同时支持多个微信账号,也只需在消息层做多账号路由,其余两层保持不变。

1.2 技术选型

模块技术
Web 框架FastAPI(异步,性能好)
AI 模型通义千问 qwen-plus(性价比高)
会话存储Redis(支持 TTL 自动过期)
运行环境Python 3.10+,公网服务器

选择 FastAPI 而非 Flask 的原因是异步支持。通义千问 API 调用属于 IO 密集型操作,在并发较高时,异步框架能显著减少线程开销,同一台服务器可以处理更多并发请求。Redis 则是会话持久化的首选方案,内置 TTL 机制让会话自动过期,既省存储又不需要额外的清理任务。


二、环境准备

2.1 申请通义千问 API Key

在阿里云 DashScope 控制台创建 API Key。免费额度足够测试阶段使用,生产环境按 token 计费。Key 格式形如 sk-xxxxxxxxxxxxxxxx,后面写进配置文件。

建议把 API Key 存入环境变量,而非硬编码在代码里。即使是私有仓库,硬编码 Key 也容易因误操作泄露,后果较严重。

2.2 安装依赖

bashpip install fastapi uvicorn httpx redis dashscope

2.3 配置文件

python# config.py
BASE  = "https://你的接口域名"   # 注册后在官方文档获取
TOKEN = "你的Token"
APPID = "你的appId"
HEADERS = {"token": TOKEN}       # 鉴权字段名以官方文档为准

DASHSCOPE_API_KEY = "你的通义千问ApiKey"
QWEN_MODEL = "qwen-plus"         # 也可换 qwen-turbo / qwen-max

REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
CONTEXT_TTL = 3600  # 会话上下文保留 1 小时
代码为示例,具体接口及字段以官方文档为准。

三、消息收发模块

3.1 注册回调地址

微信接口层通过"回调"机制把收到的消息推送给你的服务:平台将消息 POST 到你事先用 setCallback 注册的 URL。服务需要对外可访问(公网 IP 或域名),且收到推送后返回 HTTP 200。

如果本地开发阶段没有公网 IP,可以借助内网穿透工具(如 frp、ngrok)临时暴露本地端口做测试,等逻辑稳定后再迁移到云服务器。

pythonimport httpx
import asyncio

async def register_callback(callback_url: str):
    """向平台注册消息回调地址"""
    async with httpx.AsyncClient() as client:
        resp = await client.post(
            f"{BASE}/message/setCallback",
            headers=HEADERS,
            json={"appId": APPID, "callbackUrl": callback_url}
        )
    data = resp.json()
    if data.get("ret") == 200:
        print("回调注册成功")
    else:
        print(f"回调注册失败:{data}")

3.2 接收回调消息

pythonfrom fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.post("/wechat/callback")
async def wechat_callback(request: Request):
    """接收微信消息回调"""
    payload = await request.json()

    msg_type = payload.get("type")
    from_wxid = payload.get("fromWxid")
    content   = payload.get("content", "")
    app_id    = payload.get("appId")

    # 只处理文本消息(type==1),其他类型可按需扩展
    if msg_type == 1 and content.strip():
        # 异步处理,先返回 200 避免平台超时重试
        asyncio.create_task(handle_text_message(app_id, from_wxid, content))

    return JSONResponse({"code": 200})

回调务必先返回 200,再异步处理业务逻辑。如果处理耗时超过平台超时阈值,平台会判定失败并重发,造成重复回复。

3.3 发送文本消息

pythonasync def send_text(to_wxid: str, content: str):
    """向指定微信 ID 发送文字消息"""
    async with httpx.AsyncClient(timeout=10) as client:
        resp = await client.post(
            f"{BASE}/message/postText",
            headers=HEADERS,
            json={"appId": APPID, "toWxid": to_wxid, "content": content}
        )
    result = resp.json()
    if result.get("ret") != 200:
        print(f"发送失败:{result}")
    return result

发送消息时建议设置合理的超时(示例中为 10 秒)。如果不设超时,遇到网络抖动时协程会长期挂起,积压过多后可能耗尽服务器的并发资源。


四、会话管理模块

多轮对话的关键是上下文:通义千问需要知道前几轮的问答才能给出连贯回复。我们用 Redis 以 ctx:{wxid} 为 key 存储每个用户的消息列表。

pythonimport json
import redis

r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)

def get_context(wxid: str) -> list:
    """读取用户历史上下文"""
    raw = r.get(f"ctx:{wxid}")
    return json.loads(raw) if raw else []

def save_context(wxid: str, messages: list):
    """保存上下文,TTL 到期自动清除"""
    r.set(f"ctx:{wxid}", json.dumps(messages, ensure_ascii=False), ex=CONTEXT_TTL)

def trim_context(messages: list, max_turns: int = 10) -> list:
    """最多保留最近 N 轮(system 提示词除外),防止 token 超限"""
    system_msgs = [m for m in messages if m["role"] == "system"]
    non_system  = [m for m in messages if m["role"] != "system"]
    # 每轮 = user + assistant 两条,保留最近 max_turns 轮
    kept = non_system[-(max_turns * 2):]
    return system_msgs + kept

系统提示词(system 角色)只在会话首次创建时写入,之后每次从 Redis 读出、追加新消息、再存回去即可。

需要特别注意的是 trim_context 这个截断逻辑。通义千问不同版本有各自的上下文窗口限制,超出限制后 API 会报错。保留最近 10 轮对话在大多数客服场景下已经足够——用户的问题通常在 3~5 轮内能得到解决,超过 10 轮的历史对当前问题的参考价值也很有限。如果你的场景需要更长的记忆,可以考虑对历史对话做摘要压缩,而非无限堆叠原始消息。


五、AI 调用模块

5.1 调用通义千问

pythonimport dashscope
from dashscope import Generation

dashscope.api_key = DASHSCOPE_API_KEY

SYSTEM_PROMPT = """你是一个专业、友善的客服助手。
请用简洁的中文回答用户问题,如遇到无法解答的问题,请礼貌地告知用户联系人工客服。
回复长度控制在 200 字以内,避免使用 Markdown 格式(微信不渲染)。"""

async def ask_qwen(wxid: str, user_input: str) -> str:
    """
    读取上下文 → 追加用户消息 → 调用通义千问 → 存储新上下文 → 返回回复
    """
    messages = get_context(wxid)

    # 首次对话,插入系统提示
    if not messages:
        messages.append({"role": "system", "content": SYSTEM_PROMPT})

    messages.append({"role": "user", "content": user_input})
    messages = trim_context(messages)

    try:
        response = Generation.call(
            model=QWEN_MODEL,
            messages=messages,
            result_format="message",
            max_tokens=512,
            temperature=0.7,
        )
        if response.status_code == 200:
            reply = response.output.choices[0].message.content
        else:
            reply = "抱歉,AI 服务暂时不可用,请稍后重试。"
    except Exception as e:
        print(f"通义千问调用异常:{e}")
        reply = "系统繁忙,请稍后重试。"

    # 存储含 AI 回复的完整上下文
    messages.append({"role": "assistant", "content": reply})
    save_context(wxid, messages)

    return reply

系统提示词的设计对回复质量影响很大。示例中特别加了"避免使用 Markdown 格式"这条限制——微信聊天界面不渲染 Markdown,如果 AI 输出了大量星号和反引号,用户看到的是一堆杂乱符号,体验很差。此外,把回复长度限制在 200 字以内也很重要:手机屏幕空间有限,过长的回复会让用户需要大量滚动,降低阅读意愿。

5.2 串联处理逻辑

pythonasync def handle_text_message(app_id: str, from_wxid: str, content: str):
    """完整处理一条用户文字消息"""
    # 过滤自己发出的消息(回调也会推送发件方消息,避免死循环)
    if from_wxid == APPID:
        return

    ai_reply = await ask_qwen(from_wxid, content)
    await send_text(from_wxid, ai_reply)

过滤自发消息这一步容易被忽略,却非常关键。回调通知通常会把账号自己发出的消息也推送过来,如果不过滤,机器人会对自己的每条回复再回复一条,造成无限循环,直到触发平台限频或 Redis 上下文溢出。


六、进阶功能

6.1 关键词触发人工客服

纯 AI 并不能覆盖所有场景,加一层关键词拦截,把敏感词或退出指令转人工:

pythonHUMAN_KEYWORDS = {"人工", "转人工", "客服", "投诉", "退款"}

async def handle_text_message(app_id: str, from_wxid: str, content: str):
    if from_wxid == APPID:
        return

    if any(kw in content for kw in HUMAN_KEYWORDS):
        await send_text(from_wxid, "正在为您转接人工客服,请稍候……")
        # 在此接入你的工单/IM 人工系统
        return

    ai_reply = await ask_qwen(from_wxid, content)
    await send_text(from_wxid, ai_reply)

关键词列表可以根据业务场景灵活扩展。除了退出类关键词,还可以加入业务敏感词(如"法律""诉讼"等),触发后不仅转人工,还同时给内部人员发告警通知,确保高风险问题不被遗漏。

6.2 图片消息的处理思路

通义千问 VL(视觉语言)版本支持图片理解。回调中图片消息的 type 值与文本不同,content 通常是图片 ID 或 CDN URL,可先调用下载接口获取文件,再传给 Qwen-VL 做分析。图片下载建议做队列、每条间隔 3~10 秒,避免频率过高触发风控。

6.3 微信接口层的选择

收发微信消息依赖能稳定对接个人微信的 HTTP 接口。此处使用的是托管形式的 REST 接口——WechatApi 提供扫码登录、消息收发、好友与群管理等 REST 接口,HTTP 调用即可,省去自行维护协议层的工作。配置好 BASETOKENAPPID 三个参数后,本文所有代码均可直接运行。

6.4 启动服务

bashuvicorn main:app --host 0.0.0.0 --port 8000

服务启动后,调用 register_callback("https://你的公网域名/wechat/callback") 完成回调注册,随后在微信给账号发一条消息,验证整条链路是否通畅。

建议准备一份简单的冒烟测试脚本,在每次部署后自动发一条固定消息、检查是否收到 AI 回复,确保链路没有在部署过程中被意外中断。


七、生产建议

7.1 防封与稳定性

场景建议
批量回复每条消息之间加 1~3 秒随机延迟,避免被判定为机器人
新账号登录稳定 3 天后再开启自动回复
异常重试通义千问调用失败时,最多重试 2 次,之间间隔 2 秒
消息去重msgId + Redis SETNX 防止回调重发导致重复回复

微信对自动化行为比较敏感,连续高频的机械回复很容易触发风控。随机延迟模拟真人打字间隔是目前最稳妥的方式。新号前几天建议保持正常的社交行为(收发好友请求、偶尔主动发消息),让账号"养熟"后再开启自动回复,稳定性会好很多。

7.2 Token 成本控制

通义千问按输入+输出 token 计费。上下文轮数越多,每次调用消耗的 token 越多。建议:

FAQ 本地匹配是降低成本最直接的手段。整理出高频问题(通常 20~50 条就能覆盖 60%+ 的咨询量),用简单的关键词或向量匹配做分流,命中后直接返回固定答案,既省钱又快,响应延迟也从 1~3 秒降到几十毫秒。

7.3 隐私与合规

合规问题不可轻视。即便是小规模运营,如果有用户投诉"没有告知使用了 AI",也可能带来不必要的麻烦。在首次对话时发一条简短的提示(例如"您好,我是 AI 客服助手,如需转人工请回复【人工】")既满足告知义务,也能引导用户了解如何退出自动回复。

7.4 监控与告警

pythonimport time

async def ask_qwen_with_metrics(wxid: str, user_input: str) -> str:
    start = time.monotonic()
    reply = await ask_qwen(wxid, user_input)
    elapsed = time.monotonic() - start
    if elapsed > 5:
        print(f"[WARN] 通义千问响应过慢:{elapsed:.2f}s,用户:{wxid}")
    return reply

记录每次 AI 调用耗时,超过阈值打印告警,便于排查网络抖动或模型负载问题。在实际运营中,建议把这些告警接入企业微信群机器人或钉钉通知,方便第一时间发现并处理异常,而不是等用户主动投诉才知道系统出了问题。


总结

把通义千问接进微信并不复杂,核心是三件事:用回调稳定接收消息、用 Redis 维护多轮上下文、用 DashScope SDK 异步调用 AI。三个模块职责清晰、各自可独立替换,整套方案在小规模客服场景下完全够用。

实际落地时,系统提示词的调优、关键词转人工的兜底、以及合理的防封策略,往往比代码本身更影响最终效果。建议先用最小化配置跑通全流程,再根据真实用户反馈逐步迭代提示词和业务规则,比一开始就追求"完美系统"要高效得多。

想动手试试?

WechatApi 提供扫码登录、消息收发、好友与群管理等 REST 接口,注册后几分钟跑通。

立即免费注册查看开发文档

相关产品页

🔗 微信机器人开发(产品页)🔗 微信客服机器人(产品页)🔗 微信群管理机器人(产品页)

相关文章

微信机器人接入知识库 RAG,做企业专属智能问答微信 AI 机器人多轮对话与上下文管理实战微信 AI 客服意图识别与智能转人工方案用 Dify / Coze 工作流驱动微信机器人(低代码)
© 2025 WechatApi · 企业级微信智能机器人接入平台
官网价格帮助文档博客
苏ICP备2024128799号 · 苏ICP备2023038368号