পাইথন SDK ব্যবহার করে OpenAI-এর সহকারী API-এর সাহায্যে এজেন্ট তৈরির মাল্টি-পার্ট সিরিজের এটি প্রথম অংশ।
আমি যেভাবে এটি দেখতে চাই, একজন এজেন্ট আসলেই একটি সফ্টওয়্যারের একটি অংশ যা একটি এলএলএম (বড় ভাষা মডেল) ব্যবহার করে এবং মানুষের আচরণ অনুকরণ করার চেষ্টা করে। এর অর্থ হল এটি কেবল কথোপকথন করতে এবং ভাষা বুঝতে পারে না, তবে এটি এমন কাজও করতে পারে যা বাস্তব জগতের উপর প্রভাব ফেলে। এই ক্রিয়াগুলিকে সাধারণত টুল বলা হয়।
এই ব্লগ পোস্টে, আমরা তাদের পাইথন SDK ব্যবহার করে OpenAI-এর সহকারী API ব্যবহার করে কীভাবে একটি এজেন্ট তৈরি করব তা অন্বেষণ করব। পার্ট 1 হবে শুধু সহকারীর কঙ্কাল। যে, শুধু কথোপকথন অংশ.
আমি ফ্রেমওয়ার্ক অজ্ঞেয়বাদী হওয়ার উদ্দেশ্যে একটি CLI অ্যাপ তৈরি করতে বেছে নিয়েছি। আমরা উদ্দেশ্যমূলকভাবে আমাদের বাস্তবায়নকে একটি এজেন্ট বলব এবং সহজে দুটির মধ্যে পার্থক্য করতে সহকারী হিসাবে OpenAI SDK বাস্তবায়নকে উল্লেখ করব।
যখন এজেন্ট কল করতে সক্ষম এমন ফাংশনগুলির ক্ষেত্রে আসে তখন আমি শর্তাবলী সরঞ্জাম এবং ফাংশনগুলিকে বিনিময়যোগ্যভাবে ব্যবহার করি৷ পার্ট 2 আরও বিস্তারিতভাবে ফাংশন কলিন কভার করবে।
এই টিউটোরিয়ালটি অনুসরণ করতে, আপনার নিম্নলিখিতগুলির প্রয়োজন হবে:
অ্যাসিস্ট্যান্ট : অ্যাসিস্ট্যান্ট এপিআই-এর একজন অ্যাসিস্ট্যান্ট হল ব্যবহারকারীর মেসেজের উত্তর দেওয়ার জন্য কনফিগার করা একটি সত্তা। এটি নির্দেশাবলী, একটি নির্বাচিত মডেল, এবং ফাংশনের সাথে ইন্টারঅ্যাক্ট করতে এবং উত্তর প্রদান করার জন্য টুল ব্যবহার করে।
থ্রেড : একটি থ্রেড সহকারী API-এ একটি কথোপকথন বা সংলাপ উপস্থাপন করে। এটি প্রতিটি ব্যবহারকারীর ইন্টারঅ্যাকশনের জন্য তৈরি করা হয়েছে এবং চলমান কথোপকথনের জন্য একটি ধারক হিসাবে পরিবেশন করে একাধিক বার্তা থাকতে পারে।
বার্তা : একটি বার্তা হল একটি থ্রেডে যোগাযোগের একক। এটিতে পাঠ্য রয়েছে (এবং ভবিষ্যতে সম্ভাব্য ফাইল) এবং একটি থ্রেডের মধ্যে ব্যবহারকারীর প্রশ্ন বা সহকারী প্রতিক্রিয়া জানাতে ব্যবহৃত হয়।
রান : একটি রান একটি থ্রেড প্রক্রিয়াকরণ সহকারীর একটি উদাহরণ। এতে থ্রেড পড়া, টুল কল করবেন কিনা তা সিদ্ধান্ত নেওয়া এবং থ্রেডের বার্তাগুলির মডেলের ব্যাখ্যার উপর ভিত্তি করে প্রতিক্রিয়া তৈরি করা জড়িত।
প্রথম ধাপ হল venv
ব্যবহার করে একটি ভার্চুয়াল পরিবেশ তৈরি করা এবং এটি সক্রিয় করা। এটি নিশ্চিত করবে যে আমাদের নির্ভরতাগুলি পাইথন ইনস্টলেশন সিস্টেম থেকে বিচ্ছিন্ন করা হয়েছে:
python3 -m venv venv source venv/bin/activate
আসুন আমাদের একমাত্র নির্ভরতা ইনস্টল করি: openai
প্যাকেজ:
pip install openai
একটি main.py
ফাইল তৈরি করুন। আমাদের CLI অ্যাপের জন্য কিছু মৌলিক রানটাইম লজিক দিয়ে তৈরি করা যাক:
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
আপনি দেখতে পাচ্ছেন, CLI ইনপুট হিসাবে একটি ব্যবহারকারীর বার্তা গ্রহণ করে, এবং আমাদের প্রতিভা সহকারীর এখনও মস্তিষ্ক নেই 🧠 তাই সে ঠিক ফিরে বার্তাটি পুনরাবৃত্তি করে। এখনো তেমন স্মার্ট নই।
এখন, মজা 😁 (বা মাথাব্যথা 🤕) শুরু হয়। আমি এখনই চূড়ান্ত ক্লাসের জন্য প্রয়োজনীয় সমস্ত আমদানি সরবরাহ করব, যাতে আমি সংক্ষিপ্ততার জন্য কোডের নমুনাগুলি আমদানির বাইরে রেখেছিলাম বলে জিনিসগুলি কোথা থেকে আসছে তা আপনি আপনার মস্তিষ্কে তাক করবেন না। আসুন একটি নতুন ফাইল 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" )
ক্লাস কনস্ট্রাক্টরে, আমরা আমাদের OpenAI API কী পাস করে ওপেনএআই ক্লায়েন্টকে ক্লাস প্রপার্টি হিসাবে আরম্ভ করি। এর পরে, আমরা একটি assistant
শ্রেণীর সম্পত্তি তৈরি করি যা আমাদের নতুন তৈরি সহকারীর সাথে মানচিত্র তৈরি করে। আমরা name
এবং personality
পরবর্তীতে ব্যবহারের জন্য শ্রেণি বৈশিষ্ট্য হিসেবে সংরক্ষণ করি।
আমরা তৈরি করার পদ্ধতিতে যে name
যুক্তি দিয়ে যাচ্ছি তা শুধুমাত্র OpenAI ড্যাশবোর্ডে সহকারীকে শনাক্ত করার জন্য, এবং AI আসলে এই মুহুর্তে এটি সম্পর্কে সচেতন নয়। আপনাকে আসলে নামটি instructions
পাস করতে হবে যা আমরা পরে দেখব।
সহকারী তৈরি করার সময় আপনি ইতিমধ্যেই instructions
সেট করতে পারেন, কিন্তু এটি আসলে আপনার সহকারীকে গতিশীল পরিবর্তনের জন্য কম নমনীয় করে তুলবে।
আপনি client.beta.assistants.update
কল করে একজন সহকারীকে আপডেট করতে পারেন, কিন্তু গতিশীল মানগুলি পাস করার জন্য আরও ভাল জায়গা রয়েছে যা আমরা রানে পৌঁছানোর পরে দেখতে পাব।
মনে রাখবেন যে আপনি যদি এখানে instructions
পাস করেন এবং তারপরে একটি রান তৈরি করার সময়, সহকারীর instructions
রানের instructions
দ্বারা ওভাররাইট করা হবে। তারা একে অপরের পরিপূরক নয়, তাই আপনার প্রয়োজনের উপর ভিত্তি করে একটি বেছে নিন: স্ট্যাটিক নির্দেশাবলীর জন্য সহকারী স্তর বা গতিশীল নির্দেশাবলীর জন্য রান স্তর।
মডেলটির জন্য, আমি gpt-4-turbo-preview
মডেলটি বেছে নিয়েছি যাতে আমরা এই সিরিজের পার্ট 2-এ ফাংশন কলিং যোগ করতে পারি। আপনি gpt-3.5-turbo
ব্যবহার করতে পারেন যদি আপনি একটি পয়সার কয়েকটি ভগ্নাংশ সঞ্চয় করতে চান এবং যখন আমরা সরঞ্জামগুলি প্রয়োগ করি তখন নিজেকে বিশুদ্ধ হতাশার মাইগ্রেন দেওয়ার সময়।
GPT 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
এখনও খুব স্মার্ট না. একটি তোতাপাখির কাছাকাছি 🦜 একটি hobbit থেকে. পরবর্তী বিভাগে, আসল মজা শুরু হয়।
আপনি যখন একটি রান তৈরি করেন, তখন রানের স্থিতি পরীক্ষা করার জন্য আপনাকে পর্যায়ক্রমে Run
অবজেক্টটি পুনরুদ্ধার করতে হবে। এটাকে বলা হয় পোলিং, এবং এটা খুবই খারাপ। আপনার এজেন্টের পরবর্তী করণীয় নির্ধারণ করার জন্য আপনাকে পোল করতে হবে। ওপেনএআই এটিকে সহজ করার জন্য স্ট্রিমিংয়ের জন্য সমর্থন যোগ করার পরিকল্পনা করেছে। ইতিমধ্যে, আমি এই পরবর্তী বিভাগে পোলিং সেট আপ কিভাবে দেখাব.
নিম্নলিখিত পদ্ধতির নামগুলিতে _
নোট করুন যা পাইথনের মানদণ্ড নির্দেশ করে যে পদ্ধতিটি অভ্যন্তরীণ ব্যবহারের জন্য এবং সরাসরি বহিরাগত কোড দ্বারা অ্যাক্সেস করা উচিত নয়।
প্রথমে, আসুন একটি সাহায্যকারী পদ্ধতি তৈরি করি _create_run
একটি 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
অবজেক্ট গ্রহণ করে এবং বর্তমান Run status
বের করে। সমস্ত উপলব্ধ অবস্থা OpenAI ডক্সে পাওয়া যাবে। আমরা শুধু আমাদের বর্তমান উদ্দেশ্য অনুসারে কয়েকটি ব্যবহার করব।
আমরা এখন কিছু ত্রুটির পরিস্থিতি পরিচালনা করার সময় একটি সম্পূর্ণ স্থিতি পরীক্ষা করার জন্য একটি সময় লুপ চালাই। অ্যাসিস্ট্যান্ট API-এর প্রকৃত বিলিং কিছুটা ঘোলাটে, তাই নিরাপদে থাকার জন্য, আমি 2 মিনিটের পরে আমার রান বাতিল করতে বেছে নিয়েছি।
যদিও ওপেনএআই বাতিল 10 মিনিটের পরে চলে তার জন্য একটি expired
স্থিতি রয়েছে। যদি একটি দৌড়ে 2 মিনিটের বেশি সময় নেয়, তবে আপনার সম্ভবত সমস্যা আছে।
যেহেতু আমি প্রতি কয়েক মিলিসেকেন্ডে পোল করতে চাই না, তাই আমি 2-মিনিট চিহ্নে আঘাত না করা পর্যন্ত এবং আমার দৌড় বাতিল না করা পর্যন্ত প্রতি 1 সেকেন্ডে ভোট দেওয়ার মাধ্যমে আমার অনুরোধটি থ্রোটল করি। আপনি যা উপযুক্ত মনে করেন তার সাথে এটি সামঞ্জস্য করতে পারেন।
বিলম্বের পরে প্রতিটি পুনরাবৃত্তি, আমরা আবার রান স্ট্যাটাস নিয়ে আসি।
এখন, আসুন আমাদের 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 এ সম্পূর্ণ কোড খুঁজে পেতে পারেন।
আপনার পড়ার জন্য আপনাকে ধন্যবাদ. মন্তব্যে কোন চিন্তা এবং প্রতিক্রিয়া শুনতে খুশি. এই ধরনের আরও কন্টেন্টের জন্য আমাকে Linkedin-এ অনুসরণ করুন: https://www.linkedin.com/in/jean-marie-dalmasso-1b5473141/