paint-brush
अपना खुद का कुछ भी जीपीटी कैसे बनाएं - एक बॉट जो आपकी इच्छानुसार उत्तर देता हैद्वारा@balakhonoff
1,538 रीडिंग
1,538 रीडिंग

अपना खुद का कुछ भी जीपीटी कैसे बनाएं - एक बॉट जो आपकी इच्छानुसार उत्तर देता है

द्वारा Kirill Balakhonov24m2023/07/25
Read on Terminal Reader

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

हम चैटजीपीटी का एक अनुकूलित संस्करण बनाने के बारे में बात करेंगे जो एक बड़े ज्ञान आधार को ध्यान में रखते हुए सवालों के जवाब देता है। हम OpenAI से प्रासंगिक एम्बेडिंग का उपयोग करेंगे (ज्ञान आधार से प्रासंगिक प्रश्नों की वास्तव में उच्च गुणवत्ता वाली खोज के लिए)। हम उत्तरों को प्राकृतिक मानव भाषा में प्रारूपित करेंगे।
featured image - अपना खुद का कुछ भी जीपीटी कैसे बनाएं - एक बॉट जो आपकी इच्छानुसार उत्तर देता है
Kirill Balakhonov HackerNoon profile picture
0-item
1-item
2-item


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


इसे प्राप्त करने के लिए, हम OpenAI (ज्ञान आधार से प्रासंगिक प्रश्नों की वास्तव में उच्च गुणवत्ता वाली खोज के लिए) और ChatGPT API (प्राकृतिक मानव भाषा में उत्तरों को प्रारूपित करने के लिए) से प्रासंगिक एम्बेडिंग का उपयोग करेंगे।


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


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


लेकिन मैं गुणवत्ता के स्वीकार्य स्तर के साथ ऐसा करने के लिए फ्रेमवर्क को कॉन्फ़िगर नहीं कर सका, और फ्रेमवर्क को डीबग करना इस कार्य के लिए ओवरकिल जैसा लग रहा था। अंत में, मैंने अपना स्वयं का बॉयलरप्लेट कोड बनाया और इस दृष्टिकोण से संतुष्ट हुआ।

काम

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


कोड लिखते समय, मैं अक्सर चैटजीपीटी का उपयोग करता हूं (और मुझे इससे कोई शर्म नहीं है🙂)। हालाँकि, 2022+ वर्ष के लिए डेटा की कमी के कारण, कभी-कभी अपेक्षाकृत नई तकनीकों में समस्याएँ आती हैं।


विशेष रूप से, ग्राफ़ प्रोटोकॉल के लिए सबग्राफ विकसित करते समय (ईवीएम-संगत ब्लॉकचेन से अनुक्रमित डेटा पुनर्प्राप्त करने के लिए ईटीएल बनाने का सबसे लोकप्रिय तरीका, आप मेरे पिछले लेखों [ 1 ] और [ 2 ]) में इसके बारे में अधिक पढ़ सकते हैं, पुस्तकालयों में स्वयं कई ब्रेकिंग संगतता परिवर्तन हुए हैं। चैटजीपीटी के "पुराने" उत्तर अब मददगार नहीं हैं, और मुझे या तो दुर्लभ दस्तावेज़ों में या सबसे खराब स्थिति में, डेवलपर्स के डिस्कॉर्ड में सही उत्तरों की खोज करनी होगी, जो बहुत सुविधाजनक नहीं है (यह स्टैक ओवरफ़्लो की तरह नहीं है)।


समस्या का दूसरा भाग यह है कि हर बार आपको वार्तालाप संदर्भ सही ढंग से प्रदान करने की आवश्यकता होती है क्योंकि चैटजीपीटी अक्सर सबग्राफ के विषय को बंद कर देता है, ग्राफक्यूएल, एसक्यूएल, या उच्च गणित ("ग्राफ", "सबग्राफ", आदि अद्वितीय शब्द नहीं हैं और कई अलग-अलग व्याख्याएं और विषय हैं) पर चले जाते हैं।


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


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

शीर्ष स्तरीय समाधान

अंत में, इस समस्या को हल करने के लिए, मैंने दो स्रोतों का उपयोग करने का निर्णय लिया:

  1. प्रश्नों और उत्तरों का मैन्युअल रूप से संकलित ज्ञान आधार, सेमी-ब्लाइंड मोड में चुना गया (अक्सर मैंने दस्तावेज़ीकरण से विषय शीर्षक को प्रश्न के रूप में लिया, और जानकारी के पूरे पैराग्राफ को उत्तर के रूप में लिया)।

  2. पिछले 2 वर्षों से प्रोटोकॉल डेवलपर्स डिस्कॉर्ड से निर्यात किए गए संदेश (2021 के अंत से लापता अवधि को कवर करने के लिए)।


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


मैन्युअल रूप से संकलित प्रश्नोत्तर के लिए,

  1. प्रत्येक प्रश्न के लिए, एक प्रासंगिक एम्बेडिंग उत्पन्न होती है (एक वेक्टर जो बहुआयामी स्थिति में इस प्रश्न का वर्णन करता है), टेक्स्ट-एंबेडिंग-एडीए-002 मॉडल के माध्यम से प्राप्त किया जाता है,

  2. फिर, कोसाइन दूरी खोज फ़ंक्शन का उपयोग करके, ज्ञान आधार से शीर्ष 3 सबसे समान प्रश्न पाए जाते हैं (3 के बजाय, आपके डेटासेट के लिए सबसे उपयुक्त संख्या का उपयोग किया जा सकता है),

  3. इन 3 प्रश्नों के उत्तर अंतिम संकेत में अनुमानित विवरण के साथ जोड़े जाते हैं, " इस प्रश्नोत्तर स्निपेट का उपयोग केवल तभी करें जब यह दिए गए प्रश्न के लिए प्रासंगिक हो। "


    डिस्कॉर्ड से निर्यात किए गए संदेशों के लिए, निम्नलिखित एल्गोरिदम का उपयोग किया गया था:

  4. प्रश्न चिह्न वाले प्रत्येक संदेश के लिए, एक प्रासंगिक एम्बेडिंग भी उत्पन्न होती है (उसी मॉडल का उपयोग करके),

  5. फिर, इसी तरह, शीर्ष 5 सबसे समान प्रश्नों का चयन किया जाता है,

  6. और उत्तर के संदर्भ के रूप में, उस प्रश्न के बाद के 20 संदेशों को जोड़ा जाता है, जिनके बारे में माना जाता है कि उनमें प्रश्न का उत्तर शामिल होने की एक निश्चित संभावना है,

  7. और यह जानकारी अंतिम संकेत में लगभग इस तरह जोड़ी गई थी: "यदि आपको संलग्न प्रश्नोत्तर स्निपेट में प्रश्न का स्पष्ट उत्तर नहीं मिला, तो मूल प्रश्न का उत्तर देने के लिए डेवलपर द्वारा निम्नलिखित चैट अंश आपके लिए उपयोगी हो सकते हैं..."


इसके अलावा, यदि विषय स्पष्ट रूप से नहीं दिया गया है, तो प्रश्नोत्तर स्निपेट्स और चैट की उपस्थिति से उत्तरों में अस्पष्टता हो सकती है, जो उदाहरण के लिए, इस प्रकार दिख सकती है:



तो, यह समझा जाता है कि प्रश्न संदर्भ से अलग था और उत्तर भी संदर्भ से अलग स्वीकार किया गया था। तब बताया गया कि इस तरह के डेटा का उपयोग किया जा सकता है, और इसका सारांश इस प्रकार है:

  1. दरअसल, जवाब इस तरह हो सकता है...
  2. और यदि सन्दर्भ पर विचार करें तो वह इस प्रकार होगा...


इससे बचने के लिए, हम एक विषय की अवधारणा पेश करते हैं, जिसे स्पष्ट रूप से परिभाषित किया गया है और प्रॉम्प्ट की शुरुआत में डाला गया है:

"मुझे 'ग्राफ़ सबग्राफ़ विकास' विषय से संबंधित एक प्रश्न का उत्तर प्राप्त करने की आवश्यकता है: {{{सबग्राफ़ क्या है?}}}"


इसके अलावा, अंतिम वाक्य में, मैं यह भी जोड़ता हूं:

अंत में, केवल यदि उपरोक्त जानकारी पर्याप्त नहीं है, तो आप प्रश्न का उत्तर देने के लिए 'द ग्राफ सबग्राफ डेवलपमेंट' विषय में अपने ज्ञान का उपयोग कर सकते हैं।


अंत में, पूरा संकेत (चैट से प्राप्त भाग को छोड़कर) इस प्रकार दिखता है:

 ==I need to get an answer to the question related to the topic of "The Graph subgraph development": {{{what is a subgraph?}}}.== ==Possibly, you might find an answer in these Q&As \[use the information only if it is actually relevant and useful for the question answering\]:== ==Q: <What is a subgraph?>== ==A: <A subgraph is a custom API built on blockchain data. Subgraphs are queried using the GraphQL query language and are deployed to a Graph Node using the Graph CLI. Once deployed and published to The Graph's decentralized network, Indexers process subgraphs and make them available to be queried by subgraph consumers.>== ==Q: <Am I still able to create a subgraph if my smart contracts don't have events?>== ==A: <It is highly recommended that you structure your smart contracts to have events associated with data you are interested in querying. Event handlers in the subgraph are triggered by contract events and are by far the fastest way to retrieve useful data. If the contracts you are working with do not contain events, your subgraph can use call and block handlers to trigger indexing. Although this is not recommended, as performance will be significantly slower.>== ==Q: <How do I call a contract function or access a public state variable from my subgraph mappings?>== ==A: <Take a look at Access to smart contract state inside the section AssemblyScript API. https://thegraph.com/docs/en/developing/assemblyscript-api/>== ==Finally, only if the information above was not enough you can use your knowledge in the topic of "The Graph subgraph development" to answer the question.==


इनपुट पर इस सेमी-ऑटो-जनरेटेड प्रॉम्प्ट के साथ उपरोक्त अनुरोध की प्रतिक्रिया शुरू से ही सही दिखती है:



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

सोर्स कोड

मुझे तुरंत ध्यान देना चाहिए कि अंत में रिपॉजिटरी के लिए एक लिंक होगा , ताकि आप बॉट को वैसे ही चला सकें, "विषय" को अपने से बदल सकें, प्रश्नोत्तर ज्ञान आधार फ़ाइल को अपने से बदल सकें, और ओपनएआई और टेलीग्राम बॉट के लिए अपनी खुद की एपीआई कुंजी प्रदान कर सकें। इसलिए यहां विवरण का उद्देश्य GitHub पर स्रोत कोड से पूरी तरह मेल खाना नहीं है, बल्कि कोड के मुख्य पहलुओं को उजागर करना है।

1 - आभासी वातावरण तैयार करना

आइए एक नया वर्चुअल वातावरण बनाएं और require.txt से निर्भरताएँ स्थापित करें:


 virtualenv -p python3.8 .venv source .venv/bin/activate pip install -r requirements.txt

2 - ज्ञानकोष, मैन्युअल रूप से एकत्र किया गया

जैसा कि ऊपर उल्लेख किया गया है, यह माना जाता है कि प्रश्नों और उत्तरों की एक सूची है, इस मामले में निम्न प्रकार की एक्सेल फ़ाइल के प्रारूप में:



दिए गए प्रश्न के सबसे समान प्रश्न को खोजने के लिए, हमें इस फ़ाइल की प्रत्येक पंक्ति में प्रश्न का एक एम्बेडिंग (स्टेट स्पेस में एक बहुआयामी वेक्टर) जोड़ना होगा। हम इसके लिए add_embeddings.py फ़ाइल का उपयोग करेंगे। स्क्रिप्ट में कई सरल भाग होते हैं।

लाइब्रेरी आयात करना और कमांड लाइन तर्क पढ़ना:


 import pandas as pd import openai import argparse # Create an Argument Parser object parser = argparse.ArgumentParser(description='Adding embeddings for each line of csv file') # Add the arguments parser.add_argument('--openai_api_key', type=str, help='API KEY of OpenAI API to create contextual embeddings for each line') parser.add_argument('--file', type=str, help='A source CSV file with the text data') parser.add_argument('--colname', type=str, help='Column name with the texts') # Parse the command-line arguments args = parser.parse_args() # Access the argument values openai.api_key = args.openai_api_key file = args.file colname = args.colname


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


 if file[-4:] == '.csv': df = pd.read_csv(file) else: df = pd.read_excel(file) # filter NAs df = df[~df[colname].isna()] # Keep only questions df = df[df[colname].str.contains('\?')]


और अंत में - मॉडल text-embedding-ada-002 के एपीआई को कॉल करके एक एम्बेडिंग उत्पन्न करने के लिए एक फ़ंक्शन, कुछ बार-बार अनुरोध किए जाने के बाद से एपीआई कभी-कभी अतिभारित हो सकता है और एक त्रुटि के साथ प्रतिक्रिया दे सकता है, और इस फ़ंक्शन को डेटाफ़्रेम की प्रत्येक पंक्ति में लागू कर सकता है।


 def get_embedding(text, model="text-embedding-ada-002"): i = 0 max_try = 3 # to avoid random OpenAI API fails: while i < max_try: try: text = text.replace("\n", " ") result = openai.Embedding.create(input=[text], model=model)['data'][0]['embedding'] return result except: i += 1 def process_row(x): return get_embedding(x, model='text-embedding-ada-002') df['ada_embedding'] = df[colname].apply(process_row) df.to_csv(file[:-4]+'_question_embed.csv', index=False)


अंत में, इस स्क्रिप्ट को निम्नलिखित कमांड के साथ कॉल किया जा सकता है:


 python add_embeddings.py \ --openai_api_key="xxx" \ --file="./subgraphs_faq.xlsx" \ --colname="Question"


OpenAI API कुंजी, नॉलेज बेस वाली फ़ाइल और उस कॉलम का नाम सेट करना जहां प्रश्न टेक्स्ट स्थित है। अंतिम बनाई गई फ़ाइल, सबग्राफ_faq._question_embed.csv, में कॉलम "प्रश्न", "उत्तर", और "ada_embedding " शामिल हैं।

3 - डिस्कॉर्ड से डेटा संग्रह (वैकल्पिक)

यदि आप एक साधारण बॉट में रुचि रखते हैं जो केवल मैन्युअल रूप से एकत्रित ज्ञान आधार के आधार पर प्रतिक्रिया देता है, तो आप इसे और निम्नलिखित अनुभाग को छोड़ सकते हैं। हालाँकि, मैं यहां डिस्कॉर्ड चैनल और टेलीग्राम समूह दोनों से डेटा एकत्र करने के लिए संक्षेप में कोड उदाहरण प्रदान करूंगा। फ़ाइल discord-channel-data-collection.py में दो भाग होते हैं। पहले भाग में पुस्तकालयों को आयात करना और कमांड लाइन तर्कों को आरंभ करना शामिल है:


 import requests import json import pandas as pd import argparse # Create an Argument Parser object parser = argparse.ArgumentParser(description='Discord Channel Data Collection Script') # Add the arguments parser.add_argument('--channel_id', type=str, help='Channel ID from the URL of a channel in browser https://discord.com/channels/xxx/{CHANNEL_ID}') parser.add_argument('--authorization_key', type=str, help='Authorization Key. Being on the discord channel page, start typing anything, then open developer tools -> Network -> Find "typing" -> Headers -> Authorization.') # Parse the command-line arguments args = parser.parse_args() # Access the argument values channel_id = args.channel_id authorization_key = args.authorization_key


दूसरा चैनल से डेटा पुनर्प्राप्त करने और इसे पांडा डेटाफ़्रेम में सहेजने का कार्य है, साथ ही निर्दिष्ट पैरामीटर के साथ इसकी कॉल भी है।


 def retrieve_messages(channel_id, authorization_key): num = 0 limit = 100 headers = { 'authorization': authorization_key } last_message_id = None # Create a pandas DataFrame df = pd.DataFrame(columns=['id', 'dt', 'text', 'author_id', 'author_username', 'is_bot', 'is_reply', 'id_reply']) while True: query_parameters = f'limit={limit}' if last_message_id is not None: query_parameters += f'&before={last_message_id}' r = requests.get( f'https://discord.com/api/v9/channels/{channel_id}/messages?{query_parameters}', headers=headers ) jsonn = json.loads(r.text) if len(jsonn) == 0: break for value in jsonn: is_reply = False id_reply = '0' if 'message_reference' in value and value['message_reference'] is not None: if 'message_id' in value['message_reference'].keys(): is_reply = True id_reply = value['message_reference']['message_id'] text = value['content'] if 'embeds' in value.keys(): if len(value['embeds'])>0: for x in value['embeds']: if 'description' in x.keys(): if text != '': text += ' ' + x['description'] else: text = x['description'] df_t = pd.DataFrame({ 'id': value['id'], 'dt': value['timestamp'], 'text': text, 'author_id': value['author']['id'], 'author_username': value['author']['username'], 'is_bot': value['author']['bot'] if 'bot' in value['author'].keys() else False, 'is_reply': is_reply, 'id_reply': id_reply, }, index=[0]) if len(df) == 0: df = df_t.copy() else: df = pd.concat([df, df_t], ignore_index=True) last_message_id = value['id'] num = num + 1 print('number of messages we collected is', num) # Save DataFrame to a CSV file df.to_csv(f'../discord_messages_{channel_id}.csv', index=False) if __name__ == '__main__': retrieve_messages(channel_id, authorization_key)


यहां उपयोगी जानकारी से, एक विवरण है जो मुझे हर बार ज़रूरत पड़ने पर नहीं मिल पाता है - एक प्राधिकरण कुंजी प्राप्त करना। यह ध्यान में रखते हुए कि चैनल_आईडी ब्राउज़र में खोले गए डिस्कॉर्ड चैनल के यूआरएल (लिंक में अंतिम लंबी संख्या) से प्राप्त किया जा सकता है, प्राधिकरण_की केवल चैनल में एक संदेश टाइप करना शुरू करके पाया जा सकता है, फिर डेवलपर टूल का उपयोग करके नेटवर्क अनुभाग में " टाइपिंग " नामक ईवेंट ढूंढें और हेडर से पैरामीटर निकालें।



इन मापदंडों को प्राप्त करने के बाद, आप चैनल से सभी संदेशों को एकत्र करने के लिए निम्नलिखित कमांड चला सकते हैं (अपने स्वयं के मूल्यों को प्रतिस्थापित करें):


 python discord-channel-data-collection.py \ --channel_id=123456 \ --authorization_key="123456qwerty"


4 - टेलीग्राम से डेटा एकत्रित करना

चूंकि मैं अक्सर टेलीग्राम में चैट/चैनल से विभिन्न डेटा डाउनलोड करता हूं, इसलिए मैंने इसके लिए कोड प्रदान करने का भी निर्णय लिया, जो एक समान प्रारूप ( add_embeddings.py स्क्रिप्ट के संदर्भ में संगत) सीएसवी फ़ाइल उत्पन्न करता है। तो, टेलीग्राम-ग्रुप-डेटा- संग्रह.py स्क्रिप्ट इस प्रकार दिखती है। लाइब्रेरीज़ आयात करना और कमांड लाइन से तर्क प्रारंभ करना:


 import pandas as pd import argparse from telethon import TelegramClient # Create an Argument Parser object parser = argparse.ArgumentParser(description='Telegram Group Data Collection Script') # Add the arguments parser.add_argument('--app_id', type=int, help='Telegram APP id from https://my.telegram.org/apps') parser.add_argument('--app_hash', type=str, help='Telegram APP hash from https://my.telegram.org/apps') parser.add_argument('--phone_number', type=str, help='Telegram user phone number with the leading "+"') parser.add_argument('--password', type=str, help='Telegram user password') parser.add_argument('--group_name', type=str, help='Telegram group public name without "@"') parser.add_argument('--limit_messages', type=int, help='Number of last messages to download') # Parse the command-line arguments args = parser.parse_args() # Access the argument values app_id = args.app_id app_hash = args.app_hash phone_number = args.phone_number password = args.password group_name = args.group_name limit_messages = args.limit_messages


जैसा कि आप देख सकते हैं, आप खुद को पहले व्यक्ति के रूप में अधिकृत किए बिना चैट से सभी संदेशों को आसानी से डाउनलोड नहीं कर सकते। दूसरे शब्दों में, https://my.telegram.org/apps के माध्यम से एक ऐप बनाने (APP_ID और APP_HASH प्राप्त करने) के अलावा, आपको टेलीथॉन लाइब्रेरी से टेलीग्राम क्लाइंट क्लास का एक उदाहरण बनाने के लिए अपने फोन नंबर और पासवर्ड का उपयोग करने की भी आवश्यकता होगी।


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


स्क्रिप्ट के दूसरे भाग में संदेशों को निर्यात करने और उसके निष्पादन के लिए वास्तविक फ़ंक्शन शामिल है (महत्वपूर्ण त्रुटियों से बचने के लिए आवश्यक फ़िल्टरिंग के साथ जो संग्रह को आधे रास्ते में रोक देगा):


 async def main(): messages = await client.get_messages(group_name, limit=limit_messages) df = pd.DataFrame(columns=['date', 'user_id', 'raw_text', 'views', 'forwards', 'text', 'chan', 'id']) for m in messages: if m is not None: if 'from_id' in m.__dict__.keys(): if m.from_id is not None: if 'user_id' in m.from_id.__dict__.keys(): df = pd.concat([df, pd.DataFrame([{'date': m.date, 'user_id': m.from_id.user_id, 'raw_text': m.raw_text, 'views': m.views, 'forwards': m.forwards, 'text': m.text, 'chan': group_name, 'id': m.id}])], ignore_index=True) df = df[~df['user_id'].isna()] df = df[~df['text'].isna()] df['date'] = pd.to_datetime(df['date']) df = df.sort_values('date').reset_index(drop=True) df.to_csv(f'../telegram_messages_{group_name}.csv', index=False) client = TelegramClient('session', app_id, app_hash) client.start(phone=phone_number, password=password) with client: client.loop.run_until_complete(main())


अंत में, इस स्क्रिप्ट को निम्नलिखित कमांड के साथ निष्पादित किया जा सकता है (मानों को अपने साथ बदलें):


 python telegram-group-data-collection.py \ --app_id=123456 --app_hash="123456qwerty" \ --phone_number="+xxxxxx" --password="qwerty123" \ --group_name="xxx" --limit_messages=10000


5 - टेलीग्राम बॉट स्क्रिप्ट जो वास्तव में सवालों के जवाब देती है

अधिकांश समय, मैं अपनी पसंदीदा परियोजनाओं को टेलीग्राम बॉट्स में लपेटता हूं क्योंकि इसे लॉन्च करने के लिए न्यूनतम प्रयास की आवश्यकता होती है और तुरंत क्षमता दिखाई देती है। इस मामले में मैंने वैसा ही किया. मुझे कहना होगा कि बॉट कोड में वे सभी कोने के मामले शामिल नहीं हैं जिनका उपयोग मैं सबग्राफजीपीटी बॉट के उत्पादन संस्करण में करता हूं, क्योंकि इसमें मेरे एक अन्य पसंदीदा प्रोजेक्ट से काफी विरासत में मिला तर्क है। इसके बजाय, मैंने बुनियादी कोड की न्यूनतम मात्रा छोड़ी है जिसे आपकी आवश्यकताओं के लिए संशोधित करना आसान होना चाहिए।

टेलीग्राम-bot.py स्क्रिप्ट में कई भाग होते हैं। सबसे पहले, पहले की तरह, लाइब्रेरी आयात की जाती हैं और कमांड लाइन तर्क प्रारंभ किए जाते हैं।


 import threading import telegram from telegram.ext import Updater, CommandHandler, MessageHandler, Filters import openai from openai.embeddings_utils import cosine_similarity import numpy as np import pandas as pd import argparse import functools # Create an Argument Parser object parser = argparse.ArgumentParser(description='Run the bot which uses prepared knowledge base enriched with contextual embeddings') # Add the arguments parser.add_argument('--openai_api_key', type=str, help='API KEY of OpenAI API to create contextual embeddings for each line') parser.add_argument('--telegram_bot_token', type=str, help='A telegram bot token obtained via @BotFather') parser.add_argument('--file', type=str, help='A source CSV file with the questions, answers and embeddings') parser.add_argument('--topic', type=str, help='Write the topic to add a default context for the bot') parser.add_argument('--start_message', type=str, help="The text that will be shown to the users after they click /start button/command", default="Hello, World!") parser.add_argument('--model', type=str, help='A model of ChatGPT which will be used', default='gpt-3.5-turbo-16k') parser.add_argument('--num_top_qa', type=str, help="The number of top similar questions' answers as a context", default=3) # Parse the command-line arguments args = parser.parse_args() # Access the argument values openai.api_key = args.openai_api_key token = args.telegram_bot_token file = args.file topic = args.topic model = args.model num_top_qa = args.num_top_qa start_message = args.start_message


कृपया ध्यान दें कि इस मामले में, आपको एक ओपनएआई एपीआई कुंजी की भी आवश्यकता होगी, क्योंकि नॉलेज बेस से उपयोगकर्ता द्वारा दर्ज किए गए सबसे समान प्रश्न को खोजने के लिए, आपको सबसे पहले एपीआई को कॉल करके उस प्रश्न की एम्बेडिंग प्राप्त करनी होगी जैसा कि हमने नॉलेज बेस के लिए किया था।


इसके अलावा, आपको आवश्यकता होगी:


  • टेलीग्राम_बॉट_टोकन - बॉटफादर की ओर से टेलीग्राम बॉट के लिए एक टोकन
  • फ़ाइल - नॉलेज बेस फ़ाइल के लिए एक पथ (मैं जानबूझकर यहां डिस्कॉर्ड के संदेशों के मामले को छोड़ देता हूं, क्योंकि मुझे लगता है कि यह एक विशिष्ट कार्य है, लेकिन यदि आवश्यक हो तो उन्हें आसानी से कोड में एकीकृत किया जा सकता है)
  • विषय - विषय का पाठ्य सूत्रीकरण (लेख की शुरुआत में उल्लिखित) जिसमें बॉट संचालित होगा
  • स्टार्ट_मैसेज - वह संदेश जिसे /स्टार्ट पर क्लिक करने वाले उपयोगकर्ता को दिखाई देगा (डिफ़ॉल्ट रूप से, "हैलो, वर्ल्ड!")
  • मॉडल - मॉडल का चयन (डिफ़ॉल्ट रूप से सेट)
  • num_top_qa - ज्ञानकोष से सबसे समान प्रश्न-उत्तर की संख्या जिसका उपयोग चैटजीपीटी अनुरोध के संदर्भ के रूप में किया जाएगा


इसके बाद नॉलेज बेस फ़ाइल की लोडिंग और प्रश्न एम्बेडिंग का आरंभीकरण होता है।


 # reading QA file with embeddings df_qa = pd.read_csv(file) df_qa['ada_embedding'] = df_qa.ada_embedding.apply(eval).apply(np.array)


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


 def retry_on_error(func): @functools.wraps(func) def wrapper(*args, **kwargs): max_retries = 3 for i in range(max_retries): try: return func(*args, **kwargs) except Exception as e: print(f"Error occurred, retrying ({i+1}/{max_retries} attempts)...") # If all retries failed, raise the last exception raise e return wrapper @retry_on_error def call_chatgpt(*args, **kwargs): return openai.ChatCompletion.create(*args, **kwargs)


OpenAI की अनुशंसा के अनुसार, टेक्स्ट को एम्बेडिंग में बदलने से पहले, नई पंक्तियों को रिक्त स्थान से प्रतिस्थापित किया जाना चाहिए।


 def get_embedding(text, model="text-embedding-ada-002"): text = text.replace("\n", " ") return openai.Embedding.create(input=[text], model=model)['data'][0]['embedding']


सबसे समान प्रश्नों की खोज करने के लिए, हम सीधे ओपनई लाइब्रेरी से लिए गए दो प्रश्नों के एम्बेडिंग के बीच कोसाइन दूरी की गणना करते हैं।


 def search_similar(df, question, n=3, pprint=True): embedding = get_embedding(question, model='text-embedding-ada-002') df['similarities'] = df.ada_embedding.apply(lambda x: cosine_similarity(x, embedding)) res = df.sort_values('similarities', ascending=False).head(n) return res


दिए गए सबसे समान प्रश्न-उत्तर युग्मों की एक सूची प्राप्त करने के बाद, आप उन्हें एक पाठ में संकलित कर सकते हैं, इसे इस तरह से चिह्नित कर सकते हैं कि चैटजीपीटी स्पष्ट रूप से निर्धारित कर सके कि क्या है।


 def collect_text_qa(df): text = '' for i, row in df.iterrows(): text += f'Q: <'+row['Question'] + '>\nA: <'+ row['Answer'] +'>\n\n' print('len qa', len(text.split(' '))) return text


उसके बाद, लेख की शुरुआत में वर्णित संकेत के "टुकड़ों" को एक पूरे में इकट्ठा करना पहले से ही आवश्यक है।


 def collect_full_prompt(question, qa_prompt, chat_prompt=None): prompt = f'I need to get an answer to the question related to the topic of "{topic}": ' + "{{{"+ question +"}}}. " prompt += '\n\nPossibly, you might find an answer in these Q&As [use the information only if it is actually relevant and useful for the question answering]: \n\n' + qa_prompt # edit if you need to use this also if chat_prompt is not None: prompt += "---------\nIf you didn't find a clear answer in the Q&As, possibly, these talks from chats might be helpful to answer properly [use the information only if it is actually relevant and useful for the question answering]: \n\n" + chat_prompt prompt += f'\nFinally, only if the information above was not enough you can use your knowledge in the topic of "{topic}" to answer the question.' return prompt


इस मामले में, मैंने डिस्कॉर्ड के संदेशों का उपयोग करके भाग को हटा दिया है, लेकिन यदि चैट_प्रॉम्प्ट! = कोई नहीं है तो आप अभी भी तर्क का पालन कर सकते हैं।


इसके अलावा, हमें एक ऐसे फ़ंक्शन की आवश्यकता होगी जो ChatGPT API से प्राप्त प्रतिक्रिया को टेलीग्राम संदेशों (4096 वर्णों से अधिक नहीं) में विभाजित कर दे:


 def telegram_message_format(text): max_message_length = 4096 if len(text) > max_message_length: parts = [] while len(text) > max_message_length: parts.append(text[:max_message_length]) text = text[max_message_length:] parts.append(text) return parts else: return [text]


बॉट चरणों के एक विशिष्ट अनुक्रम के साथ शुरू होता है, /स्टार्ट कमांड द्वारा ट्रिगर किए जाने वाले दो कार्यों को निर्दिष्ट करता है और उपयोगकर्ता से एक व्यक्तिगत संदेश प्राप्त करता है:


 bot = telegram.Bot(token=token) updater = Updater(token=token, use_context=True) dispatcher = updater.dispatcher dispatcher.add_handler(CommandHandler("start", start, filters=Filters.chat_type.private)) dispatcher.add_handler(MessageHandler(~Filters.command & Filters.text, message_handler)) updater.start_polling()


/start पर प्रतिक्रिया देने वाला कोड सीधा है:


 def start(update, context): user = update.effective_user context.bot.send_message(chat_id=user.id, text=start_message)


और किसी फ्री-फ़ॉर्म संदेश का जवाब देने के लिए, यह बिल्कुल स्पष्ट नहीं है।


सबसे पहले , विभिन्न उपयोगकर्ताओं के थ्रेड्स को अवरुद्ध करने से बचने के लिए, आइए थ्रेडिंग लाइब्रेरी का उपयोग करके उन्हें तुरंत स्वतंत्र प्रक्रियाओं में "अलग" करें।


 def message_handler(update, context): thread = threading.Thread(target=long_running_task, args=(update, context)) thread.start()


दूसरे , सभी तर्क long_running_task फ़ंक्शन के अंदर होंगे। बॉट के कोड को संशोधित करते समय त्रुटियों को आसानी से स्थानीयकृत करने के लिए मैंने जानबूझकर मुख्य अंशों को प्रयास/छोड़कर लपेट दिया।


  • सबसे पहले, हम संदेश को पुनः प्राप्त करते हैं और यदि उपयोगकर्ता संदेश के बजाय कोई फ़ाइल या छवि भेजता है तो त्रुटि को संभालते हैं।
  • फिर, हम search_similar का उपयोग करके सबसे समान प्रश्न-उत्तर खोजते हैं।
  • उसके बाद, हम Collect_text_qa का उपयोग करके सभी प्रश्न-उत्तरों को एक टेक्स्ट में एकत्रित करते हैं।
  • और हम Collect_full_prompt का उपयोग करके चैटजीपीटी एपीआई के लिए अंतिम संकेत उत्पन्न करते हैं।


 def long_running_task(update, context): user = update.effective_user context.bot.send_message(chat_id=user.id, text='🕰️⏰🕙⏱️⏳...') try: question = update.message.text.strip() except Exception as e: context.bot.send_message(chat_id=user.id, text=f"🤔It seems like you're sending not text to the bot. Currently, the bot can only work with text requests.") return try: qa_found = search_similar(df_qa, question, n=num_top_qa) qa_prompt = collect_text_qa(qa_found) full_prompt = collect_full_prompt(question, qa_prompt) except Exception as e: context.bot.send_message(chat_id=user.id, text=f"Search failed. Debug needed.") return


चूँकि ज्ञान आधार और विषय को अपने आधार से प्रतिस्थापित करते समय त्रुटियाँ हो सकती हैं, उदाहरण के लिए, स्वरूपण के कारण, एक मानव-पठनीय त्रुटि प्रदर्शित होती है।


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


 try: print(full_prompt) completion = call_chatgpt( model=model, n=1, messages=[{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": full_prompt}] ) result = completion['choices'][0]['message']['content'] except Exception as e: context.bot.send_message(chat_id=user.id, text=f'It seems like the OpenAI service is responding with errors. Try sending the request again.') return parts = telegram_message_format(result) for part in parts: update.message.reply_text(part, reply_to_message_id=update.message.message_id)



यह कोड के साथ भाग का समापन करता है।

प्रोटोटाइप

अब, ऐसे बॉट का एक प्रोटोटाइप सीमित प्रारूप में निम्नलिखित लिंक पर उपलब्ध है। जैसा कि एपीआई का भुगतान किया जाता है, आप प्रति दिन 3 अनुरोध तक कर सकते हैं, लेकिन मुझे नहीं लगता कि यह किसी को सीमित करेगा, क्योंकि सबसे दिलचस्प बात एक संकीर्ण विषय पर केंद्रित एक विशेष बॉट नहीं है, बल्कि AnythingGPT प्रोजेक्ट का कोड है, जो इस उदाहरण के आधार पर अपने ज्ञान के आधार के साथ अपने विशिष्ट कार्य को हल करने के लिए अपना खुद का बॉट बनाने के संक्षिप्त निर्देश के साथ GitHub पर उपलब्ध है। यदि आपने अंत तक पढ़ा है, तो मुझे आशा है कि यह लेख आपके लिए उपयोगी रहा होगा।



एक बॉट के साथ दैनिक संचार का स्क्रीनशॉट