Prompting vs. RAG vs. Fine-tuning
The three levers for changing model behavior, what each is good and bad at, and how to choose without guessing.
When an AI feature is wrong, you have three main levers: change the prompt, retrieve better context, or change the model with fine-tuning. Senior AI engineering is knowing which lever moves the failure you actually have.
The common mistake is treating fine-tuning as a memory upgrade. It is usually the wrong tool for fresh facts, private documents, or anything users expect to cite. Fine-tuning changes behavior; RAG supplies knowledge at request time.
The mental model
Think of an LLM product as a function with three inputs you control:
def ai_feature(user_request, *, prompt_template, retrieved_context, model):
messages = prompt_template.render(
request=user_request,
context=retrieved_context,
)
return model.generate(messages)
- Prompting changes the instructions and examples sent on this call.
- RAG changes the facts available on this call.
- Fine-tuning changes the model weights used for every call.
Those levers operate at different timescales. A prompt can ship in minutes. A RAG index can update as documents change. A fine-tuned model needs data curation, training, evaluation, deployment, and rollback.
Prompting: the first lever
Prompting is the cheapest and fastest way to change behavior. Use it when the model already has the capability and you need to specify the task boundary: format, tone, refusal policy, examples, or routing criteria.
Good prompting fixes:
- inconsistent output shape,
- missing constraints,
- ambiguous task definitions,
- weak examples for edge cases,
- accidental instruction/data mixing.
Prompting does not reliably teach new private facts, and it gets expensive if you paste a large manual into every request.
RAG: the knowledge lever
RAG retrieves facts at query time and places them in the context window. Use it when the answer depends on knowledge outside the base model: private docs, customer data, recent policies, product catalogs, or source code.
RAG is strongest when you need:
- freshness without retraining,
- citations and auditability,
- access control per user,
- a clear path to update or delete knowledge.
Its maintenance cost moves into chunking, indexing, retrieval evaluation, and prompt assembly. If retrieval misses, generation quality collapses.
Fine-tuning: the behavior lever
Fine-tuning trains the model on examples so the weights shift toward a desired behavior. Use it when prompting examples are too many, too expensive, or too weak to reliably teach the behavior.
Good fine-tuning targets:
- a narrow output style or schema,
- domain-specific classification,
- tool-call argument patterns,
- repeated transformations with many labeled examples,
- reducing prompt length by baking examples into the model.
High-level PEFT methods like LoRA add small trainable adapter matrices instead of updating every model weight. That makes training cheaper and easier to swap, but it does not remove the need for clean data and evals.
Cost, latency, and maintenance
The levers have different operational shapes:
- Prompting is low setup cost but can increase per-request tokens and become fragile as rules accumulate.
- RAG adds retrieval latency and index maintenance, but keeps facts fresh and auditable.
- Fine-tuning adds data, training, deployment, and rollback work, but can reduce prompt size and improve consistency at inference time.
A mature product often uses all three: prompt for the task, RAG for facts, and a fine-tuned or smaller model for a repeated narrow behavior.
| Prompting | RAG | Fine-tuning | |
|---|---|---|---|
| Changes | Instructions | Available facts | Model behavior |
| Time to ship | Minutes | Days | Weeks |
| Fresh / private facts | Weak | Strong | Weak |
| Citations & ACLs | No | Yes | No |
| Per-request cost | Grows with rules | + retrieval | Smaller prompt |
| Consistency of style | Medium | Medium | High |
| Maintenance | Low | Index + eval | Train + rollback |
Pattern recognition
Variations
Worked problems
Pick the lever for three scenarios
For each scenario, choose prompting, RAG, fine-tuning, or a combination: (1) a bot must answer questions from a changing HR handbook with citations, (2) a model must return a strict normalized JSON record from messy vendor emails, and (3) a writing assistant should sound more like the company’s brand voice.
Approach. Ask what needs to change: instructions, facts, or behavior. Then check whether citations, freshness, and training data exist.
Show solution
def choose_lever(scenario: str) -> str:
if scenario == "changing_hr_handbook":
return "RAG + prompt"
if scenario == "vendor_email_json":
return "prompt + schema validation; fine-tune if evals stay weak"
if scenario == "brand_voice":
return "prompt with examples first; fine-tune if high volume and examples exist"
raise ValueError(scenario)HR needs fresh, citeable knowledge, so use RAG. Vendor email extraction is a structured behavior; start with a strong prompt and validation, then fine-tune if you have labeled examples and persistent failures. Brand voice can start as prompting; fine-tuning becomes attractive when the style is high-volume and hard to capture in a small prompt.
Complexity. O(1). The value is the diagnostic frame, not the code.
Avoid the fine-tuning trap
A PM asks to fine-tune a model on 20,000 product docs so it can answer customer questions. Docs change weekly, answers must cite sources, and enterprise users can only see their own contracts. Push back with a better design.
Approach. Identify requirements fine-tuning cannot satisfy cleanly: freshness, citations, deletion, and per-user authorization. Then propose the minimal system that satisfies them.
Show solution
A better design is RAG:
- Chunk product docs and contracts.
- Store embeddings with metadata: tenant, document ID, version, ACL, and URL.
- At query time, retrieve only chunks the user can access.
- Prompt the model to answer only from retrieved context.
- Return citations to chunk IDs or document URLs.
- Re-index changed documents when they update.
Fine-tuning could later improve answer style or tool-call consistency, but it should not be the knowledge store.
def should_finetune_for_knowledge(fresh: bool, needs_citations: bool, acl: bool) -> bool:
return not (fresh or needs_citations or acl)Complexity. The decision is O(1); the system cost moves into indexing and retrieval evaluation. That is the right cost because the requirements are runtime knowledge requirements.
When fine-tuning earns its keep
A support team has 50,000 labeled transcripts showing how expert agents convert free-form customer complaints into one of 120 internal issue codes. A prompt with 20 examples works but costs too much and still confuses rare codes. Should you fine-tune?
Approach. Fine-tuning is plausible when you have many labeled examples, a stable label space, repeated traffic, and a measurable offline eval.
Show solution
Yes, run a fine-tuning experiment, but keep the current prompt as the baseline. Split the labeled transcripts into train, validation, and held-out test sets. Measure macro-F1 so rare issue codes matter, not just overall accuracy. Compare:
- current prompted model,
- smaller prompted model,
- fine-tuned candidate,
- fine-tuned candidate with a short instruction prompt.
def macro_f1_by_label(confusions):
scores = []
for label, counts in confusions.items():
tp = counts["tp"]
fp = counts["fp"]
fn = counts["fn"]
precision = tp / (tp + fp) if tp + fp else 0.0
recall = tp / (tp + fn) if tp + fn else 0.0
f1 = 2 * precision * recall / (precision + recall) if precision + recall else 0.0
scores.append(f1)
return sum(scores) / len(scores)Ship only if the tuned model beats the baseline on the held-out set and does not create unacceptable regressions in rare, high-severity codes.
Complexity. O(L) for L labels to compute macro-F1 from aggregated counts. The fine-tuning decision depends on eval quality more than training mechanics.