你有没有遇到过这种情况:跟某个AI助手聊了大半个月,它依然会在第二天早上问你“早上好,请问今天有什么可以帮您”。你上个月刚说过自己凌晨两点才下班,它转头就能在深夜给你推一段“提神醒脑”的播客。你反复跟它强调过家里有个三岁小孩,它还是会在工作日白天安排一场“家庭影院观影提醒”。
这就是没有“记性”的AI助手最让人抓狂的地方。
AC-AIBot 这个项目要解决的,正是这个问题。它把“全局记忆”作为核心能力搬进了AI助手,配合专门为客厅大屏设计的展示模式,让助手从“你说一句它答一句”的问答机器,变成一个真正记得住你的偏好、习惯、日程甚至情绪状态的家庭智能体。这篇文章我会把整个设计思路、记忆系统的分层方案、大屏模式的适配逻辑,以及实际开发中踩过的坑完整拆一遍,希望能给正在做类似项目的开发者一点参考。
1. 核心需求拆解:先搞清楚什么叫“有记性”
1.1 “说完就忘”的根因不在模型,在架构
很多人以为AI助手记性差是模型能力不行,换个大参数模型就完事了。实际上,绝大多数助手的“失忆”问题出在架构上——每次对话session都是独立的,服务端拿到请求后,能看到的只有当前这轮消息和上下文窗口里有限的消息记录,之前的交互早就被丢弃了。
这种无状态设计的初衷是好的,节省资源、保护隐私、避免上下文污染。但在“个人助手”这个场景里,它天然是反人性的。用户跟助手聊“我下周要去出差”,本质上是希望助手记住“下周出差”这个事实,并在后续推荐行程、提醒天气、规划任务时主动关联。可无状态架构下,助手听到这句话之后立刻忘光,所有后续交互都得重新解释一遍背景。
AC-AIBot 的思路是,在模型层之上额外构建一层“记忆管理层”。聊天记录、用户偏好、长期目标、家庭信息这类数据,经过结构化和语义化处理后单独落库,每次对话开始时先做记忆召回,把和当前话题相关的记忆片段注入到上下文里。这样模型本身没变,但“记得”这件事被系统化地解决了。
1.2 哪些场景最吃“记忆”
不是所有AI助手场景都迫切需要全局记忆,但下面这几类场景,缺了记忆能力基本就是残废。
家庭场景是最大刚需。家里可能有四五口人,每个人偏好不同,老人要看戏曲频道,小孩要听睡前故事,年轻人关注球赛和电影。助手如果分不清说话的是谁,记不住各自的偏好,那它在家庭环境里就是个摆设。AC-AIBot 初期最核心的落地场景就是客厅,靠声纹或账号体系区分家庭成员,每个成员拥有独立的记忆空间,但又能共享家庭公用信息,比如“周六全家去郊游”这种事件,家庭成员都能看到。
个人工作场景也很依赖记忆。比如习惯把重要文件放在哪个目录、常用哪几个编辑器、项目术语是什么、合作对象的称呼是什么。这些信息不是一次对话能讲完的,而是长时间协作中一点点沉淀下来的。助手如果能记住这些,价值会成倍提升。
还有一种是“长周期任务”场景。比如用户在减肥,每天问饮食建议,如果助手能记住昨晚他说“今天吃了火锅”,下次推荐时就会自觉地避开高油高盐方案。这种跨天的连续性,没有全局记忆根本做不到。
1.3 大屏场景为什么特殊
AC-AIBot 专门做了“大屏模式”,是因为大屏和手机/电脑的使用逻辑完全不同。手机是私密、近距离、高交互频率的设备,用户随时拿起来打字或语音输入;大屏则通常是客厅的电视或智能镜子,距离三到五米,交互方式以遥控器或远场语音为主,信息密度不能太高,长时间停留在同一个界面。
大屏还有一个特点:它是“共享设备”。一家人在客厅里,谁都有可能喊一声助手。所以大屏模式下信息展示必须有“公共性”,不能把某个成员的个人偏好霸占整个屏幕,同时又得让每个成员能快速进入自己的专属界面。
这些需求叠加在一起,AC-AIBot 的大屏模式不是简单地把聊天界面放大,而是把AI助手从“对话框产品”重构成“信息面板产品”。这就引出了后面一整套设计。
2. 全局记忆系统:从设计到落地的完整思路
2.1 记忆分层:别把所有东西塞进一个袋子
AC-AIBot 里的记忆不是一锅粥,而是分了层级的。我把整个记忆体系分成四层,对应不同的存储周期和重要程度。
第一层是“瞬时记忆”,保存的是当前会话中的临时状态。比如用户正在跟助手讨论周末去哪个公园,聊到一半说“等一下我再想想”,这种过程性信息不需要长期保存,会话结束就清理。实现上直接放在进程内存或Redis,设置过期时间就行。
第二层是“短期记忆”,通常是最近几天的交互摘要。比如用户前天问过“附近有什么好吃的川菜馆”,助手推荐了某家店,但是用户后来没去。这个信息属于短期热点,存个三五天就够了,到期后如果一直没被重复提及,就自动降级清理。
第三层是“长期偏好”,这类记忆最珍贵,包括用户的饮食禁忌、作息时间、喜欢的语言风格、家庭成员构成、常用设备等。一旦写入,基本不会过期,需要永久保存。这类记忆必须经过确认机制才能写入,不能看一眼聊天记录就全塞进长期库,否则会存一堆垃圾。
第四层是“语义记忆”,保存的是助手通过学习总结出来的规则性知识。比如用户每次问天气都会同时问空气质量,系统自动归纳出“该用户关注空气质量”这条语义记忆,写入长期库后,用户只问天气也会收到空气质量信息。
分层的意义在于,不同记忆的生命周期、写入门槛和召回权重完全不同,混在一起会导致系统既存不下重要信息,又会被噪声淹没。用关系型数据库存长期记忆、向量库存语义记忆、Redis存短期记忆,各司其职。
2.2 记忆写入流程:严格设门槛
全局记忆系统最大的风险不是记不住,而是乱记。如果把用户随口一句“今天好累”当成长期偏好存下来,那记忆库很快就变成垃圾场。
AC-AIBot 的记忆写入流程我设计了三级审核:
第一级是“触发词检测”。并不是所有对话内容都进入记忆提取流程,系统会先判断当前消息是否包含可记忆的价值点。比如时间、地点、人物、偏好、目标、否定表达等关键词或句式出现时,才触发记忆提取。纯粹寒暄、闲聊、情绪发泄类内容,直接跳过。
第二级是“语义结构化”。触发提取后,会调用一个小模型做槽位填充和语义解析,把自然语言转成结构化记忆条目。比如“我儿子对花生过敏”这句话会被解析成:家庭成员=儿子,禁忌=花生,类型=过敏,严重程度=高。结构化之后,后续召回和推理才有办法使用。
第三级是“人工确认窗口”。这一步是最关键的,在聊天界面上,系统会把识别到的长期记忆以卡片形式展示给用户,并附上“我会记住这个信息”的提示。只有用户点了确认,或者这条信息在多轮对话中被反复提及且没有否定,才会写入长期记忆库。所有记忆,用户都能随时查看和删除。
2.3 记忆召回策略:不是每次都要全量搬运
记忆系统的另一个关键问题是召回。总不可能每次请求都把几千条记忆全部塞进提示词,那上下文窗口根本扛不住。
AC-AIBot 用的是“三级召回”策略。每次新对话开始时,先做关键词匹配,把当前消息里的实体词和历史记忆做精确匹配,命中则优先召回。这个能覆盖“你上次说的那个公园叫什么来着”这类明确指代。如果精确匹配不到,就对当前消息做向量化,在向量库里做相似度检索,召回语义相近的记忆片段。最后再根据时间衰减因子,把用户最近几天的高频主题拉出来做补充。
召回结果会按相关度打分排序,只取最靠前的几条注入到模型提示词里。正常情况下,一次对话注入的记忆上下文控制在1000字符以内,避免对模型形成干扰。这个参数我调了很久,注入太少了模型记不住,注入太多了模型会被陈旧信息带偏。
2.4 记忆冲突与更新
记忆系统一定会遇到冲突。比如用户上周说“我一般十一点睡”,这周又说“最近都凌晨一点才睡”。系统如果傻乎乎地覆盖更新,那就忽略了“一般”和“最近”的语义差别。
AC-AIBot 的做法是给每条记忆打时间标签和可信度标签。新记忆写入时不直接覆盖旧记忆,而是先生成一个并存条目,标记为“最近更新”,再由决策模块判断是否要合并到主记忆。如果用户说“我之前说错了,不是十一点,是十二点”,系统会识别到明确的否定修正,直接将旧条目标记为废弃。
这个机制说起来简单,实际实现时踩了不少坑,具体留在后面问题排查部分展开。
3. 大屏模式:从“对话框”到“信息面板”的范式转变
3.1 大屏UI设计:放弃聊天气泡
几乎所有AI助手在做大屏适配时,第一反应都是“把聊天界面拉宽拉大”。这个方向是错的。电视上你跟助手一来一回打字不太现实,语音输入为主,而且在客厅场景里,用户往往不是正襟危坐地等着助手回答,而是边做自己的事边听。
AC-AIBot 的大屏模式默认界面是一个“信息面板”,由三部分组成。顶部是一条常驻状态栏,显示天气、时间、待办提醒、空气质量等即时信息。中部是大焦点区,显示当前会话的核心结果,比如查询结果、推荐内容、待办列表。底部是一条横向时间轴,展示最近几天的记忆摘要,比如“周三张阿姨来电”“周五交了水电费”“昨天看了两小时电影”。
用户说一句话,助手回答后,回答内容会动态渲染到焦点区,而不是一直保留聊天记录流。这样大屏永远像一面“助理信息墙”,而不是一台只会弹对话的聊天机。视觉上清爽很多,也更适合远距离阅读。
3.2 多用户识别与公共信息适配
前面提到大屏是共享设备,AC-AIBot 用声纹+遥控器双通道识别用户。如果用户是通过遥控器进入,直接按账号切换;如果是远场语音唤起,则先做声纹识别,匹配到对应家庭成员后,以该成员的身份加载个人记忆。识别不出的人会归入“访客”角色,只开放公共记忆。
公共记忆在这个系统里是单独维护的一个命名空间。像“家里路由器周三要重启”“下个月物业检修电梯”“冰箱坏了要找人修”这类信息,属于家庭公共信息,任何成员都能查询。大屏首页展示的内容也是以公共信息为主,个人偏好信息只有在用户主动唤起时才显示,避免家庭成员之间的隐私尴尬。
3.3 大屏信息呈现的节奏控制
做电视端产品的人都有一个体会:电视不是电脑,用户不会盯着屏幕一点点读文字,信息要有“节奏感”。AC-AIBot 的大屏模式设计了一套动态展示规则:新信息到达时,焦点区内容先做高亮闪烁,控制在一到两秒内吸引注意力;如果用户没有追问,三十秒后焦点区自动切换为当前时间天气等基础信息,把屏幕让出来。
整个UI的字体基准字号我调到了屏幕高度的百分之二以上,最小可读字号不低于32像素,所有文字对比度保持在7比1以上。这些参数都是按视力正常成年人坐在三米外能清晰辨识的标准来设定的,属于大屏开发的基础功。
4. 实操过程与核心环节解析
4.1 总体架构与模块划分
AC-AIBot 的前端分为两块:一块是跑在手机/电脑上的交互客户端,负责近场对话和管理界面;另一块是大屏端Web应用,负责客厅场景的信息展示和远场交互。后端统一走一套API网关,服务层拆成六个模块:
- 语音服务:负责ASR、声纹识别、TTS。
- 对话服务:负责管理会话状态,调用大模型,组装提示词。
- 记忆管理服务:负责记忆写入、召回、更新、清理。
- 知识服务:负责外部知识检索和指令执行。
- 用户服务:负责账号、家庭成员、权限管理。
- 大屏服务:负责大屏端的推送、布局渲染、动态信息流。
记忆管理服务是核心,其他模块都通过内部接口调用它。对话服务每次收到用户请求时,先异步调用记忆服务做召回,再拿着召回结果去拼提示词。大屏服务则定时从记忆服务拉取公共信息和各成员短期动态,生成展示面板的数据。
4.2 记忆写入和召回的核心代码逻辑
记忆写入部分用的是异步管道,避免阻塞对话主流程。伪代码如下:
async def process_message_for_memory(user_id, message): # 第一级:触发词检测 if not has_memory_trigger(message): return # 第二级:结构化解析 structured = extract_memory_entities(message) if not structured: return # 第三级:人工确认(异步任务,推送到前端展示确认卡片) memory_item = MemoryItem( user_id=user_id, content=structured["content"], memory_type=structured["type"], source_message_id=message.id, status="pending_confirmation", timestamp=now() ) await memory_db.insert(memory_item) await notify_client(memory_item)召回部分才是重灾区,一开始我只用向量检索,结果效果很差。后来改成混合召回:
async def recall_memory(user_id, query): # 级别1:实体精确匹配 exact_hits = await memory_db.search_exact(user_id, extract_entities(query)) # 级别2:向量相似度检索 query_vec = embed(query) vec_hits = await vector_db.search(query_vec, top_k=5) # 级别3:时间衰减近期主题 recent_hits = await memory_db.search_recent(user_id, days=7, limit=3) # 合并打分去重 candidates = merge_and_dedupe(exact_hits, vec_hits, recent_hits) scored = [candidate.with_score(score_memory(recall_time=candidate.timestamp))] scored.sort(key=lambda x: x.score, reverse=True) return scored[:5]实测下来,混合召回比纯向量检索在准确率上大概提升了30%,主要收益来自实体精确匹配,它能规避掉向量检索在语义漂移上的误差。
4.3 大屏端布局实现
大屏模式我用的Web技术栈,页面按1920乘1080基准分辨率设计,用CSS Grid布局。顶部状态栏固定高度,中间焦点区自适应,底部时间轴用flex-row排列。
大屏页面最重要的一个实现细节是“自动聚焦”。电视没有鼠标,遥控器的焦点移动逻辑必须做得非常干净。我封装了一个focus遍历管理器,所有可交互组件注册后,按上下左右方向在DOM树上做几何距离计算,把最近的可聚焦元素设为活动态。
还有一个细节是远程推送。用户在手机端说“帮我记一下周三交水电费”,这个记忆经过确认后,要能实时出现在电视大屏的底部时间轴上。这里我用了WebSocket做服务端推送,记忆写入成功后由记忆服务广播一条全局事件,大屏服务监听到事件后刷新时间轴数据。延迟控制在200毫秒以内,实测体验很流畅。
4.4 数据存储:分库分场景
存储方面我用了SQLite做本地开发,生产环境切到PostgreSQL。记忆表结构大概是这样:
CREATE TABLE memories ( id UUID PRIMARY KEY, user_id VARCHAR(64), family_id VARCHAR(64), memory_type VARCHAR(16), -- instant / short_term / preference / semantic content TEXT, entities JSONB, status VARCHAR(16), -- pending / confirmed / deprecated source_message_id VARCHAR(128), created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, expires_at TIMESTAMPTZ );索引方面,最常用的查询是WHERE user_id = ? AND status = 'confirmed',所以联合索引必须建上。向量字段存储在单独的向量库里,只存embedding向量和记忆ID,其他信息都回表查,避免向量库和业务数据过度耦合。
5. 常见问题与排查技巧实录
5.1 记忆串线:家庭成员身份识别错误
做多用户大屏智能助手,第一个坑绝对是身份识别。某次测试时,我对着电视说“帮我点一首周杰伦的歌”,结果系统把这条指令记到了家里另外一个成员的记忆库里面。后来排查发现,ASR转文本的同时做了声纹识别,但声纹置信度低于阈值时走了默认兜底逻辑——直接归给了“家庭成员A”。
这类问题不能靠提高声纹阈值解决,因为远场麦克风采集到的声音质量本来就不稳定。稳妥方案是增加二次确认机制:声纹识别置信度不高时,大屏顶部弹一个小气泡“请问您是?”列表显示家庭成员头像,让用户用遥控器确认。同时,高置信度声纹才允许写入个人长期记忆,低置信度一律记入公共记忆,宁可不记也不能记错。
5.2 记忆膨胀:系统越用越“笨”
项目运行到第三周,我明显感觉到助手回复质量下降了,经常会扯出一些跟当前话题无关的陈年旧事。查了一下记忆库,发现长期记忆里已经塞了三千多条数据,其中大概一半是重复信息或过时信息。
原因出在记忆合并策略太弱。用户说“我最近开始跑步”和“我昨天跑了五公里”,系统分别存了两条长期记忆,但实际上这只是同一条偏好的递进更新。后来我补了一个“记忆合并日志”机制:每次写入新记忆时,都会在语义空间里跟旧记忆做相似度匹配,相似度超过阈值的,自动合并内容并更新时间戳,而不是新增条目。
同时我加了一个“记忆衰减”定时任务,每24小时跑一次,把短期记忆里超过七天且从未被召回过的记录标记为过期清理。这个规则能让记忆库瘦身不少,系统回复的聚焦度有了明显改善。
5.3 大屏渲染性能:内存占用持续走高
大屏端在开发早期有内存泄漏问题。因为底部时间轴每收到一次WebSocket推送都会重新渲染整个DOM树,页面连续跑两三个小时后,内存占用能从80MB一路涨到700MB。而且电视盒子性能参差不齐,低端设备卡顿非常明显。
排查后发现是事件订阅没有释放。每个推送过来创建的新组件都注册了事件监听,但组件销毁时没有解绑。另外,时间轴DOM节点只增不减,导致节点数爆炸。解决方案是给所有滚动列表加虚拟滚动,只渲染可视区域内的节点,外加全量Debounce合并推送,高频推送时只做数据累积,每两秒才刷新一次界面。
优化之后内存稳定在150MB以内,低端盒子也能流畅跑完一整天的连续展示。
5.4 大模型提示词里的记忆有歧义
记忆召回做得再怎么好,最终还是要拼进提示词给模型用。早期我直接把召回内容拼进system prompt,结果模型有时会混淆“用户说的话”和“记忆内容”。比如用户说“我不想吃辣的”,系统召回的记忆里有一条“用户爱吃辣”,模型就会理解矛盾,回答得模棱两可。
后来我在提示词里给记忆内容加了明确的标记:
以下是关于用户的长期记忆信息,来源于历史对话整理,仅供参考: [记忆] 用户家庭:4人,有两个孩子 [记忆] 偏好:不吃香菜 [记忆] 最近关注:洗衣机选购 请严格区分记忆信息与当前对话内容,当前对话中与记忆冲突的信息以当前对话为准。加了这段标记之后,模型的回答准确度高了很多,也不容易出现自我矛盾了。这个细节强烈建议大家在做类似系统时都要加上。
6. 数据安全与隐私保护:记忆这东西是把双刃剑
全局记忆系统最大的争议在隐私层面。一个能记住你所有偏好的助手,意味着它掌握了大量个人数据。AC-AIBot 的设计原则是“用户可看、可改、可删”:记忆管理页面上,每条长期记忆都展示来源、创建时间、最近引用时间,用户可以单独删除或一键清空全部记忆。
还有一个很实用的功能是“记忆休眠”。用户可以在大屏或手机上设置一个时间段,比如“每天晚上十一点后不写入长期记忆”“周末不写入工作偏好”。这样能防止非活跃状态下系统偷偷积攒信息。这个功能的初衷是给用户一个心理安全阀——不是系统不让记,而是用户决定记不记。
在存储层,所有记忆数据落库前都做字段级加密,对话记录和记忆表分开存放。模型调用时,记忆内容只在单次请求中注入,请求结束即销毁。这些做法能在一定程度上缓解用户对隐私的担忧,但说实话,这仍然是一个需要持续迭代和公开透明的工程。
关于记忆系统,我个人的体会是:做一个“有记性”的AI助手,技术难度其实还在其次,最难的是做取舍——记住什么、忘掉什么、什么时候主动更新、什么时候保持沉默。这已经不只是工程问题,而是产品哲学问题。AC-AIBot 目前的方案肯定不是最优解,但方向是对的。如果你也在做类似方向的项目,我建议从家庭场景或办公场景中选一个切入,先把记忆闭环跑通,再考虑加更多花哨的功能。另外,所有记忆相关操作都要加上日志和审计能力,这东西后期排查问题的时候真的能救命。
这个大屏模式和全局记忆的组合,后续还能扩展出不少东西。比如根据记忆生成家庭成员的每周偏好报告,或者在多模态大模型成熟后,让电视画面根据用户习惯自动生成专属动态壁纸。这些方向都值得继续试,等我有新进展了再同步给大家。