Site icon Co robię w wolnym czasie

Prompt engineering z OpenAI oraz biblioteką LangChain

DALL·E A minimalist watercolor style illustration of interconnected abstract shapes representing the concept of AI and prompt engineering Soft pastel colors

Tworzenie promptów to technika, która pozwala projektować precyzyjne i skuteczne pytania lub instrukcje dla modeli AI, aby generowały sensowne i dokładne odpowiedzi. Uważam, że z narzędziami takimi jak LangChain i OpenAI cały proces jest dużo prostszy i można uzyskać naprawdę imponujące rezultaty. Ale jak to działa w praktyce? Zapraszam do lektury 🙂

Pierwsze kroki

Na początek trzeba zainstalować odpowiednią bibliotekę:

pip install langchain_openai

Nie zapomnij wybrać modelu i ustawić swojego klucza API OpenAI:

OPENAI_API_KEY="paste your key here"
MODEL_ID="gpt-4o-mini"

Dlaczego LangChain?

Uważam, że integracja LangChain z OpenAI to świetna sprawa – tworzenie i interakcja z modelami AI stają się naprawdę proste. Oto szybki przykład:

from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model=MODEL_ID,
    openai_api_key=OPENAI_API_KEY
)

response = model.invoke("What is the wider meaning of life? Explain it in one sentence with simple and understandable language.")
print(response.content)

W tym przypadku model generuje odpowiedź:

The wider meaning of life often refers to the search for purpose and connection, where we find fulfillment through relationships, experiences, and contributions to something greater than ourselves.

Tworzenie promptów pomaga ukierunkować odpowiedzi modelu, dając mu jasne i konkretne instrukcje. A co, jeśli potrzebujesz większej kontroli? Możesz dostosować parametry, takie jak temperature, max_tokens czy timeout:

model = ChatOpenAI(
    model=MODEL_ID,
    openai_api_key=OPENAI_API_KEY,
    temperature=0,
    max_tokens=None,
    timeout=None,
    max_retries=2
    )

result = model.invoke("What is the wider meaning of life? Explain it in one sentence with simple and understandable language.")
result.content

Taka elastyczność pozwala dostosować zachowanie modelu do różnych zadań. Dzięki temu można uzyskać odpowiedzi wysokiej jakości, które idealnie pasują do naszych celów. Uważam, że tworzenie promptów z LangChain i OpenAI to świetny sposób na lepsze wykorzystanie AI. Wypróbuj samodzielnie! 🌟

One-shot in-context learning

One-shot in-context learning to sposób na nauczenie modelu wykonania zadania, podając mu tylko jeden, jasny przykład. Zamiast trenować model na wielu danych, wystarczy, że umieścisz pojedynczy przykład bezpośrednio w promptcie. Dzięki temu model zrozumie zadanie bez większego wysiłku czy dodatkowego szkolenia.

Jak to działa? Najpierw pokazujesz modelowi przykład tego, czego od niego oczekujesz. Na przykład, jeśli zadaniem jest klasyfikacja zdań jako zdanie “szczęśliwe” lub “smutne”, możesz napisać coś takiego:

model.invoke("""
Classify the following sentence as either 'happy' or 'sad'.

Sentence: I love my friend!
Feeling: happy

Sentence: I am feeling unhappy today.
Feeling: """).content

Tutaj model widzi jeden kompletny przykład (“I love my friend!”) i na jego podstawie ma sklasyfikować nowe zdanie (“I am feeling unhappy today”). Odpowiedź, którą uzyskasz, to:

sad

One-shot learning jest świetny, kiedy nie masz wystarczająco dużo danych, żeby pokazać więcej przykładów, ale zależy Ci, żeby model dobrze zrozumiał zadanie. Uważam, że to niesamowite, ile model potrafi zdziałać, mając tylko jeden przykład, prawda? 😊

Ustrukturyzowana odpowiedź z LLM

Dlaczego warto korzystać ze strukturalnych odpowiedzi z modelu AI? Wyobraź sobie, że tworzysz aplikację, w której dane wyjściowe muszą być jasne, uporządkowane i łatwe do przetworzenia. Zamiast generować swobodny tekst, model może zwracać dane w określonym formacie, na przykład jako obiekt lub JSON. To sprawia, że odpowiedzi są ustrukturyzowane i od razu gotowe do użycia.

Uważam, że dobrym rozwiązaniem jest wykorzystanie w Pythonie biblioteki Pydantic, która pozwala określić dokładną strukturę wyjścia. Zobacz, jak to działa:

from pydantic import BaseModel, Field

class Feeling(BaseModel):
    feeling: str = Field(description="either happy or sad")
model_with_structure = model.with_structured_output(Feeling)

response = model_with_structure.invoke("""
Classify the following sentence as either 'happy' or 'sad'.

Sentence: I love my friend!
Feeling: happy

Sentence: I am feeling unhappy today.
Feeling: """).feeling
print(response)
# Output: sad

W tym przykładzie model upewnia się, że odpowiedź pasuje do struktury Feeling. Dzięki temu łatwiej jest zintegrować wynik z innymi częściami systemu, np. interfejsem użytkownika lub bazą danych.

A co z JSON-em? Jeśli wolisz, możesz poprosić model o zwracanie danych w formacie JSON. Jest on szeroko stosowany i łatwy do przetwarzania w wielu językach programowania. Oto przykład:

from pydantic import BaseModel, Field

model_with_structure = model.with_structured_output(None, method="json_mode")
result = model_with_structure.invoke("""
Classify the following sentence as either 'happy' or 'sad'.
Return a JSON object.

Sentence: I love my friend!
Feeling: happy

Sentence: I am feeling unhappy today.
Feeling: """)
print(result['Feeling'])
# Output: sad

Kiedy korzystasz ze strukturalnych odpowiedzi, Twoja aplikacja staje się bardziej niezawodna. Nie musisz już zgadywać ani analizować, co model miał na myśli. Dostajesz dokładnie to, o co prosisz – uporządkowane i gotowe do użycia dane. Wypróbuj samodzielnie!

Named Entity Recognition

Named Entity Recognition (NER) to proces, w którym identyfikujemy konkretne informacje w tekście, takie jak imiona, daty, zawody czy miejsca. Dlaczego to jest istotne? Wyobraź sobie, że masz długi dokument i musisz szybko znaleźć kluczowe fakty. NER automatycznie oznacza te informacje, oszczędzając czas i wysiłek.

Spójrz na ten przykład promptu, w którym model ma zidentyfikować i oznaczyć jednostki w zdaniach. Dozwolone etykiety to: NAME, YEAR, JOB i CITY:

prompt = """
Identify and label the entities in the following sentence. Allowed labels are: NAME, YEAR, JOB, CITY.

Sentence: John Smith was born in New York. He started his own company in 2005.
Entities: NAME: John Smith, YEAR: 2005, JOB: self employed, CITY: New York

Sentence: Ann working on large datasets in modern logistic companies.
Entities: NAME: Ann, YEAR: None, JOB: data scientist, CITY: None

Sentence: Bob works in London since 2010. He owns a small shop with souvenirs.
Entities: """

model.invoke(prompt).content

Po wywołaniu modelu otrzymujemy odpowiedź, która wyraźnie identyfikuje istotne szczegóły:

NAME: Bob, YEAR: 2010, JOB: shop owner, CITY: London

Za pomocą tego przykładu widać, jak NER upraszcza wyciąganie informacji. Uważam, że to świetne narzędzie, które może być niezwykle przydatne – czy to do podsumowywania artykułów, organizowania danych, czy wspierania obsługi klienta. Z jasnymi regułami i przykładami NER staje się skutecznym sposobem na zrozumienie i uporządkowanie informacji.

Chain-of-thought

Chain-of-thought to metoda, w której model rozwiązuje problemy krok po kroku, dzieląc złożone zadania na mniejsze, łatwiejsze do zrozumienia etapy. To podejście jest jak nakłonienie modelu do “myślenia na głos”, pomagając mu dojść do poprawnej odpowiedzi poprzez logiczne łączenie każdego kroku. Wygląda to, jakby odtwarzał sposób, w jaki ludzie rozwiązują problemy, organizując swoje myśli, co jest szczególnie pomocne przy zadaniach wymagających rozumowania logicznego lub matematyki.

Spójrz na poniższy prompt:

prompt = """
Problem: John has 11 books. He has lent one of them. How many books does John have now?

Phase1: John has 11 books.
Phase2: John has lent one of them.
Phase3: John has 11 - 1 = 10 books.

Answer: John has now 10 books.

Problem: Ann has 2 cars. She has rented one of them. After that she has bought one more. How many cars does Ann have now?
"""
model.invoke(prompt).content

Chain-of-thought pomaga modelowi rozwiązać problem w uporządkowany sposób:

Phase 1: Ann has 2 cars.  
Phase 2: Ann has rented one of them.  
Phase 3: Ann has 2 - 1 = 1 car left.  
Phase 4: Ann has bought one more car.  
Phase 5: Ann now has 1 + 1 = 2 cars.  

Answer: Ann has now 2 cars.

Dzieląc problem na logiczne etapy, model zapewnia, że każdy krok jest jasny i łatwy do śledzenia. Ta metoda jest pomocna zarówno dla uczniów, jak i profesjonalistów, ponieważ naśladuje sposób, w jaki ludzie podchodzą do rozwiązywania problemów – krok po kroku, etap za etapem.

Multi-task Prompting

Jak nazwa wskazuje, prompting typu Multi-tasking sprawia aby model wykonał więcej niż jedną rzecz naraz – pozwala na zadanie modelowi wykonania kilku zadań w jednym promptcie. To nie tylko oszczędza czas, ale także zapewnia, że wszystkie zadania są powiązane z tym samyk kontekstem. Na przykład, wyobraź sobie, że chcesz podsumowanie jakiegos tekstu i wypisania z niego kluczowych punktów. Dlaczego pisać dwa oddzielne prompty, skoro może to zrobić jeden? 😊

Jak to działa:

prompt = """
Below this is a sample text.
1. Resume this text in 100 chars
2. Write down the three points.

Text: String theory is a scientific idea that suggests that the smallest building blocks of the universe are not tiny particles, like atoms, but rather tiny, vibrating strings. These strings can vibrate in different ways, and the way they vibrate determines the type of particle they represent, such as electrons or quarks. Essentially, string theory aims to explain how everything in the universe is connected and how the fundamental forces of nature work together.
"""
model.invoke(prompt).content

Wynik może wyglądać tak:

1. String theory posits that the universe's building blocks are tiny, vibrating strings, not particles.

2. 
- Smallest building blocks are vibrating strings, not particles.
- Vibrations determine particle types (e.g., electrons, quarks).
- Aims to explain connections and fundamental forces in the universe.

To podejście zapewnia, że model generuje jasne i uporządkowane wyniki, jednocześnie radząc sobie z łatwością z wieloma zadaniami na raz. W praktyce jest to przydatne przy podsumowywaniu artykułów, analizowaniu tekstów czy przygotowywaniu informacji do prezentacji.

ChatPromptTemplate

ChatPromptTemplate to przydatne narzędzie w LangChain, które pomaga tworzyć dynamiczne prompty poprzez wstawianie zmiennych do szablonu. Ale po co to właściwie robić? Wyobraź sobie, że chcesz wyjaśnić skomplikowane zagadnienia, takie jak “teoria strun”, w prostych słowach zrozumiałych dla dzieci. Chcesz być gotowy na zmianę słowa na inne. Czy nie byłoby świetnie móc ponownie użyć tej samej struktury i po prostu zmienić słowo, które wyjaśniasz? Dokładnie to robi ChatPromptTemplate!

Oto przykład. W tym przypadku prosimy model o zdefiniowanie “teorii strun” w sposób, który dzieci mogą łatwo zrozumieć:

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_template("Write down the definition of the word {word} in simple and understandable language, suitable for kiddie level.")
chain = prompt | model
chain.invoke({"word": "the string theory"}).content
String theory is an idea in science that says everything in the universe, like stars, planets, and even tiny particles, is made up of tiny, wiggly strings. These strings are so small that we can't see them, but they vibrate in different ways, kind of like guitar strings. The way they vibrate helps to create all the different things we see around us. So, instead of thinking of particles as little dots, string theory tells us to think of them as tiny strings that can make all sorts of shapes and sizes!

Tak jak w poprzednich przykładach, możemy uzyskać odpowiedź w postaci tekstu dzięki StrOutputParser:

from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_template("Write down the definition of the word {word} in simple and understandable language, suitable for kiddie level.")
chain = prompt | model | StrOutputParser()
chain.invoke({"word": "the string theory"})
String theory is an idea in science that says everything in the universe, like stars, planets, and even tiny particles, is made up of tiny, wiggly strings. These strings are so small that we can't see them, but they vibrate in different ways, kind of like guitar strings. The way they vibrate helps to create all the different things we see around us. So, instead of thinking of particles as little dots, string theory tells us to think of them as tiny strings that make up everything!

A jeśli chcemy otrzymać uporządkowaną odpowiedź, na przykład obiekt JSON, możemy dodać JsonOutputParser:

from langchain_core.output_parsers import JsonOutputParser

prompt = ChatPromptTemplate.from_template("Write down the definition of the word {word} in simple and understandable language, suitable for kiddie level. Return the JSON object with the key: 'meaning'")
chain = prompt | model | JsonOutputParser()
response = chain.invoke({"word": "the string theory"})
response['meaning']
String theory is an idea in science that says everything in the universe is made up of tiny, tiny strings that are too small to see. These strings can vibrate, kind of like how a guitar string makes music when it vibrates. Depending on how they vibrate, they can create different things, like particles that make up atoms. So, string theory tries to explain how everything in the universe works by looking at these little strings!

Meta-prompting

Czym jest Meta-prompting? To sprytny sposób na ulepszenie promptu, wykorzystując samego LLM! Jak to działa? Najpierw tworzysz początkowy prompt, a potem prosisz LLM o jego poprawę. Celem jest uzyskanie bardziej szczegółowej, uporządkowanej lub specyficznej wersji oryginalnego promptu. W poniższych przykładach proces ten wykorzystuje zmienną o nazwie meta_prompt, która zawiera oryginalny prompt, który potrzebuje ulepszenia. Proste, prawda? 😊

Dlaczego warto używać Meta-prompting? Czasami stworzenie idealnego promptu może być trudne. Pozwalając LLM na jego dopracowanie, możesz zaoszczędzić czas i zapewnić lepsze rezultaty. Na przykład, możesz chcieć, aby ulepszony prompt nakierował LLM do generowania bardziej uporządkowanych lub dogłębnych odpowiedzi. A oto mądry trik: użyj bardziej zaawansowanego modelu do poprawy promptu. W ten sposób nawet prostszy model poradzi sobie z zadaniem, ponieważ to właśnie ulepszony prompt wykona większość ciężkiej pracy.

Oto przykład, jak zaimplementować Meta-prompting w kodzie:

from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

def run_chain(prompt_template, model_name, inputs, key="article"):
    prompt = ChatPromptTemplate.from_template(prompt_template)
    model = ChatOpenAI(model=model_name, openai_api_key=OPENAI_API_KEY)
    chain = prompt | model | StrOutputParser()
    return chain.invoke(inputs)

simple_prompt = """
Summarize this news article: {article}
"""
meta_prompt = """
Improve the following prompt to generate a more detailed summary. 
Adhere to prompt engineering best practices. 
Make sure the structure is clear and intuitive and contains the type of news, tags, and sentiment analysis.

{simple_prompt}

Only return the prompt.
"""
inputs = {"simple_prompt": simple_prompt}
improved_prompt = run_chain(meta_prompt, "o1-preview", inputs, key="simple_prompt")

article = """String theory is an idea in science that says everything in the universe, like stars, planets, 
and even tiny particles, is made up of tiny, wiggly strings. These strings are so small that we can't see them, 
but they vibrate in different ways, kind of like guitar strings. The way they vibrate helps to create all the different things 
we see around us. So, instead of thinking of particles as little dots, string theory tells us to think of them as tiny strings 
that can make all sorts of shapes and sizes!"""

result = run_chain(improved_prompt, "gpt-4o-mini", {"article": article}, key="article")
print(result)

Prosty prompt ewoluuje z podstawowego:

Summarize this news article: {article}

do bardziej zaawansowanego i szczegółowego:

Please provide a detailed summary of the following news article, ensuring that the structure is clear and intuitive. Your summary should include the following elements:

- **Type of News**: Specify the category or genre of the news (e.g., Politics, Technology, Sports, etc.).
- **Tags**: List relevant keywords or topics associated with the article.
- **Sentiment Analysis**: Analyze the overall sentiment of the article (e.g., positive, negative, neutral) and provide a brief explanation for your assessment.

**Article:**

{article}

W rezultacie, końcowe wywołanie API generuje wzbogaconą treść, taką jak:

**Type of News**: Science

**Tags**: String Theory, Physics, Universe, Particles, Vibrations, Theoretical Physics

**Sentiment Analysis**: The overall sentiment of the article is positive. This is due to the tone of wonder and fascination conveyed through the description of string theory, which presents an intriguing and innovative perspective on the composition of the universe. The language used evokes curiosity about the nature of reality, indicating an optimistic embrace of scientific exploration and understanding.

Meta-prompting przekształca prosty prompt w potężne narzędzie, umożliwiając dostarczanie precyzyjnych i uporządkowanych wyników przy minimalnym wysiłku.

Dla zainteresowanych: meta-prompting jest szeroko stosowany m.in w rozwiązaniach typu RAG.

Podsumowanie

Uważam, że korzystanie z LangChain razem z OpenAI z czasem zmieniło moje podejście do inżynierii promptów. Największą zaletą jest to, jak bardzo upraszcza tworzenie i ulepszanie promptów, co sprawia, że cały proces jest szybki i bardziej efektywny. Idealnie wpasowuje się w każdy workflow, niezależnie od tego, czy jesteś deweloperem, pisarzem, czy po prostu ciekawym jego użycia. Wypróbuj samodzielnie i zobacz, jak odmieni Twoje projekty! 😊

Exit mobile version