TH ▾
รับคีย์ API

บอท Discord AI เขียนโค้ด: สร้างด้วย API LLM

บอท Discord AI ที่เขียนโค้ดตอบคำถามการเขียนโปรแกรมภายในเซิร์ฟเวอร์ของคุณ โดยใช้โมเดลภาษาผ่าน API การสอนนี้สร้างบอทด้วยโค้ด Python ประมาณ 70 บรรทัด และครอบคลุมส่วนที่มักเกิดปัญหา

อัปเดต

ภาพบอทแชทนามธรรมล้อมรอบด้วยฟองพูดเรืองแสง แสดงถึงบอท Discord ที่ใช้ API LLM

ประเด็นสำคัญ

  • บอทต้องการ discord.py, แพ็กเกจ openai, โทเคนบอท และคีย์ API LLM
  • เปิดใช้งาน Message Content Intentใน Discord developer portal หรือบอทจะเห็นข้อความว่างเปล่า
  • เก็บประวัติสั้นๆ ต่อช่องและตัดทอน เนื่องจากโมเดลไม่จำอะไรระหว่างการเรียก
  • แบ่งคำตอบที่ 2,000 อักขระ และจำกัดการเรียกพร้อมกันเพื่อให้คุณอยู่ภายใต้ขีดจำกัดการทำงานพร้อมกัน 8 คำขอ

บอทจะทำอะไร

เป้าหมายคือ บอท Discord AI ที่เขียนโค้ด ที่ตอบสนองเมื่อมีคนกล่าวถึงบอท เขียน "@helper ทำไม list comprehension ของฉันถึงคืนค่า None?" บอทส่งข้อความไปยังโมเดล และคำตอบของโมเดลจะกลับมาเป็นคำตอบในช่องเดียวกัน บอทยังจำการสนทนาล่าสุดของช่องต่างๆ เพื่อให้คำถามต่อเนื่องทำงานได้

การออกแบบมีสามส่วน discord.py จัดการการเชื่อมต่อกับ Discord OpenAI Python SDK ติดต่อ API LLM ซึ่งทำงานที่นี่เพราะ API เข้ากันได้กับ OpenAI ดิกชันนารีขนาดเล็กเก็บประวัติการสนทนา คุณไม่จำเป็นต้องใช้ฐานข้อมูลเพื่อเริ่มต้น

คุณต้องการ Python 3.9 ขึ้นไป บัญชี Discord ที่สามารถสร้างแอปพลิเคชันได้ และคีย์ API จาก หน้าคีย์ API บัญชีใหม่สามารถรับเครดิตทดลองใช้ $0.50 ได้ด้วยการคลิกหนึ่งครั้ง ซึ่งเพียงพอสำหรับการสร้างและทดสอบบอทนี้

ขั้นตอนที่ 1: สร้างแอป Discord

  1. เปิดพอร์ทัลผู้พัฒนา Discord และสร้างแอปใหม่
  2. ในส่วน Bot ให้สร้างผู้ใช้บอทและคัดลอกโทเคนของมัน ให้ปฏิบัติต่อมันเหมือนรหัสผ่าน
  3. บนหน้าเดียวกัน ให้เปิดใช้งาน Message Content Intent ภายใต้ Privileged Gateway Intents โดยไม่มีมัน บอทจะรับข้อความที่มีข้อความว่างเปล่า และคุณจะใช้เวลาหนึ่งชั่วโมงสงสัยว่าทำไมมันไม่ตอบ
  4. ใน OAuth2 ให้สร้างลิงก์เชิญโดยใช้สโคป bot และสิทธิ์ในการอ่านข้อความและส่งข้อความ เปิดลิงก์แล้วเพิ่มบอทลงในเซิร์ฟเวอร์ทดสอบ

ติดตั้งไลบรารีและตั้งค่าตัวแปรสภาพแวดล้อมสองตัว

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 ไม่เซ็นเซอร์
curl -s https://api.codingllmapi.com/v1/chat/completions \
  -H "Authorization: Bearer $LLM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"ไม่เซ็นเซอร์","messages":[{"role":"user","content":"ping"}],"max_tokens":5}'

ขั้นตอนที่ 2: โค้ดบอท

บันทึกสิ่งนี้เป็น bot.py มันจะตอบเมื่อมีการกล่าวถึง เก็บข้อความสิบสองข้อความของประวัติต่อช่อง แสดงตัวบ่งชี้การพิมพ์ขณะรอ และแบ่งคำตอบที่ยาว

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="ไม่เซ็นเซอร์",
                    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"])

รันด้วย python bot.py จากนั้นเรียกใช้งานบอทในเซิร์ฟเวอร์ทดสอบ คำตอบแรกควรมาถึงภายในไม่กี่วินาที

เหตุผลที่เขียนโค้ดแบบนี้

ประวัติถูกตัดโดยเจตนา

โมเดลไม่มีหน่วยความจำ ดังนั้นแต่ละการเรียกจึงส่งการสนทนาล่าสุด โดยไม่มี del log[:-MAX_TURNS] รายการจะเติบโตตลอดไป ในที่สุดมันจะชนกับหน้าต่าง 100,000 โทเคน และค่าใช้จ่ายของคุณจะเพิ่มขึ้นในทุกข้อความ สิบสองรายการเพียงพอสำหรับการสนทนาเกี่ยวกับข้อบกพร่องหนึ่งรายการ สำหรับเซิร์ฟเวอร์ที่มีสายการสนทนาที่ยาว ให้เพิ่มค่านี้หรือสรุปการสนทนาเก่า

ขีดจำกัด 2,000 ตัวอักษรของ Discord

Discord ปฏิเสธข้อความที่ยาวกว่า 2,000 อักขระ และคำตอบโค้ดชนกับขีดจำกัดนั้นอย่างรวดเร็ว ตัวช่วย split_reply ตัดที่บรรทัดใหม่ใกล้กับขีดจำกัด มันไม่รู้จัก code fences ดังนั้นบล็อกอาจถูกตัดตรงกลาง หากสิ่งนั้นทำให้คุณรำคาญ ให้เพิ่มลอจิกที่ปิด fence ที่ส่วนท้ายของส่วนหนึ่งและเปิดใหม่ที่จุดเริ่มต้นของส่วนถัดไป

เซมิโฟร์สำหรับการทำงานพร้อมกัน

คีย์แบบเต็มอนุญาตคำขอพร้อมกัน 8 คำขอ และคีย์ทดลองอนุญาต 2 คำขอ เซิร์ฟเวอร์ที่วุ่นวายสามารถสร้างคำถามหลายคำถามพร้อมกันได้ เซมิโฟร์จัดคิวการเรียกเพิ่มเติมภายในกระบวนการของคุณแทนที่จะส่งพวกมันและรับ 429 พร้อมรหัสข้อผิดพลาด concurrency หากคุณยังอยู่บนคีย์ทดลอง ให้เปลี่ยน Semaphore(6) เป็น Semaphore(2)

ไม่คิดค่าคำขอที่ล้มเหลว

คำขอที่ล้มเหลวหรือถูกปฏิเสธจะไม่ถูกเรียกเก็บเงิน ดังนั้นการลองใหม่หลังจากข้อผิดพลาดจะไม่เพิ่มค่าใช้จ่ายเป็นสองเท่า โค้ดจะลบข้อความของผู้ใช้ออกจากประวัติเมื่อล้มเหลว ดังนั้นการลองใหม่จะไม่ทิ้งคำถามที่ค้างอยู่ซึ่งโมเดลได้เห็นไปแล้วสองครั้ง

ปรับปรุงคุณภาพคำตอบ

เมื่อวงจรการทำงานพื้นฐานทำงานได้แล้ว การเปลี่ยนแปลงเล็กน้อยไม่กี่จุดจะช่วยปรับปรุงคุณภาพได้มาก

  • สตรีมมิงคำตอบ ส่ง stream=True และแก้ไขข้อความบอททุก ๆ วินาทีหรือประมาณนั้นด้วยข้อความที่มีอยู่ อย่าแก้ไขทุกโทเคน เพราะ Discord ใช้ขีดจำกัดอัตราของตัวเองสำหรับการแก้ไขข้อความ คำขอแบบสตรีมมิงจะครอบครองสล็อตการทำงานพร้อมกันจนกว่าจะจบ ดังนั้นให้ใช้เซมิโฟร์ตลอดการสตรีมมิง
  • ใช้คำสั่งแบบสแลช คำสั่งเช่น /ask หลีกเลี่ยงการแยกวิเคราะห์การกล่าวถึง และไม่ต้องใช้ Message Content Intent เลย ซึ่งทำให้บอทตรวจสอบได้ง่ายขึ้นในภายหลังหากขยายเกินเซิร์ฟเวอร์จำนวนน้อย
  • ประวัติรายผู้ใช้ แยกประวัติตามช่องและผู้ใช้หากมีหลายคนถามคำถามที่ไม่เกี่ยวข้องกันในช่องเดียวกัน
  • ปรับแต่งพรอมต์ระบบ ระบุภาษาที่ชุมชนของคุณใช้ ขอคำตอบสั้น ๆ และบอกให้โมเดลถามคำถามเพื่อชี้แจงเมื่อปัญหาไม่ชัดเจน การเปลี่ยนแปลงพรอมต์ทำได้ฟรีและมักได้ผลดีกว่าการเปลี่ยนโมเดล
  • ลดค่า temperature สำหรับโค้ด ค่าระหว่าง 0.2 ถึง 0.4 ให้ผลลัพธ์ที่เสถียรกว่าค่าเริ่มต้น

การแก้ปัญหาความล้มเหลวทั่วไป

เมื่อบอทใหม่เงียบ สาเหตุมักเกิดจากหนึ่งในรายการด้านล่าง ให้ตรวจสอบตามลำดับนี้

  1. บอทออนไลน์แต่ไม่ตอบเลย สิทธิ์ Message Content ถูกปิดในพอร์ทัลผู้พัฒนา หรือ intents.message_content = True หายไปจากโค้ด ต้องตั้งค่าทั้งสองอย่าง
  2. ตอบเฉพาะในบางช่อง บอทไม่มีสิทธิ์อ่านหรือส่งข้อความในช่องอื่น ๆ ให้ตรวจสอบการกำหนดสิทธิ์ของช่อง ไม่ใช่เพียงบทบาทระดับเซิร์ฟเวอร์
  3. ทุกการตอบกลับคือข้อความ "กำลังยุ่ง" คุณน่าจะเจอ 429 พิมพ์ข้อความข้อยกเว้นหนึ่งครั้ง หากโค้ดคือ rate_limited คุณเกินขีดจำกัดต่อนาทีที่ 300 สำหรับคีย์เต็ม หรือ 60 สำหรับคีย์ทดลอง หากเป็น concurrency ให้ลดขนาดเซมิโฟร์
  4. เกิดข้อผิดพลาดการตรวจสอบสิทธิ์ที่การเรียกใช้ครั้งแรก คีย์ LLM หายไปจากสภาพแวดล้อมของกระบวนการที่รันบอท คีย์ที่ส่งออกในแท็บเทอร์มินัลหนึ่งจะไม่ปรากฏต่อบริการที่เริ่มทำงานที่อื่น
  5. คำตอบถูกตัดขาด ค่า max_tokens=900 ในตัวอย่างจำกัดคำตอบที่ยาว ให้เพิ่มค่านี้หรือลบออก เนื่องจาก max_tokens เป็นค่าไม่บังคับ และการสร้างข้อความอาจใช้พื้นที่ที่เหลือของหน้าต่างบริบท
  6. บอทตอบข้อความของตัวเอง วงจรนี้เกิดขึ้นหากคุณละการตรวจสอบ message.author.bot ให้คงการตรวจสอบนี้ไว้

สำหรับการดีบัก ให้บันทึกค่า X-Request-Id สำหรับการเรียกใช้แต่ละครั้ง ด้วย OpenAI SDK คุณสามารถอ่านค่านี้จาก raw response โดยเรียก llm.chat.completions.with_raw_response.create(...) แล้วอ่าน .headers หากติดต่อฝ่ายสนับสนุนเกี่ยวกับข้อผิดพลาดเฉพาะเจาะจง ID นี้ช่วยให้พวกเขาพบคำขอนั้นได้

ต้นทุนในการรัน

ราคาอยู่ที่ $0.15 ต่อ 1M โทเคนอินพุต และ $0.50 ต่อ 1M โทเคนเอาต์พุต โดยไม่มีค่าสมาชิก ลองพิจารณาการแลกเปลี่ยนทั่วไป: พรอมต์ระบบรวมกับรายการประวัติ 12 รายการ ทำให้มีโทเคนอินพุตประมาณ 2,500 โทเคน และคำตอบมีประมาณ 400 โทเคน

อินพุต: 2,500 x $0.15 / 1,000,000 = $0.000375. เอาต์พุต: 400 x $0.50 / 1,000,000 = $0.0002. คำตอบหนึ่งข้อมีค่าใช้จ่ายประมาณ $0.000575 ชุมชนที่ถาม 500 คำถามต่อวันจะใช้จ่าย 500 x $0.000575 = $0.2875 น้อยกว่าสามสิบเซ็นต์เล็กน้อย ตัวเลขของคุณจะต่างออกไปหากมีการวางโค้ดที่ยาวกว่า ดังนั้นให้บันทึก resp.usage เป็นเวลาหนึ่งสัปดาห์แล้วอ่านค่าเฉลี่ยที่แท้จริง หน้า ราคา แสดงตัวเลือกการเติมเงิน: จำนวนเต็มตั้งแต่ $20 ถึง $500 พร้อมโบนัส +5% จาก $50, +10% จาก $100 และ +15% จาก $200 ชำระด้วยบัตรหรือคริปโต

เพื่อจำกัดการใช้งานที่ผิดปกติ ให้เพิ่มเวลาพักต่อผู้ใช้และจำกัดความยาวของคำถาม การตรวจสอบสองอย่างนี้ปกป้องยอดเงินของคุณได้ดีกว่าการเพิ่มฟีเจอร์อื่นใด

หมายเหตุเกี่ยวกับการโฮสติ้งและความปลอดภัย

บอทเป็นกระบวนการที่ทำงานต่อเนื่อง เครื่องเสมือนหรือโฮสต์คอนเทนเนอร์ขนาดเล็กก็เพียงพอ เนื่องจากงานหนักเกิดขึ้นที่ฝั่ง API ให้รันภายใต้ตัวจัดการกระบวนการเพื่อให้เริ่มใหม่หลังเกิดข้อผิดพลาด และเก็บโทเคนทั้งสองไว้ในตัวแปรสภาพแวดล้อมหรือที่เก็บข้อมูลลับ โดยไม่เก็บไว้ในที่เก็บโค้ด

พิจารณาว่าเซิร์ฟเวอร์ของคุณอนุญาตอะไร โมเดลถูกปรับให้ตอบโดยไม่มีการปฏิเสธเนื้อหาสำหรับการใช้งานผู้ใหญ่ที่ถูกต้องตามกฎหมาย ดังนั้นจึงจะพูดถึงการทดสอบความปลอดภัยหรือนวนิยายแนวขอบที่ผู้ช่วยที่เข้มงวดกว่าอาจปฏิเสธได้ หากคุณมีสมาชิกที่อายุน้อยกว่าในชุมชน ให้ตั้งกฎในพรอมต์ระบบ จำกัดบอทให้อยู่ในช่องเฉพาะ และจำไว้ว่าข้อกำหนดของ Discord เองก็ใช้กับบอทของคุณเช่นกัน เนื้อหาทางเพศที่เกี่ยวข้องกับผู้เยาว์ต้องห้ามใน API นี้โดยไม่มีข้อยกเว้น

รายละเอียดพารามิเตอร์ทั้งหมด เช่น stop, seed และการเรียกใช้ฟังก์ชัน อยู่ในเอกสาร API หากต้องการให้บอทรันโค้ดหรืออ่าน repository การเรียกใช้ฟังก์ชันคือทางออก และคุณจะเป็นผู้กำหนดว่าโมเดลอาจขอใช้ฟังก์ชันใด สำหรับการใช้งานโมเดลนี้เพื่อเขียนโปรแกรม ดูหน้าเกี่ยวกับ โมเดล AI ไม่เซ็นเซอร์ สำหรับเขียนโค้ด

ถาม-ตอบ

ฉันต้องการ Message Content intent หรือไม่?

ใช่ หากบอทอ่านข้อความปกติและการกล่าวถึงบอท หากคุณใช้เพียงสแลชคอมมานด์ คุณก็ไม่จำเป็นต้องเปิดใช้งาน

บอทสามารถสตรีมการตอบกลับได้หรือไม่?

ได้ ตั้งค่า stream=True และแก้ไขข้อความ Discord ที่ช่วงเวลาที่เหมาะสม เช่น ครั้งต่อวินาที เพื่อให้คุณอยู่ภายในขีดจำกัดของ Discord เอง

เกิดอะไรขึ้นหากมีคนถามพร้อมกันมากเกินไป?

หลังจากคำขอพร้อมกัน 8 คำขอบนคีย์เต็มแล้ว API จะส่ง HTTP 429 พร้อมโค้ด concurrency เซมิโฟร์ในโค้ดของคุณจะจัดคิวคำถามที่เกินมาแทน

บอทจะจำการสนทนาหลังการรีสตาร์ทได้หรือไม่?

ไม่ได้ หากใช้ดิกชันนารีแบบที่ใช้ที่นี่ เก็บประวัติใน SQLite หรือ Redis หากต้องการให้ข้อมูลอยู่รอดหลังจากการรีสตาร์ท

คีย์ API ปลอดภัยในบอท Discord หรือไม่?

ปลอดภัยหากยังคงอยู่บนเซิร์ฟเวอร์ของคุณในตัวแปรสภาพแวดล้อม อย่าโพสต์ในช่องหรือคอมมิตไปยังที่เก็บโค้ดสาธารณะ

คีย์ของคุณอยู่ห่างแค่แบบฟอร์มเดียว

สร้างบัญชี คัดลอกคีย์ และเปลี่ยน base URL นั่นคือการตั้งค่าทั้งหมด

รับคีย์ API