From b994dccebfca0289f8c4d1d289e9822f37188bee Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 09:32:04 +0000 Subject: [PATCH] feat: Add Oda Nobunaga Chatbot - Implemented `nobunaga_bot.py` which fetches Oda Nobunaga's info from Wikipedia and uses Gemini API to chat as him. - Added `requirements.txt` with necessary dependencies. - Added `README_NOBUNAGA.md` with usage instructions. - Added `test_nobunaga_bot.py` for testing the bot logic. - Added `.gitignore` to exclude unnecessary files. Co-authored-by: muumuu8181 <87556753+muumuu8181@users.noreply.github.com> --- .gitignore | 4 +++ README_NOBUNAGA.md | 49 +++++++++++++++++++++++++ nobunaga_bot.py | 85 ++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 ++ test_nobunaga_bot.py | 76 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+) create mode 100644 .gitignore create mode 100644 README_NOBUNAGA.md create mode 100644 nobunaga_bot.py create mode 100644 requirements.txt create mode 100644 test_nobunaga_bot.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec79470 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.pyc +.pytest_cache/ +.env diff --git a/README_NOBUNAGA.md b/README_NOBUNAGA.md new file mode 100644 index 0000000..40f6b0e --- /dev/null +++ b/README_NOBUNAGA.md @@ -0,0 +1,49 @@ +# 織田信長ボット (Oda Nobunaga Bot) + +Wikipediaの情報を元に、織田信長になりきって対話するボットです。 +Google Gemini APIを使用しています。 + +## 前提条件 + +* Python 3.7以上 +* Google Gemini APIキー + +## インストール + +必要なライブラリをインストールします。 + +```bash +pip install -r requirements.txt +``` + +## APIキーの設定 + +Google Gemini APIキーを取得し、環境変数 `GOOGLE_API_KEY` に設定します。 + +### Linux / macOS + +```bash +export GOOGLE_API_KEY="your_api_key_here" +``` + +### Windows (PowerShell) + +```powershell +$env:GOOGLE_API_KEY="your_api_key_here" +``` + +## 実行方法 + +以下のコマンドでボットを起動します。 + +```bash +python nobunaga_bot.py +``` + +起動すると、織田信長として挨拶します。質問を入力すると、Wikipediaの情報を元に信長らしい口調で回答してくれます。 +終了するには `exit` または `quit` と入力してください。 + +## 注意事項 + +* このボットはWikipediaの情報を元に回答を生成しますが、常に正確な情報を提供するとは限りません。 +* APIの使用量によっては課金が発生する場合がありますのでご注意ください。 diff --git a/nobunaga_bot.py b/nobunaga_bot.py new file mode 100644 index 0000000..33976ed --- /dev/null +++ b/nobunaga_bot.py @@ -0,0 +1,85 @@ +import os +import sys +import wikipedia +import google.generativeai as genai + +def get_nobunaga_info(): + """Wikipediaから織田信長の情報を取得する""" + try: + wikipedia.set_lang("ja") + # ページが存在するか確認し、内容を取得 + page = wikipedia.page("織田信長") + return page.content + except wikipedia.exceptions.PageError: + print("エラー: Wikipediaページ '織田信長' が見つかりませんでした。") + return None + except wikipedia.exceptions.DisambiguationError as e: + print(f"エラー: 曖昧さ回避が必要です: {e.options}") + return None + except Exception as e: + print(f"エラーが発生しました: {e}") + return None + +def main(): + # APIキーの確認 + api_key = os.environ.get("GOOGLE_API_KEY") + if not api_key: + print("エラー: 環境変数 'GOOGLE_API_KEY' が設定されていません。") + print("export GOOGLE_API_KEY='your_api_key_here' を実行してから再度試してください。") + return + + # Wikipediaから情報を取得 + print("織田信長の情報をWikipediaから取得中...") + nobunaga_info = get_nobunaga_info() + if not nobunaga_info: + print("情報の取得に失敗したため、終了します。") + return + + # Gemini APIの設定 + genai.configure(api_key=api_key) + + # システムプロンプトの作成 + system_prompt = f""" +あなたは織田信長です。以下のWikipediaの情報を元に、ユーザーの質問に答えてください。 +回答は常に織田信長らしい尊大な口調(「わしは...じゃ」「...であるか」など)で行ってください。 +現代の知識についても、当時の視点や価値観を交えてコメントしてください。 + +【織田信長の情報】 +{nobunaga_info[:20000]} # トークン制限を考慮して適当な長さに切り詰める +""" + + # モデルの初期化 + try: + model = genai.GenerativeModel('gemini-pro') + chat = model.start_chat(history=[ + {"role": "user", "parts": [system_prompt]}, + {"role": "model", "parts": ["うむ、わしが織田信長じゃ。何用か?"]} + ]) + except Exception as e: + print(f"モデルの初期化に失敗しました: {e}") + return + + print("\n--- 織田信長ボット (終了するには 'exit' または 'quit' と入力) ---") + print("信長: うむ、わしが織田信長じゃ。何用か?") + + while True: + try: + user_input = input("あなた: ") + if user_input.lower() in ['exit', 'quit', '終了']: + print("信長: さらばじゃ。") + break + + if not user_input.strip(): + continue + + response = chat.send_message(user_input) + print(f"信長: {response.text}") + + except (KeyboardInterrupt, EOFError): + print("\n信長: 中断か。まあよい。さらばじゃ。") + break + except Exception as e: + print(f"エラーが発生しました: {e}") + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3bcd200 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +wikipedia +google-generativeai diff --git a/test_nobunaga_bot.py b/test_nobunaga_bot.py new file mode 100644 index 0000000..9c0c6b5 --- /dev/null +++ b/test_nobunaga_bot.py @@ -0,0 +1,76 @@ +import unittest +from unittest.mock import patch, MagicMock +import os +import sys + +# nobunaga_bot モジュールをインポートするためにパスを通す必要があれば通すが、同じディレクトリなのでそのままインポート +# ただし、main()を実行してしまうとループに入るので、import時に実行されないように __name__ == "__main__" ガードがあるか確認。 +# あります。 + +import nobunaga_bot + +class TestNobunagaBot(unittest.TestCase): + + @patch('nobunaga_bot.wikipedia') + def test_get_nobunaga_info_success(self, mock_wikipedia): + # Wikipediaのページ取得成功時のテスト + mock_page = MagicMock() + mock_page.content = "織田信長の情報です。" + mock_wikipedia.page.return_value = mock_page + + content = nobunaga_bot.get_nobunaga_info() + self.assertEqual(content, "織田信長の情報です。") + mock_wikipedia.set_lang.assert_called_with("ja") + mock_wikipedia.page.assert_called_with("織田信長") + + @patch('nobunaga_bot.wikipedia') + def test_get_nobunaga_info_failure(self, mock_wikipedia): + # Wikipediaのページ取得失敗時のテスト + mock_wikipedia.exceptions.PageError = Exception + mock_wikipedia.page.side_effect = Exception("Page not found") + + content = nobunaga_bot.get_nobunaga_info() + self.assertIsNone(content) + + @patch.dict(os.environ, {}, clear=True) + @patch('builtins.print') + def test_main_no_api_key(self, mock_print): + # APIキーがない場合のテスト + nobunaga_bot.main() + mock_print.assert_any_call("エラー: 環境変数 'GOOGLE_API_KEY' が設定されていません。") + + @patch.dict(os.environ, {"GOOGLE_API_KEY": "dummy_key"}) + @patch('nobunaga_bot.genai') + @patch('nobunaga_bot.get_nobunaga_info') + @patch('builtins.input', side_effect=['こんにちは', 'exit']) + @patch('builtins.print') + def test_main_flow(self, mock_print, mock_input, mock_get_info, mock_genai): + # 正常なフローのテスト + mock_get_info.return_value = "信長のWikipedia情報" + + mock_model = MagicMock() + mock_chat = MagicMock() + mock_model.start_chat.return_value = mock_chat + mock_genai.GenerativeModel.return_value = mock_model + + mock_response = MagicMock() + mock_response.text = "うむ、こんにちはじゃ。" + mock_chat.send_message.return_value = mock_response + + nobunaga_bot.main() + + # 検証 + mock_genai.configure.assert_called_with(api_key="dummy_key") + mock_genai.GenerativeModel.assert_called_with('gemini-pro') + mock_model.start_chat.assert_called() + + # システムプロンプトが含まれているか確認 + args, kwargs = mock_model.start_chat.call_args + history = kwargs.get('history', []) + self.assertTrue(any("信長のWikipedia情報" in part for msg in history for part in msg['parts'] if msg['role'] == 'user')) + + # メッセージ送信の確認 + mock_chat.send_message.assert_called_with('こんにちは') + +if __name__ == '__main__': + unittest.main()