3,164 odczyty
3,164 odczyty

Ta jedna praktyka ułatwia budowanie, testowanie i skalowanie LLM

przez Andrew Prosikhin7m2025/04/07
Read on Terminal Reader

Za długo; Czytać

LLM prompt modularization pozwala bezpiecznie wprowadzać zmiany do systemu w czasie. Jak i kiedy to zrobić, opisano poniżej.
featured image - Ta jedna praktyka ułatwia budowanie, testowanie i skalowanie LLM
Andrew Prosikhin HackerNoon profile picture

Jest to część trwającej serii: zobacz pierwszy i drugi post.

Zasada III: Modularyzacja monitów

Okropna potworność. Każdy doświadczony inżynier widział taką: kod tak rozległy, ryzykowny i trudny do zrozumienia, że nikt nie ośmiela się go dotknąć. Nie ma testów jednostkowych, każda zmiana jest powodem małego zawału serca. Jedynymi, którzy się do niego zbliżają, są starzy wyjadacze — ci, którzy byli w pobliżu, gdy powstawał potwór, i zbliżają się tylko wtedy, gdy nie ma alternatywy. Jest przestarzały, niemodularny, a zależności są nieaktualne. Komponent jest zbyt niebezpieczny, aby poważnie go zmieniać.


Pamiętam pierwszą potworność, na którą się natknąłem. Funkcja z 5000 wierszami, która była centralnym punktem działalności firmy wartej setki milionów dolarów; prawie nikt nie miał odwagi, by jej dotknąć. Kiedy się zepsuła, całe zespoły były wybudzane w środku nocy. Cały rozwój w firmie został spowolniony z powodu zależności od tego kluczowego komponentu. Miliony dolarów wydano, próbując zarządzać potworem.


Co to wszystko ma wspólnego z podpowiedziami LLM? One też mogą stać się potworami! Tak przerażające, że nikt ich nie dotyka. Albo odwrotnie, zespoły próbują je naprawić i powodują lawinę incydentów.


Czego potrzebują klienci

Klienci nie chcą płacić za oprogramowanie, które działa poprawnie tylko we wtorki i czwartki; wymagają stałej niezawodności i strumienia nowych funkcji. Podczas tworzenia długoterminowych systemów o wysokiej niezawodności konieczne jest umożliwienie aplikacji ewolucji przy jednoczesnym ciągłym utrzymywaniu świateł włączonych. Dotyczy to zarówno aplikacji Gen AI, jak i tradycyjnego oprogramowania.


Jak więc uzyskać zdrową aplikację opartą na sztucznej inteligencji, a nie monstrum? Istnieje ponad tuzin podejść, wszystkie omówione w tej serii. Wszystkie zaczynają się od jednej zasady: zamiast jednego ogromnego monitu, chcesz wielu mniejszych, ukierunkowanych monitów, z których każdy ma na celu rozwiązanie pojedynczego problemu.


Czym jest modularyzacja

Modularyzacja to praktyka rozbijania złożonego systemu na mniejsze, samodzielne i wielokrotnego użytku komponenty. W tradycyjnej inżynierii oprogramowania oznacza to pisanie funkcji, klas i usług, z których każda obsługuje określone zadanie. W kontekście inżynierii promptów dla LLM modularność oznacza dzielenie dużego, monolitycznego prompta na mniejsze, skoncentrowane prompty — każdy zaprojektowany do wykonywania pojedynczego, dobrze zdefiniowanego zadania.


Korzyści z modularizacji

Modularyzacja pozwala bezpiecznie wprowadzać zmiany do systemu w czasie. Jej znaczenie rośnie, gdy:

  • Czas utrzymywania aplikacji ulega wydłużeniu.
  • Oczekuje się, że liczba i złożoność dodanych funkcji wzrośnie.
  • Wymagania dotyczące niezawodności systemu stają się bardziej rygorystyczne.

Wszystkie te wymiary muszą być zrozumiałe podczas planowania systemu.


Ale jak konkretnie modularyzacja pomaga utrzymać system? Główne korzyści są opisane poniżej.

Redukcja ryzyka

Wydajność monitu LLM jest z natury niestabilna. Ich natura jest taka, że każda zmiana może wpłynąć na wynik w nieprzewidywalny sposób. Możesz zarządzać tym ryzykiem, dzieląc duże monity na komponenty, gdzie zmiana może wpłynąć tylko na wydajność części systemu. Nawet jeśli jeden monit zostanie uszkodzony, reszta systemu będzie działać tak jak przed zmianą.


Ale co jeśli monity działają jak łańcuch? Czy zerwanie jednego komponentu nie zerwie całego łańcucha? Tak, zerwanie mogłoby, ale w tym scenariuszu szkody są nadal mniejsze. Błędne dane wyjściowe w łańcuchu monitów mogą dostarczyć monitom w dół strumienia błędnych danych wejściowych, ale każdy komponent nadal działałby tak jak przed zmianą zestawu prawidłowych danych wejściowych. Porównaj to ze zmianą gigantycznego monitu — zmiana może (i będzie!) wpływać na każdy bit logiki zakodowanej w tym monicie. Nie złamałeś jednego aspektu systemu — potencjalnie złamałeś każdą jego część.


(Bezpieczne działanie łańcuchów komunikatów to przyszły rozdział w serii. Musisz zaplanować różne rodzaje awarii i mieć plany awaryjne. Ale to wykracza poza zakres tego artykułu)

Ulepszona testowalność

Każdy, kto pisał testy jednostkowe, wie, że prosta funkcja, która robi jedną rzecz, jest o WIELE łatwiejsza do przetestowania niż złożona funkcja, która próbuje robić wiele różnych rzeczy. To samo dotyczy monitów — mały, skoncentrowany monit można przetestować o wiele dokładniej zarówno ręcznie, jak i w sposób całkowicie zautomatyzowany.

Lepsza wydajność

Liczne dowody wskazują, że krótsze polecenia mają większą skuteczność niż dłuższe: 1 , 2 , 3 .


Badania nad wpływem wielozadaniowości na wydajność monitu są bardziej zróżnicowane: 4 , 5. Idealnie zoptymalizowany monit może, w odpowiednich okolicznościach, wykonywać wiele zadań jednocześnie. W praktyce jednak znacznie łatwiej jest optymalizować skoncentrowane monity, w których można śledzić wydajność wzdłuż jednego głównego wymiaru. Powinieneś dążyć do bardziej skoncentrowanych monitów, gdzie to możliwe.

Łatwość dzielenia się wiedzą

Wyjaśnienie zawiłości superpodpowiedzi z 3 tysiącami słów nowemu członkowi zespołu to podróż. I bez względu na to, jak wiele wyjaśnisz, jedynymi, którzy mają wyczucie tej bestii, będą autorzy, którzy się do tego przyczynią.


System monitów, w którym każda część jest stosunkowo prosta, można wdrożyć znacznie szybciej; inżynierowie szybciej zaczną pracować wydajniej.

Optymalizacja kosztów

Dzięki stosowaniu różnych modeli w różnych częściach systemu można znacząco obniżyć koszty i opóźnienia, nie wpływając przy tym na jakość odpowiedzi.


Na przykład monit, który określa język wprowadzania, nie musi być szczególnie inteligentny — nie wymaga najnowszego i najdroższego modelu. Z drugiej strony monit, który generuje odpowiedź na podstawie dokumentacji, mógłby skorzystać z wbudowanego łańcucha rozumowania myślowego osadzonego w modelach high-end.


Kiedy NIE stosować modularności

Większość aplikacji opartych na oprogramowaniu wymaga bezpiecznego dodawania funkcji przez dłuższy czas. Istnieje jednak wyjątek. Aplikacje prototypowe nie są przeznaczone do długotrwałego utrzymywania; nie otrzymają nowych funkcji i nie są przeznaczone do wysokiej niezawodności. Dlatego nie marnuj czasu na modułowość podczas tworzenia prototypów. W rzeczywistości większość wzorców w tej serii nie ma zastosowania do aplikacji prototypowych. Podczas tworzenia prototypu — działaj szybko, zweryfikuj krytyczne nieznane elementy, a następnie wyrzuć kod.


Innym zagadnieniem jest wiedza, kiedy zakończyć modularizację. Istnieje narzut związany z zarządzaniem dodatkowymi monitami, a jeśli korzyści z dalszej modularizacji są niskie - należy zaprzestać dalszego dzielenia systemu.


Infrastruktura do modularyzacji

Gdyby modularizacja promptów była trywialna - każdy by to robił. Aby zarządzać wieloma promptami w systemie, musisz zainwestować w infrastrukturę - bez niej będziesz miał chaos. Oto minimalne wymagania dla infrastruktury promptów LLM:

  • Możliwość szybkiego i bezproblemowego dodawania monitów w sposób ujednolicony. Szczególnie ważne, gdy monity są ładowane spoza bazy kodu. Zobacz Zasada II: Bezpieczne ładowanie monitów (jeśli naprawdę musisz) .

  • Możliwość automatycznego wdrażania monitów.

  • Możliwość rejestrowania i monitorowania danych wejściowych/wyjściowych poszczególnych monitów.

  • Możliwość dodawania automatycznych testów obejmujących monity.

  • Łatwy sposób na śledzenie wydatków tokenów/dolarów na różne monity.


Studium przypadku

Zobaczmy, jak w praktyce sprawdza się budowa systemu opartego na sztucznej inteligencji generacji z zastosowaniem modułowości i bez niej.

Brak modularności

Budujesz aplikację pomocy technicznej i jesteś zdecydowany wdrożyć ją za pomocą jednego monitu. W najprostszej wersji możesz sobie wyobrazić monit monolityczny, który generuje odpowiedzi podczas ładowania odpowiedniej dokumentacji za pośrednictwem RAG .

System niemodularny

Wygląda ładnie i łatwo, prawda? Ale gdy dodajesz funkcje - pojawiają się problemy z tą architekturą:

  • Chcesz odpowiadać na wiadomości w ustalonej liście języków, ale nie obsługiwać innych. Aby to osiągnąć, dodaj instrukcje szybkiego odpowiadania tylko w określonych językach i zmuś LLM do zwrócenia pola „język” w celach raportowania.

  • Chcesz, aby wszystkie konwersacje były klasyfikowane. Dodaj pole „etykieta” do wyniku monitu.

  • Gdy użytkownik jest niezadowolony — eskaluj sprawę do pomocy technicznej. Dodaj zmienną wyjściową „escalate_to_human” wraz z instrukcjami w monicie.

  • Potrzebne jest tłumaczenie wszystkich wiadomości wysłanych do audytu wewnętrznego. Zwróć pole „przetłumaczone” z wiadomością w języku angielskim.

  • Potrzebujesz ochrony, aby mieć pewność, że aplikacja nigdy nie zapyta użytkowników o ich lokalizację i na kogo głosowali w ostatnich wyborach. Dodaj szybkie instrukcje i przetestuj ją ręcznie.

  • Potrzebujesz podsumowania każdej konwersacji? Dodaj pole „podsumowanie” do każdego wyjścia.


Być może zaczynasz dostrzegać problem – ten monit ma teraz sześć wyników. Testowanie go będzie koszmarem. Dodajesz obsługę innego języka, a nagle Twoja aplikacja zaczyna zwracać podsumowanie po hiszpańsku zamiast po angielsku. Dlaczego? Kto wie, wyniki LLM są niestabilne, więc zmiana monitu ma nieprzewidywalne rezultaty.


Gratulacje - stworzyłeś potwora! Z czasem urośnie i będzie sprawiał jeszcze więcej bólu.

Z modularizacją

System modułowy

Zarówno Prompt Chain , jak i całkowicie oddzielony prompt klasyfikacji są używane. Oryginalny duży prompt jest modularny tak bardzo, jak to możliwe.

Jeden monit wykrywa język, jeden zapewnia tłumaczenie, jeden ustala, czy użytkownik jest zdenerwowany i eskaluje do ludzi, monit odpowiedzi generuje odpowiedź, guardrail weryfikuje zgodność odpowiedzi. Wyniki jednego monitu są połączone, aby stać się danymi wejściowymi następnego; tradycyjny kod może działać między tymi monitami, aby na przykład sprawdzić kwalifikowalność języka, bez angażowania LLM.


Zmiana nadal może naruszyć dany monit, ale ryzyko jest znacznie zmniejszone, ponieważ:

  • Zmiana jednej części nie niesie za sobą ryzyka uszkodzenia pozostałych części logiki aplikacji.
  • Testowanie jest o wiele łatwiejsze, a szanse na wczesne wykrycie błędu są duże.
  • Każde polecenie jest stosunkowo proste, więc łatwiej je zrozumieć i jest mniejsze prawdopodobieństwo, że zmiana spowoduje jakieś szkody.
  • Zmiany są łatwiejsze do przejrzenia.

Otrzymujesz wszystkie korzyści Gen AI, ale ryzyko jest znacznie zmniejszone. Ponadto możesz używać tańszych modeli niektórych komponentów, aby zaoszczędzić pieniądze.


Wniosek

Modularyzacja pozwala na izolowanie błędów, poprawę łatwości obsługi i zbudowanie bardziej niezawodnego systemu. Nawet aplikacje o średniej wielkości będą miały dziesiątki, jeśli nie setki, monitów komponentów. Rozbijaj monity, aż każdy z nich wykona pojedyncze zadanie, a korzyści z dalszej modularizacji zostaną przeważone przez dodatkową złożoność operacyjną. Modularyzacja monitów jest koniecznością, jeśli Twoje aplikacje oparte na sztucznej inteligencji mają pozostać niezawodne i nadal dodawać funkcje w dłuższej perspektywie. Istnieje już wiele „potwornych” systemów — uważaj, aby nie tworzyć nowych!


Jeśli podobał Ci się ten cykl - zasubskrybuj, aby otrzymywać więcej postów.

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks