Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__pycache__/
*.pyc
.pytest_cache/
.env
49 changes: 49 additions & 0 deletions README_NOBUNAGA.md
Original file line number Diff line number Diff line change
@@ -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の使用量によっては課金が発生する場合がありますのでご注意ください。
85 changes: 85 additions & 0 deletions nobunaga_bot.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
wikipedia
google-generativeai
76 changes: 76 additions & 0 deletions test_nobunaga_bot.py
Original file line number Diff line number Diff line change
@@ -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()