Add new file

This commit is contained in:
Markov Andrey
2026-06-30 22:51:49 +00:00
parent 95165b7f37
commit 47ebf6842f

140
rag/functions/tools.py Normal file
View File

@@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
"""
Реальные обработчики инструментов для ReAct-цикла.
Каждый обработчик — асинхронная функция, принимающая параметры и возвращающая строку с результатом.
Используются внешние API (поиск, погода и т.д.).
"""
import json
import logging
from typing import Dict, Any, Optional
import aiohttp
import asyncio
from rag.config_models import AppConfig
logger = logging.getLogger(__name__)
class ToolHandler:
"""
Класс-контейнер для обработчиков инструментов.
Инициализируется с конфигом (для доступа к API-ключам) и HTTP-сессией.
"""
def __init__(self, config: AppConfig):
self.config = config
self.session: Optional[aiohttp.ClientSession] = None
async def _get_session(self) -> aiohttp.ClientSession:
if self.session is None:
self.session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=30))
return self.session
async def close(self):
if self.session:
await self.session.close()
self.session = None
# ------------------------------------------------------------------
# 1. Поиск в интернете (используем DuckDuckGo Instant Answer API)
# ------------------------------------------------------------------
async def search(self, query: str) -> str:
"""
Выполняет поиск в интернете через DuckDuckGo Instant Answer API.
Возвращает краткую выжимку или ссылки.
"""
if not query or not query.strip():
return "❌ Пустой поисковый запрос."
url = "https://api.duckduckgo.com/"
params = {
"q": query,
"format": "json",
"no_html": 1,
"skip_disambig": 1
}
try:
session = await self._get_session()
async with session.get(url, params=params) as resp:
if resp.status != 200:
return f"❌ Ошибка поиска: HTTP {resp.status}"
data = await resp.json()
# Извлекаем релевантную информацию
results = []
if data.get("AbstractText"):
results.append(f"📌 {data['AbstractText']}")
if data.get("RelatedTopics"):
for topic in data["RelatedTopics"][:3]:
if "Text" in topic:
results.append(f"{topic['Text']}")
if not results:
return "😕 Ничего не найдено по вашему запросу."
return "\n".join(results)
except asyncio.TimeoutError:
return "⏱️ Таймаут при выполнении поиска."
except Exception as e:
logger.exception("Ошибка в search")
return f"❌ Ошибка поиска: {str(e)}"
# ------------------------------------------------------------------
# 2. Погода (OpenWeatherMap)
# ------------------------------------------------------------------
async def get_weather(self, city: str) -> str:
"""
Получает текущую погоду для города через OpenWeatherMap API.
Требует API-ключ в конфиге: config.weather_api_key
"""
api_key = getattr(self.config, 'weather_api_key', None)
if not api_key:
return "❌ API-ключ для погоды не настроен (weather_api_key)."
if not city or not city.strip():
return "❌ Город не указан."
url = "https://api.openweathermap.org/data/2.5/weather"
params = {
"q": city,
"appid": api_key,
"units": "metric",
"lang": "ru"
}
try:
session = await self._get_session()
async with session.get(url, params=params) as resp:
if resp.status != 200:
return f"❌ Ошибка погоды: HTTP {resp.status}"
data = await resp.json()
if data.get("cod") != 200:
return f"{data.get('message', 'Неизвестная ошибка')}"
weather = data["weather"][0]["description"]
temp = data["main"]["temp"]
feels_like = data["main"]["feels_like"]
return f"🌤️ Погода в {city}: {weather}, температура {temp:.1f}°C (ощущается {feels_like:.1f}°C)"
except asyncio.TimeoutError:
return "⏱️ Таймаут при запросе погоды."
except Exception as e:
logger.exception("Ошибка в get_weather")
return f"❌ Ошибка получения погоды: {str(e)}"
# ------------------------------------------------------------------
# 3. Бронирование авиабилетов (заглушка — демонстрация)
# ------------------------------------------------------------------
async def book_flight(self, origin: str, destination: str, date: str) -> str:
"""
Заглушка для бронирования авиабилетов.
В реальности здесь был бы вызов API авиакомпании или агрегатора.
"""
return (f"✈️ Заглушка бронирования: рейс {origin}{destination} на {date}.\n"
"В реальной системе здесь был бы вызов API бронирования.")
# ------------------------------------------------------------------
# 4. Получение курса валют (заглушка)
# ------------------------------------------------------------------
async def get_exchange_rate(self, from_currency: str, to_currency: str) -> str:
"""
Заглушка для получения курса валют.
Можно использовать exchangerate-api.com или аналоги.
"""
return (f"💱 Заглушка курса: 1 {from_currency} = 1.0 {to_currency} (пример)\n"
"В реальности здесь был бы вызов API курсов валют.")