LLM Local : une autre façon de travailler

Pourquoi les grands modèles en cloud ne sont pas la seule réponse — et comment construire une alternative souveraine, précise et adaptée à votre codebase.

Le point de départ

Quand on utilise ChatGPT ou Claude pour coder, on pose une question générale et on reçoit une réponse générale. Le modèle ne connaît pas votre projet, vos conventions, vos patterns internes, vos dépendances spécifiques. Chaque conversation recommence à zéro.

Les grands LLMs en cloud ont un avantage évident : ils savent tout sur tout. Mais sur votre code interne, sur vos APIs privées, sur vos choix d'architecture — ils ne savent rien. Et ils ne pourront jamais le savoir sans que vous le leur expliquiez à chaque fois.

Le problème fondamental : un LLM cloud répond en fonction de ce qu'il a vu pendant son entraînement. Votre code, lui, n'y était pas. Vous êtes condamné à lui réexpliquer le contexte à chaque session.

Un LLM local change la donne. Non pas parce qu'il est plus intelligent — il ne l'est probablement pas — mais parce qu'on peut lui donner exactement le contexte dont il a besoin, de manière persistante, structurée, et sous notre contrôle complet.

L'approche habituelle vs l'approche RAG locale

LLM Cloud — approche classique
LLM Local + RAG — notre approche
Contexte recopié manuellement à chaque session
Contexte indexé une fois, disponible en permanence
Code propriétaire envoyé sur des serveurs tiers
Tout reste sur votre infrastructure
Réponses génériques basées sur des patterns publics
Réponses ancrées dans votre codebase réel
Le modèle ignore vos conventions internes
Le modèle connaît vos classes, vos routes, vos patterns
Coût croissant avec le volume de tokens
Coût fixe une fois le hardware en place

Ce qu'on a construit : PyContextBuilder

PyContextBuilder est un outil standalone PySide6 qui analyse statiquement un projet Python (et ses fichiers front-end associés), extrait les symboles pertinents, et les injecte dans une instance LLMInternal locale.

L'idée centrale : au lieu de coller du code dans un chat, on préindexe toute la structure du projet. Le LLM peut alors récupérer précisément le bon contexte au moment de chaque question.

1050 artefacts extraits
905 chunks embeddables
2 couches d'injection

Ces chiffres viennent d'une analyse réelle de LLMInternal lui-même — 49 fichiers Python + 57 fichiers statiques (JS, HTML, CSS, YAML). Le système analyse et s'auto-indexe.

Le pipeline d'analyse : du code aux vecteurs

1
Collecte des fichiers source
SSH/SFTP ou local. Hash SHA1 par fichier pour la réindexation incrémentale — seuls les fichiers modifiés sont réanalysés.
2
Analyse AST Python
Extraction des classes, méthodes, fonctions avec leur signature, docstring, décorateurs, visibilité. Priorité calculée (p=8 pour les classes, p=6 pour les fonctions publiques, p=1 pour le privé).
3
Analyse statique multi-langage
JS (fonctions, classes, arrows), HTML (blocs Jinja2, scripts inline), CSS (règles groupées), YAML/JSON (clés top-level), Markdown (sections), SQL (statements). Chunking à 1200 chars avec overlap 120.
4
Cross-référencement Python ↔ JS
Le CrossRefAnalyzer résout les dépendances entre routes FastAPI (@router.post("/upload")) et les appels fetch JS (authFetch('/api/documents/upload')). 216 liens cross-langage détectés sur LLMInternal.
5
Construction des embed_text
Chaque artefact reçoit un embed_text optimisé : signature complète + docstring (≤500c) + début du corps (budget restant jusqu'à 1400c max). Tronquage propre à la ligne avec marqueur.
6
Export local
artifacts.parquet (merge incrémental par symbol_id), embeddings_queue.jsonl (embeddable=True uniquement), manifest.json (stats + graphe d'imports).
7
Injection dans LLMInternal — deux couches
Couche 1 : artefacts sémantiques reconstruits (pyctx__filename.txt). Couche 2 : fichiers sources bruts tels quels (src__path__file.py). LLMInternal chunke et embed avec Qwen3-Embedding-4B.

Ce qui est injecté — le graphe des artefacts

Sur une analyse de PyContextBuilder lui-même (27 fichiers Python + 1 YAML de config) :

// Distribution des artefacts par type — PyContextBuilder v0.14
216 162 108 54 216 method 40 function 28 class 27 config_block 18 summary_file embeddable (130/329 = 39%) non-embeddable (privé, corps < 3 lignes)

La sélection embeddable=True est critique : on n'injecte que ce qui a une valeur sémantique réelle — fonctions et classes publiques avec docstring ou corps suffisant. Le reste (getters d'une ligne, méthodes privées) polluerait l'index et dégraderait la précision des réponses.

La structure d'un artefact

# Exemple réel extrait de LLMInternal
{
  "symbol_id":       "226e617b-498b-54bd-94c3-...",
  "qualified_name":  "src.rag.rag_engine.RAGEngine.chat_agent",
  "symbol_type":    "method",
  "file_path":      "/opt/llminternal/src/rag/rag_engine.py",
  "start_line":     142,
  "end_line":       264,
  "priority":       6,      // public method, non-test
  "embeddable":     true,
  "confidence":     "certain",
  "embed_text":     "async def chat_agent(self, query, ctx):\n    \"\"\"Runs the agent pipeline...\"\"\"\n    intent = await self._intent_analyzer...",
  "used_by":        ["static.js.chat.sendMessage"]  // cross-ref JS→Python
}

Le graphe d'imports — la topologie du projet

// Graphe d'imports Python — LLMInternal (49 modules, 63 arêtes)
ENTRY API CORE INFRA main.py run.py auth_routes chat_routes documents_routes projects_routes admin_routes rag_engine agent_pipeline doc_processor auth.manager chunker database schemas hw_optimizer perf_monitor JS callers chat.js → chat_routes docs.js → doc_routes

Ce graphe n'est pas juste décoratif : il guide la priorité d'injection. Les modules centraux (rag_engine, agent_pipeline) qui sont importés par beaucoup d'autres reçoivent une priorité d'indexation plus haute. Le LLM trouvera d'abord le contexte le plus structurant.

Ce que ça change concrètement

Une fois LLMInternal indexé avec le contexte complet de votre projet, les questions deviennent beaucoup plus précises :

# Question classique (LLM cloud)
"Comment implémenter un système de RAG en Python ?"
→ Réponse générique, LangChain, ChromaDB, patterns publics

# Question avec contexte injecté (LLM local + LLMInternal)
"Dans notre RAGEngine, comment fonctionne le mode agent ?"
→ Réponse ancrée dans chat_agent(), agent_pipeline.py,
   les vrais agents (intent_analyzer, reranker, retriever)
   et les appels fetch() depuis chat.js qui le déclenchent

La différence n'est pas dans la capacité du modèle. Elle est dans la précision du contexte fourni. Un Qwen3-32B local avec le bon contexte surpasse souvent un GPT-4 qui travaille dans le vide.

Le principe clé : un LLM local n'a pas besoin d'être le plus grand modèle du monde. Il a besoin d'être dans le bon contexte. PyContextBuilder construit ce contexte — méthodiquement, incrémentiellement, et sous votre contrôle.

Stack technique

Tout est open-source et auto-hébergeable :

PyContextBuilder
 
PySide6
LLMInternal
 
FastAPI
Embeddings
 
Qwen3-4B
Vector DB
 
ChromaDB
LLM
 
llama.cpp

Le hardware : un serveur AMD avec GPU ROCm (GFX1151) héberge les modèles. 

Quand le projet devient multi-utilisateurs

Depuis les premiers billets sur ce projet, j’ai surtout parlé de modèles, d’embeddings et de pipelines RAG. Tout ce qui se passe côté machine. Mais il y a un autre axe d’évolution que j’ai mené en parallèle ces dernières semaines, moins spectaculaire techniquement mais tout aussi important en  […]

Lire la suite

Pourquoi j’ai ajouté un mode RAG piloté par agents dans Internal LLM

Au départ, un LLM local peut déjà aider pour écrire, expliquer ou générer du code. Mais il a une limite simple : il ne connaît pas automatiquement mes documents, mes scripts, mes exports ou la structure réelle de mes projets. C’est pour ça que j’ai ajouté un vrai mode RAG dans Internal LLM. L’idée  […]

Lire la suite

Internal LLM : pourquoi l’architecture repose sur trois moteurs spécialisés

Dans Internal LLM, je n’ai pas choisi un seul modèle pour tout faire. J’ai préféré une architecture en trois services spécialisés, chacun avec un rôle précis. Le service principal du code repose sur Qwen3-Coder-30B-A3B-Instruct en Q5_K_M sur le port 8080, le service de raisonnement sur Qwen3-32B en  […]

Lire la suite

Internal LLM : pourquoi j’ai choisi Ubuntu, un kernel récent et une architecture LLM en plusieurs modèles

Après avoir présenté le projet, je voulais revenir sur un point essentiel : dans un environnement d’IA locale, le choix du système compte presque autant que le choix des modèles. Pour Internal LLM, j’ai retenu Ubuntu 24.04.4 LTS avec un kernel 6.14.0-37, sur une machine basée sur AMD Ryzen AI Max+ 395 avec Radeon 8060S. Ce n’est pas un choix “par défaut”, mais un compromis entre compatibilité matérielle, stabilité et capacité d’expérimentation.

Lire la suite

Internal LLM : pourquoi je construis un assistant IA local.

Je démarre ce blog pour partager l’évolution de Internal LLM, un projet centré sur l’IA locale et l’exploitation intelligente de documentation technique, de code, de requêtes SQL et de connaissances internes. L’objectif est de construire un assistant capable de retrouver, comprendre et exploiter  […]

Lire la suite

Haut de page