首页 / 文章

在严格令牌预算限制下的人工智能智能体的客户记忆系统

智能体记忆层如何提取事实、评估重要性、以0.92的余弦值去重、将向量存储在pgvector中,并在2,048个令牌的限制下进行检索并实现淘汰机制。

发布者:REACTAPP.TOP阅读约 8 分钟1942 词

来源: Engineering a Multi-Agent AI Platform — Part 6: Memory Under Token Constraints

客服代表与客户交流时,所有所需信息都必须容纳在同一个上下文窗口中。GPT-4o拥有128K个标记,而较小的模型可能只有32K或更少。该窗口需同时包含系统提示、工具定义、检索到的知识、当前的对话内容,以及代表对该客户所记住的各类信息。本文介绍了针对这种场景的记忆设计方案:即如何从已结束的对话中提取事实、对其进行评分、去重、以向量形式存储,并在严格的标记限制下再次调取这些信息。

该窗口是预算限制,而非无限容量

若将关于客户的全部信息都放入提示词中,其他输入内容就会被迫压缩。知识检索会被截断,对话历史也会被删减,而系统提示的位置可能会退得过远,导致模型忘记自身的操作指令。

如果不设置任何限制,系统会将每次对话都视为初次交流。有客户询问预约时间时,即便他们上周二就已经提供过该信息,系统仍会回复“我没有这些信息”。

解决此问题的方法是为客户的记忆容量设定固定上限。在本文描述的设计中,该上限为2,048个标记。系统会在这一范围内收录当前对话中对该客户而言最重要的信息——并非所有信息,也不是随机选取的,而是最相关的那些。

从已完成对话中提取信息

对话结束后,会有一套整合流程对其进行分析,筛选出值得保留的信息。提取过程会使用大语言模型(GPT-4o-mini),并通过结构化的提示语来询问可以获取哪些关于客户的事实信息,同时针对每条信息的准确性、实用性及重复出现的可能性进行0到1的分级评估。

“具体”是关键所在。所描述的事实必须针对该客户,而非泛泛而谈。“更喜欢上午预约”属于事实,“提出了一个问题”则不属于。

模型会以JSON数组的形式回复,每个元素包含一个文本字符串和一项重要性得分。处理流程只会保留得分至少为0.5的事实,这样就能剔除那些得分通常在0.2到0.3之间的无关信息,同时保留可能对后续有帮助的内容。

一次典型的十条消息的交流通常能产生两到四个符合条件的事实,而长时间的故障排查过程则可能产生六到八个。大多数对话至少能产生一个符合条件的事实。

重要性得分所体现的内容

这个0.0到1.0之间的数值综合了三项特征。

  • 具体性。该信息对这位客户而言有多具体?“更喜欢通过邮件沟通而非电话”属于具体信息,而“是客户”则不属于。
  • 可操作性。客服人员能否利用它来在下次提供更好的回复?“发票#4821存在反复出现的问题”符合要求,而“打了招呼”则不符合。
  • 重复性。该信息在多次对话中还会重要吗?每次安排时间时都会出现UTC+3时区信息,但关于今天天气的问题只会出现一次。

设计团队尝试为每个维度单独打分,发现综合得分能更准确地表征信息,同时也能让提示更简洁。根据他们的测试,0.5的阈值可以过滤掉大约60%的提取信息,仅保留信号较强的那些。

嵌入模型与重复内容控制

每个保留下来的事实都会被转化为一个3072维的向量,这一数值恰好是所使用的嵌入模型——OpenAI的text-embedding-3-large的输出维度。更高的维度能够实现更精细的相似度区分,这在需要区分改头换面的重复内容与真正的新信息时尤为重要。

在保存新事实之前,会先将其与该客户所有的现有事实进行比对。若余弦相似度高于0.92,则判定为“重复内容,跳过”。

为何是这个数值?人们表达同一件事的方式多种多样。“更喜欢通过电子邮件沟通”、“希望收到电子邮件联系”以及“表示不常查看手机,改用电子邮件”其实都在描述同一个核心事实。0.92的数值能让系统可靠地识别出这类语义重复。若使用0.85或0.88这样的较低阈值,就会将实际上截然不同的事实混为一谈;而0.95或0.97这样较高的阈值则会让过多的不同表述堆积起来。

该检查会在每个数据整合周期中执行。如果没有它,那些在每次聊天中都会提及自己时区的客户,将会产生数十条几乎完全相同的记录,从而因重复内容而占用过多的存储空间和查询资源。

为何选择带pgvector的Postgres

这些事实存储在PostgreSQL数据库中,由pgvector扩展负责处理相似度搜索。customer_memories表中的一行包含:

  • content,以纯文本形式呈现的事实内容
  • embedding,用于相似性搜索的向量(3072)列
  • importance,在提取时赋予的分数
  • source_channel,该事实的来源(网页聊天、WhatsApp、语音等)
  • created_at,首次插入的时间
  • last_accessed_at,最近一次在聊天中使用时 的时间

通常的替代方案是Qdrant或Pinecone这类专用存储服务,但有三点理由不推荐使用它们。

  1. 数据量极小。每个用户最多只有500条事实。使用独立服务会增加网络跳转、连接处理以及一致性维护的麻烦,而在这种规模下其实并无必要。
  2. 事务至关重要。所有数据都属于客户记录。根据GDPR规定,当客户信息被删除时,其相关数据必须与其他所有信息一同消失。在pgvector中,这可以通过一个事务实现,而独立的向量存储则需要分布式一致性保障。
  3. 查询很简单。通过客户ID进行精确匹配筛选,然后按向量相似度排序,pgvector可通过复合索引高效处理这一操作。

知识库则是另一回事。产品文档、常见问题解答和政策手册都存储在Qdrant中,因为这类数据量庞大,每个租户拥有数千份文档,需要处理高并发访问,且无需与关系型数据保持事务关联。问题不同,工具也应不同。

带令牌上限的检索

当有新消息到达且智能体需要回复时,就会启用内存检索机制:

  1. 使用与存储时相同的3072维模型来嵌入传入的消息。
  2. 从customer_memories中选取该customer_id对应的距离最近的十行数据,排序依据是每个存储的嵌入向量与查询向量之间的<=>距离。
  3. 按相似度顺序逐一查看这些结果,同时记录累计的标记数。
  4. 将每个事实的标记数估算为len(content) / 4,这是一个粗略但较为保守的数值。
  5. 一旦添加下一个事实后累计标记数超过2,048,立即停止。
  6. 更新所有被检索到的事实的last_accessed_at时间。

<=>运算符用于计算余弦距离,数值越小表示与当前消息的匹配度越高。

上限是固定的。智能体会获取最相关的信息,这些信息需在2,048个标记符范围内,并按相关性排序。如果最相关的一条信息占800个标记符,可能只能容纳两三条信息;而如果每条信息都很短,仅为50到80个标记符,那么大概可以容纳15到20条。系统会自动根据实际内容调整,无需手动调整。默认的10行限制在实践中往往并不重要,因为通常首先会受到标记符上限的限制,但这能防止数据库查询变得无限制。

last_accessed_at的写入功能具有双重作用:它告知淘汰策略哪些信息仍应保留,同时显示智能体实际使用了哪些记忆,哪些则未被使用。

500条信息的限制及被淘汰的对象

每位客户最多可存储500条事实。这一限制有助于控制每位客户的存储成本,并保持检索效率,因为500个向量时的余弦相似度计算既快速又有意义,而超过50,000个向量时则会出现噪声。

当系统需要节省存储空间且客户已存储满500条事实时,就会开始移除部分数据。移除顺序如下:

  1. 90天或更长时间未被访问过、已过时且很可能不再相关的事实。
  2. 在剩余事实中,重要性得分最低的那些。
  3. 若重要性得分相同,则按创建时间最早的优先移除。

90天这个时间长度符合业务场景。三个月前了解到但此后从未使用过的信息很可能无关紧要,而六个月前学到并每周都会使用的信息,比如时区设置,则会一直保留,因为其last_accessed_at时间会不断更新。一旦数据整合达到上限,系统会进行清理,通常只删除1到3条必要的信息,而非大规模清除。

记忆与知识是不同的概念

这两个系统解决不同的问题,却要争夺相同的存储空间。

记忆是针对每个客户的,规模较小、具有个人特色,通过互动形成:比如知道某个女儿的名字叫阿玛拉,报告应为PDF格式,之前的配送投诉已通过给予15%的折扣解决。而知识则是针对整个租户群体或全局的,规模较大、可共享,由人们编写:包括产品文档、价格表、退货政策以及故障排除指南。

每个模块都有独立的令牌预算:内存模块为2,048,而知识模块的预算则更高,通常在4,096到8,192之间。它们共享模型的上下文窗口,但不会互相占用资源。即便内存模块用尽了全部预算,知识模块仍能获得完整的预算。

这种分离机制避免了不良情况的发生。如果没有独立的预算分配,拥有500条丰富信息的客户可能会挤占所有的知识检索资源,导致智能体记住该客户的所有信息,却忘记了帮助客户所需的产品文档。

内存如何影响推理深度

内存还通过两种方式为决定请求所需推理量的复杂性分类器提供数据支持。

首先是“上下文依赖”信号。当检索结果返回高度相关的事实,即与当前消息的余弦距离较低时,上下文依赖得分就会上升。分类器会将此情况理解为“正确答案取决于客户特定的数据”,并可能将请求发送到更高级的推理层级。与没有购买历史的客户提出相同问题相比,如果存储的事实中提到了待处理的订单,那么“您能帮我查看一下订单吗?”这个请求的得分会更高。

第二种限制是与思维预算的竞争。在完整思维链模式下,模型会根据复杂度分别为思维过程分配4,096或16,384个标记。注入提示词中的记忆信息也会占用同一有限窗口内的标记,因此携带2,048个标记记忆的客户所剩空间会比携带200个标记的客户更少。这两种预算都是独立管理的,且都存在固定的上限,因此系统绝不会互相折衷。

仍然存在的难题

架构图中并未显示两种困难。

事实漂移。用户的偏好会发生变化。原本“更喜欢上午预约”的人,三个月后可能会变成“更喜欢下午预约”。系统无法自动检测新旧事实之间的矛盾。0.92的阈值有时能捕捉到这类矛盾,前提是新表述与旧表述足够相似从而匹配上,但并不能做到始终可靠。目前,旧事实会因时效性和访问规则被剔除,而更完善的矛盾处理机制仍在开发中。

提取幻觉。负责提取的大型语言模型偶尔会推断出根本没人说过的内容。以某种方式表述的中性问题可能会被存储为“客户似乎对产品感到不满”。0.5的阈值虽然能去除最弱的推断,但并不能完全消除它们。去重功能也能起到间接作用,因为凭空编造的推断很少会再次出现,因此只会以极少被使用的记录形式存在,直到被清除。

这些方法都并非完美的解决方案。通过上限设定、清除策略以及访问频率信号,可以共同确保优先保留最新、最常用且重要性最高的事实。那些过时的猜测和陈旧的偏好则会沉到最底层,等到需要空间时就会被移除。

总结:两种处理流程

在对话结束后,整合流程会利用大语言模型提取事实、评估其重要性并设置0.5的过滤阈值,生成嵌入向量,以0.92的余弦相似度阈值去除重复内容,存储新信息,当信息量达到500条的上限时则移除旧数据。在生成回复之前,首先会对输入消息进行嵌入处理,通过余弦相似度筛选出最重要的事实,确保这些事实在2,048个标记的限制范围内,随后将其注入智能体的上下文中。

相关阅读

  • 从单一巨大提示词到模块化AutoGen购物助手 — 为何单一聊天机器人提示词无法适用于电子商务,以及AutoGen与MCP设计如何将语言理解与确定的业务逻辑分开。