☰
本地大模型接入微信全攻略:架构设计与踩坑修复指南
2026/9/26 5:10:31 网站建设 项目流程

我先说结论:这件事能做成,而且做成了之后,你手里相当于多了一个随身携带、数据完全不出本机的 AI 秘书。但你要是照着网上那些教程直接抄,大概率会在登录、回调、上下文、内存这几个环节逐个翻车。我前后折腾了三个晚上,把所有能踩的坑都踩了一遍,这篇文章把我最终的方案、源码思路和每一处翻车的修复链路完整写出来,你可以直接照着搭。

先说清楚我到底做了什么:把本地部署的大模型(Ollama 拉起的那种),通过一套消息中转服务接入微信,让微信变成大模型的对话入口。你在微信里给机器人发消息,它能带着上下文连续对话,而且完全不用把对话内容发到任何云端 API。适合自用、小团队内部用、或者单纯想折腾的人。如果你是第一次接触“本地 AI 接入微信”这个概念,那么这篇文章就是为你准备的。

1. 先泼一盆冷水:本地模型接入微信,到底图什么

很多人一听到“接入微信”第一反应是做营销机器人,满群自动回复、关键词触发、广告推送。我先把这个想法掐掉:个人微信的自动化操作一直处于灰色地带,轻则掉线重则限制,我做完之后也只敢在自己的号和几个测试群里跑。真正有价值的场景是下面这几个:

  • 你在外面用手机,随时给本地模型丢一段文字,让它总结、翻译、写邮件草稿,云端那些服务你一个都不需要。
  • 你不想把聊天记录、私有数据发给任何一个外部接口。本地部署的大模型保证对话内容只在你的机器里。
  • 团队内部搭一个共享的 AI 助手,成员用微信就能访问内网知识库或文档检索,不需要每个人都去学命令行。

所以,这个项目图的是“私密、可控、随时可用”。如果你要的是营销裂变、自动加粉,请到此为止,别往下看了。技术本身是中性的,但用法必须自己想清楚。

1.1 三种方案对比:个人微信、公众号、企业微信

我在动手前先对比了三条路,每条路都有致命缺点,选错了后面全是坑。

方案一:个人微信 + Hook 框架

这是网上讨论最多、最“酷”的方案。通过 Hook 微信进程或者拦截协议,监听收到的消息,调用本地模型,再自动回复。优点是完全模拟真人,可以在任意群、任意联系人之间对话,周围人看不出是机器人。

缺点是稳定性差,微信一升级可能就失效;登录扫码有时候会被风控;自动化程度高的时候有封号风险。我自己实测下来,纯自用、消息量不大,短期内没问题,但如果拿去做营销,大概率活不过一周。技术选型上,这个方案在 Node.js 生态里首选 Wechaty,配合不同的 puppet(协议实现)使用。我后面用的就是这条路。

方案二:微信公众号(服务号/订阅号)

公众号有官方 API,消息收发稳定、合规,官方给你发消息的接口,你只需要一个公网服务器接收事件推送。缺点是公众号的本质是“粉丝给号主留言”,不是普通聊天;而且服务号每个月群发次数有限制,订阅号的消息被动回复交互还行,但主动推送能力很弱。更麻烦的是,你想让 AI 主动说点什么也很别扭。

公众号适合做对外客服、内容订阅,不适合做“随时问一嘴”的个人助理。而且如果你只是自己用,申请公众号还需要审核,太慢了。

方案三:企业微信

企业微信有完备的 API 接收消息,且支持机器人 webhook,合规性最好。如果你公司已经用了企业微信,这是最稳妥的企业内部 AI 助手落地方案。缺点是它毕竟不是个人微信,你没法用自己平时聊天的微信号跟它说话;另外消息回调需要做签名验证、并且字段结构比个人微信复杂不少。

三种方案放在一起,结论很明显:自用且追求体验,选个人微信;对外服务,选公众号;企业内部,选企业微信。

1.2 我的最终选型

我最后选了“个人微信 + Wechaty + Ollama 本地大模型”的组合。理由很简单:

  • Wechaty 的 API 设计对消息监听、发消息、群聊处理很友好,几行代码就能跑通收发。
  • Ollama 是目前本地部署大模型最省心的工具,一条命令ollama run qwen2.5:7b就能把模型跑起来,还暴露了 HTTP API,方便程序调用。
  • 整个链路的私密性最强,所有请求都发生在我的机器上。

这个组合踩坑最多,但也最值得写。

2. 架构先行:微信、消息管道、模型层怎么分工

很多新人犯的错是一上来就对着 Wechaty 写回调函数,结果联动、上下文、超时全混在一起,代码越写越乱。我建议先画清楚链路。不要指望有一个大函数把所有事干完,分布式拆开,后面排查问题会省很多时间。

整个链路由三层组成:

  1. 接入层:Wechaty 监听微信消息,拿到消息文本、发消息的人、所在群。
  2. 调度层:一个本地 Node.js 服务,负责维护每个会话的上下文、调用模型、处理并发和重试。
  3. 模型层:Ollama 服务,负责任何真正消耗算力的推理工作。

消息走的方向是:微信消息 -> Wechaty 回调 -> 调度层组装上下文 -> 请求 Ollama -> 得到回复 -> 调度层把回复交给 Wechaty -> 发回微信。

为什么中间要插一个调度层?因为 Wechaty 的回调是串行事件流,但模型推理是耗时的同步操作。如果你在回调里直接“同步等模型返回”,模型推理 30 秒,微信端的消息就卡住 30 秒,期间任何新消息都会被阻塞。更严重的是,如果同时有三个人发消息,三个人会抢同一个回调上下文,回错人就发生了。这也是我后面踩到的最大的坑。

2.1 为什么要用“会话 ID”隔离

微信里区分一个对话非常容易:如果是私聊,用联系人的 wxid 作为会话 ID;如果是群聊,用群 ID。每一次收到消息,调度层就根据会话 ID 取出该会话的历史记录,拼上这条新消息,丢给大模型,得出回复之后把这段问答追加回历史记录。

这就是经典的“多会话上下文隔离”。

实现上我维护了一个内存里的 Map:

  • key 是会话 ID
  • value 是一个数组,保存最近 N 轮对话
  • 内存有上限,超过后做一次滚动丢弃

这样每个人、每个群都有自己的记忆,互不干扰。

2.2 在线、离线消息与排队策略

微信消息是异步到达的,模型推理是串行资源。我的做法是给每个会话建一个 FIFO 队列,消息来了先入队,调度层按顺序处理。同一个会话的多条消息严格串行,避免乱序;不同会话之间轮询处理,避免一个人刷屏把别人阻塞。

排队还有个好处:如果模型正在跑一个长问题,其他消息不会直接丢失,而是排队等待。这样用户体验比直接丢消息好得多。你会看到微信端偶尔延迟几秒回复,但至少不会吞消息。

3. 保姆级部署:从空机器到回复第一句话

假设你现在手里有一台 Mac 或 Linux 机器,Windows 也能跑,但会遇到更多奇奇怪怪的问题(后面坑里说)。下面是从零到通的完整步骤。

3.1 环境准备

我们需要三样东西:Node.js 18+、Python 3.10+(可选,主要是后处理脚本)、Ollama。

安装 Node.js 我用的是 nvm,避免系统权限问题:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install 18 nvm use 18

安装并启动 Ollama:

curl -fsSL https://ollama.com/install.sh | sh ollama serve

然后拉一个模型。个人自用我推荐qwen2.5:7b,中文能力强、显存要求不高,16G 内存的 Mac 都能跑得很顺。

ollama pull qwen2.5:7b

如果你显卡是 8G 显存,可以试试qwen2.5:3b,速度更快但聪明程度明显下降。机器差就别勉强,7B 是体验和资源的最佳平衡点。

验证 Ollama 是否就绪,可以直接敲命令:

curl http://localhost:11434/api/generate \ -d '{"model": "qwen2.5:7b", "prompt": "你好", "stream": false}'

能返回 JSON 就说明模型层通了。

3.2 初始化 Node 项目和安装依赖

mkdir local-wechat-ai cd local-wechat-ai npm init -y npm install wechaty npm install qrcode-terminal

Wechaty 的安装有时候会因为网络问题卡住,国内环境建议先配好 npm 镜像。装好之后写一个最小的启动脚本,只做一件事:登录并打印二维码。

// step1-login.js const { WechatyBuilder } = require('wechaty'); const qrcodeTerminal = require('qrcode-terminal'); const bot = WechatyBuilder.build({ name: 'local-ai-bot', }); bot.on('scan', (qrcode, status) => { qrcodeTerminal.generate(qrcode, { small: true }); console.log('请使用微信扫码登录'); }); bot.on('login', (user) => { console.log(`登录成功:${user}`); }); bot.on('logout', (user) => { console.log(`退出登录:${user}`); }); bot.start() .then(() => console.log('启动中,等待扫码...')) .catch(console.error);

运行node step1-login.js,终端里会输出二维码。手机微信扫一下,终端显示登录成功,第一步就通了。

这一步如果卡住,那你接下来会进入这个项目最折磨人的阶段:登录不了。这个问题我单独放在后面的神坑章节里讲。

3.3 把“收到消息”和“调用模型”接起来

登录通了的下一步,就是把收到消息时的回调接到 Ollama。先写一个最简版本,不处理上下文,只做单轮问答:

const { WechatyBuilder } = require('wechaty'); const axios = require('axios'); const bot = WechatyBuilder.build({ name: 'local-ai-bot' }); async function askLocalModel(prompt) { const resp = await axios.post('http://127.0.0.1:11434/api/generate', { model: 'qwen2.5:7b', prompt, stream: false, }); return resp.data.response; } bot.on('message', async (message) => { if (message.self()) return; if (message.type() !== bot.Message.Type.Text) return; const text = message.text().trim(); if (!text) return; try { const reply = await askLocalModel(text); await message.say(reply); } catch (e) { console.error('调用失败', e); await message.say('本地模型暂时抽风了,请稍后再试'); } }); bot.start();

跑起来之后,你用微信给机器人发一句“你好”,它大概率会回你一句自我介绍。到这里,最简链路已经通了,剩下所有工作都是让它更好用、更稳定、更不易翻车。

4. 核心源码逐段拆解:别抄了就跑

上面那段代码能跑,但只能单轮对话,没有任何上下文记忆,也容易并发错乱。下面是我最终在用的核心代码框架,我会拆开讲每一段都在干什么。

4.1 消息监听:只处理要处理的

微信里的消息类型很杂:文本、图片、语音、视频、文件、位置、名片。本地大模型擅长处理文本,图片需要额外走视觉模型或多模态流程,语音要先转文字,视频就直接忽略。

我的策略很简单:只处理Text类型,以及把语音当作文本来处理需要额外换模型,暂时不考虑。图片消息可以选择把路径传给支持视觉的模型(比如qwen2.5-vl),但这个对显存要求高,属于进阶玩法。

另外,必须过滤掉自己发的消息,否则机器人的回复会再次触发回调,形成“我回我,我又回我”的死循环。这个细节最容易被忽视,一旦出事日志会像刷屏一样。

bot.on('message', async (message) => { if (message.self()) return; const room = message.room(); const contact = message.talker(); const text = message.text(); if (message.type() !== bot.Message.Type.Text) { console.log(`忽略非文本消息, type=${message.type()}`); return; } const sessionId = room ? `room:${room.id}` : `contact:${contact.id}`; console.log(`[${sessionId}] 收到: ${text}`); enqueueMessage(sessionId, text); });

这里有个很关键的取舍:群聊里收到的消息,不仅包含机器人被 @ 的情况,还包含所有群成员的普通聊天。如果全量回,那个群就炸了。所以我在群聊场景下加了一个判断逻辑:消息文本中是否包含机器人的名字,或者是否被 @。只有满足条件才处理。

function shouldHandleInRoom(text, room) { if (!room) return true; const selfName = bot.currentUserSelf.name(); return text.includes(selfName) || text.includes('@'); }

4.2 会话管理:滑动窗口保持记忆

大模型本身没有记忆。每一次调用都是独立的,上下文完全靠你在请求里塞多少历史。最粗暴的做法是把这个会话所有消息全部拼进去,但 token 会爆炸,回答会越来越慢,还会超长报错。

我的方案是滑动窗口:

  • 每个会话最多保留 12 条历史消息(约 6 轮对话)。
  • 新消息到达时,先把历史数组取出来,拼上当前消息,再调用模型。
  • 模型回复后,把“用户提问 + 模型回复”追加进历史数组。
  • 如果数组超过 12 条,从最前面丢弃 2 条,避免频繁截断。

为什么丢弃 2 条而不是 1 条?因为对话的最小单元是一条问和一条答,丢 1 条会破坏问答配对,让上下文变得诡异。

class SessionManager { constructor(maxLen = 12) { this.maxLen = maxLen; this.sessions = new Map(); } get(sessionId) { if (!this.sessions.has(sessionId)) { this.sessions.set(sessionId, []); } return this.sessions.get(sessionId); } append(sessionId, userMessage, assistantMessage) { const history = this.get(sessionId); history.push({ role: 'user', content: userMessage }); history.push({ role: 'assistant', content: assistantMessage }); if (history.length > this.maxLen) { history.splice(0, 2); } } buildPrompt(sessionId, currentMessage) { const history = this.get(sessionId); const parts = []; for (const item of history) { parts.push(`${item.role === 'user' ? '用户' : '助手'}:${item.content}`); } parts.push(`用户:${currentMessage}`); return parts.join('\n'); } }

这个 SessionManager 是整个服务的中枢。每个会话独立维护,不会串号,也不会无限增长。

4.3 模型调用:用流式还是非流式

Ollama 的/api/generate支持两种模式:流式(stream=true)和非流式(stream=false)。非流式简单,等模型全部生成完一次性返回。流式会通过 SSE 一点点返回 token。

我最终选了非流式。原因只有一个词:简单。本地 7B 模型在无 GPU 的 Mac 上生成 200 字大概要 20~40 秒,流式返回的中间片段还得想办法从微信端发出去,体验并不好,反而容易把消息拆得七零八落。非流式配合“先回复一条’正在思考…’”的体验,比流式更稳。

async function askLocalModel(prompt, systemPrompt = '你是我的个人助手,请用简洁直接的中文回答。') { const messages = [ { role: 'system', content: systemPrompt }, ]; const historyLines = prompt.split('\n'); for (const line of historyLines) { const idx = line.indexOf(':'); if (idx === -1) continue; const role = line.substring(0, idx) === '用户' ? 'user' : 'assistant'; const content = line.substring(idx + 1); messages.push({ role, content }); } const resp = await axios.post('http://127.0.0.1:11434/api/chat', { model: 'qwen2.5:7b', messages, stream: false, options: { temperature: 0.7, num_predict: 2000, }, }); return resp.data.message.content.trim(); }

这里我用了/api/chat而不是/api/generate。因为/api/chat直接接受 OpenAI 风格的 messages 数组,system、user、assistant 角色清晰,不需要自己手动拼 prompt 字符串。这让上下文管理变得干净很多。

4.4 发送回复:先安抚再干活

本地模型慢,用户等太久会以为机器人死了。我的处理方式是:收到消息后先立刻回一条“收到,思考中...”,然后等模型返回后再把正文发出去。但这引入了另一个问题:短时间发两条消息,在微信端会被折叠,体验反而不好。

我最后的折中方案是:只对可能超过 10 秒的请求发“思考中...”的提示,短问题直接憋住等结果。判断逻辑很简单——看会话历史长度和当前消息是否包含“总结/解释/写”这类重任务关键词。

async function handleMessage(sessionId, text) { const shouldDelay = text.length > 30 || /(总结|解释|写一|详细|分析)/.test(text); if (shouldDelay) { // 这里的 replyTarget 需要从外部传入,代码里做了简化 await sendText(sessionId, '收到,让我想一下...'); } const prompt = sessionManager.buildPrompt(sessionId, text); const reply = await askLocalModel(prompt); sessionManager.append(sessionId, text, reply); await sendText(sessionId, reply); }

不要小看这个细节。我实测过,没有“思考中”提示时,用户会在 10 秒左右开始怀疑是不是挂了,然后连发三条追问,把上下文彻底搅浑。加了提示之后,整个交互就顺了。

5. 神坑复盘:我踩过的坑与修复链路

这一章节是全文的核心,标题说“踩齐了所有神坑”,你在这里看到的每一个坑都是我真实遇到过、并且最后给出了修复方案的。我会按“现象 -> 排查过程 -> 根因 -> 修复”的顺序写,方便你对照复现。

5.1 坑一:二维码扫了四五次,登录总是失败

这是最劝退的坑。我第一天晚上就是倒在这里。

现象:脚本正常启动,终端输出二维码,手机扫码后提示“请在手机上确认”,但微信端没有最终确认,脚本迟迟不触发 login 事件。

排查过程:我开始以为是网络问题,反复重启;后来又怀疑二维码过期,重新生成很多次。最后注意到日志里 scan 事件返回的 status 变了,开始是 200 表示等待扫描,后来变成 408 表示过期。

根因:我用的是 Wechaty 默认的 puppet,本质是 Web 微信协议。但现在大量微信账号已经无法登录网页版微信,官方页面会提示“当前版本已不支持”。扫码后手机端不能确认,就是这个原因。

修复:换用支持非 Web 协议的 puppet。这一步需要获取一个 token,配合 puppet 服务来走手机协议。我换成了wechaty-puppet-wechat,这套方案通过注入方式实现,绕开了 Web 微信的限制。换完之后,扫码几乎秒登录,没有再掉过。需要说明的是,这个方案属于非官方手段,只建议个人自用,并且别拿去做营销。

另外一个提高成功率的小技巧:首次登录后会在本地生成凭证文件,下次启动时只要凭证没过期,就不需要重新扫码。

5.2 坑二:两个人同时发消息,回复串号了

现象:我在一个测试群里和一个小号同时发消息,过一会儿发现 A 的问题回复给了 B,B 的问题回复给了 A。看了一眼代码,整个人麻了。

排查过程:一开始怀疑是会话 ID 的问题,但我打印了 sessionId,发现每次消息的 sessionId 都是正确的。后来我加了时间戳日志,发现第十秒收到了 B 的消息,第十一秒模型返回了 A 的答复,发送动作却用到了当前消息的会话上下文,也就是说回复被异步竞争污染了。

根因:每个消息的回调都是异步函数,模型调用是耗时操作。消息到达的顺序是 A、B,但模型返回的顺序可能是 B、A。我的代码里如果用全局变量或循环变量去记录“当前消息”,就会出现 B 的回复被塞进 A 的会话里。

修复:引入“会话任务队列”。同一个 sessionId 的消息逐个处理,不允许并发。实现上我写了一个简单的 Promise 链:

const queues = new Map(); function enqueueMessage(sessionId, text) { if (!queues.has(sessionId)) { queues.set(sessionId, Promise.resolve()); } queues.set(sessionId, queues.get(sessionId).then(async () => { await handleMessage(sessionId, text); }).catch(err => { console.error(`[${sessionId}] 处理失败:`, err); })); }

这段代码看起来简单,但它把“同一个会话的消息串行化”变成了不可逾越的约束。此后我再也没有遇到过串号。

5.3 坑三:模型推理慢到微信端直接超时

现象:第一次完整跑通时,我给机器人发了“帮我写一份周报”,它沉默了整整一分钟。微信端既没有错误提示,也没收到任何回复,这个体验是灾难级的。

排查过程:看了一下日志,模型实际推理花了 42 秒。我的机器是 Apple Silicon 16G 的统一内存,跑 7B 量化模型速度也就那样。于是我开始想怎么缩短时间。

根因有两层:一是模型量化等级低,推理慢;二是我没有任何“慢任务”提示,用户以为服务挂了。

修复分为两部分:

  • 模型层面,换用更激进的量化版本,比如qwen2.5:7b-q4_K_M,速度比默认高不少,效果损失可以接受。
  • 交互层面,加入前面说的“收到,让我想一下...”提示。

另外我还设置了超时重试机制。如果单次请求超过 90 秒,断开此次连接并给用户发一条失败提示,把上下文保留,允许用户追问。不要因为一次卡死就把整个会话丢掉。

5.4 坑四:上下文越聊越慢,最终直接报错

现象:连续聊了 20 轮之后,模型响应时间从 10 秒慢慢涨到 25 秒,最后直接抛context length exceeded错误。

排查过程:看错误信息后我突然意识到,会话历史一直在增长。最开始我设计了 maxLen,但在实现时图省事,直接用了history.push后不做截断,maxLen 形同虚设。日志里打印出来的 prompt 长度从几百涨到了几千 token。

根因:滑动窗口没有真正生效。

修复:回到 SessionManager,强制在 append 后检查长度并裁剪。同时我在调用模型前打印一条日志记录 token 数,超过 8000 直接主动丢弃历史的一半,而不是等到模型报错。

注意:改完上下文管理逻辑之后,务必重启并连续测试 30 轮以上,确认不会第二次踩到超长。千万别只测一轮就说好了。

5.5 坑五:服务跑两天之后,内存爆掉

现象:发消息的响应速度越来越慢,我用ps aux一看,Node 进程内存占用从启动时的 120MB 涨到了 1.2GB。

排查过程:先怀疑是 Wechaty 内部缓存,网上也有人提到。但后来我把自己代码里的 session 数量打印出来,发现测试过程中创建了几百个 session,而且每 session 都存了 12 条历史消息。群聊里非 @ 的普通消息也进了队列,导致无意义 session 暴涨。

根因:session ID 的 key 太“散”了。我在早期版本中把群内每一条非 @ 消息也生成了 session,但没有处理逻辑,白白占内存。另外,真实的消息对象被保存在了历史数组里,消息对象内部嵌套大量字段,更吃内存。

修复:

  • 只有真正需要回复的消息才创建 session。
  • session 设置了空闲过期时间,48 小时不活跃就删掉。
  • 历史数组中只保存字符串文本,不保存消息对象。
function cleanExpiredSessions() { const now = Date.now(); for (const [id, session] of sessions.entries()) { if (now - session.lastActiveAt > 48 * 3600 * 1000) { sessions.delete(id); } } } setInterval(cleanExpiredSessions, 60 * 60 * 1000);

5.6 坑六:中文乱码与表情符号处理

现象:模型返回的内容里有 emoji,发到微信后变成了问号或空白。有些长文本里的换行符也消失了,一坨挤在一起没法看。

排查过程:一开始以为是微信接口的问题,后来我在终端里直接打印模型返回的内容,发现数据本身是完整的。问题出在消息发送链路:当消息里包含多个星号、反引号等 Markdown 符号时,微信会把它们当成格式标记吃掉,或者某些字符在传输时被转义。

根因:个人微信的自动化协议对特殊字符的兼容性并不好;同时 Ollama 返回的内容是 Markdown 风格,直接发给微信体验极差。

修复:写了一个清洗函数:

function sanitizeReply(text) { return text .replace(/```[\s\S]*?```/g, (m) => `\n${m.replace(/```/g, '')}\n`) .replace(/[#*>`~]/g, '') .replace(/\n{3,}/g, '\n\n') .trim(); }

不同模型的输出风格不一样,清洗规则也要跟着调。我一开始清洗太激进,把代码块里的缩进也删了,导致发送的代码没法看。后来改成只去掉零散符号,保留代码块完整。

6. 稳定性治理:从跑通到长期跑

项目跑通和项目能长期稳定跑,是两个完全不同的问题。我把这套东西放在那跑了三个月,中间经历过掉线、内存泄露、模型卡死。提升稳定性的核心做法是这三点。

6.1 用进程守护接管存活

Node 服务绝对不能裸奔在前台,一个意外异常就能让整个服务死掉。我用的是 PM2:

npm install -g pm2 pm2 start server.js --name wechat-ai pm2 save pm2 startup

PM2 会自动守护进程,崩溃后重启。我额外加了max_memory_restart参数,让进程内存超过 600MB 时自动重启:

pm2 start server.js --name wechat-ai --max-memory-restart 600M

这样就算代码里还有隐藏的内存泄漏,也能通过自动重启兜底,不会一夜之间死透。

6.2 多群隔离与权限控制

如果机器人同时被拉进多个群,你绝对不希望所有群都能使唤它,更不希望陌生人加你微信就能用你的算力。我配置了一个白名单机制:

  • 私聊:只有白名单里的联系人才能触发 AI。
  • 群聊:只有白名单里的群 ID 才会响应。
const ALLOWED_CONTACTS = new Set(['wxid_xxx', 'wxid_yyy']); const ALLOWED_ROOMS = new Set(['xxx@chatroom']); function isAllowed(sessionId) { if (sessionId.startsWith('room:')) return ALLOWED_ROOMS.has(sessionId.slice(5)); return ALLOWED_CONTACTS.has(sessionId.slice(8)); }

不在白名单直接忽略。这个机制避免了很多无用调用消耗算力,也防止别人在一个测试群里把模型问麻。

6.3 日志与可观测性

我的日志统一走 JSON 格式,每条日志包含时间、会话 ID、行为类型、耗时。这样排查问题时,可以直接用日志文件定位:

{"time":"2025-01-15 10:23:45","session":"contact:wxid_xxx","event":"recv","content":"你好"} {"time":"2025-01-15 10:24:20","session":"contact:wxid_xxx","event":"reply","content":"你好,我是本地AI助手","cost":35}

我还加了一个简单的健康检查接口:每隔 5 分钟请求一次 Ollama,如果失败就发一条提醒消息给我自己,告诉我模型挂了。这个机制救了我好几次,半夜偷偷看的都知道。

7. 最后说几点

这套东西的核心价值不在于代码本身,而在于你想清楚了自己为什么需要它。我见过很多人花几天时间搭好了机器人,新鲜劲一过就熄火了。对我来说,它是我每天真正在用的小工具:临时查资料、整理思路、起草信息,手机微信上随手就发。

最后分享两个小技巧。第一个,微信消息的自动回复尽量保持简短,200 字以内的回复体验比长篇大论好非常多,可以在系统提示词里加一句“回复控制在 200 字以内”。第二个,不要把服务搭建完就不管了,每周看一次日志,尤其关注那些被忽略的异常报错,早点处理早点安心。这摊子事不难,但需要耐心。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询