Bu, Python SDK'yı kullanarak OpenAI'nin Asistan API'sine sahip Aracılar oluşturmaya ilişkin çok parçalı bir serinin ilk bölümüdür.
Benim bakış açıma göre, bir temsilci aslında sadece bir LLM'den (Büyük Dil Modeli) yararlanan ve insan davranışını taklit etmeye çalışan bir yazılım parçasıdır. Bu, yalnızca konuşup dili anlamakla kalmayıp, aynı zamanda gerçek dünya üzerinde etkisi olan eylemler de gerçekleştirebildiği anlamına geliyor. Bu eylemlere genellikle araçlar denir.
Bu blog yazısında, Python SDK'sını kullanarak OpenAI'nin Asistan API'sini kullanarak nasıl aracı oluşturulacağını keşfedeceğiz. Bölüm 1 asistanın sadece iskeleti olacak. Yani sadece konuşma kısmı.
Çerçeveden bağımsız olmak amacıyla bir CLI uygulaması oluşturmayı seçtim. Uygulamamıza bilerek bir Aracı adını vereceğiz ve OpenAI SDK uygulamasını, ikisini kolayca ayırt etmek için bir Asistan olarak adlandıracağız.
Aracının arayabileceği işlevler söz konusu olduğunda araçlar ve işlevler terimlerini birbirinin yerine kullanıyorum. Bölüm 2'de işlev çağrısını daha ayrıntılı olarak ele alacağız.
Bu öğreticiyi takip etmek için aşağıdakilere ihtiyacınız olacak:
Asistan : Asistanlar API'sindeki Asistan, kullanıcı mesajlarına yanıt verecek şekilde yapılandırılmış bir varlıktır. İşlevlerle etkileşimde bulunmak ve yanıtlar sağlamak için talimatları, seçilmiş bir modeli ve araçları kullanır.
Konu : Konu Başlığı, Asistanlar API'sindeki bir görüşmeyi veya diyaloğu temsil eder. Her kullanıcı etkileşimi için oluşturulur ve devam eden görüşme için bir kapsayıcı görevi gören birden fazla Mesaj içerebilir.
Mesaj : Mesaj, Konudaki bir iletişim birimidir. Metin içerir (ve muhtemelen gelecekte dosyalar) ve bir Konu içindeki kullanıcı sorgularını veya asistan yanıtlarını iletmek için kullanılır.
Çalıştır : Çalıştırma, Asistanın bir İş Parçacığını işlemesinin bir örneğidir. Konunun okunmasını, araçların çağrılıp çağrılmayacağına karar verilmesini ve modelin Konu Mesajlarına ilişkin yorumuna dayalı yanıtlar üretilmesini içerir.
İlk adım venv
kullanarak sanal bir ortam oluşturmak ve onu etkinleştirmektir. Bu, bağımlılıklarımızın sistem Python kurulumundan izole edilmesini sağlayacaktır:
python3 -m venv venv source venv/bin/activate
Tek bağımlılığımız olan openai
paketini kuralım:
pip install openai
Bir main.py
dosyası oluşturun. CLI uygulamamız için bazı temel çalışma zamanı mantığını dolduralım:
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
komutunu çalıştırarak deneyin:
python3 main.py User: hi Assistant: You said hi
Gördüğünüz gibi, CLI bir Kullanıcı mesajını girdi olarak kabul ediyor ve dahi Asistanımızın henüz bir beyni 🧠 yok, bu yüzden mesajı hemen tekrarlıyor. Henüz o kadar akıllı değil.
Artık eğlence 😁 (veya baş ağrıları 🤕) başlıyor. Son ders için gereken tüm içe aktarımları şimdi sağlayacağım, böylece, kısa olması açısından kod örneklerinin içe aktarımını dışarıda tuttuğum için, işlerin nereden geldiği konusunda kafanızı yormazsınız. agent.py
adlı yeni bir dosyada bir Agent
sınıfı oluşturarak başlayalım:
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" )
Sınıf yapıcısında, OpenAI API anahtarımızı ileterek OpenAI istemcisini bir sınıf özelliği olarak başlatıyoruz. Daha sonra yeni oluşturduğumuz Asistanımızla eşlenen bir assistant
sınıfı özelliği oluşturuyoruz. name
ve personality
daha sonra kullanmak üzere sınıf özellikleri olarak saklarız.
Create yöntemine aktardığımız name
argümanı yalnızca OpenAI panosundaki Asistanı tanımlamak içindir ve AI aslında bu noktada bunun farkında değildir. Aslında ismi daha sonra göreceğimiz instructions
aktarmanız gerekiyor.
Asistan'ı oluştururken instructions
zaten ayarlayabilirsiniz, ancak bu aslında Asistanınızın dinamik değişikliklere karşı daha az esnek olmasına neden olacaktır.
Bir Asistanı client.beta.assistants.update
öğesini çağırarak güncelleyebilirsiniz, ancak Çalıştırmalara geldiğimizde göreceğimiz dinamik değerleri iletmek için daha iyi bir yer vardır.
Bir Koşu oluştururken instructions
burada tekrar tekrar iletirseniz, koşu instructions
Asistan instructions
üzerine yazılacağını unutmayın. Birbirlerini tamamlamazlar; bu nedenle ihtiyaçlarınıza göre birini seçin: Statik talimatlar için Asistan seviyesi veya dinamik talimatlar için Çalıştırma seviyesi.
Model olarak gpt-4-turbo-preview
modelini seçtim ki bu serinin 2. bölümünde fonksiyon çağırmayı ekleyelim. Araçları uyguladığımızda kendinize saf bir hayal kırıklığı yaşatırken bir yandan da bir kuruşun birkaç kesrini tasarruf etmek istiyorsanız gpt-3.5-turbo
kullanabilirsiniz.
GPT 3.5, araçları çağırma konusunda berbattır; bununla başa çıkmaya çalışırken kaybettiğim saatler bunu söylememe izin veriyor. 😝 Bu konuyu burada bırakacağım ve bu konuya daha sonra değineceğim.
Bir temsilci oluşturduktan sonra bir konuşma dizisi başlatmamız gerekecek.
class Agent: # ... (rest of code) def create_thread(self): self.thread = self.client.beta.threads.create()
Ve bu konuya mesaj eklemenin bir yolunu isteyeceğiz:
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 )
Şu anda yalnızca user
rolüyle mesaj eklemenin mümkün olduğunu unutmayın. Oldukça sınırlayıcı olduğundan OpenAI'nin gelecekteki bir sürümde bunu değiştirmeyi planladığına inanıyorum.
Artık başlıktaki son mesajı alabiliriz:
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
Daha sonra, şu ana kadar elimizde olanı test etmek için bir giriş noktası run_agent
yöntemi oluşturuyoruz. Şu anda run_agent
yöntemi yalnızca ileti dizisindeki son iletiyi döndürür. Aslında bir Çalıştırma gerçekleştirmez. Hala beyinsiz.
class Agent: # ... (rest of code) def run_agent(self): message = self.get_last_message() return message
main.py
döndüğümüzde aracıyı ve ilk iş parçacığımızı oluşturuyoruz. Konuya bir mesaj ekliyoruz. Daha sonra aynı mesajı kullanıcıya geri gönderin, ancak bu sefer o canlı mesaj dizisinden geliyor.
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}")
Hadi çalıştıralım:
python3 main.py User: hi Assistant: hi
Yine de pek akıllı değil. Bir hobbitten ziyade bir papağana 🦜 daha yakın. Bir sonraki bölümde asıl eğlence başlıyor.
Bir çalıştırma oluşturduğunuzda, çalıştırmanın durumunu kontrol etmek için Run
nesnesini düzenli aralıklarla almanız gerekir. Buna oylama denir ve berbat bir şeydir. Temsilcinizin bundan sonra ne yapması gerektiğini belirlemek için anket yapmanız gerekir. OpenAI, bunu daha basit hale getirmek için akış desteği eklemeyi planlıyor. Bu arada, bir sonraki bölümde size oylamayı nasıl ayarlayacağınızı göstereceğim.
Aşağıdaki yöntem adlarında, yöntemin dahili kullanıma yönelik olduğunu ve harici kodla doğrudan erişilmemesi gerektiğini belirtmek için Python'da standart olan _
işaretine dikkat edin.
Öncelikle Run
oluşturmak için _create_run
yardımcı yöntemini oluşturalım ve bu yöntemi çağırmak için run_agent
güncelleyelim:
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
Bir çalıştırma oluşturmak için thread.id
ve assistant.id
değerlerini nasıl ilettiğimize dikkat edin.
Başlangıçta dinamik talimatların ve verilerin aktarılacağı daha iyi bir yer olduğunu söylediğimi hatırlıyor musunuz? Bu, Çalıştırmayı oluştururken instructions
parametresi olacaktır. Bizim durumumuzda kahvaltı count
bir veritabanından alınmasını sağlayabiliriz. Bu, her yanıt tetiklemek istediğinizde farklı ilgili dinamik verileri kolayca aktarmanıza olanak tanır.
Artık temsilciniz, etrafında değişen dünyanın farkındadır ve buna göre hareket edebilir. Talimatlarımda ilgili dinamik bağlamı koruyan bir meta veri JSON nesnesinin olmasını seviyorum. Bu, verileri daha az ayrıntılı olarak ve Yüksek Lisans'ın gerçekten iyi anladığı bir formatta aktarmamı sağlıyor.
Bunu henüz çalıştırmayın; işe yaramayacak çünkü son mesajı alırken çalıştırmanın tamamlanmasını beklemiyoruz, dolayısıyla bu yine de son kullanıcı mesajı olacak.
Bunu oylama mekanizmamızı geliştirerek çözelim. İlk olarak, bir çalıştırmayı tekrar tekrar ve kolayca geri getirmenin bir yoluna ihtiyacımız olacak, bu yüzden bir _retrieve_run
yöntemi ekleyelim:
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)
Belirli bir çalıştırmayı bulmak için hem run.id
hem de thread.id
nasıl aktarmamız gerektiğine dikkat edin.
Agent sınıfımıza bir _poll_run
yöntemi ekleyin:
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.")
🥵 Vay be, bu çok fazla... Haydi paketi açalım.
_poll_run
bağımsız değişken olarak bir Run
nesnesini alır ve geçerli Run status
çıkarır. Mevcut tüm durumlar OpenAI belgelerinde bulunabilir. Şu anki amacımıza uygun olanlardan sadece birkaçını kullanacağız.
Şimdi birkaç hata senaryosunu ele alırken tamamlanmış durumu kontrol etmek için bir while döngüsü çalıştırıyoruz. Asistan API'sinin gerçek faturalandırması biraz belirsiz, bu yüzden güvenli tarafta olmak için koşularımı 2 dakika sonra iptal etmeyi tercih ettim.
OpenAI'nin 10 dakika sonra çalışmayı iptal etmesi için expired
bir durum olmasına rağmen. Bir koşu 2 dakikadan fazla sürüyorsa muhtemelen bir sorununuz vardır.
Ayrıca birkaç milisaniyede bir yoklama yapmak istemediğim için, 2 dakikalık sınıra ulaşana kadar yalnızca her 1 saniyede bir yoklama yaparak isteğimi kısıtlıyorum ve koşumu iptal ediyorum. Bunu uygun gördüğünüz her şeye göre ayarlayabilirsiniz.
Gecikmeden sonraki her yinelemede Çalıştırma durumunu yeniden getiririz.
Şimdi tüm bunları run_agent
yöntemimize yerleştirelim. İlk önce _create_run
ile çalıştırmayı oluşturduğumuzu, ardından bir cevap alana veya bir hata oluşana kadar _poll_run
ile yoklama yaptığımızı ve son olarak yoklama bittiğinde, artık aracıdan gelecek olan iş parçacığından son mesajı aldığımızı fark edeceksiniz.
Daha sonra mesajı çalışma zamanı döngümüze geri göndeririz, böylece kullanıcıya geri gönderilebilir.
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
Voilà, şimdi temsilcinizi tekrar çalıştırdığınızda dost canlısı Temsilcimizden bir yanıt alacaksınız:
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. bölümde Temsilcimizin araçları çağırma özelliğini ekleyeceğiz.
Kodun tamamını GitHub'ımda bulabilirsiniz.
Okuduğunuz için teşekkür ederiz. Yorumlarda her türlü düşünceyi ve geri bildirimi duymaktan mutluluk duyarım. Bunun gibi daha fazla içerik için beni Linkedin'de takip edin: https://www.linkedin.com/in/jean-marie-dalmasso-1b5473141/