
Si ya has leído nuestra guía de implementación de DeepSearch/DeepResearch, profundicemos en algunos detalles que pueden mejorar enormemente la calidad. En esta publicación, nos centraremos en dos desafíos clave: aprovechar los embeddings para la selección de fragmentos de páginas web extensas y usar rerankers para priorizar URLs para el rastreo.
Algunos recordarán nuestra conclusión anterior que indicaba que "los embeddings solo eran útiles para la deduplicación de consultas como tareas STS (similitud textual semántica), mientras que los rerankers ni siquiera formaban parte de nuestra implementación original de DeepSearch". Bueno, resulta que ambos siguen siendo bastante valiosos, solo que no de la manera convencional que uno podría esperar. Siempre hemos seguido el camino más eficiente posible. No agregamos componentes solo para justificar su existencia o nuestro valor como proveedor de embeddings y rerankers. Nos basamos en lo que la búsqueda realmente necesita en su fundamento.
Así que después de semanas de experimentos e iteraciones, hemos descubierto usos poco comunes pero efectivos para ambos en sistemas DeepSearch/DeepResearch. Al aplicarlos, hemos mejorado significativamente la calidad de Jina DeepSearch (siéntete libre de probarlo). Nos gustaría compartir estos conocimientos con otros profesionales que trabajan en este campo.
tagSeleccionar Fragmentos de Contenido Largo
El problema es este: después de usar Jina Reader para leer el contenido de la página web, necesitamos agregarlo como un elemento de conocimiento al contexto del agente para el razonamiento. Si bien volcar todo el contenido en la ventana de contexto del LLM es la forma más simple, no es óptima cuando se consideran los costos de tokens y la velocidad de generación. En la práctica, necesitamos identificar qué partes del contenido son más relevantes para la pregunta y agregar selectivamente solo esas partes como conocimiento al contexto del agente.
El filtrado basado en LLM tiene los mismos problemas de costo y latencia, así que busquemos soluciones de modelos más pequeños: necesitamos modelos más pequeños y económicos, pero aún multilingües – un factor crucial ya que no podemos garantizar que tanto la consulta como los documentos siempre estén en inglés.
Tenemos una pregunta de un lado (ya sea la consulta original o una pregunta de brecha) y un gran contenido markdown del otro lado, donde la mayoría del contenido es irrelevante. Necesitamos seleccionar los fragmentos más relevantes para la consulta. Esto se asemeja al problema de chunking con el que la comunidad RAG ha lidiado desde 2023 - recuperar solo fragmentos relevantes usando modelos de recuperación para colocarlos en la ventana de contexto para la summarización. Sin embargo, hay dos diferencias clave en nuestro caso:
- Fragmentos limitados de un número limitado de documentos. Si cada fragmento contiene aproximadamente 500 tokens, entonces un documento web largo típico tiene alrededor de 200,000 tokens (p50) a 1,000,000 tokens (p99), y usamos Jina Reader para obtener 4-5 URLs en cada paso, esto produciría aproximadamente cientos de fragmentos - lo que significa cientos de vectores de embedding y cientos de similitudes de coseno. Esto es fácilmente manejable con JavaScript en memoria sin una base de datos vectorial.
- Necesitamos fragmentos consecutivos para formar snippets de conocimiento efectivos. No podemos aceptar snippets que combinen oraciones dispersas como
[1-2, 6-7, 9, 14, 17, ...].
Un snippet de conocimiento más útil seguiría patrones como[3-15, 17-24, ...]
- manteniendo siempre texto consecutivo. Esto facilita que el LLM copie y cite desde la fuente de conocimiento y reduce la alucinación.
El resto son todas las advertencias de las que se quejan los profesionales: cada fragmento no puede ser demasiado largo ya que los modelos de embedding no pueden manejar bien contextos largos; el chunking introduce pérdida de contexto y hace que los embeddings de fragmentos sean i.i.d; y ¿cómo encontrar las mejores señales de límite que mantengan tanto la legibilidad como la semántica? Si sabes de lo que estamos hablando, probablemente te han perseguido estos problemas en tus implementaciones de RAG.
Pero para resumir - el late-chunking con jina-embeddings-v3 resuelve bellamente los tres problemas. El late chunking mantiene la información de contexto para cada fragmento, es insensible a las señales de límite, y jina-embeddings-v3 en sí es SOTA en tareas de recuperación multilingüe asimétrica. Los lectores interesados pueden seguir nuestras publicaciones de blog o papers para más detalles, pero aquí está la implementación general.
Conv1D
. El proceso comienza dividiendo un documento largo en fragmentos de longitud fija, que luego se embeben con jina-embeddings-v3 con la opción de late-chunking activada. Después de calcular los puntajes de similitud entre cada fragmento y la consulta, una ventana deslizante se mueve a través de los puntajes de similitud para encontrar la ventana con el valor promedio más alto.


function cherryPick(question, longContext, options) {
if (longContext.length < options.snippetLength * options.numSnippets)
return longContext;
const chunks = splitIntoChunks(longContext, options.chunkSize);
const chunkEmbeddings = getEmbeddings(chunks, "retrieval.passage");
const questionEmbedding = getEmbeddings([question], "retrieval.query")[0];
const similarities = chunkEmbeddings.map(embed =>
cosineSimilarity(questionEmbedding, embed));
const chunksPerSnippet = Math.ceil(options.snippetLength / options.chunkSize);
const snippets = [];
const similaritiesCopy = [...similarities];
for (let i = 0; i < options.numSnippets; i++) {
let bestStartIndex = 0;
let bestScore = -Infinity;
for (let j = 0; j <= similarities.length - chunksPerSnippet; j++) {
const windowScores = similaritiesCopy.slice(j, j + chunksPerSnippet);
const windowScore = average(windowScores);
if (windowScore > bestScore) {
bestScore = windowScore;
bestStartIndex = j;
}
}
const startIndex = bestStartIndex * options.chunkSize;
const endIndex = Math.min(startIndex + options.snippetLength, longContext.length);
snippets.push(longContext.substring(startIndex, endIndex));
for (let k = bestStartIndex; k < bestStartIndex + chunksPerSnippet; k++)
similaritiesCopy[k] = -Infinity;
}
return snippets.join("\n\n");
}
Usando chunking tardío y mean pooling tipo Conv1D para seleccionar el mejor fragmento con respecto a la pregunta.
Asegúrate de llamar a la API de Jina Embeddings con los parámetros de recuperación task
, late_chunking
y truncate
configurados como se muestra a continuación:
await axios.post(
'https://api.jina.ai/v1/embeddings',
{
model: "jina-embeddings-v3",
task: "retrieval.passage",
late_chunking: true,
input: chunks,
truncate: true
},
{ headers });
Para incrustar la pregunta, asegúrate de cambiar task
a retrieval.query
y desactivar late_chunking
La implementación completa se puede encontrar en Github:
tagClasificar URLs para la siguiente lectura
El problema es este: durante una sesión de DeepSearch, probablemente recopilarás muchas URLs de las páginas de resultados del motor de búsqueda (SERP) y descubrirás aún más cada vez que leas páginas web individuales (esos enlaces en la página). El recuento total de URLs únicas puede alcanzar fácilmente los cientos. Nuevamente, simplemente volcar todas las URLs directamente en el contexto del LLM es ineficiente - desperdicia valioso espacio de ventana de contexto y, más problemáticamente, descubrimos que los LLMs esencialmente eligen URLs al azar. Es crucial guiar al LLM hacia URLs que tengan la mayor probabilidad de contener la respuesta que necesitas.
curl https://r.jina.ai/https://example.com \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-Retain-Images: none" \
-H "X-Md-Link-Style: discarded" \
-H "X-Timeout: 20" \
-H "X-With-Links-Summary: all"
Mejor opción para usar Jina Reader para rastrear una página en DeepSearch. Esto recopilará todos los enlaces de la página en un campo separado links
y los eliminará del campo content
.
Piensa en este problema como un PageRank en contexto donde necesitamos ponderar cientos de URLs durante una sesión. Clasificamos las URLs basándonos en múltiples factores que combinan tiempo de última actualización, frecuencia de dominio, estructura de ruta y, lo más importante, relevancia semántica para la consulta para crear una puntuación compuesta. Recuerda que solo podemos usar la información disponible antes de visitar realmente la URL:
Señales de Frecuencia: Las URLs que aparecen múltiples veces en diferentes fuentes reciben peso adicional. Las URLs de dominios que aparecen frecuentemente en los resultados de búsqueda reciben un impulso, ya que los dominios populares suelen contener contenido autoritativo.
Estructura de Ruta: Analizamos las rutas de URL para identificar clusters de contenido. Las URLs dentro de jerarquías de ruta comunes reciben puntuaciones más altas, con un factor de decaimiento aplicado a rutas más profundas.
Relevancia Semántica: Usamos jina-reranker-v2-base-multilingual para evaluar la relevancia semántica entre la pregunta y la información textual de cada URL, que es un problema clásico de reclasificación. La información textual de cada URL proviene de:
- Título y fragmentos de resultados de la API SERP (
https://s.jina.ai/
con'X-Respond-With': 'no-content'
) - Texto de anclaje de URLs en la página (
https://r.jina.ai
con'X-With-Links-Summary': 'all'
)
Tiempo de Última Actualización: Algunas consultas de DeepSearch son sensibles al tiempo, por lo que las URLs actualizadas recientemente son más valiosas que las antiguas. Sin ser un motor de búsqueda importante como Google, determinar de manera confiable el tiempo de última actualización es un desafío. Hemos implementado un enfoque de múltiples capas que combina la siguiente señal y proporciona una marca de tiempo con puntuación de confianza que prioriza el contenido más reciente cuando es necesario.
- Filtros de API SERP (como el parámetro
tbs
de s.jina.ai para filtrar por antigüedad) - Análisis de encabezados HTTP (Last-Modified, ETag)
- Extracción de metadatos (meta tags, marcas de tiempo Schema.org)
- Reconocimiento de patrones de contenido (fechas visibles en HTML)
- Indicadores específicos de CMS para plataformas como WordPress, Drupal y Ghost
Contenido Restringido: Algunos contenidos en plataformas de redes sociales están restringidos o simplemente detrás de muros de pago, y sin iniciar sesión o violar sus ToS, no hay una manera legítima de obtener este contenido. Debemos mantener activamente una lista de URLs y nombres de host problemáticos para reducir sus clasificaciones, evitando perder tiempo en contenido inaccesible.
Diversidad de Dominio: En algunos casos, las URLs con mayor peso provienen todas de los mismos nombres de host, lo que puede atrapar a DeepSearch en un óptimo local y reducir la calidad final de los resultados. Observa los ejemplos anteriores donde todas las URLs principales son de StackOverflow. Para mejorar la diversidad, podemos implementar un enfoque de exploración-explotación seleccionando las k URLs mejor clasificadas de cada nombre de host.
La implementación completa de la clasificación de URLs se puede encontrar en nuestro Github.
<action-visit>
- Crawl and read full content from URLs, you can get the fulltext, last updated datetime etc of any URL.
- Must check URLs mentioned in <question> if any
- Choose and visit relevant URLs below for more knowledge. higher weight suggests more relevant:
<url-list>
+ weight: 0.20 "https://huggingface.co/docs/datasets/en/loading": "Load - Hugging FaceThis saves time because instead of waiting for the Dataset builder download to time out, Datasets will look directly in the cache. Set the environment ...Some datasets may have more than one version based on Git tags, branches, or commits. Use the revision parameter to specify the dataset version you want to load ..."
+ weight: 0.20 "https://huggingface.co/docs/datasets/en/index": "Datasets - Hugging Face🤗 Datasets is a library for easily accessing and sharing datasets for Audio, Computer Vision, and Natural Language Processing (NLP) tasks. Load a dataset in a ..."
+ weight: 0.17 "https://github.com/huggingface/datasets/issues/7175": "[FSTimeoutError] load_dataset · Issue #7175 · huggingface/datasetsWhen using load_dataset to load HuggingFaceM4/VQAv2, I am getting FSTimeoutError. Error TimeoutError: The above exception was the direct cause of the following ..."
+ weight: 0.15 "https://github.com/huggingface/datasets/issues/6465": "`load_dataset` uses out-of-date cache instead of re-downloading a ...When a dataset is updated on the hub, using load_dataset will load the locally cached dataset instead of re-downloading the updated dataset."
+ weight: 0.12 "https://stackoverflow.com/questions/76923802/hugging-face-http-request-on-data-from-parquet-format-when-the-only-way-to-get-i": "Hugging face HTTP request on data from parquet format when the ...I've had to get the data from their data viewer using the parquet option. But when I try to run it, there is some sort of HTTP error. I've tried downloading ..."
</url-list>
</action-visit>
Recuerda poner los pesos de las URL en el contexto del agente e instruir a los LLM para que respeten los pesos.
tagConclusión
Desde el lanzamiento de nuestro sistema DeepSearch el 2 de febrero de 2025, hemos descubierto dos detalles de implementación que mejoraron sustancialmente la calidad. Sorprendentemente, ambos utilizan embeddings y rerankers multilingües de manera "in-context" - operando a una escala mucho menor que los índices precomputados que estos modelos típicamente requieren. Esto explica nuestra omisión inicial.
Esto apunta a una fascinante polarización en el futuro de la tecnología de búsqueda. Consideremos un marco análogo a la teoría del proceso dual de Kahneman:
- Pensamiento rápido (grep, BM25, SQL): Coincidencia de patrones rápida y gobernada por reglas con demandas computacionales mínimas.
- Pensamiento lento (LLM): Razonamiento integral con comprensión contextual profunda, que requiere una computación significativa.
- Pensamiento medio (embeddings, rerankers): ¿Atrapado en el limbo? Demasiado "avanzado"/semántico para la simple coincidencia de patrones pero sin verdaderas capacidades de razonamiento.
Podríamos estar presenciando la popularidad de una arquitectura bifurcada donde SQL/BM25 ligero y eficiente maneja la recuperación inicial de contenido, alimentando directamente a LLM poderosos para el procesamiento profundo. Estos LLM incorporan cada vez más las funciones semánticas que anteriormente requerían modelos especializados de nivel medio. El papel restante para los modelos de pensamiento medio se desplaza hacia tareas in-context especializadas: filtrado, deduplicación y operaciones de alcance limitado donde el razonamiento completo sería ineficiente.
Sin embargo, la selección de fragmentos críticos y la clasificación de URL siguen siendo componentes fundamentales con impacto directo en la calidad del sistema DeepSearch/DeepResearch. Esperamos que nuestras ideas inspiren mejoras en sus propias implementaciones.
La expansión de consultas continúa siendo otro determinante crucial de la calidad. Estamos evaluando activamente múltiples enfoques, desde reescrituras básicas basadas en prompts hasta modelos de lenguaje pequeños y métodos basados en razonamiento. Busque nuestros próximos hallazgos sobre este frente pronto. Estén atentos.