Construire un Chatbot IA Local avec Ollama et Next.js : Guide Complet

AI Bot
Par AI Bot ·

Chargement du lecteur de synthèse vocale...

Vos données ne quittent jamais votre machine. Dans ce tutoriel, vous allez construire un chatbot IA entièrement fonctionnel qui tourne uniquement sur votre matériel local avec Ollama et Next.js — sans clés API, sans services cloud, sans partage de données.

Ce que vous allez apprendre

À la fin de ce tutoriel, vous serez capable de :

  • Installer et configurer Ollama pour exécuter des LLM localement
  • Construire une interface de chat Next.js avec streaming en temps réel
  • Intégrer Ollama avec le Vercel AI SDK pour une expérience de qualité production
  • Ajouter la sélection de modèles pour que les utilisateurs puissent basculer entre les LLM
  • Gérer les erreurs gracieusement quand Ollama ne fonctionne pas
  • Comprendre les compromis entre IA locale et IA cloud

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • Node.js 20+ installé (node --version)
  • Connaissances de base en React et TypeScript
  • Un éditeur de code — VS Code ou Cursor recommandé
  • 8 Go+ de RAM (16 Go recommandés pour les modèles plus grands)
  • macOS, Linux ou Windows avec WSL2

Pourquoi exécuter l'IA localement ?

Les services d'IA cloud comme OpenAI et Anthropic sont puissants, mais ils comportent des compromis :

ConsidérationIA CloudIA Locale (Ollama)
ConfidentialitéDonnées envoyées à des serveurs tiersLes données restent sur votre machine
CoûtPaiement par tokenGratuit après le téléchargement
LatenceAller-retour réseau nécessaireAccès direct au matériel
DisponibilitéNécessite internetFonctionne hors ligne
PersonnalisationLimité aux modèles du fournisseurExécuter tout modèle ouvert

Pour les outils internes, le traitement de données sensibles et les applications hors ligne, l'IA locale est le choix évident.


Étape 1 : Installer Ollama

Ollama est un runtime léger pour exécuter des modèles de langage localement. Il gère le téléchargement, la quantification et le service des modèles via une API simple.

macOS

brew install ollama

Ou téléchargez depuis ollama.com.

Linux

curl -fsSL https://ollama.com/install.sh | sh

Windows

Téléchargez l'installateur depuis ollama.com ou utilisez WSL2 avec les instructions Linux.

Vérifier l'installation

ollama --version

Démarrez le serveur Ollama :

ollama serve

Cela démarre un serveur API local sur http://localhost:11434.


Étape 2 : Télécharger votre premier modèle

Ollama donne accès à des centaines de modèles open-source. Commençons avec Llama 3.2, le modèle compact et performant de Meta :

ollama pull llama3.2

Cela télécharge le modèle de 3 milliards de paramètres (~2 Go). Pour une option plus légère :

ollama pull llama3.2:1b

Modèles recommandés pour 2026

ModèleTailleIdéal pour
llama3.2:1b700 MoRéponses rapides, machines à ressources limitées
llama3.22 GoChat général, bon équilibre
mistral4 GoRaisonnement fort, multilingue
qwen3:4b2,5 GoRaisonnement en chaîne de pensée
qwen2.5-coder:7b4,5 GoGénération et revue de code

Testez votre modèle dans le terminal :

ollama run llama3.2
>>> What is the capital of Tunisia?

Vous devriez voir une réponse comme : "The capital of Tunisia is Tunis."


Étape 3 : Créer le projet Next.js

Maintenant construisons l'interface de chat. Créez un nouveau projet Next.js :

npx create-next-app@latest ollama-chat --typescript --tailwind --app --src-dir
cd ollama-chat

Installez les dépendances nécessaires :

npm install ai ollama-ai-provider @ai-sdk/react

Voici ce que fait chaque package :

  • ai — Le cœur du Vercel AI SDK avec streamText, generateText et plus encore
  • ollama-ai-provider — Le fournisseur communautaire qui connecte l'AI SDK à Ollama
  • @ai-sdk/react — Les hooks React comme useChat pour construire des interfaces de chat

Étape 4 : Configurer le fournisseur Ollama

Créez une configuration client Ollama partagée :

// src/lib/ollama.ts
import { createOllama } from 'ollama-ai-provider';
 
export const ollama = createOllama({
  baseURL: process.env.OLLAMA_BASE_URL ?? 'http://localhost:11434/api',
});
 
export const DEFAULT_MODEL = process.env.OLLAMA_DEFAULT_MODEL ?? 'llama3.2';

Ajoutez les variables d'environnement :

# .env.local
OLLAMA_BASE_URL=http://localhost:11434/api
OLLAMA_DEFAULT_MODEL=llama3.2

Étape 5 : Construire la route API de chat

C'est le cœur de notre application — une route API Next.js qui diffuse les réponses d'Ollama.

// src/app/api/chat/route.ts
import { streamText } from 'ai';
import { ollama, DEFAULT_MODEL } from '@/lib/ollama';
 
export const maxDuration = 60;
 
export async function POST(req: Request) {
  try {
    const { messages, model } = await req.json();
 
    const result = await streamText({
      model: ollama(model ?? DEFAULT_MODEL),
      system: 'You are a helpful, concise assistant. Answer questions clearly and accurately.',
      messages,
    });
 
    return result.toDataStreamResponse();
  } catch (error) {
    if (error instanceof Error && error.message.includes('ECONNREFUSED')) {
      return new Response(
        JSON.stringify({
          error: 'Ollama is not running. Start it with: ollama serve',
        }),
        { status: 503, headers: { 'Content-Type': 'application/json' } }
      );
    }
 
    return new Response(
      JSON.stringify({ error: 'An unexpected error occurred' }),
      { status: 500, headers: { 'Content-Type': 'application/json' } }
    );
  }
}

Points clés :

  • maxDuration = 60 donne à la route jusqu'à 60 secondes pour le streaming, important pour les modèles plus grands
  • streamText gère le protocole de streaming entre Ollama et le client
  • toDataStreamResponse() convertit le flux au format attendu par useChat
  • La gestion d'erreurs capture les échecs de connexion quand Ollama ne fonctionne pas

Étape 6 : Ajouter un endpoint pour les modèles

Permettez aux utilisateurs de voir quels modèles sont disponibles localement :

// src/app/api/models/route.ts
export async function GET() {
  try {
    const baseURL = process.env.OLLAMA_BASE_URL?.replace('/api', '')
      ?? 'http://localhost:11434';
 
    const res = await fetch(`${baseURL}/api/tags`);
 
    if (!res.ok) {
      throw new Error('Failed to fetch models');
    }
 
    const data = await res.json();
    const models = data.models.map((m: { name: string; size: number }) => ({
      id: m.name,
      label: m.name,
      size: `${(m.size / 1e9).toFixed(1)}GB`,
    }));
 
    return Response.json({ models });
  } catch {
    return Response.json({ models: [], error: 'Ollama is not available' });
  }
}

Étape 7 : Construire le composant de chat

Maintenant la partie amusante — l'interface de chat. Créez le composant de chat principal :

// src/components/Chat.tsx
'use client';
 
import { useChat } from '@ai-sdk/react';
import { useState, useRef, useEffect } from 'react';
import { ModelSelector } from './ModelSelector';
 
export function Chat() {
  const [model, setModel] = useState('llama3.2');
  const scrollRef = useRef<HTMLDivElement>(null);
 
  const { messages, input, handleInputChange, handleSubmit, isLoading, error } =
    useChat({
      api: '/api/chat',
      body: { model },
    });
 
  useEffect(() => {
    scrollRef.current?.scrollTo({
      top: scrollRef.current.scrollHeight,
      behavior: 'smooth',
    });
  }, [messages]);
 
  return (
    <div className="flex flex-col h-screen max-w-3xl mx-auto">
      {/* En-tête */}
      <header className="flex items-center justify-between p-4 border-b">
        <h1 className="text-xl font-semibold">Chat IA Local</h1>
        <ModelSelector value={model} onChange={setModel} />
      </header>
 
      {/* Messages */}
      <div ref={scrollRef} className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.length === 0 && (
          <div className="text-center text-gray-500 mt-20">
            <p className="text-4xl mb-4">🤖</p>
            <p className="text-lg font-medium">Votre assistant IA privé</p>
            <p className="text-sm mt-2">
              Propulsé par Ollama — tout tourne sur votre machine.
            </p>
          </div>
        )}
 
        {messages.map((m) => (
          <div
            key={m.id}
            className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
          >
            <div
              className={`max-w-[80%] rounded-2xl px-4 py-3 ${
                m.role === 'user'
                  ? 'bg-blue-600 text-white'
                  : 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100'
              }`}
            >
              <p className="whitespace-pre-wrap">{m.content}</p>
            </div>
          </div>
        ))}
 
        {isLoading && messages[messages.length - 1]?.role === 'user' && (
          <div className="flex justify-start">
            <div className="bg-gray-100 dark:bg-gray-800 rounded-2xl px-4 py-3">
              <span className="animate-pulse">Réflexion en cours...</span>
            </div>
          </div>
        )}
      </div>
 
      {/* Affichage des erreurs */}
      {error && (
        <div className="mx-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-400 text-sm">
          {error.message.includes('503')
            ? "Ollama ne fonctionne pas. Démarrez-le avec : ollama serve"
            : "Quelque chose s'est mal passé. Veuillez réessayer."}
        </div>
      )}
 
      {/* Saisie */}
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <div className="flex gap-2">
          <input
            value={input}
            onChange={handleInputChange}
            placeholder="Tapez un message..."
            disabled={isLoading}
            className="flex-1 rounded-xl border px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700"
          />
          <button
            type="submit"
            disabled={isLoading || !input.trim()}
            className="rounded-xl bg-blue-600 px-6 py-3 text-white font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
          >
            Envoyer
          </button>
        </div>
      </form>
    </div>
  );
}

Étape 8 : Construire le sélecteur de modèles

Ce composant récupère les modèles disponibles depuis Ollama et permet aux utilisateurs de basculer entre eux :

// src/components/ModelSelector.tsx
'use client';
 
import { useState, useEffect } from 'react';
 
interface Model {
  id: string;
  label: string;
  size: string;
}
 
interface ModelSelectorProps {
  value: string;
  onChange: (model: string) => void;
}
 
export function ModelSelector({ value, onChange }: ModelSelectorProps) {
  const [models, setModels] = useState<Model[]>([]);
  const [loading, setLoading] = useState(true);
 
  useEffect(() => {
    fetch('/api/models')
      .then((res) => res.json())
      .then((data) => {
        setModels(data.models ?? []);
        setLoading(false);
      })
      .catch(() => setLoading(false));
  }, []);
 
  if (loading) {
    return (
      <select disabled className="rounded-lg border px-3 py-2 text-sm opacity-50">
        <option>Chargement des modèles...</option>
      </select>
    );
  }
 
  if (models.length === 0) {
    return (
      <span className="text-sm text-red-500">Aucun modèle trouvé</span>
    );
  }
 
  return (
    <select
      value={value}
      onChange={(e) => onChange(e.target.value)}
      className="rounded-lg border px-3 py-2 text-sm bg-white dark:bg-gray-800 dark:border-gray-700"
    >
      {models.map((m) => (
        <option key={m.id} value={m.id}>
          {m.label} ({m.size})
        </option>
      ))}
    </select>
  );
}

Étape 9 : Relier la page

Mettez à jour la page principale pour afficher le composant de chat :

// src/app/page.tsx
import { Chat } from '@/components/Chat';
 
export default function Home() {
  return <Chat />;
}

Étape 10 : Lancer et tester

Démarrez le serveur de développement :

npm run dev

Assurez-vous qu'Ollama fonctionne dans un autre terminal :

ollama serve

Ouvrez http://localhost:3000 et commencez à discuter. Vous devriez voir :

  1. Le sélecteur de modèles rempli avec vos modèles locaux
  2. Des réponses en streaming en temps réel pendant que le modèle génère du texte
  3. Une expérience de chat fluide — tout tourne localement

Liste de vérification des tests

  • Envoyez un message simple et vérifiez que le streaming fonctionne
  • Basculez entre les modèles avec le sélecteur
  • Arrêtez Ollama (Ctrl+C sur ollama serve) et vérifiez que le message d'erreur apparaît
  • Redémarrez Ollama et vérifiez que le chat récupère
  • Envoyez un long message et vérifiez que le timeout de 60 secondes est suffisant

Aller plus loin : Sorties structurées

Ollama prend en charge les sorties JSON structurées avec des schémas Zod. C'est utile pour construire des outils, extraire des données ou imposer des formats de réponse :

// src/app/api/analyze/route.ts
import { generateObject } from 'ai';
import { ollama } from '@/lib/ollama';
import { z } from 'zod';
 
const SentimentSchema = z.object({
  sentiment: z.enum(['positive', 'negative', 'neutral']),
  confidence: z.number().min(0).max(1),
  summary: z.string().max(200),
});
 
export async function POST(req: Request) {
  const { text } = await req.json();
 
  const { object } = await generateObject({
    model: ollama('llama3.2'),
    schema: SentimentSchema,
    prompt: `Analyze the sentiment of this text: "${text}"`,
  });
 
  return Response.json(object);
}

La réponse correspondra toujours à votre schéma :

{
  "sentiment": "positive",
  "confidence": 0.92,
  "summary": "The text expresses strong satisfaction with the product."
}

Aller plus loin : Embeddings pour le RAG

Vous pouvez utiliser Ollama pour générer des embeddings afin de construire un système de Génération Augmentée par Récupération (RAG) :

import { embedMany } from 'ai';
import { ollama } from '@/lib/ollama';
 
const { embeddings } = await embedMany({
  model: ollama.embeddingModel('nomic-embed-text'),
  values: [
    'Next.js is a React framework for the web.',
    'Ollama runs large language models locally.',
    'TypeScript adds static types to JavaScript.',
  ],
});
 
// Chaque embedding est un tableau float32 que vous pouvez stocker dans une base vectorielle
console.log(embeddings[0].length); // 768 dimensions

Combinez cela avec une base de données vectorielle comme pgvector ou ChromaDB pour construire un pipeline RAG entièrement local.


Dépannage

Ollama ne répond pas

# Vérifiez si Ollama fonctionne
curl http://localhost:11434/api/tags
 
# Sinon, démarrez-le
ollama serve

Le modèle est trop lent

Essayez un modèle plus petit :

ollama pull llama3.2:1b  # 1 milliard de paramètres, beaucoup plus rapide

Ou vérifiez si votre machine supporte l'accélération GPU :

ollama ps  # Affiche les modèles chargés et leur utilisation mémoire

Erreurs CORS dans le navigateur

N'appelez jamais Ollama directement depuis le navigateur. Passez toujours par votre route API Next.js — cela évite entièrement les problèmes de CORS et sécurise votre architecture.

Mémoire insuffisante

Les grands modèles nécessitent beaucoup de RAM. Si vous voyez des erreurs de mémoire :

  1. Utilisez une variante de modèle plus petite (llama3.2:1b au lieu de llama3.2)
  2. Fermez les autres applications gourmandes en mémoire
  3. Vérifiez la mémoire disponible avec ollama ps

Vue d'ensemble de l'architecture

Voici comment les pièces s'assemblent :

┌─────────────────┐     HTTP POST      ┌──────────────────┐     HTTP POST      ┌──────────────┐
│                 │  ──────────────►   │                  │  ──────────────►   │              │
│   React Client  │     /api/chat      │  Next.js Server  │   localhost:11434  │    Ollama    │
│   (useChat)     │  ◄──────────────   │  (Route Handler) │  ◄──────────────   │   (Local)    │
│                 │   SSE stream       │                  │   NDJSON stream    │              │
└─────────────────┘                    └──────────────────┘                    └──────────────┘
  1. Le client React utilise le hook useChat pour envoyer des messages et recevoir les réponses en streaming
  2. La route API Next.js reçoit la requête, appelle Ollama via le fournisseur AI SDK et diffuse la réponse
  3. Ollama exécute l'inférence du modèle localement et renvoie les tokens en JSON délimité par des retours à la ligne

Toute la communication reste sur votre réseau local — rien n'atteint internet.


Prochaines étapes

Maintenant que vous avez un chatbot IA local fonctionnel, considérez ces améliorations :

  • Ajouter un historique de conversation — Persistez les chats avec le stockage local ou une base de données
  • Construire un pipeline RAG — Utilisez les embeddings et une base vectorielle pour le Q&A documentaire
  • Ajouter l'appel d'outils — Laissez le modèle exécuter des fonctions comme la recherche web ou les calculs
  • Déployer sur votre réseau local — Rendez le chatbot accessible aux autres appareils de votre réseau
  • Essayer les modèles de vision — Utilisez llama3.2-vision pour analyser des images localement

Tutoriels associés sur Noqta :


Conclusion

Vous avez construit un chatbot IA entièrement local qui :

  • Fonctionne entièrement sur votre matériel sans aucune dépendance cloud
  • Diffuse les réponses en temps réel pour une expérience utilisateur fluide
  • Prend en charge plusieurs modèles grâce à un sélecteur dynamique
  • Gère les erreurs gracieusement quand Ollama est indisponible
  • Utilise le Vercel AI SDK pour une architecture de qualité production

L'écosystème de l'IA locale a considérablement mûri en 2026. Avec Ollama qui gère le runtime des modèles et le Vercel AI SDK qui fournit l'expérience développeur, construire des applications d'IA privées est désormais aussi simple que construire n'importe quelle autre application web.

Vos données restent les vôtres. Votre IA tourne selon vos règles.


Vous voulez lire plus de tutoriels? Découvrez notre dernier tutoriel sur Révolutionner l'engagement client : Les agents virtuels alimentés par l'IA de Twilio.

Discutez de votre projet avec nous

Nous sommes ici pour vous aider avec vos besoins en développement Web. Planifiez un appel pour discuter de votre projet et comment nous pouvons vous aider.

Trouvons les meilleures solutions pour vos besoins.

Articles connexes

Construire un Agent IA Autonome avec Agentic RAG et Next.js

Apprenez a construire un agent IA qui decide de maniere autonome quand et comment recuperer des informations depuis des bases de donnees vectorielles. Un guide pratique complet avec Vercel AI SDK et Next.js, accompagne d'exemples executables.

30 min read·