1. Typed Context Engineering: Decomposing RAG Inputs for Cache Efficiency and Budget Control

Treating all retrieved content as a single undifferentiated blob is the most common RAG performance mistake — splitting context into typed inputs unlocks selective caching, granular budget control, and cleaner observability instrumentation.

  • Typed inputs expose what the LLM actually consumes per call, making it possible to trace which context category (document chunk, conversation history, tool output, system instruction) drove a given response — critical for debugging retrieval quality in production.
  • Cache hit rates improve dramatically when typed inputs are separated because static system context and slow-moving document chunks can be cached independently of volatile conversation turns and tool outputs.
  • Budget governance becomes precise rather than approximate: you can enforce token ceilings per input type at the pipeline layer, not just at prompt assembly time, which means downstream agents inherit well-shaped context rather than overflow.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]

The four-typed-input framing — document chunks, conversation context, tool outputs, and system instructions — maps almost perfectly onto the context budget problem that plagues high-throughput RAG pipelines. When these categories are structurally distinct at the pipeline layer (not just semantically distinct in the prompt string), you can make routing decisions about each one independently: cache the document embeddings, stream the conversation, debounce the tool calls, and pin the system prompt. This is less about prompt engineering and more about treating context assembly as a typed data pipeline with its own governance surface. Source: [Context Engineering for RAG: The Four Typed Inputs Behind Every RAG Answer] — Author not specified (https://towardsdatascience.com/context-engineering-for-rag-the-four-typed-inputs-behind-every-rag-answer/)

AIThis connects directly to the observability and governance maturation happening across agentic systems more broadly — the same discipline that drives provenance logging in agentic pipelines (tracking which tool produced which output) applies here to context provenance: knowing exactly which typed input produced which tokens in the final prompt is the foundation for both latency optimization and auditability.

Reference Architecture
// TypeScript: Typed context assembler with per-category budget enforcement
const BUDGET: Record<ContextType, number> = {
  system: 512,
  documents: 2048,
  conversation: 1024,
  tools: 512,
};

type ContextType = 'system' | 'documents' | 'conversation' | 'tools';

interface TypedContextInput {
  type: ContextType;
  content: string;
  sourceId: string;  // for provenance logging
  tokenCount: number;
}

function assembleContext(
  inputs: TypedContextInput[],
  tokenizer: (s: string) => number
): { prompt: string; provenance: Record<ContextType, string[]>; overflow: ContextType[] } {
  const grouped = inputs.reduce((acc, input) => {
    acc[input.type] = [...(acc[input.type] ?? []), input];
    return acc;
  }, {} as Record<ContextType, TypedContextInput[]>);

  const overflow: ContextType[] = [];
  const provenance: Record<ContextType, string[]> = { system: [], documents: [], conversation: [], tools: [] };
  const parts: string[] = [];

  for (const [type, items] of Object.entries(grouped) as [ContextType, TypedContextInput[]][]) {
    let budget = BUDGET[type];
    for (const item of items) {
      if (item.tokenCount > budget) {
        overflow.push(type);
        continue;
      }
      parts.push(item.content);
      provenance[type].push(item.sourceId);
      budget -= item.tokenCount;
    }
  }

  return { prompt: parts.join('\n\n'), provenance, overflow };
}
State Interaction Chart
flowchart TD Q[User Query] --> CtxAssembler[Context Assembler] SysPrompt[System Instructions\ncached, static] --> CtxAssembler DocChunks[Document Chunks\ncached by embedding hash] --> CtxAssembler ConvHistory[Conversation Context\nstreamed, volatile] --> CtxAssembler ToolOutputs[Tool Outputs\ndebounced, TTL-scoped] --> CtxAssembler CtxAssembler --> BudgetGate{Token Budget\nper type enforced} BudgetGate -->|within limits| LLM[LLM Call] BudgetGate -->|overflow| Trimmer[Per-Type Trimmer] Trimmer --> LLM LLM --> Response[Typed Response\nwith provenance tags]

2. LTAP Architecture: Eliminating CDC Lag from Operational RAG Knowledge Bases

If your RAG system grounds answers in live business data (orders, accounts, tickets), LTAP's single-copy columnar storage model cuts the pipeline from 'transaction committed' to 'retrievable by the agent' from minutes to near-zero without adding infrastructure.

  • CDC pipelines introduce a hidden freshness tax in operational RAG — by the time a database change propagates through Kafka, into a secondary store, and gets re-embedded, the data a customer-facing agent retrieves can be 5-15 minutes stale at best.
  • LTAP stores data once in open columnar format that both Postgres and lakehouse query engines read natively, meaning the same row a transaction just committed is immediately queryable by your retrieval layer without a second write.
  • The architectural shift from HTAP (one engine for both workloads) to LTAP (one storage format, two engines) means transactional throughput doesn't degrade when the RAG retrieval layer scales up — they don't compete for the same compute resources.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]

The conventional RAG knowledge base update cycle looks like: write to Postgres → CDC event → Kafka topic → consumer → embedding service → vector store upsert. Each hop adds latency and a failure surface. LTAP collapses this by making the operational data store itself the source of truth for analytics — the columnar format is the storage layer, not a derived copy. For customer-facing agents answering questions about live account state or recent activity, this is the difference between grounding in current reality versus grounding in a snapshot. Source: [From monolith to Lakebase to LTAP: rethinking the database from storage up] — Author not specified (https://www.databricks.com/blog/lakebase-ltap-rethinking-database-storage)

AIThis connects to the broader pattern of eliminating intermediate state in agentic pipelines — the same instinct that drives people toward direct tool execution rather than pre-cached tool results. The fewer transformation hops between 'fact exists in the world' and 'fact is retrievable by the agent,' the more trustworthy the agent's answers become. That's not just a latency win; it's a governance win, because you reduce the number of places where data can diverge.

Reference Architecture
// TypeScript: Retrieval adapter that sources from LTAP-backed query endpoint
// No CDC polling, no vector store TTL management — queries live columnar data directly

interface LTAPRetrievalConfig {
  endpoint: string;
  maxRows: number;
  freshnessWindowMs: number; // still useful for rate-limiting staleness assertions
}

async function retrieveFromLTAP(
  query: string,
  entityId: string,
  config: LTAPRetrievalConfig
): Promise<{ rows: Record<string, unknown>[]; retrievedAtMs: number; lagMs: number }> {
  const start = Date.now();

  const response = await fetch(config.endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      sql: query,
      bindings: { entity_id: entityId },
      limit: config.maxRows,
    }),
  });

  const { rows, committedAtMs } = await response.json();
  const retrievedAtMs = Date.now();
  const lagMs = retrievedAtMs - committedAtMs;

  if (lagMs > config.freshnessWindowMs) {
    // Emit to observability — not throw; agent should degrade gracefully
    console.warn({ event: 'ltap_freshness_breach', lagMs, entityId, retrievedAtMs });
  }

  return { rows, retrievedAtMs, lagMs };
}
State Interaction Chart
flowchart TD subgraph Traditional_RAG_Freshness TXN1[Postgres Commit] --> CDC[CDC Event] CDC --> Kafka[Kafka Topic] Kafka --> Embedder[Embedding Service] Embedder --> VectorDB[Vector Store Upsert] VectorDB --> Agent1[Agent Retrieval] end subgraph LTAP_RAG_Freshness TXN2[Postgres Commit] --> ColStore[Columnar Storage\nopen format, single copy] ColStore --> PG[Postgres Engine\ntransactional queries] ColStore --> Lakehouse[Lakehouse Engine\nRAG retrieval queries] Lakehouse --> Agent2[Agent Retrieval] end style Traditional_RAG_Freshness fill:#2d2d2d,stroke:#ff6b6b style LTAP_RAG_Freshness fill:#2d2d2d,stroke:#51cf66

3. Local Inference for RAG Preprocessing: Where the Latency Math Now Works

For RAG preprocessing steps — query expansion, chunk reranking, summarization — running a local 7B-20B model is now faster and cheaper per request than an API round-trip, which changes the architecture of what belongs in the hot path versus the cold path.

  • Query expansion latency drops from 200-400ms (hosted API round-trip) to 30-80ms on M2/M3 class hardware for 7B-13B models, making it viable to expand every user query before retrieval rather than only for low-confidence cases.
  • Local reranking models can run as a sidecar to your retrieval service — no network hop, no token-based pricing, no rate limit risk during traffic spikes — making them a strong fit for the highest-throughput node in a customer-facing RAG pipeline.
  • The failure mode shifts from 'model API is down' to 'local model memory pressure under load,' which is a different operational problem — one that benefits from the same circuit-breaker patterns you already use for database connection pools.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]

Running Qwen 3 MOE or Gemma 3 locally on developer and staging hardware is now routine, which signals a more important shift: the models are reliable enough to embed into production pipeline stages where they were previously too slow or too unreliable to trust. For a RAG pipeline serving customer-facing agents, the reranking step is the biggest latency contributor after the vector search itself — and it's also the step most sensitive to API rate limits under load. Offloading that step to a local inference process co-located with the retrieval service eliminates the network RTT and the rate limit risk simultaneously. Source: [Running local models is good now] — Vicki Boykis (https://vickiboykis.com/2026/06/15/running-local-models-is-good-now/)

AIThis connects to the typed context engineering pattern from block_1: if reranking and query expansion run locally without a network hop, you can afford to run them inline in the context assembly phase rather than as a separate async preprocessing step. That reduces pipeline stages, simplifies the observability surface, and makes the latency profile more predictable — all things that matter for production-grade agentic systems.

Reference Architecture
// TypeScript: Local inference sidecar client for RAG preprocessing
// Wraps llama.cpp HTTP server — same interface whether local or hosted

const localInference = {
  async expandQuery(query: string): Promise<string[]> {
    const res = await fetch('http://localhost:8080/completion', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        prompt: `Generate 3 search query variants for: "${query}"\nReturn as JSON array.`,
        max_tokens: 256,
        temperature: 0.3,
      }),
    });
    const { content } = await res.json();
    try {
      return JSON.parse(content) as string[];
    } catch {
      return [query]; // degrade gracefully — original query still works
    }
  },

  async rerankChunks(
    query: string,
    chunks: { id: string; text: string }[]
  ): Promise<{ id: string; score: number }[]> {
    const res = await fetch('http://localhost:8080/rerank', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ query, documents: chunks.map(c => c.text) }),
    });
    const { scores } = await res.json();
    return chunks
      .map((c, i) => ({ id: c.id, score: scores[i] as number }))
      .sort((a, b) => b.score - a.score);
  },
};
State Interaction Chart
sequenceDiagram participant Agent participant ContextAssembler participant LocalModel as Local Inference\n(sidecar, 7B-13B) participant VectorDB participant HostedLLM as Hosted LLM\n(final generation) Agent->>ContextAssembler: raw user query ContextAssembler->>LocalModel: expand query\n(30-80ms local) LocalModel-->>ContextAssembler: expanded query variants ContextAssembler->>VectorDB: multi-variant retrieval VectorDB-->>ContextAssembler: candidate chunks (top-k) ContextAssembler->>LocalModel: rerank chunks\n(no network hop) LocalModel-->>ContextAssembler: ranked chunks ContextAssembler->>HostedLLM: typed context bundle HostedLLM-->>Agent: grounded response

4. Frontier Model Governance as a Template for RAG Pipeline Policy Gates

The security and isolation model AWS uses for safely releasing frontier models — input/output filtering, provenance boundaries, tiered access — maps directly onto what a production RAG pipeline needs to govern retrieved content before it reaches the LLM.

  • Provenance boundaries in model release governance (knowing which data touched which model version) are structurally identical to the retrieval provenance problem in RAG — you need to trace which document chunk produced which claim in the agent's response.
  • Input filtering before the LLM call isn't just a model safety concern — it's a RAG quality gate, since retrieved chunks can contain outdated, contradictory, or policy-violating content that should be intercepted at the pipeline layer, not left to the model to handle.
  • Tiered access controls applied to which retrieved content different agent roles can see mirrors the principle of least privilege — a customer-facing agent should not be able to retrieve internal cost data even if that data is semantically relevant to the query.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]

AWS Bedrock's approach to releasing frontier models with layered security — privacy controls, input/output guardrails, model isolation — treats each model invocation as a governed transaction, not a raw API call. That's the same discipline a production RAG pipeline needs: every retrieval is a governed transaction, and the chunks that flow into context assembly should pass through a policy gate before they touch the LLM. Most teams implement this as an afterthought (a post-retrieval content filter) rather than as a first-class pipeline stage with its own observability and rollback surface. Source: [Safely Releasing Frontier Models to Customers] — Author not specified (https://aws.amazon.com/blogs/machine-learning/safely-releasing-frontier-models-to-customers/)

AIThis governance framing connects to the longitudinal pattern from last week: the industry is shifting from 'build it and add guardrails later' to 'policy gates are structural, not bolted on.' For RAG specifically, that means the retrieval policy — what can be retrieved, by which agent role, from which data source — belongs in the architecture diagram, not in a README note.

Reference Architecture
// TypeScript: Policy-gated retrieval with provenance tagging
// Policy gate runs before VectorDB query, not after — fail fast, not fail quietly

interface AgentContext {
  agentRole: 'customer_facing' | 'internal_ops' | 'admin';
  sessionId: string;
}

const ROLE_ALLOWED_COLLECTIONS: Record<AgentContext['agentRole'], string[]> = {
  customer_facing: ['product_docs', 'public_kb', 'order_history'],
  internal_ops: ['product_docs', 'public_kb', 'order_history', 'internal_runbooks'],
  admin: ['product_docs', 'public_kb', 'order_history', 'internal_runbooks', 'cost_data'],
};

async function policyGatedRetrieval(
  query: string,
  requestedCollections: string[],
  agentCtx: AgentContext,
  vectorClient: VectorClient
): Promise<{ chunks: ProvenanceTaggedChunk[]; blockedCollections: string[] }> {
  const allowed = ROLE_ALLOWED_COLLECTIONS[agentCtx.agentRole];
  const permitted = requestedCollections.filter(c => allowed.includes(c));
  const blocked = requestedCollections.filter(c => !allowed.includes(c));

  if (blocked.length > 0) {
    // Emit governance event — not a throw; agent should proceed with permitted scope
    emit('retrieval_policy_block', { agentRole: agentCtx.agentRole, blocked, sessionId: agentCtx.sessionId });
  }

  const rawChunks = await vectorClient.search(query, { collections: permitted, topK: 10 });

  const chunks: ProvenanceTaggedChunk[] = rawChunks.map(c => ({
    ...c,
    sourceCollection: c.metadata.collection,
    retrievedAtMs: Date.now(),
    sessionId: agentCtx.sessionId,
  }));

  return { chunks, blockedCollections: blocked };
}

function emit(event: string, payload: Record<string, unknown>): void {
  console.log(JSON.stringify({ event, ...payload, ts: new Date().toISOString() }));
}
State Interaction Chart
flowchart TD Q[Agent Query + Role Context] --> RetrievalPolicy{Retrieval Policy Gate} RetrievalPolicy -->|allowed sources for role| VectorSearch[Vector Search] VectorSearch --> ChunkCandidates[Retrieved Chunks] ChunkCandidates --> ContentFilter{Content Filter} ContentFilter -->|pass: fresh, policy-compliant| ProvenanceTag[Tag with Source + Timestamp] ContentFilter -->|fail: stale, restricted, contradictory| Quarantine[Quarantine Log] ProvenanceTag --> ContextAssembler[Context Assembler] Quarantine --> ObsEmitter[Observability Emitter] ContextAssembler --> LLM[LLM Call] LLM --> OutputGuardrail{Output Guardrail} OutputGuardrail -->|clean| AgentResponse[Agent Response] OutputGuardrail -->|violation| HumanReview[Human Review Queue]

5. Agent Tool Architecture and the Retrieval-Action Boundary

The most consequential architectural decision in a customer-facing agentic RAG system isn't the retrieval model — it's where you draw the line between reading (retrieval) and writing (action), because that boundary determines your rollback options and your human-oversight insertion points.

  • Retrieval tools are idempotent and safe to retry; action tools are not — mixing them in the same tool registry without explicit typing means your agent has no structural way to know when it's about to cross from observation into consequence.
  • The perception-action loop in a production agent needs retrieval as a distinct phase, not just another tool call, because you want to be able to inspect, cache, and replay the retrieval step independently of the action step during incident investigation.
  • Human-in-the-loop checkpoints are easiest to implement when the retrieval-action boundary is explicit in the graph — you insert the approval node between the last retrieval node and the first action node, not scattered across tool call handlers.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]

Huyen Chip's framing of agents as systems combining perception, memory, action, and planning is useful here not as a definition but as a design scaffold: it suggests that retrieval belongs in the perception and memory phases, while tool execution belongs in the action phase — and those phases should be structurally distinct in your graph or pipeline, not just semantically different. When retrieval and action are both 'just tool calls,' you lose the ability to pause the agent at the transition point, which is precisely where human oversight is most valuable in high-stakes customer interactions. Source: [Agents] — Huyenchip (https://huyenchip.com//2025/01/07/agents.html)

AIThis connects to the governance maturation pattern from last week: the move toward structured evidence emission and governed async processing implies that each phase boundary (retrieval → synthesis → action) needs its own audit record. When retrieval and action are interleaved in a flat tool list, you can't easily reconstruct the agent's reasoning path during a post-incident review — which is increasingly a compliance requirement, not just a debugging nicety.

Reference Architecture
// TypeScript: Explicit retrieval-action boundary in a LangGraph-style node graph
// Retrieval nodes are typed as ReadOnlyNode — action nodes require explicit promotion

type NodeType = 'retrieval' | 'synthesis' | 'action';

interface AgentNode {
  id: string;
  type: NodeType;
  execute: (state: AgentState) => Promise<Partial<AgentState>>;
}

const retrieveCustomerHistory: AgentNode = {
  id: 'retrieve_customer_history',
  type: 'retrieval',  // safe to retry, safe to cache, safe to replay
  execute: async (state) => {
    const history = await policyGatedRetrieval(
      state.query,
      ['order_history', 'customer_kb'],
      state.agentContext,
      vectorClient
    );
    return { retrievedChunks: history.chunks, retrievalProvenance: history };
  },
};

const issueRefund: AgentNode = {
  id: 'issue_refund',
  type: 'action',  // irreversible — requires human checkpoint before execution
  execute: async (state) => {
    if (!state.humanApproval?.approved) {
      throw new Error('Action node requires explicit human approval in state');
    }
    const result = await refundService.process(state.orderId, state.refundAmount);
    return { actionResult: result, actionCompletedAt: Date.now() };
  },
};

function buildGraph(nodes: AgentNode[]): CompiledGraph {
  const actionNodes = nodes.filter(n => n.type === 'action');
  // Automatically inject human-in-the-loop checkpoint before first action node
  return graph.addCheckpointBefore(actionNodes[0].id, humanApprovalNode);
}
State Interaction Chart
flowchart TD Input[User Request] --> PerceptionPhase[Perception Phase\nparse intent, extract entities] PerceptionPhase --> MemoryPhase[Memory Phase\nretrieve context, history, docs] MemoryPhase --> PlanningPhase[Planning Phase\nselect action sequence] PlanningPhase --> CheckPoint{Action Boundary\nHuman Review if high-stakes} CheckPoint -->|approved or auto-approved| ActionPhase[Action Phase\nexecute tool calls] CheckPoint -->|escalated| HumanQueue[Human Review Queue] ActionPhase --> AuditLog[Audit Log\nper-phase provenance] AuditLog --> Response[Agent Response] style MemoryPhase fill:#1a3a52,stroke:#51cf66 style ActionPhase fill:#3a1a1a,stroke:#ff6b6b style CheckPoint fill:#2d2d0a,stroke:#ffd43b

6. Scaling Laws as a Calibration Tool for RAG Model Selection

Scaling law curves tell you the point of diminishing returns for model size at a given context length — which is directly useful for deciding whether your RAG pipeline needs a 70B model or whether a well-retrieved 7B response is already at the compute-optimal frontier.

  • Compute-optimal allocation in scaling law research shows that model size and data/context volume follow predictable power-law tradeoffs — for RAG, this means you can model the quality curve for 'larger model, less retrieved context' versus 'smaller model, richer retrieved context' before running expensive benchmarks.
  • Retrieval quality multiplies a model's effective capability in a way that isn't captured by scaling laws alone — a 7B model with precise, well-ranked retrieval routinely outperforms a 70B model with noisy retrieval on grounded factual tasks, which is a practical calibration point for infrastructure cost decisions.
  • Token budget and model size are not independent variables in a RAG system — as context length grows, smaller models degrade faster on retrieval-dependent tasks, which means your context budget governance (from block_1) and your model selection are coupled decisions that should be made together.
TECHNICAL DEEP DIVE & CODE ARTIFACTS Expand [+]

Lilian Weng's deep treatment of scaling laws frames them as a framework for allocating compute optimally between model size N, dataset size D, and compute C — the insight for RAG practitioners is that retrieved context at inference time acts as a form of injected 'dataset' for the specific task, temporarily increasing effective D without changing N. This means the Chinchilla-style question for RAG isn't just 'how big should my model be?' but 'at what point does improving retrieval quality yield more quality per dollar than upgrading the model?' Source: [Scaling Laws, Carefully] — Lilian Weng (https://lilianweng.github.io/posts/2026-06-24-scaling-laws/)

AIFor a customer-facing agentic system running thousands of RAG queries per hour, this is a real infrastructure cost lever. A retrieval pipeline that consistently returns high-precision, low-noise chunks to a 13B model may outperform a noisy retrieval pipeline feeding a 70B model — and cost a third as much per query. The governance angle: model selection and retrieval quality are both auditable decisions that should appear in your system's architecture records, not just in someone's notebook experiment.

Reference Architecture
// TypeScript: Retrieval quality vs model size cost estimator
// Helps calibrate infrastructure decisions before running full benchmarks

interface ModelConfig {
  name: string;
  paramsBillions: number;
  costPerMillionTokens: number; // USD
  avgContextDegradationFactor: number; // 1.0 = no degradation, <1.0 = degrades with noisy context
}

interface RetrievalConfig {
  precisionScore: number; // 0-1, measured from your eval set
  avgChunksReturned: number;
  avgTokensPerChunk: number;
}

function estimateQualityPerDollar(
  model: ModelConfig,
  retrieval: RetrievalConfig,
  queriesPerHour: number
): { qualityScore: number; costPerHour: number; qualityPerDollar: number } {
  const tokensPerQuery = retrieval.avgChunksReturned * retrieval.avgTokensPerChunk;
  const effectiveQuality = retrieval.precisionScore * model.avgContextDegradationFactor;
  const costPerQuery = (tokensPerQuery / 1_000_000) * model.costPerMillionTokens;
  const costPerHour = costPerQuery * queriesPerHour;

  return {
    qualityScore: effectiveQuality,
    costPerHour,
    qualityPerDollar: effectiveQuality / costPerHour,
  };
}

// Usage: compare models to find compute-optimal frontier for your retrieval config
const configs: ModelConfig[] = [
  { name: 'llama-70b-hosted', paramsBillions: 70, costPerMillionTokens: 0.90, avgContextDegradationFactor: 0.95 },
  { name: 'qwen-13b-hosted',  paramsBillions: 13, costPerMillionTokens: 0.20, avgContextDegradationFactor: 0.88 },
  { name: 'gemma-7b-local',   paramsBillions: 7,  costPerMillionTokens: 0.02, avgContextDegradationFactor: 0.80 },
];

const retrieval: RetrievalConfig = { precisionScore: 0.87, avgChunksReturned: 6, avgTokensPerChunk: 320 };

configs
  .map(m => ({ model: m.name, ...estimateQualityPerDollar(m, retrieval, 5000) }))
  .sort((a, b) => b.qualityPerDollar - a.qualityPerDollar)
  .forEach(r => console.log(r));
State Interaction Chart
flowchart LR subgraph Quality_vs_Cost A1[Large Model\n70B params\nnoisy retrieval] -->|high cost\nlow precision| Q1[Quality: 0.72] A2[Small Model\n13B params\nhigh-precision retrieval] -->|low cost\nhigh precision| Q2[Quality: 0.78] A3[Small Model\n7B params\nlocal inference\n+ reranking] -->|lowest cost| Q3[Quality: 0.71] end Q2 -->|optimal operating point| Decision[Select 13B + invest\nin retrieval quality] style A2 fill:#1a3a1a,stroke:#51cf66 style Q2 fill:#1a3a1a,stroke:#51cf66