Prompting, agents, and retrieval — where the certificate becomes application engineering
9 hours · 3 modules · LangChain + Hugging Face + IBM watsonx
Where the certificate becomes application engineering. Fine-tuning changes the model; RAG and agents change the system around the model.
The model learns from examples placed in the prompt, with no weight updates at all. An emergent property of scale — small models can't do it, large ones can.
Classify the sentiment: "The film dragged."
Review: "Brilliant pacing." Sentiment: positive
Review: "I walked out." Sentiment: negative
Review: "The film dragged." Sentiment:
Few-shot's main value is often format control — showing the model the exact output shape you want is more reliable than describing it.
Limits: it consumes context window, it doesn't persist between calls, and beyond a handful of examples returns diminish. When you need consistent behaviour on thousands of calls, fine-tune instead — that's cheaper per call and more reliable.
Core components of a good prompt:
Patterns the course names:
| Pattern | What it does |
|---|---|
| Zero/few-shot | examples in the prompt |
| Chain-of-Thought (CoT) | "Let's think step by step" — makes reasoning explicit; big gains on arithmetic and logic |
| Self-consistency | sample several CoT paths, take the majority answer |
| ReAct | interleave Reasoning and Acting — the basis of tool-using agents |
| Persona | assign a role to shape vocabulary and depth |
| Template / output-format | force JSON, tables, a fixed schema |
| Tree of Thoughts | explore and evaluate multiple reasoning branches |
| Instruction-first | put the instruction before the data for long inputs |
Why CoT works: the model has a fixed amount of computation per token. Forcing it to write intermediate steps gives it more tokens — therefore more computation — before it must commit to an answer. It isn't "thinking harder"; it's being given room to work.
Practical rules:
###, XML tags, triple backticks).You'll set these on every LLM call for the rest of the certificate:
| Parameter | Effect |
|---|---|
| temperature | scales logits before softmax. 0 = deterministic, 0.7 = balanced, >1 = wild |
| top_p (nucleus) | sample from the smallest set of tokens with cumulative probability ≥ p |
| top_k | sample from the k most likely tokens |
| max_tokens | output length cap |
| frequency/presence penalty | discourage repetition |
| stop sequences | strings that end generation |
Rule of thumb: temperature ~0 for extraction, classification and factual QA; 0.7–1.0 for creative work. Tune temperature or top_p, not both.
Definition: a system where an LLM decides what to do, not merely what to say — it can choose actions, call tools, observe results, and iterate toward a goal.
The loop:
Goal → Think (plan) → Act (call a tool) → Observe (read the result) → repeat → Answer
Components:
ReAct, the classic agent pattern, made concrete:
Thought: I need current data for this.
Action: search("Cloudflare Pages pricing 2026")
Observation: <results>
Thought: I now have what I need.
Final Answer: ...
Chain vs agent — the distinction that gets quizzed:
capable of looping forever.
Use a chain when you know the steps. Reach for an agent only when you genuinely don't.
LLM knowledge is frozen at training time, has no access to your private data, hallucinates confidently, and can't cite sources.
RAG (Retrieval-Augmented Generation): retrieve relevant documents at query time and put them in the prompt. The model reads rather than recalls.
Query → embed → search vector DB → top-k chunks → prompt(query + chunks) → LLM → grounded answer
Why it's the default answer to hallucination: you've converted a memory problem into a reading-comprehension problem. Reading comprehension is what these models are best at.
The single most examinable table in the second half of the certificate:
| RAG | Fine-tuning | |
|---|---|---|
| Adds | knowledge | behaviour, style, format |
| Update cost | re-index a document (seconds) | retrain (hours) |
| Freshness | live | frozen at training |
| Citations | yes, natural | no |
| Hallucination | much reduced | unchanged or worse |
| Inference cost | higher (long prompts) | baseline |
| Setup | vector DB, chunking, retrieval | GPU, labelled data |
| Private data | stays in your store | baked into weights |
They compose. Fine-tune for domain vocabulary and output format; use RAG for the facts. That's what a production system does.
Indexing (offline, once per document):
Retrieval + generation (online, per query):
Text → dense vector. Semantically similar text → nearby vectors. Compared with cosine similarity.
This is why RAG beats keyword search: a query about "revenue decline" retrieves a passage about "falling sales" even with no shared words.
Non-negotiable rule: query and documents must be embedded by the same model. Different models produce incompatible vector spaces, and the failure is silent — you get results, they're just meaningless.
Vector databases: Chroma (local, simple — what the labs use), FAISS (Meta's library, fast, in-process), Pinecone / Weaviate / Qdrant / Milvus (managed or production-grade), pgvector (Postgres extension). They use approximate nearest-neighbour indexes (HNSW, IVF) because exact search over millions of vectors is too slow — you trade a small amount of recall for enormous speed.
The parameter that most determines RAG quality, and the one most people neglect.
and dilute attention
Typical: 500–1000 characters with 10–20% overlap. Overlap prevents a sentence that straddles a boundary from being lost to both chunks.
Strategies (Course 13 covers the code):
can. The sensible default.
degrade answers.
identifiers, product codes and names, which pure embeddings often miss.
A cross-encoder reads query and document together, so it's much more accurate but too slow to run over the whole corpus.
five near-identical chunks.
Worth knowing because debugging RAG is the actual job:
hybrid search
instruction, chunk ordering ("lost in the middle" — models attend better to the start and end of long contexts)
how to resolve
only from the context provided; if it isn't there, say so"
A framework for composing LLM applications: a common interface across model providers, plus building blocks for prompts, chains, retrieval, memory and agents.
The pitch: swap OpenAI for watsonx for a local model by changing one line.
The honest caveat, worth holding: LangChain adds abstraction layers that can obscure what's actually being sent to the model. For simple applications, direct API calls are often clearer. Learn it because it's on every job description and because the certificate uses it — but know when it's overhead.
Chat models and LLMs
from langchain_ibm import WatsonxLLM
llm = WatsonxLLM(model_id='meta-llama/llama-3-70b-instruct',
url=..., project_id=..., params={'temperature': 0.5, 'max_new_tokens': 500})
Prompt templates
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
prompt = PromptTemplate(input_variables=['topic', 'audience'],
template="Explain {topic} to {audience} in three sentences.")
chat_prompt = ChatPromptTemplate.from_messages([
("system", "You are a concise technical writer."),
("human", "{question}"),
])
Output parsers — turn text into structured data
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
LCEL (LangChain Expression Language) — the pipe syntax that composes them:
chain = prompt | llm | StrOutputParser()
chain.invoke({'topic': 'colour grading', 'audience': 'a producer'})
| passes each component's output to the next. Chains built this way support .invoke(), .batch(), .stream() and their async equivalents for free.
Memory — LLM APIs are stateless; memory is what makes a conversation.
from langchain.memory import ConversationBufferMemory, ConversationSummaryMemory
Tools and agents
from langchain.agents import AgentExecutor, create_react_agent
from langchain_core.tools import tool
@tool
def get_frame_rate(codec: str) -> str:
"""Return the standard frame rate for a given broadcast codec."""
...
agent = create_react_agent(llm, tools=[get_frame_rate], prompt=react_prompt)
executor = AgentExecutor(agent=agent, tools=[get_frame_rate],
verbose=True, max_iterations=5,
handle_parsing_errors=True)
executor.invoke({'input': 'What frame rate should I use for XDCAM HD422?'})
The docstring is not documentation — it is the tool's specification to the model. The LLM reads the name, the docstring and the type hints to decide when and how to call it. A vague docstring means a tool that never gets used, or gets used wrongly. This is the highest-leverage detail in agent building.
max_iterations is a required safety valve — agents loop.
Document loaders and retrievers — Course 13's subject:
from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
The pattern the final project builds on:
from langchain_core.runnables import RunnablePassthrough
retriever = vectorstore.as_retriever(search_kwargs={'k': 4})
template = """Answer the question using only the context below.
If the context does not contain the answer, say you don't know.
Context:
{context}
Question: {question}
Answer:"""
prompt = ChatPromptTemplate.from_template(template)
def format_docs(docs):
return "\n\n".join(d.page_content for d in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
rag_chain.invoke("What is the retention policy for raw footage?")
RunnablePassthrough() forwards the original input unchanged while the retriever branch runs in parallel — that's how one input feeds two slots in the prompt.
| do in LCEL?RunnablePassthrough() doing in the RAG chain above?@tool docstrings ARE the tool spec