IT ▾
Ottieni la chiave API

Bot di coding Discord AI: crealo con un'API LLM

Un bot Discord AI per il coding risponde alle domande di programmazione nel tuo server, usando un modello linguistico dietro un'API. Questo tutorial ne crea uno in circa 70 righe di Python e copre le parti che di solito si rompono.

Aggiornato

Figura astratta di un bot chat circondata da bolle di dialogo luminose, illustra un bot Discord alimentato da un'API LLM

Punti chiave

  • Il bot ha bisogno di discord.py, del pacchetto openai, di un token bot e di una chiave API LLM.
  • Abilita l'intent Message Content nel portale sviluppatori Discord o il bot vedrà messaggi vuoti.
  • Mantieni una cronologia breve per canale e tagliala, poiché il modello non ricorda nulla tra una chiamata e l'altra.
  • Dividi le risposte a 2.000 caratteri e limita le chiamate parallele per restare sotto il limite di concorrenza di 8 richieste.

Cosa farà il bot

L'obiettivo è creare un bot di coding per Discord che risponda quando qualcuno lo menziona. Un utente scrive "@helper perché la mia list comprehension restituisce None?", il bot invia il messaggio al modello e la risposta del modello torna come reply nello stesso canale. Il bot ricorda anche gli ultimi scambi di ogni canale, così le domande di follow-up funzionano.

Il design ha tre parti. discord.py gestisce la connessione a Discord. L'SDK Python OpenAI parla con l'API LLM, che funziona qui perché l'API è compatibile con OpenAI. Un piccolo dizionario tiene la cronologia delle conversazioni. Non ti serve un database per iniziare.

Ti serviranno Python 3.9 o superiore, un account Discord che può creare un'applicazione e una chiave API dalla pagina delle chiavi API. I nuovi account possono richiedere $0,50 di credito di prova con un clic, sufficiente per costruire e testare questo bot.

Fase 1: crea l'applicazione Discord

  1. Apri il portale sviluppatori Discord e crea una nuova applicazione.
  2. Nella sezione Bot, crea l'utente bot e copia il suo token. Trattalo come una password.
  3. Nella stessa pagina, attiva Message Content Intent sotto le Intents di Gateway privilegiate. Senza di essa, il bot riceve i messaggi con testo vuoto e perderai un'ora a chiederti perché non risponde mai.
  4. In OAuth2, genera un link di invito con l'ambito bot e i permessi per leggere e inviare messaggi. Apri il link e aggiungi il bot a un server di test.

Installa le librerie e imposta due variabili d'ambiente.

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}'

Fase 2: il codice del bot

Salva questo come bot.py. Risponde quando viene menzionato, mantiene dodici messaggi di cronologia per canale, mostra l'indicatore di digitazione mentre aspetta e divide le risposte lunghe.

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"])

Eseguilo con python bot.py, poi menziona il bot nel tuo server di test. La prima risposta dovrebbe arrivare entro pochi secondi.

Perché il codice è scritto così

La cronologia viene tagliata appositamente

Il modello non ha memoria, quindi ogni chiamata invia gli scambi recenti. Senza del log[:-MAX_TURNS] la lista crescerebbe all'infinito. Alla fine raggiungerebbe la finestra da 100.000 token e i tuoi costi salirebbero con ogni messaggio. Dodici voci sono sufficienti per una conversazione su un bug. Per un server con thread lunghi, aumentalo o riassumi gli scambi più vecchi.

Il limite di 2.000 caratteri di Discord

Discord rifiuta i messaggi più lunghi di 2.000 caratteri e le risposte in codice lo raggiungono rapidamente. La funzione split_reply taglia a un a capo vicino al limite. Non conosce i blocchi di codice, quindi un blocco può essere tagliato a metà. Se questo ti dà fastidio, aggiungi una logica che chiuda un blocco alla fine di una parte e lo riapra all'inizio della successiva.

Un semaforo per la concorrenza

Una chiave completa permette 8 richieste parallele, una chiave di prova ne permette 2. Un server occupato può generare diverse domande insieme. Il semaforo accoda le chiamate extra nel tuo processo invece di inviarle e ricevere un 429 con il codice di errore concurrency. Se sei ancora sulla chiave di prova, cambia Semaphore(6) in Semaphore(2).

Gli errori non sono fatturati

Le richieste fallite o rifiutate non vengono fatturate, quindi un retry dopo un errore non raddoppia il costo. Il codice rimuove il turno dell'utente dalla cronologia in caso di errore, così un retry non lascia una domanda pendente che il modello ha già visto due volte.

Migliorare la sensazione delle risposte

Una volta che il loop di base funziona, alcune piccole modifiche lo migliorano molto.

  • Fai lo streaming della risposta. Passa stream=True e modifica il messaggio del bot ogni secondo circa con il testo generato finora. Non modificare su ogni token, perché Discord applica i suoi limiti di richieste alle modifiche dei messaggi. Una richiesta in streaming mantiene il suo slot di concorrenza fino alla fine, quindi mantieni il semaforo per tutto lo streaming.
  • Usa i comandi slash. Un comando come /ask evita l'analisi delle menzioni e non richiede affatto l'intent Message Content, il che rende anche il bot più facile da verificare in seguito se cresce oltre un piccolo numero di server.
  • Cronologia per utente. Indicizza la cronologia per canale e utente se più persone fanno domande non correlate in un canale.
  • Regola il prompt di sistema. Indica le lingue usate dalla tua community, chiedi risposte brevi e dì al modello di fare una domanda di chiarimento quando il problema è vago. Le modifiche al prompt sono gratuite e spesso battono le modifiche al modello.
  • Abbassa la temperatura. Per il codice, valori tra 0,2 e 0,4 danno risultati più stabili rispetto al default.

Risoluzione dei problemi comuni

Quando un nuovo bot resta silenzioso, la causa è quasi sempre una delle voci seguenti. Controllale in questo ordine.

  1. Il bot è online ma non risponde mai. L'intent Message Content è disattivato nel portale sviluppatori, o manca intents.message_content = True nel codice. Entrambi devono essere impostati.
  2. Risponde solo in alcuni canali. Al bot mancano i permessi per leggere o inviare messaggi negli altri. Controlla le sovrascrizioni dei permessi del canale, non solo il ruolo a livello di server.
  3. Ogni risposta è un messaggio "occupato". Probabilmente stai colpendo un 429. Stampa il corpo dell'eccezione una volta. Se il codice dice rate_limited, hai superato il limite per minuto di 300 richieste su una chiave completa o 60 su una chiave di prova. Se dice concurrency, riduci la dimensione del semaforo.
  4. Appare un errore di autenticazione alla prima chiamata. La chiave LLM manca nell'ambiente del processo che esegue il bot. Una chiave esportata in una scheda del terminale non è visibile a un servizio avviato altrove.
  5. La risposta è tagliata. Il max_tokens=900 nel campione ferma le risposte lunghe. Aumentalo, o rimuovilo, poiché max_tokens è opzionale e il completion può usare il resto della finestra.
  6. Il bot risponde ai propri messaggi. Questo loop accade se salti il controllo message.author.bot. Tienilo.

Per il debug, registra il valore X-Request-Id per ogni chiamata. Con l'SDK OpenAI puoi leggerlo dalla risposta grezza chiamando llm.chat.completions.with_raw_response.create(...) e poi leggendo .headers. Se contatti il supporto per un fallimento specifico, quell'ID permette loro di trovare la richiesta esatta.

Quanto costa l'esecuzione

Il prezzo è di $0,15 per 1 milione di token in input e $0,50 per 1 milione di token in output, senza abbonamento. Prendiamo un tipico scambio: il prompt di sistema più dodici voci di cronologia equivalgono a circa 2.500 token in input, e la risposta è di circa 400 token.

Input: 2.500 × $0,15 / 1.000.000 = $0,000375. Output: 400 × $0,50 / 1.000.000 = $0,0002. Una risposta costa circa $0,000575. Una community che pone 500 domande al giorno spenderebbe 500 × $0,000575 = $0,2875, poco meno di trenta centesimi. I tuoi numeri varieranno con incollaggi di codice più lunghi, quindi registra resp.usage per una settimana e leggi le medie reali. La pagina prezzi mostra le opzioni di ricarica: qualsiasi importo intero da $20 a $500, con +5% da $50, +10% da $100 e +15% da $200, pagabili con carta o cripto.

Per limitare gli abusi, aggiungi un tempo di attesa per utente e un limite alla lunghezza della domanda. Questi due controlli proteggono il tuo saldo meglio di qualsiasi altra cosa tu possa aggiungere.

Note sull'hosting e sulla sicurezza

Il bot è un processo a lunga esecuzione. Una piccola macchina virtuale o un host di container è sufficiente, poiché il lavoro pesante avviene sul lato API. Esegui il processo sotto un process manager in modo che si riavvii dopo un crash e conserva entrambi i token in variabili d'ambiente o in un archivio segreti, mai nel repository.

Pensa a cosa permette il tuo server. Il modello è ottimizzato per rispondere senza rifiuti sui contenuti per l'uso adulto lecito, quindi discuterà di test di sicurezza o narrativa controversa che un assistente più rigoroso potrebbe rifiutare. Se la tua community include membri più giovani, imposta regole nel prompt di sistema, limita il bot a canali specifici e ricorda che i termini di servizio di Discord si applicano anche al tuo bot. I contenuti che sessualizzano i minorenni sono vietati su questa API senza eccezioni.

I dettagli completi sui parametri, come stop, seed e la chiamata di funzioni, sono nella documentazione API. Se vuoi che il bot esegua codice o legga repository, la chiamata di funzioni è la strada da seguire e tu decidi quali funzioni il modello può richiedere. Per un uso di questo modello focalizzato sulla programmazione, consulta la pagina sul modello AI senza censura per la codifica.

Domande e risposte

Ho bisogno dell'intent Message Content?

Sì, se il bot legge messaggi normali e menzioni. Se usi solo i comandi slash, non ne hai bisogno.

Il bot può trasmettere le risposte in streaming?

Sì. Imposta stream=True e modifica il messaggio Discord a intervalli tranquilli, ad esempio una volta al secondo, in modo da rimanere entro i limiti di Discord.

Cosa succede se troppi utenti chiedono contemporaneamente?

Oltre 8 richieste parallele su una chiave completa, l'API restituisce HTTP 429 con il codice concurrency. Un semaforo nel tuo codice accoda le domande in eccesso.

Il bot ricorderà le conversazioni dopo un riavvio?

No con il dizionario usato qui. Memorizza la cronologia in SQLite o Redis se ti serve che sopravviva ai riavvi.

La chiave API è sicura in un bot Discord?

Sì se rimane sul tuo server in una variabile d'ambiente. Non pubblicarla mai in un canale o commitarla in un repository pubblico.

La tua chiave è a un modulo di distanza

Crea un account, copia la chiave, modifica l'URL di base. È tutta la configurazione.

Ottieni la chiave API