iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
AI 自動化

AI 公司模擬器:Discord x Multi-Agent 架構實作系列 第 6

DAY 6|讓 Discord Bot 取得 Qwen 回覆

  • 分享至 

  • xImage
  •  

昨天已經讓 Python 可以呼叫 Ollama,送出 Prompt 後也能取得 qwen3.5:4b 的回答。

不過,現在還是透過 manual_test.py 在終端機測試。今天要把這段功能接回 Discord,建立一個簡單的 !ask 指令。

完成後,使用者只要在 Discord 頻道輸入問題,Bot 就會把問題交給本機的 Qwen,再將回答傳回原本的頻道。

先把範圍壓小

  • 建立 !ask 文字指令
  • 將問題交給 LLMService
  • 在等待模型時顯示處理中訊息
  • 限制輸入與輸出長度
  • 處理 Ollama 未啟動與回應逾時

先看串接流程

目前 Discord Bot 與 Ollama 已經可以分別執行,但兩邊還沒有連在一起。

今天要完成的流程如下:

Discord 使用者
    ↓ !ask 問題
bot.py
    ↓ 呼叫 chat()
LLMService
    ↓
Ollama 與 qwen3.5:4b
    ↓
Discord 顯示回答

bot.py 的責任是接收 Discord 指令與顯示結果;呼叫 Ollama、解析回應和處理連線錯誤,仍然由昨天建立的 LLMService 負責。

這樣可以避免所有功能都塞進 bot.py。未來就算把 !ask 改成 Slash Command,模型呼叫的程式也不需要重寫。

在 bot.py 載入 LLMService

先在 bot.py 加入以下匯入:

from services.llm_service import LLMService, LLMServiceError

接著建立一個共用的 Service:

llm_service = LLMService(timeout=300)

這裡沒有在每次收到指令時重新建立 LLMService,而是在 Bot 啟動時建立一次,後續的問題共用同一個 Service。

timeout=300 代表最多等待 300 秒。第一次呼叫模型時可能還包含載入時間,因此先保留較寬鬆的等待範圍。只要模型提早完成,程式就會立刻繼續執行,不是每次都要等滿 300 秒。

建立 !ask 指令

在原本的 !hello 指令下方加入:

@bot.command()
async def ask(ctx, *, question: str = ""):
    question = question.strip()

    if not question:
        await ctx.send("請在 `!ask` 後面輸入問題。")
        return

    status = await ctx.send("Qwen 正在思考中……")

    try:
        result = await llm_service.chat(question[:1000])
        await status.edit(content=result.content[:1900])
    except LLMServiceError as error:
        await status.edit(content=f"暫時無法取得回答:{error}")

使用方式如下:

!ask 請用繁體中文簡單說明什麼是 Discord Bot

函式中的 * 會讓 question 接收指令後方剩下的全部文字。因此問題中即使包含空格,也不需要額外加上引號。

例如:

!ask Python 的 async 和 await 有什麼用途?

整句都會成為 question,再交給 LLMService.chat()

使用 ! 開頭的文字指令時,除了程式中的 intents.message_content = True,也要確認 Discord Developer Portal 已啟用 Message Content Intent。

為什麼先顯示「Qwen 正在思考中」?

本機模型不像 !hello 可以立刻回覆。第一次使用時,Ollama 可能還需要把模型載入記憶體,產生回答也會受到問題長度和電腦硬體影響。

如果 Bot 在等待期間完全沒有反應,使用者可能會以為指令沒有成功送出。因此程式會先傳送一則狀態訊息:

status = await ctx.send("Qwen 正在思考中……")

模型完成回答後,再編輯同一則訊息:

await status.edit(content=result.content[:1900])

這樣頻道中不會同時出現一則「處理中」和另一則正式回答,畫面也比較乾淨。

https://ithelp.ithome.com.tw/upload/images/20260920/20183880y2IpLH7zki.jpg

https://ithelp.ithome.com.tw/upload/images/20260920/2018388011Qom1WhBu.png

為什麼限制問題與回答長度?

程式目前將問題限制為前 1000 個字元:

question[:1000]

這是專案第一版自行設定的保護,不是 Ollama 的固定限制。它可以避免使用者一次貼入過長內容,讓本機模型占用太多推論時間。

回答則先保留前 1900 個字元:

result.content[:1900]

一般 Discord 訊息最多可包含 2000 個字元。這裡保留一些空間,避免日後在回答前後加入提示文字時超過限制。

直接截斷不是最完整的做法,但適合先確認串接流程。之後可以再將長回答切成多則訊息傳送。

處理 Ollama 連線失敗

昨天的 LLMService 已經將常見問題轉換成 LLMServiceError,例如:

  • Ollama 沒有啟動
  • 模型回應逾時
  • API 回傳錯誤狀態
  • 回應中缺少 message.content

因此 !ask 不需要理解 HTTPX 或 Ollama 套件的所有例外,只要捕捉專案自己的錯誤:

except LLMServiceError as error:
    await status.edit(content=f"暫時無法取得回答:{error}")

例如 Ollama 沒有啟動時,使用者預期會看到:

暫時無法取得回答:無法連線到 Ollama,請確認服務是否已啟動。

錯誤訊息會留在 Discord,方便知道問題出在本機模型,而不是讓 Bot 一直停在「思考中」。

啟動並測試

先確認 Ollama 正在執行,而且本機已有模型:

ollama ls

接著啟用專案的虛擬環境並啟動 Bot:

source .venv/bin/activate
python bot.py

在 Discord 頻道測試:

!ask 請只回答:Discord 與 Qwen 串接成功

預期流程是:

  1. Bot 先顯示「Qwen 正在思考中……」。
  2. Python 將問題交給本機 Ollama。
  3. Qwen 完成推論後,原本的狀態訊息更新為模型回答。
  4. 如果 Ollama 無法連線,狀態訊息改成容易理解的錯誤提示。

https://ithelp.ithome.com.tw/upload/images/20260920/20183880Kar6lthEzp.jpg

接起來之後

這次把 Discord 和 Qwen 接在一起,但仍讓 bot.py 處理指令,LLMService 專心呼叫模型。現在只做單輪問答,回答太長會直接截斷,也還沒有保存對話;實際畫面等測試完成後再補。下一篇先整理專案結構與測試。


上一篇
DAY 5|使用 Python 呼叫 Ollama API
下一篇
DAY 7|整理設定,替 Discord Bot 加上測試
系列文
AI 公司模擬器:Discord x Multi-Agent 架構實作9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言