Insights

Applied AI Architecture - 2. An Anatomy of Applied AI

Paste a video URL in the inspector

This is the second in a series documenting patterns from production AI systems. The first article covered why component-based architecture works where autonomous agents fail. Here we map the anatomy - the common structures that emerge across domains, and the failure modes that drive architectural responses.


When people talk about 'AI systems' inside companies, what they usually mean is a collection of loosely connected scripts, APIs, and model calls that happen to produce something useful most of the time. They call it a system because it works end to end, not because it was ever designed as one.

If you examine these projects closely - inside banks, e-commerce platforms, insurance providers, or analytics vendors - similar patterns emerge. A model call wrapped in a function. A retrieval index hanging off a vector database. A classifier fine-tuned for a single task. A handful of prompt templates. Glue code for retries and fallbacks. Less in the way of explicit boundaries, versioning discipline, visibility into model drift or latency chains.

Here we map that common anatomy and why it emerges.

A typical stack

Most enterprise 'AI systems' decompose into a recognisable structure:

A wrapper layer of thin Python or TypeScript functions calling a foundation-model API - OpenAI, Anthropic, Bedrock, Azure. Most teams start here.

A retrieval layer built as a RAG pipeline on FAISS, Weaviate, or Pinecone. It gives the model external context, though implementations often lack versioning or provenance control.

A classifier layer where a fine-tuned or prompted model assigns intent, topic, or sentiment labels for routing downstream. This is often integrated into the same script that handles orchestration.

An orchestration layer of chains implemented through LangChain, CrewAI, Autogen, or home-grown task queues. These coordinate LLM calls, API requests, and validation steps.

Validation and logging, often added incrementally as issues emerge - JSON parsing wrapped in try/except, with logs piped to Datadog or S3. Tracking structured metrics like schema conformity or prompt drift is less common.

A front-end that's usually a chat interface or API endpoint - lightweight, fast, and unaware of the probabilistic components behind it.

LangChain case studies show that production deployments are dominated by RAG and sequential chain patterns. Post-mortems from teams building agent-like systems describe similar architectures: a task router, a retriever, an LLM call, and a JSON validator. Industry surveys find that most production use cases combine RAG with deterministic orchestration. When analyses trace multi-agent system failures to their root causes, they consistently find missing boundaries or contracts between retrieval, generation, and validation modules.

Evolution of the stack

This type of structure doesn't arise from careful and intentional design. It emerges through iteration as teams add capabilities to working prototypes. The evolutionary stages are predictable.

It starts with a wrapper - a simple function that wraps a call to an LLM vendor's API. The initial prompt is written directly in the code. It works well for the initial use case.

Then the system needs to extract specific information from the LLM's free-text output. The team adds a parser using regular expressions or string splitting. Eventually, a slight change in the model's output format breaks the parser. The team adds a try/except block and some retry logic - the first layer of glue code.

The prompt grows too long. The team bolts on a vector database and a retrieval function. The system is now a RAG pipeline, but the retrieval, prompting, and parsing logic are all tightly coupled in the same script.

The system needs to handle different kinds of queries. The team adds if/else statements based on keywords in the user's input - a simple, brittle router.

As usage grows, the single script becomes a bottleneck. The team splits the logic into microservices, but the implicit dependencies between prompts and parsers remain, creating what amounts to a distributed monolith.

This process produces a system that works but carries unstated assumptions about prompt formats, model behaviours, and data schemas. These assumptions - the glue code - become a common source of failures in production.

The agentic spectrum

The term 'agentic' describes a spectrum, not a binary state or taxonomy. At one end sit deterministic workflows where an LLM handles specific tasks within a scripted sequence. At the other end sit autonomous systems where an LLM decides what to do next based on goals and observations.

(Before anyone says 'but agency' we need to consider that just this week Karpathy had self-driving agents a decade away.) Putting aside what you may have been reading in 2025, the fact is that production systems cluster toward the workflow end of this spectrum.

Post-mortems from developers tell a familiar story: they started with open-ended agent abstractions, hit problems with runaway loops and hallucinated tool invocations, and migrated to explicit function calls. The final designs are typically deterministic orchestrations - a classifier determines which tool to call, a validator checks the output. Early benchmarks of the Devin software engineering agent showed a 13.86% success rate on the SWE-bench benchmark without human assistance.

Framework evolution reflects this shift. Microsoft's Autogen now emphasises StateFlow, a state-oriented workflow model where agents can be replaced by explicit state transitions. LangChain's LangGraph positions itself as a tool for creating 'multi-agent workflows', but its core components are nodes, edges, and deterministic branching. CrewAI uses the term 'actors' within structured 'flows'.

Major consulting firms note in their reports that enterprises have begun pausing open-ended agent projects in favour of controlled, auditable workflows.

This clustering reflects how language models work: they generate one token at a time without seeing the complete solution ahead. This makes autonomous planning difficult to validate and error-prone to execute. Workflow-oriented designs bound this uncertainty by controlling where models make decisions and where deterministic logic takes over.

Common compositional patterns

Across domains, similar patterns recur:

Article content

The Databricks Documentation Assistant, described in internal case studies, demonstrates this composition. Retrieval from a vector index of product docs. LLM summarisation wrapped in deterministic schema output. A critic LLM verifies completeness. Output delivered to a chat UI.

Each step is a component with defined inputs and outputs. The probabilistic elements - retrieval, generation, validation - are wrapped in deterministic boundaries: schema enforcement, pass/fail checks, control flow.

Not all systems follow this pattern. When teams skip these boundaries, predictable failure modes emerge.

Common failure modes

Boundary collapse. Teams chain probabilistic components without deterministic checks - a boundary collapse. A faulty schema passes downstream and corrupts data. Post-mortems from MLOps platforms show that production incidents often originate from unvalidated intermediate outputs.

State loss. Each LLM call operates statelessly, so context must be manually persisted. Early LLM chains often failed when intermediate state wasn't properly serialized. Modern frameworks like LangGraph now externalise state machines for this reason.

Dependency drift. Un-versioned retrieval indices, prompt templates, and model versions create silent dependencies. An update to any one component can alter system behavior without warning. A common example: updating embedding models changes which documents get retrieved, degrading output quality for weeks before anyone notices.

Hidden coupling. Developers integrate new LLM calls directly into old workflows. When one changes output shape, several others break. The absence of explicit interfaces makes systems fragile.

Observability gaps. Unlike traditional services, LLM components don't emit predictable metrics. Failures appear as vague 'bad outputs' rather than stack traces or error codes. Without structured logging that captures prompt versions, model IDs, and input hashes, issues are difficult to reproduce.

These failures reflect common trade-offs made during rapid development. They stabilise once the architecture is made explicit.

Emerging architectural responses

As organisations reach production scale, common patterns emerge in response.

Contract enforcement. LLM boundaries now have declared schemas, enforced at runtime. Both Anthropic's API and OpenAI's API include features for structured outputs. Teams use libraries like Pydantic or JSON Schema to validate and reject malformed responses.

Workflow engines replacing chains. Tools like LangGraph and Autogen's StateFlow are shifting from imperative function chaining to declarative state transitions. This mirrors traditional service-oriented orchestration.

Retrieval versioning. Databricks, Weaviate, and other data platforms now support versioning for embedding indices, attaching metadata for provenance. This converts an invisible drift risk into an auditable dataset.

Validation loops. Two-pass systems - generator then critic - are becoming standard practice. The idea of a validation component is now formalised in various MLOps frameworks.

Microservice wrappers. Firms are extracting LLM logic into standalone microservices with APIs, SLOs, and monitoring. This architectural pattern, described in Octomind's 'Why We No Longer Use LangChain' blog post, treats every LLM invocation as a remote service, not an inline function.

These approaches converge on a component architecture: probabilistic inside, deterministic at the boundaries.

The integration layer

Between models and traditional applications sits code that's specific to your domain. It's not the model, and it's not the app. It's the logic that connects them: parsing model output into your domain objects, enforcing your business rules, validating against your databases, formatting for your systems.

This layer is where most engineering complexity lives. In practice, it's where applied AI succeeds or fails.

Capital One's internal architecture, profiled by Emerj, demonstrates this approach. The company treats LLM calls as black-box components wrapped by deterministic interfaces. Each call has a control layer that manages prompt configuration, schema enforcement, and audit logging. Governance and compliance are built into this application layer.

Why deterministic boundaries help

LLM systems differ from traditional software in a fundamental way.

Traditional software follows a path: design, verify correctness, implement, execute. LLM systems work differently: generate, validate, potentially regenerate.

Every token an LLM generates is selected from adjacent possibilities in probability space. The model cannot see the complete solution before starting. It cannot predict whether token 50 will work while generating token 10. It cannot verify the full output will satisfy requirements before beginning generation.

This isn't a temporary limitation - it's how language models work.

Architectural implications:

You cannot verify plans before execution. When an LLM generates a multi-step plan, it constructs it token-by-token. Step 5 might contradict step 2, but the model won't know until after it's generated both.

You cannot assess correctness from the output alone. The text might look valid - proper grammar, plausible numbers, correct format - but be factually wrong. Validation requires checking against external data: your database, your business rules, authoritative sources.

Models cannot reliably self-evaluate. 'Confidence scores' and 'self-correction' are pattern-matching behaviours from training, not actual assessment of correctness.

Architectural response:

Component architectures make these constraints manageable. Each LLM call becomes observable (logged with inputs, outputs, and validations), bounded (maximum attempts, timeouts), controlled (orchestrator decides when to retry or fail), and validated (external checks, not self-assessment).

Deterministic boundaries don't eliminate the probabilistic nature of model outputs - they contain it. Each component can be probabilistic internally, but the system as a whole behaves predictably because the interfaces between components are explicit and enforced.

Evidence from production

Production AI systems cluster around similar patterns. Projects fail when notebooks move to production without proper interfaces. Successful applications are workflow-based. Case studies across customer support, compliance, and documentation show the same components recurring: an interpreter, a retriever, a classifier, and a validator.

Patterns that stabilise systems

Understanding common patterns helps when designing, testing, and operating LLM systems.

Treat every LLM call as a component. Define its input and output contracts explicitly. Separate retrieval from reasoning - don't let embeddings silently mutate the model's context. Introduce validation early. Reject malformed outputs before they propagate.

Persist state explicitly. Don't rely on session memory. Instrument everything. Logs should include model version, prompt hash, latency, token cost, and schema result. Version everything. Indexes, prompts, validators - all should have unique identifiers.

From patterns to architecture

What we observe today is a transitional phase. Production systems work, but many still carry architecture debt from their prototype origins - implicit dependencies, missing contracts, inadequate observability. The patterns described here formalise what's already emerging: explicit boundaries, versioned components, structured validation. Early web applications evolved from Perl scripts into layered architectures. Applied AI is making the same shift from notebooks to composable systems.

Built on edition.pm