Curs 2 — Ecosistemul de modele¶
Scopul acestui notebook: testăm 2-3 modele diferite pe același input și alegem modelul potrivit pentru proiect.
Vom folosi:
- Gemini — providerul principal, prin cheia obținută din Google AI Studio.
- OpenRouter — provider alternativ, util pentru comparație și backup când Gemini are limite de quota.
OpenRouter — de unde luăm cheia¶
- Intră pe https://openrouter.ai/
- Creează cont sau autentifică-te.
- Mergi la Keys.
- Creează un nou API key.
- Copiază cheia în fișierul
.env:
env
OPENROUTER_API_KEY=pune-cheia-ta-aici
---
from openai import OpenAI
from dotenv import load_dotenv
import os
import json
1. Configurare — mai multe modele¶
MODELE = [
("gemini", "gemini-2.5-flash-lite", "Gemini 2.5 Flash Lite"),
("gemini", "gemini-2.5-flash", "Gemini 2.5 Flash"),
("openrouter", "openrouter/free", "OpenRouter Free"),
]
print("Modele pregătite:", [nume for _, _, nume in MODELE])
Modele pregătite: ['Gemini 2.5 Flash Lite', 'Gemini 2.5 Flash', 'OpenRouter Free']
# Configurăm providerii și cheile API din fișierul .env
load_dotenv()
BASE_URLS = {
"gemini": "https://generativelanguage.googleapis.com/v1beta/openai/",
"openrouter": "https://openrouter.ai/api/v1"
}
API_KEYS = {
"gemini": os.getenv("GEMINI_API_KEY"),
"openrouter": os.getenv("OPENROUTER_API_KEY")
}
def make_client(provider):
"""Creează clientul API pentru providerul ales."""
return OpenAI(
api_key=API_KEYS[provider],
base_url=BASE_URLS[provider]
)
2. Funcție helper — trimitem același prompt la orice model¶
În loc să scriem același cod de 3 ori, facem o funcție.
# varianta minimala
# fara functie
client = make_client("gemini")
prompt = "Explică în 2 propoziții ce este un LLM."
response = client.chat.completions.create(
model="gemini-2.5-flash-lite",
messages=[
{"role": "user", "content": prompt}
]
)
print(response.choices[0].message.content)
# cu functie
def ask(provider, model, prompt):
client = make_client(provider)
messages = [
{"role": "user", "content": prompt}
]
response = client.chat.completions.create(
model=model,
messages=messages
)
return response.choices[0].message.content
# iar functia poate fi apelata astfel:
raspuns = ask(
provider="gemini",
model="gemini-2.5-flash-lite",
prompt="Explică în 2 propoziții ce este un LLM."
)
print(raspuns)
from openai import RateLimitError, APIError, AuthenticationError
import json
def ask(provider, model, prompt, system=None, temperature=0.7, json_schema=None):
"""Trimite un prompt la model. Poate returna text simplu sau JSON structurat."""
client = make_client(provider)
messages = []
if system:
messages.append({"role": "system", "content": system})
messages.append({"role": "user", "content": prompt})
extra_args = {}
if json_schema:
extra_args["response_format"] = {
"type": "json_schema",
"json_schema": json_schema
}
try:
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
**extra_args
)
text = response.choices[0].message.content.strip()
if json_schema:
return json.loads(text)
return text
except RateLimitError:
return f"[Eroare: quota/rate limit pentru modelul {model}.]"
except AuthenticationError:
return "[Eroare: API key invalidă sau lipsă. Verifică .env.]"
except APIError as e:
return f"[Eroare API: {e}]"
except Exception as e:
return f"[Eroare: {type(e).__name__} — {e}]"
3. Test 1 — Calitatea pe limba română¶
Testăm dacă modelele înțeleg și răspund corect în română.
PROMPT_RO = """
Rezumă în exact 2 propoziții scurte, în română, principalele schimbări din politica românească din ultimii 5 ani.
Maximum 80 de cuvinte.
Răspunde pe baza faptelor, fără opinii politice.
"""
print("=" * 60)
for client, model_id, nume in MODELE:
print(f"\n[ {nume} ]")
raspuns = ask(
client=client,
model=model_id,
prompt=PROMPT_RO,
temperature=0.2,
)
print(raspuns)
print()
============================================================ [ Gemini 2.5 Flash Lite ] În ultimii 5 ani, politica românească a fost marcată de o instabilitate guvernamentală frecventă, cu multiple schimbări de premier și coaliții. De asemenea, s-au intensificat dezbaterile și acțiunile legate de combaterea corupției și reforma justiției. [ Gemini 2.5 Flash ] Ultimii cinci ani au fost marcați de o dinamică guvernamentală intensă, cu multiple schimbări de coaliții și prim-miniștri. O transformare majoră a fost formarea unei coaliții guvernamentale între partidele tradițional rivale, PSD și PNL. [ OpenRouter Free ] În 2019, coaliția PSD-PNL a câștigat alegerile, dar a fost înfrângată în 2020. În 2021, PSD a câștigat din nou, dar a fost înfrângată în 2022 de o coaliție cu PSM, iar în 2023-2024 s-au produs schimbări în lideratele și politicile.
4. Test 2 — Urmează instrucțiunile din system prompt+ adnotare¶
Vedem dacă modelele respectă rolul dat prin system.
SYSTEM = """
Ești un asistent de cercetare care adnotează comentarii politice.
Răspunzi scurt, clar și nu inventezi informații.
"""
PROMPT = """
Analizează următorul comentariu politic:
"Toți politicienii fură, iar oamenii simpli plătesc nota. Nimeni nu mai ascultă poporul."
Răspunde în 4 linii:
Ton:
Emoție dominantă:
Țintă principală:
Populism: da/nu
"""
for provider, model, name in MODELE:
print("\n---", name, "---")
print(ask(
provider=provider,
model=model,
prompt=PROMPT,
system=SYSTEM,
temperature=0
))
--- Gemini 2.5 Flash Lite --- Ton: Critic, cinic Emoție dominantă: Frustrare, neîncredere Țintă principală: Clasa politică Populism: da --- Gemini 2.5 Flash --- Ton: Acuzator, cinic. Emoție dominantă: Frustrare. Țintă principală: Clasa politică. Populism: da --- OpenRouter Free --- Ton: angă. Emoție dominantă: angă. Țintă principală: Populism. Populism: da/nu.
5. Test 3 — Output structurat (JSON)¶
Agenții noștri vor trebui să returneze date structurate. Testăm dacă modelele pot produce JSON valid la cerere.
SCHEMA_ADNOTARE = {
"name": "adnotare_comentariu_politic",
"schema": {
"type": "object",
"properties": {
"ton": {
"type": "string",
"enum": ["pozitiv", "negativ", "neutru"]
},
"emotie_dominanta": {
"type": "string",
"enum": ["furie", "frica", "speranta", "dezamagire", "ironie", "neutru"]
},
"tinta_principala": {
"type": "string"
},
"populism": {
"type": "boolean"
},
"explicatie_scurta": {
"type": "string"
}
},
"required": [
"ton",
"emotie_dominanta",
"tinta_principala",
"populism",
"explicatie_scurta"
],
"additionalProperties": False
}
}
COMENTARIU = "Toți politicienii fură, iar oamenii simpli plătesc nota. Nimeni nu mai ascultă poporul."
for client, model_id, nume in MODELE:
print("=" * 60)
print(f"\n[ {nume} ]")
try:
raspuns = client.chat.completions.create(
model=model_id,
messages=[
{
"role": "system",
"content": "Ești un asistent de cercetare care adnotează comentarii politice."
},
{
"role": "user",
"content": f"Adnotează următorul comentariu politic: {COMENTARIU}"
}
],
temperature=0.1,
response_format={
"type": "json_schema",
"json_schema": SCHEMA_ADNOTARE
}
)
text = raspuns.choices[0].message.content
date = json.loads(text)
print(date)
except Exception as e:
print(f"[Eroare pentru {model_id}: {type(e).__name__} — {e}]")
print()
============================================================
[ Gemini 2.5 Flash Lite ]
{'ton': 'negativ', 'emotie_dominanta': 'furie', 'tinta_principala': 'politicieni', 'populism': True, 'explicatie_scurta': "Comentariul exprimă frustrare și neîncredere față de politicieni, sugerând că aceștia sunt corupți și nu reprezintă interesele cetățenilor obișnuiți. Afirmația 'Nimeni nu mai ascultă poporul' este o retorică populistă clasică."}
============================================================
[ Gemini 2.5 Flash ]
{'ton': 'negativ', 'emotie_dominanta': 'furie', 'tinta_principala': 'politicienii', 'populism': True, 'explicatie_scurta': "Comentariul exprimă furie și dezamăgire față de clasa politică, acuzând-o de corupție și de ignorarea poporului, folosind un limbaj populist prin contrastul 'oameni simpli' vs. 'politicieni'."}
============================================================
[ OpenRouter Free ]
[Eroare pentru openrouter/free: TypeError — 'NoneType' object is not subscriptable]
6. Test 4 — Stabilitate la temperature diferite¶
Un model bun pentru agenți trebuie să fie stabil — același input, răspunsuri similare. Testăm cu Gemini (poți schimba cu orice model).
PROMPT_STAB = """
Curtea Constituțională a anulat alegerile.
Explică în 2 propoziții ce poate însemna acest lucru pentru viața politică.
Răspunde neutru, fără opinii partizane.
"""
TEMPERATURI = [0.1, 0.7, 1.2]
print("=" * 60)
print("[ Test 4 — stabilitate: același prompt, temperaturi diferite ]")
for client, model_id, nume in MODELE:
print("\n" + "=" * 60)
print(f"[ {nume} ]")
for temp in TEMPERATURI:
raspuns = ask(
client=client,
model=model_id,
prompt=PROMPT_STAB,
temperature=temp
)
print(f"\ntemperature={temp}:")
print(raspuns)
============================================================ [ Gemini — același prompt, temperaturi diferite ] temperature=0.1: Anularea alegerilor de către Curtea Constituțională poate duce la organizarea de noi alegeri, ceea ce implică o perioadă de incertitudine politică și o posibilă reconfigurare a forțelor politice. Acest proces poate influența stabilitatea guvernamentală și direcția politicilor publice pe termen scurt și mediu. temperature=0.7: Anularea alegerilor de către Curtea Constituțională poate duce la organizarea de noi alegeri, ceea ce implică o perioadă de incertitudine politică și potențiale reconfigurări ale forțelor politice. Acest proces poate influența stabilitatea guvernamentală și agenda legislativă pe termen scurt și mediu. temperature=1.2: Anularea alegerilor de către Curtea Constituțională poate duce la organizarea unor noi alegeri într-un termen stabilit prin lege. Această situație poate genera o perioadă de incertitudine politică și poate influența procesul de formare a unei noi majorități guvernamentale.
7. Alegerea modelului pentru proiect¶
Completați tabelul după testele de mai sus. Nu căutați „cel mai bun model” în general, ci modelul cel mai potrivit pentru proiectul vostru.
| Model | Răspunde bine în română? | Respectă instrucțiunile? | Merge pentru adnotare? | Are erori / quota? | Observație scurtă |
|---|---|---|---|---|---|
| Gemini 2.5 Flash Lite | da / nu / parțial | da / nu / parțial | da / nu / parțial | da / nu | |
| OpenRouter Free | da / nu / parțial | da / nu / parțial | da / nu / parțial | da / nu | |
| Llama / alt model testat | da / nu / parțial | da / nu / parțial | da / nu / parțial | da / nu |
Decizie¶
Model principal ales:
Model de rezervă:
Temperature recomandată:
De ce am ales acest model?
Scrieți 2-3 propoziții. Menționați calitatea răspunsului, stabilitatea și dacă modelul poate fi folosit pentru adnotarea comentariilor.
8. Configurația finală a proiectului¶
putem să copiem asta in core/config.py
# core/config.py
# Configurația modelului ales de echipă după testele din Cursul 2.
# Nu puneți chei API aici. Cheile rămân doar în fișierul local .env.
PROVIDER_PRINCIPAL = "gemini"
MODEL_PRINCIPAL = "gemini-2.5-flash-lite"
PROVIDER_FALLBACK = "openrouter"
MODEL_FALLBACK = "openrouter/free"
TEMPERATURE = 0.2
Livrabile C2¶
Până la cursul următor:
- Notebook completat cu 2-3 modele testate
- Matricea de decizie completată cu observații reale
- README actualizat cu modelul ales și justificarea
.envconfigurat cu cheia pentru modelul ales