上周我把半年多的聊天记录导出,想复盘一个跨会话的项目决策链,结果发现 ChatGPT 的记忆完全是薛定谔的------你永远不知道它还在不在,直到你重读历史发现自己像个复读机。那一刻我就知道,光靠人工抽查早晚要出事,于是花了两个晚上搭了一套 Playwright 自动化巡检,直接暴露了隐藏的记忆蒸发问题。
问题拆解
如果你用 ChatGPT 干正事,大概率会依赖"Memory"功能让它记住你的技术栈、项目约束和偏好。但 Memory 有个致命弱点:不给你任何版本历史或变更通知。它可以在某次交互后悄悄更新或遗忘,而你要在几天后的对话里,从 AI 莫名其妙的回答中反向猜测"它是不是又把我的数据库选型忘了"。
常规做法是手动翻"Settings > Personalization > Memory",看条目还在不在------但这只能验证"键"的存在,没法验证"值"是否扭曲,更没法做到高频检查。对团队来说,业务规则如果藏在 Memory 里(比如"退款逻辑必须走异步补偿"),一次遗忘就可能让客服给客户错误承诺。这已经不是体验问题,是隐性生产事故。
根因很简单:Memory 是黑盒存储,没有暴露 API,也没有变更事件。你必须像个验尸官一样,不断翻聊天记录去比对。而我需要的是一套可重复、可报警的自动化验证,能明确告诉我:今天的记忆和昨天的基线相比,哪些条目消失了,哪些内容被篡改。
方案设计
直接对 ChatGPT 的 Web 端做 UI 自动化是唯一可行的路,因为 OpenAI 没开放记忆读写的接口。技术栈选 Playwright 而不是 Selenium,理由很粗暴:
- 自动等待机制 让脚本不用满屏
sleep,维护心智成本低 - 内置录制 + codegen 可以快速定位记忆面板的元素选择器
- 对单页应用的 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。