Photon: Construcción de un motor de recuperación y clasificación desde cero
Cómo los formatos de datos especializados, las lecturas por lotes y la separación de la construcción de índices del servicio de consultas mejoran la búsqueda con IA integrada.
Introducción
La pila de búsqueda con IA integrada de Perplexity recupera y clasifica las páginas candidatas, y muestra el contenido más relevante para que el modelo fundamente sus respuestas. Originalmente, esta etapa se ejecutaba en un motor de código abierto que adaptamos a nuestro sistema. Sin embargo, a medida que nuestro índice y nuestras cargas de trabajo crecieron, nos topamos con límites estrictos en cuanto a costos, latencia de cola y tiempo de recuperación de nodos que no podíamos resolver dentro de la plataforma de terceros existente.
Para superar estos desafíos, construimos Photon, un nuevo motor interno de recuperación y clasificación. Esto continúa con nuestro enfoque de construir una infraestructura de búsqueda con IA integrada desde cero, tal como lo hicimos con CobbleDB, nuestro almacén de clave-valor distribuido para servir registros de páginas, y nuestra API de búsqueda.
Photon utiliza formatos de datos compactos para leer y decodificar únicamente lo que cada consulta necesita, con almacenamiento en caché compatible con lotes y lecturas asincrónicas para reducir el acceso a los discos y solapar las esperas. La creación de índices se ejecuta por separado del servicio de consultas, lo que actualiza un grupo de servicio a la vez y calienta sus cachés antes de enrutar las consultas en vivo hacia él.
Photon ahora impulsa nuestra canalización de búsqueda y es también la base de un nuevo preajuste rápido para nuestra API de búsqueda, con una latencia de 160 ms a p50 y de 230 ms a p95 para una sola llamada de búsqueda. En seis pruebas de rendimiento públicas, el preajuste rápido redujo el costo estimado del modelo más la búsqueda por tarea en un 68 %, al tiempo que mantuvo una calidad de tarea agregada comparable a la del preajuste predeterminado. Lo estamos lanzando hoy para que los desarrolladores puedan acceder a estas mejoras.
Lo que requiere el motor de recuperación y clasificación
Para cada consulta, el motor de recuperación y clasificación busca en un índice grande y distribuido para seleccionar y ordenar un conjunto pequeño de páginas relevantes dentro del presupuesto de latencia de la pila de búsqueda. El sistema también debe mantener el índice actualizado a medida que llegan nuevas páginas y las páginas existentes cambian, sin permitir que ese trabajo ralentice las consultas en vivo.
Evaluamos varias opciones de código abierto y elegimos un motor de recuperación y clasificación maduro y de propósito general que satisfacía nuestras necesidades iniciales. Luego lo adaptamos a nuestra arquitectura y cargas de trabajo, manteniendo una latencia y un rendimiento estables a escala de producción.
Donde nuestro sistema anterior se quedaba corto
Mantuvimos nuestra propia bifurcación para adaptar el motor a nuestro índice en crecimiento, pero abordar las limitaciones clave requería cada vez más el rediseño de grandes partes de la plataforma. Varios condicionantes resultaron difíciles de resolver sin cambios arquitectónicos fundamentales:
1. Costos de escalado. Tanto el número de documentos que almacenamos como la cantidad de datos por documento que necesitamos recuperar y clasificar han ido aumentando con el tiempo. Este escalado ejerció presión sobre nuestro hardware, especialmente sobre la cantidad de RAM disponible. Para controlar los costos, necesitábamos representaciones de datos más compactas y un control más preciso sobre lo que permanece en la memoria y lo que se lee del disco bajo demanda.
2. Mala latencia de cola. En el tráfico de producción, el tiempo de respuesta de p99 del sistema anterior era de unos 800 ms. El conjunto de datos superaba la RAM disponible, por lo que no pudimos usar mlock para mantenerlo completamente en la memoria. El acceso a los datos no residentes podía desencadenar fallos de página importantes, lo que paralizaba la ejecución de consultas mientras el sistema operativo leía desde el disco.
El motor también fusionaba periódicamente su índice de disco principal con índices más pequeños vaciados de la memoria. Durante este proceso de fusión de índices de disco, el número de fallos de página aumentaba aún más, y la latencia de p99 alcanzaba aproximadamente 1.2 s durante 10-15 minutos. A medida que las cachés se calentaban, la frecuencia de los fallos de página y la latencia disminuían, lo que sugería que las lecturas en frío eran un factor importante que contribuía a los picos. La precarga de datos y el calentamiento del índice redujeron los picos, pero no abordaron adecuadamente la alta latencia de cola.
3. Recuperación y sincronización prolongadas. En el sistema anterior, desplegar un clúster adicional y sincronizar sus datos podía llevar más de una semana. La sincronización también era necesaria al agregar grupos de nodos para contener copias de datos adicionales o al recuperarse de fallos de nodos. Durante la recuperación, una mayor proporción de las respuestas de búsqueda eran parciales, lo que reducía las puntuaciones de calidad de la respuesta final.
En última instancia, concluimos que sería más sencillo y económico construir desde cero en lugar de seguir adaptando y manteniendo nuestra bifurcación. Aprovechando nuestra experiencia en la operación del sistema anterior, un pequeño equipo de ingeniería trabajó junto a un enjambre de agentes de codificación persistentes para construir Photon.
Dónde encaja Photon en la pila de búsqueda
Photon está diseñado para dos flujos de trabajo principales: dar servicio a las solicitudes de búsqueda e ingerir actualizaciones de la canalización de procesamiento.
Una solicitud del servicio de búsqueda de nivel superior llega al agente de tránsito de Photon a través de un equilibrador de carga. El agente selecciona un grupo de fragmentos y reenvía la solicitud a cada fragmento, supervisando los tiempos de espera y emitiendo solicitudes adicionales cuando es necesario.
Cada fragmento realiza la recuperación, la clasificación inicial y la clasificación de segunda etapa, y luego devuelve sus mejores candidatos al agente de tránsito. El agente combina estos candidatos en una única lista clasificada y recupera los campos clave de los documentos para las páginas seleccionadas de los fragmentos relevantes. A continuación, devuelve estos resultados al servicio de búsqueda de nivel superior, el cual recopila el contenido de la página necesario para que el agente genere una respuesta fundamentada.
Las actualizaciones de índices siguen una ruta independiente. Pillar, nuestra capa de publicación, prepara los datos y los almacena en tablas de YTsaurus. Los indexadores convierten estas tablas en estructuras de índice que los fragmentos pueden cargar para la búsqueda. El controlador coordina la preparación de datos, la construcción de índices y el despliegue de nuevas versiones de índices en los grupos de servicio. Antes de gestionar el tráfico en vivo, cada fragmento reproduce consultas de búsqueda reales para calentar sus cachés.
Diseño de Photon para nuestros flujos de trabajo
Tres opciones de diseño hacen que Photon se adapte bien a nuestra carga de trabajo: representaciones de datos compactas, almacenamiento en caché y lecturas de disco asincrónicas, y la separación de las actualizaciones de índices del servicio de consultas.
Representaciones de datos para la recuperación y la clasificación
La recuperación y la clasificación acceden a los datos de los documentos de forma diferente. La recuperación léxica comienza con los términos de consulta y encuentra los documentos que los contienen. A continuación, la clasificación necesita información detallada sobre cada documento seleccionado, incluidas las frecuencias y las posiciones de los términos.
Photon utiliza dos representaciones complementarias para admitir estos patrones de acceso. Un índice invertido almacena una lista de documentos coincidentes para cada término, llamada lista de publicación, para respaldar la selección de candidatos. Los registros independientes por documento proporcionan los datos necesarios para la clasificación. Tambiénoptimizamos la forma en que se almacenan y recorren las listas de publicación para mejorar la selección dentro de un presupuesto de trabajo fijo.
Las representaciones de datos especializadas nos permiten almacenar datos clave de manera más eficiente. Un índice invertido más compacto reduce la cantidad de datos leídos o movidos por consulta, lo que mejora la latencia al eliminar operaciones de E/S innecesarias. Estos formatos también permiten comprobar si un documento aparece en una lista sin decodificarlo por completo, una propiedad en la que se basa nuestra lógica de recorrido para controlar cuánto trabajo realiza una consulta bajo un presupuesto fijo. La optimización de los registros por documento nos permite almacenar más datos por documento para la clasificación, al tiempo que reduce los costos de escalado asociados con nuestro sistema anterior.
El índice invertido
Para cada término, el índice invertido almacena una lista de publicación. Estas listas pueden variar desde un puñado de documentos hasta millones, por lo que ningún formato único funciona bien para todas las listas. Photon elige el formato en función del número de documentos que contienen el término.
Las listas cortas utilizan una representación en línea que cabe en una sola página y no requiere una tabla de bloques. Las listas más largas que no caben en el formato en línea se dividen en bloques que cubren rangos fijos de identificadores de documentos. La representación de cada bloque depende de su densidad: los bloques dispersos almacenan matrices ordenadas de desplazamientos de ID de documento, mientras que los bloques densos utilizan mapas de bits. Los bloques dispersos utilizan la búsqueda acelerada ("galloping search"), que avanza en pasos cada vez más grandes para omitir regiones de la lista. Los bloques densos permiten realizar comprobaciones de pertenencia buscando el bit correspondiente a un documento.
La frecuencia de consulta de un término determina dónde se coloca su lista de publicación en el disco. Las listas consultadas con frecuencia se almacenan cerca unas de otras, lo que concentra los datos activos en una región de almacenamiento más pequeña. Dentro de cada lista, los documentos se ordenan mediante una señal de utilidad independiente para que los candidatos más prometedores se encuentren antes.
Recorrido de listas dentro de un presupuesto de trabajo
La selección de candidatos requiere recorrer las listas de publicación para encontrar documentos potencialmente relevantes. El escaneo exhaustivo de cada lista es costoso, por lo que Photon utiliza un algoritmo de tipo WAND que recorre solo algunas listas sin dejar de tener en cuenta las coincidencias en las demás.
El algoritmo divide las listas en dos grupos. Las listas de control avanzan en el recorrido y proponen documentos candidatos. Las listas de sondeo contribuyen a las puntuaciones de esos candidatos, pero no generan candidatos por sí mismas.
La puntuación de candidatos combina la cobertura ponderada de los términos de consulta y una contribución de frecuencia, con la normalización de la longitud del documento y un límite en las ganancias de los términos repetidos. Photon comprueba primero la presencia de términos en las matrices y mapas de bits para acotar la puntuación máxima posible de cada candidato. Lee las frecuencias exactas de los términos únicamente para los candidatos que aún podrían cumplir con el umbral de selección.
A continuación, la selección procede en tres pasos:
- Recorrido: Comience con un requisito estricto de cobertura de consultas. Si se encuentran muy pocos candidatos, repita con un requisito relajado.
- Retención: Los candidatos se mantienen en un conjunto acotado utilizando una puntuación que combina la cobertura y la frecuencia. A medida que los mejores candidatos recopilados mejoran, el umbral de admisión aumenta.
- Selección final: Los resultados finales de la pasada se eligen del conjunto retenido en función de las puntuaciones de frecuencia. Cada campo ejecuta su propia pasada y los resultados se combinan utilizando ponderaciones de campo.
El algoritmo produce un conjunto de candidatos de documentos relevantes que se utilizarán en una clasificación posterior.
Docblob
Una vez seleccionadas las páginas candidatas, la fase de clasificación necesita las frecuencias de los términos, la cobertura de los campos y las ubicaciones y la proximidad de las apariciones. Obtener estos datos a través del índice invertido requeriría accesos dispersos en muchos pares de término-documento. En Photon, almacenamos un registro compacto por documento llamado docblob que contiene las frecuencias de los términos, los campos en los que apareció un término y el lugar del documento en el que ocurrió. Como resultado, clasificar a un candidato solo requiere una búsqueda por documento.
El formato está diseñado para evitar decodificar más de lo necesario. Dentro de un docblob, los campos se agrupan por analizador de texto. Cada grupo almacena una lista de términos ordenada y codificada con Elias-Fano, de modo que se puede buscar un término de consulta directamente sin necesidad de expandir primero toda la lista. Una máscara de campo marca dónde apareció un término, un recuento de frecuencias realiza un seguimiento de la frecuencia con la que lo hizo y las posiciones reales se encuentran en bloques separados por par de término-campo codificados como diferencias de posición. Solo se decodifican los bloques de los términos que realmente coincidieron, mientras que todo lo demás en el registro permanece en el formato comprimido.
Esa decodificación selectiva permite que docblob admita el almacenamiento de más datos por documento sin un aumento proporcional en los costos. El crecimiento del registro no obliga a realizar más trabajo por búsqueda, ya que la clasificación solo interactúa con las piezas relevantes para los términos coincidentes en lugar de con todo el registro.
Lecturas por lotes y trabajo con la memoria
Incluso con las representaciones de datos más eficientes, la RAM se agota antes que otros recursos, por lo que a menudo necesitamos acceder a los datos almacenados en el disco. Photon utiliza lecturas de disco asincrónicas y una caché optimizada para lecturas por lotes para acelerar estos accesos, lo que reduce el tiempo de espera por solicitud. Esto contribuye a una mejora significativa en la latencia de cola en comparación con el sistema anterior.
Solapamiento de las esperas de disco
La clasificación requiere obtener las características de un centenar de documentos ubicados en diferentes partes de un archivo. Nuestro sistema anterior usaba mmap para mapear el archivo en el espacio de direcciones virtuales para poder leerlo a través de punteros de memoria. Sin embargo, acceder a una página que estaba ausente de la memoria aún requería una lectura de disco. El hilo tenía que esperar a que los datos estuvieran listos antes de continuar con el cálculo, y este tiempo de espera se acumulaba cuando el proceso necesitaba leer registros fríos secuencialmente.
Photon aprovecha que los desplazamientos de los registros se conocen de antemano, por lo que podemos enviar varias lecturas a la vez. De esto se encarga el reactor: los hilos de trabajadores dedicados envían lotes a través de io_uring y pasan los datos que leen al código de llamada. Cada hilo mantiene su propio io_uring; los lotes se envían al reactor con menos carga. Los descriptores de archivos y los grupos de búferes prerregistrados reducen la sobrecarga de las lecturas repetidas. Esta ejecución asincrónica permite que las esperas de varias operaciones se solapen, lo que reduce la latencia de cola.
Reducción del número de accesos a unidades de disco
Antes de enviar las lecturas, Photon comprueba todo el lote en la caché en la memoria. Solo las solicitudes de registros faltantes van al disco; a continuación, los datos leídos llenan la caché.
La caché está diseñada para lecturas por lotes. Específicamente:
- No hay una asignación separada en caso de acierto. En su lugar, los datos se copian en un búfer preparado de antemano por el código de llamada.
- El lector no adquiere ningún bloqueo. En su lugar, comprueba la versión de los datos y vuelve a intentarlo si entra en conflicto con una escritura.
- Utiliza el desalojo CLOCK, en lugar de requerir actualizaciones a una lista LRU compartida en cada lectura.
- Utiliza la prebúsqueda en la caché de la CPU, donde los metadatos y los datos de los próximos registros se solicitan de antemano.
Estas optimizaciones reducen la latencia sin perjudicar el rendimiento en las cargas de trabajo que nos importan.
Construcción y actualización del índice por separado del servicio
En Photon, la construcción de índices y el servicio de consultas se ejecutan en nodos diferentes. Los indexadores transforman los datos en estructuras de almacenamiento listas, mientras que los nodos de servicio los leen y realizan la recuperación y la clasificación. Como resultado, la construcción de índices no compite con las consultas de búsqueda por la CPU, y los recursos para estas tareas se pueden escalar de forma independiente.
Esta separación aborda los cuellos de botella de recuperación y sincronización presentes en la implementación anterior. Como la construcción de índices se ejecutaba dentro del mismo clúster que servía las consultas, recuperar un nodo fallido o agregar una nueva copia de los datos podía significar volver a indexar los documentos en el lugar, un proceso que tomaba días e incrementaba las respuestas parciales durante la recuperación. Photon elimina ese acoplamiento; los indexadores se ejecutan como trabajos distribuidos que producen estructuras de índice versionadas y listas para servir de forma independiente a los nodos de servicio, por lo que construir la siguiente versión del índice ya no compite con el tráfico en vivo.
Preparación y construcción distribuida
Una compilación comienza seleccionando documentos para el índice activo y produciendo la exportación de origen. El controlador registra la versión de origen, las tablas de entrada y el número esperado de fragmentos. Los trabajos de MapReduce en YTsaurus preparan texto, metadatos, fragmentos, características e incrustaciones precomputadas en tablas versionadas particionadas por fragmento. Cada indexador transmite la tabla preparada, construye las estructuras para su fragmento y fusiona los segmentos de texto. El índice listo, junto con una descripción de sus componentes, se carga en el almacenamiento de objetos.
El recuento de fragmentos y el número de indexadores que se ejecutan simultáneamente se configuran de forma independiente, por a que una compilación se puede ejecutar en varias oleadas o se pueden asignar más máquinas para que trabajen en paralelo. La preparación distribuida y los indexadores paralelos permiten que el índice web completo se construya en un número de horas de un solo dígito, lo que supone una mejora significativa con respecto al sistema anterior.
Despliegue de una nueva versión
Antes del despliegue, el controlador comprueba que el nuevo índice esté listo. Si los datos están incompletos, el despliegue se bloquea automáticamente.
Cuando hay varios grupos de servicio, el controlador los actualiza uno a la vez. Primero, elimina un grupo del enrutamiento del agente de tránsito y lo inicia en la nueva versión del índice. Antes de dar servicio a las solicitudes, los fragmentos pasan por una etapa de calentamiento independiente basada en los registros de búsqueda, lo que pre-puebla el estado inicial y hace que la transición al tráfico real sea más fluida. Después del calentamiento, el controlador comprueba el estado del índice y la preparación, y luego devuelve el grupo al enrutamiento. Los otros grupos continúan dando servicio a las solicitudes durante este tiempo. Debido a que el estado de la operación se persiste, un reinicio no obliga a que una compilación comience de nuevo; el controlador puede volver a adjuntarse a la preparación que ya está en marcha, reutilizar un resultado listo o continuar una rotación interrumpida a partir del paso guardado.
Los tiempos de actualización de datos difieren entre los sectores verticales de búsqueda. Para los sectores con altos requisitos de frescura, la entrega de documentos toma un número de minutos de un solo dígito. Se puede desplegar una copia adicional de los datos en aproximadamente media hora; los índices listos se cargan y se adjuntan a los fragmentos sin necesidad de volver a indexar los documentos de origen.
Migración y validación dentro de nuestra pila de búsqueda
Nuestro primer objetivo era reemplazar el sistema anterior por un motor que conservara la funcionalidad y la calidad de la búsqueda al tiempo que reducía los tiempos de respuesta. Validamos Photon por etapas, comenzando con pruebas de carga reproducibles y evaluaciones de calidad fuera de línea antes de pasar al tráfico en vivo. Photon ahora impulsa la recuperación y la clasificación en nuestra pila de búsqueda de producción, con tiempos de respuesta sustancialmente más bajos que los del sistema anterior.
Pruebas de carga y evaluaciones
Para las pruebas de rendimiento, creamos una herramienta de prueba de carga que reproduce consultas de nuestros registros de búsqueda a tasas de llegada configurables. Compara los resultados entre ejecuciones y aplica pruebas estadísticas para evaluar si las diferencias de rendimiento observadas son coherentes. Por separado, evaluamos Photon y el sistema anterior en los mismos conjuntos de datos en nuestros sistemas de evaluación fuera de línea internos para comprobar si había regresiones de calidad.
Después de estas comprobaciones, enviamos consultas en vivo a ambos motores simultáneamente utilizando tráfico sombra. Esto nos permitió comparar su rendimiento y sus resultados en las cargas de trabajo de producción antes de cambiar qué motor daba servicio a los usuarios. A continuación, habilitamos Photon para subconjuntos pequeños de usuarios y ampliamos gradualmente el despliegue, supervisando la calidad y el rendimiento en todo momento. La primera versión superó las pruebas de aceptación sin regresiones de calidad y continuamos con el despliegue hasta que Photon dio servicio a todo el tráfico de producción.
Mejoras medidas en producción
En nuestro flujo de trabajo de producción, el tiempo de respuesta de p99 cayó de aproximadamente 800 ms en el sistema anterior a aproximadamente 65 ms en Photon. Esta medición cubre todas las etapas dentro de Photon, pero excluye las etapas posteriores en la pila de búsqueda de nivel superior. Tampoco observamos ya los picos de latencia asociados anteriormente con el cambio de versiones de índices.
Photon utiliza aproximadamente un 20 % menos de máquinas de servicio equivalentes que los nodos de contenido del sistema anterior, a la vez que almacena aproximadamente 2.5 veces más datos por documento. Tras la migración, utilizamos estos datos adicionales para mejorar la calidad de clasificación por encima de la del sistema anterior.
Photon también reduce la cantidad de datos que deben permanecer en la memoria. Fijar el mismo conjunto de datos de Photon en la RAM con mlock requeriría una cantidad estimada de memoria residente 4.6 veces mayor que la que Photon utiliza actualmente.
Evaluación del preajuste rápido de la API de búsqueda
También utilizamos Photon como base para un nuevo preajuste rápido para nuestra API de búsqueda, diseñado para cargas de trabajo sensibles a la latencia. Según nuestras mediciones, la latencia de llamadas de búsqueda individuales para el preajuste rápido es de 160 ms a p50 y 230 ms a p95, inferior o comparable a las cifras autoinformadas por otros proveedores de la API de búsqueda, aunque los percentiles de cola informados difieren.
Evaluamos el preajuste rápido en seis pruebas de rendimiento agénticas: WideSearch, BrowseComp, DSQA, FRAMES, SEAL-0 y SEAL-Hard. En estas evaluaciones, presentó el costo total estimado del modelo más la búsqueda más bajo y la puntuación de tarea agregada más alta entre las configuraciones probadas.
El preajuste rápido combina Photon con una clasificación optimizada para flujos de trabajo agénticos, lo que ofrece un rendimiento comparable al del preajuste predeterminado a un costo sustancialmente menor. Sin embargo, conlleva un compromiso en términos de una calidad de búsqueda más amplia. El preajuste rápido utiliza menos recursos de cómputo para la clasificación que el preajuste predeterminado. En las pruebas de rendimiento internas que cubren consultas de cola larga, cobertura amplia y diversidad de resultados, obtiene 0.24 puntos menos en relevancia y aproximadamente 3 puntos porcentuales menos en disponibilidad de respuestas. Creemos que este es un compromiso razonable para los flujos de trabajo agénticos, ya que el propio razonamiento y conocimiento del modelo pueden ayudar a compensar la diferencia.
Recomendamos el preajuste rápido para las tareas agénticas cotidianas. Para preguntas más complejas o ambiguas que no sean tan sensibles al tiempo, el preajuste predeterminado ofrece una calidad más sólida, lo que justifica su mayor latencia y costo.
Conclusión
Estamos trasladando constantemente nuestra pila de búsqueda a una infraestructura interna basada en Rust. Reemplazar el motor de código abierto con Photon, nuestro motor de recuperación y clasificación personalizado, es el paso más reciente. Photon nos otorga un control total sobre las representaciones de datos, la forma en que se organizan las lecturas y cómo se despliegan los nuevos índices, lo que nos permite optimizar cada capa para nuestras cargas de trabajo.
Photon ha permitido una búsqueda más rápida en los productos de Perplexity y proporciona la base para nuestro nuevo preajuste rápido en la API de búsqueda. Tras la migración, la latencia de recuperación y clasificación de p99 cayó de aproximadamente 800 ms a 65 ms. Nuestros formatos de datos compactos también nos permiten almacenar 2.5 veces más datos por documento, lo que proporciona a la recuperación y la clasificación acceso a información de documentos más rica.
El preajuste rápido ofrece una latencia de 160 ms a p50 y de 230 ms a p95 para una sola llamada de búsqueda. En las seis pruebas de rendimiento agénticas evaluadas, redujo el costo estimado del modelo más la búsqueda por tarea en un 68 % en relación con el preajuste predeterminado, al tiempo que mantuvo una calidad de tarea agregada comparable. Estos resultados demuestran los beneficios prácticos de la infraestructura diseñada en torno a nuestras cargas de trabajo de búsqueda, tanto para los productos de Perplexity como para los desarrolladores que construyen con nuestra API.
Seguimos mejorando el rendimiento y la eficiencia de almacenamiento de Photon. Nuestro objetivo es admitir un índice más grande y datos de documentos más ricos en el mismo hardware, manteniendo al mismo tiempo una búsqueda rápida a medida que crecen nuestras cargas de trabajo.
Citación
@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}
}