Featured image of post LLM en local sur GPU AMD : ROCm, Ollama, OpenCode

LLM en local sur GPU AMD : ROCm, Ollama, OpenCode

Faire tourner un agent de codage IA en local sur une RX 7900 XTX, sans CUDA, sans cloud, sans donner sa carte bleue à OpenAI.

Introduction

CUDA partout, ROCm plus discret. C’est le sentiment qu’on peut avoir en cherchant à faire tourner un LLM en local sur une carte AMD : la majorité des tutoriels et benchmarks qui circulent partent du principe qu’on a une carte NVIDIA. Ici, c’est une RX 7900 XTX (Navi 31, RDNA3), 24 Go de VRAM, et pas de CUDA à l’horizon.

Bonne nouvelle : ROCm a eu le temps de mûrir depuis, il est packagé nativement dans les dépôts CachyOS/Arch, et Ollama sait très bien s’en servir. La mauvaise nouvelle, c’est qu’il y a deux ou trois pièges classiques (iGPU qui se fait passer pour le GPU de compute, mauvais paquet, mauvais tag de modèle…) qu’il vaut mieux connaître avant de perdre une soirée à débugger.

Voici la stack complète : ROCm → Ollama → OpenCode, du driver jusqu’à l’agent de codage qui tourne dans le terminal.

Lexique

Quelques termes qui reviennent tout au long de l’article, pour ceux qui n’auraient pas suivi l’actualité IA de près :

LLM (Large Language Model)Un modèle de langage entraîné sur d'énormes volumes de texte, capable de générer/comprendre du texte (et du code).
ROCmLa stack logicielle open source d'AMD équivalente à CUDA chez NVIDIA - pilotes, runtime de calcul (HIP) et bibliothèques (BLAS, etc.) pour faire tourner du calcul GPU générique.
HIPL'API de programmation GPU d'AMD, pensée pour être proche de CUDA. C'est ce que ROCm expose en interne, d'où les noms de variables HIP_VISIBLE_DEVICES.
OllamaUn serveur qui télécharge, charge en mémoire et sert des LLM localement via une API HTTP, avec gestion automatique du backend GPU (CUDA/ROCm/CPU selon le paquet installé).
OpenCodeUn agent de codage IA en ligne de commande (façon Aider/Claude Code) qui peut se connecter à n'importe quel provider compatible OpenAI, dont Ollama en local.
VRAMLa mémoire embarquée sur le GPU. Elle contient à la fois les poids du modèle chargé et le contexte en cours de traitement (KV-cache) - c'est la ressource limitante sur ce genre de setup.
GPU / iGPULe GPU dédié (carte graphique, ici la RX 7900 XTX) fait le calcul lourd ; l'iGPU (intégré au CPU) n'est pas fait pour ça et doit être exclu du calcul.
Contexte (context window)Le nombre maximum de tokens (mots/fragments de mots) qu'un modèle peut "voir" en une fois, prompt + réponse compris. Un contexte trop court limite la taille des fichiers qu'un agent comme OpenCode peut analyser.
KV-cacheLa mémoire utilisée par le modèle pour stocker les calculs intermédiaires de tout le contexte en cours - elle grandit avec la taille du contexte utilisé, en plus des poids du modèle.
MoE (Mixture of Experts)Une architecture où seule une partie des paramètres du modèle est activée par token traité - plus rapide à taille égale qu'un modèle "dense", mais qui occupe quand même toute la VRAM pour stocker l'ensemble des experts.
Tokens/sLa vitesse de génération d'un modèle, mesurée en tokens produits par seconde. Un bon indicateur de confort d'usage au quotidien.

Le matériel

  • GPU dédié : AMD Radeon RX 7900 XTX (Navi 31, gfx1100) - 24 Go de VRAM
  • iGPU (à ignorer pour le compute) : AMD Raphael (gfx1036) - non supporté officiellement par ROCm
  • OS : CachyOS (Arch-based)

Le point important ici : la machine a deux GPU AMD détectés par ROCm, dont un qui ne doit surtout pas être utilisé pour l’inférence. On y revient plus bas.

1. ROCm : le minimum syndical

Pas besoin du script d’installation AMD ni de l’AUR - ROCm est dans le dépôt officiel extra. Et contrairement à ce qu’on pourrait croire, il n’est pas nécessaire d’installer le SDK ROCm complet (rocm-hip-sdk, rocm-opencl-sdk) juste pour faire tourner Ollama : le paquet ollama-rocm embarque déjà tout le runtime dont il a besoin via sa dépendance hipblas (qui tire hip-runtime-amd, rocblas, rocsolver…).

Seuls deux outils sont utiles à installer séparément, pour le diagnostic et le monitoring :

sudo pacman -S rocminfo rocm-smi-lib

Droits d’accès au GPU

sudo usermod -aG render,video $USER

Déconnexion/reconnexion (ou reboot) obligatoire pour que ça prenne effet.

Identifier le bon GPU

rocm-smi --showproductname
GPU[0]          : Card Series:          AMD Radeon RX 7900 XTX
GPU[0]          : GFX Version:          gfx1100
GPU[1]          : Card Series:          AMD Ryzen 7 7700X 8-Core Processor
GPU[1]          : GFX Version:          gfx1036

Sur une machine avec iGPU + GPU dédié, ça liste deux agents. Et bonne nouvelle : le [N] affiché devant chaque GPU est directement l’index attendu par HIP_VISIBLE_DEVICES/ROCR_VISIBLE_DEVICES - pas besoin de le déduire soi-même.

GPUArchitectureCompute ROCm ?
Raphael (iGPU)gfx1036Non
RX 7900 XTXgfx1100Oui

Ici : 0 pour la 7900 XTX, 1 pour l’iGPU. Cet index sert ensuite à forcer explicitement le bon GPU, parce qu’Ollama (comme pas mal d’outils ROCm) a parfois la mauvaise idée de vouloir utiliser tous les GPU détectés. Et on en profite pour relever tout de suite la fenêtre de contexte par défaut d’Ollama (voir plus bas pourquoi c’est indispensable) :

export HIP_VISIBLE_DEVICES=0
export ROCR_VISIBLE_DEVICES=0
export OLLAMA_CONTEXT_LENGTH=65536
export OLLAMA_FLASH_ATTENTION=1
export OLLAMA_KV_CACHE_TYPE=q8_0

0 correspond à l’index de la RX 7900 XTX relevé à l’étape précédente - à adapter si le tien diffère.

Pourquoi OLLAMA_FLASH_ATTENTION + OLLAMA_KV_CACHE_TYPE=q8_0 : sur gfx1100, la flash attention seule n’apporte aucun gain de vitesse mesurable, mais c’est le prérequis pour activer la quantization du KV-cache (q8_0), qui réduit son empreinte VRAM à contexte élevé. Testé sans régression sur qwen3.6:27b (18 Go au lieu de 20 à contexte 65536, même débit ~34 tokens/s) - utile pour laisser de la marge quand un modèle a un KV-cache plus lourd.

Sous fish, export VAR=VALEUR fonctionne nativement, donc les commandes ci-dessus s’utilisent telles quelles pour la session courante. Pour les rendre persistantes, deux options :

  • Variables universelles (recommandé, aucun fichier à éditer) : à exécuter une seule fois dans un terminal - stockées par fish, valables pour toutes les sessions présentes et futures.
    set -Ux HIP_VISIBLE_DEVICES 0
    set -Ux ROCR_VISIBLE_DEVICES 0
    set -Ux OLLAMA_CONTEXT_LENGTH 65536
    set -Ux OLLAMA_FLASH_ATTENTION 1
    set -Ux OLLAMA_KV_CACHE_TYPE q8_0
    
  • Via config.fish (si on préfère versionner ses dotfiles) : ajouter dans ~/.config/fish/config.fish - réexécuté à chaque nouveau shell.
    set -gx HIP_VISIBLE_DEVICES 0
    set -gx ROCR_VISIBLE_DEVICES 0
    set -gx OLLAMA_CONTEXT_LENGTH 65536
    set -gx OLLAMA_FLASH_ATTENTION 1
    set -gx OLLAMA_KV_CACHE_TYPE q8_0
    

Monitoring

watch -n1 rocm-smi

Indispensable pendant l’inférence pour vérifier que c’est bien la 7900 XTX qui chauffe, et pas l’iGPU qui reste sagement à 0 %.

2. Ollama avec le backend ROCm

Trois variantes du paquet existent : ollama (CPU), ollama-cuda (NVIDIA), ollama-rocm (AMD). Sans surprise, c’est la troisième qu’il faut, et elle est dans les dépôts officiels - pas besoin du script curl | sh :

sudo pacman -S ollama-rocm

Ne pas installer ollama et ollama-rocm en même temps : même binaire, conflit garanti.

Démarrage manuel

ollama serve

Écoute par défaut sur http://localhost:11434. Les variables HIP_VISIBLE_DEVICES/ROCR_VISIBLE_DEVICES/OLLAMA_CONTEXT_LENGTH doivent être exportées dans le même shell avant cette commande.

Option : service systemd

Le paquet installe une unit ollama.service prête à l’emploi :

sudo systemctl enable --now ollama

Pour lui injecter les variables GPU de façon permanente (un service systemd ne voit pas les variables exportées dans un shell) :

sudo systemctl edit ollama
[Service]
Environment="HIP_VISIBLE_DEVICES=0"
Environment="ROCR_VISIBLE_DEVICES=0"
Environment="OLLAMA_CONTEXT_LENGTH=65536"
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
sudo systemctl daemon-reload
sudo systemctl restart ollama

Télécharger et tester un modèle

ollama pull qwen3-coder:30b
ollama run qwen3-coder:30b "écris une fonction fibonacci en python"

ollama pull devstral-small-2:24b
ollama run devstral-small-2:24b "écris une fonction fibonacci en python"

ollama pull qwen3.6:27b
ollama run qwen3.6:27b "écris une fonction fibonacci en python"

Lister les modèles déjà téléchargés :

ollama list

Vérifier que le bon GPU travaille

Pendant l’inférence, watch -n1 rocm-smi doit montrer la charge sur la 7900 XTX uniquement. Les logs (journalctl -u ollama -f ou le terminal si lancé manuellement) mentionnent aussi l’architecture détectée (gfx1100) au démarrage.

3. OpenCode branché sur Ollama

OpenCode est un agent de codage open source qui tourne dans le terminal (façon Aider/Claude Code), compatible avec plus de 75 providers via Models.dev - dont Ollama en local.

sudo pacman -S opencode

Là aussi, disponible directement dans extra, pas besoin de l’AUR ni du script d’install.

Copier/coller (session Wayland)

Sous Wayland, la copie de texte sélectionné dans OpenCode (“copied to clipboard”) nécessite wl-copy, absent par défaut :

sudo pacman -S wl-clipboard

Redémarrer OpenCode après l’installation - le process déjà lancé ne détecte pas l’outil rétroactivement.

Prérequis : le contexte

OpenCode recommande un modèle avec au moins 64k tokens de contexte. Ça élimine d’office pas mal de modèles “coder” historiques limités à 32K - qwen2.5-coder:32b par exemple. qwen3-coder:30b (256K), devstral-small-2:24b (256-384K) et qwen3.6:27b (256K) n’ont pas ce problème… sur le papier.

Le piège : peu importe le contexte annoncé par le modèle, Ollama charge par défaut une fenêtre de 32768 tokens seulement. Ces 256K et plus ne servent donc à rien tant qu’on n’a pas explicitement augmenté OLLAMA_CONTEXT_LENGTH (fait plus haut) et redémarré le service. Pire : au démarrage, Ollama (≥ 0.15.5) logue une ligne du genre vram-based default context ... default_num_ctx=32768 - un simple palier calculé sur la VRAM totale, affiché indépendamment de la variable qu’on a définie. Ça ne veut pas dire que ton override n’a pas fonctionné. La seule façon fiable de vérifier le contexte réellement chargé : ollama ps (colonne CONTEXT) une fois le modèle en mémoire.

Pour la valeur à choisir : le KV-cache du contexte consomme de la VRAM en plus des poids du modèle, sans formule simple. Avec 24 Go et qwen3-coder:30b ou qwen3.6:27b (~18 Go de poids, ~6 Go de marge) ou devstral-small-2:24b (~15 Go de poids, ~9 Go de marge), 65536 est un point de départ raisonnable - à monter vers 131072 s’il reste de la marge (rocm-smi pendant une session chargée), à redescendre sinon.

Configuration persistante

Fichier ~/.config/opencode/opencode.json :

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "ollama": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama (local)",
      "options": {
        "baseURL": "http://localhost:11434/v1"
      },
      "models": {
        "qwen3-coder:30b": {
          "name": "Qwen3-Coder 30B-A3B"
        },
        "devstral-small-2:24b": {
          "name": "Devstral Small 2 24B"
        },
        "qwen3.6:27b": {
          "name": "Qwen3.6 27B"
        }
      }
    }
  },
  "model": "ollama/qwen3-coder:30b",
  "permission": {
    "bash": "ask"
  }
}

Chaque modèle qu’on veut voir apparaître dans OpenCode doit être listé ici explicitement - il n’y a pas de découverte automatique des modèles Ollama installés.

permission.bash: "ask" demande une approbation avant toute commande shell lancée par l’agent (bash est le nom de l’outil OpenCode, il couvre toute commande quel que soit le shell système sous-jacent - bash, zsh, fish…). Autres valeurs possibles : "allow" (aucune confirmation) ou "deny" (bloqué). Voir la doc Permissions.

ollama launch opencode existe aussi pour lancer rapidement une session avec un modèle choisi dans un picker. Pratique pour tester, mais ça ne touche jamais opencode.json : les deux mécanismes sont indépendants.

4. Le vrai sujet : quel modèle sur 24 Go de VRAM

24 Go, c’est une limite dure. Modèle + contexte au-delà, et c’est le swap CPU (lent) ou le refus de charger.

Voici une sélection vérifiée directement sur ollama.com/library, tags et tailles à jour :

ModèleTagDisqueContexteRemarque
Qwen3-Coderqwen3-coder:30b18 Go256KMoE 30B-A3B, tool-calling natif de première classe (parser dédié Ollama ≥ 0.12), ~113 tokens/s (le plus rapide testé), pas de mode “thinking”
Devstral Small 2 (Mistral)devstral-small-2:24b15 Go256-384KModèle dense, tool-calling natif confirmé, ~41 tokens/s, pas de mode “thinking”, SWE-bench Verified 68%
Qwen3.6qwen3.6:27b17 Go256KBon si besoin de raisonnement poussé, mais latence élevée (mode “thinking”, ~1900 tokens pour une réponse simple)
Qwen2.5-Coderqwen2.5-coder:32b20 Go32K-
DeepSeek-Coder-V2deepseek-coder-v2:16b8.9 Go160K⚠️ Ne supporte pas le tool-calling - inutilisable dans OpenCode malgré sa rapidité
Codestralcodestral:22b13 Go32K-
GLM-4.7-Flashglm-4.7-flash:q4_K_M19 Go198K-

qwen3-coder:30b sort du lot : architecture MoE (30B au total, 3B de paramètres actifs par token), tool-calling natif de première classe intégré à Ollama (parser dédié, pas un template bricolé), pas de mode “thinking”. Le plus rapide testé sur ce matériel (~113 tokens/s, ~10s pour une fonction simple). devstral-small-2:24b reste une excellente alternative (modèle dense, SWE-bench Verified 68%) ; qwen3.6:27b si le raisonnement poussé compte plus que la vitesse. deepseek-coder-v2:16b, malgré son contexte généreux, est à écarter faute de tool-calling.

Pour comparer soi-même plusieurs candidats avant de se fixer, télécharger les prétendants et les faire tourner sur le même prompt :

ollama pull qwen3-coder:30b
ollama pull devstral-small-2:24b
ollama pull qwen3.6:27b

ollama run qwen3-coder:30b --verbose "écris une fonction qui calcule fibonacci en python"
ollama run devstral-small-2:24b --verbose "écris une fonction qui calcule fibonacci en python"
ollama run qwen3.6:27b --verbose "écris une fonction qui calcule fibonacci en python"

Le flag --verbose affiche les tokens/s en fin de réponse - un mini-benchmark maison pour comparer vitesse et style de réponse entre modèles, en gardant un œil sur rocm-smi pour vérifier que la VRAM utilisée reste sous la limite (pas de swap CPU).

Conclusion

Pas de CUDA, pas de cloud, pas d’abonnement : une RX 7900 XTX, ROCm packagé nativement, ollama-rocm et OpenCode suffisent à avoir un agent de codage local complet. Le seul vrai travail, c’est de bien identifier le GPU de compute quand il y a un iGPU dans les parages, de ne pas oublier OLLAMA_CONTEXT_LENGTH (sinon les 256K de contexte annoncés restent théoriques), et de vérifier les tags de modèles avant de les recommander à l’aveugle - un contexte généreux ou un bon score de benchmark ne garantissent rien si le modèle ne supporte pas le tool-calling qu’OpenCode utilise en permanence.