DEV Community

BAOFUFAN
BAOFUFAN

Posted on

I Used ChatGPT for 3 Months Before Realizing It Was Forgetting — So I Automated Memory Checks with Playwright

Last week I exported over six months of chat history to trace a cross-session project decision chain, only to find ChatGPT’s memory was truly Schrödinger’s — you never know if it’s still there until you reread the history and realize you sound like a broken record. That’s when I knew manual spot-checks would eventually fail, so I spent a couple nights building a Playwright automation to directly expose the hidden memory evaporation.


Breaking Down the Problem

If you use ChatGPT for real work, you probably rely on the “Memory” feature to remember your tech stack, project constraints, and preferences. But Memory has a fatal flaw: it provides no version history or change notifications. It can silently update or forget something after a single interaction, leaving you a few days later to guess from a nonsensical AI response: “Did it forget my database choice again?”

The typical manual approach is to open Settings > Personalization > Memory and check if entries are still there — but that only verifies the existence of “keys,” not whether “values” have been distorted, and certainly can’t be done at high frequency. For a team, if business rules live in Memory (e.g., “refund logic must go through async compensation”), a single forgotten rule could cause a support agent to give wrong promises to a customer. This is no longer a UX annoyance — it’s a silent production incident.

The root cause is simple: Memory is a black-box store with no public API and no change events. You’re forced to act like a coroner, constantly combing through chat history to compare. What I needed was a repeatable, alertable automated check that could tell me exactly: which entries disappeared, and which content was altered, compared to yesterday’s baseline.


Designing the Solution

UI automation against ChatGPT’s web interface was the only viable path, since OpenAI exposes no read/write API for memory. The tool of choice was Playwright over Selenium for a few blunt reasons:

  1. Auto-waiting mechanisms mean fewer sleep calls cluttering scripts, lowering the mental maintenance burden.
  2. Built-in recording + codegen helps quickly locate element selectors in the memory panel.
  3. Cleaner handling of Shadow DOM in single-page apps.

Architecturally I broke verification into a three-step pipeline: login → scrape all current Memory entries → diff against a baseline JSON stored in Git. Some asked why not just use a browser extension — because extensions can’t run inside CI containers. Others proposed faking API calls, but I had long confirmed that internal endpoints like /backend-api/memories could be blocked at any moment; relying on undocumented APIs is even more brittle than UI automation.

The final decision: Playwright + GitHub Actions on a schedule, with diff results pushed directly to WeCom. Not for show — to transform the vague “probably” of forgetting into item-level alerts.


Core Implementation

This script solves “automatically pulling a complete snapshot of ChatGPT Memory”

The core logic: simulate a logged-in session, navigate to the settings page, grab the text of each memory entry one by one, and finally overwrite current_memory.json. Note that the memory management panel is a scrolling list; each memory item has its own data-testid="memory-item" (an attribute I located; it may change anytime, so I added fallback selectors).

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()
Enter fullscreen mode Exit fullscreen mode

Top comments (0)