यह पायथन एसडीके का उपयोग करके ओपनएआई के सहायक एपीआई के साथ एजेंटों के निर्माण पर एक बहु-भाग श्रृंखला में पहला भाग है।
जिस तरह से मैं इसे देखना पसंद करता हूं, एक एजेंट वास्तव में एलएलएम (बड़े भाषा मॉडल) का लाभ उठाने वाले सॉफ्टवेयर का एक टुकड़ा है और मानव व्यवहार की नकल करने की कोशिश कर रहा है। इसका मतलब है कि यह न केवल बातचीत कर सकता है और भाषा को समझ सकता है, बल्कि यह ऐसे कार्य भी कर सकता है जिनका वास्तविक दुनिया पर प्रभाव पड़ता है। इन क्रियाओं को आम तौर पर उपकरण कहा जाता है।
इस ब्लॉग पोस्ट में, हम यह पता लगाएंगे कि ओपनएआई के असिस्टेंट एपीआई का उपयोग करके उनके पायथन एसडीके का उपयोग करके एक एजेंट कैसे बनाया जाए। भाग 1 सहायक का ढाँचा मात्र होगा। यानी सिर्फ बातचीत का हिस्सा.
मैंने फ़्रेमवर्क अज्ञेयवादी होने के उद्देश्य से एक सीएलआई ऐप बनाने का निर्णय लिया। हम जानबूझकर अपने कार्यान्वयन को एक एजेंट कहेंगे और दोनों के बीच आसानी से अंतर करने के लिए ओपनएआई एसडीके कार्यान्वयन को एक सहायक के रूप में संदर्भित करेंगे।
जब उन कार्यों की बात आती है जिन्हें एजेंट कॉल करने में सक्षम होता है तो मैं टूल और फ़ंक्शन शब्दों का परस्पर उपयोग करता हूं। भाग 2 में फ़ंक्शन कॉलिन को अधिक विस्तार से कवर किया जाएगा।
इस ट्यूटोरियल का अनुसरण करने के लिए, आपको निम्नलिखित की आवश्यकता होगी:
असिस्टेंट : असिस्टेंट एपीआई में एक असिस्टेंट उपयोगकर्ता संदेशों का जवाब देने के लिए कॉन्फ़िगर की गई एक इकाई है। यह कार्यों के साथ बातचीत करने और उत्तर प्रदान करने के लिए निर्देशों, एक चुने हुए मॉडल और टूल का उपयोग करता है।
थ्रेड : असिस्टेंट एपीआई में एक थ्रेड एक वार्तालाप या संवाद का प्रतिनिधित्व करता है। यह प्रत्येक उपयोगकर्ता इंटरैक्शन के लिए बनाया गया है और इसमें कई संदेश शामिल हो सकते हैं, जो चल रही बातचीत के लिए एक कंटेनर के रूप में काम करेंगे।
संदेश : एक संदेश एक थ्रेड में संचार की एक इकाई है। इसमें टेक्स्ट (और भविष्य में संभावित फ़ाइलें) शामिल हैं और इसका उपयोग थ्रेड के भीतर उपयोगकर्ता प्रश्नों या सहायक प्रतिक्रियाओं को व्यक्त करने के लिए किया जाता है।
रन : रन असिस्टेंट द्वारा थ्रेड को प्रोसेस करने का एक उदाहरण है। इसमें थ्रेड को पढ़ना, टूल को कॉल करना है या नहीं, यह तय करना और थ्रेड के संदेशों की मॉडल की व्याख्या के आधार पर प्रतिक्रियाएं उत्पन्न करना शामिल है।
पहला कदम venv
उपयोग करके एक आभासी वातावरण बनाना और उसे सक्रिय करना है। यह सुनिश्चित करेगा कि हमारी निर्भरताएँ सिस्टम पायथन इंस्टालेशन से अलग हैं:
python3 -m venv venv source venv/bin/activate
आइए अपनी एकमात्र निर्भरता स्थापित करें: openai
पैकेज:
pip install openai
एक main.py
फ़ाइल बनाएँ। आइए हमारे सीएलआई ऐप के लिए कुछ बुनियादी रनटाइम तर्क को शामिल करें:
while True: user_input = input("User: ") if user_input.lower() == 'exit': print("Exiting the assistant...") break print(f"Assistant: You said {user_input}")
इसे python3 main.py
चलाकर आज़माएँ:
python3 main.py User: hi Assistant: You said hi
जैसा कि आप देख सकते हैं, सीएलआई एक उपयोगकर्ता संदेश को इनपुट के रूप में स्वीकार करता है, और हमारे जीनियस असिस्टेंट के पास अभी तक दिमाग नहीं है इसलिए वह संदेश को तुरंत दोहरा देता है। अभी इतना स्मार्ट नहीं हूं.
अब, मज़ा 😁 (या सिरदर्द 🤕) शुरू होता है। मैं अभी अंतिम कक्षा के लिए आवश्यक सभी आयात प्रदान करूंगा, ताकि आप अपना दिमाग इस बात पर न लगाएं कि चीजें कहां से आ रही हैं क्योंकि मैंने संक्षिप्तता के लिए आयात को कोड नमूनों से बाहर रखा है। आइए एक नई फ़ाइल agent.py
में Agent
क्लास बनाकर शुरुआत करें:
import time import openai from openai.types.beta.threads.run import Run class Agent: def __init__(self, name: str, personality: str): self.name = name self.personality = personality self.client = openai.OpenAI(api_key="sk-*****") self.assistant = self.client.beta.assistants.create( name=self.name, model="gpt-4-turbo-preview" )
क्लास कंस्ट्रक्टर में, हम अपनी ओपनएआई एपीआई कुंजी पास करके ओपनएआई क्लाइंट को क्लास प्रॉपर्टी के रूप में प्रारंभ करते हैं। इसके बाद, हम एक assistant
क्लास प्रॉपर्टी बनाते हैं जो हमारे नव निर्मित असिस्टेंट से मैप होती है। हम बाद में उपयोग के लिए name
और personality
वर्ग गुणों के रूप में संग्रहीत करते हैं।
क्रिएट मेथड में हम name
तर्क दे रहे हैं, वह केवल ओपनएआई डैशबोर्ड में असिस्टेंट की पहचान करने के लिए है, और एआई को वास्तव में इस बिंदु पर इसकी जानकारी नहीं है। आपको वास्तव में instructions
को नाम देना होगा जिसे हम बाद में देखेंगे।
असिस्टेंट बनाते समय आप पहले से ही instructions
सेट कर सकते हैं, लेकिन यह वास्तव में आपके असिस्टेंट को गतिशील परिवर्तनों के प्रति कम लचीला बना देगा।
आप client.beta.assistants.update
पर कॉल करके असिस्टेंट को अपडेट कर सकते हैं, लेकिन गतिशील मूल्यों को पारित करने के लिए एक बेहतर जगह है जिसे हम रन पर पहुंचने पर देखेंगे।
ध्यान दें कि यदि आप रन बनाते समय यहां बार-बार instructions
पारित करते हैं, तो सहायक के instructions
रन के instructions
से अधिलेखित हो जाएंगे। वे एक-दूसरे के पूरक नहीं हैं, इसलिए अपनी आवश्यकताओं के आधार पर एक चुनें: स्थिर निर्देशों के लिए सहायक स्तर या गतिशील निर्देशों के लिए रन स्तर।
मॉडल के लिए, मैंने gpt-4-turbo-preview
मॉडल चुना ताकि हम इस श्रृंखला के भाग 2 में फ़ंक्शन कॉलिंग जोड़ सकें। यदि आप उपकरण लागू करते समय अपने आप को शुद्ध हताशा का माइग्रेन देते हुए पैसे का कुछ अंश बचाना चाहते हैं तो आप gpt-3.5-turbo
उपयोग कर सकते हैं।
जीपीटी 3.5 कॉलिंग टूल्स में भयानक है; इससे निपटने की कोशिश में मैंने जो घंटे गँवाए हैं, वे मुझे यह कहने की अनुमति देते हैं। 😝 मैं इसे यहीं छोड़ दूँगा, और इस पर बाद में और अधिक जानकारी दूंगा।
एक एजेंट बनाने के बाद, हमें एक वार्तालाप सूत्र शुरू करने की आवश्यकता होगी।
class Agent: # ... (rest of code) def create_thread(self): self.thread = self.client.beta.threads.create()
और हम उस थ्रेड में संदेश जोड़ने का एक तरीका चाहेंगे:
class Agent: # ... (rest of code) def add_message(self, message): self.client.beta.threads.messages.create( thread_id=self.thread.id, role="user", content=message )
ध्यान दें कि फिलहाल, केवल user
भूमिका के साथ संदेश जोड़ना संभव है। मेरा मानना है कि ओपनएआई भविष्य के रिलीज में इसे बदलने की योजना बना रहा है क्योंकि यह काफी सीमित है।
अब, हम थ्रेड में अंतिम संदेश प्राप्त कर सकते हैं:
class Agent: # ... (rest of code) def get_last_message(self): return self.client.beta.threads.messages.list( thread_id=self.thread.id ).data[0].content[0].text.value
इसके बाद, हमारे पास अब तक क्या है इसका परीक्षण करने के लिए हम एक प्रवेश बिंदु run_agent
विधि बनाते हैं। वर्तमान में, run_agent
विधि केवल थ्रेड में अंतिम संदेश लौटाती है। यह वास्तव में रन नहीं करता है। यह अभी भी बुद्धिहीन है.
class Agent: # ... (rest of code) def run_agent(self): message = self.get_last_message() return message
वापस main.py
में, हम एजेंट और अपना पहला थ्रेड बनाते हैं। हम थ्रेड में एक संदेश जोड़ते हैं। फिर वही संदेश उपयोगकर्ता को वापस लौटाएं, लेकिन इस बार, उस लाइव थ्रेड से आ रहा है।
from agent import Agent agent = Agent(name="Bilbo Baggins", personality="You are the accomplished and renowned adventurer from The Hobbit. You act like you are a bit of a homebody, but you are always up for an adventure. You worry a bit too much about breakfast.") agent.create_thread() while True: user_input = input("User: ") if user_input.lower() == 'exit': print("Exiting the agent...") break agent.add_message(user_input) answer = agent.run_agent() print(f"Assistant: {answer}")
आइए इसे चलाएँ:
python3 main.py User: hi Assistant: hi
अभी भी बहुत स्मार्ट नहीं है. हॉबिट की तुलना में तोते के अधिक करीब। अगले भाग में असली मज़ा शुरू होता है।
जब आप एक रन बनाते हैं, तो आपको रन की स्थिति की जांच करने के लिए समय-समय पर Run
ऑब्जेक्ट को पुनः प्राप्त करने की आवश्यकता होती है। इसे मतदान कहते हैं, और यह बेकार है। आपके एजेंट को आगे क्या करना चाहिए यह निर्धारित करने के लिए आपको मतदान करने की आवश्यकता है। OpenAI ने इसे सरल बनाने के लिए स्ट्रीमिंग के लिए समर्थन जोड़ने की योजना बनाई है। इस बीच, मैं आपको दिखाऊंगा कि इस अगले भाग में मतदान कैसे स्थापित किया जाए।
निम्नलिखित विधि नामों पर _
नोट करें जो कि पायथन में यह इंगित करने के लिए मानक है कि विधि आंतरिक उपयोग के लिए है और इसे सीधे बाहरी कोड द्वारा एक्सेस नहीं किया जाना चाहिए।
सबसे पहले, आइए Run
बनाने के लिए एक सहायक विधि _create_run
बनाएं, और इस विधि को कॉल करने के लिए run_agent
अपडेट करें:
class Agent: # ... (rest of code) def get_breakfast_count_from_db(self): return 1 def _create_run(self): count = self.get_breakfast_count_from_db() return self.client.beta.threads.runs.create( thread_id=self.thread.id, assistant_id=self.assistant.id, instructions=f""" Your name is: {self.name} Your personality is: {self.personality} Metadata related to this conversation: {{ "breakfast_count": {count} }} """, ) def run_agent(self): run = self._create_run() # add this line message = self.get_last_message() return message
ध्यान दें कि रन बनाने के लिए हम thread.id
और assistant.id
कैसे पास करते हैं।
याद रखें कि मैंने शुरुआत में कैसे कहा था कि गतिशील निर्देश और डेटा भेजने के लिए एक बेहतर जगह है? रन बनाते समय वह instructions
पैरामीटर होगा। हमारे मामले में, हम नाश्ते की count
एक डेटाबेस से प्राप्त कर सकते हैं। यह आपको हर बार जब आप कोई उत्तर देना चाहते हैं तो विभिन्न प्रासंगिक गतिशील डेटा को आसानी से पारित करने की अनुमति देगा।
अब, आपका एजेंट अपने चारों ओर बदल रही दुनिया से अवगत है और उसके अनुसार कार्य कर सकता है। मुझे अपने निर्देशों में एक मेटाडेटा JSON ऑब्जेक्ट रखना पसंद है जो प्रासंगिक गतिशील संदर्भ रखता है। यह मुझे कम क्रियाशीलता के साथ और ऐसे प्रारूप में डेटा भेजने की अनुमति देता है जिसे एलएलएम वास्तव में अच्छी तरह से समझता है।
इसे अभी तक न चलाएं; यह काम नहीं करेगा क्योंकि हम अंतिम संदेश प्राप्त होने पर रन पूरा होने की प्रतीक्षा नहीं कर रहे हैं, इसलिए यह अभी भी अंतिम उपयोगकर्ता संदेश होगा।
आइए अपना मतदान तंत्र बनाकर इसका समाधान करें। सबसे पहले, हमें एक रन को बार-बार और आसानी से पुनः प्राप्त करने के लिए एक तरीके की आवश्यकता होगी, तो आइए एक _retrieve_run
विधि जोड़ें:
class Agent: # ... (rest of code) def _retrieve_run(self, run: Run): return self.client.beta.threads.runs.retrieve( run_id=run.id, thread_id=self.thread.id)
ध्यान दें कि किसी विशिष्ट रन को खोजने के लिए हमें run.id
और thread.id
दोनों को कैसे पास करना होगा।
हमारे एजेंट वर्ग में _poll_run
विधि जोड़ें:
class Agent: # ... (rest of code) def _cancel_run(self, run: Run): self.client.beta.threads.runs.cancel( run_id=run.id, thread_id=self.thread.id) def _poll_run(self, run: Run): status = run.status start_time = time.time() while status != "completed": if status == 'failed': raise Exception(f"Run failed with error: {run.last_error}") if status == 'expired': raise Exception("Run expired.") time.sleep(1) run = self._retrieve_run(run) status = run.status elapsed_time = time.time() - start_time if elapsed_time > 120: # 2 minutes self._cancel_run(run) raise Exception("Run took longer than 2 minutes.")
🥵 ओह, यह बहुत है... चलो इसे खोलो।
_poll_run
एक Run
ऑब्जेक्ट को तर्क के रूप में प्राप्त करता है और वर्तमान रन status
निकालता है। सभी उपलब्ध स्थितियाँ OpenAI डॉक्स में पाई जा सकती हैं। हम बस उनमें से कुछ का उपयोग करेंगे जो हमारे वर्तमान उद्देश्य के अनुरूप हों।
अब हम कुछ त्रुटि परिदृश्यों को संभालते हुए पूर्ण स्थिति की जांच करने के लिए थोड़ी देर का लूप चलाते हैं। असिस्टेंट एपीआई की वास्तविक बिलिंग थोड़ी अस्पष्ट है, इसलिए सुरक्षित रहने के लिए, मैंने 2 मिनट के बाद अपने रन रद्द करने का विकल्प चुना।
हालाँकि, जब OpenAI 10 मिनट के बाद चलता है तो इसकी समय सीमा expired
है। यदि एक दौड़ में 2 मिनट से अधिक समय लगता है, तो संभवतः आपको कोई समस्या है।
चूँकि मैं भी हर कुछ मिलीसेकंड में मतदान नहीं करना चाहता, इसलिए मैं अपने अनुरोध को केवल हर 1 सेकंड में मतदान करके दबा देता हूँ जब तक कि मैं 2 मिनट के निशान तक नहीं पहुँच जाता और अपनी दौड़ रद्द नहीं कर देता। आप इसे अपनी इच्छानुसार समायोजित कर सकते हैं।
देरी के बाद प्रत्येक पुनरावृत्ति, हम रन स्थिति फिर से लाते हैं।
अब, आइए उन सभी को हमारी run_agent
विधि में प्लग करें। आप देखेंगे कि हम पहले _create_run
के साथ रन बनाते हैं, फिर हम _poll_run
के साथ पोल करते हैं जब तक कि हमें कोई उत्तर नहीं मिलता है या कोई त्रुटि सामने नहीं आती है, और अंत में जब पोलिंग समाप्त हो जाती है, तो हम थ्रेड से अंतिम संदेश प्राप्त करते हैं जो अब एजेंट से होगा।
फिर हम संदेश को अपने रनटाइम लूप पर लौटाते हैं, ताकि इसे उपयोगकर्ता को वापस भेजा जा सके।
class Agent: # ... (rest of code) def run_agent(self): run = self._create_run() self._poll_run(run) # add this line message = self.get_last_message() return message
वोइला, अब जब आप अपना एजेंट दोबारा चलाएंगे, तो आपको हमारे मित्रवत एजेंट से उत्तर मिलेगा:
python3 main.py User: hi Assistant: Hello there! What adventure can we embark on today? Or perhaps, before we set out, we should think about breakfast. Have you had yours yet? I've had mine, of course – can't start the day without a proper breakfast, you know. User: how many breakfasts have you had? Assistant: Ah, well, I've had just 1 breakfast today. But the day is still young, and there's always room for a second, isn't there? What about you? How can I assist you on this fine day?
भाग 2 में, हम अपने एजेंट के लिए टूल को कॉल करने की क्षमता जोड़ेंगे।
आप पूरा कोड मेरे GitHub पर पा सकते हैं।
आपके पढ़ने के लिए धन्यवाद. टिप्पणियों में कोई भी विचार और प्रतिक्रिया सुनकर खुशी हुई। इस तरह की अधिक सामग्री के लिए लिंक्डइन पर मुझे फ़ॉलो करें: https://www.linkedin.com/in/jean-marie-dalmasso-1b5473141/