weiwuji

痛み: 午後の時間をかけてエージェントを調整したのに、翌朝になると白紙状態で — 昨日がなかったかのように。
学べること: 4段階の進化(Prompt → Context → Harness → Loop)と、メモリを永続化する50行のループエージェント。


0. 前提条件

  • Python ≥ 3.10
  • pip install openai (openai ≥ 1.0.0)
  • OpenAI API Key
  • OS: macOS / Linux / Windows WSL

目標: コードをコピー&ペーストして実行し、忘れないループエージェントを確認する。


1. 痛み: エージェントが一晩で忘れる理由

午前2時、ようやく多段階ワークフローが動作した。エージェントは丹念に設計したプロンプトに従い — データ取得、クリーニング、分析、可視化。満足してノートPCを閉じる。

翌朝、期待に胸を膨らませて会話を開くと、エージェントは白紙状態で — まるで何も起こらなかったかのように。

ログを確認する。エラーも例外もない。エージェントはすべてを再生成した — 昨日どこで止まったかを「忘れて」しまった。

これは冗談ではない。本気のAgent開発者なら誰でも経験する悪夢だ。根本原因は「モデルが賢くない」ではない。より根本的な事実: エージェントは夜を越えるよう設計されていなかった。


2. 4段階の進化: Prompt → Context → Harness → Loop

これを理解するため、シンプルな進化フレームワークを使う:

Stage What You Do Fatal Flaw
Prompt Engineering タスク記述、例、形式をプロンプトに記述する 予期しない入力で出力が破綻する
Context Engineering 履歴+中間結果をコンテキストウィンドウに詰め込む トークンコストが線形に増大し、ウィンドウ上限に到達する
Harness Engineering ツール呼び出し、構造化出力、エラー捕捉を追加 フレームワークはできたが、エージェントは依然として「ワンショット」
Loop Engineering 閉じたループを構築: state + memory + feedback + retry + persistence 本当のエンジニアリング — エージェントが「生き始める」

Loop EngineeringはPrompt Engineeringの否定ではなく、超越である。 Promptは依然として重要だ。ただしそれはエンジンであり、エンジンを車のように扱うことはできない。


3. 最小限の実行可能ループエージェント(50行)

以下が完全な、コピー&ペースト可能な、実行可能なループエージェントである。まず実行し、その後1行ずつ理解してほしい。

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. 再度実行 — エージェントは自動的にメモリを読み込む
python loop_agent.py

Enter fullscreen mode Exit fullscreen mode

エージェントは agent_memory.json から facts を読み込み、既知のコンテキストとして渡す。これが「一晩で記憶を保持する」仕組みである。


5. コードにマッピングされた6つのコンポーネント

Component Code Location Description
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 は非常に安価(1回あたり約$0.00015)である。レート制限を受けた場合は time.sleep(1) を増やすか、OpenAIダッシュボードを確認する。


7. 50行からの次のステップ

これで完全な実行可能ループエージェントが完成した。永続化にプレーンなJSONファイルを使用しており、手で各コンポーネントに触れられる。

拡張の方向性:

  • JSON → ChromaDBへの置き換え(ベクトルメモリ)
  • Celeryとの接続(本物の非同期タスクキュー)
  • Feishu/Slack webhookの追加(完了通知)
  • 構造化ログ+Trace IDの追加(可観測性)

Loop Engineeringはインストールするパッケージではない。アーキテクチャ思考のシフトである。 この50行から、あなたはステージ4の入り口に立っている。


次の記事: エージェントにどれだけのメモリが必要か? — メモリストア選択ガイド

著者について: Wu Ji (无记) — Agentエンジニアリング、Loop Engineering、デジタルトランスフォーメーションに注力するAI&デジタル化の実践者。実践的でハンズオンなチュートリアル — 実際に動く。