用了 3 个月 ChatGPT,才发现它一直在遗忘——用 Playwright 自动化验证记忆存储一致性

上周我把半年多的聊天记录导出,想复盘一个跨会话的项目决策链,结果发现 ChatGPT 的记忆完全是薛定谔的------你永远不知道它还在不在,直到你重读历史发现自己像个复读机。那一刻我就知道,光靠人工抽查早晚要出事,于是花了两个晚上搭了一套 Playwright 自动化巡检,直接暴露了隐藏的记忆蒸发问题。


问题拆解

如果你用 ChatGPT 干正事,大概率会依赖"Memory"功能让它记住你的技术栈、项目约束和偏好。但 Memory 有个致命弱点:不给你任何版本历史或变更通知。它可以在某次交互后悄悄更新或遗忘,而你要在几天后的对话里,从 AI 莫名其妙的回答中反向猜测"它是不是又把我的数据库选型忘了"。

常规做法是手动翻"Settings > Personalization > Memory",看条目还在不在------但这只能验证"键"的存在,没法验证"值"是否扭曲,更没法做到高频检查。对团队来说,业务规则如果藏在 Memory 里(比如"退款逻辑必须走异步补偿"),一次遗忘就可能让客服给客户错误承诺。这已经不是体验问题,是隐性生产事故。

根因很简单:Memory 是黑盒存储,没有暴露 API,也没有变更事件。你必须像个验尸官一样,不断翻聊天记录去比对。而我需要的是一套可重复、可报警的自动化验证,能明确告诉我:今天的记忆和昨天的基线相比,哪些条目消失了,哪些内容被篡改。


方案设计

直接对 ChatGPT 的 Web 端做 UI 自动化是唯一可行的路,因为 OpenAI 没开放记忆读写的接口。技术栈选 Playwright 而不是 Selenium,理由很粗暴:

  1. 自动等待机制 让脚本不用满屏 sleep,维护心智成本低
  2. 内置录制 + codegen 可以快速定位记忆面板的元素选择器
  3. 对单页应用的 Shadow DOM 处理更干净

架构上我把验证拆成三步流水线:登录 → 爬取当前 Memory 全量条目 → 与 Git 中的基线 JSON 做 diff 。有人问我为啥不直接用浏览器插件,因为插件没法在 CI 容器里跑。也有人提用 API 模拟,但我早就确认过 /backend-api/memories 这类内部接口随时可能被封,依赖非公开 API 比 UI 自动化更脆。

最终选择:Playwright + GitHub Actions 定时触发,diff 结果直接推企业微信。不是为了炫技,是为了让遗忘这件事从"大概可能"变成"精确到条目"的告警。


核心实现

这段代码解决「自动拉取 ChatGPT Memory 完整快照」的问题

整个脚本的核心逻辑是:模拟登录态,进设置页,逐个抓取记忆条目的文本,最后覆盖保存为 current_memory.json。注意,ChatGPT 的记忆管理在设置面板中是一个滚动列表,每一条记忆都有独立的 data-testid="memory-item"(但这是我自己定位到的属性,官方随时可改,所以加了备选选择器)。

python 复制代码
import json
import os
from datetime import datetime
from playwright.sync_api import sync_playwright

# 你自己的 session cookie 值,从浏览器 Application 面板复制
SESSION_COOKIE = os.getenv("CHATGPT_SESSION", "")
BASE_URL = "https://chatgpt.com"
MEMORY_PANEL_URL = f"{BASE_URL}/#settings/Personalization"

def fetch_memories(page):
    """打开记忆面板,滚动加载所有记忆条目,返回文本列表"""
    page.goto(MEMORY_PANEL_URL, wait_until="networkidle")
    # 等记忆列表容器出现
    page.wait_for_selector("[data-testid='memory-item'], .memory-item", timeout=15000)

    memories = set()  # 去重,因为滚动可能导致重复抓取
    last_count = 0
    while True:
        # 用 evaluate 直接拿文本,避免 selector 失效
        items = page.evaluate("""
            () => {
                const nodes = document.querySelectorAll('[data-testid="memory-item"] p, .memory-item p');
                return Array.from(nodes).map(n => n.textContent.trim()).filter(Boolean);
            }
        """)
        memories.update(items)
        if len(memories) == last_count:
            break  # 没有新条目,认为已全部加载
        last_count = len(memories)
        # 按向下翻页键或者滚动容器------记忆面板不是无限滚动,但以防万一
        page.keyboard.press("PageDown")
        page.wait_for_timeout(500)
    return sorted(list(memories))

def main():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        context = browser.new_context()
        # 注入 session cookie 实现免密登录,防止 2FA 打断脚本
        context.add_cookies([{
            "name": "__Secure-next-auth.session-token",
            "value": SESSION_COOKIE,
            "domain": ".chatgpt.com",
            "path": "/",
            "httpOnly": True,
            "secure": True,
        }])
        page = context.new_page()
        memories = fetch_memories(page)
        with open("current_memory.json", "w", encoding="utf-8") as f:
            json.dump({
                "timestamp": datetime.now().isoformat(),
                "count": len(memories),
                "items": memories
            }, f, indent=2, ensure_ascii=False)
        browser.close()
        print(f"Snapshotted {len(memories)} memory items.")

if __name__ == "__main__":
    main()

这段代码解决「对比新旧快照,输出人类可读的变更」的问题

有了快照,diff 才是灵魂。我不想只看文本差异,还得知道是"丢失"还是"新增",所以专门写了个纯函数。

python 复制代码
import json
from typing import List, Dict

def diff_memories(baseline_file: str, current_file: str) -> Dict[str, List[str]]:
    with open(baseline_file, "r", encoding="utf-8") as f:
        baseline = set(json.load(f)["items"])
    with open(current_file, "r", encoding="utf-8") as f:
        current = set(json.load(f)["items"])
    return {
        "removed": sorted(list(baseline - current)),
        "added": sorted(list(current - baseline)),
    }

# 使用示例
if __name__ == "__main__":
    changes = diff_memories("baseline_memory.json", "current_memory.json")
    if changes["removed"]:
        print("⚠️ 这些记忆被悄悄删除了:")
        for item in changes["removed"]:
            print(f"  - {item}")
    if changes["added"]:
        print("✅ 记忆新增:")
        for item in changes["added"]:
            print(f"  + {item}")
    if not changes["removed"] and not changes["added"]:
        print("记忆无变化,一致性验证通过。")

这段代码解决「集成到 CI 中自动告警」的问题

在 GitHub Actions 里,我把 baseline 提交到仓库,每次跑完如果发现有 removed,直接让 step fail,再配合企业微信 webhook 发告警。

yaml 复制代码
- name: Check memory consistency
  run: |
    python fetch_memory.py
    python diff_memory.py
    if [ -s changes.txt ]; then
      curl -X POST ${{ secrets.WECOM_WEBHOOK }} \
        -H 'Content-Type: application/json' \
        -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"ChatGPT记忆变更:$(cat changes.txt)\"}}"
      exit 1
    fi

告警消息示例:⚠️ 这些记忆被悄悄删除了:- 用户偏好使用 FastAPI 作为默认后端框架 - 生产环境 K8s 版本 1.28。看到这个,我就可以在出问题前手动补回记忆。


踩坑记录

坑 1:Cookie 过期导致静默失败,记忆快照变成空数组

现象是 CI 连续两天报"记忆无变化",可我明明手动加了新条目。查日志发现 fetch_memories 返回 0 条,但脚本没报错。原因在于 Playwright 用过期 cookie 进入页面后,ChatGPT 直接重定向到登录页,而内存面板的 selector 永远等不到,可我设的 timeout 是 15000ms,超时后直接跳过了------代码里没处理等待超时的异常,导致空列表返回。解决:加了一个前置断言,检查页面 URL 是否仍为设置页,如果不是直接抛异常,强制 CI 失败,避免"虚假告警"。

坑 2:滚动加载的假象------你永远不知道记忆总数

官方记忆面板没有总条数标记,分页也不是标准接口,我最初的 while 循环凭感觉滚动了 10 次就退出。结果某次基线有 34 条记忆,当前抓取只拿到 32 条,diff 误报删除。解决 :改成 until-stable 策略:只要连续两次 PageDown 后条目数不变,再额外滚动两次确认,才算拉取完成。这里加了 500ms 的等待,以防渲染延迟。

官方文档没告诉你的事 :记忆条目的 DOM 结构在不同语言设置下会变,p 标签可能被 span 替代,所以 evaluate 里的选择器要写得宽松,或者干脆抓取整个记忆卡片的 innerText


效果验证

以前人工抽查,一周也就看两三次,发现记忆丢失时往往已经过去好几天,影响已扩散。用 Playwright 自动化后,每 6 小时自动快照一次 ,上线两周内成功捕获到 3 次隐式遗忘 :一次是某个项目约束条目莫名消失,两次是长内容被截断(记忆被更新但值变短)。现在基线文件放在仓库里,每次变更都有 Git 历史可追溯,从"可能丢了"到精确告警,排查时间从 20 分钟降到 0


可直接用的代码 / 工具

上面那套脚本和 CI 配置我放到了公开模板仓库,你只需替换 CHATGPT_SESSION 环境变量,就可以在 5 分钟内拥有自己的记忆巡检。如果你不想弄 CI,也可以直接用这一条命令手动跑快照:

bash 复制代码
pip install playwright && playwright install chromium
CHATGPT_SESSION="你的session_token" python fetch_memory.py

#Python #Playwright #ChatGPT #自动化测试 #DevOps

关于作者

我是宝富,一个专啃硬骨头的后端/自动化工程师,相信"一切人工验证终将被脚本取代"。

GitHub: github.com/baofugege --- 这里有更多 AI 工具链自动化方案。

Sponsor: github.com/sponsors/ba... --- 如果这篇文章帮你省下了排查时间,请我喝杯咖啡。

提供服务:Python 后端性能优化 / 自动化脚本定制 / 技术咨询,联系 Telegram @baofugege

相关推荐
玄玄子1 小时前
xss前端解决方案
前端·浏览器·xss
林希_Rachel_傻希希1 小时前
web性能优化之——AI总结视频
前端·javascript·面试
前端炒粉1 小时前
个人简历面经总结二
前端·网络·vue.js·react.js·面试
spmcor1 小时前
CSS 黏性定位完全指南:从入门到精通
css
用户059540174462 小时前
用了半年 LangChain Memory,才发现回滚测试压根没测对
前端·css
木木的木云2 小时前
从零构建微前端框架:PavilionMfe 设计揭秘
前端·架构·vite
weedsfly2 小时前
Cookie 安全三属性:HttpOnly、Secure、SameSite 分别防什么?
前端·javascript·面试
IT_陈寒2 小时前
SpringBoot自动配置没生效?你可能漏了这个注解
前端·人工智能·后端
monologues2 小时前
Vue3 底层原理深度解析:从编译到运行的源码之旅
前端