Add new file
This commit is contained in:
140
rag/functions/tools.py
Normal file
140
rag/functions/tools.py
Normal 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 курсов валют.")
|
||||||
Reference in New Issue
Block a user