経緯
- DifyでDeepResearchのテンプレートができていたので、それを流用してDiscordにリリースしようと思った。
- DiscordとかSlack経由でLLMを動かすのって心躍るでしょ?
- そういえば、クラウド・無料版Difyってウェブアプリ版を切ってバックエンドサービスだけ動かしておけば、APIキーがパスワード代わりになって非公開化できるんじゃない?という思いつき。
作ったもの
- DeepResearch用のフォーラムを作成し、そこに調べたい事柄の名前でスレッドを立てるとしばらくしたのちDifyワークフローの返信が来る。
つまづきポイント
- ボットをXserverでホストしようとした。
- 学生無料プランをいただいているから選んだ。
かっこいいからJavaScriptの練習になると思ってNode.jsを使おうといろいろ試行錯誤したが、無理だった。- XserverはCentOSで、その関係かわからないけどNode18とかインストールできない。
- Node16はインストールできたが、Discord.jsの依存ライブラリが非対応だったり。
- 結局Pythonを使うことにして、一瞬で解決。
- 最後にちょっと詰まったのは、一度に送れる文字数が2000文字だというところ
- あと、async/awaitをDeepResearchのリクエストに対して行うところ。aiohttpというライブラリで実装(してとo3-miniに言われた)。Requestがタイムアウトしてしまう対策。
参考にさせていただいたサイト一覧
※順不同
- Discord.pyで新規スレッドを作成する方法 https://qiita.com/Shichimi555/items/dc45c5246f6e9791121e
- 常に動かす:https://gamercatsplus.com/2020/06/discord_bot_on_xserver/#toc6
- Bot初期設定など:https://note.com/exteoi/n/n00342a623c93
- chatgpt.com
(ほぼAIが書いた)コード
うごくからOK。まぁまぁ理解はした。
import discord
from discord.ext import commands
from deepresearchapi import DeepResearch # aiohttp 版をインポートする
import aiohttp
# Discord Botのトークン
TOKEN = "(ちゃんと書いてね!)"
# Botのプレフィックス
PREFIX = "!"
# intentsの設定
intents = discord.Intents.default()
intents.message_content = True
intents.guilds = True
intents.messages = True
intents.members = True # thread.owner (Memberオブジェクト) を使う場合は必要
# Botのインスタンスを作成
bot = commands.Bot(command_prefix=PREFIX, intents=intents)
# フォーラムチャンネルのID
FORUM_CHANNEL_ID = (ちゃんとかいてね!)# 実際のIDに置き換え
@bot.event
async def on_ready():
print(f"{bot.user.name} が起動しました")
@bot.event
async def on_thread_create(thread):
"""新しいスレッドが作成されたときの処理 (フォーラムチャンネル限定)"""
if thread.parent_id == FORUM_CHANNEL_ID:
try:
starter_message = await thread.fetch_message(thread.id)
# starter_message = thread.starter_message # v2.3 以降
except discord.NotFound:
print(f"Error: Could not find starter message for thread {thread.id}")
return
except discord.HTTPException as e:
print(f"HTTP Error fetching starter message: {e}")
return
except Exception as e:
print(f"Error: {e}")
return
# スレッド作成者の情報を取得 (thread.owner と starter_message.author の両対応)
if thread.owner: # thread.owner が利用可能な場合 (サーバーメンバー)
author = thread.owner
else: # thread.owner が None の場合 (Webhookなど)
author = starter_message.author
# オウム返しメッセージを作成
if isinstance(author, discord.Member): # authorがMemberオブジェクトであるか
# reply_message = f"{author} さんが作成したスレッドの最初のメッセージをオウム返しします:\n{starter_message.content}"
try:
reply_message = await DeepResearch(starter_message.content, str(author.id))
except Exception as e:
reply_message = "ワークフロー中にエラーが発生しました。"
print(e)
else:
reply_message = "エラー"
if reply_message: # Noneでないことを確認
max_length = 2000
for i in range(0, len(reply_message), max_length):
await thread.send(reply_message[i:i + max_length])
bot.run(TOKEN)
# deepresearchapi.py (aiohttp バージョン)
import asyncio
import json
import aiohttp
async def DeepResearch(query, user):
"""
Sends a chat message to the Dify.ai API and returns the answer (async version).
"""
api_key = "(DifyワークフローのAPIキーをここにいれる)"
url = "https://api.dify.ai/v1/chat-messages"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
data = {
"inputs": {},
"query": query,
"response_mode": "blocking", # Dify API が blocking をサポートしている場合。
"conversation_id": "",
"user": user,
"files": [],
}
try:
async with aiohttp.ClientSession() as session:
async with session.post(
url, headers=headers, json=data, timeout=60
) as response: # タイムアウトは長めに
response.raise_for_status()
json_response = await response.json()
return json_response.get("answer")
except aiohttp.ClientError as e:
print(f"Dify API request failed: {e}")
return None
except json.JSONDecodeError as e:
print(f"Failed to decode JSON: {e}")
return None
except Exception as e:
print(f"An unexpected error occurred: {e}")
return None
if __name__ == "__main__":
async def main():
query = "iPhone 13 Pro Maxの仕様は何ですか?"
user_id = "user123"
answer = await DeepResearch(query, user_id)
if answer:
print("\nAnswer from DeepResearch:")
print(answer)
else:
print("\nDeepResearch failed to return an answer.")
asyncio.run(main())
コメントを残す