curs final · C8

EchoChamber Studio — hartă vizuală și conceptuală

Acest material este harta completă a aplicației finale: cum se leagă pipeline-ul offline, vectorstore-urile, core/retriever.py, core/agent.py, core/graph.py, app/app.py, rolurile YAML și interfața Gradio. Nu este un rezumat și nu este un README — este materialul de curs complet, vizual și explicativ, care leagă fiecare componentă de etapa în care a fost construită (C1–C8).

echochamber-app/ C1–C8 → aplicație integrată 11 diagrame ~45 min

Ce trebuie să înțelegi la final

  • Aplicația are două faze: un pipeline offline care pregătește datele și indexurile, și un runtime Gradio care servește utilizatorul.
  • core/ conține logica reutilizabilă (retrieval, agent, orchestrare); app/ doar o expune într-o interfață — nu rescrie logica.
  • Știrea sau textul introdus este obiectul comentariului; corpusul recuperat din FAISS este context discursiv (ton, vocabular, poziționare), nu subiectul.
  • C8 nu introduce o tehnologie nouă; integrează componentele construite în C2–C7 într-un singur sistem coerent.
  • Orice eroare poate fi localizată cu o singură întrebare: problema este în date, în vectorstore, în agent, în graph sau în UI?
albastru = runtime / aplicație verde = date / pipeline offline amber = vectorstore / punte offline↔runtime roșu = orchestrare / dezbatere mov / C1–C7 = trasabilitatea cursurilor

Cum folosești acest material

  1. Citește mai întâi schema offline / runtime (secțiunea 02) — îți dă cadrul general.
  2. Verifică structura repo-ului (01) — unde se află fiecare piesă.
  3. Urmărește apelul unui agent (06) — cum se produce un răspuns.
  4. Urmărește fluxul de dezbatere (07) — cum devine multi-agent.
  5. Rulează notebook-ul C8 (C8_gradio_tutorial_echochamber.ipynb) — construiește app-ul incremental.
  6. Verifică aplicația finală cu lista de criterii (secțiunea 14).
Partea I · Arhitectura

Cum funcționează aplicația, vizual

De la API-ul YouTube la răspunsul afișat în Gradio. Fiecare diagramă arată exact ce intră și ce iese.

01Structura repo-ului

Această secțiune răspunde la întrebarea: Unde se află fiecare piesă a aplicației?

Trei zone, fiecare cu o responsabilitate clară: scripts/ + data/ pentru pipeline-ul offline, core/ pentru logica de business reutilizabilă, app/ pentru interfață. Albastru = folder, gri = fișier.

echochamber-app/ — structura (fără notebooks)
app/
└─ app.py                     # UI: 6 tab-uri — Setări·Chat·Rezumat·Agent·Toți agenții·Dezbatere

core/                         # logica de business (importată de app)
├─ agent.py                   # un agent: rol + retrieval + apel LLM (C6)
├─ graph.py                   # LangGraph: orchestrare multi-agent (C7)
├─ retriever.py               # FAISS retrieval pe vectorstore-ul unui agent (C5)
├─ config.py                  # referință (varianta minimă a app-ului nu îl mai folosește)
└─ metrics.py                 # placeholder

scripts/                      # pipeline CLI: collect → clean → annotate → vectorstore
├─ collect_youtube.py  clean_youtube.py
└─ annotate_axis.py    build_vectorstore.py

assets/
├─ roles/   roles.yaml   # cardurile agenților — sursa pentru app/agent
└─ vectorstores/<slug>/     index.faiss + index.pkl

data/ raw/ cleaned/ annotated/ bubbles/   # pipeline-ul de date
.env  # GEMINI / DEEPSEEK / YOUTUBE keys   requirements.txt
index.faiss vs index.pkl. index.faiss conține vectorii numerici (reprezentarea matematică a textelor, pentru căutare rapidă). index.pkl conține textele originale + metadatele (sursă, agent). FAISS găsește care vectori sunt apropiați; pkl-ul spune ce text era acolo. Ai nevoie de ambele ca să recuperezi un fragment lizibil.

Regula slug-ului. Aceeași valoare (anti_sistem, conspirationist, …) apare în 3 locuri și trebuie să fie identică: cheia din assets/roles/roles.yaml, fișierul data/bubbles/<slug>.jsonl și folderul assets/vectorstores/<slug>/. De ce contează: slug-ul este cheia care leagă cele trei. Dacă într-un loc scrie anti_sistem și în altul antisistem, retrieverul caută un folder care nu există și pică cu FileNotFoundError — agentul nu mai are corpus.

02Cele două faze

Această secțiune răspunde la întrebarea: Ce se întâmplă offline și ce se întâmplă când aplicația rulează?

Sistemul are două faze separate. Faza A (offline) se rulează o singură dată și produce indexurile FAISS. Faza B (runtime) servește utilizatorul prin Gradio. Singura legătură dintre ele: vectorstore-urile.

Ce înseamnă „runtime". Runtime = momentul în care aplicația rulează și răspunde la acțiunile utilizatorului (apeși un buton → se execută cod → vezi un răspuns). Opusul e offline: pregătirea făcută înainte, o singură dată, fără utilizator în fața ecranului.

Faza A · offline

Pipeline de date

YouTube → colectare → curățare → adnotare → bule → vectorstore. Rulat manual din linia de comandă. Cod în scripts/, rezultate în data/ și assets/vectorstores/.

Faza B · runtime

Aplicația Gradio

6 tab-uri. Tab-ul Setări încarcă o știre; restul tab-urilor o folosesc automat ca obiect al comentariului. Cod în app/app.py + core/.

Puntea unică. Faza A produce assets/vectorstores/<slug>/. Faza B le citește la fiecare cerere prin core/retriever.py. În rest, cele două faze sunt complet decuplate — poți reface pipeline-ul fără să atingi aplicația, și invers.

03Pipeline de date offline — fișier cu fișier

Această secțiune răspunde la întrebarea: Cum ajunge un comentariu brut de pe YouTube să fie un vectorstore căutabil?

Fiecare script consumă un fișier și produce altul. Dacă pipeline-ul se rupe, te uiți care fișier lipsește între două etape.

Întrebare
Cum se transformă datele, etapă cu etapă, până la vectorstore?
De observat
Lanțul liniar: fiecare cutie albastră (script) produce un fișier verde, care devine inputul scriptului următor. Punctele mov = config injectat.
În cod
scripts/collect_youtube.pyclean_youtube.pyannotate_axis.pybuild_vectorstore.py
YouTube Data API .env YOUTUBE_API_KEY prompts/ annotation_prompt.md collect_youtube.py data/raw/*.jsonl clean_youtube.py lungime · alpha-ratio · dedupe data/cleaned/*.jsonl annotate_axis.py LLM → target/stance/tone + 5 axe data/annotated/*.jsonl split manual pe agenți data/bubbles/<slug>.jsonl build_vectorstore.py MiniLM multilingv → IndexFlatIP vectorstores/<slug>/ index.faiss + index.pkl
script CLI fișier de date vectorstore (output final) config / prompt
Fig. 1 — Pipeline-ul offline. Săgeți pline = flux de date. Punctate mov = config injectat în scripturi.

04Blocuri de construcție (building blocks)

Această secțiune răspunde la întrebarea: Cum se compune app.py din straturi, și ce intră în fiecare strat?

app.py nu este o cutie neagră — se construiește în straturi, ca niște cărămizi. Fundația sunt modulele core/ din C5–C7. Peste ele, fiecare secțiune din fișier adaugă un strat, până la interfața care rulează.

Întrebare
Din ce straturi e făcut app.py și pe ce se sprijină fiecare?
De observat
Fundația = core/ (C5–C7). Săgețile din stânga = ce intră în fiecare strat. Săgețile verticale = fiecare strat se sprijină pe cel de sub el.
În cod
Secțiunile numerotate §1–§6 din app/app.py
§6 UI — aplicația care rulează logo + 6 gr.Interface în gr.Blocks + gr.Tabs + .render() temă gr.themes.Soft(orange) §4–5 Funcțiile celor 6 tab-uri setup · chat · summary · agent · all_agents · debate fiecare funcție = un tab §3 Apeluri LLM / backend ask_llm() · _agent() · _subject() lipiciul dintre UI și core/ §2 Config + stare partajată PROVIDERS · MODELS · ROLES · CFG · ART dropdown unic „provider|model" §1 Imports & setup aduce core/ + gradio în program FUNDAȚIA — core/ (construit în C5–C7) agent.py · graph.py · retriever.py roles.yaml · FAISS · vectorstores/ gr.themes (temă) cele 6 funcții-tab core.agent · core.graph .env (chei) · roles.yaml gradio·openai·yaml·requests·bs4 fiecare strat se așază peste cel de dedesubt ↑
fundație (C5–C7) §1 imports §2 config+stare §3 backend §4–5 tab-uri §6 UI
Fig. 2 — Aplicația ca turn de blocuri de construcție. Săgețile din stânga = ce intră în fiecare strat. Săgețile verticale = fiecare strat se sprijină pe cel de sub el.

Ce blocuri intră în fiecare tab

Fiecare tab e compus din aceleași tipuri de cărămizi, dar combinate diferit. Citește fiecare coloană de jos în sus: input → procesare → backend.

Întrebare
Ce module intră în fiecare dintre cele 6 tab-uri?
De observat
Aceleași 5 tipuri de cărămizi (input · stare · _subject · ask_llm · core.agent/graph) recombinate. Setări scrie starea; restul o citesc.
În cod
Funcțiile setup·chat·summary·agent·all_agents·debate din §4–5
Setări Chat Rezumat Agent Toți agenții Dezbatere input: URL +provider·model requests + bs4scrape știre scrie înCFG · ART input: prompt citește ART(știrea, dacă e) ask_llm()→ LLM direct fără input —doar ART prompt fix„rezumă în 5" ask_llm()→ LLM (t=0.2) input: subiect+ alege agent _subject()subiect+știre _agent()core.agent →retriever+roles+LLM input: subiect _subject()+ loop ROLES _agent() × No voce / roldin roles.yaml subiect + agenți+ nr. ture _subject()→ stimulus core.graphrun_thread()→ core.agent ×N(round-robin) ↓ rezultatul se întoarce în Gradio și se afișează în tab (text / markdown / carduri HTML) CFG (provider/model/temp) e citit de toate tab-urile care cheamă un model
input / LLM direct citește starea ART _subject / loop core.agent (RAG) core.graph (LangGraph)
Fig. 3 — Building blocks per tab. Aceleași cărămizi, combinate diferit. Setări scrie starea; restul o citesc și cheamă backend-ul potrivit.

De ce contează. Odată ce vezi cele 5 tipuri de cărămizi, oricare tab e doar o combinație de 3–4 dintre ele. Asta face codul ușor de citit și de predat: nu memorezi 6 tab-uri, înțelegi 5 piese.

05Cele 6 tab-uri — parcursul utilizatorului

Această secțiune răspunde la întrebarea: Ce face fiecare tab și de unde își ia datele?

Aplicația finală are 6 tab-uri: Setări, Chat, Rezumat, Agent, Toți agenții și Dezbatere. Nu există un sidebar separat — Setări este tab-ul care scrie starea partajată (CFG, ART); celelalte cinci o citesc. Fiecare tab este un gr.Interface.

Întrebare
Cine scrie starea și cine o citește?
De observat
Tab-ul Setări este singurul care scrie în CFG/ART. Celelalte 5 doar citesc starea și cheamă backend-ul corespunzător cursului lor.
În cod
setup() scrie; chat·summary·agent·all_agents·debate citesc
User · deschide app.py Setări provider·model + URL știre setup() CFG · ART provider/model/temp text/titlu știre scrie ChatQ&A pe știre / LLM Rezumatrezumă știrea Agentun agent RAG Toți agențiiloop pe roles.yaml DezbatereLangGraph citesc starea ask_llm() → LLM API (C2) core.agent (retriever+roles+LLM) C5+C6 core.graph C7 core.agent → retriever (FAISS) + roles.yaml + LLM reutilizează
Setări (scrie starea) LLM direct — C2 agent RAG — C5+C6 dezbatere — C7
Fig. 4 — Cele 6 tab-uri. Setări scrie CFG/ART; celelalte 5 citesc starea și cheamă backend-ul potrivit cursului lor.
TabFuncție în app.pyCe foloseșteCurs
Setărisetup()scrie CFG+ART; requests+bs4C2
Chatchat()ask_llm (răspuns pe știre, dacă există)C2
Rezumatsummary()ask_llm (t=0.2) pe ARTC2
Agentagent()_subject_agentcore.agentC5+C6
Toți agențiiall_agents()buclă pe ROLES_agentC6
Dezbateredebate()core.graph.run_threadC7

Regula _subject(). Dacă există ȘI o știre încărcată ȘI un subiect scris în tab → subiectul intră peste știre („discută subiectul X în contextul acestei știri"). Doar știre → se discută știrea. Doar subiect → se discută subiectul.

06Un apel agent

Această secțiune răspunde la întrebarea: Ce se întâmplă când utilizatorul apasă butonul Generează răspuns în tab-ul Agent?

Distincție conceptuală

știrea / textul introdus
obiectul despre care agentul trebuie să răspundă (subiectul concret).
corpusul recuperat prin FAISS
exemple de ton, vocabular și poziționare discursivă — stilul, nu subiectul.
roles.yaml
identitatea discursivă a agentului (cine vorbește și cu ce reguli).
LLM-ul
generatorul propriu-zis al răspunsului.
app.py
stratul care le conectează pe toate în interfață.

Citește traseul de mai jos având în minte distincția: agentul nu trebuie să comenteze generic corpusul; trebuie să reacționeze la știre, folosind corpusul recuperat doar ca stil și context discursiv, iar rolul ca poziție.

Întrebare
Ce traseu parcurge o cerere din tab-ul Agent?
De observat
app.py nu generează singur răspunsul: apelează core.agent, care citește rolul din roles.yaml, caută context în FAISS și trimite promptul la LLM. Răspunsul + contextul se întorc în UI.
În cod
agent()_agent()generate_agent_response() din core/agent.py
User app.py agent.py roles.yaml retriever FAISS LLM 1. subiect (_subject) + slug + provider + k 2. generate_agent_response(slug, …) 3. load_role(slug) 4. role.system 5. Retriever(slug).search(stimulus, k) 6. encode + index.search 7. top-k (texte + scoruri) 8. rag_text 9. invoke([SystemMessage(role), HumanMessage(prompt)]) 10. răspuns text 11. { response, rag_chunks } 12. comentariul afișat în tab
Fig. 5 — Secvența unui apel din tab-ul Agent. Săgeți pline = cereri; întrerupte verzi = răspunsuri. Subiectul (pasul 1) vine din _subject() — subiect peste știre.

Reutilizare, nu duplicare. Pașii 2–11 sunt exact ce reutilizează „Toți agenții" (în buclă peste roluri) și „Dezbatere" (prin core.graph). Logica RAG este scrisă o singură dată, în core/agent.py.

07Dezbatere multi-agent

Această secțiune răspunde la întrebarea: Cum se transformă un răspuns izolat într-un thread multi-agent?

Tab-ul Dezbatere ridică un nivel deasupra unui singur agent: router_node alege round-robin cine vorbește, fiecare agent_node reutilizează generate_agent_response() din secțiunea precedentă, apoi revine la router.

De ce core/graph.py nu trebuie să conțină UI. graph.py are o singură responsabilitate: orchestrarea (cine vorbește, în ce ordine, când se oprește). Dacă ar conține și cod de interfață, n-ai mai putea rula o dezbatere din linia de comandă sau dintr-un test, iar logica de orchestrare ar deveni imposibil de reutilizat. Separarea responsabilităților = fiecare modul face un singur lucru.

Întrebare
Cum devine un singur răspuns o conversație între agenți?
De observat
Routerul alege round-robin următorul agent; fiecare agent_node reutilizează exact apelul din secțiunea 06; bucla se închide când s-a atins numărul de intervenții.
În cod
run_thread() și route_decision() din core/graph.py
START router_node turn < total? agent_node(slug_A) anti_sistem agent_node(slug_B) conspirationist agent_node(slug_C) pro_european generate_ agent_response() din core/agent.py reutilizat (Fig. 5) agent → router END __end__
Fig. 6 — Dezbaterea multi-agent. Routerul alege round-robin: active_slugs[current_turn % len(active_slugs)]. Bucla continuă până la current_turn == total_turns.

08Imaginea de ansamblu — offline → runtime

Această secțiune răspunde la întrebarea: Cum se leagă tot lanțul, de la API extern la răspunsul afișat?

Cele două faze sunt separate; singura legătură este linia punctată amber: vectorstore-urile produse offline, citite de retriever la runtime.

Întrebare
Unde e granița dintre offline și runtime?
De observat
Cele două chenare punctate (verde = offline, albastru = runtime) nu se ating decât prin săgeata amber: vectorstore citit de retriever.
În cod
scripts/ + data/ (offline) vs app/ + core/ (runtime)
FAZA A — PIPELINE OFFLINE YouTube collect clean annotate bubbles/ build_vs vectorstores FAZA B — RUNTIME (6 TAB-URI) User app.pyGradio · 6 tab-uri Setări → CFG/ART Chat / Rezumat → LLM Agent / Toți → core.agent Dezbatere → core.graph core.agentnod central core.graph retriever LLMAPI roles.yaml vectorstore citit de retriever
Fig. 7 — Imaginea completă. Faza A (verde) construiește vectorstore-urile o singură dată. Faza B (albastru), cu 6 tab-uri, le consumă la fiecare cerere. Puntea galbenă = singura legătură.

09Dependențe între module — cine importă pe cine

Această secțiune răspunde la întrebarea: Ce importă fiecare modul și de ce app.py nu trebuie să fie gros?

Săgeata = „importă / depinde de". app.py importă direct doar core.agent și core.graph (plus gradio/openai/yaml/requests/bs4). Restul logicii e ascuns în spatele lor.

Întrebare
Cine depinde de cine, și unde stă logica grea?
De observat
core/agent.py e nodul către care converg săgețile; app.py e doar vârful subțire.
În cod
Liniile import din capul app/app.py și core/graph.py
app/app.py Gradio UI · 6 tab-uri core/agent.py nod central (C6) core/graph.py LangGraph (C7) core/retriever.py roles.yaml vectorstores/<slug> index.faiss + pkl .env · API keys gradio openai langgraph faiss + ST yaml·requests·bs4 orchestrază
entry point modul core LangGraph asset chei API
Fig. 8 — Graful de import. app.py atinge doar core.agent + core.graph. Retrieverul e singurul care deschide vectorstore-urile.
De ce core/agent.py este nodul central. Și un singur agent (tab Agent), și „Toți agenții" (buclă), și „Dezbatere" (prin graph.py) trec prin generate_agent_response(). Scrii logica RAG o dată, o reutilizezi de trei ori. Dacă o repari acolo, se repară peste tot.
De ce app.py trebuie să fie un strat subțire. Dacă rescrii retrieval-ul sau prompting-ul în app.py, ai două copii ale aceleiași logici care se desincronizează. app.py doar cheamă core/ și afișează rezultatul. Schimbi backend-ul → UI rămâne neatins; schimbi UI-ul → backend rămâne neatins.

10Debugging: unde cauți problema?

Această secțiune răspunde la întrebarea: Aplicația nu merge — în care strat caut?

Localizezi problema după simptom. Fiecare simptom corespunde unui strat anume — nu căuta la întâmplare.

SimptomProbabil problema este înCe verifici
Aplicația nu porneșteapp/app.py sau importurirulează python -m app.app și citește mesajul de eroare (import lipsă, sintaxă)
Agentul nu găsește contextvectorstore / retrieverassets/vectorstores/<slug>/index.faiss există? Slug-ul e identic peste tot?
Agentul răspunde genericprompt / agentcore/agent.py — separarea știre vs corpus; subiectul ajunge ca obiect, nu corpusul
Dezbaterea nu porneștecore/graph.pyrun_thread() — minim 2 agenți, numărul de intervenții > 0
Un agent lipsește din listăroles.yaml / slugnumele agentului în roles.yaml, data/bubbles/ și vectorstores/ — identic?
Eroare de API.env / providercheia API există în .env și corespunde providerului ales în Setări
Partea II · Referință

Referință tehnică rapidă

Tabelul de import, convențiile care nu se negociază, comenzile de rulare și criteriile de verificare.

11Cum se leagă fișierele

Această secțiune răspunde la întrebarea: Ce importă fiecare fișier și cine îl folosește?

ModulImportăFolosit de
core/retriever.pyfaiss, sentence_transformerscore/agent.py
core/agent.pyretriever, langchain_openai, yamlapp.py, graph.py
core/graph.pycore/agent.py, langgraphapp.py (tab Dezbatere)
scripts/build_vectorstore.pyfaiss, sentence_transformersrulat manual offline
scripts/annotate_axis.pyopenai, annotation_prompt.mdrulat manual offline
app/app.pycore.agent, core.graph, gradio, openai, yaml, requests, bs4python -m app.app
Trasabilitate concept → fișier. RAG → core/agent.py · retrieval → core/retriever.py · vectorstore → assets/vectorstores/<slug>/ · roluri → assets/roles/roles.yaml · dezbatere → core/graph.py · interfață → app/app.py · pipeline offline → scripts/ + data/.

12Convenții cheie

Această secțiune răspunde la întrebarea: Ce reguli nu se negociază în acest proiect?

Slug

Identitatea agentului

Aceeași valoare în 3 locuri: cheia din roles.yaml, data/bubbles/<slug>.jsonl, vectorstores/<slug>/. Inconsistent → FileNotFoundError.

Embedding

Model unic

paraphrase-multilingual-MiniLM-L12-v2, 384 dim, la build ȘI la search. Schimbarea impune rebuild la toate vectorstore-urile.

Provider · Model

Un singur dropdown

Opțiunile sunt "provider|model" (ex. gemini|gemini-2.5-flash). Imposibil ca UI-ul să cuprindă o combinație nepotrivită.

Stare partajată

CFG + ART (modul)

Două dicționare la nivel de modul. Tab-ul Setări scrie, restul citesc. (Alternativa „canonică" Gradio = gr.State; varianta minimă alege simplitatea.)

13Entry points — cum se rulează

Această secțiune răspunde la întrebarea: Ce comandă pornește fiecare parte a sistemului?

comenzi terminal
# Aplicația (Gradio UI · 6 tab-uri)
python -m app.app          # sau:  python app/app.py

# Un singur agent RAG, din terminal
python -m core.agent --agent anti_sistem --text "..." --provider gemini --k 5

# Dezbatere multi-agent
python -m core.graph --agents anti_sistem conspirationist pro_european \
    --text "..." --turns 4 --provider gemini

# Pipeline offline (rulat o singură dată / per refresh date)
python scripts/collect_youtube.py    --handle <canal> --output data/raw/x.jsonl
python scripts/clean_youtube.py      --input data/raw/x.jsonl --output data/cleaned/x.jsonl
python scripts/annotate_axis.py      --input data/cleaned/x.jsonl \
                                     --output data/annotated/x.jsonl --provider gemini
python scripts/build_vectorstore.py

14Criterii de verificare pentru C8

Această secțiune răspunde la întrebarea: Cum știu că aplicația C8 este completă și corectă?

Înainte de predare, verifică punct cu punct:

  • aplicația pornește cu python -m app.app;
  • se poate încărca o știre din tab-ul Setări;
  • tab-ul Chat răspunde pe baza știrii încărcate;
  • tab-ul Agent răspunde explicit la știre (nu comentează generic corpusul);
  • tab-ul Toți agenții produce răspunsuri diferențiate între roluri;
  • tab-ul Dezbatere rulează cu cel puțin doi agenți și mai multe intervenții;
  • contextul FAISS nu se afișează în UI dacă este ținut în backend;
  • nu există chei API în repo (sunt doar în .env, negitat);
  • README explică cum se rulează aplicația și pipeline-ul.
Partea III · Trasabilitatea componentelor

De la exerciții separate la sistem integrat

Fiecare săptămână a introdus o componentă și a adăugat fizic o bucată în aplicație. Singură, nicio etapă nu produce o aplicație. Împreună, formează lanțul.

15Timeline — C1 până la aplicația finală

Această secțiune răspunde la întrebarea: Ce a adăugat fiecare săptămână și unde a aterizat în repo?

Opt etape, fiecare cu o componentă. Săgeata = „construiește baza pentru". La C8 toate se conectează în aplicația integrată.

Întrebare
Cum se leagă conceptul predat de fișierul produs?
De observat
Sus: ce a învățat fiecare săptămână. Jos: ce a aterizat în repo. Linia punctată leagă explicit conceptul de cod.
În cod
Coloana C8 = app/app.py care le compune pe toate
C1 C2 C3 C4 C5 C6 C7 APP LLMintro Modeleprovider+fallback ColectareYouTube Adnotare5 axe RetrievalFAISS Agent RAGrol+context LangGraphmulti-agent Gradio6 tab-uri Ce produce fiecare curs în repo (notebook)exersare ask_llmSetări/Chat collect/clean .py annotateprompt.md retrievervectorstores agent.pyroles.yaml graph.pyLangGraph app.py6 tabs
Fig. 9 — Timeline-ul cursului. Sus: ce a învățat fiecare săptămână. Jos: ce a aterizat în repo. Linia punctată leagă conceptul de cod.

16Ce am construit, curs cu curs

Această secțiune răspunde la întrebarea: Ce am învățat la fiecare curs și ce bucată din aplicație a produs?

Fiecare card: ce s-a învățat, ce s-a construit fizic în aplicație, ce fișiere au apărut. Aici culoarea transmite informație — fiecare culoare este un curs.

C1LLM intro
Ce e un LLM, token-uri, temperatură.
În app: doar exersare în notebook — fundația conceptuală.
notebooks/C1_llm_intro.ipynb
C2Ecosistem modele
Apel API spre mai mulți provideri prin endpoint OpenAI-compatible.
În app: tab-urile Setări + Chat + Rezumat + dropdown unic provider|model.
app.py → ask_llm(), setup(), chat(), summary()
C3Colectare YouTube
Colectare corpus din YouTube Data API + primul prompt structurat.
În app: pipeline-ul de colectare (offline) — primele date.
scripts/collect_youtube.py, clean_youtube.py
C4Adnotare corpus
De la prompt la context engineering. Schemă: target/stance/tone + 5 axe.
În app: etapa de adnotare (offline) — comentariile capătă structură.
scripts/annotate_axis.py · prompts/annotation_prompt.md
C5Regăsire semantică
Embeddings, FAISS, cosinus, split pe bule.
În app: indexurile + retrieverul — stratul care alege contextul.
scripts/build_vectorstore.py · core/retriever.py
C6Agent RAG
Rol YAML + context recuperat + LLM = voce discursivă simulată.
În app: tab-urile Agent + Toți agenții — prima voce discursivă.
core/agent.py · roles.yaml · app.py → agent(), all_agents()
C7LangGraph multi-agent
State, noduri, router, conditional edges → orchestrare.
În app: tab-ul Dezbatere — agenții discută între ei (round-robin).
core/graph.py · app.py → debate()
C8Integrare — această pagină
Toate componentele se conectează. Aplicația = suma celor opt etape.
Rezultat: 6 tab-uri, fiecare trasabil la etapa lui.

17Transformarea unui comentariu în context discursiv

Această secțiune răspunde la întrebarea: Cum devine un comentariu brut de pe YouTube context pentru o voce discursivă?

Cel mai clar mod de a vedea cum se leagă etapele: urmărește un singur comentariu cum se transformă, pas cu pas, până devine context discursiv pentru un agent. Fiecare transformare corespunde unui curs.

Întrebare
Ce i se întâmplă unui comentariu, de la colectare la voce?
De observat
Același text trece prin 6 transformări (C3 colectat/curățat, C4 adnotat, C5 bule/vectorizat/indexat, C6 recuperat/transformat în voce).
În cod
scripts/ (C3–C5) apoi core/agent.py (C6) la runtime
C3 collect_youtube.py → data/raw/*.jsonl { "text": "CCR a furat alegerile, rușine!!!", "video": "...", "author": "..." } C3 clean_youtube.py → data/cleaned/*.jsonl trece filtrul (lungime ok · alpha-ratio ok · nu e duplicat) → păstrat C4 annotate_axis.py → data/annotated/*.jsonl { target: "CCR", stance: "anti", sentiment: "negativ", tone: "acuzator",   bubble_candidate: "neincredere_sistemica", institutional: -2 } C5 split pe bule → data/bubbles/anti_sistem.jsonl → encode() comentariul intră în bula „anti_sistem" → vector 384-dim [ 0.23, -0.11, 0.47, … ] (normalizat, lungime 1) C5 build_vectorstore.py → assets/vectorstores/anti_sistem/ index.faiss (vectorul) + index.pkl (textul + metadate) — gata de căutat C6 runtime: subiect → retriever.search() → comentariul e recuperat subiect: „CCR a anulat alegerile…" → top-k include comentariul nostru { text: "CCR a furat alegerile…", agent: "Anti-sistem", score: 0.82 } C6 agent.py: rol „anti_sistem" + context recuperat → LLM COMENTARIU: „Decizia CCR confirmă ce știam deja: instituțiile nu mai   răspund nimănui. Sistemul s-a închis în jurul propriei puteri."
Fig. 10 — Transformarea unui comentariu. Același text trece prin C3 (colectat, curățat), C4 (adnotat), C5 (bule, vectorizat, indexat), C6 (recuperat, transformat în voce). Fiecare culoare = un curs.

Aici totul se leagă: un comentariu real, scris de cineva pe YouTube, devine — prin șase transformări, fiecare predată într-o săptămână — context pentru o intervenție discursivă simulată. Nimic nu este inventat: răspunsul este ancorat în comentarii reale, recuperate semantic, filtrate prin rol.

18Cum se asamblează etapele cursului

Această secțiune răspunde la întrebarea: Cum se combină cele opt etape într-o singură aplicație?

Etapele nu sunt o listă — sunt piese care se conectează. Săgeți groase = „construiește direct", punctate = „pregătește baza".

Întrebare
Care etape construiesc direct app-ul și care doar pregătesc baza?
De observat
C3→C4→C5 e lanțul de date offline. C5+C6 fac agentul RAG. C6→C7 ridică la multi-agent. Toate converg în app.py.
În cod
Coloana din dreapta = cele 6 tab-uri din app/app.py
C1 înțelegere LLM C2 multi-provider C3 collect/clean .py C4 annotate_axis.py C5 retriever + vectorstore C6 agent.py + roles.yaml C7 graph.py · LangGraph app/app.py Setări ← C2 Chat · Rezumat ← C2 Agent ← C5+C6 Toți agenții ← C6 Dezbatere ← C7 = suma celor 8 etape bază
construiește direct pregătește baza
Fig. 11 — Asamblarea etapelor. C3→C4→C5 e lanțul de date offline. C5+C6 fac agentul RAG. C6→C7 ridică la multi-agent. Totul converge în app.py.

19Cum citim app.py prin componentele cursului

Această secțiune răspunde la întrebarea: Cum recunosc, deschizând app.py, ce a venit din ce curs?

Deschizi app/app.py și fiecare secțiune are o origine clară în curs. Codul nu este o cutie neagră scrisă cândva — fiecare bloc este trasabil.

app/app.py — structură adnotată pe componente
app/app.py
│
├── §1 IMPORTS & SETUP                       # aduce core/ + gradio
│
├── §2 CONFIG (PROVIDERS, MODELS, ROLES,     ← C2  (multi-provider + .env)
│            CFG, ART)                        # dropdown unic provider|model
│
├── §3 APELURI LLM / BACKEND
│      ├── ask_llm()                       ← C2  (LLM direct)
│      ├── _agent()                        ← C6  (RAG: rol+FAISS+LLM)
│      └── _subject()                      ←      (regula: subiect peste știre)
│
├── §4 TAB Setări — setup()              ← C2  (provider/model + scrape știre)
│
├── §5 ACȚIUNI TAB-URI
│      ├── chat()                          ← C2
│      ├── summary()                       ← C2
│      ├── agent()                         ← C5 + C6
│      ├── all_agents()                    ← C6  (buclă pe roles.yaml)
│      └── debate()                        ← C7  (core.graph.run_thread)
│
└── §6 UI — 6 gr.Interface în gr.Blocks
       ├── Tab "Setări"                   ← C2
       ├── Tab "Chat" / "Rezumat"         ← C2
       ├── Tab "Agent"                    ← C5 + C6
       ├── Tab "Toți agenții"             ← C6
       └── Tab "Dezbatere"               ← C7

Aceasta este ideea centrală a cursului final. Codul nu este o cutie neagră. Fiecare linie are o etapă de origine. Poți deschide app.py și citi cele opt săptămâni în structura lui.

20Concluzie: de la exerciții la sistem integrat

★ Concluzia

C8 închide trecerea de la exerciții separate la sistem integrat. Valoarea aplicației nu stă într-un singur model sau într-un singur prompt, ci în separarea clară a responsabilităților:

datele sunt pregătite offline → contextul este recuperat prin FAISS →
rolurile definesc poziții discursive → core/agent.py produce răspunsul →
core/graph.py orchestrează conversația → app.py expune totul în interfață

Acesta este sistemul integrat construit pe parcursul cursului. Fiecare componentă este trasabilă la etapa în care a fost introdusă, iar straturile se sprijină unul pe altul ca niște blocuri de construcție. Aplicația nu este un proiect separat — este suma exactă a celor opt etape.

Material conex: Notebook C8 — cum se construiește app.py pas cu pas.