Production RAG Stack Development Through 37 Iterations for Financial Services

jonfernandes · Finance · year n/a · source Agentic: no · promotional bias: low · source-backed · extracted with prompt 0.5.6 (schema v2)

Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.

Problem

Domain: Production RAG stack for financial-services and on-premise knowledge-base question answering

The presenter needed to move from naive RAG that returned irrelevant answers to a production-ready, privacy-conscious stack that financial institutions could run on-premise while preserving retrieval quality, observability, and evaluation.

Constraints:

Architecture

The final stack uses LlamaIndex orchestration, open embedding models such as BGE for production, Qdrant for vector storage, GPT-4 or API services for prototyping, open Llama/Qwen models served through Ollama or HuggingFace TGI for production, cross-encoder reranking, Arize Phoenix tracing, RAGAS evaluation, and Docker Compose to deploy ingestion, vector database, frontend, model serving, tracing, and evaluation containers. The design separates prototype convenience from production privacy and deployment needs.

Diagram confidence: reconstructed (reconstructed from prose).

flowchart LR
  data-ingestion-service["Data ingestion service"] --> embedding-model["Embedding model"]
  data-ingestion-service["Data ingestion service"] --> qdrant-vector-database["Qdrant vector database"]
  embedding-model["Embedding model"] --> qdrant-vector-database["Qdrant vector database"]
  embedding-model["Embedding model"] --> rag-orchestrator["RAG orchestrator"]
  qdrant-vector-database["Qdrant vector database"] --> rag-orchestrator["RAG orchestrator"]
  qdrant-vector-database["Qdrant vector database"] --> reranker["Reranker"]
  reranker["Reranker"] --> rag-orchestrator["RAG orchestrator"]
  reranker["Reranker"] --> llm-serving["LLM serving"]
  rag-orchestrator["RAG orchestrator"] --> llm-serving["LLM serving"]
  llm-serving["LLM serving"] --> frontend-application["Frontend application"]
  llm-serving["LLM serving"] --> arize-phoenix-tracing["Arize Phoenix tracing"]
  arize-phoenix-tracing["Arize Phoenix tracing"] --> ragas-evaluation["RAGAS evaluation"]
  ragas-evaluation["RAGAS evaluation"] --> rag-orchestrator["RAG orchestrator"]
  docker-compose-runtime["Docker Compose runtime"] --> data-ingestion-service["Data ingestion service"]
  docker-compose-runtime["Docker Compose runtime"] --> qdrant-vector-database["Qdrant vector database"]
  docker-compose-runtime["Docker Compose runtime"] --> llm-serving["LLM serving"]
Drag to pan · scroll or use +/− to zoom · reconstructed from the source's described component topology

Components

Component Role Interacts with
Data ingestion service Loads knowledge-base documents into the RAG pipeline. Embedding model, Qdrant vector database
Embedding model Creates document and query embeddings, using closed APIs for prototyping and open/self-hosted models in production. Data ingestion service, Qdrant vector database, RAG orchestrator
Qdrant vector database Stores vectors and returns top-k candidate documents for a query. Data ingestion service, Embedding model, RAG orchestrator, Reranker
RAG orchestrator LlamaIndex-based orchestration for retrieval, reranking, context assembly, and answer generation. Embedding model, Qdrant vector database, Reranker, LLM serving
Reranker Cross-encoder stage that re-scores retrieved query-document pairs for semantic relevance. Qdrant vector database, RAG orchestrator, LLM serving
LLM serving Serves GPT models for prototyping or open Llama/Qwen models through Ollama or TGI in production. RAG orchestrator, Frontend application, Arize Phoenix tracing
Frontend application User-facing question-answering interface. LLM serving
Arize Phoenix tracing Observes latency and pipeline behavior across embedding, retrieval, reranking, and generation. LLM serving, RAGAS evaluation
RAGAS evaluation Automated evaluation layer for systematic quality checks across many questions. Arize Phoenix tracing, RAG orchestrator
Docker Compose runtime Runs ingestion, Qdrant, frontend, model serving, tracing, and evaluation as separate deployable containers. Data ingestion service, Qdrant vector database, Frontend application, LLM serving, Arize Phoenix tracing, RAGAS evaluation

Data flow

Documents enter an ingestion service and are embedded by a production-suitable open embedding model. Qdrant stores the vectors and retrieves top candidates for a query. A reranker, prototyped with Cohere and moved toward Nvidia for production, scores the top retrieved documents and selects the most relevant context. The LLM then generates a deterministic answer with temperature 0, Phoenix traces latency and pipeline behavior, and RAGAS evaluates quality across many queries.

3 supporting quotes
“The presenter describes a Docker Compose-based architecture where different components (ingestion, vector database, frontend, model serving, tracing, evaluation) run as separate containers.”summary, stated
“the top 5 results from the vector database are retrieved, then reranked to select the top 2 most relevant documents before passing to the LLM.”summary, stated
“Monitoring enables visibility into latency across different components (embedding, retrieval, reranking, generation) and helps identify bottlenecks or issues in production systems.”summary, stated

Why this architecture

The stack reflects a repeated lesson from 37 failed attempts: production RAG needs a full pipeline, not just embeddings plus a prompt. Financial-services constraints push production toward open/self-hosted models and Dockerized deployment, while retrieval accuracy requires reranking and monitoring. Separating containers lets teams deploy on-premise or cloud while scaling/updating components independently.

3 supporting quotes
“This case study is framed as lessons learned from 37 failed implementations, making it a practical guide rather than a theoretical overview.”summary, stated
“This transition from closed to open models is driven by the need for on-premise deployment in financial services contexts, where sending data to external APIs may not be acceptable.”summary, stated
“This microservices approach allows for independent scaling and updating of components, which is a best practice for production ML systems.”summary, stated

Models used: OpenAI text-embedding-ada-002 · OpenAI text-embedding-3-large · BGE-small · Nvidia embedding models · GPT-3.5 Turbo · GPT-4 · Llama 3.2 · Qwen 3 4B · Cohere reranker · Nvidia reranker

Scale

Alternatives considered (roads not taken)

Naive RAG with embedding, vector retrieval, and generation only

Rejected because: It produced irrelevant answers and failed to select the best contextual evidence.

“The naive approach—embedding the query, retrieving similar documents from the vector database, and generating a response—produces unsatisfactory results.”summary, stated

API-based closed models for production in financial services

Rejected because: On-premise and data-privacy requirements can make external APIs unacceptable.

“open models for production. The specific open models mentioned include those from Nvidia and BAI (BAAI), with the BGE-small model from BAI demonstrated”summary, stated

Cross-encoders for initial retrieval across the whole corpus

Rejected because: They are more accurate but do not scale because every query-document pair must be processed together.

“While more accurate for semantic comparison, this approach doesn't scale well because it requires processing each query-document pair together.”summary, stated

Single-query manual assessment

Rejected because: One test query cannot establish whether the RAG system maintains quality as data and queries vary.

“The presenter notes that a single test query is insufficient for assessing RAG quality—a systematic evaluation across many queries is necessary.”summary, stated

Patterns

rag-retrieval (retrieval)

The stack uses LlamaIndex plus Qdrant to retrieve candidate context before generation.

“For orchestration, the presenter uses LlamaIndex for both prototyping and production. LangGraph is mentioned as an alternative for prototyping.”summary, stated
“Qdrant is the chosen vector database, praised for its excellent scalability characteristics. The presenter notes it works well from "a couple of documents to hundreds of thousands of documents." This scalability is important for production systems that need to handle growing knowledge bases.”summary, stated

retrieval-ranking (retrieval)

A cross-encoder reranking stage improves the top-k retrieved documents before context is sent to the LLM.

“Therefore, cross-encoders are positioned as a post-retrieval reranking step, applied only to the top-k documents already retrieved by the bi-encoder.”summary, stated

model-routing (architecture)

The stack deliberately uses closed/API models for prototyping and open/self-hosted models for production privacy and deployment needs.

“The LLM strategy mirrors the embedding approach: closed models (OpenAI GPT-3.5 Turbo, GPT-4) for prototyping, open models for production.”summary, stated

observability-tracing (operations)

Phoenix tracing is included as a production container to show latency and behavior across embedding, retrieval, reranking, and generation.

“Monitoring enables visibility into latency across different components (embedding, retrieval, reranking, generation) and helps identify bottlenecks or issues in production systems.”summary, stated

eval-driven-development (quality)

The stack uses RAGAS and multi-query evaluation because learning from failed iterations requires systematic quality measurement.

“RAGAS works with LLMs to automate evaluation, making it more scalable than manual assessment. This is a critical LLMOps consideration, as continuous evaluation is needed to ensure system quality doesn't degrade as knowledge bases grow or change.”summary, stated

Tradeoffs

Prototype with closed APIs, deploy with open models

Chose API models for fast experimentation and self-hosted open models for production over Using one model-serving approach across all environments.

Rationale: API models accelerate iteration, while open models satisfy privacy and deployment control requirements.

“there's a clear distinction between prototyping and production tooling—what's convenient for experimentation (API-based closed models) often isn't appropriate for production”summary, stated

Use bi-encoders before cross-encoders

Chose Fast bi-encoder retrieval followed by accurate cross-encoder reranking over Cross-encoding every document pair upfront.

Rationale: This keeps broad retrieval scalable while applying expensive semantic scoring only to a small candidate set.

“Bi-encoders use separate encoder models for the query and document, producing embeddings that can be compared using cosine similarity. This approach is fast and scalable”summary, stated
“Cross-encoders, in contrast, take both the query and document together through a single BERT model with a classifier head”summary, stated

Containerize the RAG stack

Chose Docker Compose with separate services over A single monolithic runtime.

Rationale: Containers make the same system deployable on-premise or in cloud environments and allow components to be scaled or updated independently.

“For production deployment, Docker is the technology of choice. This decision is driven by the flexibility it provides—the same containerized solution can run on-premise”summary, stated

Gotchas (what bit them)

Naive vector similarity can retrieve the wrong practical answer.

Root cause: Bi-encoder retrieval is fast but does not jointly score the query and document for final relevance.

Resolution: Use a cross-encoder reranker on top-k retrieved candidates before generation.

“This reranking step is shown to significantly improve response quality.”summary, stated

Prototype choices may violate production privacy constraints.

Root cause: API-based closed embeddings and LLMs are convenient, but financial institutions may require on-premise processing.

Resolution: Transition production to open/self-hosted embeddings, LLMs, rerankers, and Dockerized deployment.

“This is a critical consideration for LLMOps—the trade-off between the convenience of API-based models and the control and privacy of self-hosted alternatives.”summary, stated

RAG quality cannot be diagnosed without component-level traces.

Root cause: Latency or answer errors can originate in embedding, retrieval, reranking, or generation.

Resolution: Run Phoenix or similar tracing alongside the RAG stack.

“The importance of monitoring and tracing RAG solutions is emphasized, particularly for troubleshooting and understanding where time is spent in the pipeline.”summary, stated

One successful demo query does not prove production readiness.

Root cause: Knowledge bases grow and query distributions change, so quality can degrade without continuous evaluation.

Resolution: Use RAGAS or similar evaluation across many queries.

“This is a critical LLMOps consideration, as continuous evaluation is needed to ensure system quality doesn't degrade as knowledge bases grow or change.”summary, stated

Evaluation

The presenter recommends systematic RAG evaluation with RAGAS across many queries, plus Phoenix traces to locate latency and quality problems by pipeline component.

Metrics: RAGAS evaluation · many-query test coverage · latency by component · embedding latency · retrieval latency · reranking latency · generation latency · top-k retrieval quality

“For evaluation, the RAGAS framework is recommended. The presenter notes that a single test query is insufficient for assessing RAG quality—a systematic evaluation across many queries is necessary.”summary, stated
“a single test query is insufficient for assessing RAG quality—a systematic evaluation across many queries is necessary.”summary, stated

Outcomes

Key scale anchors include 37 failed iterations, Qdrant's range from a couple of documents to hundreds of thousands, top-5 to top-2 reranking, and multi-container Docker Compose deployment.

3 supporting quotes
“The case study demonstrates the evolution from a naive RAG implementation to a more sophisticated one. The naive approach—embedding the query, retrieving similar documents from the vector database, and generating a response—produces unsatisfactory results.”summary, stated
“the answer to "Where can I get help in London?" evolves from irrelevant responses about wheelchair-accessible taxis to accurate information about assistance at London St. Pancras International.”summary, stated
“having a well-designed stack with proper evaluation makes learning from those failures systematic rather than chaotic.”summary, stated