Bot Discord AI lập trình: Xây dựng với API LLM
Bot Discord AI lập trình trả lời câu hỏi lập trình trong máy chủ của bạn, sử dụng mô hình ngôn ngữ phía sau API. Hướng dẫn này xây dựng một bot khoảng 70 dòng Python và bao gồm các phần thường gặp lỗi.
Cập nhật

Điểm chính
- Bot cần discord.py, gói openai, token bot và khóa API LLM.
- Bật intent Message Content trong cổng developer Discord hoặc bot sẽ nhận được tin nhắn trống.
- Giữ lịch sử ngắn theo kênh và cắt bớt, vì mô hình không nhớ gì giữa các lần gọi.
- Chia nhỏ phản hồi ở 2.000 ký tự và giới hạn các lệnh gọi đồng thời để bạn không vượt quá giới hạn concurrency 8 yêu cầu.
Bot sẽ làm gì
Mục tiêu là một bot Discord AI lập trình phản ứng khi ai đó mention nó. Người dùng viết "@helper tại sao list comprehension của tôi trả về None?", bot gửi tin nhắn đến mô hình và câu trả lời của mô hình quay lại dưới dạng phản hồi trong cùng kênh. Bot cũng nhớ vài lượt gần nhất của mỗi kênh, vì vậy các câu hỏi tiếp theo sẽ hoạt động.
Thiết kế có ba phần. discord.py xử lý kết nối với Discord. OpenAI Python SDK giao tiếp với API LLM, hoạt động ở đây vì API tương thích OpenAI. Một từ điển nhỏ lưu trữ lịch sử hội thoại. Bạn không cần cơ sở dữ liệu để bắt đầu.
Bạn sẽ cần Python 3.9 trở lên, tài khoản Discord có thể tạo ứng dụng và khóa API từ trang khóa API. Tài khoản mới có thể nhận $0,50 tín dụng dùng thử miễn phí chỉ với một cú nhấp chuột, đủ để xây dựng và kiểm tra bot này.
Bước 1: tạo ứng dụng Discord
- Mở cổng developer Discord và tạo ứng dụng mới.
- Trong phần Bot, tạo người dùng bot và sao chép token của nó. Coi nó như mật khẩu.
- Trên cùng trang, bật Message Content Intent dưới Privileged Gateway Intents. Không có nó, bot nhận được tin nhắn với văn bản trống và bạn sẽ dành một giờ tự hỏi tại sao nó không bao giờ trả lời.
- Trong OAuth2, tạo liên kết mời với phạm vi
botvà quyền đọc tin nhắn và gửi tin nhắn. Mở liên kết và thêm bot vào máy chủ thử nghiệm.
Cài đặt các thư viện và đặt hai biến môi trường.
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}'Bước 2: mã bot
Lưu cái này dưới dạng bot.py. Nó trả lời khi được mention, giữ mười hai tin nhắn lịch sử theo kênh, hiển thị chỉ báo đang gõ trong khi chờ và chia nhỏ các câu trả lời dài.
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"])
Chạy nó với python bot.py, sau đó mention bot trong máy chủ thử nghiệm của bạn. Câu trả lời đầu tiên sẽ đến trong vài giây.
Tại sao mã được viết như vậy
Lịch sử được cắt bớt có chủ đích
Mô hình không có bộ nhớ, vì vậy mỗi lần gọi gửi các lượt gần đây. Không có del log[:-MAX_TURNS] danh sách sẽ phát triển mãi mãi. Cuối cùng nó sẽ đạt cửa sổ ngữ cảnh 100.000 token và chi phí của bạn sẽ tăng lên với mỗi tin nhắn. Mười hai mục nhập là đủ cho một cuộc hội thoại về một lỗi. Đối với máy chủ có các luồng dài, hãy tăng nó hoặc tóm tắt các lượt cũ hơn.
Giới hạn 2.000 ký tự của Discord
Discord từ chối tin nhắn dài hơn 2.000 ký tự và câu trả lời mã nhanh chóng đạt giới hạn đó. Hàm trợ giúp split_reply cắt ở dấu xuống dòng gần giới hạn. Nó không biết về code fences, vì vậy một khối có thể bị cắt ở giữa. Nếu điều đó làm bạn khó chịu, hãy thêm logic đóng một fence ở cuối một phần và mở lại ở đầu phần tiếp theo.
Semaphore cho độ đồng thời
Khóa đầy đủ cho phép 8 yêu cầu đồng thời và khóa dùng thử cho phép 2. Máy chủ bận có thể tạo ra nhiều câu hỏi cùng lúc. Semaphore xếp hàng các lệnh gọi bổ sung trong quy trình của bạn thay vì gửi chúng và nhận mã 429 với mã lỗi concurrency. Nếu bạn vẫn đang dùng khóa dùng thử, hãy thay đổi Semaphore(6) thành Semaphore(2).
Lỗi không bị tính phí
Các yêu cầu thất bại hoặc bị từ chối không được tính phí, vì vậy việc thử lại sau lỗi không làm tăng gấp đôi chi phí. Mã xóa lượt người dùng khỏi lịch sử khi thất bại, vì vậy việc thử lại không để lại câu hỏi treo mà mô hình đã thấy hai lần.
Làm cho câu trả lời tốt hơn
Khi vòng lặp cơ bản hoạt động, một vài thay đổi nhỏ cải thiện nó rất nhiều.
- Truyền phát câu trả lời. Đặt
stream=Truevà chỉnh sửa tin nhắn bot khoảng mỗi giây một lần với phần văn bản đã nhận được cho đến lúc đó. Không chỉnh sửa trên mỗi token, vì Discord áp dụng giới hạn tốc độ riêng cho việc chỉnh sửa tin nhắn. Yêu cầu truyền phát giữ slot concurrency của nó cho đến khi kết thúc, vì vậy hãy giữ semaphore xung quanh toàn bộ stream. - Sử dụng slash command. Lệnh như
/asktránh việc phân tích mention và không cần intent Message Content chút nào, điều này cũng làm cho bot dễ xác minh hơn sau này nếu nó phát triển vượt quá một số lượng nhỏ máy chủ. - Lịch sử theo người dùng. Tách lịch sử theo kênh và người dùng nếu nhiều người hỏi các câu hỏi không liên quan trong một kênh.
- Điều chỉnh prompt hệ thống. Đặt tên các ngôn ngữ cộng đồng của bạn sử dụng, yêu cầu câu trả lời ngắn và nói với mô hình hỏi câu hỏi làm rõ khi vấn đề mơ hồ. Thay đổi prompt miễn phí và thường tốt hơn thay đổi mô hình.
- Giảm nhiệt độ. Đối với mã, các giá trị giữa 0,2 và 0,4 cho kết quả ổn định hơn mặc định.
Khắc phục sự cố các lỗi thường gặp
Khi bot mới im lặng, nguyên nhân gần như luôn là một trong các mục dưới đây. Kiểm tra chúng theo thứ tự này.
- Bot trực tuyến nhưng không bao giờ trả lời. Intent Message Content bị tắt trong cổng developer hoặc
intents.message_content = Truebị thiếu trong mã. Cả hai phải được đặt. - Nó trả lời chỉ trong một số kênh. Bot thiếu quyền đọc hoặc gửi tin nhắn trong các kênh khác. Kiểm tra ghi đè quyền của kênh, không chỉ vai trò toàn máy chủ.
- Mọi câu trả lời đều là thông báo "bận". Bạn đang gặp phải lỗi 429. In thân ngoại lệ một lần. Nếu mã nói
rate_limited, bạn đã vượt quá giới hạn mỗi phút là 300 yêu cầu trên khóa đầy đủ hoặc 60 trên khóa dùng thử. Nếu nó nóiconcurrency, hãy giảm kích thước semaphore. - Lỗi xác thực xuất hiện ở lần gọi đầu tiên. Khóa LLM bị thiếu trong môi trường của quy trình chạy bot. Khóa được xuất trong một tab terminal không hiển thị cho dịch vụ được bắt đầu ở nơi khác.
- Câu trả lời bị cắt ngắn.
max_tokens=900trong mẫu dừng các câu trả lời dài. Tăng nó hoặc bỏ nó, vìmax_tokenslà tùy chọn và phần hoàn thành có thể sử dụng phần còn lại của cửa sổ. - Bot trả lời chính tin nhắn của mình. Vòng lặp đó xảy ra nếu bạn bỏ kiểm tra
message.author.bot. Hãy giữ nó.
Để gỡ lỗi, hãy ghi nhật ký giá trị X-Request-Id cho mỗi lần gọi. Với OpenAI SDK, bạn có thể đọc nó từ phản hồi thô bằng cách gọi llm.chat.completions.with_raw_response.create(...) và sau đó đọc .headers. Nếu bạn liên hệ hỗ trợ về một lỗi cụ thể, ID đó cho phép họ tìm yêu cầu chính xác.
Chi phí vận hành
Giá là $0,15 cho mỗi 1 triệu token đầu vào và $0,50 cho mỗi 1 triệu token đầu ra, không có phí đăng ký. Hãy xem một ví dụ điển hình: prompt hệ thống cộng với mười hai mục lịch sử tạo ra khoảng 2.500 token đầu vào, và câu trả lời là khoảng 400 token.
Đầu vào: 2.500 x $0,15 / 1.000.000 = $0,000375. Đầu ra: 400 x $0,50 / 1.000.000 = $0,0002. Một câu trả lời tốn khoảng $0,000575. Một cộng đồng hỏi 500 câu hỏi mỗi ngày sẽ chi 500 x $0,000575 = $0,2875, chưa đầy ba mươi xu. Số liệu của bạn sẽ khác với các đoạn mã dài, vì vậy hãy ghi lại resp.usage trong một tuần và xem các giá trị trung bình thực tế. Trang giá cả hiển thị các tùy chọn nạp tiền: bất kỳ số nguyên nào từ $20 đến $500, với +5% từ $50, +10% từ $100 và +15% từ $200, thanh toán bằng thẻ hoặc tiền mã hóa.
Để hạn chế lạm dụng, hãy thêm thời gian chờ giữa các lần sử dụng cho mỗi người dùng và giới hạn độ dài câu hỏi. Hai kiểm tra này bảo vệ số dư của bạn tốt hơn bất kỳ thứ gì khác bạn có thể thêm vào.
Lưu ý về lưu trữ và an toàn
Bot là một tiến trình chạy liên tục. Một máy ảo nhỏ hoặc máy chủ container là đủ, vì công việc nặng xảy ra ở phía API. Chạy nó dưới một trình quản lý tiến trình để nó tự khởi động lại sau khi gặp sự cố, và giữ cả hai token trong các biến môi trường hoặc kho bí mật, không bao giờ trong kho lưu trữ mã nguồn.
Hãy suy nghĩ về những gì máy chủ của bạn cho phép. Mô hình được tinh chỉnh để trả lời mà không có từ chối nội dung cho mục đích người trưởng thành hợp pháp, vì vậy nó sẽ thảo luận về kiểm thử bảo mật hoặc truyện hư cấu gai góc mà một trợ lý nghiêm ngặt hơn có thể từ chối. Nếu cộng đồng của bạn bao gồm các thành viên nhỏ tuổi hơn, hãy đặt quy tắc trong prompt hệ thống, hạn chế bot vào các kênh cụ thể và nhớ rằng các điều khoản của Discord cũng áp dụng cho bot của bạn. Nội dung tình dục hóa trẻ vị thành niên bị cấm trên API này mà không có ngoại lệ.
Chi tiết đầy đủ về các tham số, chẳng hạn như stop, seed và gọi hàm, có trong tài liệu API. Nếu bạn muốn bot chạy mã hoặc đọc kho lưu trữ, gọi hàm là cách tiếp cận, và bạn quyết định những hàm nào mà mô hình có thể yêu cầu. Đối với việc sử dụng mô hình này tập trung vào lập trình, hãy xem trang về mô hình AI không kiểm duyệt cho lập trình.
Hướng dẫn khác
Hỏi đáp
Tôi có cần intent Message Content không?
Có, nếu bot đọc các tin nhắn bình thường và mention. Nếu bạn chỉ sử dụng lệnh slash, bạn không cần nó.
Bot có thể truyền phát phản hồi không?
Có. Đặt stream=True và chỉnh sửa tin nhắn Discord ở một khoảng thời gian ổn định, chẳng hạn như một lần mỗi giây, để bạn vẫn nằm trong giới hạn của riêng Discord.
Sẽ xảy ra gì nếu quá nhiều người hỏi cùng một lúc?
Quá 8 yêu cầu đồng thời trên một khóa đầy đủ, API trả về HTTP 429 với mã concurrency. Một semaphore trong mã của bạn sẽ xếp hàng các câu hỏi bổ sung thay thế.
Bot có nhớ các cuộc trò chuyện sau khi khởi động lại không?
Không với từ điển được sử dụng ở đây. Hãy lưu trữ lịch sử trong SQLite hoặc Redis nếu bạn cần nó tồn tại sau các lần khởi động lại.
Khóa API có an toàn trong bot Discord không?
Có nếu nó vẫn ở trên máy chủ của bạn trong một biến môi trường. Không bao giờ đăng nó trong một kênh hoặc commit nó vào một kho lưu trữ công khai.
Khóa của bạn chỉ cách một biểu mẫu
Tạo tài khoản, sao chép khóa, thay đổi URL cơ sở. Đó là toàn bộ thiết lập.
Lấy khóa API