Documentation Technique

My Toyota

Architecture · Flux de données · API · Base de données — pour développeurs

Saifallah MansourAuteur
Mai 2026Version 1.0
Next.js · Claude Haiku · Supabase pgvectorStack
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/
fitz.open()
⚙️ ingest.py
PyMuPDF
chunk + embed
🤖 OpenAI
text-embedding-3-small
vecteurs
🗄 toyota_chunks
pgvector HNSW
PNG pages
🖼 Supabase Storage
toyota-images
☁️ Runtime  —  Vercel · SSE streaming
🌐 Navigateur
ToyotaChat.tsx
POST /api/chat
⚡ /api/chat
Next.js · Vercel
embed question
🤖 OpenAI Embed
text-embedding-3-small
match_toyota_chunks()
🔍 Supabase pgvector
top 5 chunks + image_urls
contexte + prompt
🤖 Claude Haiku
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.

👤 Utilisateur ToyotaChat .tsx (Next.js) /api/chat Vercel · Node.js OpenAI Embeddings Supabase pgvector Supabase Storage Claude Haiku Anthropic Saisit une question POST /api/chat {messages} ① Vectorisation embeddings.create(question) vector[1536] ② Recherche sémantique RPC match_toyota_chunks(vector, top=5) [{content, metadata, image_urls}] × 5 ③ Génération streaming stream(system_prompt + contexte chunks) text delta — répété, token par token SSE {type:"delta", text} × N loop streaming ④ Métadonnées (fin stream) SSE {type:"sources", pages[]} SSE {type:"images", imageUrls[]} SSE {type:"done"} GET image_urls[] (PNG par page) Images PNG (1.5× zoom) Réponse + schémas techniques 👤 Utilisateur ToyotaChat /api/chat OpenAI pgvector Storage Claude Haiku Appel Réponse / retour Résultat final
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.

ColonneTypeDescription
iduuidClé primaire (gen_random_uuid)
contenttextTexte du chunk (≤ 800 chars)
embeddingvector(1536)Vecteur OpenAI text-embedding-3-small
metadatajsonb{source, page, image_urls: []}
created_attimestamptzDate d'insertion
ObjetTypeUsage
toyota_chunks_embedding_idxHNSW cosineRecherche approximative O(log n)
match_toyota_chunks()RPC plpgsqlRetourne 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ération
sources{ sources: [{source, page}] } — pages citées, après fin du stream texte
images{ imageUrls: string[] } — URLs publiques Supabase Storage des pages PNG
done{} — fin de la réponse, le client ferme le reader SSE
07 — Stack technique

Technologies

CoucheTechnologieJustification
Frontend + APINext.js 16 App RouterSSE natif, déploiement Vercel optimal
LLMClaude HaikuRapide, économique, compréhension FR/technique
Embeddingstext-embedding-3-small1 536 dims, multilingue, meilleur rapport qualité/coût
Vector DBSupabase pgvector HNSWPostgreSQL natif, cloud géré, index approximatif
ImagesSupabase StorageS3-compatible, URLs publiques, même compte
Ingestion PDFPython + PyMuPDFget_pixmap() capture les diagrammes vectoriels Toyota
DéploiementVercelCI/CD automatique, CDN global, HTTPS inclus
08 — Paramètres RAG

Configuration de la recherche

ParamètreValeurJustification
CHUNK_SIZE800 charsCompromis contexte / précision de retrieval
CHUNK_OVERLAP100 charsContinuité sémantique entre chunks adjacents
match_threshold0.4Seuil similarité cosinus — filtre le bruit
match_count5Top 5 chunks ≈ 4 000 chars de contexte max
Page zoom1.5×PNG lisible sans être trop lourd (≈ 200 KB/page)
Embed batch20 chunksLimite rate-limit OpenAI embeddings
09 — Structure du projet

Organisation des fichiers

FichierRôle
app/api/chat/route.tsRoute SSE — embed + RAG + streaming Claude
components/ToyotaChat.tsxInterface chat, streaming, galerie images, lightbox
lib/supabase.tsFonction matchChunks() via RPC pgvector
lib/system-prompt.tsPrompt système Claude (FR uniquement, citations page)
ingest/ingest.pyIngestion PDF one-shot (texte + PNG → Supabase)
ingest/migrate.pyDDL Supabase via Management API
next.config.tsDomaines images Supabase whitelistés
10 — Variables d'environnement

Configuration

VariableUsageVercel ?
ANTHROPIC_API_KEYClaude Haiku (streaming)
OPENAI_API_KEYEmbeddings question + ingestion
SUPABASE_URLEndpoint Supabase
SUPABASE_SECRET_KEYAccès service_role (lecture + écriture)
SUPABASE_MGMT_TOKENDDL via Management API (ingestion only)✗ local seulement