Architecture — vue visuelle
Ce document rassemble les diagrammes d'architecture de l'écosystème Telaria. Chaque section présente un angle de lecture distinct : du système global jusqu'au schéma de base de données, en passant par les flux IA internes.
1. C4 Context — vue système
Le système Telaria s'articule autour de trois acteurs principaux et de deux systèmes applicatifs distincts. telaria.dev (portfolio et vitrine IA) est distinct de adoption.lan (application autonome d'accompagnement à l'adoption IA). Les deux s'appuient sur Claude (Anthropic) comme moteur de génération.
C4Context
title Telaria — vue système (C4 Context)
Person(mathieu, "Mathieu", "Administrateur, développeur owner du projet")
Person(visiteur, "Visiteur", "Consultant la vitrine, testant le chatbot")
System(telaria, "telaria.dev", "Portfolio technique et vitrine IA — Symfony multisite avec bundles IA (veille, chatbot RAG, MCP)")
System(adoption, "adoption.lan", "Application autonome d'accompagnement à l'adoption IA — génération multi-vendor")
System_Ext(claude, "Claude (Anthropic)", "LLM externe — résumé veille, génération chatbot, génération adoption")
System_Ext(vps, "VPS OVH KVM", "Infrastructure hébergement — Ubuntu 26.04 LTS, Apache 2.4, MySQL 8.4, PHP 8.5")
System_Ext(rss, "Sources RSS/Atom", "Flux de veille IA (Anthropic, HuggingFace, arXiv, Hacker News…)")
Rel(mathieu, telaria, "Administre — back-office, sources veille, config chat")
Rel(visiteur, telaria, "Consulte la vitrine, interroge le chatbot RAG")
Rel(mathieu, adoption, "Pilote et teste")
Rel(telaria, claude, "Appelle via API — résumé veille (Haiku), chat RAG (configurable)")
Rel(adoption, claude, "Appelle via API — génération contenu multi-vendor")
Rel(telaria, rss, "Collecte planifiée — Scheduler + Messenger, cadence 15 min")
Rel(telaria, vps, "Déployé sur")
Rel(adoption, vps, "Déployé sur")
2. C4 Container — conteneurs dans telaria.dev
L'application telaria-app (Symfony) orchestre trois bundles Composer spécialisés et délègue la vectorisation à un microservice Python indépendant. Les deux moteurs de persistance sont MySQL (données applicatives) et SQLite+sqlite-vec (index vectoriel RAG).
C4Container
title telaria.dev — vue conteneurs (C4 Container)
Person(mathieu, "Mathieu", "Admin")
Person(visiteur, "Visiteur", "Utilisateur public")
System_Boundary(telaria_sys, "telaria.dev") {
Container(app, "telaria-app", "Symfony 7.2 / PHP 8.5", "Application hôte — multisite, auth 2FA, CMS, surface web (chat, docs, veille admin)")
Container(tlr_symfony, "tlr-symfony-bundle", "Composer / PHP", "Socle générique multisite — entités Site, CmsContent, CmsTag, CmsImage ; resolver hôte ; moteur CMS")
Container(tlr_codexia, "tlr-codexia-bundle", "Composer / PHP", "Produit doc-IA — Veille (collecte, résumé, classification), Chat (RAG), Metrics, AppSettings")
Container(tlr_rag, "tlr-rag-bundle", "Composer / PHP", "Coeur RAG L0 — ingestion .md, chunking, embeddings, index sqlite-vec, retrieval top-k")
Container(tlr_mcp, "tlr-mcp-bundle", "Composer / PHP", "Serveur MCP — JSON-RPC 2.0, outils list_docs/read_doc/search_docs, gouvernance multi-tenant, audit")
Container(embeddings_svc, "tlr-embeddings", "Python / FastAPI", "Microservice vectorisation — modele multilingual-e5-base (768 dim), endpoints /embed et /health")
ContainerDb(mysql, "MySQL 8.4", "MySQL", "Donnees applicatives — entites Doctrine des 4 bundles")
ContainerDb(sqlite, "SQLite + sqlite-vec", "SQLite", "Index vectoriel RAG — chunks, vecteurs kNN cosinus ; fichier var/rag/index.sqlite")
}
System_Ext(claude_api, "Claude API (Anthropic)", "LLM generation et resume")
Rel(mathieu, app, "HTTPS — back-office admin, config, revue veille")
Rel(visiteur, app, "HTTPS — chatbot /assistant, docs /docs")
Rel(app, tlr_symfony, "Composer require — multisite, CMS")
Rel(app, tlr_codexia, "Composer require — veille, chat, metrics")
Rel(app, tlr_rag, "Composer require — retrieval, ingest")
Rel(app, tlr_mcp, "Composer require — serveur MCP")
Rel(tlr_codexia, tlr_rag, "Appelle RetrievalService pour le chat RAG")
Rel(tlr_mcp, tlr_rag, "Appelle RetrievalService pour search_docs")
Rel(tlr_rag, embeddings_svc, "HTTP POST /embed — vectorisation chunks et requetes")
Rel(tlr_rag, sqlite, "Read/Write — upsert chunks, recherche kNN")
Rel(tlr_codexia, claude_api, "HTTPS — resume veille (Haiku), generation chat")
Rel(app, mysql, "Doctrine ORM — toutes les entites applicatives")
3. Flux pipeline veille IA
Le pipeline de veille est entierement Symfony-natif (Scheduler + Messenger). Il n'ecrit jamais directement dans pilotage/veille/ — la bascule reste manuelle apres revue humaine. Les entites cles sont VeilleSource, VeilleItem et VeilleAttempt.
sequenceDiagram
participant SCH as Scheduler<br/>(cadence 15 min)
participant MSG as Messenger<br/>(worker async)
participant COL as Collector<br/>(RSS/Atom fetcher)
participant SRC as VeilleSource<br/>(MySQL)
participant ITM as VeilleItem<br/>(MySQL)
participant ART as ArticleFetcher<br/>(fetch DOM lazy)
participant SUM as Summarizer<br/>(Claude Haiku)
participant CLS as Classifier<br/>(mots-cles V1)
participant ATT as VeilleAttempt<br/>(MySQL)
participant PRO as Proposals<br/>(var/veille/proposals/*.md)
participant HUM as Humain<br/>(revue /admin/veille)
SCH->>MSG: Dispatch CollectVeilleMessage
MSG->>SRC: Charger sources actives (is_active=true, standby=false)
SRC-->>MSG: Liste VeilleSource[]
loop Pour chaque source
MSG->>COL: Parser flux RSS/Atom (url)
COL-->>MSG: Items bruts (titre, url, date)
MSG->>ITM: Verifier deduplication (url OU content_hash SHA-256)
alt Item deja connu
MSG-->>MSG: Skip
else Nouvel item
MSG->>ITM: Creer VeilleItem (status=pending)
MSG->>ATT: Creer VeilleAttempt (stage=fetch)
MSG->>ART: Fetch URL article (extraction DOM article/main/body)
ART-->>MSG: raw_content (ou erreur HTTP)
alt Fetch OK
MSG->>SUM: Appel Claude Haiku — resume + points cles
SUM-->>MSG: summary, title_fr, relevance, tokens
MSG->>ITM: Maj VeilleItem (summary, model_used, status=proposed)
MSG->>CLS: Classifier (mots-cles word-boundary)
CLS-->>MSG: theme
MSG->>PRO: Ecrire proposal .md (frontmatter YAML)
MSG->>ATT: Maj VeilleAttempt (outcome=success, stage=done)
else Fetch KO
MSG->>ITM: Maj VeilleItem (status=failed)
MSG->>ATT: Maj VeilleAttempt (outcome=failed, fetch_error)
MSG->>SRC: Incrementer consecutive_failures
alt N echecs consecutifs (seuil=3)
MSG->>SRC: Passer standby=true (mise en retrait automatique)
end
end
end
end
HUM->>PRO: Revue /admin/veille/proposals
HUM-->>ITM: Action accept / reject / reopen
HUM-->>PRO: Copie manuelle vers pilotage/veille/
4. Flux pipeline RAG (question vers reponse)
Le chatbot RAG suit trois etapes sequentielles : vectorisation de la question, recherche kNN dans l'index SQLite, puis generation Claude avec le contexte assemble. Le garde-fou de seuil de score evite tout appel LLM si le contexte est insuffisant.
sequenceDiagram
participant USR as Utilisateur
participant CTL as ChatController<br/>(Symfony)
participant SVC as ChatService<br/>(App\Chat)
participant RET as RetrievalService<br/>(tlr-rag)
participant EMB as tlr-embeddings<br/>(Python FastAPI)
participant VEC as SQLite + sqlite-vec<br/>(index vectoriel)
participant LLM as Claude API<br/>(Anthropic)
USR->>CTL: POST /assistant (question)
CTL->>SVC: chat(question, config)
SVC->>RET: retrieve(question, k)
RET->>EMB: POST /embed {type:"query", texts:[question]}
Note over EMB: Prefixe e5 "query:" applique<br/>Modele multilingual-e5-base (768 dim)
EMB-->>RET: {vectors: [[float x 768]]}
RET->>VEC: vec_search(vecteur_question, k=5)
Note over VEC: Recherche kNN cosinus<br/>Table virtuelle vec0 (sqlite-vec)
VEC-->>RET: chunks[] triés par score cosinus
RET-->>SVC: Hit[] {chunk, score, path, section, anchor}
alt Tous scores < seuil (score_threshold)
SVC-->>CTL: "Cette information n'est pas dans la documentation"
CTL-->>USR: Reponse hors-perimetre (sans appel LLM)
else Au moins un chunk pertinent
Note over SVC: Assemblage prompt :<br/>system + contexte (passages + sources)<br/>+ question
SVC->>LLM: POST messages API (prompt cache sur bloc stable)
Note over LLM: Modele configurable (ChatConfig)<br/>Temperature basse, max_tokens borne
LLM-->>SVC: Reponse generee
SVC-->>CTL: {reponse, sources: [{path, section}]}
CTL-->>USR: Reponse + liste des sources avec liens
end
5. Carte des entites BDD (par bundle)
Les 23 tables MySQL sont reparties entre 4 bundles autonomes via auto-mapping Doctrine. L'application principale (telaria-app) ne conserve que ses propres entites d'auth et SEO. L'index vectoriel SQLite (RAG) est separe de MySQL par conception.
erDiagram
%% --- telaria-app ---
user {
int id PK
string email
string password
json roles
string api_token
bool is2fa_enabled
bool is_verified
}
reset_password_request {
int id PK
int user_id FK
string selector
string hashed_token
datetime expires_at
}
cms_content_seo {
int id PK
int content_id FK
string canonical_url
}
%% --- tlr-symfony ---
site {
int id PK
string host
string slug
string label
string locale_default
bool enabled
bool is_primary
}
cms_content {
int id PK
int site_id FK
int author_id FK
string title
string slug
string status
string visibility
string type
text markdown
}
cms_tag {
int id PK
string name
string slug
}
cms_content_tag {
int cms_content_id FK
int cms_tag_id FK
}
cms_image {
int id PK
int owner_id FK
int site_id FK
int content_id FK
string filename
string mime
int size
}
%% --- tlr-codexia ---
veille_source {
int id PK
string slug
string name
string url
string type
bool is_active
bool standby
int consecutive_failures
}
veille_item {
int id PK
int source_id FK
string url
string content_hash
string status
text summary
string model_used
float relevance
string theme
}
veille_attempt {
int id PK
int item_id FK
string outcome
string stage_reached
int duration_ms
int fetch_http_status
string llm_model
int tokens_input
int tokens_output
}
veille_read {
int id PK
int user_id FK
int item_id FK
datetime read_at
}
app_setting {
string name PK
text value
}
chat_config {
int id PK
string model
int top_k
float score_threshold
float temperature
int max_tokens
int history_turns
}
metrics_usage {
int id PK
date day
string model
string api_key_id
bigint output_tokens
}
metrics_daily {
int id PK
date day
string label
decimal cost_cents
}
metrics_alert {
int id PK
string label
decimal amount_usd
string period
bool enabled
}
metrics_api_key {
string id PK
string name
string workspace_id
}
%% --- tlr-mcp ---
mcp_tenant {
int id PK
string name
string status
}
mcp_api_client {
int id PK
int tenant_id FK
string token_hash
json scopes
int rate_limit_per_minute
bool revoked
}
mcp_project {
int id PK
int tenant_id FK
string slug
string root_path
string status
}
mcp_tool_audit_log {
int id PK
int tenant_id
int project_id
int api_client_id
string tool_name
string status
datetime timestamp
}
%% Relations telaria-app
user ||--o{ reset_password_request : "possede"
user ||--o{ cms_content_seo : "via cms_content (companion)"
%% Relations tlr-symfony
site ||--o{ cms_content : "heberge"
user ||--o{ cms_content : "autheur (CmsUserInterface)"
cms_content ||--o| cms_content_seo : "extension SEO"
cms_content }o--o{ cms_tag : "via cms_content_tag"
cms_content_tag }o--|| cms_content : ""
cms_content_tag }o--|| cms_tag : ""
site ||--o{ cms_image : "images du site"
user ||--o{ cms_image : "proprietaire"
cms_content ||--o{ cms_image : "images rattachees"
%% Relations tlr-codexia
veille_source ||--o{ veille_item : "produit"
veille_item ||--o{ veille_attempt : "tentatives de traitement"
user ||--o{ veille_read : "marque lu (VeilleReaderInterface)"
veille_item ||--o{ veille_read : "lu par"
%% Relations tlr-mcp
mcp_tenant ||--o{ mcp_api_client : "possede"
mcp_tenant ||--o{ mcp_project : "possede"
Voir aussi
specs/ia-coeur.md— pipeline RAG L0, contrats d'interface, schema SQLitespecs/ia-mcp.md— serveur MCP, protocole JSON-RPC, gouvernance multi-tenantspecs/ia-veille.md— pipeline veille, composants Symfony, guard-fousspecs/ia-chatbot.md— chatbot RAG, surfaces/assistantet/docs../03-comment-je-travaille/bdd/schema.md— schema BDD complet (source de verite, colonnes detaillees)pilotage/ecosystem.md— topologie des repos et instances Claude