๐๏ธ Architecture Documentation - AI Quiz Bot
System design, API specifications, and data structures.
๐ Table of Contents
- System Architecture
- API Specifications
- Data Structures
- Request/Response Examples
- Error Handling
- Scalability Considerations
๐๏ธ System Architecture
High-Level Overview
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Telegram Users โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Telegram Bot API (python-telegram-bot) โ
โ - Polling for messages โ
โ - Sending polls and messages โ
โ - File download capability โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ AI Quiz Bot (bot.py) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Message Handlers โ โ
โ โ - handle_file() [Images/PDFs] โ โ
โ โ - handle_text() [Text messages] โ โ
โ โ - Commands (/start, /help) โ โ
โ โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Processing Layer โ โ
โ โ - extract_text_from_image() โ โ
โ โ - extract_text_from_pdf() โ โ
โ โ - parse_num_questions() โ โ
โ โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Generation Layer โ โ
โ โ - generate_quiz_questions() โ โ
โ โ - Quiz JSON parsing โ โ
โ โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Delivery Layer โ โ
โ โ - send_quiz_poll() โ โ
โ โ - Completion messages โ โ
โ โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโดโโโโโโโโโโโ
โ โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Ollama Cloud API โ โ External APIs โ
โ (REST HTTP/HTTPS)โ โ Telegram API โ
โ โ โ โ
โ - /api/chat โ โ (https) โ
โ Vision Model โ โ โ
โ Chat Model โ โ โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
Component Layers
1. Telegram Integration Layer
- Receives messages via polling
- Handles file downloads
- Sends messages and polls
- Manages message edits
2. Message Handler Layer
- Routes incoming messages to appropriate handlers
- Validates message type (file vs text vs command)
- Extracts metadata (chat ID, user ID, caption, etc.)
- Shows loading messages
3. Processing Layer
- Extracts text from images (Vision API)
- Extracts text from PDFs (PyPDF2)
- Parses user input (question count)
- Handles errors and retries
4. Generation Layer
- Sends text to Chat API
- Manages prompts and system instructions
- Parses JSON responses
- Validates question format
5. Delivery Layer
- Sends each question as a Telegram poll
- Manages rate limiting (1-second delays)
- Sends completion messages
- Logs delivery success/failure
๐ API Specifications
Ollama Cloud API
Base URL: https://ollama.com
Authentication: Bearer Token (in Authorization header)
Endpoint: POST /api/chat
Purpose: Vision analysis, chat completion, JSON generation
Request Headers:
Authorization: Bearer {OLLAMA_API_KEY}
Content-Type: application/json
Request Body:
{
"model": "ministral-3:8b",
"messages": [
{
"role": "user",
"content": "Text or prompt",
"images": ["base64_encoded_image"] // Optional
}
],
"stream": false,
"format": "json" // Optional, ensures JSON output
}
Response:
{
"model": "ministral-3:8b",
"created_at": "2026-02-01T10:00:00Z",
"message": {
"role": "assistant",
"content": "Response text or JSON"
},
"done": true,
"total_duration": 1234567,
"load_duration": 567,
"prompt_eval_count": 50,
"eval_count": 100,
"eval_duration": 100000
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Yes | Model name (e.g., "ministral-3:14b") |
| messages | array | Yes | Array of message objects |
| stream | boolean | No | Default: false (we don't use streaming) |
| format | string | No | "json" to ensure JSON output |
Message Object:
{
"role": "user" | "assistant" | "system",
"content": "Text content",
"images": ["base64_string"] // Optional, only for vision models
}
Telegram Bot API
Used Methods:
1. send_poll
await bot.send_poll(
chat_id=12345,
question="Question text?",
options=["A", "B", "C", "D"],
type="quiz",
correct_option_id=1,
is_anonymous=False
)
2. send_message
await bot.send_message(
chat_id=12345,
text="Message text",
reply_to_message_id=67890
)
3. edit_message_text
await message.edit_text("Updated text")
4. get_file
file = await bot.get_file(file_id)
file_bytes = await file.download_as_bytearray()
๐ Data Structures
Question Object (JSON)
{
"question": "What is the capital of France?",
"options": ["London", "Berlin", "Paris", "Madrid"],
"correct_option_id": 2
}
Fields:
question(string): Question text (max 300 chars in Telegram)options(array): Array of option strings (max 10 options in Telegram)correct_option_id(number): Index of correct option (0-based)
Questions Array
[
{
"question": "Question 1?",
"options": ["A", "B", "C", "D"],
"correct_option_id": 0
},
{
"question": "Question 2?",
"options": ["A", "B", "C", "D"],
"correct_option_id": 1
}
]
Image Object
Format: Base64-encoded JPEG/PNG/WebP
Usage in API:
{
"role": "user",
"content": "Describe this image",
"images": ["iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="]
}
๐ค Request/Response Examples
Example 1: Image Analysis
User Action: Send image with caption "5"
Request to Ollama:
POST https://ollama.com/api/chat
Authorization: Bearer 830f2fd6fedc491ba49c91a0709c94d9.4Z84I1pRSwd8iPDT1J4Tfu6c
Content-Type: application/json
{
"model": "ministral-3:8b",
"messages": [
{
"role": "user",
"content": "Just transcribe all the text in the image in details. Output only the text, nothing else.",
"images": ["base64_encoded_image_here..."]
}
],
"stream": false
}
Response from Ollama:
{
"message": {
"role": "assistant",
"content": "The extracted text from the image goes here...\nThis is the text the bot analyzed."
},
"done": true
}
Bot Processing:
- Receives extracted text
- Parses caption: 5 questions
- Calls quiz generation
Example 2: Quiz Generation
Request to Ollama:
POST https://ollama.com/api/chat
Authorization: Bearer 830f2fd6fedc491ba49c91a0709c94d9.4Z84I1pRSwd8iPDT1J4Tfu6c
Content-Type: application/json
{
"model": "ministral-3:14b",
"messages": [
{
"role": "system",
"content": "You are a strict Quiz Generator.\nRules:\n1. Output ONLY the questions and answers.\n2. Do NOT provide any conversational text..."
},
{
"role": "user",
"content": "Based on the text I provide, generate 5 multiple-choice quiz questions.\n\nYou must output valid JSON only...\n\nText to analyze:\nThe extracted text goes here..."
}
],
"stream": false,
"format": "json"
}
Response from Ollama:
{
"message": {
"role": "assistant",
"content": "[{\"question\":\"Q1?\",\"options\":[\"A\",\"B\",\"C\",\"D\"],\"correct_option_id\":0},{\"question\":\"Q2?\",\"options\":[\"A\",\"B\",\"C\",\"D\"],\"correct_option_id\":1}]"
},
"done": true
}
Bot Processing:
- Receives JSON string
- Cleans markdown if present
- Parses JSON
- Validates each question
- Sends as Telegram polls
Example 3: Send Quiz Poll to Telegram
Request to Telegram:
POST https://api.telegram.org/bot7319290683:AAFpfIEGCZGOky3OOB2V-xcn8rq8pc7TEUQ/sendPoll
Content-Type: application/json
{
"chat_id": 123456789,
"question": "What is the capital of France?",
"options": ["London", "Berlin", "Paris", "Madrid"],
"type": "quiz",
"correct_option_id": 2,
"is_anonymous": false
}
Response from Telegram:
{
"ok": true,
"result": {
"message_id": 12345,
"chat": {"id": 123456789},
"poll": {
"id": "poll123",
"question": "What is the capital of France?",
"options": [
{"text": "London", "voter_count": 0},
{"text": "Berlin", "voter_count": 0},
{"text": "Paris", "voter_count": 0},
{"text": "Madrid", "voter_count": 0}
],
"type": "quiz",
"total_voter_count": 0,
"is_closed": false,
"is_anonymous": false,
"allows_multiple_answers": false,
"correct_option_id": 2
}
}
}
๐ก๏ธ Error Handling
API Error Types
| Error | HTTP Code | Cause | Bot Response |
|---|---|---|---|
| Invalid API Key | 401 | Wrong/expired OLLAMA_API_KEY | "ุญุตู ู ุดููุฉ" + retry |
| Model Not Found | 404 | Invalid VISION_MODEL or CHAT_MODEL | "Model not found" error |
| Rate Limited | 429 | Too many requests | Retry with backoff |
| Timeout | 504 | API slow | "API timeout" message |
| DNS Error | N/A | Wrong OLLAMA_HOST | Network error |
Error Flow
Error Occurs
โ
Logger.error() called with details
โ
Exception raised/caught
โ
IF API Error: Notify user with friendly message
ELSE IF Validation Error: Show specific error
ELSE: Generic "ุญุตู ู
ุดููุฉ" message
โ
Message edit/delete if needed
โ
Continue waiting for next user input
User-Facing Error Messages
# Network errors
"โ ููุน ู
ูู ู
ุด ู
ุฏุนูู
!"
# Processing errors
"โ ู
ุด ูุงุฏุฑ ุงุณุชุฎุฑุฌ ูุต ูุงูู"
# Generation errors
"โ ู
ุด ูุงุฏุฑ ุงุนู
ู ุงุณุฆูุฉ ู
ู ุงููุต ุฏู"
# Validation errors
"โ ุงููุต ูุตูุฑ ุดููุฉ!"
# Generic errors
"โ ุญุตู ู
ุดููุฉ: {error_details}"
๐ Scalability Considerations
Current Limitations
Sequential Processing
- Each question sent with 1-second delay
- Multiple users processed sequentially
- No concurrent request batching
Memory Usage
- Entire image/PDF loaded into memory
- Full text stored in variables
- No streaming or chunking
Rate Limiting
- No rate limiting per user
- Telegram has built-in limits
- Ollama API has unknown limits
Scaling Improvements
1. Add Request Queuing
import asyncio
from asyncio import Queue
class BotQueue:
def __init__(self, max_concurrent=3):
self.queue = Queue()
self.processing = set()
async def process(self, user_id, request):
await self.queue.put((user_id, request))
await self._process_queue()
2. Add Redis Caching
import redis
cache = redis.Redis(host='localhost', port=6379)
async def generate_quiz_questions_cached(text, num_questions):
cache_key = f"quiz:{hash(text)}:{num_questions}"
cached = cache.get(cache_key)
if cached:
return json.loads(cached)
# Generate new questions
questions = await generate_quiz_questions(text, num_questions)
cache.setex(cache_key, 3600, json.dumps(questions)) # 1 hour TTL
return questions
3. Add Database Logging
import sqlite3
def log_request(user_id, request_type, status):
conn = sqlite3.connect('bot.db')
c = conn.cursor()
c.execute('''INSERT INTO requests (user_id, type, status, timestamp)
VALUES (?, ?, ?, ?)''',
(user_id, request_type, status, datetime.now()))
conn.commit()
4. Concurrent File Processing
# For multiple images/PDFs
async def process_multiple_files(files):
tasks = [extract_text_from_image(f) for f in files]
results = await asyncio.gather(*tasks)
return results
Performance Metrics
Current Performance:
- Image analysis: 5-15 seconds
- PDF extraction: 1-5 seconds
- Quiz generation: 10-30 seconds
- Total time: 20-50 seconds
Bottlenecks:
- Ollama API response time (50-80%)
- Image base64 encoding (5-10%)
- Network latency (10-20%)
๐ Deployment Architecture
Development
Local PC
โโโ Python 3.9+
โโโ bot.py
โโโ .env (with test credentials)
โโโ Internet connection to Ollama Cloud
Production
Server/Cloud
โโโ Python 3.9+
โโโ bot.py
โโโ .env (with production credentials)
โโโ Systemd/Docker service
โโโ Error logging
โโโ Database backup
Docker Deployment
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY bot.py .
COPY .env .
CMD ["python", "bot.py"]
docker build -t aiquiz-bot .
docker run -d --name aiquiz-bot aiquiz-bot
Last Updated: February 2026
Architecture Version: 1.0
API Version: Ollama Cloud API v1