JA ▾
API キーを取得

無検閲LLM搭載のオープンソースDiscordボット

LLMを介して会話するオープンソースのDiscordボットはPythonで約80行です。このガイドでは、遵守すべき制限や応答ごとのコスト、合法な成人向けリクエストを拒否しないモデルを使用したOpenAI互換APIのコードを提供します。

チャットウィンドウと分岐するコードツリーに接続された、光るキューブで作られたボットのアイソメトリックイラスト

更新日:

主要ポイント

  • discord.pyとOpenAI SDKで十分です。SDKを https://api.openrouternsfw.com/v1 に指向させ、モデル uncensored を使用してください。
  • Discord開発者ポータルでメッセージコンテンツの権限を有効にしてください。そうしないと、ボットは空のメッセージしか取得できません。
  • 同時実行数制限内に収めるためにセマフォを8未満に設定し、アダルトなコンテンツは年齢制限付きチャンネルに限定してください。
  • 入力トークン1,500、出力トークン300の返信には$0.000375かかり、10,000件の返信あたり約$3.75です。

カード不要。サインアップ後、ワンクリックで$0.50の無料テストクレジットを請求できます(1人1回限り)。

API キーを取得

あなたが構築しているもの

以下のボットは、ユーザーがメンションした際に応答します。チャンネルごとに短い履歴を保持し、チャット完了エンドポイントに送信し、応答を投稿します。純粋なPythonでMITライセンス準拠、データベースも不要です。すべてを一度に読めるため、オープンソースプロジェクトの目的通り、他の人がフォークして理解することができます。

その背後にあるモデルは単一のオープンウェイトテキストモデルであり、独立したプロバイダによって実行され、法的な成人向け利用においてコンテンツ拒否なしで回答するようにチューニングされています。これはフィクション、ロールプレイ、または成人向けの会話を軸としたDiscordコミュニティに適しています。最初から2つのルールが適用されます。第一に、未成年者を含む性的コンテンツはAPIで禁止されており、ボットの設計に含まれてはいけません。第二に、Discordには独自のルールと年齢制限付きチャンネル設定があり、それらに従う責任があります。成人向けコンテンツは、そのようなマークが付けられたチャンネルと、メンバーが成人であるサーバーに限定してください。

Discordとキーの設定

  1. Discord開発者ポータルを開き、アプリケーションを作成してから、それにボットを追加してください。
  2. ボット設定で、メッセージコンテンツインテントを有効にしてください。これを有効にしないと、ボットは空のテキストを持つメッセージを受信する可能性があります。
  3. ボットトークンをコピーしてください。パスワードのように扱ってください。
  4. OAuth2 URLジェネレーターを使用して、選択したチャンネルでメッセージの読み取りと送信の権限を与えてボットを招待してください。
  5. キーページからAPIキーを取得してください。新規ユーザーはワンクリックで$0.50のトライアルクレジットを請求できます。支払い情報の登録は不要で、7日間有効です。

両方のシークレットを環境変数として保存し、リポジトリには含めないでください。オープンソースプロジェクトでは、コードが公開されるため、通常よりもこれが重要になります。

pip install discord.py openai python-dotenv
printf 'DISCORD_TOKEN=your-discord-token
API_KEY=your-api-key
' > .env
echo ".env" >> .gitignore

ボットコード

これを bot.py として保存してください。コメントには、自明ではない判断の理由が説明されています。

import asyncio
import os
from collections import defaultdict, deque

import discord
from dotenv import load_dotenv
from openai import AsyncOpenAI

load_dotenv()

ai = AsyncOpenAI(
    base_url="https://api.openrouternsfw.com/v1",
    api_key=os.environ["API_KEY"],
    max_retries=4,          # the SDK retries 429 and honors Retry-After
)

SYSTEM = (
    "You are Vex, a sharp-tongued storyteller in a Discord server for adults. "
    "Stay in character, keep replies under 150 words, and never write "
    "anything sexual involving minors."
)

intents = discord.Intents.default()
intents.message_content = True
bot = discord.Client(intents=intents)

slots = asyncio.Semaphore(6)                      # stay under 8 concurrent
history = defaultdict(lambda: deque(maxlen=12))   # last 12 turns per channel


async def ask(channel_id, user_text):
    turns = history[channel_id]
    turns.append({"role": "user", "content": user_text})
    messages = [{"role": "system", "content": SYSTEM}] + list(turns)
    async with slots:
        resp = await ai.chat.completions.create(
            model="uncensored",
            messages=messages,
            temperature=0.9,
            max_tokens=400,
        )
    reply = resp.choices[0].message.content or ""
    turns.append({"role": "assistant", "content": reply})
    return reply


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")


@bot.event
async def on_message(message):
    if message.author.bot or bot.user not in message.mentions:
        return
    text = message.clean_content.replace(f"@{bot.user.display_name}", "").strip()
    if not text:
        return
    async with message.channel.typing():
        try:
            reply = await ask(message.channel.id, f"{message.author.display_name}: {text}")
        except Exception as exc:
            print("API error:", exc)
            await message.reply("The model is busy. Try again in a few seconds.")
            return
    for i in range(0, len(reply), 1900):          # Discord caps messages at 2000 chars
        await message.reply(reply[i:i + 1900])


bot.run(os.environ["DISCORD_TOKEN"])

python bot.py で実行し、ボットが読み取れるチャンネルでボットをメンションしてください。最初の応答は数秒で届くはずです。

何も起こらない場合は、順番に3点を確認してください。ボットはメンバーリストでオンラインである必要があり、そのトークンは有効です。ボットはそのチャンネルでメッセージの読み取りと送信の権限を持っている必要があります。また、メンションはオートコンプリートリストから選択された実際のDiscordメンションである必要があり、ボット名だけのプレーンテキストは message.mentions に現れないためです。ターミナルにはAPIエラーが出力されるため、間違ったキーは認証失敗として表示されます。

コードがこのような構造になっている理由

履歴は意図的に小さく保つ

コンテキストウィンドウは100,000トークンなので、大量のデータを送信できます。ただし、すべての入力トークンは呼び出しごとに課金され、Discordのチャットは主に短い行で構成されています。12ターンに保つことで、メッセージが大きな請求額になることなく、返信の一貫性を維持できます。ストーリーチャネルでは継続性が重要なので、maxlenを上げてください。

セマフォがキーを保護する

フルキーでは8つの同時リクエストと1分あたり300リクエストが可能です。トライアルキーではそれぞれ2と60のみです。10人が同時にボットにメンションし、10個のリクエストを送信した場合、追加のリクエストはHTTP 429とconcurrencyエラーコードを受け取ります。セマフォはそれらをあなたのプロセス内でキューに入れます。トライアルキーの間は2に設定してください。ストリーミングを有効にする場合は、ストリーミングリクエストはストリーミングが完了するまでスロットを保持し続けることを覚えておいてください。

リトライはSDKから行われる

OpenAI Python SDKは429でリトライし、サービスが一時的な過負荷時にRetry-After: 5として送信するRetry-Afterヘッダーを尊重します。max_retriesを4に設定することで、短いスパイクに対応できます。失敗または拒否されたリクエストは課金されないので、リトライが失敗してもトークンは消費されません。

単一の共有ペルソナ

システムプロンプトはすべての呼び出しの最初に配置されます。チャンネルごとのペルソナは小さな拡張機能です:チャンネルIDからプロンプトへの辞書を保持し、ask でそれを取得してください。

良いオープンソースプロジェクトにする

コードは機能しますが、公開リポジトリには、見知らぬ人が使用できるようになるためのいくつかの追加が必要です。

  • ライセンス。 小さなボットにはMITまたはApache 2.0が一般的です。リポジトリのルートにそのファイルを追加してください。
  • .env.example。 DISCORD_TOKEN と API_KEY をダミー値でリストし、設定すべき値を人がわかるようにしてください。
  • 設定可能なベースURL。 環境から BASE_URL と MODEL を読み込み、上記の値をデフォルト値とします。フォークした人は誰でもOpenAI互換サーバーを使用できます。
  • Dockerfile。 Python slimイメージに基づく短いファイルで、bot.py をコピーし、3つのパッケージをインストールしてスクリプトを実行することで、どこでもホストできます。
  • 意図に関する警告を含むREADME。 欠落しているメッセージコンテンツの意図は、すべてのDiscordボットにとってのトップサポート質問です。

トークンとキーをGit履歴に残さないようにしてください。1つが漏洩した場合は、DiscordポータルまたはAPIキーページで無効化し、新しいものを発行してください。

ホスティングとコスト

ボットは軽量なCPU使用の単一の長時間実行プロセスです。小型のVPS、予備のマシン、またはコンテナプラットフォームで十分です。ボットがDiscordに接続するため、公開するWebサーバーはありません。

APIのコストは簡単に計算できます。典型的な応答では、1,500入力トークン(システムプロンプトと12の短いターン)と300出力トークンを使用します。

  • 入力: 1,500 x $0.15 / 1M = $0.000225
  • 出力: 300 x $0.50 / 1M = $0.00015
  • 応答あたり: $0.000375

これにより、1,000回の応答で$0.375、10,000回の応答で$3.75になります。$0.50のトライアルは約1,333回の応答をカバーします。$20のチャージは53,000回以上をカバーします。チャージは$20から$500まで、$50以上で+5%、$100以上で+10%、$200以上で+15%です。クレジットは期限切れにならず、サブスクリプションもないため、静かなサーバーの維持費はほぼゼロです。詳細は 料金ページ を参照してください。支払いは暗号資産のみで、TRC20上のUSDTまたはBase上のUSDCを使用します。

次のステップ

努力する価値のある3つのアップグレードがあります。チャンクが届くたびに単一のメッセージを編集してストリーミングを追加しますが、Discordの編集レート制限に注意してください。チャネル履歴をリセットするスラッシュコマンドを追加します。ツール呼び出しを追加して、ボットがサイコロを振ったりキャラクターシートを検索したりできるようにします。APIはOpenAI形式でtools、tool_choice、role: "tool" をサポートしています。

APIドキュメント はサポートされているすべてのパラメータをリストしており、Hermes API記事 はプロジェクト内で無検閲モデルがどのように動作するかをカバーしています。APIに送信されるプロンプトはトレーニングに使用されないため、メンバーが尋ねた場合にサーバーのルールで言及する価値があります。

質問と回答

ボットにメッセージコンテンツの権限が必要ですか?

はい。Discord開発者ポータルとコードで intents.message_content を true に設定してください。設定しないと、ボットは空のメッセージしか取得できません。

ボットは同時に何件の応答を処理できますか?

フルキーでは8つの同時リクエストと1分あたり300リクエストが可能です。トライアルキーではそれぞれ2つの同時リクエストと1分あたり60リクエストが可能です。ボット内のセマフォにより、制限内に収めることができます。

ボットのコードを公開できますか?

DiscordトークンとAPI キーがリポジトリに含まれていなければ公開可能です。環境変数から読み込み、.env.exampleを同梱してください。

アダルトなテーマの使用は許可されていますか?

Discordのルールおよび年齢制限付きチャンネルの設定に則った合法な成人向け利用であれば可能です。未成年者を含む性的コンテンツは決して許可されません。

運用コストはいくらですか?

入力トークン1,500、出力トークン300の返信には$0.000375かかり、10,000回の返信あたり約$3.75となります。これに加えてプロセスのホスティングコストがかかります。

キーはあと1つのフォームで手に入ります

アカウントを作成し、キーをコピーし、ベースURLを変更します。これでセットアップは完了です。

API キーを取得