
简介一款基于大模型的智能对话客服工具工程包面向需要搭建多平台客服机器人的开发者、运营者及企业AI应用团队。源码覆盖微信、千牛、哔哩哔哩、抖音、抖音企业号、抖店、微博、小红书、知乎等平台的接入逻辑支持预设回复、接入ChatGPT生成个性化答复、发送图片与二进制文件并内置知识库上传与独立插件系统便于打造数字分身或企业专属客服助手。压缩包共151个文件以ts/tsx源码、js脚本、json配置为主含png图标资源、md说明文档、样式表及环境配置示例等整体仅858KB结构清晰适合二次开发。内容预览显示包含App.css、loader.css、.env、index.ejs等关键工程文件另含eslint配置、gitignore等可快速定位界面与配置并开展定制。已有554人学习下载是探索多平台AI客服落地方案的高性价比参考。1. 大模型智能对话客服工具为什么值得自己搭一个接入层客服同学每天在微信、千牛、B站后台之间来回切每个平台都有“快捷回复”但回答质量取决于人肉知识库。把大模型接到对话框并不难难的是把多个渠道的会话统一交给一个模型管理同时不丢消息、不串线、不回错店。下面要拆的不是某个现成产品的使用而是如果要自己做一个“基于大模型的智能对话客服工具”中间要解决哪些问题。适合一边维护客服系统、一边研究大模型应用的工程师读完能搭出一个能接微信企业微信/公众号、千牛、B站、抖音、微博、小红书、知乎的最小版本。2. 先定架构统一消息、外置会话大模型只做生成2.1 大模型客服的消息进出的两条通道入站 Webhook 与出站 OpenAPI几乎所有平台都遵循同一套规则用户消息通过 HTTP 回调推给服务器回复消息通过平台提供的开放接口发送。差异只在鉴权和消息格式。把这两条通道抽象出来就得到整个工具的骨架入站适配器把不同回调体转成标准消息出站适配器把标准回复转成平台要求的 JSON。class ChannelAdapter(ABC): platform: str abstractmethod def parse_inbound(self, raw: dict) - InboundMessage: 把平台回调体解析成标准消息 ... abstractmethod def send_reply(self, conversation: str, reply: str) - str: 把回复内容发送到平台会话 ...适配器接口里不出现任何大模型逻辑只做格式转换。这样换模型、加平台都不会互相污染。设计上要强制区分“用户消息”和“事件消息”比如商品下单、客服在线状态变化。事件消息不应该直接进入对话生成流程而是作为上下文补充。比如“用户刚拍下未付款”这件事推送过来后写进会话摘要等用户提问时再参与生成而不是让模型对着一条订单事件自说自话。2.2 标准化消息结构平台差异在这一步全部抹平这一步是整个接入层的核心。平台回调字段命名差异极大抖音叫OpenId千牛叫buyer_nick公众号里是FromUserName。如果不统一后续 prompt 拼接、日志检索、知识库召回都会非常痛苦。字段类型说明必填platformstr微信 / 千牛 / 哔哩哔哩 / 抖音 等是conversation_idstr平台侧会话唯一 ID用于定位回复是msg_idstr平台消息 ID用于幂等去重是sender_idstr用户唯一 ID是msg_typeenumtext/image/order/product/event是contentstr文本内容非文本时放可由模型理解的描述是rawdict原始回调体排障时用否conversation_id在不同平台含义不同微信里可能是一个 OpenID 加一个客服账号千牛里可能是买家 Nick 加店铺 ID。统一成一个字符串适配器负责拼好。字段越少越好避免后续模型 prompt 塞进一堆无用原始字段。2.3 会话状态放 Redis不让大模型记状态大模型接口天生无状态。同一个用户问三句话如果每次都把全部历史拼进 prompttoken 会迅速失控。常见做法是维护一个滑动窗口会话缓冲区最近 10 轮文本加上一份会话摘要一起交给模型。async def build_context(conversation_id: str, user_msg: str) - str: history await redis.lrange(fconv:{conversation_id}, 0, 19) summary await redis.get(fconv:{conversation_id}:summary) return f会话摘要{summary}\n历史消息\n{history}\n用户最新说{user_msg}这个设计把“记忆”拆成两层原始消息存 Redis List摘要存 String 或独立存储。摘要可以用大模型每 20 轮生成一次也可以用简单规则如“上一轮总结 新消息”直接追加避免频繁调用模型。等摘要超过 500 字再触发一次模型重写效果和全量历史差别不大但成本低一个数量级。2.4 选型自研接入层还是用开源对话平台如果只是给公司内部搭一个能用的客服机器人FastGPT、Dify 这类开源大模型应用平台已经内置了多渠道发布但它们的渠道适配往往偏“网页聊天接入”。要把千牛、抖音企业号这些偏电商后台的渠道接进去通常还是需要自己写适配层再通过 Webhook 把消息交给这些平台处理。我一般会采用混合方案核心会话引擎复用开源平台渠道接入层自研两边通过 Webhook 通信。这样开发量可控也不至于被开源项目的渠道实现绑住。如果业务对数据合规有要求比如对话记录必须留在私有环境那开源平台本身也要走私有化部署。接入层依旧自研但消息不再发到公网 API而是发到内网的大模型网关。架构上始终记住一句话平台适配和大模型生成之间必须有一层标准消息协议否则每接一个新平台都要动主流程。3. 最小可运行的大模型客服核心FastAPI 大模型 API 工具调用3.1 入站消息处理和回复出口先跑通一个最小闭环收到消息 - 整理上下文 - 调大模型 - 发送回复。app.post(/webhook/{platform}) async def webhook(platform: str, raw: dict): adapter get_adapter(platform) msg adapter.parse_inbound(raw) if not msg or not msg.msg_id: return {code: ignored} # 幂等去重防止平台重试导致重复回复 if not await redis.set(fmsg:{platform}:{msg.msg_id}, 1, nxTrue, ex3600): return {code: duplicate} reply await generate_reply(msg) if reply and reply.target bot: await adapter.send_reply(msg.conversation_id, reply.content) return {code: ok}逻辑说明Webhook 入口只做三件事——适配器解析、幂等去重、触发生成。get_adapter(platform)从注册表里按名称取实例每个适配器都是独立模块。nxTrue表示只有 key 不存在时才写入天然过滤平台的重试重复推送。参数说明ex3600表示去重 key 保留 1 小时一般平台重试窗口远小于这个时间。如果消息 ID 在某些推送上为空要主动丢弃而不是放行否则会导致重复回复。3.2 调用大模型OpenAI 兼容接口与本地 Ollama 二选一用 httpx 异步调用连接超时和时间超时都要显式设置。智能客服对延迟敏感平台一般要求 5 秒内响应所以模型调用要放在异步任务里先回给平台一个“收到”再通过客服消息接口把结果推给用户。async def generate_reply(msg: InboundMessage): prompt build_prompt(msg) payload { model: qwen-plus if use_cloud else llama3.1:8b, messages: [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: prompt}, ], temperature: 0.2, top_p: 0.8, max_tokens: 500, } async with httpx.AsyncClient(timeouthttpx.Timeout(3.0, read15.0)) as client: resp await client.post( f{LLM_BASE_URL}/chat/completions, jsonpayload, headers{Authorization: fBearer {LLM_API_KEY}}, ) data resp.json() return data[choices][0][message][content]参数说明temperature0.2是客服场景的合理默认值过高会让话术飘过低会让生硬。read15.0允许模型慢慢吐字但连接超时压到 3 秒避免网络黑洞拖死任务。LLM_BASE_URL可以指向云端兼容接口也可以是本地 Ollama 的http://127.0.0.1:11434/v1。3.3 工具调用让大模型查订单、查物流、改备注只靠模型生成的客服不会有真正解决问题的能力。开放平台一般都提供查询订单、查物流、改备注等接口通过 function calling 暴露给模型。tools [{ type: function, function: { name: query_order, description: 根据订单号查询订单状态和物流信息, parameters: { type: object, properties: { order_id: {type: string, description: 订单号} }, required: [order_id], }, }, }]模型先返回一个 tool_call代码拿到参数后去调千牛或抖店 API结果再作为 assistant 消息塞回模型得到最终用户话术。这个闭环里要特别注意工具调用返回的内容是结构化数据不能直接对应答给用户必须在第二次生成中让模型“用客服语气说人话”。3.4 隐私脱敏和敏感词拦截客服消息进模型之前先把手机号、地址、身份证号替换成占位符模型产出话术后再把脱敏数据放回去。另外平台对客服回复有明确风控规则回复里出现“加微信”“手机号”“线下交易”很容易触发限流。建议在发送出口挂一层正则加白名单模型双检查。SENSITIVE_PATTERNS [ r1[3-9]\d{9}, r加\s*微信, rV\s*X[:_]?, ] def mask_pii(text: str) - str: for pattern in SENSITIVE_PATTERNS: text re.sub(pattern, [已隐藏], text) return text不要把这层只放在模型输入侧输出侧才是风控的最终兜底。很多平台侧的回调失败不是因为技术而是因为回复里带了导流词所以出口检查优先级最高。4. 大模型客服的多平台接入微信、千牛、哔哩哔哩、抖音、微博、小红书、知乎的共性与差异4.1 平台接入参数速查表平台鉴权方式消息获取方式需要注意的点微信公众号/企业微信Token EncodingAESKeyAES 解密HTTP 回调需要验证 URL 合法性消息体加密才是常态千牛应用 Key/SecretOAuth 2.0Webhook 订阅消息也可主动拉取绑定店铺维度需处理多店铺隔离哔哩哔哩开放平台 client_id/client_secret回调推送部分接口主动拉取UP 主私信需逐用户授权抖音企业号/抖店OAuth 2.0access_token 有效期约 15 天消息订阅 WebhookToken 失效要能自动刷新微博聊天超级私信AppKey AppSecret接口轮询/回调有消息类型限制发图需先上传素材小红书专业号开放平台授权 token消息订阅/查询接口专业号需品牌资质回调需验签知乎开放平台 token私信接口权限申请较严优先用官方 SDK表格不是让照抄配置而是展示一个问题每个平台的鉴权细节都不一样但二三十个平台的适配层能共用同一套 OAuth 凭据管理、Token 换新、回调验签框架。把差异化逻辑收敛到每个 adapter 的auth()方法里上层业务完全不用关心“抖音 token 多少天过期”这种细节。4.2 千牛和抖店工具调用下单场景最重电商客服最大的诉求不是闲聊而是买家常问“发货没有”“什么时候到”“能不能改地址”。这两个平台的开放 API 提供了订单详情、物流轨迹、售后申请等接口。接入时需要先创建一个unified_order表把不同平台的订单统一成一条记录让大模型工具调用只认这张表。class UnifiedOrder(BaseModel): platform: str order_id: str buyer_id: str status: str # unpaid/paid/shipped/finished/closed logistics: str items: list[dict] []接入千牛时可能发现它推送的是加密 buyerId抖音推的是 openid淘宝订单号是纯数字。这些差异全部在适配器里换算不要让业务逻辑出现if platform ...的判断。4.3 内容平台私信B站、小红书、微博、知乎内容平台的私信更依赖历史上下文和用户身份。用户可能是从视频、笔记、文章来的会话里通常会提到“你的那个视频”“第三期笔记”之类的指代。这类问题纯靠当前消息很难答准建议在适配层把“来源页面标题/URL”一起塞进标准消息的extra字段再拼进 prompt。# B站私信回调里一般能拿到来源稿件信息 msg.extra { source_page: raw[data].get(page_title), source_url: raw[data].get(page_url), }这一层对用户体验提升非常大。用户问“你上次说的那个配置”模型如果看到上一轮里出现了视频标题就能把指代关系接上。4.4 消息媒体的统一处理表情、图片、商品卡片在不同平台都以不同的 JSON 存在。不要企图直接把这些二进制或链接发给大模型。常见做法是把图片转存到自有对象存储再把 URL 转成文字语义描述比如“用户发送了一张图片https://…/abc.png”。如果需要多模态支持再用视觉模型看图但大多数客服场景不需要。5. 提示词工程与知识库决定大模型客服下限的不只是模型5.1 客服系统提示词的固定结构一套可维护的客服提示词至少要拆成四个部分角色定义、平台约束、知识库引用、转人工条件。平台约束必须明确。把这些写成 system prompt而不是附加到每轮用户消息里。你是某品牌的智能客服支持微信、千牛、哔哩哔哩、抖音、小红书、知乎等渠道。 - 回答不超过50字先给结论再解释。 - 只使用知识库中提供的信息不要编造活动规则。 - 当用户要求退款、投诉、要求人工时输出 [ESCALATE]。每次拼接用户消息时把从知识库检索出的片段拼在问题前面防止模型自由发挥。5.2 RAG把商品资料、物流政策、售后规则向量化用 Embedding 模型把常见问题片段建索引。召回后不直接塞进 prompt先用相似度阈值过滤低于 0.7 的知识片段不要给模型否则模型会被不相关上下文带偏。query_vec await embed(user_msg) hits await vector_db.search( collectionknowledge, query_vectorquery_vec, limit5, ) relevant [h for h in hits if h.score 0.7] if not relevant: context 没有检索到相关知识请基于客服常识谨慎回答并主动提示转人工。 else: context \n.join([h.payload[content] for h in relevant])如果知识库里有上千条商品规则必须做重排单纯向量相似度经常把相近的 SKU 描述排太靠前。常见做法是加一个 rerank 模型或者用小模型对 top20 做 relevance 二分类。没有预算的话一个简单的评分规则先顶上去关键词命中加 0.2标题命中加 0.4重叠后归一化。5.3 大模型参数与成本控制大语言模型生成时temperature、top_p、max_tokens 要分开调。客服要求输出稳定temperature0.2起步需要创意类文案再上调到 0.7。max_tokens千万别设 2048客服回答普遍在 200 字内设 300~500 就能显著降低成本和延迟。另外给模型加stop参数比如遇到[ESCALATE]就截断减少无效输出。对于高频简单问答可以在大模型前加一个规则层或小分类模型如果用户消息命中正则或 FAQ 完全匹配库直接返回固定话术不走大模型。这样平台回调的峰值流量先由规则层扛住只有 30% 的复杂问题才到大模型。这个比例优化后token 费用通常能降 40% 左右。5.4 本地部署大模型作为降级方案云端大模型 API 一旦限流或故障客服会集体失明。成熟的方案是本地备一台 Ollama 或 vLLM 服务加载 7B~14B 模型作为降级链路。云端正常时本地模型不处理对话仅用来做意图分类或摘要云端不可达时切到本地模型虽然话术质量下降但服务不中断。切换逻辑写在抽象层后面业务代码不感知。6. 大模型客服的部署、监控与三个最容易踩的隐形坑6.1 用 Docker Compose 拉起依赖FastAPI 服务独立扩缩容services: api: build: . environment: REDIS_URL: redis://redis:6379 LLM_BASE_URL: ${LLM_BASE_URL} LLM_API_KEY: ${LLM_API_KEY} depends_on: - redis - qdrant redis: image: redis:7-alpine command: redis-server --appendonly yes qdrant: image: qdrant/qdrant ports: [6333:6333]api服务至少要开 2 个副本并配置--workers 4。不要把一个平台回调进程和模型调用进程混在一个 worker 里模型调用是 IO 密集型且会阻塞事件循环。建议用FastAPI加任务队列将生成任务切到后台消费者进程由消费者负责调用模型和回传。6.2 幂等消息过滤与会话锁一个可以直接抄的技巧平台重试是常态。尤其是千牛、抖音这种高并发回调经常会同一 msg_id 推两次。只靠SETNX做去重不够因为第一条消息还没处理完时第二条重复消息会被误放行。更稳的做法是组合 Redis 的SETNX与EXPIRE做一个“处理中锁”锁未释放前重复消息直接丢弃。lock_key flock:{platform}:{msg_id} if await redis.set(lock_key, worker_id, nxTrue, ex30): try: await process(msg) finally: await redis.delete(lock_key) else: return duplicate这个实现的关键在finally里释放锁。若处理超时超过 30 秒锁自动过期平台重试就能重新触达。锁值worker_id用于排障日志里能定位是哪个 worker 在处理。会话锁同样重要。同一个客户由多个机器人实例或多个客服同时回复会出现消息乱序。给conversation_id加互斥锁锁内完成“取上下文—生成—发送”全过程async with redis.lock(fconversation:{conversation_id}, timeout20): context await build_context(conversation_id, msg) reply await generate_reply(context) await adapter.send_reply(msg.conversation_id, reply) await append_history(conversation_id, msg, reply)锁超时设 20 秒确保大模型超时后锁能被释放不会连累后续消息。客服场景里一个用户同时来多条消息的概率很低这个代价完全值得。6.3 监控指标盯这四个数字上线后不要只看 CPU。客服系统的健康度要看人工介入率、首响时长、token 成本、上下文命中率。这四个指标分别反映知识库质量、链路延迟、成本和模型能力。日志里统一打印platform|conversation_id|duration|cost|escalated按平台聚合就能快速定位问题出在模型、知识库还是渠道适配。如果发现某个平台“答非所问”特别多优先查消息顺序。很多乱序来自回调重试用户先问退款后问物流但模型先看到了物流。建议在上下文里记录每轮消息的recv_ts生成前校验是否比上一条已处理消息旧旧的直接忽略避免二次污染会话历史。本文还有配套的精品资源点击获取