“Should we use RAG or fine-tune the model?” is one of the most-asked questions in enterprise AI, and it is asked wrong. They are not two solutions to one problem — they are solutions to two different problems that happen to look similar from a distance.
This article gives you the decision framework, then the .NET specifics of each path.
The one-sentence rule
RAG changes what the model knows. Fine-tuning changes how the model behaves.
Everything else follows from that. So the first question is not “which technique?” — it is “what is my model actually getting wrong?”
Diagnose the failure first
Take ten real failures from your system and sort them into these buckets. The distribution tells you what to build.
Bucket 1 — it does not know something. “What is our refund policy for enterprise customers?” and the model invents one, or says it does not know. The information exists in your documents; the model has never seen them. → RAG.
Bucket 2 — it knows, but says it wrong. Correct answer, wrong voice: too chatty for a support widget, too casual for a regulated disclosure, uses “customer” where your domain says “policyholder”. → Fine-tuning (or a much better system prompt — try that first).
Bucket 3 — the output shape is unreliable. You need JSON matching a schema and it is right 92% of the time. → Neither. This is structured output, a constrained-decoding feature you turn on. Fine-tuning to fix a format problem is spending a week to solve a config problem.
Bucket 4 — it needs to do something, not say something. Look up an order, hit an API, run a query. → Neither. This is function calling / tools.
Bucket 5 — it does not understand your domain’s language at all. Not “does not know a fact” but genuinely mis-parses your jargon — dense medical coding, a trading desk’s shorthand, a legacy internal taxonomy. Retrieval does not help because the model cannot even read the retrieved passages correctly. → Fine-tuning, legitimately.
In most enterprise systems I have seen, buckets 1, 3 and 4 account for the overwhelming majority. That is why the default answer is RAG — not because RAG is magic, but because “does not know our data” is the most common failure.
What each one actually costs you
Cost is where the decision usually gets made in practice, and the sticker prices mislead.
RAG
| Up-front work | Ingest pipeline, chunking, embeddings, a vector store |
| Ongoing cost | Vector store hosting + embedding calls on new content + extra input tokens per request |
| Time to first result | Days |
| Updating knowledge | Re-index the changed document. Minutes. |
| Attribution | Free — you know which chunks were retrieved, so you can cite sources |
| Main failure mode | Retrieval returns the wrong passages, and the model confidently answers from them |
Fine-tuning
| Up-front work | A labelled dataset — realistically hundreds to thousands of high-quality examples |
| Ongoing cost | Re-training on drift + often a higher per-token rate and sometimes idle hosting charges for the deployment |
| Time to first result | Weeks, dominated by dataset creation |
| Updating knowledge | Rebuild the dataset and retrain. Days. |
| Attribution | None — the knowledge is baked into weights, so the model cannot cite where it came from |
| Main failure mode | The dataset encodes a bias or an error, and the model reproduces it everywhere |
The asymmetry that matters: RAG’s knowledge update is a re-index; fine-tuning’s is a retraining cycle. If your domain content changes weekly, that difference compounds forever. If it is genuinely static — a fixed regulatory framework, a stable product taxonomy — it matters much less.
The cost line people miss is dataset maintenance. The training run is cheap. Producing a few thousand examples that are actually correct, actually representative, and actually consistent with each other is the expensive part, and it is a recurring cost, not a one-off.
The token-cost objection, examined
The commonest argument for fine-tuning is: “RAG stuffs thousands of tokens into every request; fine-tuning bakes it in and the prompts stay short.”
It is true, and it is usually smaller than it sounds. Work it out for your own numbers before treating it as decisive:
- RAG adds roughly 1,000–3,000 input tokens per request. Input tokens are the cheap ones, and prompt caching on a stable system prompt cuts the repeated part further.
- Fine-tuned deployments frequently carry a higher per-token rate than the equivalent base model, and some providers bill for the hosted deployment whether or not it serves traffic.
So the “savings” often shrink to nothing or invert once the fine-tuned model’s own premium is counted. Run the arithmetic at your real request volume — the answer differs a lot between 10,000 requests a month and 10 million. Our guide on controlling AI agent costs in production covers measuring this properly, and caching LLM responses usually beats both on the requests that repeat.
The .NET path for each
RAG on .NET
The stack is settled and every piece has a first-party or well-maintained option:
// One IChatClient abstraction over any provider; swap by configuration.
IChatClient chat = new AzureOpenAIClient(endpoint, credential)
.GetChatClient(deploymentName)
.AsIChatClient();
IEmbeddingGenerator<string, Embedding<float>> embeddings =
new AzureOpenAIClient(endpoint, credential)
.GetEmbeddingClient(embeddingDeployment)
.AsIEmbeddingGenerator();
From there: chunk your documents, embed the chunks, store them with their vectors, and at query time embed the question, fetch the nearest chunks, and pass them as context. The full build is in building a RAG knowledge base for a .NET agent; the storage decision is in our vector database comparison for .NET.
The single highest-leverage thing you can do here is not picking a fancier vector store. It is chunking well and then measuring retrieval quality separately from answer quality. When a RAG system gives a bad answer, retrieval is at fault far more often than generation, and you cannot tell which unless you measure them apart.
Fine-tuning on .NET
There is a real gap worth knowing before you commit: the fine-tuning ecosystem is Python-first. Dataset preparation, training loops, evaluation harnesses, PEFT/LoRA tooling — the mature libraries are Python. This does not block you, but it changes the shape of the project.
The practical arrangement for a .NET shop:
- Prepare the dataset wherever your data lives — that part is ordinary .NET data work, and doing it in C# against your existing repositories is usually easier than exporting to Python.
- Run the training job through a managed service (Azure AI Foundry, OpenAI’s fine-tuning API) rather than standing up a training stack. You submit a JSONL file and get back a deployment.
- Consume the result through the same
IChatClient. This is the payoff ofMicrosoft.Extensions.AI: a fine-tuned deployment is just a different model id. Your application code does not change at all.
That last point is worth planning around. If you build on IChatClient from day one, “try a fine-tuned model” becomes a configuration experiment you can A/B against the base model, rather than a rewrite you have to justify in advance.
Do both — but in this order
The mature end state is frequently both: a fine-tuned model that nails your house style and output shape, fed current facts by retrieval. But the order is not arbitrary.
Build the RAG half first. It is cheaper, it ships in days rather than weeks, and — this is the part teams underestimate — it often removes the need for the second half. A surprising number of “the model does not sound like us” complaints turn out to be “the model is guessing because it has no grounding”, and they disappear once retrieval is working.
Only reach for fine-tuning once you have a grounded system, an eval set, and a residual behavioural gap you can describe precisely. If you cannot write down what fine-tuning is supposed to fix in one sentence, you are not ready to spend three weeks on a dataset.
Takeaway
Ask what your model is getting wrong before asking which technique to use. Missing knowledge is a RAG problem; wrong behaviour is a fine-tuning problem; wrong format and wrong actions are neither — those are structured output and tool calling, and they are far cheaper to fix. RAG updates in minutes and can cite its sources; fine-tuning updates in days and cannot. Start with RAG, measure retrieval quality separately from answer quality, and add fine-tuning only when you can name the specific behavioural gap it is meant to close.
Have a correction or a topic you want covered? Email mani.bc72@gmail.com.