Intelligent Agents — Module 2

A.Y. 2026/2027


Prof. Giovanni Ciatto


Compiled on: 2026-10-22 — printable version

Table of Contents

  1. About the course (this page):

  2. Generative AI 101

  3. LLM-as-a-Service

  4. Prompt Engineering & Structured Outputs

  5. Validating Generative Software

  6. Tools and Agents

  7. Retrieval-Augmented Generation (RAG)

  8. Agentic Skills

  9. Workflows and Agent Orchestration

  10. AI Governance 101

  11. [Appendix] Free Access to LLMs

Teachers

Prof. Giovanni Ciatto

Prioritize the General forum

  • All technical question
  • Any other non-personal question

When using the email

  • Include all teachers in CC, always, there including prof. Omicini
  • Clarify the academic year and the name of the course in the subject

Organization of Module 2

  • Lectures in lab, with immediate hands-on
  • Examples and exercises in Python, all in the lab-snippets repository
    • each exercise is followed by a (skippable) walkthrough of its solution: try before looking!
  • Optional project work in any programming language of your choice (cf. exam and rules)

Time—table

Changes will be published on the News forum

Exam

Detailed rules: Exam page on APICe

  • Written test (mandatory, max 27/30): closed-book, open-ended questions, in person on campus, on the university’s computers
  • Project work (optional, max 6/30): assesses the practical knowledge
    • it should cover, or follow from, a specific topic of the course
    • theoretical, technological, or methodological (e.g. an analysis, a technology, a methodology)
  • Final score = written test + project, capped at 30/30
    • after passing the written test (18/30 or higher), tell the teachers whether you:
      1. accept the result, skipping the project (exam finalised), or
      2. continue with the project (finalisation postponed)

Workflow for the project work (pt. 1)

Detailed rules: Exam page on APICe

  1. Propose your project idea on the Projects forum

    • individual, or 1-3 students per group
      • groups must be declared on Virtuale before starting, and stay unchanged until the end
    • open a discussion thread named [Surname1, Surname2, ...] Project Proposal: <your project name>
    • please indicate:
      1. names and email-addresses of the members of the group;
      2. the project vision, i.e. what you want to realise, eliciting the functionalities of the envisioned system;
      3. a motivation of why/how the project is letting you explore the topics of the course (e.g. structured outputs, tools, RAG, skills, orchestration, validation);
      4. which technologies (programming languages, libraries, LLM providers and models) you intend to use and why;
      5. which deliverable(s) you intend to produce (application, library, presentation, etc), aside from the final report;
      6. some strict temporal constraint, if any (e.g., someone in the group is going to graduate soon)
  2. Any subsequent communication on the project should be done on the same thread

    • use the thread as a backlog of any official communication concerning the administration of your project work
    • if you want to talk privately about your project work, mention the URL of your project’s thread in your email
  3. Wait for the teachers to approve your proposal

    • the topic is negotiated, to ensure its focus on Intelligent Agents, and its feasibility in a reasonable time
    • most likely, we’ll try to estimate the effort and ask for changes if too much or too little
    • we’ll also check for overlaps with other groups

Workflow for the project work (pt. 2)

  1. Create a GitHub repository where to develop your project and post the URL onto your project’s thread

    • make all the members of the group collaborators on the repository
    • give full admin rights to the teachers:
  2. Work on your project:

    • commit and push your changes often
      • only the master or main branch will be considered for the evaluation
    • never commit API keys (read them from environment variables, as in lab-snippets)
    • make the project runnable by the teachers: document how to install and run it, and which LLM provider/model it needs
      • prefer free providers or local models (cf. Free Access to LLMs), or let the model be configurable
    • validate your system (cf. Validating Generative Software), and report on how well it works
    • in parallel, or later, write a report about your project following this LaTeX template
  3. Once done, post a message on your project’s thread to signal the end of the work

    • we’ll clone your repository and evaluate the code
  4. Edits may be requested by the teachers, to either the code or the report (or both)

  5. Once the teachers are satisfied, you’ll be requested to present your work to the teachers

    • all members of the group present together
    • about half an hour: presentation + discussion
    • both the software and the report are evaluated

Goals

  • Teach you how to design, develop, and validate LLM-based agentic software, in practice…
  • … via a running example (namely, an assistant for a PhD admission committee)
  • Give you a reference for developing your final project work

Topics

  • Generative AI and LLMs, consumed as a service (APIs, costs, caching, retries)
  • Prompt engineering and structured outputs
  • Validating generative software (testing non-deterministic systems)
  • Tools and agents (function calling, MCP)
  • Retrieval-Augmented Generation (RAG)
  • Agentic skills
  • Workflows and agent orchestration (A2A)
  • AI governance (regulation, model choice, costs)

Prerequisites

  • Basic knowledge of Python (see Python Programming 101 if missing)
    • including type hints, virtual environments, and (ideally) async/await
  • Basic knowledge of Git (see these slides if missing)
  • Basic knowledge of the Linux shell (see these slides if missing)
  • Basic understanding of HTTP APIs and JSON
  • Basic notions of machine learning and neural networks (recalled in Generative AI 101)

Software Requirements

Required

  • A working internet connection
  • A working Python 3.11+ installation, and Git
  • An API key for some LLM provider, or a local LLM runtime (cf. Free Access to LLMs)
  • Visual Studio Code OR PyCharm
  • A decent Unix terminal
    • prefer Git Bash on Windows

The code of examples and exercises: why a repository?

  • All examples (and exercises) of the course live in a single repository: unibo-fc-isi-iag/lab-snippets

    • slides only show excerpts: the repository contains the full, commented, runnable code
  • Why one repository, rather than copy-pasting code from the slides?

    • examples build upon each other: the running example grows across lectures
      • e.g. the letter-scoring system of the prompting lecture is the system under test of the validating lecture
      • e.g. the tools of the agents lecture are shared by several agents, local or via MCP
    • one environment for all lectures: dependencies are declared once (pyproject.toml), and locked (poetry.lock), so that everybody runs the same versions of the same libraries
    • no copy-paste errors: code on the slides is the code in the repository (excerpts are taken from it)
    • versioned: fixes and updates (e.g. for libraries’ breaking changes, or deprecated models) reach you via git pull
    • checked: a CI pipeline verifies that all snippets compile, and can be found by the runner
    • exercises in place: each exercise has its own (empty) package, where your solution can import the examples’ code

The code of examples and exercises: directory structure

lab-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
    • e.g. llmaas, prompting, agents (free-access becomes free_access)
  • ID: the example’s (or exercise’s) number, as in the slides
    • e.g. 1, 2, …, or 1bis for “Example 1 (bis)”
  • CANDIDATE: a candidate of the running example
    • e.g. mario-rossi, jean-dupont, mohammed-ali
  • e.g. “Example 1 (bis)” of the prompting lecture lives in snippets/lecture_prompting/example1bis/
  • the README lists the suggested order of lectures, examples, and exercises (which is not always the order of IDs)

The code of examples and exercises: setup

  1. Requirements: Python 3.11+, and Git

  2. 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!)

  3. 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-..."
    
  4. Open the repository’s directory in your IDE (e.g. VS Code), and select the interpreter in .venv/

The code of examples and exercises: running examples

  • 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 available
    • python -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)
    • prefer the runner to python path/to/snippet.py: same command for every snippet, no paths to remember
  • Don’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).
    # >
    

The code of examples and exercises: working on exercises

  • Each exercise has its own placeholder package, e.g. snippets/lecture_llmaas/exercise1/

    • its __init__.py recalls the exercise’s goal: put your solution in the same directory (as one or more .py files)
    • each exercise’s slides recall where its solution goes, and how to run it
    • each exercise’s slides are followed by a (skippable) walkthrough of its solution, available on the master branch
  • Run 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 ..."
    
    • every .py file in the exercise’s directory is runnable, except those starting with _ (e.g. _helpers.py): use them for private helpers
  • Starting from an example? Copy its file into the exercise’s directory, then change the copy

    • the original remains available, for comparison (and for the others’ exercises, which may build upon it)
  • 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()
    
    • only import modules whose main code is under if __name__ == "__main__": (others run as soon as they are imported!)

Lecture is Over


Compiled on: 2026-10-22 — printable version

back to ToC