====== Использование AI-агентов ======
===== Реклама =====
* Использование AI-агентов чаще всего упоминается в связи с автоматизацией клиентского сервиса, анализом данных, маркетингом, продажами и разработкой программного обеспечения. Но на нашем вебинаре мы увидим, что и в инженерных IT-задачах им можно найти полезное применение. Последовательно научимся использовать возможности LLM: от подсказок в чате до полной автоматизации развертывания, управления и тестирования систем.
===== Техническое задание =====
* Использовать AI-агентов для анализа и управления конфигурацией Linux-систем
* Интегрировать AI-агентов в GitLab CI/CD
* Написать своего первого AI-агента на Python
* Реализовать MVP с доступом к голосовому AI-ассистенту через Asterisk
===== Запись вебинара =====
* Рутуб: https://rutube.ru/video/private/d1d43ee640c9ec4166f75fce562b384c/
* Вк: https://vkvideo.ru/video-2190892_456239505
* Ютуб: https://youtu.be/ULv9aKzdc20
* Тэги: Yandex AI, LLM, AI Agent, Harness, VSCode, Cline AI, OpenCode AI, AI-driven DevOps,
===== С чего начиналось =====
* [[Голосовые сервисы помогут голосовой почте!]] (Занятие СЕМ 03.12.2019)
===== Сценарий 1: Вы немного программист, пишем свой ИИ чат =====
* [[https://www.specialist.ru/course/python1-a|Программирование на Python. Уровень 1. Базовый курс]]
* [[Язык программирования Python#Виртуальная среда Python]]
* [[Yandex AI]]
* **Промпт**: самый простой пример чат бота на python [[#simple_chat.py]] с использованием yandex ai
* **Промпт**: Привет, меня зовут Вчеслав, я инженер
* **Промпт**: Как меня зовут?
* Python приложение/модуль [[Язык программирования Python#request_to_llm.py]] (обсудить role system/user/assistant, context)
* Использование OpenAI API [[Язык программирования Python#openai_request_to_llm.py]]
* **Промпт**: Как называется твоя модель?
===== Сценарий 2: Вы специалист по Asterisk, добавляем интеграцию с ИИ чатом =====
* [[https://www.specialist.ru/course/asterisk3|Asterisk. Уровень 3. Интеграция с внешними системами]]
* [[Asterisk. Интеграция#Упражнение 6.4. Asterisk REST интерфейс]] до ARI Stasis
# curl http://localhost:8088/ari/asterisk/ping?api_key=asterisk:asterisk
{"ping":"pong" ...
* **Промпт**: "Asterisk ARI самый простой пример на Python 3.12 приложения Stasis(my-first-app), выводит на экран нажатые цифры, параметры подключения аналогичны curl ..." [[#ari_print.py]]
* **Промпт**: "Asterisk 20, ARI самый простой пример на Python 3.12 приложения Stasis(my-first-app), повторяет фразы пользователя, параметры подключения аналогичны curl ..." [[#ari_echo.py]]
* Python приложение [[Язык программирования Python#ari_echo_talk_detect.py]]
* [[Yandex SpeechKit]] (обсудить)
* Python модуль [[Язык программирования Python#speech_and_text.py]]
* Python модуль [[Язык программирования Python#wav_and_ogg.py]]
* **Итог**: Python приложение [[Язык программирования Python#ari_chat.py]] или [[Язык программирования Python#ari_chat_talk_detect.py]]
==== ToDo ====
=== Улучшение UX (User Experience) ===
- Если звоним в первый раз (контекста еще нет) - представиться (пересказать системный промпт?)
=== Добавление функциональности (Feature Management) ===
превращаем бота в агента ([[Язык программирования Python#agent_rand_hello.py]]):
- Добавить возможность отправить список дел на почту
- Добавить возможность агенту позвонить и напомнить о деле
===== Сценарий 3: Вы администратор систем Linux, используем ИИ агентов в работе =====
* [[https://www.specialist.ru/course/yun2-b|Linux. Уровень 2. Администрирование сетевых сервисов]]
* [[Администрирование сервисов и сетей Linux]]
* [[https://www.specialist.ru/course/git|Система управления версиями Git]]
* Установить [[Сервис Git#etckeeper]] на gate
gate# git -C /etc/ status
gate# git -C /etc/ log
* Проверить наличие, или установить [[Сервис DHCP]]
gate# git -C /etc/ diff
gate# git -C /etc/ add -v .
gate# git -C /etc/ commit -m "dhcp confgured"
* Импортируем в LAN Windows (LTSC) или Linux клиент
* Устанавливаем на него [[VSCode]] (отключаем Copilot)
* [[VSCode#Visual Studio Code Remote - SSH]] на gate
* [[Сервис Git]] (VSCode Open Folder)
* [[Continue]]
* **Промпт**: кратко формат файла resolv.conf
gate:/etc# code resolv.conf
code ex1.bash
#!/ ...
for i ...
* [[Cline]]
* **Plan Промпт**: какой диапазон адресов выдает dhcp сервер?
* **Act Промпт**: расширь используемый диапазон до 119-го адреса
* [[Сервис DHCP#Статистика DHCP сервера]]
* Cтатьи в разделе [[Нейросети]]
* [[OpenCode]](может понадобиться [[Переменные окружения]])
* **Plan Промпт**: какие сервисы установлены в этой системе? (Plan · Big Pickle OpenCode Zen)
* **Build Промпт**: установи squid, создай минимальный файл конфигурации, разреши доступ всем к сайтам из файла /etc/squid/permit_domains.txt, не создавай сертификаты, задай уточняющие вопросы
* Результат - шаг 2 из статьи [[https://habr.com/ru/articles/716454/|Самый простой пример CI/CD]]
* [[Утилита curl]]
===== Сценарий 4: Вы DevOps инженер, используем ИИ агентов в pipeline =====
* [[https://www.specialist.ru/course/kuber|DevOps. Уровень 1. Инфраструктура как код, основные инструменты]]
* [[DevOps1. Основные инструменты]]
* Выполняем шаги 4 и 5 из статьи [[https://habr.com/ru/articles/716454/|Самый простой пример CI/CD]]
* Запускаем [[OpenCode]] из под пользователя gitlab-runner на gate
* Может понадобиться
gate# apt install xxd
* Выполняем шаг 6 из статьи [[https://habr.com/ru/articles/716454/|Самый простой пример CI/CD]]
* Добавляем [[Инструмент GitLab#Пример CI/CD с использованием ИИ]]
===== Черновик =====
==== simple_chat.py ====
import os
import requests
# Вставьте сюда ваши данные
YANDEX_API_KEY = "..." # API-ключ сервисного аккаунта
FOLDER_ID = "b1gg4j6ru4esprat3uu7" # ID каталога в Yandex Cloud
URL = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion"
def get_ai_response(user_message: str) -> str:
headers = {
"Authorization": f"Api-Key {YANDEX_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"modelUri": f"gpt://{FOLDER_ID}/yandexgpt/latest",
"completionOptions": {
"stream": False,
"temperature": 0.7,
"maxTokens": "200"
},
"messages": [
{"role": "user", "text": user_message}
]
}
resp = requests.post(URL, json=payload, headers=headers, timeout=30)
resp.raise_for_status()
data = resp.json()
# Yandex GPT возвращает список вариантов; берём первый
return data["result"]["alternatives"][0]["message"]["text"]
if __name__ == "__main__":
print("Чат-бот с Yandex GPT (введите 'выход' для завершения)")
while True:
user_input = input("Вы: ")
if user_input.lower() in ("выход", "quit", "exit"):
break
try:
ai_reply = get_ai_response(user_input)
print("Бот:", ai_reply)
except Exception as e:
print("Ошибка:", e)
==== ari_print.py ====
# pip install aiohttp asyncio
import asyncio
import json
import aiohttp
# Параметры подключения (из вашего curl-примера)
ARI_HOST = "localhost"
ARI_PORT = 8088
ARI_USER = "asterisk"
ARI_PASS = "asterisk"
APP_NAME = "my-first-app"
WS_URL = f"ws://{ARI_HOST}:{ARI_PORT}/ari/events?app={APP_NAME}&api_key={ARI_USER}:{ARI_PASS}"
REST_URL = f"http://{ARI_HOST}:{ARI_PORT}/ari"
async def main():
async with aiohttp.ClientSession() as session:
# Проверяем, что Asterisk жив (как ваш curl-ping)
async with session.get(
f"{REST_URL}/asterisk/ping?api_key={ARI_USER}:{ARI_PASS}"
) as resp:
if resp.status != 200:
print(f"Не удалось подключиться к ARI: HTTP {resp.status}")
return
print("Asterisk ARI доступен, подключаемся к WebSocket…")
# Подписка на события приложения my-first-app
async with session.ws_connect(WS_URL) as ws:
print(f"WebSocket открыт. Приложение «{APP_NAME}» слушает события.")
print("Позвоните на экстеншен с Stasis(my-first-app) и нажимайте клавиши.\n")
async for msg in ws:
if msg.type != aiohttp.WSMsgType.TEXT:
continue
event = json.loads(msg.data)
etype = event.get("type")
if etype == "StasisStart":
channel = event["channel"]
print(f"[Звонок поступил] {channel['name']} (id: {channel['id']})")
elif etype == "ChannelDtmfReceived":
digit = event.get("digit", "?")
channel = event.get("channel", {})
name = channel.get("name", "?")
duration = event.get("duration_ms", 0)
print(f" Нажато: «{digit}» (канал: {name}, длительность: {duration} мс)")
elif etype == "StasisEnd":
channel = event["channel"]
print(f"[Звонок завершён] {channel['name']}")
# Можно uncomment для отладки — увидите все события:
# else:
# print(f" (событие: {etype})")
if __name__ == "__main__":
asyncio.run(main())
==== ari_echo.py ====
root#
mkdir -p /var/spool/asterisk/recording/
chown -R asterisk:asterisk /var/spool/asterisk/recording/
chmod -R 775 /var/spool/asterisk/recording/
pip install websocket-client requests
import json
import requests
import websocket
# --- Настройки подключения (аналогичны curl из вопроса) ---
ARI_URL = "http://localhost:8088/ari"
WS_URL = "ws://localhost:8088/ari/events?app=my-first-app&api_key=asterisk:asterisk"
AUTH = ("asterisk", "asterisk")
# Состояние каждого канала: "recording" или "playing"
channels: dict[str, str] = {}
def start_recording(cid: str):
"""Запустить запись речи звонящего (макс 5 сек, пауза 2 сек — стоп)."""
channels[cid] = "recording"
requests.post(f"{ARI_URL}/channels/{cid}/record", auth=AUTH, params={
"name": f"echo-{cid}",
"format": "wav",
"maxDurationSeconds": 5,
"maxSilenceSeconds": 2,
"beep": "true",
})
def on_message(ws, message):
event = json.loads(message)
etype = event.get("type")
if etype == "StasisStart":
cid = event["channel"]["id"]
print(f"→ Звонок от {event['channel']['name']}")
requests.post(f"{ARI_URL}/channels/{cid}/answer", auth=AUTH)
start_recording(cid)
elif etype == "RecordingFinished":
cid = event["recording"]["target_uri"].removeprefix("channel:")
if cid in channels:
print(" Запись готова, воспроизвожу…")
channels[cid] = "playing"
requests.post(f"{ARI_URL}/channels/{cid}/play", auth=AUTH, params={
"media": f"recording:echo-{cid}",
})
elif etype == "PlaybackFinished":
cid = event["playback"]["target_uri"].removeprefix("channel:")
if cid in channels and channels[cid] == "playing":
print(" Воспроизведение окончено, снова запись…")
start_recording(cid)
elif etype == "StasisEnd":
cid = event["channel"]["id"]
channels.pop(cid, None)
print(f"← Звонок завершён: {event['channel']['name']}")
if __name__ == "__main__":
print("Echo-приложение 'my-first-app' запущено. Ожидание звонков…")
ws = websocket.WebSocketApp(WS_URL, on_message=on_message)
ws.run_forever()