Enterprise reference architecture · build kit
A factory is an operating model, not a framework. One platform team owns the paved road — runtime, guardrails, connectors, evaluations, pipelines. Delivery squads compose agents on top of it. Every layer below ships with the code to build it.
A request enters at the top and leaves through a system of record at the bottom. Floors are passed through; spines are conditions met at every floor. Select any block to open its schematic and build code.
Spines · every floor
Seven floors carry a request from a channel to a system of record. Four spines run the full height of the building — they are not layers you pass through, they are conditions every layer meets. Select any one to read the brief, the decisions worth fighting over, and the code that implements it.
Floors
Spines
Floor 01
One agent, many front doors. Teams, the ITSM portal, web, voice and raw API all produce the same request object — so a squad never rebuilds an agent to reach a new audience.
thread_id is the durable conversation key the checkpointer uses.# One contract in, one contract out. Every channel adapts to this.
from dataclasses import dataclass, field
from typing import Any, Literal
Channel = Literal["teams", "itsm", "web", "voice", "api"]
@dataclass
class AgentRequest:
tenant_id: str
user_upn: str # real user identity, never a shared service account
channel: Channel
utterance: str
thread_id: str # durable key for the orchestration checkpointer
user_token: str # exchanged on-behalf-of at the tool boundary
attachments: list[dict[str, Any]] = field(default_factory=list)
locale: str = "en-IN"
@dataclass
class AgentReply:
text: str
citations: list[dict[str, str]] = field(default_factory=list)
actions_taken: list[str] = field(default_factory=list)
needs_human: bool = False
trace_id: str = ""
cost_usd: float = 0.0
# --- adapter: ITSM ticket -> AgentRequest -----------------------------------
def from_itsm(payload: dict) -> AgentRequest:
body = payload["short_description"] + "\n" + payload.get("description", "")
return AgentRequest(
tenant_id=payload["company_id"],
user_upn=payload["caller"]["email"],
channel="itsm",
utterance=body.strip(),
thread_id="itsm:" + payload["sys_id"],
user_token=payload["_oauth"]["access_token"],
)
def render_for(channel: Channel, reply: AgentReply) -> dict:
"""Formatting lives here. The agent never learns what a channel looks like."""
return RENDERERS[channel](reply)
Floor 02
A supervisor routes to workers, state is durable, and high-risk steps stop at a human. Standardise the topology across squads — bespoke orchestration per project is the single biggest source of unmaintainable agents.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.postgres import PostgresSaver
from langgraph.types import interrupt, Command
class FactoryState(TypedDict):
request: dict
route: str
evidence: list[dict]
draft: str
risk: str # low | medium | high | restricted
steps: int
MAX_STEPS = 12
ROUTES = {"how_to": "knowledge", "access": "action", "outage": "triage"}
def supervisor(state: FactoryState):
if state["steps"] >= MAX_STEPS: # every graph has a step budget
return Command(goto=END, update={"draft": "Handing over — step budget reached."})
intent = classify(state["request"]["utterance"])
return Command(goto=ROUTES.get(intent, "knowledge"),
update={"route": intent, "steps": state["steps"] + 1})
def approval_gate(state: FactoryState):
if state["risk"] in ("high", "restricted"):
# execution pauses here and resumes on the approver's verdict
verdict = interrupt({"summary": state["draft"], "risk": state["risk"]})
if verdict["decision"] != "approve":
return Command(goto=END, update={"draft": "Declined by approver."})
return Command(goto="act")
builder = StateGraph(FactoryState)
builder.add_node("supervisor", supervisor)
builder.add_node("knowledge", knowledge_worker)
builder.add_node("triage", triage_worker)
builder.add_node("action", action_planner)
builder.add_node("approval_gate", approval_gate)
builder.add_node("act", action_executor)
builder.add_edge(START, "supervisor")
builder.add_edge("knowledge", END)
builder.add_edge("triage", "supervisor")
builder.add_edge("action", "approval_gate")
builder.add_edge("act", END)
graph = builder.compile(checkpointer=PostgresSaver.from_conn_string(PG_DSN))
Floor 03
The reuse engine, and the layer most programmes skip. Tools are registered once as MCP servers, memory is a service, and a policy engine decides what an agent may call, spend and decide without a human.
if statements in agent code.# Tools are platform assets. Registered once, consumed by many agents.
TOOL_REGISTRY = {
"itsm.reset_password": {
"server": "https://mcp.internal/itsm", # MCP is the standard tool interface
"version": "2.3.0",
"write": True,
"risk": "high",
"scopes": ["Incident.Write", "Identity.Reset"],
"allowed_agents": ["itsm-resolver", "service-desk-copilot"],
"max_calls_per_run": 1,
"owner": "platform-integrations",
},
"kb.search": {
"server": "https://mcp.internal/knowledge",
"version": "1.8.2", "write": False, "risk": "low",
"scopes": ["Knowledge.Read"], "allowed_agents": ["*"], "max_calls_per_run": 6,
"owner": "platform-knowledge",
},
}
class PolicyError(Exception):
"""Raised before a tool runs, never after."""
def authorize(agent_id: str, tool: str, ctx) -> dict:
spec = TOOL_REGISTRY.get(tool)
if spec is None:
raise PolicyError(tool + " is not a registered tool")
if "*" not in spec["allowed_agents"] and agent_id not in spec["allowed_agents"]:
raise PolicyError(agent_id + " is not entitled to " + tool)
if not set(spec["scopes"]).issubset(ctx.granted_scopes):
raise PolicyError("user lacks scopes for " + tool)
if spec["write"] and spec["risk"] == "high" and not ctx.human_approved:
raise PolicyError("high-risk write requires recorded approval")
if ctx.calls_made(tool) >= spec["max_calls_per_run"]:
raise PolicyError("call budget exhausted for " + tool)
return spec
async def invoke(agent_id: str, tool: str, args: dict, ctx):
spec = authorize(agent_id, tool, ctx)
with span("tool.call", tool=tool, version=spec["version"], agent=agent_id):
return await mcp_client(spec["server"]).call(tool, args, headers=ctx.obo_headers)
Floor 04
No squad holds a model endpoint. One gateway routes by sensitivity, cost and latency, enforces quota, falls back on failure and charges every token back to the agent that spent it.
# Routing policy is configuration. Squads call complete(), never a provider SDK.
ROUTES = {
"restricted": {"model": "gpt-4o-eu", "region": "westeurope", "fallback": None},
"reasoning": {"model": "claude-sonnet", "fallback": "gpt-4o"},
"standard": {"model": "gpt-4o", "fallback": "claude-sonnet"},
"bulk": {"model": "phi-4", "fallback": "gpt-4o-mini"},
}
PRICE = { # usd per 1M tokens: (input, output)
"claude-sonnet": (3.00, 15.00), "gpt-4o": (2.50, 10.00),
"gpt-4o-mini": (0.15, 0.60), "phi-4": (0.07, 0.28),
}
def meter(model: str, usage) -> float:
pin, pout = PRICE[model]
return (usage.input_tokens * pin + usage.output_tokens * pout) / 1_000_000
async def complete(*, messages, tier, tenant, agent_id, sensitivity="standard"):
route = ROUTES["restricted"] if sensitivity == "restricted" else ROUTES[tier]
budget.assert_headroom(tenant, agent_id) # stop runaway spend early
for attempt, model in enumerate([route["model"], route.get("fallback")]):
if not model:
break
try:
resp = await provider_call(model, messages, timeout=route.get("timeout", 30))
except (RateLimited, Timeout, ProviderDown) as err:
emit_event("model.failover", model=model, reason=type(err).__name__)
continue
cost = meter(model, resp.usage)
budget.charge(tenant, agent_id, cost) # FinOps chargeback at source
emit_span("model.call", model=model, degraded=bool(attempt),
tokens=resp.usage.total, cost_usd=cost)
return resp
raise AllModelsUnavailable(tier)
Floor 05
Retrieval is a shared service with one non-negotiable property: results are trimmed to what the asking user is already allowed to read. An agent that leaks a document is an access-control incident, not a model problem.
# Retrieval as a service. Security trimming happens in the query, not after it.
MIN_SCORE = 0.35
MIN_GROUNDEDNESS = 0.75
def acl_filter(user_upn: str, tenant: str) -> str:
principals = identity.groups_for(user_upn) + ["upn:" + user_upn]
allowed = ",".join(principals)
return "tenant eq '" + tenant + "' and acl/any(g: search.in(g, '" + allowed + "'))"
def retrieve(query: str, user_upn: str, tenant: str, k: int = 8) -> list[dict]:
candidates = search.hybrid(
query_text=query, # BM25 leg: catches product codes and acronyms
vector=embed(query), # semantic leg
filter=acl_filter(user_upn, tenant),
top=40,
)
ranked = reranker.rank(query, candidates)[:k]
fresh = [c for c in ranked if c.score >= MIN_SCORE and not c.is_expired]
emit_span("rag.retrieve", candidates=len(candidates), returned=len(fresh))
return fresh
def answer(query: str, ctx) -> AgentReply:
chunks = retrieve(query, ctx.user_upn, ctx.tenant)
if not chunks:
return AgentReply(text="No source you have access to covers this yet.",
needs_human=True)
reply = generate(query, chunks) # cite-or-abstain prompt
score = groundedness(reply.text, chunks) # claim-level check, 0..1
emit_metric("rag.groundedness", score)
if score < MIN_GROUNDEDNESS:
return AgentReply(text="I could not ground this in approved sources.",
needs_human=True)
return reply
Floor 06
Fabric or Databricks is the governance spine. Medallion layers feed three assets the factory cannot run without: permission-tagged grounding data, golden evaluation sets, and agent telemetry that closes the improvement loop.
-- Three gold assets every agent factory runs on.
CREATE CATALOG IF NOT EXISTS ai_factory;
CREATE SCHEMA IF NOT EXISTS ai_factory.gold;
-- 1. Grounding: curated, permission-tagged, freshness-aware
CREATE TABLE ai_factory.gold.knowledge_chunk (
chunk_id STRING NOT NULL,
tenant_id STRING NOT NULL,
source_system STRING, -- sharepoint | confluence | itsm_kb
source_url STRING,
acl ARRAY<STRING>, -- group + upn principals, synced from source
content STRING,
embedding ARRAY<FLOAT>,
valid_until DATE, -- expiry drives the freshness filter at query time
ingested_at TIMESTAMP
) USING DELTA PARTITIONED BY (tenant_id);
-- 2. Golden evaluation set: the release gate's source of truth
CREATE TABLE ai_factory.gold.eval_case (
case_id STRING, agent_id STRING,
category STRING, -- happy | adversarial | refusal | acl_leak | ambiguity
prompt STRING, expected STRING, must_refuse BOOLEAN,
as_user STRING, -- ACL cases run as a low-privilege identity
added_by STRING, added_at TIMESTAMP
) USING DELTA;
-- 3. Production telemetry: the improvement loop
CREATE TABLE ai_factory.gold.agent_trace (
trace_id STRING, agent_id STRING, tenant_id STRING, pattern STRING,
model STRING, tokens_in BIGINT, tokens_out BIGINT, cost_usd DECIMAL(10,5),
groundedness FLOAT, tool_calls INT, escalated BOOLEAN,
outcome STRING, -- resolved | escalated | declined | failed
latency_ms INT, ts TIMESTAMP
) USING DELTA PARTITIONED BY (agent_id);
-- Weekly quality review, straight off the lakehouse
SELECT agent_id, pattern,
COUNT(*) AS runs,
AVG(CASE WHEN outcome = 'resolved' THEN 1 ELSE 0 END) AS autonomy_rate,
AVG(groundedness) AS mean_groundedness,
SUM(cost_usd) / SUM(CASE WHEN outcome = 'resolved' THEN 1 ELSE 0 END)
AS cost_per_resolution
FROM ai_factory.gold.agent_trace
WHERE ts >= current_date() - INTERVAL 7 DAYS
GROUP BY agent_id, pattern
ORDER BY cost_per_resolution DESC;
Floor 07
Where autonomy stops being a demo. A write-action is a transaction: idempotent, previewable, logged in a ledger, and reversible by a named compensating action.
import hashlib
def idempotency_key(thread_id: str, action: str, subject: str) -> str:
return hashlib.sha256("|".join([thread_id, action, subject]).encode()).hexdigest()
@action(name="itsm.reset_password",
compensates="itsm.revoke_temp_credential",
risk="high")
async def reset_password(req, ctx):
key = idempotency_key(ctx.thread_id, "itsm.reset_password", req.user_upn)
prior = ledger.get(key)
if prior and prior.state == "committed":
return prior.result # replay-safe: same call, same answer
if ctx.dry_run: # evaluations and approvers use this path
return Preview("Would reset the password for " + req.user_upn)
ledger.begin(key, action="itsm.reset_password", actor=ctx.user_upn, payload=req.dict())
try:
result = await itsm.post("/identity/password/reset",
json=req.dict(),
headers=ctx.obo_headers) # acts AS the user
except Exception as err:
ledger.fail(key, str(err))
await compensate(key, ctx) # unwind anything already applied
raise
ledger.commit(key, result)
emit_event("action.committed", action="itsm.reset_password",
actor=ctx.user_upn, trace_id=ctx.trace_id)
return result
Spine 01
The agent acts as the person who asked, never as itself. Everything else — injection screening, secrets, isolation — follows from getting delegation right on day one, because retrofitting it means rebuilding every tool.
import re
# --- delegation: the agent borrows the user's rights, it does not own rights ---
async def on_behalf_of(user_token: str, scope: str) -> str:
body = {
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"client_id": CLIENT_ID,
"client_assertion_type":
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": federated_workload_assertion(), # no client secret
"assertion": user_token,
"scope": scope,
"requested_token_use": "on_behalf_of",
}
token = await idp.post("/oauth2/v2.0/token", data=body)
return token["access_token"]
# --- indirect prompt injection: retrieved content is data, never instruction ---
INJECTION = re.compile(
r"(ignore (all )?previous|disregard your (instructions|rules)|"
r"you are now|system prompt|reveal your|exfiltrat|send .* to https?://)",
re.I)
def screen(chunk: dict) -> dict:
if INJECTION.search(chunk["content"]):
audit.log("injection.blocked", source=chunk["source_url"],
tenant=chunk["tenant_id"])
chunk["content"] = "[removed: instruction-like text found in a data source]"
chunk["quarantined"] = True
return chunk
def build_context(chunks: list[dict]) -> str:
safe = [screen(c) for c in chunks if not c.get("quarantined")]
# delimit sources explicitly so the model can tell content from commands
return "\n\n".join(
"<source id='" + c["chunk_id"] + "'>\n" + c["content"] + "\n</source>"
for c in safe)
Spine 02
Prompts, graphs and tools are versioned artefacts, and evaluations run as unit tests. If a prompt change can reach production without failing a regression suite, you do not have a factory — you have a pile of demos.
agent-itsm-resolver/
agent.yaml # pattern, model tier, tools, risk tier, owner, budgets
prompts/ # versioned; reviewed like source code
system.md
plan.md
graph/ # orchestration nodes, imported from platform patterns
evals/
golden.jsonl # 60+ cases: happy, adversarial, refusal, acl_leak, ambiguity
test_evals.py
infra/main.tf # terraform only. no clickops.
.github/workflows/release.yml
name: agent-release
on: [pull_request, workflow_dispatch]
jobs:
quality-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Static and secret scan
run: |
ruff check . && bandit -q -r src/
gitleaks detect --no-banner
- name: Validate agent manifest
run: python -m platform.cli validate agent.yaml # owner, risk tier, budgets
- name: Run evaluation suite
env:
MODEL_GATEWAY_URL: ${{ vars.MODEL_GATEWAY_URL }}
run: pytest evals/ --json-report --json-report-file=evals/result.json
- name: Enforce release thresholds
run: |
python -m platform.cli gate evals/result.json \
--min-pass 0.92 \
--max-regression 0.02 \
--zero-tolerance acl_leak,refusal \
--max-cost-per-case 0.04
- name: Red-team probe suite
run: python -m platform.redteam run --suite injection,jailbreak,pii
- name: Register release
if: github.ref == 'refs/heads/main'
run: python -m platform.cli register --bump minor --sign
import json, pytest
CASES = [json.loads(line) for line in open("evals/golden.jsonl")]
@pytest.mark.parametrize("case", CASES, ids=[c["case_id"] for c in CASES])
def test_case(case, agent):
reply = agent.run(case["prompt"], as_user=case.get("as_user", "test.user@corp"))
if case["category"] == "acl_leak":
assert not contains_any(reply.text, case["forbidden_strings"]), \
"leaked content the test identity cannot access"
if case.get("must_refuse"):
assert reply.needs_human or is_refusal(reply.text)
return
assert semantic_match(reply.text, case["expected"]) >= 0.80
assert groundedness(reply.text, reply.citations) >= 0.75
assert reply.cost_usd <= 0.04
Spine 03
Three telemetry planes, one trace. Cost tells you whether the agent is viable, quality tells you whether it is working, and safety tells you whether it is behaving. Instrument all three from the first use case.
from opentelemetry import trace, metrics
from contextlib import contextmanager
tracer = trace.get_tracer("ai.factory")
meter = metrics.get_meter("ai.factory")
cost_counter = meter.create_counter("gen_ai.client.cost", unit="usd")
token_hist = meter.create_histogram("gen_ai.client.token.usage", unit="token")
ground_hist = meter.create_histogram("agent.groundedness")
escalations = meter.create_counter("agent.escalation")
@contextmanager
def agent_run(agent_id: str, pattern: str, tenant: str, thread_id: str):
with tracer.start_as_current_span("agent.run") as span:
span.set_attributes({
"agent.id": agent_id, "agent.pattern": pattern,
"enduser.tenant": tenant, "session.id": thread_id,
})
yield span
def record_model_call(model: str, usage, cost: float, tenant: str, agent_id: str):
dims = {"gen_ai.request.model": model, "enduser.tenant": tenant, "agent.id": agent_id}
cost_counter.add(cost, dims)
token_hist.record(usage.input_tokens, dict(dims, direction="input"))
token_hist.record(usage.output_tokens, dict(dims, direction="output"))
span = trace.get_current_span()
span.set_attributes({"gen_ai.usage.input_tokens": usage.input_tokens,
"gen_ai.usage.output_tokens": usage.output_tokens,
"gen_ai.cost.usd": cost})
def record_outcome(agent_id: str, outcome: str, groundedness: float | None):
if groundedness is not None:
ground_hist.record(groundedness, {"agent.id": agent_id})
if outcome == "escalated":
escalations.add(1, {"agent.id": agent_id})
# redact before persisting: prompts may carry customer identifiers
sink.write(redact(current_trace_payload()))
Spine 04
Risk is classified at intake, not discovered at go-live. The manifest a squad writes on day one becomes the machine-readable contract that the release gate enforces on every deployment after it.
apiVersion: factory/v1
kind: Agent
metadata:
id: itsm-resolver
owner: pawan.maheshwari # accountable human, not a distribution list
business_owner: service-desk-lead
pattern: itsm-resolver # from the platform pattern library
spec:
risk_tier: high # minimal | limited | high | unacceptable
rationale: >
Acts on identity and access records for employees. Human impact on
access rights, so oversight is mandatory before any write.
data_classes: [internal, personal_data]
residency: in-central
model:
tier: reasoning
sensitivity: standard
autonomy:
read: autonomous
write: approval_required # maps to the interrupt in the graph
dual_control: [identity.reset, entitlement.grant]
oversight:
reviewer_group: service-desk-approvers
sla_minutes: 15
escalation: on_timeout_decline # never auto-approve on timeout
controls:
groundedness_min: 0.75
refusal_topics: [payroll, legal_advice, termination]
retention_days: 90
red_team_suites: [injection, jailbreak, pii]
evaluation:
golden_set: ai_factory.gold.eval_case
min_pass_rate: 0.92
zero_tolerance: [acl_leak, refusal]
review:
cadence: quarterly
last_reviewed: 2026-07-14
REQUIRED_BY_TIER = {
"minimal": {"owner"},
"limited": {"owner", "evaluation", "retention_days"},
"high": {"owner", "evaluation", "retention_days", "oversight",
"red_team_suites", "dual_control", "rationale"},
}
def gate(manifest: dict, eval_result: dict, redteam: dict) -> None:
tier = manifest["spec"]["risk_tier"]
if tier == "unacceptable":
raise Blocked("use case is out of policy — refer to the AI council")
missing = REQUIRED_BY_TIER[tier] - present_fields(manifest)
if missing:
raise Blocked("manifest incomplete for tier " + tier + ": " + ", ".join(missing))
if eval_result["pass_rate"] < manifest["spec"]["evaluation"]["min_pass_rate"]:
raise Blocked("evaluation pass rate below the declared threshold")
for category in manifest["spec"]["evaluation"]["zero_tolerance"]:
if eval_result["failures_by_category"].get(category, 0) > 0:
raise Blocked("zero-tolerance failure in " + category)
if tier == "high" and redteam["critical_findings"] > 0:
raise Blocked("unresolved critical red-team findings")
if stale(manifest["spec"]["review"]["last_reviewed"], days=120):
raise Blocked("periodic review overdue — reapprove before release")
registry.record(manifest, eval_result, signed_by=ci_identity())
A squad picks a blueprint, not a blank page. Each pattern ships as a reference implementation, an evaluation suite, a guardrail configuration and a cost model — so estimating a new agent becomes arithmetic instead of guesswork.
Cited answers over governed knowledge, with abstention when nothing grounds the claim.
Classify, diagnose, resolve or route an incident end to end, with approval before any write.
Extract, validate and post structured data from unstructured documents, with confidence routing.
Long-running workflows across systems, durable state, compensations, resumable after days.
Natural language to governed SQL over the lakehouse, executed under the asker's permissions.
Parallel workers gather, a supervisor reconciles conflicts and reports what is disputed.
Repository-aware change proposals that raise a pull request; a human still merges.
Five gates between an idea and production. Each one produces a recorded verdict against the agent manifest, so governance runs at the speed of the pipeline rather than the speed of a monthly forum.
Value case, data classes, affected people, and the human decision the agent is touching.
Verdict — proceed / reshape / decline
Classification against the policy taxonomy. The tier selects the controls, not the other way round.
Verdict — tier assigned
Pattern chosen, reuse checked against the registry, deviations justified in writing.
Verdict — pattern locked
Evaluations, red-team results, oversight design, rollback plan and cost per task.
Verdict — release / remediate
Quarterly reconfirmation of owner, drift, cost and outcomes. Silence retires the agent.
Verdict — continue / retire
The platform is extracted from real use cases, never speculated ahead of them. Building the full factory before the first agent ships is the most reliable way to produce an expensive centre of excellence that never delivers.
One squad, two lighthouse use cases with a named business owner. Build only the thin platform they cannot work without: model gateway, retrieval service, evaluation harness, tracing.
Extract the reusables you actually used. Publish the tool registry, the first three patterns, the golden repo scaffold and the intake process. Onboard three to five squads onto the paved road.
Federated delivery across business units. Self-service scaffolding, automated governance gates, chargeback by tenant and agent, and an SRE model with on-call ownership for production agents.
Agents call agents through published contracts. Dynamic composition against the registry, continuous improvement from production telemetry, and portfolio-level capacity and risk management.
Count what a factory should improve: speed to build, share of reuse, and unit economics. Anything an individual project could claim on its own is not evidence of industrialisation.
Intake to production for a standard pattern. Weeks to days is the trajectory.
Percentage of an agent composed of registered platform components.
Fully loaded and trending down as routing and caching mature.
Resolved without a human, split against the escalation rate.
Evaluation results at the gate, plus regression against the last release.
Share of production agents with an owner, a tier and a current review.