乐于分享
好东西不私藏

从「工具」到「伙伴」:打造 AI 个性化专家

从「工具」到「伙伴」:打造 AI 个性化专家

产品思考

年初写年终总结的时候就计划,今年要做出一款面向c端的个人产品,那时候其实脑子里面大概是有了一个雏形。年后玩了一段时间 openClaw,最近也是抽出时间出来,开始逐步开发自己的个人项目。

产品设计

产品形态主要是微信小程序,因为有免费的微信小程序云开发能用,服务可以直接用这个,数据库也可以直接免费用。网页端不太适合我这个产品,开发app周期又很长。小程序是最初阶段最好落地的场景。

产品设计是: AI驱动的宠物行为解读工具,让主人听懂宠物的情绪和需求。核心价值是:即时解读3秒、健康守护、新手友好。主要是想通过 AI 的能力去帮助用户解读宠物的意图。但是做到后面发现我的方向好像是错误的。我太想去实现一个通用的 AI 宠物智能体,而没有想到宠物是没有语言的,所谓“喵喵”或者“汪汪”叫都是宠物情绪的一种表达,在动物的世界里是没有语言这个说法的,他们是通过各种行为特征来表达自己语言的

所以一开始的通过 AI 解读宠物语言是完全错误的方向,这种所谓的通用 agent 也是完全不成立的。宠物在和主人的相处过程中,除了宠物的一些底层代码,宠物和主人之间相处会形成自己特殊的行为特征,简单说这是一个千人千面的产品

这个问题也是在实现的过程中才发现,但是当时已经开发了差不多百分之七十的功能,本来想着直接放弃的,但是想起导师阿伟的至理名言:想起来全是困难,做起来全是办法,先按照之前的计划先把一些通用的功能完成再说。

按照原计划完成之后的产品形态就是:接入 AI 大模型接口,然后翻译宠物语音内容,然后加入打卡,宠物通用语、指南、百科、训练指导、宠物问诊等一些通用的功能。这些基本上是撑起一个宠物产品的基本功能形态,但是使用了两天发现完全没有继续使用的欲望。首先所谓的翻译,ai根本就不能做到翻译,所以很多时候 AI 其实完全是在胡说,它最多只能通过宠物的一些语气来做到简单的感情变化的识别(生气、不开心),而其他几个功能对于宠物新手来说还会愿意看一看学习一下,对于我来说我的宠物的几个特征我都基本了解了,我似乎完全没有使用的必要,完全无法形成产品依赖性。

转折

上周五,约阿兴一块探店雍和宫附近的一家粤菜,正好他最近一直在研究 AI 产品相关的内容。就着开水拌叉烧(不敢和他喝酒,怕回不去家),从7点一直畅聊到9点半。听了他对 AI 产品的理解和想法,也给我介绍讲解了几个很“非主流”但是很赚钱的 AI 产品,受益匪浅。对我影响最大的一句话是:移动互联网时代做的大部分产品是平台化,做一系列的通用能力,去解决一群人的一部分的通用问题。在 AI 时代 C 端应该做的是私人定制化的能力,每个用户都希望有一个了解我的“私人秘书”来帮助我解决问题,而不是我去平台找这个功能来解决这个问题,其实最近爆火的 openclaw 解决的就是这个问题。

这很好的启发了我对项目的改造,我做的应该是对每个用户(宠物)做定制化的服务,通过用户的每次交流和使用,记录宠物的行为特征,作为记忆和背景,逐渐了解宠物,打造专属个人的宠物 AI 服务,我做的不是“工具”而是一个逐渐理解用户(宠物)的“伙伴”

核心技术架构

档案模块

首先需要添加一个档案的功能,记录一下宠物的特征(记忆备份)

然后就是最核心的记忆系统的构建:将用户与助手的每次对话转化为宠物专属知识,随时间积累形成「只懂这只宠物」的个性化专家系统。

记忆系统

循序渐进一共分成四步

第一步:基础版(存档 + 提取)

在 pets 集合中新增 memory 字段:

{_id"xxx",name"薯条",type"cat",breed"英短",// 新增字段memory: {health:      [{ content"2026年3月食欲不振"date"2026-03"source"healthchat" }],behavior:    [{ content"喜欢抓沙发左侧角"date"2026-03"source"trainingchat" }],diet:        [{ content"不吃鸡肉罐头,喜欢金枪鱼"date"2026-03"source"healthchat" }],events:      [{ content"2026-02 做了绝育手术"date"2026-02"source"user_input" }],personality: ["胆小""粘人"],lastUpdated"2026-03-23"  }}

用户每次离开聊天页面时,将本轮完整对话发给大模型,大模型按照定义好的字段格式提取字段,加到memory字段内。

核心Prompt:

你是一个宠物信息提取助手。以下是用户与宠物顾问的完整对话:[对话内容]宠物信息:名字={{petName}},种类={{petType}},品种={{breed}}请从对话中提取关于这只宠物的新增关键信息。规则:1. 只提取用户明确陈述的事实,不推断2. 模糊表述("好像""可能")不提取3. 已知的通用知识不提取,只提取该宠物的个性化信息输出 JSON 格式(没有信息的维度输出空数组):{"health":      ["健康相关具体信息"],"behavior":    ["行为特征"],"diet":        ["饮食偏好"],"events":      ["发生的具体事件,尽量带时间"],"personality": ["性格标签"],"hasNewInfo":  true/false}

然后在每次对话的时候,都将 memory 拼入 system prompt中:

// 构建 system promptfunctionbuildSystemPrompt(petName, petType, memory{let prompt = `你是${petName}专属的宠物健康顾问。`;if (memory) {const parts = [];if (memory.health?.length)      parts.push(`健康历史:${memory.health.map((i) => i.content).join(";")}`);if (memory.behavior?.length)      parts.push(`行为特征:${memory.behavior.map((i) => i.content).join(";")}`,      );if (memory.diet?.length)      parts.push(`饮食偏好:${memory.diet.map((i) => i.content).join(";")}`);if (memory.personality?.length)      parts.push(`性格:${memory.personality.join("、")}`);if (memory.events?.length)      parts.push(`重要事件:${memory.events.map((i) => i.content).join(";")}`);if (parts.length > 0) {      prompt += `\n\n关于${petName}你已知的信息:\n${parts.join("\n")}`;      prompt += `\n\n请基于以上背景提供个性化建议,如有关联请主动结合分析。`;    }  }return prompt;}

效果:

对话次数
用户体验
第 1 次
通用顾问,只知道名字和品种
第 3 次
记住了饮食偏好和主要行为问题
第 5 次+
能主动关联:「你上次提到它不吃鸡肉……」

第二步:强化版(置信度 + 双轨存储)

置信度评分系统

提取时增加置信度字段,低于阈值的信息不存档

上一阶段:

{"health": ["两天未进食""好像有点不舒服"],"hasNewInfo"true}

升级后:

// 提取结果新增 confidence 字段{"health": [    { "content""两天未进食""confidence"95 },      // 明确事实 → 存档    { "content""好像有点不舒服""confidence"35 }   // 模糊描述 → 不存档  ]}

置信度判断规则(在 Prompt 中明确要求):

信息特征
置信度范围
用户明确陈述具体事实
80 – 95
医生 / 专业机构确诊
95 – 100
用户模糊描述 / 猜测
20 – 50
用户主动否定
0(触发删除旧记录)

存档阈值:confidence ≥ 60 才写入数据库。

事件流 + 状态快照双轨制

将 memory 拆分为两个集合:

集合 1:pet_memory_snapshot(状态快照,可覆盖)

// 当前宠物的最新状态,每次更新直接覆盖{petId"xxx",health:      "轻微肾结石(已控制),无其他慢性病",diet:        "喜欢金枪鱼冻干,不吃鸡肉,只喝流动水",behavior:    "喜欢抓沙发,见陌生人躲猫窝",personality: ["胆小""粘人""夜间活跃"],updatedAt:   "2026-03-23"}

集合 2:pet_memory_events(事件流,只追加)

// 每条独立事件,永久保留,带时间戳{petId:      "xxx",content:    "呕吐两次,后自愈",category:   "health",date:       "2026-03-10",confidence92,source:     "healthchat",createdAt:  serverDate}

查询策略

  • 日常对话 → 只读 snapshot(快,省 Token)
  • 用户问「它以前有什么病史」→ 查询 events(按需加载)
语义去重

提取新信息时,先把已有 snapshot 传给大模型,让它判断是否重复:

已有档案信息:- 饮食:不吃鸡肉罐头本次新提取到:- 「它对鸡肉过敏」请判断:1. 与已有信息是否语义重复或矛盾?2. 如重复:选择更准确的表述覆盖3. 如矛盾:标记冲突,两条并存,新条目加「最新」标记4. 如全新:直接新增输出操作指令:"action""update/append/conflict""content""xxx" }

第三步:高级版(记忆衰减 + 分层注入 + 主动追问)

记忆衰减机制

每条记忆引入有效分数,控制是否注入 prompt:

// 有效分数计算functioncalcMemoryScore(memory{const daysSince = (Date.now() - newDate(memory.date)) / 86400000;// 时间衰减(180天后衰减到37%)const decay = Math.exp(-daysSince / 180);// 引用次数强化(每被引用一次+10%,上限200%)const reinforcement = Math.min(1 + memory.mentionCount * 0.12.0);// 来源权重const sourceWeight =    {vet_confirmed1.5// 医生确诊user_stated1.0// 用户陈述inferred0.6// 系统推断    }[memory.source] || 1.0;return memory.confidence * decay * reinforcement * sourceWeight;}// 注入时过滤:分数低于阈值的记忆不注入 promptconst activeMemories = memories.filter((m) => calcMemoryScore(m) > 40);
分层注入策略

将记忆分三层,控制 Token 消耗:

第一层:核心档案(每次必注入,~200 Token)├── 品种、年龄、绝育状态├── 已知慢性病└── 最重要的 3 条饮食/行为特征第二层:近期动态(最近 30 天,每次注入,~300 Token)├── 近期健康状况变化└── 近期行为问题第三层:历史归档(按需检索,0~500 Token)├── 超过 30 天的历史事件└── 仅在用户明确询问历史时查询注入
// 分层注入实现asyncfunctionbuildLayeredPrompt(petId, userMessage{// 第一层:永远注入const snapshot = await getSnapshot(petId);// 第二层:近30天事件const recentEvents = await getRecentEvents(petId, 30);// 第三层:检测用户是否在问历史const isAskingHistory = /以前|历史|之前|过去|曾经/.test(userMessage);const historyEvents = isAskingHistory ? await getAllEvents(petId) : [];return buildPrompt(snapshot, recentEvents, historyEvents);}

主动追问式信息补全

检测档案中的缺失字段,在合适时机主动追问:(这也是你使用很多AI产品可能还有有一些奇怪的追问,其实就是在收集补全信息)

// 定义档案完整度权重const FIELD_WEIGHTS = {vaccine_date: { label"疫苗接种时间"weight9 },spayed: { label"是否绝育"weight8 },chronic_disease: { label"慢性病史"weight8 },allergy: { label"过敏史"weight7 },diet_brand: { label"常用粮食品牌"weight5 },};// 每次对话随机概率追问一个缺失字段(避免每次都问,烦人)functionshouldAskForInfo(snapshot{const missing = Object.keys(FIELD_WEIGHTS).filter((k) => !snapshot[k]);if (missing.length === 0returnnull;if (Math.random() > 0.3returnnull// 70% 概率不追问// 按权重选最重要的缺失字段  missing.sort((a, b) => FIELD_WEIGHTS[b].weight - FIELD_WEIGHTS[a].weight);return FIELD_WEIGHTS[missing[0]].label;}// 在回复末尾追加// "顺便问一下,薯条打过疫苗吗?记录一下方便以后参考~"
冲突检测与处理

新旧信息矛盾时,不简单覆盖,而是并存+标记:

// 冲突记录结构{field"behavior",old: { content"不喜欢被抱"date"2026-01"status"outdated" },new: { content"变得很粘人,喜欢蜷在主人身上"date"2026-03"status"active" },conflictNote"行为可能随年龄/季节发生变化,以最新记录为主"}// 注入 prompt 时的处理// "注意:用户1月反映不喜欢被抱,3月反映变得粘人,//  可能性格随年龄发生变化,请以最新信息为主,但可提示用户关注此变化。"

整体流程

阶段一:对话结束时

① extractMemory 云函数 — 节点1:写入时埋下衰减基础数据

触发时机:用户关闭页面 / 返回上一页

提取信息 → 写入数据库,同时为每条记录埋入衰减算法所需字段:

写入位置
字段
说明
pet_memory_events

 每条记录
mentionCount: 0
引用次数初始为 0
pet_memory_events

 每条记录
createdAt: serverDate
写入时间戳
pet_memory_snapshot

 每个字段
createdAt: "2026-03-24"
供衰减公式计算天数

阶段二:下次对话时

② 用户打开聊天页面,发出消息

③ askVet / askTrainer 云函数 — 节点2:分层读取 + 衰减过滤

调用 loadPetMemoryLayered(),按需加载三层记忆:

  • 第一层:读取 snapshot,经 applyDecayFilter() 过滤掉评分过低的过期记忆
  • 第二层:读取近 30 天 events,每次必加载
  • 第三层:读取更早 events,仅当用户消息含「以前 / 历史 / 之前 / 曾经」时才加载

④ buildMemoryContext() — 节点3:分层拼装注入 System Prompt

  • 【核心档案】 第一层(必注入,~200 Token)
  • 【近期动态】 第二层(必注入,~300 Token)
  • 【历史记录】 第三层(按需注入,0~500 Token)

⑤ shouldAskForInfo() — 节点4:主动追问缺失字段

检测 snapshot 里哪些关键字段为空,以 30% 的概率在回复末尾追问一句。

⑥ 调用 MiniMax 大模型

基于完整上下文(档案背景 + 分层记忆 + 追问指令)返回个性化回复。


阶段三:回复返回后

⑦ updateMentionCounts() — 节点5:异步更新引用次数(不阻塞)

本次被注入 Prompt 的每条 event → mentionCount + 1,形成「重要记忆自动强化」效果。


步骤四:闭环版(用户反馈矫正 + 数据飞轮)

Phase 1~3 的记忆系统都是单向的。

用户说话 → 提取信息 → 存档 → 下次注入

但这个过程有一个致命漏洞:信息可能是错的,却没有人纠正它。 比如: 用户6月说”薯条不吃鸡肉”,存档了 9月薯条口味变了,现在吃鸡肉了 但系统永远记着”不吃鸡肉”,还一直用这条错误信息给建议 Phase 4 要做的,就是让这个系统形成一个能自我纠错的闭环。

隐式反馈识别

自动识别对话中的矫正信号,无需用户手动操作:

// 矫正信号识别 Prompt`检查用户的最新回复是否包含对已有档案信息的矫正:已有信息:「不吃鸡肉」用户回复:「它现在吃鸡肉了,只是不吃罐头」如果是矫正,输出:{ "isCorrection": true, "field": "diet", "oldContent": "不吃鸡肉", "newContent": "不吃鸡肉罐头,干粮可以" }如果不是,输出:{ "isCorrection": false }`;
档案管理页(用户显式反馈)

在宠物档案页新增「记忆管理」Tab:

薯条的专属记忆                    [+ 手动添加]─────────────────────────────🏥 健康记录  • 2026-03 食欲不振(已恢复)    [✓] [✕]  • 2026-02 绝育手术              [✓] [✕]🍽️ 饮食偏好  • 不吃鸡肉罐头                  [✓] [✕] [✏️]  • 喜欢金枪鱼冻干                [✓] [✕]🐾 行为特征  • 喜欢抓沙发                    [✓] [✕]─────────────────────────────档案完整度:72%  [去完善]

用户点击「✕」删除或「✏️」修改,直接更新数据库,同时记录「用户主动矫正」日志,用于后续优化提取 Prompt。

多模态信息源汇入

除对话外,其他数据源也自动归档:

录音分析结果 ──────────→ 情绪趋势记录  「2026-03-23 情绪=焦虑(87%)」打卡记录 ──────────────→ 长期健康趋势  「连续7天情绪评分下滑 → 触发提醒」用户手动填写 ──────────→ 高可信度档案  疫苗记录、绝育状态、过敏原

趋势分析触发提醒

// 打卡数据分析(每周一次)functionanalyzeCheckinTrend(checkins{const recent7 = checkins.slice(-7);const avgScore = recent7.reduce((s, c) => s + c.score, 0) / 7;if (avgScore < 60) {// 推送提醒    wx.cloud.callFunction({name"sendSubscribeMessage",data: {templateId"xxx",content`${petName}最近一周情绪偏低,要不要和健康助手聊聊?`,      },    });  }}

短期对话记忆优化处理

这里在补充一个关于对话记忆的处理:

问题

  • 成本激增:对话进行到第 20 轮时,每次请求携带的历史 Token 可能超过 3000+,是第 1 轮的 10 倍
  • 响应变慢:大模型处理更长上下文时延迟明显上升
  • 边际效益低:10 轮以前的对话对当前回答的参考价值很低

其实上面的记忆系统已经涵盖解决了一部分问题,就是跨轮次的记忆其实已经是存在memory中去了,后续重新开启对话,其实都是有记忆的。

但是本轮次长对话是没有存储进去的,有可能就会出现本轮对话中的记忆缺失。虽然作为宠物辅助的这种产品形态,一般不会有一轮超长对话,但是这是很好的问题,值得花时间去探索如何解决。

常用的方法(十条截断或者基于 Token 长度的滑动窗口截断)

十条截断

任务 10 轮以前的对话对当前回答的参考价值很低,本轮对话中直接截断丢弃,只要最近的 10 轮

问题:每条消息本身可能很长,10 条仍可能携带大量文本,需要消耗很多token,且10轮以前真的有很重要的信息,我们确实保存需要。

Token 长度的滑动窗口截断

不按条数截断,改为按字符数(近似 Token 数)截断,超出阈值时从最旧的消息开始丢弃。

/** * 按字符数截断历史记录,保证传入云函数的上下文不超过阈值 * @param {Array}messages - 完整消息数组 * @param {number}maxChars - 最大字符数(约等于 Token 数 × 1.5,默认 2000) * @returns {Array}截断后的消息数组 */functionbuildSlidingWindowHistory(messages, maxChars = 2000{// 过滤掉 loading 占位和空消息const valid = messages    .filter((m) => !m.loading && m.content)    .map((m) => ({ role: m.role, content: m.content }));let total = 0;const result = [];// 从最新的消息往前取,直到超出字符限制for (let i = valid.length - 1; i >= 0; i--) {const len = valid[i].content.length;if (total + len > maxChars && result.length > 0break;    result.unshift(valid[i]);    total += len;  }return result;}// 在 _sendText 中替换原有历史构建逻辑const historyForCloud = buildSlidingWindowHistory(messages, 2000);

效果

对话轮数
优化前传入字符数
优化后传入字符数
第 5 轮
~500
~500(不变)
第 10 轮
~1500
~1500(不变)
第 20 轮
~4000
~2000(截断)
第 50 轮
~12000
~2000(稳定)

问题: 解决了token过长消耗过多的问题,但是依旧会丢失重要对话。

摘要压缩

当历史超过阈值时,不是直接丢弃旧消息,而是调用大模型将旧消息压缩成一段摘要,用摘要替代原始历史,同时保留最近几轮的完整对话。流程

完整历史(20 轮)       │       ▼  超出阈值? ──否──→ 直接发送       │是       ▼  旧消息(前15轮)──→ 调用大模型压缩       │                    │       │              生成摘要(~200字)       │                    │       └────────────────────┘               │    [摘要消息] + [最近5轮完整对话]               │               ▼
// 在前端或云函数中执行摘要压缩asyncfunctionbuildCompressedHistory(messages{const valid = messages.filter((m) => !m.loading && m.content);const KEEP_RECENT = 6// 保留最近 6 条完整对话const COMPRESS_THRESHOLD = 2000// 超过 2000 字符才压缩const totalChars = valid.reduce((s, m) => s + m.content.length, 0);if (totalChars <= COMPRESS_THRESHOLD) {// 未超阈值,直接返回return valid.map((m) => ({ role: m.role, content: m.content }));  }// 超阈值:压缩旧消息const oldMessages = valid.slice(0, -KEEP_RECENT);const recentMessages = valid.slice(-KEEP_RECENT);// 调用云函数做摘要(或直接在前端调用 API)const summaryRes = await wx.cloud.callFunction({name"summarizeHistory",data: { messages: oldMessages },  });const summary = summaryRes.result?.summary || "";// 将摘要作为 system 角色消息注入return [    { role"system"content`以下是之前对话的摘要:${summary}` },    ...recentMessages.map((m) => ({ role: m.role, content: m.content })),  ];}

这套系统本质上是在做三件事:

  1. 记忆:把用户每次输入的碎片信息,结构化存起来
  2. 理解:通过置信度、衰减、去重,让记忆越来越准确
  3. 进化:用户反馈形成闭环,越用越懂这只宠物

这是从「工具」到「伙伴」的本质跨越,也是产品护城河的来源。


最后虽然整个产品已经改造完成了,也打包上传了,但是很遗憾卡在了审核,现在小程序的关于AI的产品个人是无法审核上架,必须要有企业资质才行。草!如果想体验产品的朋友可以私我,加体验版的权限使用。

一些思考

感觉最近 AI 给大家带来太多的焦虑,上到企业家,下到打工人,很多人无所适从,不知道该怎么办,似乎随时要被这个时代抛弃了。我之前也很焦虑,但是最近看到三句话,帮我摆脱了这种焦虑的困境。

就在这里分享给大家与诸君共勉:

你是领先的

世界总人口现在是81亿,如果画成一个方块图,每个方块代表320万人。

68.8亿人还从没用过 AI(灰色方块),占84%。

13.8亿人跟 AI 有过对话(绿色方块),占16%。

1500万~2500万人付费订阅了 AI 服务(黄色方块),占0.3%。

200万~500万人使用 AI 生成了自己的编程项目(红色方块),占0.04%。

正在阅读这份文章的你,如果是关注 AI 动态,用过 AI 生成代码,就已经领先全世界99%的人了。(出自阮一峰第387期周报)

保持思考和学习

王师傅:大家不要因为每次AI的狼来了的故事而感到什么焦虑,什么我觉得要被AI淘汰了,不要纠结这些事情,你只要去学习它就好了,我觉得人最重要的就是学习能力,如果你什么时候会被淘汰,只有在你停止学习的那一刻,才会被淘汰。

保持健康和热情

在 AI 时代,生产力在很大程度上取决于你能够调用的算力;而当你所能掌控的算力达到你的带宽(你的打字速度和精力)极限时,真正稀缺的,反而是你的身体健康,以及持续保持高能状态的能力。(好像也是出自阮一峰老师的周报,具体哪一期找不到了)

AI 是对整体社会形态的重构,不是单一职业或者行业的影响,作为互联网相关行业,你现在已经领先了很多人,不用过分焦虑,保持思考和学习,多去尝试。最后无论如何保持身体健康,因为不管是 AI 时代还是其他任何时代身体最重要。

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 从「工具」到「伙伴」:打造 AI 个性化专家

猜你喜欢

  • 暂无文章