ChatGPT local avec FastAPI : créer son propre assistant IA open-source
Publié le — Par l'équipe DEV-AI
ChatGPT Local
FastAPI + LLaMA 3 / Mistral / Phi-3
llama-cpp-python · Ollama · Transformers HuggingFace
/ask REST, gère l'historique de conversation, et inclut une interface web simple — le tout sans envoyer une seule donnée dans le cloud.
Pourquoi héberger son propre assistant IA ?
De plus en plus d'équipes cherchent à déployer des assistants IA en interne, sans dépendance aux APIs d'OpenAI ou d'Anthropic. Les raisons sont multiples :
- Confidentialité des données : aucune donnée ne quitte vos serveurs — essentiel pour les projets liés au RGPD, à la santé ou aux données clients sensibles
- Maîtrise des coûts : à volume élevé, un modèle local devient beaucoup moins cher que des appels API (GPT-4 coûte ~0,01 $/1000 tokens ; un serveur GPU loué à l'heure peut traiter des millions de tokens)
- Contrôle total : vous choisissez le modèle, les paramètres de génération, le system prompt et la façon dont les réponses sont filtrées
- Latence : un modèle local bien configuré peut répondre en quelques secondes, sans dépendre des latences réseau ou des rate limits des APIs externes
Choisir son modèle : comparatif 2025
| Modèle | Paramètres | RAM / VRAM min | Qualité | Idéal pour |
|---|---|---|---|---|
| Phi-3 Mini | 3.8B | 4 Go RAM (CPU) | ⭐⭐⭐ | Machines légères, Edge |
| Mistral-7B | 7B | 8 Go RAM / 4 Go VRAM (Q4) | ⭐⭐⭐⭐ | Meilleur rapport qualité/perf |
| LLaMA 3 8B | 8B | 10 Go RAM / 6 Go VRAM (Q4) | ⭐⭐⭐⭐ | Usage général, instructions |
| LLaMA 3 70B | 70B | 40 Go RAM / 24 Go VRAM (Q4) | ⭐⭐⭐⭐⭐ | Production, haute qualité |
| Mixtral 8x7B | 46B actifs (MoE) | 24 Go RAM / 16 Go VRAM | ⭐⭐⭐⭐⭐ | Raisonnement complexe |
Pour la plupart des cas d'usage, Mistral-7B quantisé (GGUF Q4_K_M) est le meilleur point de départ : excellent rapport qualité/performance, tourne sur un GPU 8 Go ou sur un CPU puissant.
Installation des dépendances
# Créer un environnement virtuel
python -m venv venv
source venv/bin/activate # Linux/Mac
# ou venv\Scripts\activate # Windows
# Installer les dépendances
pip install fastapi uvicorn python-multipart
pip install llama-cpp-python --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu
pip install huggingface_hub
CMAKE_ARGS="-DLLAMA_CUDA=on" pip install llama-cpp-python pour activer l'accélération CUDA et multiplier les performances par 5 à 10x.
Télécharger un modèle GGUF
Les modèles GGUF sont des versions quantisées optimisées pour llama-cpp. Téléchargez-les depuis HuggingFace :
from huggingface_hub import hf_hub_download
# Télécharger Mistral-7B Q4_K_M (~4 Go)
model_path = hf_hub_download(
repo_id="TheBloke/Mistral-7B-Instruct-v0.2-GGUF",
filename="mistral-7b-instruct-v0.2.Q4_K_M.gguf",
local_dir="./models"
)
print(f"Modèle téléchargé : {model_path}")
Charger le modèle et tester
from llama_cpp import Llama
# Charger le modèle (ajustez n_ctx et n_threads selon votre machine)
llm = Llama(
model_path="./models/mistral-7b-instruct-v0.2.Q4_K_M.gguf",
n_ctx=4096, # taille de la fenêtre de contexte
n_threads=8, # nombre de threads CPU
n_gpu_layers=35, # couches sur GPU (0 si CPU seulement)
verbose=False
)
# Test simple
response = llm(
"Explique-moi les transformers en NLP en 3 phrases.",
max_tokens=300,
temperature=0.7,
stop=["", "[INST]"]
)
print(response["choices"][0]["text"])
Créer l'API FastAPI complète
Voici une API production-ready avec gestion de l'historique de conversation :
from fastapi import FastAPI, HTTPException, Depends, Header
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from llama_cpp import Llama
from typing import List, Optional
import os
app = FastAPI(title="ChatGPT Local API", version="1.0.0")
# CORS — autorise le frontend local
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000", "http://localhost:8000"],
allow_methods=["GET", "POST"],
allow_headers=["*"],
)
# Charger le modèle au démarrage
llm = Llama(
model_path="./models/mistral-7b-instruct-v0.2.Q4_K_M.gguf",
n_ctx=4096,
n_threads=8,
n_gpu_layers=35,
verbose=False
)
# Modèles de données
class Message(BaseModel):
role: str # "user" ou "assistant"
content: str
class ChatRequest(BaseModel):
message: str
history: Optional[List[Message]] = []
max_tokens: int = 512
temperature: float = 0.7
class ChatResponse(BaseModel):
response: str
tokens_used: int
# Authentification simple par API key
API_KEY = os.environ.get("API_KEY", "dev-secret-key")
def verify_api_key(authorization: Optional[str] = Header(None)):
if authorization != f"Bearer {API_KEY}":
raise HTTPException(status_code=401, detail="Clé API invalide")
return True
def build_prompt(message: str, history: List[Message]) -> str:
"""Construit un prompt au format Mistral Instruct."""
prompt = ""
for msg in history[-6:]: # Garder les 6 derniers échanges
if msg.role == "user":
prompt += f"[INST] {msg.content} [/INST]"
else:
prompt += f" {msg.content} "
prompt += f"[INST] {message} [/INST]"
return prompt
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest, authenticated: bool = Depends(verify_api_key)):
"""Endpoint principal de conversation avec historique."""
prompt = build_prompt(request.message, request.history)
output = llm(
prompt,
max_tokens=request.max_tokens,
temperature=request.temperature,
stop=["[INST]", ""],
echo=False
)
response_text = output["choices"][0]["text"].strip()
tokens_used = output["usage"]["total_tokens"]
return ChatResponse(response=response_text, tokens_used=tokens_used)
@app.get("/health")
async def health():
"""Vérification que l'API est opérationnelle."""
return {"status": "ok", "model": "mistral-7b-instruct"}
@app.get("/")
async def root():
return {"message": "ChatGPT Local API — /docs pour la documentation"}
Démarrer l'API
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
L'API est maintenant accessible sur http://localhost:8000. Rendez-vous sur http://localhost:8000/docs pour la documentation Swagger interactive.
Tester l'API avec curl
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-H "Authorization: Bearer dev-secret-key" \
-d '{
"message": "Quelle est la différence entre supervised et unsupervised learning ?",
"history": [],
"max_tokens": 400,
"temperature": 0.7
}'
Interface web minimaliste
Ajoutez ce fichier static/index.html pour une interface de chat :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Mon ChatGPT Local</title>
<style>
body { font-family: sans-serif; max-width: 700px; margin: 2rem auto; padding: 1rem; }
#chat { border: 1px solid #ddd; border-radius: 8px; height: 400px; overflow-y: auto; padding: 1rem; margin-bottom: 1rem; }
.user { color: #1e40af; margin: 0.5rem 0; }
.assistant { color: #065f46; margin: 0.5rem 0; }
input { width: 80%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; }
button { padding: 0.5rem 1rem; background: #6366f1; color: white; border: none; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>
<h1>Mon Assistant IA Local</h1>
<div id="chat"></div>
<input id="input" type="text" placeholder="Posez votre question..." />
<button onclick="sendMessage()">Envoyer</button>
<script>
const history = [];
async function sendMessage() {
const input = document.getElementById('input');
const message = input.value.trim();
if (!message) return;
appendMessage('user', message);
history.push({ role: 'user', content: message });
input.value = '';
const res = await fetch('/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer dev-secret-key'
},
body: JSON.stringify({ message, history: history.slice(0, -1) })
});
const data = await res.json();
appendMessage('assistant', data.response);
history.push({ role: 'assistant', content: data.response });
}
function appendMessage(role, text) {
const div = document.createElement('div');
div.className = role;
div.textContent = (role === 'user' ? '🧑 ' : '🤖 ') + text;
document.getElementById('chat').appendChild(div);
document.getElementById('chat').scrollTop = 9999;
}
document.getElementById('input').addEventListener('keydown', e => {
if (e.key === 'Enter') sendMessage();
});
</script>
</body>
</html>
Servez ce fichier depuis FastAPI en ajoutant dans main.py :
app.mount("/", StaticFiles(directory="static", html=True), name="static")
Déploiement avec Docker
Pour un déploiement reproductible, voici un Dockerfile complet :
FROM python:3.11-slim
WORKDIR /app
# Dépendances système
RUN apt-get update && apt-get install -y \
build-essential cmake git \
&& rm -rf /var/lib/apt/lists/*
# Dépendances Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Code et modèle
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# Builder et lancer
docker build -t chatgpt-local .
docker run -p 8000:8000 -v $(pwd)/models:/app/models chatgpt-local
Alternative : Ollama + FastAPI
Si vous préférez une approche plus simple sans gérer llama-cpp directement, Ollama expose une API REST compatible OpenAI que vous pouvez proxy via FastAPI :
# Installer Ollama
curl -fsSL https://ollama.ai/install.sh | sh
# Télécharger et lancer Mistral
ollama pull mistral
ollama serve # tourne sur http://localhost:11434
import httpx
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class ChatRequest(BaseModel):
message: str
@app.post("/chat")
async def chat(request: ChatRequest):
async with httpx.AsyncClient() as client:
response = await client.post(
"http://localhost:11434/api/generate",
json={"model": "mistral", "prompt": request.message, "stream": False},
timeout=120
)
data = response.json()
return {"response": data["response"]}
Pour aller plus loin
- Streaming : utilisez
StreamingResponsede FastAPI pour afficher les tokens au fur et à mesure - RAG (Retrieval Augmented Generation) : connectez votre LLM à une base vectorielle (Chroma, Qdrant) pour interroger vos propres documents
- Fine-tuning : entraînez le modèle sur vos données métier avec LoRA/QLoRA pour des réponses spécialisées
- Rate limiting : ajoutez
slowapipour limiter les requêtes par IP - Monitoring : intégrez Prometheus + Grafana pour surveiller la latence et les erreurs
Maîtrisez le NLP derrière les LLMs
Transformers, tokenisation, attention mechanism, fine-tuning — tout ce qu'il faut comprendre pour vraiment maîtriser les modèles comme Mistral et LLaMA. Formation pratique avec projets concrets en Python.
Découvrir la formation NLP →