Module 1 · Generative AI Foundations

Generative AI and Large Language Models

Lesson 1 of 24 12 min read Updated

Generative AI creates new text, images, audio, code, or other artifacts from patterns learned during training. In this lesson the focus is large language models, or LLMs: models that receive text-like tokens and produce more tokens. The practical outcome is not magic conversation. It is controlled probabilistic text generation: given instructions and context, the model estimates which token is likely to come next, repeats that step, and turns the resulting token stream back into human-readable text.

This foundation matters throughout the Generative AI course because almost every later technique, including prompt design, retrieval augmented generation, tool calling, evaluation, and agent workflows, depends on the same core fact. An LLM does not retrieve a stored paragraph by default, and it does not possess a built-in truth meter. It generates plausible continuations from the context it can see. Good systems therefore shape the context, constrain the output, check the result, and know when the model should not answer.

Purpose and Outcome

After this lesson you should be able to explain what an LLM is doing internally at generation time, read the basic anatomy of an LLM request, predict how sampling settings affect output, and diagnose common failures such as hallucinated facts, context overflow, and unstable formatting. You should also be able to build a small local demonstration that separates model-like probability behavior from truthfulness.

How Next-Token Generation Works

Text is first broken into tokens. A token may be a whole word, part of a word, punctuation, whitespace, or a byte-like fallback unit depending on the tokenizer. The model never directly sees English words as people do. It sees token ids. During training, the model processes huge sequences of token ids and learns parameters that make the next token in those sequences more predictable.

Modern LLMs are commonly based on transformer architecture. A transformer represents each token as a vector, adds information about position, and repeatedly applies attention and feed-forward layers. Attention lets each token representation weigh other tokens in the context. In a sentence such as The trophy did not fit in the suitcase because it was too big, attention helps connect it to the likely referent. The model does not use a grammar rulebook for this; it learns statistical relationships that are useful for prediction.

At generation time, the model receives a context window containing system instructions, developer instructions, user messages, retrieved documents, tool results, and prior assistant text. It computes a score, often called a logit, for each possible next token in its vocabulary. A softmax converts those logits into a probability distribution. Decoding then chooses one token using a strategy such as greedy choice, temperature sampling, top-p sampling, or beam-like search. The chosen token is appended to the context, and the process repeats until a stop condition is reached.

This repeated mechanism explains both the power and the weakness of LLMs. They can perform translation, summarization, classification, drafting, and coding because many tasks can be represented as patterns of token continuation. They can also produce false statements because a fluent continuation is not the same as verified knowledge. If the prompt asks for a citation that is not in context, the model may still continue with something citation-shaped unless the application prevents or detects that behavior.

Request Anatomy

A basic LLM request has four important parts. The first is instruction text: what role the model should play and what output shape is expected. The second is task input: the user’s question, document, data, or command. The third is generation configuration: limits and sampling controls. The fourth is optional grounding material: retrieved passages, tool outputs, or structured facts the answer should use.

Common configuration terms include max_output_tokens, which caps the length of generated text; temperature, which changes how strongly the model prefers high-probability tokens; top_p, which samples from a cumulative probability mass; and stop sequences, which end generation when a marker appears. Lower temperature tends to be more stable, while higher temperature tends to be more varied. Neither setting makes the model truthful by itself.

Example 1: A Tiny Next-Token Model

The following example is not a neural network, but it demonstrates the essential shape of next-token prediction. It counts which token follows a two-token prefix in a small corpus. For the prefix large language, the next token distribution comes from observed continuations.

from collections import Counter, defaultdict

corpus = [
    "large language models generate text",
    "large language models predict tokens",
    "a large language model predicts tokens",
]

counts = defaultdict(Counter)

for sentence in corpus:
    tokens = sentence.split()
    for index in range(len(tokens) - 2):
        prefix = tuple(tokens[index:index + 2])
        next_token = tokens[index + 2]
        counts[prefix][next_token] += 1

print(counts[("large", "language")].most_common())

The deterministic output is [('models', 2), ('model', 1)]. A real LLM replaces these small counts with billions of learned parameters and a much richer context representation, but the generation question is still similar: given what came before, which token should come next?

Example 2: Temperature Changes the Distribution

Temperature does not add knowledge. It reshapes the probability distribution used for selection. A low temperature concentrates probability on the strongest candidate; a higher temperature gives weaker candidates more room. This example applies softmax to three made-up logits.

import math

logits = {"accurate": 3.0, "plausible": 2.0, "wild": 0.0}

def probabilities(temperature: float) -> dict[str, float]:
    highest = max(logits.values())
    scaled = {
        token: math.exp((score - highest) / temperature)
        for token, score in logits.items()
    }
    total = sum(scaled.values())
    return {token: round(value / total, 3) for token, value in scaled.items()}

print(probabilities(0.5))
print(probabilities(2.0))

The output is {'accurate': 0.879, 'plausible': 0.119, 'wild': 0.002} and then {'accurate': 0.547, 'plausible': 0.331, 'wild': 0.122}. The higher temperature makes unusual continuations more likely. That may help brainstorming, but it is usually a poor fit for regulated summaries, data extraction, or answers that must match supplied evidence.

Example 3: Grounding Changes What Can Be Answered

Grounding means supplying evidence and requiring the answer to stay within it. This miniature function refuses to answer when the needed fact is absent. The important behavior is the refusal, not the string formatting.

facts = {
    "Ada Lovelace": "Ada Lovelace published notes on the Analytical Engine in 1843.",
    "Grace Hopper": "Grace Hopper worked on early compilers and helped popularize COBOL.",
}

def grounded_answer(question: str, evidence: dict[str, str]) -> str:
    for subject, fact in evidence.items():
        if subject.lower() in question.lower():
            return fact
    return "I do not have enough provided evidence to answer."

print(grounded_answer("When did Ada Lovelace publish her notes?", facts))
print(grounded_answer("When was the first GPU released?", facts))

The first line returns the supplied Ada Lovelace fact. The second returns I do not have enough provided evidence to answer. Real applications use retrieval systems, citations, and validators instead of a dictionary, but the design principle is the same: when evidence is missing, the correct behavior may be non-answering.

Example 4: Checking a Structured Answer

LLM output is text, even when it looks like JSON. If downstream code depends on fields, validate them before use. This example accepts only an object with a supported label and a bounded confidence score.

import json

allowed_labels = {"billing", "technical", "account"}

def validate_ticket_classification(raw_text: str) -> dict[str, object]:
    data = json.loads(raw_text)
    if data.get("label") not in allowed_labels:
        raise ValueError("unsupported label")
    confidence = data.get("confidence")
    if not isinstance(confidence, (int, float)) or not 0 <= confidence <= 1:
        raise ValueError("confidence must be between 0 and 1")
    return data

print(validate_ticket_classification('{"label": "billing", "confidence": 0.82}'))

The expected output is {'label': 'billing', 'confidence': 0.82}. A malformed answer, an extra category, or a confidence value above one should fail before any routing or automation occurs.

Design Choices and Trade-Offs

Choosing an LLM pattern starts with the task. For open-ended ideation, diversity and style may matter more than determinism. For extraction, classification, or policy-sensitive answers, stable schemas and low variance are more important. Longer context can include more evidence but increases cost and may dilute attention across irrelevant text. Retrieval can make answers fresher and more grounded, but bad retrieval can inject unrelated or conflicting passages. Fine-tuning can teach a style or specialized mapping, but it does not automatically keep facts current.

Prompting is the quickest control surface, but prompts are instructions, not hard guarantees. Schemas, validators, retrieval filters, tool permissions, and human review are stronger controls. A small model may be cheaper and faster for classification, while a larger model may handle complex reasoning or messy instructions better. The right choice depends on measured task performance, not on model size alone.

Failure Modes and Troubleshooting

Symptom: the answer sounds confident but contains a false fact. Cause: the prompt asked for knowledge that was absent, stale, or ambiguous. Diagnosis: inspect the exact context sent to the model and mark which sentence supports each claim. Correction: add retrieval, require citations to supplied passages, or make refusal acceptable when evidence is missing.

Symptom: repeated runs produce different categories or formats. Cause: sampling is too variable, the prompt leaves multiple valid interpretations, or the output format is not validated. Diagnosis: run the same input several times and compare parsed fields. Correction: lower temperature, give explicit allowed labels, request structured output, and reject invalid responses.

Symptom: the model ignores an important document section. Cause: the context is too long, poorly ordered, or filled with irrelevant text. Diagnosis: count tokens, review retrieved chunks, and check whether the needed passage appears near competing material. Correction: improve chunking, rank evidence, summarize long background, or split the task into smaller calls.

Symptom: a user instruction overrides the intended behavior. Cause: untrusted text was placed in context without separation from developer instructions or evidence. Diagnosis: look for phrases inside documents or user input that tell the model to ignore previous instructions. Correction: label untrusted content as data, avoid executing instructions found inside retrieved documents, and apply permission checks outside the model.

Security, Performance, and Reliability

Security risk comes from treating generated text as authority. A model may suggest unsafe commands, leak sensitive prompt content, or follow malicious instructions embedded in user data. Keep secrets out of prompts, scope tools narrowly, and require explicit approval for irreversible actions. Performance risk comes from token volume and repeated calls. Cache stable intermediate results, trim irrelevant context, and measure latency by model call, retrieval, validation, and post-processing. Reliability comes from testing behavior on representative examples, including cases where the model should refuse or ask for clarification.

Hands-On Lab: Build a Grounded Mini Answerer

Prerequisites: Python 3, a terminal, and no external API key. This lab uses deterministic code so the verification does not depend on a hosted model.

  1. Create a file named mini_grounded_answerer.py.
  2. Add the code from Example 3.
  3. Run python3 mini_grounded_answerer.py.
  4. Verify that the Ada Lovelace question returns the provided 1843 fact.
  5. Verify that the GPU question refuses because the evidence dictionary does not contain that fact.
  6. Add a new evidence entry for first GPU and rerun the script.
  7. Confirm that the formerly refused question now answers only from the new evidence.

Cleanup: delete the lab file when finished, or remove the added evidence entry to return to the original behavior. The lesson is not the dictionary technique itself. The lesson is the contract: answers must be traceable to supplied evidence, and missing evidence must produce a controlled refusal.

Assessment Exercises

  • A model answers a legal question with a case name that was not in the retrieved documents. Describe the likely generation mechanism and the application control you would add first.
  • You need a customer support classifier with exactly five labels. Which is more important: a high temperature or a strict output schema? Explain the trade-off.
  • A long prompt includes ten pages of policy and one short user question. The answer misses an exception in page eight. Name two diagnostics and two corrections.
  • Design three test cases for a grounded Q and A feature: one answerable, one ambiguous, and one unanswerable. State the expected behavior for each.
  • Explain why a fluent paragraph can be less reliable than a terse refusal in a retrieval-based system.

Summary

Large language models generate text by repeatedly estimating and selecting the next token from a probability distribution shaped by context and decoding settings. Transformer attention lets the model use relationships across the prompt, but generation remains different from verification. Temperature, context length, grounding, schemas, and validators are design controls with real trade-offs. In practical generative AI engineering, the key skill is not merely getting a plausible answer once. It is designing the surrounding system so the model answers when evidence and permissions support it, refuses when they do not, and produces output that downstream software can safely interpret.

Progress is saved in this browser.