Roadie
Roadie’s Blog

Prompt Engineering vs Context Engineering: What's the Actual Difference and Why It Matters for Production AI

By David TuiteMarch 26th, 2026
Prompt Engineering vs Context Engineering

Many AI projects fail because of poor or irrelevant context inputs rather than model capability limitations. As Andrej Karpathy puts it , the LLM is the CPU and the context window is RAM. Everything the model can reason about during inference must fit into that buffer before the first token is generated. Your documentation site, service catalog, and runbooks are invisible to the model unless they’re explicitly loaded in.

This constraint defines where prompt engineering ends and context engineering begins. Prompts control how a model behaves, and context engineering determines what information actually reaches the context window when the model is called.

What is Prompt Engineering?

Prompt engineering covers everything within the instruction itself, such as the role specification, task description, output format, and behavioral constraints. It uses several well-understood techniques, including few-shot examples to calibrate output style, chain-of-thought decomposition to externalize reasoning, JSON mode to constrain structure, and negative instructions to prevent specific failure patterns.

If you provide a stable, well-scoped problem, prompt engineering can produce precise and consistent outputs for tasks where the input is predictable, the task is bounded, and the instruction is the primary variable. Some examples include classifying a support ticket, extracting fields from a contract, or generating a unit test from a function signature. These techniques assume the required information is already present in the context window.

However, the limitations of this approach become clear quickly in practice. A service dependency lookup agent can have a carefully crafted system prompt that is role‑specified, chain‑of‑thought enabled, and output schema constrained, but still return incorrect results when it runs without the relevant entity metadata:

vbnet
User: Which services does payments-service depend on?
Agent: The payments-service typically depends on fraud-detection,
       transaction-gateway, and ledger-store. [hallucinated]

If you inject the actual entity metadata from your catalog, an identical prompt with the relevant context produces a correct, grounded answer instead of a hallucinated one:

vbnet
User: Which services does payments-service depend on?
Agent: Based on the catalog data, payments-service depends on
       fraud-detection-service (v2.1), ledger-api (v1.4).
       [correct, sourced from injected context]

Nothing about the prompt changed. The improvement came entirely from what was loaded into the context window, which is exactly what context engineering controls.

What is Context Engineering?

Context engineering is about designing, populating, and optimizing everything that enters the context window before each LLM call. It’s a runtime system that assembles context dynamically for each invocation.

LangChain frames the scope across four operations.

  1. “Write” defines what state to persist across turns, such as conversation summaries, tool outputs, and agent memory.
  2. “Select” determines what to retrieve for a given call, pulling only the relevant subset of the knowledge base.
  3. “Compress” reduces token volume without discarding signal, using techniques like summarization, LLMLingua-style compression, and chunk deduplication.
  4. “Isolate” decides what to offload to sub-agents rather than placing everything in a single context window, which matters for multi-step tasks that would otherwise exhaust the budget.

Scope is where prompt engineering and context engineering diverge most clearly. A prompt is a static artifact that a developer writes, reviews, and commits. A context pipeline is infrastructure made up of retrieval chains, reranking models, summarization steps, schema injection, token budget management, and assembly logic. These components execute at runtime before each model call. Prompt engineering is typically owned by the person who writes the system prompt. Context engineering is owned by platform teams and lives alongside API servers, data pipelines, and observability tooling.

Diagnosing a context pipeline that misbehaves in production involves a different debugging surface. Rewriting the system prompt won’t resolve the issue. Instead, you need to trace retrieval results, audit the assembled context, and inspect token utilization.

Four Ways Prompt-Only Approaches Break in Production

1. Positional Attention Degrades Recall on Long Contexts

Stanford's "Lost in the Middle" research (Nelson F. Liu et al., 2023) showed that LLM recall performance degrades significantly when critical information appears in the middle of a long context. Models attend most reliably to content at the start and end of the context window. Put 30 retrieved documents into the context in arbitrary order, and the model will effectively ignore the middle 20. This is a positional attention pattern baked into how transformers process long sequences, which you can’t instruct your way around. The solution is to fix how you assemble and order the context.

2. Context Rot Hits Before You Hit Token Limits

Many teams observe accuracy degradation as context grows, even before hitting token limits. This is often due to reduced signal density and attention dilution rather than hard limits. Research on context rot shows that even on simple tasks, performance drops as total token count grows, well before you approach 128K or 200K limits. Switching to a larger context window, for example, from GPT‑4o to Claude’s 200K window, can delay the failure, but it does not address the underlying issue. A smaller context with tightly selected, high‑signal content consistently outperforms a much larger context filled with irrelevant material.

3. Static Prompts Can't Track Dynamic Systems

A static prompt encodes knowledge at write time. It doesn't know that payments-service changed its primary upstream dependency last Tuesday, that the on-call rotation shifted, or that the runbook was updated after last quarter's incident. The model answers from training data or from whatever static text you last injected, both stale by definition in a dynamic engineering environment.

4. Non-Determinism From Uncontrolled Context Is Impossible to Debug

For pipelines that depend on parseable, stable responses, such as incident triage agents, onboarding bots, and automated code review tools, it can be very difficult to identify failures due to information changing between prompts. You can't reproduce these by running the prompt again. You need observability on what was actually in the context window for the failing call.

Anatomy of a Production Context Pipeline

A typical production context pipeline runs as a sequence: user query, intent classification, parallel retrieval (vector search plus catalog API lookup), reranking, compression, context assembly, token budget audit, and LLM call. Anatomy of a Production Context Pipeline

A Python implementation of the core assembly step can use LangChain with a Backstage catalog API and a pgvector store:

python
import tiktoken
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import PGVector
import requests

ENCODING = tiktoken.encoding_for_model("gpt-4o")
TOKEN_BUDGET = 128_000
SLOT_WEIGHTS = {
    "system_instructions": 0.10,  # ~12,800 tokens
    "retrieved_context":   0.55,  # ~70,400 tokens
    "tool_schemas":        0.15,  # ~19,200 tokens
    "conversation_history": 0.20, # ~25,600 tokens
}

def fetch_catalog_entities(service_name: str, backstage_url: str) -> list[dict]:
    """Query Backstage catalog API for a component and its dependency metadata."""
    resp = requests.get(
        f"{backstage_url}/api/catalog/entities",
        params={"filter": f"kind=Component,metadata.name={service_name}"},
        headers={"Authorization": "Bearer \${BACKSTAGE_TOKEN}"},
    )
    resp.raise_for_status()
    entities = resp.json()
    return [
        {
            "name":        e["metadata"]["name"],
            "owner":       e["spec"].get("owner", "unknown"),
            "depends_on":  e["spec"].get("dependsOn", []),
            "description": e["metadata"].get("description", ""),
            "annotations": e["metadata"].get("annotations", {}),
        }
        for e in entities
    ]

def assemble_context(
    query: str,
    system_instructions: str,
    tool_schemas: list[dict],
    conversation_history: list[dict],
    backstage_url: str,
    vector_store: PGVector,
    service_name: str,
) -> dict:
    # Fetch typed entity metadata from the catalog
    catalog_entities = fetch_catalog_entities(service_name, backstage_url)

    # Vector similarity search over embedded TechDocs chunks
    docs = vector_store.similarity_search(query, k=8)

    # Assemble named slots with explicit budget allocation
    context = {
        "system_instructions": system_instructions,
        "retrieved_context": {
            "catalog_entities": catalog_entities,
            "techdocs": [d.page_content for d in docs],
        },
        "tool_schemas": tool_schemas,
        "conversation_history": conversation_history,
        "user_message": query,
    }

    # Enforce token budget before the LLM call
    # This is an approximation—actual token usage depends on how the context is serialized into the model’s message format.
    total_tokens = len(ENCODING.encode(str(context)))
    assert total_tokens < TOKEN_BUDGET, (
        f"Context exceeds budget: {total_tokens} / {TOKEN_BUDGET} tokens"
    )
    return context

The slot allocation (10% system instructions, 55% retrieved context, 15% tool schemas, 20% conversation history) is a starting point, not a fixed rule. Incident triage agents typically allocate more budget to retrieved context because the reasoning depends heavily on documents. Coding assistants often shift more budget toward conversation history to preserve code state across turns. You can encode these allocations directly in code so you can see exactly how a context was constructed when a call fails.

In production, each stage of the pipeline needs to be inspectable. That usually includes the retrieved documents, reranked results, final assembled context, and token counts. Without this visibility, debugging becomes guesswork. You can address this by capturing full context snapshots per request using tools like LangSmith or custom logging pipelines.

When a task no longer fits cleanly within a single context window, you can decompose it into sub‑agents. The sub‑agent boundary determines what information each agent needs access to and what it can safely ignore.

Prompt Engineering vs Context Engineering: Scope, Ownership, and Failure Mode

In production systems, prompt engineering functions as one component within a larger context assembly pipeline, with different ownership, tooling, and failure modes.

DimensionPrompt EngineeringContext Engineering
ScopeInstruction design for a single callFull context window lifecycle across all calls
OwnerPrompt author / ML engineerPlatform engineer / infrastructure team
Primary artifactSystem prompt stringRetrieval and assembly pipeline
Repeatability guaranteeVaries with context stateHigh when context assembly is deterministic
Failure modeNon-determinism from uncontrolled inputsContext rot or retrieval miss
Debugging approachRewrite instructions and re-testTrace retrieval results, audit token budgets, inspect assembled context

The Developer Portal as Context Infrastructure

For AI workflows inside an engineering organization, document retrieval on its own is rarely enough. When teams ask questions about a service, they usually need concrete facts like who owns payments-service, what it depends on upstream, what its SLO targets are, where the runbook lives, and who is on call. That information exists as structured data, not as natural language text, and treating it as just another document tends to produce unreliable results.

The Backstage catalog API exposes this data directly. A GET /api/catalog/entities?filter=kind=Component call returns JSON with fields like kind, spec.owner, spec.dependsOn, metadata.annotations, and metadata.description. These map directly to an LLM context schema with minimal transformation. The more structured the source data, the smaller the hallucination surface area. The model doesn't need to infer ownership or dependency relationships from text because the typed fields make both explicit.

The difference becomes most apparent during incident triage workflows. In practice, this usually involves an internal tool being queried as the first step of triage. For example, if an agent receives a PagerDuty alert for payments-service without catalog context, it falls back to generic advice like checking database connections, CPU utilization, or recent deployments. When catalog data is injected at call time, including the owning team, upstream dependencies, and the linked runbook, the response references the actual dependency chain and points to the correct escalation path.

Roadie provides this data layer for both human engineers and AI agents. The catalog API, TechDocs indexing, and entity relationship graph are maintained out of the box. Teams don't absorb the operational cost of self-hosting Backstage, including upgrade cycles timed with every Backstage release, plugin compatibility work, and infrastructure ownership, while also building the AI pipelines on top of it. Roadie handles the context source, while your team focuses on the retrieval and assembly pipeline.

Start Here: Wire Your Catalog Into a Context Pipeline Today

The service metadata in your catalog is already structured, authoritative, and queryable. You can wire that data into a retrieval pipeline that your agents can consume in three steps:

1. Export catalog entities: Call GET /api/catalog/entities?filter=kind=Component against your instance. The response gives you all service entities as JSON. Extract metadata.name, spec.owner, spec.dependsOn, and metadata.description for embedding.

2. Chunk and embed: Concatenate the relevant fields into a single text block per entity and generate embeddings.

ini
```python
import openai

entity_text = (
    f"{entity['name']}: {entity['description']}. "
    f"Owner: {entity['owner']}. "
    f"Depends on: {', '.join(entity['depends_on'])}"
)
response = openai.embeddings.create(
    model="text-embedding-3-small",
    input=entity_text,
)
vector = response.data[0].embedding
# Store vector alongside raw entity JSON in pgvector
```

Store vectors alongside the raw JSON entity payload in pgvector . For higher query volume or managed infrastructure, Pinecone offers the same cosine similarity search without the Postgres operational overhead.

3. Build the context assembly function: On each agent query, run similarity search against the vector store, retrieve the top‑k entity payloads, and inject the serialized JSON into the model’s context before the LLM call.

Any team with a populated service catalog can implement this with minimal overhead and typically see an immediate reduction in hallucinated service names and incorrect ownership references. Once real service metadata occupies the context window, the model stops inventing relationships from training data.

If your team is already running Backstage or Roadie, your context source exists. The next step is building the retrieval pipeline on top of it. Book a demo to see how Roadie structures catalog data for AI-ready context retrieval.

Become a Backstage expert

To get the latest news, deep dives into Backstage features, and a roundup of recent open-source action, sign up for Roadie's Backstage Weekly. See recent editions.