Tair 在淘宝闪购 AI Agent 中的短期记忆架构实践

Tair在淘宝闪购AI Agent中构建了高性能的短期记忆系统,通过多种数据结构和云原生特性,实现了秒级响应和弹性扩展,为AI Agent提供可靠的记忆支撑。

原文标题:Tair 短期记忆架构实践:淘宝闪购 AI Agent 的秒级响应记忆系统

原文作者:阿里云开发者

冷月清谈:

本文介绍了阿里云Tair在淘宝闪购AI Agent“一句话点外卖”场景中的短期记忆架构实践。Tair作为Agent记忆层的核心,通过高性能、低延迟的特性,解决了AI Agent对延迟的敏感性问题。文章详细阐述了Tair在数据模型设计、并发控制和弹性扩展等方面的关键实践,包括使用List存储模型记忆(对话历史),使用Hash存储业务上下文记忆,以及使用分布式锁保障并发安全。同时,Tair云原生架构通过多线程内核、读写分离和弹性带宽等机制,有效应对了春节红包活动期间10倍的瞬时流量压力,实现了秒级响应和业务无感弹性伸缩。最后,文章还展望了AI Agent记忆系统从对话级向用户级演进的趋势,以及Tair在未来长期记忆建设中的角色。

怜星夜思:

1、在AI Agent的记忆管理中,除了Tair文章中提到的List和Hash结构,你认为还有哪些数据结构或技术可以应用,以优化不同类型记忆的存储和访问效率?
2、文章提到了使用分布式锁来保证并发安全,但锁的粒度是会话级别。在高并发场景下,如果会话时间较长,这种锁可能会成为性能瓶颈。你认为有哪些方法可以进一步优化并发控制策略?
3、文章中提到Tair通过多线程内核和弹性扩容来应对流量洪峰。假设让你来设计一个类似的具备弹性的缓存系统,除了这些,你还会考虑哪些因素来保证系统的高可用和可扩展性?

原文内容

引言

当你对千问说一句"帮我点杯霸王茶姬的伯牙绝弦,少糖去冰,送到公司",背后的 AI Agent 需要在数秒内完成意图识别、地址解析、商品搜索、规格匹配、加购下单等一系列操作。整个过程涉及多轮对话、多次工具调用,每一步都依赖对"之前发生了什么"的准确记忆。

这就是 AI Agent 的短期记忆问题。在淘宝闪购与千问合作的"句话点外卖"项目中,Tair 承担了 Agent 短期记忆层的核心角色。本文将从这一真实业务场景出发,介绍 Tair 在 AI Agent 记忆管理中的数据模型设计、压缩策略与并发控制等关键实践。

AI Agent 为什么需要高性能记忆层

淘宝闪购 Agent 支持用户通过自然语言完成从导购到下单支付的完整点单流程,目标是将传统 3-5 分钟的点单耗时压缩至 30 秒以内。一次典型的多轮对话如下:

在这个过程中,Agent 需要在每一轮交互时"记住"之前的全部上下文:第1轮获取到用户当前的位置,第2轮需要知道当前推荐的是哪个商品,第3轮需要确定购物车内容并汇总所有信息提交订单。这些"记忆"全部存储在 Tair 中。

性能分水岭:从 Little 定律看 Agent 延迟的滚雪球效应

AI Agent 的记忆系统对延迟极度敏感。根据 Little 定律 “并发数 ≈ QPS × 延迟” ,记忆访问延迟从 5ms 上升到 50ms,系统在途请求数就会膨胀10倍,这可能会迅速耗尽连接、线程和队列资源。而 Agent 每轮对话涉及多次记忆读写,延迟会被反复叠加放大,最终可能引发排队、超时乃至雪崩。

5ms 和 50ms 的差别,不是体验上的优化,而是系统能否稳定扩展的分水岭。 这正是淘宝闪购 Agent 选用 Tair 作为记忆层的核心原因 —— 通过自研多线程内核提供稳定的低延迟,将记忆访问稳定在安全水位,从根本上避免高并发下的恶性循环。

淘宝闪购 AI Agent 秒级响应记忆系统架构

淘宝闪购 Agent 的记忆层(Memory Service)位于 Agent 中枢与底层工具服务之间,通过 Tair 提供会话级的状态管理能力。整体架构如下:

记忆分类与 Tair 数据模型设计

淘宝闪购 Agent 选择 Tair 作为短期记忆存储引擎,核心考量包括:

  • 低延迟Agent 对话链路对响应时间极度敏感,Tair 自研多线程内核提供微秒级读写能力,可满足实时交互需求。

  • 丰富的数据结构不同类型的记忆数据可以选用最匹配的数据结构,简化业务开发。

  • 弹性扩展能力:支持集群无感扩缩容和带宽弹性突发,在流量洪峰期快速扩展、业务无感。

  • TTL 生命周期管理会话记忆具有自然过期属性,TTL 机制可自动清理过期数据。

短期记忆被划分为两大类,分别对应不同的 Tair 数据结构:

模型记忆(Model Memory)—— List

模型记忆存储供大语言模型消费的对话历史。每轮对话中用户的输入和 Agent 的回复,都会被记录并在下一轮推理时作为上下文传入模型。

使用 Tair 的 List 结构存储,每个会话一个 Key:

Key:  memory:model:{sessionId}
Type: List

示例数据:
[
  {“role”: “user”,      “content”: “帮我点杯奶茶”},
  {“role”: “assistant”, “content”: “为你找到附近3家奶茶店…”, “cards”: […]},
  {“role”: “user”,      “content”: “就这个,少糖去冰”},
  {“role”: “assistant”, “content”: “已选择:伯牙绝弦 少糖去冰 大杯…”}
]


核心操作: 

# 每轮对话结束后,追加新的对话记录
RPUSH memory:model:{sessionId} "{对话记录JSON}"

模型推理前,读取最近N轮对话作为上下文

LRANGE memory:model:{sessionId} -{N} -1

设置会话过期时间(如30分钟)

EXPIRE memory:model:{sessionId} 1800


说明 对话记录在写入前,会将原始数据(包含文本和卡片等富媒体内容)转换为模型更易理解的自然语言格式,减少 Token 消耗。

业务上下文记忆(Business Context Memory)—— Hash

业务上下文记忆记录业务流程中的结构化状态信息,供 Agent 的工具层和意图处理器在执行业务逻辑时查询和更新。

按业务领域拆分为 6 个子模块,使用 Tair 的 Hash 结构存储:

Key:  memory:context:{sessionId}
Type: Hash

Field 结构:
{
  “session”:      “{会话元信息: 用户ID、渠道、会话阶段等}”,
  “search”:       “{搜索状态: 当前query、搜索结果、推荐商品列表等}”,
  “order”:        “{订单状态: 购物车内容、已选SKU、商品数量等}”,
  “conversation”: “{对话状态: 当前意图、上一轮意图、意图切换标记等}”,
  “coupon”:       “{优惠信息: 可用优惠券列表、已选优惠等}”,
  “bizState”:     “{业务状态: 收货地址、配送方式、支付状态等}”
}


核心操作:

# 单独更新某个子模块(如用户确认了收货地址)
HSET memory:context:{sessionId} bizState "{更新后的业务状态JSON}"

读取特定子模块

HGET memory:context:{sessionId} order

一次性读取所有上下文(用于意图识别等需要全局信息的场景)

HGETALL memory:context:{sessionId}

设置过期时间

EXPIRE memory:context:{sessionId} 1800


说明 使用 Hash 的 field 级读写能力,各业务模块可以独立更新,互不干扰,避免了读取完整 JSON 再回写的竞争问题。例如搜索模块更新商品推荐结果时,不会影响订单模块正在写入的购物车数据。

数据结构选型对比

记忆类型

Redis 数据结构

选型理由

对话历史

List

对话是有序时间序列,List 支持有序追加(RPUSH)和范围读取(LRANGE)

业务上下文

Hash

按领域拆分为多个字段,支持 field 级独立读写,避免读写竞争

会话状态标记

String

原子性状态标记(如会话阶段),操作简单

分布式锁

String

基于 SET NX EX 实现,保障并发安全

并发隔离:基于 Tair 分布式锁的一致性保障方案

在实际业务中,同一会话可能存在并发写入。例如用户快速连续发送消息,或流式响应过程中用户再次输入,都会导致多个请求同时修改同一会话的记忆数据。

淘宝闪购 Agent 使用 Tair 分布式锁保护记忆的读写一致性,锁粒度为单个会话:

# 获取会话级分布式锁(锁超时3秒,防止异常阻塞)
SET lock:memory:{sessionId} {requestId} NX EX 3

成功获取锁后,执行记忆读写操作

HSET memory:context:{sessionId} order “{更新后的订单状态}”
RPUSH memory:model:{sessionId} “{新对话记录}”

操作完成后释放锁(通过Lua脚本确保只释放自己持有的锁)

EVAL 
  if redis.call(‘GET’, KEYS[1]) == ARGV[1] then
    return redis.call(‘DEL’, KEYS[1])
  else
    return0
  end


说明 锁粒度为会话级(sessionId),而非全局锁。不同用户的会话之间完全无锁竞争,不会影响系统整体吞吐。锁超时设置为秒级,避免持锁进程异常退出时造成长时间阻塞。

弹性进化:Tair 云原生架构如何应对 10 倍瞬时流量?

在千问春节红包活动期间,淘宝闪购 Agent 承受了超过 10 倍于预估峰值的并发压力。每次用户对话可能触发数十次 Tair 操作(读取历史、更新状态、锁操作等),Agent 的并发请求量会被放大为数量级更高的 Tair 操作量。

淘宝闪购 Agent 的记忆层底层使用的是云数据库 Tair(兼容 Redis)。相比自建 Redis,Tair 在内核性能、弹性扩展和运维方面的能力是应对此次流量洪峰的关键支撑。

突破单线程瓶颈:利用多线程内核压榨单节点吞吐

Tair(企业版)内存型采用多线程模型,读写性能达到同规格 Redis 开源版实例的 3 倍。这意味着在相同的实例规格下,Tair 能够承载 3 倍于开源 Redis 的操作吞吐量。

在 AI Agent 场景中,这一性能优势尤为关键。一次用户对话可能触发数十次 Tair 操作(读取对话历史、更新业务上下文、分布式锁获取释放等),如果使用开源 Redis 的单线程模型,在高并发场景下很容易成为瓶颈。Tair 的多线程内核使得单个节点可以充分利用多核 CPU 资源,在不增加节点数量的前提下即可承载更高的并发量。

读写比10:1下的弹性扩展与无感扩缩容

在 Agent 对话链路中,记忆数据呈现典型的读多写少特征:每轮推理前需要读取完整的对话历史和业务上下文(读操作),而写操作仅在每轮对话结束后追加一条记录。读写比通常在 5:1 到 10:1 之间。

Tair 支持集群架构开启读写分离功能,主节点挂载只读副本(Read Replica),读请求自动分发到只读节点,写请求路由到主节点。只读副本支持 1~9 个灵活调整,集群分片支持 2~256 个水平扩展。在流量高峰前增加只读副本或分片即可线性提升吞吐,峰值过后缩减节点降低成本。

日常流量:
  集群 8 分片,每分片 1 只读副本 → 满足日常业务需求

春节活动(5~10 倍峰值):
  方案一:每分片扩展至 5 只读副本 → 读吞吐线性提升
  方案二:集群扩至 16 分片 + 每分片 3 只读副本 → 读写能力同步翻倍


而这些扩缩容操作对业务完全透明。传统 Redis 集群在 Slot 迁移过程中可能产生 -ASK-TRYAGAIN 等错误,对 Agent 对话场景来说,任何一次请求失败都可能导致对话中断或记忆丢失。Tair 云原生版通过内核级优化实现了无感扩缩容——数据以 Slot 为单位原子性整体迁移(而非逐 Key 迁移),不会造成 Slot 分裂;同时通过中心化的控制组件协调集群行为,迁移效率更高、决策更精准。

说明 在数据迁移的最终阶段,对应 Slot 的写请求时延会略有增加,但不会失败。对于 Agent 记忆服务而言,表现为个别请求延迟略升,但不会出现数据丢失或请求报错。

运维团队可以根据业务流量实时调整集群规模——活动前扩容、活动后缩容——全程业务无感,无需应用层做任何适配。

解决带宽侧的木桶效应

除了 QPS 压力,在千问春节红包的活动中还面临了显著的带宽挑战。AI Agent每次记忆读取涉及对话历史和业务上下文的传输,单次请求的数据远大于传统缓存场景中的简单KV读写。在业务高峰期,带宽很大可能先于 CPU 和 内存成为瓶颈。

Tair 云原生架构提供了两层弹性机制:

  • 集群带宽水平扩展:集群架构可以通过增加 LB 数量来扩展实例总带宽,单个 LB 带宽上限 20Gbps,当分片数超过 8 个时,可按需新增 LB,新增过程中不会中断现有连接。

  • 弹性突发带宽:当瞬时流量超过固定带宽时,系统会秒级自动扩展带宽(单节点最高可达 288MB/s),流量回落后自动回收,按实际突发量计费。开启和关闭均不会影响实例正常访问。弹性突发带宽以分片为粒度独立生效。当某个分片因热点 Key 出现带宽瓶颈时,仅该分片的带宽会被自动扩展,不会影响其他分片。既精准应对热点问题,又避免不必要的成本开销。

说明 弹性突发带宽特别适合 Agent 场景中不可预测的流量尖峰。相比提前购买高规格的固定带宽包,按需突发在成本上更省。

利用 TTL 实现冷热数据自动清理

所有会话 Key 设置合理的 TTL(如 30 分钟),流量高峰过后内存自动回落,无需人工干预。结合 Tair 的弹性扩缩容能力,实现了"高峰扩、低谷缩、过期自动数据清理"的全自动资源管理。

最终,整套记忆服务在春节流量洪峰期间保持稳定运行,P99 延迟始终控制在毫秒级。

总结与展望

在淘宝闪购 "一句话点外卖"场景中,Tair 作为 AI Agent 短期记忆层的核心存储,提供了以下关键能力:

能力

实现方式

解决的问题

低延迟访问

Tair 内存读写

匹配 Agent 对话链路的实时性要求

灵活的数据建模

List + Hash + String 组合

适配对话历史、业务上下文等不同记忆类型

生命周期管理

TTL 自动过期

会话结束后自动清理,降低运维成本

并发安全

分布式锁(SET NX EX)

保障多请求并发写入时的数据一致性

弹性抗压

Pipeline + 读写分离

支撑春节活动 10 倍峰值流量,业务无感弹性

随着 AI Agent 技术的演进,记忆管理正在向更深层次发展。短期记忆之外,长期记忆(用户偏好、历史行为模式)的建设已在规划之中,让 Agent 不仅记住"这次对话说了什么",更记住"这个用户是谁、喜欢什么"。

AI Agent 的记忆系统,正在从"对话级"走向"用户级",Tair 在这一演进过程中将持续扮演关键角色。

我会特别关注 数据一致性 的问题。在分布式环境下,如何保证缓存数据与底层数据源的一致性是一个很大的挑战。另外,缓存预热 也很重要,避免在系统启动或扩容后出现大量缓存未命中,导致性能下降。我觉得可以搞一个定时任务,提前把热点数据加载到缓存里。

从数据库的角度来看,文章中提到的List和Hash在key数量膨胀后,始终会存在性能问题,我理解是和底层存储引擎有关,这方面NewSQL可能会更好一点,不过如果跳出数据库的思维,纯粹做缓存可能效果更好,比如使用类似Caffeine这种本地缓存,甚至可以使用GPU来做embedding向量的相似度计算,速度应该会更快

会话级别的锁在高并发下的确可能成为瓶颈。以下是一些可以尝试的优化方向:

1. 更细粒度的锁: 将锁的粒度细化到更小的业务单元,例如只锁定购物车中的特定商品,而不是整个会话。当然,这需要更精细的业务逻辑拆分。
2. 乐观锁: 使用版本号或时间戳等机制,在更新数据时检查是否有其他线程修改过。如果没有冲突,则更新成功;否则,进行重试或采取其他冲突解决策略。
3. 无锁数据结构: 考虑使用无锁数据结构,例如ConcurrentHashMap,来减少锁的竞争。但要注意,无锁数据结构也有其适用场景和限制。
4. 异步化: 将一些非核心的写操作异步化,例如使用消息队列,降低对锁的依赖。

我倾向于认为,对于更复杂的关系,图数据库可能是个不错的选择。比如,可以构建一个知识图谱,将用户、商品、店铺、属性等实体连接起来,实现更智能的推荐和导购。此外,像时间序列数据库,在分析用户行为的时序模式上,应该也能发挥作用。

除了多线程内核和弹性扩容,我还会考虑以下几个关键因素:

1. 自动故障转移: 当节点发生故障时,系统能够自动检测并切换到备用节点,保证服务的连续性。
2. 完善的监控和告警: 实时监控系统的各项指标(QPS、延迟、错误率等),并在出现异常时及时告警,以便快速响应。
3. 优雅降级: 在系统压力过大时,能够自动关闭一些非核心功能,例如个性化推荐,保证核心服务的可用性。
4. 异地多活: 将系统部署在多个地理位置,即使某个地区发生故障,也能保证服务的可用性。
5. 可观测性: 完善的日志、Tracing等,帮助我们快速定位问题。

我觉得可以考虑使用 Redlock 算法,虽然实现更复杂,但理论上可以提供更高的可用性。另外,如果业务允许一定程度的最终一致性,可以考虑使用 CAP 理论 中的 AP 方案,牺牲一致性来提高性能。

安全性也很重要!要考虑数据加密,防止敏感信息泄露。另外,权限管理 也很关键,要控制不同用户对缓存数据的访问权限。云原生架构下,要考虑容器安全,避免容器逃逸等安全风险。