Chapter 5 of 8
Build the runtime from deterministic seams
Chapter 5
About 5 minutes · includes examples, an exercise, and references
Chapter at a glance
- •Build admission, compilation, policy, validation, authorization, execution, normalization, reduction, and verification as explicit seams.
- •Keep reducers deterministic and persist events with state changes atomically.
- •Treat final answers as proposals whose completion claims require independent verification.
A first-principles agent runtime can be small. Its essential modules are a state repository, context compiler, policy adapter, action decoder, policy gate, tool dispatcher, observation normalizer, reducer, verifier, and event recorder. The implementation becomes reliable not by adding framework abstractions but by making each seam typed, replaceable, and independently testable.
The run begins with admission. Validate the goal against a product schema, authenticate the principal, choose a policy and tool-catalogue version, allocate budgets, and create immutable run metadata. Reject goals that are unsupported, contradictory, or outside authority before paying for a model call. Record a correlation identifier and initial state in the same durable transaction. Admission is also the place for tenant quotas, abuse controls, and an explicit risk tier that determines approvals and sandbox policy.
The context compiler transforms authoritative state into the policy input. It should be a pure function whenever practical. Inputs include the instruction version, goal, allowed action schemas, current facts, selected evidence, recent observations, unresolved errors, and remaining budgets. Output includes both the model payload and a manifest of included artifacts. The manifest is crucial: when behavior changes, an operator can see whether a fact was unavailable, excluded by ranking, summarized incorrectly, or ignored by the model.
The policy adapter isolates provider details. It accepts a model-neutral request and returns either a typed action proposal or a classified model failure. Capture model identifier, configuration, latency, usage, response identifier, and schema-validation status. Do not allow provider retries to be invisible; they consume time and money and can produce different outputs. Apply a deadline that leaves enough run budget to execute and verify an action.
The decoder must fail closed. Reject prose masquerading as JSON, unknown action names, extra fields, invalid enums, oversized arguments, and multiple actions when the state permits only one. Repair prompts can be useful for low-risk syntax errors, but repairs consume a bounded attempt and must never bypass authorization. A structured final answer is also an action proposal. The verifier decides whether it can terminate the run.
The policy gate evaluates the proposal using deterministic facts: principal, tenant, state version, phase, tool risk, budgets, approvals, and environment. Its result is allow, deny, require_approval, or transform_to_safer_action. The gate emits a reason code that enters the trace. A deny may return a bounded observation to the model or terminate immediately, depending on whether a safe alternative exists. Do not expose detailed security policy that helps an attacker search for bypasses.
The dispatcher executes one authorized action with a deadline and stable identity. Reads may still be expensive or sensitive, so enforce query limits and output bounds. Writes use an idempotency key and generate an effect receipt. Sandbox tools run under a manifest that fixes base image, repository revision, filesystem roots, network destinations, environment variables, CPU, memory, process count, and wall time. Cleanup is part of the tool contract, not a best-effort afterthought.
The observation normalizer converts provider-specific output into a closed result. Store large output in an artifact service and return a digest, preview, media type, and access label. Redact secrets before any model-visible projection. Classify errors using the tool error algebra. Attach provenance and freshness. If the tool succeeded but its response was lost, reconciliation should be available before retrying a write.
The reducer consumes current state, proposal, policy decision, observation, and receipts. It checks optimistic concurrency, appends an event, updates budgets, records artifacts, and computes the next phase. The reducer should not call the model, clock, random generator, or network directly. Pass nondeterministic facts as events so replay produces the same state. Commit the state and event atomically; publish downstream notifications through an outbox or equivalent mechanism.
Verification happens after every action, not only at the end. Local verification can check schemas, hashes, counts, invariants, and tests. Domain verification can re-read authoritative systems. A critic model may identify possible defects, but it is not independent when it shares the same blind spots and context as the actor. Use deterministic checks where available, separate models or prompts where useful, and human review where consequences demand accountable judgment.
The loop scheduler evaluates guards in a stable order: canceled, invariant violation, success verified, approval required, unrecoverable failure, no progress, budget exhausted, then continue. Ordering matters. A successful-looking observation must not override cancellation or an invariant violation. Record one terminal reason with supporting evidence. Terminal processing revokes temporary credentials, stops sandboxes, closes traces, persists final usage, and emits user-facing status.
Testing follows the seams. Use fixtures for context compilation, recorded policy outputs for decoder and reducer tests, fake tools for error cases, and deterministic clocks. Contract-test each tool against its schema. Run end-to-end tasks in disposable environments. Replay production traces with external calls disabled. Property tests can generate action sequences and assert budgets never go negative, terminal states never transition, approvals bind to one digest, and write receipts are never counted twice.
Frameworks can provide models, tools, sessions, handoffs, guardrails, and tracing, but the application still owns the contract. Keep domain state and effect receipts outside a provider-specific transcript. Wrap framework callbacks at the deterministic seams. This makes migration and incident investigation possible and prevents a framework upgrade from silently redefining authority or completion.
Key points
- Build admission, compilation, policy, validation, authorization, execution, normalization, reduction, and verification as explicit seams.
- Keep reducers deterministic and persist events with state changes atomically.
- Treat final answers as proposals whose completion claims require independent verification.
- Test with recorded proposals, fake tools, disposable environments, replay, and invariant-generating action sequences.
A complete bounded runtime with a recorded policy
Read the expected behavior in the surrounding walkthrough, then copy and run this reference implementation.
from __future__ import annotations
from dataclasses import dataclass, replace
from typing import Callable
@dataclass(frozen=True)
class State:
goal: str
facts: tuple[str, ...] = ()
steps: int = 0
terminal: str | None = None
Policy = Callable[[State], dict]
def authorize(action: dict) -> bool:
if not isinstance(action, dict):
return False
if action.get("name") == "lookup":
return (
set(action) == {"name", "query"}
and isinstance(action["query"], str)
and bool(action["query"].strip())
)
if action.get("name") == "finish":
return (
set(action) == {"name", "answer"}
and isinstance(action["answer"], str)
and bool(action["answer"].strip())
)
return False
def execute(action: dict) -> dict:
if action["name"] == "lookup":
return {"status": "ok", "fact": f"result:{action['query']}"}
return {"status": "ok"}
def reduce(state: State, action: dict, observation: dict) -> State:
if action["name"] == "lookup" and observation["status"] == "ok":
return replace(state, facts=state.facts + (observation["fact"],), steps=state.steps + 1)
if action["name"] == "finish":
answer = action.get("answer", "")
verified = bool(state.facts) and all(fact in answer for fact in state.facts)
return replace(state, steps=state.steps + 1, terminal="verified_success" if verified else "unverified_claim")
return replace(state, steps=state.steps + 1, terminal="tool_failure")
def run(goal: str, policy: Policy, max_steps: int = 3) -> State:
state = State(goal)
while state.terminal is None and state.steps < max_steps:
proposal = policy(state)
if not authorize(proposal):
return replace(state, terminal="policy_denied")
state = reduce(state, proposal, execute(proposal))
return state if state.terminal else replace(state, terminal="budget_exhausted")
def recorded_policy(state: State) -> dict:
return ({"name": "lookup", "query": "inventory"} if not state.facts
else {"name": "finish", "answer": "Inventory evidence: " + " ".join(state.facts)})
result = run("Report inventory evidence", recorded_policy)
assert result.terminal == "verified_success"
print(result)Worked examples
Toy
Recorded-policy weather agent
A runtime asks a recorded policy for either get_weather or finish and verifies that the final answer cites the last observation.
Because policy outputs and tool fixtures are deterministic, decoder, budget, reducer, and verifier tests run without a model or network. A malformed finish action cannot terminate.
- Policy fixtures are versioned.
- The reducer is pure.
- Finish requires observation evidence.
Application
Invoice anomaly investigator
The agent queries bounded invoice summaries, requests supporting records, and drafts a case without changing finance data.
Admission validates tenant and date range. Queries are parameterized and limited. Artifacts are stored by digest. The final case must identify invoice IDs and cite retrieved fields; a separate workflow owns any financial correction.
- The investigator is read-only.
- Every claim maps to an artifact.
- Correction authority is separate.
System
Versioned runtime deployment
Two runtime versions process different cohorts while sharing canonical state and effect services.
Runs pin policy, prompt, catalogue, schema, reducer, and verifier versions at admission. Trace comparison isolates behavioral differences. A rollback stops new admissions without corrupting running state.
- Version manifests are immutable per run.
- Effect identities survive runtime rollback.
- Evaluation compares matched task cohorts.
Exercise
Implement one deterministic seam
Build a runtime slice that can be tested entirely without a live model or external tool.
- Define state, proposal, observation, policy-decision, and event types.
- Implement a pure reducer and stable guard order for success, cancellation, failure, and budget exhaustion.
- Create recorded policy and tool fixtures for success, malformed output, denial, timeout, and duplicate receipt.
- Replay the same event sequence twice and compare serialized final state byte for byte.
Success criteria
- The reducer has no hidden clock, randomness, model, or network dependency.
- Malformed or unauthorized proposals never reach the executor.
- Duplicate receipts do not double-count effects.
- Replay produces the same state and terminal reason.
Reflect: Which framework convenience would make this seam harder to replay or migrate?
References and further reading
- OpenAI Agents SDK DocumentationOfficial documentation for agent loops, tools, handoffs, guardrails, sessions, and tracing.
- OpenAI Function Calling GuideOfficial documentation for typed tool definitions, arguments, execution, and tool results.
- ReAct: Synergizing Reasoning and Acting in Language ModelsThe ICLR 2023 paper that studies interleaved reasoning traces and environment actions.