微信小程序AI聊天源码:完整架构设计与避坑指南 简介这是一份微信小程序AI聊天页面的完整源码资源面向希望快速上手小程序开发与LLM大语言模型对接的开发者尤其适合需要在儿童教育、商城客服、法律咨询等场景中自定义AI人设和回复语气的项目参考。资源共6个文件压缩包约386KB包含wxml页面结构、wxss样式、js交互逻辑、json全局配置以及2张png界面示意图代码结构精简页面与逻辑分离便于替换图片和修改配置后直接复用。当前已有977人学习浏览。通过这份源码读者可以直观看到小程序如何完成与大模型服务的数据交互、如何传入系统提示词来设定AI角色、回复风格和约束条件也能参考其中关于消息列表渲染、输入发送按钮触发、对话历史维护等常见实现从而快速打通小程序端到LLM的完整调用链路并以此为基础扩展更多自定义对话应用。1. 微信小程序聊天页面 AI 大语言模型这份源码包能帮你少踩多少坑做 AI 聊天类小程序最耗时间的其实不是调 LLM 接口——而是聊天页面本身。消息列表怎么渲染才不卡、输入框怎么处理键盘弹起、流式输出时 setData 怎么节流、多轮对话的 history 怎么拼才不会爆 token这些才是真正会拖垮进度的细节。这份「微信小程序聊天页面 AI 大语言模型」源码包把聊天 UI 和 LLM 对接的完整链路都给你串好了前端页面、请求封装、服务端转发逻辑都有你拿到手不是看演示是能直接改着用的。适合手里有小程序开发基础、想快速把大模型能力接进产品里的人不用从零去趟消息渲染和请求转发的那些坑。2. 小程序为什么适合做 AI 聊天载体架构选型与源码模块拆解2.1 小程序聊天页的天然优势微信小程序做 AI 聊天产品有一个网页端不具备的优势用户不需要下载、不需要注册扫码或者搜索就能进。对 LLM 应用来说获客成本被压得很低。另一个是微信的登录体系wx.login换 openid 的过程是现成的不用自己搭账号系统。但小程序也有自己的限制。最典型的是网络层——wx.request只支持 HTTPS而且域名必须在小程序后台配置合法白名单否则真机调试直接 403。这就意味着你不能像网页开发那样随口把请求打到本地http://localhost:8000开发期要用开发工具里的「不校验合法域名」开关上线前必须配好正式域名。这套源码的架构思路是标准的「小程序端 服务端转发」模式。小程序端只负责聊天 UI 和用户交互LLM 的 API Key 放在服务端由服务端统一向外部的 LLM API 发请求。这样做有两个直接好处一是 API Key 不进小程序包不会被反编译扒走二是你可以在服务端做多模型切换、缓存和计费后续扩展不用动小程序端。2.2 源码包的目录结构与文件定位先看整体目录源码包解压后大概是这样的结构ai-chat-miniprogram/ ├── pages/ │ ├── chat/ # 聊天主页面 │ │ ├── chat.js # 页面逻辑消息管理、请求封装 │ │ ├── chat.wxml # 页面结构消息列表 输入区 │ │ ├── chat.wxss # 聊天页样式 │ │ └── chat.json # 页面配置 │ ├── index/ # 入口页可选一般是跳转到 chat │ └── ... ├── utils/ │ ├── request.js # wx.request 封装 │ └── token.js # token 计算与截断工具 ├── app.js # 小程序入口全局配置与登录态 ├── app.json # 全局配置页面路由注册 └── project.config.json # 项目配置appid 在这里改pages/chat/是这份源码的核心。chat.wxml里是消息列表和输入框的骨架chat.js里是整套聊天逻辑——消息数组管理、发送请求、接收返回。utils/request.js封装的不是微信原生wx.request而是对齐了 LLM API 的请求格式你换模型的时候只需要改服务端的 baseURL 和认证头。2.3 运行环境和基础库要求源码基于微信小程序原生框架不依赖第三方 UI 库。基础库版本建议在 2.20.0 以上因为聊天页用到了scroll-view的局部滚动和键盘弹出相关的 API老版本基础库对这两个特性的支持有兼容问题。开发工具用微信官方开发者工具就行导入项目后把project.config.json里的 appid 换成你自己的否则预览不了。如果你用的是 uni-app 或者 Taro 这类跨端框架这份源码不能直接跑需要先在小程序原生工程里跑通再看逻辑迁移到跨端框架——但消息数据结构和请求封装思路是可以照搬的。3. 把项目跑起来从申请密钥到首次对话的完整流程3.1 小程序端appid、合法域名与开发调试第一步用微信开发者工具导入源码工程。导入时需要填 AppID——用测试号也可以但测试号的wx.login和云开发功能会受限。建议直接用小程序后台注册一个正式 AppID免费。第二步处理域名白名单。开发阶段有两个办法一是在开发者工具的「详情 → 本地设置」里勾选「不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书」这样可以在本地直接请求你的开发服务器二是老老实实把 API 域名加到小程序后台的「开发管理 → 开发设置 → 服务器域名」里。上线前必须走正式配置这一步跳不过去。// utils/request.js 中的请求封装注意 url 是服务端转发地址 const BASE_URL https://your-server.com/api; // 改成你自己的服务端地址 function chatRequest(payload) { return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}/chat, method: POST, data: payload, header: { Content-Type: application/json, // 这里的 token 是业务层校验用的不是 LLM 的 API Key X-Auth-Token: wx.getStorageSync(authToken) }, success: (res) { if (res.statusCode 200) { resolve(res.data); } else { reject(new Error(HTTP ${res.statusCode})); } }, fail: (err) reject(err) }); }); }这段代码的关键在于BASE_URL指向的是你自己的服务端不是直接指向 LLM 服务商的域名。为什么因为微信小程序后台的合法域名只能配一个固定域名把这个域名指向你的服务端你就可以在服务端自由调度不同的 LLM 供应商不用每次换模型都去改小程序后台配置。X-Auth-Token是业务鉴权用的不是 LLM API Key。API Key 放在服务端环境变量里小程序端永远拿不到。3.2 服务端API Key 管理与请求转发源码包里的服务端代码是一个 Node.js 的轻量转发层用 Express 搭的。核心就一个接口接收小程序的聊天请求整理成 LLM API 的格式转发出去再把结果返回给小程序。// server/index.js —— 服务端转发层的核心逻辑简化版 const express require(express); const axios require(axios); const app express(); app.use(express.json()); // 从环境变量读取 LLM API Key绝不写在代码里 const LLM_API_KEY process.env.LLM_API_KEY; const LLM_BASE_URL process.env.LLM_BASE_URL || https://api.openai.com/v1; app.post(/api/chat, async (req, res) { const { messages, model gpt-3.5-turbo } req.body; try { const response await axios.post( ${LLM_BASE_URL}/chat/completions, { model, messages }, { headers: { Content-Type: application/json, Authorization: Bearer ${LLM_API_KEY} }, timeout: 60000 // 流式对话可能耗时较长设 60 秒超时 } ); res.json(response.data); } catch (err) { console.error(LLM API error:, err.message); res.status(502).json({ error: LLM 服务异常请稍后重试 }); } }); app.listen(3000, () console.log(AI chat server on :3000));这个转发的意义在于第一API Key 只在服务端出现第二你可以在try/catch里捕获 LLM 服务的各种异常转换成小程序能识别的错误信息第三如果以后要接多个模型比如国内的大模型只需要在服务端加一个模型路由逻辑小程序端完全不用动。注意timeout: 60000——LLM 请求不是普通 API思考时间可能长达几十秒默认的 axios 超时5 秒会直接掐断请求。这是我见过最常见的翻车点之一。3.3 联调第一步跑通一个非流式对话服务端起好后先用 curl 验证服务端转发是否正常再回到小程序端联调。这个顺序能帮你快速定位问题出在前端还是后端。# 先测试服务端 curl -X POST http://localhost:3000/api/chat \ -H Content-Type: application/json \ -d {messages:[{role:user,content:你好}],model:gpt-3.5-turbo} # 正常的话会返回一个包含 choices[0].message.content 的 JSON返回结构大概是{ choices: [{ message: { content: 你好有什么可以帮你 } }] }。如果这里就报错问题在服务端环境变量或 API Key如果这里通了但小程序里收不到消息问题在wx.request的域名配置或请求头。小程序端的联调相对简单。打开pages/chat/chat.js把utils/request.js里的BASE_URL改成你的服务端地址在开发者工具里触发一次发送即可。开发者工具里勾了「不校验合法域名」的话本地联调不需要 HTTPS用http://localhost:3000就行。3.4 参数说明model、temperature、max_tokens 怎么设对接 LLM 时请求体里这三个参数是核心源码默认给了一组比较安全的初值但实际用下来需要根据自己的场景调整参数默认值说明与调整建议modelgpt-3.5-turbo低成本场景用 3.5 系需要复杂推理用 4.0 系。国内模型按服务商文档替换模型名temperature0.702。聊天助手用 0.71.0 比较自然知识问答、代码生成建议 0.20.3减少编造max_tokens1024控制单次回复长度。客户聊天用 512 足够文案生成可以开到 2048。注意它和输入 token 总和不能超过模型上下文上限有个容易被忽略的细节messages数组的长度直接决定你这次请求的 token 消耗。每多一轮对话history 数组就多两条消息user assistant上下文窗口是有限的后面第四章讲怎么截断。4. 核心逻辑拆解消息渲染、流式输出与上下文拼接4.1 消息列表的数据结构与渲染方案聊天页的数据结构是这份源码里最值得借鉴的部分。每条消息统一成对象用role区分谁发的后续扩展标记比如「AI 正在输入」也不破结构// pages/chat/chat.js 中的消息管理逻辑 Page({ data: { messages: [], // 消息数组每条消息 { role, content, loading } inputValue: , // 输入框当前内容 isSending: false, // 防止重复提交 scrollTop: 0 // 滚动位置消息更新时强制滚到底部 }, // 用户发送 - 推入 messages - 调 LLM - 返回后更新 async handleSend() { if (this.data.isSending) return; const content this.data.inputValue.trim(); if (!content) return; const userMsg { role: user, content, loading: false }; const aiMsg { role: assistant, content: , loading: true }; // 先更新界面再发起请求 this.setData({ messages: this.data.messages.concat([userMsg, aiMsg]), inputValue: , // 清空输入框 isSending: true, scrollTop: this.data.scrollTop 1000 }); try { const history this.buildHistory(this.data.messages); const reply await chatRequest({ messages: history }); this.setData({ messages: this.appendContent(this.data.messages, reply), isSending: false }); } catch (err) { // 请求失败时把 loading 消息替换成错误提示 this.setData({ messages: this.markError(this.data.messages), isSending: false }); } } });这里的布局思路是先给 AI 回复占个位loading: true请求回来再填充内容。这样用户在等待时能看到「AI 正在回复」的状态而不是干等。buildHistory负责从完整消息数组里抽出rolecontent的列表传给服务端——注意过滤掉loading字段和错误标记字段LLM API 只认{role, content}结构。渲染端用scroll-view包裹消息列表scroll-with-animation开起来消息更新时维护scrollTop实现自动滚动到底部。这里有个性能关键点如果消息很多wx:for渲染时一定要绑wx:key源码里用的是index——虽然性能不是最优但对聊天场景足够了因为单屏消息量一般不会超过几十条。4.2 流式输出WebSocket 还是普通 HTTP 请求非流式请求的体验有个硬伤模型生成完整回复可能要 1030 秒用户盯着一个转圈看半天体感很差。流式输出逐字或逐段返回能大幅改善体验但实现成本高不少。源码默认用的是非流式请求等完整结果一次返回。如果你想升级成流式体验有两条路路线一服务端换 SSEServer-Sent Events。微信小程序的wx.request不支持读取分块响应但wx.request的同门兄弟wx.downloadFile也不合适——更靠谱的做法是用 WebSocket。小程序原生支持wx.connectSocket服务端用 ws 库把 LLM 的流式输出转发给小程序。// 流式场景下的小程序端 WebSocket 封装 const socketTask wx.connectSocket({ url: wss://your-server.com/ws/chat, success: () console.log(Socket connected) }); socketTask.onMessage((res) { // 服务端把流式片段打包发给小程序 const chunk JSON.parse(res.data); const { delta } chunk; // delta.content 是这一片段的新增文本 if (delta delta.content) { this.setData({ messages: this.appendStreaming(this.data.messages, delta.content), scrollTop: this.data.scrollTop 1000 }); } });路线二前端模拟流式——在不改服务端的情况下等完整响应返回后让小程序端用定时器逐字吐出内容。这种用户体验是「伪流式」但胜在改动小适合快速验证产品需求。我一般建议先上非流式跑通业务再上 WebSocket 真流式一步到位反而容易混淆前后端问题。4.3 上下文管理history 拼接、token 截断与 system 指令多轮对话最核心的坑在 context。你发给 LLM 的messages数组越长消耗的 token 越多而且所有模型都有上下文窗口上限比如 8k、32k、128k 不等。超过上限请求直接报 400 错误。源码里给了一个基础方案保留最近 N 轮对话超过就丢弃最早的。// utils/token.js —— 简单的轮数截断策略 const MAX_TURNS 10; // 保留最近 10 轮user assistant 共 20 条 function buildHistory(messages) { // 过滤掉 loading 状态和错误消息 const clean messages.filter(m (m.role user || m.role assistant) m.content ); return clean.slice(-MAX_TURNS * 2); // 每轮 2 条取末尾 N 轮 }这个策略的问题在于它不是按 token 数量截断而是按轮数截断。中文对话每条消息 token 数差异很大——一句「好的」可能 2 个 token一段 500 字的分析可能 800 个 token。按轮数截断在高负载场景下可能超限每条都很长在轻负载下又浪费了可用的上下文窗口。更稳的做法是按 token 预算截断设定一个MAX_CONTEXT_TOKENS比如 3000从最新的消息开始往前翻累计超过预算就切掉更早的部分。服务端做这个计算更合适因为 API Key 在服务端token 计算工具也在服务端。如果你用国内的大模型它的 tokenizer 可能跟 OpenAI 的不一样需要找对应的计算库。还有一个容易漏掉的配置system消息。它不在对话历史里但会持续影响模型行为。源码默认没有配system但实际产品里基本都要配——比如设定 AI 的人设、禁止回答的范围、输出格式要求等。把system消息放在messages数组的第一位注意在截断时不要把system消息切掉。5. 避坑与常见问题上线前后最容易翻车的五个坑5.1 坑一iOS 上输入框被键盘顶飞现象Android 上聊天页正常iOS 上点击输入框后键盘弹起输入框被键盘遮住或页面整体错位。原因adjust-position属性默认是true键盘弹起时微信会自动把页面往上推但如果页面里用了position: fixed的底部输入栏自动推屏和 fixed 定位会打架。解决给input加adjust-position{{true}}同时给输入区容器设padding-bottom用bindfocus和bindblur事件拿到键盘高度动态补偿。源码里聊天页默认没加这个适配跑 iOS 真机时有大概率踩到。5.2 坑二流式输出时频繁 setData 导致页面卡顿现象用 WebSocket 流式输出时每个 chunk 都调setData更新消息文本AI 回复长一点页面直接卡成 PPT。原因setData的代价和传入的数据量成正比而且每次调用都会触发视图层 diff。流式场景每秒可能触发 1020 次setData每次都传整个messages数组性能瞬间被打满。解决节流合并。把 chunk 先累积在本地变量里每 100ms 批量推一次setData同时只更新变化的那条消息不要整个messages数组都传。this.setData({ [messages[ lastIndex ].content]: accumulatedText })这种路径更新语法性能差距在长对话场景下非常明显。5.3 坑三请求合法域名没配好真机调试直接 403现象开发工具里一切正常一上真机预览所有请求全部报http 403或fail url not in domain list。原因开发工具勾了「不校验合法域名」所以本地能跑真机上微信强制校验域名白名单。另外开发者工具默认用的是 HTTP真机要求 HTTPS。解决上线前在小程序后台「开发管理 → 开发设置 → 服务器域名」里加入request合法域名域名必须是 HTTPS 且已备案。注意每次改完域名配置要等几分钟生效而且小程序后台域名配置一个月只能改 50 次——改名次数要省着用。5.4 坑四token 超限报 400多轮对话越聊越「傻」现象聊天前几轮正常聊到第十轮左右突然报400 context_length_exceeded或者不再报错但 AI 开始「失忆」答非所问。原因上下文窗口被打满截断策略太粗糙。按轮数截断挡不住长消息更隐蔽的是把system消息一起截掉了导致模型丢掉了人设指令「失忆」就是这么来的。解决把截断逻辑挪到服务端按 token 预算截断而不是按条数并且单独保护system消息不被切掉。最好做一层「近 N 轮 预算截断」双保险——先按轮数砍掉太老的对话再在服务端按 token 数做二次裁剪。5.5 坑五把 API Key 明文写在小程序包里现象有些人图省事直接在chat.js里写const API_KEY sk-xxxx小程序打包发版后有人把 wxapkg 包解包就能看到明文密钥。原因小程序源码包在客户端可解包wx.request发出去的每个请求头、每个常量都暴露在用户的手机上。API Key 没有服务端保护等于裸奔。解决API Key 必须放服务端环境变量小程序端只通过你自己的服务端中转。服务端再加一层用户鉴权比如X-Auth-Token防止别人直接调你的转发接口白嫖。从设计上就不让 API Key 出现在任何客户端代码里这是底线。6. 进阶玩法把单轮聊天升级成带人设的智能助手源码默认的聊天页面是一个基础对话实现但真正上线的 AI 聊天产品很少直接原样裸奔——都要加一层人设包装。这里给一个通用做法在服务端的请求组装层加一个systemPrompt模板根据不同场景切换人设。// server/prompts.js —— 人设模板集中管理 const prompts { customer_service: 你是智聊客服性格耐心、专业。 规则 1. 只回答与产品相关的问题 2. 不确定的信息明确说需要核实不编造 3. 回复控制在 100 字以内。 , writing_assistant: 你是资深中文编辑擅长帮用户改写、润色、扩写文案。 每次回复先给修改后的文本再附一句修改说明。 }; // 组装请求时把 system 放在数组第一位 function buildMessages(userMessages, persona) { return [ { role: system, content: prompts[persona] || prompts.customer_service }, ...userMessages ]; }配合人设你还需要做两件收尾工作。第一在服务端把temperature参数按人设差异化。客服场景建议 0.3 以下减少模型自由发挥文案助手可以 0.8 左右保留创造力。这个参数是人设能不能立住的最大变量——很多「AI 说话怪怪的」的投诉根源都是 temperature 太高。第二做一层简单的敏感内容过滤。不要依赖 LLM 自己拒绝服务端在返回前过一道关键词和正则检查命中直接替换成预设话术。注意这不是价值观问题是产品风险控制——任何上线的 AI 聊天产品都绕不开这一层。最后说一个我自己踩过不少次的习惯从那以后每次联调 LLM我都强制走一遍「curl 测服务端 → 小程序发一句话 → 连续聊 10 轮看 token 消耗 → 换一个模型再跑一遍」这套流程。这四个步骤能覆盖 80% 以上的线上故障场景跑完再提测。这份源码帮你省掉了从零搭聊天页的功夫但真正的上线质量还是得靠自己的测试流程兜底希望帮到你。本文还有配套的精品资源点击获取