Repenser la recherche en tant que génération de code
Faites évoluer la recherche, des services monolithiques aux primitives programmables pour l'ère des harnais d'agents.
La recherche est une primitive fondamentale pour les systèmes d'IA. Les modèles de pointe gagnent en capacité chaque mois, mais ils ont toujours besoin d'accéder à des connaissances fraîches, exactes et bien organisées provenant du monde extérieur. La recherche est le principal moyen pour les systèmes d'IA d'accéder à ces connaissances et constitue donc un composant fondamental de tout produit qui doit 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 dépassés à l'ère des agents. La recherche traditionnelle répond aux requêtes, mais les agents d'aujourd'hui accomplissent des tâches qui peuvent revêtir d'innombrables formes. Ces tâches exigent que les agents définissent des stratégies de récupération spécifiques aux tâches directement au sein de leurs harnais. Au sein de Perplexity Computer, nous avons vu de simples tâches 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 tout à fait naturel pour les agents.
Dans ce monde, la recherche elle-même doit devenir agentique, ses blocs de construction étant accessibles directement sous forme de trousses de développement logiciel (SDK) au sein du harnais de l'agent. Nous présentons Search as Code (SaC) comme la 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 dans l'ensemble de nos applications et de notre API Platform. 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 a bien fonctionné pour répondre aux besoins des premiers utilisateurs de l'IA. Compte tenu de la simplicité relative de leurs demandes, il n'y avait aucune raison de s'inquiéter de la conception exacte du pipeline de recherche ou de savoir si l'architecture du pipeline était optimale pour la tâche à accomplir. Les valeurs par défaut étaient présumées suffisantes, de même que les interfaces par défaut (appel de fonctions et MCP).
Pourtant, aujourd'hui, cette approche devient de plus en plus obsolète mois après mois. Les utilisateurs exigent beaucoup plus qu'une simple analyse ponctuelle de la part de l'IA. Ils s'attendent à ce que les agents 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 matière d'information, et les architectures monolithiques plient sous le poids de ces exigences.
Le principal col de bouteille est finalement celui du contrôle. Les modèles de pointe sont déjà très doués pour raisonner sur un contexte fixe. Cependant, les systèmes d'IA les plus puissants nécessiteront la capacité de piloter la façon dont ce contexte est récupéré, traité, agrégé et restitué 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 s'attendre à ce que les utilisateurs humains exercent un contrôle granulaire sur les aspects internes du pipeline de recherche, même si cela leur était proposé. Les premiers modèles d'IA ne peuvent contrôler la recherche que par le biais d'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 granulaire sur toutes les primitives informatiques imaginables au moyen de code informatique. Notre tâche consiste à fournir les bonnes primitives.
Pour répondre à ce besoin, nous introduisons une nouvelle architecture de recherche dans l'ensemble de nos produits : Search as Code (SaC). Cette nouvelle architecture permet aux modèles de plonger au cœur de la pile de recherche elle-même plutôt que de se contenter de 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 requête nécessitant une recherche, un modèle assemble ces primitives à la demande dans un pipeline de récupération adapté à cette requête 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 génération de code pour la recherche, nous ne nous contentons pas d'insérer une API de recherche traditionnelle dans un environnement d'exécution de shell ou de langage. Au lieu de cela, nous avons minutieusement conçu un SDK de recherche agentique qui expose les blocs de construction individuels de la recherche au niveau le plus atomique possible.
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, ramifications, rendu, etc. Il donne également au modèle un accès efficace à l'état intermédiaire tel 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 s'étendant sur des milliers d'opérations de récupération, d'optimiser ces pipelines en cours d'exécution et de consommer uniquement 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 des bancs d'essai 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é de l'IA au sens large.
La rigidité de la recherche traditionnelle
Les tout premiers moteurs de recherche au monde ont été conçus pour les utilisateurs humains. Ces utilisateurs en sont venus à attendre une expérience prévisible, consistant généralement en un nombre fixe de documents classés par pertinence. Les utilisateurs ne voulaient pas microgérer le processus de recherche ; ils voulaient saisir une requête et trouver un résultat utile.
Par conséquent, les moteurs de recherche axés sur l'humain ont convergé vers une architecture monolithique commune, orientée vers la production de pages de résultats de moteurs de recherche (SERP) adaptées aux humains et contenant les $n$ meilleurs résultats pour une requête d'entrée. Ce modèle a alimenté des décennies de progrès dans la recherche grand public. Il est rapide, familier et extrêmement efficace pour la navigation humaine.
Sont ensuite arrivés les grands modèles de langage (LLM), et les systèmes d'IA sont également devenus des consommateurs de recherche. Perplexity a été le pionnier de ce changement avec le lancement de notre moteur de réponse en 2022. Depuis lors, nos efforts d'ingénierie de la 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 bâtir l'IA la plus précise et la plus fiable au monde, nous avons dû concevoir un système de recherche axé sur l'optimisation de chaque jeton (token) en termes de densité d'information. Les innovations qui en ont résulté en matière de récupération de sous-documents, d'efficacité du contexte, de compréhension sémantique et d'autres domaines clés ont entraîné des améliorations spectaculaires de la capacité des systèmes d'IA à récupérer des informations et à agir sur celles-ci.
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 ensemble de résultats entièrement traité. Pour les requêtes simples, cette approche fonctionne bien. Mais pour les requêtes plus complexes, cela devient un goulot d'étranglement de plus en plus limitant qui dégrade les performances, la latence et les coûts.
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 la requête. Le moteur de recherche gère le reste du pipeline selon 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 la requête est rigide — la forme du calcul réel échappe au contrôle du modèle.
Il s'agit d'une limite raisonnable lorsque le consommateur est un humain qui parcourt des liens. C'est une piètre limite lorsque le consommateur est un modèle d'IA essayant de résoudre des tâches à forte intensité de connaissances en boucle fermée. D'après 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 pour toutes les requêtes. Par exemple, si un modèle a besoin d'un élément d'information unique et hautement chirurgical, mais qu'il 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 nombreux éléments d'information nécessitant chacun des stratégies de recherche différentes, il peut être contraint d'invoquer en série le même pipeline sous-optimal pour tous, ce qui entraîne une explosion des coûts et un contexte bruyant.
Incapacité à tirer parti des connaissances du domaine. Dans de nombreux cas, le modèle peut posséder des connaissances sur le domaine (provenant de son entraînement, d'une Skill d'agent, de la mémoire ou de jetons antérieurs dans la trajectoire du 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 requête spécifique qu'il devrait combiner des 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 mises en œuvre par le biais de paramètres de requête, le modèle ne peut pas réellement agir en conséquence.
Flux de contrôle inefficace et pollution du contexte. De nombreux flux de travail de recherche ne sont pas naturellement sériels. Ils peuvent nécessiter une diffusion en éventail sur des variantes de requêtes, une récupération en 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 par le biais de tours de modèles répétés ajoute de la latence et rend le flux de travail plus difficile à optimiser. Cela pollue également le contexte du modèle avec un état intermédiaire bruyant qui peut ne pas être utile au raisonnement du modèle, ce qui entraîne de moins bonnes performances et des compactages plus fréquents.
Il n'y avait pas grand-chose à faire face à ces défis pendant les ères des appels de fonctions et des MCP de l'IA. Lorsque chaque opération de recherche nécessite son propre aller-retour vers l'inférence du LLM, les développeurs préfèrent naturellement en faire le plus possible lors de cet aller-retour. Cela implique 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.
Par conséquent, la plupart des systèmes de recherche axés sur l'IA fonctionnent encore aujourd'hui selon 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 à des tâches qui exécutent des milliers d'actions chaque minute. Les systèmes de recherche doivent encore exploiter pleinement ce potentiel.
Nous soupçonnons depuis longtemps que la bonne limite pour la recherche native de l'IA se situe plus bas dans la pile. Les modèles ne devraient pas se contenter d'appeler un moteur de recherche. Ils devraient être capables d'orchestrer les différentes pièces de la pile de recherche selon les besoins de la tâche spécifique. Cela nécessite une architecture fondamentalement différente.
Conception d'une architecture de recherche programmable
La prochaine génération d'architectures de recherche s'articulera autour d'un principe fondamental : la recherche doit être nativement programmable par les agents d'IA. Un agent ne doit pas être limité à un ensemble de pipelines de recherche préétablis. 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, pas une seule opération de récupération n'est envoyée par le biais d'un appel de fonction ou d'une interface MCP. Toutes les opérations sont au contraire 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 quelques requêtes adressées à un point de terminaison de recherche de haut niveau. Mais pour les 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 à un large éventail de primitives de bas niveau.
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 mettre en œuvre ces pipelines.
Les bacs à sable de calcul fournissent un calcul déterministe par le biais d'un environnement d'exécution de code sécurisé. Ils offrent un canevas aux modèles pour mettre en œuvre le flux de contrôle, la mise en 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é dans les environnements d'exécution des bacs à sable, permettant aux modèles d'orchestrer jusqu'à des milliers d'opérations au cours 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 de la recherche programmable. Il est important de noter que notre SDK n'est pas une API de recherche préexistante empaquetée sous forme de bibliothèque. Au cours des derniers mois, notre équipe d'ingénierie a restructuré notre pile de recherche en primitives modulaires et composables. Nous avons conçu le SDK de recherche agentique pour offrir un accès direct à ces primitives, créant ainsi un canevas beaucoup plus puissant sur lequel le modèle peut orchestrer des pipelines de récupération spécifiques à des tâches.
Les pipelines de recherche de haut niveau et de bout en bout sont toujours disponibles au sein du SDK, mais ils ne constituent plus la seule option. Ils servent plutôt de raccourci pour les modèles de recherche courants. Le modèle est libre de les utiliser ou de les contourner selon les besoins 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 omniprésence et son écosystème de bibliothèques de traitement de données. Des tests internes ont confirmé qu'il en était ainsi, 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'autorecherche : Nous avons également optimisé la facilité d'utilisation du SDK pour les grands modèles de langage de pointe grâce à une boucle d'autorecherche itérative. Cette boucle, fonctionnant en continu pendant des semaines, propose et valide les améliorations du SDK par rapport à des mesures telles que la latence, la qualité de la génération de code et la performance globale des tâches. La boucle d'autorecherche 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 par le biais de l'autorecherche à mesure que de nouveaux modèles de pointe et composants de recherche émergeront.
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, donnant accès au SDK de recherche agentique pour faciliter l'interaction avec les primitives de recherche individuelles.
Nous avons réalisé des investissements importants dans l'optimisation et le renforcement de nos bacs à sable, et la conception globale de leur système mérite un article à elle seule. 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 d'un tour à l'autre. Par exemple, un modèle peut souhaiter récupérer certains documents au tour 1, inspecter un sous-échantillon de ces documents au tour 2 pour décider de la marche à suivre, puis développer un pipeline de recherche en aval pour enquêter plus avant sur ces documents à partir du tour 3. Comme indiqué ci-dessus, le passage des états intermédiaires par l'espace des jetons n'est pas une stratégie efficace.
Nous avons examiné deux approches pour gérer les états intermédiaires d'un tour à l'autre :
Système de fichiers persistant + sérialisation/désérialisation (serde) explicite : Les bacs à sable peuvent exposer des systèmes de fichiers persistants que les modèles peuvent utiliser d'un tour à l'autre. 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 (que ce soit pour la propre inspection des modèles ou pour une utilisation en aval). L'approche du système de fichiers permet aux modèles de contrôler directement et explicitement la manière dont l'état intermédiaire est transmis d'un tour à l'autre. Elle garantit également la traçabilité en imposant l'identification explicite et la persistances des informations utilisées par le modèle. Un inconvénient réside dans la surcharge de latence et de contexte générée par la production du code de sérialisation/désérialisation, 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 d'un tour à l'autre 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, puisque les variables définies ou modifiées lors des tours précédents peuvent simplement être référencées par leur nom dans les tours suivants. L'approche REPL est plus efficace en termes de jetons car elle évite d'avoir à générer du code de sérialisation/désérialisation. Cependant, l'absence d'é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 s'encombre, il devient plus difficile de garder une trace de ce qui est précisément persisté d'un tour à l'autre et pourquoi.
Nous avons testé les deux approches et avons constaté que, bien qu'elles affichent des performances similaires lors d'une utilisation ordinaire, la sérialisation/désérialisation basée sur le système de fichiers offre une meilleure fiabilité sur les trajectoires particulièrement longues. Nous adoptons donc la sérialisation/désérialisation basée sur le système de fichiers comme solution. Nous supposons qu'en obligeant les modèles à transmettre l'état de manière déclarative plutôt qu'implicite, on les aide à gérer cet état plus efficacement. Il s'agit d'une conclusion provisoire et nous continuerons d'itérer sur les deux approches.
Modèles
Les modèles alimentant les harnais d'agents servent de plan de contrôle de SaC : ils assemblent 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 envoient ces pipelines sous forme de code pour l'exécution dans le bac à sable.
Perplexity emploie une variété de modèles au sein de nos harnais d'agents, et nous avons constaté que les derniers modèles de pointe sont tout à fait efficaces sur toute la ligne pour la génération de code à usage général. Le principal défi consiste à induire une génération de code efficace spécifiquement pour le SDK de recherche agentique. En d'autres termes, comment pouvons-nous inciter les modèles à tisser efficacement les blocs de construction de notre SDK pour en faire des pipelines optimisés pour les tâches ?
Contrairement à la bibliothèque standard d'un langage, un SDK conçu 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 facilité d'utilisation du SDK apportées par l'autorecherche, de nombreux modèles sont toujours entraînés à interagir avec les systèmes de recherche par le biais d'appels de fonctions et d'appels directs de MCP. Il est peu probable que ces modèles maîtrisent habilement le SDK en se basant uniquement sur le code source et la documentation générée automatiquement.
Pour relever ce défi, nous avons développé des Skills d'agent hautement optimisées pour apprendre aux modèles à exploiter efficacement notre SDK. Les versions initiales ont été développées conformément aux principes de conception de Perplexity pour les Skills, puis optimisées par le biais d'une boucle d'autorecherche dédiée axée sur les mêmes mesures que celles utilisées dans l'optimisation du SDK. Nous limitons également la taille des Skills pour nous prémunir contre l'enflure du contexte, et nos versions finales comportent moins de 2 000 jetons dans leurs fichiers racine SKILL.md.
Les Skills optimisées vont au-delà de la simple liste des blocs de construction disponibles du SDK (ce qui peut tout aussi bien être obtenu par 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 en motifs complexes. Armés de ces Skills, nous observons que les modèles exploitent efficacement le SDK pour orchestrer des pipelines de recherche qui s'étendent à des milliers d'opérations distinctes.
Le code en tant qu'orchestrateur et combleur 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 trouve au-dessus de la recherche et interagit avec elle par le biais d'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, le bac à sable et le SDK de recherche agentique fournissant les interfaces nécessaires à ces éléments.
Mais qu'en est-il des fonctionnalités qui n'existent pas nativement ? Le code est un moyen puissant pour orchestrer des fonctionnalités préexistantes. Pourtant, la puissance du code va au-delà de l'orchestration. Il peut également servir de solution de rechange pour les fonctionnalité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 potentnelle 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érons, par exemple, une expression régulière (regex) complexe qui ne peut pas être mise en œuvre efficacement par le biais de la syntaxe de requête propre au système de recherche. Sans capacités de codage, le modèle serait contraint de générer sa meilleure approximation, de faire une requête et d'effectuer un filtrage dans l'espace des jetons sur un ensemble de résultats très bruyant. Avec SaC, le modèle peut à la place générer un programme qui effectue des appels SDK parallèles pour collecter un sur-ensemble de l'ensemble de résultats de l'expression régulière souhaitée. Après la déduplication via le SDK, le modèle peut ensuite écrire du code supplémentaire pour restreindre de manière déterministe les résultats à l'expression régulière exacte. De cette manière, les agents peuvent mettre en œuvre des fonctionnalités de recherche personnalisées sans alourdir le SDK avec des sous-programmes excessivement spécialisés.
Étude de cas : Avis des fournisseurs CVE
Nous présentons une étude de cas basée sur une tâche du monde réel 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 sévérité de 2023 à 2025. Chaque enregistrement doit citer l'avis du fournisseur concerné, nommer le produit et la version du correctif, et montrer que la version du correctif est liée à la CVE spécifique.
La réponse de SaC a obtenu un score de 100 % en matière de précision, l'utilisation totale des jetons ayant chuté de 85,1 % par rapport à la référence sans SaC (passant de 288,7 K jetons à 42,9 K jetons). Les systèmes testés n'appartenant pas à Perplexity, 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 de mettre en œuvre des stratégies efficaces.
# 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 la pure orchestration. Le programme généré commence par encoder la règle de classe de source directement dans le plan de requête : seuls les formats d'avis appartenant au fournisseur sont pertinents. Les sources n'appartenant pas au fournisseur, y compris NVD, MITRE, CVE Details, les articles de presse, les pages CERT et les bases de données tierces, sont structurellement hors champ. Les contraintes de phrase 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.
# 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 grand modèle de langage (LLM) comme sous-programme 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 schémas utiles de la trajectoire, tels que les sondes de phrases exactes ciblées sur le site et le remplissage adaptatif, sans coder en dur un robot d'indexation d'avis CVE sur mesure dans le 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")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. Bien que les sous-programmes de recherche puissent 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 correctif dans le texte rédigé par le fournisseur. Le schéma rend cette relation explicite, de sorte que le code en aval peut dédupliquer par CVE, rejeter les URL d'agrégateur, écarter les preuves de version faibles et continuer à remplir jusqu'à ce que le seuil minimal de nombre d'enregistrements soit atteint.
Résultats de l'évaluation
Nous évaluons l'efficacité de SaC sur une suite de bancs d'essai complète, en nous concentrant à la fois sur les performances absolues et sur la frontière coût-performance.
Bancs d'essai et systèmes
Notre suite de bancs d'essai comprend cinq bancs d'essai au total, conçus pour soumettre à rude épreuve les flux de travail d'IA dépendants de la recherche dans divers domaines, formats de tâches et niveaux de complexité. Nous utilisons quatre bancs d'essai préexistants : DeepSearchQA (DSQA), BrowseComp, Humanity's Last Exam (HLE) et WideSearch. Pour les trois premiers bancs d'essai (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 nouveau banc d'essai WANDR, qui se concentre sur les tâches difficiles de « recherche élargie » nécessitant une orchestration minutieuse de la recherche, du calcul et du raisonnement du modèle. WANDR s'inspire des tâches professionnelles à forte intensité de connaissances que Perplexity Computer gère pour les utilisateurs. Il reprend WideSearch et des bancs d'essai similaires, mais met l'accent sur des tâches et des structures de tâches plus complexes. Nous publierons le banc d'essai WANDR dans les semaines à venir.
Nous évaluons cinq systèmes basés sur des agents. Nous examinons les exécutions individuelles plutôt que les scores « best-of-N » afin d'isoler la performance 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'Agent API de production de Perplexity. GPT 5.5 (raisonnement élevé) sert de modèle sous-jacent.
OpenAI : Nous évaluons l'API Responses d'OpenAI 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'agents 20260401 et les commandes autorisées automatiquement. 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 indique les scores des bancs d'essai pour chaque système. SaC surpasse tous les autres systèmes sur quatre des cinq bancs d'essai. Sur le banc d'essai restant (HLE), SaC est pratiquement à égalité pour la première place avec OpenAI Responses. La figure 2 de l'Introduction visualise ces scores sous forme graphique.
Banc d'essai | 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 |
Table 1 | Scores des bancs d'essai pour chaque système évalué. Meilleur score par ligne en gras.
Bien que SaC atteigne des performances de pointe sur l'ensemble de la suite de bancs d'essai, son avantage est particulièrement prononcé sur WANDR. La figure 5 décompose les scores de WANDR, montrant que l'implémentation SaC de Perplexity surpasse le système suivant le plus performant par un facteur de 2,5×. Malgré cette marge importante, WANDR reste saturé et s'avère difficile même pour SaC. Nous pensons que les flux de travail de recherche complexes et hautement horizontaux requis par les tâches de WANDR nécessiteront des progrès supplémentaires en matière de recherche et d'ingénierie de la recherche pour atteindre des performances régulièrement élevées.
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 banc d'essai. Sur toute la ligne, SaC offre une augmentation substantielle des performances, avec le gain absolu le plus élevé sur DSQA (+19,77 pp, 29 %) et le gain relatif le plus élevé sur WANDR (+12,00 pp, 45 %).
Frontière coût-performance
Dans la pratique, les utilisateurs se soucient des performances par rapport au coût. Nous évaluons donc la frontière coût-performance sur DSQA et WideSearch. Nous mesurons le coût et les performances de SaC à des niveaux de raisonnement de modèle bas, moyen et élevé, parallèlement aux autres systèmes. Les figures 7 et 8 tracent le score du banc d'essai par rapport au prix par tâche, l'axe des x étant inversé de sorte que les points plus à droite sont moins chers et que les points plus hauts représentent de meilleures performances.
Sur DSQA, les trois paramètres SaC se situent sur la frontière supérieure droite. Le paramètre de raisonnement bas est moins cher que tous les autres systèmes tout en obtenant de meilleurs résultats que deux d'entre eux. Le paramètre de raisonnement moyen surpasse tous les systèmes non-SaC pour moins de 1 $ par tâche. Le paramètre de raisonnement élevé atteint des performances de pointe avec un coût compétitif.
WideSearch montre une frontière de forme similaire. Comme pour DSQA, le paramètre de raisonnement bas atteint des performances compétitives à un coût inférieur à celui de tous les systèmes non-SaC, tandis que les raisonnements moyen et élevé surpassent les systèmes non-SaC en termes de performance des tâches.
Vers une nouvelle architecture de l'informatique
Search as Code reflète un changement plus vaste dans la conception des 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 processeurs (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 performants 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 des preuves nécessaires et de la manière dont l'incertitude doit être résolue. Les environnements d'exécution déterministes conviennent bien à la mise en 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 éléments sont intégrés, 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 libérer 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 en 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 grâce au 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'autorecherche commune. Plus audacieux encore, les modèles peuvent être entraînés à tirer parti des primitives de recherche de bas niveau exposées sous forme de SDK. On pourrait même essayer de co-développer la conception du SDK pendant le processus d'entraînement du modèle lui-même. Nous nous réjouissons à l'idée de poursuivre des stratégies de co-conception plus agressives dans de futurs travaux.
Nous avons conçu SaC pour permettre aux modèles de contrôler le processus de recherche, plutôt que de se contenter de consommer ses résultats. Nos résultats prouvent la puissance de ce contrôle : SaC fait progresser à la fois les performances absolues et la frontière coût-performance sur les bancs d'essai d'agents à forte intensité de connaissances. Nous sommes ravis d'apporter les capacités de SaC à nos utilisateurs, à commencer dès aujourd'hui par Perplexity Computer et Agent API. Nous continuerons d'optimiser l'architecture SaC à toutes les couches de la pile pour veiller à ce que nos utilisateurs (et les agents qui les servent) disposent des capacités de recherche les plus puissantes et les plus efficaces possibles.
Citation
@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}
}