Repenser la recherche comme génération de code

Évolution de la recherche, des services monolithiques vers des primitives programmables pour l'ère des harnais d'agents.

AuteursPerplexity Research

La recherche est une primitive fondamentale pour les systèmes d'IA. Les modèles de pointe deviennent plus performants chaque mois, mais ils ont encore besoin d'accéder à des connaissances récentes, précises et bien sélectionnées provenant du monde extérieur. La recherche est le principal moyen par lequel les systèmes d'IA exploitent ces connaissances, et donc un composant fondamental de tout produit devant tirer des conclusions, prendre des mesures et effectuer un travail dans le monde réel.

Nous pensons que les pipelines de recherche traditionnels sont de plus en plus obsolètes à l'ère des agents. La recherche traditionnelle répond aux requêtes, mais les agents d'aujourd'hui accomplissent des tâches qui peuvent prendre d'innombrables formes. Ces tâches exigent que les agents définissent des stratégies de récupération spécifiques à la tâche directement au sein de leurs harnais. Au sein de Perplexity Computer, nous avons vu des tâches uniques invoquer des centaines, voire des milliers d'opérations de récupération en quelques minutes : un flux de travail impossible pour les humains, mais absolument naturel pour les agents.

Dans ce monde, la recherche elle-même doit devenir agentique, avec ses blocs de construction accessibles directement en tant que SDK au sein du harnais d'agent. Nous introduisons Search as Code (SaC) comme nouvelle architecture de recherche de référence de Perplexity.

Introduction

La pile de recherche de Perplexity traite des milliers de requêtes chaque seconde à travers nos applications et notre plateforme API. En septembre 2025, nous avons publié le premier aperçu architectural de nos systèmes de recherche. L'innovation constante au sein de ces systèmes a soutenu le lancement de nouvelles offres telles que Search API, Agent API et Computer, avec des boucles d'auto-amélioration optimisant la pile de recherche pour mieux servir les utilisateurs jour après jour.

Traditionnellement, les systèmes d'IA ont traité la recherche comme un monolithe : un modèle d'IA émet une requête, le moteur de recherche exécute son pipeline prédéfini et le modèle consomme les résultats en tant que contexte. Pour la plupart, cela fonctionnait bien pour répondre aux besoins des premiers utilisateurs d'IA. Compte tenu de la relative simplicité de leurs demandes, il n'y avait aucune raison de s'inquiéter de la conception exacte du pipeline de recherche, ni de savoir si l'architecture du pipeline était optimale pour la tâche en cours. Les valeurs par défaut étaient présumées suffisantes, tout comme les interfaces par défaut (appel de fonction et MCP).

Les architectures de recherche traditionnelles exposent un système unique et fixe que les modèles appellent en série, tandis que Search as Code expose des primitives de recherche atomisées que les agents composent via du code généré.

Pourtant, aujourd'hui, cette approche devient plus obsolète à chaque mois qui passe. Les utilisateurs exigent bien plus qu'une simple analyse ponctuelle de la part de l'IA. Ils attendent des agents qu'ils accomplissent des tâches de bout en bout sur des heures, voire des jours. Ces tâches peuvent être complexes, ouvertes et hautement variables dans leurs besoins en informations, et les architectures monolithiques s'effondrent sous le poids de ces exigences.

Le goulot d'étranglement clé est finalement une question de contrôle. Les modèles de pointe sont déjà très performants pour raisonner sur un contexte fixe. Cependant, les systèmes d'IA les plus puissants nécessiteront la capacité de diriger comment ce contexte est récupéré, traité, agrégé et rendu au modèle.

Les systèmes de recherche traditionnels n'ont pas été conçus avec ce degré de contrôlabilité à l'esprit. Après tout, on ne peut pas attendre des utilisateurs humains qu'ils exercent un contrôle précis sur les rouages internes du pipeline de recherche, même si cela leur était offert. Les premiers modèles d'IA ne peuvent contrôler la recherche que par une trajectoire linéaire d'appels de fonctions et de MCP. Mais les modèles de pointe d'aujourd'hui, pilotés par des harnais d'agents capables de coder, peuvent exercer un contrôle précis sur toutes les primitives informatiques imaginables via le code informatique. Notre tâche devient de fournir les bonnes primitives.

Pour répondre à ce besoin, nous introduisons une nouvelle architecture de recherche dans nos produits : Search as Code (SaC). Cette nouvelle architecture permet aux modèles d'atteindre l'intérieur de la pile de recherche elle-même plutôt que de simplement consommer ses résultats finaux. L'idée centrale est simple : nous exposons les composants de la pile de recherche en tant que primitives au sein d'un SDK. Pour toute demande nécessitant une recherche, un modèle assemble ces primitives à la demande dans un pipeline de récupération adapté à cette demande spécifique.

L'assemblage de ce pipeline s'effectue par la génération et l'exécution de code au sein d'un bac à sable sécurisé. Contrairement à d'autres approches de recherche basées sur la génération de code, nous ne nous contentons pas d'insérer une API de recherche traditionnelle dans un shell ou un environnement d'exécution de langage. Au lieu de cela, nous avons soigneusement conçu un SDK de recherche agentique qui expose les blocs de construction individuels de la recherche au niveau le plus atomique possible.

Search as Code (SaC) fait progresser la frontière de performance de la recherche agentique à travers une suite diversifiée de benchmarks.

Armé de ces blocs de construction, SaC donne aux modèles un contrôle direct sur chaque étape de recherche individuelle : récupération, classement, filtrage, fan-out, rendu, et plus encore. Il donne également au modèle un accès efficace aux états intermédiaires tels que les listes de candidats et les signaux de classement. Ensemble, ces deux leviers de contrôle et de lisibilité permettent aux agents de concevoir des pipelines de recherche sur mesure couvrant des milliers d'opérations de récupération, d'optimiser ces pipelines en cours de vol et de ne consommer que les informations les plus utiles en tant que contexte du modèle.

Cet article décrit la motivation, la conception et la mise en œuvre de SaC, ainsi que les résultats empiriques sur les benchmarks existants et nouveaux. SaC établit une nouvelle frontière coût-performance pour la recherche agentique, et nous sommes ravis de la partager avec nos utilisateurs et la communauté plus large de l'IA.

La rigidité de la recherche traditionnelle

Les premiers moteurs de recherche au monde ont été construits pour les utilisateurs humains. Ces utilisateurs s'attendaient à une expérience prévisible, consistant généralement en un nombre fixe de documents classés par pertinence. Les utilisateurs ne voulaient pas micro-gérer le processus de recherche ; ils voulaient saisir une requête et trouver un résultat utile.

En conséquence, les moteurs de recherche destinés aux humains ont convergé vers une architecture monolithique commune, orientée vers la production de pages de résultats de moteur de recherche (SERP) conviviales contenant les n meilleurs résultats pour une requête donnée. Ce modèle a alimenté des décennies de progrès dans la recherche grand public. Il est rapide, familier et très efficace pour la navigation humaine.

Puis sont venus les LLM, et les systèmes d'IA sont également devenus des consommateurs de recherche. Perplexity a été pionnier dans ce changement avec le lancement de notre moteur de réponse en 2022. Depuis lors, nos efforts en ingénierie de recherche se sont concentrés sur la manière d'optimiser la valeur des résultats de recherche pour les modèles d'IA. Pour construire l'IA la plus précise et la plus fiable au monde, nous avons dû concevoir un système de recherche axé sur le fait de rendre chaque jeton aussi riche en informations que possible. Les innovations qui en ont résulté dans la récupération de sous-documents, l'efficacité contextuelle, la compréhension sémantique et d'autres domaines clés ont conduit à des améliorations spectaculaires de la capacité des systèmes d'IA à récupérer et à agir sur les informations.

Cependant, même les systèmes optimisés pour l'IA ont largement hérité du même contrat fondamental que les moteurs de recherche traditionnels : accepter une requête, exécuter un pipeline de recherche prédéfini, renvoyer un jeu de résultats entièrement traité. Pour les demandes simples, cette approche fonctionne bien. Mais pour des demandes plus complexes, cela devient un goulot d'étranglement de plus en plus limitant qui dégrade les performances, la latence et le coût.

Les systèmes de recherche traditionnels forcent les flux de travail agentiques à séquencer la recherche à travers des tours visibles par le modèle. Les modèles doivent appeler à plusieurs reprises les mêmes pipelines de recherche à travers différents paramètres de requête et introduire tous les résultats dans le contexte du modèle.

Modes de défaillance liés à la rigidité

Les pipelines de recherche traditionnels sont conçus autour d'un point de contrôle unique : les paramètres de requête. Le moteur de recherche possède le reste du pipeline conformément à sa logique prédéfinie, et le modèle doit s'y adapter. Pour le modèle, tout ce qui se trouve en aval des paramètres de requête est rigide — la forme de l'exécution réelle est hors du contrôle du modèle.

C'est une frontière raisonnable lorsque le consommateur est un humain parcourant des liens. C'est une mauvaise frontière lorsque le consommateur est un modèle d'IA essayant de résoudre des tâches intensives en connaissances en boucle fermée. Selon notre expérience, cette rigidité crée trois modes de défaillance récurrents :

Contexte grossier. Les modèles d'IA sont sensibles à la qualité et à la compacité du contexte. La qualité et la compacité dépendent fortement de la requête, et les pipelines de recherche monolithiques ne sont pas conçus pour fonctionner de manière optimale sur toutes les requêtes. Par exemple, si un modèle a besoin d'une seule information hautement chirurgicale mais n'a accès qu'à un point de terminaison de recherche de bout en bout qui privilégie le rappel, l'utilisation de ce point de terminaison introduira une grande quantité d'informations non pertinentes dans le contexte du modèle. D'un autre côté, si un modèle a besoin de nombreuses informations nécessitant chacune des stratégies de recherche différentes, il peut être contraint d'invoquer en série le même pipeline sous-optimal pour toutes, ce qui entraîne une augmentation des coûts et un contexte bruyant.

Échec à tirer parti des connaissances du domaine. Dans de nombreux cas, le modèle peut disposer de connaissances du domaine (issues de son entraînement, d'une compétence d'agent (Skill), de la mémoire ou de jetons antérieurs dans la trajectoire LLM) qui devraient guider la stratégie de recherche. Cependant, une interface de recherche rigide empêche le modèle de tirer parti de ces connaissances. Par exemple, le modèle peut se rendre compte pour une demande spécifique qu'il doit mélanger les signaux lexicaux et sémantiques d'une manière particulière, donner la priorité à certaines sources ou agréger les résultats par une clé spécifique. À moins que ces informations ne puissent être implémentées via des paramètres de requête, le modèle ne peut pas agir en conséquence.

Flux de contrôle inefficace et pollution du contexte. De nombreux flux de travail de recherche ne sont pas naturellement en série. Ils peuvent nécessiter un fan-out sur des variantes de requête, une récupération parallèle, une déduplication et d'autres opérations qui nécessitent un flux de contrôle non linéaire et asynchrone. Forcer ces étapes à travers des tours de modèle répétés ajoute de la latence et rend le flux de travail plus difficile à optimiser. Il pollue également le contexte du modèle avec un état intermédiaire bruyant qui peut ne pas être utile au raisonnement du modèle, entraînant de moins bonnes performances et des compactages plus fréquents.

On ne pouvait pas faire grand-chose face à ces défis durant les ères des appels de fonctions et des MCP de l'IA. Lorsqu'une opération de recherche nécessite son propre aller-retour vers l'inférence LLM, les développeurs préfèrent naturellement accomplir autant que possible lors de cet aller-retour. Cela signifie des pipelines de recherche de bout en bout qui renvoient des ensembles de résultats entièrement traités pour une consommation directe par un modèle.

En conséquence, la plupart des systèmes de recherche destinés à l'IA fonctionnent encore aujourd'hui sous ce contrat monolithique. Cependant, avec la croissance continue de l'intelligence des modèles et des exigences des utilisateurs, les lacunes de ce contrat deviennent plus prononcées. Les systèmes d'IA les plus avancés d'aujourd'hui, construits sur des harnais axés sur le code, peuvent composer des flux de travail spécifiques à la tâche qui exécutent des milliers d'actions chaque minute. Les systèmes de recherche n'ont pas encore pleinement réalisé ce potentiel.

Nous soupçonnions depuis longtemps que la bonne frontière pour la recherche native de l'IA se situait plus bas dans la pile. Les modèles ne devraient pas simplement appeler un moteur de recherche. Ils devraient être capables d'orchestrer les pièces individuelles de la pile de recherche selon les exigences de la tâche spécifique. Cela nécessite une architecture fondamentalement différente.

Concevoir une architecture de recherche programmable

La prochaine génération d'architectures de recherche dépendra d'un principe fondamental : la recherche doit être nativement programmable par des agents d'IA. Un agent ne doit pas être limité à un ensemble de pipelines de recherche préordonnés. Au contraire, un agent doit être capable de construire le pipeline requis par la tâche spécifique, en utilisant des blocs de construction composables conçus à différents niveaux d'abstraction.

Notre nouvelle architecture Search as Code (SaC) incarne ce principe. Dans SaC, aucune opération de récupération n'est distribuée via une interface d'appel de fonction ou MCP. Toutes les opérations sont plutôt orchestrées via du code Python généré par le modèle. Pour les besoins de recherche les plus simples, ce code peut consister en une poignée de requêtes vers un point de terminaison de recherche de haut niveau. Mais pour des tâches complexes, le code peut être aussi complexe que nécessaire, impliquant une exécution conditionnelle, l'asynchronisme, le parallélisme et des appels à une large gamme de primitives de bas niveau.

L'architecture Search as Code (SaC) repose sur une pile comprenant des modèles, des bacs à sable de calcul et un SDK de recherche agentique situé au-dessus de l'infrastructure de recherche de Perplexity.

SaC implique trois couches étroitement couplées :

Les modèles servent de plan de contrôle. Ils raisonnent sur la directive de l'utilisateur (ou de l'agent parent), décomposent la directive en tâches, décident quels pipelines de récupération et de traitement sont nécessaires pour chaque tâche et génèrent du code pour implémenter ces pipelines.

Les bacs à sable de calcul fournissent un calcul déterministe via un environnement d'exécution de code sécurisé. Ils offrent une toile pour que les modèles puissent implémenter le flux de contrôle, le traitement par lots, les nouvelles tentatives, le filtrage, la jointure, l'agrégation et d'autres opérations déterministes.

Notre SDK de recherche agentique expose la pile de recherche de Perplexity en tant que primitives composables. Il fournit des blocs de construction allant des opérations de récupération de bas niveau à l'analyse sémantique de haut niveau. Le SDK est intégré aux environnements d'exécution des bacs à sable, permettant aux modèles d'orchestrer jusqu'à des milliers d'opérations au sein d'un seul tour d'inférence. Ci-dessous, nous discutons de nos décisions de conception pour chaque couche.

Le SDK de recherche agentique

Le SDK de recherche agentique définit les blocs de construction pour la recherche programmable. Il est important de noter que notre SDK n'est pas une API de recherche préexistante emballée sous forme de bibliothèque. Au cours des derniers mois, nos équipes d'ingénierie ont réarchitecturé notre pile de recherche en primitives modulaires et composables. Nous avons construit le SDK de recherche agentique pour fournir un accès direct à ces primitives, créant une toile beaucoup plus puissante sur laquelle le modèle peut orchestrer des pipelines de récupération spécifiques à la tâche.

Les pipelines de recherche de haut niveau de bout en bout sont toujours disponibles dans le SDK, mais ils ne sont plus la seule option. Ils servent plutôt de forme de raccourci pour les modèles de recherche courants. Le modèle est libre de les utiliser ou de les contourner selon les exigences de la tâche.

Choix de l'environnement d'exécution : Nous avons envisagé Python, Rust, TypeScript et Bash comme environnement d'exécution du SDK. Nous soupçonnions que Python serait le choix le plus naturel étant donné son ubiquité et son écosystème de bibliothèques de traitement de données. Les tests internes ont confirmé cela, et la version du SDK déployée aujourd'hui est basée sur Python. Nous prévoyons de réévaluer périodiquement le choix de l'environnement d'exécution pour nous assurer qu'il continue d'offrir la meilleure expérience d'agent.

Amélioration continue via l'auto-recherche : Nous avons également optimisé la consommabilité du SDK pour les LLM de pointe grâce à une boucle d'auto-recherche itérative. Cette boucle, fonctionnant en continu sur des semaines, propose et valide les améliorations du SDK par rapport à des métriques incluant la latence, la qualité de la génération de code et les performances globales des tâches. La boucle d'auto-recherche a déjà apporté de nombreux changements à la fois à la structure et à l'esthétique du SDK, avec des gains significatifs dans toutes les dimensions. Nous prévoyons d'affiner davantage le SDK via l'auto-recherche à mesure que de nouveaux modèles de pointe et composants de recherche émergent.

Bacs à sable

Les bacs à sable fournissent des environnements sécurisés pour définir et exécuter les pipelines SaC. Ils exécutent le code généré par le modèle, fournissant un accès au SDK de recherche agentique pour faciliter l'interaction avec les primitives de recherche individuelles.

Nous avons investi de manière significative dans l'optimisation et le renforcement de nos bacs à sable, et leur conception système globale mérite un article à part entière. Ici, nous nous concentrons sur la considération de conception la plus pertinente pour SaC : la gestion des états intermédiaires.

SaC permet aux agents d'orchestrer des pipelines complexes au sein d'un seul tour d'inférence. Cependant, certains états intermédiaires doivent être transmis à travers les tours. Par exemple, un modèle peut vouloir récupérer des documents au tour 1, inspecter un sous-échantillon de ces documents au tour 2 pour décider quoi faire ensuite, puis développer un pipeline de recherche en aval pour enquêter davantage sur ces documents à partir du tour 3. Comme discuté ci-dessus, le passage des états intermédiaires à travers l'Espace des jetons n'est pas une stratégie efficace.

Nous avons envisagé deux approches pour gérer les états intermédiaires à travers les tours :

Système de fichiers persistant + serde explicite : Les bacs à sable peuvent exposer des systèmes de fichiers persistants que les modèles peuvent utiliser à travers les tours. Les modèles souhaitant persister l'état intermédiaire peuvent inclure une étape de sérialisation dans le code généré pour un tour, ainsi qu'une étape de désérialisation dans un tour ultérieur (soit pour la propre inspection des modèles, soit pour une utilisation en aval). L'approche du système de fichiers permet aux modèles de contrôler directement et explicitement comment l'état intermédiaire est transmis à travers les tours. Elle assure également la traçabilité en imposant une identification explicite et la persistance des informations utilisées par le modèle. Un inconvénient est la latence et la surcharge de contexte encourues par la génération du code serde, bien que des fonctions utilitaires bien conçues puissent atténuer cette surcharge.

REPL : Alternativement, un bac à sable peut persister l'environnement d'exécution lui-même à travers les tours dans un environnement de style REPL. Cela permet aux modèles de maintenir l'état intermédiaire en mémoire sans avoir à le sérialiser du tout, car les variables définies ou modifiées dans les tours précédents peuvent simplement être référencées par leur nom dans les tours ultérieurs. L'approche REPL est plus efficace en termes de jetons puisqu'elle évite le besoin de générer du code serde. Cependant, l'absence d'une étape de sérialisation explicite peut nuire aux performances en aval pour des raisons familières à quiconque a travaillé avec un bloc-notes Jupyter de 100 cellules : à mesure que l'espace de noms des variables devient encombré, il devient plus difficile de suivre ce qui est précisément persisté à travers les tours et pourquoi.

Nous avons testé les deux approches et avons constaté que, bien qu'elles fonctionnent de manière similaire en usage ordinaire, le serde basé sur le système de fichiers offre une meilleure fiabilité sur les trajectoires particulièrement longues. Nous adoptons donc le serde basé sur le système de fichiers comme notre solution. Nous conjecturons qu'exiger des modèles qu'ils transmettent l'état de manière déclarative plutôt qu'implicite les aide à gérer cet état plus efficacement. Il s'agit d'une découverte préliminaire et nous continuerons à itérer sur les deux approches.

Modèles

Les modèles alimentant les harnais d'agents servent de plan de contrôle de SaC : assemblant dynamiquement des pipelines de recherche à partir des blocs de construction du SDK de recherche agentique pour répondre aux besoins de chaque tâche, puis distribuant ces pipelines sous forme de code pour l'exécution en bac à sable.

Perplexity utilise une variété de modèles au sein de nos harnais d'agents, et nous avons trouvé que les derniers modèles de pointe sont assez efficaces dans l'ensemble pour la génération de code à usage général. Le défi principal est d'induire une génération de code efficace spécifiquement pour le SDK de recherche agentique. C'est-à-dire, comment pouvons-nous amener les modèles à tisser efficacement les blocs de construction de notre SDK dans des pipelines optimisés pour les tâches ?

Contrairement à la bibliothèque standard d'un langage, un SDK construit sur mesure a peu de chances d'être représenté dans les données de pré-entraînement. Même avec les améliorations de la consommabilité du SDK issues de l'auto-recherche, de nombreux modèles sont encore entraînés à interagir avec les systèmes de recherche via des appels de fonction et l'invocation directe de MCP. Ces modèles sont peu susceptibles de manier le SDK avec expertise en utilisant uniquement le code source et des documents générés automatiquement.

Pour résoudre ce défi, nous avons développé des Skills d'agent hautement réglées pour apprendre aux modèles à exploiter efficacement notre SDK. Les versions initiales ont été développées selon les principes de conception de Perplexity pour les Skills, puis optimisées via une boucle d'auto-recherche dédiée axée sur les mêmes métriques utilisées dans l'optimisation du SDK. Nous limitons également la taille des Skills pour éviter l'encombrement du contexte, et nos versions finales ont moins de 2000 jetons dans leurs fichiers SKILL.md racine.

Les Skills optimisées vont au-delà de la simple liste des blocs de construction disponibles du SDK (quelque chose qui peut être obtenu tout aussi facilement via la réflexion au moment de l'exécution). La majeure partie des jetons est consacrée à fournir des conseils concis et généralisables ainsi que des exemples de few-shot pour composer ces blocs dans des modèles complexes. Armés de ces Skills, nous observons les modèles exploiter efficacement le SDK pour orchestrer des pipelines de recherche qui montent en charge jusqu'à des milliers d'opérations discrètes.

Le code comme orchestrateur et remplisseur de lacunes

En combinant ces couches, SaC modifie la relation entre les modèles et la pile de recherche. Dans les systèmes de recherche à pipeline fixe, le modèle se situe au-dessus de la recherche et interagit avec elle via une interface d'appel en série étroite. Dans SaC, le modèle devient un participant actif dans l'orchestration du processus de recherche lui-même. Tous les éléments de la pile de recherche deviennent directement programmables par le modèle, avec le bac à sable et le SDK de recherche agentique fournissant les interfaces nécessaires vers ces éléments.

Mais qu'en est-il des capacités qui n'existent pas nativement ? Le code est un moyen puissant pour orchestrer des capacités préexistantes. Pourtant, le pouvoir du code va au-delà de l'orchestration. Il peut également servir de remplisseur de lacunes pour les capacités qui ne sont pas présentes dans la pile de recherche ou le SDK. Les agents bénéficient de la parcimonie, et il serait inefficace que le SDK couvre chaque opération potentielle avec une fonction dédiée. Au lieu de cela, le SDK fournit les primitives les plus fondamentales, et le modèle peut construire tout composant supplémentaire à la volée avec du code.

Considérez, par exemple, une regex complexe qui n'est pas implémentable efficacement via la syntaxe de requête du système de recherche lui-même. Sans capacités de codage, le modèle serait contraint de générer sa meilleure approximation, d'effectuer une requête et d'effectuer un filtrage dans l'espace des jetons sur un jeu de résultats très bruyant. Avec SaC, le modèle peut plutôt générer un programme qui effectue des appels SDK parallèles pour collecter un sur-ensemble du jeu de résultats de la regex souhaitée. Après déduplication via le SDK, le modèle peut ensuite écrire du code supplémentaire pour réduire déterministiquement les résultats à la regex exacte. De cette manière, les agents peuvent implémenter des capacités de recherche personnalisées sans alourdir le SDK avec des sous-routines trop spécifiques.

Étude de cas : Avis des fournisseurs de CVE

Nous présentons une étude de cas basée sur une tâche réelle au sein de notre suite de tests. Cette tâche demande à un agent d'identifier et de caractériser plus de 200 CVE de haute gravité de 2023–2025. Chaque enregistrement doit citer l'avis du fournisseur concerné, nommer le produit et la version de correction, et montrer que la version de correction est liée à la CVE spécifique.

La réponse de SaC a obtenu un score de 100 % en précision, avec une utilisation totale des jetons en baisse de 85,1 % par rapport à la ligne de base non-SaC (de 288,7 K jetons à 42,9 K jetons). Les systèmes non-Perplexity testés, discutés plus en détail dans la section 4, ont tous obtenu un score inférieur à 25 %.

Nous présentons trois blocs de code stylisés de la trajectoire pour illustrer comment la programmabilité de SaC permet au modèle d'implémenter des stratégies efficaces.

python
# 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"]) ]

Ce premier bloc est de l'orchestration pure. Le programme généré commence par encoder la règle de classe source directement dans le plan de requête : seuls les formats d'avis appartenant aux fournisseurs sont pertinents. Les sources non fournies, y compris NVD, MITRE, CVE Details, les articles de presse, les pages CERT et les bases de données tierces, sont structurellement hors de portée. Les contraintes d'expression exacte poussent également la recherche vers des pages dont le texte indexé contient déjà les détails précis de la CVE nécessaires en aval.

python
# 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, )

Le deuxième bloc utilise un LLM comme sous-routine de planification intermédiaire. Le code résume quelles paires fournisseur-année produisent suffisamment de pages candidates, demande des raffinements ciblés et valide chaque requête proposée avant l'exécution. Cela préserve les modèles utiles de la trajectoire, tels que les sondes d'expression exacte à portée de site et le remplissage adaptatif, sans coder en dur un robot d'exploration d'avis CVE sur mesure dans le SDK.

python
# 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")

Le bloc final est un vérificateur de résultats dont la logique est entièrement définie par l'agent via la génération de code. Alors que les sous-routines de recherche peuvent trouver des pages d'avis plausibles, la tâche exige une relation plus stricte : l'avis doit lier une CVE à un produit affecté et une version de correction dans le texte rédigé par le fournisseur. Le schéma rend cette relation explicite, de sorte que le code en aval puisse dédupliquer par CVE, rejeter les URL agrégateurs, supprimer les preuves de version faibles et continuer à remplir jusqu'à ce que le seuil de nombre d'enregistrements soit atteint.

Résultats de l'évaluation

Nous évaluons l'efficacité de SaC sur une suite de benchmarks complète, en nous concentrant à la fois sur les performances absolues et sur la frontière coût-performance.

Benchmarks et systèmes

Notre suite de benchmarks comprend cinq benchmarks au total, conçus pour stresser les flux de travail d'IA dépendants de la recherche dans un éventail de domaines, de formats de tâches et de niveaux de complexité. Nous utilisons quatre benchmarks préexistants : DeepSearchQA (DSQA), BrowseComp, Humanity's Last Exam (HLE) et WideSearch. Pour les trois premiers benchmarks (DSQA, BrowseComp et HLE), nous rapportons la précision comme métrique principale. Pour WideSearch, nous rapportons le score F1 par ligne.

Nous utilisons également le benchmark WANDR nouvellement développé, qui se concentre sur les tâches difficiles de « recherche large » nécessitant une orchestration minutieuse de la recherche, du calcul et du raisonnement du modèle. WANDR est inspiré par les tâches professionnelles intensives en connaissances que Perplexity Computer gère pour les utilisateurs. Il itère sur WideSearch et des benchmarks similaires, mais met l'accent sur des tâches et des structures de tâches plus complexes. Nous publierons le benchmark WANDR dans les semaines à venir.

Nous évaluons cinq systèmes basés sur des agents. Nous considérons des exécutions individuelles plutôt que des scores « best-of-N » afin d'isoler les performances de l'architecture sous-jacente plutôt que les avantages de la parallélisation. La configuration de chaque système est décrite ci-dessous :

Perplexity (SaC) : Nous évaluons l'architecture SaC au sein de l'API Agent de production de Perplexity. GPT 5.5 (raisonnement élevé) sert de modèle sous-jacent.

OpenAI : Nous évaluons l'API OpenAI Responses avec à la fois la recherche (web_search) et l'environnement d'exécution Python (code_interpreter) activés. GPT 5.5 (raisonnement élevé) sert de modèle sous-jacent.

Anthropic : Nous évaluons Anthropic Managed Agents avec l'ensemble d'outils d'agent 20260401 et les commandes auto-autorisées. Opus 4.7 (raisonnement élevé) sert de modèle sous-jacent.

Exa : Nous évaluons Exa Agent via l'API de production, avec l'effort réglé sur élevé.

Parallel : Nous évaluons Parallel Tasks via l'API de production, avec le processeur réglé sur ultra4x.

Performance comparative

Le tableau 1 rapporte les scores de benchmark pour chaque système. SaC surpasse tous les autres systèmes sur quatre des cinq benchmarks. Sur le benchmark restant (HLE), SaC est essentiellement à égalité pour la première place avec OpenAI Responses. La figure 2 de l'Introduction visualise ces scores sous forme graphique.

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

Tableau 1 | Scores de benchmark pour chaque système évalué. Meilleur score par ligne en gras.

Bien que SaC atteigne des performances de pointe à travers la suite de benchmarks, son avantage est particulièrement prononcé sur WANDR. La figure 5 décompose les scores WANDR, montrant que l'implémentation SaC de Perplexity surpasse le système le plus proche d'un facteur de 2,5×. Malgré cette marge importante, WANDR reste insaturé et s'avère difficile même pour SaC. Nous pensons que les flux de travail de recherche complexes et très horizontaux requis par les tâches de WANDR nécessiteront des avancées supplémentaires dans la recherche et l'ingénierie de recherche pour atteindre des performances constamment élevées.

Scores WANDR à travers les systèmes évalués. Perplexity Agent API mène devant le système suivant d'un facteur de 2,5×.

Enfin, nous mesurons les gains relatifs de SaC par rapport à un pipeline de recherche plus traditionnel qui exploite la même infrastructure de recherche Perplexity. La figure 6 rapporte la différence de scores entre les architectures SaC et non-SaC pour chaque benchmark. Dans l'ensemble, SaC fournit un boost substantiel de performance, avec le plus grand gain absolu sur DSQA (+19,77 pp, 29 %) et le plus grand gain relatif sur WANDR (+12,00 pp, 45 %).

Ligne de base Perplexity vs score SaC sur chaque benchmark, avec les deltas absolus et relatifs annotés.

Frontière coût-performance

En pratique, les utilisateurs se soucient de la performance par rapport au coût. Nous évaluons donc la frontière coût-performance sur DSQA et WideSearch. Nous mesurons le coût et la performance de SaC à travers des niveaux de raisonnement de modèle faible, moyen et élevé, aux côtés des autres systèmes. Les figures 7 et 8 tracent le score de benchmark par rapport au prix par tâche, avec l'axe x inversé de sorte que les points plus à droite soient moins chers et les points plus élevés représentent une meilleure performance.

Score DSQA par rapport au prix par tâche. La ligne pointillée relie les configurations de raisonnement du modèle SaC.

Sur DSQA, les trois paramètres SaC se situent sur la frontière supérieure droite. Le paramètre de raisonnement faible est moins cher que tous les autres systèmes tout en étant plus performant que deux d'entre eux. Le paramètre de raisonnement moyen surpasse tous les systèmes non-SaC à moins de 1 $ par tâche. Le paramètre de raisonnement élevé atteint une performance de pointe avec un coût compétitif.

Score WideSearch par rapport au prix par tâche. La ligne pointillée relie les configurations de raisonnement du modèle SaC.

WideSearch montre une frontière de forme similaire. Comme avec DSQA, le paramètre de raisonnement faible atteint une performance compétitive à un coût inférieur à tous les systèmes non-SaC, tandis que les raisonnements moyen et élevé surpassent les systèmes non-SaC en performance de tâche.

Vers une nouvelle architecture informatique

Search as Code reflète un changement plus large dans la conception de logiciels. Pendant des décennies, les systèmes logiciels ont été organisés autour d'instructions déterministes exécutées par des environnements d'exécution traditionnels sur des CPU. Les modèles de pointe introduisent une nouvelle forme de calcul : le raisonnement dans l'espace des jetons qui peut à la fois suivre et générer des instructions. Les systèmes informatiques les plus capables combineront ces deux formes de calcul plutôt que de choisir entre elles.

La recherche est un terrain d'essai naturel pour cette architecture hybride. Les modèles sont bien adaptés pour décider quelles preuves sont nécessaires et comment l'incertitude doit être résolue. Les environnements d'exécution déterministes sont bien adaptés au traitement par lots, au parallélisme, au filtrage, au classement et à l'agrégation. L'infrastructure de recherche sert de couche d'E/S universelle, fournissant à l'environnement d'exécution des poignées utiles sur les informations du monde qui peuvent soutenir des milliers d'opérations par minute. Lorsque ces pièces sont intégrées, le système qui en résulte est beaucoup plus puissant et efficace pour accomplir des tâches du monde réel.

Chaque partie de cette architecture est nécessaire pour débloquer ces capacités. Sans modèles intelligents, le système ne peut pas raisonner sur la stratégie de recherche. Sans bacs à sable, les modèles sont contraints à des E/S série et à un traitement inefficace dans l'espace des jetons. Et sans une pile de recherche atomisée, le modèle n'a rien à orchestrer. SaC fonctionne parce que le raisonnement, le calcul déterministe et les E/S sont conçus conjointement pour accentuer les forces de chaque couche.

Cette co-conception peut être poussée encore plus loin par le développement de boucles d'amélioration continue. Par exemple, le SDK de recherche agentique et les Skills d'agent SaC pourraient être optimisés conjointement dans une boucle d'auto-recherche commune. Plus ambitieusement, les modèles peuvent être entraînés à tirer parti des primitives de recherche de bas niveau exposées en tant que SDK. On pourrait même essayer de co-évoluer la conception du SDK pendant le processus d'entraînement du modèle lui-même. Nous sommes impatients de poursuivre des stratégies de co-conception plus agressives dans nos travaux futurs.

Nous avons construit SaC pour permettre aux modèles de contrôler le processus de recherche, plutôt que de simplement consommer ses résultats. Nos résultats prouvent le pouvoir de ce contrôle : SaC fait progresser à la fois la performance absolue et la frontière coût-performance sur les benchmarks d'agents intensifs en connaissances. Nous sommes ravis d'apporter les capacités de SaC à nos utilisateurs, dès aujourd'hui avec Perplexity Computer et l'API Agent. Nous continuerons à optimiser l'architecture SaC à travers toutes les couches de la pile pour garantir que nos utilisateurs (et les agents qui les servent) disposent des capacités de recherche les plus puissantes et efficaces possibles.

Citation

BibTeX
@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}
}