Incepcja, czyli programowanie wewnątrz promptu

W latach 50. w Stanach Zjednoczonych zawodowo programowaniem miało zajmować się ok. 1500 osób (według Martina Campbell-Kelly’ego).

Ponieważ do zapisywania kodu używano wówczas kart perforowanych, pisanie programów oraz tym bardziej ich poprawianie było umiejętnością niezwykle rzadką. Warto nadmienić natomiast, że wówczas programowanie stanowiło naturalną ścieżkę kariery dla kobiet – szacuje się, że od 30 do 50% programistów stanowiły kobiety.
Najnowsze źródła (Statista) podają, że obecnie mamy w populacji jakieś dwadzieścia parę milionów programistów i programistek. Ten absolutnie kosmiczny wzrost jest zapewne pochodną wielu czynników – powstawania nowych technologii, budowania aplikacji webowych dla niemal każdego produktu i usługi, potrzeby utrzymywania starych systemów, tysięcy hobbystycznych projektów pisanych przez amatorów.
Tylko czym tak naprawdę zajmują się dziś programiści i programistki, czym jest programowanie? Yannick Kilcher, badacz i youtuber, znany z dobitnego komentowania praktyk w świecie data science, ma gotową odpowiedź:

Ale każdy, kto jest w tej branży dłużej, powiedzmy od dwóch–trzech dekad, doświadczył przynajmniej kilku cykli mód, jakie zaczynały panować na kolejne nowe języki programowania, na nowy framework, programistyczny wzorzec. Jeśli zdarzyło Ci się jeszcze programować w językach niskopoziomowych, poczujesz, że wspominamy „stare dobre czasy” i że to, co teraz, to już nie to samo, bo dzisiaj nie trzeba tyle umieć (na pamięć!), nie trzeba dbać o zasoby sprzętowe z taką czułością, nie trzeba rozumieć niuansów systemu, a w nocy nie spędzają nam snu z powiek wskaźniki, przerwania i tzw. deadlocki.

Słowem – Assembler czy C są dla „prawdziwych programistów”, a Python dla dzieciaków.
I o ile owszem, tu i ówdzie można jeszcze spotkać relikty przeszłości, jak np. język COBOL czy Fortran, szczególnie w starych systemach bankowych albo w niektórych projektach uniwersyteckich, o tyle reszta świata przeniosła się raczej parę poziomów wyżej, na wygodniejsze warstwy programowania. Tutaj możesz prześledzić, jak zmieniała się popularność języków programowania:
https://www.youtube.com/watch?v=qQXXI5QFUfw

Ale co dziś, co jutro? Data science jako dziedzina wciąż się rozwija – mimo zagorzałych zwolenników MatlabaJuliiczy przenoszenia całego ekosystemu do JavaScriptu zdecydowanie najwięcej zwolenników ma właśnie Python, w którym dzisiaj można nawet zdawać maturę. Co istotne, to właśnie Python stał się międzynarodowym standardem w projektowaniu eksperymentów z AI.

Inżynieria promptu

Aczkolwiek, nawet jeśli Python jest „dla dzieciaków”, to dzieciaków programujących. A co z tymi, którzy tej umiejętności nie posiadają? Otóż tutaj wkracza prompt engineering, który daje zupełnie nowe możliwości nawet osobom, które do tej pory nigdy nie programowały.

Tworzenie promptu to programowanie?
Tej tezie można się przyjrzeć w dwójnasób. Po pierwsze, za pomocą odpowiedniego promptu możemy wygenerować kod, np. napisany w języku Python, ale wtedy zwykle ostatecznie i tak przydaje się podstawowa wiedza, jak ten kod poprawić, zmodyfikować itd.

Znacznie ciekawsza jest perspektywa druga, która samo tworzenie promptu, jego inżynierię, już traktuje jako – owszem, bardzo wysokopoziomowe – ale jednak programowanie. Oczywiście, jak to w przypadku dawnych wielbicieli Assemblera, pojawią się głosy, że to nie jest prawdziwe programowanie i tak się nie robi na produkcji, i to „dla dzieciaków”. Bo przecież tę samą operację można wykonać w Pythonie po stokroć szybciej (zużywając jednocześnie mniej energii czy pieniędzy).

Mimo to pokuszę się o eksperyment, który pozwoli sprawdzić możliwości, jakie daje samo napisanie lub modyfikowanie promptu, niekoniecznie znając się na przekleństwie zależności (dependency hell) czy wiedząc, jak odpalić dany kontener, na jakim klastrze i dlaczego najwyższe uprawnienia ma tylko kolega, który akurat jest na urlopie.

Prompting jako programowanie

Spróbujmy się przekonać, ile typowych konstrukcji znanych z języków programowania da się już teraz zasymulować, używając jedynie promptów, czyli pseudokodu bazującego na opisach kolejnych kroków i zależności w języku naturalnym.
Testowane z Claude Sonnet 3.5 i OpenAI GPT-4o.

Poniżej startowy kod, aby łatwo było korzystać z kolejno omawianych promptów. W promptach znajdują się „pola”, które wypełniam, używając mechanizmu prompt_template z LangChain.


import os
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI, ChatOpenAI

# Set your OpenAI API key
os.environ["OPENAI_API_KEY"] = "tu wklej swój API key dla openAI"

# Prompt template
prompt_template = "tu wklejasz swój prompt"

# Konfiguracja modeli
llm = OpenAI(temperature=0, model="gpt-4o")
chatmodel = ChatOpenAI(model="gpt-4o", temperature=0)

# Tworzenie szablonu promptu
prompt = PromptTemplate(
    template=prompt_template,
    input_variables=["initial_value", "max_value"]
)

# Tutaj wymieniasz wartości, które mają być "wstrzyknięte" do promptu
# W prompcie używasz {nazwa_zmiennej} w miejscu, gdzie ma pojawić się informacja
final_prompt = prompt.format(initial_value=105, max_value=150)

# Uruchamianie łańcucha z finalnym promptem
result = chatmodel.invoke(final_prompt)
print(result.content)

Zainstalowane pakiety przy użyciu pip jako środowisko typu „venv”.

langchain==0.0.235

langchain-core==0.3.13

langchain-openai==0.2.3

WARUNEK I PĘTLA WHILE

Zacznijmy od prostego warunku „if” i pętli typu „while”,

chcąc uzyskać dość trywialne dodawanie co 5.

You are an AI assistant capable of simulating loop-like behavior. Follow these instructions carefully:

  1. Begin with the initial value: {initial_value}.
  2. Apply this operation: Add 5 to the current value.
  3. Verify if the new value is less than or equal to {max_value}.
  4. If the current value is smaller than or equal to {max_value}, repeat steps 2-4 using the updated value.
  5. If the current value exceeds {max_value}, terminate the loop and display the final result.

Throughout the process, record each step and iteration. Your response should include:

  • Each intermediate value after an iteration.
  • The final result once the loop ends.

Example format of your response:
Iteration 1: 5 + 5 = 10
Iteration 2: 10 + 5 = 15
Iteration 3: 15 + 5 = 20

Final result: 35

Now, begin the loop simulation with the following values:
Initial value: {initial_value}
Max value: {max_value}

Your response:

W odpowiedzi dostaniemy piękne kolejne wartości wydrukowane jak na pierwszych zajęciach z informatyki.

Iteration 1: 105 + 5 = 110

Iteration 2: 110 + 5 = 115

Iteration 3: 115 + 5 = 120

Iteration 4: 120 + 5 = 125

Iteration 5: 125 + 5 = 130

Iteration 6: 130 + 5 = 135

Iteration 7: 135 + 5 = 140

Iteration 8: 140 + 5 = 145

Iteration 9: 145 + 5 = 150

Final result: 150

PĘTLA FOR

Czy w przypadku tej pętli też sobie tak dobrze poradzi?

You are an AI assistant capable of simulating a basic programming flow using commands. Follow these instructions carefully:

  • Do not include any additional information in the response; only follow the instructions.

Repeat the following process {number_of_times} times:

  1. Start with the initial text: {incoming_text}, and add something completely absurd to form a sentence.
  2. Append this sentence to the output.
  3. Use the new sentence as the incoming text for the next iteration.

Your response:

Również udaje się bez problemu wykonać taką instrukcję. Zaczynamy od „Poland is an amazing country” {incoming_text} i po 5 iteracjach {number_of_times} otrzymujemy:

SKOK W KODZIE
Zwykle w programach potrzebujemy też „skoków” do innych miejsc w kodzie. W najprostszej wersji języków typu Basic będzie to skok typu „goto”, czyli przejście do konkretnej linijki.
You are an AI assistant capable of simulating a basic programming flow commands. Follow these instructions carefully:
Do not add any additional information to the response, only follow the instructions.

  1. Asses initial text {incoming_text} sentiment and decide if it is positive or negative.
  2. If the sentiment is positive, go to step nr 5.
  3. If the sentiment is negative go to step nr 4.
  4. Return „this text is negative”. Finish the process.
  5. Return „this text is positive”. Finish the process.

W prostych sytuacjach taka konstrukcja może w zupełności wystarczyć. Ale jeśli chcemy robić to bardziej profesjonalnie, potrzebujemy już funkcji i/lub metod. Można zauważyć, że 3 i 4 linijka w powyższym prompcie to nasz odpowiednik funkcji „main”, który uruchamia poszczególne funkcje, sprytnie przekazując wartości. Dodatek „Finish the process” powoduje, że po wykonaniu 4 kroku, nie idziemy do piątego, tylko kończymy generację – trochę jak w przypadku metody „return” czy „exit”.

FUNKCJE
Zwykle szybko z jednolinijkowych instrukcji chcemy stworzyć bardziej rozbudowane i je zgrupować, tworząc metody lub funkcje. Poniżej przedstawiam próbę zasymulowania właśnie takich zachowań wewnątrz promptu.
You are an AI assistant capable of simulating a basic programming flow commands. Follow these instructions carefully:
Do not add any additional information to the response, only follow the instructions. Do not add anything about functions.
Creates an interesting story for a children’s book. Follow these instructions carefully:
Take the initial text {incoming_text} and run function „Create an blue version” {blue_times} times. Use this function output and run function „Create a white version” {white_times} times.

Function „Create a white version”

  1. Add new idea containing word „blue”. Do not repeat the same word. Make it funny.
  2. Return new sentence as input for the next iteration.

Function „Create an blue version”

  1. Add new idea containing word „white”. Do not repeat the same word. Make it serious.
  2. Return new sentence as input for the next iteration.
    Return only the final sentence.
    Verify you have followed the instructions correctly.

Your response should be in format:
<final_response> response </final_response>

Jeśli uruchomimy ten prompt, wpisując „I love red cars”, otrzymamy:
<final_response> I love red cars with white stripes that zoom past white clouds, and sometimes they race with white snowflakes, creating a white winter wonderland, where blue penguins juggle blue balloons, and blue elephants dance with blue butterflies. </final_response>

To dodatkowe zdanie „verify you have followed the instructions correctly” to klasyczna podpowiedź, aby LLM sprawdził swoje obliczenia przed podaniem wyników.

W takich miniprogramach często potrzebne jest wykonanie drobnych obliczeń. Ale przecież LLM-y nie potrafią w matematykę (jak mawiają na mieście…)?


You are an AI assistant capable of simulating a basic programming flow commands. Follow these instructions carefully:

  • Do not add any additional information to the response, only follow the instructions.
  • Do not add anything about functions.
  • Do not round calculations.

Take the initial value {incoming_value} and run function „multiplier”.
Take output from multiplier and run function „divider” with this value.

Function „multiplier”

  1. Start with input value and multiply it by 5.5
  2. Add „multiply_result: <result>” to final response.
  3. Return <result>

Function „divider”

  1. Start with input value and divide it by 11
  2. Add „divide_result: <result>” to final response.
  3. Return <result>

Verify you have followed the instructions correctly.
Your response should be in format:
<final_response> response </final_response>

Jeśli uruchomię powyższy prompt z wartością dla {incoming_value} równą 10, to z GPT-4o mini oraz Claude Sonnet otrzymam prawidłowe wyniki, czyli 55 i 5.

You are an AI assistant capable of simulating basic programming flow commands. Follow these instructions carefully:

  • Do not add any additional information to the response.
  • Do not add anything about functions.

You will receive a piece of data {ai_dict}.
The counter is set to 0.

Run the following steps in a loop {max_value} times:

  1. Increase the counter by 1.
  2. Replace the id field with the value of the counter.
  3. Create a 1-sentence summary of the „reasons” field. Be creative and funny. Use a different seed every time.
  4. Place the summary in the summary field.
  5. Add the full updated data to the response.
  6. Using the regex \bA(?:.?I.?|I)\b, replace the found word in „reasons” with a random word. Do not modify the summary.

Your response must match the exact format of the incoming data.

Na wejściu weźmy następujący słownik:

prepared_dict = {
„id”: 1,
„name”: „AI Benefits”,
„reasons”: [
„AI can process vast amounts of data quickly.”,
„It enhances decision-making in various fields.”,
„AI improves efficiency in many industries.”,
„It can perform dangerous tasks, keeping humans safe.”,
„AI assists in medical diagnoses and treatment plans.”
],
„summary”: „”
}

Po wykonaniu tego promptu otrzymamy 3 słowniki o id 1, 2, 3 z różnymi podsumowaniami wstawionymi w pole „summary” oraz lekko zmienionymi wersjami pola „reasons”:

{
„id”: 1,
„name”: „AI Benefits”,
„reasons”: [
„Banana can process vast amounts of data quickly.”,
„It enhances decision-making in various fields.”,
„Banana improves efficiency in many industries.”,
„It can perform dangerous tasks, keeping humans safe.”,
„Banana assists in medical diagnoses and treatment plans.”
],
„summary”: „AI is like a superhero, saving time, lives, and making decisions faster than a speeding bullet.”
}
{
„id”: 2,
„name”: „AI Benefits”,
„reasons”: [
„Cat can process vast amounts of data quickly.”,
„It enhances decision-making in various fields.”,
„Cat improves efficiency in many industries.”,
„It can perform dangerous tasks, keeping humans safe.”,
„Cat assists in medical diagnoses and treatment plans.”
],
„summary”: „AI is the ultimate multitasker, juggling data, decisions, and safety like a pro.”
}
{
„id”: 3,
„name”: „AI Benefits”,
„reasons”: [
„Pizza can process vast amounts of data quickly.”,
„It enhances decision-making in various fields.”,
„Pizza improves efficiency in many industries.”,
„It can perform dangerous tasks, keeping humans safe.”,
„Pizza assists in medical diagnoses and treatment plans.”
],
„summary”: „AI is like a wizard, conjuring up solutions and safety with a wave of its digital wand.”
}

WYJĄTKI
A co, gdy wkrada się błąd, który chcielibyśmy wyśledzić? Czy da się zasymulować łapanie wyjątków w stylu „try/except”?
Do not write python code. Do not add explanations.
simple_list is 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Try to:
Calculate 10/0
In case of failure:
Add to response „Division by zero is not allowed”
Try to:
Get the 11th element from the list
In case of failure:
Add to response „Index out of range”
W wyniku otrzymamy:
Division by zero is not allowed
Index out of range

Na tych kilku absolutnie trywialnych przykładach widać, że proste operacje znane z języków programowania są w pełni wspierane przez model, nawet jeśli formułujemy je wyłącznie w języku naturalnym. Oczywiście trzeba poprosić model o weryfikację, dodać ograniczenia odpowiedzi modelu, ale mimo że LLM-y nie uruchamiają wygenerowanego kodu, widać, że mogą uruchamiać zestawy instrukcji przypominające pseudokod.

No dobrze, ale dokąd nas zaprowadzą te odkrycia? Będziemy teraz wchodzić jeszcze na wyższą warstwę i chować logikę w językowym opisie promptu? I już nie trzeba będzie się uczyć Pythona czy innych języków programowania?

Obrzydzenie do promptowania
Nieraz już zaobserwowałem, że specjaliści AI/ML, którzy pracują w branży od paru lat, mają dość sarkastyczne podejście do pracy z LLM-ami. Pojawiają się wtedy komentarze typu „LLM jest @#$!@#$”, „Nigdy nie wiesz, co z niego wyjdzie”, „Niby działa”, „Prompt engineering jest dla dzieci” (jak ten Python chciałoby się dodać…) i tak dalej.

Jak się przekonać do tego programowania w języku, choćby angielskim, skoro nierzadko długimi latami uczyliśmy się projektowania modeli AI, błądząc w labiryntach tensorflow czy pytorch? Podążanie wzrokiem na wykres spadającej funkcji straty podczas uczenia się modelu, który zaprogramowaliśmy, jest chyba po prostu silniejszym źródłem dopaminy i bardziej uzależniającym niż czekanie na to, co w odpowiedzi wygeneruje nam LLM.

Prototypowanie
Przynajmniej w pierwszej fazie, kiedy chcemy coś zademonstrować czy omówić z klientem – zamiast budować start-up i pracować nad genialnym pomysłem dwa lata, można osiągnąć postać prototypu już w kilka tygodni, a może nawet dni (no dobra, nocy). Potrzebujesz systemu wyszukującego nazwy własne i konkretne informacje? Albo porównywarki plików? A może interfejsu graficznego „na szybko”? Albo masz do wykonania jakieś operacje na tekście – jest duża szansa, że zanim zbudujesz to rozwiązanie od zera, możesz je wypromptować, czyli na tyle składnie i logicznie opisać językiem naturalnym, żeby model zrobił wszystko, czego do realizacji swojego pomysłu potrzebujesz.

Przy wojnie cenowej między OpenAI i Google może okazać się, że Twój prototyp jest wystarczająco dobry i tani, żeby stał się jak typowa polska prowizorka w trakcie remontu, która zamiast dwóch tygodni wytrzymuje następnych 10 lat.

A nawet jeśli tak nie będzie, to dzięki szybkiej symulacji pozwoli i Tobie, i firmie zrozumieć, czego naprawdę (ale naprawdę) chciałby klient, bez ponoszenia kosztów prawdziwej produkcji. Szczególnie teraz, gdy LLM-y są multimodalne, a Google Gemini pozwala nawet na załączenie pliku PDF, wiele rzeczy można zrobić inaczej, prościej, szybciej.

I oczywiście nadal jest wiele czynników, które będą przemawiały za tym, by zbudować własny (mniejszy i tańszy) model: mała ilość danych, wymogi bezpieczeństwa, ograniczenia regionalne, branże regulowane i inne.

Masz prompt i co dalej?
Nawet jeśli nie skorzystasz z żadnej ciekawej techniki w swoim prompcie, a po prostu w kilku zwięzłych zdaniach opiszesz, co chcesz, by model zrobił z Twoimi danymi, to i tak istnieje prawdopodobieństwo, że:
• ktoś poprosi, żebyś zmienił prompt na dający odpowiedzi bardziej „takie i takie”, • zmieniasz model (biorąc pod uwagę, jak często OpenAI / Gemini / Anthropic wypuszczają nowe wersje, zmieniają ceny czy aktualizują istniejące modele, pewnie robisz to raz w tygodniu),
• używasz wspólnego parsera, w którym nagle ktoś coś zmieni (langchain!),
• plik json, który dostajesz z modelu LLM z niezrozumiałego powodu ZNÓW jest !@#$!#@$%, czyli strukturę ma „prawie” właściwą,
• albo potrzebna jest zmiana kontraktu API (który oparty jest o odpowiedź z LLM).

Każda z tych sytuacji rozgrywa się na styku kodu w Pythonie, opisu w prompcie i dostawcy modelu LLM, powodując spore zamieszanie w funkcjonowaniu Twojego programu. Prompt ma wtedy praktycznie takie samo znaczenie jak to, co programujesz w Pythonie.

Jak to ustabilizować?
Sprawdź format wyjścia z modelu
Testy parserów i instrukcji formatujących.

Sprawdź jakość wyjścia z modelu
Pydantic, walidatory.

Sprawdź tempo odpowiedzi
Czasem zmiana promptu na bardziej „programowy” lub ze złożonymi instrukcjami powoduje ogromny skok w opóźnieniach, np. z 2 s robi się 20 s i całość staje się bezużyteczna, jeśli użytkownik musi patrzeć tak długo na pasek postępu.

Sprawdź, czy model nie wpada w „repetitive spiral of death”
Pewnie widziałeś już coś w stylu „Polska to piękny kraj. Polska to świetny kraj. Polska to świetny i piękny kraj. Polska to niezwykły kraj” w litanii na łącznie kilkaset tokenów. Niby zdarza się to coraz rzadziej, ale nadal niektóre modele gigantów udaje się do tego sprowokować.

Przyjrzyj się uważnie
Automatyczne testy to tylko częściowe rozwiązanie. Czasem to, czy Twój product manager, Twój szef, ludzie w firmie i później klienci polubią odpowiedzi generowane przez Twój kod/model, będzie tak samo ważną metryką jak dokładność czy precyzja, tak uwielbiane w świecie uczenia maszynowego.

Kiedy zaczynaliśmy pierwsze LLM-owe projekty w Egnyte, też czułem pewien opór, bo miałem wrażenie, że to nie jest „data science na serio”, ale wkrótce okazało się, że krzywa nauki jest dość stroma, a próba balansu pomiędzy elastycznością, kreatywnością, użytecznością i prawdomównością LLM-ów jest grą na nieskończonym horyzoncie. Natomiast z czasem wszyscy pogodzili się z tym, że poprawki i tuningowanie promptu może trwać tak samo długo, jak szukanie błędu w kodzie przy użyciu debuggera, a może czasem nawet dłużej, biorąc pod uwagę brak stuprocentowej powtarzalności pomiędzy kolejnymi zapytaniami do modelu (a raczej niemal gwarantowaną odmienność tych odpowiedzi).

Patrząc na niesamowite tempo rozwoju modeli, które coraz lepiej radzą sobie nawet ze skomplikowanymi instrukcjami, można się spodziewać, że coraz lepiej będą też rozumiały takie „programowanie po angielsku”. I wtedy bardzo wysokopoziomowe pseudokody nikogo nie będą dziwić.

Let’s prompt!

Autor

Michal Dulemba

Doświadczony inżynier ML, youtuber i autor, znany ze swojego interdyscyplinarnego podejścia do technologii i mediów

Podziel się

Może Cię zainteresować