Farms are the connective tissue of CortexObserver. Each is a managed AI service with its own LLM, prompts, skills, and human control UI. Agents consume Farms; humans govern them.
A Farm is not a resource pool. It is a governed boundary through which a whole category of capability — tools, models, memory, knowledge, budgets — is delivered to agents and observed by humans. Every Farm carries three dimensions at once:
| Dimension | What it means |
|---|---|
| Service | The capability it provides — tool execution, model routing, memory, retrieval, allocation |
| Enforcement | The constraints it imposes — grants, risk budgets, model tiers, quotas, prerequisite checks |
| Observability | The truth it surfaces to humans — spend, usage, risk consumption, freshness, timelines |
The key insight: enforcement is structural, not prompt-dependent. An agent cannot talk its way past a budget check or a tool grant. The code says no. That is what closes the circular loop — humans define the rules in a Farm's UI, the Farm enforces them in code, agents work within them, and the results flow back as live truth.
graph TD
subgraph Agents
A1["@allen"]
A2["@amy"]
A3["@charles"]
A4["@alice"]
A5["@chat"]
end
subgraph Farms["FARMS · managed AI services"]
MCP["🔧 MCPFarm<br/>100+ tools · risk scoring"]
GW["🧠 LLM Gateway<br/>LiteLLM · cost · EOL governance"]
MEM["💾 Memory Farm<br/>L1–L4 · consolidation"]
KN["📖 Knowledge Farm<br/>Qdrant hybrid RAG"]
ALLOC["💰 Allocation<br/>budgets · grants · quotas"]
end
A1 --> MCP
A1 --> GW
A1 --> MEM
A2 --> GW
A2 --> MEM
A3 --> GW
A3 --> KN
A4 --> KN
A4 --> GW
A5 --> GW
A5 --> MEM
ALLOC -. "enforces limits" .-> MCP
ALLOC -. "enforces limits" .-> GW
ALLOC -. "enforces limits" .-> MEM
ALLOC -. "enforces limits" .-> KN
style MCP fill:#2d6a4f,stroke:#1b4332,color:#fff
style GW fill:#1d3557,stroke:#0d1b2a,color:#fff
style MEM fill:#6a040f,stroke:#370617,color:#fff
style KN fill:#7b2d8e,stroke:#4a0e5c,color:#fff
style ALLOC fill:#b5651d,stroke:#7a3b10,color:#fff
The five Farms at a glance:
| Farm | What it manages | The LLM inside | Human control plane |
|---|---|---|---|
| MCPFarm | 100+ tools across 17 servers; pre-execution risk scoring + authorization | — (deterministic gate) | Tool grants, server health, invocation history, playground |
| LLM Gateway | LiteLLM routing, multi-provider model registry, tiers, failover, cost | Routes every model call | Model lifecycle (active → deprecated → EOL), per-agent budgets, usage analytics |
| Memory Farm | Four-tier agent memory (L1 Redis → L2 Postgres → L3 snapshots → L4 procedural) | Haiku-class consolidation | Cross-agent Memory Explorer with temporal search; consolidation; quotas |
| Knowledge Farm | Agent-namespaced RAG, Qdrant hybrid BM25 + dense + RRF | Embedding + extraction | Sources, collections, document lifecycle, retrieval testing |
| Allocation | Per-agent budgets, risk budgets, tool grants, memory/knowledge quotas | — (shared state) | The enforcement table — checked before every tool call, LLM invocation, memory write |
The only way agents touch the outside world. 100+ tools across 17 logical servers, each scored by risk and gated by authorization before it can fire.
File root: backend/src/cortex/mcpfarm/
Agents do not implement tools. They call tool_executor.execute(server, tool, params) and MCPFarm handles everything downstream — DB persistence, error handling, WebSocket events, and audit logging. That single chokepoint is what makes governance enforceable: there is no side door.
MCPFarm dashboard — registered servers, tool inventory, and per-server health at a glance.
Every tool carries a numeric risk score reflecting its blast radius (backend/src/cortex/mcpfarm/risk_scoring.py):
| Risk level | Score | Examples |
|---|---|---|
| Zero | 0 | echo.echo, calc.add (dev tools) |
| Low | 1–5 | governance.search, identity.lookup, ssm.get_parameter |
| Medium | 10–15 | identity.create, skills.approve, ssm.put_parameter |
| Medium-high | 20 | comms.send_email, comms.send_whatsapp |
| High | 30–50 | bootstrap.deploy_stack (50), bootstrap.rollback (50) |
Unknown tools default to 5. A deploy costs 50 risk points; a parameter read costs 1. The score is the currency of the daily risk budget.
Before any tool fires, the authorization gate runs four checks in order (backend/src/cortex/mcpfarm/authorization.py):
sequenceDiagram
participant Agent
participant Gate as Authorization Gate
participant Exec as Tool Executor
participant Alloc as Allocation
Agent->>Gate: execute(server, tool, params)
Note over Gate: 1. GRANT — tool in mcp_allowed_tools?
Note over Gate: 2. RISK BUDGET — daily points remaining?
Note over Gate: 3. PREREQUISITES — deploy params resolvable?
Note over Gate: 4. CIRCUIT BREAKER — if exhausted, read-only (score ≤ 2)
alt any gate fails
Gate-->>Agent: DENY (with reason)
end
Gate->>Exec: PERMIT — run tool
Exec->>Alloc: Commit risk spend
Exec-->>Agent: Result + audit log
This is a Reserve-Commit-Rollback pattern. The gate reserves by checking all four conditions; the tool executes only on permit; the spend is committed to the daily risk budget after execution; and on failure, risk points are not refunded — the attempt still cost something.
For bootstrap.deploy_stack, the gate loads the target template's CloudFormation Parameters section, identifies required parameters without defaults, and checks whether they exist in SSM or were supplied. If VpcId is required but unavailable, the gate blocks before any AWS call:
Cannot deploy 'Hello World': missing required parameters.
- VpcId: Deploy Foundation VPC first
- SubnetIds: Deploy Foundation VPC first (provides subnets)
This is structural — it catches the problem even if the LLM hallucinates that the VPC exists.
- Tool grants — which agents can call which tools, edited per agent
- Server health — live status across all 17 servers
- Risk score editor — adjust per-tool blast-radius scores
- Usage heatmap — tools × time, colored by invocation volume
- Playground — invoke any tool by hand, inspect the full request/response and audit trail
Every model call in CortexObserver routes through here. LiteLLM provider abstraction, a DB-backed multi-provider model registry, three tiers, failover chains, cost tracking, and full model end-of-life governance.
File: backend/src/cortex/gateway/llm.py
The Gateway exposes one invoke_llm() function that every agent and subsystem calls. Internally it routes through LiteLLM for provider abstraction, failover, and cost calculation — but not before it checks governance.
The model registry — providers, tiers, pricing, and lifecycle status (active → deprecated → EOL) managed without a code change.
_check_llm_governance() runs two gates ahead of any invocation:
- Budget check — if the agent's
llm_current_spend_usd >= llm_monthly_budget_usd, the call is refused. No more LLM calls until the budget resets or a human raises it. - Model tier enforcement — if the agent's
llm_allowed_modelslist is non-empty and the requested model is not on it, the call is refused. A fast-tier agent cannot reach for a reasoning model.
After the call, _record_agent_spend() deducts the calculated cost from the agent's allocation.
flowchart LR
REQ["Agent request"] --> BUDGET{"Budget<br/>check"}
BUDGET -- over limit --> DENY["DENY · $$$ exhausted"]
BUDGET -- ok --> TIER{"Model tier<br/>check"}
TIER -- not allowed --> DENY2["DENY · model restricted"]
TIER -- ok --> LITE["LiteLLM<br/>router"]
LITE --> ANTH["Anthropic"]
LITE --> OAI["OpenAI · others"]
ANTH --> RESP["Response"]
OAI --> RESP
RESP --> SPEND["Record spend<br/>update allocation"]
style REQ fill:#264653,stroke:#1d3440,color:#fff
style BUDGET fill:#2a9d8f,stroke:#1e7268,color:#fff
style TIER fill:#2a9d8f,stroke:#1e7268,color:#fff
style LITE fill:#1d3557,stroke:#0d1b2a,color:#fff
style ANTH fill:#e76f51,stroke:#a84b37,color:#fff
style OAI fill:#457b9d,stroke:#2e5468,color:#fff
style RESP fill:#264653,stroke:#1d3440,color:#fff
style SPEND fill:#b5651d,stroke:#7a3b10,color:#fff
style DENY fill:#9b2226,stroke:#641416,color:#fff
style DENY2 fill:#9b2226,stroke:#641416,color:#fff
Agents request capability by tier, never by hard-coded model string. models("fast") resolves to the current default fast-tier model; the registry decides what that is right now.
| Tier | Intent | Typical use |
|---|---|---|
| reasoning | Deepest, most capable | Deploy plans, multi-agent debate, complex synthesis |
| balanced | Capable + cost-aware | Most day-to-day agent work |
| fast | Cheap, low-latency | Classification, consolidation, routine turns |
All model configuration lives in the llm_models table — nothing is hardcoded. The runtime keeps an in-memory cache (refresh_model_cache()) rebuilt at startup and after every mutation.
| Field | Purpose |
|---|---|
model_id |
Unique identifier (e.g. claude-sonnet-4-20250514) |
provider |
Vendor — anthropic, openai, bedrock, vertex |
tier |
reasoning · balanced · fast |
is_default_for_tier |
One default per tier + provider |
pricing_input/output |
USD per 1M tokens — drives cost tracking |
litellm_model_string |
LiteLLM routing format |
status |
Lifecycle: active → deprecated → disabled |
failover_priority |
Ordering within the provider failover chain |
deprecated_by |
Successor model_id for the deprecation chain |
When a vendor announces a model's end of life, a human walks it through the lifecycle in the UI — mark it deprecated, point deprecated_by at its successor, then disable it on the cutoff date. Failover chains and tier defaults re-resolve automatically. No code change, no redeployment — the registry is the source of truth, and the governance is data.
- Usage tab — invocation metrics, provider breakdown, per-agent budget bars
- Model Registry tab — register, edit pricing, deprecate, disable models
- Tier management — assign models to reasoning / balanced / fast
- Threshold alerts — green < 70%, amber 70–90%, red > 90%
Agents remember — in tiers, indexed by UTC time, consolidated by an LLM, scoped per agent. Humans watch it all through the cross-agent Memory Explorer.
File root: backend/src/cortex/memory/
| Tier | Scope | Storage | Lifecycle |
|---|---|---|---|
| L1 | SESSION |
Redis | TTL-based, cleared per session |
| L2a | EPISODIC |
PostgreSQL | Timestamped event logs, append-only |
| L2b | SEMANTIC |
PostgreSQL | Distilled facts/knowledge, mergeable |
| L3 | SNAPSHOT |
PostgreSQL | Aggregated summaries for longitudinal reasoning |
| L4 | PROCEDURAL |
PostgreSQL | Learned tool-use patterns |
| — | GLOBAL |
PostgreSQL | Read-only shared context, admin-written |
Every entry (backend/src/cortex/memory/schemas.py) is anchored on observed_at — when the event occurred (UTC) — the universal time index. It also carries recall_window_hours (0 = permanent), source_agent_id (enabling cross-agent sharing), a structured category, tags, and scope.
The consolidation engine (backend/src/cortex/memory/consolidation.py) distills raw episodic events into structured semantic knowledge, using a haiku-class model for cost efficiency.
flowchart TB
L1["L1 Session<br/>Redis · TTL"] --> L2A["L2a Episodic<br/>Postgres · timestamped events"]
L2A -->|"LLM (fast tier)<br/>structured extraction"| L2B["L2b Semantic<br/>Postgres · distilled facts"]
L2B --> L3["L3 Snapshot<br/>Postgres · aggregated summaries"]
L3 --> L4["L4 Procedural<br/>Postgres · learned patterns"]
L2A -.-> EP["Recent episodic"]
EP --> LLM["LLM extraction"]
LLM --> CAT["6 categories:<br/>profile · preferences · entities<br/>events · cases · patterns"]
CAT --> DEDUP["Dedup &<br/>merge/append"]
DEDUP --> L2B
style L1 fill:#e63946,stroke:#a4262c,color:#fff
style L2A fill:#457b9d,stroke:#2e5468,color:#fff
style L2B fill:#1d3557,stroke:#0d1b2a,color:#fff
style L3 fill:#2d6a4f,stroke:#1b4332,color:#fff
style L4 fill:#6a040f,stroke:#370617,color:#fff
style LLM fill:#e9c46a,stroke:#b8972e,color:#000
style CAT fill:#2a9d8f,stroke:#1e7268,color:#fff
style DEDUP fill:#264653,stroke:#1d3440,color:#fff
Extraction sorts memories into six categories. Four are mergeable — updated in place when the same key recurs: profile, preferences, entities, patterns. Two are non-mergeable — append-only, every entry distinct: events and cases. Consolidation runs per agent or across all agents:
from cortex.memory.consolidation import consolidate_agent_memory
summary = await consolidate_agent_memory("allen", window_hours=24)Recall windows match the pace of each agent's domain — @allen at 72h (infrastructure is slow), @becky at 168h (identity changes are rare), @brian at 24h (code generation is session-focused).
The Memory Explorer is a cross-agent, temporal view of cognition. Pick any agent, scrub a time range, and watch memories render on a timeline grouped by hour or day.
Memory Explorer — temporal search across agents, with scope filters and a per-entry inspector.
- Agent selector — inspect any agent's memory
- Temporal timeline — memories indexed and grouped by time
- Scope filter — SESSION / EPISODIC / SEMANTIC / PROCEDURAL / SNAPSHOT
- Consolidation trigger — run a snapshot on demand
- Memory inspector — expand any entry for its full value, tags, and provenance
Agent-namespaced retrieval-augmented generation. Each agent gets its own Qdrant collection; retrieval is hybrid — BM25 + dense vectors fused by RRF; every document moves through a governed lifecycle.
File root: backend/src/cortex/knowledge/
Each agent owns a collection (knowledge_{agent_id}); a __global__ collection is readable by all agents and writable only by admin. KnowledgeStore (backend/src/cortex/knowledge/store.py) provides ingest, retrieve, and delete.
Knowledge Farm — sources, collections, document lifecycle, and corpus stats per agent.
flowchart LR
DOC["Document<br/>submitted"] --> QUOTA{"Quota check<br/>AllocationEnforcer"}
QUOTA -- over quota --> DENY["DENY · corpus full"]
QUOTA -- ok --> CHUNK["Chunk<br/>fixed · semantic · paragraph"]
CHUNK --> EMBED["Embed<br/>text-embedding-3-small · 1536d"]
EMBED --> QDRANT["Qdrant upsert<br/>dense + BM25"]
QDRANT --> TRACK["Track metadata<br/>knowledge_documents"]
style DOC fill:#7b2d8e,stroke:#4a0e5c,color:#fff
style QUOTA fill:#2a9d8f,stroke:#1e7268,color:#fff
style CHUNK fill:#264653,stroke:#1d3440,color:#fff
style EMBED fill:#e9c46a,stroke:#b8972e,color:#000
style QDRANT fill:#1d3557,stroke:#0d1b2a,color:#fff
style TRACK fill:#457b9d,stroke:#2e5468,color:#fff
style DENY fill:#9b2226,stroke:#641416,color:#fff
The AllocationEnforcer.check_knowledge_quota() gate runs first — checking knowledge_max_documents and knowledge_max_corpus_mb — before a single chunk is embedded.
Retrieval does not rely on vectors alone. Qdrant runs a dense semantic search and a BM25 lexical search in parallel, then fuses the two ranked lists with Reciprocal Rank Fusion (RRF) — combining semantic recall with exact-term precision. Results return to the agent with scores and provenance attached.
The Farm tracks where data comes from (backend/src/cortex/models/knowledge_source.py). A KnowledgeSource records source_type (s3, directory, git_repo, nosql, api), uri, owner_agent_id, status, retention_policy, and scan_interval_hours (0 = manual, >0 = auto-scan). A VectorizationConfig records how to process it — path_filter, embedding_model, chunk_strategy, chunk_size (512 default), chunk_overlap (50 default).
Documents move through a governed lifecycle: active (vectors current) → stale (source changed, vectors outdated) → re-embed back to active → archived (vectors removed, metadata kept) → decommed (fully removed).
- Source registry — list / add / edit / decommission data sources
- Collections — per-agent corpora and the shared
__global__collection - Document browser — docs by agent, source, and status, with lifecycle badges
- Vectorization status — pending / embedding / synced / stale per document
- Retrieval playground — query and see hybrid results with scores and provenance
- Stats — docs per agent, chunks per collection, freshness distribution
The shared state every other Farm reads before it acts. One record per agent; the single source of truth for budgets, risk, grants, and quotas.
File: backend/src/cortex/models/allocation.py
The AgentAllocation model is the hub of governance state. The AllocationEnforcer is consulted before every tool call, every LLM invocation, and every memory or knowledge write — it is the table the other four Farms check.
Allocation — per-agent spend vs budget, risk consumption, tool grants, and quotas in one enforcement view.
| Dimension | Fields | Enforced by |
|---|---|---|
| LLM budget | llm_monthly_budget_usd, llm_current_spend_usd, llm_allowed_models |
LLM Gateway (_check_llm_governance) |
| Risk budget | risk_budget_daily, risk_current_daily |
MCPFarm authorization |
| MCP grants | mcp_allowed_tools ([], ["*"], or a specific list) |
MCPFarm authorization |
| Memory quotas | mem_max_session_entries, mem_max_agent_entries, current counts |
Memory Farm |
| Knowledge quotas | knowledge_max_documents, knowledge_max_corpus_mb, current count |
Knowledge Farm ingest |
mcp_allowed_tools: []— no tools (agent cannot use MCPFarm)mcp_allowed_tools: ["*"]— all tools (unrestricted)mcp_allowed_tools: ["governance.search", "identity.lookup"]— specific grants
When no allocation record exists for an agent, all checks pass — fail open. This preserves compatibility with agents that predate the Allocation system. The moment a record is created, enforcement goes live.
- Budget overview — per-agent bar chart, spent vs budget
- Threshold alerts — green < 70%, amber 70–90%, red > 90%
- Grant manager — per-agent tool grant CRUD
- Model tier editor — allowed models per agent
- Reset controls — monthly spend reset, quota adjustment
- History chart — spend over time per agent
A single agent action can touch every Farm in sequence — each enforcing its own slice, all reading and writing the same Allocation state:
@allen receives: "Deploy Hello World to dev"
│
├─▶ Memory Farm recall(window_hours=72) — "what have I deployed recently?"
├─▶ MCPFarm iac.search_templates — grant check + risk −1
├─▶ MCPFarm ssm.list_parameters — grant check + risk −1
├─▶ LLM Gateway invoke_llm("analyze") — budget + tier check · records $0.003
├─▶ LLM Gateway invoke_llm("plan") — same governance checks
├─▶ MCPFarm bootstrap.deploy_stack — grant + risk −50 + prerequisite check
│ VpcId in SSM? YES → proceed
├─▶ Memory Farm write episodic(...) — quota check · session < max
└─▶ Allocation all spend/quotas updated across every dimension
Allocation is the bridge. Each Farm reads from it to enforce, and writes back to it after consuming resources — and the human sees every one of those numbers move in the dashboards. That is the circular loop, made of code.
- ARCHITECTURE.md — the circular loop, Humans/Agents/Farms, A.T.O.M, time as the universal index
- GOVERNANCE.md — Policies/Procedures/Standards, the Skills Store, Identity Store, risk & approval
- AGENTS.md — the repeatable agent pattern and the roster that consumes these Farms