Загрузить файлы в «/»
This commit is contained in:
277
ouput_chat.py
Normal file
277
ouput_chat.py
Normal file
@@ -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}")
|
||||||
Reference in New Issue
Block a user