# Why Your AI Agent Forgets Everything Overnight — From Prompt to Loop Engineering

> Source: <https://dev.to/weiwuji/why-your-ai-agent-forgets-everything-overnight-from-prompt-to-loop-engineering-4b96>
> Published: 2026-08-01 03:01:33+00:00

The Pain: You spent an afternoon tuning your agent. Next morning, it stares at you blankly — as if yesterday never happened.

What You'll Learn: The 4-stage evolution (Prompt → Context → Harness → Loop), and a runnable 50-line Loop Agent that persists memory.

`pip install openai`

(openai ≥ 1.0.0)**Goal**: Copy-paste the code, run it, and see a Loop Agent that doesn't forget.

At 2 AM, you finally got that multi-step workflow working. The agent followed your carefully designed prompt — data fetching, cleaning, analysis, charting. You close your laptop, satisfied.

Next morning, you open the conversation full of hope — and the agent looks at you blankly, as if none of it ever happened.

You check the logs. No errors. No exceptions. The agent regenerated everything — it just "forgot" where it stopped yesterday.

This isn't a joke. It's the nightmare every serious Agent developer experiences. The root cause isn't "the model isn't smart enough." It's a more fundamental fact: **your agent was never designed to survive the night.**

To understand this, let's use a simple evolution framework:

| Stage | What You Do | Fatal Flaw |
|---|---|---|
Prompt Engineering |
Write task description, examples, format into prompt | Any unexpected input crashes output |
Context Engineering |
Stuff history + intermediate results into context window | Token cost grows linearly, hits window limit |
Harness Engineering |
Add tool calling, structured output, error capture | Framework built, but agent is still "one-shot" |
Loop Engineering |
Build closed loop: state + memory + feedback + retry + persistence | True engineering — agent starts to "live" |

**Loop Engineering isn't a rejection of Prompt Engineering — it's a transcendence.** Prompt still matters. But it's the engine, and you can't drive an engine like a car.

Here's the complete, copy-pasteable, runnable Loop Agent. Run it first, then understand it line by line.

``` python
import json, os, time
from pathlib import Path
from datetime import datetime
from openai import OpenAI

client = OpenAI()  # reads OPENAI_API_KEY from env

STATE_FILE = Path("./agent_state.json")
MEMORY_FILE = Path("./agent_memory.json")
MAX_RETRIES = 3

def load_memory() -> dict:
    if MEMORY_FILE.exists():
        return json.loads(MEMORY_FILE.read_text())
    return {"facts": {}, "errors": []}

def save_memory(mem: dict):
    MEMORY_FILE.write_text(json.dumps(mem, indent=2, ensure_ascii=False))

def load_state() -> dict:
    if STATE_FILE.exists():
        return json.loads(STATE_FILE.read_text())
    return {"state": "idle", "step": 0}

def save_state(state: str, step: int):
    STATE_FILE.write_text(json.dumps({
        "state": state, "step": step,
        "updated_at": datetime.now().isoformat()
    }, indent=2, ensure_ascii=False))

def execute(task: str, memory: dict, error_ctx: str = "") -> str:
    system = f"You are a task-execution agent. Known facts: {json.dumps(memory.get('facts', {}), ensure_ascii=False)}"
    if error_ctx:
        system += f"\nLast error: {error_ctx}\nPlease fix."
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "system", "content": system},
                  {"role": "user", "content": task}]
    )
    return resp.choices[0].message.content

def check(output: str, keywords: list[str]) -> tuple[bool, str]:
    missing = [kw for kw in keywords if kw not in output]
    if missing:
        return False, f"Missing: {missing}"
    if len(output) < 20:
        return False, "Output too short"
    return True, ""

def run(task: str, keywords: list[str]):
    memory = load_memory()
    save_state("running", 0)

    for i in range(1, MAX_RETRIES + 1):
        error = memory["errors"][-1]["reason"] if memory["errors"] else ""
        output = execute(task, memory, error)
        ok, reason = check(output, keywords)

        if ok:
            save_state("done", i)
            memory["facts"][task[:30]] = output[:100]
            save_memory(memory)
            return f"OK on attempt {i}:\n{output}"
        else:
            save_state("retrying", i)
            memory["errors"].append(
                {"task": task, "reason": reason, "attempt": i}
            )
            save_memory(memory)
            print(f"Retry {i} failed: {reason}")
            time.sleep(1)

    save_state("failed", MAX_RETRIES)
    return f"All {MAX_RETRIES} attempts failed"

if __name__ == "__main__":
    result = run(
        task="List 3 Python web frameworks and their features",
        keywords=["Flask", "Django", "FastAPI"]
    )
    print(result)
```

Copy this to `loop_agent.py`

and run it.

```
# 1. Install
pip install openai

# 2. Set API key
export OPENAI_API_KEY="sk-..."

# 3. First run
python loop_agent.py
```

Expected output:

```
OK on attempt 1:
The 3 mainstream Python web frameworks:
1. **Flask**: lightweight micro-framework...
2. **Django**: full-stack, batteries included...
3. **FastAPI**: modern async, auto OpenAPI docs...
```

Now verify persistence — close the terminal, reopen, run again:

```
# 4. Check state file
cat agent_state.json
```

Expected:

```
{
  "state": "done",
  "step": 1,
  "updated_at": "2026-07-01T12:00:00.000000"
}
# 5. Check memory file
cat agent_memory.json
```

Expected: the `facts`

dict contains your task and the output.

```
# 6. Run again — agent loads memory automatically
python loop_agent.py
```

The agent reads `facts`

from `agent_memory.json`

and passes them as known context. **That's the "remembers overnight" mechanism.**

| Component | Code Location | Description |
|---|---|---|
| Memory Store |
`load_memory()` / `save_memory()`
|
Persist memory to JSON |
| State Machine |
`load_state()` / `save_state()`
|
Persist state to JSON |
| Executor | `execute()` |
Call OpenAI API |
| Checker | `check()` |
Verify keywords present |
| Task Scheduler | `run()` |
Retry loop + state transitions |
| Guardrails | `MAX_RETRIES = 3` |
Retry limit |

**Error 1: ModuleNotFoundError: No module named 'openai'**

```
pip install openai
```

**Error 2: openai.AuthenticationError: 401**

You didn't set the API key, or it's invalid.

```
export OPENAI_API_KEY="sk-..."
```

**Error 3: Agent output missing Flask/Django/FastAPI**

This is normal! LLMs don't always follow instructions perfectly. That's exactly the Loop Agent's value — the Checker detects missing keywords, rejects the output, and auto-retries. You'll see:

```
Retry 1 failed: Missing: ['Flask', 'Django', 'FastAPI']
Retry 2 failed: Missing: ['Django']
OK on attempt 3:
...
```

**Error 4: API timeout or rate limit**

`gpt-4o-mini`

is very cheap (~$0.00015/call). If rate-limited, increase `time.sleep(1)`

or check your OpenAI dashboard.

Now you have a complete runnable Loop Agent. It uses plain JSON files for persistence — which is exactly what lets you touch every component by hand.

Extend it:

**Loop Engineering isn't a package you install. It's a shift in architecture thinking.** Starting from these 50 lines, you're standing at the threshold of stage 4.

**Next article**: How Much Memory Does Your Agent Need? — Memory Store Selection Guide

*About the author: Wu Ji (无记) — AI & digitalization practitioner focused on Agent engineering, Loop Engineering, and digital transformation. Practical, hands-on tutorials — follow along and it just works.*
