Wróć do katalogu
Season 2 15 Odcinki 59 min 2026

Learning DSPy (v3.1 - 2026 Edition)

Kompleksowy program nauczania krok po kroku dla frameworka DSPy, przechodzący od podatnych na błędy promptów tekstowych do modułowego, ustrukturyzowanego programowania i automatycznej optymalizacji.

Frameworki AI/ML Inżynieria promptów
Learning DSPy (v3.1 - 2026 Edition)
Teraz odtwarzane
Click play to start
0:00
0:00
1
Programowanie, a nie promptowanie
Ten odcinek omawia podstawową filozofię DSPy: odejście od kruchych promptów tekstowych na rzecz modułowego, ustrukturyzowanego programowania. Słuchacze dowiedzą się, dlaczego oddzielenie architektury systemu od instrukcji dla modelu językowego pozwala tworzyć bardziej solidne aplikacje AI.
3m 43s
2
Konfigurowanie modeli językowych
Dowiedz się, jak konfigurować i zarządzać modelami językowymi w DSPy. Ten odcinek omawia ustawianie modeli domyślnych, obsługę buforowania, nadpisywanie ustawień generowania oraz uzyskiwanie dostępu do różnych dostawców modeli za pośrednictwem LiteLLM.
4m 12s
3
Deklaratywne promptowanie za pomocą Signatures
Odkryj, jak Signatures w DSPy zastępują tradycyjne promptowanie. Ten odcinek wyjaśnia, jak deklaratywnie zdefiniować zachowanie wejścia i wyjścia modułu, używając zarówno wbudowanych ciągów znaków, jak i definicji opartych na klasach ze ścisłym typowaniem.
4m 12s
4
Bloki konstrukcyjne z Modules
Poznaj Modules w DSPy, podstawowe bloki konstrukcyjne dla programów opartych na modelach językowych. Ten odcinek omawia dspy.Predict, dspy.ChainOfThought oraz sposoby łączenia wielu modułów w większy, spójny potok.
4m 17s
5
Łączenie modeli za pomocą Adapters
Zrozum rolę Adapters w DSPy. Ten odcinek wyjaśnia, w jaki sposób ChatAdapter i JSONAdapter wypełniają lukę między abstrakcyjnymi sygnaturami DSPy a rzeczywistymi, wieloturowymi wiadomościami wysyłanymi do API modeli językowych.
3m 44s
6
Zarządzanie danymi za pomocą Examples
Dowiedz się, jak DSPy obsługuje zbiory danych do uczenia maszynowego. Ten odcinek omawia obiekt dspy.Example, rozróżnianie kluczy wejściowych i etykiet oraz przygotowywanie danych do ewaluacji i optymalizacji.
3m 54s
7
Definiowanie sukcesu za pomocą Metrics
Odkryj, jak oceniać programy DSPy za pomocą metryk. Z tego odcinka dowiesz się, jak pisać niestandardowe funkcje w Pythonie do oceniania wyników, jak używać argumentu trace, a nawet jak wykorzystać podejście AI-as-a-judge do ewaluacji dłuższych form.
4m 35s
8
Wprowadzenie do Optimizers
Wejdź w sam środek magii DSPy: Optimizers. Ten odcinek zawiera przegląd tego, co robią optymalizatory, omawia iteracyjny cykl optymalizacji oraz nietypową strategię podziału danych 20/80 do optymalizacji promptów.
3m 46s
9
Automatyczne uczenie Few-Shot
Dowiedz się, jak DSPy automatyzuje promptowanie few-shot. Ten odcinek skupia się na BootstrapFewShot i BootstrapFewShotWithRandomSearch, wyjaśniając, w jaki sposób syntetyzują, filtrują i wstrzykują wysokiej jakości przykłady do twoich promptów.
3m 50s
10
Optymalizacja instrukcji z MIPROv2
Zanurz się w automatyczne dostrajanie instrukcji. Ten odcinek bada MIPROv2 i COPRO, pokazując, jak DSPy wykorzystuje optymalizację bayesowską i algorytm coordinate ascent do odkrywania lepszych, nieintuicyjnych instrukcji w promptach.
4m 04s
11
Finetuning z BootstrapFinetune
Odkryj, jak destylować masywne modele językowe do mniejszych, wydajnych wersji. Ten odcinek omawia BootstrapFinetune, wyjaśniając, jak przekształcić program DSPy oparty na promptach w spersonalizowany model ze zaktualizowanymi wagami.
3m 27s
12
Zautomatyzowane użycie narzędzi z ReAct
Dowiedz się, jak dać modelom językowym dostęp do zewnętrznych narzędzi. Ten odcinek omawia moduł dspy.ReAct, demonstrując, jak budować autonomicznych agentów, którzy wnioskują i dynamicznie wchodzą w interakcje z API.
4m 06s
13
Ręczna obsługa narzędzi dla pełnej kontroli
Przejmij pełną kontrolę nad wykonywaniem narzędzi. Ten odcinek omawia ręczną obsługę narzędzi w DSPy przy użyciu dspy.Tool, dspy.ToolCalls oraz natywne wywoływanie funkcji dla aplikacji wrażliwych na opóźnienia.
3m 48s
14
Integracja narzędzi z MCP
Połącz swoich agentów z uniwersalnymi serwerami narzędzi. Ten odcinek wyjaśnia, jak używać Model Context Protocol (MCP) w DSPy, aby przy minimalnej konfiguracji wykorzystać ustandaryzowane narzędzia w różnych frameworkach.
4m 21s
15
Ensembles i meta-optymalizacja
Wykorzystaj DSPy do granic możliwości. Ostatni odcinek omawia transformacje programów za pomocą dspy.Ensemble oraz eksperymentalny meta-optymalizator BetterTogether, który łączy dostrajanie promptów z finetuningiem wag w celu uzyskania maksymalnej wydajności.
3m 34s

Odcinki

1

Programowanie, a nie promptowanie

3m 43s

Ten odcinek omawia podstawową filozofię DSPy: odejście od kruchych promptów tekstowych na rzecz modułowego, ustrukturyzowanego programowania. Słuchacze dowiedzą się, dlaczego oddzielenie architektury systemu od instrukcji dla modelu językowego pozwala tworzyć bardziej solidne aplikacje AI.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Uczymy się DSPy, odcinek 1 z 15. Spędzasz trzy godziny na perfekcyjnym dopracowaniu promptu, żeby twój model językowy wygenerował odpowiednie outputy. Potem dostawca wypuszcza nowy model, podmieniasz klucz API i cały twój pipeline się sypie. Utknąłeś na utrzymywaniu kruchych stringów, zamiast budować soft. To właśnie ten problem rozwiązuje filozofia pod hasłem Programming, Not Prompting. Ludzie często słyszą o DSPy i zakładają, że to po prostu kolejne narzędzie do szablonowania promptów, służące do wstawiania zmiennych w bloki tekstu. Wcale tak nie jest. DSPy to framework do kompilowania i optymalizacji control flow. Weźmy standardowy setup systemu, który czyta dokumenty i generuje raport z cytatami. W tradycyjnym podejściu piszesz ogromny blok tekstu. Tłumaczysz zadanie, dorzucasz dokumenty, dodajesz manualne instrukcje w stylu think step by step i dokładnie określasz, jak mają wyglądać cytaty. Takie podejście mocno sprzęga architekturę twojego systemu z przypadkowymi decyzjami. Twoja architektura to główna logika. Czyli wyciąganie faktów, tworzenie draftu podsumowania i doklejanie cytatów. Te przypadkowe decyzje to konkretne słowa, których użyłeś, żeby zmusić jeden konkretny model językowy do posłuszeństwa. Te same słowa nie zadziałają optymalnie na innym modelu, a nawet na innej wersji tego samego modelu. Kiedy dane lekko się zmienią, twój prompt się psuje. DSPy oddziela architekturę systemu od tych przypadkowych decyzji w prompcie. Przestajesz pisać długie stringi z instrukcjami. Zamiast tego definiujesz swoje zadanie wyłącznie w kategoriach inputów i outputów. Dla generatora raportów deklarujesz, że input to lista fragmentów tekstu, a output to draft tekstu i zestaw referencji do cytatów. Kiedy inputy i outputy są już zdefiniowane, łączysz je ze sobą za pomocą standardowego kodu. Tworzysz komponent do ekstrakcji, przekazujesz mu dokumenty i zbierasz fakty. Następnie przekazujesz te fakty do komponentu tworzącego draft. Na koniec możesz użyć prostej pętli, żeby zweryfikować draft z oryginalnymi faktami. Nie ma tu żadnych ręcznie pisanych stringów, które błagają model o poprawne sformatowanie outputu. Jest tylko ustrukturyzowana logika. I tu pojawia się kluczowy wniosek. Ponieważ twoja architektura jest zdefiniowana jako modułowy kod, kompilator może automatycznie przetłumaczyć tę strukturę na rzeczywiste prompty, których wymaga model językowy, jakiego akurat używasz. DSPy traktuje instrukcje dla modelu, kroki wnioskowania i przykłady few-shot jako parametry wewnętrzne. To są zmienne, które framework ma za zadanie zoptymalizować, a nie statyczny tekst, który wklepujesz ręcznie. Budujesz pipeline, definiujesz kształty danych i piszesz kroki egzekucji w standardowym Pythonie. Framework bierze na siebie nieprzewidywalne zadanie odkrycia najlepszego sposobu, by poprosić model językowy o niezawodne wykonanie tych kroków. To fundamentalnie zmienia developer experience. Spędzasz czas na debugowaniu logiki swojej aplikacji, a nie na zgadywaniu, który przymiotnik sprawi, że model zwróci uwagę na koniec zdania. Architektura twojego systemu powinna przetrwać dziwactwa modelu językowego, do którego akurat dzisiaj routujesz requesty. Dzięki za wysłuchanie, miłego kodowania wszystkim!
2

Konfigurowanie modeli językowych

4m 12s

Dowiedz się, jak konfigurować i zarządzać modelami językowymi w DSPy. Ten odcinek omawia ustawianie modeli domyślnych, obsługę buforowania, nadpisywanie ustawień generowania oraz uzyskiwanie dostępu do różnych dostawców modeli za pośrednictwem LiteLLM.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 2 z 15. Zrobienie pojedynczego API calla do modelu językowego jest proste. Ale zarządzanie wieloma providerami, cachowanie odpowiedzi i śledzenie historii promptów zazwyczaj zmusza cię do zbudowania i utrzymywania customowego wrappera. Konfiguracja modeli językowych w DSPy eliminuje tę żmudną robotę. Możesz zakładać, że potrzebujesz OpenAI SDK dla modeli GPT, Anthropic SDK dla Claude'a i osobnej biblioteki dla modeli lokalnych. Wcale nie. DSPy ogarnia to w jednolity sposób za pomocą pojedynczej klasy LM, która pod spodem używa LiteLLM. Aby użyć modelu, tworzysz instancję obiektu LM z DSPy i przekazujesz string zawierający nazwę providera, slash i nazwę modelu. Na przykład przekazujesz open ai slash gpt-4o-mini. Tworząc ten obiekt, możesz też przekazać standardowe parametry, takie jak temperature czy max tokens. Dzięki ujednoliconemu backendowi, nazwy tych parametrów pozostają spójne, niezależnie od tego, jakiego providera faktycznie wywołujesz. Możesz wchodzić w interakcję z tym obiektem LM bezpośrednio. Wywołujesz go jak zwykłą funkcję w Pythonie, przekazując prosty text string albo listę chat dictionaries. Przetwarza on input i zwraca listę wygenerowanych stringów. Domyślnie zwraca pojedynczy string w tej liście, ale możesz zażądać wielu completions. Takie bezpośrednie użycie jest proste, ale ręczne przekazywanie obiektu modelu do każdej funkcji w dużym codebase szybko robi się chaotyczne. Aby to rozwiązać, DSPy używa globalnego systemu konfiguracji. Definiujesz swój domyślny model językowy tylko raz, wywołując dspy dot configure i przypisując swój obiekt LM jako target. Każda kolejna operacja w DSPy jest automatycznie routowana przez ten model. Ale co, jeśli chcesz porównać outputy od różnych providerów? Powiedzmy, że chcesz przetestować, jak Claude 3.5 Sonnet radzi sobie z konkretną funkcją w porównaniu do twojego domyślnego modelu GPT. Zamiast nadpisywać global state, używasz dspy dot context. Tworzy to tymczasowy scope. Otwierasz blok with w Pythonie używając dspy dot context, przypisujesz Claude'a jako lokalny default i odpalasz swój kod. Kiedy blok się kończy, DSPy automatycznie wraca do twojego globalnego modelu GPT-4o-mini. To tyle, jeśli chodzi o routowanie requestów. A co z wydajnością? DSPy domyślnie cachuje każdą generację modelu, żeby oszczędzić czas i koszty API. Jeśli odpalisz dokładnie ten sam prompt z dokładnie takimi samymi parametrami, natychmiast zaserwuje ci zcachowaną odpowiedź. I tu pojawia się kluczowa kwestia. Czasami potrzebujesz świeżej generacji bez zmieniania promptu czy modyfikowania temperature. Żeby to zrobić, DSPy używa parametru o nazwie rollout id. Kiedy przekażesz nowe rollout id, na przykład unikalnego integera, DSPy traktuje to jako osobny request i omija cache. To wymusza na modelu wygenerowanie nowej sekwencji, dając ci kontrolę nad różnorodnością generacji, podczas gdy twoje główne inputy pozostają statyczne. Na koniec, podczas eksperymentowania, musisz dokładnie widzieć, co poszło po sieci. Każdy obiekt LM utrzymuje swój własny log interakcji. Możesz uzyskać dostęp do surowych danych przez atrybut history w obiekcie modelu. Aby uzyskać czytelne dla człowieka podsumowanie, wywołujesz metodę inspect history. Wypisuje ona dokładny prompt wysłany do providera i dokładną odpowiedź, którą otrzymałeś. Prawdziwą wartością tej warstwy konfiguracyjnej jest to, że całkowicie oddziela logikę twojej aplikacji od dziwactw providerów, zamieniając wybór modelu i cachowanie w proste, deklaratywne przełączniki. Dzięki za wysłuchanie, miłego kodowania wszystkim!
3

Deklaratywne promptowanie za pomocą Signatures

4m 12s

Odkryj, jak Signatures w DSPy zastępują tradycyjne promptowanie. Ten odcinek wyjaśnia, jak deklaratywnie zdefiniować zachowanie wejścia i wyjścia modułu, używając zarówno wbudowanych ciągów znaków, jak i definicji opartych na klasach ze ścisłym typowaniem.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 3 z 15. Standardowe sygnatury funkcji w Pythonie mówią twojemu kodowi, jakich typów danych ma się spodziewać. A co, gdyby sygnatura mogła dyktować faktyczną logikę samej funkcji, bez konieczności jawnego pisania instrukcji przez ciebie? To właśnie jest założenie deklaratywnego promptowania z sygnaturami. Możesz naturalnie pomylić sygnaturę DSPy ze standardową sygnaturą funkcji w Pythonie. Wyglądają podobnie, ale ich role są zasadniczo różne. Standardowa sygnatura w Pythonie definiuje ścisły interfejs danych. Sygnatura DSPy tak naprawdę deklaruje i inicjalizuje zachowanie modelu językowego. Nie piszesz promptu. Piszesz deklaratywną specyfikację tego, co ma się wydarzyć. Framework bierze tę specyfikację, analizuje oczekiwania dotyczące wejścia i wyjścia, i konstruuje dla ciebie pod spodem gotowy prompt. Najprostszym sposobem na zdefiniowanie sygnatury jest zrobienie tego inline, używając krótkiego stringa. Określasz swoje zmienne wejściowe, piszesz strzałkę kierunkową i określasz zmienne wyjściowe. Na przykład string "question strzałka answer" mówi DSPy, że model otrzyma pytanie i musi wygenerować odpowiedź. Możesz przekazać wiele wejść, na przykład "context przecinek question strzałka answer". I tu zaczyna się robić ciekawie. Nazwy zmiennych, które wybierzesz, mają realną wagę semantyczną. DSPy używa dokładnie tych nazw ze stringów do przypisywania ról w prompcie. Jeśli nazwiesz wejście "context", model zinterpretuje je jako informacje kontekstowe. Nie przekombinuj z tymi nazwami ani nie próbuj hackować słów kluczowych sprytnymi sztuczkami w prompcie. Używaj jasnych, opisowych angielskich słów do definiowania ról. Kiedy stringi inline nie są wystarczająco ekspresywne, przechodzisz na sygnatury oparte na klasach. Tworzysz nową klasę, która dziedziczy po klasie bazowej DSPy Signature. Wewnątrz tej klasy definiujesz swoje wejścia i wyjścia jako atrybuty. Przypisujesz te atrybuty używając funkcji Input Field i Output Field dostarczanych przez DSPy. Takie podejście daje ci precyzyjną kontrolę nad zachowaniem modelu. Docstring samej klasy staje się główną instrukcją dla modelu językowego, definiując ogólne zadanie. Rozważ scenariusz multimodalnej klasyfikacji obrazów. Chcesz przekazać obraz i pytanie tekstowe do modelu wizyjnego, i wyciągnąć konkretną rasę psa. Tworzysz klasę o nazwie ClassifyDogBreed. Na samej górze klasy piszesz docstring o treści: Zidentyfikuj rasę psa na podstawie dostarczonego obrazu i pytania. Następnie definiujesz swoje wejścia. Tworzysz atrybut o nazwie "image" i przypisujesz go jako Input Field. Tworzysz drugi atrybut o nazwie "question" i przypisujesz go jako Input Field. Na koniec definiujesz atrybut o nazwie "breed" i przypisujesz go jako Output Field. Wewnątrz tego Output Field możesz przekazać argument description o treści: Dokładna nazwa rasy psa, bez dodatkowego tekstu. Sygnatury oparte na klasach obsługują również type resolution. Możesz określić standardowe type hinty z Pythona dla swoich pól. Jeśli dodasz type hint do swojego pola wyjściowego jako boolean, DSPy zrozumie, że model musi zwrócić wartość true lub false. Framework przetwarza te adnotacje typów i automatycznie wstrzykuje ograniczenia do struktury promptu, kierując model w stronę poprawnego formatu wyjściowego. Struktura twoich danych i nazwy twoich zmiennych to faktyczne instrukcje. Jasno nazwane pole i precyzyjny docstring w deklaratywnej sygnaturze będą dyktować zachowanie modelu znacznie bardziej niezawodnie niż akapit po akapicie ręcznie tworzonego prompt engineeringu. Dzięki za wysłuchanie, miłego kodowania wszystkim!
4

Bloki konstrukcyjne z Modules

4m 17s

Poznaj Modules w DSPy, podstawowe bloki konstrukcyjne dla programów opartych na modelach językowych. Ten odcinek omawia dspy.Predict, dspy.ChainOfThought oraz sposoby łączenia wielu modułów w większy, spójny potok.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 4 z 15. Zazwyczaj, gdy chcesz, żeby model językowy przemyślał jakiś problem, uciekasz się do sklejania stringów, chaotycznie doklejając do swojego promptu frazy w stylu think step by step. Plączesz logikę aplikacji z kruchymi instrukcjami tekstowymi. W DSPy techniki promptowania to nie są stringi. To ustrukturyzowane, wymienne komponenty nazywane modułami. Łatwo pomylić moduły z sygnaturami. Pomyśl o sygnaturze jak o kontrakcie. Definiuje ona „co”, mapując konkretne pola wejściowe na konkretne pola wyjściowe. Moduł definiuje „jak”. To sparametryzowany, wywoływalny obiekt, który przyjmuje twoją sygnaturę i stosuje konkretną strategię promptowania, żeby spełnić ten kontrakt. Najbardziej podstawowym modułem jest moduł Predict. Inicjalizujesz go, przekazując swoją sygnaturę jako argument. Jeśli twoja sygnatura wymaga zamiany dokumentu w podsumowanie, moduł Predict ogarnia formatowanie promptu i wywołuje model językowy. Ale może zadanie jest złożone i wymaga logiki pośredniej. Możesz łatwo podmienić Predict na moduł Chain of Thought. Nie zmieniasz swojej sygnatury. Po prostu przekazujesz ją do modułu Chain of Thought. Pod spodem ten moduł automatycznie modyfikuje architekturę promptu. Instruuje model językowy, żeby wygenerował ślad wnioskowania krok po kroku, zanim wyprodukuje końcowe pola wyjściowe, które zdefiniowałeś. Kiedy wywołujesz moduł Chain of Thought ze swoimi danymi wejściowymi, zwraca on obiekt zawierający twoje żądane dane wyjściowe. Ponieważ użyłeś Chain of Thought, ten obiekt zawiera również nowe pole z uzasadnieniem modelu. Możesz dokładnie sprawdzić, jak model językowy doszedł do swojej odpowiedzi, całkowicie oddzielonej od końcowej, wyciągniętej wartości. I tu jest kluczowa sprawa. Możesz zagnieżdżać te wbudowane moduły, żeby tworzyć złożone programy, podobnie jak układasz warstwy sieci neuronowej w PyTorch. Możemy zbudować pipeline do multi-hop retrieval, żeby zobaczyć to w akcji. Zaczynasz od zdefiniowania customowej klasy. W jej fazie inicjalizacji deklarujesz mniejsze moduły, których będziesz potrzebować. Dla architektury multi-hop możesz zadeklarować moduł generatora zapytań używający Chain of Thought oraz moduł syntezy odpowiedzi używający standardowego Predict. Następnie definiujesz metodę forward, żeby routować dane między nimi. Metoda forward przyjmuje początkowe pytanie użytkownika. Przekazuje to pytanie do twojego modułu generatora zapytań, który zwraca search query. Wykonujesz to wyszukiwanie na swojej bazie danych, żeby pobrać dokument. Jeśli potrzebujesz drugiego hopa, przekazujesz pobrany dokument i oryginalne pytanie z powrotem do modułu generatora zapytań, żeby wygenerować bardziej doprecyzowane search query. Na koniec przekazujesz wszystkie pobrane dokumenty i pytanie użytkownika do modułu syntezy odpowiedzi, żeby wygenerować ostateczną odpowiedź. Właśnie zbudowałeś customowy, wykonywalny graf z modułowych komponentów. Kiedy chainujesz ze sobą wiele wywołań w ten sposób, kluczowe jest, żeby dokładnie widzieć, co jest wysyłane do modelu pod spodem. DSPy globalnie śledzi twoje zużycie modelu językowego. Możesz wywołać polecenie inspect history na swoim obiekcie modelu językowego, żeby wyprintować ostatnie interakcje. To renderuje dokładny string, który model otrzymał, i dokładny string, który wygenerował, upewniając cię, że twój skomponowany pipeline poprawnie składa kontekst. Rozdzielając definicję zadania na sygnatury, a strategię wykonania na sparametryzowane moduły, przekształcasz promptowanie z uciążliwej edycji tekstu w decyzję architektoniczną. Pozwala ci to na upgrade możliwości wnioskowania twojego pipeline'u po prostu poprzez podmianę nazwy klasy. Dzięki za wysłuchanie, miłego kodowania wszystkim!
5

Łączenie modeli za pomocą Adapters

3m 44s

Zrozum rolę Adapters w DSPy. Ten odcinek wyjaśnia, w jaki sposób ChatAdapter i JSONAdapter wypełniają lukę między abstrakcyjnymi sygnaturami DSPy a rzeczywistymi, wieloturowymi wiadomościami wysyłanymi do API modeli językowych.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Uczymy się DSPy, odcinek 5 z 15. Piszesz czyste, silnie typowane signature w swoim kodzie, przekazujesz je do modelu językowego i w jakiś sposób dostajesz z powrotem idealnie ustrukturyzowany obiekt Pythona. Pomiędzy twoim czystym kodem a surowym tekstowym API modelu siedzi chaotyczna warstwa tłumacząca. Ta ukryta warstwa to koncepcja łączenia modeli z adapterami. Zanim przyjrzymy się, jak działają, wyjaśnijmy częste nieporozumienie. Możesz mylić adaptery z modułami. Moduły zarządzają strategią wnioskowania. Decydują, czy model powinien użyć Chain of Thought, czy polegać na zewnętrznych toolach. Adaptery nie dbają o strategię. Adapter to czysto warstwa tłumacząca. Obsługuje surowy string i serializację JSON, które faktycznie lecą po sieci do API modelu. Modele językowe nie rozumieją deklaratywnych signature. Oczekują wieloturowych arrayów wiadomości, zawierających konkretne role i bloki tekstu. Adapter wypełnia tę lukę. Domyślnym toolem do tego w DSPy jest ChatAdapter. Kiedy wywołujesz moduł, ChatAdapter przechwytuje twoje signature i formatuje je w standardową historię czatu. Główna instrukcja z signature jest mapowana bezpośrednio na system prompt. Twoje pola inputu są zbierane i umieszczane w wiadomości użytkownika. Oto kluczowa sprawa. ChatAdapter używa specjalnych znaczników tekstowych, żeby utrzymać twoje inputy w ścisłym porządku. Owija każdą nazwę pola w podwójne nawiasy kwadratowe i podwójne hashe. Jeśli twój input nazywa się context, model językowy widzi znacznik z nawiasami i hashami wokół słowa context, a zaraz po nim właściwe dane kontekstowe. Ta wizualna granica zapobiega przypadkowemu pomyleniu przez model twoich instrukcji systemowych z tekstem inputu użytkownika. Powtarza ten wzorzec dla oczekiwanych pól outputu, promptując model do wygenerowania dokładnie tych samych znaczników w odpowiedzi. Wyobraź sobie scenariusz, w którym ekstrahujesz newsy naukowe. Twój input to surowy tekst artykułu, a twój output musi pasować do klasy Pydantic z konkretnymi polami na nagłówek i główne odkrycie naukowe. Kiedy przepuszczasz to wymaganie przez ChatAdapter, analizuje on twoją klasę Pydantic, generuje kompletny JSON schema i wstrzykuje ten schema bezpośrednio do system promptu. Wprost mówi modelowi językowemu, jak sformatować odpowiedź tekstową. Kiedy model w końcu odpowie, ChatAdapter przechwytuje surowy string tekstowy. Szuka oczekiwanych znaczników outputu, wyciąga blok tekstu między nimi i parsuje te dane z powrotem do dokładnie takich obiektów Pythona, jakich wymaga twoja aplikacja. To pokrywa inputy i parsowanie dla interakcji opartych na tekście. Ale nowoczesne modele językowe często mają natywne wsparcie dla structured output. I tu do gry wkracza JSONAdapter. Zamiast mocno modyfikować system prompt i polegać na znacznikach tekstowych, JSONAdapter obiera bardziej bezpośrednią drogę. Deleguje ograniczenia formatowania do natywnego JSON mode dostawcy modelu albo API od structured outputs. Model jest zmuszony na poziomie protokołu do zwrócenia poprawnego obiektu JSON, który zawiera wszystkie twoje żądane pola outputu. Ponieważ API modelu obsługuje strukturę natywnie, omija to potrzebę przeszukiwania przez adapter surowego tekstu w poszukiwaniu znaczników typu string. Jeśli twój docelowy model wspiera tę funkcję, przełączenie twojego pipeline'u na użycie JSONAdaptera zazwyczaj skutkuje niższym latency i znacznie bardziej niezawodnym parsowaniem. Adapter to sztywna granica między twoją deterministyczną logiką aplikacji a generowaniem nieustrukturyzowanego tekstu przez model językowy. Kontrolując dokładnie, jak inputy są serializowane, a outputy parsowane, adaptery upewniają się, że twój pipeline nigdy się nie wysypie z powodu źle sformatowanego stringa. Dzięki za wysłuchanie, udanego kodowania wszystkim!
6

Zarządzanie danymi za pomocą Examples

3m 54s

Dowiedz się, jak DSPy obsługuje zbiory danych do uczenia maszynowego. Ten odcinek omawia obiekt dspy.Example, rozróżnianie kluczy wejściowych i etykiet oraz przygotowywanie danych do ewaluacji i optymalizacji.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 6 z 15. Uruchamiasz optimizer, żeby ulepszyć swój pipeline modelu językowego, ale osiąga on idealny wynik już za pierwszym razem. Przyglądasz się bliżej i zdajesz sobie sprawę, że przypadkowo przekazałeś docelowe odpowiedzi bezpośrednio do promptu. Żeby traktować modele językowe jak tradycyjne komponenty machine learningu, potrzebujesz szczelnego sposobu na zarządzanie swoimi zbiorami treningowymi, dev i testowymi bez wycieku odpowiedzi. Zarządzanie danymi za pomocą obiektów Example w DSPy rozwiązuje dokładnie ten problem. W DSPy podstawową strukturą danych jest obiekt Example. Używasz go do budowania wszystkich swoich datasetów. Na pierwszy rzut oka działa bardzo podobnie do standardowego słownika w Pythonie. Tworzysz go, przekazując pary klucz-wartość, które reprezentują twoje dane. Weźmy na przykład zadanie podsumowywania tekstu. Tworzysz nowy obiekt Example i dajesz mu dwa pola. Przypisujesz długi string do pola o nazwie report, a krótki string do pola o nazwie summary. Możesz odczytać te wartości w dowolnym momencie, używając standardowej notacji kropkowej, pobierając pole report lub pole summary bezpośrednio z obiektu. Często traktujesz obiekt Example po prostu jako wrapper na słownik, ale użycie zwykłego słownika zepsuje proces kompilacji. Kiedy przekazujesz dataset do optimizera DSPy, kompilator musi oddzielić to, co trafia do pipeline'u, od tego, co jest używane do jego oceny. Wymaga to wyraźnych granic między danymi wejściowymi a oczekiwanymi odpowiedziami. I tu robi się ciekawie. Obiekt Example kontroluje te granice za pomocą specjalnej metody o nazwie with_inputs. Kiedy tworzysz instancję Example zawierającą report i summary, na samym końcu chainujesz metodę with_inputs. Przekazujesz jej string "report". To jawnie taguje pole report jako dane wejściowe. Każde pole, którego nie podasz w tej metodzie, automatycznie staje się labelem. Optimizer wie teraz, że musi wysłać do twojego pipeline'u tylko report. Pole summary pozostaje całkowicie ukryte podczas inference'u. Kiedy masz już skonfigurowany pojedynczy przykład, grupujesz wiele przykładów w standardowe listy w Pythonie, żeby utworzyć swoje zbiory treningowe, dev i testowe. Ponieważ DSPy traktuje prompt engineering jako problem optymalizacji w machine learningu, posiadanie tych wyraźnie podzielonych datasetów jest ścisłym wymogiem. Kiedy optimizer uruchamia twój pipeline na zbiorze treningowym, przetwarza jeden Example na raz. Usuwa labele, przekazuje inputy dalej, przechwytuje wygenerowany output, a następnie ocenia wynik. Kiedy piszesz własne metryki ewaluacji, będziesz potrzebować dostępu do tych oddzielonych pól. Obiekt Example udostępnia dwie metody do ich wyciągnięcia. Wywołanie metody inputs zwraca słownik zawierający tylko te dane, które oznaczyłeś jako inputy. Wywołanie metody labels zwraca słownik zawierający ukryte dane docelowe. Twoja funkcja ewaluacji wywołuje metodę labels, żeby pobrać docelowe summary, porównuje je z wygenerowanym tekstem i przypisuje wynik na podstawie tego, jak dobrze do siebie pasują. Prawidłowa konfiguracja twoich obiektów Example gwarantuje, że twój pipeline faktycznie nauczy się mapować inputy na outputy. Ścisłe oddzielenie inputów i labeli zapobiega wyciekowi danych podczas optymalizacji, dając pewność, że twój system się poprawia, a nie tylko zapamiętuje dostarczone odpowiedzi. Dzięki za wysłuchanie, miłego kodowania wszystkim!
7

Definiowanie sukcesu za pomocą Metrics

4m 35s

Odkryj, jak oceniać programy DSPy za pomocą metryk. Z tego odcinka dowiesz się, jak pisać niestandardowe funkcje w Pythonie do oceniania wyników, jak używać argumentu trace, a nawet jak wykorzystać podejście AI-as-a-judge do ewaluacji dłuższych form.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 7 z 15. Nie możesz konsekwentnie ulepszać tego, czego nie potrafisz zmierzyć, a w przypadku modeli językowych poleganie na ludzkiej intuicji do oceny jakości outputu po prostu się nie skaluje. Aby automatycznie przepisywać twoje prompty lub dostrajać system, kompilator potrzebuje matematycznego drogowskazu, i to jest dokładnie to, co zapewnia Defining Success with Metrics. W DSPy metryka to standardowa funkcja w Pythonie. Przyjmuje ona dwa główne argumenty. Pierwszy to example, który reprezentuje złoty standard inputu i oczekiwanego outputu z twojego datasetu. Drugi to prediction, czyli rzeczywista odpowiedź wygenerowana przez twój program w DSPy. Funkcja metryki porównuje prediction z example i zwraca score. Ten score to zazwyczaj float, integer albo prosta wartość boolean, jak true lub false. W przypadku podstawowych zadań klasyfikacyjnych, twoja metryka może być prostą logiką w Pythonie. Możesz napisać funkcję typu exact match, która sprawdza, czy przewidywany string jest idealnie równy oczekiwanemu stringowi. Aby systematycznie wykonywać ten pomiar na twoich danych, DSPy udostępnia wbudowane narzędzie o nazwie Evaluate. Przekazujesz do niego swój deweloperski dataset, funkcję metryki oraz parametry wykonania, takie jak liczba równoległych threadów. Narzędzie Evaluate uruchamia twoją metrykę dla każdego prediction, agreguje wyniki i zwraca pojedynczy wynik liczbowy reprezentujący ogólny performance twojego systemu. Jednak exact matching jest prawie zawsze zbyt sztywne dla generatywnych zadań językowych. W tym miejscu przechodzisz od prostego sprawdzania stringów do korzystania z feedbacku AI, wzorca powszechnie znanego jako LLM-as-a-judge. Ponieważ moduły DSPy to po prostu kod w Pythonie, twoja funkcja metryki może zainstancjonować i wywołać mniejszy, oddzielny program DSPy, aby oceniać złożone, semantyczne outputy. Weźmy pod uwagę konkretny scenariusz. Budujesz system, który generuje promocyjnego tweeta w odpowiedzi na pytanie użytkownika. Metryka typu exact match całkowicie tu zawodzi, ponieważ dobry tweet można sformułować na niezliczone, poprawne sposoby. Zamiast tego, twoja funkcja metryki powinna oceniać wiele wymiarów outputu. Po pierwsze, używa podstawowego sprawdzania długości w Pythonie, aby upewnić się, że wygenerowany tekst ma poniżej dwustu osiemdziesięciu znaków. Po drugie, sprawdza, czy tekst zawiera merytoryczną odpowiedź wymaganą przez example. Na koniec przekazuje wygenerowany tekst do wyspecjalizowanego signature DSPy, które prosi mniejszy model językowy o ocenę, czy tweet jest angażujący. Twoja funkcja metryki łączy następnie sprawdzenie długości, fact check i wynik zaangażowania z modelu językowego w jedną, ostateczną wartość matematyczną. Kiedy w końcu zaczniesz kompilować i optymalizować te programy, twoja funkcja metryki musi przyjmować trzeci, opcjonalny argument. Nazywa się on trace. Słuchacze często mylą argument trace z logiem debugowania, który wypisuje błędy w konsoli lub historię wykonania. A tym właśnie nie jest. Argument trace to konkretny obiekt używany przez kompilator DSPy podczas optymalizacji do walidacji pośrednich kroków wnioskowania. Jeśli twój program łączy w chain wiele wywołań modelu językowego, trace zawiera konkretną ścieżkę wnioskowania, którą model obrał, aby dotrzeć do końca. Uzyskując dostęp do trace wewnątrz twojej metryki, twoja funkcja może zweryfikować nie tylko to, czy końcowy tweet był dobry, ale także czy pośrednie kroki użyte do jego przygotowania były logicznie poprawne. I to jest ta część, która ma znaczenie. Twoja metryka ściśle definiuje, jak wygląda sukces, a kompilator DSPy będzie bezlitośnie optymalizował twój system, aby zmaksymalizować ten konkretny score. Jeśli twoja metryka jest wadliwa, twój skompilowany program będzie wadliwy w dokładnie ten sam sposób. Dzięki za wysłuchanie, happy coding wszystkim!
8

Wprowadzenie do Optimizers

3m 46s

Wejdź w sam środek magii DSPy: Optimizers. Ten odcinek zawiera przegląd tego, co robią optymalizatory, omawia iteracyjny cykl optymalizacji oraz nietypową strategię podziału danych 20/80 do optymalizacji promptów.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 8 z 15. Załóżmy, że piszesz jakiś soft i zamiast ręcznie modyfikować jego wewnętrzną logikę, żeby przechodził testy, kompilator automatycznie przepisuje instrukcje, dzięki czemu program sam z siebie działa lepiej. Nie dotykasz kodu. Po prostu dostarczasz zestaw testowy. Właśnie to robi DSPy, wykorzystując koncept zwany Optimizerami. Optimizery, które w starszych wersjach frameworka nazywano teleprompterami, to algorytmy, które dostrajają parametry twojego programu. W tradycyjnym machine learningu parametry oznaczają wagi sieci neuronowej. W DSPy parametry oznaczają przede wszystkim same stringi promptów i instrukcje wysyłane do modelu językowego, chociaż mogą też zawierać wagi. Zadaniem Optimizera jest dostosowanie tych parametrów, żeby zmaksymalizować zdefiniowaną przez ciebie metrykę. Ten proces dzieje się zanim zrobisz deploy swojej aplikacji. To czysty compute w czasie pre-inference. Zużywasz moc obliczeniową z góry, żeby znaleźć najlepsze instrukcje, dzięki czemu twoja aplikacja będzie później działać precyzyjnie. Słysząc słowo Optimizer, możesz założyć, że potrzebujesz ogromnych datasetów, tak jak do fine-tuningu tradycyjnego modelu. Wcale tak nie jest. Optimizery promptów są bardzo wydajne. Zazwyczaj wymagają zaledwie od trzydziestu do trzystu przykładów. Ponieważ dataset jest tak mały, DSPy zaleca nietypowe podejście do podziału danych. Zamiast standardowego podziału osiemdziesiąt do dwudziestu, gdzie większość danych trafia do treningu, odwracasz to. Używasz dwudziestu procent do treningu, a osiemdziesięciu do walidacji. Jeśli masz pięćdziesiąt przykładów, dajesz dziesięć Optimizerowi do zbudowania promptów, a pozostałe czterdzieści wykorzystujesz do oceny, czy te prompty faktycznie się generalizują. Ten odwrotny podział zapobiega temu, żeby Optimizer zrobił overfitting wygenerowanych promptów do malutkiego zestawu danych wejściowych. Oto kluczowy wniosek. Iteracyjny cykl developmentu w DSPy kręci się wokół odpalania tej pętli optymalizacyjnej. Przejdźmy przez konkretny scenariusz. Budujesz prostego bota do odpowiadania na pytania. Najpierw definiujesz swój program w DSPy i swoją metrykę. Następnie zbierasz swój dataset pięćdziesięciu nieoznaczonych pytań. Dzielisz te dane, przekazując małą część treningową do obiektu Optimizera. Mówisz Optimizerowi, żeby skompilował twój program, używając twoich danych treningowych i metryki. Optimizer odpala się, eksperymentując pod spodem z różnymi strukturami promptów. Sprawdza outputy z twoją metryką, uczy się, co działa, i udoskonala prompty. Kiedy Optimizer skończy, zwraca nową, skompilowaną wersję twojego programu. Ten skompilowany program zawiera nowo dostrojone parametry. Nie musisz odpalać tego kroku optymalizacji za każdym razem, gdy startuje twoja aplikacja. Zamiast tego wywołujesz metodę save na skompilowanym programie, podając ścieżkę do pliku. To zapisuje wszystkie zoptymalizowane prompty i konfiguracje do standardowego pliku JSON. Kiedy robisz deploy swojej aplikacji na produkcję, twój kod po prostu tworzy instancję bazowego programu i wywołuje metodę load, wskazując na ten konkretny plik JSON. Twój bot jest natychmiast gotowy do odpowiadania na pytania, używając zoptymalizowanych instrukcji. Prawdziwą mocą Optimizerów w DSPy jest to, że oddzielają logikę twojej aplikacji od dokładnego sformułowania promptów, pozwalając mocy obliczeniowej znaleźć dla ciebie najlepsze słowa. Dzięki za wysłuchanie, happy coding wszystkim!
9

Automatyczne uczenie Few-Shot

3m 50s

Dowiedz się, jak DSPy automatyzuje promptowanie few-shot. Ten odcinek skupia się na BootstrapFewShot i BootstrapFewShotWithRandomSearch, wyjaśniając, w jaki sposób syntetyzują, filtrują i wstrzykują wysokiej jakości przykłady do twoich promptów.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 9 z 15. Ręczne wybieranie najlepszych przykładów do umieszczenia w prompcie jest żmudne i podatne na stronniczość. Zgadujesz, które przykłady mają znaczenie, hardkodujesz je w swoich stringach i masz nadzieję, że model zwróci na nie uwagę. DSPy aktywnie syntetyzuje, testuje i wybiera dla ciebie idealne demonstracje. Ten proces to automatyczny few-shot learning, a DSPy obsługuje go za pomocą trzech konkretnych optymalizatorów. Najprostszym podejściem jest LabeledFewShot. Dostarczasz zestaw przykładów treningowych z etykietami. Optymalizator losowo wybiera podzbiór tych par input i output, po czym wstawia je bezpośrednio do twoich promptów jako demonstracje. Daje to modelowi podstawowy wzorzec do naśladowania. Działa to dobrze, jeśli twoje dane treningowe dokładnie pokrywają się z krokami pośrednimi, których potrzebuje twój program. Zazwyczaj tak nie jest. To prowadzi nas do BootstrapFewShot. Częstym błędem jest myślenie, że BootstrapFewShot po prostu losowo wybiera przykłady z twojego datasetu treningowego. Wcale tak nie jest. Aktywnie generuje on pośrednie kroki wnioskowania, które nigdy nie istniały w twoich surowych danych. Oto jak przebiega proces bootstrappingu. Optymalizator wymaga programu typu teacher. Domyślnie jest to po prostu niezoptymalizowana wersja zero-shot twojego własnego programu. Teacher przechodzi przez twoje przykłady treningowe. Dla każdego przykładu próbuje wygenerować odpowiedź. Następnie DSPy przekazuje tę odpowiedź do twojej metryki ewaluacyjnej. Jeśli metryka uzna, że output jest poprawny, DSPy zapisuje cały trace tego udanego wykonania. Taki trace zawiera input, output i, co najważniejsze, całą pracę pośrednią, jaką program wykonał, aby do niego dojść. Weźmy na przykład klasyfikator sentymentu. Twój surowy dataset zawiera tylko recenzje klientów oraz pozytywną lub negatywną etykietę. Twój program DSPy prosi model językowy o użycie chain-of-thought przed zwróceniem sentymentu. Podczas bootstrappingu, teacher czyta recenzję i wypisuje akapit z wnioskowaniem, zanim odgadnie sentyment. Jeśli ostateczny wynik pasuje do prawdziwej etykiety, to wygenerowane wnioskowanie jest uznawane za udane. Optymalizator zbiera te udane trace'y. Bierze cztery z nich i wstrzykuje je do przyszłych promptów. Twój klasyfikator zero-shot jest teraz eksperckim klasyfikatorem four-shot, wyposażonym w zsyntetyzowane kroki wnioskowania. BootstrapFewShot zatrzymuje się, gdy znajdzie wystarczającą liczbę udanych trace'ów. Jednak pierwsze udane trace'y nie zawsze są tymi najlepszymi. I tu pojawia się kluczowa kwestia. BootstrapFewShotWithRandomSearch rozwiązuje ten problem, uruchamiając cały proces bootstrappingu wielokrotnie. Za każdym razem pobiera losowy podzbiór twoich danych treningowych. Tworzy to kilka różnych kandydujących zestawów demonstracji few-shot. Następnie optymalizator bierze wszystkie te kandydujące zestawy i ewaluuje je na twoich danych walidacyjnych. Sprawdza, która konkretna kombinacja demonstracji daje najwyższy wynik ogólny. Odrzuca słabe zestawy i zachowuje matematycznego zwycięzcę. Prawdziwa siła automatycznego few-shot learningu to nie tylko oszczędność czasu na pisaniu promptów, ale też odkrywanie skutecznych, pośrednich ścieżek wnioskowania, których twój dataset nigdy wprost nie zawierał. Dzięki za wysłuchanie, miłego kodowania wszystkim!
10

Optymalizacja instrukcji z MIPROv2

4m 04s

Zanurz się w automatyczne dostrajanie instrukcji. Ten odcinek bada MIPROv2 i COPRO, pokazując, jak DSPy wykorzystuje optymalizację bayesowską i algorytm coordinate ascent do odkrywania lepszych, nieintuicyjnych instrukcji w promptach.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Uczymy się DSPy, odcinek 10 z 15. Czasami najskuteczniejsza instrukcja dla modelu językowego brzmi dla człowieka jak nonsens. Spędzasz godziny na skrupulatnym tworzeniu idealnego sformułowania, tylko po to, by odkryć, że wygenerowany automatycznie, lekko niespójny prompt całkowicie deklasuje twoją pracę. Dlatego powinieneś pozwolić algorytmom pisać twoje prompty. Dziś przyjrzymy się optymalizacji instrukcji za pomocą MIPROv2. Po pierwsze, zapomnij o prompt templatingu. Tu nie chodzi o wstawianie zmiennych do statycznego stringa. Optymalizacja instrukcji tak naprawdę przepisuje instrukcje systemowe, które sterują twoim pipelinem. Wcześniejsze algorytmy, takie jak COPRO i SIMBA, radziły sobie z tym, generując krok po kroku warianty promptów i udoskonalając je z czasem. MIPROv2 idzie z tą koncepcją znacznie dalej, traktując instrukcje i przykłady few-shot jako ujednoliconą przestrzeń poszukiwań. MIPROv2 działa w trzech odrębnych etapach. Pierwszy etap to bootstrapping. Optymalizator uruchamia twój niezoptymalizowany program na danych treningowych, aby zbudować pulę execution traces. Te trace'y zawierają rzeczywiste inputy, kroki pośrednie i outputy przepływające przez twój system. Drugi etap to grounded proposal. Optymalizator nie zgaduje nowych instrukcji na ślepo. Używa oddzielnego modelu językowego, zwanego prompterem, do przeanalizowania tych wygenerowanych trace'ów. Analizując, gdzie twój pipeline zadziałał, a gdzie zawiódł, prompter przygotowuje zestaw nowych kandydatów na instrukcje. Ci kandydaci są bezpośrednio oparci na rzeczywistym działaniu twojego programu, a nie na generycznych szablonach. Trzeci etap to discrete search. MIPROv2 ocenia nowe instrukcje wraz z różnymi kombinacjami trace'ów few-shot. Aby zrobić to wydajnie, opiera się na optymalizacji bayesowskiej. Zamiast sprawdzać każdą możliwą kombinację metodą brute-force, MIPROv2 buduje surrogate model. Ten surrogate model działa jak lekkie proxy. Przewiduje, które kombinacje instrukcji i trace'ów dadzą najwyższy wynik w twojej konkretnej metryce ewaluacyjnej. Optymalizacja bayesowska pozwala modelowi surrogate zmapować przestrzeń promptów i demonstracji. Oblicza oczekiwaną poprawę z przetestowania nowej kombinacji. To systematycznie balansuje eksplorację nieprzetestowanych instrukcji z eksploatacją kombinacji, które już osiągają dobre wyniki. Optymalizator namierza optymalną konfigurację bez wykonywania tysięcy nadmiarowych wywołań sieciowych. Rozważmy konkretny scenariusz. Budujesz agenta ReAct do odpowiadania na złożone zapytania. Początkowo jego dokładność wynosi 24 procent. Przekazujesz tego agenta do MIPROv2, konfigurujesz go do działania w trybie light i dostarczasz dataset 500 pytań. Optymalizator bootstrapuje trace'y, proponuje instrukcje w ramach grounded proposal i przeszukuje przestrzeń za pomocą modelu surrogate. Kiedy skończy, dokładność twojego agenta skacze z 24 do 51 procent. Końcowy prompt, który napędza ten skok wydajności, prawdopodobnie będzie zawierał instrukcje i dobór trace'ów, których człowiek nigdy by nie wymyślił. Oto kluczowy wniosek. MIPROv2 eliminuje wąskie gardło, jakim jest ludzka intuicja. Traktuje twoje instrukcje w języku naturalnym dokładnie tak samo jak dostrajane wagi w modelu matematycznym, zmieniając tworzenie promptów z nieprzewidywalnej sztuki w deterministyczny problem optymalizacyjny. Dzięki za wysłuchanie, udanego kodowania wszystkim!
11

Finetuning z BootstrapFinetune

3m 27s

Odkryj, jak destylować masywne modele językowe do mniejszych, wydajnych wersji. Ten odcinek omawia BootstrapFinetune, wyjaśniając, jak przekształcić program DSPy oparty na promptach w spersonalizowany model ze zaktualizowanymi wagami.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 11 z 15. Promptowanie potężnych modeli to świetny sposób na szybkie postawienie prototypu. Ale kiedy ta logika trafia na produkcję, latency i koszt modelu z siedemdziesięcioma miliardami parametrów szybko stają się problemem. Potrzebujesz mocy wnioskowania dużego modelu, ale szybkości i ceny modelu z ośmioma miliardami parametrów. Właśnie tym zajmuje się BootstrapFinetune. BootstrapFinetune kompiluje twój program DSPy do fine-tune'owanego modelu. To ostateczna optymalizacja pod kątem wydajności. Aktualizuje same wagi mniejszego target modelu, aby dokładnie odwzorować zachowanie twojego ciężkiego pipeline'u. Powszechnym mitem jest to, że aby zrobić fine-tuning modelu, musisz ręcznie zebrać tysiące przykładów, sformatować je do nudnych plików JSONL i niańczyć training loop. BootstrapFinetune całkowicie to automatyzuje. Ogarnia generowanie datasetu, formatowanie i aktualizację wag w całości poprzez trace'y z wykonania twojego programu. Weźmy konkretny scenariusz z klasyfikatorem intencji bankowych. Program przyjmuje chaotyczną wiadomość od klienta i ją kategoryzuje. Na początku budujesz moduł DSPy przy użyciu potężnego modelu, takiego jak GPT-4o-mini, skonfigurowanego do korzystania z chain-of-thought. Model analizuje krok po kroku sformułowania klienta, zanim zwróci intencję. Daje poprawne odpowiedzi, ale jest zbyt wolny i drogi do czatu w czasie rzeczywistym. Aby to zoptymalizować, inicjujesz BootstrapFinetune. Przekazujesz mu swoją metrykę ewaluacji do pomiaru sukcesu i określasz mniejszy, tańszy target model, który chcesz zdeployować. Następnie kompilujesz program. Kiedy odpalasz kompilację, DSPy uruchamia twój niezoptymalizowany program na danych treningowych. Używa ciężkiego teacher modelu do generowania outputów. Optymalizator obserwuje to wykonanie. Za każdym razem, gdy teacher model uzyska prawidłową odpowiedź zgodnie z twoją metryką, BootstrapFinetune przechwytuje trace. Zapisuje inputy, rozumowanie krok po kroku i końcowy output. Mapuje wewnętrzną logikę potężnego modelu na format, który mały target model może przyswoić. Po zebraniu wystarczającej liczby udanych trace'ów, BootstrapFinetune automatycznie strukturyzuje je w treningowy dataset. Następnie odpala proces fine-tuningu na twoim target modelu. Mały model jest trenowany bezpośrednio na wysokiej jakości ścieżkach rozumowania wygenerowanych przez duży model. I tu jest sedno. Mniejszy model uczy się specyficznej dystrybucji zadania i stylu rozumowania wymaganego do jego rozwiązania, bez konieczności odpalania ciężkiego chain-of-thought podczas inference'u. W naszym przykładzie klasyfikatora bankowego, standardowy mały model może osiągnąć zaledwie sześćdziesiąt sześć procent accuracy out of the box. Ale po kompilacji z BootstrapFinetune, ten sam mały model skacze do osiemdziesięciu siedmiu procent accuracy. Fine-tuning nie jest już oddzielnym projektem infrastrukturalnym; to po prostu kolejny krok kompilacji, który zamienia drogi reasoning pipeline w szybki i tani zasób na produkcję. Dzięki za wysłuchanie, miłego kodowania wszystkim!
12

Zautomatyzowane użycie narzędzi z ReAct

4m 06s

Dowiedz się, jak dać modelom językowym dostęp do zewnętrznych narzędzi. Ten odcinek omawia moduł dspy.ReAct, demonstrując, jak budować autonomicznych agentów, którzy wnioskują i dynamicznie wchodzą w interakcje z API.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 12 z 15. Danie LLM-owi dostępu do zewnętrznych API daje mu niesamowite możliwości, ale napisanie pętli do zarządzania jego reasoningiem, parsowania outputów i podnoszenia się po błędach egzekucji to straszny ból głowy. Rozwiązaniem jest w pełni zautomatyzowany tool use z wykorzystaniem modułu DSPy ReAct. Ludzie często mylą ReAct ze zwykłym function callingiem. Function calling to po prostu mechanizm API, który pozwala modelowi językowemu sformatować swój output jako request o ustrukturyzowane dane. ReAct to konkretny paradygmat behawioralny. Skrót ten pochodzi od słów Reason and Act. To execution loop, w którym model przechodzi przez trzy odrębne kroki: Thought, Action i Observation. Moduł DSPy ReAct całkowicie zarządza tą orkiestracją za ciebie. Nie piszesz execution loop. Nie łapiesz ręcznie wyjątków API. ReAct wrapuje signature DSPy i listę toolsów, przekształcając statyczny prompt w autonomicznego agenta. Aby z niego skorzystać, musisz najpierw zdefiniować swoje toolsy. W DSPy toolsy to po prostu standardowe funkcje Pythona. Piszesz funkcję, definiujesz jej parametry wejściowe i dodajesz jasny docstring. Ten docstring jest kluczowy. DSPy wyciąga nazwę funkcji i docstring, przekazując je do modelu językowego, dzięki czemu model dokładnie wie, co dany tool robi i kiedy go zdeployować. Wyobraź sobie scenariusz, w którym tworzysz prostego agenta do pogody i wyszukiwania. Piszesz funkcję Pythona o nazwie get weather, która przyjmuje nazwę miasta jako string i odpytuje API, aby zwrócić aktualną temperaturę. Tworzysz instancję modułu dspy dot ReAct, przekazując mu standardowy signature typu question and answer wraz z listą zawierającą twoją funkcję get weather. Gdy pytasz moduł, jaka jest pogoda w Tokio, rozpoczyna się pętla ReAct. Najpierw model generuje Thought. Dochodzi do wniosku, że potrzebuje aktualnych danych meteorologicznych dla Tokio. Następnie generuje Action. Decyduje się wywołać twój tool get weather, przekazując Tokio jako argument. I tu jest kluczowa sprawa. Nie odpalasz tej funkcji samodzielnie. Moduł DSPy ReAct przechwytuje Action modelu, wykonuje twoją funkcję Pythona w tle i przechwytuje output. Jeśli funkcja zakończy się sukcesem, DSPy przekazuje dane o temperaturze z powrotem do modelu jako Observation. Jeśli model zhalucynuje parametr albo funkcja rzuci błąd Pythona, DSPy łapie ten błąd i przekazuje komunikat o błędzie z powrotem jako Observation. Model odczytuje błąd, generuje nowy Thought, aby poprawić swój błąd, i próbuje wykonać nowy Action. Gdy model zaobserwuje prawidłowe dane o temperaturze, rozpoznaje, że cel został osiągnięty. Wychodzi z pętli i formatuje ostateczną odpowiedź dla użytkownika. Aby zapobiec niekontrolowanym egzekucjom, ten cykl jest ściśle ograniczony parametrem o nazwie max iters, co oznacza maksymalną liczbę iteracji. Ten parametr określa, ile cykli Thought, Action i Observation moduł ma prawo wykonać. Jeśli model ma problemy ze znalezieniem poprawnych danych i dobije do limitu iteracji, ReAct wymusza na nim przerwanie wyszukiwania i wygenerowanie ostatecznej odpowiedzi, używając tylko tych informacji, które udało mu się zebrać. Prawdziwą siłą tego modułu jest to, że abstrahuje kruchy, podatny na błędy control flow pętli agentowych, pozwalając ci traktować złożony tool augmented reasoning po prostu jako kolejny przewidywalny komponent w twoim pipeline. Dzięki za wysłuchanie, miłego kodowania wszystkim!
13

Ręczna obsługa narzędzi dla pełnej kontroli

3m 48s

Przejmij pełną kontrolę nad wykonywaniem narzędzi. Ten odcinek omawia ręczną obsługę narzędzi w DSPy przy użyciu dspy.Tool, dspy.ToolCalls oraz natywne wywoływanie funkcji dla aplikacji wrażliwych na opóźnienia.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 13 z 15. Zautomatyzowane agenty są świetne, gdy masz luźne, otwarte zadanie. Ale gdy potrzebujesz absolutnej, deterministycznej kontroli nad tym, jak, kiedy i czy zewnętrzna funkcja zostanie wywołana, całkowite oddanie sterów modelowi językowemu jest zbyt ryzykowne. Musisz zajrzeć pod maskę i samodzielnie zarządzać wykonaniem. I tu właśnie wkracza manualny tool handling, który daje ci pełną kontrolę. Użycie zautomatyzowanej pętli agenta abstrahuje warstwę wykonania, co może powodować nieprzewidywalne latency lub ukrywać runtime errory. Manualny handling to alternatywa dla power userów. Przywraca ci kontrolę nad error recovery, limitami timeout i dokładną kolejnością wykonywania. Aby zbudować to w DSPy, zaczynasz od opakowania standardowej funkcji w Pythonie za pomocą klasy dspy.Tool. Wyobraź sobie, że masz funkcję w Pythonie, która działa jak kalkulator mnożący dwie liczby. Przekazujesz tę funkcję do dspy.Tool. Jeśli twoja funkcja obsługuje zapytania do bazy danych lub requesty sieciowe, możesz też opakować funkcje asynchroniczne, a klasa tool natywnie zarządzi asynchronicznym wykonaniem. Gdy twój tool kalkulatora jest gotowy, musisz go udostępnić modelowi językowemu. Robisz to, przekazując listę zawierającą twój tool bezpośrednio do modułu DSPy Predict. W swoim wywołaniu Predict definiujesz parametr o nazwie tools. Gdy model przetwarza input, analizuje prompt i decyduje, czy potrzebuje kalkulatora do wygenerowania ostatecznej odpowiedzi. Gdy model decyduje się na użycie twojego toola, opiera się na mechanizmie pod spodem zwanym Adapterem. Domyślnie DSPy używa JSONAdapter. Ten adapter automatycznie tłumaczy twój tool w Pythonie na natywny format function calling, wymagany przez API konkretnego modelu językowego, którego używasz. Dzięki temu model zwraca niezawodny, ustrukturyzowany JSON podczas żądania toola. Zwróć na to uwagę. Łatwo założyć, że korzystanie z natywnego tool calling automatycznie zapewnia wyższą jakość outputów. Dokumentacja DSPy wyraźnie ostrzega, że to błędne przekonanie. Natywny tool calling zapewnia lepszą niezawodność składni requestu, ale nie gwarantuje lepszej jakości reasoningu niż standardowe parsowanie tekstu. Model nie staje się nagle mądrzejszy tylko dlatego, że formatuje JSON. Ponieważ zarządzasz tym procesem manualnie, model tak naprawdę nie uruchamia kalkulatora. Zatrzymuje się i zwraca odpowiedź zawierającą jego intencję. Uzyskujesz dostęp do tej intencji, sprawdzając response outputs kropka tool calls. Ta właściwość zwraca obiekt dspy.ToolCalls, który zachowuje się jak lista instrukcji. Każdy element na tej liście określa, którego toola model chce użyć i jakie dokładnie argumenty wygenerował, na przykład pięć i dziesięć dla kalkulatora. Następnie piszesz standardową pętlę w Pythonie, aby iterować po tych zażądanych tool calls. Dla każdego calla manualnie wywołujesz jego metodę execute. Wywołanie execute odpala twój rzeczywisty kod w Pythonie, używając wygenerowanych przez model argumentów, i zwraca wynik. Jeśli argumenty są nieprawidłowe lub kalkulator rzuci błędem, twoja pętla w Pythonie go przechwyci. Obsługujesz błąd na własnych warunkach, zamiast liczyć na to, że zautomatyzowana pętla sama zrobi recovery. Manualny tool handling czysto oddziela decyzję modelu o zażądaniu akcji od fizycznego wykonania tej akcji, dając twojej aplikacji deterministyczną niezawodność, której wymagają środowiska produkcyjne. Dzięki za wysłuchanie, happy coding wszystkim!
14

Integracja narzędzi z MCP

4m 21s

Połącz swoich agentów z uniwersalnymi serwerami narzędzi. Ten odcinek wyjaśnia, jak używać Model Context Protocol (MCP) w DSPy, aby przy minimalnej konfiguracji wykorzystać ustandaryzowane narzędzia w różnych frameworkach.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 14 z 15. Za każdym razem, gdy sięgasz po nowy framework AI, zazwyczaj kończy się to przepisywaniem tych samych, własnych wrapperów w Pythonie do zapytań do bazy danych, wyszukiwania w sieci i odczytu plików. Zamiast utrzymywać w nieskończoność zduplikowane wrappery API, co by było, gdyby twoje agenty mogły natychmiast podłączać się do uniwersalnych, ustandaryzowanych serwerów narzędziowych? To właśnie obiecuje Model Context Protocol, a dziś przyjrzymy się integracji narzędzi z MCP. Model Context Protocol to otwarty standard wprowadzony przez Anthropic. Zapewnia uniwersalny sposób łączenia modeli AI z zewnętrznymi źródłami danych i narzędziami. Dzięki temu standardowi developerzy piszą narzędzie raz, hostują je na serwerze MCP i używają w dowolnym wspieranym frameworku. DSPy wspiera to natywnie. Nie musisz pisać skomplikowanych klas adapterów, żeby wpiąć te zewnętrzne narzędzia do swoich programów w DSPy. Często błędnie zakłada się, że DSPy samo obsługuje połączenia z serwerami tych narzędzi pod spodem. Wcale tak nie jest. DSPy całkowicie polega na oficjalnej paczce mcp w Pythonie, jeśli chodzi o zarządzanie siecią i nawiązywanie połączenia. DSPy wkracza do akcji dopiero na samym końcu, żeby skonwertować aktywny obiekt narzędzia MCP do natywnego formatu narzędzia w DSPy. Żeby zobaczyć, jak to działa, przejdźmy przez proces podłączania lokalnego serwera narzędziowego do agenta DSPy. Najpierw musisz mieć uruchomiony serwer MCP. W swoim skrypcie w Pythonie importujesz klasę parametrów serwera z paczki MCP. Jeśli odpalasz lokalny proces, definiujesz parametry serwera dla standard IO i wskazujesz plik wykonywalny serwera. Alternatywnie, jeśli twoje narzędzia żyją na zdalnym serwerze, konfigurujesz połączenie klienta HTTP. Obie metody określają, jak twoja aplikacja będzie komunikować się z dostawcą narzędzi. Następnie używasz biblioteki MCP, żeby otworzyć sesję klienta. Wewnątrz kontekstu tej sesji inicjalizujesz połączenie. Na tym etapie DSPy w ogóle jeszcze nie bierze w tym udziału. Prosisz aktywną sesję MCP o wylistowanie dostępnych narzędzi. Serwer odpowiada listą obiektów narzędzi. Każdy obiekt zawiera nazwę narzędzia, opis tego, co robi, oraz oczekiwane argumenty wejściowe. Teraz łączysz to wszystko w całość. I tu robi się ciekawie. Dla każdego narzędzia zwróconego przez serwer, wywołujesz metodę from mcp tool na bazowej klasie Tool w DSPy. Przekazujesz do tej metody dwa konkretne argumenty: surowy obiekt narzędzia i aktywną sesję klienta. To jedno polecenie odczytuje schemat dostarczony przez serwer MCP i natychmiast opakowuje go w kompatybilny interfejs. Masz teraz gotową do użycia listę natywnych narzędzi DSPy. Na koniec przekazujesz tę nowo przekonwertowaną listę narzędzi do agenta. Inicjalizujesz moduł ReAct i przekazujesz mu swoją tablicę narzędzi DSPy. Kiedy uruchomisz agenta, może on teraz płynnie wywoływać zewnętrzne narzędzia MCP. Argumenty przepływają z modułu ReAct, przez przekonwertowany wrapper DSPy, przez sesję klienta MCP do serwera, a wynik wraca, żeby zasilić kolejny krok wnioskowania. Prawdziwą siłą tej integracji jest decoupling. Twoje moduły DSPy mogą bezpiecznie uzyskiwać dostęp do firmowych baz danych lub lokalnych systemów plików z zerową ilością własnego kodu dla wrapperów, jednocześnie dając pewność, że ten sam serwer narzędziowy pozostaje w pełni użyteczny dla zupełnie innych frameworków. Dzięki za wysłuchanie, miłego kodowania wszystkim!
15

Ensembles i meta-optymalizacja

3m 34s

Wykorzystaj DSPy do granic możliwości. Ostatni odcinek omawia transformacje programów za pomocą dspy.Ensemble oraz eksperymentalny meta-optymalizator BetterTogether, który łączy dostrajanie promptów z finetuningiem wag w celu uzyskania maksymalnej wydajności.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Nauka DSPy, odcinek 15 z 15. Co się stanie, gdy połączysz bayesowską optymalizację promptów z fine-tuningiem wag w deep learningu? Przestajesz traktować prompt engineering i trenowanie modeli jako odizolowane kroki, a zaczynasz traktować je jako ciągły pipeline. W tym miejscu docierasz na sam szczyt zautomatyzowanego AI engineeringu, opierając się na Ensembles i metaoptymalizacji. Musimy od razu coś wyjaśnić. Kiedy słyszysz słowo ensemble, prawdopodobnie myślisz o jednoczesnym odpytywaniu pięciu różnych foundation models. W DSPy ensemble to coś zupełnie innego. Ensemble oznacza tutaj uruchomienie wielu zoptymalizowanych programów na tym samym bazowym language model. Łączy różne struktury promptów i różne reasoning traces, aby agregować ich outputy. Logika jest tu prosta. Podczas głębokiej optymalizacji różne konfiguracje często odkrywają odrębne, równie poprawne reasoning paths, aby dotrzeć do właściwej odpowiedzi. Załóżmy, że właśnie uruchomiłeś optymalizator MIPROv2. Ewaluuje on setki konfiguracji i przechowuje historię tych najlepszych. Zamiast wybierać pojedynczy program z najwyższym wynikiem i odrzucać resztę, wyciągasz pięć najlepszych programów kandydujących. Przekazujesz je do transformacji DSPy Ensemble. Kiedy pojawia się nowy input, ensemble wykonuje wszystkie pięć programów. Agreguje ich outputy, zazwyczaj poprzez majority voting, i zwraca ostateczną, wysoce solidną odpowiedź. Zasadniczo skalujesz swój compute podczas inference'u, aby zagwarantować wyższą jakość wyniku. Uruchomienie pięcioprogramowego ensemble'u na ogromnym foundation model zapewnia niesamowitą dokładność, ale jest kosztowne i powolne. W tym miejscu do gry wchodzą metaoptymalizatory. Metaoptymalizator zarządza wykonywaniem innych optymalizatorów, sekwencjonując je w celu skumulowania ich korzyści. Głównym przykładem w DSPy jest BetterTogether. BetterTogether systematycznie nakłada na siebie ulepszenia. Pozwala ci wziąć ogromne możliwości reasoningu twojego ensemble'u i wydestylować je do szybkiego, sfinetunowanego modelu. Najpierw konfigurujesz BetterTogether, aby użył optymalizacji promptów do wygenerowania niezwykle wysokiej jakości reasoning traces z twojego ciężkiego ensemble'u. Następnie automatycznie przekazuje te traces do optymalizatora wag. Optymalizator wag wykorzystuje te dane do fine-tuningu parametrów znacznie mniejszego i tańszego student model. Na koniec BetterTogether może uruchomić drugą rundę optymalizacji promptów, tym razem dostosowując instrukcje specjalnie do nowo zaktualizowanych wag student model. Przechodzisz od optymalizacji promptów, do optymalizacji wag, i z powrotem do optymalizacji promptów. Outputem jest wysoce wyspecjalizowany, szybki model, który uchwycił zróżnicowane reasoning paths oryginalnego ensemble'u, bez ogromnych kosztów inference'u. Sekwencyjne nakładanie na siebie technik optymalizacji to sposób na wypełnienie luki między ciężkim, kosztownym reasoningiem a szybkim, gotowym na produkcję inference'em. Tym sposobem docieramy do końca serii. Gorąco zachęcam cię do zapoznania się z oficjalną dokumentacją DSPy, spróbowania zbudowania tych pipeline'ów hands-on, lub odwiedzenia devstories dot eu, aby zasugerować tematy, które chcesz, żebym omówił w następnej kolejności. Dziękuję za wysłuchanie, happy coding wszystkim!