Przemyślenie wyszukiwania jako generowania kodu
Ewolucja wyszukiwania od monolitycznych usług do programowalnych prymitywów w erze systemów sterowania agentów.
Wyszukiwanie stanowi podstawowy element systemów AI. Modele Frontier z miesiąca na miesiąc stają się coraz bardziej wydajne, lecz nadal wymagają dostępu do świeżej, dokładnej i dobrze wyselekcjonowanej wiedzy z szerszego świata. Wyszukiwanie jest głównym sposobem, w jaki systemy AI czerpią z tej wiedzy, a tym samym stanowi fundamentalny komponent każdego produktu, który musi wyciągać wnioski, podejmować działania i wykonywać pracę w świecie rzeczywistym. Wierzymy, że tradycyjne potoki wyszukiwania stają się coraz bardziej przestarzałe w erze agentów. Tradycyjne wyszukiwanie odpowiada na zapytania, ale dzisiejsi agenci realizują zadania, które mogą przybierać niezliczone formy. Zadania te wymagają od agentów zdefiniowania strategii wyszukiwania specyficznych dla danego zadania bezpośrednio w ich systemach sterowania. W ramach Perplexity Computer zauważyliśmy, że pojedyncze zadania wywołują setki, a nawet tysiące operacji wyszukiwania w ciągu kilku minut: przepływ pracy niemożliwy dla ludzi, ale absolutnie naturalny dla agentów. W tym świecie samo wyszukiwanie musi stać się agentowe, a jego bloki konstrukcyjne muszą być dostępne bezpośrednio jako zestawy SDK w systemie sterowania agenta. Wprowadzamy Search as Code (SaC) jako nową referencyjną architekturę wyszukiwania Perplexity.
Wprowadzenie
Stos wyszukiwania Perplexity obsługuje tysiące zapytań na sekundę w naszych aplikacjach oraz platformie API. We wrześniu 2025 r. opublikowaliśmy pierwszy przegląd architektury naszych systemów wyszukiwania. Stałe innowacje wewnątrz tych systemów wsparły wprowadzenie nowych ofert, takich jak Search API, Agent API oraz Computer, przy czym pętle samodoskonalenia optymalizują stos wyszukiwania, aby z każdym dniem lepiej służyć użytkownikom.
Tradycyjnie systemy AI traktowały wyszukiwanie jako monolit: model AI wysyła zapytanie, wyszukiwarka uruchamia swój predefiniowany potok, a model konsumuje wyniki jako kontekst. W większości przypadków sprawdzało się to w zaspokajaniu potrzeb wczesnych użytkowników AI. Biorąc pod uwagę względną prostotę ich żądań, nie było powodów, by martwić się o to, jak dokładnie zaprojektowano potok wyszukiwania ani czy jego architektura jest optymalna dla danego zadania. Domyślne ustawienia uznawano za wystarczająco dobre, podobnie jak domyślne interfejsy (wywoływanie funkcji i MCP).
Mimo to, podejście to staje się coraz bardziej przestarzałe z każdym mijającym miesiącem. Użytkownicy wymagają od AI znacznie więcej niż tylko jednorazowej analizy. Oczekują, że agenci będą realizować zadania kompleksowo, w ciągu godzin lub nawet dni. Zadania te mogą być złożone, otwarte i wysoce zmienne pod względem potrzeb informacyjnych, a monolityczne architektury uginają się pod ciężarem tych wymagań.
Kluczowym wąskim gardłem jest ostatecznie kwestia kontroli. Modele Frontier są już całkiem dobre w wnioskowaniu na podstawie ustalonego kontekstu. Jednak najbardziej zaawansowane systemy AI będą wymagały zdolności do kierowania tym, w jaki sposób ten kontekst jest wyszukiwany, przetwarzany, agregowany i renderowany do modelu.
Tradycyjne systemy wyszukiwania nie zostały zaprojektowane z myślą o takim stopniu kontrolowalności. Ostatecznie, od użytkowników-ludzi nie można oczekiwać sprawowania szczegółowej kontroli nad wewnętrznymi mechanizmami potoku wyszukiwania, nawet gdyby było to oferowane. Wczesne modele AI mogą kontrolować wyszukiwanie jedynie poprzez liniową trajektorię wywołań funkcji i MCP. Jednak dzisiejsze modele Frontier, napędzane przez systemy sterowania agentów zdolne do obsługi kodu, mogą wywierać szczegółową kontrolę nad wszelkimi wyobrażalnymi prymitywami obliczeniowymi poprzez kod komputerowy. Naszym zadaniem staje się zapewnienie odpowiednich prymitywów.
Aby sprostać tej potrzebie, wprowadzamy nową architekturę wyszukiwania w naszych produktach: Search as Code (SaC). Ta nowa architektura umożliwia modelom sięgnięcie do wnętrza samego stosu wyszukiwania zamiast jedynie konsumowania jego końcowych wyników. Główna idea jest prosta: udostępniamy komponenty stosu wyszukiwania jako prymitywy w ramach zestawu SDK. W przypadku każdego zapytania wymagającego wyszukiwania, model składa te prymitywy na żądanie w potok wyszukiwania dostosowany do tego konkretnego zapytania.
Montaż tego potoku odbywa się poprzez generowanie i wykonywanie kodu w bezpiecznej piaskownicy. W przeciwieństwie do innych podejść do wyszukiwania opartych na generowaniu kodu, nie po prostu umieszczamy tradycyjnego interfejsu API wyszukiwania w powłoce lub środowisku uruchomieniowym języka. Zamiast tego starannie zaprojektowaliśmy zestaw SDK Agentic Search, który udostępnia poszczególne bloki konstrukcyjne wyszukiwania na najbardziej atomowym poziomie, jaki jest możliwy.
Wyposażony w te bloki konstrukcyjne, SaC daje modelom bezpośrednią kontrolę nad każdym indywidualnym etapem wyszukiwania: pobieraniem, rankingiem, filtrowaniem, fanoutami, renderowaniem i nie tylko. Zapewnia również modelowi wydajny dostęp do stanów pośrednich, takich jak listy kandydatów i sygnały rankingowe. Razem te dwa dźwignie kontroli i czytelności pozwalają agentom projektować niestandardowe potoki wyszukiwania obejmujące tysiące operacji pobierania, optymalizować te potoki w locie i konsumować tylko najbardziej użyteczne informacje jako kontekst modelu.
Niniejszy artykuł opisuje motywację, projekt i implementację SaC, wraz z empirycznymi wynikami zarówno na istniejących, jak i nowych benchmarkach. SaC ustanawia nową granicę kosztów i wydajności dla wyszukiwania agentowego i z entuzjazmem dzielimy się nim z naszymi użytkownikami oraz szerszą społecznością AI.
Sztywność tradycyjnego wyszukiwania
Pierwsze na świecie wyszukiwarki zostały zbudowane z myślą o użytkownikach-ludziach. Użytkownicy ci oczekiwali przewidywalnego doświadczenia, zazwyczaj składającego się z ustalonej liczby dokumentów uszeregowanych według trafności. Użytkownicy nie chcieli mikrozarządzać procesem wyszukiwania; chcieli wpisać zapytanie i znaleźć użyteczny wynik.
W rezultacie wyszukiwarki skierowane do ludzi zbiegły się w kierunku wspólnej monolitycznej architektury, nastawionej na generowanie przyjaznych dla ludzi stron wyników wyszukiwania (SERP) zawierających n najlepszych trafień dla zapytania wejściowego. Ten schemat napędzał dekady postępu w wyszukiwaniu konsumenckim. Jest on szybki, znany i wysoce skuteczny w przeglądaniu przez ludzi.
Następnie pojawiły się LLM-y, a systemy AI również stały się konsumentami wyszukiwania. Perplexity zapoczątkowało tę zmianę wraz z uruchomieniem naszego silnika odpowiedzi w 2022 roku. Od tego czasu nasze prace inżynieryjne nad wyszukiwaniem koncentrowały się na tym, jak zoptymalizować wartość wyników wyszukiwania dla modeli AI. Aby zbudować najdokładniejszą i najbardziej niezawodną sztuczną inteligencję na świecie, musieliśmy zaprojektować system wyszukiwania skupiony na sprawieniu, aby każdy token był jak najbardziej nasycony informacjami. Wynikające z tego innowacje w zakresie pobierania poddokumentów, efektywności kontekstu, zrozumienia semantycznego i innych kluczowych obszarów doprowadziły do dramatycznych usprawnień w zdolności systemów AI do pobierania i działania na podstawie informacji.
Jednak nawet systemy zoptymalizowane pod kątem AI w dużej mierze odziedziczyły tę samą fundamentalną umowę, co tradycyjne wyszukiwarki: akceptują zapytanie, uruchamiają predefiniowany potok wyszukiwania, zwracają w pełni przetworzony zestaw wyników. W przypadku prostych zapytań podejście to sprawdza się dobrze. Jednak w przypadku bardziej złożonych zapytań staje się to coraz bardziej ograniczającym wąskim gardłem, które obniża wydajność, zwiększa opóźnienia i koszty.
Tryby awarii wynikające ze sztywności
Tradycyjne potoki wyszukiwania są zaprojektowane wokół pojedynczego punktu kontroli: parametrów zapytania. Wyszukiwarka kontroluje resztę potoku zgodnie ze swoją predefiniowaną logiką, a model musi się do niej dostosować. Dla modelu wszystko, co znajduje się za parametrami zapytania, jest sztywne — kształt właściwych obliczeń znajduje się poza kontrolą modelu.
Jest to rozsądna granica, gdy konsumentem jest człowiek skanujący linki. Jest to słaba granica, gdy konsumentem jest model AI próbujący rozwiązywać zadania intensywne pod kątem wiedzy w zamkniętej pętli. Z naszego doświadczenia wynika, że ta sztywność tworzy trzy powtarzające się tryby awarii:
Szorstki kontekst. Modele AI są wrażliwe na jakość i zwartość kontekstu. Zarówno jakość, jak i zwartość są wysoce zależne od zapytania, a monolityczne potoki wyszukiwania nie są zaprojektowane tak, aby działały optymalnie dla wszystkich zapytań. Na przykład, jeśli model potrzebuje pojedynczej, wysoce precyzyjnej informacji, ale ma dostęp tylko do punktu końcowego wyszukiwania typu end-to-end, który priorytetyzuje przypominanie (recall), użycie tego punktu wprowadzi dużą ilość nieistotnych informacji do kontekstu modelu. Z drugiej strony, jeśli model potrzebuje wielu informacji, z których każda wymaga różnych strategii wyszukiwania, może zostać zmuszony do szeregowego wywoływania tego samego suboptymalnego potoku dla wszystkich z nich, co skutkuje wzrostem kosztów i zaszumionym kontekstem.
Niewykorzystanie wiedzy dziedzinowej. W wielu przypadkach model może posiadać wiedzę dziedzinową (z treningu, umiejętności agenta (Agent Skill), pamięci lub wcześniejszych tokenów w trajektorii LLM), która powinna kierować strategią wyszukiwania. Jednak sztywny interfejs wyszukiwania uniemożliwia modelowi wykorzystanie tej wiedzy. Na przykład model może zdać sobie sprawę dla konkretnego żądania, że powinien w określony sposób łączyć sygnały leksykalne i semantyczne, priorytetyzować określone źródła lub agregować wyniki według określonego klucza. Jeśli te spostrzeżenia nie dają się zaimplementować poprzez parametry zapytania, model nie może na nie faktycznie działać.
Nieefektywny przepływ sterowania i zanieczyszczenie kontekstu. Wiele przepływów pracy związanych z wyszukiwaniem nie jest naturalnie szeregowych. Mogą one wymagać rozgałęzienia (fan-out) dla wariantów zapytań, równoległego pobierania, deduplikacji i innych operacji, które wymagają nieliniowego i asynchronicznego przepływu sterowania. Wymuszanie tych kroków poprzez powtarzające się tury modelu dodaje opóźnienia i utrudnia optymalizację przepływu pracy. Zanieczyszcza to również kontekst modelu zaszumionym stanem pośrednim, który może nie być użyteczny dla wnioskowania modelu, co skutkuje gorszą wydajnością i częstszymi kompaktami.
Niewiele można było zrobić w obliczu tych wyzwań w erze wywoływania funkcji i MCP w AI. Gdy każda operacja wyszukiwania wymaga własnej komunikacji z wnioskowaniem LLM, programiści naturalnie wolą wykonać jak najwięcej w tej komunikacji. Oznacza to potoki wyszukiwania end-to-end, które zwracają w pełni przetworzone zestawy wyników do bezpośredniej konsumpcji przez model.
W rezultacie większość systemów wyszukiwania skierowanych do AI działa obecnie w ramach tej monolitycznej umowy. Jednak wraz z ciągłym wzrostem inteligencji modeli i wymagań użytkowników, wady tej umowy stają się bardziej wyraźne. Dzisiejsze najbardziej zaawansowane systemy AI, zbudowane na systemach sterowania opartych na kodzie, potrafią komponować specyficzne dla zadania przepływy pracy, które wykonują tysiące akcji w każdej minucie. Systemy wyszukiwania nie w pełni zrealizowały jeszcze ten potencjał.
Od dawna podejrzewaliśmy, że właściwa granica dla wyszukiwania natywnego AI znajduje się niżej w stosie. Modele nie powinny jedynie wywoływać wyszukiwarki. Powinny być w stanie orkiestrować poszczególne elementy stosu wyszukiwania w zależności od potrzeb konkretnego zadania. To wymaga fundamentalnie innej architektury.
Projektowanie programowalnej architektury wyszukiwania
Następna generacja architektur wyszukiwania będzie opierać się na głównej zasadzie: wyszukiwanie powinno być natywnie programowalne przez agentów AI. Agent nie może być ograniczony do zestawu z góry ustalonych potoków wyszukiwania. Raczej agent musi być w stanie skonstruować potok, którego wymaga konkretne zadanie, używając komponowalnych bloków konstrukcyjnych zaprojektowanych na różnych poziomach abstrakcji.
Nasza nowa architektura Search as Code (SaC) ucieleśnia tę zasadę. W SaC ani jedna operacja pobierania nie jest wysyłana przez wywołanie funkcji lub interfejs MCP. Wszystkie operacje są zamiast tego orkiestrowane poprzez kod Python wygenerowany przez model. W przypadku najprostszych potrzeb wyszukiwania kod ten może składać się z kilku zapytań do punktu końcowego wyszukiwania wysokiego poziomu. Ale w przypadku złożonych zadań kod może być tak skomplikowany, jak to konieczne, obejmując warunkowe wykonanie, asynchroniczność, równoległość i wywołania do szerokiego zakresu prymitywów niskiego poziomu.
SaC obejmuje trzy ściśle powiązane warstwy:
Modele służą jako płaszczyzna sterowania. Rozumują one nad dyrektywą użytkownika (lub agenta nadrzędnego), dekomponują dyrektywę na zadania, decydują, które potoki pobierania i przetwarzania są potrzebne dla każdego zadania, oraz generują kod w celu wdrożenia tych potoków.
Piaskownice obliczeniowe zapewniają deterministyczne obliczenia poprzez bezpieczne środowisko uruchomieniowe wykonywania kodu. Dostarczają one kanwę dla modeli do wdrażania przepływu sterowania, przetwarzania wsadowego, ponownych prób, filtrowania, łączenia, agregacji i innych deterministycznych operacji.
Nasz Agentic Search SDK udostępnia stos wyszukiwania Perplexity jako komponowalne prymitywy. Dostarcza on bloki konstrukcyjne, od niskopoziomowych operacji pobierania po wysokopoziomowe parsowanie semantyczne. Zestaw SDK jest osadzony w środowiskach uruchomieniowych piaskownic, co pozwala modelom na orkiestrowanie nawet tysięcy operacji w ramach jednej tury wnioskowania. Poniżej omawiamy nasze decyzje projektowe dla każdej warstwy.
Agentic Search SDK
Agentic Search SDK definiuje bloki konstrukcyjne dla programowalnego wyszukiwania. Co ważne, nasz zestaw SDK nie jest wcześniej istniejącym interfejsem API wyszukiwania spakowanym jako biblioteka. W ciągu ostatnich kilku miesięcy nasz zespół inżynieryjny przebudował nasz stos wyszukiwania na modułowe, komponowalne prymitywy. Zbudowaliśmy Agentic Search SDK, aby zapewnić bezpośredni dostęp do tych prymitywów, tworząc znacznie potężniejszą kanwę, na której model może orkiestrować specyficzne dla zadania potoki pobierania.
Wysokopoziomowe potoki wyszukiwania end-to-end są nadal dostępne w ramach zestawu SDK, ale nie są już jedyną opcją. Służą raczej jako forma skrótu dla typowych wzorców wyszukiwania. Model może swobodnie z nich korzystać lub je omijać w zależności od potrzeb zadania.
Wybór środowiska uruchomieniowego: Rozważaliśmy Python, Rust, TypeScript i Bash jako środowisko uruchomieniowe SDK. Podejrzewaliśmy, że Python będzie najbardziej naturalnym wyborem, biorąc pod uwagę jego wszechobecność i ekosystem bibliotek do przetwarzania danych. Testy wewnętrzne potwierdziły ten fakt, a wersja zestawu SDK wprowadzana dzisiaj jest oparta na języku Python. Oczekujemy, że będziemy okresowo ponownie oceniać wybór środowiska uruchomieniowego, aby zapewnić, że nadal zapewnia ono najlepsze wrażenia z pracy z agentem.
Ciągłe doskonalenie poprzez autoresearch: Zoptymalizowaliśmy również użyteczność SDK dla modeli LLM Frontier poprzez iteracyjną pętlę autoresearch. Pętla ta, działająca nieprzerwanie przez tygodnie, proponuje i weryfikuje ulepszenia SDK w odniesieniu do metryk, w tym opóźnień, jakości generowanego kodu i ogólnej wydajności zadań. Pętla autoresearch dokonała już licznych zmian zarówno w strukturze, jak i estetyce SDK, z istotnymi zyskami we wszystkich wymiarach. Planujemy dalej udoskonalać SDK poprzez autoresearch w miarę pojawiania się nowych modeli Frontier i komponentów wyszukiwania.
Piaskownice
Piaskownice zapewniają bezpieczne środowiska do definiowania i uruchamiania potoków SaC. Wykonują one wygenerowany przez model kod, zapewniając dostęp do Agentic Search SDK w celu ułatwienia interakcji z poszczególnymi prymitywami wyszukiwania.
Poczyniliśmy znaczące inwestycje w optymalizację i utwardzanie naszych piaskownic, a ich ogólny projekt systemu zasługuje na osobny artykuł. Tutaj skupiamy się na rozważaniach projektowych najbardziej istotnych dla SaC: zarządzaniu stanami pośrednimi.
SaC umożliwia agentom orkiestrowanie złożonych potoków w ramach jednej tury wnioskowania. Jednak pewne stany pośrednie muszą być przekazywane między turami. Na przykład model może chcieć pobrać kilka dokumentów w turze 1, zbadać podpróbkę tych dokumentów w turze 2, aby zdecydować, co zrobić dalej, a następnie opracować potok wyszukiwania niższego rzędu, aby dalej badać te dokumenty od tury 3 wzwyż. Jak omówiono powyżej, przekazywanie stanów pośrednich poprzez przestrzeń tokenów nie jest skuteczną strategią.
Rozważaliśmy dwa podejścia do zarządzania stanami pośrednimi między turami:
Trwały system plików + jawna serializacja/deserializacja (serde): Piaskownice mogą udostępniać trwałe systemy plików, z których modele mogą korzystać między turami. Modele chcące utrwalić stan pośredni mogą uwzględnić krok serializacji w wygenerowanym kodzie dla danej tury, wraz z krokiem deserializacji w kolejnej turze (zarówno do własnej inspekcji modelu, jak i do dalszego wykorzystania). Podejście z systemem plików pozwala modelom bezpośrednio i jawnie kontrolować sposób przekazywania stanu pośredniego między turami. Zapewnia również identyfikowalność poprzez wymuszanie jawnej identyfikacji i trwałości informacji używanych przez model. Jedną wadą jest opóźnienie i narzut kontekstu wynikający z generowania kodu serde, chociaż dobrze zaprojektowane funkcje użytkowe mogą złagodzić ten narzut.
REPL: Alternatywnie, piaskownica może utrwalić samo środowisko wykonawcze między turami w środowisku typu REPL. Pozwala to modelom na utrzymanie stanu pośredniego w pamięci bez konieczności serializacji, ponieważ zmienne zdefiniowane lub zmodyfikowane w poprzednich turach mogą być po prostu przywoływane z nazwy w kolejnych turach. Podejście REPL jest bardziej efektywne pod kątem tokenów, ponieważ pozwala uniknąć konieczności generowania kodu serde. Jednak brak jawnego kroku serializacji może zaszkodzić późniejszej wydajności z powodów znanych każdemu, kto pracował ze 100-komórkowym notatnikiem Jupyter: w miarę jak przestrzeń nazw zmiennych staje się zagracona, trudniej jest śledzić, co dokładnie jest utrwalane między turami i dlaczego.
Przetestowaliśmy oba podejścia i stwierdziliśmy, że chociaż działają one podobnie w zwykłym użytkowaniu, serializacja oparta na systemie plików zapewnia lepszą niezawodność na szczególnie długich trajektoriach. Dlatego przyjmujemy serializację opartą na systemie plików jako nasze rozwiązanie. Przypuszczamy, że wymaganie od modeli deklaratywnego, a nie domniemanego przekazywania stanu pomaga im skuteczniej zarządzać tym stanem. Jest to wstępne ustalenie i będziemy kontynuować iterację nad oboma podejściami.
Modele
Modele zasilające systemy sterowania agentów służą jako płaszczyzna sterowania SaC: dynamicznie montują potoki wyszukiwania z bloków konstrukcyjnych Agentic Search SDK, aby sprostać potrzebom każdego zadania, a następnie wysyłają te potoki jako kod do wykonania w piaskownicy.
Perplexity zatrudnia różnorodne modele w naszych systemach sterowania agentów i stwierdziliśmy, że najnowsze modele Frontier są całkiem skuteczne w ogólnym generowaniu kodu. Głównym wyzwaniem jest skłonienie do efektywnego generowania kodu specjalnie dla Agentic Search SDK. Innymi słowy, jak sprawić, aby modele skutecznie łączyły bloki konstrukcyjne naszego SDK w potoki zoptymalizowane pod kątem zadań?
W przeciwieństwie do biblioteki standardowej języka, niestandardowy zestaw SDK prawdopodobnie nie jest reprezentowany w danych przedtreningowych. Nawet przy usprawnieniach użyteczności SDK wynikających z autoresearch, wiele modeli jest nadal trenowanych do interakcji z systemami wyszukiwania poprzez wywołania funkcji i bezpośrednie wywołania MCP. Modele te prawdopodobnie nie będą sprawnie posługiwać się zestawem SDK, polegając tylko na kodzie źródłowym i automatycznie wygenerowanej dokumentacji.
Aby rozwiązać to wyzwanie, opracowaliśmy wysoce dostrojone umiejętności agentów (Agent Skills), aby nauczyć modele skutecznego wykorzystywania naszego zestawu SDK. Początkowe wersje zostały opracowane zgodnie z zasadami projektowania umiejętności Perplexity, a następnie zoptymalizowane poprzez dedykowaną pętlę autoresearch skupioną na tych samych metrykach, które są używane w optymalizacji SDK. Ograniczamy również rozmiar umiejętności, aby zabezpieczyć się przed przepełnieniem kontekstu, a nasze wersje końcowe mają mniej niż 2000 tokenów w swoich głównych plikach SKILL.md.
Zoptymalizowane umiejętności (Skills) wykraczają poza listowanie dostępnych bloków konstrukcyjnych SDK (coś, co można łatwo uzyskać poprzez refleksję czasu wykonania). Większość tokenów poświęca się na zapewnienie zwięzłych, uogólnionych wskazówek i przykładów typu few-shot dla komponowania tych bloków w złożone wzorce. Wyposażeni w te umiejętności, obserwujemy, jak modele skutecznie wykorzystują SDK do orkiestrowania potoków wyszukiwania, które skalują się do tysięcy dyskretnych operacji.
Kod jako orkiestrator i wypełniacz luk
Łącząc te warstwy, SaC zmienia relację między modelami a stosem wyszukiwania. W systemach wyszukiwania o stałym potoku model znajduje się nad wyszukiwaniem i wchodzi z nim w interakcję poprzez wąski, szeregowy interfejs wywołujący. W SaC model staje się aktywnym uczestnikiem orkiestrowania samego procesu wyszukiwania. Wszystkie elementy stosu wyszukiwania stają się bezpośrednio programowalne przez model, przy czym piaskownica i Agentic Search SDK zapewniają niezbędne interfejsy do tych elementów.
Ale co z możliwościami, które nie istnieją natywnie? Kod jest potężnym medium do orkiestrowania wcześniej istniejących możliwości. Jednak moc kodu wykracza poza orkiestrację. Może on również służyć jako wypełniacz luk dla możliwości, których nie ma w stosie wyszukiwania lub SDK. Agenci czerpią korzyści z parsymonii i nieefektywne byłoby, gdyby SDK pokrywało każdą potencjalną operację dedykowaną funkcją. Zamiast tego, SDK zapewnia najbardziej fundamentalne prymitywy, a model może zbudować wszelkie dodatkowe komponenty w locie za pomocą kodu.
Rozważmy, na przykład, złożony regex, który nie jest efektywnie implementowalny poprzez własną składnię zapytań systemu wyszukiwania. Bez możliwości kodowania, model byłby zmuszony wygenerować swoje najlepsze przybliżenie, wykonać zapytanie i przeprowadzić filtrowanie w przestrzeni tokenów na bardzo zaszumionym zestawie wyników. Dzięki SaC, model może zamiast tego wygenerować program, który wykonuje równoległe wywołania SDK w celu zebrania nadzbioru żądanego zestawu wyników regex. Po deduplikacji przez SDK, model może następnie napisać dodatkowy kod w celu deterministycznego zawężenia wyników do dokładnego wyrażenia regularnego. W ten sposób agenci mogą wdrażać niestandardowe możliwości wyszukiwania bez obciążania SDK zbyt niszowymi podprogramami.
Studium przypadku: Doradztwo dostawcy CVE
Prezentujemy studium przypadku oparte na rzeczywistym zadaniu w ramach naszego pakietu testowego. Zadanie to wymaga od agenta zidentyfikowania i scharakteryzowania ponad 200 CVE o wysokim stopniu dotkliwości z lat 2023–2025. Każdy rekord musi cytować własne zalecenie (advisory) dotkniętego dostawcy, podawać nazwę produktu i wersję poprawki oraz wykazywać, że wersja poprawki jest powiązana z konkretnym CVE.
Odpowiedź SaC uzyskała wynik 100% pod względem dokładności, przy czym całkowite zużycie tokenów spadło o 85,1% w stosunku do linii bazowej bez SaC (z 288,7 tys. tokenów do 42,9 tys. tokenów). Testowane systemy innych firm niż Perplexity, omówione bardziej szczegółowo w sekcji 4, uzyskały wyniki poniżej 25%.
Prezentujemy trzy wystylizowane bloki kodu z trajektorii, aby zilustrować, w jaki sposób programowalność SaC pozwala modelowi wdrażać skuteczne strategie.
# Part 1 of 3: fan out over official advisory formats.
templates = [
("Mozilla",
'site:mozilla.org/en-US/security/advisories/mfsa{year} '
'"CVE-{year}-" "Fixed in" "Impact high"'),
("Jenkins",
'site:jenkins.io/security/advisory/{year} '
'"CVE-{year}" "Severity" "High" "Fix"'),
("Chrome",
'site:chromereleases.googleblog.com/{year} '
'"High CVE-{year}" "Stable channel has been updated"'),
("Android",
'source.android.com/docs/security/bulletin/{year}-{month:02d}-01 '
'"High" "CVE-{year}"'),
...
]
queries = [
{"vendor": vendor, "query": pattern.format(year=year, month=month)}
for year in [2023, 2024, 2025]
for vendor, pattern in templates
for month in ([1] if "{month" not in pattern else range(1, 13))
]
seed_hits = sdk.search.web_many(queries, limit_per_query=8, concurrency=12)
pages = [
{"vendor": q["vendor"], "url": h.url, "text": join_result_fields(h)}
for q, hits in zip(queries, seed_hits)
for h in hits
if official_vendor_advisory(h.url, q["vendor"])
]Ten pierwszy blok to czysta orkiestracja. Wygenerowany program rozpoczyna się od zakodowania reguły klasy źródłowej bezpośrednio w planie zapytania: tylko formaty zaleceń (advisory) będące własnością dostawcy są istotne. Źródła inne niż dostawcy, w tym NVD, MITRE, CVE Details, artykuły prasowe, strony CERT i bazy danych stron trzecich, są strukturalnie poza zakresem. Ograniczenia dotyczące dokładnych fraz również kierują wyszukiwanie w stronę stron, których zaindeksowany tekst już zawiera precyzyjne szczegóły CVE potrzebne w dalszej części.
# Part 2 of 3: synthesize refinements for sparse vendor-years.
coverage = summarize(pages, by=["vendor", "year", "url_kind"])
prompt = """
Goal: 230+ high or critical CVEs from official vendor advisories.
Avoid aggregators, CERTs, news, NVD, MITRE, and CVE databases.
Current coverage:
{coverage}
Suggest more site-scoped exact-phrase queries for sparse vendor-years.
Prefer per-advisory pages over archive or month-index pages.
Return JSON lines with vendor and query.
""".format(coverage=coverage)
raw = query_llm(prompt)
expanded_queries = [
row for row in parse_jsonl(raw)
if official_scope(row["query"]) and mentions_cve_year(row["query"])
]
expanded_hits = sdk.search.web_many(
unique(expanded_queries),
limit_per_query=8,
concurrency=12,
)Drugi blok wykorzystuje LLM jako pośredni podprogram planowania. Kod podsumowuje, które pary dostawca-rok produkują wystarczającą liczbę stron kandydackich, prosi o ukierunkowane udoskonalenia i weryfikuje każde zaproponowane zapytanie przed wykonaniem. Zachowuje to użyteczne wzorce z trajektorii, takie jak sondy dokładnych fraz o zasięgu witryny (site-scoped) i adaptacyjne uzupełnianie, bez hardkodowania niestandardowego crawlera zaleceń CVE wewnątrz SDK.
# Part 3 of 3: verify CVE-to-version binding.
all_hits = dedupe_by_url(flatten(seed_hits) + flatten(expanded_hits))
items = [
{"url": h.url, "vendor_hint": infer_vendor(h.url),
"text": join_result_fields(h)}
for h in all_hits
if official_vendor_advisory(h.url, infer_vendor(h.url))
]
verified = sdk.llm.extract_many(
items,
instruction=(
"Keep only vendor advisories where the page ties a high or critical "
"CVE to a specific fixed version, build, patch, or security level."
),
schema={
"matches": bool,
"cve": str,
"vendor": str,
"product": str,
"fix_version": str,
"severity": str,
"source_url": str,
"evidence": str,
"version_bound_to_cve": bool,
"confidence": float,
},
)
records = [
to_cve_record(x) for x in verified
if x.matches and x.version_bound_to_cve
if high_or_critical(x.severity) and x.confidence > 0.75
]
records = dedupe_by(records, key="cve")Ostatni blok to weryfikator wyników, którego logika jest całkowicie zdefiniowana przez agenta poprzez generowanie kodu. Chociaż podprogramy wyszukiwania mogą znajdować wiarygodne strony zaleceń, zadanie wymaga ściślejszej relacji: zalecenie musi wiązać jedno CVE z jednym dotkniętym produktem i jedną wersją poprawki w tekście autorstwa dostawcy. Schemat czyni tę relację jawną, więc kod niższego rzędu może deduplikować według CVE, odrzucać adresy URL agregatorów, odrzucać słabe dowody wersji i kontynuować uzupełnianie, aż zostanie osiągnięte minimum liczby rekordów.
Wyniki ewaluacji
Oceniamy skuteczność SaC na kompleksowym pakiecie benchmarków, koncentrując się zarówno na bezwzględnej wydajności, jak i granicy kosztów i wydajności.
Benchmarki i systemy
Nasz pakiet benchmarków składa się z pięciu benchmarków, zaprojektowanych w celu przetestowania stresowego przepływów pracy AI zależnych od wyszukiwania w różnych domenach, formatach zadań i poziomach złożoności. Używamy czterech wcześniej istniejących benchmarków: DeepSearchQA (DSQA), BrowseComp, Humanity's Last Exam (HLE) oraz WideSearch. Dla pierwszych trzech benchmarków (DSQA, BrowseComp i HLE) raportujemy dokładność jako główną metrykę. Dla WideSearch raportujemy wynik F1 dla każdego wiersza.
Używamy również nowo opracowanego benchmarku WANDR, który koncentruje się na trudnych zadaniach „szerokiego badania” (wide research), wymagających starannej orkiestracji wyszukiwania, obliczeń i wnioskowania modelu. WANDR jest inspirowany intensywnymi pod kątem wiedzy zadaniami zawodowymi, które Perplexity Computer obsługuje dla użytkowników. Iteruje on na WideSearch i podobnych benchmarkach, ale kładzie nacisk na bardziej złożone zadania i struktury zadań. Opublikujemy benchmark WANDR w nadchodzących tygodniach.
Oceniamy pięć systemów opartych na agentach. Rozważamy indywidualne uruchomienia, a nie wyniki „best-of-N”, aby wyizolować wydajność leżącej u podstaw architektury, a nie korzyści płynące z paralelizacji. Konfiguracja każdego systemu jest opisana poniżej:
Perplexity (SaC): Oceniamy architekturę SaC w ramach produkcyjnego Agent API Perplexity. GPT 5.5 (wysokie wnioskowanie) służy jako leżący u podstaw model.
OpenAI: Oceniamy interfejs OpenAI Responses API z włączonym zarówno wyszukiwaniem (web_search), jak i środowiskiem uruchomieniowym Python (code_interpreter). GPT 5.5 (wysokie wnioskowanie) służy jako leżący u podstaw model.
Anthropic: Oceniamy Anthropic Managed Agents z zestawem narzędzi agenta 20260401 i automatycznie dozwolonymi poleceniami. Opus 4.7 (wysokie wnioskowanie) służy jako leżący u podstaw model.
Exa: Oceniamy Exa Agent poprzez produkcyjne API, z wysiłkiem ustawionym na wysoki.
Parallel: Oceniamy Parallel Tasks poprzez produkcyjne API, z procesorem ustawionym na ultra4x.
Porównawcza wydajność
Tabela 1 przedstawia wyniki benchmarków dla każdego systemu. SaC przewyższa wszystkie inne systemy w czterech z pięciu benchmarków. W pozostałym benchmarku (HLE) SaC zasadniczo zajmuje pierwsze miejsce ex aequo z OpenAI Responses. Rysunek 2 z Wprowadzenia wizualizuje te wyniki w formie graficznej.
Benchmark | Perplexity (SaC) | OpenAI | Anthropic | Exa | Parallel |
DSQA | 0.871 | 0.733 | 0.815 | 0.530 | 0.810 |
BrowseComp | 0.805 | 0.720 | 0.598 | 0.380 | 0.560 |
HLE | 0.612 | 0.614 | 0.566 | 0.387 | 0.515 |
WideSearch | 0.651 | 0.522 | 0.590 | 0.471 | 0.584 |
WANDR | 0.386 | 0.130 | 0.152 | 0.057 | 0.126 |
Tabela 1 | Wyniki benchmarków dla każdego ocenianego systemu. Najlepszy wynik w wierszu pogrubiony.
Chociaż SaC osiąga najnowocześniejszą wydajność w całym pakiecie benchmarków, jego przewaga jest szczególnie wyraźna w WANDR. Rysunek 5 rozbija wyniki WANDR, pokazując, że implementacja SaC w Perplexity przewyższa kolejny najlepszy system o współczynnik 2,5×. Pomimo tej dużej marży, WANDR pozostaje nienasycony i okazuje się wyzwaniem nawet dla SaC. Wierzymy, że złożone, wysoce horyzontalne przepływy pracy wyszukiwania wymagane przez zadania WANDR będą wymagały dodatkowych postępów w badaniach i inżynierii wyszukiwania, aby osiągnąć stale wysoką wydajność.
Na koniec mierzymy względne zyski SaC w porównaniu z bardziej tradycyjnym potokiem wyszukiwania, który wykorzystuje tę samą infrastrukturę wyszukiwania Perplexity. Rysunek 6 przedstawia różnicę w wynikach między architekturami SaC i nie-SaC dla każdego benchmarku. Ogólnie rzecz biorąc, SaC zapewnia znaczny wzrost wydajności, z największym bezwzględnym zyskiem w DSQA (+19,77 pp, 29%) i największym względnym zyskiem w WANDR (+12,00 pp, 45%).
Granica kosztów i wydajności
W praktyce użytkownicy dbają o wydajność w stosunku do kosztów. Dlatego oceniamy granicę kosztów i wydajności w DSQA i WideSearch. Mierzymy koszt i wydajność SaC na niskich, średnich i wysokich poziomach wnioskowania modelu, obok innych systemów. Rysunki 7 i 8 przedstawiają wynik benchmarku w stosunku do ceny za zadanie, z odwróconą osią x, tak aby punkty bardziej na prawo były tańsze, a punkty wyżej reprezentowały lepszą wydajność.
W DSQA wszystkie trzy ustawienia SaC znajdują się na górnej prawej granicy. Ustawienie niskiego wnioskowania jest tańsze niż wszystkie inne systemy, przy czym działa lepiej niż dwa z nich. Ustawienie średniego wnioskowania przewyższa wszystkie systemy nie-SaC przy koszcie poniżej 1 USD za zadanie. Ustawienie wysokiego wnioskowania osiąga najwyższą wydajność przy konkurencyjnym koszcie.
WideSearch pokazuje podobnie ukształtowaną granicę. Podobnie jak w przypadku DSQA, ustawienie niskiego wnioskowania osiąga konkurencyjną wydajność przy niższym koszcie niż wszystkie systemy nie-SaC, podczas gdy zarówno średnie, jak i wysokie wnioskowanie przewyższają systemy nie-SaC pod względem wydajności zadań.
W stronę nowej architektury obliczeniowej
Search as Code odzwierciedla szerszą zmianę w projektowaniu oprogramowania. Przez dziesięciolecia systemy oprogramowania były organizowane wokół deterministycznych instrukcji wykonywanych przez tradycyjne środowiska uruchomieniowe na procesorach. Modele Frontier wprowadzają nową formę obliczeń: wnioskowanie w przestrzeni tokenów, które potrafi zarówno podążać za instrukcjami, jak i je generować. Najbardziej wydajne systemy obliczeniowe połączą te dwie formy obliczeń, zamiast wybierać między nimi.
Wyszukiwanie jest naturalnym poligonem doświadczalnym dla tej hybrydowej architektury. Modele dobrze nadają się do decydowania, jakie dowody są potrzebne i jak należy rozstrzygać niepewność. Deterministyczne środowiska uruchomieniowe dobrze nadają się do przetwarzania wsadowego, paralelizacji, filtrowania, rankingu i agregacji. Infrastruktura wyszukiwania służy jako uniwersalna warstwa I/O, zapewniając środowisku uruchomieniowemu użyteczne uchwyty do informacji na świecie, które mogą obsłużyć tysiące operacji na minutę. Gdy te elementy zostaną zintegrowane, powstały system jest znacznie potężniejszy i wydajniejszy w realizacji zadań w świecie rzeczywistym.
Każda część tej architektury jest niezbędna do odblokowania tych możliwości. Bez inteligentnych modeli system nie może wnioskować o strategii wyszukiwania. Bez piaskownic modele są zmuszone do szeregowego I/O i nieefektywnego przetwarzania w przestrzeni tokenów. A bez atomowego stosu wyszukiwania model nie ma nic do orkiestrowania. SaC działa, ponieważ wnioskowanie, obliczenia deterministyczne i I/O są wspólnie zaprojektowane w celu uwydatnienia mocnych stron każdej warstwy.
To wspólne projektowanie (codesign) można jeszcze bardziej rozwinąć poprzez rozwój pętli ciągłego doskonalenia. Na przykład Agentic Search SDK i umiejętności SaC Agent Skills mogłyby być wspólnie optymalizowane we wspólnej pętli autoresearch. Co ambitniej, modele można szkolić tak, aby wykorzystywały niskopoziomowe prymitywy wyszukiwania udostępnione jako SDK. Można by nawet spróbować wspólnie ewoluować projekt SDK w trakcie samego procesu szkolenia modelu. Z niecierpliwością oczekujemy realizacji bardziej agresywnych strategii wspólnego projektowania w przyszłych pracach.
Zbudowaliśmy SaC, aby umożliwić modelom kontrolowanie procesu wyszukiwania, zamiast jedynie konsumowania jego wyników. Nasze wyniki dowodzą siły tej kontroli: SaC przesuwa zarówno bezwzględną wydajność, jak i granicę kosztów i wydajności w benchmarkach agentów intensywnie korzystających z wiedzy. Z entuzjazmem udostępniamy możliwości SaC naszym użytkownikom, zaczynając od dzisiaj od Perplexity Computer i Agent API. Będziemy nadal optymalizować architekturę SaC we wszystkich warstwach stosu, aby zapewnić naszym użytkownikom (i służącym im agentom) najpotężniejsze i najbardziej wydajne możliwości wyszukiwania.
Cytowanie
@online{perplexity2026RethinkingSearchAsCodeGeneration,
author = {{Perplexity} and Denis Bykov and Aleksandr Nikolenko and Artur Zayats and Maksim Gaiduk and Vitalii Polshkov and Akshay Nair and James Liounis and Kirill Priemko and Ryan Buchmayer and Phi Hoang and Jerry Ma},
title = {{Rethinking search as code generation}},
date = {2026-06-01},
url = {https://perplexity.coolkeypoint.com/hub/blog/rethinking-search-as-code-generation}
}