AI Application Development Master Class: Six-Week Online Course

A six-week online master class for experienced developers who want to build complete, full-stack AI applications with Docker, Python, Django, React and the OpenAI API. Over 18 hours, students build one application of their own, module by module, while they read how a production platform for LLM applications solves each problem: the openai and langchain packages, Pydantic and JSON, API keys kept secret, Django REST Framework, an OpenAI-compatible prompt handler, conversations and multi-step prompts, tool calls, a system prompt changed for each request, retrieval, streaming, a chat UI of their own built from open-source React components, a single-page chat app, guardrails, testing without a paid LLM, and shipping it all in containers. Each topic is a one-hour module: a 30-minute recorded lecture, and a lab done together in the week’s live session. Week 1 covers the foundations, week 2 talking to the LLM, week 3 the back end, week 4 making the LLM do things, week 5 the front end, and week 6 production. See Course Formats and Grading for the grading scale and the course policies, and AI Application Development Master Class for an overview of the subject.

At a Glance

Students

Professional developers who build web applications and want to build AI applications: back-end, front-end and full-stack engineers, and technical leads.

Format

Each week, three 30-minute recorded lectures (1 hour 30) and one 90-minute live session for the week’s three labs: 3 hours of instruction a week, 18 in all.

Deployment

On each student’s computer, under Docker Compose, working before the first live session: Smarter from source, for reading and comparing, and the course app, which students start in Module 1.

Models

An OpenAI model for chat and tool calls, and an OpenAI embedding model for retrieval. Smarter’s LLMClients use the same key.

Workload

3 hours of instruction and 3 to 5 hours of assignments each week, and 10 to 15 hours for the capstone, in week 6 and the week after.

Cost

Free software, plus an OpenAI API key with a few dollars of credit: about $5 to $10 per student for the course and the capstone. Cap each student’s spending with a Budget on a shared Smarter Provider, or with a project budget in the OpenAI dashboard.

Learning Objectives

By the end of the course, students can:

  1. Explain the architecture of a full-stack AI application in MVC and MVT terms, and place its LLM, prompt handler and chat window in it.

  2. Call LLMs with the openai package, validate their JSON with Pydantic, and use the langchain packages for embeddings and retrieval.

  3. Keep API keys and other sensitive data out of source code, Git history, logs, images and browsers.

  4. Build an OpenAI-compatible prompt API with Django REST Framework that handles conversation history, multi-step prompts and tool calls.

  5. Shape an LLM’s behavior for each request by mutating the system prompt, adding retrieved context and applying guardrails, and defend the application against prompt injection.

  6. Build a single-page React chat app of their own from @chatscope/chat-ui-kit-react and @microlink/react-json-view, against a typed contract with the back end, with streamed progress.

  7. Test an AI application without calling a paid LLM, and run the whole stack with Docker Compose.

Technical Prerequisites

  • Required: professional experience with at least one programming language, such as Python, Visual Basic, C, Rust or Go. Students who know another language well can catch up on Python with the official tutorial in week 1.

  • Required: building complete web sites with HTML, CSS and JavaScript, and familiarity with a JavaScript framework, such as React, Vue or Angular.

  • Required: proficiency with REST APIs and JSON: methods, status codes, headers and authentication, with curl or a client such as Postman.

  • Required: working in a terminal with common Linux commands, and Git and GitHub: clone, branch, commit, push and pull requests.

  • Required: the basics of relational databases and SQL.

  • Required by the first live session: Docker Desktop or Docker Engine with Compose, Node.js LTS, Python 3.12 or later, a code editor, at least 16 GB of memory, and Smarter running from source as described in Developer Setup Guide.

  • Required by the first live session: an OpenAI API key with credit, set in Smarter’s .env, and a prompt answered in Smarter’s LLMClient workbench.

  • Helpful: reading TypeScript, Django, and the Model-View-Controller pattern. Students new to Docker can take Docker for Software Development first.

  • Not needed: machine learning, statistics or experience with LLMs beyond using a chat assistant.

Schedule

Students watch the week’s three lectures before its live session. Each module is described under The Modules.

Week

Lectures

Assignment

1

Modules 1 to 3: the full-stack AI application; Python for AI applications; React for AI applications.

Assignment 1: The Course App, Running.

2

Modules 4 to 6: the openai package; Pydantic and JSON; keeping secrets secret.

Assignment 2: A Typed, Secret-Safe LLM Call.

3

Modules 7 to 9: Django REST Framework for LLM APIs; an OpenAI-compatible prompt handler; conversations and multi-step prompts.

Assignment 3: An OpenAI-Compatible Endpoint.

4

Modules 10 to 12: tool calls; mutating the system prompt; retrieval with LangChain.

Assignment 4: Tools, Context and Retrieval.

5

Modules 13 to 15: streaming and progress; building a chat UI with chatscope; a single-page chat web app.

Assignment 5: The Single-Page Chat App. Capstone choice.

6

Modules 16 to 18: guardrails, prompt injection and cost; testing AI applications; shipping the whole stack.

Capstone, due one week after the last live session.

Live Sessions

This course’s live sessions differ from the standard session in Course Formats and Grading, because they hold the labs:

  • 15 minutes: questions on the week’s lectures, and common problems seen in the previous week’s assignment.

  • 75 minutes: the week’s three labs, 25 minutes each. The instructor demonstrates the start of each lab, then students work in pairs in breakout rooms, sharing one screen.

Students finish any lab that runs over on their own, before starting the week’s assignment. Assignments are due before the next live session.

The Modules

Each module is a 30-minute lecture followed by a lab of about 30 minutes. Labs build one application through the whole course, the course app: a Django and Django REST Framework back end, a React single-page chat app built with Vite, and Redis, in one Docker Compose project in the student’s own Git repository. Smarter runs beside it, from source, as the reference implementation that each lecture reads from and each lab compares against. The file references are listed in full under The Modules in the Source Code.

#

Lecture

Lab

1

The full-stack AI application. MVC and Django’s MVT, and where an AI application’s parts fit into them: the model is the conversation and its configuration, the view is a prompt handler that calls the LLM, and the template is often a React app that Django hosts. One chat message’s path through Smarter, from the chat window through the prompt API, the handler and the provider, and back. The course’s stack in Docker Compose. ADR 001, ADR 002, ADR 019.

Send a prompt in Smarter’s LLMClient workbench and follow it in the Console’s tabs. Then scaffold the course app: a Django project and a Vite React app, each with a Dockerfile, in one Compose file with Redis, modeled on Smarter’s.

2

Python for AI applications. The Python an AI back end leans on: type hints and type aliases, enums, dataclasses against Pydantic models, decorators, context managers, generators, async and await, and evaluating untrusted input safely, as Smarter’s calculator tool does with ast instead of eval().

Write a typed, documented function with an enum argument and a Pydantic return value, with unit tests, in the course app. It becomes a tool in Module 10.

3

React for AI applications. Components, props and state; useState, useEffect, useRef and custom hooks; controlled inputs; fetch with async handlers and AbortController; rendering a list of messages; error boundaries; and Vite builds. Read from Smarter Chat, the React app that Smarter’s LLMClient workbench is built on.

Build a chat page in the course app’s React app: an input, a send button, and a message list that renders an echo endpoint’s replies, with a loading state and an error state.

4

The openai package. The OpenAI client and its settings (api_key, base_url, timeouts and retries); chat.completions.create(); roles and messages; models, temperature and maximum tokens; usage and finish reasons; the SDK’s typed responses; its exception classes, mapped to HTTP status codes; and the Chat Completions API against the Responses API.

Write a Django management command that sends a prompt with the openai package, prints the reply and its token usage, and turns a rate limit, a bad key and a timeout into clear messages.

5

Pydantic and JSON. Every object the openai package returns is a Pydantic model: model_dump(), model_dump_json() and model_validate(). Asking an LLM for JSON with JSON mode and with structured outputs; validating what comes back, with extra="forbid" and pydantic.JsonValue; JSON Schema from Pydantic models; and Pydantic as the contract of an API. ADR 031.

Ask the LLM to classify a support message into a Pydantic model with an enum field, using structured outputs. Feed it a reply that does not validate, and handle the failure.

6

Keeping secrets secret. Where API keys leak: source code, Git history, logs, error pages, front-end bundles and LLM responses. Twelve-factor configuration; pydantic-settings with SecretStr; placeholders that count as missing; Fernet encryption at rest; masking keys in logs; and a proxy that adds the provider’s key on the server, so that it never reaches the browser. ADR 007, ADR 016.

Move the course app’s OpenAI key into a SecretStr setting read from .env, add .env to .gitignore, mask the key in a log line, and show with git log -p and the browser’s network tab that it is nowhere else.

7

Django REST Framework for LLM APIs. API views and viewsets; authentication classes and hashed API keys; parsers; Pydantic models in place of serializers for the request and the response; one error envelope with matching status codes; and CORS and CSRF for a chat window on another origin. ADR 006, ADR 023.

Add a /api/v1/chat/ endpoint to the course app that accepts a list of messages, validates it with a Pydantic model, calls the LLM, and returns the reply, authenticated by an API key, with tests.

8

An OpenAI-compatible prompt handler. Why a back end should speak the OpenAI API: every OpenAI SDK, and most other providers’ APIs, can then use it by changing one base_url. The request and response of /v1/chat/completions; one handler class for many providers; a passthrough endpoint; and Smarter’s Proxy, which forwards any request, streams included, with the provider’s key added.

Add /v1/chat/completions to the course app, and call it with the official openai package from a script, changing only base_url and api_key.

9

Conversations and multi-step prompts. The API is stateless, so the conversation is the message list. Storing history; counting tokens with tiktoken and trimming to a budget; summarizing old turns; a prompt that takes two or more LLM requests; and orchestrating several LLM calls: sequential hand-offs, routing by intent, and a supervisor with workers.

Store the course app’s conversations in a Django model, send the history with each prompt, and trim it to a token budget. Add a two-step prompt that drafts a reply and then checks it.

10

Tool calls. Tool definitions in JSON Schema; tool_choice; the tool call loop: the LLM asks, the application runs the function, appends a tool message for each call, and asks again; parallel tool calls; pairing each tool message with its call; and treating tool arguments as untrusted input. ADR 008.

Turn the Module 2 function into a tool, run the loop in the course app’s handler, and log each call and its result.

11

Mutating the system prompt. The system prompt as configuration; adding instructions, context and rules for one request; changing the copy that is sent rather than the history that is saved; plugins that add to the system prompt when a prompt matches them; MCP servers’ instructions; and why text from users and tools must never be trusted as instructions.

Add the signed-in user’s name, the date and a formatting rule to the system prompt of each request in the course app, and prove with a test that none of it is saved in the history.

12

Retrieval-augmented generation with LangChain. Embeddings and similarity; splitting documents with langchain-text-splitters; OpenAIEmbeddings from langchain-openai; vector stores behind one interface; and adding what was retrieved to the system prompt, with its sources.

Index a folder of Markdown files with LangChain, retrieve the best passages for each prompt, add them to the system prompt, and have the reply cite its sources.

13

Streaming and progress. Server-Sent Events; stream=True in the openai package and its chunks; async Django views and StreamingHttpResponse; streaming a prompt’s progress (LLM requests, tool calls, plugin calls) while it runs; and reading a stream in the browser.

Stream the course app’s replies to its chat page as they are generated, with a progress line for each tool call.

14

Building a chat UI with chatscope. A chat window from open-source parts: @chatscope/chat-ui-kit-react’s MainContainer, ChatContainer, ConversationHeader, MessageList, Message, MessageInput and TypingIndicator; theming @chatscope/chat-ui-kit-styles with CSS variables; the LLM’s Markdown rendered with marked and sanitized with DOMPurify, because the messages are HTML; and a developer panel that shows each request, response and tool call with @microlink/react-json-view. Smarter Chat, which is built from exactly these packages, is the case study.

Replace the Module 3 chat page with one built from chatscope’s components, with Markdown replies and a collapsible JSON panel that shows the last request and response.

15

A single-page chat web app. Single-page app design; one typed contract between the front end and the back end, from Pydantic models to JSON Schema to generated TypeScript types; configuration loaded at startup; sessions, cookies and API keys in a browser; and building and serving the app’s static assets.

Generate TypeScript types from the course app’s JSON Schema, use them in its React app, and add a test that fails when the back end’s contract changes.

16

Guardrails, prompt injection and cost. Input and output guardrails; regular expressions, detectors for personal data and secrets, moderation, and an LLM judge; direct and indirect prompt injection; token budgets and charges per user; and what a guardrail should do when it fails.

Add an input guardrail to the course app that blocks a prompt containing an API key or a credit card number, and a daily token budget per user.

17

Testing AI applications. Never calling a paid LLM in a unit test: fake transports, recorded responses and streams, and mocked SDK clients. Testing the tool call loop and the system prompt; Mock Service Worker in the React app; and evaluation sets for the prompts themselves.

Test the course app’s tool call loop and stream with recorded responses and no network, then write five evaluation cases for its system prompt.

18

Shipping the whole stack. Multi-stage Dockerfiles, a non-root user and a small image; settings per environment; the React build collected as static files; health checks; Compose for development and for a server; and Smarter’s Docker quickstart. ADR 002, ADR 004.

Build production images of the course app, run the whole stack with docker compose up from a clean clone, and write its README’s quickstart.

The Modules in the Source Code

Every lecture, lab and assignment in this course rests on the files and commits below. Every reference is to the main branch of a public repository in github.com/smarter-sh, so that students can open each one during class. Python paths in the smarter repository are under smarter/smarter/.

Foundations

Module

What to read

Where to look

  1. The full-stack AI application

Django’s views and templates host each web console page, a template tag renders a React app’s Vite build into it, and the chat window calls the prompt API that the prompt handler answers. Six services in one Compose file, four of them from one image. ADR 001, ADR 002, ADR 019.

apps/prompt/views/detailviews/prompt_config_view.py, lib/django/templatetags/smarter_react_templatetag_manager.py, smarter-chat, docker-compose.yml, Dockerfile

  1. Python for AI applications

A calculator tool that parses an expression with ast and evaluates only the nodes it allows; a date calculator; a weather function with enums and Pydantic models; and the project’s type aliases.

apps/prompt/functions/calculator.py, apps/prompt/functions/date_calculator.py, apps/prompt/functions/function_weather/, lib/typing.py

  1. React for AI applications

Smarter Chat’s main component, its API calls, its message helpers and its error boundary.

smarter-chat: src/components/SmarterChat/Component.tsx, src/lib/api.ts, src/lib/messages.ts, src/components/ErrorBoundary/Component.tsx

Talking to the LLM

Module

What to read

Where to look

  1. The openai package

A passthrough client that adapts a request to the provider’s current API (OpenAI’s reasoning models refuse max_tokens), the SDK’s exceptions mapped to HTTP status codes, and a small client that calls embeddings, moderation and chat completions.

apps/provider/clients.py, apps/provider/services/text_completion/lib/exception_map.py, apps/guardrail/services/strategies/clients.py

  1. Pydantic and JSON

An LLM judge that asks for JSON mode and parses the verdict. The chat API’s contract: Pydantic models with no dict[str, Any], extra="forbid" on models of ORM rows, a committed JSON Schema and a test that it is current. ADR 031. Commit 0630dcb55 made the contract explicit, and its body lists what the implicit one had allowed.

apps/guardrail/services/strategies/llm_judge_strategy.py, apps/prompt/contract/ (models.py, schema.py, chat-contract.schema.json)

  1. Keeping secrets secret

Settings typed as SecretStr; is_missing_value(), which treats SET-ME-PLEASE placeholders as unset; Secrets encrypted with Fernet; and a Proxy that adds the provider’s key on the server. Commit 990cab95b found that a placeholder key was stored and sent as a real one. Commit 07a42204c built the Proxy. ADR 007, ADR 016.

common/conf/settings.py, common/conf/env.py, .env.example, apps/secret/models.py, apps/proxy/services.py

The Back End

Module

What to read

Where to look

  1. Django REST Framework for LLM APIs

A TokenAuthentication subclass for hashed API keys, a YAML parser, a base view for APIs that browsers and API clients both call without a login redirect, and the LLMClient chat API views. ADR 006, ADR 023.

lib/drf/token_authentication.py, lib/drf/parsers.py, lib/django/views.py, apps/llmclient/api/v1/views/base.py

  1. An OpenAI-compatible prompt handler

A base class for chat providers, one OpenAI-compatible handler that every provider extends, the protocols that describe them, a passthrough view, and the Proxy, which forwards any request with the provider’s key added.

apps/provider/services/text_completion/lib/ (chat_provider_base.py, openai_compatible_chat_provider.py, protocols.py), apps/provider/services/text_completion/providers.py, apps/prompt/api/v1/views/passthrough.py, apps/proxy/

  1. Conversations and multi-step prompts

Prompts and their history saved by a mixin of the handler, a first request and a second request after tool calls, and the Orchestrator’s runs and steps for workflows of several LLMClients.

apps/prompt/models/ (prompt.py, prompt_history.py), apps/provider/services/text_completion/lib/mixins.py, openai_compatible_chat_provider.py (prep_first_request(), prep_second_request()), apps/orchestrator/models/, Smarter Orchestrator

Making the LLM Do Things

Module

What to read

Where to look

  1. Tool calls

Tool factories that return JSON Schema definitions, the handler’s tool call loop, and plugins whose tools query a database or an API. Commit 5a378b47f (item 7) fixed the OpenAI 400 error for a tool message without its call, and commit 533f0c54c fixed SQL injection through a tool’s arguments. ADR 008.

apps/prompt/functions/, openai_compatible_chat_provider.py (process_tool_call(), handle_function_provided()), apps/provider/services/text_completion/utils.py (pair_tool_messages()), apps/plugin/plugin/sql.py, apps/plugin/plugin/api.py

  1. Mutating the system prompt

A plugin that adds its own system prompt when it is selected; a Markdown note added to the copy of the messages that is sent, never to the saved history; and MCP servers’ instructions. Commit c5d2cae9f explains why the note goes into the copy. Commit 86c0ecdc9 added MCP servers.

apps/plugin/plugin/base.py (customize_prompt()), openai_compatible_chat_provider.py (openai_messages, add_markdown_system_prompt(), handle_plugin_selected(), handle_mcp_clients()), apps/mcpclient/toolkit.py

  1. Retrieval-augmented generation

Embeddings from langchain-openai, documents split with langchain-text-splitters, and Pinecone and Qdrant behind one backend interface.

apps/vectorstore/service.py, apps/vectorstore/embeddings.py, apps/vectorstore/backends/, apps/provider/services/embedding.py, Smarter Vectorstore

The Front End

Module

What to read

Where to look

  1. Streaming and progress

A prompt runs in a worker thread while an async generator streams its progress as Server-Sent Events, forwarded from signals; the Proxy streams a provider’s own SSE response; and the chat component reads a stream.

apps/prompt/progress.py, apps/prompt/tests/test_progress.py, apps/proxy/tests/data/openai-stream.txt, smarter-chat: src/lib/logStream.ts

  1. Building a chat UI with chatscope

Smarter Chat is built from three open-source packages. Its main component composes chatscope’s containers, message list, input and typing indicator. Its styles override chatscope’s theme with CSS variables. Its message helpers escape the user’s text and render the LLM’s Markdown with marked and DOMPurify, and its Console shows requests and responses with react-json-view. Commit afb1b5e63 applied an LLMClient’s colors to chatscope’s components.

smarter-chat: package.json, src/components/SmarterChat/ (Component.tsx, styles.css), src/lib/styles.ts, src/lib/messages.ts, src/components/Console/Component.tsx

  1. A single-page chat web app

The contract’s JSON Schema, copied into the front end’s repository and compiled into TypeScript types, and the make target that ties them together. Commit cd61f2ee7 is the front end’s half of commit 0630dcb55.

smarter-chat: contract/chat-contract.schema.json, scripts/contract-types.mjs, src/contract.gen.ts; Makefile (chat-contract)

Production

Module

What to read

Where to look

  1. Guardrails, prompt injection and cost

A guardrail pipeline run in priority order before and after the LLM, detectors for personal data and secrets, an LLM judge that treats the judged text as untrusted data, and budgets that refuse a prompt once they are spent. Commit 329527e8e added guardrails, and commit ab24142d2 budgets.

apps/guardrail/services/ (pipeline.py, strategies/detectors.py, strategies/llm_judge_strategy.py), Smarter Guardrail, Smarter Budget

  1. Testing AI applications

Tests of the prompt handler, a Proxy test base class that installs a fake provider, recorded responses and streams, and Mock Service Worker handlers for Smarter Chat. ADR 013.

apps/provider/tests/test_openai_compatible_chat_provider_units.py, apps/proxy/tests/base_classes.py, apps/proxy/tests/data/, smarter-chat: src/mocks/handlers.ts

  1. Shipping the whole stack

A multi-stage Dockerfile that ends as a non-root user, a Compose file with health checks, a Makefile that builds and runs it, and the Docker quickstart for running Smarter from published images. ADR 002, ADR 004.

Dockerfile, docker-compose.yml, Makefile, smarter-deploy: docker-compose.yml

Grading

Component

Weight

Graded assignments

50% (5 × 10%)

Participation (live sessions and labs)

10%

Capstone project

40%

The grading scale, the rubric levels and the course policies are those of Course Formats and Grading.

Graded Assignments

Each assignment builds on the week’s labs, in the student’s course app repository, and is submitted as a pull request against their own repository.

Assignment 1: The Course App, Running

Students finish the course app’s skeleton: a Django project and a Vite React app in one Compose file with Redis, the Module 2 function with tests, and the Module 3 chat page talking to an echo endpoint. A one-page note traces one chat message through Smarter, from the chat window to the provider and back, naming the file of each step.

Turn in: the pull request, and the one-page trace.

Criterion

Pts

Exemplary

Developing

Stack

6

docker compose up starts all three services from a clean clone.

Needs manual steps.

Python and React

6

A typed, tested function, and a chat page with loading and error states.

Untyped, or no error state.

Trace

8

Every step, in order, with the file, checked against the workbench’s Console.

Guessed from file names.

Assignment 2: A Typed, Secret-Safe LLM Call

Students finish the Module 5 classifier as a Django service function that returns a Pydantic model, with the API key in a SecretStr setting, and the SDK’s errors turned into clear messages. A paragraph compares their key handling with Smarter’s settings.py and secret/models.py, and says what commit 990cab95b would have caught in their own code.

Turn in: the pull request, with tests that use a fake client, and the paragraph.

Criterion

Pts

Exemplary

Developing

Structured output

6

Validated into a strict model, with invalid output handled.

json.loads() and hope.

Secrets

6

SecretStr from the environment, masked in logs, absent from Git history.

Key in source, or printed in a log.

Errors

4

Rate limits, bad keys and timeouts each give a clear message.

A stack trace.

Tests

4

A fake client; no network.

Tests that call the paid API.

Assignment 3: An OpenAI-Compatible Endpoint

Students finish the course app’s /v1/chat/completions endpoint in Django REST Framework, authenticated by API key, with conversations stored in a model and trimmed to a token budget, and a two-step prompt that drafts a reply and then checks it. They call it from a script with the official openai package, changing only base_url and api_key.

Turn in: the pull request, with tests, and the script.

Criterion

Pts

Exemplary

Developing

Compatibility

6

The official SDK works against it unchanged, errors included.

A custom request shape.

History

6

Stored, sent, and trimmed by counted tokens.

Trimmed by message count, or not at all.

Multi-step prompt

4

Two requests, the second checking the first, both logged.

One request that asks for both.

Tests

4

Authentication refused and accepted, with a fake LLM.

None.

Assignment 4: Tools, Context and Retrieval

Students add two tools in a working tool call loop, a system prompt changed for each request but never saved, and retrieval over a folder of documents with LangChain, with sources cited in the reply.

Turn in: the pull request, with tests.

Criterion

Pts

Exemplary

Developing

Tool call loop

8

Every call answered by a paired tool message, arguments validated, then asked again.

One tool call, or arguments trusted.

System prompt and history

6

Mutated copy sent, saved history unchanged, with a test that proves it.

The mutation saved into history.

Retrieval

6

Split, embedded and retrieved, with cited sources.

Whole documents pasted into the prompt.

Assignment 5: The Single-Page Chat App

Students put a React single-page app of their own in front of the course app’s endpoint, built from @chatscope/chat-ui-kit-react with a @microlink/react-json-view panel that shows each request and tool call, TypeScript types generated from the back end’s JSON Schema, and streamed progress for each LLM request and tool call.

Turn in: the pull request, and a two-minute screen recording of a streamed reply that uses a tool.

Criterion

Pts

Exemplary

Developing

Contract

7

Generated types, with a test that fails when the back end changes.

Hand-written types.

Streaming

7

Progress and reply streamed, errors shown in the thread.

The page waits for the whole reply.

Rendering

6

Markdown rendered and sanitized; no key in the bundle.

Raw HTML from the LLM inserted into the page.

Capstone Projects

Students choose one in week 5, work on it in week 6, and submit it one week after the last live session.

Capstone A: A Complete AI Application

Learning objective. Build, test and ship a complete full-stack AI application that solves a real problem, using the patterns that Smarter uses in production.

Students finish the course app as an assistant for a domain of their choice, for example a course’s teaching assistant, a help desk for an invented product, or a recipe planner. Its back end is Django and Django REST Framework, with an OpenAI-compatible /v1/chat/completions endpoint, conversation history trimmed to a token budget, at least two tools, a system prompt changed for each request without being saved, retrieval over a folder of documents with LangChain, and an input guardrail. Its front end is a React single-page app of their own, built from @chatscope/chat-ui-kit-react and @chatscope/chat-ui-kit-styles, with a @microlink/react-json-view panel for requests and tool calls, against TypeScript types generated from the back end’s JSON Schema, with streamed progress. The API key is a SecretStr setting, and the whole stack runs with docker compose up from a clean clone.

What students turn in:

  • The application’s Git repository, with a README quickstart, unit tests that never call the LLM, and at least ten evaluation cases for its prompts.

  • A one-page design note: the system prompt and what is added to it per request, the tools and how their arguments are checked, and where the API key goes and where it never goes.

  • A six-minute recorded walkthrough, from docker compose up to a conversation that uses a tool and a retrieved document.

How it is graded (100 points):

Criterion

Pts

Exemplary

Developing

Prompt handler

25

OpenAI-compatible, with history, a token budget, a working tool call loop and a system prompt that is changed per request but never saved.

Sends one prompt at a time, or saves the mutated system prompt.

Retrieval and guardrails

15

Relevant passages, cited in replies; the guardrail blocks with a clear message, and its failure is handled.

Whole documents pasted into the prompt.

Front end and contract

20

Generated types, streamed progress, Markdown rendered safely, errors shown.

Hand-written types that drift from the back end.

Secrets and shipping

15

No key in the repository, the image, the logs or the browser; runs from a clean clone.

Key in settings.py, or “works on my machine”.

Tests and evaluation

15

Unit tests with a fake LLM, and evaluation cases whose results are reported honestly.

Tests that call the paid API, or no evaluation.

Walkthrough

10

Clear and on time.

Overruns, or the stack does not start.

Capstone B: A New Built-in Tool for Smarter

Learning objective. Add an AI feature to a large production codebase, end to end, following the patterns that the codebase already uses.

Students add a built-in function to their fork of Smarter, beside the calculator, the date calculator and the weather function: a tool factory that returns its JSON Schema definition, the function itself, with its arguments validated as untrusted input, its registration in LLMClientFunctions and in the handler, a system prompt note that tells the LLM when to use it, and tests that run the tool call loop with a fake LLM. An LLMClient manifest uses the function, and a Vite single-page app of their own, built from @chatscope/chat-ui-kit-react, talks to that LLMClient’s prompt API. A capstone that proposes its function to Smarter itself must be agreed with the maintainers in a GitHub issue first.

What students turn in:

  • A pull request against their fork, with the pre-commit hooks passing and at least 90% coverage of the new code.

  • The LLMClient manifest, and the single-page app’s repository.

  • A one-page design note: the tool’s definition, how its arguments are checked, and what the LLM does with a malformed call.

  • A six-minute recorded walkthrough.

How it is graded (100 points):

Criterion

Pts

Exemplary

Developing

Tool definition

15

Precise names, descriptions and JSON Schema; the LLM calls it when it should.

Vague descriptions, so the LLM never calls it.

Implementation

25

Arguments validated, errors returned to the LLM as results, registered where the other functions are.

Trusts the arguments, or raises into the handler.

Tests

20

Tool call loop tested with a fake LLM, including a malformed call; 90% coverage.

Tests that call the paid API.

Front end

15

The app talks to the LLMClient, and its JSON panel shows the tool call.

The workbench only.

Design note and quality

15

Hooks pass, docstrings, code that reads like the code around it, and a clear note.

Hooks skipped.

Walkthrough

10

Clear and on time.

Overruns.

Further Study