Building AI Agents with Open Source Models
Open Source Track — Final Module Module 24 of 24 — Track Complete
⏱ 75 min △ Intermediate
🚀
Open Source Track — M24 (Final) The open source AI ecosystem is evolving faster than any other part of software. New models ship weekly. Fine-tuning tools have collapsed from PhD-level to a weekend project. This module maps what exists now, what is arriving, and the three concrete paths forward from here.

M24: What's Next — The Open Source Frontier

The models you used throughout this track are the floor, not the ceiling. In this module you will survey the current model landscape, learn to fine-tune a model on your own data, use reasoning models for hard problems, and choose a 90-day path that deepens your skills in the direction that matters most for your work.

Learning Objectives

  • Navigate the 2026 open source model landscape: workhorses, reasoning models, and multimodal models
  • Call a multimodal model via the OpenAI-compatible Ollama API (Python and TypeScript)
  • Understand LoRA and QLoRA — what they do, when to use them, and how to run Unsloth
  • Use DeepSeek-R1 for chain-of-thought reasoning and strip <think> blocks for end users
  • Choose when to adopt LangChain, CrewAI, or AutoGen versus staying manual
  • Pick a 90-day learning path aligned with your goals
  • Scaffold your next agent project with the reference directory structure

The Open Source Model Landscape (2026)

Before the Pain → The Mapping

Two years ago, the open source model landscape was a single shelf: Llama 2, Mistral 7B, and a handful of derivatives. If you needed something the 7B could not handle, you either upgraded to a 70B (expensive) or switched to a cloud API (expensive differently).

The pain: the 7B made consistent factual errors on your domain, could not reason through multi-step math, and could not look at an image. You were stuck with a hammer when the job needed a Swiss Army knife.

The mapping today: the open source ecosystem now has specialized models for reasoning (DeepSeek-R1DeepSeek-R1 is an open source reasoning model that outputs a chain-of-thought "thinking" block before its final answer. The reasoning traces are visible in the response content wrapped in <think>...</think> tags, making it useful for debugging complex inference chains.), vision (LLaVALLaVA (Large Language and Vision Assistant) is a multimodal model that accepts both images and text. It runs locally via Ollama with `ollama pull llava` and exposes the same OpenAI-compatible chat completions API with image support.), compact on-device inference (Phi-4 mini), and massive capability at 7B scale (Qwen 2.5). You can now match the right model to the right job within a single Ollama installation.

Current Generation: What You Have Been Using

These are the stable, well-documented workhorses that appeared throughout this course:

ModelPull commandStrengthsContextBest for
mistral:7bollama pull mistral Tool use, instruction following32KAgents, structured output
llama3.1:8bollama pull llama3.1 Coding, reasoning, general128KCode generation, RAG
qwen2.5:7bollama pull qwen2.5 Multilingual, long context128KDocument analysis, i18n apps
phi3:miniollama pull phi3:mini Compact, fast, good reasoning4KEdge devices, low-latency

Newer and Emerging Models

These are the models released in 2024–2025 that represent a significant capability leap:

ModelArchitectureKey capabilityPull command
llama4-scout MoEMixture of Experts (MoE) architecture routes each token to a subset of "expert" sub-networks instead of activating the full model. A 17B MoE model may use only 3.4B active parameters per forward pass, giving large-model quality at small-model speed. 17B (3.4B active) Natively multimodal, fast inference ollama pull llama4:scout
llama4-maverick MoE 400B (17B active) Near-frontier reasoning + vision ollama pull llama4:maverick
deepseek-r1:7b Dense transformer + GRPO training Chain-of-thought reasoning built in ollama pull deepseek-r1:7b
mistral-small2 Dense 22B Huge quality jump from 7B series ollama pull mistral-small2
phi4:mini Dense 3.8B Reasoning-dense, tiny footprint ollama pull phi4:mini
gemma3:9b Dense 9B (Google) Strong instruction following ollama pull gemma3:9b
Why This Matters

The capability gap between open source and frontier API models has shrunk dramatically. DeepSeek-R1-7B matches GPT-4o on many reasoning benchmarks. Llama 4 Scout processes images natively. You now have access to models that, 18 months ago, would have required a $200/month API budget — running entirely on your laptop.

Animation: Model Capability Comparison
Model comparison: Mistral 7B (fast, tool-use), Llama 3.1 8B (coding, long context), DeepSeek-R1 7B (reasoning +++), Llama 4 Scout (multimodal, MoE), Qwen 2.5 7B (multilingual, long context).

Multimodal Models via Ollama

Definition: Multimodal Model

A multimodalA multimodal model accepts multiple types of input — most commonly text + images. The model encodes each modality through separate encoders, then a fusion layer combines them before the language model produces a response. Vision capability is not a separate API call; the image tokens are injected directly into the context. model accepts both text and images (and sometimes audio or video) as input within a single inference call. Ollama supports several vision-capable models via the standard OpenAI-compatible chat completions API — you send images as base64-encoded data URIs alongside text in the message content array.

Available Vision Models

  • LLaVA — the original. Strong general image understanding. ollama pull llava
  • Moondream2 — lightweight vision model, runs on 4 GB VRAM. ollama pull moondream
  • Bakllava — Mistral 7B backbone + LLaVA visual encoder. ollama pull bakllava
  • Llama 4 Scout — natively multimodal, best quality. ollama pull llama4:scout
Calling a vision model is a one-line change from your existing text code: instead of a string content, you pass an array with an image_url object and a text object. The same Ollama server handles it — no new dependencies.
WHAT — Send an image to LLaVA via the OpenAI-compatible API using base64 encoding
import base64
from openai import OpenAI

# WHY: same client you've used throughout this track — no new dependencies
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

# WHAT: read and encode the image as a base64 data URI
def encode_image(path: str) -> str:
    with open(path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

def analyze_image(image_path: str, question: str) -> str:
    b64 = encode_image(image_path)

    # WHAT: content is an array — one image_url item, one text item
    # GOTCHA: the image_url.url must be a valid data URI with the correct MIME type
    response = client.chat.completions.create(
        model="llava",   # or "llama4:scout" for better quality
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/jpeg;base64,{b64}"
                        }
                    },
                    {
                        "type": "text",
                        "text": question
                    }
                ]
            }
        ],
        max_tokens=512
    )
    return response.choices[0].message.content

# Example usage
if __name__ == "__main__":
    result = analyze_image("screenshot.png", "What UI elements are visible? List them.")
    print(result)
import OpenAI from "openai";
import fs from "fs";
import path from "path";

// WHY: same Ollama endpoint — TypeScript SDK works identically
const client = new OpenAI({
  baseURL: "http://localhost:11434/v1",
  apiKey: "ollama",
});

function encodeImage(filePath: string): string {
  const buffer = fs.readFileSync(filePath);
  return buffer.toString("base64");
}

async function analyzeImage(
  imagePath: string,
  question: string
): Promise {
  const b64 = encodeImage(imagePath);
  const ext = path.extname(imagePath).slice(1).toLowerCase();
  const mimeType = ext === "png" ? "image/png" : "image/jpeg";

  // WHAT: identical content array structure as Python
  // GOTCHA: ensure the file exists before calling — no streaming fallback here
  const response = await client.chat.completions.create({
    model: "llava",
    messages: [
      {
        role: "user",
        content: [
          {
            type: "image_url",
            image_url: { url: `data:${mimeType};base64,${b64}` },
          },
          { type: "text", text: question },
        ],
      },
    ],
    max_tokens: 512,
  });

  return response.choices[0]?.message?.content ?? "";
}

// Example
analyzeImage("screenshot.png", "Describe what you see in this image.")
  .then(console.log)
  .catch(console.error);
What Just Happened?

You sent a JPEG to LLaVA using the exact same client.chat.completions.create() call you have used for text throughout this track. The model tokenizes the image using a CLIP-style visual encoder and injects those tokens into the context window alongside your question. No new SDK. No separate endpoint. Just a different content shape.

Fine-Tuning with LoRA and QLoRA

Before the Pain → The Mapping

You run a 7B model for customer support at a medical device company. It gives reasonable answers — but it keeps confusing "510(k) clearance" with "CE marking," mixing up your product model numbers, and using generic language when your documentation has very specific phrasing. Every response needs editing before it goes to a customer.

The pain: the base model was trained on the general internet, not on your 800 FDA submission documents, your internal style guide, and three years of validated Q&A pairs. It cannot learn your domain vocabulary from prompts alone — the context window is not the same as learned weights.

The mapping: fine-tuning on 500–1000 domain-specific examples teaches the model your terminology, output format, and reasoning patterns. The model is still 7B parameters — you are not rebuilding it. You are adding thin trainable layers (LoRA) on top of frozen weights, costing 1–2 hours on an A10 GPU and producing a model that makes the right calls 80% of the time instead of 40%.

Definition: LoRA

LoRALow-Rank Adaptation (LoRA) injects small trainable matrices (rank-r decompositions) into the frozen attention layers of a pretrained model. Only these adapter matrices — roughly 0.1–1% of total parameters — are updated during fine-tuning. The base model weights are never changed, so you can stack or swap LoRA adapters on a single base model. (Low-Rank Adaptation) adds small trainable matrices to the attention layers of a frozen model. During fine-tuning, only these matrices — typically 0.1–1% of total parameters — are updated. This means you can fine-tune a 7B model on a single A10 (24 GB VRAM) instead of needing a multi-GPU cluster. QLoRAQLoRA (Quantized LoRA) runs LoRA fine-tuning on a 4-bit quantized base model, cutting VRAM requirements roughly in half again. A 7B model fine-tune that needs 16 GB VRAM with standard LoRA needs only 6–8 GB with QLoRA. The quantization affects only the base weights; the LoRA adapters train in full precision (bf16). goes further: it runs LoRA on a 4-bit quantizedQuantization reduces the numerical precision of model weights (e.g., from 32-bit float to 4-bit integer), shrinking memory footprint by 4–8x. Inference quality degrades slightly but is often indistinguishable from full precision on most tasks. Ollama uses GGUF quantization formats like Q4_K_M, Q5_K_M, Q8_0. base model, so a 7B fine-tune fits in 6–8 GB VRAM — a consumer RTX 3060.

Animation: LoRA Parameter Efficiency
Base Model Weights
params7,000,000,000
trainableFROZEN
VRAM (4-bit)6–8 GB
+
LoRA Adapters
params~7M (0.1%)
trainableACTIVE
rank r16
efficiency gain
LoRA: freeze 7B base weights, add ~7M trainable adapter parameters (0.1%). QLoRA: quantize base to 4-bit, fits in 6–8 GB VRAM. Fine-tune in 1–2 hours on A10.

The Unsloth Workflow

UnslothUnsloth is a Python library that makes LoRA and QLoRA fine-tuning 2–5x faster than the standard HuggingFace PEFT implementation, while using 60% less VRAM. It hand-optimizes the backward pass for modern GPU architectures and integrates directly with HuggingFace Trainer and TRL's SFTTrainer. is currently the fastest LoRA/QLoRA implementation available. On an A10, it runs fine-tuning 2–5x faster than standard HuggingFace PEFT with 60% less VRAM. The workflow ends with a GGUFGGUF (GPT-Generated Unified Format) is the quantized model format used by llama.cpp and Ollama. After fine-tuning with Unsloth, you export to GGUF and register the model in Ollama via a Modelfile, which lets you run `ollama run my-fine-tuned-model` exactly like any built-in model. export that registers directly in Ollama:

WHAT — Load a 4-bit QLoRA base, attach LoRA adapters, train on your data, export to GGUF
# pip install unsloth trl datasets
from unsloth import FastLanguageModel
from trl import SFTTrainer
from transformers import TrainingArguments
from datasets import load_dataset

# WHAT: load Mistral 7B in 4-bit (QLoRA) — fits in 6-8 GB VRAM
# WHY: load_in_4bit=True triggers the quantized base model
# GOTCHA: first run downloads ~4 GB; ensure HF_HOME has space
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/mistral-7b-bnb-4bit",
    max_seq_length=4096,
    load_in_4bit=True,
)

# WHAT: attach LoRA adapters to attention projection matrices
# WHY: q_proj and v_proj are the highest-impact targets for most tasks
# GOTCHA: larger r (rank) = more capacity but more VRAM; r=16 is a good default
model = FastLanguageModel.get_peft_model(
    model,
    r=16,               # rank of the adapter matrices
    lora_alpha=16,      # scaling factor — usually equal to r
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    lora_dropout=0.0,   # 0 is fine for most fine-tuning tasks
    bias="none",
)

# WHAT: load your training data (JSONL with "text" field containing full conversations)
dataset = load_dataset("json", data_files="my_domain_data.jsonl", split="train")

# WHAT: train with HuggingFace SFTTrainer (supervised fine-tuning)
trainer = SFTTrainer(
    model=model,
    tokenizer=tokenizer,
    train_dataset=dataset,
    dataset_text_field="text",
    max_seq_length=4096,
    args=TrainingArguments(
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,
        warmup_steps=10,
        num_train_epochs=3,
        learning_rate=2e-4,
        logging_steps=25,
        output_dir="./lora-output",
        fp16=True,
    ),
)
trainer.train()

# WHAT: export to GGUF so Ollama can load it
# WHY: GGUF is the format ollama uses natively — no conversion step needed
# GOTCHA: q4_k_m gives the best size/quality tradeoff for most tasks
model.save_pretrained_gguf(
    "my-domain-model",
    tokenizer,
    quantization_method="q4_k_m"
)
print("Exported: my-domain-model-unsloth.Q4_K_M.gguf")
Note: LoRA is Python-First

The LoRA/QLoRA training ecosystem (Unsloth, HuggingFace PEFT, TRL) is Python-only. TypeScript is not applicable here. Once the fine-tuned model is exported to GGUF and registered in Ollama, you call it from TypeScript exactly like any other model — the distinction only applies during the training step.

Registering the Fine-Tuned Model in Ollama

After the GGUF export, create a Modelfile in the same directory:

# Modelfile — place in the same directory as the .gguf file

# WHAT: points Ollama at the exported GGUF file
FROM ./my-domain-model-unsloth.Q4_K_M.gguf

# WHAT: system prompt baked into every conversation
# WHY: reduces per-request token cost; sets domain context permanently
SYSTEM """You are a specialized assistant for [Your Domain].
Use precise domain terminology. Always cite relevant standards when applicable.
Never use generic language when domain-specific phrasing exists."""

# WHAT: set inference parameters
PARAMETER temperature 0.3
PARAMETER top_p 0.9
PARAMETER num_ctx 4096
# Register with Ollama — runs once
ollama create my-domain-model -f Modelfile

# Test immediately
ollama run my-domain-model "What is a 510(k) clearance?"

# Use via API exactly like any other model
# client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
# response = client.chat.completions.create(model="my-domain-model", ...)
What Just Happened?

You trained a 7B model on your domain data using QLoRA (less than 8 GB VRAM), exported it to GGUF format, and registered it in Ollama with a custom system prompt baked in. From this point forward, ollama run my-domain-model behaves exactly like any built-in model — but it knows your domain.

Reasoning Models: DeepSeek-R1

Definition: Reasoning Model

A reasoning modelA reasoning model is trained (via GRPO or similar reinforcement learning from outcome feedback) to produce a step-by-step thinking trace before its final answer. DeepSeek-R1 outputs this trace inside <think>...</think> tags. The thinking tokens are part of the model's context but can be stripped before showing the response to users. produces a visible chain-of-thought before its final answer. DeepSeek-R1 was trained using Group Relative Policy Optimization (GRPO) — a reinforcement learning method that rewards correct final answers, which causes the model to develop structured reasoning strategies. The thinking trace appears in the response content wrapped in <think>...</think> tags.

When to Use DeepSeek-R1

  • Use it for: multi-step math, logic puzzles, code debugging, complex planning, anything that requires holding multiple constraints simultaneously
  • Avoid it for: simple Q&A, high-volume classification, real-time chat (it is slower — the thinking adds 200–800 tokens before the answer)
WHAT — Call DeepSeek-R1 and optionally strip the thinking trace before showing to users
import re
from openai import OpenAI

# Pull once before first run: ollama pull deepseek-r1:7b
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

def think_and_answer(problem: str, show_thinking: bool = False) -> dict:
    """
    Call DeepSeek-R1.
    Returns {"thinking": str, "answer": str}.
    Set show_thinking=True to debug reasoning; False for end-user responses.
    """
    response = client.chat.completions.create(
        model="deepseek-r1:7b",
        messages=[
            {
                "role": "user",
                "content": problem
            }
        ],
        max_tokens=2048,
        temperature=0.6   # R1 works well at 0.5-0.7 for reasoning tasks
    )

    full_content = response.choices[0].message.content or ""

    # WHAT: extract the thinking trace
    # GOTCHA: some R1 variants use different tag formats — check with your version
    think_match = re.search(r"<think>(.*?)</think>", full_content, re.DOTALL)
    thinking = think_match.group(1).strip() if think_match else ""

    # WHAT: strip thinking tags to get the clean final answer
    answer = re.sub(r"<think>.*?</think>", "", full_content, flags=re.DOTALL).strip()

    return {
        "thinking": thinking if show_thinking else "[hidden]",
        "answer": answer
    }

# Example: a logic problem that benefits from chain-of-thought
result = think_and_answer(
    "A farmer has 17 sheep. All but 9 die. How many are left? "
    "Explain your reasoning step by step.",
    show_thinking=True  # set to False for production
)
print("THINKING:\n", result["thinking"])
print("\nANSWER:\n", result["answer"])
import OpenAI from "openai";

// Pull once: ollama pull deepseek-r1:7b
const client = new OpenAI({
  baseURL: "http://localhost:11434/v1",
  apiKey: "ollama",
});

interface ThinkResult {
  thinking: string;
  answer: string;
}

async function thinkAndAnswer(
  problem: string,
  showThinking: boolean = false
): Promise<ThinkResult> {
  const response = await client.chat.completions.create({
    model: "deepseek-r1:7b",
    messages: [{ role: "user", content: problem }],
    max_tokens: 2048,
    temperature: 0.6,
  });

  const fullContent = response.choices[0]?.message?.content ?? "";

  // WHAT: extract the thinking trace between <think> tags
  // GOTCHA: use DOTALL equivalent — the thinking block can span many lines
  const thinkMatch = fullContent.match(/<think>([\s\S]*?)<\/think>/);
  const thinking = thinkMatch ? thinkMatch[1].trim() : "";

  // WHAT: remove the thinking block to get the clean answer
  const answer = fullContent
    .replace(/<think>[\s\S]*?<\/think>/g, "")
    .trim();

  return {
    thinking: showThinking ? thinking : "[hidden]",
    answer,
  };
}

// Example
thinkAndAnswer(
  "If I have 3 red balls and 2 blue balls, and I remove one without looking, " +
  "what is the probability it was red?",
  true
).then(({ thinking, answer }) => {
  console.log("THINKING:\n", thinking);
  console.log("\nANSWER:\n", answer);
});
Example output (show_thinking=True)
THINKING: Let me think about this carefully. The farmer starts with 17 sheep. "All but 9 die" means 9 survive — the phrasing means 17 - 9 = 8 die, leaving 9 alive. This is a classic trick question... ANSWER: 9 sheep are left. "All but 9" means all except 9 survive, so exactly 9 remain.
Production Note: Hide the Thinking

The <think> block can be 200–800 tokens of internal reasoning. Do not display this to end users — it looks like the model is "talking to itself" and can confuse non-technical users. Use show_thinking=False in production. However, keep show_thinking=True in your development and eval environments — the thinking trace is your primary debugging tool for understanding why the model reached a wrong answer.

Framework Landscape: Do You Need One?

Analogy

A joiner learning woodworking does not start with a CNC machine. The CNC is faster and more precise — once you know what shapes you need. But if you start with the CNC, you skip learning to read the grain, feel the resistance, and understand why a joint holds. When the CNC breaks (and it will), you are helpless.

Frameworks are the CNC machines of agent development. LangChain, CrewAI, and AutoGen are faster for shipping common patterns — but when something goes wrong (and it will — wrong tools called, infinite loops, memory leaks, hallucinated tool schemas), you need to understand what the framework is doing underneath to debug it.

Throughout this track you built every component manually: the ReAct loop, the multi-agent handoff, the RAG pipeline. That knowledge is now your competitive advantage. You can read LangChain source code and understand it. You can adopt CrewAI for a crew-based pattern and know exactly which abstraction maps to which primitive you already understand.

Framework Tradeoffs

Approach Control Complexity Best pattern When to choose
Manual (this course) Full You own it all Any custom architecture Always start here
LangChain Medium Complex internals, magic chains Linear pipelines, integrations Need 50+ integrations fast
CrewAI Medium-low Role/Task DSL to learn Role-based multi-agent Crew/Task model fits your problem
AutoGen Medium Conversation-first abstraction Research / exploratory agents Multi-agent chat patterns
LlamaIndex High Index/retrieval abstractions RAG and knowledge bases Complex retrieval patterns
Recommendation

Build manual first (you have done this). Then adopt frameworks surgically: use LlamaIndex when your RAG pipeline becomes complex enough that the manual version is brittle. Use CrewAI when you have a clear crew/role mental model that maps directly to its DSL. Never start a new agent project with a framework — you will not understand what it is doing and debugging will be an order of magnitude harder.

Everything you built manually in this track maps directly to a framework abstraction. Your M12 ReAct loop is LangChain AgentExecutor. Your M14 multi-agent handoff is CrewAI's Crew + Task. Your M09-M10 RAG pipeline is LlamaIndex VectorStoreIndex. The manual knowledge transfers completely; the frameworks just compress the boilerplate.

Your 90-Day Roadmap

Three distinct paths forward, depending on your goal. Click a path to see the week-by-week breakdown.

Animation: 90-Day Learning Path
Path A: Week 1–4 fine-tune a model, Week 5–8 build a domain RAG pipeline, Week 9–12 deploy to cloud GPU and serve real users.
Path B: Swap client to Anthropic, take main course M12–M22, add prompt caching and Claude hooks.
Path C: Take MCP Track, add M18–M21 eval + monitoring, harden your production agent.

Your Starter Project Structure

Copy this directory structure as your scaffold for any new agent project. Every component maps to a module in this track:

my-agent/ ├── agent.py # ReAct loop (M12) ├── tools.py # tool implementations (M05-M06) ├── memory.py # conversation + vector memory (M11) ├── rag/ │ ├── ingest.py # document chunking + embedding (M09) │ └── retrieve.py # similarity search + reranking (M10) ├── eval/ │ ├── harness.py # eval runner (M18) │ └── cases.jsonl # test cases ├── guardrails/ │ ├── input.py # input validation (M16) │ └── output.py # output safety checks (M17) ├── api.py # FastAPI wrapper (M21) ├── Dockerfile # production container (M21B) └── requirements.txt # pinned deps

Final Exercise: Extend Your Agent

This is the final exercise in the Open Source Track. Choose one extension and implement it in your agent from Module 12. There is no single expected output — this is open-ended exploration. Click an option to see setup instructions.

Knowledge Check

Five questions covering the content of this module. Select an answer to see immediate feedback.

1. You need to run a fine-tune on a 7B model but only have 8 GB of VRAM. Which technique makes this possible?

AFull fine-tuning — update all 7B parameters simultaneously
BLoRA only — add adapter matrices to frozen weights, standard precision
CQLoRA — LoRA on a 4-bit quantized base model, reducing VRAM to 6–8 GB
DDistillation — train a smaller student model from the 7B teacher
Correct. QLoRA combines 4-bit quantization of the base model (halving VRAM again) with LoRA adapters that train in bf16. A 7B fine-tune that normally needs 14–16 GB with standard LoRA fits into 6–8 GB with QLoRA, making a consumer RTX 3060 viable.
Not quite. Full fine-tuning requires much more VRAM than 8 GB for a 7B model. Standard LoRA reduces it but still needs ~14–16 GB. QLoRA (C) is the correct answer — it quantizes the base model to 4-bit, cutting VRAM requirements enough to fit on 8 GB.

2. DeepSeek-R1 outputs <think>...</think> blocks. What should you do with them in a production user-facing app?

ADisplay them prominently — users appreciate transparency about model reasoning
BStrip them from the user-facing response; keep them in dev/eval for debugging
CReturn them as a separate JSON field so the frontend can optionally render them
DPass them back to the model as context in the next message to improve continuity
Correct. The <think> block is an internal reasoning trace — typically 200–800 tokens of "talking to itself." End users find it confusing. Strip it for production using a regex substitution. In development and eval environments, keep it visible because it is your primary debugging tool for understanding wrong answers.
Not quite. The thinking trace is an internal reasoning artifact, not user-facing content. Strip it for production (B) but preserve it in dev/eval environments where it helps you debug wrong answers. Returning it as a separate JSON field (C) is a reasonable pattern for advanced UIs but the primary recommendation is to strip it for standard production deployments.

3. Which architecture does Llama 4 Scout use that lets it achieve large-model quality at smaller active parameter counts?

ASparse attention — only attending to every other token
BMixture of Experts (MoE) — routing each token to a subset of expert sub-networks
CDistilled knowledge — compressed from a much larger 400B teacher model
DQuantization — all weights stored in 4-bit format
Correct. Mixture of Experts (MoE) routes each token to a small subset of specialist sub-networks rather than activating the full model. Llama 4 Scout has 17B total parameters but only ~3.4B active parameters per forward pass. This gives large-model capability at small-model inference cost.
Not quite. Llama 4 Scout uses Mixture of Experts (B) — a routing mechanism that activates only a subset of expert sub-networks per token. A 17B MoE model might use only 3.4B active parameters per forward pass, giving large-model knowledge at smaller inference cost. This is different from quantization or distillation.

4. What file format does Unsloth export to that allows you to register a fine-tuned model with Ollama?

ASafeTensors (.safetensors) — the HuggingFace native format
BONNX (.onnx) — for cross-platform inference
CGGUF (.gguf) — the llama.cpp / Ollama native quantized format
DPyTorch (.pt) — the default PyTorch checkpoint format
Correct. GGUF (GPT-Generated Unified Format) is the quantized model format used by llama.cpp and Ollama. Unsloth exports directly to GGUF with `model.save_pretrained_gguf()`. You then run `ollama create my-model -f Modelfile` to register it, after which `ollama run my-model` works exactly like any built-in model.
Not quite. Ollama uses GGUF format (C) — the llama.cpp native quantized format. After calling `model.save_pretrained_gguf()` in Unsloth, you create a Modelfile pointing at the .gguf file and run `ollama create` to register it. SafeTensors and PyTorch checkpoints need additional conversion steps.

5. According to the framework landscape section, what is the recommended order of operations when starting a new agent project?

AStart with LangChain or CrewAI to move fast, then optimize by removing framework layers
BEvaluate all frameworks first, then choose the one with the most GitHub stars
CBuild manually first; adopt framework abstractions only when a specific pattern clearly maps to their DSL
DAlways use AutoGen for multi-agent work since it is the most flexible option
Correct. Build manually first (as you did in this track) to understand every component. Then adopt frameworks surgically when a specific pattern — crew/role for CrewAI, complex retrieval for LlamaIndex — clearly maps to their abstraction. This way you can read framework source code, debug failures, and know exactly what the framework is doing underneath.
Not quite. Starting with a framework (A) means you will not understand what it is doing and debugging becomes very hard. The correct approach (C) is: build manually first, understand every component, then adopt frameworks surgically only when a specific pattern clearly maps to their DSL. The manual knowledge you built in this track is your debugging foundation.
0/5