ID ▾
Dapatkan kunci API

Bot Discord AI Pemrograman: Buat dengan API LLM

Bot Discord coding AI menjawab pertanyaan pemrograman di server Anda, menggunakan model bahasa di balik API. Tutorial ini membuat satu dalam sekitar 70 baris Python dan mencakup bagian yang biasanya rusak.

Diperbarui

Gambar bot obrolan abstrak dikelilingi gelembung obrolan bercahaya, mengilustrasikan bot Discord yang didukung API LLM

Poin utama

  • Bot memerlukan discord.py, paket openai, token bot, dan kunci API LLM.
  • Aktifkan intent Konten Pesan di portal pengembang Discord atau bot akan melihat pesan kosong.
  • Simpan riwayat singkat per saluran dan pangkas, karena model tidak mengingat apa pun di antara panggilan.
  • Bagi balasan pada 2.000 karakter dan batasi panggilan paralel agar Anda tetap di bawah batas konkurensi 8 permintaan.

Apa yang akan dilakukan bot

Tujuannya adalah membuat bot Discord AI coding yang merespons saat seseorang menyebutnya. Pengguna menulis "@helper mengapa list comprehension saya mengembalikan None?", bot mengirim pesan tersebut ke model, dan jawaban model kembali sebagai balasan di saluran yang sama. Bot juga mengingat beberapa putaran terakhir dari setiap saluran, sehingga pertanyaan lanjutan dapat berfungsi.

Desainnya memiliki tiga bagian. discord.py menangani koneksi ke Discord. SDK Python OpenAI berbicara dengan API LLM, yang berfungsi di sini karena API kompatibel dengan OpenAI. Kamus kecil menyimpan riwayat percakapan. Anda tidak memerlukan database untuk memulai.

Anda memerlukan Python 3.9 atau lebih baru, akun Discord yang dapat membuat aplikasi, dan kunci API dari halaman kunci API. Akun baru dapat mengklaim $0,50 kredit uji coba dengan satu klik, yang cukup untuk membuat dan menguji bot ini.

Langkah 1: buat aplikasi Discord

  1. Buka portal pengembang Discord dan buat aplikasi baru.
  2. Di bagian Bot, buat pengguna bot dan salin token-nya. Perlakukan seperti kata sandi.
  3. Di halaman yang sama, aktifkan Message Content Intent di bawah Privileged Gateway Intents. Tanpanya, bot menerima pesan dengan teks kosong, dan Anda akan menghabiskan satu jam bertanya-tanya mengapa bot tidak pernah menjawab.
  4. Dalam OAuth2, buat tautan undangan dengan ruang lingkup bot dan izin untuk membaca pesan serta mengirim pesan. Buka tautan tersebut dan tambahkan bot ke server uji coba.

Instal pustaka dan atur dua variabel lingkungan.

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

Langkah 2: kode bot

Simpan ini sebagai bot.py. Bot ini merespons saat disebut, menyimpan riwayat dua belas pesan per saluran, menampilkan indikator pengetikan saat menunggu, dan membagi jawaban panjang.

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

Jalankan dengan python bot.py, lalu sebutkan bot di server uji coba Anda. Jawaban pertama harus tiba dalam beberapa detik.

Mengapa kode ditulis seperti ini

Riwayat dipangkas dengan sengaja

Model tidak memiliki memori, sehingga setiap panggilan mengirimkan putaran terbaru. Tanpa del log[:-MAX_TURNS], daftar akan tumbuh selamanya. Pada akhirnya, daftar akan mencapai jendela 100.000 token dan biaya Anda akan meningkat seiring setiap pesan. Dua belas entri sudah cukup untuk percakapan tentang satu bug. Untuk server dengan thread panjang, tingkatkan nilai ini atau ringkas putaran yang lebih lama.

Batas 2.000 karakter Discord

Discord menolak pesan yang lebih panjang dari 2.000 karakter, dan jawaban kode mencapai batas itu dengan cepat. Fungsi bantu split_reply memotong pada baris baru di dekat batas. Fungsi ini tidak mengetahui pagar kode, sehingga blok dapat terpotong di tengah. Jika hal itu mengganggu Anda, tambahkan logika yang menutup pagar di akhir satu bagian dan membukanya kembali di awal bagian berikutnya.

Semaphore untuk konkurensi

Kunci penuh mengizinkan 8 permintaan paralel, dan kunci uji coba mengizinkan 2. Server yang sibuk dapat menghasilkan beberapa pertanyaan sekaligus. Semaphore mengantrikan panggilan tambahan di dalam proses Anda, bukan mengirimkannya dan menerima 429 dengan kode kesalahan concurrency. Jika Anda masih menggunakan kunci uji coba, ubah Semaphore(6) menjadi Semaphore(2).

Kesalahan tidak dibebankan

Permintaan yang gagal atau ditolak tidak ditagih, sehingga percobaan ulang setelah kesalahan tidak menggandakan biaya. Kode menghapus giliran pengguna dari riwayat saat gagal, sehingga percobaan ulang tidak meninggalkan pertanyaan menggantung yang telah dilihat model dua kali.

Membuat jawaban terasa lebih baik

Setelah loop dasar berfungsi, beberapa perubahan kecil meningkatkannya secara signifikan.

  • Stream jawaban. Berikan stream=True dan edit pesan bot setiap detik atau lebih dengan teks sejauh ini. Jangan mengedit setiap token, karena Discord menerapkan batas laju sendiri untuk pengeditan pesan. Permintaan streaming memegang slot konkurensinya hingga berakhir, jadi pertahankan semaphore di sekitar seluruh stream.
  • Gunakan perintah slash. Perintah seperti /ask menghindari parsing sebutan dan sama sekali tidak memerlukan Message Content intent, yang juga membuat bot lebih mudah diverifikasi nanti jika berkembang melampaui sejumlah kecil server.
  • Riwayat per pengguna. Kelompokkan riwayat berdasarkan saluran dan pengguna jika beberapa orang mengajukan pertanyaan yang tidak terkait di satu saluran.
  • Sesuaikan prompt sistem. Sebutkan bahasa yang digunakan komunitas Anda, minta jawaban singkat, dan beri tahu model untuk mengajukan pertanyaan klarifikasi ketika masalahnya samar. Perubahan prompt gratis dan sering kali lebih baik daripada perubahan model.
  • Turunkan suhu. Untuk kode, nilai antara 0,2 dan 0,4 memberikan hasil yang lebih stabil dibandingkan nilai default.

Pemecahan masalah kegagalan umum

Ketika bot baru tetap diam, penyebabnya hampir selalu salah satu dari item di bawah ini. Periksa mereka dengan urutan ini.

  1. Bot online tetapi tidak pernah membalas. Message Content Intent dinonaktifkan di portal pengembang, atau intents.message_content = True hilang dari kode. Keduanya harus diatur.
  2. Bot membalas hanya di beberapa saluran. Bot tidak memiliki izin untuk membaca atau mengirim pesan di saluran lainnya. Periksa penimpaan izin (permission override) saluran, bukan hanya peran di seluruh server.
  3. Setiap balasan adalah pesan "sibuk". Kemungkinan besar Anda mencapai batas 429. Cetak isi (body) exception sekali. Jika kode menunjukkan rate_limited, Anda telah melewati batas per menit 300 permintaan pada kunci penuh atau 60 pada kunci uji coba. Jika menunjukkan concurrency, kurangi ukuran semaphore.
  4. Kesalahan autentikasi muncul pada panggilan pertama. Kunci LLM hilang dari lingkungan proses yang menjalankan bot. Kunci yang diekspor di satu tab terminal tidak terlihat oleh layanan yang dimulai di tempat lain.
  5. Jawaban terpotong. max_tokens=900 pada sampel menghentikan balasan panjang. Tingkatkan nilai ini, atau hapus, karena max_tokens bersifat opsional dan penyelesaian dapat menggunakan sisa jendela.
  6. Bot membalas pesan sendiri. Loop itu terjadi jika Anda menghapus pemeriksaan message.author.bot. Pertahankan pemeriksaan tersebut.

Untuk debugging, log nilai X-Request-Id untuk setiap panggilan. Dengan SDK OpenAI, Anda dapat membacanya dari respons mentah dengan memanggil llm.chat.completions.with_raw_response.create(...) dan kemudian membaca .headers. Jika Anda menghubungi dukungan tentang kegagalan tertentu, ID tersebut memungkinkan mereka menemukan permintaan yang tepat.

Biaya untuk menjalankannya

Harga adalah $0,15 per 1 juta token input dan $0,50 per 1 juta token output, tanpa langganan. Ambil contoh pertukaran umum: prompt sistem ditambah dua belas entri riwayat menghasilkan sekitar 2.500 token input, dan jawabannya sekitar 400 token.

Input: 2,500 x $0.15 / 1,000,000 = $0.000375. Output: 400 x $0.50 / 1,000,000 = $0.0002. Satu jawaban berharga sekitar $0.000575. Komunitas yang mengajukan 500 pertanyaan sehari akan menghabiskan 500 x $0.000575 = $0.2875, sedikit di bawah tiga puluh sen. Angka Anda akan berbeda dengan paste kode yang lebih panjang, jadi log resp.usage selama seminggu dan baca rata-rata sebenarnya. Halaman harga menampilkan opsi isi saldo: jumlah bulat dari $20 hingga $500, dengan +5% dari $50, +10% dari $100 dan +15% dari $200, dibayar dengan kartu atau kripto.

Untuk membatasi penyalahgunaan, tambahkan waktu tunggu per pengguna dan batas panjang pertanyaan. Dua pemeriksaan itu melindungi saldo Anda lebih baik daripada apa pun yang dapat Anda tambahkan.

Catatan hosting dan keamanan

Bot ini adalah proses yang berjalan lama. Mesin virtual kecil atau host container sudah cukup, karena pekerjaan berat terjadi di sisi API. Jalankan di bawah manajer proses agar restart setelah crash, dan simpan kedua token dalam variabel lingkungan atau tempat penyimpanan rahasia, jangan di repositori.

Pikirkan apa yang diizinkan oleh server Anda. Model ini disesuaikan untuk menjawab tanpa penolakan konten untuk penggunaan dewasa yang sah, sehingga akan membahas pengujian keamanan atau fiksi yang kontroversial yang mungkin ditolak oleh asisten yang lebih ketat. Jika komunitas Anda termasuk anggota yang lebih muda, atur aturan dalam prompt sistem, batasi bot ke saluran tertentu, dan ingat bahwa ketentuan Discord sendiri berlaku untuk bot Anda juga. Konten seksual yang melibatkan anak di bawah umur dilarang di API ini tanpa pengecualian.

Detail parameter lengkap, seperti stop, seed, dan pemanggilan fungsi, terdapat di dokumentasi API. Jika Anda ingin bot menjalankan kode atau membaca repositori, pemanggilan fungsi adalah jalurnya, dan Anda memutuskan fungsi mana yang boleh diminta oleh model. Untuk penggunaan model ini yang berfokus pada pemrograman, lihat halaman tentang model AI tanpa sensor untuk coding.

Tanya jawab

Apakah saya memerlukan Message Content intent?

Ya, jika bot membaca pesan normal dan sebutan. Jika Anda hanya menggunakan perintah slash, Anda tidak memerlukannya.

Dapatkah bot melakukan streaming balasan?

Ya. Atur stream=True dan edit pesan Discord pada interval yang tenang, seperti sekali per detik, agar Anda tetap berada dalam batas Discord sendiri.

Apa yang terjadi jika terlalu banyak orang bertanya sekaligus?

Melebihi 8 permintaan paralel pada kunci penuh, API mengembalikan HTTP 429 dengan kode concurrency. Semaphore dalam kode Anda mengantri pertanyaan tambahan.

Apakah bot akan mengingat percakapan setelah restart?

Tidak dengan kamus yang digunakan di sini. Simpan riwayat di SQLite atau Redis jika Anda membutuhkannya agar bertahan setelah restart.

Apakah kunci API aman di bot Discord?

Ya jika tetap berada di server Anda dalam variabel lingkungan. Jangan pernah mempostingnya di saluran atau mengkomitnya ke repositori publik.

Kunci Anda hanya satu formulir lagi

Buat akun, salin kunci, ubah URL dasar. Itu saja seluruh proses pengaturannya.

Dapatkan kunci API