微信小程序连接大模型开发一个集成通义千问的智能聊天小程序最近在捣鼓一些有意思的小项目想着能不能把现在很火的大模型能力塞进我们天天都在用的微信小程序里。比如做个智能客服助手、个人知识库问答或者就是个能聊天的AI伙伴应该挺有意思的。说干就干我花了点时间把通义千问的模型能力接入了微信小程序做了一个能实时对话的智能聊天应用。整个过程下来感觉并没有想象中那么复杂核心就是小程序端做好界面和交互后端处理好模型调用和API转发。这篇文章我就把从零开始搭建这个“小程序大模型”应用的完整过程以及其中遇到的一些坑和解决方案跟大家详细分享一下。如果你也想给自己的小程序加点AI智能希望这篇内容能给你带来一些实用的参考。1. 整体思路与准备工作在动手写代码之前我们先理清楚整个项目是怎么跑起来的。简单来说就是“前后端分离”的模式微信小程序作为前端负责展示界面和收集用户输入我们还需要一个后端服务器它充当中间人的角色接收小程序的请求再去调用通义千问的官方API拿到AI的回复后再返回给小程序。为什么不能小程序直接调大模型API呢主要是两个原因安全和密钥管理。大模型服务的API Key是非常敏感的信息绝对不能放在小程序的前端代码里否则很容易被别人扒走。所以我们必须通过自己的后端服务器来中转把密钥安全地保存在服务器端。为此我们需要准备以下几样东西一个通义千问的API密钥去对应的云服务平台申请这是调用模型能力的通行证。一台后端服务器用来部署我们的中转API服务。可以选择常见的云服务器或者一些Serverless服务能运行Python或Node.js环境就行。注册一个微信小程序在微信公众平台注册拿到小程序的AppID这是开发的必备条件。本地开发环境安装微信开发者工具以及后端的开发环境比如Python的Flask/Django框架或者Node.js的Express/Koa框架。我这次选择的技术栈是小程序端用原生的JavaScript/微信小程序语法后端用Python的FastAPI框架因为它轻量、异步支持好适合处理这种聊天请求。数据库暂时没用到因为是个简单的对话demo如果你需要记录历史对话可以加上MySQL或MongoDB。2. 后端API服务搭建后端是我们的核心中转站它的任务很明确提供一个安全的接口给小程序调用然后它自己再去请求通义千问最后把结果整理好返回。2.1 项目初始化与依赖安装首先我们在服务器上创建一个新的项目目录然后初始化Python环境。我强烈建议使用虚拟环境来管理依赖避免包冲突。# 创建项目目录并进入 mkdir wechat-ai-assistant cd wechat-ai-assistant # 创建虚拟环境以venv为例 python -m venv venv # 激活虚拟环境 # 在Windows上 venv\Scripts\activate # 在Mac/Linux上 source venv/bin/activate # 安装必要的包 pip install fastapi uvicorn httpx python-dotenv这里简单说明一下这几个包的作用fastapi和uvicorn用来快速创建高性能的Web API服务。httpx一个现代化的HTTP客户端我们将用它来请求通义千问的API它比传统的requests库对异步支持更好。python-dotenv用来管理环境变量比如我们的API密钥就不会硬编码在代码里。2.2 核心API接口实现接下来我们创建主要的应用文件比如叫main.py。在这个文件里我们要做几件事创建FastAPI应用、设置跨域因为小程序要访问、实现一个处理对话的接口。# main.py import os from typing import Optional from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware import httpx from pydantic import BaseModel from dotenv import load_dotenv # 加载环境变量从同目录下的 .env 文件读取 load_dotenv() app FastAPI(title微信小程序AI助手后端API) # 配置跨域允许微信小程序的域名访问 # 注意上线前需要根据小程序的实际域名进行精确配置 app.add_middleware( CORSMiddleware, allow_origins[*], # 开发阶段可以用*生产环境务必替换为具体域名 allow_credentialsTrue, allow_methods[*], allow_headers[*], ) # 从环境变量获取通义千问的API密钥和基础URL API_KEY os.getenv(QWEN_API_KEY) BASE_URL os.getenv(QWEN_BASE_URL, https://dashscope.aliyuncs.com/compatible-mode/v1) # 示例URL请替换为实际 if not API_KEY: raise ValueError(请在 .env 文件中设置 QWEN_API_KEY 环境变量) # 定义请求数据模型 class ChatRequest(BaseModel): message: str # 用户发送的消息 conversation_id: Optional[str] None # 可选用于多轮对话会话管理 # 定义响应数据模型 class ChatResponse(BaseModel): reply: str # AI的回复 conversation_id: Optional[str] None # 返回会话ID便于后续延续对话 app.post(/api/chat, response_modelChatResponse) async def chat_with_ai(request: ChatRequest): 处理小程序发来的聊天请求转发至通义千问API并返回结果。 if not request.message or request.message.strip() : raise HTTPException(status_code400, detail消息内容不能为空) # 准备请求通义千问API的头部和数据 headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } # 这里需要根据通义千问API的实际要求构造请求体 # 以下是一个示例结构具体字段请查阅官方文档 payload { model: qwen-max, # 指定模型例如 qwen-max, qwen-plus等 input: { messages: [ {role: user, content: request.message} ] }, parameters: { result_format: message # 指定返回格式 } } async with httpx.AsyncClient(timeout30.0) as client: # 设置一个较长的超时时间 try: # 发送请求到通义千问API resp await client.post( f{BASE_URL}/chat/completions, # 具体的API端点路径 headersheaders, jsonpayload ) resp.raise_for_status() # 如果响应状态码不是2xx抛出异常 result resp.json() # 解析通义千问API的返回结果提取AI回复文本 # 这里的解析逻辑需要根据API返回的实际JSON结构进行调整 ai_reply result.get(output, {}).get(choices, [{}])[0].get(message, {}).get(content, 抱歉我暂时无法理解。) # 简单处理如果返回内容为空给个默认回复 if not ai_reply or ai_reply.strip() : ai_reply 我已收到你的消息但回复内容为空。 return ChatResponse(replyai_reply, conversation_idrequest.conversation_id) except httpx.RequestError as e: # 处理网络请求错误 raise HTTPException(status_code500, detailf请求模型服务时发生网络错误: {str(e)}) except httpx.HTTPStatusError as e: # 处理API返回的错误状态码如鉴权失败、额度不足等 raise HTTPException(status_codee.response.status_code, detailf模型服务返回错误: {e.response.text}) except (KeyError, IndexError) as e: # 处理响应数据解析错误 raise HTTPException(status_code502, detailf解析模型服务响应时出错: {str(e)}) if __name__ __main__: # 使用uvicorn启动服务指定主机和端口 import uvicorn uvicorn.run(app, host0.0.0.0, port8000)代码看起来有点长但逻辑是清晰的。我们创建了一个/api/chat的接口它接收用户消息然后带着我们的API密钥去问通义千问拿到回答后再原路返回。这里特别要注意错误处理网络请求、API错误、数据解析错误都要考虑到给小程序一个明确的错误信息。2.3 环境配置与运行我们还需要一个.env文件来保存密钥切记不要把这个文件提交到Git等代码仓库# .env QWEN_API_KEY你的通义千问API密钥在这里 QWEN_BASE_URLhttps://dashscope.aliyuncs.com/compatible-mode/v1现在在项目根目录下运行我们的后端服务python main.py如果一切正常你会看到服务启动在http://0.0.0.0:8000。你可以用浏览器访问http://localhost:8000/docs这是FastAPI自动生成的交互式API文档可以在这里测试/api/chat接口是否工作正常。3. 微信小程序前端开发后端跑起来了接下来就是打造小程序的界面了。打开微信开发者工具新建一个项目填入你的AppID。3.1 项目结构与页面布局小程序的基本结构我们就不赘述了主要关注聊天页面。假设我们有一个主要的聊天页面pages/chat/chat。首先设计chat.wxml文件这是我们的页面结构!-- pages/chat/chat.wxml -- view classcontainer !-- 聊天消息列表区域 -- scroll-view classmessage-list scroll-y scroll-into-view{{scrollToView}} scroll-with-animation block wx:for{{messages}} wx:keyindex view classmessage-item {{item.role}} view classavatar image wx:if{{item.role user}} src/images/user-avatar.png/image image wx:if{{item.role assistant}} src/images/ai-avatar.png/image /view view classbubble text classcontent{{item.content}}/text /view /view /block !-- 加载指示器 -- view wx:if{{isLoading}} classmessage-item assistant view classavatar image src/images/ai-avatar.png/image /view view classbubble loading text正在思考.../text /view /view /scroll-view !-- 底部输入区域 -- view classinput-area input classinput-box placeholder请输入您的问题... value{{inputValue}} bindinputonInput bindconfirmsendMessage focus{{autoFocus}} / button classsend-btn bindtapsendMessage disabled{{isLoading || !inputValue.trim()}}发送/button /view /view然后在chat.wxss中加上样式让界面看起来像个聊天软件/* pages/chat/chat.wxss */ .container { height: 100vh; display: flex; flex-direction: column; background-color: #f5f5f5; } .message-list { flex: 1; padding: 20rpx; box-sizing: border-box; overflow: auto; } .message-item { display: flex; margin-bottom: 30rpx; } .message-item.user { flex-direction: row-reverse; } .message-item.assistant { flex-direction: row; } .avatar image { width: 80rpx; height: 80rpx; border-radius: 50%; } .bubble { max-width: 500rpx; padding: 20rpx; border-radius: 10rpx; margin: 0 20rpx; word-break: break-word; } .user .bubble { background-color: #95ec69; color: #000; } .assistant .bubble { background-color: #fff; color: #333; box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1); } .bubble.loading text { color: #999; font-style: italic; } .input-area { display: flex; align-items: center; padding: 20rpx; background-color: #fff; border-top: 1rpx solid #eee; } .input-box { flex: 1; height: 80rpx; padding: 0 20rpx; border: 1rpx solid #ddd; border-radius: 40rpx; margin-right: 20rpx; } .send-btn { width: 140rpx; height: 80rpx; line-height: 80rpx; border-radius: 40rpx; background-color: #07c160; color: #fff; font-size: 28rpx; } .send-btn[disabled] { background-color: #ccc; color: #999; }3.2 页面逻辑与API调用界面有了灵魂在于逻辑。我们来看chat.js// pages/chat/chat.js // 这里填写你刚刚启动的后端API地址本地测试时可能是局域网IP上线后需改为正式域名 const API_BASE_URL http://192.168.1.100:8000; // 请替换为你的实际后端地址 Page({ data: { messages: [], // 聊天记录数组格式如 [{role: user, content: 你好}, {role: assistant, content: 你好}] inputValue: , // 输入框内容 isLoading: false, // 是否正在加载AI回复 scrollToView: , // 用于控制滚动到底部的视图ID autoFocus: true // 自动聚焦输入框 }, onLoad: function() { // 页面加载时可以尝试从本地缓存读取历史记录 const history wx.getStorageSync(chatHistory); if (history Array.isArray(history)) { this.setData({ messages: history }); this.scrollToBottom(); } }, // 输入框内容变化 onInput: function(e) { this.setData({ inputValue: e.detail.value }); }, // 发送消息 sendMessage: function() { const userMessage this.data.inputValue.trim(); if (!userMessage || this.data.isLoading) { return; } // 1. 清空输入框并将用户消息添加到界面 this.setData({ inputValue: , messages: [...this.data.messages, { role: user, content: userMessage }], isLoading: true }, () { this.scrollToBottom(); // 确保滚动到最新消息 }); // 2. 调用后端API获取AI回复 wx.request({ url: ${API_BASE_URL}/api/chat, method: POST, header: { content-type: application/json }, data: { message: userMessage // 如果需要多轮对话可以在这里传入 conversation_id }, success: (res) { if (res.statusCode 200 res.data.reply) { // 成功收到回复添加到消息列表 this.setData({ messages: [...this.data.messages, { role: assistant, content: res.data.reply }], isLoading: false }, () { this.scrollToBottom(); this.saveHistory(); // 保存到本地历史 }); } else { // 处理API返回的业务错误 this.showError(获取回复失败 (res.data.detail || 未知错误)); } }, fail: (err) { // 处理网络请求失败 console.error(请求失败:, err); this.showError(网络请求失败请检查后端服务是否运行及地址是否正确。); } }); }, // 滚动到底部 scrollToBottom: function() { // 通过设置scroll-into-view到最后一个消息的id来实现滚动 // 这里简单使用消息索引作为id const lastIndex this.data.messages.length - 1; if (lastIndex 0) { // 给最后一条消息设置一个id这里用索引模拟 // 注意在实际的wxml中需要动态设置id这里是一种简化实现思路。 // 更优做法是在渲染消息时使用 idmsg{{index}} this.setData({ scrollToView: msg${lastIndex} }); } }, // 显示错误提示 showError: function(msg) { wx.showToast({ title: msg, icon: none, duration: 3000 }); this.setData({ isLoading: false }); }, // 保存聊天记录到本地缓存 saveHistory: function() { // 为了防止缓存过大可以只保存最近N条记录 const historyToSave this.data.messages.slice(-50); // 保存最近50条 wx.setStorageSync(chatHistory, historyToSave); }, // 清空聊天记录 clearHistory: function() { wx.showModal({ title: 提示, content: 确定要清空聊天记录吗, success: (res) { if (res.confirm) { this.setData({ messages: [] }); wx.removeStorageSync(chatHistory); wx.showToast({ title: 已清空, icon: success }); } } }); } })为了让滚动到底部更精确我们稍微修改一下chat.wxml中消息的渲染部分给每条消息加上动态ID!-- 修改消息渲染部分 -- block wx:for{{messages}} wx:keyindex view idmsg{{index}} classmessage-item {{item.role}} !-- ... 头像和气泡内容保持不变 ... -- /view /block3.3 配置网络请求域名微信小程序对网络请求有严格的安全限制只能访问事先在后台配置过的域名。所以在开发阶段我们可以在微信开发者工具的“详情”-“本地设置”中勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”以便用本地IP地址进行调试。但是小程序正式上线前必须完成以下配置将后端API服务部署到拥有HTTPS域名https://的服务器上。登录微信公众平台进入你的小程序管理后台。在“开发”-“开发管理”-“开发设置”-“服务器域名”中将你的后端API域名如https://api.yourdomain.com添加到request合法域名列表中。4. 功能扩展与优化建议一个最基础的聊天功能已经完成了。但要让这个小程序更实用、更健壮我们还可以做很多事。4.1 实现连续对话会话管理上面的例子是“一问一答”没有上下文关联。要让AI记住之前的对话我们需要在后端维护一个“会话”。简单的方法是小程序在第一次请求时生成一个唯一的conversation_id并保存之后每次请求都带上这个ID。后端根据这个ID在请求大模型API时将之前的历史消息也一并发送过去。这需要后端引入一个临时存储比如Redis来保存会话历史或者将历史直接传递给模型API如果API支持上下文传递。4.2 增加流式输出打字机效果目前我们是等AI完全生成完所有文本后一次性返回并显示。这样如果回答很长用户需要等待较长时间。更好的体验是“流式输出”即AI生成一个字就返回一个字像打字机一样实时显示在小程序上。这需要后端和模型API都支持Server-Sent Events (SSE) 或 WebSocket。后端以流的方式从模型API接收数据并同样以流的形式推送给小程序。小程序端则需要使用wx.connectSocket来建立WebSocket连接或者处理分块传输的HTTP响应。4.3 完善错误处理与用户体验网络状态提示检测网络断开时给出友好提示。消息重发机制发送失败的消息允许用户点击重发。加载动画发送中和接收中可以有更丰富的动画效果。消息复制与分享长按消息气泡可以复制AI回复的内容。4.4 后端安全与性能加固接口鉴权为你的后端API增加简单的鉴权比如小程序AppSecret验证防止被他人滥用。请求限流防止单个用户恶意刷接口保护你的模型API额度。敏感词过滤在后端对用户输入和AI输出进行基本的合规性检查。服务监控与日志记录请求日志便于排查问题和分析使用情况。5. 总结走完这一趟你会发现把大模型能力集成到微信小程序里技术路径是清晰的。核心就是构建一个安全可靠的后端桥梁然后在小程序端做好交互。这个过程里调试和配置可能会花些时间尤其是网络域名、跨域这些细节问题。我把自己这个demo跑起来后感觉还是挺有成就感的。它不仅仅是一个玩具你可以基于这个框架扩展出很多实用的功能。比如加上语音输入输出就变成了语音助手对接特定的知识库就能做成垂直领域的智能问答工具结合小程序的拍照功能还能玩出“拍照问AI”的花样。开发过程中多查阅微信小程序官方文档和通义千问的API文档大部分问题都能找到答案。希望这个从零开始的案例能帮你打开思路。动手试试吧从最简单的对话开始一步步添加你想要的功能这个过程本身就是一个很好的学习体验。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。