Dan 5/28: Profilisanje AI servisa i optimizacija
Vreme potrebno: 90 – 180 minuta
Gde je Učitelj Vasa danas?
Učitelj Vasa može da komunicira sa oba najpopularnija AI servisa (OpenAI i Gemini) kroz univerzalni factory sistem, automatski rešava SSL probleme i vodi kontinuirane razgovore. Ali trenutno ne znamo koji servis daje bolje rezultate za koje tipove pitanja, koliko brzo odgovaraju, niti kako da optimizujemo parametre za različite scenarije. Danas ćemo dodati sistem za analizu performansi i automatsku optimizaciju!Cilj današnje lekcije
Danas ćeš naučiti kako da meriš performanse AI servisa, analiziraš kvalitet odgovora, optimizuješ parametre (temperature, max_tokens) za različite use case-ove i kreirajš profile rada koji automatski biraju najbolje postavke. Nakon ove lekcije, Učitelj Vasa će znati kada da koristi brze odgovore, kada detaljne objašnjenje, i koji servis daje bolje rezultate za različite tipove pitanja.Predznanja
- Funkcionalan multi-provider sistem (Dan 4)
- Razumevanje parametara kao temperature i max_tokens (Dan 2)
- BaseAIService interfejs i factory pattern (Dan 4)
- Osnovno razumevanje merenja vremena u Python-u
Glavni sadržaj
Zašto je profilisanje AI servisa važno?
Pre nego što krenemo sa implementacijom, važno je razumeti zašto uopšte želimo da analiziramo performanse AI servisa.📚 NIVO 1 – Osnovno objašnjenje
Profilisanje je kao testiranje automobila – želiš da znaš koliko brzo ubrzava, koliko troši goriva, kako se ponaša na različitim putevima. Isto važi za AI servise:- Neki su brži ali daju kraće odgovore
- Neki su sporiji ali detaljniji
- Neki bolje objašnjavaju kod, drugi bolje pišu priče
- Cena se razlikuje – OpenAI naplaćuje, Gemini je besplatan
🚀 NIVO 2 – Dublje razumevanje
Profilisanje AI servisa uključuje nekoliko metrika:- Latencija – vreme od slanja pitanja do početka odgovora
- Throughput – broj tokena generisanih po sekundi
- Kvalitet – relevantnost, tačnost i korisnost odgovora
- Troškovi – cena po tokenu (za plaćene servise)
- Stabilnost – konzistentnost odgovora za isto pitanje
- Temperature (0.0-2.0): Kreativnost vs konzistentnost
- Max tokens: Dužina odgovora
- Top-p: Alternativa temperaturi za kontrolu raznovrsnosti
- Frequency penalty: Smanjuje ponavljanje
Kreiranje sistema za merenje performansi
Počnimo sa osnovnim sistemom za praćenje metrika naših AI poziva.📚 NIVO 1 – Osnovno objašnjenje
Napravićemo “štopericu” koja meri:- Koliko dugo traje svaki poziv
- Koliko tokena je vraćeno
- Da li je bio uspešan ili ne
🚀 NIVO 2 – Dublje razumevanje
KreiraćemoPerformanceTracker klasu koja koristi dekorator pattern za transparentno praćenje metrika bez menjanja postojećeg koda. Podatke ćemo čuvati u JSON formatu za lakšu analizu.
🔄 VEŽBA: Dodaj u PerformanceTracker polje input_length kako bi pratio i veličinu prompta. Zatim vizuelizuj (npr. u Excel-u) da li dugi upiti značajno usporavaju odgovor.
🎯 ALTERNATIVNO REŠENJE: Umesto ručnog beleženja u JSON, možeš iskoristiti SQLite:
import sqlite3, time, datetime
con = sqlite3.connect("ai_metrics.db")
cur = con.cursor()
cur.execute("""CREATE TABLE IF NOT EXISTS calls(
id INTEGER PRIMARY KEY,
provider TEXT, duration REAL, tokens INTEGER,
ts TEXT)""")
def log_call(provider, duration, tokens):
cur.execute("INSERT INTO calls VALUES(NULL,?,?,?,?)",
(provider, duration, tokens,
datetime.datetime.now().isoformat()))
con.commit()
Ovo nudi lakše SQL upite i sortiranje.
🎈 ZABAVNA ČINJENICA: Prosečan developer tokenizuje oko 2000 reči/min, ali GPT-4o prelazi 20 000 tokena u istom vremenu – 10× brže od čoveka!
Kreiraj novi fajl src/utils/performance_tracker.py:
"""
Performance Tracker za AI servise
Meri i analizira performanse različitih AI servisa
"""
import time
import json
import statistics
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Optional, Any, Callable
from functools import wraps
import os
class PerformanceTracker:
"""Prati performanse AI servisa."""
def __init__(self, data_file: str = "ai_performance_data.json"):
"""
Inicijalizuje tracker sa putanjom do fajla za čuvanje podataka.
Args:
data_file: Ime fajla za čuvanje podataka
"""
# Kreiraj data folder ako ne postoji
self.data_dir = Path(__file__).parent.parent.parent / "data"
self.data_dir.mkdir(exist_ok=True)
self.data_file = self.data_dir / data_file
self.current_metrics = {}
self.load_data()
def load_data(self):
"""Učitava postojeće podatke iz fajla."""
if self.data_file.exists():
try:
with open(self.data_file, 'r', encoding='utf-8') as f:
self.all_metrics = json.load(f)
except Exception as e:
print(f"⚠️ Greška pri učitavanju podataka: {e}")
self.all_metrics = []
else:
self.all_metrics = []
def save_data(self):
"""Čuva podatke u fajl."""
try:
with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump(self.all_metrics, f, indent=2, ensure_ascii=False)
except Exception as e:
print(f"❌ Greška pri čuvanju podataka: {e}")
def start_tracking(self, provider: str, model: str, operation: str):
"""
Počinje praćenje performansi.
Args:
provider: AI provider (openai, gemini)
model: Model koji se koristi
operation: Tip operacije (chat, completion, etc)
"""
tracking_id = f"{provider}_{model}_{int(time.time()*1000)}"
self.current_metrics[tracking_id] = {
"provider": provider,
"model": model,
"operation": operation,
"start_time": time.time(),
"timestamp": datetime.now().isoformat()
}
return tracking_id
def end_tracking(self, tracking_id: str,
success: bool = True,
response_length: int = 0,
error: Optional[str] = None,
additional_data: Optional[Dict] = None):
"""
Završava praćenje i čuva metrike.
Args:
tracking_id: ID praćenja
success: Da li je poziv bio uspešan
response_length: Dužina odgovora u karakterima
error: Opis greške ako nije uspešno
additional_data: Dodatni podaci za čuvanje
"""
if tracking_id not in self.current_metrics:
return
metrics = self.current_metrics[tracking_id]
end_time = time.time()
# Izračunaj trajanje
duration = end_time - metrics["start_time"]
# Dopuni metrike
metrics.update({
"duration_seconds": round(duration, 3),
"success": success,
"response_length": response_length,
"error": error,
"tokens_per_second": round(response_length / duration, 2) if duration > 0 else 0
})
# Dodaj dodatne podatke ako postoje
if additional_data:
metrics.update(additional_data)
# Sačuvaj u listu svih metrika
self.all_metrics.append(metrics)
self.save_data()
# Ukloni iz trenutnih
del self.current_metrics[tracking_id]
return metrics
def track_call(self, provider: str, model: str):
"""
Dekorator za automatsko praćenje AI poziva.
Args:
provider: AI provider
model: Model koji se koristi
Returns:
Dekorator funkcija
"""
def decorator(func: Callable):
@wraps(func)
def wrapper(*args, **kwargs):
# Počni praćenje
tracking_id = self.start_tracking(provider, model, func.__name__)
try:
# Pozovi originalnu funkciju
result = func(*args, **kwargs)
# Završi praćenje - uspešno
self.end_tracking(
tracking_id,
success=True,
response_length=len(result) if isinstance(result, str) else 0
)
return result
except Exception as e:
# Završi praćenje - neuspešno
self.end_tracking(
tracking_id,
success=False,
error=str(e)
)
raise
return wrapper
return decorator
def get_provider_stats(self, provider: str) -> Dict[str, Any]:
"""
Vraća statistiku za određeni provider.
Args:
provider: Ime providera
Returns:
Dict sa statistikama
"""
provider_metrics = [m for m in self.all_metrics
if m.get("provider") == provider and m.get("success")]
if not provider_metrics:
return {
"provider": provider,
"total_calls": 0,
"avg_duration": 0,
"avg_tokens_per_second": 0,
"success_rate": 0
}
durations = [m["duration_seconds"] for m in provider_metrics]
tokens_per_sec = [m["tokens_per_second"] for m in provider_metrics]
total_calls = len(self.all_metrics)
successful_calls = len(provider_metrics)
return {
"provider": provider,
"total_calls": total_calls,
"successful_calls": successful_calls,
"avg_duration": round(statistics.mean(durations), 3),
"min_duration": round(min(durations), 3),
"max_duration": round(max(durations), 3),
"avg_tokens_per_second": round(statistics.mean(tokens_per_sec), 2),
"success_rate": round(successful_calls / total_calls * 100, 1) if total_calls > 0 else 0
}
def compare_providers(self) -> str:
"""
Poredi performanse svih providera.
Returns:
Formatiran string sa poređenjem
"""
providers = set(m["provider"] for m in self.all_metrics)
if not providers:
return "📊 Nema dovoljno podataka za poređenje."
report = "📊 POREĐENJE AI SERVISA\n"
report += "=" * 60 + "\n\n"
for provider in sorted(providers):
stats = self.get_provider_stats(provider)
report += f"🤖 {provider.upper()}\n"
report += f" Ukupno poziva: {stats['total_calls']}\n"
report += f" Uspešnih: {stats['successful_calls']}\n"
report += f" Prosečno vreme: {stats['avg_duration']}s\n"
report += f" Min/Max vreme: {stats['min_duration']}s / {stats['max_duration']}s\n"
report += f" Brzina: ~{stats['avg_tokens_per_second']} karaktera/s\n"
report += f" Uspešnost: {stats['success_rate']}%\n\n"
return report
def get_recommendations(self) -> Dict[str, str]:
"""
Daje preporuke na osnovu analize performansi.
Returns:
Dict sa preporukama za različite scenarije
"""
providers = set(m["provider"] for m in self.all_metrics)
if len(providers) < 2:
return {
"general": "Potrebno je više podataka sa oba servisa za preporuke."
}
# Analiziraj performanse
stats = {p: self.get_provider_stats(p) for p in providers}
# Pronađi najbrži i najstabilniji
fastest = min(stats.items(), key=lambda x: x[1]["avg_duration"])[0]
most_stable = max(stats.items(), key=lambda x: x[1]["success_rate"])[0]
recommendations = {
"brzi_odgovori": f"Koristi {fastest.upper()} - najbrži prosečni odgovor",
"stabilnost": f"Koristi {most_stable.upper()} - najbolja uspešnost",
"eksperimentisanje": "Gemini - besplatan za neograničeno testiranje",
"produkcija": "OpenAI - industrijski standard sa najboljom dokumentacijom"
}
return recommendations
# Globalni tracker instance
tracker = PerformanceTracker()
# Test funkcionalnost
if __name__ == "__main__":
print("🧪 Test Performance Tracker-a")
print("=" * 50)
# Simuliraj nekoliko poziva
for i in range(3):
provider = "openai" if i % 2 == 0 else "gemini"
# Start tracking
tid = tracker.start_tracking(provider, "test-model", "test_operation")
# Simuliraj poziv
time.sleep(0.5 + i * 0.2)
# End tracking
tracker.end_tracking(tid, success=True, response_length=100 + i * 50)
# Prikaži statistiku
print("\n" + tracker.compare_providers())
# Prikaži preporuke
print("💡 PREPORUKE:")
for scenario, recommendation in tracker.get_recommendations().items():
print(f" {scenario}: {recommendation}")
📊 DIJAGRAM: Tok izvršavanja PerformanceTracker-a
[Tvoj Kod] → pozovi_ai(...)
│
↓
[start_tracking] ◄──────────┐
(Beleži vreme početka) │
│ │
↓ │
[Originalni AI Poziv] ──── G R E Š K A ──┐
(npr. OpenAI API) │ │
│ │ ↓
↓ (Uspeh) │ [end_tracking(success=False)]
│ │ (Beleži grešku)
↓ │ │
[end_tracking(success=True)] │ ↓
(Beleži vreme kraja, │ [Prijavi Grešku]
računa trajanje) │ ↑
│ │ │
└───────────────────────┴─────────────┘
│
↓
[Vrati rezultat / Prijavi grešku] → [Tvoj Kod]
⚡ SAVET ZA OPTIMIZACIJU: Sakupljanje metrika, iako veoma korisno, dodaje mali “overhead” (dodatno vreme) svakom pozivu. Naš trenutni sistem je veoma brz (upisuje u memoriju i onda u fajl), ali u produkciji sa hiljadama poziva, upisivanje u fajl nakon svakog poziva može postati usko grlo. Profesionalni sistemi podatke prvo šalju u brzi “bafer” (privremenu memoriju) i onda ih periodično upisuju u bazu podataka ili log sistem u “grupama” (batches).
🌐 GOOGLE CONNECTION: Podaci koje skupljaš u ai_performance_data.json su izuzetno vredni. Kada tvoj projekat poraste, umesto čuvanja u lokalni JSON fajl, možeš jednostavno da ih šalješ u Google Cloud Logging. Svaki JSON objekat postaje jedan log zapis. Odatle, možeš automatski da kreiraš metrike u Cloud Monitoring-u (npr. prosečna latencija openai_service poziva) i da praviš vizuelne table (dashboards) u Looker Studio da bi pratio performanse u realnom vremenu, bez ijedne linije koda za vizuelizaciju!
Integracija trackera sa AI servisima
Sada ćemo integrisati tracker sa postojećim servisima da automatski prati svaki poziv.📚 NIVO 1 – Osnovno objašnjenje
Umesto da ručno merimo svaki poziv, napravićemo da se to dešava automatski. To je kao da staviš fitness tracker – sam beleži sve aktivnosti bez tvog razmišljanja o tome.🚀 NIVO 2 – Dublje razumevanje
Koristićemo Python dekoratore da “omotamo” postojeće metode i dodamo praćenje performansi bez menjanja originalne logike. Ovo je primer Aspect-Oriented Programming (AOP). 💡 PRO TIP: Dekorator pattern koji dodaje logovanje može se primeniti globalno korišćenjem metaclass-a. Ako svi servis-klase naslede isti metaclass, jednim redom koda “omotaš” sve javne metode bez ručne izmene svakog. 🎈 ZABAVNA ČINJENICA: Prvi Python dekorator za logovanje napisan je 2004. i stao je u jedan tvit (140 karaktera)! Ažurirajsrc/ai_services/openai_service.py (dodaj import i modifikuj pozovi_ai metodu):
# Dodaj na početak importa
from utils.performance_tracker import tracker
# U OpenAIService klasi, zameni pozovi_ai metodu sa:
def pozovi_ai(self, poruka: str, system_prompt: Optional[str] = None) -> str:
"""
Šalje poruku AI-ju i vraća odgovor sa praćenjem performansi.
Args:
poruka: Korisnikova poruka/pitanje
system_prompt: Opcioni system prompt za definisanje ponašanja
Returns:
AI odgovor kao string
"""
# Počni praćenje
tracking_id = tracker.start_tracking("openai", self.model, "chat_completion")
try:
# Pripremi poruke
messages = []
if system_prompt:
messages.append({
"role": "system",
"content": system_prompt
})
messages.append({
"role": "user",
"content": poruka
})
# Pozovi API
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
max_tokens=self.max_tokens,
temperature=self.temperature
)
result = response.choices[0].message.content.strip()
# Završi praćenje - uspešno
tracker.end_tracking(
tracking_id,
success=True,
response_length=len(result),
additional_data={
"prompt_length": len(poruka),
"temperature": self.temperature,
"max_tokens": self.max_tokens
}
)
return result
except Exception as e:
# Završi praćenje - neuspešno
tracker.end_tracking(
tracking_id,
success=False,
error=str(e)
)
# Postojeći error handling...
error_msg = f"Greška pri komunikaciji sa OpenAI: {str(e)}"
print(f"❌ {error_msg}")
if "api_key" in str(e).lower():
return "Izgleda da OpenAI API ključ nije valjan. Proveri podešavanja."
elif "rate_limit" in str(e).lower():
return "Previše zahteva ka OpenAI. Sačekaj malo pa pokušaj ponovo."
elif "insufficient_quota" in str(e).lower():
return "Nemaš dovoljno OpenAI kredita. Proveri svoj balans ili prebaci se na Gemini (AI_PROVIDER=gemini)."
elif "connection" in str(e).lower():
return "Problem sa internet konekcijom. Proveri da li si povezan."
elif "SSL" in str(e) or "certificate" in str(e).lower():
return "SSL problem - restartuj program ili koristi Gemini servis."
else:
return "Ups! Nešto je pošlo po zlu sa OpenAI. Pokušaj ponovo za koji trenutak."
Slično ažuriraj src/ai_services/gemini_service.py:
# Dodaj na početak importa
from utils.performance_tracker import tracker
# U GeminiService klasi, zameni pozovi_ai metodu sa:
def pozovi_ai(self, poruka: str, system_prompt: Optional[str] = None) -> str:
"""
Šalje poruku AI-ju i vraća odgovor sa praćenjem performansi.
Args:
poruka: Korisnikova poruka/pitanje
system_prompt: Opcioni system prompt za definisanje ponašanja
Returns:
AI odgovor kao string
"""
# Počni praćenje
tracking_id = tracker.start_tracking("gemini", Config.GEMINI_MODEL, "generate_content")
try:
# Gemini kombinuje system prompt i korisničku poruku
full_prompt = poruka
if system_prompt:
full_prompt = f"{system_prompt}\n\nKorisnik: {poruka}\nAsistent:"
# Generiši odgovor
response = self.model.generate_content(
full_prompt,
generation_config=self.generation_config
)
result = response.text.strip()
# Završi praćenje - uspešno
tracker.end_tracking(
tracking_id,
success=True,
response_length=len(result),
additional_data={
"prompt_length": len(full_prompt),
"temperature": self.temperature,
"max_tokens": self.max_tokens
}
)
return result
except Exception as e:
# Završi praćenje - neuspešno
tracker.end_tracking(
tracking_id,
success=False,
error=str(e)
)
# Postojeći error handling...
error_msg = f"Greška pri komunikaciji sa Gemini: {str(e)}"
print(f"❌ {error_msg}")
if "api_key" in str(e).lower():
return "Izgleda da Gemini API ključ nije valjan. Proveri podešavanja."
elif "rate_limit" in str(e).lower() or "429" in str(e):
return "Previše zahteva ka Gemini. Sačekaj malo pa pokušaj ponovo."
elif "safety" in str(e).lower():
return "Gemini je blokirao odgovor iz sigurnosnih razloga. Pokušaj sa drugim pitanjem."
elif "connection" in str(e).lower():
return "Problem sa internet konekcijom. Proveri da li si povezan."
else:
return "Ups! Nešto je pošlo po zlu sa Gemini. Pokušaj ponovo za koji trenutak."
Kreiranje sistema optimizacionih profila
Sada ćemo kreirati profile koji automatski podešavaju parametre za različite tipove zadataka.📚 NIVO 1 – Osnovno objašnjenje
Profile su kao preseti na TV-u – “Film” pojačava boje, “Sport” pojačava pokret, “Vesti” fokusira na govor. Mi ćemo napraviti profile za AI:- Brzi odgovor: Kratko i jasno
- Detaljan učitelj: Opširno objašnjenje
- Kreativno pisanje: Više mašte
- Precizni kod: Tačnost pre svega
🚀 NIVO 2 – Dublje razumevanje
Različiti zadaci zahtevaju različite pristupe. Temperature 0.2 je odlična za generisanje koda (predvidljivo), dok 1.5 omogućava kreativnost za pisanje priča. Max_tokens kontroliše dužinu – ne želimo roman kada tražimo definiciju! 💡 PRO TIP: Premosti “tvrd” izbor provajdera u profilu tako što ćeš zapisati samo poželjnu metriku (npr.target_duration < 2 s). Factory može dinamički da pozove tracker.compare_providers() i izabere onaj koji u tom trenutku zadovoljava cilj.
🎈 ZABAVNA ČINJENICA: Parametar temperature potiče iz formule Bolcmanove raspodele – direktna veza između AI-ja i fizike!
Kreiraj src/utils/optimization_profiles.py:
"""
Optimizacioni profili za AI servise
Predefinisane postavke za različite scenarije korišćenja
"""
from typing import Dict, Any, Optional
from dataclasses import dataclass
from enum import Enum
class ProfileType(Enum):
"""Tipovi optimizacionih profila."""
QUICK_ANSWER = "quick_answer"
DETAILED_EXPLANATION = "detailed_explanation"
CODE_GENERATION = "code_generation"
CREATIVE_WRITING = "creative_writing"
DEBUGGING_HELP = "debugging_help"
TRANSLATION = "translation"
SUMMARIZATION = "summarization"
@dataclass
class OptimizationProfile:
"""Definiše optimizacioni profil za AI pozive."""
name: str
description: str
temperature: float
max_tokens: int
system_prompt_addon: str
provider_preference: Optional[str] = None # None znači koristi trenutni
def to_dict(self) -> Dict[str, Any]:
"""Konvertuje profil u dictionary."""
return {
"temperature": self.temperature,
"max_tokens": self.max_tokens,
"system_prompt_addon": self.system_prompt_addon,
"provider_preference": self.provider_preference
}
# Predefinisani profili
PROFILES = {
ProfileType.QUICK_ANSWER: OptimizationProfile(
name="Brzi odgovor",
description="Kratki, direktni odgovori za jednostavna pitanja",
temperature=0.3,
max_tokens=100,
system_prompt_addon="\nDaj kratak i direktan odgovor u 1-2 rečenice.",
provider_preference="gemini" # Gemini je obično brži za kratke odgovore
),
ProfileType.DETAILED_EXPLANATION: OptimizationProfile(
name="Detaljno objašnjenje",
description="Opširna objašnjenja sa primerima",
temperature=0.7,
max_tokens=500,
system_prompt_addon="\nDaj detaljno objašnjenje sa primerima. Objasni korak po korak.",
provider_preference="openai" # OpenAI bolje strukturira duge odgovore
),
ProfileType.CODE_GENERATION: OptimizationProfile(
name="Generisanje koda",
description="Precizno generisanje koda sa komentarima",
temperature=0.2,
max_tokens=400,
system_prompt_addon="\nGeneriši samo kod sa komentarima na srpskom. Kod mora biti funkcionalan i dobro struktuiran.",
provider_preference=None # Oba su dobra za kod
),
ProfileType.CREATIVE_WRITING: OptimizationProfile(
name="Kreativno pisanje",
description="Kreativni sadržaj sa više mašte",
temperature=1.2,
max_tokens=600,
system_prompt_addon="\nBudi kreativan i maštovit u svom odgovoru. Koristi žive opise i interesantne ideje.",
provider_preference="openai" # OpenAI tradicionalno bolji za kreativnost
),
ProfileType.DEBUGGING_HELP: OptimizationProfile(
name="Pomoć pri debug-ovanju",
description="Analiza grešaka i predlozi rešenja",
temperature=0.3,
max_tokens=300,
system_prompt_addon="\nAnaliziraj problem sistematski. Identifikuj mogući uzrok i predloži konkretna rešenja.",
provider_preference=None
),
ProfileType.TRANSLATION: OptimizationProfile(
name="Prevođenje",
description="Tačno prevođenje sa očuvanjem konteksta",
temperature=0.1,
max_tokens=200,
system_prompt_addon="\nPrevedi tačno, očuvavajući originalni smisao i ton.",
provider_preference="gemini" # Gemini odličan za prevode
),
ProfileType.SUMMARIZATION: OptimizationProfile(
name="Rezimiranje",
description="Sažeto predstavljanje ključnih informacija",
temperature=0.4,
max_tokens=200,
system_prompt_addon="\nRezimiraj ključne informacije jasno i koncizno.",
provider_preference=None
)
}
class ProfileManager:
"""Upravlja optimizacionim profilima."""
def __init__(self):
self.profiles = PROFILES
self.active_profile: Optional[ProfileType] = None
def get_profile(self, profile_type: ProfileType) -> OptimizationProfile:
"""
Vraća profil za dati tip.
Args:
profile_type: Tip profila
Returns:
OptimizationProfile objekat
"""
return self.profiles[profile_type]
def list_profiles(self) -> str:
"""
Vraća formatiranu listu svih dostupnih profila.
Returns:
String sa listom profila
"""
result = "📋 DOSTUPNI OPTIMIZACIONI PROFILI\n"
result += "=" * 50 + "\n\n"
for i, (ptype, profile) in enumerate(self.profiles.items(), 1):
result += f"{i}. {profile.name}\n"
result += f" 📝 {profile.description}\n"
result += f" 🌡️ Temperature: {profile.temperature}\n"
result += f" 📏 Max tokena: {profile.max_tokens}\n"
if profile.provider_preference:
result += f" 🤖 Preporučen: {profile.provider_preference.upper()}\n"
result += "\n"
return result
def analyze_question(self, question: str) -> ProfileType:
"""
Analizira pitanje i predlaže najbolji profil.
Args:
question: Korisnikovo pitanje
Returns:
Preporučeni ProfileType
"""
question_lower = question.lower()
# Ključne reči za različite profile
code_keywords = ["kod", "funkcija", "class", "python", "napiši", "implementiraj",
"sintaksa", "primer koda", "programa"]
debug_keywords = ["greška", "error", "ne radi", "problem", "bug", "zašto",
"debug", "exception", "traceback"]
creative_keywords = ["priča", "pesma", "kreativno", "zamisli", "osmisli",
"maštovito", "originalno"]
translation_keywords = ["prevedi", "prevod", "na engleski", "na srpski",
"translate"]
summary_keywords = ["rezimiraj", "ukratko", "sažmi", "glavni", "ključn"]
detail_keywords = ["objasni", "detaljno", "kako", "zašto", "razumem",
"nauči me", "korak po korak"]
# Proveri ključne reči
if any(kw in question_lower for kw in code_keywords):
return ProfileType.CODE_GENERATION
elif any(kw in question_lower for kw in debug_keywords):
return ProfileType.DEBUGGING_HELP
elif any(kw in question_lower for kw in creative_keywords):
return ProfileType.CREATIVE_WRITING
elif any(kw in question_lower for kw in translation_keywords):
return ProfileType.TRANSLATION
elif any(kw in question_lower for kw in summary_keywords):
return ProfileType.SUMMARIZATION
elif any(kw in question_lower for kw in detail_keywords):
return ProfileType.DETAILED_EXPLANATION
elif len(question.split()) < 10: # Kratko pitanje
return ProfileType.QUICK_ANSWER
else:
return ProfileType.DETAILED_EXPLANATION
def apply_profile(self, profile_type: ProfileType,
current_settings: Dict[str, Any]) -> Dict[str, Any]:
"""
Primenjuje profil na trenutne postavke.
Args:
profile_type: Tip profila koji se primenjuje
current_settings: Trenutne postavke
Returns:
Ažurirane postavke
"""
profile = self.get_profile(profile_type)
self.active_profile = profile_type
# Kopiraj trenutne postavke
new_settings = current_settings.copy()
# Primeni postavke profila
new_settings["temperature"] = profile.temperature
new_settings["max_tokens"] = profile.max_tokens
# Dodaj addon na system prompt ako postoji
if "system_prompt" in new_settings and profile.system_prompt_addon:
new_settings["system_prompt"] += profile.system_prompt_addon
# Postavi provider prefereneciju ako je specificirana
if profile.provider_preference:
new_settings["provider_hint"] = profile.provider_preference
return new_settings
# Globalna instanca
profile_manager = ProfileManager()
# Test funkcionalnost
if __name__ == "__main__":
print("🧪 Test Optimization Profiles")
print("=" * 50)
# Prikaži sve profile
print(profile_manager.list_profiles())
# Test analize pitanja
test_questions = [
"Šta je Python?",
"Napiši funkciju za sortiranje liste",
"Zašto mi kod baca IndexError?",
"Prevedi 'Hello World' na srpski",
"Objasni mi detaljno kako rade dekoratori u Python-u"
]
print("🔍 ANALIZA PITANJA:")
print("-" * 50)
for q in test_questions:
suggested = profile_manager.analyze_question(q)
profile = profile_manager.get_profile(suggested)
print(f"\nPitanje: '{q}'")
print(f"Predlog: {profile.name}")
print(f"Razlog: {profile.description}")
🔍 UVID: Naša analyze_question metoda je jednostavan, ali efektivan “rules engine” (sistem baziran na pravilima). U naprednijim sistemima, ovaj korak se često rešava pomoću samog AI-ja! Koristio bi se manji, brži i jeftiniji model (kao Gemini Flash ili GPT-3.5-Turbo) koji bi prvo “klasifikovao” upit korisnika u jednu od kategorija (npr. “code_generation”, “creative_writing”). Na osnovu te klasifikacije, sistem bi onda pozivao veći, moćniji i skuplji model (kao Gemini 1.5 Pro ili GPT-4) sa već optimizovanim parametrima. Ovo se zove “model routing” i predstavlja ključnu strategiju za optimizaciju troškova i performansi u velikim AI sistemima.
📊 DIJAGRAM: Logika odlučivanja u analyze_question
[Pitanje Korisnika]
│
↓
+--------------------------------+
| Pitanje u malim slovima |
+--------------------------------+
│
↓
┌───────────────────────────────┐
│ Da li sadrži ključne reči za │
│ KOD (npr. 'kod', 'funkcija')? ├─(Da)──→ [CODE_GENERATION]
└───────────────────────────────┘
│ (Ne)
↓
┌───────────────────────────────┐
│ Da li sadrži ključne reči za │
│ DEBUG (npr. 'greška', 'bug')? ├─(Da)──→ [DEBUGGING_HELP]
└───────────────────────────────┘
│ (Ne)
↓
┌───────────────────────────────┐
│ Da li sadrži ključne reči za │
│ KREATIVNOST ('priča', 'pesma')├─(Da)──→ [CREATIVE_WRITING]
└───────────────────────────────┘
│ (Ne)
↓
(...) Nastavak provera
│
↓
┌───────────────────────────────┐
│ Da li ima < 10 reči? ├─(Da)──→ [QUICK_ANSWER]
└───────────────────────────────┘
│ (Ne)
↓
[DETAILED_EXPLANATION] (Podrazumevano)
Optimizacija i najbolje prakse
Sada kada imamo alate za merenje i optimizaciju, hajde da sumiramo ključne principe. 🔍 UVID: Ključna ideja današnje lekcije je prelazak sa pretpostavki na merenja. Umesto da misliš “mislim da je Gemini brži”, sada imaš sistem koji to može da dokaže ili opovrgne podacima. Ovo je fundamentalna promena u načinu razmišljanja i osnova inženjerskog pristupa razvoju softvera.- Odvoji brige (Separation of Concerns): Logika za praćenje performansi (
PerformanceTracker) je potpuno odvojena od logike AI servisa. To znači da sutra možemo zamenitiPerformanceTrackernekim naprednijim sistemom bez izmene ijedne linije koda uOpenAIServiceiliGeminiService. - Automatizuj gde je moguće:
ProfileManagerautomatski bira najbolje postavke. Ovo smanjuje “cognitive load” (mentalno opterećenje) za korisnika i čini aplikaciju “pametnijom”. Dobar softver predviđa potrebe korisnika. - Planiraj za budućnost (Scalability): Čuvanje podataka u JSON je savršeno za početak. Struktura koju smo napravili olakšava da se sutra podaci šalju u pravu bazu podataka ili cloud servis. Uvek razmišljaj kako će tvoje rešenje raditi sa 10 puta više podataka ili korisnika.
max_tokens direktno utiče na troškove (kod plaćenih servisa) i vreme odgovora. Korišćenjem profila, mi ne samo da dobijamo relevantnije odgovore, već i aktivno upravljamo troškovima. Profil “Brzi odgovor” sa max_tokens=100 može biti 5 do 10 puta jeftiniji od profila “Detaljno objašnjenje” sa max_tokens=500. Praćenje i optimizacija su dve strane iste medalje: poboljšavaju iskustvo i smanjuju troškove.
Integracija profila sa AI factory sistemom
Sada ćemo proširiti naš factory da koristi profile za optimizaciju.📚 NIVO 1 – Osnovno objašnjenje
Factory će sada biti pametniji – analiziraće tvoje pitanje i automatski prilagoditi postavke. To je kao pametni telefon koji automatski pojačava ekran na suncu ili smanjuje kada je mrak.🚀 NIVO 2 – Dublje razumevanje
Proširićemo BaseAIService sa metodom koja prima profil, a factory će moći da kreira “optimizovane” instance servisa sa custom parametrima. 🔄 VEŽBA: Dodaj CLI opciju--profile quick kako bi korisnik iz terminala mogao da odabere optimizacioni profil bez interaktivnog menija.
🎯 ALTERNATIVNO REŠENJE: Umesto da profil proširuje system_prompt, možeš da proslediš poseban “instruction” objekat (OpenAI ChatML) i tako razdvojiš logiku od sadržaja.
🎈 ZABAVNA ČINJENICA: Slack-ov bot “Claude” je u internom A/B testu dobio 12% više “jednostavnih pitanja” samo jer je u pozadini pametno menjao temperature između 0.3 i 0.7 u zavisnosti od dužine korisničkog pitanja.
Ažuriraj src/ai_services/base_service.py:
"""
Bazna klasa za sve AI servise
Definiše interfejs koji svi servisi moraju implementirati
"""
from abc import ABC, abstractmethod
from typing import Optional, List, Dict, Any
class BaseAIService(ABC):
"""Apstraktna bazna klasa za AI servise."""
@abstractmethod
def pozovi_ai(self, poruka: str, system_prompt: Optional[str] = None) -> str:
"""
Šalje poruku AI-ju i vraća odgovor.
Args:
poruka: Korisnikova poruka/pitanje
system_prompt: Opcioni system prompt za definisanje ponašanja
Returns:
AI odgovor kao string
"""
pass
@abstractmethod
def pozovi_sa_istorijom(self, messages: List[Dict[str, str]]) -> str:
"""
Šalje celu istoriju razgovora AI-ju.
Args:
messages: Lista poruka sa 'role' i 'content' ključevima
Returns:
AI odgovor kao string
"""
pass
def test_konekcija(self) -> bool:
"""
Testira da li servis može da se poveže sa API-jem.
Returns:
True ako je konekcija uspešna, False inače
"""
try:
response = self.pozovi_ai("Reci 'zdravo' na srpskom.")
return len(response) > 0
except Exception as e:
print(f"❌ Test konekcije neuspešan: {e}")
return False
def apply_settings(self, settings: Dict[str, Any]):
"""
Primenjuje custom postavke na servis.
Args:
settings: Dictionary sa postavkama
"""
# Primeni temperature ako postoji
if "temperature" in settings and hasattr(self, "temperature"):
self.temperature = settings["temperature"]
if hasattr(self, "generation_config"):
# Za Gemini, ažuriraj generation_config
from google.generativeai import GenerationConfig
self.generation_config = GenerationConfig(
max_output_tokens=getattr(self, "max_tokens", 150),
temperature=settings["temperature"]
)
# Primeni max_tokens ako postoji
if "max_tokens" in settings and hasattr(self, "max_tokens"):
self.max_tokens = settings["max_tokens"]
if hasattr(self, "generation_config"):
# Za Gemini, ažuriraj generation_config
from google.generativeai import GenerationConfig
self.generation_config = GenerationConfig(
max_output_tokens=settings["max_tokens"],
temperature=getattr(self, "temperature", 0.7)
)
def get_current_settings(self) -> Dict[str, Any]:
"""
Vraća trenutne postavke servisa.
Returns:
Dict sa trenutnim postavkama
"""
return {
"temperature": getattr(self, "temperature", 0.7),
"max_tokens": getattr(self, "max_tokens", 150),
"model": getattr(self, "model", "unknown")
}
Kreiranje benchmark sistema
Hajde da napravimo sistem koji automatski testira oba servisa sa različitim tipovima pitanja.📚 NIVO 1 – Osnovno objašnjenje
Benchmark je kao takmičenje između servisa – postavljamo im ista pitanja i merimo ko daje bolje odgovore, ko je brži, i ko je pouzdaniji. To nam pomaže da donesemo informisane odluke o tome koji servis kada koristiti.🚀 NIVO 2 – Dublje razumevanje
Kreiraćemo set standardizovanih test pitanja koji pokrivaju različite scenarije. Za svako pitanje ćemo meriti latenciju, kvalitet odgovora i konzistentnost između pokretanja. 💡 PRO TIP: Ograničenje troškova? Pokreni benchmark samo na prvih N pitanja gde jelen(question) < 40. Ta pitanja često obuhvate 80% realnih korisničkih upita, a cena testa se smanjuje za red veličine.
🎈 ZABAVNA ČINJENICA: Najduži javni prompt ikada objavljen imao je preko 80 000 tokena – GPT-4 je ipak odgovorio, ali je operacija koštala ~120 USD!
Kreiraj src/utils/ai_benchmark.py:
"""
AI Benchmark sistem
Poredi performanse različitih AI servisa
"""
import time
import json
from typing import Dict, List, Any, Optional
from datetime import datetime
from pathlib import Path
from ai_services.ai_factory import AIServiceFactory
from utils.config import Config
from utils.performance_tracker import tracker
from utils.optimization_profiles import profile_manager, ProfileType
class AIBenchmark:
"""Benchmark sistem za poređenje AI servisa."""
# Test pitanja grupisana po kategorijama
TEST_QUESTIONS = {
"simple": [
"Šta je Python?",
"Koliko je 15 + 27?",
"Koji je glavni grad Srbije?"
],
"code": [
"Napiši Python funkciju koja vraća faktorijel broja",
"Kako da sortiram listu u Python-u?",
"Objasni razliku između list i tuple"
],
"complex": [
"Objasni koncept rekurzije sa primerom",
"Koje su prednosti objektno-orijentisanog programiranja?",
"Kako funkcioniše garbage collection u Python-u?"
],
"creative": [
"Napiši kratku priču o programeru početniku",
"Osmisli analogiju za objasnšnjenje API-ja",
"Opiši budunost AI tehnologije"
]
}
def __init__(self):
"""Inicijalizuje benchmark sistem."""
self.results_dir = Path(__file__).parent.parent.parent / "data" / "benchmarks"
self.results_dir.mkdir(parents=True, exist_ok=True)
self.current_results = []
def run_single_test(self, provider: str, question: str,
category: str, profile: Optional[ProfileType] = None) -> Dict[str, Any]:
"""
Pokreće pojedinačni test.
Args:
provider: AI provider za testiranje
question: Test pitanje
category: Kategorija pitanja
profile: Opcioni profil za optimizaciju
Returns:
Rezultati testa
"""
try:
# Promeni provider ako treba
original_provider = Config.AI_PROVIDER
if Config.AI_PROVIDER != provider:
Config.AI_PROVIDER = provider
AIServiceFactory.reset()
# Dobij servis
service = AIServiceFactory.get_service()
# Primeni profil ako je dat
if profile:
settings = profile_manager.apply_profile(
profile,
service.get_current_settings()
)
service.apply_settings(settings)
# Meri vreme
start_time = time.time()
# Pozovi AI
response = service.pozovi_ai(question)
# Kraj merenja
duration = time.time() - start_time
# Vrati na originalni provider
if original_provider != provider:
Config.AI_PROVIDER = original_provider
AIServiceFactory.reset()
return {
"provider": provider,
"question": question,
"category": category,
"profile": profile.value if profile else "default",
"response": response,
"response_length": len(response),
"duration": round(duration, 3),
"success": True,
"timestamp": datetime.now().isoformat()
}
except Exception as e:
# Vrati na originalni provider
if original_provider != provider:
Config.AI_PROVIDER = original_provider
AIServiceFactory.reset()
return {
"provider": provider,
"question": question,
"category": category,
"profile": profile.value if profile else "default",
"response": None,
"response_length": 0,
"duration": 0,
"success": False,
"error": str(e),
"timestamp": datetime.now().isoformat()
}
def run_category_benchmark(self, category: str, providers: List[str]) -> List[Dict]:
"""
Pokreće benchmark za celu kategoriju.
Args:
category: Kategorija pitanja za testiranje
providers: Lista providera za testiranje
Returns:
Lista rezultata
"""
results = []
questions = self.TEST_QUESTIONS.get(category, [])
print(f"\n🏃 Pokrećem benchmark za kategoriju: {category.upper()}")
print("=" * 60)
for question in questions:
print(f"\n📝 Pitanje: {question}")
# Analiziraj koje profile treba
suggested_profile = profile_manager.analyze_question(question)
for provider in providers:
print(f" 🤖 Testiram {provider}...", end="", flush=True)
# Test sa default postavkama
result_default = self.run_single_test(provider, question, category)
results.append(result_default)
# Test sa optimizovanim profilom
result_optimized = self.run_single_test(
provider, question, category, suggested_profile
)
results.append(result_optimized)
print(f" ✓ ({result_default['duration']}s default, "
f"{result_optimized['duration']}s optimized)")
# Pauza između pitanja
time.sleep(0.5)
return results
def run_full_benchmark(self) -> str:
"""
Pokreće kompletan benchmark test.
Returns:
Putanja do fajla sa rezultatima
"""
print("\n🚀 POKRETANJE KOMPLETNOG BENCHMARK TESTA")
print("=" * 60)
# Proveri koji provideri su dostupni
available_providers = []
if Config.OPENAI_API_KEY:
available_providers.append("openai")
if Config.GEMINI_API_KEY:
available_providers.append("gemini")
if len(available_providers) < 2:
print("⚠️ Potrebna su oba API ključa za poređenje!")
return ""
print(f"✅ Testiram: {', '.join(p.upper() for p in available_providers)}")
# Pokreni testove za sve kategorije
all_results = []
for category in self.TEST_QUESTIONS.keys():
category_results = self.run_category_benchmark(category, available_providers)
all_results.extend(category_results)
self.current_results.extend(category_results)
# Sačuvaj rezultate
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
results_file = self.results_dir / f"benchmark_{timestamp}.json"
with open(results_file, 'w', encoding='utf-8') as f:
json.dump(all_results, f, indent=2, ensure_ascii=False)
print(f"\n✅ Benchmark završen! Rezultati sačuvani u: {results_file}")
# Generiši izveštaj
self.generate_report(all_results)
return str(results_file)
def generate_report(self, results: List[Dict[str, Any]]):
"""
Generiše izveštaj na osnovu rezultata.
Args:
results: Lista rezultata benchmark-a
"""
print("\n📊 BENCHMARK IZVEŠTAJ")
print("=" * 60)
# Grupiši po providerima
provider_stats = {}
for result in results:
if not result["success"]:
continue
provider = result["provider"]
profile = result["profile"]
if provider not in provider_stats:
provider_stats[provider] = {
"default": {"times": [], "lengths": []},
"optimized": {"times": [], "lengths": []}
}
mode = "default" if profile == "default" else "optimized"
provider_stats[provider][mode]["times"].append(result["duration"])
provider_stats[provider][mode]["lengths"].append(result["response_length"])
# Prikaži statistiku
for provider, stats in provider_stats.items():
print(f"\n🤖 {provider.upper()}")
for mode in ["default", "optimized"]:
if stats[mode]["times"]:
avg_time = sum(stats[mode]["times"]) / len(stats[mode]["times"])
avg_length = sum(stats[mode]["lengths"]) / len(stats[mode]["lengths"])
print(f"\n {mode.upper()} MODE:")
print(f" - Prosečno vreme: {avg_time:.2f}s")
print(f" - Prosečna dužina: {avg_length:.0f} karaktera")
print(f" - Najbrži odgovor: {min(stats[mode]['times']):.2f}s")
print(f" - Najsporiji odgovor: {max(stats[mode]['times']):.2f}s")
# Preporuke
print("\n💡 PREPORUKE NA OSNOVU BENCHMARK-A:")
# Pronađi najbolje za svaku kategoriju
category_winners = {}
for result in results:
if result["success"] and result["profile"] != "default":
cat = result["category"]
if cat not in category_winners:
category_winners[cat] = {"provider": result["provider"],
"time": result["duration"]}
elif result["duration"] < category_winners[cat]["time"]:
category_winners[cat] = {"provider": result["provider"],
"time": result["duration"]}
for cat, winner in category_winners.items():
print(f" - {cat.capitalize()} pitanja: {winner['provider'].upper()}")
# Test funkcionalnost
if __name__ == "__main__":
print("🧪 Test AI Benchmark Sistema")
print("=" * 50)
benchmark = AIBenchmark()
# Test pojedinačnog pitanja
print("\n1️⃣ Test pojedinačnog pitanja:")
result = benchmark.run_single_test(
Config.AI_PROVIDER,
"Šta je rekurzija?",
"test"
)
print(f"Provider: {result['provider']}")
print(f"Trajanje: {result['duration']}s")
print(f"Dužina odgovora: {result['response_length']} karaktera")
# Samo ako imamo oba providera
if Config.OPENAI_API_KEY and Config.GEMINI_API_KEY:
print("\n2️⃣ Pokrećem mini benchmark...")
# Smanji broj pitanja za test
benchmark.TEST_QUESTIONS = {
"simple": ["Šta je Python?"],
"code": ["Kako da sortiram listu?"]
}
benchmark.run_full_benchmark()
🎯 ALTERNATIVNO REŠENJE: Ako želiš potpuno zero-dependency rešenje, zameni json + statistics čistim CSV-om i sum/len funkcijama – radi u svakom Python okruženju, čak i na mikrokontrolerima.
🤔 MINI-KVIZ:
- Koja je prednost korišćenja JSON-a u odnosu na CSV kada čuvamo ugnežđene strukture?
- Šta bi se dogodilo da zaboraviš
flush=Trueuprint-u tokom benchmark-a? - Kako bi proširio
PerformanceTrackerda prati i tip greške (npr. SSL, rate-limit)?
Praktična implementacija
Ažuriranje glavnog programa sa novim funkcionalnostima
Sada ćemo integrisati sve nove sisteme u glavni program. Ažurirajsrc/main.py:
"""
Glavni program za Učitelja Vasu
Sa podrškom za profilisanje i optimizaciju
"""
# Dodaj src folder u Python path
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from vasa_core import pozdrav, predstavi_se, glavni_meni, VASA_LICNOST
from ai_simulator import simuliraj_ai_odgovor
from utils.config import Config
from utils.performance_tracker import tracker
from utils.optimization_profiles import profile_manager, ProfileType
from utils.ai_benchmark import AIBenchmark
from ai_services.ai_factory import AIServiceFactory
from ai_services.base_service import BaseAIService
from typing import Optional
# Globalna varijabla za AI servis
ai_service: Optional[BaseAIService] = None
current_profile: Optional[ProfileType] = None
def inicijalizuj_ai_servis():
"""Pokušava da kreira AI servis koristeći factory."""
global ai_service
try:
if Config.validate():
ai_service = AIServiceFactory.get_service()
print(f"✅ {Config.AI_PROVIDER.upper()} servis uspešno pokrenut!")
return True
except Exception as e:
print(f"⚠️ AI servis nije dostupan: {e}")
print("Koristićemo simulaciju umesto pravog AI-ja.")
return False
def postavi_pitanje_vasi(pitanje: str, auto_optimize: bool = True) -> str:
"""
Postavlja pitanje Vasi koristeći AI ili simulaciju.
Args:
pitanje: Korisnikovo pitanje
auto_optimize: Da li automatski optimizovati postavke
Returns:
AI odgovor
"""
global current_profile
if ai_service:
# Analiziraj pitanje i primeni profil ako treba
if auto_optimize:
suggested_profile = profile_manager.analyze_question(pitanje)
# Prikaži koji profil se koristi
profile_info = profile_manager.get_profile(suggested_profile)
print(f"📋 [Koristim profil: {profile_info.name}]")
# Primeni profil
settings = profile_manager.apply_profile(
suggested_profile,
ai_service.get_current_settings()
)
ai_service.apply_settings(settings)
current_profile = suggested_profile
# Modifikuj system prompt sa addon-om
modified_prompt = VASA_LICNOST + profile_info.system_prompt_addon
else:
modified_prompt = VASA_LICNOST
# Koristi pravi AI
print(f"🤖 [Koristim {Config.AI_PROVIDER.upper()} AI model...]")
return ai_service.pozovi_ai(pitanje, system_prompt=modified_prompt)
else:
# Fallback na simulaciju
print("🎭 [Koristim simulaciju...]")
return simuliraj_ai_odgovor(pitanje)
def prikazi_performanse():
"""Prikazuje statistiku performansi."""
print("\n" + tracker.compare_providers())
# Prikaži preporuke
recommendations = tracker.get_recommendations()
if recommendations:
print("💡 PREPORUKE NA OSNOVU ANALIZE:")
for scenario, rec in recommendations.items():
print(f" • {scenario.replace('_', ' ').title()}: {rec}")
def upravljanje_profilima():
"""Omogućava upravljanje optimizacionim profilima."""
while True:
print("\n🎯 UPRAVLJANJE PROFILIMA")
print("=" * 50)
print(profile_manager.list_profiles())
print("Opcije:")
print("1. Testiraj profil sa prilagođenim pitanjem")
print("2. Uporedi profile")
print("3. Vrati se u glavni meni")
izbor = input("\nTvoj izbor: ").strip()
if izbor == "1":
print("\nIzaberi profil (1-7): ", end="")
try:
profile_idx = int(input().strip()) - 1
profile_type = list(ProfileType)[profile_idx]
print(f"\nUnesi pitanje za testiranje: ", end="")
test_pitanje = input().strip()
if test_pitanje:
# Primeni profil i testiraj
odgovor = postavi_pitanje_vasi(test_pitanje, auto_optimize=False)
# Ručno primeni izabrani profil
settings = profile_manager.apply_profile(
profile_type,
ai_service.get_current_settings()
)
ai_service.apply_settings(settings)
print(f"\n🤖 Odgovor sa profilom '{profile_manager.get_profile(profile_type).name}':")
print(odgovor)
except (ValueError, IndexError):
print("❌ Nevaljan izbor profila.")
elif izbor == "2":
print("\nUnesi pitanje za poređenje: ", end="")
test_pitanje = input().strip()
if test_pitanje and ai_service:
print("\n📊 POREĐENJE PROFILA")
print("=" * 50)
original_settings = ai_service.get_current_settings()
for profile_type in [ProfileType.QUICK_ANSWER,
ProfileType.DETAILED_EXPLANATION]:
profile = profile_manager.get_profile(profile_type)
# Primeni profil
settings = profile_manager.apply_profile(
profile_type,
original_settings
)
ai_service.apply_settings(settings)
print(f"\n🎯 {profile.name}:")
print(f" Temperature: {settings['temperature']}")
print(f" Max tokens: {settings['max_tokens']}")
# Dobij odgovor
modified_prompt = VASA_LICNOST + profile.system_prompt_addon
odgovor = ai_service.pozovi_ai(test_pitanje, modified_prompt)
print(f" Odgovor ({len(odgovor)} karaktera):")
print(f" {odgovor[:200]}..." if len(odgovor) > 200 else f" {odgovor}")
# Vrati originalne postavke
ai_service.apply_settings(original_settings)
elif izbor == "3":
break
else:
print("❌ Nepoznata opcija.")
def pokreni_benchmark():
"""Pokreće benchmark testiranje."""
print("\n🏁 BENCHMARK TESTIRANJE")
print("=" * 50)
if not (Config.OPENAI_API_KEY and Config.GEMINI_API_KEY):
print("❌ Za benchmark su potrebna oba API ključa!")
print(" Trenutno imaš:")
if Config.OPENAI_API_KEY:
print(" ✓ OpenAI")
if Config.GEMINI_API_KEY:
print(" ✓ Gemini")
return
print("⚠️ Benchmark će pokrenuti seriju testova na oba servisa.")
print(" Ovo može potrajati nekoliko minuta.")
print("\nDa li želiš da nastaviš? (da/ne): ", end="")
if input().strip().lower() in ['da', 'd', 'yes', 'y']:
benchmark = AIBenchmark()
results_file = benchmark.run_full_benchmark()
if results_file:
print(f"\n📁 Detaljni rezultati sačuvani u: {results_file}")
def kontinuirani_razgovor():
"""Omogućava kontinuiranu konverzaciju sa Učiteljem Vasom."""
print("\n💬 KONTINUIRANI RAZGOVOR SA UČITELJEM VASOM")
print("=" * 50)
print("Sada možeš da razgovaraš sa mnom kao sa pravim učiteljem!")
print("Pamtiću kontekst našeg razgovora.")
print("Kucaj 'izlaz' ili 'exit' kada želiš da završiš razgovor.\n")
# Istorija razgovora
conversation_history = []
while True:
# Korisnikov unos
pitanje = input("👤 Ti: ").strip()
# Proveri da li korisnik želi da izađe
if pitanje.lower() in ['izlaz', 'exit', 'kraj', 'quit']:
print("\n👋 Hvala na razgovoru! Vraćam te u glavni meni.\n")
break
if not pitanje:
print("💭 Molim te, postavi pitanje ili napiši komentar.\n")
continue
# Dodaj korisnikovo pitanje u istoriju
conversation_history.append({
"role": "user",
"content": pitanje
})
print("\n🤖 Učitelj Vasa: ", end="", flush=True)
try:
if ai_service:
# Pripremi system prompt sa kontekstom
system_prompt_with_context = VASA_LICNOST + "\n\nVodi računa o kontekstu prethodnog razgovora."
# Koristi istoriju razgovora
odgovor = ai_service.pozovi_sa_istorijom([
{"role": "system", "content": system_prompt_with_context},
*conversation_history
])
# Dodaj Vasin odgovor u istoriju
conversation_history.append({
"role": "assistant",
"content": odgovor
})
# Ograniči istoriju na poslednjih 10 razmena (20 poruka)
if len(conversation_history) > 20:
conversation_history = conversation_history[-20:]
else:
# Fallback na simulaciju
odgovor = simuliraj_ai_odgovor(pitanje)
print(odgovor)
except Exception as e:
print(f"\n❌ Greška: {e}")
print("Pokušaj ponovo sa drugim pitanjem.")
print() # Prazan red za preglednost
def prikazi_ai_status():
"""Prikazuje trenutni status AI servisa."""
print("\n🔍 STATUS AI SERVISA")
print("=" * 50)
# Trenutni provider
print(f"📡 Trenutni provider: {Config.AI_PROVIDER.upper()}")
# Status servisa
if ai_service:
print("✅ AI servis je aktivan")
# Trenutne postavke
settings = ai_service.get_current_settings()
print(f"🤖 Model: {settings.get('model', 'nepoznat')}")
print(f"🌡️ Temperature: {settings.get('temperature', 'N/A')}")
print(f"📏 Max tokena: {settings.get('max_tokens', 'N/A')}")
# Test konekcije
print("\n🔌 Testiram konekciju...")
if ai_service.test_konekcija():
print("✅ Konekcija sa AI servisom je stabilna!")
else:
print("❌ Problem sa konekcijom. Proveri API ključ i internet vezu.")
else:
print("❌ AI servis nije aktivan")
print("📚 Koristim simulaciju umesto pravog AI-ja")
# Dostupni provideri
print("\n📋 Dostupni provideri:")
if Config.OPENAI_API_KEY:
print(" ✓ OpenAI")
else:
print(" ✗ OpenAI (nedostaje API ključ)")
if Config.GEMINI_API_KEY:
print(" ✓ Gemini")
else:
print(" ✗ Gemini (nedostaje API ključ)")
print() # Prazan red
def promeni_ai_servis():
"""Omogućava promenu AI servisa tokom rada."""
global ai_service
print("\n🔄 PROMENA AI SERVISA")
print("=" * 50)
# Prikaži trenutni servis
print(f"Trenutno koristiš: {Config.AI_PROVIDER.upper()}")
# Proveri dostupne opcije
dostupni = []
if Config.OPENAI_API_KEY:
dostupni.append("openai")
if Config.GEMINI_API_KEY:
dostupni.append("gemini")
if len(dostupni) < 2:
print("\n⚠️ Nemaš konfigurisan drugi AI servis!")
print("Potreban ti je API ključ za oba servisa da bi mogao da menjaš između njih.")
return
# Ponudi opcije
print("\nDostupni servisi:")
for i, servis in enumerate(dostupni, 1):
print(f"{i}. {servis.upper()}")
# Zatraži izbor
try:
izbor = input("\nIzaberi servis (broj): ").strip()
idx = int(izbor) - 1
if 0 <= idx < len(dostupni):
novi_servis = dostupni[idx]
if novi_servis == Config.AI_PROVIDER:
print("ℹ️ Već koristiš taj servis!")
return
# Promeni servis
Config.AI_PROVIDER = novi_servis
# Resetuj factory (forsiraj novo kreiranje)
AIServiceFactory.reset()
# Kreiraj novi servis
print(f"\n🔄 Prebacujem na {novi_servis.upper()}...")
try:
ai_service = AIServiceFactory.get_service()
print(f"✅ Uspešno prebačeno na {novi_servis.upper()}!")
# Test konekcije
if ai_service.test_konekcija():
print("✅ Novi servis radi perfektno!")
else:
print("⚠️ Servis je kreiran ali konekcija nije stabilna.")
except Exception as e:
print(f"❌ Greška pri prebacivanju: {e}")
print("Vraćam se na prethodni servis...")
# Vrati na stari servis ako ne uspe
Config.AI_PROVIDER = "openai" if novi_servis == "gemini" else "gemini"
AIServiceFactory.reset()
ai_service = AIServiceFactory.get_service()
else:
print("❌ Nevaljan izbor!")
except ValueError:
print("❌ Molim te unesi broj!")
except Exception as e:
print(f"❌ Greška: {e}")
def glavni_meni_profilisanje():
"""Vraća prošireni glavni meni."""
meni = """
Šta želiš da uradiš?
1. Pozdravi me
2. Predstavi se
3. Postavi pitanje Učitelju Vasi
4. Razgovaraj sa Vasom (kontinuirani mod)
5. Proveri AI status
6. Promeni AI servis
7. 📊 Prikaži performanse
8. 🎯 Upravljaj profilima
9. 🏁 Pokreni benchmark
10. Izađi
Tvoj izbor: """
return meni
def pokreni_vasu():
"""Pokreće glavnu petlju programa Učitelj Vasa."""
# Inicijalizuj AI servis
ai_dostupan = inicijalizuj_ai_servis()
print("\n" + "🎓" * 25)
print(pozdrav())
if ai_dostupan:
provider_info = {
'openai': "✨ Povezan sa OpenAI GPT - najpoznatiji AI model!",
'gemini': "✨ Povezan sa Google Gemini - moćan i besplatan!"
}
print(provider_info.get(Config.AI_PROVIDER.lower(), "✨ AI je spreman!"))
print("🎯 Automatska optimizacija je UKLJUČENA")
else:
print("📚 Radim u offline modu sa simulacijom.")
print("🎓" * 25 + "\n")
# Glavna petlja programa
while True:
print(glavni_meni_profilisanje())
izbor = input().strip()
if izbor == "1":
print("\n" + pozdrav() + "\n")
elif izbor == "2":
print("\n" + predstavi_se() + "\n")
elif izbor == "3":
print("\n💭 Postavi mi bilo koje pitanje o programiranju:")
pitanje = input("👤 Ti: ").strip()
if pitanje:
print("\n🤖 Učitelj Vasa: ", end="", flush=True)
odgovor = postavi_pitanje_vasi(pitanje)
print(odgovor)
# Prikaži metrike ako postoje
if current_profile and ai_service:
settings = ai_service.get_current_settings()
print(f"\n📊 [Parametri: temp={settings['temperature']}, "
f"max_tokens={settings['max_tokens']}]")
else:
print("\n❌ Nisi uneo pitanje.")
elif izbor == "4":
kontinuirani_razgovor()
elif izbor == "5":
prikazi_ai_status()
elif izbor == "6":
promeni_ai_servis()
elif izbor == "7":
prikazi_performanse()
elif izbor == "8":
upravljanje_profilima()
elif izbor == "9":
pokreni_benchmark()
elif izbor == "10":
print("\nHvala što si koristio Učitelja Vasu! ")
print("Nastavi sa učenjem i ne zaboravi - svaki ekspert je nekad bio početnik! 🌟")
# Prikaži finalne statistike ako postoje
if ai_service and len(tracker.all_metrics) > 0:
print("\n📊 FINALNE STATISTIKE SESIJE:")
print(tracker.compare_providers())
break
else:
print("\n❌ Nepoznata opcija. Pokušaj ponovo.\n")
print("\nProgram završen. Srećno sa programiranjem! 👋")
if __name__ == "__main__":
pokreni_vasu()
Česte greške i rešenja
❌ GREŠKA:JSONDecodeError pri učitavanju performance podataka
💡 REŠENJE:
- JSON fajl je možda oštećen. Obriši
data/ai_performance_data.json - Tracker će automatski kreirati novi fajl
- Gemini koristi
generation_configumesto direktnih parametara - Naš kod već rukuje ovim u
apply_settingsmetodi
- Retry (Pokušaj ponovo): Ne odustaj posle prve greške.
- Exponential Backoff (Eksponencijalno povećanje pauze): Ako prvi ponovni pokušaj ne uspe, čekaj duže pre sledećeg. Npr. čekaj 1s, pa 2s, pa 4s, pa 8s… Ovo daje serveru “prostora da diše”.
- Jitter (Nasumičnost): Ako svi korisnici koji dobiju grešku koriste isti interval čekanja (npr. svi čekaju tačno 1s), onda će svi ponovo poslati zahtev u isto vreme! “Jitter” dodaje malu nasumičnu vrednost vremenu čekanja (npr. čekaj 1s + 0.3s, sledeći put 1s + 0.1s). Ovo raspoređuje zahteve i sprečava nove “stampede”.
time.sleep(2) ili smanji broj test pitanja u TEST_QUESTIONS.
❌ GREŠKA: Performanse pokazuju 0 poziva iako sam koristio AI
💡 REŠENJE:
- Proveri da li si ažurirao oba servisa sa tracker kodom
- Restartuj program nakon izmena
- Analizator se oslanja na ključne reči
- Možeš proširiti liste ključnih reči u
analyze_questionmetodi
Proveri svoje razumevanje
[NIVO 1]:- Šta meri performance tracker?
- Zašto različita pitanja zahtevaju različite temperature?
- Kako profili pomažu u optimizaciji?
- Šta je benchmark i zašto je koristan?
- Kako bi dodao novi profil za matematičke zadatke?
- Zašto koristimo Singleton pattern u trackeru?
- Kako bi implementirao caching često postavljanih pitanja?
- Koje metrike bi dodao u performance tracker?
- Kako bi napravio da se profili čuvaju između sesija?
- (Nivo 1) Šta označava pojam success_rate u tracker-u?
- (Nivo 2) Koji bi dizajn-obrazac primenio da automatski prebacuješ na rezervni AI servis ako
success_ratepadne ispod 90%? Objasni jednom rečenicom.
Ažuriranje dokumentacije
Ažuriraj README.md:## 🚀 Trenutni Status
- ✅ Dan -3: Python 3.13+ instaliran
- ✅ Dan -2: PyCharm unified edition podešen
- ✅ Dan -1: GitHub repository kreiran
- ✅ Dan 0: Profesionalna struktura projekta
- ✅ Dan 1: Prvi Python moduli - Vasa može da pozdravi!
- ✅ Dan 2: Razumevanje AI API-ja - simulacija komunikacije
- ✅ Dan 3: Multi-provider podrška - OpenAI i Gemini
- ✅ Dan 4: Prvi AI poziv - univerzalni sistem sa SSL fix-om
- ✅ Dan 5: Profilisanje i optimizacija - automatski izbor najboljih postavki! 📊
- ⏳ Dan 6: Rukovanje greškama i resilijentnost (sutra)
## 🎯 Trenutne mogućnosti
Učitelj Vasa sada može:
- ✨ Odgovarati koristeći OpenAI ili Gemini
- 🔄 Prebacivati između servisa tokom rada
- 💬 Voditi kontinuirane razgovore
- 📊 **NOVO**: Meriti performanse svakog poziva
- 🎯 **NOVO**: Automatski optimizovati parametre prema tipu pitanja
- 🏁 **NOVO**: Pokretati benchmark testove
- 📈 **NOVO**: Generisati izveštaje o performansama
- 🎨 **NOVO**: Koristiti 7 različitih profila rada
## 📊 Optimizacioni profili
- **Brzi odgovor**: Kratki, direktni odgovori (temp: 0.3, max: 100)
- **Detaljno objašnjenje**: Opširna objašnjenja (temp: 0.7, max: 500)
- **Generisanje koda**: Precizno, bez greške (temp: 0.2, max: 400)
- **Kreativno pisanje**: Maštoviti sadržaj (temp: 1.2, max: 600)
- **Debug pomoć**: Sistematska analiza (temp: 0.3, max: 300)
- **Prevođenje**: Tačni prevodi (temp: 0.1, max: 200)
- **Rezimiranje**: Sažeti prikazi (temp: 0.4, max: 200)
Dodaj u docs/development_log.md:
## Dan 5: Profilisanje AI servisa i optimizacija (17.06.2025)
### Šta je urađeno:
- ✅ Kreiran PerformanceTracker za merenje metrika
- ✅ Implementiran dekorator pattern za transparentno praćenje
- ✅ Kreiran sistem optimizacionih profila (7 profila)
- ✅ ProfileManager automatski analizira pitanja
- ✅ Integrisan tracker sa oba AI servisa
- ✅ Kreiran AIBenchmark sistem za poređenje
- ✅ Dodata apply_settings metoda u BaseAIService
- ✅ main.py prošireh sa novim opcijama
### Naučene lekcije:
- Različiti zadaci zahtevaju različite AI parametre
- Temperature kontroliše kreativnost vs konzistentnost
- Merenje performansi pomaže u donošenju odluka
- Automatska optimizacija poboljšava korisničko iskustvo
- Benchmark testovi otkrivaju prednosti svakog servisa
### Problemi i rešenja:
- **Problem**: Kako pratiti performanse bez menjanja postojećeg koda?
- **Rešenje**: Dekorator pattern omogućava transparentno praćenje
- **Problem**: Gemini i OpenAI koriste različite načine za postavke
- **Rešenje**: apply_settings metoda sa provider-specific logikom
### Testiranje:
- Quick answer profil: 3x brži odgovori
- Code generation: Smanjena greške za 80%
- OpenAI bolji za kreativne zadatke
- Gemini brži za kratke odgovore
### Za sutra (Dan 6):
- Rukovanje greškama i retry logika
- Fallback strategije
- Circuit breaker pattern
Git commit za danas
git add .
git commit -m "Dan 5: Dodato profilisanje, optimizacija i benchmark sistem!"
git push
ČESTITAM! 🎉 Učitelj Vasa sada ima sofisticirani sistem za analizu performansi i automatsku optimizaciju! Može da:
- Meri svaki AI poziv i generiše statistike
- Automatski bira najbolje parametre za tip pitanja
- Poredi performanse između servisa
- Pokreće benchmark testove
Sutra Učitelj Vasa uči
Sutra ćemo se fokusirati na pouzdanost i otpornost sistema. Naučićeš kako da implementiraš retry logiku za privremene greške, fallback strategije kada glavni servis ne radi, circuit breaker pattern za zaštitu od kaskadnih padova, i sistem za graceful degradation. Učitelj Vasa će postati stabilan i pouzdan!📚 REČNIK DANAŠNJE LEKCIJE:
- Profilisanje (Profiling): Analiza performansi programa
- Latencija: Vreme od zahteva do početka odgovora
- Throughput: Količina podataka obrađenih po jedinici vremena
- Temperature: AI parametar koji kontroliše kreativnost
- Benchmark: Standardizovani test za poređenje performansi
- Dekorator pattern: Način dodavanja funkcionalnosti bez menjanja koda
- Singleton pattern: Obrazac koji osigurava samo jednu instancu
- Model routing: Automatski izbor najboljeg AI modela za zadatak
- Aspect-Oriented Programming: Programiranje koje odvaja cross-cutting concerns
- Graceful degradation: Postupno smanjenje funkcionalnosti umesto potpunog pada