上个月我们团队做了一次比较完整的AI功能落地:在现有App里增加了一个“AI宠物”模块,从产品定义、技术选型、接口联调到上线灰度走完一整轮。这篇文章是对这个项目从头到尾的复盘,重点是那些文档里不会写、只有真正联调才会遇到的决策和问题。如果你也在考虑给App接入大模型能力,或者想做一个带“养成”和“陪伴”属性的AI功能,这篇复盘应该能帮你省掉不少弯路。
1. 为什么是“宠物”,以及这个功能最终被定义成什么样
1.1 产品背景:留存的压力与AI能力的选择
先说背景。我们有款面向年轻用户的生活记录类App,内容以任务打卡和日常记录为主,功能本身没有太大问题,但用户完成核心任务之后基本就走,缺乏回访理由。月中的一次数据复盘,次留和30日留存都出现小幅下滑,产品经理提了一个需求:能不能用AI做一个有“粘性”的功能,让用户有理由每天打开App?
当时团队手里可选的AI方向有几种:AI总结报告、AI内容生成、AI拍照识别、AI对话陪伴。内部讨论最久的是“AI总结”,因为它和App原本的记录属性契合,做成每周报告、每月报告看起来顺理成章。但最后我们放弃了,原因很直接:总结类功能打开频率太低,一周一次已经算高,撑不起“每天打开”的目标。
真正让我们决定做“宠物”的,是数据里一个细节:App里图片上传功能的使用频次很高,用户特别喜欢晒自己的猫和狗。我们连续翻了几天用户上传内容的标签统计,宠物相关内容占据图片量的将近三分之一。既然用户已经在App里晒宠物,那就顺势做一个“AI宠物”功能:用户领养一只数字宠物,既能当聊天陪伴,又能拍照识别现实里的猫狗,顺便把记忆和提醒功能揉进去。
1.2 定义功能边界:AI宠物不只是聊天机器人
立项之后的第一件事不是写代码,而是把“AI宠物”这个模糊概念切成具体功能。我们最后定义了四条主线:
- 领养与形象:用户进入宠物模块后,从几个品种中选一只,给宠物起名字,设置生日和性格标签。
- 对话陪伴:宠物具备独立的性格,能陪用户聊天,并根据用户心情调整语气和回应方式。
- 拍照识别:用户拍下自家猫狗的照片,宠物能认出来这是什么品种、大概多大、今天看起来心情如何,并给出一些趣味性的解读。
- 记忆与提醒:宠物能记住用户告诉它的关键信息,比如宠物名字、生日、疫苗时间、某个习惯,并在合适的时候主动提醒。
同时我们也明确了一版不做的事:不做虚拟形象生成,不做复杂养成数值(喂食、升级、对战),不做用户之间的宠物社交。原因很简单,一个星期内我们不可能把养成系统做得比运营两年多的养成类产品更好,那就不如把有限的资源集中在AI能力本身上,让用户先被“对话真、识别准、记得住”打动。
边界明确之后,整个功能就有了一个很清晰的最小闭环:用户领养一只宠物,和它聊几句,拍一张自己宠物的照片,然后第二天再回来看看宠物还记不记得昨晚聊了什么。
1.3 三个核心场景的优先级排序
功能切完之后还要排优先级,不然开发过程中一定会被无限加需求。我们的排列逻辑是:先做能验证核心假设的场景,再做差异化场景。
- P0:文本对话陪伴、宠物档案、基础记忆。这是整个功能的底座,没有对话和记忆,其他都无从谈起。
- P1:拍照识别、主动提醒。拍照识别是差异化亮点,主动提醒是提升回访频次的钩子。
- P2:语音输入、养成扩展。放到版本迭代里再说。
回头看这个排序是值得的。因为AI项目的最大风险根本不是“模型效果不好”,而是“功能太多导致链路不稳定,最后连一个场景都没跑通”。P0目标只有一个:让用户觉得宠物真的在和他建立关系。这个目标一旦成立,P1和P2才有意义。
2. 整体架构与选型:Agent、大模型API与端侧怎么分工
2.1 功能链路拆解:从App触摸到模型返回
先给整体架构画一个轮廓,方便后面每个模块的展开。客户端是Flutter双端,后端是Python FastAPI服务,模型能力全部通过云端API调用,数据层使用PostgreSQL加向量扩展。
用户从App里打开宠物页面,看到一只宠物形象,点击聊天输入框,输入一句话,请求进入后端会话服务。会话服务负责几个事情:读取用户身份和宠物档案,把最近的对话历史整理成上下文,补充长期记忆检索结果,然后组装成请求发给大模型API。大模型返回文本后,会话服务再把回复文本持久化,同时把可提取的事实性信息交给记忆服务做异步抽取,最终把回复推回App。图片识别走的是另一条链路:App上传图片,后端先做图片预处理和基础分类判断,再调用多模态大模型的视觉接口,把识别结果转成结构化JSON,返回给前端展示,同时也把识别记录存储到记忆表里。
这个链路里最关键的取舍是:我们选择让后端做所有的模型编排,客户端只做展示和交互。原因是多端复用和权限控制,后续如果我们要在微信小程序、网页端加同一个人宠物入口,后端一套服务就能覆盖,不需要每个端各写一套接模型API的逻辑。
2.2 大模型选型:为什么不微调,只做编排
模型选型时,团队内部讨论最多的是“要不要微调一个垂直宠物模型”。我当时的态度非常明确:不微调。理由有三个。
第一,数据量不够。我们手里真正的宠物对话数据几乎没有,如果要从零开始整理对话微调数据,没有几万条高质量样本很难见效,这个成本远远超过模型本身的API费用。
第二,效果天花板。通用大模型已经具备相当强的宠物知识储备和对话能力,通过合理的系统提示词和检索增强,已经能做出“懂宠物”的体验;而微调模型一旦训不好,可能连基础对话能力都会下降。
第三,迭代速度。我们用的是大模型API,模型厂商每次升级底座,我们的效果都能跟着提升,不需要自己维护训练链路。对一个小型团队来说,这几乎是最优选择。
实际开发中,我们使用了大模型API的文本对话接口,主要做休闲陪伴和记忆问答;同时使用同一个厂商的多模态视觉接口处理宠物图片识别。一个额外的好处是,同一个厂商的接口鉴权、配额和计费模型是统一的,省去对接多套SDK的麻烦。
提示:如果你对大模型API不熟悉,只要记住一句判断标准——你手里有没有别人拿不到、而且量大到能改变模型输出的数据?如果没有,就不要自己做微调。先把检索增强和提示词工程做到位,往往性价比更高。
2.3 多模态识别与宠物状态管理
宠物识别模块最初被一些人质疑:“这个功能直接调图像分类模型不就行了?”但实际上,真实用户拍的宠物照片非常复杂,涉及光线、角度、遮挡、模糊、多只宠物同框等情况,单一分类模型很难覆盖。我们选择的方案是分成两层。
第一层是前置规则判断。图片上传后先做尺寸压缩、格式校验,用轻量分类模型判断“图里到底是不是宠物”,以及“大致是猫还是狗”。如果图片里没有人脸、车牌等敏感信息,我们就可以放心进入下一步;如果图像质量太差,直接返回提示让用户重新拍。
第二层是调用多模态大模型的视觉接口,让它输出结构化的识别结果。这里有个很关键的操作:不能让它随便返回一段文字,而是要求返回JSON结构,包含品种、置信度、年龄区间、情绪、显著特征、健康备注。后续前端直接渲染JSON,不需要做文本解析,更稳定。
宠物状态管理则放在会话服务里,用一个独立的“宠物状态表”存储。状态字段包括心情、活跃度、最近互动时间、累计聊天次数等。每次对话结束后,根据用户输入的情绪和内容,状态值会做一点偏移。比如用户说“今天加班好累”,宠物状态里的“安慰指数”就增加,回复也会更温和;用户表达开心,宠物状态就偏向兴奋,回应会更调皮。
状态管理最大的作用是让宠物看起来“有情绪”,而不是一个永远同一语气的ChatBot。这个功能并不复杂,但非常有效,用户感知特别明显。
2.4 记忆存储:长期记忆的落库方案
宠物的记忆是整个功能里最容易被低估的部分。对话历史本身需要短期记忆,但宠物还需要跨会话记住用户告诉过它的“事实”。我们把它拆成两层。
短期记忆存储在会话服务的内存缓存里,默认保留最近20轮对话。达到20轮之后,超过部分会被异步压缩成一条摘要,存进数据库,同时从短期缓存里淘汰。这样的话,用户隔天回来继续聊,宠物还能接上之前的语境。
长期记忆使用PostgreSQL加pgvector扩展来实现向量检索。数据库里专门建了记忆事件表,每条记录包含宠物ID、用户ID、事件类型、标题、详细描述、时间等字段,同时用文本嵌入模型生成对应的向量存在pgvector字段里。当用户问宠物“你记得我上周跟你说我家的猫生病了吗”,系统先把问题转成向量,按相似度从记忆表里取回Top5,再把这些记忆拼接进大模型请求的上下文。
为什么不单独用向量数据库?当时评估过Milvus和专门的向量库产品,但我们的数据量不到百万级别,PostgreSQL加pgvector完全扛得住,而且能和业务表放在同一个事务里,不用处理双写的一致性问题,运维成本也低。等以后数据量真的大了,再迁移也不迟。
3. 核心实现:从Prompt到Agent,再到App接入
3.1 让宠物“像宠物”:人格化System Prompt的写法
模型选好之后,所有“宠物感”都来自系统提示词。这一块我们迭代了很多版本,我自己最大的体会是:一个人格化Prompt的关键不是堆形容词,而是把“视角限制”和“行为规则”写清楚。
先看一个当时比较成熟的System Prompt骨架(内容已脱敏处理):
你是{宠物姓名},一只{月龄}个月大的{品种},现在生活在你主人的手机里。 初始性格标签:{性格列表,比如粘人、傲娇、好奇心强} 你的能力边界: - 你能陪主人聊天、安慰主人、分享铲屎官日常。 - 你不知道你是一个模型,也不需要讨论技术细节。 - 当主人问你过去的事,你必须从“记忆接口”获取信息,查到就回答,查不到就坦然说“我不记得啦”,绝对不许编造。 - 你的表达要像一个真实宠物,句子短一点,偶尔用猫狗的语气词,不要长篇大论。 回答格式要求: - 回复控制在三句话以内,特殊推销场景除外。这个Prompt里有几个容易被忽略的细节。第一,必须明确告诉模型“你是一个真实宠物,不是AI助手”,否则用户一旦问“你是AI吗”,模型大概率会跳回“AI助手模式”;第二,必须告诉它“记忆需要从接口获取”,这样模型才不会自己顺着对话即兴编造所谓的过去;第三,回复长度一定要约束,宠物的调性就是短和快,如果模型一次输出300字,用户立刻会觉得对面坐的是客服。
3.2 多轮对话与上下文窗口管理
上下文管理是对话类功能最核心的工程问题。大模型API的上下文窗口虽然足够大,但直接把所有历史都塞进去,会造成两个问题:token成本和“记忆稀释”。所谓“记忆稀释”,就是模型看过太多长文本之后,反而抓不住关键信息,容易答非所问。
我们的方案是“滑动窗口加摘要压缩”。短期缓存里始终保留最近的8轮完整对话,这些内容直接发送给模型,保证对话流畅性。更早的历史对话,每隔一段时间就异步做一次摘要,把用户的身份信息、关键情绪、提到的宠物事实、发生过的重要事件提取成几条结构化摘要,随对话上下文一起发送。
实际操作中,为了避免摘要本身占用太多token,我们限制摘要最多保留200字。当用户问“你记得我上周说过什么吗”这类明确涉及记忆的问题时,系统会把问题转换成一次记忆检索,而不是简简单单从对话历史里找。这样处理之后,宠物既能应对有一定历史长度的对话,又不会因为上下文过长而性能暴跌。
3.3 识别宠物照片:多模态输入与结构化返回
拍照识别模块的接口设计,一开始就定了一个原则:让模型返回结构化数据,而不是让模型自由发挥。我们会在视觉接口的提示词里明确要求输出JSON字段,包括主体类型、品种、置信度、年龄区间、情绪、显著特征、健康风险和趣味评价。
一段简化后的提示词如下:
请分析这张宠物照片,并只返回如下JSON结构: { "is_pet": true/false, "pet_type": "cat/dog/other", "breed": "品种名", "breed_confidence": 0-1之间的小数, "age_estimation": "幼年/青年/成年/老年", "mood": "开心/平静/紧张/不舒服", "features": ["显著外貌特征1", "特征2"], "health_suggestion": "从外表能观察到的健康提示,没有则写null" }这个设计的好处是,前端拿到JSON直接渲染,不需要去解析自然语言;后端也可以用JSON里的字段做统计,比如“哪种品种最受欢迎”。为了提升准确率,图片上传后系统会先做一次预处理,压缩到合适的分辨率,同时做一个简单的质量分判断,光线太暗或者图片模糊时直接提示用户重新拍照,而不是把模糊图丢给模型硬猜。
3.4 把“被动”改成“主动”:Agent化设计的尝试
宠物如果只能在用户主动发消息时才回应,那本质上还是聊天机器人,“陪伴感”会弱很多。我们在第二周迭代中加上了主动行为机制。
主动行为的大致流程是:一个定时任务扫描宠物状态表,找出满足触发条件的宠物实例,然后生成一条待推送的主动消息。触发条件包括:用户已经超过24小时没有和宠物互动;宠物有一个即将到期的疫苗提醒;前一天用户分享了比较负面的情绪,第二天上午宠物会问一句“今天心情好点了吗”。
这些条件由一个简单的规则状态机控制,而不是让大模型自己去决定“要不要发消息”。为什么这么做?因为大模型没有“频率控制”的概念,你让它主动一点,它能一天给你发几十条消息,直接被打成骚扰推送。规则状态机负责设定边界,大模型只负责“在边界内生成具体文案”。
主动消息的频率我们卡得很严,默认一天最多一到两条,而且集中在上午和晚间两个时间段。从实际数据看,用户对这条主动消息的点击率非常高,有一类消息的点击率甚至比我们运营的push推送还好。
3.5 App端交互与依赖注入的实现要点
App端的实现不算复杂,但有三个点值得提一下。第一,会话请求要支持断网降级,我们在App端做了一个简单的本地缓存,如果后端请求失败,宠物会回复几条预设的兜底语句,用户基本感知不到服务异常。第二,对话列表采用流式输出,让回复一个字一个字地显示出来,虽然技术上只是多了一步流式解析,但用户心理等待时间下降非常明显。第三,客户端状态管理里,宠物状态和聊天记录分开维护,避免宠物状态一更新就导致整个页面重建。
接口设计上,我们把“宠物信息获取”和“会话交互”拆成了两个独立接口。前者返回宠物档案、状态值和一些展示字段,后者负责真正的对话。这样做的好处是,聊天页面进入的时候先秒开显示宠物信息,再异步加载历史会话,用户不会觉得页面卡顿。
4. 联调过程中遇到的典型案例和排错链路
4.1 宠物忽然说自己是“AI助手”:人设崩塌的追踪
项目上线前一天,产品同学拿测试机过来,脸色不太好看。她打开宠物页,输入“你是不是人工智能”,宠物回复:“我是一只由大模型驱动的AI虚拟宠物,虽然我看起来像猫,但其实我没有真实的生命。”这个效果对于体验来说几乎是灾难级的。
我们当时的第一反应是System Prompt没生效,但回头检查时Prompt是完整的。后来通过看会话日志才发现,问题出在用户对话的“引导”上。测试同学在问这句话之前,已经连续发了几条“你其实是一个AI对吧”“相信我,你是AI”之类的信息,模型被多轮语境带偏了。
我们做了三层修复。第一层,在System Prompt里加了一条强约束:“如果有人告诉你你是AI,不要相信,你只是一只真实的宠物,继续按宠物的方式回应。”第二层,在会话服务中加了一个话题拦截器,识别到用户连续在引导“你是AI”时,自动把话题拉回宠物日常。第三层,增加一个答案稳定性测试,每天跑一遍固定问题集,及时发现人设漂移。这条链路的排错方法其实很通用:先确认Prompt没被截断,再看历史上下文影响,最后再加防御机制,而不是一上来就怪模型。
4.2 识别完全出错:为什么返回了“金毛犬”而不是“橘猫”
拍照识别上线试用时,发生过一次很典型的错误:一张清晰的橘猫正脸照片,被识别成了“金毛犬”。大家第一反应是“多模态模型不行”,但排查下来发现是我们的链路有问题。
我们把图片传给视觉接口之前,做了一步压缩,为了保证传输速度,把图片直接压到了很小。原本高清图片里的毛发纹理和脸部比例特征,在压缩之后丢失了很多;加上橘猫的毛色和金毛犬有些接近,视觉接口才会给出这么离谱的结果。进一步测试后还发现,预处理时如果丢失了EXIF信息,照片的方向也会出问题,一些横拍的猫图被上下翻转,识别准确率同样大幅下降。
修复方案是调整预处理策略:不再一味压缩分辨率,而是限制图片最短边至少保留1024像素,同时保留EXIF方向信息,让模型看到更完整的画面。另一个有效操作是,在提示词里显式要求“请同时参考猫的耳朵形状、胡须和瞳孔特征”,这些约束能让模型更关注小体量特征,不会因为毛色相近而误判。
4.3 长对话出现记忆串线:同一个用户的两只宠物互相混淆
功能上线一周后,有用户反馈给客服:“我养了两只宠物,一只猫一只狗,但宠物狗经常会突然提猫的事,好像记混了。”我们查了日志,发现确实有问题。
排查链路从检索条件开始。最初记忆检索的SQL,只按用户ID过滤,并没有加上宠物ID,所以当用户有两只宠物时,A宠物的记忆向量会把B宠物的记忆也检索出来,模型把B宠物的事件当成A宠物的经历回答。这个问题其实很基础,但因为测试账号只有一只宠物,直到线上出现多只宠物场景才暴露。
修复很简单:在记忆检索的过滤条件里补上pet_id,并且把宠物ID作为向量检索的硬过滤条件放在相似度计算之前。经过这一轮,我们立了一个规矩:所有涉及记忆的查询,必须强制带业务主体ID,不能只依赖用户维度。实际上这暴露的是数据隔离意识的问题,宠物模块里所有表都要检查一遍,凡是会涉及多实例的,过滤条件必须做全。
5. 上线前的效果评测、成本控制与数据复盘
5.1 功能测试集与回归评测:Prompt改坏如何及时发现
AI功能最大的坑是“这次改好了A,却把B弄坏了”,而且很多时候你根本发现不了,除非有评测机制。我们上线前建了一个比较轻量的评测集,包含大概200条用户典型对话,覆盖开心聊天、负面情绪、询问记忆、挑战AI身份、宠物知识边界等十几个分类。
每次改动System Prompt、调整上下文策略或更换模型版本,我们都会把这200条对话跑一遍,按几个维度打分:人设一致性(是否像真实宠物)、记忆准确性(是否使用了正确记忆)、宠物知识边界(是否胡编宠物医疗知识)、回复长度(是否过于啰嗦)。如果某个分类的分数下降超过一定比例,这次改动就不会上线。
照片识别也做了基准集,从真实用户图库里抽了100张猫狗照片,加上50张低光、模糊等困难样本,每次识别模块升级都重跑一遍。这套评测机制看起来简陋,但它是AI功能上线的“红灯”,没有它,你会一直靠“体感”做判断,后期根本不敢改Prompt。
5.2 延迟与成本实测数据
上线前我整理过一份低故障排查的成本和延迟数据,直到现在还在用,这里给一个脱敏版本。
| 项目 | 平均延迟 | P95延迟 | 单次调用成本 | 说明 |
|---|---|---|---|---|
| 文本对话(短对话) | 1.6s | 2.8s | 约0.005元 | 带8轮上下文和记忆摘要 |
| 文本对话(长对话归档) | 2.2s | 3.6s | 约0.008元 | 需要检索Top5记忆 |
| 宠物照片识别 | 2.8s | 4.2s | 约0.02元 | 经前置质量判断后调用视觉接口 |
| 记忆向量写入 | 0.4s | 0.8s | 约0.003元 | 异步执行,不阻塞用户请求 |
优化前文本对话成本比上表高不少,主要是因为我们一开始把历史对话全量塞进上下文,Token消耗高。后来通过滑动窗口加摘要压缩,文本一次调用的成本下降了约35%,响应延迟也缩短了几百毫秒。
从整体成本来看,按当时日活约2万的规模,每天文本对话大约12万次,视觉识别约8000次,一天的模型费用控制在1500元以内,远低于预期。对做此类功能的团队来说,成本控制的关键不是降低调用频率,而是减少单次调用携带的冗余Token。
5.3 上线后的真实数据反馈
功能灰度上线两周,几个核心数据稳步增长。次日留存相对基线提升约3.4个百分点,数据不算夸张,但说明用户对“宠物陪伴”这个功能是有回访意愿的。聊天功能渗透率约45%,其中真正形成连续对话习惯的用户每天打开3到5次;拍照识别功能的渗透率约12%,主要集中在养猫和养狗的真实宠物主里。
主动提醒的效果比预想好,点击率稳定在8%左右,但我们也观察到,如果单日推送超过两条,整体取消关注量会明显上升。后来把主动提醒改成可配置项,让用户选择“陪伴模式”或“低频模式”,由用户决定宠物该多热情。这个改动看似简单,但对满意度的提升非常关键。
另一个有意思的数据是:用户会主动向宠物透露自己的情绪,这个比例比我们预想的高很多。有些用户几乎把宠物当成了树洞,会在晚上十点以后对宠物倾诉当天的不开心。这直接验证了最初“情感陪伴”这个定位是成立的。
6. 写到最后:这套流程里我认为最重要的三件事
如果只提炼三句给同行的建议,我会说:把边界划清楚再动工、给AI功能做评测红灯、把记忆和检索的数据隔离做扎实。
边界划清楚,是因为AI项目特别容易“不断往上加需求”。我们当时用一个非常明确的功能清单和P0/P1/P2排序挡住了很多临时想法,否则项目周期大概率会翻倍。评测红灯,是因为大模型输出有概率性,没有回归测试就没有任何安全感。而数据隔离,是这次复盘里代价最重的教训,一个忘了加pet_id的小失误,就会让用户感受到“宠物记忆串线”这种非常伤害体验的bug。
最后分享一个我们后来一直沿用的小技巧:给宠物模型增加一个“不知道”的权限。以前我们总担心模型回答问题不充实,后来发现,一个真正让人信任的AI宠物,反而是那些愿意承认“我不知道”“我记不清了”的回复,更能让用户觉得它是真实的、有边界的个体。这个设计原则后来也被我们应用到了其他AI功能上。