1 points par GN⁺ 3 시간 전 | 1 commentaires | Partager sur WhatsApp
  • La configuration d’un agent de code local permet d’exécuter des modèles sur macOS via une API compatible OpenAI même en cas de panne d’Internet, et de traiter des entrées texte et image dans Pi
  • Les tests ont été réalisés sur un Apple M1 Max 64 Go sous macOS 15.7.7 avec llama.cpp Metal et le modèle Gemma 4 26B-A4B GGUF, avec une vitesse de génération de base de 58.2 tok/s
  • Après ajout d’un modèle draft MTP et réglage de --spec-draft-n-max 3, la vitesse de génération est montée à 72.2 tok/s, soit une amélioration d’environ 24 %
  • Il faut charger mmproj-BF16.gguf avec --mmproj et définir l’entrée du modèle Pi sur ["text", "image"] pour que les entrées image comme les captures d’écran soient transmises
  • La configuration finale exécute le serveur llama.cpp sur 127.0.0.1:8080/v1, utilisé par Pi comme fournisseur local, et bien que Qwen3.6 35B-A3B ait montré de meilleurs benchmarks d’agent de code, il était plus lent dans ce test à 55 tok/s

Objectif de la configuration d’un agent de code local

  • Plusieurs pannes d’Internet ayant empêché l’usage d’un agent de code ont motivé l’essai d’une configuration en local
  • La configuration visée devait être suffisamment rapide sur Mac pour un usage réel, et utilisable aussi depuis d’autres outils via une API compatible OpenAI
  • L’objectif était également de pouvoir traiter des captures d’écran ou images au besoin, afin de réinjecter en entrée les résultats produits par l’agent
  • La configuration finale repose sur llama.cpp, Gemma 4 26B-A4B GGUF, un modèle draft MTP Q8, le projecteur multimodal Gemma 4 et l’agent de code en terminal Pi
  • L’environnement de test était un Apple M1 Max, 64 Go de mémoire unifiée, sous macOS 15.7.7

Modèle

  • Le modèle principal était gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf, disponible dans le dépôt Hugging Face unsloth-gemma-4-26B-A4B-it-GGUF
  • Ce fichier pèse environ 16 Go, et avec la tête draft MTP et le projecteur multimodal, le dossier du modèle atteint environ 17 Go
  • Le prompt de benchmark était Write a compact Python function that parses a unified diff and returns the changed file paths. Then explain two edge cases.
  • Chaque benchmark générait environ 128 tokens

Exécution de base : llama.cpp + Metal

  • Le modèle principal a été exécuté directement avec llama.cpp et l’accélération Metal
  • La commande utilisait llama-cli en indiquant le chemin du modèle, -ngl 999, -fa on, -c 4096, -n 128
  • Dans la configuration de base, la vitesse de traitement du prompt était de 298.0 tok/s, et la vitesse de génération de 58.2 tok/s
  • 58 tok/s n’est pas particulièrement rapide, mais reste exploitable, et les tâches d’agent de code exigent autant de vitesse que possible à cause des nombreux appels d’outils

Ajout du modèle draft MTP

  • Gemma 4 fournit un modèle draft MTP sous la forme MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf
  • Dans llama.cpp, il est chargé pour le speculative decoding via --model-draft, --spec-type draft-mtp, --spec-draft-n-max
  • Le premier essai avec MTP a atteint 69.2 tok/s avec 4 draft tokens
  • La documentation Unsloth recommande --spec-draft-n-max 2 comme point de départ, mais conseille de tester de 1 à 6 selon le matériel pour retenir la valeur la plus rapide
  • Après réglage de --spec-draft-n-max, la meilleure vitesse a été obtenue avec 3 draft tokens, à 72.2 tok/s
  • Le modèle principal seul atteignait 58.2 tok/s, contre 72.2 tok/s avec le modèle draft MTP Q8 ajouté
  • La vitesse de traitement du prompt est restée quasiment inchangée, tandis que la génération a progressé d’environ 24 %

Résultats de réglage MTP

  • Les valeurs de --spec-draft-n-max de 1 à 6 ont été testées
  • La valeur 1 donnait 295.5 tok/s pour le prompt et 68.4 tok/s en génération
  • La valeur 2 donnait 299.1 tok/s pour le prompt et 72.0 tok/s en génération
  • La valeur 3 était la plus rapide avec 295.6 tok/s pour le prompt et 72.2 tok/s en génération
  • Avec la valeur 4, la génération tombait à 70.7 tok/s, puis à 63.7 tok/s pour 5 et 61.2 tok/s pour 6
  • Sur M1 Max, 3 était le plus rapide, et 2 donnait aussi un résultat très proche

Comparaison avec MLX

  • Pour vérifier s’il existait une méthode plus rapide d’exécution sur Mac, des modèles MLX basés sur mlx-lm ont aussi été testés
  • llama.cpp Metal + MTP a atteint 72.2 tok/s avec la combinaison Unsloth GGUF Q4 + MTP Q8
  • llama.cpp Metal seul a atteint 58.2 tok/s avec Unsloth GGUF Q4
  • MLX-LM a atteint 45.8 tok/s avec Unsloth UD MLX 4-bit
  • MLX-LM a atteint 43.9 tok/s avec mlx-community 4-bit, et 38.1 tok/s avec mlx-community OptiQ 4-bit
  • Dans cette configuration précise, llama.cpp était plus rapide que MLX, et llama.cpp avec MTP constituait le meilleur choix
  • Un essai de Gemma 4 MTP via gemma-4-swift-mlx a aussi été tenté, mais le checkpoint MLX 26B 4-bit testé ne correspondait pas aux weight keys attendues par le loader, et l’essai a été abandonné sans retélécharger ni ajuster un nouveau modèle

Ajout de la prise en charge des images

  • Pour joindre des captures d’écran dans Pi, l’entrée du modèle ne devait pas être limitée au texte
  • À l’origine, l’entrée du modèle local était réglée sur "input": ["text"], ce qui empêchait Pi d’envoyer correctement au modèle la sortie des outils image
  • Le serveur llama.cpp nécessite aussi le projecteur multimodal Gemma 4 mmproj-BF16.gguf pour la prise en charge multimodale
  • En chargeant le projecteur avec --mmproj, llama.cpp annonce la prise en charge multimodale et Pi peut envoyer des images
  • Un test de llama.cpp Metal + MTP sans projecteur a donné 120.3 tok/s pour le prompt et 71.4 tok/s en génération
  • L’exécution finale avec mmproj-BF16.gguf chargé a donné 297.4 tok/s pour le prompt et 72.2 tok/s en génération
  • Aucun ralentissement de génération de texte n’a été observé dans l’exécution finale avec le projecteur chargé

Installation de llama.cpp

  • Les dépendances cmake, git, tmux, python@3.11 ont été installées via Homebrew
  • Le chemin ~/Developer/ML-Models/Gemma4/repos a été créé et le dépôt ggml-org/llama.cpp a été cloné dans repos/llama.cpp
  • La build a été configurée avec cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_METAL=ON -DGGML_ACCELERATE=ON
  • Ensuite, une build Release a été lancée avec cmake --build build --config Release -j
  • La build testée utilisait les réglages GGML_METAL=ON, GGML_ACCELERATE=ON, GGML_BLAS=ON, GGML_BLAS_VENDOR=Apple

Téléchargement des fichiers du modèle

  • Un environnement virtuel Python 3.11 a été créé et huggingface_hub ainsi que hf_xet ont été installés
  • huggingface-cli download a servi à récupérer le modèle principal Gemma 4, mmproj-BF16.gguf et le modèle draft MTP
  • Les fichiers téléchargés étaient gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf, mmproj-BF16.gguf, MTP/gemma-4-26B-A4B-it-Q8_0-MTP.gguf
  • Le dossier final du modèle contient ces trois fichiers sous models/unsloth-gemma-4-26B-A4B-it-GGUF/

Démarrage du serveur local

  • Le serveur final est lancé avec llama-server, en indiquant à la fois le modèle principal, le modèle draft MTP et le projecteur multimodal
  • Les options principales sont --spec-type draft-mtp, --spec-draft-n-max 3, -ngl 999, -fa on, -c 65536, --parallel 1
  • Le serveur est exécuté avec --host 127.0.0.1 --port 8080
  • L’endpoint compatible OpenAI est http://127.0.0.1:8080/v1
  • Le wrapper start_server.sh lance le serveur dans une session tmux et écrit les logs dans logs/llama-server-mtp.log
  • Après chmod +x start_server.sh, le serveur est démarré avec ./start_server.sh
  • Son bon fonctionnement se vérifie avec curl http://127.0.0.1:8080/v1/models

Configuration de Pi

  • Pi lit la configuration des fournisseurs de modèles depuis ~/.pi/agent/models.json
  • Le baseUrl du fournisseur local gemma4-local pointe vers http://127.0.0.1:8080/v1
  • api vaut openai-completions, et comme il s’agit d’un serveur local, authHeader est laissé à false
  • L’ID du modèle est gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf, et son nom est défini sur Gemma 4 26B-A4B Q4 + MTP
  • input doit être ["text", "image"], sinon Pi traite le modèle comme text-only
  • La fenêtre de contexte est définie à 65536, avec un maximum de 8192 tokens
  • Si besoin, defaultProvider peut être réglé sur gemma4-local et defaultModel sur ce nom de fichier GGUF dans ~/.pi/agent/settings.json
  • En exécutant pi --offline --list-models gemma, on s’attend à voir yes pour la prise en charge des images
  • L’exécution avec le modèle local se fait via pi --provider gemma4-local --model gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf
  • L’exécution non interactive prend la forme pi -p --provider gemma4-local --model gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf "Explain what this repository does"
  • Une entrée par capture d’écran se fait sous la forme pi -p @"/path/to/screenshot.png" "Describe this image and point out anything relevant to the UI"

Configuration finale

  • Le runtime d’inférence final est llama.cpp
  • L’accélération sur macOS combine Metal + Accelerate
  • Le modèle principal est gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf
  • Le modèle draft est gemma-4-26B-A4B-it-Q8_0-MTP.gguf
  • Le réglage MTP est --spec-draft-n-max 3
  • Le projecteur multimodal est mmproj-BF16.gguf
  • Le serveur est llama-server sur 127.0.0.1:8080
  • L’API est le /v1 compatible OpenAI
  • L’agent de code est Pi, avec une entrée modèle Pi définie sur ["text", "image"]
  • Dans cet environnement, le modèle draft MTP a fait passer la vitesse de génération de Gemma 4 de 58.2 tok/s à 72.2 tok/s, tout en gardant une configuration assez simple pour être exécutée comme serveur local compatible OpenAI

Alternative Qwen3.6 35B-A3B

  • Certains recommandent d’utiliser Qwen3.6 35B-A3B à la place de Gemma 4 26B-A4B
  • D’après les benchmarks vérifiables, Qwen est jugé nettement meilleur que Gemma 4 comme agent de code
  • Mais la configuration Qwen était plus lente, avec 55 tok/s pour la combinaison Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf, unsloth-Qwen3.6-35B-A3B-MTP-GGUF, mmproj-BF16.gguf
  • 55 tok/s au lieu de 72 tok/s représente une différence importante quand l’utilisateur doit attendre
  • Le téléchargement du modèle Qwen se fait depuis unsloth/Qwen3.6-35B-A3B-MTP-GGUF, en récupérant Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf et mmproj-BF16.gguf
  • Le serveur Qwen utilise le même llama-server, mais s’exécute sur --port 8081
  • Dans la configuration Pi, le nom du fournisseur Qwen est qwen36-local et son baseUrl est http://127.0.0.1:8081/v1
  • La configuration du modèle Qwen utilise reasoning: true, input: ["text", "image"], contextWindow: 65536, maxTokens: 8192

1 commentaires

 
GN⁺ 3 시간 전
Commentaires sur Hacker News
  • Si le prompt du benchmark était « écrire une fonction Python concise qui analyse un unified diff pour renvoyer les chemins des fichiers modifiés, et expliquer deux cas limites », et que chaque benchmark ne générait qu’environ 128 tokens, alors 128 tokens semblent bien trop peu pour obtenir un résultat pertinent
    L’accélération MTP dépend de la fréquence d’adoption des tokens prédits ; d’après mon expérience, le taux d’adoption est plus élevé au début de la sortie, donc des tests courts peuvent produire une accélération faussement positive
    llama.cpp dispose d’un outil de benchmark dédié qui parcourt les arguments sans avoir à redémarrer le serveur ni à renvoyer le prompt : https://github.com/ggml-org/llama.cpp/blob/master/tools/llam...
    La section sur le téléchargement des modèles aurait aussi dû mentionner que l’argument -hf de llama.cpp peut télécharger le modèle à votre place. Merci à l’auteur d’avoir partagé son expérience, mais ce n’est peut-être pas le meilleur guide pour les débutants

    • Ce n’était pas censé être un vrai guide pour développeurs. L’enregistrement d’écran a reçu beaucoup de favoris et j’ai commencé à recevoir des messages demandant comment le configurer, donc j’ai simplement rédigé rapidement comment j’avais monté ce test
      En voyant l’annonce « 2x faster » d’Unclothe, je me suis demandé : « est-ce que ça devient enfin assez rapide pour être vraiment utilisable ? » et je l’ai donc configuré moi-même
      J’avais aussi testé l’an dernier avec des choses comme Devstral, mais c’était trop lent et trop bête pour me donner envie de continuer, alors que cette fois j’ai enfin l’impression qu’on atteint quelque chose de vraiment utilisable à la fois en vitesse et en intelligence
    • En pratique, il faudrait tester avec de vrais prompts utilisateur arbitraires, plus un system prompt suffisamment conséquent. Au minimum 1000 tokens, et idéalement plutôt autour de 3000 tokens
      llama.cpp a un outil pour cela, et pour mesurer correctement il faut inclure un prefill avant la génération de tokens. Il devient aussi de plus en plus important de mesurer la vitesse de génération sur des contextes longs comme 32k ou 64k
    • Avec 128 tokens, on ne benchmarke pas l’opéra, juste l’ouverture
    • C’est un peu comme dire « ça marche sur ma machine » sans regarder le vrai problème. 128 tokens, ce n’est vraiment rien, à peine plus long qu’une courte réponse de salutation
  • J’avais écrit un billet similaire il y a quelque temps en utilisant ollama et opencode : https://blog.kulman.sk/running-local-llm-coding-server/

    • Ollama n’est pas un bon choix : https://sleepingrobots.com/dreams/stop-using-ollama/
      Est-ce qu’opencode ne consomme pas trop de contexte avec son system prompt ? Les modèles locaux sont très limités en contexte, et de mémoire opencode en utilise environ 10k, ou quelque chose de cet ordre
    • C’est réellement utile, et avec l’interface graphique d’ollama on pourrait probablement encore simplifier les choses
  • Si on n’utilise que llama.cpp, il ne semble pas nécessaire d’avoir huggingface-cli juste pour télécharger quelque chose. Passer -hf ... permet de télécharger le modèle
    Pour changer l’emplacement de téléchargement, il suffit de définir LLAMA_CACHE :
    LLAMA_CACHE="models" ./llama-server \
    -hf unsloth/gemma-4-31B-it-GGUF:UD-Q4_K_XL \
    ...

    • Pour le modèle de draft, il suffit d’utiliser -hfd
  • Si la RAM en mémoire unifiée est grande mais que les téraflops et la bande passante en Go/s sont moyens ou faibles, alors en général MoE est ce qu’il y a de plus prometteur. Sur ma config, un M2 Max 96GB, le numéro 1 actuel selon (intelligence, tok/s, profondeur de contexte) est DeepSeek-V4-Flash REAP25 <65gb gguf + ds4-server + pi agent
    Bien sûr, ce n’est pas meilleur qu’une API cloud, mais c’est suffisamment acceptable pour supporter ce compromis si nécessaire. Même pendant un vol de 4 heures sans Internet, le LLM local tirait 60W et la batterie a largement tenu
    La branche ds4 qui prend en charge REAP est ici : https://github.com/ljubomirj/ds4/tree/reap-compact-support
    Le fait que DS4F ne tombe à un niveau inutilisable de moins de 10 tok/s qu’à 784K de contexte fait une énorme différence

  • Je me demande si ce type de modèle local peut vraiment résoudre des problèmes même pour des utilisateurs qui ne sont pas experts dans un langage de programmation donné
    Au-delà de l’autocomplétion inline ou de l’implémentation d’une unité, je ne suis pas certain qu’il puisse réellement concevoir et assembler des spécifications techniques qui fonctionnent

  • Utiliser llama.cpp/server pour lancer un LLM local et l’employer avec Claude Code ou Codex-CLI est relativement simple
    Comme les réglages nécessaires de llama server sont souvent dispersés un peu partout, je maintiens ici des consignes pour quelques LLM open populaires : https://pchalasani.github.io/claude-code-tools/integrations/...

    • Tu l’utilises au quotidien ? Les prompts de Claude Code sont énormes, donc sur des modèles locaux le traitement du prompt prend énormément de temps, et on finit aussi très vite par épuiser tout le contexte
  • J’ai utilisé omlx.ai avec pas mal de succès pour télécharger différents modèles MLX adaptés à mon matériel, puis exécuter automatiquement des harness open source et fermés (Claude Code, Codex) avec ces modèles
    Ça fonctionne aussi bien dans l’interface web que desktop, donc personnellement je trouve qu’avec omlx il n’est pas nécessaire de suivre un billet de blog

    • Sur un M1 Max 64GB, je n’ai pas vu d’avantage particulier de oMLX ou MLX par rapport au GGUF de llama.cpp
      Les builds Gemma 4 MLX que j’ai trouvés jusqu’ici étaient plus lents à quantification égale, et bien plus lents avec MTP
      Une fois le modèle choisi, l’interface web intégrée de llama.cpp est plutôt bonne, et pour bricoler un peu LM Studio est aussi correct
      Gemma-4 et Qwen 3.6 n’ont absolument pas besoin des gros blocs habituels de system prompt d’opencode, et c’est même mieux sans
    • Si tu cherches une sandbox à brancher sur oMLX et Pi, il y a ceci : https://github.com/Dotnaught/pi-sandbox
    • À mes yeux, c’est l’état de l’art pour l’inférence locale sur Mac. Même quand il y a des régressions, les développeurs réagissent extrêmement vite, et c’est l’un des projets open source les plus impressionnants que j’aie vus récemment
  • DeepSeek v4 Flash exécuté avec le ds4 d’antirez était assez impressionnant
    En termes de « connaissance stockée », il donne l’impression d’un modèle de niveau GPT-4, mais il gère mieux les appels d’outils sur de longues séquences que les modèles de niveau GPT-4
    Sur un MBP M4 Max 128GB, on obtient environ 24 t/s en génération et environ 200 t/s en prefill. Je m’attendais à ce que ce soit lent, et pour des tâches comme la génération de code ça l’est effectivement, mais comme orchestrateur de machines pour des tâches simples c’est étonnamment utile
    Pour des usages non agentiques, c’est un modèle tout à fait correct pour discuter, avec en plus l’avantage d’être totalement autonome et privé
    [0]https://github.com/antirez/ds4

  • Si vous voulez faire ça avec un minimum d’effort, il suffit d’ouvrir Claude Code dans le terminal, de lui montrer cet article, puis de lui dire simplement « fais-le »

    • Je n’utilise presque plus Google Search. Neuf fois sur dix, la qualité de l’information est médiocre et il est difficile d’extraire ce qu’il faut au milieu du spam ambiant
      À l’inverse, Claude le fait d’un coup, ou avec très peu de retouches
      La porte d’entrée vers la connaissance et l’exécution, c’est désormais le LLM, et Google Search donne l’impression d’un dinosaure
      C’est encore plus bluffant que le smartphone ; on a presque l’impression de vivre un siècle dans le futur