User Tools

Site Tools


язык_программирования_python

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
язык_программирования_python [2026/07/12 06:15]
val [ari_rec.py]
язык_программирования_python [2026/07/17 06:25] (current)
val [ari_chat.py]
Line 1: Line 1:
 ====== Язык программирования Python ====== ====== Язык программирования Python ======
 +
 +  * [[Все что должен знать DevOps про Python]]
  
   * [[https://​habr.com/​ru/​articles/​277679/​|Пишем shell скрипты на Python и можно ли заменить им Bash]]   * [[https://​habr.com/​ru/​articles/​277679/​|Пишем shell скрипты на Python и можно ли заменить им Bash]]
Line 400: Line 402:
  
 ==== wav_and_ogg.py ==== ==== wav_and_ogg.py ====
 +
 +  * [[Перекодировка звука#​Перекодировка в ogg формат]]
 +
 <​code>​ <​code>​
 +$ pip install pydub pip #​audioop-lts
 +</​code><​code>​
 import sys import sys
 from pydub import AudioSegment from pydub import AudioSegment
Line 422: Line 429:
 #    wav_to_ogg(target_file) #    wav_to_ogg(target_file)
 #    ogg_to_wav(target_file) #    ogg_to_wav(target_file)
 +</​code><​code>​
 +$ python wav_and_ogg.py speech
 </​code>​ </​code>​
  
 ==== speech_and_text.py ==== ==== speech_and_text.py ====
 +
 +  * [[Yandex AI]]
 +
 <​code>​ <​code>​
 import os import os
Line 430: Line 442:
 import requests import requests
  
-API_KEY = os.getenv("​API_KEY") +API_KEY = os.getenv("​YANDEX_API_KEY") 
-FOLDER_ID = os.getenv("​FOLDER_ID")+FOLDER_ID = os.getenv("​YANDEX_FOLDER_ID")
  
 def speech_to_text(file_path):​ def speech_to_text(file_path):​
Line 495: Line 507:
  
     sdk = AIStudio(     sdk = AIStudio(
-        folder_id=os.getenv("​FOLDER_ID"), +        folder_id=os.getenv("​YANDEX_FOLDER_ID"), 
-        auth=os.getenv("​API_KEY")+        auth=os.getenv("​YANDEX_API_KEY")
     )     )
     messages = [{"​role":​ "​system",​ "​text":​ system_prompt}]     messages = [{"​role":​ "​system",​ "​text":​ system_prompt}]
Line 542: Line 554:
 ==== ari_rec.py ==== ==== ari_rec.py ====
 <​code>​ <​code>​
 +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
 +</​code><​code>​
 import json import json
 import requests import requests
Line 551: Line 570:
 AUTH = (ARI_USER, ARI_PASS) AUTH = (ARI_USER, ARI_PASS)
  
-def play_beep(channel_id):​ +def detect_speech(channel_id):​ 
-    print(f"​Проигрываем сигнал перед записью в канал {channel_id}..."​) +    print(f"​Запускаем TALK_DETECT для канала {channel_id}..."​) 
-    ​play_url ​= f"​{BASE_URL}/​channels/​{channel_id}/​play+    talk_url = f"​{BASE_URL}/​channels/​{channel_id}/​variable"​ 
-    ​play_payload ​= {"media": "sound:beep"} + 
-    requests.post(play_urlparams=play_payload, auth=AUTH)+    # 2000 мс тишины для завершения фразы, 500 мс речи для начала 
 +    talk_payload = { 
 +        "​variable":​ "​TALK_DETECT(set)",​ 
 +        "​value":​ "​2000,​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_urljson=talk_payload, auth=AUTH
 +    print(f"​Статус-код ответа:​ {response.status_code}"​) 
 +    print(f"​Текст ответа:​ {response.text}"​)
  
 def start_recording(channel_id):​ def start_recording(channel_id):​
-    print(f"​Включаем запись для канала {channel_id} ​на 5 секунд...")+    print(f"​Включаем запись для канала {channel_id}"​)
     record_url = f"​{BASE_URL}/​channels/​{channel_id}/​record"​     record_url = f"​{BASE_URL}/​channels/​{channel_id}/​record"​
     recording_name = f"​rec_{channel_id}"​     recording_name = f"​rec_{channel_id}"​
Line 564: Line 604:
         "​name":​ recording_name,​         "​name":​ recording_name,​
         "​format":​ "​wav",​         "​format":​ "​wav",​
-        "​maxDuration": ​5+        ​"​ifExists":​ "​overwrite",​ 
-        "​terminateOn":​ "#",​ +        #"​maxDuration": ​29
-        "ifExists": ​"​overwrite"​+        ​#"​terminateOn":​ "#",​ 
 +        ​#"beep": ​True,
     }     }
     requests.post(record_url,​ params=record_payload,​ auth=AUTH)     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): def on_message(wsapp,​ message):
Line 577: Line 624:
     if event_type == "​StasisStart":​     if event_type == "​StasisStart":​
         channel_id = event.get("​channel",​ {}).get("​id"​)         channel_id = event.get("​channel",​ {}).get("​id"​)
- 
         print(f"​Отвечаем на звонок {channel_id}"​)         print(f"​Отвечаем на звонок {channel_id}"​)
         requests.post(f"​{BASE_URL}/​channels/​{channel_id}/​answer",​ auth=AUTH)         requests.post(f"​{BASE_URL}/​channels/​{channel_id}/​answer",​ auth=AUTH)
  
-        ​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)
  
-    # Сценарий ​2: Запись завершилась -> Проигрываем её обратно+    # Сценарий ​N: Запись завершилась -> Проигрываем её обратно
     elif event_type == "​RecordingFinished":​     elif event_type == "​RecordingFinished":​
         recording_data = event.get("​recording",​ {})         recording_data = event.get("​recording",​ {})
Line 598: Line 659:
         print("​Ответ ARI на воспроизведение:",​ response.status_code)         print("​Ответ ARI на воспроизведение:",​ response.status_code)
  
-    # Сценарий ​3: Воспроизведение завершено (бип или записанный голос)+    # Сценарий ​N: Воспроизведение завершено (бип или записанный голос)
     elif event_type == "​PlaybackFinished":​     elif event_type == "​PlaybackFinished":​
         playback_data = event.get("​playback",​ {})         playback_data = event.get("​playback",​ {})
Line 605: Line 666:
         channel_id = target_uri.split("​channel:"​)[1]         channel_id = target_uri.split("​channel:"​)[1]
  
-        ​if media_uri == "​sound:​beep":​ +        ​#start_recording(channel_id) 
-            print(f"​Сигнал завершен. Начинаем запись для канала {channel_id}..."​) +        # or 
-            ​start_recording(channel_id) +        ​detect_speech(channel_id)
- +
-        # Вариант Б: Закончилось воспроизведение старой записи -> Идем на новый круг (снова бип) +
-        ​elif "​recording:​rec_"​ in media_uri:​ +
-            print(f"​Воспроизведение записи завершено. Идем на новый круг для канала {channel_id}..."​) +
-            play_beep(channel_id)+
  
-    # Сценарий ​4: Абонент повесил трубку -> Удаляем файл+    # Сценарий ​N: Абонент повесил трубку -> Удаляем файл
     elif event_type == "​StasisEnd":​     elif event_type == "​StasisEnd":​
         channel_id = event.get("​channel",​ {}).get("​id"​)         channel_id = event.get("​channel",​ {}).get("​id"​)
Line 649: Line 705:
 SYSTEM_PROMPT = ( SYSTEM_PROMPT = (
     "Ты — ассистент.\n"​     "Ты — ассистент.\n"​
-    "​Отвечай краткопроси продолжить ​диалог.\n"+    "​Отвечай кратко.\n" 
 +    "Не спрашивай когда напомнить.\n"​
 ) )
  
 +COMPRESS_PROMPT = (
 +    "Ты — инструмент оптимизации контекста. Проанализируй историю диалога. "
 +    "​Удали выполненные дела, приветствия и воду. Сформулируй ОДНО лаконичное "
 +    "​сообщение от лица пользователя,​ в котором отражена только актуальная суть "
 +    "и невыполненные задачи/​оставшиеся вопросы на текущий момент."​
 +)
  
 # Структура:​ {channel_id:​ [{"​role":​ "​user",​ "​text":​ "​..."​},​ {"​role":​ "​assistant",​ "​text":​ "​..."​}]} # Структура:​ {channel_id:​ [{"​role":​ "​user",​ "​text":​ "​..."​},​ {"​role":​ "​assistant",​ "​text":​ "​..."​}]}
 channel_contexts = {} channel_contexts = {}
 +
 +channel_caller_numbers = {}
  
 def get_channel_context(channel_id):​ def get_channel_context(channel_id):​
Line 666: Line 731:
     context.append({"​role":​ "​assistant",​ "​text":​ agent_response})     context.append({"​role":​ "​assistant",​ "​text":​ agent_response})
     return context     return context
 +
 +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 start_recording(channel_id):​ def start_recording(channel_id):​
-    print(f"​Включаем запись для канала {channel_id} на секунд..."​)+    print(f"​Включаем запись для канала {channel_id} на 29 секунд..."​)
     record_url = f"​{BASE_URL}/​channels/​{channel_id}/​record"​     record_url = f"​{BASE_URL}/​channels/​{channel_id}/​record"​
     recording_name = f"​rec_{channel_id}"​     recording_name = f"​rec_{channel_id}"​
Line 674: Line 751:
         "​name":​ recording_name,​         "​name":​ recording_name,​
         "​format":​ "​wav",​         "​format":​ "​wav",​
-        "​maxDuration": ​5,+        ​"​ifExists":​ "​overwrite",​ 
 +        ​"​maxDuration": ​29,
         "​terminateOn":​ "#",​         "​terminateOn":​ "#",​
-        "ifExists": ​"​overwrite"​+        "beep": ​True,
     }     }
     requests.post(record_url,​ params=record_payload,​ auth=AUTH)     requests.post(record_url,​ params=record_payload,​ auth=AUTH)
 +
 +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 on_message(wsapp,​ message): def on_message(wsapp,​ message):
Line 684: Line 769:
     event_type = event.get("​type"​)     event_type = event.get("​type"​)
  
-    # Сценарий 1: Звонок поступил ​-> Отвечаем и включаем запись+    # Сценарий 1: Звонок поступил
     if event_type == "​StasisStart":​     if event_type == "​StasisStart":​
         channel_id = event.get("​channel",​ {}).get("​id"​)         channel_id = event.get("​channel",​ {}).get("​id"​)
Line 691: Line 776:
         requests.post(f"​{BASE_URL}/​channels/​{channel_id}/​answer",​ auth=AUTH)         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])
         start_recording(channel_id)         start_recording(channel_id)
  
Line 699: Line 786:
         target_uri = recording_data.get("​target_uri",​ ""​)         target_uri = recording_data.get("​target_uri",​ ""​)
         channel_id = target_uri.split("​channel:"​)[1]         channel_id = target_uri.split("​channel:"​)[1]
 +
 +        caller_number = channel_caller_numbers[channel_id]
 +        print(f"​Номер звонящего в RecordingFinished:​ {caller_number}"​)
  
         import wav_and_ogg         import wav_and_ogg
Line 705: Line 795:
  
         user_request = speech_and_text.speech_to_text(f"​{REC_PATH}{recording_name}.ogg"​)         user_request = speech_and_text.speech_to_text(f"​{REC_PATH}{recording_name}.ogg"​)
-        print(user_request)+        print(f"user_request: {user_request}"​)
  
-        ​import request_to_agent +        ​if user_request:​ 
-        context = get_channel_context(channel_id+            context = get_channel_context(channel_id)
-        agent_response = request_to_agent.request_to_agent(user_request,​ SYSTEM_PROMPT,​ context) +
-        print(agent_response)+
  
-        ​#​agent_response = user_request+            import request_to_agent 
 +            agent_response = request_to_agent.request_to_agent(user_request,​ SYSTEM_PROMPT,​ context) 
 +            ​#​agent_response = user_request
  
-        update_channel_context(channel_id, user_request, ​agent_response)+            print(agent_response)
  
-        '''​ +            update_channel_context(channel_id, user_request,​ agent_response)
-        print("​Текущий контекст:"​) +
-        from pprint import pprint +
-        pprint(channel_contexts) +
-        '''​+
  
-        speech_and_text.text_to_speech(agent_response,​f"{REC_PATH}{recording_name}.response.ogg") +            print("Текущий контекст:​") 
-        ​wav_and_ogg.ogg_to_wav(f"​{REC_PATH}{recording_name}.response"​)+            from pprint import pprint 
 +            pprint(channel_contexts[channel_id])
  
-        ​print(f"​Запись {recording_name} готова! Проигрываем её обратно в канал {channel_id}..."​) +            speech_and_text.text_to_speech(agent_response,​f"​{REC_PATH}{recording_name}.response.ogg"​) 
-        play_url = f"​{BASE_URL}/​channels/​{channel_id}/​play"​ +            wav_and_ogg.ogg_to_wav(f"​{REC_PATH}{recording_name}.response"​) 
-        play_payload = { + 
-            #"​media":​ f"​recording:​{recording_name}"​ +            ​print(f"​Запись {recording_name} готова! Проигрываем её обратно в канал {channel_id}..."​) 
-            "​media":​ f"​recording:​{recording_name}.response"​ +            play_url = f"​{BASE_URL}/​channels/​{channel_id}/​play"​ 
-        +            play_payload = { 
-        response = requests.post(play_url,​ params=play_payload,​ auth=AUTH) +                #"​media":​ f"​recording:​{recording_name}"​ 
-        print("​Ответ ARI на воспроизведение:",​ response.status_code)+                "​media":​ f"​recording:​{recording_name}.response"​ 
 +            
 +            response = requests.post(play_url,​ params=play_payload,​ auth=AUTH) 
 +            print("​Ответ ARI на воспроизведение:",​ response.status_code)
  
-    # Сценарий 3: Воспроизведение завершено ​-> Возвращаемся в режим записи...+    # Сценарий 3: Воспроизведение завершено
     elif event_type == "​PlaybackFinished":​     elif event_type == "​PlaybackFinished":​
         playback_data = event.get("​playback",​ {})         playback_data = event.get("​playback",​ {})
 +        media_uri = playback_data.get("​media_uri",​ ""​)
         target_uri = playback_data.get('​target_uri',​ ''​)         target_uri = playback_data.get('​target_uri',​ ''​)
         channel_id = target_uri.split("​channel:"​)[1]         channel_id = target_uri.split("​channel:"​)[1]
  
-        print(f"​Воспроизведение завершено. Возвращаемся в режим записи..."​) 
         start_recording(channel_id)         start_recording(channel_id)
  
-        '''​ +    # Сценарий 4: Абонент повесил ​трубку
-        print(f"​Вешаем трубку ​для channel_id {channel_id}"​) +
-        hangup_url = f"​{BASE_URL}/​channels/​{channel_id}"​ +
-        requests.delete(hangup_url,​ auth=AUTH) +
-        '''​ +
     elif event_type == "​StasisEnd":​     elif event_type == "​StasisEnd":​
         channel_id = event.get("​channel",​ {}).get("​id"​)         channel_id = event.get("​channel",​ {}).get("​id"​)
-        ​print(f"​Звонок завершен пользователем анал {channel_id}). Очистка ​ресурсов...")+ 
 +        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         import glob
         recording_name = f"​rec_{channel_id}"​         recording_name = f"​rec_{channel_id}"​
язык_программирования_python.1783826135.txt.gz · Last modified: 2026/07/12 06:15 by val