> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mka1.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Evaluando almacenes de texto

> Evalúa la precisión y latencia de recuperación para las APIs de MKA1 Text Store y Tables usando el conjunto de datos BEIR SciFact.

Esta guía evalúa las APIs de búsqueda de MKA1 frente a un conjunto de datos estándar de recuperación de información.
Demuestra RAG con múltiples tecnologías de almacenamiento (Text Store y Tables) y procesamiento de documentos a gran escala — indexando miles de documentos, ejecutando consultas y midiendo precisión y latencia en cada etapa del flujo.

El benchmark utiliza [SciFact](https://huggingface.co/datasets/BeIR/scifact) de la suite BEIR — 5,183 resúmenes científicos con 300 consultas de prueba y juicios de relevancia anotados por humanos.
Todas las incrustaciones se generan con `meetkai:functionary-es-mini` (4,096 dimensiones).

## Resumen de resultados

Una consulta de recuperación pasa por dos etapas: **inferencia de incrustación** (convertir el texto de la consulta en un vector) y **búsqueda en la base de datos** (encontrar los vectores más cercanos en el almacén). El tiempo total de respuesta es la suma de ambas.

| Métrica        | Text Store | Tables |
| -------------- | ---------- | ------ |
| **NDCG\@10**   | 72.4       | 71.8   |
| **Recall\@10** | 83.1       | 82.6   |
| **MRR\@10**    | 68.9       | 68.3   |

| Latencia                                                     | Text Store   | Tables       |
| ------------------------------------------------------------ | ------------ | ------------ |
| **Inferencia de incrustación** (por consulta)                | 12 ms        | 12 ms        |
| **Búsqueda en base de datos p50** (lado servidor)            | 8 ms         | 6 ms         |
| **Búsqueda en base de datos p95** (lado servidor)            | 14 ms        | 11 ms        |
| **Respuesta extremo a extremo p50** (red + embed + búsqueda) | 45 ms        | 38 ms        |
| **Respuesta extremo a extremo p95** (red + embed + búsqueda) | 82 ms        | 71 ms        |
| **Rendimiento de indexado** (5,183 docs)                     | 182 docs/seg | 166 docs/seg |

La latencia de búsqueda en base de datos es reportada por el propio servidor (`search_time_ms` en el cuerpo de la respuesta) y excluye la sobrecarga de red y la inferencia de incrustación. El tiempo de respuesta extremo a extremo incluye todo lo que mediría un cliente.

El resto de esta guía muestra cómo se producen estos números, paso a paso.

## Configuración

Instala las dependencias y carga las variables de entorno.

```ts theme={null}
import { SDK } from '@meetkai/mka1';

const API_KEY = process.env.MK_API_KEY!;
const BASE_URL = process.env.MKA1_BASE_URL || 'https://apigw.mka1.com';

const sdk = new SDK({
  serverURL: BASE_URL,
  bearerAuth: `Bearer ${API_KEY}`,
});
```

## Paso 1 — Cargar el conjunto de datos SciFact

Descarga el corpus, las consultas y los juicios de relevancia desde HuggingFace.

```ts theme={null}
async function fetchJsonl(url: string): Promise<any[]> {
  const res = await fetch(url);
  const text = await res.text();
  return text.trim().split('\n').map((line) => JSON.parse(line));
}

// Corpus: 5,183 resúmenes científicos
const corpus = await fetchJsonl(
  'https://huggingface.co/datasets/BeIR/scifact/resolve/main/corpus.jsonl'
);

// Consultas: 300 consultas de prueba (afirmaciones científicas)
const queries = await fetchJsonl(
  'https://huggingface.co/datasets/BeIR/scifact/resolve/main/queries.jsonl'
);

// Juicios de relevancia: query_id → doc_id → relevancia (binario)
const qrelsRaw = await fetch(
  'https://huggingface.co/datasets/BeIR/scifact/resolve/main/qrels/test.tsv'
);
const qrelsText = await qrelsRaw.text();
const qrels: Record<string, Record<string, number>> = {};
for (const line of qrelsText.trim().split('\n').slice(1)) {
  const [queryId, , docId, relevance] = line.split('\t');
  qrels[queryId] ??= {};
  qrels[queryId][docId] = parseInt(relevance);
}

console.log(`Corpus: ${corpus.length} documentos`);
console.log(`Consultas: ${queries.length}`);
console.log(`Juicios de relevancia: ${Object.keys(qrels).length} consultas con etiquetas`);
```

<Accordion title="Salida esperada">
  ```
  Corpus: 5183 documentos
  Consultas: 300
  Juicios de relevancia: 300 consultas con etiquetas
  ```
</Accordion>

## Paso 2 — Calcular incrustaciones

Incrusta todos los documentos y consultas usando `meetkai:functionary-es-mini`.
Procesa en lotes para manejar el volumen.

```ts theme={null}
async function embedBatch(texts: string[], model = 'meetkai:functionary-es-mini'): Promise<number[][]> {
  const res = await fetch(`${BASE_URL}/api/v1/llm/embeddings`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${API_KEY}`,
    },
    body: JSON.stringify({ input: texts, model }),
  });
  const json = await res.json();
  return json.data.map((d: any) => d.embedding);
}

const BATCH_SIZE = 64;

// Incrustar corpus
console.log('Incrustando corpus...');
const corpusTexts = corpus.map((d) => `${d.title} ${d.text}`);
const corpusEmbeddings: number[][] = [];
const embedStart = performance.now();

for (let i = 0; i < corpusTexts.length; i += BATCH_SIZE) {
  const batch = corpusTexts.slice(i, i + BATCH_SIZE);
  const embeddings = await embedBatch(batch);
  corpusEmbeddings.push(...embeddings);
  if ((i / BATCH_SIZE) % 10 === 0) {
    console.log(`  ${corpusEmbeddings.length} / ${corpusTexts.length} documentos incrustados`);
  }
}

const embedMs = performance.now() - embedStart;
console.log(`Incrustación del corpus: ${(embedMs / 1000).toFixed(1)}s (${(embedMs / corpus.length).toFixed(1)} ms/doc)`);

// Incrustar consultas y medir latencia de inferencia por consulta
console.log('Incrustando consultas...');
const queryTexts = queries.map((q) => q.text);
const queryEmbedStart = performance.now();
const queryEmbeddings = await embedBatch(queryTexts);
const queryEmbedMs = performance.now() - queryEmbedStart;
const perQueryEmbedMs = queryEmbedMs / queryTexts.length;

console.log(`Incrustación de consultas: ${queryEmbeddings.length} consultas en ${queryEmbedMs.toFixed(0)} ms`);
console.log(`Inferencia de incrustación: ${perQueryEmbedMs.toFixed(1)} ms/consulta`);
```

<Accordion title="Salida esperada">
  ```
  Incrustando corpus...
    64 / 5183 documentos incrustados
    704 / 5183 documentos incrustados
    ...
  Incrustación del corpus: 42.3s (8.2 ms/doc)
  Incrustando consultas...
  Incrustación de consultas: 300 consultas en 3600 ms
  Inferencia de incrustación: 12.0 ms/consulta
  ```
</Accordion>

## Paso 3 — Evaluar el Text Store

Indexa los 5,183 documentos, luego ejecuta las 300 consultas. Mide el rendimiento de indexado y la latencia de búsqueda.

### Indexar documentos

```ts theme={null}
const TEXT_STORE_NAME = `scifact_bench_${Date.now()}`;
const DIMENSION = 4096;
const HEADERS = {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${API_KEY}`,
};

// Crear text store
await fetch(`${BASE_URL}/api/v1/search/text-store/stores`, {
  method: 'POST',
  headers: HEADERS,
  body: JSON.stringify({ store_name: TEXT_STORE_NAME, dimension: DIMENSION }),
});

console.log(`Text store creado: ${TEXT_STORE_NAME}`);

// Indexar en lotes
const INDEX_BATCH = 100;
const indexStart = performance.now();
let indexed = 0;

for (let i = 0; i < corpus.length; i += INDEX_BATCH) {
  const batchDocs = corpus.slice(i, i + INDEX_BATCH);
  const batchVecs = corpusEmbeddings.slice(i, i + INDEX_BATCH);

  await fetch(`${BASE_URL}/api/v1/search/text-store/stores/${TEXT_STORE_NAME}/texts`, {
    method: 'POST',
    headers: HEADERS,
    body: JSON.stringify({
      texts: batchDocs.map((d) => `${d.title} ${d.text}`),
      vectors: batchVecs,
      group: 'scifact',
    }),
  });

  indexed += batchDocs.length;
  if (indexed % 500 === 0) console.log(`  Indexados ${indexed} / ${corpus.length}`);
}

const indexMs = performance.now() - indexStart;
console.log(`Indexado: ${corpus.length} docs en ${(indexMs / 1000).toFixed(1)}s`);
console.log(`Rendimiento: ${(corpus.length / (indexMs / 1000)).toFixed(0)} docs/seg`);
```

<Accordion title="Salida esperada">
  ```
  Text store creado: scifact_bench_1774982400000
    Indexados 500 / 5183
    Indexados 1000 / 5183
    ...
    Indexados 5000 / 5183
  Indexado: 5183 docs en 28.4s
  Rendimiento: 182 docs/seg
  ```
</Accordion>

### Ejecutar consultas y medir latencia

Cada solicitud de búsqueda devuelve un campo `search_time_ms` — la duración de la búsqueda en base de datos del lado del servidor, excluyendo el viaje de ida y vuelta de red y cualquier procesamiento adicional.
Medimos tanto este tiempo del servidor como el tiempo de respuesta extremo a extremo observado por el cliente.

```ts theme={null}
const K = 10;
const results: Record<string, Record<string, number>> = {};
const endToEndLatencies: number[] = [];
const dbLatencies: number[] = [];

console.log(`Ejecutando ${queries.length} consultas contra text store (top-${K})...`);

for (let i = 0; i < queries.length; i++) {
  const q = queries[i];
  const qVec = queryEmbeddings[i];

  const start = performance.now();
  const res = await fetch(
    `${BASE_URL}/api/v1/search/text-store/stores/${TEXT_STORE_NAME}/search`,
    {
      method: 'POST',
      headers: HEADERS,
      body: JSON.stringify({ query: q.text, vector: qVec, limit: K }),
    },
  );
  const searchResult = await res.json();
  const elapsed = performance.now() - start;

  endToEndLatencies.push(elapsed);
  dbLatencies.push(searchResult.search_time_ms ?? 0);

  // Mapear resultados a IDs de documentos del corpus por coincidencia de texto
  results[q._id] = {};
  const hits = searchResult.results ?? [];
  for (let rank = 0; rank < hits.length; rank++) {
    const hitText = hits[rank].text;
    const doc = corpus.find((d) => `${d.title} ${d.text}` === hitText);
    if (doc) {
      results[q._id][doc._id] = 1.0 / (rank + 1); // puntuación por ranking
    }
  }
}

function percentile(arr: number[], p: number) {
  const sorted = [...arr].sort((a, b) => a - b);
  return sorted[Math.floor(sorted.length * p)];
}

console.log(`\nText Store — tiempo de respuesta extremo a extremo (${queries.length} consultas):`);
console.log(`  p50: ${percentile(endToEndLatencies, 0.5).toFixed(0)} ms`);
console.log(`  p95: ${percentile(endToEndLatencies, 0.95).toFixed(0)} ms`);
console.log(`  p99: ${percentile(endToEndLatencies, 0.99).toFixed(0)} ms`);

console.log(`\nText Store — latencia de búsqueda en base de datos (search_time_ms del servidor):`);
console.log(`  p50: ${percentile(dbLatencies, 0.5).toFixed(0)} ms`);
console.log(`  p95: ${percentile(dbLatencies, 0.95).toFixed(0)} ms`);
console.log(`  p99: ${percentile(dbLatencies, 0.99).toFixed(0)} ms`);
```

<Accordion title="Salida esperada">
  ```
  Ejecutando 300 consultas contra text store (top-10)...

  Text Store — tiempo de respuesta extremo a extremo (300 consultas):
    p50: 45 ms
    p95: 82 ms
    p99: 110 ms

  Text Store — latencia de búsqueda en base de datos (search_time_ms del servidor):
    p50: 8 ms
    p95: 14 ms
    p99: 18 ms
  ```

  La diferencia entre la latencia extremo a extremo y la de base de datos es el viaje de ida y vuelta de red y la sobrecarga del gateway.
</Accordion>

## Paso 4 — Evaluar la API de Tables

Indexa el mismo corpus en un almacén de Tables con esquema explícito e índice vectorial, luego ejecuta las mismas consultas.

### Crear tabla e indexar documentos

```ts theme={null}
const TABLE_NAME = `scifact_table_${Date.now()}`;

await sdk.search.tables.createTable({
  name: TABLE_NAME,
  schema: {
    fields: [
      { name: 'doc_id', type: 'string', nullable: false },
      { name: 'content', type: 'string', nullable: false, index: 'FTS' },
      { name: 'embedding', type: 'vector', nullable: false, dimensions: DIMENSION },
    ],
  },
});

console.log(`Tabla creada: ${TABLE_NAME}`);

// Indexar en lotes
const tableIndexStart = performance.now();
indexed = 0;

for (let i = 0; i < corpus.length; i += INDEX_BATCH) {
  const batchDocs = corpus.slice(i, i + INDEX_BATCH);
  const batchVecs = corpusEmbeddings.slice(i, i + INDEX_BATCH);

  await sdk.search.tables.insertData({
    tableName: TABLE_NAME,
    insertDataRequest: {
      data: batchDocs.map((d, j) => ({
        doc_id: d._id,
        content: `${d.title} ${d.text}`,
        embedding: batchVecs[j],
      })),
      refresh: true,
    },
  });

  indexed += batchDocs.length;
  if (indexed % 500 === 0) console.log(`  Indexados ${indexed} / ${corpus.length}`);
}

const tableIndexMs = performance.now() - tableIndexStart;
console.log(`Indexado en tabla: ${corpus.length} docs en ${(tableIndexMs / 1000).toFixed(1)}s`);
console.log(`Rendimiento: ${(corpus.length / (tableIndexMs / 1000)).toFixed(0)} docs/seg`);
```

<Accordion title="Salida esperada">
  ```
  Tabla creada: scifact_table_1774982400000
    Indexados 500 / 5183
    ...
  Indexado en tabla: 5183 docs en 31.2s
  Rendimiento: 166 docs/seg
  ```
</Accordion>

### Ejecutar consultas con búsqueda vectorial

La API de Tables también devuelve `searchTimeMs` — la duración de la búsqueda en base de datos del lado del servidor.

```ts theme={null}
const tableResults: Record<string, Record<string, number>> = {};
const tableEndToEnd: number[] = [];
const tableDbLatencies: number[] = [];

console.log(`Ejecutando ${queries.length} consultas contra tabla (vector_search, top-${K})...`);

for (let i = 0; i < queries.length; i++) {
  const q = queries[i];
  const qVec = queryEmbeddings[i];

  const start = performance.now();
  const res = await sdk.search.tables.searchData({
    tableName: TABLE_NAME,
    searchRequest: {
      operations: [
        {
          type: 'vector_search',
          field: 'embedding',
          vector: qVec,
          distanceType: 'cosine',
          limit: K,
        },
      ],
      returnColumns: ['doc_id', 'content'],
    },
  });
  const elapsed = performance.now() - start;

  tableEndToEnd.push(elapsed);
  tableDbLatencies.push(res.searchTimeMs ?? 0);

  tableResults[q._id] = {};
  const rows = res.results ?? [];
  for (let rank = 0; rank < rows.length; rank++) {
    tableResults[q._id][rows[rank].doc_id] = 1.0 / (rank + 1);
  }
}

console.log(`\nTables — tiempo de respuesta extremo a extremo (${queries.length} consultas):`);
console.log(`  p50: ${percentile(tableEndToEnd, 0.5).toFixed(0)} ms`);
console.log(`  p95: ${percentile(tableEndToEnd, 0.95).toFixed(0)} ms`);
console.log(`  p99: ${percentile(tableEndToEnd, 0.99).toFixed(0)} ms`);

console.log(`\nTables — latencia de búsqueda en base de datos (searchTimeMs del servidor):`);
console.log(`  p50: ${percentile(tableDbLatencies, 0.5).toFixed(0)} ms`);
console.log(`  p95: ${percentile(tableDbLatencies, 0.95).toFixed(0)} ms`);
console.log(`  p99: ${percentile(tableDbLatencies, 0.99).toFixed(0)} ms`);
```

<Accordion title="Salida esperada">
  ```
  Ejecutando 300 consultas contra tabla (vector_search, top-10)...

  Tables — tiempo de respuesta extremo a extremo (300 consultas):
    p50: 38 ms
    p95: 71 ms
    p99: 95 ms

  Tables — latencia de búsqueda en base de datos (searchTimeMs del servidor):
    p50: 6 ms
    p95: 11 ms
    p99: 15 ms
  ```
</Accordion>

## Paso 5 — Calcular métricas de precisión

Evalúa la calidad de recuperación usando métricas estándar de BEIR: NDCG\@10, Recall\@10 y MRR\@10.

```ts theme={null}
function computeMetrics(
  results: Record<string, Record<string, number>>,
  qrels: Record<string, Record<string, number>>,
  k: number,
) {
  let totalNdcg = 0;
  let totalRecall = 0;
  let totalMrr = 0;
  let count = 0;

  for (const queryId of Object.keys(qrels)) {
    const relevant = qrels[queryId];
    const retrieved = results[queryId] ?? {};
    const totalRelevant = Object.values(relevant).filter((r) => r > 0).length;

    if (totalRelevant === 0) continue;
    count++;

    // Ordenar recuperados por puntuación descendente, tomar top-K
    const ranked = Object.entries(retrieved)
      .sort(([, a], [, b]) => b - a)
      .slice(0, k)
      .map(([docId]) => docId);

    // NDCG@K
    let dcg = 0;
    let idcg = 0;
    for (let i = 0; i < ranked.length; i++) {
      const rel = relevant[ranked[i]] ?? 0;
      if (rel > 0) dcg += 1 / Math.log2(i + 2);
    }
    const idealRanks = Math.min(totalRelevant, k);
    for (let i = 0; i < idealRanks; i++) {
      idcg += 1 / Math.log2(i + 2);
    }
    totalNdcg += idcg > 0 ? dcg / idcg : 0;

    // Recall@K
    const hits = ranked.filter((docId) => (relevant[docId] ?? 0) > 0).length;
    totalRecall += hits / totalRelevant;

    // MRR@K
    const firstRelevantRank = ranked.findIndex((docId) => (relevant[docId] ?? 0) > 0);
    totalMrr += firstRelevantRank >= 0 ? 1 / (firstRelevantRank + 1) : 0;
  }

  return {
    ndcg: (totalNdcg / count) * 100,
    recall: (totalRecall / count) * 100,
    mrr: (totalMrr / count) * 100,
    queriesEvaluated: count,
  };
}
```

### Imprimir resultados

```ts theme={null}
const textStoreMetrics = computeMetrics(results, qrels, K);
const tableMetrics = computeMetrics(tableResults, qrels, K);

console.log('=== Precisión de Recuperación (SciFact, 5,183 docs, 300 consultas) ===');
console.log('');
console.log(`Métrica         Text Store    Tables (vector_search)`);
console.log(`NDCG@10        ${textStoreMetrics.ndcg.toFixed(1)}          ${tableMetrics.ndcg.toFixed(1)}`);
console.log(`Recall@10      ${textStoreMetrics.recall.toFixed(1)}          ${tableMetrics.recall.toFixed(1)}`);
console.log(`MRR@10         ${textStoreMetrics.mrr.toFixed(1)}          ${tableMetrics.mrr.toFixed(1)}`);
console.log('');
console.log('=== Desglose de Latencia ===');
console.log('');
console.log(`Etapa                          Text Store       Tables`);
console.log(`Inferencia de incrustación (por c)    ${perQueryEmbedMs.toFixed(0)} ms            ${perQueryEmbedMs.toFixed(0)} ms`);
console.log(`Búsqueda BD p50 (servidor)    ${percentile(dbLatencies, 0.5).toFixed(0)} ms             ${percentile(tableDbLatencies, 0.5).toFixed(0)} ms`);
console.log(`Búsqueda BD p95 (servidor)    ${percentile(dbLatencies, 0.95).toFixed(0)} ms            ${percentile(tableDbLatencies, 0.95).toFixed(0)} ms`);
console.log(`Respuesta extremo a extremo p50        ${percentile(endToEndLatencies, 0.5).toFixed(0)} ms            ${percentile(tableEndToEnd, 0.5).toFixed(0)} ms`);
console.log(`Respuesta extremo a extremo p95        ${percentile(endToEndLatencies, 0.95).toFixed(0)} ms            ${percentile(tableEndToEnd, 0.95).toFixed(0)} ms`);
console.log(`Rendimiento de indexado               ${(corpus.length / (indexMs / 1000)).toFixed(0)} docs/s        ${(corpus.length / (tableIndexMs / 1000)).toFixed(0)} docs/s`);
```

<Accordion title="Salida esperada">
  ```
  === Precisión de Recuperación (SciFact, 5,183 docs, 300 consultas) ===

  Métrica         Text Store    Tables (vector_search)
  NDCG@10        72.4          71.8
  Recall@10      83.1          82.6
  MRR@10         68.9          68.3

  === Desglose de Latencia ===

  Etapa                          Text Store       Tables
  Inferencia de incrustación (por c)    12 ms            12 ms
  Búsqueda BD p50 (servidor)    8 ms             6 ms
  Búsqueda BD p95 (servidor)    14 ms            11 ms
  Respuesta extremo a extremo p50        45 ms            38 ms
  Respuesta extremo a extremo p95        82 ms            71 ms
  Rendimiento de indexado               182 docs/s       166 docs/s
  ```

  * **Inferencia de incrustación** es la misma para ambos backends — ejecuta el mismo modelo.
  * **Búsqueda BD** es el tiempo puro de base de datos reportado por el servidor (`search_time_ms` / `searchTimeMs`). Aquí es donde difieren Text Store y Tables — Text Store ejecuta búsqueda híbrida (vector + FTS), Tables ejecuta búsqueda vectorial pura.
  * **Respuesta extremo a extremo** incluye viaje de red, sobrecarga de gateway y búsqueda en base de datos. La diferencia entre extremo a extremo y BD es la sobrecarga.
</Accordion>

## Paso 6 — Limpieza

```ts theme={null}
await fetch(`${BASE_URL}/api/v1/search/text-store/stores/${TEXT_STORE_NAME}`, {
  method: 'DELETE',
  headers: HEADERS,
});
await sdk.search.tables.deleteTable({ tableName: TABLE_NAME });
console.log('Almacenes de benchmark eliminados.');
```

## Interpretando los resultados

### Contexto de precisión

El leaderboard de BEIR reporta NDCG\@10 en SciFact como referencia:

| Modelo                 | NDCG\@10 |
| ---------------------- | -------- |
| BM25 (base léxica)     | \~66.5   |
| text-embedding-ada-002 | \~70–73  |
| text-embedding-3-large | \~74–76  |
| Estado del arte (2024) | \~78–80  |

Puntajes NDCG\@10 en el rango 70–76 indican una calidad de recuperación fuerte, competitiva con los principales modelos de incrustación.

### Qué observar

* **NDCG\@10** es la métrica principal. Penaliza documentos relevantes que aparecen en posiciones bajas.
* **Recall\@10** mide cuántos documentos relevantes aparecen en el top 10 — importante para pipelines RAG donde la generación depende de la recuperación completa.
* **MRR\@10** mide cuán rápido aparece el primer resultado relevante — importante para búsquedas de cara al usuario.
* **Text Store vs Tables**: Text Store agrega búsqueda híbrida (vector + palabra clave) automáticamente. Tables te da control explícito sobre tipo de índice, métrica de distancia y filtros.

### Cómo leer el desglose de latencia

Una consulta de recuperación tiene tres componentes de latencia:

1. **Inferencia de incrustación** — el tiempo para convertir el texto de la consulta en un vector. Es inferencia de modelo y es igual sin importar el backend de almacenamiento.
2. **Búsqueda en base de datos** — el tiempo que el motor de búsqueda dedica a encontrar los vecinos más cercanos. Reportado por el servidor en `search_time_ms` (Text Store) o `searchTimeMs` (Tables). Es tiempo puro de búsqueda vectorial/híbrida sin sobrecarga de red.
3. **Respuesta extremo a extremo** — lo que observa el cliente: viaje de red + enrutamiento de gateway + búsqueda en base de datos.

Si la latencia extremo a extremo es alta pero la búsqueda en base de datos es rápida, el cuello de botella es la red o el gateway.
Si la búsqueda en base de datos es alta, considera un tipo de índice diferente (por ejemplo, IVF\_HNSW\_SQ para búsqueda aproximada más rápida).

### Comparación de tecnologías de almacenamiento

|                     | Text Store                                               | Tables                                                               |
| ------------------- | -------------------------------------------------------- | -------------------------------------------------------------------- |
| **Configuración**   | Cero configuración — solo nombre + dimensión             | Esquema explícito, tipos de campo, índices                           |
| **Búsqueda**        | Híbrida (vector + texto completo) automática             | Componer operaciones: vector\_search, FTS, filtro                    |
| **Ideal para**      | Prototipos RAG rápidos, búsqueda híbrida lista para usar | Esquemas personalizados, búsqueda filtrada, estrategias multi-índice |
| **Tipos de índice** | Automático                                               | IVF\_FLAT, IVF\_PQ, IVF\_HNSW\_PQ, IVF\_HNSW\_SQ                     |

## Ver también

* [Indexar y buscar texto](/es/docs/search) para la referencia completa de las APIs de Text Store y Tables.
* [Archivos y almacenes vectoriales](/es/docs/files-and-vector-stores) para la API compatible con vector store de OpenAI.
* [Evaluación GraphRAG](/es/docs/graphrag) para benchmarking de recuperación multi-hop.
