02-ce-que-je-construis/architecture-visuelle.md

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

Assistant documentaire

Posez une question sur la documentation. Les réponses citent leurs sources — un clic ouvre le document à gauche.

Loading…
Loading the web debug toolbar…
Attempt #