I'm always excited to take on new projects and collaborate with innovative minds.

Social Links

Chat With Your CSV Files: Building a Local RAG App with Blazor and Ollama

A step-by-step look at BlazorCsvRagChat — a complete, dependency-free RAG pipeline that turns CSV files into a chat interface using .NET 8 and Ollama, entirely on your own machine.

Every dataset often starts the same way: as a spreadsheet someone exported, saved as a CSV, and asked you to analyze. Your typical options are writing custom SQL queries or building pivot tables. Alternatively, you could query your spreadsheet in plain English and receive direct answers.

That is the exact premise of BlazorCsvRagChat, a lightweight Retrieval-Augmented Generation (RAG) application built with Blazor (.NET 8) and Ollama. You upload a CSV file, the application embeds its contents, and you can query the data using natural language.

The solution requires no SQL, no specialized search syntax, and runs entirely locally on your machine with zero API keys or external cloud dependencies.

Why Local RAG Matters

Many "chat with your data" demonstrations function by sending the entire CSV file to a hosted LLM API inside the prompt window. That approach presents three main challenges:

  1. Privacy: Data leaves your local environment.

  2. Cost: Token costs scale with dataset size on every query.

  3. Scale: Large CSVs quickly exceed context window limits.

RAG resolves these issues by decoupling data ingestion from query generation:

┌─────────────────────────────────────────────────────────────┐
│                    1. Parse & Chunk Data                    │
│   (Split CSV into small, header-aware text fragments)       │
└──────────────────────────────┬──────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────┐
│                   2. Generate Embeddings                    │
│   (Convert chunks into dense vectors via Ollama)            │
└──────────────────────────────┬──────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────┐
│                  3. Query & Vector Search                   │
│   (Retrieve top-K most relevant chunks using Cosine Similarity)│
└──────────────────────────────┬──────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────┐
│                    4. Grounded LLM Response                 │
│   (Pass context + user prompt to local LLM for completion)  │
└─────────────────────────────────────────────────────────────┘

The model only receives the specific data slices relevant to the active question, keeping prompt sizes small, deterministic, and fully local.

Pipeline Architecture

The end-to-end flow follows a standard RAG pipeline optimized for local execution without external vector databases:

  1. CSV Ingestion: Parse raw rows into structured text chunks (~500–700 characters each) while prepending header context.

  2. Local Embedding: Send each chunk to Ollama’s /api/embeddings endpoint using the mxbai-embed-large model.

  3. In-Memory/On-Disk Storage: Store vectors in a simple JSON file on disk.

  4. Similarity Retrieval: Embed incoming user questions and compute cosine similarity against stored vectors to extract the top 4 most relevant chunks.

  5. Grounded Completion: Inject retrieved chunks into the llama3.1:8b-instruct prompt with strict instructions to answer exclusively using the provided context.

Implementation Details

1. Header-Aware Chunking

Raw CSV rows like 47, 3.2, 88.1 lack semantic context when evaluated independently by an LLM. To resolve this, the ingestion engine builds header-aware chunks: each text block contains group rows prefixed with the original column header line. This ensures the model always understands field mappings.

2. Semantic Search via Local Embeddings

By embedding chunks with mxbai-embed-large, queries map semantically rather than relying on exact keyword matching. A query like "Who earns the highest salary?" correctly matches chunks containing compensation data, even if the exact words differ.

3. File-Based Vector Storage

Instead of introducing external database dependencies, the vector index uses a lightweight List<Chunk> serialized directly to App_Data/index.json. Cosine similarity is computed directly via standard C# loops. For typical operational spreadsheets (a few thousand rows), performance is virtually instantaneous and eliminates external infrastructure setup.

4. Zero-Hallucination System Prompting

The system prompt enforces strict grounding:

"Answer strictly from the provided CSV context. If the answer is not in the context, state that you do not know."

Combined with a low generation temperature (0.1), the LLM focuses entirely on factual extraction from your data.

Tech Stack

LayerComponent
FrameworkBlazor Server (Interactive Server Components), .NET 8
Embeddings ModelOllama + mxbai-embed-large
LLM InferenceOllama + llama3.1:8b-instruct
Vector StorageOn-disk JSON file ( index.json )
DependenciesNone (Fully offline, zero cloud APIs)

The UI is managed within Home.razor, supported by four core services:

  • EmbeddingClient: Communicates with Ollama's embeddings endpoint.

  • LlmClient: Manages chat completion generation.

  • VectorIndex: Handles vector storage and similarity scoring.

  • KnowledgeService: Orchestrates parsing, indexing, and context retrieval.

Technical Trade-offs

  • Basic Delimiter Parsing: The default string-splitting parser assumes clean CSV structures. Files containing quoted strings with internal commas or newlines may require robust parsing libraries like CsvHelper.

  • Sequential Embedding Ingestion: Generating embeddings row-by-row via HTTP calls creates initial ingestion overhead during large uploads. Querying remains fast as it only embeds the active user question.

  • Linear Vector Search: Linear cosine scans scale well for standard spreadsheet sizes, though ultra-large datasets would eventually require dedicated vector indexing engines (e.g., Qdrant or Milvus).

4 min read
Aug 16, 2025
By Dheer Gupta
Share

Leave a comment

Your email address will not be published. Required fields are marked *