User Tools

Site Tools


использование_ai-агентов

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
использование_ai-агентов [2026/07/16 10:00]
val [Черновик]
использование_ai-агентов [2026/08/03 09:52] (current)
val [Черновик]
Line 4: Line 4:
  
   * Сегодня,​ наверное,​ нет пользователя сети Интернет,​ который бы не "​общался"​ (хотя бы через чат в поисковой системе) с большими языковыми моделями (LLM), или, как это часто называют,​ искусственным интеллектом (AI)    * Сегодня,​ наверное,​ нет пользователя сети Интернет,​ который бы не "​общался"​ (хотя бы через чат в поисковой системе) с большими языковыми моделями (LLM), или, как это часто называют,​ искусственным интеллектом (AI) 
-  * Невероятные результаты,​ которые они показывают в качестве справочных и систем обработки данных,​ вызывают желание ​"поручить" ​им самостоятельное решение каких либо задач+  * Невероятные результаты,​ которые они показывают в качестве справочных и систем обработки данных,​ вызывают желание ​найти им применение в решении каких либо ​повседневных ​задач
   * Для этого используются AI агенты,​ и если Вы пока не работали с ними, наш вебинар для Вас   * Для этого используются AI агенты,​ и если Вы пока не работали с ними, наш вебинар для Вас
  
Line 10: Line 10:
  
   * Написать своего первого AI агента на python   * Написать своего первого AI агента на python
-  * Реализовать MVP с использованием AI и Asterisk по такому ​описанию: ​перед походом в магазин,​ звоним (можно несколько раз) ​на некий ​номер ​и просим ​запомнить, что хотим купить, в магазине,​ время от времени,​ звоним ​на тот же номер, говорим,​ что купили и просим ​напомнить, что еще осталось +  * Реализовать MVP с использованием AI и Asterisk по описанию: ​Перед походом в магазин,​ звоним (можно несколько раз) ​ИИ агенту и просим ​добавить ​в список покупок ​товар, о котором "вспомнили". Затем, в магазине,​ время от времени, ​снова ​звоним агенту, говорим,​ что купили и просим перечислить, что еще осталось 
-  * При разработке, ​в свою очередь, активно использовать LLM+  * При разработке, ​так же, активно использовать LLM
  
 ===== Запись вебинара ===== ===== Запись вебинара =====
Line 23: Line 23:
 ===== Шаг 1. Что у нас есть, для начала ===== ===== Шаг 1. Что у нас есть, для начала =====
  
 +  * [[Голосовые сервисы помогут голосовой почте!]] (Занятие СЕМ 03.12.2019)
   * [[Язык программирования Python]]   * [[Язык программирования Python]]
 +
 +===== ToDo =====
 +
 +  * Если звоним в первый раз (контекста еще нет) - представиться (пересказать системный промпт?​)
 +  * Добавить возможность отправить список дел на почту
 +  * Добавить возможность агенту позвонить и напомнить о деле
  
 ===== Черновик ===== ===== Черновик =====
 +
 +<​code>​
 +Привет,​ я хочу купить яблоки,​ хлеб и колбасу
 +Еще надо позвонить петрову и иванову
 +Я купил яблоки и колбасу,​ что еще осталось?​
 +Петрову позвонил
 +Что надо сделать
 +</​code>​
 +<​code>​
 +~/ai$ cp ari_rec_ai.py ari_rec_ai_content.py
 +</​code>​
  
   * [[Сервис Asterisk#​Asterisk ARI]]   * [[Сервис Asterisk#​Asterisk ARI]]
 +  * [[Yandex SpeechKit]]
   * [[Yandex AI]]   * [[Yandex AI]]
 +
 +<​code>​
 +$ cat request_to_agent_with_tools.py
 +import sys
 +import os
 +import random
 +
 +from yandex_ai_studio_sdk import AIStudio
 +#from rich import inspect
 +
 +def request_to_agent_with_tools(user_request,​ system_prompt,​ context_messages=None,​ tools=None):​
 +
 +    sdk = AIStudio(
 +        folder_id=os.getenv("​YANDEX_FOLDER_ID"​),​
 +        auth=os.getenv("​YANDEX_API_KEY"​)
 +    )
 +
 +    sdk_tools = []
 +    for item in tools:
 +        tool = sdk.tools.function(
 +            parameters={"​type":​ "​object","​properties":​ {}},
 +            name=item["​func_name"​],​
 +            description=item["​description"​]
 +        )
 +        sdk_tools.append(tool)
 +
 +    messages = [{"​role":​ "​system",​ "​text":​ system_prompt}]
 +
 +    if context_messages:​
 +        messages.extend(context_messages)
 +
 +    messages.append({"​role":​ "​user",​ "​text":​ user_request})
 +
 +    #model = sdk.chat.completions("​yandexgpt"​)
 +    model = sdk.chat.completions("​aliceai-llm"​)
 +    model = model.configure(
 +        temperature=0.3,​
 +        max_tokens=1500,​
 +        tools=sdk_tools
 +    )
 +
 +    #​inspect(messages,​ methods=True)
 +    #​inspect(messages)
 +    response = model.run(messages)
 +    #​inspect(response,​ methods=True)
 +
 +    choice = response.choices[0]
 +    if choice.tool_calls:​
 +        return choice.tool_calls[0].function.name
 +    else:
 +        return choice.text
 +
 +if __name__ == "​__main__":​
 +
 +    system_prompt = (
 +        "Ты — ассистент.\n"​
 +        "​Отвечай кратко.\n"​
 +    )
 +
 +    chat_context = []
 +    #​chat_context = [
 +    #    {"​role":​ "​user",​ "​text":​ "​Привет! Меня зовут Алексей,​ я программист."​},​
 +    #    {"​role":​ "​assistant",​ "​text":​ "​Привет,​ Алексей! Рад знакомству. Чем могу помочь?"​}
 +    #]
 +
 +    # Определяем функцию для инструмента
 +    def get_random_number() -> int:
 +        return random.randint(1,​ 100)
 +    def get_random_fruit() -> str:
 +        words = ["​яблоко",​ "​банан",​ "​апельсин",​ "​груша",​ "​киви"​]
 +        return random.choice(words)
 +    #​print(get_random_number())
 +    #​print(get_random_fruit())
 +
 +    func_list = [
 +        {
 +            "​func_name":​ "​random_number",​
 +            "​description":​ "​Используй эту функцию,​ когда пользователь просит назвать его счастливое число."​
 +        },
 +        {
 +            "​func_name":​ "​random_fruit",​
 +            "​description":​ "​Используй эту функцию,​ когда пользователь просит назвать его любимый фрукт."​
 +        }
 +    ]
 +    ​
 +    while True:
 +        try:
 +            user_request = input("​Введите ваш вопрос:​ ")
 +        except (KeyboardInterrupt,​ EOFError):
 +            print("​\nСессия завершена!"​)
 +            sys.exit(0)
 +
 +        reply_text = request_to_agent_with_tools(user_request,​ system_prompt,​ chat_context,​ func_list)
 +        if '​random_number'​ in reply_text:
 +            print(get_random_number())
 +        elif '​random_fruit'​ in reply_text:
 +            print(get_random_fruit())
 +        else:
 +            print(reply_text)
 +            chat_context.append({"​role":​ "​user",​ "​text":​ user_request})
 +            chat_context.append({"​role":​ "​assistant",​ "​text":​ reply_text})
 +</​code>​
 +<​code>​
 +# cat ~asterisk/​ai/​ari_rec_ai_content.py
 +</​code><​code>​
 +import os
 +import json
 +import requests
 +import websocket
 +
 +ARI_USER = "​asterisk"​
 +ARI_PASS = "​asterisk"​
 +BASE_URL = "​http://​localhost:​8088/​ari"​
 +AUTH = (ARI_USER, ARI_PASS)
 +
 +REC_PATH = "/​var/​spool/​asterisk/​recording/"​
 +
 +SYSTEM_PROMPT = (
 +    "Ты — ассистент.\n"​
 +    "​Отвечай кратко.\n"​
 +    "Не спрашивай когда напомнить.\n"​
 +)
 +
 +COMPRESS_PROMPT = (
 +    "Ты — инструмент оптимизации контекста. Проанализируй историю диалога. "
 +    "​Удали выполненные дела, приветствия и воду. Сформулируй ОДНО лаконичное "
 +    "​сообщение от лица пользователя,​ в котором отражена только актуальная суть "
 +    "и невыполненные задачи/​оставшиеся вопросы на текущий момент."​
 +)
 +
 +# Структура:​ {channel_id:​ [{"​role":​ "​user",​ "​text":​ "​..."​},​ {"​role":​ "​assistant",​ "​text":​ "​..."​}]}
 +channel_contexts = {}
 +
 +channel_caller_numbers = {}
 +
 +def load_context(caller_number):​
 +    file_path = f"​{REC_PATH}{caller_number}.json"​
 +    if os.path.exists(file_path):​
 +        with open(file_path,​ '​r',​ encoding='​utf-8'​) as f:
 +            return json.load(f)
 +    return []
 +
 +def save_context(caller_number,​ context):
 +    file_path = f"​{REC_PATH}{caller_number}.json"​
 +    with open(file_path,​ '​w',​ encoding='​utf-8'​) as f:
 +        json.dump(context,​ f, ensure_ascii=False,​ indent=2)
 +
 +def get_caller_number(channel_id):​
 +    url = f"​{BASE_URL}/​channels/​{channel_id}"​
 +    response = requests.get(url,​ auth=AUTH, timeout=5)
 +    channel_info = response.json()
 +    caller_number = channel_info.get("​caller",​ {}).get("​number"​)
 +    return caller_number
 +
 +def get_channel_context(channel_id):​
 +    if channel_id not in channel_contexts:​
 +        channel_contexts[channel_id] = []
 +    return channel_contexts[channel_id]
 +
 +def update_channel_context(channel_id,​ user_request,​ agent_response):​
 +    context = get_channel_context(channel_id)
 +    context.append({"​role":​ "​user",​ "​text":​ user_request})
 +    context.append({"​role":​ "​assistant",​ "​text":​ agent_response})
 +    return context
 +
 +def detect_speech(channel_id):​
 +    print(f"​Запускаем TALK_DETECT для канала {channel_id}..."​)
 +    talk_url = f"​{BASE_URL}/​channels/​{channel_id}/​variable"​
 +
 +    # 1000 мс тишины для завершения фразы, 500 мс речи для начала
 +    talk_payload = {
 +        "​variable":​ "​TALK_DETECT(set)",​
 +        "​value":​ "​1000,​500"​
 +    }
 +
 +    response = requests.post(talk_url,​ json=talk_payload,​ auth=AUTH)
 +    print(f"​Статус-код ответа:​ {response.status_code}"​)
 +    print(f"​Текст ответа:​ {response.text}"​)
 +
 +def undetect_speech(channel_id):​
 +    print(f"​Отключаем TALK_DETECT для канала {channel_id}..."​)
 +    talk_url = f"​{BASE_URL}/​channels/​{channel_id}/​variable"​
 +
 +    talk_payload = {
 +        "​variable":​ "​TALK_DETECT(set)",​
 +        "​value":​ "​remove"​
 +    }
 +
 +    response = requests.post(talk_url,​ json=talk_payload,​ auth=AUTH)
 +    print(f"​Статус-код ответа:​ {response.status_code}"​)
 +    print(f"​Текст ответа:​ {response.text}"​)
 +
 +def play_beep(channel_id):​
 +    print(f"​Проигрываем сигнал перед записью в канал {channel_id}..."​)
 +    play_url = f"​{BASE_URL}/​channels/​{channel_id}/​play"​
 +    play_payload = {"​media":​ "​sound:​beep"​}
 +    requests.post(play_url,​ params=play_payload,​ auth=AUTH)
 +
 +def start_recording(channel_id):​
 +    print(f"​Включаем запись для канала {channel_id}"​)
 +    record_url = f"​{BASE_URL}/​channels/​{channel_id}/​record"​
 +    recording_name = f"​rec_{channel_id}"​
 +    record_payload = {
 +        "​name":​ recording_name,​
 +        "​format":​ "​wav",​
 +        "​ifExists":​ "​overwrite",​
 +        #"​maxDuration":​ 29,
 +        #"​terminateOn":​ "#",​
 +        #"​beep":​ True,
 +    }
 +    requests.post(record_url,​ params=record_payload,​ auth=AUTH)
 +
 +def stop_recording(channel_id):​
 +    print(f"​Останавливаем запись для канала {channel_id}"​)
 +    recording_name = f"​rec_{channel_id}"​
 +    stop_url = f"​{BASE_URL}/​recordings/​live/​{recording_name}/​stop"​
 +    requests.post(stop_url,​ auth=AUTH)
 +
 +def on_message(wsapp,​ message):
 +    event = json.loads(message)
 +    event_type = event.get("​type"​)
 +
 +    # Сценарий 1: Звонок поступил -> Отвечаем и воспроизводим beep
 +    if event_type == "​StasisStart":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Отвечаем на звонок {channel_id}"​)
 +        requests.post(f"​{BASE_URL}/​channels/​{channel_id}/​answer",​ auth=AUTH)
 +
 +        channel_caller_numbers[channel_id]=get_caller_number(channel_id)
 +        channel_contexts[channel_id] = load_context(channel_caller_numbers[channel_id])
 +        play_beep(channel_id)
 +
 +        #​start_recording(channel_id)
 +        # or
 +        #​detect_speech(channel_id)
 +
 +    # Сценарий N
 +    elif event_type == "​ChannelTalkingStarted":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Обнаружена речь в канале {channel_id}! Начинаем запись..."​)
 +        start_recording(channel_id)
 +
 +    # Сценарий N
 +    elif event_type == "​ChannelTalkingFinished":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Обнаружено молчание в канале {channel_id}"​)
 +        stop_recording(channel_id)
 +        undetect_speech(channel_id)
 +
 +    # Сценарий N: Запись завершилась -> Проигрываем её обратно
 +    elif event_type == "​RecordingFinished":​
 +        recording_data = event.get("​recording",​ {})
 +        recording_name = recording_data.get("​name"​)
 +        target_uri = recording_data.get("​target_uri",​ ""​)
 +        channel_id = target_uri.split("​channel:"​)[1]
 +
 +        import wav_and_ogg
 +        import speech_and_text
 +        wav_and_ogg.wav_to_ogg(f"​{REC_PATH}{recording_name}"​)
 +
 +        user_request = speech_and_text.speech_to_text(f"​{REC_PATH}{recording_name}.ogg"​)
 +        print(f"​user_request:​ {user_request}"​)
 +
 +        if user_request:​
 +            context = get_channel_context(channel_id)
 +
 +            import request_to_agent
 +            agent_response = request_to_agent.request_to_agent(user_request,​ SYSTEM_PROMPT,​ context)
 +            #​agent_response = user_request
 +
 +            print(agent_response)
 +
 +            update_channel_context(channel_id,​ user_request,​ agent_response)
 +
 +            print("​Текущий контекст:"​)
 +            from pprint import pprint
 +            pprint(channel_contexts[channel_id])
 +
 +            speech_and_text.text_to_speech(agent_response,​f"​{REC_PATH}{recording_name}.response.ogg"​)
 +            wav_and_ogg.ogg_to_wav(f"​{REC_PATH}{recording_name}.response"​)
 +
 +            print(f"​Запись {recording_name} готова! Проигрываем её обратно в канал {channel_id}..."​)
 +            play_url = f"​{BASE_URL}/​channels/​{channel_id}/​play"​
 +            play_payload = {
 +                #"​media":​ f"​recording:​{recording_name}"​
 +                "​media":​ f"​recording:​{recording_name}.response"​
 +            }
 +            response = requests.post(play_url,​ params=play_payload,​ auth=AUTH)
 +            print("​Ответ ARI на воспроизведение:",​ response.status_code)
 +
 +    # Сценарий N: Воспроизведение завершено (бип или записанный голос)
 +    elif event_type == "​PlaybackFinished":​
 +        playback_data = event.get("​playback",​ {})
 +        media_uri = playback_data.get("​media_uri",​ ""​)
 +        target_uri = playback_data.get('​target_uri',​ ''​)
 +        channel_id = target_uri.split("​channel:"​)[1]
 +
 +        #​start_recording(channel_id)
 +        # or
 +        #​detect_speech(channel_id)
 +
 +        if media_uri == "​sound:​beep":​
 +            print(f"​Сигнал завершен. Начинаем запись для канала {channel_id}..."​)
 +            detect_speech(channel_id)
 +
 +        elif "​recording:​rec_"​ in media_uri:
 +            print(f"​Воспроизведение записи завершено. Идем на новый круг для канала {channel_id}..."​)
 +            play_beep(channel_id)
 +
 +    # Сценарий N: Абонент повесил трубку -> Удаляем файл
 +    elif event_type == "​StasisEnd":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +
 +        #​print(f"​Звонок {channel_id} завершен абонентом. Удаляем файл записи..."​)
 +        #​recording_name = f"​rec_{channel_id}"​
 +        #delete_url = f"​{BASE_URL}/​recordings/​stored/​{recording_name}"​
 +        #​requests.delete(delete_url,​ auth=AUTH)
 +
 +        context = get_channel_context(channel_id)
 +        caller_number = channel_caller_numbers[channel_id]
 +        print(f"​Номер звонящего в StasisEnd: номер {caller_number} канал {channel_id}"​)
 +
 +        print(f"​Сжатие контекста"​)
 +
 +        import request_to_agent
 +        agent_response = request_to_agent.request_to_agent("​Сожми контекст",​ COMPRESS_PROMPT,​ context)
 +        print(agent_response)
 +        context = [{"​role":​ "​user",​ "​text":​ agent_response}]
 +
 +        print(f"​Сохранение контекста и удаление медиа файлов"​)
 +
 +        save_context(caller_number,​context)
 +
 +        import glob
 +        recording_name = f"​rec_{channel_id}"​
 +        file_mask = f"​{REC_PATH}{recording_name}*"​
 +        matched_files = glob.glob(file_mask)
 +        for file_path in matched_files:​
 +            os.remove(file_path)
 +            print(f"​Файл удален:​ {file_path}"​)
 +
 +def on_error(ws,​ error):
 +    print(f"​Произошла ошибка:​ {error}"​)
 +
 +wsapp = websocket.WebSocketApp(
 +    f"​ws://​localhost:​8088/​ari/​events?​api_key={ARI_USER}:​{ARI_PASS}&​app=my-first-app",​
 +    on_message=on_message,​
 +    on_error=on_error
 +)
 +wsapp.run_forever()
 +</​code>​
 +
использование_ai-агентов.1784185220.txt.gz · Last modified: 2026/07/16 10:00 by val