- Spis Treści
- AI_devs3-Zadania
- PreworkApi
- S01E01 — Interakcja z dużym modelem językowym
- S01E02 — Przygotowanie własnych danych dla modelu
- S01E03 — Limity Dużych Modeli językowych i API
- S01E04 — Techniki optymalizacji
- S01E05 — Produkcja
- S02E01 — Audio i interfejs głosowy
- S02E02 - Rozumienie obrazu i wideo
- S02E03 — Generowanie i modyfikacja obrazów
- S02E04 — Połączenie wielu formatów
- S02E05 — Multimodalność w praktyce
- S03E01 — Dokumenty
- S03E02 — Wyszukiwanie Semantyczne
- S03E05 - Bazy Grafowe
- S04E02 — Przetwarzanie treści
Moje rozwiązania do zadań z kursu AI_devs3.
Gdy spisuję moje rozwiązania i przemyślenia to więcej wynoszę z nauki 😃
Zadanie polegało na przetestowaniu komunikacji z API, którego będziemy używać w dalszych zadaniach.
Była do dla mnie świetna okazja, żeby nauczyć się korzystać z Cursor IDE.
Moje rozwiązanie: PreworkApi
Jestem pod wrażeniem, jak przyjemnie programowało mi się z pomocą AI. Do tej pory używałem trochę GitHub Copilot, ale nie byłem pod jakimś dużym wrażeniem. Teraz gdy użyłem znacznie bardziej zintegrowanego z AI narzędzia, czułem się bardzo fajnie. AI sprawdziło się bardzo dobrze, może dlatego, że zadanie było dość prostsze, jednak zawsze w pracy są też prostsze czynności, które często są nudne. Chciałbym częściej używać do nich AI.
Zadanie polegało na automatyzacji procesu logowania z użyciem AI do rozwiązywania pytań ANTY CAPTCHA - to jak CAPTCHA, ale może je przejść tylko robot 🤖.
Moje rozwiązanie: S01E01
W implementacji wykorzystałem:
- HtmlAgilityPack do parsowania strony i wydobycia pytania
- OpenAI API (model GPT-4) do analizy pytania i generowania odpowiedzi
- HttpClient do komunikacji z API
- Dependency Injection do zarządzania zależnościami
- User Secrets do bezpiecznego przechowywania kluczy API i danych logowania
Ciekawe było wykorzystanie System Message w OpenAI API, który wymusza zwracanie wyłącznie liczby, bez dodatkowych wyjaśnień. To pokazuje, jak ważny jest odpowiedni prompt engineering w pracy z AI.
Zadanie polegało na wykorzystaniu modelu językowego do przetwarzania i analizy wiadomości w dwóch krokach.
Moje rozwiązanie: S01E02
W implementacji zastosowałem ciekawe podejście wykorzystujące sekwencyjne połączenie dwóch promptów, gdzie:
- Pierwszy prompt przygotowywał dane dla drugiego
- Model efektywnie pracował z własnymi wynikami
- Większość kodu została wygenerowana przez Cursor IDE, co pokazuje potencjał AI w codziennej pracy programisty
To doświadczenie pokazało, jak skuteczne może być łączenie różnych technik prompt engineeringu oraz wykorzystanie narzędzi AI do wspomagania procesu programowania.
Zadanie polegało na analizie dużego pliku z pytaniami i poprawienie w nim błędów.
Moje rozwiązanie: S01E03
Głównym wyzwaniem była wielkość pliku wejściowego — zbyt duża, by przekazać całość do modelu językowego. Zastosowałem więc podejście hybrydowe:
- Obliczenia matematyczne wykonałem programistycznie, bez użycia LLM
- Model językowy wykorzystałem tylko do analizy pytań i generowania odpowiedzi na podstawie wcześniej obliczonych wyników
To pokazuje, jak ważne jest odpowiednie rozdzielenie zadań między tradycyjne programowanie a AI, szczególnie przy ograniczeniach technicznych modeli językowych.
Zadanie polegało na napisaniu promptu, który nakieruje robota do celu, omijając przeszkody. Wykorzystanie do tego LLM okazało się zaskakująco trudne. Na początku próbowałem zrobić to bez wyznaczania konkretnej trasy modelowi, jednak bez skutku. Na razie rozwiązałem to zadanie w łatwiejszej wersji, gdzie model miał podążać za z góry ustaloną trasą. Moje rozwiązanie to definedTrackSolution.
W tym zadaniu należało wykorzystać lokalnie uruchomiony model językowy do anonimizacji tekstu poprzez ocenzurowanie wrażliwych danych (imion, nazwisk, nazw miast, ulic i wieku).
Moje rozwiązanie wykorzystało:
- Model llama2 uruchomiony lokalnie przez ollama w kontenerze Docker
- Dwuetapowe podejście do rozwiązania problemu:
- Wykorzystanie modelu tylko do identyfikacji wrażliwych danych w tekście
- Programistyczna zamiana zidentyfikowanych fragmentów na "[CENSORED]" przy pomocy prostych operacji na tekście
Początkowo próbowałem rozwiązać problem jednym promptem, który miał zarówno identyfikować jak i zamieniać dane, ale model miał z tym problemy. Podzielenie zadania na dwa prostsze kroki znacząco poprawiło skuteczność rozwiązania.
Interesująco było też to, że model początkowo odmawiał "cenzurowania" danych ze względów etycznych, ale zaakceptował zadanie gdy zostało ono przedstawione jako "anonimizacja" danych.
Kod rozwiązania: S01E05
Zadanie polegało na wygenerowaniu transkrypcji z plików audio z zeznaniami świadków oraz wykorzystanie modelu do przeanalizowania ich w celu ustalenia pewnego konkretnego adresu.
Tym razem dla odmiany zdecydowałem się użyć pythona.
Generowanie transkrypcji: S02E01
Podczas pracy z modelem whisper w pythonie miałem kilka kłopotów. Najpierw ze zbyt nową wersją pythona, a potem z wersją numpy.
Już od paru lat nie używałem pythona, więc znalezienie rozwiązania mogłoby mi zając sporo czasu.
Cursor na szczęście niezwykle sprawnie podsunął mi gotowe rozwiązania :D
Mając gotowe transkrypcje również przy pomocy Cursor'a przeanalizowałem je i znalazłem odowiedź.
Mój prompt: prompt.txt
Co ciekawe najpierw uruchmiłem go na modelu gpt-4o. Model ten dał znacznie gorszy wynik niż claude-3.5-sonnet.
Zadanie polegało na odnalezieniu miasta na podstawie czterech fragmentów mapy, z których jeden był celowo błędny. Wykorzystałem model gpt-4o do analizy obrazów i znalezienia odpowiedzi.
Moje rozwiązanie: S02E02
W implementacji wykorzystałem:
- OpenAI API z modelem gpt-4o do analizy obrazów map
- Trzy etapowe podejście:
- Analiza każdego fragmentu mapy osobno w celu wydobycia kluczowych informacji
- Wygenerowanie zestawów map z jedną wykluczoną.
- Połączenie informacji i identyfikacja niespójnego fragmentu w celu znalezienia właściwego miasta
Na początku próbowałem podać wszystkie fragmenty mapy, łącznie z błędnym do zapytania, nie dostałem jednak w ten sposób poprawnej odpowiedzi. Uznałem, że skoro zapytania tekstowe są stosunkowo tanie i szybkie to mogę zrobić ich kilka, w każdym eliminując jeden z fragmentów mapy.
Tym razem Cursor kiepsko radził sobie z pisaniem kodu do zapytań OpenAI. Mimo dodania do kontekstu dokumentacji musiałem samemu ją przeczytać i napisać poprawny kod.
Zadanie polegało na wygenerowaniu obrazu robota na podstawie chaotycznych zeznań osoby, która go widziała. Wykorzystałem do tego DALL-E 3 poprzez OpenAI API.
Moje rozwiązanie: S02E03
W moim rozwiązaniu:
- Pobrałem zeznania świadka
- Przy pomocy LLM przekształciłem je na opis robota
- Wygenerowałem prompt do generacji obrazu
- Stworzyłem obraz robota
Przy okazji rozwiązania tego zadania dowiedziałem się, że OpenAI pozwala wygenerować obraz, do którego pobrania otrzyma się URL. Dzięki temu nie musiałem samemu hostować pliku obrazu :D
Celem zadania było znalezienie raportów zawierające informacje o schwytanych ludziach lub o śladach ich obecności oraz o naprawionych usterkach hardwarowych, oraz przygotowanie listy tylko tych dotyczących schwytanych ludzi, oraz oddzielnie tych, które dotyczyły usterek. Dane w raportach były w formatach tekstowych, audio oraz graficznych.
Główny kod: S02E04 Moje prompty: Prompts
Rozwiązałem zadanie następująco:
- Przekształciłem dane do formatu tekstowego
- text — bez transformacji
- audio — transkrypcja modelem Whisper
- zdjęcia — ekstrakcja tekstu za pomocą gpt-4o
- Użyłem modelu gpt-4o do analizy raportów i kategoryzacji ich na:
- raporty o schwytanych ludziach
- raporty o usterkach technicznych
- pozostałe
- Przygotowałem odpowiedź w wymaganym formacie JSON
Dodatkowo zaimplementowałem cache dla transformacji obrazów i audio. Dzięki temu nie musiałem powtarzać tych kosztownych operacji przy dopracowywaniu aplikacji oraz miałem łatwy wgląd w ich postać tekstową.
Rozwiązanie pokazuje, jak skutecznie można łączyć różne modele AI do przetwarzania danych w różnych formatach oraz jak ważna jest optymalizacja w przypadku kosztownych operacji AI.
Celem zadania było autonomicznie odpowiedzenie na pytania na podstawie informacji z artykułu internetowego.
Częścią wyzwania było to, że artykuł, jak to strony w internecie, był w formacie HTML z linkami do zdjęć i plików audio.
Moje rozwiązanie: S02E05
Moje rozwiązanie wykorzystuje:
- Zastosowanie serwisu Firecrawl do pobrania artykułu jako Markdown
- Parser markdown do wyodrębniania referencji do plików audio
- GPT-4o do wygenerowania opisu zdjęć
- Model Whisper do transkrypcji plików dźwiękowych
- Cache'owanie opisów zdjęć i transkrypcjach, żeby uniknąć powtarzania tych operacji
- GPT-4o do odpwiedzi na pytania na podstawie wiedzy z przygotowanego artykułu
Celowo przekonwertowałem artykuł na format Markdown przed przesłaniem go do modelu. Ten format dalej lepsze wyniki niż treść w HTMLu.
Moje rozwiązanie: S03E01
Opis dodam niedługo
Zadanie polegało na wykorzystaniu bazy wektorowej do znalezienia odpowiedniego raportu na podstawie zadanego pytania. Wykorzystałem do tego:
- Bazę wektorową Qdrant uruchomioną lokalnie w kontenerze Docker
- Model OpenAI text-embedding-3-small do generowania embeddingów
Kod rozwiązania: S03E02
W moim rozwiązaniu:
- Przygotowałem kolekcję w bazie Qdrant o nazwie "weapons_tests"
- Wczytałem raporty z testów broni z plików tekstowych
- Wygenerowałem embeddingi dla każdego raportu przy pomocy modelu OpenAI
- Zapisałem wektory wraz z metadanymi (data raportu) do bazy Qdrant
- Dla zadanego pytania wygenerowałem embedding i wykonałem wyszukiwanie wektorów najbardziej podobnych
Było to moje pierwsze doświadczenie z bazami wektorowymi. Szczególnie interesujące było to, jak baza potrafi znajdować semantycznie podobne dokumenty, nawet jeśli nie zawierają dokładnie tych samych słów, co zapytanie.
Wygenerowałem wizualizację podobieństwa wektorów w mojej bazie :D
Zadanie polegało na znalezieniu najkrótszej ścieżki połączeń między dwoma osobami na podstawie ich znajomości. Należy wykorzystać do tego dowolną bazę grafową.
Zdecydowałem się użyć Neo4j. Nie miałem wcześniej styczności z bazami grafowymi. Uznałem, że chcę poznać najpopularniejsze rozwiązanie.
Zanim zabrałem się za kodowanie przerobiłem podstawowy kurs Cypher Fundamentals. Znając już fundamenty mojej wybranej bazy wziąłem się za rozwiązanie zadana.
Moje rozwiązanie: S03E05
W implementacji wykorzystałem:
- Neo4j jako bazę grafową uruchomioną lokalnie w kontenerze Docker
- Oficjalny driver Neo4j dla .NET do komunikacji z bazą
- Język zapytań Cypher do operacji na grafie
Rozwiązanie składało się z następujących kroków:
- Pobranie danych użytkowników i ich połączeń z zewnętrznego API
- Załadowanie danych do bazy Neo4j:
- Utworzenie węzłów dla każdego użytkownika (
:User
) - Utworzenie relacji
:KNOWS
między połączonymi użytkownikami
- Utworzenie węzłów dla każdego użytkownika (
- Znalezienie najkrótszej ścieżki między wskazanymi użytkownikami za pomocą zapytania Cypher wykorzystującego algorytm
SHORTEST PATH
- Przekształcenie wyniku do wymaganego formatu i weryfikacja odpowiedzi
Zadanie nie było trudne, ale była to moja pierwsza styczność z bazami grafowymi. Teraz zdecydowanie lepiej rozumiem, jak z nich korzystać i do rozwiązywania jakiego rodzaju problemów będę mógł je wykorzystać w przyszłosci😄
Zadanie polegało na użyciu fine-tuning, do rozpoznawania poprawnych oraz błędnych danych. Za pomocą kodu przygotowałem dane w odpowiednim formacie, na podstawie danych źródłowych.
Moje rozwiązanie: S04E02
W implementacji wykorzystałem:
- Format JSONL do przygotowania danych treningowych
- Strukturę danych zgodną z wymaganiami OpenAI dla fine-tuningu
- Podział danych na zbiór treningowy i walidacyjny w proporcji 80% do 20%
Program przetwarza dwa pliki źródłowe:
correct.txt
- zawierający przykłady poprawnych danych (oznaczone jako "1")incorrect.txt
- zawierający przykłady niepoprawnych danych (oznaczone jako "0")
Każdy przykład treningowy składa się z trzech wiadomości:
- System prompt definiujący zadanie
- Treść do kategoryzacji
- Oczekiwana odpowiedź (0 lub 1)
Do fine-tuningu wybrałem model 4o-mini, z uwagi na niskie koszty.
Po zakończeniu procesu fine-tuning skategoryzwałem dane w Open AI Playground.