diff --git a/solution.py b/solution.py new file mode 100644 index 0000000..22a2e6b --- /dev/null +++ b/solution.py @@ -0,0 +1,114 @@ +<|channel|>final solution.py<|message|>import sys +from typing import Optional + +# Библиотеки, которые должны быть установлены в окружении задачи. +# Мы используем langchain для взаимодействия с LLM и rich для красивого вывода. +try: + from langchain.chat_models import ChatOpenAI +except ImportError as exc: # pragma: no cover + raise RuntimeError( + "Не удалось импортировать langchain. Убедитесь, что пакет установлен." + ) from exc + +try: + from rich.console import Console + from rich.prompt import Prompt + from rich.text import Text +except ImportError as exc: # pragma: no cover + raise RuntimeError( + "Не удалось импортировать rich. Убедитесь, что пакет установлен." + ) from exc + + +class TextAdventureGame: + """ + Простая текстовая игра на основе LLM. + Игрок вводит команды, которые отправляются в модель, + а ответ модели выводится пользователю. + """ + + def __init__(self, model_name: str = "gpt-3.5-turbo", temperature: float = 0.7): + """ + Инициализация игрового процесса. + + :param model_name: имя LLM из OpenAI + :param temperature: степень случайности генерации текста + """ + self.console = Console() + # Создаём объект модели. Для работы требуется переменная окружения OPENAI_API_KEY. + try: + self.llm = ChatOpenAI(model=model_name, temperature=temperature) + except Exception as exc: # pragma: no cover + raise RuntimeError( + "Не удалось создать экземпляр LLM. Проверьте ключ OpenAI." + ) from exc + + def _display_welcome(self) -> None: + """Показываем приветственное сообщение.""" + welcome = Text("Добро пожаловать в текстовую игру!", style="bold green") + self.console.print(welcome) + + def _get_player_input(self, prompt: str = "Что вы делаете?") -> Optional[str]: + """ + Получаем ввод от игрока. Если пользователь введёт 'quit', + возвращаем None для завершения игры. + """ + try: + user_input = Prompt.ask(prompt) + except KeyboardInterrupt: # pragma: no cover + self.console.print("\n[red]Игра прервана пользователем.[/red]") + return None + + if user_input.strip().lower() in {"quit", "exit"}: + return None + return user_input + + def _generate_response(self, player_action: str) -> str: + """ + Отправляем действие игрока в LLM и получаем ответ. + + :param player_action: строка с командой игрока + :return: текст ответа модели + """ + # Формируем сообщение для чат‑модели. + messages = [ + {"role": "system", "content": "Вы — мудрый рассказчик, описывающий события в мире приключений."}, + {"role": "user", "content": player_action}, + ] + try: + response = self.llm.invoke(messages) + except Exception as exc: # pragma: no cover + raise RuntimeError("Ошибка при вызове LLM.") from exc + + return response.content.strip() + + def run(self) -> None: + """Основной цикл игры.""" + self._display_welcome() + while True: + action = self._get_player_input() + if action is None: # пользователь ввёл quit или прервал + self.console.print("[bold magenta]Спасибо за игру![/bold magenta]") + break + + self.console.print(f"[cyan]Вы сказали:[/cyan] {action}") + try: + result = self._generate_response(action) + except Exception as exc: # pragma: no cover + self.console.print(f"[red]Ошибка генерации ответа: {exc}[/red]") + continue + + self.console.print("\n[green]Ответ модели:[/green]\n") + self.console.print(result, style="italic") + self.console.print("-" * 40) + + +if __name__ == "__main__": + # Запускаем игру. Если переменная окружения OPENAI_API_KEY не задана, + # langchain выбросит исключение при создании модели. + game = TextAdventureGame() + try: + game.run() + except Exception as exc: # pragma: no cover + sys.stderr.write(f"Произошла ошибка: {exc}\n") + sys.exit(1) \ No newline at end of file