简介这是一套面向企业客服场景的微信在线AI客服系统开源源码基于PHP开发深度集成企业微信客服帮助开发者与中小企业搭建7×24小时智能应答与人工转接并存的客服体系。系统支持上下文理解、个性化提示词配置并可将公司简介、产品列表、FAQ与促销活动写入知识库让AI优先依据业务资料作答同时具备图片与视频内容分析能力用户发送多媒体消息后可自动解析并保存至服务器。人工转接方面支持自定义关键词触发、转接前友好提示以及后台对话管理页的一键介入与用户ID自动映射。资源包共43个文件以31个PHP源码为核心辅以3个txt说明、3个html页面及md文档、图标等压缩包约20.58MB目录涵盖接口、配置、会话管理与日志模块结构清晰便于二次开发。目前已有144人学习下载适合具备PHP基础、希望快速落地微信AI客服的开发者参考。1. 微信在线AI客服系统从一条消息到一次自动回复的完整链路用户在公众号后台发一句“订单一直没发货”三秒内收到一条带订单状态的回复同时人工客服侧只看到一条“AI已处理”的标记——这就是微信在线AI客服系统要干的事。它不是一个单点脚本而是把微信生态的接入层、消息路由、大模型推理、业务数据查询、人工兜底串成一条可观测的链路。2026年做这套东西和两年前最大的区别是模型侧不用自己训了接入侧微信官方接口更严了所以真正花时间的不是“让AI说话”而是“让AI在微信的规则里稳定说话”。这套开源源码适合两类人一类是想给自己小店或私域做自动应答的开发者一类是想把客服系统当底座、再往上叠行业知识库的团队。下面按“先跑通最小闭环再补业务和兜底”的顺序拆。2. 接入层选型公众号、小程序还是企业微信2.1 三种入口的能力边界对比微信生态里能接AI客服的入口主要有三个选错了后面全是返工。公众号服务号适合做被动回复和模板消息用户主动发消息触发回复窗口是48小时小程序适合做站内客服用户在小程序里点客服按钮走的是微信官方的客服消息接口企业微信适合做内部外部联系人客服能主动发消息但外部联系人需要客户同意。很多团队一上来就想“全都要”结果消息路由写成了一团乱麻。我的建议是先选一个主入口跑通另外两个用适配器模式挂上去。入口触发方式主动消息接入难度适合场景服务号用户发消息48小时内可回复中私域、通知类小程序用户点客服仅被动回复低站内交易咨询企业微信客户发消息可主动需授权高内部外部协同2.2 用 Flask 写一个最小消息接收与回复服务不管选哪个入口微信服务端的接入逻辑是相似的验证签名、解析XML、返回XML。下面是一个能跑通公众号被动回复的最小服务用的是Flask不依赖任何微信SDK方便你看清每一步。# app.py import hashlib import xml.etree.ElementTree as ET from flask import Flask, request, make_response app Flask(__name__) WECHAT_TOKEN your_token_here # 和公众号后台配置的Token一致 def check_signature(signature, timestamp, nonce): # 微信签名校验token、timestamp、nonce 字典序排序后拼接做 sha1 tmp sorted([WECHAT_TOKEN, timestamp, nonce]) tmp_str .join(tmp).encode(utf-8) return hashlib.sha1(tmp_str).hexdigest() signature app.route(/wechat, methods[GET, POST]) def wechat(): if request.method GET: # 首次接入验证 signature request.args.get(signature, ) timestamp request.args.get(timestamp, ) nonce request.args.get(nonce, ) echostr request.args.get(echostr, ) if check_signature(signature, timestamp, nonce): return echostr return signature error, 403 # POST收到用户消息 xml_data request.data root ET.fromstring(xml_data) msg_type root.find(MsgType).text from_user root.find(FromUserName).text to_user root.find(ToUserName).text if msg_type text: content root.find(Content).text # 这里先写死回复后面换成AI推理 reply_text f收到你的消息{content} else: reply_text 暂时只支持文字消息 reply_xml fxml ToUserName![CDATA[{from_user}]]/ToUserName FromUserName![CDATA[{to_user}]]/FromUserName CreateTime{int(__import__(time).time())}/CreateTime MsgType![CDATA[text]]/MsgType Content![CDATA[{reply_text}]]/Content /xml resp make_response(reply_xml) resp.content_type application/xml return resp if __name__ __main__: app.run(port8080)这段代码的关键点有三个。第一GET请求是微信服务器验证你的服务地址用的必须原样返回echostr否则后台配置不通过。第二POST请求里微信发来的是XML不是JSON解析要用ElementTree注意CDATA包裹的字段。第三回复的XML里ToUserName和FromUserName要和收到的反过来这是新手最容易翻车的地方写反了用户收不到任何回复。参数上WECHAT_TOKEN要和公众号后台“服务器配置”里填的Token完全一致大小写敏感。跑起来之后用内网穿透工具把8080端口暴露出去填到公众号后台点“提交”能通过就说明接入层通了。2.3 消息路由把文本、图片、事件分开处理真实场景里用户不会只发文字。图片、语音、关注事件、菜单点击事件都会进来如果全塞进一个if里后面加AI逻辑会非常痛苦。我一般会做一个简单的路由表按MsgType和Event分派到不同的handler。比如关注事件直接回复欢迎语文本消息走AI推理图片消息先存下来再回复“已收到图片”。这一步不需要多复杂但一定要在接AI之前做完否则AI会收到一堆它处理不了的消息类型。3. AI推理层把大模型接进微信消息流3.1 为什么用“检索生成”而不是纯生成纯让大模型自由发挥在客服场景里会出两类问题一是编造业务信息比如用户问“我的订单到哪了”模型不知道订单号就开始瞎猜二是回复风格不稳定同一个问题两次回答不一样。所以2026年做微信AI客服主流做法是RAG检索增强生成先从业务数据库或知识库里查出相关信息再把信息塞进prompt让模型组织语言。这样模型只负责“说人话”不负责“记事实”。检索层可以用向量库也可以简单点用关键词匹配看你的知识库规模。3.2 用 OpenAI 兼容接口做一次带上下文的推理下面这段代码演示的是收到用户文本后先查一个模拟的订单数据再把订单信息和用户问题一起发给模型最后返回回复。用的是OpenAI兼容的接口格式换成任何一家兼容的模型服务都一样。# ai_handler.py import requests API_KEY your_api_key API_URL https://api.example.com/v1/chat/completions # 换成你的模型服务地址 MODEL your_model_name # 模拟业务数据查询 def query_order(user_id, question): # 真实场景这里查数据库这里用假数据演示 if 订单 in question or 发货 in question: return 订单号 20260101状态已发货预计明天送达 return def build_prompt(user_question, context): system 你是一个微信客服助手只根据提供的业务信息回答不要编造。语气友好简洁。 if context: user f业务信息{context}\n用户问题{user_question} else: user f用户问题{user_question}\n如果没有相关信息请回复“我帮你转人工确认一下”。 return system, user def ask_ai(user_id, question): context query_order(user_id, question) system, user build_prompt(question, context) payload { model: MODEL, messages: [ {role: system, content: system}, {role: user, content: user} ], temperature: 0.3, # 客服场景要稳定温度调低 max_tokens: 300 } headers {Authorization: fBearer {API_KEY}} resp requests.post(API_URL, jsonpayload, headersheaders, timeout10) data resp.json() return data[choices][0][message][content]逻辑上query_order是业务查询的占位真实项目里换成你的数据库查询或API调用。build_prompt把业务信息和用户问题拼在一起system prompt里明确“不要编造”这是减少幻觉最直接的手段。temperature设0.3而不是0.7是因为客服回复不需要创意需要稳定。max_tokens限制300防止模型长篇大论微信消息太长体验很差。超时设10秒因为微信服务器等太久会重试导致用户收到重复回复。把ask_ai的返回值替换掉第2章里的reply_text整条链路就通了。3.3 多轮对话的上下文怎么存微信的消息是无状态的每次用户发消息都是一个独立的POST请求。要做多轮对话必须自己存上下文。最简单的做法是用一个字典key是用户的OpenIDvalue是最近几轮的消息列表。但字典重启就丢所以生产环境要换成Redis。存的时候只存最近3到5轮太长的上下文既费token又容易让模型跑偏。另外要注意微信的48小时窗口内可以多次回复但每次回复都是一次独立的HTTP响应不能像WebSocket那样持续推送。4. 业务数据对接让AI回答“我的订单到哪了”4.1 订单查询接口的封装与缓存AI客服最常被问的就是订单状态、物流、退款进度。这些数据在你的业务系统里AI不能直接查数据库要走一层封装好的接口。封装的时候有两个原则一是接口要能按用户身份过滤不能让A用户查到B用户的订单二是加缓存同一个订单号在5分钟内重复查询直接返回缓存减少数据库压力。下面是一个带缓存的查询封装示例。# order_service.py import time import requests _cache {} # 生产环境换成Redis CACHE_TTL 300 # 5分钟 def get_order_status(user_id, order_no): cache_key forder:{user_id}:{order_no} now time.time() if cache_key in _cache: data, ts _cache[cache_key] if now - ts CACHE_TTL: return data # 调用内部订单服务带上user_id做权限校验 resp requests.get( https://internal.example.com/api/order, params{user_id: user_id, order_no: order_no}, timeout5 ) if resp.status_code ! 200: return None data resp.json() _cache[cache_key] (data, now) return data这里的关键是user_id必须从微信的OpenID映射过来不能由用户输入。映射关系存在你的用户表里OpenID是微信侧的唯一标识。缓存TTL设5分钟是个经验值太短没意义太长用户看到的状态会滞后。如果订单服务挂了返回NoneAI侧就回复“系统繁忙请稍后再试”而不是让模型瞎编一个状态。4.2 知识库的两种落地方式除了订单这种结构化数据还有大量非结构化的知识比如退换货政策、使用教程、常见问题。这些内容有两种接法一种是直接塞进prompt适合内容少几千字以内的场景另一种是做成向量检索用户问题先检索出最相关的几段再塞进prompt。向量检索的落地路径是把知识文档切块、调embedding接口、存向量库、查询时算相似度。如果知识库不大用关键词匹配也能凑合但用户换个说法就匹配不到了所以2026年做新项目我一般直接上向量检索省得后面返工。5. 避坑与排查微信AI客服最容易翻车的五个地方5.1 回复超时导致用户收到重复消息现象用户发一条消息收到两条一样的回复。原因微信服务器等待你的响应有超时限制一般5秒左右如果AI推理超过这个时间微信会认为你没收到重试一次你的服务就处理了两遍。解决把AI推理做成异步先立刻返回一个“正在查询”的回复再用客服消息接口主动推送最终结果。或者把推理时间压到3秒以内用更快的模型或加缓存。5.2 签名校验失败但找不到原因现象公众号后台配置服务器地址时一直提示“token验证失败”。原因最常见的是Token填错或者URL里的端口没对上或者你的服务返回的不是纯echostr而是带了额外字符。解决先在本地用curl模拟微信的GET请求看返回是否和echostr完全一致。注意echostr不要做任何编码转换原样返回。5.3 模型回复里带了Markdown但微信不渲染现象AI回复里带了加粗或列表符号用户在微信里看到的是原始符号。原因微信文本消息不支持Markdown渲染。解决在返回给微信之前做一次清洗把Markdown符号去掉或者让模型直接输出纯文本。在system prompt里加一句“不要使用Markdown格式”也能缓解。5.4 用户OpenID在不同入口下不一致现象同一个用户从公众号和小程序进来系统认为是两个人。原因公众号的OpenID和小程序的OpenID是两套体系即使同一个人也不同。解决用UnionID做统一标识前提是你的公众号和小程序绑在同一个开放平台账号下。如果没有UnionID就在业务侧做手机号绑定来合并身份。5.5 敏感词被微信拦截导致回复发不出去现象AI生成的回复在日志里正常但用户没收到。原因回复内容里包含了微信的敏感词被平台拦截了。解决在返回前做一次敏感词过滤命中就替换成安全话术。另外AI客服不要涉及医疗、金融等强监管领域的确定性建议这类问题直接转人工。6. 进阶技巧用日志和回放把AI客服调稳跑通之后真正决定这套系统好不好用的是“出了事能不能查”。我一般会在消息入口和出口各打一条结构化日志记录OpenID、用户问题、检索到的上下文、模型原始回复、最终回复、耗时。这些日志存下来每周抽一批看重点看三类模型回复和业务数据对不上的、用户连续追问三次以上的、回复耗时超过3秒的。前两类说明prompt或检索有问题第三类说明性能要优化。回放是个很实用的技巧。把日志里的用户问题重新喂给当前的prompt和模型对比新旧回复就能在改prompt之前预判影响。下面是一个简单的回放脚本框架。# replay.py import json from ai_handler import ask_ai def replay(log_file): with open(log_file, r, encodingutf-8) as f: for line in f: record json.loads(line) old_reply record[reply] new_reply ask_ai(record[user_id], record[question]) if old_reply ! new_reply: print(f问题{record[question]}) print(f旧{old_reply}) print(f新{new_reply}) print(---) if __name__ __main__: replay(chat_logs.jsonl)这个脚本跑一遍你就能看到改prompt之后哪些问题的回复变了。如果变好的多、变差的少就上线如果大量原本正确的回复被改坏就回滚。参数上日志文件建议按天切分回放时只取最近一周的太老的日志业务已经变了参考价值不大。最后说一个我自己的习惯每次上线新的prompt或模型版本之前一定先拿20条真实用户问题跑一遍人工看一遍回复。这20条里至少要有5条是“业务数据查不到”的情况看模型会不会老实说“我转人工”而不是硬编一个答案。这个习惯帮我省了很多次线上事故。希望帮到你。本文还有配套的精品资源点击获取