Photon: Budování vyhledávacího a řadícího enginu od základu
Jak specializované datové formáty, dávkové čtení a oddělení sestavování indexu od obsluhy dotazů zlepšují vyhledávání zaměřené na AI.
Úvod
Vyhledávací zásobník zaměřený na AI od Perplexity načítá a řadí kandidátní stránky a vynáší na povrch nejrelevantnější obsah pro model k podložení jeho odpovědí. Původně tato fáze běžela na open-source enginu, který jsme přizpůsobili našemu systému. Jak však náš index a pracovní zátěže rostly, narazili jsme na pevné limity nákladů, latence chvostu a doby obnovy uzlů, které jsme nemohli vyřešit v rámci stávající platformy třetí strany.
K překonání těchto výzev jsme vytvořili Photon, nový interní engine pro načítání a řazení. To navazuje na náš přístup budování vyhledávací infrastruktury zaměřené na AI od základu, jak jsme to udělali s CobbleDB, naším distribuovaným úložištěm klíč-hodnota pro obsluhu záznamů stránek, a naším Search API.
Photon používá kompaktní datové formáty k čtení a dekódování pouze toho, co každý dotaz potřebuje, s mezipamětí podporující dávky a asynchronním čtením ke snížení přístupu na disk a překrývání čekání. Sestavování indexu probíhá odděleně od obsluhy dotazů, přičemž aktualizuje jednu obslužnou skupinu najednou a zahřívá její mezipaměti před směrováním živých dotazů na ni.
Photon nyní pohání naši vyhledávací tubu a je také základem nové rychlé předvolby pro naše Search API, s latencí jednoho vyhledávacího volání 160 ms při p50 a 230 ms při p95. Napříč šesti veřejnými srovnávacími testy snížila rychlá předvolba odhadované náklady na model plus vyhledávání na úkol o 68 % při zachování celkové kvality úkolu srovnatelné s výchozí předvolbou. Vydáváme ji dnes, aby vývojáři mohli získat přístup k těmto vylepšením.
Co vyhledávací a řadící engine vyžaduje
Pro každý dotaz prohledává vyhledávací a řadící engine velký distribuovaný index, aby vybral a uspořádal malou sadu relevantních stránek v rámci rozpočtu latence vyhledávacího zásobníku. Systém musí také udržovat index aktuální s tím, jak přicházejí nové stránky a stávající se mění, aniž by tato práce zpomalovala živé dotazy.
Vyhodnotili jsme několik možností open-source a zvolili vyzrálý, univerzální vyhledávací a řadící engine, který splnil naše počáteční potřeby. Poté jsme jej přizpůsobili naší architektuře a pracovním zátěžím při zachování stabilní latence a propustnosti v produkčním měřítku.
Kde náš předchozí systém selhal
Udržovali jsme vlastní fork pro přizpůsobení enginu našemu rostoucímu indexu, ale řešení klíčových omezení stále více vyžadovalo přepracování velkých částí platformy. Několik omezení se ukázalo jako obtížně řešitelných bez zásadních architektonických změn:
1. Škálovací náklady: Jak počet dokumentů, které ukládáme, tak množství dat na dokument, která potřebujeme načíst a řadit, v průběhu času rostly. Toto škálování vyvíjelo tlak na náš hardware, zejména na množství dostupné paměti RAM. Pro kontrolu nákladů jsme potřebovali kompaktnější datové reprezentace a jemnější kontrolu nad tím, co zůstává v paměti a co se čte z disku na vyžádání.
2. Špatná latence chvostu. Na produkčním provozu byla doba odezvy p99 starého systému přibližně 800 ms. Datová sada překročila dostupnou paměť RAM, takže jsme nemohli použít mlock k jejímu udržení výhradně v paměti. Přístup k nerezidentním datům mohl vyvolat hlavní výpadky stránky, což pozastavilo provádění dotazů, zatímco operační systém četl z disku.
Engine také pravidelně slučoval svůj hlavní diskový index s menšími indexy vyčištěnými z paměti. Během tohoto procesu fúze diskového indexu se počet výpadků stránky ještě zvýšil, přičemž latence p99 dosáhla přibližně 1,2 s po dobu 10–15 minut. Jak se mezipaměti zahřály, frekvence výpadků stránky i latence klesly, což naznačuje, že čtení zastudena bylo hlavním přispěvatelem k špičkám. Přednačtení dat a zahřátí indexu snížilo špičky, ale adekvátně neřešilo vysokou latenci chvostu.
3. Zdlouhavá obnova a synchronizace. V předchozím systému mohlo nasazení dodatečného clusteru a synchronizace jeho dat trvat déle než týden. Synchronizace byla vyžadována také při přidávání skupin uzlů k uložení dalších kopií dat nebo při obnově po selháních uzlů. Během obnovy byl větší podíl odpovědí na vyhledávání částečný, což snižovalo skóre kvality konečné odpovědi.
Nakonec jsme dospěli k závěru, že by bylo jednodušší a levnější stavět od základu namísto pokračování v přizpůsobování a údržbě našeho forku. S čerpáním z našich zkušeností s provozem předchozího systému pracoval malý inženýrský tým po boku roje perzistentních kódovacích agentů na budování Photonu.
Kam Photon zapadá do vyhledávacího zásobníku
Photon je navržen pro dva primární pracovní postupy: obsluhu požadavků na vyhledávání a příjem aktualizací z potrubí zpracování.
Požadavek z vyhledávací služby vyšší úrovně se dostane k brokeru Photon přes vyrovnávač zatížení. Broker vybírá skupinu shardů a přeposílá požadavek na každý shard, přičemž sleduje časové limity a v případě potřeby vydává další požadavky.
Každý shard provádí načítání, počáteční řazení a řazení druhé fáze, a poté vrací své nejlepší kandidáty brokerovi. Broker kombinuje tyto kandidáty do jednoho seřazeného seznamu a načítá klíčová pole dokumentů pro vybrané stránky z příslušných shardů. Poté vrací tyto výsledky vyhledávací službě vyšší úrovně, která shromažďuje obsah stránek potřebný pro agenta k vygenerování podložené odpovědi.
Aktualizace indexu následují oddělenou cestou. Pillar, naše publikační vrstva, připravuje data a ukládá je do tabulek YTsaurus. Indexery převádějí tyto tabulky na indexové struktury, které shardy mohou načíst pro vyhledávání. Kontrolér koordinuje přípravu dat, sestavování indexu a nasazení nových verzí indexu do obslužných skupin. Před zpracováním živého provozu každý shard přehrává skutečné dotazy na vyhledávání, aby zahřál své mezipaměti.
Návrh Photonu pro naše pracovní postupy
Tři návrhová rozhodnutí činí Photon dobře přizpůsobeným naší pracovní zátěži: kompaktní datové reprezentace, mezipaměť a asynchronní čtení z disku a oddělení aktualizací indexu od obsluhy dotazů.
Datové reprezentace pro vyhledávání a řazení
Načítání a řazení přistupují k datům dokumentu odlišně. Lexikální načítání začíná výrazy dotazu a hledá dokumenty, které je obsahují. Řazení pak vyžaduje podrobné informace o každém vybraném dokumentu, včetně frekvencí termínů a pozic.
Photon používá dvě doplňkové reprezentace na podporu těchto vzorů přístupu. Invertovaný index ukládá seznam odpovídajících dokumentů pro každý výraz, nazývaný seznam zveřejnění, na podporu výběru kandidátů. Samostatné záznamy pro každý dokument poskytují data potřebná pro řazení. Optimalizujeme také způsob, jakým jsou seznamy zveřejnění ukládány a procházeny, abychom zlepšili výběr v rámci pevného pracovního rozpočtu.
Specializované datové reprezentace nám umožňují efektivněji ukládat klíčová data. Kompaktnější invertovaný index snižuje množství dat čtených nebo přesouvaných na jeden dotaz, což zlepšuje latenci eliminací zbytečných operací I/O. Tyto formáty také umožňují zkontrolovat, zda se dokument objevuje v seznamu bez jeho úplného dekódování, což je vlastnost, na kterou spoléhá naše logika procházení pro kontrolu množství práce, kterou dotaz provádí v rámci pevného rozpočtu. Optimalizace záznamů pro jednotlivé dokumenty nám umožňuje uložit více dat na dokument pro řazení a zároveň snižuje škálovací náklady spojené s naším předchozím systémem.
Invertovaný index
Pro každý výraz ukládá invertovaný index seznam zveřejnění. Tyto seznamy mohou sahat od několika dokumentů až po miliony, takže žádný jediný formát nefunguje dobře pro každý seznam. Photon vybírá formát na základě počtu dokumentů obsahujících výraz.
Krátké seznamy používají vkládanou reprezentaci, která se vejde na jedinou stránku a nevyžaduje tabulku bloků. Delší seznamy, které se do vkládaného formátu nezveřejní, jsou rozděleny do bloků pokrývajících pevné rozsahy identifikátorů dokumentů. Reprezentace každého bloku závisí na jeho hustotě: řídká pole ukládají tříděná pole ofsetů ID dokumentu, zatímco hustá pole používají bitmapy. Řídké bloky používají cvalové vyhledávání, které postupuje ve stále větších krocích, aby přeskočilo oblasti seznamu. Husté bloky umožňují kontroly členství vyhledáním bitu odpovídajícího dokumentu.
Frekvence dotazu výrazu určuje, kde je jeho seznam zveřejnění umístěn na disku. Často dotazované seznamy jsou uloženy blízko sebe, čímž se koncentrují horká data v menší oblasti úložiště. V rámci každého seznamu jsou dokumenty seřazeny podle samostatného signálu užitečnosti, takže slibnější kandidáti jsou potkáni dříve.
Procházení seznamů v rámci pracovního rozpočtu
Výběr kandidátů vyžaduje procházení seznamů zveřejnění za účelem nalezení potenciálně relevantních dokumentů. Vyčerpávající skenování každého seznamu je nákladné, takže Photon používá algoritmus typu WAND, který prochází pouze některé seznamy a přitom stále zohledňuje shody v ostatních.
Algoritmus rozděluje seznamy do dvou skupin. Hnací seznamy posouvají procházení vpřed a navrhují kandidátní dokumenty. Seznamy sond přispívají ke skóre těchto kandidátů, ale sami o sobě generují kandidáty.
Bodování kandidátů kombinuje vážené pokrytí výrazů dotazu a frekvenční příspěvek s normalizací délky dokumentu a limitem zisků z opakovaných výrazů. Photon nejprve zkontroluje přítomnost výrazu v polích a bitmapách, aby ohraničil maximální možné skóre každého kandidáta. Čte přesné frekvence výrazů pouze pro kandidáty, kteří by stále mohli splnit práh výběru.
Výběr pak probíhá ve třech krocích:
- Procházení: Začněte s přísným požadavkem na pokrytí dotazu. Pokud je nalezeno příliš málo kandidátů, opakujte s uvolněným požadavkem.
- Zachování: Kandidáti jsou udržováni v ohraničené sadě pomocí skóre, které kombinuje pokrytí a frekvenci. Jak se shromáždění nejlepší kandidáti zlepšují, práh přijetí stoupá.
- Konečný výběr: Konečné výsledky pro průchod jsou vybrány ze zachované množiny na základě frekvenčního skóre. Každé pole provádí vlastní průchod a výsledky jsou sloučeny pomocí vah polí.
Algoritmus vytváří kandidátní množinu relevantních dokumentů, která se použije při dalším řazení.
Docblob
Po výběru kandidátních stránek potřebuje fáze řazení frekvence výrazů, pokrytí polí a umístění a blízkost výskytů. Získání těchto dat prostřednictvím invertovaného indexu by vyžadovalo roztroušené přístupy napříč mnoha páry výraz-dokument. V Photonu ukládáme kompaktní záznam pro každý dokument zvaný docblob, který obsahuje frekvence výrazů, pole, ve kterých se výraz objevil, a kde v dokumentu k němu došlo. Výsledkem je, že řazení kandidáta vyžaduje pouze jedno vyhledávání na dokument.
Formát je vytvořen tak, aby se zabránilo dekódování nad rámec nutnosti. V rámci docblobu jsou pole seskupena podle textového analyzátoru. Každá skupina ukládá seřazený seznam výrazů kódovaný pomocí Elias-Fano, takže dotazovaný výraz lze vyhledat přímo bez předchozího rozbalení celého seznamu. Maska pole označuje, kde se výraz vyskytl, počítadlo frekvence sleduje, jak často, a skutečné pozice jsou v samostatných blocích pro každý pár výraz-pole kódovaných jako rozdíly pozic. Dekódují se pouze bloky pro výrazy, které se skutečně shodovaly, zatímco vše ostatní v záznamu zůstává v komprimovaném formátu.
Toto selektivní dekódování umožňuje docblobu podporovat ukládání více dat na dokument bez úměrného zvýšení nákladů. Růst záznamu nenutí k větší práci na vyhledávání, protože řazení se vždy dotýká pouze částí relevantních pro shodné termíny namísto celého záznamu.
Dávkové čtení a práce s pamětí
Dokonce i s efektivnějšími datovými reprezentacemi RAM stále dochází dříve než ostatní prostředky, takže často potřebujeme přistupovat k datům uloženým na disku. Photon používá asynchronní čtení z disku a mezipaměť optimalizovanou pro dávkové čtení k zrychlení těchto přístupů, čímž snižuje dobu čekání na požadavek. To přispívá k významnému zlepšení latence chvostu ve srovnání s předchozím systémem.
Překrývající se čekání na disk
Řazení vyžaduje získání rysů sta dokumentů umístěných v různých částech souboru. Náš předchozí systém používal mmap k mapování souboru do virtuálního adresního prostoru, aby jej bylo možné číst přes ukazatele paměti. Přístup k stránce, která chyběla v paměti, však stále vyžadoval čtení z disku. Vlákno muselo počkat na data před pokračováním ve výpočtu a tato doba čekání se sčítala, když proces potřeboval číst studené záznamy sekvenčně.
Photon využívá skutečnosti, že ofsety záznamů jsou předem známy, takže můžeme odeslat několik čtení najednou. To řídí reaktor: vyhrazená pracovní vlákna odesílají dávky prostřednictvím io_uring a předávají načtená data volajícímu kódu. Každé vlákno udržuje vlastní io_uring; dávky jsou odesílány do nejméně zatíženého reaktoru. Předem registrované deskriptory souborů a bazény vyrovnávacích pamětí snižují režii opakovaného čtení. Toto asynchronní provádění umožňuje překrývání čekání na několik operací, což snižuje latenci chvostu.
Snížení počtu přístupů na disk
Před odesláním čtení Photon zkontroluje celou dávku v mezipaměti v paměti. Pouze požadavky na chybějící záznamy jdou na disk; přečtená data pak naplní mezipaměť.
Mezipaměť je navržena pro dávkové čtení. Konkrétně:
- Při zásahu neexistuje žádné samostatné přidělení. Místo toho jsou data zkopírována do vyrovnávací paměti připravené předem volajícím kódem.
- Čtenář neprovádí získání zámku. Místo toho zkontroluje verzi dat a provede opakovaný pokus, pokud dojde ke konfliktu se zápisem.
- Používá vyhnání CLOCK namísto vyžadování aktualizací sdíleného seznamu LRU při každém čtení.
- Využívá přednačítání mezipaměti procesoru, kdy jsou metadata a data pro nadcházející záznamy vyžádána předem.
Tato optimalizace snižuje latenci bez poškození výkonu na pracovních zátěžích, na kterých nám záleží.
Sestavování a aktualizace indexu odděleně od obsluhy
V Photonu běží sestavování indexu a obsluha dotazů na různých uzlech. Indexery transformují data do připravených úložných struktur, zatímco obslužné uzly je čtou a provádějí načítání a řazení. Výsledkem je, že sestavování indexu nesoutěží s vyhledávacími dotazy o procesor a prostředky pro tyto úkoly lze škálovat nezávisle.
Toto oddělení řeší úzká hrdla obnovy a synchronizace přítomná v předchozí implementaci. Protože sestavování indexu probíhalo uvnitř stejného clusteru, který obsluhoval dotazy, obnova selhaného uzlu nebo přidání nové kopie dat mohlo znamenat reindexaci dokumentů na místě, což byl proces, který trval dny a zvyšoval částečné odpovědi během obnovy. Photon odstraňuje toto propojení; indexery běží jako distribuované úlohy, které produkují verzované indexové struktury připravené k obsluze nezávisle na obslužných uzlech, takže sestavení další verze indexu již nesoutěží s živým provozem.
Příprava a distribuované budování
Sestavení začíná výběrem dokumentů pro aktivní index a vytvořením zdrojového exportu. Kontrolér zaznamenává zdrojovou verzi, vstupní tabulky a očekávaný počet shardů. Úlohy MapReduce v YTsaurus připravují text, metadata, fragmenty, rysy a předem vypočítané vložení do verzovaných tabulek rozdělených podle shardů. Každý indexer streamuje připravenou tabulku, vytváří struktury pro svůj shard a slučuje textové segmenty. Připravený index spolu s popisem jeho součástí je nahrán do objektového úložiště.
Počet shardů a počet současně běžících indexerů jsou konfigurovány nezávisle, takže sestavení může probíhat v několika vlnách nebo lze pro paralelní práci přidělit více strojů. Distribuovaná příprava a paralelní indexery umožňují vytvoření celého webového indexu v řádu jednociferných hodin, což je výrazné zlepšení oproti předchozímu systému.
Nasazení nové verze
Před nasazením kontrolér zkontroluje, zda je nový index připraven. Pokud jsou data neúplná, nasazení je automaticky zablokováno.
Pokud existuje více obslužných skupin, kontrolér je aktualizuje jednu po druhé. Nejprve odstraní skupinu ze směrování brokera a spustí ji na nové verzi indexu. Před obsluhou požadavků procházejí shardy samostatnou fází zahřívání založenou na protokolech vyhledávání, která předběžně naplní počáteční stav a učiní přechod na reálný provoz plynulejším. Po zahřátí kontrolér zkontroluje stav indexu a připravenost a poté vrátí skupinu do směrování. Ostatní skupiny během této doby nadále obsluhují požadavky. Protože stav provozu je zachován, restart nenutí k zahájení sestavení od začátku; kontrolér se může znovu připojit k již probíhající přípravě, znovu použít připravený výsledek nebo pokračovat v přerušené rotaci od uloženého kroku.
Časy aktualizace dat se liší napříč vertálami vyhledávání. Pro vertály s vysokými požadavky na čerstvost trvá doručení dokumentu jednociferný počet minut. Další kopie dat může být nasazena přibližně za půl hodiny; připravené indexy jsou načteny a připojeny k shardům bez reindexace zdrojových dokumentů.
Migrace a ověření v našem vyhledávacím zásobníku
Naším prvním cílem bylo nahradit starý systém enginem, který zachoval funkčnost a kvalitu vyhledávání a zároveň snížil doby odezvy. Ověřili jsme Photon po etapách, počínaje reprodukovatelnými zátěžovými testy a offline hodnocením kvality před přechodem na živý provoz. Photon nyní pohání načítání a řazení v našem produkčním vyhledávacím zásobníku, s podstatně nižšími dobami odezvy než starý systém.
Zátěžové testy a vyhodnocení
Pro testování výkonu jsme vytvořili nástroj pro zátěžové testování, který přehrává dotazy z našich protokolů vyhledávání při konfigurovatelných příchozích frekvencích. Porovnává výsledky napříč běhy a aplikuje statistické testy k posouzení, zda jsou pozorované rozdíly ve výkonu konzistentní. Samostatně jsme vyhodnotili Photon a starý systém na stejných datových sadách v našich interních offline vyhodnocovacích systémech, abychom zkontrolovali regrese kvality.
Po těchto kontrolách jsme odeslali živé dotazy do obou strojů současně pomocí stínového provozu. To nám umožnilo porovnat jejich výkon a výsledky na produkčních pracovních zátěžích před změnou stroje, který obsluhoval uživatele. Poté jsme povolili Photon pro malé podskupiny uživatelů a postupně rozšiřovali zavádění, přičemž jsme po celou dobu sledovali kvalitu a výkon. První verze prošla akceptačním testováním bez regresí kvality a v zavádění jsme pokračovali, dokud Photon neobsluhoval veškerý produkční provoz.
Naměřená vylepšení v produkci
V našem produkčním pracovním postupu klesla doba odezvy p99 z přibližně 800 ms ve starém systému na přibližně 65 ms v Photonu. Toto měření pokrývá všechny fáze uvnitř Photonu, ale nezahrnuje následné fáze ve vyhledávacím zásobníku vyšší úrovně. Rovněž již nepozorujeme špičky latence dříve spojené s přepínáním verzí indexu.
Photon používá přibližně o 20 % méně ekvivalentních obslužných strojů než uzly obsahu starého systému a přitom ukládá přibližně 2,5krát více dat na dokument. Po migraci jsme tato dodatečná data využili ke zlepšení kvality řazení nad rámec starého systému.
Photon také snižuje množství dat, která musí zůstat v paměti. Připnutí stejné datové sady Photon v RAM pomocí mlock by vyžadovalo odhadem 4,6krát větší rezidentní paměť, než Photon aktuálně používá.
Hodnocení rychlé předvolby Search API
Photon jsme také použili jako základ pro novou rychlou předvolbu pro naše Search API, navrženou pro pracovní postupy citlivé na latenci. Podle našich měření je latence jednoho vyhledávacího volání pro rychlou předvolbu 160 ms při p50 a 230 ms při p95, což je nižší než nebo srovnatelné s vlastními hlášenými čísly jiných poskytovatelů Search API, přestože hlášené percentily chvostu se liší.
Vyhodnotili jsme rychlou předvolbu na šesti srovnávacích testech agentů: WideSearch, BrowseComp, DSQA, FRAMES, SEAL-0 a SEAL-Hard. Napříč těmito hodnoceními měla nejnižší odhadované celkové náklady na model plus vyhledávání a nejvyšší souhrnné skóre úkolů mezi testovanými konfiguracemi.
Předvolba fast kombinuje model Photon s hodnocením optimalizovaným pro pracovní postupy agentů, čímž poskytuje srovnatelný výkon jako výchozí předvolba za podstatně nižší náklady. Přináší však ústupek v širší kvalitě vyhledávání. Předvolba fast využívá pro hodnocení méně výpočetního výkonu než výchozí předvolba. V interních srovnávacích testech zaměřených na dlouhé dotazy, širší pokrytí a rozmanitost výsledků získává o 0,24 bodu nižší hodnocení v relevantnosti a přibližně o 3 procentní body nižší skóre v dostupnosti odpovědí. Věříme, že pro pracovní postupy agentů jde o rozumný kompromis, protože vlastní uvažování a znalosti modelu mohou pomoci tento rozdíl vyrovnat.
Doporučujeme rychlou předvolbu pro každodenní úkoly agentů. Pro náročnější nebo nejednoznačné otázky, které jsou méně citlivé na čas, nabízí výchozí předvolba robustnější kvalitu, což odůvodňuje její vyšší latenci a náklady.
Závěr
Plynule přesouváme náš vyhledávací zásobník na interní infrastrukturu založenou na jazyku Rust. Náhrada open-source enginu Photonem, naším vlastním vyhledávacím a řadícím enginem, je posledním krokem. Photon nám dává plnou kontrolu nad datovými reprezentacemi, způsobem organizace čtení a nasazováním nových indexů, což nám umožňuje optimalizovat každou vrstvu pro naše pracovní zátěže.
Photon umožnil rychlejší vyhledávání napříč produkty Perplexity a poskytuje základ pro naši novou rychlou předvolbu v Search API. Po migraci klesla latence načítání a řazení p99 z přibližně 800 ms na 65 ms. Naše kompaktní datové formáty nám také umožňují uložit 2,5krát více dat na dokument, což poskytuje načítání a řazení přístup k bohatším informacím o dokumentech.
Rychlá předvolba poskytuje latenci jednoho vyhledávacího volání 160 ms při p50 a 230 ms při p95. Napříč šesti hodnocenými agentními srovnávacími testy snížila odhadované náklady na model plus vyhledávání na úkol o 68 % ve výchozí předvolbě při zachování srovnatelné celkové kvality úkolů. Tyto výsledky demonstrují praktické výhody infrastruktury navržené kolem našich vyhledávacích pracovních zátěží, jak pro produkty Perplexity, tak pro vývojáře stavějící s naším API.
Pokračujeme ve zlepšování výkonu a efektivitě úložiště Photonu. Naším cílem je podporovat větší index a bohatší data dokumentů na stejném hardwaru a zároveň udržet rychlé vyhledávání s růstem našich pracovních zátěží.
Citace
@online{perplexity2026Photon,
author = {{Perplexity} and Maksim Morozov and Bogdan Savko and Ruslan Kovalev and Aleksei Bykov and Artur Zayats and Denis Bykov and Nikita Kireev and Jarrod Bryan and Claire Beamer and Jeremy Yang},
title = {{Photon: Building a retrieval and ranking engine from scratch}},
date = {2026-09-24},
url = {https://www.perplexity.ai/hub/blog/photon}
}