IA de codage pour bot Discord : en créer un avec une API LLM
Un bot Discord IA de codage répond aux questions de programmation dans votre serveur, en utilisant un modèle de langage via une API. Ce tutoriel en crée un en environ 70 lignes de Python et couvre les parties qui posent souvent problème.
Mis à jour le

Points clés
- Le bot a besoin de discord.py, du package openai, d'un token de bot et d'une clé API LLM.
- Activez l'intent Message Content dans le portail développeur Discord ou le bot verra des messages vides.
- Conservez un historique court par canal et réduisez-le, car le modèle ne se souvient de rien entre les appels.
- Découpez les réponses à 2 000 caractères et limitez les appels parallèles pour rester sous le plafond de 8 requêtes simultanées.
Ce que le bot va faire
L'objectif est un bot Discord de codage IA qui réagit lorsqu'on le mentionne. Un utilisateur écrit "@helper pourquoi ma compréhension de liste renvoie-t-elle None ?", le bot envoie le message au modèle, et la réponse du modèle revient en tant que réponse dans le même canal. Le bot se souvient également des derniers tours de chaque canal, afin que les questions de suivi fonctionnent.
La conception comporte trois éléments. discord.py gère la connexion à Discord. Le SDK Python OpenAI communique avec l'API LLM, ce qui fonctionne ici car l'API est compatible OpenAI. Un petit dictionnaire conserve l'historique des conversations. Vous n'avez pas besoin de base de données pour commencer.
Vous aurez besoin de Python 3.9 ou plus récent, d'un compte Discord capable de créer une application, et d'une clé API depuis la page clé API. Les nouveaux comptes peuvent réclamer 0,50 $ de crédit d'essai en un clic, ce qui suffit pour construire et tester ce bot.
Étape 1 : créer l'application Discord
- Ouvrez le portail développeur Discord et créez une nouvelle application.
- Dans la section Bot, créez l'utilisateur bot et copiez son token. Traitez-le comme un mot de passe.
- Sur la même page, activez l'Intent Message Content sous les Intentions Gateway Privilégiées. Sans cela, le bot reçoit des messages avec du texte vide, et vous passerez une heure à vous demander pourquoi il ne répond jamais.
- Dans OAuth2, générez un lien d'invitation avec la portée
botet les permissions pour lire et envoyer des messages. Ouvrez le lien et ajoutez le bot à un serveur de test.
Installez les bibliothèques et définissez deux variables d'environnement.
pip install discord.py openai
export DISCORD_TOKEN="your-discord-bot-token"
export LLM_API_KEY="your-llm-api-key"
# quick check that the LLM key works, using model uncensored
curl -s https://api.codingllmapi.com/v1/chat/completions \
-H "Authorization: Bearer $LLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"uncensored","messages":[{"role":"user","content":"ping"}],"max_tokens":5}'Étape 2 : le code du bot
Enregistrez ceci sous bot.py. Il répond lorsqu'il est mentionné, conserve douze messages d'historique par canal, affiche l'indicateur de frappe en attendant, et découpe les longues réponses.
import asyncio
import os
import discord
from openai import AsyncOpenAI, RateLimitError, APIError
llm = AsyncOpenAI(
base_url="https://api.codingllmapi.com/v1",
api_key=os.environ["LLM_API_KEY"],
)
SYSTEM = (
"You are a helpful programming assistant in a Discord server. "
"Answer briefly. Put code in fenced blocks. Say so when you are unsure."
)
MAX_TURNS = 12
history = {} # channel id -> list of messages
slots = asyncio.Semaphore(6) # stay under the 8 concurrent request cap
intents = discord.Intents.default()
intents.message_content = True
bot = discord.Client(intents=intents)
def split_reply(text, limit=1900):
parts = []
while len(text) > limit:
cut = text.rfind("\n", 0, limit)
if cut < limit // 2:
cut = limit
parts.append(text[:cut])
text = text[cut:].lstrip("\n")
parts.append(text)
return parts
@bot.event
async def on_message(message):
if message.author.bot or bot.user not in message.mentions:
return
question = message.clean_content.replace(f"@{bot.user.display_name}", "").strip()
if not question:
await message.reply("Ask me a coding question after the mention.")
return
log = history.setdefault(message.channel.id, [])
log.append({"role": "user", "content": question})
del log[:-MAX_TURNS]
async with message.channel.typing():
try:
async with slots:
resp = await llm.chat.completions.create(
model="uncensored",
messages=[{"role": "system", "content": SYSTEM}] + log,
temperature=0.4,
max_tokens=900,
)
except RateLimitError:
await message.reply("The model is busy. Try again in a few seconds.")
log.pop()
return
except APIError:
await message.reply("The model call failed. Try again shortly.")
log.pop()
return
answer = resp.choices[0].message.content or "(empty reply)"
log.append({"role": "assistant", "content": answer})
for part in split_reply(answer):
await message.reply(part)
bot.run(os.environ["DISCORD_TOKEN"])
Exécutez-le avec python bot.py, puis mentionnez le bot dans votre serveur de test. La première réponse devrait arriver en quelques secondes.
Pourquoi le code est écrit ainsi
L'historique est réduit intentionnellement
Le modèle n'a pas de mémoire, donc chaque appel envoie les tours récents. Sans del log[:-MAX_TURNS], la liste grandirait indéfiniment. Elle finirait par atteindre la fenêtre de 100 000 tokens et vos coûts augmenteraient à chaque message. Douze entrées suffisent pour une conversation sur un bug. Pour un serveur avec de longs fils de discussion, augmentez-le ou résumez les tours plus anciens.
La limite de 2 000 caractères de Discord
Discord rejette les messages de plus de 2 000 caractères, et les réponses de code atteignent cette limite rapidement. L'utilitaire split_reply coupe à une nouvelle ligne près de la limite. Il ne connaît pas les blocs de code, donc un bloc peut être coupé en deux. Si cela vous dérange, ajoutez une logique qui ferme un bloc à la fin d'une partie et le rouvre au début de la suivante.
Un sémaphore pour la concurrence
Une clé complète permet 8 requêtes simultanées, et une clé d'essai en permet 2. Un serveur occupé peut générer plusieurs questions à la fois. Le sémaphore met en file d'attente les appels supplémentaires dans votre processus au lieu de les envoyer et de recevoir un 429 avec le code d'erreur concurrency. Si vous êtes encore sur la clé d'essai, changez Semaphore(6) en Semaphore(2).
Les erreurs ne sont pas facturées
Les requêtes échouées ou refusées ne sont pas facturées, donc une nouvelle tentative après une erreur ne double pas le coût. Le code retire le tour de l'utilisateur de l'historique en cas d'échec, afin qu'une nouvelle tentative ne laisse pas une question pendante que le modèle a déjà vue deux fois.
Améliorer le ressenti des réponses
Une fois la boucle de base fonctionnelle, quelques petits changements l'améliorent considérablement.
- Diffusez la réponse. Passez
stream=Trueet modifiez le message du bot toutes les secondes environ avec le texte généré jusque-là. Ne modifiez pas à chaque token, car Discord applique ses propres limites de débit aux modifications de message. Une requête en streaming occupe sa place de concurrence jusqu'à sa fin, donc gardez le sémaphore autour de tout le streaming. - Utilisez des commandes slash. Une commande comme
/askévite l'analyse des mentions et n'a pas besoin de l'intent Message Content du tout, ce qui facilite également la vérification ultérieure du bot s'il dépasse un petit nombre de serveurs. - Historique par utilisateur. Indexez l'historique par canal et utilisateur si plusieurs personnes posent des questions non liées dans un même canal.
- Ajustez le prompt système. Nommez les langues utilisées par votre communauté, demandez des réponses courtes, et indiquez au modèle de poser une question de clarification lorsque le problème est vague. Les modifications de prompt sont gratuites et battent souvent les modifications de modèle.
- Baissez la température. Pour le code, des valeurs entre 0,2 et 0,4 donnent des résultats plus stables que la valeur par défaut.
Dépannage des échecs habituels
Lorsqu'un nouveau bot reste silencieux, la cause est presque toujours l'un des éléments ci-dessous. Vérifiez-les dans cet ordre.
- Le bot est en ligne mais ne répond jamais. L'intent Message Content est désactivé dans le portail développeur, ou
intents.message_content = Truemanque dans le code. Les deux doivent être définis. - Il répond uniquement dans certains canaux. Le bot n'a pas la permission de lire ou d'envoyer des messages dans les autres. Vérifiez les autorisations de canal, pas seulement le rôle à l'échelle du serveur.
- Chaque réponse est un message "occupé". Vous atteignez probablement un 429. Imprimez le corps de l'exception une fois. Si le code indique
rate_limited, vous avez dépassé le plafond par minute de 300 requêtes sur une clé complète ou 60 sur une clé d'essai. S'il indiqueconcurrency, réduisez la taille du sémaphore. - Une erreur d'authentification apparaît au premier appel. La clé LLM manque dans l'environnement du processus qui exécute le bot. Une clé exportée dans un onglet de terminal n'est pas visible pour un service démarré ailleurs.
- La réponse est coupée. Le
max_tokens=900dans l'exemple arrête les longues réponses. Augmentez-le, ou supprimez-le, carmax_tokensest facultatif et la complétion peut utiliser le reste de la fenêtre. - Le bot répond à ses propres messages. Cette boucle se produit si vous supprimez la vérification
message.author.bot. Conservez-la.
Pour le débogage, journalisez la valeur X-Request-Id pour chaque appel. Avec le SDK OpenAI, vous pouvez la lire depuis la réponse brute en appelant llm.chat.completions.with_raw_response.create(...) puis en lisant .headers. Si vous contactez le support pour un échec spécifique, cet ID leur permet de trouver la requête exacte.
Ce que cela coûte à exécuter
Le tarif est de 0,15 $ par 1 M de tokens d'entrée et de 0,50 $ par 1 M de tokens de sortie, sans abonnement. Prenons un échange typique : le prompt système plus douze entrées d'historique représentent environ 2 500 tokens d'entrée, et la réponse fait environ 400 tokens.
Entrée : 2 500 x 0,15 $ / 1 000 000 = 0,000 375 $. Sortie : 400 x 0,50 $ / 1 000 000 = 0,000 2 $. Une réponse coûte environ 0,000 575 $. Une communauté qui pose 500 questions par jour dépenserait 500 x 0,000 575 $ = 0,2875 $, soit un peu moins de trente centimes. Vos chiffres varieront avec des copies de code plus longues, alors journalisez resp.usage pendant une semaine et lisez les moyennes réelles. La page tarification affiche les options de rechargement : tout montant entier de 20 $ à 500 $, avec +5 % à partir de 50 $, +10 % à partir de 100 $ et +15 % à partir de 200 $, payés par carte ou crypto.
Pour limiter les abus, ajoutez un délai d'attente (cooldown) par utilisateur et une limite sur la longueur des questions. Ces deux vérifications protègent votre solde mieux que tout autre ajout.
Notes sur l'hébergement et la sécurité
Le bot est un processus à longue durée de vie. Une petite machine virtuelle ou un hôte de conteneur suffit, car le travail lourd se fait côté API. Exécutez-le sous un gestionnaire de processus pour qu'il redémarre après un crash, et conservez les deux tokens dans des variables d'environnement ou un gestionnaire de secrets, jamais dans le dépôt.
Pensez à ce que votre serveur autorise. Le modèle est ajusté pour répondre sans refus de contenu pour une utilisation adulte légale, il discutera donc de tests de sécurité ou de fiction osée qu'un assistant plus strict refuserait. Si votre communauté comprend des membres plus jeunes, définissez des règles dans le prompt système, restreignez le bot à des canaux spécifiques et rappelez-vous que les propres conditions d'utilisation de Discord s'appliquent aussi à votre bot. Le contenu sexualisant les mineurs est interdit sur cette API sans exception.
Les détails complets des paramètres, tels que stop, seed et l'appel de fonctions, se trouvent dans la documentation de l'API. Si vous voulez que le bot exécute du code ou lise des dépôts, l'appel de fonctions est la voie à suivre, et vous décidez quelles fonctions le modèle peut demander. Pour une utilisation de ce modèle axée sur la programmation, consultez la page sur le modèle d'IA sans censure pour le codage.
Plus de guides
Questions et réponses
Ai-je besoin de l'intent Message Content ?
Oui, si le bot lit les messages normaux et les mentions. Si vous n'utilisez que les commandes slash, vous n'en avez pas besoin.
Le bot peut-il diffuser des réponses ?
Oui. Définissez stream=True et modifiez le message Discord à un intervalle calme, comme une fois par seconde, afin de rester dans les limites propres à Discord.
Que se passe-t-il si trop de personnes posent des questions en même temps ?
Au-delà de 8 requêtes simultanées avec une clé pleine, l'API renvoie HTTP 429 avec le code concurrency. Un sémaphore dans votre code met en file d'attente les questions supplémentaires.
Le bot se souviendra-t-il des conversations après un redémarrage ?
Non avec le dictionnaire utilisé ici. Stockez l'historique dans SQLite ou Redis si vous avez besoin qu'il survive aux redémarrages.
La clé API est-elle sûre dans un bot Discord ?
Oui si elle reste sur votre serveur dans une variable d'environnement. Ne la publiez jamais dans un canal ni ne la validez dans un dépôt public.
Votre clé n'est qu'à un formulaire d'ici
Créez un compte, copiez la clé, modifiez l'URL de base. C'est toute la configuration.
Obtenir une clé API