01 — Vue d'ensemble
Architecture globale
Deux phases indépendantes : une ingestion locale one-shot, et un runtime cloud serverless qui répond aux requêtes en temps réel.
🔧 Ingestion — local · one-shot
📂 PDF files
data/
data/
fitz.open()
⚙️ ingest.py
PyMuPDF
PyMuPDF
chunk + embed
🤖 OpenAI
text-embedding-3-small
text-embedding-3-small
vecteurs
🗄 toyota_chunks
pgvector HNSW
pgvector HNSW
PNG pages
🖼 Supabase Storage
toyota-images
toyota-images
☁️ Runtime — Vercel · SSE streaming
🌐 Navigateur
ToyotaChat.tsx
ToyotaChat.tsx
POST /api/chat
⚡ /api/chat
Next.js · Vercel
Next.js · Vercel
embed question
🤖 OpenAI Embed
text-embedding-3-small
text-embedding-3-small
match_toyota_chunks()
🔍 Supabase pgvector
top 5 chunks + image_urls
top 5 chunks + image_urls
contexte + prompt
🤖 Claude Haiku
Anthropic · streaming
Anthropic · streaming
SSE ← delta / sources / images / done
Composant Next.js
Service IA (OpenAI / Anthropic)
Base de données Supabase
Supabase Storage
Interface utilisateur
02 — Diagramme de séquence
Interactions entre acteurs et composants
Vue complète des échanges entre l'utilisateur, le frontend, l'API, les services IA et la base de données — pour une requête chat.
03 — Diagramme d'activité
Pipeline d'ingestion PDF
Exécuté une seule fois en local. Traite chaque page : extraction texte + capture PNG → embeddings → Supabase.
flowchart TD
A([Démarrer ingest.py]) --> B[Charger .env.local]
B --> C[Lister les PDF dans data/]
C --> D{PDF trouvés ?}
D -- Non --> E([Erreur : aucun PDF])
D -- Oui --> F[Créer bucket toyota-images]
F --> G[Ouvrir PDF — PyMuPDF]
G --> H[Itérer sur chaque page]
H --> I[Extraire texte\npage.get_text]
H --> J[Capturer la page\nget_pixmap — zoom 1.5×]
J --> K[Uploader PNG\nSupabase Storage]
K --> L{Upload OK ?}
L -- Oui --> M[Récupérer URL publique]
L -- Non --> N[Logger erreur\ncontinuer]
I --> O[Découper en chunks\n800 chars · overlap 100]
M --> O
N --> O
O --> P[Associer métadonnées\nsource · page · image_urls]
P --> Q[Batch de 20 chunks]
Q --> R[Embedding OpenAI\ntext-embedding-3-small\n1 536 dimensions]
R --> S[Insérer dans toyota_chunks\nSupabase pgvector]
S --> T{Autre batch ?}
T -- Oui --> Q
T -- Non --> U{Autre page ?}
U -- Oui --> H
U -- Non --> V{Autre PDF ?}
V -- Oui --> G
V -- Non --> W([Ingestion terminée ✓])
04 — Diagramme d'activité
Pipeline de requête utilisateur
À chaque message : vectorisation de la question → recherche sémantique → génération Claude → SSE streaming avec images.
flowchart TD
A([Utilisateur envoie un message]) --> B[ToyotaChat.tsx\nPOST /api/chat]
B --> C[API Route — Vercel Node.js]
C --> D[Embed la question\nOpenAI text-embedding-3-small]
D --> E[RPC Supabase\nmatch_toyota_chunks\nthreshold=0.4 · top 5]
E --> F{Chunks trouvés ?}
F -- Non --> G([Réponse : information\nnon disponible])
F -- Oui --> H[Construire le contexte\ntexte des 5 chunks]
H --> I[Collecter image_urls\ndédupliquer]
I --> J[Appel Claude Haiku\nAnthropic SDK streaming]
J --> K[Stream SSE — type: delta\ntoken par token]
K --> L{Fin du stream ?}
L -- Non --> K
L -- Oui --> M[Event SSE — type: sources]
M --> N[Event SSE — type: images]
N --> O[Event SSE — type: done]
O --> P[Afficher réponse markdown]
P --> Q[Afficher chips sources\nManuel · p.XX]
Q --> R[Afficher galerie images\nschémas cliquables]
R --> S([Interaction terminée ✓])
05 — Base de données
Schéma Supabase
Table toyota_chunks avec index HNSW et fonction RPC pour la recherche vectorielle.
| Colonne | Type | Description |
|---|---|---|
| id | uuid | Clé primaire (gen_random_uuid) |
| content | text | Texte du chunk (≤ 800 chars) |
| embedding | vector(1536) | Vecteur OpenAI text-embedding-3-small |
| metadata | jsonb | {source, page, image_urls: []} |
| created_at | timestamptz | Date d'insertion |
| Objet | Type | Usage |
|---|---|---|
| toyota_chunks_embedding_idx | HNSW cosine | Recherche approximative O(log n) |
| match_toyota_chunks() | RPC plpgsql | Retourne top-N chunks par similarité cosinus |
06 — API
Protocole SSE — POST /api/chat
Flux de 4 événements successifs. Corps de requête : { messages: [{role, content}] }
delta
{ text: string } — token Claude, envoyé en continu pendant la générationsources
{ sources: [{source, page}] } — pages citées, après fin du stream texteimages
{ imageUrls: string[] } — URLs publiques Supabase Storage des pages PNGdone
{} — fin de la réponse, le client ferme le reader SSE07 — Stack technique
Technologies
| Couche | Technologie | Justification |
|---|---|---|
| Frontend + API | Next.js 16 App Router | SSE natif, déploiement Vercel optimal |
| LLM | Claude Haiku | Rapide, économique, compréhension FR/technique |
| Embeddings | text-embedding-3-small | 1 536 dims, multilingue, meilleur rapport qualité/coût |
| Vector DB | Supabase pgvector HNSW | PostgreSQL natif, cloud géré, index approximatif |
| Images | Supabase Storage | S3-compatible, URLs publiques, même compte |
| Ingestion PDF | Python + PyMuPDF | get_pixmap() capture les diagrammes vectoriels Toyota |
| Déploiement | Vercel | CI/CD automatique, CDN global, HTTPS inclus |
08 — Paramètres RAG
Configuration de la recherche
| Paramètre | Valeur | Justification |
|---|---|---|
| CHUNK_SIZE | 800 chars | Compromis contexte / précision de retrieval |
| CHUNK_OVERLAP | 100 chars | Continuité sémantique entre chunks adjacents |
| match_threshold | 0.4 | Seuil similarité cosinus — filtre le bruit |
| match_count | 5 | Top 5 chunks ≈ 4 000 chars de contexte max |
| Page zoom | 1.5× | PNG lisible sans être trop lourd (≈ 200 KB/page) |
| Embed batch | 20 chunks | Limite rate-limit OpenAI embeddings |
09 — Structure du projet
Organisation des fichiers
| Fichier | Rôle |
|---|---|
app/api/chat/route.ts | Route SSE — embed + RAG + streaming Claude |
components/ToyotaChat.tsx | Interface chat, streaming, galerie images, lightbox |
lib/supabase.ts | Fonction matchChunks() via RPC pgvector |
lib/system-prompt.ts | Prompt système Claude (FR uniquement, citations page) |
ingest/ingest.py | Ingestion PDF one-shot (texte + PNG → Supabase) |
ingest/migrate.py | DDL Supabase via Management API |
next.config.ts | Domaines images Supabase whitelistés |
10 — Variables d'environnement
Configuration
| Variable | Usage | Vercel ? |
|---|---|---|
| ANTHROPIC_API_KEY | Claude Haiku (streaming) | ✓ |
| OPENAI_API_KEY | Embeddings question + ingestion | ✓ |
| SUPABASE_URL | Endpoint Supabase | ✓ |
| SUPABASE_SECRET_KEY | Accès service_role (lecture + écriture) | ✓ |
| SUPABASE_MGMT_TOKEN | DDL via Management API (ingestion only) | ✗ local seulement |