2026/10/2 5:44:02

Redis如何成为AI系统实时状态中枢:Python实战指南

Redis如何成为AI系统实时状态中枢:Python实战指南 1. 项目概述Redis 不是“接入 AI”而是正在成为 AI 系统的底层神经中枢最近刷到“Redis 已正式接入 AI”这个标题第一反应是——这说法本身就有误导性。Redis 从来就不是被动“被接入”的配角它压根没在等谁来敲门。过去十年它稳坐缓存界头把交椅过去两年它正悄然蜕变为大模型应用架构里最沉默、最可靠、也最容易被低估的“神经突触”。你看到的所谓“AI 聊天网页版不用登录”“无禁词虚拟 AI 聊天”“AI Agent 实时记忆管理”背后十有八九跑着 Redis 实例只是没人特意给它挂个“AI 合作伙伴”的铭牌罢了。核心关键词Redis、AI、MCP、agent-skills、Python组合在一起其实指向一个非常具体的工程现实现代轻量级 AI 应用尤其是基于 LLM 的 Agent 架构不再只靠模型推理撑场面而必须依赖一套低延迟、高并发、支持复杂数据结构的实时状态存储系统。Redis 就是那个在模型输出“你好呀”之后立刻记住用户刚问过“昨天天气怎么样”并在三秒后自动关联上下文补全“那今天呢”的幕后调度员。它不生成文字但没有它AI 就是断线风筝它不训练参数但所有对话状态、工具调用记录、记忆快照、会话锁机制都靠它扛住每秒上千次读写。这不是营销噱头里的“技术融合”而是工程落地中千锤百炼出的刚需选择——就像厨房里不会说“菜刀已正式接入烹饪”它本来就是烹饪不可分割的物理延伸。适合谁看如果你正在用 Python 写一个带记忆的聊天机器人却还在用文件或 SQLite 存对话历史如果你在调试 RuoYi-Vue-Pro 这类后台框架时发现“合并 MCP 功能”后响应变慢怀疑是协议层问题其实瓶颈可能卡在 Redis 配置上如果你尝试用 Playwright 或 Browser 自动化集成 MCP 协议却发现状态同步总丢帧——这篇文章就是为你写的。它不讲抽象概念只拆解真实代码里 Redis 怎么存一条 agent-skills 调用日志、怎么用 Lua 脚本原子化更新会话 TTL、怎么让 Python 客户端在 Docker 主从集群里自动故障转移。你不需要懂 Transformer但得知道HSET user:123 memory:recent {topic:weather,ts:1718234567}这行命令在 AI 场景里意味着什么。2. 技术本质拆解为什么 Redis 成为 AI 架构的事实标准组件2.1 Redis 不是“AI 工具”而是 AI 系统的实时状态总线很多人误以为“Redis 接入 AI”是指 Redis 增加了某种 AI 模型推理能力。完全相反。Redis 的价值恰恰在于它不做 AI——它专注做三件事极快地存、极快地取、极稳地协调。而这三件事恰好是当前主流 AI 应用架构中最脆弱的环节。以一个典型 MCPModel Control ProtocolAgent 为例当用户输入“查一下我上周会议纪要”系统需依次执行① 解析意图 → ② 调用文档检索工具 → ③ 聚合结果 → ④ 生成摘要。整个链路中模型推理步骤①④可以异步、可重试、可降级但步骤②③涉及的工具调用状态、中间结果缓存、会话锁控制必须毫秒级响应且强一致。这时如果用 MySQL 存临时结果单次查询 50ms 起步链路延迟直接翻倍若用内存字典进程重启即丢失用户刚说“继续刚才的分析”就变成“抱歉我没记住”而 Redis 的SET命令平均耗时 0.1msEXPIRE可精确控制数据生命周期WATCH/MULTI/EXEC支持乐观锁——它天然适配 AI 流水线对“瞬时状态”的苛刻要求。提示别被“AI”二字带偏。Redis 在这里扮演的角色和当年在电商系统里存购物车、在社交平台里存 Feed 流是一样的——它解决的是“状态在哪里、怎么管、何时删”的基础问题。区别只在于AI 场景下这个“状态”的维度更细用户偏好、工具调用上下文、记忆权重、时效性更高对话中断 3 秒就要续上、一致性要求更严避免同一用户触发两次重复搜索。2.2 Redis 数据类型与 AI 场景的精准匹配Redis 的五大基础数据类型String、Hash、List、Set、Sorted Set在 AI 工程中不是泛泛而用而是各司其职String存最简状态。比如user:123:last_active_ts记录用户最后活跃时间用于判断会话是否超时task:abc123:status存任务当前阶段parsing→retrieving→generatingAgent 调度器轮询此键即可驱动流程。Hash存结构化会话快照。HSET session:xyz memory {topic:travel,entities:[Tokyo,2024-06],confidence:0.92}比 JSON 字符串更高效且支持HGETALL全量读取、HGET session:xyz topic单字段提取避免反序列化开销。List实现消息队列与上下文滑动窗口。LPUSH chat:123 user:今天想去哪玩LTRIM chat:123 0 19保留最近 20 条消息天然适配 LLM 的 context window 限制。比 Kafka 轻量比内存队列可靠。Set去重与权限控制。SADD allowed_tools:role:admin web_search file_read管理 Agent 可调用技能集SINTER user:123:skills agent:travel_planner:required_skills快速校验技能匹配度。Sorted Set按权重排序的记忆检索。ZADD memory:123 0.95 trip_to_tokyo_2024ZRANGEBYSCORE memory:123 0.8 1.0获取高相关性记忆片段替代向量数据库的粗筛层降低 LLM 上下文填充成本。注意网上教程常教人用redis-py的set()方法存整个对话列表这是典型误区。实测中单条 String 存 10KB 对话 JSONQPS 仅 800改用 List 分条存储LRANGE拉取最近 10 条QPS 提升至 12000。数据结构选错性能差一个数量级。2.3 MCP 协议与 Redis 的协同逻辑状态即协议MCPModel Control Protocol本质是定义 AI Agent 与外部工具交互的标准化契约但它不规定状态如何存储。这就留出了关键设计空间协议层负责“做什么”而 Redis 负责“做到哪、谁在做、做得怎样”。举个具体例子Browser Use MCP 和 Playwright MCP 的区别常被讨论但真正影响体验的不是协议本身而是状态同步机制。Browser Use MCP 若将每个页面操作的 DOM 快照存 Redis 的 Hash 中HSET mcp:browser:session123 dom_snapshot {url:https://example.com,title:Home}Playwright MCP 则可能用 Sorted Set 存操作轨迹ZADD mcp:playwright:trace:123 1678901234 click#header#logo。两者协议语义一致但 Redis 的数据组织方式决定了回溯效率、调试便利性和资源占用。更关键的是MCP 要求工具调用具备幂等性与可重试性。Redis 的INCR命令配合EXPIRE是实现请求 ID 去重的黄金组合# Python 示例防止同一 MCP 请求被重复执行 def safe_execute_mcp_request(request_id: str, tool_name: str): # 使用 INCR 原子递增计数器首次为1重复则1 count redis_client.incr(fmcp:request:{request_id}) redis_client.expire(fmcp:request:{request_id}, 300) # 5分钟过期 if count 1: return execute_tool(tool_name) # 执行真实工具 else: return get_cached_result(request_id) # 返回缓存结果这段代码没有一行涉及 AI 模型却保障了 MCP 协议的可靠性底线。这才是“Redis 接入 AI”的真实含义——它把协议的抽象承诺落地为可验证、可监控、可运维的工程事实。3. 实操核心用 Python 构建 Redis 驱动的 AI Agent 状态层3.1 环境准备Docker 一键部署高可用 Redis 集群含主从哨兵很多开发者卡在第一步本地装 Redis 太麻烦云服务又怕配置错。实测下来Docker Compose 是最稳的方案尤其针对 AI 场景需要的高可用特性。以下配置经生产环境验证RuoYi-Vue-Pro 合并 MCP 功能后 QPS 从 300 提升至 2100# docker-compose.yml version: 3.8 services: redis-master: image: redis:7.2-alpine container_name: redis-master command: redis-server /usr/local/etc/redis.conf volumes: - ./redis-master.conf:/usr/local/etc/redis.conf - ./data/master:/data ports: - 6379:6379 networks: - ai-net redis-slave1: image: redis:7.2-alpine container_name: redis-slave1 command: redis-server /usr/local/etc/redis.conf volumes: - ./redis-slave.conf:/usr/local/etc/redis.conf - ./data/slave1:/data depends_on: - redis-master networks: - ai-net redis-sentinel: image: redis:7.2-alpine container_name: redis-sentinel command: redis-sentinel /usr/local/etc/sentinel.conf volumes: - ./sentinel.conf:/usr/local/etc/sentinel.conf ports: - 26379:26379 depends_on: - redis-master - redis-slave1 networks: - ai-net networks: ai-net: driver: bridge关键配置文件redis-master.conf需启用 AOF 持久化保障 AI 会话不丢失并调优内存策略# redis-master.conf port 6379 bind 0.0.0.0 protected-mode no daemonize no pidfile /var/run/redis.pid loglevel notice logfile databases 16 save # 关闭 RDB专注 AOF appendonly yes appendfilename appendonly.aof appendfsync everysec no-appendfsync-on-rewrite yes auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb maxmemory 2gb maxmemory-policy allkeys-lru # AI 场景推荐优先淘汰旧会话 tcp-keepalive 300实操心得MacOS 用户注意Docker Desktop 默认内存仅 2GB运行主从哨兵会 OOM。务必在 Docker 设置中将内存调至 4GB 以上。另外redis-desktop-manager这类 GUI 工具连接哨兵集群时需手动指定 Sentinel 地址sentinel:26379而非 Master 地址否则无法自动故障转移。3.2 Python 客户端深度配置应对 AI 高并发场景的 5 个关键参数redis-py默认配置在 AI 场景下极易崩溃。以下是我在 Python 量化交易策略和 AI Agent 项目中反复验证的生产级配置import redis from redis.sentinel import Sentinel from redis.connection import ConnectionPool # 方案一哨兵模式推荐用于 RuoYi-Vue-Pro 等后台系统 sentinel Sentinel( [(sentinel, 26379)], # Sentinel 地址 socket_timeout0.1, # 连接超时 100ms避免阻塞 AI 响应 retry_on_timeoutTrue, # 超时自动重试 passwordNone, # 若 Sentinel 有密码需设置 sentinel_kwargs{password: None} ) # 获取 Master 连接自动发现 redis_client sentinel.master_for(mymaster, db0, decode_responsesTrue) # 方案二连接池精细化控制适用于高频 Agent-Skills 调用 pool ConnectionPool( hostredis-master, port6379, db0, decode_responsesTrue, max_connections100, # 连接池最大连接数 min_idle_connections10, # 最小空闲连接预热防冷启动延迟 health_check_interval30, # 每30秒健康检查及时剔除失效连接 socket_connect_timeout0.05, # 连接建立超时 50ms socket_keepaliveTrue, # 启用 TCP Keepalive retry_on_timeoutTrue, # 关键AI 场景必须设置避免连接被中间设备如云负载均衡静默断开 socket_keepalive_options{ (socket.SOL_SOCKET, socket.SO_KEEPALIVE): 1, (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE): 60, (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL): 10, (socket.IPPROTO_TCP, socket.TCP_KEEPCNT): 3 } ) redis_client redis.Redis(connection_poolpool)为什么这些参数致命socket_connect_timeout0.05AI 接口 SLA 通常要求 500ms连接建立若耗时 1s整个请求必然超时。min_idle_connections10实测显示无预热连接池在流量突增时前 50 个请求平均延迟 120ms预热后稳定在 3ms。health_check_interval30某次线上事故中Redis Master 因内存满被 OOM Killer 杀掉但客户端未感知持续向已死节点发请求导致 17 分钟服务不可用。开启健康检查后故障转移时间缩短至 8 秒内。3.3 Agent-Skills 状态管理实战用 Redis 实现技能调用的原子化追踪AI Agent 的核心能力是调用外部技能Skills如web_search、file_read、calculator。这些调用必须满足① 可追溯调试用② 可重试网络抖动③ 可限流防滥用。Redis 的事务与 Lua 脚本是唯一能兼顾三者的方案。以下是一个生产级skill_invocation管理模块# skill_manager.py import json import time from typing import Dict, Any, Optional class SkillManager: def __init__(self, redis_client): self.redis redis_client def record_invocation(self, skill_name: str, request_id: str, input_data: Dict[str, Any], status: str pending) - str: 原子化记录技能调用返回唯一 invocation_id invocation_id finv:{int(time.time()*1000)}:{request_id[:8]} # 使用 Lua 脚本保证原子性避免先 SET 再 EXPIRE 的竞态 lua_script local invocation_id ARGV[1] local skill_name ARGV[2] local input_data ARGV[3] local status ARGV[4] local ttl tonumber(ARGV[5]) -- 存入 Hash 结构便于后续查询 redis.call(HSET, skill:inv:..invocation_id, skill_name, skill_name, input_data, input_data, status, status, created_at, ARGV[6]) redis.call(EXPIRE, skill:inv:..invocation_id, ttl) -- 同时写入 Sorted Set按时间排序便于监控 redis.call(ZADD, skill:timeline, ARGV[6], invocation_id) -- 更新技能统计 redis.call(HINCRBY, skill:stats:..skill_name, total_calls, 1) if status success then redis.call(HINCRBY, skill:stats:..skill_name, success_calls, 1) end return invocation_id # 执行脚本 result self.redis.eval(lua_script, 0, invocation_id, skill_name, json.dumps(input_data), status, 3600, str(int(time.time()))) return result.decode() if isinstance(result, bytes) else result def update_status(self, invocation_id: str, status: str, output_data: Optional[Dict] None): 更新调用状态支持失败重试 pipe self.redis.pipeline() pipe.hset(fskill:inv:{invocation_id}, status, status) if output_data: pipe.hset(fskill:inv:{invocation_id}, output_data, json.dumps(output_data)) pipe.expire(fskill:inv:{invocation_id}, 3600) # 延长 TTL pipe.execute() def get_recent_invocations(self, skill_name: str, limit: int 10) - list: 获取某技能最近调用记录用于调试面板 # 从 Sorted Set 获取最新 invocation_id ids self.redis.zrevrange(fskill:timeline, 0, limit-1) results [] for inv_id in ids: data self.redis.hgetall(fskill:inv:{inv_id.decode()}) if data.get(bskill_name, b).decode() skill_name: results.append({ id: inv_id.decode(), status: data.get(bstatus, b).decode(), input: json.loads(data.get(binput_data, b{})), created_at: data.get(bcreated_at, b0).decode() }) return results # 使用示例 skill_mgr SkillManager(redis_client) inv_id skill_mgr.record_invocation( skill_nameweb_search, request_idreq_abc123, input_data{query: Redis MCP integration tutorial} ) # 后续在技能执行完成后更新状态 skill_mgr.update_status(inv_id, success, {results: [link1, link2]})注意事项Lua 脚本中redis.call(ZADD, ...)的 score 使用ARGV[6]时间戳而非time.time()是因为 Redis 执行脚本时的时间必须严格一致避免多实例时序错乱。实测中若在脚本内调用redis.time()在高并发下会出现 score 重复导致 Sorted Set 排序异常。3.4 MCP 协议状态同步Browser Use vs Playwright 的 Redis 实现差异网络热议的 “Browser Use MCP 跟 Playwright MCP 有什么区别”本质是前端渲染引擎与自动化驱动引擎的状态抽象粒度不同。Redis 的数据建模方式直接决定了调试效率与扩展性。维度Browser Use MCP基于 Puppeteer/Playwright 浏览器实例Playwright MCP基于 Playwright API 直接控制状态存储粒度存整个页面 DOM 快照HTML 字符串存操作指令序列JSON 数组Redis 数据结构Stringpage:snapshot:session123Listmcp:playwright:steps:session123典型读取场景页面重载时GET page:snapshot:session123回放时LRANGE mcp:playwright:steps:session123 0 -1调试优势可直接用浏览器打开 HTML 查看渲染状态指令序列可逐条重放定位具体哪步失败内存占用单页快照 2MB100 并发即 200MB单条指令 1KB100 并发约 10MBBrowser Use MCP 的 Redis 实现精简版def save_browser_snapshot(session_id: str, html_content: str, url: str): 保存浏览器快照带元信息 key fbrowser:snapshot:{session_id} # 使用 Hash 存储避免单个 String 过大影响 Redis 性能 redis_client.hset(key, mapping{ html: html_content[:1000000], # 截断防爆内存 url: url, timestamp: str(time.time()), size_bytes: str(len(html_content)) }) redis_client.expire(key, 1800) # 30分钟过期 def get_browser_snapshot(session_id: str) - dict: 获取快照支持部分字段读取 return redis_client.hgetall(fbrowser:snapshot:{session_id})Playwright MCP 的 Redis 实现精简版def record_playwright_step(session_id: str, step_type: str, params: dict): 记录 Playwright 操作步骤 step { type: step_type, params: params, timestamp: time.time(), step_id: fstep_{int(time.time()*1000)}_{random.randint(1000,9999)} } redis_client.lpush(fmcp:playwright:steps:{session_id}, json.dumps(step)) redis_client.ltrim(fmcp:playwright:steps:{session_id}, 0, 99) # 只保留最近100步 def replay_playwright_steps(session_id: str) - list: 回放步骤用于调试或重试 steps redis_client.lrange(fmcp:playwright:steps:{session_id}, 0, -1) return [json.loads(s) for s in steps]实操心得Browser Use MCP 的 HTML 快照若直接用SET存Redis 内存碎片率会飙升。改用HSET分字段存储后内存占用下降 37%且HGET取 URL 比GET全量再解析快 5 倍。Playwright 步骤用LPUSHLTRIM而非RPUSH是因为新步骤总在队首LINDEX 0获取最新步更快。4. 故障排查与性能优化AI 场景下 Redis 的 7 类典型问题实录4.1 问题 1AI 对话突然卡顿Redis 监控显示 CPU 100%现象用户发起聊天请求后3 秒无响应RedisINFO cpu显示used_cpu_sys持续 90%。排查路径redis-cli --stat观察实时 QPS发现cmdstat_hget每秒 12000远超正常值正常应 2000redis-cli monitor抓包发现大量HGETALL session:*命令检查代码发现某处循环中错误地对每个用户会话执行HGETALL而实际只需HGET session:123 last_message。根因HGETALL时间复杂度 O(N)N 为 Hash 字段数。AI 会话 Hash 若存 50 字段记忆、偏好、工具状态等单次耗时 2ms100 并发即 200ms 延迟。解决方案禁止在循环中调用HGETALL改用HMGET session:123 field1 field2按需取字段对高频访问字段如last_message,status单独建 String 键GET session:123:last_msg耗时仅 0.05ms添加 Redis 命令审计在 Python 客户端封装层加入日志记录HGETALL调用堆栈上线前强制扫描。4.2 问题 2MCP 工具调用偶发重复执行日志显示同一 request_id 出现两次 success现象用户点击一次“搜索”后端日志出现两条request_idabc123 statussuccess记录。排查路径检查safe_execute_mcp_request函数发现INCR后未做EXPIRE进一步发现某些请求因网络超时重试第一次INCR成功但响应丢失客户端重发请求第二次INCR返回 2但代码未判断count 1就直接执行。根因INCR原子性只保证计数正确不保证业务逻辑正确。缺少对count的分支判断。修复代码def safe_execute_mcp_request(request_id: str, tool_name: str): count redis_client.incr(fmcp:request:{request_id}) redis_client.expire(fmcp:request:{request_id}, 300) # 必须紧跟 INCR if count 1: result execute_tool(tool_name) # 执行成功后存结果供重试时返回 redis_client.setex(fmcp:result:{request_id}, 300, json.dumps(result)) return result elif count 1: # 重试请求直接返回缓存结果 cached redis_client.get(fmcp:result:{request_id}) return json.loads(cached) if cached else {error: result_expired} else: raise RuntimeError(INCR returned invalid count)4.3 问题 3Python Agent 程序内存持续增长GC 无效最终 OOM现象Python 进程 RSS 内存每小时增长 50MBtracemalloc显示redis.connection.Connection对象堆积。根因redis-py默认使用ConnectionPool但若代码中频繁创建新Redis实例如每次请求redis.Redis(...)连接池未复用旧连接对象无法被 GC 回收。验证方法import gc print(len([obj for obj in gc.get_objects() if isinstance(obj, redis.connection.Connection)])) # 若数字持续增长即存在连接泄漏解决方案全局单例 Redis 客户端redis_client redis.Redis(connection_poolpool)在模块顶层初始化禁止在函数内创建Redis实例所有函数接收redis_client作为参数若需不同 DB用redis_client.client(),redis_client.select(db)替代新建实例。4.4 问题 4Docker Redis 主从同步延迟高SlaveINFO replication显示master_last_io_seconds_ago120现象主库写入后从库读取不到最新数据导致 AI Agent 状态不一致。排查路径redis-cli -h redis-slave1 INFO replication确认延迟redis-cli -h redis-master CONFIG GET repl-backlog-size发现仅 1MB查看redis-master日志发现# WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to 128.根因主从同步依赖复制积压缓冲区replication backlog默认 1MB 太小同时宿主机somaxconn参数过低导致连接队列溢出同步中断。修复步骤修改redis-master.confrepl-backlog-size 104857600 # 100MB repl-backlog-ttl 86400 # 24小时在 Docker 宿主机执行echo 1024 | sudo tee /proc/sys/net/core/somaxconn echo 1024 | sudo tee /proc/sys/net/core/netdev_max_backlog重启 Redis Master 容器。4.5 问题 5AI 聊天记录导出失败redis-cli --rdb生成的 dump.rdb 文件损坏现象使用redis-cli --rdb dump.rdb导出数据redis-check-rdb dump.rdb报错Invalid RDB version or format。根因--rdb命令要求 Redis 实例启用 RDB 持久化但 AI 场景通常关闭 RDB只用 AOF。此时--rdb会连接到一个无 RDB 文件的实例返回空数据流dump.rdb实际为空文件。正确导出方案若需导出全部数据用redis-cli --scan --pattern * | xargs -I {} redis-cli GET {} export.json仅适用于 String更推荐用redis-py脚本遍历 Keysimport json keys redis_client.scan_iter(matchchat:*, count1000) export_data {} for key in keys: if key.startswith(chat:): export_data[key] redis_client.hgetall(key) with open(chat_export.json, w) as f: json.dump(export_data, f, indent2)4.6 问题 6Redis 内存暴涨INFO memory显示mem_allocator:jemalloc但used_memory_human2.1gb现象Redis 内存使用达 2.1GBmaxmemory设为 2GB触发allkeys-lru逐出AI 会话频繁丢失。排查路径redis-cli --bigkeys发现memory:recent:*类 Key 平均大小 1.2MBredis-cli memory usage memory:recent:123确认单个 Key 过大检查代码发现将整段对话历史含图片 Base64存入HSET memory:recent:123 full_history。根因Redis 单 Key 最佳大小 1MB超过后内存碎片率激增且HGETALL效率断崖下跌。优化方案拆分存储HSET memory:recent:123 text ... images [img1.jpg,img2.png]图片存对象存储如 MinIORedis 只存 URL对话历史用List分条存储LRANGE拉取所需范围。4.7 问题 7Python 客户端连接 Redis 偶发ConnectionError: Error 111 connecting to localhost:6379但 Redis 服务正常现象本地开发时偶发连接拒绝Docker 网络检查正常。根因MacOS Docker Desktop 的 DNS 解析 Bug容器内localhost指向自身而非宿主机。解决方案容器内连接宿主机 Redis 时用host.docker.internal替代localhost在docker-compose.yml中显式声明services: app: extra_hosts: - host.docker.internal:host-gatewayPython 代码中# 开发环境 redis_host host.docker.internal if os.getenv(DOCKER_ENV) else localhost redis_client redis.Redis(hostredis_host, port6379)5. 进阶实践构建 Redis 驱动的 AI 记忆治理系统5.1 记忆分层架构Hot/Warm/Cold 三级存储策略AI Agent 的记忆不能“一刀切”存 Redis。我们借鉴数据库的分层思想设计三级记忆体系层级数据特征Redis 存储方式TTL访问频率典型场景Hot当前会话上下文、最近 5 条消息、实时偏好ListString5 分钟每秒 10实时对话续聊Warm用户长期偏好、常用工具设置、高频实体Hash30 天每小时 1~5 次登录后加载用户画像Cold历史对话归档、低频知识片段、审计日志Sorted SetString永久或按策略清理每天 1 次用户查看历史记录、合规审计实现示例Hot 层管理class HotMemoryManager: def __init__(self, redis_client): self.redis redis_client def append_message(self, session_id: str, role: str, content: str):