Skip to main content

Self-Reflection Engine

The Self-Reflection Engine fires automatically when a goal reaches a terminal state (COMPLETED or FAILED). It generates a concise LLM insight about what happened, stores it per-goal, and injects it into future conversations when the same goal or similar tasks are in context.

Flow

Goal reaches COMPLETED or FAILED


asyncio.create_task(reflect_on_goal()) ← fire-and-forget, non-blocking


_generate_reflection()
├── collect task outputs (output_summary from TaskNode)
├── collect error messages (if FAILED)
└── LLM call → 1-3 sentence insight


_save_reflection()
├── Redis: reflection:{goal_id} (JSON list, max 3, TTL 7 days)
└── Redis: reflection:recent_goals (list of goal_ids, max 10)


build_context() → _reflections() → injected into system prompt

Importance Scoring

Not all reflections are equally valuable. The importance score determines injection priority:

ConditionScore
Goal FAILED0.8
Goal COMPLETED0.5
Error present in outputs+0.2 bonus

Higher-importance reflections are prioritized when context space is limited.

Storage

Reflections are stored in Redis with a 7-day TTL:

# View reflections for a goal
docker exec agent-redis redis-cli \
GET "reflection:{goal_id}" | python3 -m json.tool

# View recent reflected goals
docker exec agent-redis redis-cli \
LRANGE reflection:recent_goals 0 -1

Each reflection entry:

{
"goal_id": "abc-123",
"insight": "The web search returned stale data; in future, filter results by date.",
"importance": 0.7,
"goal_state": "failed",
"created_at": "2026-03-11T14:23:00"
}

Limits

LimitValue
Max reflections per goal3
Max recent goals tracked10
TTL per reflection7 days
Max reflections injected into context2

Context Injection

When build_context() is called with a goal_id, the engine retrieves reflections for that goal. When no goal is active, it retrieves recent reflections across goals.

Injected block format:

[PAST REFLECTIONS]
• [Goal: Research competitor pricing] The price data was outdated; use web_search with recent date filter next time.
• [Goal: Send weekly report] Email delivery succeeded but formatting was too dense; prefer bullet lists.

Source

src/reflection_engine.pyReflectionEngine class and format_reflections_for_context()

The engine is late-wired into the GoalOrchestrator at startup:

# src/main.py
reflection_engine = ReflectionEngine(model_manager, redis_url=settings.redis_url)
goal_orchestrator.reflection_engine = reflection_engine

Relation to Dream Mode

The Self-Reflection Engine and Dream Mode are complementary:

Self-Reflection EngineDream Mode
TriggerEach goal completion/failureUser inactivity (2h+)
ScopeSingle goalAll recent memories
StorageRedis per-goal (7d TTL)dream_log table (permanent)
Context injectionYes — per active goalNo direct injection
SpeedReal-time (async)Batch (minutes)