项目概述
本文介绍如何在微信小程序中集成 AI 对话功能,使用 Cogito-V1-Preview-Llama-3B 模型。核心流程为:用户输入 -> 小程序 -> 后端服务器 -> AI 模型 -> 返回结果。
1. 环境准备
在动手写代码之前,需理清整体流程。目标是用户在微信小程序里输入问题,小程序将问题发送给部署好的 AI 模型服务器,服务器处理完再把答案传回小程序显示给用户。
详细讲解了在微信小程序中集成 Cogito-V1-Preview-Llama-3B 模型实现 AI 对话功能的完整流程。内容包括后端 FastAPI 服务搭建、前端聊天界面开发、网络请求逻辑、对话历史管理以及针对延迟和上下文的优化方案。通过代理模式连接小程序与 AI 模型,解决了跨域通信、状态管理及用户体验问题,提供了可落地的代码示例与最佳实践。
本文介绍如何在微信小程序中集成 AI 对话功能,使用 Cogito-V1-Preview-Llama-3B 模型。核心流程为:用户输入 -> 小程序 -> 后端服务器 -> AI 模型 -> 返回结果。
在动手写代码之前,需理清整体流程。目标是用户在微信小程序里输入问题,小程序将问题发送给部署好的 AI 模型服务器,服务器处理完再把答案传回小程序显示给用户。
你需要准备的东西:
https://your-server-address/v1/chat/completions)。关于模型选择:Cogito-V1-Preview-Llama-3B 参数量是 30 亿,在轻量级模型里表现均衡,对话能力、逻辑推理都够用,生成速度也相对较快。最关键的是,它对服务器资源要求没那么高,部署和运行的成本更友好,非常适合作为小程序的后端服务。
后端的工作很简单,就是提供一个 API 接口。小程序发来一段对话内容,后端调用 Cogito 模型生成回复,然后再把回复传回去。这里用 Python 的 FastAPI 来写,因为它轻快,适合这种 IO 密集型的网络服务。
首先,确保你的服务器上已经部署好了模型,并且知道怎么用代码去调用它。假设模型服务本身已经在运行并提供了 API。
第一步,安装必要的包:
pip install fastapi uvicorn httpx
httpx 用来作为 HTTP 客户端,去请求我们部署的模型服务。
第二步,编写核心的后端 API: 我们在项目根目录创建一个 main.py 文件。
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import httpx
import json
from pydantic import BaseModel
from typing import List, Optional
# 定义请求体的数据模型,这决定了小程序要传什么数据过来
class ChatMessage(BaseModel):
role: str # 角色,比如 "user" 或 "assistant"
content: str # 消息内容
class ChatRequest(BaseModel):
messages: List[ChatMessage] # 对话历史列表
max_tokens: Optional[int] = 500 # 生成回复的最大长度,可选,给个默认值
# 初始化 FastAPI 应用
app = FastAPI(title="小程序 AI 对话后端")
# 关键步骤:配置 CORS(跨域资源共享)
# 因为小程序的前端域名和咱们后端域名不同,不配置这个,请求会被浏览器拦截。
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 在生产环境中,这里应该替换成你小程序的真实域名,更安全
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 这里替换成你部署在后端服务器上的 Cogito 模型 API 地址
MODEL_API_URL = "https://your-mirror-server-address/v1/chat/completions"
@app.post("/chat")
async def chat_with_ai(request: ChatRequest):
"""
处理小程序发来的聊天请求。
"""
# 准备请求给 AI 模型的数据
payload = {
"model": "cogito-v1-preview-llama-3b", # 指定模型名称
"messages": [msg.dict() for msg in request.messages],
"max_tokens": request.max_tokens,
"stream": False # 我们先处理非流式响应,更简单
}
async with httpx.AsyncClient(timeout=30.0) as client: # 设置一个较长的超时时间,因为生成文本可能需要一会儿
try:
# 将请求转发给真正的模型 API
response = await client.post(MODEL_API_URL, json=payload)
response.raise_for_status() # 如果请求失败(状态码不是 2xx),抛出异常
result = response.json() # 从模型返回的复杂结果中,提取出我们需要的纯文本回复
ai_reply = result["choices"][0]["message"]["content"]
return {"reply": ai_reply}
except httpx.RequestError as e: # 处理网络错误,比如连接不上模型服务器
raise HTTPException(status_code=503, detail=f"无法连接 AI 服务:{str(e)}")
except (KeyError, IndexError, json.JSONDecodeError) as e: # 处理模型返回的数据格式不符合预期的情况
raise HTTPException(status_code=500, detail=f"解析 AI 响应时出错:{str(e)}")
except Exception as e: # 捕获其他所有未知错误
raise HTTPException(status_code=500, detail=f"服务器内部错误:{str(e)}")
# 一个健康检查接口,用来测试后端是否正常启动
@app.get("/health")
async def health_check():
return {"status": "ok", "service": "mini-program-ai-backend"}
代码说明:
CORSMiddleware 是重中之重,没有它,小程序无法调用这个接口。/chat 接口,它接收包含对话历史的 messages。这样我们就能实现多轮对话,模型能根据之前的聊天记录来理解上下文。{"reply": "..."})传回去。try…except 包起来,给前端返回明确的错误信息,而不是直接崩溃。运行后端: 在服务器上,运行:
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
这样,你的后端服务就在 http://你的服务器 IP:8000 上跑起来了。可以用浏览器访问 http://你的服务器 IP:8000/health 测试一下。
后端准备好了,现在来打造小程序的前端。主要做两件事:做一个好看的聊天界面,以及编写逻辑去调用我们刚写好的后端接口。
我们创建一个聊天页面 pages/chat/chat。先看 chat.wxml,这是页面的结构。
<!-- pages/chat/chat.wxml -->
<view>
<!-- 聊天消息区域 -->
<scroll-view scroll-y scroll-into-view="{{'msg-' + (messageList.length - 1)}}" scroll-with-animation>
<block wx:for="{{messageList}}" wx:key="index">
<view>
<view>
<image wx:if="{{item.role === 'user'}}" src="/images/user-avatar.png"></image>
<image wx:else src="/images/ai-avatar.png"></image>
</view>
<view>
<text>{{item.content}}</text>
<!-- 加载指示器 -->
<view wx:if="{{item.role === 'assistant' && item.loading}}">
<text>.</text><text>.</text><text>.</text>
</view>
</view>
</view>
</block>
</scroll-view>
<!-- 底部输入区域 -->
<view>
<input value="{{inputValue}}" bindinput="onInput" placeholder="输入你的问题..." confirm-type="send" bindconfirm="sendMessage" focus="{{autoFocus}}" />
<button bindtap="sendMessage" disabled="{{isSending}}"> {{isSending ? '发送中' : '发送'}} </button>
</view>
</view>
为了让聊天界面看起来舒服,需要一些样式 chat.wxss:
/* pages/chat/chat.wxss */
.chat-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;
align-items: flex-start;
}
.message-item.user {
flex-direction: row-reverse;
}
.avatar image {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.bubble {
max-width: 65%;
padding: 20rpx 30rpx;
border-radius: 12rpx;
margin: 0 20rpx;
word-break: break-word;
line-height: 1.5;
}
.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);
}
.loading-dots text {
animation: blink 1.4s infinite;
font-size: 40rpx;
margin-right: 4rpx;
}
.loading-dots text:nth-child(2) {
animation-delay: 0.2s;
}
.loading-dots text:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes blink {
0%, 100% { opacity: 0.2; }
50% { opacity: 1; }
}
.input-area {
display: flex;
padding: 20rpx;
background-color: #fff;
border-top: 1rpx solid #eee;
align-items: center;
}
.input-box {
flex: 1;
padding: 20rpx 30rpx;
border: 1rpx solid #ddd;
border-radius: 40rpx;
margin-right: 20rpx;
font-size: 28rpx;
}
.send-btn {
background-color: #07c160;
color: white;
border-radius: 40rpx;
padding: 0 40rpx;
font-size: 28rpx;
}
.send-btn[disabled] {
background-color: #ccc;
}
界面有了,灵魂在 chat.js 里。这里处理用户输入、发送请求、管理对话历史。
// pages/chat/chat.js
// 这里填写你刚刚部署的后端服务器地址
const API_BASE_URL = 'https://your-backend-server.com';
Page({
data: {
inputValue: '', // 输入框的内容
messageList: [], // 所有的聊天消息
isSending: false, // 是否正在发送请求,用来防止重复点击
autoFocus: true, // 自动聚焦输入框
},
onLoad() {
// 页面加载时,可以尝试从本地缓存读取历史对话
const history = wx.getStorageSync('aiChatHistory');
if (history && Array.isArray(history)) {
this.setData({ messageList: history });
}
},
// 监听输入框变化
onInput(e) {
this.setData({ inputValue: e.detail.value });
},
// 发送消息的核心函数
async sendMessage() {
const { inputValue, messageList, isSending } = this.data;
if (!inputValue.trim() || isSending) {
return; // 空消息或正在发送时,不做任何事
}
// 1. 先把用户的消息显示在界面上
const userMessage = { role: 'user', content: inputValue };
const newList = [...messageList, userMessage];
this.setData({
messageList: newList,
inputValue: '', // 清空输入框
isSending: true,
});
// 2. 在界面上添加一个'AI 正在思考'的占位消息
const thinkingMessage = { role: 'assistant', content: '', loading: true };
this.setData({ messageList: [...newList, thinkingMessage] });
// 3. 准备请求数据:发送整个对话历史,让 AI 知道上下文
const requestMessages = newList.map(msg => ({ role: msg.role, content: msg.content }));
try {
// 4. 调用我们自己的后端接口
const response = await new Promise((resolve, reject) => {
wx.request({
url: `${API_BASE_URL}/chat`, // 你的后端/chat 接口
method: 'POST',
data: {
messages: requestMessages,
max_tokens: 300 // 控制回复长度
},
header: {
'content-type': 'application/json'
},
success: resolve,
fail: reject
});
});
if (response.statusCode === 200) {
// 5. 请求成功,用 AI 的回复替换掉'正在思考'的占位消息
const aiReply = response.data.reply;
const finalList = [...newList];
finalList.pop(); // 移除 loading 占位
finalList.push({ role: 'assistant', content: aiReply });
this.setData({ messageList: finalList });
// 保存到本地缓存
wx.setStorageSync('aiChatHistory', finalList);
} else {
// 处理后端返回的业务错误(如状态码 400, 500 等)
throw new Error(`请求失败:${response.statusCode}`);
}
} catch (error) {
// 6. 处理网络错误或请求异常
console.error('发送消息失败:', error);
wx.showToast({
title: '网络好像不太给力,请稍后再试',
icon: 'none'
});
// 出错时,移除'正在思考'的占位消息
const finalList = [...newList];
finalList.pop();
this.setData({ messageList: finalList });
} finally {
// 7. 无论成功失败,都重置发送状态
this.setData({ isSending: false });
}
},
})
关键点解析:
messageList 传给后端。这样模型就能看到之前所有的对话,实现连贯的多轮聊天。同时,我们把历史记录在本地 (wx.setStorageSync),用户下次打开小程序还能看到。try…catch 包裹网络请求。如果失败,给用户一个友好的提示(而不是一堆错误代码),并清理界面上的加载状态。把基础功能跑通只是第一步,真要上线,还得解决一些实际体验问题。
AI 生成文本需要时间,尤其是网络慢的时候。如果用户点了发送,界面毫无反应好几秒,他很可能以为程序坏了,会反复点击。我们的方案是前面提到的'立即显示 + 加载动画'。在 sendMessage 函数里,先更新界面,再发起网络请求。这个视觉反馈至关重要。
模型能处理的上下文长度是有限的(比如 4096 个 token)。如果聊天历史太长,要么会出错,要么会丢失最早的信息。 优化方案: 在后端 /chat 接口处理请求之前,可以对传入的 messages 列表做一个裁剪。
# 在 main.py 的 chat_with_ai 函数中,添加一个裁剪历史的函数
def trim_messages(messages: List[ChatMessage], max_history_turns: int = 10) -> List[ChatMessage]:
"""
保留最近 N 轮对话,并确保总 token 数不会太长(这里简化处理,按轮次裁剪)。
更精细的做法是计算 token 数,但需要模型对应的 tokenizer。
"""
# 简单策略:只保留最近 max_history_turns 轮对话。
# 通常保留用户和 AI 的最近几次交替发言即可。
if len(messages) <= max_history_turns * 2: # 假设一轮包含用户和 AI 各一条消息
return messages
return messages[-(max_history_turns * 2):]
# 在 chat_with_ai 函数中调用
@app.post("/chat")
async def chat_with_ai(request: ChatRequest):
trimmed_messages = trim_messages(request.messages, max_history_turns=5)
payload = {
"model": "cogito-v1-preview-llama-3b",
"messages": [msg.dict() for msg in trimmed_messages], # 使用裁剪后的历史
# ... 其他参数
}
# ... 后续请求逻辑
这样,无论用户聊了多久,我们只把最近 5 轮对话(10 条消息)发给模型,既能维持一定的上下文,又不会超限。
直接从模型生成的回复,有时可能不符合我们的要求,或者包含我们不希望出现的内容。 优化方案: 在后端返回给小程序之前,对 AI 的回复进行一次'后处理'。
# 在 main.py 中,添加一个后处理函数
def postprocess_reply(text: str) -> str:
"""
对模型生成的回复进行后处理。
1. 过滤敏感词。
2. 确保回复格式友好(如去掉多余的空行)。
3. 如果回复太短或无意义,可以返回一个默认提示。
"""
# 示例:简单的敏感词过滤(实际应用需要更完善的词库)
sensitive_words = ["暴力", "仇恨"] # 示例词库,请根据实际情况扩充
for word in sensitive_words:
if word in text:
text = text.replace(word, "**")
# 示例:如果回复过短,可能是模型没理解,提示用户重新提问
if len(text.strip()) < 5:
return "我好像没太明白你的意思,能换个方式问问吗?"
# 整理格式
text = text.strip()
return text
# 在 chat_with_ai 函数中,提取回复后调用
@app.post("/chat")
async def chat_with_ai(request: ChatRequest):
# ... 前面的请求和获取 ai_reply 的代码 ...
processed_reply = postprocess_reply(ai_reply)
return {"reply": processed_reply}
这是一个简单的示例,真实场景可能需要接入更专业的内容审核 API。
走完这一趟,你会发现,在微信小程序里集成一个像 Cogito-V1-Preview-Llama-3B 这样的 AI 对话功能,核心思路就是'前端收集对话,后端转发处理'。技术难点不在于算法本身,而在于如何在小程序的框架下,做好网络通信、状态管理和用户体验。
这套方案跑起来后,效果还是挺不错的。对话比较流畅,响应速度也能接受。当然,在实际运营中可能会遇到新问题,比如流量大了服务器压力怎么办,回复内容如何更精准地控制。这些问题都可以在现有框架上继续优化,比如给后端加个缓存,或者对不同的用户提问做更细致的分类处理。
如果你正准备做类似的功能,建议你先按这个流程把最小可用的版本搭起来,让对话能跑通。然后再根据你的具体业务需求,去添加历史记录清空、多主题对话、语音输入等更酷的功能。最重要的是,多从用户角度想想,怎么让这个 AI 助手用起来更自然、更贴心。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online