AI learning made simple for students and educators
New here? Try the quickstart notebook in your browser, no install needed.
HandsOnAI is a unified educational toolkit designed to teach students how modern AI systems work, by building and interacting with them directly.
It provides a clean, modular structure that introduces core AI concepts progressively through five modules:
| Module | Purpose | CLI Name |
|---|---|---|
| chat | Chatbot with system prompts, personalities, and memory | chat |
| rag | Retrieval-Augmented Generation (RAG) over your documents | rag |
| agent | Tool use and step-by-step reasoning | agent |
| workflow | Orchestrate multi-step tasks as folders of stages | (library) |
| loop | Repeat a step until a goal is met (incl. the "ratchet" loop) | (library) |
| eval | Evaluate output quality with an LLM judge | (library) |
(A small models utility module handles model detection and capabilities.)
Each module is:
- π Self-contained
- π§© Installable via one package:
pip install hands-on-ai - π§ Designed for progressive learning
hands_on_ai/
βββ chat/ β A simple prompt/response chatbot
βββ rag/ β Ask questions using your own documents
βββ agent/ β Agent reasoning + tools (ReAct-style)
βββ workflow/ β Multi-step tasks as folders of reviewable stages (ICM)
βββ loop/ β Repeat a step until a goal is met (run_loop, run_ratchet)
βββ eval/ β Score output quality with an LLM judge
βββ config.py β Shared config (model, chunk size, paths)
βββ cli.py β Meta CLI (list, config, version)
βββ models.py β Centralized model utilities
βββ utils/ β Shared tools, prompts, paths, etc.
βββ commands/ β Shared CLI commands
Examples and scripts are available in the repository:
hands-on-ai/
βββ examples/ β Example scripts for all modules
βββ scripts/ β Utility scripts for package maintenance
Each tool teaches a different level of modern AI interaction:
- chat β Prompt engineering, roles, and LLMs
- rag β Document search, embeddings, and grounded answers
- agent β Multi-step reasoning, tool use, and planning
- workflow β Orchestration as plain folders of stages you can read and review
- loop β Repetition with a goal: do, check, repeat (the core of agentic loops)
- eval β Judging output quality, the foundation of testing AI systems
Each module is intentionally small and readable: the goal is to make the concept legible, not to be production-grade. Once a concept clicks, students are encouraged to graduate to a more robust, dedicated library (LangChain, LlamaIndex, an agent framework, an eval harness, and so on).
# Install from PyPI
pip install hands-on-ai
# Or directly from GitHub
pip install git+https://github.com/michael-borck/hands-on-ai.git- Python 3.10 or higher
- Any OpenAI-compatible LLM provider (see Provider Compatibility)
Prefer a notebook? Open the starter in Colab:
import os
# Configure your provider
os.environ['HANDS_ON_AI_SERVER'] = 'https://ollama.serveur.au'
os.environ['HANDS_ON_AI_MODEL'] = 'llama3.2'
os.environ['HANDS_ON_AI_API_KEY'] = input('Enter your API key: ')
# Now use HandsOnAI
from hands_on_ai.chat import pirate_bot
print(pirate_bot("What is photosynthesis?"))Run a local Ollama server, then set environment variables and start chatting:
export HANDS_ON_AI_SERVER="http://localhost:11434"
# No API key needed for local Ollamafrom hands_on_ai.chat import pirate_bot
print(pirate_bot("What is photosynthesis?"))For more options:
from hands_on_ai.chat import get_response, friendly_bot, pirate_bot
# Basic usage with default model
response = get_response("Tell me about planets")
print(response)
# Use a personality bot
pirate_response = pirate_bot("Tell me about sailing ships")
print(pirate_response)get_response is stateless: each call is independent. For a multi-turn chat
that remembers what was said, use Conversation:
from hands_on_ai.chat import Conversation
chat = Conversation(system="You are a helpful tutor.")
chat.ask("My name is Sam.")
print(chat.ask("What's my name?")) # -> remembers "Sam"
print(f"Tokens used so far: {chat.total_tokens}")
chat.save("chat.json") # persist and resume later
later = Conversation.load("chat.json")You can also see token usage for a single call with get_response(..., return_usage=True),
which returns a (response, usage) tuple (or chat ask "..." --usage on the CLI).
Stream a response as it is generated:
from hands_on_ai.chat import stream_response
for chunk in stream_response("Tell me a short story"):
print(chunk, end="", flush=True)Want reproducible, free reruns (great for classrooms)? Set HANDS_ON_AI_CACHE to
a directory and identical calls return a cached answer instead of calling the model.
The workflow module models a multi-step task as a plain folder of numbered
stages. Each stage has a CONTEXT.md (its instructions) and an output/
folder. One model runs a stage at a time, writing a readable output.md you can
review (and edit) before moving on, no opaque orchestration framework required.
from hands_on_ai.workflow import init_workspace, Pipeline
# Create a workspace with numbered stage folders
init_workspace("essay", stages=["research", "outline", "draft"])
# -> essay/stages/01_research, 02_outline, 03_draft (each with a CONTEXT.md)
# Edit each stage's CONTEXT.md to describe what it should do, then:
pipe = Pipeline("essay")
pipe.status() # show stages and which are done
pipe.run_next() # run stage 01, write output.md, stop for review
# ...open stages/01_research/output/output.md, edit if needed...
pipe.run_next() # run stage 02 using stage 01's reviewed outputRun one reviewable stage at a time with run_next(), or run_all() once you
trust the pipeline. See the workflow guide for the
full layout (shared CONTEXT.md, references/, and more).
A loop is the same shape, repeated: do something, check whether you're done,
repeat. The loop module gives you two small functions. run_loop keeps
calling a step until a goal is satisfied, and the goal can be the eval LLM
judge:
from hands_on_ai.chat import get_response
from hands_on_ai.loop import run_loop, judged
result = run_loop(
step=lambda draft: get_response(f"Improve this paragraph, keep it short:\n{draft}"),
goal=judged("clear, concise, and friendly", threshold=4), # stop when judge scores >= 4
start="loops are when you do stuff again and again",
max_iters=5,
)
print(result["result"], "in", result["iterations"], "turns")run_ratchet is the "Ralph Wiggum" loop: it keeps a change only when it scores
higher than the best so far, so the result only ever moves forward. See the
loop guide, and the
Build a Ralph Loop project for the
three-file contract, git-as-memory, and backpressure.
HandsOnAI is designed to work with any OpenAI-compatible LLM provider. The system uses standard OpenAI API endpoints (/v1/chat/completions, /v1/models) making it compatible with a wide range of AI services.
Set your provider using environment variables:
# Set your provider's base URL
export HANDS_ON_AI_SERVER="https://your-provider-url"
# Set API key if required
export HANDS_ON_AI_API_KEY="your-api-key"
# Enable debug logging
export HANDS_ON_AI_LOG="debug"export HANDS_ON_AI_SERVER="http://localhost:11434"
# No API key needed for local Ollamaexport HANDS_ON_AI_SERVER="https://api.openai.com"
export HANDS_ON_AI_API_KEY="sk-your-openai-key"export HANDS_ON_AI_SERVER="https://api.together.xyz"
export HANDS_ON_AI_API_KEY="your-together-key"export HANDS_ON_AI_SERVER="https://openrouter.ai/api"
export HANDS_ON_AI_API_KEY="your-openrouter-key"
export HANDS_ON_AI_MODEL="openai/gpt-4o" # or any model they supportexport HANDS_ON_AI_SERVER="https://generativelanguage.googleapis.com/v1beta/openai"
export HANDS_ON_AI_API_KEY="your-gemini-key"
export HANDS_ON_AI_MODEL="gpt-4o-mini" # maps to gemini-1.5-flashexport HANDS_ON_AI_SERVER="https://api.groq.com/openai"
export HANDS_ON_AI_API_KEY="your-groq-key"export HANDS_ON_AI_SERVER="http://localhost:8080"
# API key optional depending on setupHandsOnAI works with any service that implements OpenAI-compatible endpoints:
| Provider | Base URL Example | Authentication | Status |
|---|---|---|---|
| Ollama | http://localhost:11434 |
None (local) | β Tested |
| OpenAI | https://api.openai.com |
Bearer token | β Compatible |
| Google Gemini | https://generativelanguage.googleapis.com/v1beta/openai |
Bearer token | β Compatible |
| Groq | https://api.groq.com/openai |
Bearer token | β Compatible |
| OpenRouter | https://openrouter.ai/api |
Bearer token | β Compatible |
| Together AI | https://api.together.xyz |
Bearer token | β Compatible |
| LocalAI | http://localhost:8080 |
Optional | β Compatible |
| vLLM | http://your-vllm-server |
Optional | β Compatible |
| Hugging Face | https://api-inference.huggingface.co |
Bearer token | β Compatible |
| Any OpenAI-compatible server | http://your-server |
Varies | β Compatible |
Your provider must support:
- β
/v1/chat/completionsendpoint - β
/v1/modelsendpoint - β
OpenAI message format (
{"role": "user", "content": "..."}) - β Bearer token authentication (if API key required)
This provider-agnostic approach offers several educational advantages:
- π No vendor lock-in - Switch providers without code changes
- π Industry standards - Students learn OpenAI API patterns used across the industry
- π§ Real-world skills - Transferable knowledge to other AI tools and platforms
- π‘ Flexibility - Use local models for privacy or cloud models for power
We welcome contributions! See CONTRIBUTING.md for guidelines on how to get involved.
This project is licensed under the MIT License - see the LICENSE file for details.
This package is designed to work seamlessly with Large Language Models for coding assistance and learning:
- LLM.txt - Complete reference guide for LLMs
- Documentation - Full documentation site
- DeepWiki - Interactive code exploration and wiki
- Download the LLM.txt file
- Upload it to your LLM (Claude, ChatGPT, etc.)
- Ask for help with HandsOnAI projects - get complete, working code examples
Example prompts:
- "Create a pirate chatbot using hands-on-ai"
- "Build a document Q&A system with the RAG module"
- "Make an agent that can calculate and search the web"
The LLM will have complete knowledge of the API, examples, and best practices.
- Built with education in mind
- Powered by open-source LLM technology
- Inspired by educators who want to bring AI into the classroom responsibly