Bot開発においてランダムに算出した値が同一の値で繰り返し使用される問題の解決方法

備忘録

はじめに

Botを開発していて、ランダムな値を用いて動的なメッセージを生成するシナリオは多い。
しかし、ランダムな値を生成するタイミングや場所によって、同じ値が複数のメッセージに反映されてしまう問題に直面することがある。

本記事では、Slack Bot開発における具体例を元に、この問題の原因と解決方法を解説する。


スポンサーリンク
スポンサーリンク

問題の概要

Slack Botでユーザー体験を向上させるためには、動的でランダムなメッセージ生成が欠かせない。
しかし、コードの設計によっては、期待したランダム性が得られない場合がある。次の例を見てみよう。

問題のコード

responses.py

import random

# ランダムな値を含むレスポンス
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
random_number = random.choice(numbers)

response_1 = f"今日の数字は: {random_number}"
response_2 = f"ラッキーナンバーは: {random_number}"

response_list = [response_1, response_2]

このスクリプトは、Botのレスポンスとしてランダムな数字を含むメッセージを生成することを目的としている。

bot.py

import random
from responses import response_list
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

client = WebClient(token="your-slack-bot-token")

def handle_event(event_data):
channel = event_data["channel"]
try:
client.chat_postMessage(
channel=channel,
text=random.choice(response_list)
)
except SlackApiError as e:
print(f"Error posting message: {e}")

上記コードでは、response_listからランダムにメッセージを選択してSlackに投稿している。しかし、メッセージ内のランダムな値は固定されており、複数のイベントで同じ数字が繰り返し使用される。


問題の原因

この現象の本質的な原因は、ランダムな値の生成タイミングにある。具体的には、以下のような動作が問題を引き起こす。

  1. モジュールの読み込み時に値が固定化
    responses.pyが読み込まれるタイミングで、random_numberが一度だけ計算される。その後は、この固定された値がresponse_listに保存され続ける。
  2. イベントごとの再評価が行われない
    handle_event内でrandom.choice(response_list)が呼び出されるたびに、同じrandom_numberを使用したレスポンスが選択される。

この結果、Botのメッセージは動的に見えるが、実際には固定的なランダム値しか提供できない。


解決方法

問題を解決するためには、ランダムな値をイベント処理時に生成する仕組みを導入する必要がある。以下では具体的な方法を示す。

解決策1: レスポンス生成を関数化する

ランダムな値を生成する処理を関数化し、メッセージ送信時に動的に値を生成する設計に変更する。

修正後のresponses.py

import random

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]

def generate_responses():
random_number = random.choice(numbers)
response_1 = f"今日の数字は: {random_number}"
response_2 = f"ラッキーナンバーは: {random_number}"
return [response_1, response_2]

解決策2: イベント処理時に新しい値を生成する

イベントごとに新しいレスポンスを生成するよう、イベント処理コードを改良する。

修正後のbot.py

from responses import generate_responses
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

client = WebClient(token="your-slack-bot-token")

def handle_event(event_data):
channel = event_data["channel"]
responses = generate_responses() # 動的なレスポンスを生成
try:
client.chat_postMessage(
channel=channel,
text=random.choice(responses)
)
except SlackApiError as e:
print(f"Error posting message: {e}")

応用例

このアプローチは、ランダム性を必要とする他の多くの場面でも利用できる。以下にいくつかの具体例を示す。

1. CLIツールでのメッセージ生成

コマンド実行時に異なるメッセージをユーザーに表示する。例えば、タスク実行後のランダムな祝辞メッセージ。

import random

def generate_message():
messages = ["お疲れ様!", "よくやった!", "素晴らしい進捗!"]
return random.choice(messages)

print(generate_message())

2. Web APIレスポンスの動的生成

HTTPリクエストごとに異なるデータを返すAPIエンドポイント。

from flask import Flask, jsonify
import random

app = Flask(__name__)

@app.route('/random-number')
def random_number():
number = random.randint(1, 100)
return jsonify({"message": f"ランダムな数字は: {number}"})

3. チャットボットのランダム応答

ユーザー入力に基づいて、異なるトピックやアプローチの応答を返す。

import random

def chatbot_response(user_input):
responses = [
f"こんにちは!今日は何をしますか?",
f"元気ですか?ランダムな数字ですが、{random.randint(1, 10)}を選びました!"
]
return random.choice(responses)

print(chatbot_response("Hello"))

解決策のメリット

  • 動的な体験の提供
    各イベントにおいて新しいデータを生成することで、ユーザーに常に新鮮な体験を提供できる。
  • 再利用性の向上
    関数化することで、コードが簡潔になり、他のプロジェクトでも容易に再利用可能。
  • 保守性の向上
    問題の箇所が関数内に集約されるため、修正や拡張が容易になる。

まとめ

ランダムな値の生成は一見簡単に見えるが、その生成タイミングやスコープに注意を払わなければならない。

本記事では、Slack Botのランダム性を復元するために、レスポンス生成を関数化し、イベントごとに新しい値を生成する方法を紹介した。
この手法は、他のアプリケーション開発でも役立つ基本的かつ強力な解決策である。

コメント