# AMS Weather Agent Агент для **AMS (Agent Management System)**: по названию города возвращает короткую сводку о текущей погоде. Понимает русские (кириллица) и латинские названия. ``` Пользователь: Казань Агент: В Казани переменная облачность, температура 22,2 °C. Ветер 4,1 м/с, осадков нет. Возьмите лёгкую ветровку. ``` ## Возможности - Кириллица и латиница: `Казань`, `Нижний Новгород`, `Санкт-Петербург`, `Kazan`. - Данные о погоде: температура, влажность, ветер (скорость и направление), осадки. - Ответ формулирует языковая модель, но **только из переданных фактов** — числа не выдумываются. - **Graceful degradation**: если LLM недоступна, агент отдаёт детерминированную сводку. - Понятные сообщения об ошибках вместо исключений (диалог в AMS не падает). - Кэширование геокодинга и переиспользование HTTP-соединений. - API-ключи не нужны: оба внешних сервиса бесплатные. ## Источники данных | Этап | Сервис | Ключ | |---|---|---| | Город → координаты | [Nominatim (OpenStreetMap)](https://nominatim.openstreetmap.org/) | не нужен | | Координаты → погода | [MET Norway Locationforecast](https://api.met.no/weatherapi/locationforecast/2.0/documentation) | не нужен | ## Структура ``` agents/custom/weather/ ├── config.json # метаданные агента для UI (name, description) ├── schema.py # Pydantic-модель Params (валидация входа в AMS) ├── handler.py # адаптер AMS: экспортирует генератор step(...) ├── system_prompt.txt # правила формулировки ответа для LLM └── weather.py # сервисный слой: HTTP, модели, маппинг погодных кодов tests/ └── test_weather.py # unit-тесты (без сети) ``` Разделение ответственности: `weather.py` ничего не знает про AMS и тестируется отдельно; `handler.py` — тонкий слой интеграции. ## Требования - AMS 7.005+ (Python 3.12, `requests` и `pydantic-ai` уже в окружении сервиса). - Доступ к `nominatim.openstreetmap.org` и `api.met.no` с хоста AMS. ## Установка в AMS Скопировать каталог агента и выдать права пользователю сервиса: ```bash sudo cp -r agents/custom/weather /opt/ams/agents/custom/weather sudo chown -R lexicom:lexicom /opt/ams/agents/custom/weather ``` Loader AMS подхватит изменения примерно через 10 секунд. Проверка: ```bash curl -s http://127.0.0.1:6165/ams/agent/list | jq '.agents.custom' ``` Альтернатива — импорт через API AMS (zip-архив агента). ## Использование (API AMS) `POST /ams/start/{type}/{agent_id}` с телом в формате JSON. Синхронный стрим удобнее для клиентов, которые хотят получить ответ в одном запросе (`async_mode: "no"`): ```bash curl -sN http://127.0.0.1:6165/ams/start/custom/weather \ -H 'Content-Type: application/json' \ -H 'Accept: text/event-stream' \ -d '{"async_mode":"no","params":{"city":"Казань"}}' ``` Ответ — поток JSON-строк (NDJSON): ```json {"type": "chunk", "data": "В Казани ", "chat_id": "..."} {"type": "chunk", "data": "переменная облачность...", "chat_id": "..."} {"type": "end", "data": "message", "chat_id": "..."} ``` Асинхронный режим (`async_mode: "yes"`) возвращает `chat_id`; далее читать `GET /ams/stream/{chat_id}`, продолжать диалог `POST /ams/ask/{chat_id}` и завершать `POST /ams/stop/{chat_id}`. ### Публичный доступ (пример развёртывания) AMS отдаётся наружу через reverse-proxy Apache на `git.totmin.ru`: - Swagger — - Документация — - API — `https://git.totmin.ru/weather/ams/...` Префикс `/weather` срезается прокси, а uvicorn запущен с `UVICORN_ROOT_PATH=/weather`, поэтому ссылки в Swagger формируются корректно. ### Параметры | Параметр | Тип | Описание | |---|---|---| | `city` | string | Название города (кириллица или латиница). | ## Поведение при ошибках | Ситуация | Ответ | |---|---| | Город не найден | «Не удалось получить погоду: город «…» не найден.» | | Пустой город | «Не удалось получить погоду: не указан город.» | | Внешний сервис недоступен | «Не удалось получить погоду: внешний сервис погоды недоступен.» | | LLM недоступна | Детерминированная сводка из полученных данных | ## Тесты ```bash python -m venv .venv && . .venv/bin/activate pip install pytest requests pytest ``` Тесты не обращаются к сети: HTTP-слой подменяется (`monkeypatch`). ## Лицензия Не задана (все права сохранены).