news 2026/9/19 5:44:26

AI陪伴机器人把记忆塞进Prompt的代价-为什么只能取20条

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI陪伴机器人把记忆塞进Prompt的代价-为什么只能取20条

04-把记忆塞进Prompt的代价-为什么只能取20条

黒漂技术佬的 AI 伙伴(AI-Partner)源码拆解系列。前面三篇把"记忆怎么存、怎么查、怎么注入"讲完了。本篇算一笔容易被忽略的账:把用户的记忆塞进系统提示词,到底要花多少 Token、多少延迟、多少银子?以及为什么"只能取 20 条"是个不得不做的取舍。

一、先建立直觉:系统提示词是"每轮都重发"的固定成本

很多人以为"记忆注入"只发生一次。错。在 AI 伙伴(AI-Partner)里,每次用户发一句话,后端都会走PersonaProvider.buildSystemMessage(userId)重新拼一遍系统提示词,连同这句用户消息、短期记忆窗口一起发给大模型。

也就是说:系统提示词 = 每次对话都付一次的固定开销。记忆越多、写得越啰嗦,这个固定开销就越大。它不是一次性的,是"对话次数 × 系统提示词长度"的累积。

所以"取多少条记忆"不是体验问题,是钱和延迟问题

二、Token 成本量化估算

先说计数口径。项目里ChatService.estimateTokens的规则是:中文字符约 1 字 1 token,非中文字符约 4 字符 1 token。我们就按"中文 1 字 ≈ 1 token"粗算系统提示词的构成(以下为估算,便于建立量级感,非精确测量)。

系统提示词由四段拼成(回顾PersonaProvider):

段落内容估算 Token
固定人设PERSONA_RULES性格 + 能力边界~300
【当前时间】形如2026年9月14日 14:30~15
【用户信息】昵称/角色/手机/生日 + 画像摘要(可选)基础 ~80;画像摘要最长 ~2000
【长期记忆 20 条】每条[类型·重要度X] 内容见下
【回复要求】6 条共情/字数/求助等规则~200

单条记忆的写法:[偏好·重要度3] 用户喜欢喝绿茶,前缀约 12 token + 正文约 10-30 token,取均值约 30 token/条。20 条就是:

30 token × 20 条 = 600 token(记忆段)

于是两套典型场景的系统提示词体量:

场景系统提示词 Token 估算
轻量用户(无画像摘要,20 条短记忆)300+15+80+600+200 ≈1195 token
重度用户(画像摘要 2000 字 + 20 条记忆)300+15+2080+600+200 ≈3195 token

再叠加每轮对话本身的"用户消息 + 短期记忆窗口(最多 12 条) + 模型回复"。即便用户只说"在吗",系统提示词那上千 token 也已经先发出去了。

关键结论:系统提示词是"沉默的固定税"。一次对话真实消耗的 Token ≈ 系统提示词(固定) + 短期记忆窗口 + 当轮问答。记忆段是系统提示词里最大、也最该被压缩的一块。

三、上下文膨胀:对延迟和费用的双重打击

把记忆无节制地塞进去,会从两头反噬:

1. 费用:输入 Token 累积。
虽然输入 Token 单价通常低于输出,但系统提示词每轮重发,对话量一大就是实打实的开销。假设系统提示词 1200 token、日均单用户 30 轮对话,那就是1200 × 30 = 36000 token/天/用户纯系统提示词消耗——而且这还没算问答本身。用户上了规模,记忆段每多塞 10 条,成本线性上涨。

2. 延迟:首字时间被拖长。
大模型处理请求时,要先"读完"整个上下文才开始吐字。系统提示词越长,****首字延迟(time-to-first-token)****越大,用户会明显感觉"机器人反应慢了"。陪伴场景里,慢半秒都影响"像家人"的体感。

3. 注意力稀释:贵且效果差。
更隐蔽的代价——上下文越长,模型越容易在海量记忆里"视而不见",真正该用的记忆被淹没。这叫"lost in the middle"现象。所以塞得多 ≠ 陪得好,反而可能更差。

四、为什么"只能取 20 条":一个工程取舍

回到源码,两处硬截断到 20:

  • PersonaProvider.buildSystemMessage.limit(20)注入系统提示词。
  • MemoryService.search.limit(20)工具检索返回上限。

20 是三层约束下的平衡点:

约束对条数的拉扯
Token 预算希望越少越好(省钱、快)
个性化质量希望越多越好(记得全)
注意力上限希望精选而非堆量(避免稀释)

20 条在"够个性化"和"不撑爆上下文"之间取了中间值。配合第 03 篇讲的importance DESC排序,保证被截掉的 20 名开外,本来就是相对不重要的记忆——截断有优先级保护,不是随机丢

但要诚实指出:20 是个经验值,不是算法最优解。它的成立依赖"重要度排序靠谱"。如果模型把闲聊都打 5 分(第 02 篇提到的风险),20 条里就会混进噪音,截断也救不了。

五、当前实现里一处可优化冗余

PersonaProvider源码会发现一个小浪费:同一条记忆可能出现在两个地方

系统提示词先写【用户信息】段,里面含personaSummary(画像摘要,来自MemoryService.refreshPersonaSummary前 10 条记忆的content用";"拼成);随后又写【长期记忆】段,取前 20 条记忆。于是前 10 条记忆,既在画像摘要里,又在记忆列表里——重复注入了

// 示意:系统提示词里前 10 条记忆出现两次 【用户信息】 - 画像摘要:用户喜欢喝绿茶;用户有一只叫团团的猫;...(前10条) 【关于这位用户,你记得(长期记忆)】 [偏好·重要度3] 用户喜欢喝绿茶 ← 重复 [关系·重要度3] 用户有一只叫团团的猫 ← 重复 ...(前20条)

这不算 bug(画像摘要和记忆列表角色不同:一个是"一句话印象",一个是"可逐条引用"),但从 Token 角度看,前 10 条记忆被算了两次。重度用户那 2000 字画像摘要,和记忆段高度重叠,正是系统提示词膨胀的主因之一。优化的第一刀,就该切这里。

六、进阶方案:三层记忆与召回打分

想既"记得多"又"塞得少",业内通用思路是不要把所有记忆都常驻提示词,而是分层 + 按需召回。

方案 A:分层注入(hot / warm / cold)

内容是否进系统提示词
热记忆 hot最近 + 高重要度(如 top 8)常驻注入
温记忆 warm其余生效记忆不注入,模型用searchMemory按需翻
冷记忆 cold历史/低频不注入,归档

这样系统提示词只背"最该当下用"的少量记忆,模型需要时再调工具翻笔记——和第 02 篇的searchMemory天然契合。

方案 B:摘要压缩
personaSummary已经是摘要思路的雏形。可进一步:把 20 条压成 3-5 句"用户画像简介"常驻,细节全走按需检索。相当于"给人设喂简历,不给档案"。

方案 C:召回打分(向量/语义)
当前排序只按importance + createdAt,是规则排序,理解不了"用户问猫,该调出团团那条"。引入 Embedding 后,可按"当前对话语义 × 记忆相似度"打分,挑最相关的 20 条——解决第 03 篇说的"字面匹配近义词盲区"。代价是要接向量库(项目目前没接,属可扩展方向)。

诚实边界:上面 A/B/C 均不是当前t_memory/PersonaProvider的现有实现。项目用的是"重要度排序 + 20 条截断 + 画像摘要"三板斧,已能跑通;进阶方案是给二次开发指的路,别当成已完工的功能写进文档。

七、一个具体的量级推演

光说"贵"没感觉,我们代入一组假设数字,看记忆段膨胀如何放大成月度账单。口径仍是"中文约 1 字 1 token",且仅算系统提示词里的记忆段,便于看清趋势。

假设单用户日均对话 30 轮,分三种记忆体量:

记忆体量系统提示词记忆段 Token系统提示词合计(估)日系统提示词 Token月系统提示词 Token
轻度(8 条短记忆)~240~835~25,050~75 万
当前默认(20 条)~600~1,195~35,850~107 万
贪婪(50 条)~1,500~2,095~62,850~188 万

只看"记忆段从 20 条涨到 50 条"这一步:系统提示词月消耗从约 107 万跳到 188 万,涨了约 75%,而用户感知到的陪伴质量未必同步提升——因为 50 条里大量是低重要度闲聊,模型反而更易"lost in the middle"。这正好反证"掐在 20 条"的合理性:边际记忆带来的体验收益,远抵不过边际 Token 成本

再叠加短期记忆窗口(最多 12 条,第 02 系列讲过memory-window-size=12)和用户消息,每轮真实消耗还会更高,但那是"对话本身"的开销,记忆段是其中唯一可由我们主动压缩的大头。所以优化记忆注入,是性价比最高的一刀。

八、可落地的优化清单

如果你要在现有代码上动刀,按性价比排个序:

  1. 消除重复注入:系统提示词里画像摘要与记忆段去重,或二选一精简(省下重度用户近 2000 token)。
  2. 下调常驻条数做 A/B:把limit(20)抽成配置项(如memory-inject-size,默认 20),按需调到 8-12 观察陪伴质量。
    // 示意:把硬编码的 20 提为可配置项@Value("${ai.partner.llm.memory-inject-size:20}")intmemoryInjectSize;// 使用处:.limit(memoryInjectSize)
  3. 缩短content存储:存记忆时让模型写"精炼一句",避免塞进整段闲聊(1000 字上限别用满)。
  4. 系统提示词缓存:同一userId在记忆未变时缓存buildSystemMessage结果,别每次查库重拼(注意时间字段需单独刷新)。
    // 示意:用 userId -> 系统提示词 的缓存,记忆变更时失效Map<Long,String>systemCache=newConcurrentHashMap<>();// save/remove 触发 refreshPersonaSummary 后,一并 systemCache.remove(userId)
  5. 监控 Token 用量Conversation.tokenUsage已在落库,定期看均值,定位"记忆肥胖"用户。
  6. 引入向量召回(远期):接 Embedding 服务,把"重要度排序"升级为"语义相关度排序",提升 20 条的命中率。

合规提醒:把个人记忆注入提示词,等于把用户的生活片段(可能含健康、家庭、位置)每轮都外发给大模型服务商。请务必:① 仅注入陪伴所必需的记忆,遵循数据最小化;② 对外发内容做脱敏评估,敏感字段(如精确手机号、详细住址)谨慎进入提示词;③ 明确告知用户数据会被用于处理对话,并取得授权;④ 老人/儿童数据采用更保守的注入策略。记忆越全,责任越重。

算清这笔账后会发现:陪伴机器人"记得多"的浪漫背后,是 Token、延迟和隐私的三重成本。"取 20 条"不是偷懒,是在成本与体验之间画的的一条理性分界线。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 5:37:48

MATLAB/Simulink在BMS仿真分析中的建模、SOC估算与代码生成

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 5:37:36

CANN opbase 算子开发:aclTensor::SetData 接口详解与源码实现

CANN opbase 算子开发&#xff1a;aclTensor::SetData 接口详解与源码实现 【免费下载链接】opbase 本项目是CANN算子库的基础框架库&#xff0c;为算子提供公共依赖文件和基础调度能力。 项目地址: https://gitcode.com/cann/opbase 本指南围绕 CANN 算子库基础框架 op…

作者头像 李华
网站建设 2026/9/19 5:37:14

从 npm publish 到 npx 使用:命令行工具发布全流程与高频报错排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 5:32:11

Vue3 + Three.js 三维可视化大屏项目实践指南

最近在折腾三维可视化大屏&#xff0c;一套比较稳妥的组合是Vue3 Three.js。用这套方案做3D模型与数据互动很顺手&#xff0c;比如设备模型按实时状态变色、点击模型弹出指标、数据变化驱动模型动作等。我把这套实现方案从工程搭建到项目落地完整拆一遍&#xff0c;如果你正在…

作者头像 李华