weiwuji

痛點:你花了一整個下午調教你的 agent。隔天早上,它卻盯著你發呆——彷彿昨天的一切從未發生。
你將學到:四階段演進(Prompt → Context → Harness → Loop),以及一個可執行的 50 行 Loop Agent,能夠持久保存記憶。


0. 環境需求

  • Python ≥ 3.10
  • pip install openai (openai ≥ 1.0.0)
  • OpenAI API Key
  • 作業系統:macOS / Linux / Windows WSL

目標:直接複製貼上程式碼、執行,就能看到一個不會忘記的 Loop Agent。


1. 痛點:為什麼你的 Agent 隔夜就忘光了?

凌晨兩點,你終於讓多步驟工作流程跑起來。Agent 依照你精心設計的 prompt 完成了資料抓取、清理、分析與繪圖。你關上筆電,心滿意足。

隔天早上,你滿懷期待地打開對話——Agent 卻盯著你發呆,彷彿昨天的一切從未發生。

你檢查日誌,沒有錯誤,也沒有例外。Agent 把所有步驟重新跑了一次——它只是「忘記」昨天做到哪裡。

這不是玩笑,而是每個認真的 Agent 開發者都會遇到的噩夢。根本原因不是「模型不夠聰明」,而是一個更根本的事實:你的 agent 從一開始就沒被設計成能活過一晚。


2. 四階段演進:Prompt → Context → Harness → Loop

為了理解這件事,我們使用一個簡單的演進框架:

階段 你做了什麼 致命缺陷
Prompt Engineering 把任務描述、範例、格式寫進 prompt 任何未預期的輸入都可能讓輸出崩潰
Context Engineering 把歷史紀錄與中間結果塞進 context window token 成本線性成長,最終撞到視窗上限
Harness Engineering 加入工具呼叫、結構化輸出、錯誤捕捉 框架建好了,但 agent 依然是「單次執行」
Loop Engineering 建立閉環:狀態 + 記憶 + 回饋 + 重試 + 持久化 真正的工程——agent 開始「活了」

Loop Engineering 不是否定 Prompt Engineering,而是超越它。 Prompt 依然重要,但它只是引擎,你不能把引擎當成汽車來開。


3. 最小可執行 Loop Agent(50 行)

以下是完整的、可直接複製貼上執行的 Loop Agent。請先執行,再逐行理解。

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)

Enter fullscreen mode Exit fullscreen mode

請將程式碼儲存為 loop_agent.py 並執行。


4. 驗證

# 1. 安裝
pip install openai

# 2. 設定 API 金鑰
export OPENAI_API_KEY="sk-..."

# 3. 首次執行
python loop_agent.py

Enter fullscreen mode Exit fullscreen mode

預期輸出:

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...

Enter fullscreen mode Exit fullscreen mode

現在驗證持久化——關閉終端機、重新開啟,再次執行:

# 4. 檢查狀態檔
cat agent_state.json

Enter fullscreen mode Exit fullscreen mode

預期內容:

{
  "state": "done",
  "step": 1,
  "updated_at": "2026-07-01T12:00:00.000000"
}

Enter fullscreen mode Exit fullscreen mode

# 5. 檢查記憶檔
cat agent_memory.json

Enter fullscreen mode Exit fullscreen mode

預期: facts 字典中包含你的任務與輸出結果。

# 6. 再次執行——agent 會自動載入記憶
python loop_agent.py

Enter fullscreen mode Exit fullscreen mode

Agent 會從 agent_memory.json 讀取 facts 並作為已知上下文傳入。這就是「記住隔夜」的機制。


5. 六大元件對應程式碼

元件 程式碼位置 說明
Memory Store load_memory() / save_memory() 將記憶持久化至 JSON
State Machine load_state() / save_state() 將狀態持久化至 JSON
Executor execute() 呼叫 OpenAI API
Checker check() 驗證關鍵字是否存在
Task Scheduler run() 重試迴圈與狀態轉移
Guardrails MAX_RETRIES = 3 重試次數上限

6. 常見錯誤

錯誤 1:ModuleNotFoundError: No module named 'openai'

pip install openai

Enter fullscreen mode Exit fullscreen mode

錯誤 2:openai.AuthenticationError: 401
你尚未設定 API 金鑰,或金鑰無效。

export OPENAI_API_KEY="sk-..."

Enter fullscreen mode Exit fullscreen mode

錯誤 3:Agent 輸出缺少 Flask/Django/FastAPI
這很正常!LLM 不一定每次都能完美遵循指令。這正是 Loop Agent 的價值所在——Checker 偵測到缺少關鍵字,就會拒絕輸出並自動重試。你會看到:

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

Enter fullscreen mode Exit fullscreen mode

錯誤 4:API 逾時或速率限制
gpt-4o-mini 非常便宜(每次呼叫約 $0.00015)。若遇到速率限制,可增加 time.sleep(1) 或檢查 OpenAI 儀表板。


7. 從 50 行出發的下一步

你現在已經擁有一個完整可執行的 Loop Agent。它使用純 JSON 檔案進行持久化——這正是讓你能親手觸碰每個元件的原因。

你可以延伸它:

  • 把 JSON 替換成 ChromaDB → 向量記憶
  • 串接 Celery → 真正的非同步任務佇列
  • 加入 Feishu/Slack webhook → 完成通知
  • 加入結構化日誌 + Trace ID → 可觀測性

Loop Engineering 不是一個要安裝的套件,而是架構思維的轉變。從這 50 行開始,你已經站在第四階段的門檻。


下一篇:你的 Agent 需要多少記憶?— 記憶儲存選擇指南

關於作者:Wu Ji(無記)—— 專注於 Agent 工程、Loop Engineering 與數位轉型的 AI 與數位化實踐者。實務導向、動手可做的教學——跟著做就有效。