feat: агент «Погода» для AMS (Open-Meteo + met.no)

This commit is contained in:
Maksim Totmin
2026-09-23 19:03:10 +07:00
commit 534ff9c5b0
9 changed files with 696 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
# AMS Weather Agent
Агент для **AMS (Agent Management System)**: по названию города возвращает короткую
сводку о текущей погоде. Понимает русские (кириллица) и латинские названия.
```
Пользователь: Казань
Агент: В Казани переменная облачность, температура 22,2 °C. Ветер 4,1 м/с,
осадков нет. Возьмите лёгкую ветровку.
```
## Возможности
- Кириллица и латиница: `Казань`, `Нижний Новгород`, `Санкт-Петербург`, `Kazan`.
- Данные о погоде: температура, влажность, ветер (скорость и направление), осадки.
- Ответ формулирует языковая модель, но **только из переданных фактов** — числа не выдумываются.
- **Graceful degradation**: если LLM недоступна, агент отдаёт детерминированную сводку.
- Понятные сообщения об ошибках вместо исключений (диалог в AMS не падает).
- Кэширование геокодинга и переиспользование HTTP-соединений.
- API-ключи не нужны: оба внешних сервиса бесплатные.
## Источники данных
| Этап | Сервис | Ключ |
|---|---|---|
| Город → координаты | [Open-Meteo Geocoding](https://open-meteo.com/en/docs/geocoding-api) | не нужен |
| Координаты → погода | [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` уже в окружении сервиса).
- Доступ к `geocoding-api.open-meteo.com` и `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}`.
### Параметры
| Параметр | Тип | Описание |
|---|---|---|
| `city` | string | Название города (кириллица или латиница). |
## Поведение при ошибках
| Ситуация | Ответ |
|---|---|
| Город не найден | «Не удалось получить погоду: город «…» не найден.» |
| Пустой город | «Не удалось получить погоду: не указан город.» |
| Внешний сервис недоступен | «Не удалось получить погоду: внешний сервис погоды недоступен.» |
| LLM недоступна | Детерминированная сводка из полученных данных |
## Тесты
```bash
python -m venv .venv && . .venv/bin/activate
pip install pytest requests
pytest
```
Тесты не обращаются к сети: HTTP-слой подменяется (`monkeypatch`).
## Лицензия
Не задана (все права сохранены).