← 返回AI变现
🌐 其他

AI Agent 上下文污染踩坑实录:用 pytest + Redis 揪出 3 类记忆串号 bug,排查 6 小时

来源:掘金 · 发布于 2026-08-15 20:05:04
凌晨两点,用户群里发来一张截图:AI 客服回复工单时,把另一个用户的手机号和收货地址带了出来。我第一反应是模型幻觉,结果查 Redis 发现,两个不同 `user_id` 的会话共用了同一个 `ses

AI Agent 上下文污染踩坑实录:用 pytest + Redis 揪出 3 类记忆串号 bug,排查 6 小时

用户05954017446 2026-08-15 13 阅读1分钟

凌晨两点,用户群里发来一张截图:AI 客服回复工单时,把另一个用户的手机号和收货地址带了出来。我第一反应是模型幻觉,结果查 Redis 发现,两个不同 user_id 的会话共用了同一个 session_id 作为 key,上下文直接串号。这不是模型的问题,是记忆层在裸奔。后来我用 pytest + Redis 搭了一套一致性验证,才把这类 bug 摁死。

问题拆解

AI Agent 的记忆存储跟普通缓存不一样:缓存丢了只是慢一点,记忆串了会直接泄露隐私、带偏对话。我们团队用 Redis 存 Agent 的短期记忆,多轮对话靠它保持连贯。上线后偶发出现 A 用户看到 B 的历史消息。根因不是模型,而是记忆层的三类问题:key 设计缺少用户维度、TTL 刷新逻辑混乱、序列化协议不统一。常规方案为什么不行?靠 code review 和手工测试发现不了,因为串号是并发、边界条件叠加后的结果;mock Redis 又验证不了真实 TTL 和原子性。最后我决定把记忆层当基础设施,用 pytest + Redis 写一致性测试。

方案设计

选 pytest + Redis 而不是单测 mock,核心原因是 Redis 的真实行为——TTL 计算、WATCH 乐观锁、序列化后的二进制差异——是 mock 模拟不出来的。pytest 的 fixture 可以给每个测试一个干净的 db,真实 Redis 用 Docker 起一个即可。设计上我们把记忆读写封装成 MemoryStore,然后针对三类不变量写测试:key 隔离性、TTL 与版本兼容、并发追加一致性。为什么不选直接用生产库测?太危险;为什么不选只写单测?因为单测会把 Redis 当普通 dict 用,掩盖 TTL 和并发问题。

核心实现

这段代码解决测试隔离问题:每个测试用例拿到一个独立的 Redis db,避免数据串号影响断言。

# conftest.py
import pytest
import redis

@pytest.fixture
def redis_client():
    """连接本地 Redis,测试前清空当前 db,保证隔离"""
    client = redis.Redis(host="localhost", port=6379, db=15, decode_responses=True)
    client.flushdb()
    yield client
    client.flushdb()
    client.close()

这个类封装了生产级的 Redis 记忆读写,包含 key 命名空间、JSON 版本化、SETEX TTL、以及并发安全的 WATCH 追加。关键行加了注释。

# memory_store.py
import json
from typing import Any, Dict, List, Optional

import redis
from redis.exceptions import WatchError

class MemoryStore:
    """AI Agent 短期记忆存储,负责 key 隔离、TTL 和版本化序列化"""

    def __init__(self, client: redis.Redis, ttl: int = 3600):
        self.client = client
        self.ttl = ttl
        self.version = 1  # 序列化