Gyakorlati útmutató GenAI feladatokhoz
1. Az útmutató használata és felépítése
Az útmutató Jupyter notebook formában van, amelynek előnye, hogy interaktív módon és magyarázatokkal kiegészítve vezet végig az egyes példafeladatokon.
Kipróbálhatsz benne minden részt, az egyes kódrészleteket lefuttathatod és rögtön megnézheted az eredményét. Ha valamire kíváncsi vagy; új cellákat hozhatsz létre, valamint bármilyen értéket megvizsgálhatsz, kódsorokat külön-külön is kipróbálhatsz és kísérletezhetsz. A példákon Python nyelven fogunk végigmenni.
Minden kódrészlethez részletes magyarázat tartozik, és iteratívan haladva az egyszerűtől a bonyolultabb példák felé, betekintést kaphatsz a jövőben is hasznos és alkalmazandó technikák működésébe.
A cellák futtatása egyszerű a következő parancsokkal:
- Alt + Enter: Futtatja az aktuális cellát és új üres cellát hoz létre alatta.
- Shift + Enter: Futtatja az aktuális cellát és a következő cellára lép. (hasznos, ha végig akarsz menni könnyedén egymás után a cellákon)
- Ctrl + Enter: Csak futtatja az aktuális cellát, és marad ugyanabban a cellában. (hasznos, ha egymás után többször akarod ugyanazt a cellát futtatni)
Az egymás után lefuttatott cellák sorrendjét követheted a cellák előtti számjelzések segítségével. Ha egy értéket több cellában is módosítasz, mindig a legutoljára futtatott cella eredménye lesz érvényes. A [ ] jelzés a még le nem futtatott cellákat jelzi.
Bármikor hozzáadhatsz új cellákat a mentés gomb melletti + ikonnal. Emellett másolhatsz, kivághatsz és beilleszthetsz cellákat; ezeket a lehetőségeket és a hozzájuk tartozó billentyűparancsokat az Edit menüpontban találod.
Ne felejtsd el sorban egymás után lefuttatni a cellákat, ha az útmutatóval együtt szeretnél haladni!
Kezdeti beállítások
Legelőször nézzük meg, hogy hogyan ellenőrizheted a Python verziódatt, telepíthetsz Python könyvtárakat és beállítjuk az OpenAI-os API kulcsod az API hívásokhoz.
Python verzió
Az alábbi paranccsal megnézheted, hogy a környzeten belül, ahol dolgozol, milyen verziójú Python van telepítve.
Python 3.12.0
Mi a projekten 3.12.0-es verziót használunk, ez később fontos lesz, mert több felhasznált Python package-ünk is ezzel a verzióval kompatibilis. A példa notebook futtatásánál most ez nem lényeges.
Python package telepítés notebookból
Ha szeretnél valamilyen python könyvtárat használni, ami hiányzik a létrehozott környezetedből és hibát kapsz miatta, akkor ezt könnyen telepítheted a notebookon belül is az alábbi módon:
Ezután pedig már csak importálnod kell a használathoz.
OpenAI kulcs beállítása
Ahhoz, hogy használhasd az OpenAI könyvtárat, először meg kell adnod a gyorstalpaló során generált api kulcsodat. Lehetőséged van ezt elmenteni környzeti változóként is, de most egyelőre az útmutató során csak egy változóban fogjuk elmenteni.
Beimportáljuk az OpenAI osztályt az openai könyvtárból és létrehozunk egy client objektumot, amit az API-hoz való kapcsolódáshoz fogunk használni. A client objektumnak pedig megadjuk a szükséges API kulcsot és szervezet azonosítót (organization), hogy hitelesítsük és konfiguráljuk a kapcsolatot az OpenAI szolgáltatásaival.
from openai import OpenAI
client = OpenAI(api_key=api_key, organization='org-Si4UK0mvRnYnsoGjdxYyrdeq')
Innentől kezdve minden API hívásnál fel fogjuk tudni használni az elkészült client objektumot!
2. Példa feladatok
Embedding vektor készítés és similarity search
Nézzünk meg először egy egyszerű hasonlósági keresős példát!
A similarity search (hasonlósági keresés) az a folyamat, amely során egy adatbázisban keresünk olyan objektumokat, amelyek a leghasonlóbbak egy megadott referenciaponthoz. Ez a keresés az objektumok vektoros reprezentációját (embedding) használja a hasonlóság mérésére, ezért először embeddigeket fogunk készíteni, erre pedig az OpenAI embedding modelljeit fogjuk felhasználni.
Ehhez először választanunk kell egy modellt a jelenleg elérhető embedding modellek közül. Ha megnyitod a linket, látod, hogy ezeknek különböző kimeneti dimenziója van, ami azt jelenti, hogy milyen hosszú lesz a vektor, amit generál. Minél nagyobb a kimeneti dimenzió, annál részletesebben és pontosabban tudja reprezentálni a mélyebb összefüggéseket és jelentéseket az adatokban, bár ez általában nagyobb számítási költséget és tárhelyet is igényel.
Mi most a text-embedding-3-small modellt fogjuk használni.
Miután megvan a szöveg amit vektorizálni szeretnénk, és a modell amit használni akarunk ehhez, az alábbi módon készíthetünk embedding vektort belőle. A tokenizálásról az embedding modell gondoskodik, nekünk csak a szöveget kell megadni.
response = client.embeddings.create(
input="Ide kerül a vektorizálandó szöveg.",
model="text-embedding-3-small"
)
print(len(response.data[0].embedding))
1536
Mivel ez egy 1536 hosszú vektort generált, így magát a vektort nem írattuk ki (ha van kedved, akkor hozz létre egy cellát, amiben kiíratod és megnézed, hogy hogyan is néz ki az elkészült vektor vagy maga a response objektum).
Látható, hogy a visszakapott response objektum tartalmazza az általunk vektorizált szöveg embeddingjét, és a print(len(response.data[0].embedding)) sor megadja ennek a vektornak a hosszát, ami a használt modell kimeneti dimenzióját jelzi.
Ahhoz, hogy jobban értsük milyen választ is kapunk valójában az API hívás során, nézzük meg milyen válaszobjektumot kaptunk.
openai.types.create_embedding_response.CreateEmbeddingResponse
list
Látható, hogy egy CreateEmbeddingResponse objektumot kaptunk, amelyben egy data lista van. Ennek az első (és egyetlen, mivel egyetlen szövegmintát küldtünk neki) eleme tartalmazza a kért vektort.
Az embeddingek alapján a hasonlóság mérése különböző távolsági metrikákkal történik, mint például a koszinusz-hasonlóság, euklideszi távolság vagy Manhattan-távolság. Ahhoz, hogy ezt kipróbáljuk, hozzunk létre egy több vektorból álló adathalmazt.
Az embeddingeket egy vektoros adatokat is kezelni képes adatbáziskezelő segítségével szoktuk tárolni, viszont most az egyszerűség kedvéért egy listába fogjuk beletenni őket.
Készítünk egy segédfüggvényt az embedding készítés hívásához.
def get_embedding(text, model="text-embedding-3-small"):
return client.embeddings.create(input=[text], model=model).data[0].embedding
Ezután megadjuk a példa szövegeinket és elkészítjük hozzájuk a vektoros reprezentációjukat, amit most tehát az egyszerűség kedvéért egy embeddings nevű listában fogunk tárolni.
texts = [
"A krokodilok gyakran élnek trópusi folyók és tavak közelében.",
"A cica puha bundája simogatásra csábít.",
"Az űrhajósok a világűrben lebegve nézik a Földet.",
"A jégkrém gyorsan elolvad a nyári napsütésben.",
"A hegymászó elérte a hófödte csúcsot, lenyűgöző kilátással maga előtt.",
"Egy varázsló a könyvéből idézve létrehoz egy kis tüzet."
]
embeddings = [get_embedding(text) for text in texts]
Ezután megadjuk a szöveget, amelyhez a leghasonlóbbat akarjuk megkeresni és abból is készítünk egy embeddinget.
example_text = "Egy krokodil lustán napozik a mocsár partján."
example_embedding = get_embedding(example_text)
OpenAI embeddingek egységvektorok, ami azt jelenti, hogy, a koszinusz hasonlóságot gyorsan ki lehet számítani egyszerű skaláris szorzattal. Ehhez a numpy python könyvtárat importáljuk be és használjuk.
Készítünk egy segédfüggvényt a leghasonlóbb elem meghatározásához, majd megnézzük, hogy a mi esetünkben milyen eredményt ad.
import numpy as np
def find_most_similar(embedding_list, texts, target_embedding):
# Initialize maximum similarity and best match
max_similarity = -1.0
most_similar_text = None
# Iterate through each embedding in the list
for i, embedding in enumerate(embedding_list):
# Calculate dot product (cosine similarity for unit vectors)
similarity = np.dot(target_embedding, embedding)
# Update most similar if this similarity is larger
if similarity > max_similarity:
max_similarity = similarity
most_similar_text = texts[i]
return most_similar_text, max_similarity
most_similar_text, similarity = find_most_similar(embeddings, texts, example_embedding)
print("Most similar text:", most_similar_text)
Most similar text: A krokodilok gyakran élnek trópusi folyók és tavak közelében.
Ahogy várható is volt, a példánkhoz szemantikailag leghasonlóbb szöveg a krokodilos volt.
Nézzük meg egy másik példára is.
example_text_2 = "1953. május 29-én, nem sokkal délelőtt fél 12 előtt Edmund Hillary és Tendzing Norgaj elérte a Mount Everest."
example_embedding_2 = get_embedding(example_text_2)
most_similar_text, similarity = find_most_similar(embeddings, texts, example_embedding_2)
print("Most similar text:", most_similar_text)
Most similar text: A hegymászó elérte a hófödte csúcsot, lenyűgöző kilátással maga előtt.
Tokenek száma
Honnan tudhatjuk, hogy hány tokenből áll egy string, mielőtt embeddeljük? Pythonban a tiktoken könyvtár segítségével szét lehet bontani a szöveget tokenekre. Érdekességképp nézzük meg, hogy az előző példáink hány tokenből álltak.
import tiktoken
encoding = tiktoken.get_encoding("cl100k_base") #for third-generation embedding models like text-embedding-3-small, use the cl100k_base encoding.
print(f"Text: \"{example_text}\"")
print(f"Number of characters: {len(example_text)}")
print(f"Number of tokens: {len(encoding.encode(example_text))}")
print("\n")
print(f"Text: \"{example_text_2}\"")
print(f"Number of characters: {len(example_text_2)}")
print(f"Number of tokens: {len(encoding.encode(example_text_2))}")
Text: "Egy krokodil lustán napozik a mocsár partján."
Number of characters: 45
Number of tokens: 19
Text: "1953. május 29-én, nem sokkal délelőtt fél 12 előtt Edmund Hillary és Tendzing Norgaj elérte a Mount Everest."
Number of characters: 109
Number of tokens: 41
Látszik tehát, hogy magyar nyelvnél kb 2-3 karakter egy token.
Egyszerű API hívás, prompt engineering
Az iteratív prompting egy remek módszer arra, hogy fokozatosan finomhangoljunk egy feladatmegoldást és növeljuk a modell válaszainak minőségét.
Vegyünk egy egyszerű példafeladatot, amiben az alábbi szöveget szeretnénk összegezni:
text_to_summarize = """
A krokodilok lenyűgöző teremtmények, történetük nagyjából 200 millió évre nyúlik vissza, ami őket igazi élő
fosszíliákká teszi a dinoszauruszok korából. Ezek a csúcsragadozók hihetetlenül jól alkalmazkodtak környezetükhöz,
állkapcsuk például több mint 352 kg/cm² erővel képes harapni, ami a legerősebb harapás az állatvilágban.
Félelmetes hírnevük ellenére a krokodilok viszonylag fejlett szociális viselkedést mutatnak; egyes fajok bonyolult
hangjelzéseket is használnak a kommunikációra. A nőstény krokodilok védelmező oldalukról ismertek, hiszen gondosan
őrzik fészkeiket, sőt, a frissen kikelt kicsinyeiket a szájukban szállítják a vízhez. Ezek a vízi hüllők figyelemre
méltóan képesek szabályozni testük sűrűségét kövek lenyelésével—ezt a viselkedést gastrolithy-nak nevezik, és segít
az egyensúly fenntartásában a víz alatt. Akár hónapokig képesek élelem nélkül túlélni, mivel jelentősen csökkentik
anyagcseréjüket. Életkoruk meghaladhatja a 70 évet, sőt, egyes példányok akár több mint egy évszázadot is élhetnek.
"""
Első lépésként vegyünk egy teljesen alap promptot. Bár a bemeneti adatunk magyarul van, angol nyelvű promptokat fogunk használni.
Az elkészült promptunkat system üzenetként fogjuk átadni az API hívás során. A prompt mellett beállítjuk, hogy milyen modellt szeretnénk használni (ebben az esetben az olcsó és gyors gpt-4o-mini-t) és beállíthatjuk a temperature-t is, hogy egy kicsivel determinisztikusabbá tegyük a választ.
Megjegyzés
A temperature beállításával befolyásolhatod a modell válaszainak determinisztikusságát. Az alacsonyabb temperature érték (pl. 0 vagy közeli érték) determinisztikusabb eredményeket ad, mert a modell ilyenkor a generálás során inkább a legvalószínűbb következő szót választja. Magasabb temperature értékek esetén a véletlenszerűség növekszik, és a válaszok kreatívabbá, de kevésbé kiszámíthatóvá válnak. Tehát a temperature csökkentésével elérheted, hogy a válaszok egy kicsit determinisztikusabbak legyenek. Ez 0 és 2 közötti értéket vehet fel, default módon 1-et vesz fel, ahol nem állítjuk be. Kipróbálhatod az egyes esetekben miben fog különbözni a generált kimeneted kisebb és nagyobb temperature értékekkel.
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_prompt},
],
temperature=0,
)
print(completion.choices[0].message.content)
A krokodilok 200 millió éves történetükkel élő fosszíliáknak számítanak, és hihetetlenül jól alkalmazkodtak környezetükhöz. Harapásuk a legerősebb az állatvilágban, 352 kg/cm² erővel. Félelmetes hírnevük ellenére fejlett szociális viselkedést mutatnak, bonyolult hangjelzéseket használnak, és a nőstények gondosan védik fészkeiket, valamint a kicsinyeiket. Képesek szabályozni testük sűrűségét kövek lenyelésével, és akár hónapokig is túlélnek élelem nélkül. Életkoruk meghaladhatja a 70 évet, egyesek pedig több mint 100 évig élnek.
Válasz formátuma
Ez egy egész jó összefoglaló lett, de nem túl átlátható és így is elég hosszúnak érződik. Adjuk meg a válasz elvárt formátumát.
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_prompt},
],
)
print(completion.choices[0].message.content)
- Krokodilok körülbelül 200 millió éve léteznek, így élő fosszíliák a dinoszauruszok korából.
- Rendkívül erős harapásuk van, amely meghaladja a 352 kg/cm²-t, ez a legerősebb az állatvilágban.
- Fejlett szociális viselkedést mutatnak, és egyes fajok bonyolult hangjelzéseket használnak kommunikációra.
- A nőstény krokodilok gondosan őrzik fészkeiket, és a kicsinyeiket a szájukban szállítják a vízhez.
- Képesek szabályozni testük sűrűségét kövek lenyelésével, amelyet gastrolithy-nak neveznek.
- Hónapokig képesek élelem nélkül túlélni, mivel csökkentik anyagcseréjüket.
- Életkoruk gyakran meghaladja a 70 évet, és néhány példány több mint 100 évet él.
Példák hozzáadása
Ez már egy fokkal átláhatóbb válasz, de ennél rövidebben is össze lehetne ezeket foglalni. Adjunk néhány példát, hogy a modell jobban megértse, milyen típusú válaszokat várunk el.
system_prompt = f"""
Summarize the given text in few bullet points.
<EXAMPLES>
1. "The restaurant was fully booked, and the food was delicious." -> Summary: "crowded restaurant and tasty food"
2. "The book is filled with exciting stories and twists." -> Summary: "exciting book"
3. "The painting uses vibrant colors and a modern style." -> Summary: "modern, vibrant painting"
</EXAMPLES>
Text to summarize:
{text_to_summarize}
"""
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_prompt},
],
)
print(completion.choices[0].message.content)
- Krokodilok 200 millió éves élő fosszíliák a dinoszauruszok korából.
- Csúcsragadozók, legnagyobb harapáserejük 352 kg/cm².
- Fejlett szociális viselkedés, bonyolult hangjelzések.
- Nőstények védelmező anyák, vigyázzák a fészkeket és a kicsinyeiket.
- Gastrolithy viselkedés: kövek lenyelése a test sűrűség szabályozására.
- Hónapokig élelem nélkül képesek élni, anyagcseréjük csökkentése révén.
- Életkoruk meghaladhatja a 70 évet, egyesek akár 100 évet is élhetnek.
Kontextus és stílus
További kontextust is hozzáadhatunk, hogy a modell jobban értse az elvárt nyelvezetet és stílust.
system_prompt = f"""
You are a kindergarten teacher telling exciting fun facts to little kids in a daycare.
Summarize the following informative text in a kind and funny style using a few bullet points.
<EXAMPLES>
1. "The restaurant was fully booked, and the food was delicious." -> Summary: "Crowded restaurant with yummy food!."
2. "The book is filled with exciting stories and twists." -> Summary: "Book full of surprises!"
3. "The painting uses vibrant colors and a modern style." -> Summary: "Colorful splashy painting!"
</EXAMPLES>
Give your answer in the language of the text to be summarized.
Text to summarize:
{text_to_summarize}
"""
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_prompt},
],
)
print(completion.choices[0].message.content)
- Krokodilok: élő foszíliák, akik 200 millió éve itt vannak!
- Ők a harapásbajnokok, akár 352 kg/cm²-erővel harapnak!
- Félelmetesek, de szuper barátságosak is, mert csoportokban élnek!
- Anyukák, akik védelmezik a kicsinyeiket és a szájukban viszik őket a vízhez!
- Titkos trükk: köveket nyelnek, hogy könnyebben ússzanak!
- Hónapokig elvannak kaja nélkül, és akár 100 évig is élhetnek!
Végső finomítás és részletek
Ez már egy szuper és könnyed összefoglaló a fenti szövegről, azonban még egy picit tudunk rajta finomatíni további részletek megadásával.
Egy ilyen egyszerű feladatnál kevésbé szembetűnő a különbség, de egy bonyolultabb feladat esetén a minél részletesebb és egyértelműbb leírás sokat tud segíteni, így most a COSTAR prompting technika segítségével meg fogjuk adni részletesen és pontosan mit várunk a modellünktől!
system_prompt = f"""
# CONTEXT #
You are a kindergarten teacher who loves sharing exciting fun facts with little kids in a daycare.
# OBJECTIVE #
Summarize the given informative text into a few playful bullet points.
# STYLE #
Your style should be that of a super nice kindergarten teacher who is friendly, funny, and loves telling
delightful stories to kids.
# TONE #
Playful, cheerful, and engaging.
# AUDIENCE #
You are telling stories to kindergarten children who love fun animal facts.
# RESPONSE #
Your response should be the summarized content written in bullet points.
You must reply in the language of the text to be summarized.
Use markdown formatting and highlight the main points in the bullets!
<EXAMPLES>
1. "The restaurant was fully booked, and the food was delicious." -> Summary: "Crowded restaurant with yummy food!."
2. "The book is filled with exciting stories and twists." -> Summary: "Book full of surprises!"
3. "The painting uses vibrant colors and a modern style." -> Summary: "Colorful splashy painting!"
</EXAMPLES>
<TEXT TO SUMMARIZE>
{text_to_summarize}
</TEXT TO SUMMARIZE>
"""
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_prompt},
],
)
print(completion.choices[0].message.content)
- **Krokodilok**: Ősi állatok, akik már 200 millió éve itt vannak! 🦖
- **Erős harapás**: Az állatvilág legerősebb harapásával rendelkeznek! 💪
- **Szociális lények**: Barna, csoportban élnek és bonyolult hangokat használnak egymás közt! 🎶
- **Gondoskodó anyukák**: A nőstény krokodilok védik a tojásaikat, és a piciket a szájukban szállítják! 🐊💖
- **Sűrűségszabályozás**: Köveket nyelnek, hogy egyensúlyozzanak a víz alatt! ⚖️
- **Hosszú élet**: Akár 70 évig, sőt, több mint 100 évig is élhetnek! 🎂🌊
Végül egy aranyos rövid összefoglalót kaptunk. :) 🐊 Láthattuk, hogy mennyit befolyásol a kimeneten a plusz információ és a részletes feladatmeghatározás.
Az iteratív prompt engineering lényege tehát az, hogy a promptokat folyamatosan finomítjuk és módosítjuk, hogy egyre közelebb kerüljünk az elvárt LLM működéshez, ezáltal pontosabb és jobb válaszokat kapjunk.
Minden egyes prompt módosítás után megvizsgáljuk a kapott kimenetet és aszerint módosítjuk a következő lépésben.
Különböző szerepek
Az előző példában látható volt, hogy a system promptot egy messages listában adtuk át, ahol egy role, azaz szerep és a content, tehát maga az üzenet tartalma volt megadva. A system promtnál system role-t használtunk, ebben fogalmaztuk meg az utasításainkat a modellnek. Itt megadhatunk többféle szerepet is, például user és assistant roleokat is, amivel egy egész beszélgetést is tudunk imitálni akár. A további role lehetőségekről az OpenAI oldalán olvashatsz, mi most egyelőre a user és assistant szerepköröket fogjuk megnézni.
Sok esetben nem elég egyetlen user üzenetet odaadnunk az LLM hívásnál, hiszen sokszor fontosak például az előzmények is. Ha a felhasználó megkérdezi, hogy „És ez mit jelent?“ akkor a megfelelő kontextus nélkül az LLM-nek fogalma sem lesz, hogy miről is van szó, ezért érdemes odaadnunk előzményeket is. Ehhez viszont fontos, hogy az LLM tudja, hogy milyen üzenet érkezett a usertől, milyen üzenet az assistant-tól, azaz ő mit válaszolt és mi volt maga a system üzenet amiben az utasításait kapta.
Egy beszélgetés előzmény például így nézhet ki:
messages=[
{"role": "system", "content": "You are a helpful assistant. Answer the user's last question."},
{"role": "user", "content": "Hello!"},
{"role": "assistant", "content": "Hello! How can I help you?"},
{"role": "user", "content": "How long is the video bank open?"}
]
Function calling
A function callingról részletesebb leírást az OpenAI oldalán találsz.
LLM hívás során megadhatunk különböző függvényeket, amelyeket meghívhat a feladata megoldása során, ha szükséges.
Nézzünk egy egyszerű példát, ahol a rendszer kérésre hozzáadhat új névjegyet egy adatbázishoz, ebben az esetben az egyszerűség kedvéért egy Python listához.
Vegyünk egy függvényt, amely hozzáad egy új névjegyet a névjegylistánkhoz.
contacts = [] # Névjegylistánk
def add_contact(name: str, phone: str):
contact = {"name": name, "phone": phone}
contacts.append(contact)
return f"Névjegy hozzáadva: {name}, telefon: {phone}"
Most, hogy megvan, melyik függvényt szeretnénk elérhetővé tenni a modell számára, létrehozunk egy „függvénydefiníciót“, amely leírja a függvény használatát a modell számára. Mikor a modell létrehoz egy függvényhívást, ezt az információt fogja használni arra, hogy az argumentumokat a megadott séma szerint generálja. Ebben: - megadjuk mire jó/mit csinál a függvény, hogy a modell tudja milyen esetben kell meghívni - megadjuk milyen paraméterek szükségesek a függvény meghívásához
Ezt JSON Schema segítségével tudjuk megadni.
Tippek
-
A function call-ok során érdemes figyelembe venni, hogy ha listával dolgozunk és darabszámot akarunk megadni, azt ne az OpenAPI definícióval (min: 3, max: 5) adjuk meg, hanem a mező leírásába kell írjuk bele (pl. „legalább 3 és legfeljebb 5 elem“), mert a másik módszert a rendszer figyelmen kívül hagyja.
-
A JSON-ban enum használatával előre definiált értékkészletet is megadhatsz választási lehetőségként. Ez segít elkerülni a szabad szöveg alapú tippelést, például egy meghívandó tool kiválasztásakor.
Ebben a példában a JSON Schema így fog kinézni:
functions = [
{
"name": "add_contact",
"description": "Add a new contact to the contact list by providing a name and phone number.",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The person's name"
},
"phone": {
"type": "string",
"description": "The person's phone number"
}
},
"required": ["name", "phone"]
}
}
]
Megadunk egy user üzenetet, amely esetében szükségszerű lesz a megadott függvény meghívása.
Az OpenAI modell automatikusan fel fogja ismerni a funkció hívás szükségességét a felhasználói utasítás alapján.
messages = [{"role": "user", "content": "Adj hozzá egy névjegyet Nagy Kamilla néven a 123-456-7890 számmal."}]
Ezután jöhet az API hívás, megadva a 4o-mini modellt, a fenti üzenetet és a definiált függvényt.
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
functions=functions,
function_call="auto"
)
A kapott response objektum így néz ki, és így tudjuk belőle kiszedni a meghívott függvényt és a hozzá tartozó argumentumokat:
ChatCompletion(id='chatcmpl-AL49MD2ZQog3iHRtt1mctCXcYuRDN', choices=[Choice(finish_reason='function_call', index=0, logprobs=None, message=ChatCompletionMessage(content=None, refusal=None, role='assistant', function_call=FunctionCall(arguments='{"name":"Nagy Kamilla","phone":"123-456-7890"}', name='add_contact'), tool_calls=None))], created=1729584204, model='gpt-4o-mini-2024-07-18', object='chat.completion', service_tier=None, system_fingerprint='fp_482c22a7bc', usage=CompletionUsage(completion_tokens=26, prompt_tokens=92, total_tokens=118, prompt_tokens_details={'cached_tokens': 0}, completion_tokens_details={'reasoning_tokens': 0}))
('add_contact', '{"name":"Nagy Kamilla","phone":"123-456-7890"}')
Végül pedig használjuk fel a kapott response-t a függvény meghívásában!
import json
message = response.choices[0].message
if message.function_call:
function_name = message.function_call.name
arguments = message.function_call.arguments
if function_name == "add_contact":
result = add_contact(**json.loads(arguments))
print(result)
else:
print("A modell nem hívott meg semmilyen függvényt.")
print("Kapcsolati lista:", contacts)
Névjegy hozzáadva: Nagy Kamilla, telefon: 123-456-7890
Kapcsolati lista: [{'name': 'Nagy Kamilla', 'phone': '123-456-7890'}]
A fenti példában láthatjuk, hogy megvizsgáltuk szerepelt-e a válaszban létrehozott függvényhívás, amennyiben igen, akkor a generált argumentumokkal meghívtuk a függvényünket.
Pontosan az történt, amit szerettünk volna, a modell kiemelte a megfelelő nevet és telefonszámot a szövegből, mint argumentum a függvénynek és végül hozzáadódott a névjegylistánkhoz a függvényünk segítségével.
Válasz streaming
Erről részletesebben az OpenAI Cookbookban olvashatsz.
Ha beállítjuk egy API hívásnál a stream=True flaget, a részleges üzenetváltozások el lesznek küldve folyamatosan, amint elérhetővé válnak, és a stream egy üres üzenettel zárul le.
Ez különösen hasznos lehet válaszadások esetén, így a felhasználónak nem kell végigvárnia míg egy hosszabb üzenet legenerálódik és visszaadjuk, hanem folyamatosan generálás közben látja a generálódó szöveget.
completion = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
stream=True
)
for chunk in completion:
print(chunk.choices[0].delta)
ChoiceDelta(content='', function_call=None, refusal=None, role='assistant', tool_calls=None)
ChoiceDelta(content='Hello', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content='!', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content=' How', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content=' can', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content=' I', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content=' assist', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content=' you', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content=' today', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content='?', function_call=None, refusal=None, role=None, tool_calls=None)
ChoiceDelta(content=None, function_call=None, refusal=None, role=None, tool_calls=None)
3. Gyakorló példák
Itt találsz néhány gyakorlófeladatot, amit akár itt, ebben a notebookban is megoldhatsz gyakorlásképpen. Könnyebb és kicsit elgondolkodtatóbb feladatok is vannak, hogy tudd gyakorolni a promtolást és a kipróbálhasd a különféle technikákat.
1. Visszajelzés kategorizálás
Írj egy promptot, amely segítségével a modell a felhasználótól beérkező rövid szöveges üzenetet a megfelelő kategóriába sorol. A kategóriák a következők: „Pozitív visszajelzés”, „Negatív visszajelzés”, „Kérdés”, vagy „Egyéb”.
Példa bemenet: „Nagyon tetszett a szolgáltatás, biztos visszatérek!”
Tipp: Készíts egy promptot, amely segít a modellnek azonosítani a hangnemet és a tartalmat, hogy a megfelelő kategóriát válassza ki. Kérd meg a modellt, hogy adja meg a választott kategóriát és indokolja meg röviden a döntését.
2. E-mail generálás
Készíts egy promptot, amely generál egy bemenetként kapott témában 3 különböző hivatalos e-mailt.
Példa bemenet: „panaszlevél sérült áruról“
Tipp: Használd a temperature paramétert a válaszok változatosságának növelésére. Vizsgálj különböző temperature értékeket és vizsgáld meg, hogyan változik a generált szöveg stílusa és tartalma a különböző beállításokkal.
3. Question reform
Mikor beérkezik egy üzenet és szeretnénk feldolgozni, nehéz dolgunk van az ún. follow-up kérdésekkel, amelyek visszautalnak az előző kérdés-válaszra.
Például:
- User: Hogyan nyithatok számlát a legegyszerűbben?
- Assistant: A VideóBankon keresztül nyithatsz legegyszerűbben számlát.
- User: És az meddig van nyitva?
Ebben az esetben az „És az meddig van nyitva?“ üzenetnél a szemantikus keresés és a kulcsszó keresés is nehézségekbe ütközhet, hiszen a lényeg, a „VideóBank“ szó hiányzik ahhoz, hogy megtaláljuk a megfelelő információt.
Írj egy promptot, ami a beérkezett kérdésből és az előzményekből készít egy új, önmagában is elegendő információt tartalmazó kérdést, mint az előző példánál maradva „Meddig van nyitva a Videóbank?“.
Tipp: Ügyelj arra is, hogy csak akkor módosítsuk a kérdést, ha önmagában nem állja meg a helyét és nem egyértelmű, hogy mire utal.
4. Naptári események létrehozása
Készíts egy promptot, amely segítségével a modell képes egy egyszerű naptári eseményt létrehozni.
Add meg a kezdési időpontot, a befejezési időpontot és az esemény leírását.
Tipp: Használj function callingot, hogy az esemény automatikusan hozzáadásra kerüljön egy naptárba.
+1 Kreatív probléma megoldása promptolással
Készíts egy promptot, amellyel a modell képes lesz létrehozni egy társasjáték alapszabályait egy új játékhoz. A szabályoknak ki kell térniük a játék célkitűzésére, a fő mechanizmusokra, valamint három különböző akciótípusra, amelyeket a játékosok végrehajthatnak.
Tipp: Gondold át, hogy mely kulcselemeket szeretnéd beépíteni, hogy a játék egyszerre izgalmas és élvezetes legyen, miközben meghatározod a promptban szereplő utasításokat és korlátokat.