131 lines
6.0 KiB
Markdown
131 lines
6.0 KiB
Markdown
# 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 — <https://git.totmin.ru/weather/swagger>
|
||
- Документация — <https://git.totmin.ru/weather/help/>
|
||
- 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`).
|
||
|
||
## Лицензия
|
||
|
||
Не задана (все права сохранены).
|