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:
| Condition | Score |
|---|---|
| Goal FAILED | 0.8 |
| Goal COMPLETED | 0.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
| Limit | Value |
|---|---|
| Max reflections per goal | 3 |
| Max recent goals tracked | 10 |
| TTL per reflection | 7 days |
| Max reflections injected into context | 2 |
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.py — ReflectionEngine 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 Engine | Dream Mode | |
|---|---|---|
| Trigger | Each goal completion/failure | User inactivity (2h+) |
| Scope | Single goal | All recent memories |
| Storage | Redis per-goal (7d TTL) | dream_log table (permanent) |
| Context injection | Yes — per active goal | No direct injection |
| Speed | Real-time (async) | Batch (minutes) |