# RAG


Al configurar una fuente RAG (Generación Aumentada por Recuperación) en Docker Agent, tu agente obtiene
automáticamente una herramienta de búsqueda para esa base de conocimientos. El agente decide cuándo
buscar, recupera solo la información relevante y la utiliza para responder preguntas o completar
tareas, todo sin necesidad de que gestiones manualmente lo que se incluye en el prompt.

Esta guía explica cómo funciona el sistema RAG de Docker Agent, cuándo usarlo y cómo configurarlo
eficazmente para tu contenido.

> [!NOTE]
> RAG es una característica avanzada que requiere configuración y ajustes. Los valores
> predeterminados funcionan bien para comenzar, pero adaptar la configuración a tu contenido y caso
> de uso específicos mejora significativamente los resultados.

## El problema: exceso de contexto

Tu agente puede trabajar con todo tu código fuente, pero no puede meterlo todo en su ventana de
contexto. Incluso con límites de 200K tokens, los proyectos medianos son demasiado grandes. Buscar
código relevante oculto en cientos de archivos desperdicia contexto.

Las herramientas del sistema de archivos ayudan a los agentes a leer archivos, pero el agente tiene que
adivinar qué archivos leer. No puede buscar por significado, solo por nombre de archivo. Pregunta
"encuentra la lógica de reintento" y el agente leerá archivos esperando toparse con el código
correcto.

Grep encuentra coincidencias de texto exactas pero pasa por alto conceptos relacionados. Buscar
"authentication" (autenticación) no encontrará código que use "auth" o "login". O bien obtienes
cientos de coincidencias o ninguna, y grep no comprende la estructura del código: solo hace
coincidir cadenas allí donde aparezcan.

RAG indexa tu contenido con anticipación y permite la búsqueda semántica. El agente busca contenido
preindexado por su significado, no por palabras exactas. Recupera solo fragmentos relevantes que
respetan la estructura del código. Sin desperdiciar contexto en la exploración.

## Cómo funciona RAG en Docker Agent

Configura una fuente RAG en tu configuración de Docker Agent:

```yaml
rag:
  codebase:
    docs: [./src, ./pkg]
    strategies:
      - type: chunked-embeddings
        embedding_model: openai/text-embedding-3-small
        vector_dimensions: 1536
        database: ./code.db

agents:
  root:
    model: openai/gpt-5
    instruction: You are a coding assistant. Search the codebase when needed.
    rag: [codebase]
```

Cuando haces referencia a `rag: [codebase]`, Docker Agent:

1. **Al iniciar** - Indexa tus documentos (solo en la primera ejecución, bloquea hasta completarse).
2. **Durante la conversación** - Le proporciona al agente una herramienta de búsqueda.
3. **Cuando el agente realiza una búsqueda** - Recupera los fragmentos relevantes y los añade al contexto.
4. **Con los cambios en los archivos** - Reindexa automáticamente los archivos modificados.

El agente decide cuándo buscar basándose en la conversación. Tú no gestionas lo que entra en el
contexto: el agente se encarga de ello.

### El proceso de indexación

En la primera ejecución, Docker Agent:

- Lee archivos de las rutas configuradas.
- Respeta los patrones de `.gitignore` (se puede deshabilitar).
- Divide los documentos en fragmentos (chunks).
- Crea representaciones buscables utilizando la estrategia elegida.
- Almacena todo en una base de datos local.

Las ejecuciones posteriores reutilizan el índice. Si los archivos cambian, Docker Agent lo detecta y
reindexa solo lo que haya cambiado, manteniendo tu base de conocimientos actualizada sin
intervención manual.

## Estrategias de recuperación

Diferentes contenidos requieren diferentes enfoques de recuperación. Docker Agent admite tres
estrategias, cada una optimizada para diferentes casos de uso. Los valores predeterminados funcionan
bien, pero comprender las ventajas y desventajas te ayuda a elegir el enfoque correcto.

### Búsqueda semántica (chunked-embeddings)

Convierte el texto en vectores que representan significados, lo que permite buscar por concepto en
lugar de por palabras exactas:

```yaml
strategies:
  - type: chunked-embeddings
    embedding_model: openai/text-embedding-3-small
    vector_dimensions: 1536
    database: ./docs.db
    chunking:
      size: 1000
      overlap: 100
```

Durante la indexación, los documentos se dividen en fragmentos y cada fragmento es convertido en un
vector de 1536 dimensiones por el modelo de incrustación (embedding model). Estos vectores son
esencialmente coordenadas en un espacio de alta dimensión donde los conceptos similares se posicionan
cerca unos de otros.

Cuando buscas '¿cómo autentico a los usuarios?', tu consulta secciona en un vector y la base de datos
encuentra fragmentos con vectores cercanos mediante similitud de coseno (midiendo el ángulo entre
vectores). El modelo de incrustación aprendió que 'authentication' (autenticación), 'auth' y 'login'
son conceptos relacionados, por lo que buscar uno encuentra los demás.

Ejemplo: La consulta '¿cómo autentico a los usuarios?' encuentra tanto 'La autenticación de usuario
requiere un token de API válido' como 'La autenticación basada en tokens valida las solicitudes', a
pesar de la redacción diferente. No encontrará 'Las pruebas de autenticación están fallando' porque
tiene un significado diferente a pesar de contener la palabra.

Esto funciona bien para la documentación donde los usuarios hacen preguntas usando una terminología
diferente a la de tus documentos. La desventaja es que puede pasar por alto términos técnicos exactos
y, a veces, deseas coincidencias virtuales, no semánticas. Requiere llamadas a la API de incrustación
durante la indexación.

### Búsqueda por palabras clave (BM25)

Algoritmo estadístico que busca coincidencias y las clasifica según la frecuencia y rareza del término:

```yaml
strategies:
  - type: bm25
    database: ./bm25.db
    k1: 1.5
    b: 0.75
    chunking:
      size: 1000
      overlap: 100
```

Durante la indexación, los documentos se tokenizan y el algoritmo calcula con qué frecuencia aparece
cada término (frecuencia del término) y qué tan raro es en todos los documentos (frecuencia inversa de
documento). El índice de puntuación se almacena en una base de datos SQLite local.

Cuando buscas 'HandleRequest function', el algoritmo encuentra fragmentos que contienen estos términos
exactos y los puntúa en función de la frecuencia del término, la rareza del término y la longitud del
documento. Encontrar 'HandleRequest' se puntúa como más significativo que encontrar palabras comunes
como 'function'. Piensa en ello como un grep con clasificación estadística.

Ejemplo: Buscar 'HandleRequest function' encuentra `func HandleRequest(w http.ResponseWriter, r
*http.Request)` y 'La función HandleRequest procesa solicitudes entrantes', pero no 'procesa
solicitudes HTTP' a pesar de ser semánticamente similar.

El parámetro `k1` (por defecto 1.5) controla cuánto importan los términos repetidos: valores más altos
enfatizan más la repetición. El parámetro `b` (por defecto 0.75) controla la normalización de longitud:
valores más altos penalizan más los documentos más largos.

Esto es rápido, local (sin costos de API) y predecible para encontrar nombres de funciones, nombres de
clases, endpoints de API y cualquier identificador que aparezca textualmente. La desventaja es la nula
comprensión del significado: 'RetryHandler' y 'retry logic' no coincidirán a pesar de estar
relacionados. Es un complemento esencial para la búsqueda semántica.

### Búsqueda semántica mejorada por LLM (semantic-embeddings)

Genera resúmenes semánticos con un LLM antes de la incrustación, lo que permite buscar por lo que hace
el código en lugar de por cómo se llama:

```yaml
strategies:
  - type: semantic-embeddings
    embedding_model: openai/text-embedding-3-small
    chat_model: openai/gpt-5-mini
    vector_dimensions: 1536
    database: ./code.db
    ast_context: true
    chunking:
      size: 1000
      code_aware: true
```

Durante la indexación, el código se divide utilizando la estructura AST (las funciones permanecen
intactas), luego el `chat_model` genera un resumen semántico de cada fragmento. Se incrusta el
resumen, no el código fuente. Cuando buscas, tu consulta coincide con estos resúmenes, pero se devuelve
el código original.

Esto resuelve un problema de las incrustaciones comunes: las incrustaciones de código fuente están
dominadas por nombres de variables y detalles de implementación. Una función llamada `processData` que
implementa lógica de reintento no coincidirá semánticamente con 'reintento'. Pero cuando el LLM lo
resume primero, el resumen menciona explícitamente 'lógica de reintento', haciéndolo localizable.

Ejemplo: Considera este código:

```go
func (c *Client) Do(req *Request) (*Response, error) {
    for i := 0; i < 3; i++ {
        resp, err := c.attempt(req)
        if err == nil { return resp, nil }
        time.Sleep(time.Duration(1<<i) * time.Second)
    }
    return nil, errors.New("max retries exceeded")
}
```

El resumen del LLM es: "Implementa lógica de reintento con retroceso exponencial para solicitudes HTTP, intentando hasta 3 veces con retrasos de 1s, 2s, 4s antes de fallar."

Buscar 'lógica de reintento retroceso exponencial' ahora encuentra este código, a pesar de que el
código nunca usa esas palabras. La opción `ast_context: true` incluye metadatos AST en las
instrucciones para una mejor comprensión. La fragmentación `code_aware: true` evita dividir funciones
a la mitad de su implementación.

Este enfoque destaca al buscar código por comportamiento en códigos fuente grandes con nombres
inconsistentes. La desventaja es una indexación significativamente más lenta (una llamada de LLM por
fragmento) y mayores costos de API (tanto para modelos de chat como de incrustación). A menudo es
excesivo para código bien documentado o proyectos simples.

## Combinar estrategias con recuperación híbrida

Cada estrategia tiene fortalezas y debilidades. Combinarlas captura tanto la comprensión semántica
como las coincidencias exactas de términos:

```yaml
rag:
  knowledge:
    docs: [./documentation, ./src]
    strategies:
      - type: chunked-embeddings
        embedding_model: openai/text-embedding-3-small
        vector_dimensions: 1536
        database: ./vector.db
        limit: 20

      - type: bm25
        database: ./bm25.db
        limit: 15

    results:
      fusion:
        strategy: rrf
        k: 60
      deduplicate: true
      limit: 5
```

### Cómo funciona la fusión

Ambas estrategias se ejecutan en paralelo, cada una devolviendo sus mejores candidatos (20 y 15 en este
ejemplo). La fusión combina los resultados utilizando puntuación basada en rangos, elimina los
duplicados y devuelve los 5 mejores resultados finales. Tu agente obtiene resultados que funcionan
tanto para consultas semánticas ('cómo hago...') como para búsquedas de términos exactos ('busca la
función `configure_auth`').

### Estrategias de fusión

Se recomienda RRF (Reciprocal Rank Fusion - Fusión de Rango Recíproco). Combina resultados basándose en
el rango en lugar de las puntuaciones absolutas, lo que funciona de manera confiable cuando las
estrategias usan diferentes escalas de puntuación. No requiere ajustes.

Para la fusión ponderada, le das más importancia a una estrategia:

```yaml
fusion:
  strategy: weighted
  weights:
    chunked-embeddings: 0.7
    bm25: 0.3
```

Esto requiere ajustes para tu contenido. Utilízalo cuando sepas que un enfoque funciona mejor para tu
caso de uso.

La fusión por puntuación máxima (Max score fusion) toma la puntuación más alta entre las estrategias:

```yaml
fusion:
  strategy: max
```

Esto solo funciona si las estrategias usan escalas de puntuación comparables. Es simple pero menos
sofisticado que RRF.

## Mejorar la calidad de la recuperación

### Reclasificar resultados (Reranking)

La recuperación inicial se optimiza para la velocidad. La reclasificación vuelve a puntuar los
resultados con un modelo más sofisticado para mejorar la relevancia:

```yaml
results:
  reranking:
    model: openai/gpt-5-mini
    threshold: 0.3
    criteria: |
      Al puntuar la relevancia, prioriza:
      - Documentación oficial sobre el contenido de la comunidad
      - Información reciente sobre material desactualizado
      - Ejemplos prácticos sobre explicaciones teóricas
      - Implementaciones de código sobre discusiones de diseño
  limit: 5
```

El campo `criteria` (criterios) es potente: utilízalo para codificar el conocimiento del dominio sobre
lo que hace que los resultados sean relevantes para tu caso de uso específico. Cuanto más específicos
sean tus criterios, mejor será la reclasificación.

Desventaja: Resultados significativamente mejores pero añade latencia y costos de API (llamada de LLM
para puntuar cada resultado).

### Configuración de la fragmentación (Chunking)

La forma de dividir los documentos afecta drásticamente a la calidad de la recuperación. Adapta la
fragmentación al tipo de contenido. El tamaño del fragmento se mide en caracteres (puntos de código
Unicode), no en tokens.

Para documentación y prosa, utiliza fragmentos moderados con superposición (overlap):

```yaml
chunking:
  size: 1000
  overlap: 100
  respect_word_boundaries: true
```

La superposición conserva el contexto en los límites del fragmento. Respetar los límites de las
palabras evita cortar palabras a la mitad.

Para código, utiliza fragmentos más grandes con división basada en AST:

```yaml
chunking:
  size: 2000
  code_aware: true
```

Esto mantiene las funciones intactas. El ajuste `code_aware` utiliza tree-sitter para respetar la
estructura del código.

> [!NOTE]
> Actualmente solo se admite Go; se planea el soporte para idiomas adicionales.

Para contenido corto y enfocado, como referencias de API:

```yaml
chunking:
  size: 500
  overlap: 50
```

Las secciones breves necesitan menos superposición ya que son naturalmente autónomas.

Experimenta con estos valores. Si la recuperación pierde contexto, aumenta el tamaño del fragmento o
la superposición. Si los resultados son demasiado amplios, disminuye el tamaño del fragmento.

## Tomar decisiones sobre RAG

### Cuándo usar RAG

Usa RAG cuando:

- Tu contenido sea demasiado grande para la ventana de contexto.
- Desees una recuperación dirigida, no todo a la vez.
- El contenido cambie y deba mantenerse actualizado.
- El agente necesite buscar en muchos archivos.

No uses RAG cuando:

- El contenido sea lo suficientemente pequeño como para incluirlo en las instrucciones del agente.
- La información rara vez cambie (considera la ingeniería de prompts en su lugar).
- Necesites datos en tiempo real (RAG utiliza capturas preindexadas).
- El contenido ya esté en un formato de búsqueda que el agente pueda consultar directamente.

### Elegir estrategias de recuperación

Usa la búsqueda semántica (`chunked-embeddings`) para documentación de cara al usuario, contenido con
terminología variada y búsquedas conceptuales en las que los usuarios redacten las preguntas de forma
diferente a tus documentos.

Usa la búsqueda por palabras clave (`bm25`) para identificadores de código, nombres de funciones,
endpoints de API, mensajes de error y cualquier contenido donde las coincidencias de términos exactos
importen. Esencial para jerga técnica y nombres propios.

Usa la búsqueda semántica mejorada por LLM (`semantic-embeddings`) para la búsqueda de código por
funcionalidad, localización de implementaciones por comportamiento en lugar de por nombre, o contenido
técnico complejo que requiera una comprensión profunda. Elige esta opción cuando la precisión importe
más que la velocidad de indexación.

Usa híbrido (múltiples estrategias) para la búsqueda de propósito general en contenido mixto, cuando no
estés seguro de qué enfoque funciona mejor o para sistemas de producción donde la calidad sea lo más
importante. Máxima cobertura a costa de la complejidad.

### Ajustes para tu proyecto

Comienza con los valores predeterminados y luego ajústalos según los resultados.

Si la recuperación pierde contenido relevante:

- Aumenta el `limit` en las estrategias para recuperar más candidatos.
- Ajusta el `threshold` (umbral) para que sea menos estricto.
- Aumenta el `size` (tamaño) del fragmento para capturar más contexto.
- Añade más estrategias de recuperación.

Si la recuperación devuelve contenido irrelevante:

- Disminuye el `limit` a menos candidatos.
- Aumenta el `threshold` para ser más estricto.
- Añade reclasificación (reranking) con criterios específicos.
- Disminuye el `size` del fragmento para obtener resultados más enfocados.

Si la indexación es demasiado lenta:

- Aumenta el `batch_size` para realizar menos llamadas a la API.
- Aumenta `max_embedding_concurrency` para paralelismo.
- Considera BM25 en lugar de incrustaciones (local, sin API).
- Utiliza modelos de incrustación más pequeños.

Si los resultados carecen de contexto:

- Aumenta el `overlap` (superposición) del fragmento.
- Aumenta el `size` del fragmento.
- Utiliza `return_full_content: true` para devolver documentos completos.
- Añade fragmentos vecinos a los resultados.

## Lectura adicional

- [Referencia de configuración](/ai/docker-agent/rag/reference/config/#rag) - Opciones y parámetros de RAG completos
- [Ejemplos de RAG](https://github.com/docker/docker-agent/tree/main/examples/rag) - Configuraciones en funcionamiento para diferentes escenarios
- [Referencia de herramientas](/ai/docker-agent/rag/reference/toolsets/) - Cómo funcionan las herramientas de búsqueda de RAG en los flujos de trabajo de los agentes

