Files
papers/hanzo-unified-harness/sections/03-the-seven-layer-architecture.tex

260 lines
13 KiB
TeX

\section{The Seven-Layer Architecture}
\label{sec:architecture}
\subsection{Design Principles}
The UAH architecture is guided by three principles. First, \textbf{separation
of concerns}: each layer has a single, well-defined responsibility. Second,
\textbf{typed interfaces}: all inter-layer communication uses typed contracts
with explicit invariants. Third, \textbf{cross-layer visibility}: every layer
emits structured telemetry that is readable by every other layer.
We define each layer formally as a tuple:
\begin{equation}
L_i = \left(\mathcal{I}_i,\; \mathcal{O}_i,\; \mathcal{S}_i,\;
F_i,\; \Phi_i\right),
\end{equation}
where $\mathcal{I}_i$ is the input type space, $\mathcal{O}_i$ is the output
type space, $\mathcal{S}_i$ is the internal state space, $F_i :
\mathcal{I}_i \times \mathcal{S}_i \to \mathcal{O}_i \times \mathcal{S}_i$ is
the layer's transition function, and $\Phi_i$ is the set of invariants that
must hold before and after each transition.
The complete UAH system is the composition:
\begin{equation}
\text{UAH} = L_7 \circ L_6 \circ L_5 \circ L_4 \circ L_3 \circ L_2 \circ L_1,
\end{equation}
where $\circ$ denotes functional composition with the cross-layer telemetry
bus providing side-channel communication between non-adjacent layers.
\subsection{Layer 1: LLM Access}
\begin{definition}[LLM Access Layer]
$L_1 = \left(\mathcal{I}_1, \mathcal{O}_1, \mathcal{S}_1, F_1, \Phi_1\right)$ where:
\begin{align*}
\mathcal{I}_1 &= \text{CompletionRequest} \times \text{RoutingPolicy} \\
\mathcal{O}_1 &= \text{CompletionResponse} \times \text{UsageMetrics} \\
\mathcal{S}_1 &= \text{ProviderPool} \times \text{CostLedger} \times \text{CapacityMap} \\
\Phi_1 &= \{\text{latency} \leq \tau_{\max},\; \text{cost} \leq \text{budget},\;
\text{availability} \geq 0.999\}
\end{align*}
\end{definition}
The LLM Access layer provides a unified interface to 100+ model providers
through a single API endpoint. The routing policy $\pi_{\text{route}} :
\text{Request} \to \text{Provider}$ is a learned function that minimizes
expected cost subject to latency and availability constraints:
\begin{equation}
\pi_{\text{route}}^*(r) = \argmin_{p \in \mathcal{P}}
\mathbb{E}\left[\text{cost}(p, r) \mid \text{lat}(p, r) \leq \tau_{\max}\right],
\end{equation}
where $\mathcal{P}$ is the set of available providers and $\text{lat}(p, r)$
is the predicted latency for request $r$ on provider $p$.
Key invariants include: (i) requests must receive responses within
$\tau_{\max}$ regardless of provider failures (achieved via automatic failover),
(ii) all usage is tracked in the cost ledger with sub-cent granularity, and
(iii) provider-specific constraints (context limits, rate limits, content
policies) are enforced transparently.
The layer supports heterogeneous model types: chat completions, embeddings,
image generation, audio transcription, and tool-use completions. The routing
function adapts dynamically: if a provider returns a 429 (rate limit), the
capacity map is updated and subsequent requests are routed away automatically.
\subsection{Layer 2: Tool Protocol}
\begin{definition}[Tool Protocol Layer]
$L_2 = \left(\mathcal{I}_2, \mathcal{O}_2, \mathcal{S}_2, F_2, \Phi_2\right)$ where:
\begin{align*}
\mathcal{I}_2 &= \text{ToolCallRequest} \times \text{ExecutionContext} \\
\mathcal{O}_2 &= \text{ToolCallResult} \times \text{AuditRecord} \\
\mathcal{S}_2 &= \text{ToolRegistry} \times \text{SandboxPool} \times
\text{PolicyEngine} \\
\Phi_2 &= \{\text{isolation} \geq \text{sandbox-level},\;
\text{idempotency} \in \{\text{safe, idempotent, unsafe}\}\}
\end{align*}
\end{definition}
The Tool Protocol layer implements the Model Context Protocol (MCP) standard
as its wire format, providing 54 built-in tools across seven categories:
\begin{itemize}[leftmargin=1.5em]
\item \textbf{Filesystem} (12 tools): read, write, patch, tree, glob,
watch, diff, archive, permissions, metadata.
\item \textbf{Shell Execution} (8 tools): run, background, kill, stdin,
stdout, stderr, env, signal.
\item \textbf{Code Intelligence} (10 tools): AST parse, symbol lookup,
type check, lint, refactor, definition, references, rename, format, complete.
\item \textbf{Search} (8 tools): ripgrep text, vector semantic, SQL
structured, web, docs, code, image, multimodal.
\item \textbf{Browser Automation} (9 tools): navigate, click, type,
screenshot, extract, form, cookie, network, accessibility.
\item \textbf{Process Management} (4 tools): list, kill, logs, signal.
\item \textbf{Agent Orchestration} (3 tools): spawn, handoff, broadcast.
\end{itemize}
Each tool is annotated with an idempotency class:
\begin{equation}
\text{IdempotencyClass} \in \{\texttt{SAFE},\; \texttt{IDEMPOTENT},\;
\texttt{UNSAFE}\},
\end{equation}
which determines retry behavior and sandbox isolation requirements. The policy
engine enforces per-agent capability constraints using a capability lattice
$\mathcal{C} = \langle C, \leq \rangle$ where $c_1 \leq c_2$ iff capability
$c_2$ subsumes $c_1$. An agent $a$ may invoke tool $t$ iff $\text{cap}(t) \leq
\text{grants}(a)$.
\subsection{Layer 3: Agent Runtime}
\begin{definition}[Agent Runtime Layer]
$L_3 = \left(\mathcal{I}_3, \mathcal{O}_3, \mathcal{S}_3, F_3, \Phi_3\right)$ where:
\begin{align*}
\mathcal{I}_3 &= \text{Task} \times \text{AgentSpec} \times \text{Context} \\
\mathcal{O}_3 &= \text{TaskResult} \times \text{Trace} \\
\mathcal{S}_3 &= \text{AgentGraph} \times \text{WorkflowStore} \times
\text{MemoryStore} \\
\Phi_3 &= \{\text{progress} \geq 0,\; |\text{context}| \leq C_{\max},\;
\text{quorum} \geq 1\}
\end{align*}
\end{definition}
The Agent Runtime executes agent graphs where nodes are individual agents and
edges represent handoff relationships. An agent $a_i$ is specified as:
\begin{equation}
a_i = \left(\text{name}_i,\; \text{model}_i,\; \text{instructions}_i,\;
\text{tools}_i,\; \text{handoffs}_i,\; \text{guardrails}_i\right),
\end{equation}
with $\text{handoffs}_i \subseteq \{a_j : j \neq i\}$ defining the subgraph
of agents to which $a_i$ can delegate.
Execution follows a triage-route-execute protocol. An orchestrator agent
receives the initial task, classifies it by type and complexity, routes to the
appropriate specialist agent (or spawns multiple agents for parallelizable
subtasks), and aggregates results. The runtime enforces three invariants:
(i) at least one agent is always making progress, (ii) context compression is
applied before any agent's context exceeds $C_{\max}$ tokens, and (iii) every
handoff is recorded atomically in the workflow store for resumability.
Memory is organized into three tiers following prior work~\cite{hanzo2024sdk}:
\begin{itemize}[leftmargin=1.5em]
\item \textbf{Working memory}: Current context window (ephemeral).
\item \textbf{Episodic memory}: Compressed summaries of prior turns
(persisted in Qdrant with embedding-based retrieval).
\item \textbf{Semantic memory}: Structured facts and learned patterns
(persisted in PostgreSQL with pgvector).
\end{itemize}
\subsection{Layer 4: Channel Integration}
\begin{definition}[Channel Integration Layer]
$L_4 = \left(\mathcal{I}_4, \mathcal{O}_4, \mathcal{S}_4, F_4, \Phi_4\right)$ where:
\begin{align*}
\mathcal{I}_4 &= \text{InboundMessage} \times \text{ChannelMetadata} \\
\mathcal{O}_4 &= \text{OutboundMessage} \times \text{DeliveryReceipt} \\
\mathcal{S}_4 &= \text{ChannelRegistry} \times \text{IdentityMap} \times
\text{ConversationStore} \\
\Phi_4 &= \{\text{identity.verified},\; \text{channel.active},\;
\text{delivery.at\_least\_once}\}
\end{align*}
\end{definition}
The Channel Integration layer provides a unified identity and conversation
model across 15 supported channels:
\begin{itemize}[leftmargin=1.5em, itemsep=0pt]
\item \textbf{Messaging}: Slack, Discord, Telegram, WhatsApp, iMessage,
SMS (Twilio).
\item \textbf{Email}: Gmail, Outlook (via IMAP/SMTP + OAuth).
\item \textbf{Developer}: GitHub (issues/PRs/comments), GitLab, Linear,
Jira.
\item \textbf{Voice}: Phone (Twilio), Zoom, Google Meet.
\item \textbf{Web}: REST API, WebSocket, Embedded chat widget.
\end{itemize}
The identity model maps channel-specific identities to a canonical user
representation: $\phi : \text{ChannelIdentity} \to \text{CanonicalUser}$.
This allows a conversation started on Slack to be resumed on email without
loss of context. Conversation state is persisted with a monotonic sequence
number per channel-session pair, ensuring at-least-once delivery semantics.
\subsection{Layer 5: Compute Tiers}
The Compute Tiers layer is developed extensively in Section~\ref{sec:tiers}.
For the purposes of the layered definition:
\begin{definition}[Compute Tiers Layer]
$L_5 = \left(\mathcal{I}_5, \mathcal{O}_5, \mathcal{S}_5, F_5, \Phi_5\right)$ where:
\begin{align*}
\mathcal{I}_5 &= \text{ComputeRequest} \times \text{TierPolicy} \\
\mathcal{O}_5 &= \text{ExecutionEnvironment} \times \text{ResourceMetrics} \\
\mathcal{S}_5 &= \text{TierMap} \times \text{AllocationTable} \times
\text{MigrationLog} \\
\Phi_5 &= \{\text{tier.available},\; \text{resources.within\_quota},\;
\text{isolation.enforced}\}
\end{align*}
\end{definition}
\subsection{Layer 6: Self-Improvement}
\begin{definition}[Self-Improvement Layer]
$L_6 = \left(\mathcal{I}_6, \mathcal{O}_6, \mathcal{S}_6, F_6, \Phi_6\right)$ where:
\begin{align*}
\mathcal{I}_6 &= \text{ExecutionTrace} \times \text{OutcomeSignal} \\
\mathcal{O}_6 &= \text{PolicyUpdate} \times \text{KnowledgeUpdate} \\
\mathcal{S}_6 &= \text{ExperienceBank} \times \text{PolicyStore} \times
\text{EvalHistory} \\
\Phi_6 &= \{\text{improvement.monotonic},\;
\text{regression.bounded}\}
\end{align*}
\end{definition}
The Self-Improvement layer operates four nested feedback loops at different
timescales:
\begin{enumerate}[leftmargin=1.5em]
\item \textbf{In-context loop} (seconds): Reflects on current task
execution, adjusts subsequent steps based on observed errors.
\item \textbf{Session loop} (minutes): Summarizes session outcomes,
updates the working memory with session-specific patterns.
\item \textbf{Periodic loop} (hours): Analyzes aggregated traces,
identifies systematic failure modes, updates routing policies.
\item \textbf{Epochal loop} (days): Fine-tunes specialist models or
updates semantic memory with distilled knowledge from completed tasks.
\end{enumerate}
The improvement constraint $\Phi_6$ requires that mean task-completion rate
over any rolling 24-hour window is non-decreasing across epochs, and that any
regression exceeding a threshold $\epsilon_{\text{reg}} = 0.02$ triggers an
automatic rollback of the offending policy update.
\subsection{Layer 7: Observability}
\begin{definition}[Observability Layer]
$L_7 = \left(\mathcal{I}_7, \mathcal{O}_7, \mathcal{S}_7, F_7, \Phi_7\right)$ where:
\begin{align*}
\mathcal{I}_7 &= \text{TelemetryEvent} \times \text{LayerID} \\
\mathcal{O}_7 &= \text{StructuredLog} \times \text{Metric} \times \text{Span} \\
\mathcal{S}_7 &= \text{TraceStore} \times \text{MetricStore} \times
\text{AlertRuleSet} \\
\Phi_7 &= \{\text{latency.p99} \leq 10\text{ms},\;
\text{loss} = 0,\; \text{retention} \geq 30\text{d}\}
\end{align*}
\end{definition}
The Observability layer provides a unified telemetry pipeline across all
other layers. Every LLM call, tool invocation, agent handoff, tier migration,
and improvement event is captured as a structured span in the OpenTelemetry
format~\cite{opentelemetry2023}. Spans are correlated by a globally unique
$\texttt{trace\_id}$ that is injected at task ingestion and propagated through
all seven layers.
The layer enforces three coverage invariants: (i) every agent action must
produce a span, (ii) every LLM call must record model, provider, input tokens,
output tokens, cost, and latency, and (iii) every error must capture the full
stack trace and the conversation context at the time of failure.
% ============================================================================
% 4. TIERED COMPUTE RUNTIME
% ============================================================================