paint-brush
अपना खुद का AI कन्फेशनल कैसे बनाएं: LLM में आवाज़ कैसे जोड़ेंद्वारा@slavasobolev
1,299 रीडिंग
1,299 रीडिंग

अपना खुद का AI कन्फेशनल कैसे बनाएं: LLM में आवाज़ कैसे जोड़ें

द्वारा Iaroslav Sobolev11m2024/07/14
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

फरवरी के अंत में, बाली ने [लैम्पू](https://lampu.org/) उत्सव की मेजबानी की, जिसे प्रसिद्ध बर्निंग मैन के सिद्धांतों के अनुसार आयोजित किया गया था। हम कैथोलिक कन्फेशनल के विचार और वर्तमान एलएलएम की क्षमताओं से प्रेरित थे। हमने अपना खुद का एआई कन्फेशनल बनाया, जहाँ कोई भी कृत्रिम बुद्धिमत्ता से बात कर सकता था।
featured image - अपना खुद का AI कन्फेशनल कैसे बनाएं: LLM में आवाज़ कैसे जोड़ें
Iaroslav Sobolev HackerNoon profile picture
0-item
1-item

जबकि ओपनएआई चैटजीपीटी के लिए उन्नत वॉयस मोड्स को जारी करने में देरी कर रहा है, मैं यह साझा करना चाहता हूं कि हमने अपने एलएलएम वॉयस एप्लिकेशन को कैसे बनाया और इसे एक इंटरैक्टिव बूथ में एकीकृत किया।

जंगल में AI से बात करें

फरवरी के अंत में, बाली ने लाम्पू उत्सव की मेजबानी की, जिसे प्रसिद्ध बर्निंग मैन के सिद्धांतों के अनुसार आयोजित किया गया था। इसकी परंपरा के अनुसार, प्रतिभागी अपनी खुद की स्थापनाएँ और कला वस्तुएँ बनाते हैं।


कैंप 19:19 के मेरे मित्र और मैं, कैथोलिक कन्फेशनल के विचार और वर्तमान एलएलएम की क्षमताओं से प्रेरित होकर, अपना स्वयं का एआई कन्फेशनल बनाने का विचार लेकर आए, जहां कोई भी कृत्रिम बुद्धिमत्ता से बात कर सकता था।


हमने शुरू में इसकी कल्पना इस प्रकार की थी:

  • जब उपयोगकर्ता किसी बूथ में प्रवेश करता है, तो हम यह निर्धारित करते हैं कि हमें एक नया सत्र शुरू करने की आवश्यकता है।


  • उपयोगकर्ता एक प्रश्न पूछता है, और AI सुनता है और उत्तर देता है। हम एक भरोसेमंद और निजी वातावरण बनाना चाहते थे जहाँ हर कोई अपने विचारों और अनुभवों पर खुलकर चर्चा कर सके।


  • जब उपयोगकर्ता कमरे से बाहर निकलता है, तो सिस्टम सत्र समाप्त कर देता है और सभी वार्तालाप विवरण भूल जाता है। सभी संवादों को निजी रखने के लिए यह आवश्यक है।

अवधारणा का सबूत

अवधारणा का परीक्षण करने और एलएलएम के लिए एक संकेत के साथ प्रयोग शुरू करने के लिए, मैंने एक शाम में एक सरल कार्यान्वयन तैयार किया:

  • माइक्रोफोन को सुनें.
  • स्पीच-टू-टेक्स्ट (एसटीटी) मॉडल का उपयोग करके उपयोगकर्ता की वाणी को पहचानें।
  • एलएलएम के माध्यम से प्रतिक्रिया उत्पन्न करें.
  • टेक्स्ट-टू-स्पीच (टीटीएस) मॉडल का उपयोग करके ध्वनि प्रतिक्रिया को संश्लेषित करें।
  • उपयोगकर्ता को प्रतिक्रिया वापस भेजें.



इस डेमो को लागू करने के लिए, मैंने पूरी तरह से OpenAI के क्लाउड मॉडल पर भरोसा किया: व्हिस्पर , GPT-4 और TTS । बेहतरीन लाइब्रेरी स्पीच_रिकग्निशन की बदौलत, मैंने कोड की कुछ दर्जन लाइनों में ही डेमो बना लिया।


 import os import asyncio from dotenv import load_dotenv from io import BytesIO from openai import AsyncOpenAI from soundfile import SoundFile import sounddevice as sd import speech_recognition as sr load_dotenv() aiclient = AsyncOpenAI( api_key=os.environ.get("OPENAI_API_KEY") ) SYSTEM_PROMPT = """ You are helpfull assistant. """ async def listen_mic(recognizer: sr.Recognizer, microphone: sr.Microphone): audio_data = recognizer.listen(microphone) wav_data = BytesIO(audio_data.get_wav_data()) wav_data.name = "SpeechRecognition_audio.wav" return wav_data async def say(text: str): res = await aiclient.audio.speech.create( model="tts-1", voice="alloy", response_format="opus", input=text ) buffer = BytesIO() for chunk in res.iter_bytes(chunk_size=4096): buffer.write(chunk) buffer.seek(0) with SoundFile(buffer, 'r') as sound_file: data = sound_file.read(dtype='int16') sd.play(data, sound_file.samplerate) sd.wait() async def respond(text: str, history): history.append({"role": "user", "content": text}) completion = await aiclient.chat.completions.create( model="gpt-4", temperature=0.5, messages=history, ) response = completion.choices[0].message.content await say(response) history.append({"role": "assistant", "content": response}) async def main() -> None: m = sr.Microphone() r = sr.Recognizer() messages = [{"role": "system", "content": SYSTEM_PROMPT}] with m as source: r.adjust_for_ambient_noise(source) while True: wav_data = await listen_mic(r, source) transcript = await aiclient.audio.transcriptions.create( model="whisper-1", temperature=0.5, file=wav_data, response_format="verbose_json", ) if transcript.text == '' or transcript.text is None: continue await respond(transcript.text, messages) if __name__ == '__main__': asyncio.run(main())


इस डेमो के प्रथम परीक्षण के बाद हमें जिन समस्याओं का समाधान करना था, वे तुरंत स्पष्ट हो गईं:

  • प्रतिक्रिया में देरी । एक सरल कार्यान्वयन में, उपयोगकर्ता के प्रश्न और प्रतिक्रिया के बीच की देरी 7-8 सेकंड या उससे अधिक होती है। यह अच्छा नहीं है, लेकिन जाहिर है, प्रतिक्रिया समय को अनुकूलित करने के कई तरीके हैं।


  • परिवेशीय शोर । हमने पाया कि शोर भरे वातावरण में, हम यह पता लगाने के लिए माइक्रोफ़ोन पर निर्भर नहीं रह सकते कि उपयोगकर्ता ने कब बोलना शुरू किया और कब समाप्त किया। किसी वाक्यांश की शुरुआत और अंत को पहचानना ( एंडपॉइंटिंग ) एक गैर-तुच्छ कार्य है। इसे संगीत समारोह के शोर भरे वातावरण के साथ जोड़ दें, और यह स्पष्ट है कि वैचारिक रूप से एक अलग दृष्टिकोण की आवश्यकता है।


  • लाइव बातचीत की नकल करें । हम उपयोगकर्ता को AI को बाधित करने की क्षमता देना चाहते थे। इसे प्राप्त करने के लिए, हमें माइक्रोफ़ोन चालू रखना होगा। लेकिन इस मामले में, हमें उपयोगकर्ता की आवाज़ को न केवल पृष्ठभूमि ध्वनियों से बल्कि AI की आवाज़ से भी अलग करना होगा।


  • प्रतिक्रिया . प्रतिक्रिया में देरी के कारण, कभी-कभी हमें ऐसा लगता था कि सिस्टम जम गया है। हमें एहसास हुआ कि हमें उपयोगकर्ता को यह सूचित करने की आवश्यकता है कि प्रतिक्रिया कितने समय तक संसाधित होगी


हमारे पास इन समस्याओं को हल करने का विकल्प था: उपयुक्त इंजीनियरिंग या उत्पाद समाधान की तलाश करके।

बूथ के UX के बारे में सोचना

कोड लिखने से पहले ही हमें यह तय करना था कि उपयोगकर्ता बूथ के साथ किस प्रकार बातचीत करेगा:

  • हमें यह तय करना होगा कि बूथ में नये उपयोगकर्ता का पता कैसे लगाया जाए ताकि पिछले संवाद इतिहास को रीसेट किया जा सके।


  • किसी उपयोगकर्ता के भाषण के आरंभ और अंत को कैसे पहचाना जाए, तथा यदि वे AI को बाधित करना चाहें तो क्या किया जाए।


  • जब AI से प्रतिक्रिया में देरी हो तो फीडबैक कैसे लागू किया जाए।


बूथ में किसी नए उपयोगकर्ता का पता लगाने के लिए, हमने कई विकल्पों पर विचार किया: दरवाज़ा खोलने वाले सेंसर, फ़्लोर वेट सेंसर, डिस्टेंस सेंसर और एक कैमरा + YOLO मॉडल। पीछे की तरफ़ लगा डिस्टेंस सेंसर हमें सबसे ज़्यादा विश्वसनीय लगा, क्योंकि यह आकस्मिक ट्रिगर को रोकता है, जैसे कि जब दरवाज़ा पर्याप्त रूप से बंद न हो, और वज़न सेंसर के विपरीत, इसे जटिल इंस्टॉलेशन की आवश्यकता नहीं होती।


संवाद की शुरुआत और अंत को पहचानने की चुनौती से बचने के लिए, हमने माइक्रोफ़ोन को नियंत्रित करने के लिए एक बड़ा लाल बटन जोड़ने का फैसला किया। इस समाधान ने उपयोगकर्ता को किसी भी समय AI को बाधित करने की अनुमति दी।


किसी अनुरोध को संसाधित करने पर फ़ीडबैक लागू करने के बारे में हमारे पास कई अलग-अलग विचार थे। हमने एक स्क्रीन के साथ एक विकल्प पर फैसला किया जो दिखाता है कि सिस्टम क्या कर रहा है: माइक्रोफ़ोन सुनना, किसी प्रश्न को संसाधित करना, या उत्तर देना।


हमने पुराने लैंडलाइन फोन के साथ एक स्मार्ट विकल्प पर भी विचार किया। जब उपयोगकर्ता फोन उठाता है तो सत्र शुरू हो जाता है, और सिस्टम तब तक उपयोगकर्ता की बात सुनता है जब तक वह फोन नहीं काट देता। हालांकि, हमने फैसला किया कि यह अधिक प्रामाणिक है जब उपयोगकर्ता को फोन से आवाज के बजाय बूथ द्वारा "उत्तर" दिया जाता है।


स्थापना के दौरान और उत्सव के दौरान


अंत में, अंतिम उपयोगकर्ता प्रवाह इस प्रकार सामने आया:

  • एक उपयोगकर्ता बूथ में प्रवेश करता है। उसकी पीठ के पीछे एक दूरी सेंसर सक्रिय हो जाता है, और हम उसका अभिवादन करते हैं।


  • उपयोगकर्ता संवाद शुरू करने के लिए लाल बटन दबाता है। बटन दबाए जाने के दौरान हम माइक्रोफ़ोन सुनते हैं। जब उपयोगकर्ता बटन छोड़ता है, तो हम अनुरोध को संसाधित करना शुरू करते हैं और इसे स्क्रीन पर इंगित करते हैं।


  • यदि उपयोगकर्ता एआई के उत्तर देते समय कोई नया प्रश्न पूछना चाहता है, तो वह बटन को पुनः दबा सकता है, और एआई तुरंत उत्तर देना बंद कर देगा।


  • जब उपयोगकर्ता बूथ छोड़ता है, तो दूरी सेंसर पुनः सक्रिय हो जाता है, और हम संवाद इतिहास साफ़ कर देते हैं।

वास्तुकला


Arduino दूरी सेंसर और लाल बटन की स्थिति पर नज़र रखता है। यह HTTP API के ज़रिए हमारे बैकएंड पर सभी बदलाव भेजता है, जो सिस्टम को यह निर्धारित करने की अनुमति देता है कि उपयोगकर्ता बूथ में प्रवेश कर चुका है या छोड़ चुका है और क्या माइक्रोफ़ोन को सुनना सक्रिय करना या प्रतिक्रिया उत्पन्न करना शुरू करना आवश्यक है।


वेब यूआई ब्राउज़र में खोला गया एक वेब पेज मात्र है जो बैकएंड से सिस्टम की वर्तमान स्थिति को निरंतर प्राप्त करता है और उसे उपयोगकर्ता को प्रदर्शित करता है।


बैकएंड माइक्रोफ़ोन को नियंत्रित करता है, सभी आवश्यक AI मॉडल के साथ बातचीत करता है, और LLM प्रतिक्रियाओं को आवाज़ देता है। इसमें ऐप का मुख्य तर्क शामिल है।

हार्डवेयर

Arduino के लिए स्केच को कैसे कोड करें, डिस्टेंस सेंसर और बटन को सही तरीके से कैसे कनेक्ट करें, और बूथ में यह सब कैसे असेंबल करें, यह एक अलग लेख का विषय है। आइए तकनीकी विवरण में जाए बिना संक्षेप में समीक्षा करें कि हमें क्या मिला।


हमने एक Arduino, अधिक सटीक रूप से, मॉडल ESP32 का उपयोग किया जिसमें एक अंतर्निहित वाई-फाई मॉड्यूल था। माइक्रोकंट्रोलर लैपटॉप के समान वाई-फाई नेटवर्क से जुड़ा था, जो बैकएंड चला रहा था।



हमारे द्वारा उपयोग किये गये हार्डवेयर की पूरी सूची:

बैकएंड

पाइपलाइन के मुख्य घटक स्पीच-टू-टेक्स्ट (एसटीटी), एलएलएम और टेक्स्ट-टू-स्पीच (टीटीएस) हैं। प्रत्येक कार्य के लिए, स्थानीय और क्लाउड दोनों के माध्यम से कई अलग-अलग मॉडल उपलब्ध हैं।



चूँकि हमारे पास शक्तिशाली GPU नहीं था, इसलिए हमने मॉडल के क्लाउड-आधारित संस्करण चुनने का फैसला किया। इस दृष्टिकोण की कमज़ोरी एक अच्छे इंटरनेट कनेक्शन की आवश्यकता है। फिर भी, सभी अनुकूलन के बाद भी इंटरेक्शन की गति स्वीकार्य थी, यहाँ तक कि त्यौहार में हमारे पास मौजूद मोबाइल इंटरनेट के साथ भी।


अब, आइए पाइपलाइन के प्रत्येक घटक पर करीब से नज़र डालें।

वाक् पहचान

कई आधुनिक डिवाइस लंबे समय से स्पीच रिकग्निशन का समर्थन करते हैं। उदाहरण के लिए, Apple स्पीच API iOS और macOS के लिए उपलब्ध है, और वेब स्पीच API ब्राउज़र के लिए है।


दुर्भाग्यवश, वे व्हिस्पर या डीपग्राम की तुलना में गुणवत्ता में बहुत निम्न हैं और स्वचालित रूप से भाषा का पता नहीं लगा सकते हैं।


प्रोसेसिंग समय को कम करने के लिए, सबसे अच्छा विकल्प उपयोगकर्ता के बोलते समय वास्तविक समय में भाषण को पहचानना है। यहाँ कुछ प्रोजेक्ट दिए गए हैं, जिनमें उन्हें लागू करने के तरीके के उदाहरण दिए गए हैं: कानाफूसी_स्ट्रीमिंग , whisper.cpp


हमारे लैपटॉप के साथ, इस दृष्टिकोण का उपयोग करके भाषण पहचान की गति वास्तविक समय से बहुत दूर निकली। कई प्रयोगों के बाद, हमने OpenAI के क्लाउड-आधारित व्हिस्पर मॉडल पर निर्णय लिया।

एलएलएम और प्रॉम्प्ट इंजीनियरिंग

पिछले चरण के स्पीच टू टेक्स्ट मॉडल का परिणाम वह पाठ है जिसे हम संवाद इतिहास के साथ LLM को भेजते हैं।


एलएलएम चुनते समय, हमने GPT-3.5. GPT-4 और क्लाउड की तुलना की। यह पता चला कि मुख्य कारक विशिष्ट मॉडल नहीं बल्कि उसका कॉन्फ़िगरेशन था। अंततः, हमने GPT-4 पर सहमति जताई, जिसके उत्तर हमें दूसरों की तुलना में अधिक पसंद आए।


एलएलएम मॉडल के लिए प्रॉम्प्ट का अनुकूलन एक अलग कला रूप बन गया है। इंटरनेट पर आपके मॉडल को अपनी ज़रूरत के हिसाब से ट्यून करने के तरीके के बारे में कई गाइड मौजूद हैं:



हमें मॉडल को आकर्षक, संक्षिप्त और विनोदपूर्ण ढंग से प्रतिक्रिया देने के लिए प्रॉम्प्ट और तापमान सेटिंग्स के साथ बड़े पैमाने पर प्रयोग करना पड़ा।

भाषण के पाठ

हम टेक्स्ट-टू-स्पीच मॉडल का उपयोग करके एलएलएम से प्राप्त प्रतिक्रिया को आवाज़ देते हैं और इसे उपयोगकर्ता को वापस सुनाते हैं। यह कदम हमारे डेमो में देरी का प्राथमिक स्रोत था।


एलएलएम को प्रतिक्रिया देने में काफी समय लगता है। हालांकि, वे स्ट्रीमिंग मोड में प्रतिक्रिया निर्माण का समर्थन करते हैं - टोकन दर टोकन। हम इस सुविधा का उपयोग एलएलएम से पूर्ण प्रतिक्रिया की प्रतीक्षा किए बिना प्राप्त होने वाले अलग-अलग वाक्यांशों को आवाज़ देकर प्रतीक्षा समय को अनुकूलित करने के लिए कर सकते हैं।


अलग-अलग वाक्यों को बोलना


  • एलएलएम से प्रश्न पूछें।


  • हम बफर में टोकन दर टोकन प्रतिक्रिया जमा करते हैं जब तक कि हमारे पास न्यूनतम लंबाई का पूरा वाक्य न हो जाए। न्यूनतम लंबाई पैरामीटर महत्वपूर्ण है क्योंकि यह आवाज़ के स्वर और प्रारंभिक देरी समय दोनों को प्रभावित करता है।


  • उत्पन्न वाक्य को TTS मॉडल पर भेजें, और परिणाम को उपयोगकर्ता को चलाएँ। इस चरण में, यह सुनिश्चित करना आवश्यक है कि प्लेबैक क्रम में कोई रेस कंडीशन न हो।


  • LLM प्रतिक्रिया के अंत तक पिछले चरण को दोहराएं


हम उस समय का उपयोग करते हैं जब उपयोगकर्ता प्रारंभिक खंड को सुनता है ताकि LLM से प्रतिक्रिया के शेष भागों को संसाधित करने में होने वाली देरी को छिपाया जा सके। इस दृष्टिकोण के कारण, प्रतिक्रिया में देरी केवल शुरुआत में होती है और ~ 3 सेकंड होती है।


 async generateResponse(history) { const completion = await this.ai.completion(history); const chunks = new DialogChunks(); for await (const chunk of completion) { const delta = chunk.choices[0]?.delta?.content; if (delta) { chunks.push(delta); if (chunks.hasCompleteSentence()) { const sentence = chunks.popSentence(); this.voice.ttsAndPlay(sentence); } } } const sentence = chunks.popSentence(); if (sentence) { this.voice.say(sentence); } return chunks.text; }


अंतिम स्पर्श

हमारे सभी अनुकूलन के बावजूद, 3-4 सेकंड की देरी अभी भी महत्वपूर्ण है। हमने उपयोगकर्ता को यह महसूस होने से बचाने के लिए फीडबैक के साथ UI का ख्याल रखने का फैसला किया कि प्रतिक्रिया रुकी हुई है। हमने कई तरीकों पर गौर किया:


  • एलईडी संकेतक । हमें पाँच अवस्थाएँ प्रदर्शित करनी थीं: निष्क्रिय, प्रतीक्षा, सुनना, सोचना और बोलना। लेकिन हम यह नहीं समझ पाए कि इसे एलईडी के साथ समझने में आसान तरीके से कैसे किया जाए।


  • फिलर शब्द , जैसे "मुझे सोचने दो," "हम्म," इत्यादि, वास्तविक जीवन की बातचीत की नकल करते हैं। हमने इस विकल्प को अस्वीकार कर दिया क्योंकि फिलर अक्सर मॉडल की प्रतिक्रियाओं के लहजे से मेल नहीं खाते थे।


  • बूथ में एक स्क्रीन लगाएँ। और एनिमेशन के साथ अलग-अलग अवस्थाएँ दिखाएँ।


हमने अंतिम विकल्प को चुना, जिसमें एक सरल वेब पेज था जो बैकएंड को पोल करता था तथा वर्तमान स्थिति के अनुसार एनिमेशन दिखाता था।


परिणाम

हमारा AI कन्फेशन रूम चार दिनों तक चला और इसमें सैकड़ों लोग शामिल हुए। हमने OpenAI API पर सिर्फ़ $50 खर्च किए। बदले में, हमें काफ़ी सकारात्मक प्रतिक्रियाएँ और मूल्यवान इंप्रेशन मिले।


इस छोटे से प्रयोग से पता चला कि सीमित संसाधनों और चुनौतीपूर्ण बाह्य परिस्थितियों के बावजूद भी एलएलएम में सहज और कुशल वॉयस इंटरफेस जोड़ना संभव है।


वैसे, बैकएंड स्रोत GitHub पर उपलब्ध हैं