diff --git a/ouput_chat.py b/ouput_chat.py new file mode 100644 index 0000000..7438310 --- /dev/null +++ b/ouput_chat.py @@ -0,0 +1,277 @@ +""" +Модуль для генерации описаний маршрутов и ответов на вопросы пользователей. +Использует Gemini-2.5-flash API через requests библиотеку. +""" + +import json +import requests +from typing import List, Dict, Optional + + +class RouteDescriber: + """ + Класс для работы с описанием маршрутов и ответами на вопросы пользователей. + """ + + def __init__(self): + """ + Инициализация с API ключом Gemini. + + Args: + api_key: API ключ для доступа к Gemini API + """ + self.api_key = 'AIzaSyBXGBGH5NDY8L_jVmq2zb4i8xYEV2qN-48' + self.base_url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent" + self.conversation_history = [] + self.route_points = [] + self.route_description = "" + self.parser_chat_context = "" + + def _call_gemini(self, prompt: str, system_instruction: Optional[str] = None) -> str: + """ + Выполняет запрос к Gemini API. + + Args: + prompt: Текст промпта + system_instruction: Системная инструкция для модели + + Returns: + Ответ модели + """ + url = f"{self.base_url}?key={self.api_key}" + + headers = { + "Content-Type": "application/json" + } + + # Формируем тело запроса + contents = [] + + # Добавляем историю разговора + for msg in self.conversation_history: + contents.append(msg) + + # Добавляем текущий промпт + contents.append({ + "role": "user", + "parts": [{"text": prompt}] + }) + + payload = { + "contents": contents + } + + # Добавляем системную инструкцию если есть + if system_instruction: + payload["system_instruction"] = { + "parts": [{"text": system_instruction}] + } + + try: + response = requests.post(url, headers=headers, json=payload, timeout=30) + response.raise_for_status() + + result = response.json() + + # Извлекаем текст ответа + if "candidates" in result and len(result["candidates"]) > 0: + candidate = result["candidates"][0] + if "content" in candidate and "parts" in candidate["content"]: + answer = candidate["content"]["parts"][0]["text"] + + # Сохраняем в историю + self.conversation_history.append({ + "role": "user", + "parts": [{"text": prompt}] + }) + self.conversation_history.append({ + "role": "model", + "parts": [{"text": answer}] + }) + + return answer + + return "Ошибка при получении ответа" + + except requests.exceptions.RequestException as e: + return f"Ошибка запроса: {str(e)}" + + def generate_route_description(self, route_points: List[Dict], + parser_chat_history: Optional[List[Dict]] = None) -> str: + """ + Генерирует описание маршрута на основе точек. + + Args: + route_points: Список точек маршрута с полями 'name' и 'description' + parser_chat_history: История чата с парсером в формате [{"role": "user"/"model", "text": "..."}] + + Returns: + Описание маршрута (1-2 предложения на точку) + """ + self.route_points = route_points + self.conversation_history = [] # Очищаем историю для нового маршрута + + # Обрабатываем историю чата с парсером + if parser_chat_history: + parser_messages = [] + for msg in parser_chat_history: + role = msg.get('role', 'user') + text = msg.get('text', '') + parser_messages.append(f"{role.upper()}: {text}") + self.parser_chat_context = "\n".join(parser_messages) + else: + self.parser_chat_context = "" + + # Формируем промпт + points_info = [] + for i, point in enumerate(route_points, 1): + name = point.get('name', f'Точка {i}') + description = point.get('description', '') + points_info.append(f"{i}. {name}\nОписание: {description}") + + prompt = f"""Создай краткое описание маршрута из {len(route_points)} точек. +Для каждой точки напиши 1-2 предложения, которые заинтересуют посетителя. +Используй только информацию из описаний точек. + +Точки маршрута: +{chr(10).join(points_info)} + +Формат ответа: +1. [Название точки]: [1-2 предложения описания] +2. [Название точки]: [1-2 предложения описания] +...""" + + # Добавляем контекст парсера если есть + if self.parser_chat_context: + prompt = f"""История запроса пользователя: +{self.parser_chat_context} + +{prompt}""" + + system_instruction = """Ты — помощник для описания туристических маршрутов. +Твоя задача — создавать краткие, привлекательные описания точек маршрута (1-2 предложения на точку). +Используй только информацию, представленную в описаниях точек. +Пиши на русском языке.""" + + description = self._call_gemini(prompt, system_instruction) + self.route_description = description + return description + + def answer_question(self, question: str) -> str: + """ + Отвечает на вопрос пользователя о точках маршрута. + Видит всю историю вопросов и ответов, а также описание маршрута. + + Args: + question: Вопрос пользователя + + Returns: + Ответ на основе информации из описаний точек + """ + # Формируем контекст с информацией о точках + points_context = [] + for i, point in enumerate(self.route_points, 1): + name = point.get('name', f'Точка {i}') + description = point.get('description', 'Нет описания') + points_context.append(f"{i}. {name}: {description}") + + context = "\n\n".join(points_context) + + # Добавляем информацию о парсере если есть + parser_info = "" + if self.parser_chat_context: + parser_info = f""" + +ИСТОРИЯ ЗАПРОСА ПОЛЬЗОВАТЕЛЯ (для контекста): +{self.parser_chat_context} +""" + + # Добавляем описание маршрута если оно было сгенерировано + route_desc_info = "" + if self.route_description: + route_desc_info = f""" + +ОПИСАНИЕ МАРШРУТА, КОТОРОЕ БЫЛО ПОКАЗАНО ПОЛЬЗОВАТЕЛЮ: +{self.route_description} +""" + + system_instruction = f"""Ты — помощник по туристическому маршруту. +{parser_info}{route_desc_info} + +ДОСТУПНАЯ ИНФОРМАЦИЯ О ТОЧКАХ МАРШРУТА: +{context} + +СТРОГИЕ ПРАВИЛА: +1. Отвечай ТОЛЬКО на вопросы о точках маршрута +2. Используй ТОЛЬКО информацию из описаний точек выше +3. Если в описаниях нет информации для ответа на вопрос, вежливо уходи от ответа фразами вида "Вы узнаете ответ, когда посетите это место", адаптируй под контекст. +4. НЕ давай рекомендаций и советов +5. НЕ отвечай на вопросы, не связанные с маршрутом. В таких случаях отвечай: "Я могу ответить только на вопросы о точках вашего маршрута" +6. НЕ придумывай информацию, которой нет в описаниях +7. Отвечай кратко и по существу на русском языке +8. Ты видишь всю историю разговора, поэтому можешь отвечать на уточняющие вопросы""" + + answer = self._call_gemini(question, system_instruction) + return answer + + def reset_conversation(self): + """Очищает историю разговора.""" + self.conversation_history = [] + self.route_points = [] + self.route_description = "" + self.parser_chat_context = "" + + +# Пример использования +if __name__ == "__main__": + # Инициализация (замените на ваш API ключ) + API_KEY = "YOUR_GEMINI_API_KEY" + describer = RouteDescriber(API_KEY) + + # Пример истории чата с парсером + parser_chat = [ + {"role": "user", "text": "Хочу погулять по центру Москвы, интересует история и искусство"}, + {"role": "model", "text": "Понял, вы хотите прогулку по историческому центру с посещением музеев. Сколько времени у вас есть?"}, + {"role": "user", "text": "Часа 3-4"}, + {"role": "model", "text": "Отлично, я составил маршрут из 3 точек по центру Москвы с историческими местами и музеем искусства."} + ] + + # Пример данных маршрута + route_points = [ + { + "name": "Красная площадь", + "description": "Главная площадь Москвы, окружённая историческими зданиями. Здесь находятся Кремль, собор Василия Блаженного и Мавзолей Ленина. Площадь была основана в конце XV века." + }, + { + "name": "Парк Горького", + "description": "Один из самых известных парков Москвы, расположенный на берегу Москвы-реки. Здесь есть зоны для отдыха, спортивные площадки, прокат велосипедов. Парк был открыт в 1928 году." + }, + { + "name": "Третьяковская галерея", + "description": "Художественный музей с крупнейшей коллекцией русского искусства. В экспозиции представлены работы от древнерусских икон до картин XX века. Основана купцом Павлом Третьяковым." + } + ] + + # Генерация описания маршрута с историей парсера + print("=== ОПИСАНИЕ МАРШРУТА ===") + description = describer.generate_route_description(route_points, parser_chat) + print(description) + print() + + # Примеры вопросов (теперь с контекстом истории) + print("=== ДИАЛОГ С ПОЛЬЗОВАТЕЛЕМ ===") + + questions = [ + "Когда был основан Парк Горького?", + "А что там можно делать?", # Уточняющий вопрос о предыдущей теме + "Какие картины есть в Третьяковской галерее?", + "Сколько стоит билет?", # Без контекста, но система поймет что это про музей + "Расскажи про Кремль", + "Что посоветуешь взять с собой?", # Попытка получить рекомендацию + "Как погода сегодня?" # Нерелевантный вопрос + ] + + for q in questions: + print(f"\n👤 Пользователь: {q}") + answer = describer.answer_question(q) + print(f"🤖 Бот: {answer}")