Compiled on: 2026-10-22 — printable version
About the course (this page):
Prompt Engineering & Structured Outputs
Validating Generative Software
Retrieval-Augmented Generation (RAG)
Workflows and Agent Orchestration
[Appendix] Free Access to LLMs
giovanni.ciatto@unibo.it
lab-snippets repository
Changes will be published on the News forum
Detailed rules: Exam page on APICe
Detailed rules: Exam page on APICe
Propose your project idea on the Projects forum
[Surname1, Surname2, ...] Project Proposal: <your project name>Any subsequent communication on the project should be done on the same thread
Wait for the teachers to approve your proposal
Create a GitHub repository where to develop your project and post the URL onto your project’s thread
gciattoWork on your project:
master or main branch will be considered for the evaluationlab-snippets)Once done, post a message on your project’s thread to signal the end of the work
Edits may be requested by the teachers, to either the code or the report (or both)
Once the teachers are satisfied, you’ll be requested to present your work to the teachers
async/awaitAll examples (and exercises) of the course live in a single repository: unibo-fc-isi-iag/lab-snippets
Why one repository, rather than copy-pasting code from the slides?
pyproject.toml), and locked (poetry.lock), so that everybody runs the same versions of the same librariesgit pulllab-snippets/
├── data/ # the running example's data
│ ├── __init__.py # helpers to locate data files
│ ├── letter-<CANDIDATE>.txt
│ ├── passport-<CANDIDATE>.png
│ └── transcript-<CANDIDATE>.png
├── snippets/
│ ├── __init__.py # utilities shared across lectures
│ ├── __main__.py # the runner (python -m snippets)
│ └── lecture_<NAME>/ # one package per lecture
│ ├── <UTILITY>.py # utilities shared within the lecture
│ ├── example<ID>/ # one package per example...
│ │ └── <DESCRIPTION>.py
│ └── exercise<ID>/ # ...and per exercise (placeholder)
├── pyproject.toml # dependencies (Poetry)
├── poetry.lock # exact versions of dependencies
└── README.md # suggested order of lectures and exercises
NAME: the lecture’s name, as in the URL of its slides
llmaas, prompting, agents (free-access becomes free_access)ID: the example’s (or exercise’s) number, as in the slides
1, 2, …, or 1bis for “Example 1 (bis)”CANDIDATE: a candidate of the running example
mario-rossi, jean-dupont, mohammed-alisnippets/lecture_prompting/example1bis/Clone the repository, and install its dependencies once, via Poetry (a dependency manager for Python):
git clone -b exercises https://github.com/unibo-fc-isi-iag/lab-snippets.git # the `exercises` branch!
cd lab-snippets
pip install -r requirements.txt # installs Poetry, if missing
poetry install # creates a virtual environment in .venv/, with all dependencies
optionally, fork the repository first, and clone your fork: you will be able to commit (and push) your solutions to the exercises, and still git pull updates from the original repository
the exercises branch has placeholders for exercises; the master branch has their solutions (look at them only after trying!)
Configure the LLM provider via environment variables (cf. Free Access to LLMs):
OPENAI_API_KEY: your API key (if missing, snippets ask for it interactively)OPENAI_BASE_URL (default: https://openrouter.ai/api/v1/), OPENAI_MODEL (default: nvidia/nemotron-3-super-120b-a12b:free, free)export OPENAI_API_KEY="sk-or-v1-..." # on Windows (PowerShell): $env:OPENAI_API_KEY="sk-or-v1-..."
Open the repository’s directory in your IDE (e.g. VS Code), and select the interpreter in .venv/
Always run snippets from the root directory of the repository, via the runner, by lecture NAME and example ID:
poetry run python -m snippets --lecture <NAME> --example <ID> [ARGS]
poetry run python -m snippets -l <NAME> -e <ID> [ARGS] # same, but shorter
poetry run python -m snippets -l prompting -e 1bis data/letter-mario-rossi.txt # e.g.
poetry run: runs the command inside the project’s virtual environment (.venv/), with all dependencies availablepython -m snippets: runs the snippets package as a program (i.e. its __main__.py, the runner)ARGS are passed to the snippet (each example’s slides show which ones it accepts)python path/to/snippet.py: same command for every snippet, no paths to rememberDon’t remember the ID? List the snippets of a lecture (or all of them, omitting -l):
$ poetry run python -m snippets -l llmaas --list
llmaas/example1: Example 1: Sync CLI Chat, with OpenAI's Chat Completions API.
snippets/lecture_llmaas/example1/repl_chat_openai.py
llmaas/example1bis: Example 1 (bis): the same CLI Chat, with Anthropic's Messages API (served by a local Ollama).
snippets/lecture_llmaas/example1bis/repl_chat_anthropic.py
llmaas/example2: Example 2: Async CLI Chat with Streaming, with OpenAI's Chat Completions API.
snippets/lecture_llmaas/example2/repl_chat_openai_async.py
llmaas/exercise1: Exercise 1: Caching Sync Requests.
llmaas/exercise2: Exercise 2: Retry and Exponential Backoff.
If more files match (e.g. an example made of several runnable files), the runner asks which one to run:
$ poetry run python -m snippets -l agents -e 2
# Multiple snippets found, pick one:
# 1) agents/example2/agent_mcp.py: Example 2: Simple Tools as an MCP Server (and an agent using it).
# 2) agents/example2/simple_tools_mcp_server.py: Example 2: Simple Tools as an MCP Server (and an agent using it).
# >
Each exercise has its own placeholder package, e.g. snippets/lecture_llmaas/exercise1/
__init__.py recalls the exercise’s goal: put your solution in the same directory (as one or more .py files)master branchRun your solution like an example, but with --exercise (or -x) instead of --example:
poetry run python -m snippets -l llmaas -x 1 # until you write some code: "Nothing to run in ..."
.py file in the exercise’s directory is runnable, except those starting with _ (e.g. _helpers.py): use them for private helpersStarting from an example? Copy its file into the exercise’s directory, then change the copy
Reuse the code of other snippets via absolute imports (as snippets are regular Python packages):
from snippets.lecture_agents.simple_tools import tools # a lecture-wide utility
from snippets.lecture_prompting.example1bis.letter_scoring_langchain import score_letter # an example
import data # the running example's data
letter = data.letter("mario-rossi").read_text()
if __name__ == "__main__": (others run as soon as they are imported!)Compiled on: 2026-10-22 — printable version