The LOGI Model
LOGI is a general pattern for organizing an agentic system into four clear responsibilities — Loop, Orchestrator, Gateway, Inference. It is not specific to Protege; it is a way of thinking about any agent that has to perceive input, decide, act, and sometimes act on its own initiative. Protege's architecture follows it, and each letter maps to a concrete part of the engine.
For the original conceptual framing, see The era of agentic application frameworks.
The four layers
Loop — autonomous work generation
The Loop is what lets an agent initiate action rather than only react. Work is generated on a schedule, independent of any inbound request, so the agent can act on standing goals of its own. The generated work runs through the same inference cycle an incoming request would — it is simply seeded by a scheduled instruction instead of by a message.
Implementation: Protege::Loop (engine/lib/protege/loop/) — a Scheduler that decides which duties are due and a Schedule that answers cron matching (backed by fugit) — plus the Protege::Responsibility / ResponsibilityRun records and an every-minute ResponsibilityTickJob. In The Scoop, this is how Scout mails the ops team a low-stock report every morning without anyone prompting it.
Orchestrator — the decision engine
The Orchestrator is the inference cycle. It takes work from either the Loop or the Gateway and runs the full loop: assemble context, call the model, execute any tool calls, feed the results back, and repeat — until the model answers without asking for a tool, or a turn budget runs out.
Context assembly → model call → tool calls? → execute → feed results → … → final replyImplementation: Protege::Orchestrator::Harness — an abstract base owning the tool loop, with ReplyHarness (reactive) and ResponsibilityHarness (proactive) subclasses. A max_tool_turns limit (default 8) bounds runaway tool chains.
Gateway — the protocol edge
The Gateway is the boundary between the outside channel and the engine. It normalizes inbound messages into a run and turns the agent's reply back into an outbound message, so the Orchestrator never touches the transport and the inference core never learns what a mailbox is.
Implementation: Protege::Gateway with Protege::AgentMailbox — inbound through Action Mailbox (persona routing, recursion guard, access check, bounce); outbound through SMTP, with the reply's From forced back to the sending persona's address. Tools send mail through context.deliver, never by talking to a transport themselves. So when a customer writes to support@thescoop.com, the Gateway is what routes them to Sundae, checks they're allowed in, and re-stamps the outgoing reply.
Inference — provider-agnostic generation
The Inference layer is the model, abstracted behind a normalized request/response so you can swap models without touching anything else. Adapters are strictly hash-in / hash-out; they never see the engine's internal value types.
Implementation: Protege::Inference (request building, generation, provider resolution) over provider adapters — Protege::Provider subclasses in app/providers/. The bundled Protege::OpenRouterProvider reaches many vendors through one API. The shared value objects (Inference::Provider::Request / Response / ToolCall) are the data contract; only behavior crosses the boundary, through the Inference module facade.
Module mapping
| LOGI layer | Role | Protege module |
|---|---|---|
| Loop | Autonomous work generation | Protege::Loop — responsibilities, cron dispatch |
| Orchestrator | Decision engine, tool loop | Protege::Orchestrator::Harness |
| Gateway | Protocol edge, I/O | Protege::Gateway + Protege::AgentMailbox |
| Inference | Model provider | Protege::Inference + Protege::Provider adapters |
Why this matters
The separation is not decoration — it is what keeps the engine testable and your app in control:
- Swap providers without touching tools. The harness only speaks the normalized request/response, so moving an agent from one model to another is a config change (
config.provider_id), not a rewrite. - Add tools without touching the inference loop. Tools are auto-discovered; the loop dispatches whatever the agent is granted.
- Add scheduled behavior without touching inbound handling. The Loop reuses the harness; the Gateway never learns about cron.
- Test each layer in isolation — the harness knows nothing about email, and the Gateway knows nothing about model APIs. Mail authentication and transport are Gateway concerns; routing and inference are Orchestrator concerns, and the two don't leak into each other.
The rest of this section walks each layer in turn: Gateway · Harness · Scheduler.

