OODA Loop
The akh-medu agent operates on a continuous Observe-Orient-Decide-Act (OODA) loop. Each cycle gathers state, builds context, selects a tool, executes it, and evaluates progress toward goals.
Cycle Structure
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Observe │ --> │ Orient │ --> │ Decide │ --> │ Act │
│ │ │ │ │ │ │ │
│ Goals │ │ KG ctx │ │ Tool │ │ Execute │
│ WM │ │ Infer │ │ select │ │ Eval │
│ Episodes│ │ Episodes│ │ Score │ │ Progress│
└─────────┘ └─────────┘ └─────────┘ └─────────┘
^ │
└───────────── next cycle ──────────────────────┘
Phase Details
Observe
Gathers the current state of the world:
- Active goals and their status (Active, Suspended, Completed, Failed)
- Working memory size and recent entries
- Recalled episodic memories relevant to current goals
The observation is recorded in working memory as a WorkingMemoryKind::Observation.
Orient
Builds context from available knowledge:
- Collects adjacent KG triples for each active goal's symbols
- Runs spreading-activation inference from goal-related symbols
- Incorporates knowledge from recalled episodic entries
- Measures memory pressure (ratio of WM entries to capacity)
Decide
Selects the best tool for the current situation using utility-based scoring:
total_score = base_score - recency_penalty + novelty_bonus
+ episodic_bonus + pressure_bonus + archetype_bonus
| Factor | Range | Purpose |
|---|---|---|
base_score | 0.0-1.0 | State-dependent score (e.g., kg_query scores high when KG has few triples) |
recency_penalty | -0.4 to 0.0 | Penalizes recently used tools (-0.4 last cycle, -0.2 two ago, -0.1 three ago) |
novelty_bonus | 0.0 or +0.15 | Rewards tools never used on this goal |
episodic_bonus | 0.0 or +0.2 | Rewards tools mentioned in recalled episodes |
pressure_bonus | 0.0 or +0.2 | Boosts consolidation tools when memory is nearly full |
archetype_bonus | -0.07 to +0.07 | Psyche archetype weight bias |
The decision includes a score breakdown string for transparency:
[score=0.85: base=0.80 recency=-0.00 novelty=+0.15 episodic=+0.00 pressure=+0.00 archetype=+0.030]
Act
Executes the chosen tool and evaluates the outcome:
- Shadow check: If a psyche is configured, veto/bias patterns are checked before execution.
- Tool execution: The tool runs against the engine, producing a
ToolOutput. - Goal evaluation: Success criteria are checked against KG state:
- Keywords from criteria are matched against non-metadata triples.
- Tool output content is also checked for criteria matches.
- Progress assessment: Goal is marked Completed, Failed, or Advanced.
- Provenance: An
AgentDecisionprovenance record is stored.
Goal Management
Goals are the agent's driving objectives. Each goal has:
#![allow(unused)] fn main() { Goal { symbol_id: SymbolId, // KG entity for this goal description: String, // What to achieve success_criteria: String, // Evaluated against KG state priority: u8, // 0-255 (higher = more important) status: GoalStatus, // Active, Suspended, Completed, Failed stall_threshold: usize, // Cycles without progress before decomposition } }
Stall Detection
The agent tracks per-goal progress:
cycles_worked: Total OODA cycles spent on this goal.last_progress_cycle: Cycle number when last progress was made.
If cycles_worked - last_progress_cycle >= stall_threshold, the goal is
considered stalled and decomposition fires automatically.
Goal Decomposition
Stalled goals are split into sub-goals:
- Criteria are split on commas and "and" conjunctions.
- The parent goal is suspended.
- Child goals are created with Active status and linked via
agent:parent_goal/agent:child_goalpredicates.
Working Memory
Working memory is the agent's ephemeral scratch space:
#![allow(unused)] fn main() { WorkingMemoryEntry { id: u64, content: String, // Text representation symbols: Vec<SymbolId>, // Linked entities kind: WorkingMemoryKind, // Observation, Inference, Decision, Action, Reflection timestamp: u64, relevance: f32, // 0.0-1.0 source_cycle: u64, reference_count: u64, // Incremented when consulted } }
Capacity is configurable (default: 100 entries). When full, the oldest low-relevance entries are evicted.
Episodic Memory
High-relevance working memory entries are consolidated into episodic memories -- persistent long-term records:
#![allow(unused)] fn main() { EpisodicEntry { timestamp: u64, goal: SymbolId, summary: String, learnings: Vec<SymbolId>, // Symbols found relevant derivation_kind: DerivationKind, // AgentConsolidation } }
Consolidation fires automatically when memory pressure exceeds 0.8, or
manually via agent consolidate.
Session Persistence
The agent's full state (working memory, cycle count, goals, plans, psyche) is serialized to the durable store on exit and restored on resume:
# Persists automatically on exit
akh-medu agent run --goals "..." --max-cycles 20
# Resume where you left off
akh-medu agent resume --max-cycles 50
CLI Commands
# Single cycle
akh-medu agent cycle --goal "Find mammals"
# Multi-cycle
akh-medu agent run --goals "Discover planet properties" --max-cycles 20
# Fresh start (ignores persisted session)
akh-medu agent run --goals "..." --max-cycles 10 --fresh
# Interactive REPL
akh-medu agent repl
# Resume persisted session
akh-medu agent resume
# Trigger consolidation
akh-medu agent consolidate
# Recall episodic memories
akh-medu agent recall --query "mammals" --top-k 5
Configuration
#![allow(unused)] fn main() { AgentConfig { working_memory_capacity: 100, // Max WM entries consolidation: ConsolidationConfig::default(), max_cycles: 1000, // Safety limit auto_consolidate: true, // Auto-fire when pressure > 0.8 reflection: ReflectionConfig::default(), max_backtrack_attempts: 3, // Plan retries before giving up } }