Volver a la wiki

trimChunksBySentence: Recorte a frases relevantes

Firma

export async function trimChunksBySentence(
  ai: Ai,
  queryVec: number[],
  chunks: ChunkMatch[],
  opts: { threshold: number; minSentences: number; window: number },
): Promise<ChunkMatch[]>

Propósito

Recorta cada ChunkMatch a las frases relevantes para la query. Mantiene contexto ±N frases, garantiza nunca vaciar un chunk (minSentences), y marca elisiones con [...]. Reduce tokens de input sin perder señal.

Algoritmo

1. Troceado y aplanamiento

2. Embedding batch

Embebe todas las frases en una sola llamada a embedTexts(ai, flat) → más eficiente que una por una.

3. Scoring

Calcula similaridad coseno entre queryVec y cada vector de frase. Agrupa scores por chunk.

4. Selección de frases

Para cada chunk:

5. Reconstrucción

6. Output

ChunkMatch[] modificados, mismos campos excepto .content que puede ser más corto.

Parámetros de opciones

ParámetroTipoDefaultDescripción
thresholdnumber0.38Coseno mínimo de frase para conservarla sin garantía mínima
minSentencesnumber1Frases garantizadas por chunk (las mejores si no se alcanzan por threshold)
windownumber0Frases de contexto ±N alrededor de las conservadas

Ejemplos

Ejemplo 1: Threshold puro

const chunk = {
  id: 1,
  content: "[Doc: RFC-123] Sentence A. Sentence B (relevante). Sentence C.",
  score: 0.85
};

// queryVec es un embedding de "relevante"
const opts = { threshold: 0.5, minSentences: 1, window: 0 };

// Split:
// - "[Doc: RFC-123]"
// - ["Sentence A.", "Sentence B (relevante).", "Sentence C."]

// Scores (aprox):
// - "Sentence A." → 0.3 ❌
// - "Sentence B (relevante)." → 0.8 ✅
// - "Sentence C." → 0.2 ❌

// Output:
// content = "[Doc: RFC-123]\nSentence B (relevante)."

Ejemplo 2: Ventana de contexto

const opts = { threshold: 0.5, minSentences: 1, window: 1 };

// Conserva B, pero window=1 añade A (anterior) y C (siguiente)
// Output:
// content = "[Doc: RFC-123]\nSentence A. Sentence B (relevante). Sentence C."
// (sin cambio, porque incluye todo)

Ejemplo 3: Garantía mínima

const sentences = ["A", "B", "C", "D"];
const scores = [0.1, 0.2, 0.3, 0.05];
const opts = { threshold: 0.5, minSentences: 2, window: 0 };

// Ninguna supera threshold=0.5
// Tomar 2 mejores: C (0.3), B (0.2)
// Ordenar: [B, C]
// Output: "B C"

Detalles de implementación

Estructura interna

interface ChunkProcessing {
  c: ChunkMatch;
  prefix: string;
  sentences: string[];
}

Cálculo de similaridad coseno

function cosineSimilarity(vecA: number[], vecB: number[]): number {
  let dot = 0, normA = 0, normB = 0;
  for (let i = 0; i < vecA.length; i++) {
    dot += vecA[i] * vecB[i];
    normA += vecA[i] * vecA[i];
    normB += vecB[i] * vecB[i];
  }
  return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}

Marcado de elisiones

Entre índices prev e i no contiguos:

if (prev >= 0 && i > prev + 1) pieces.push('[...]');

Fuentes de datos

Performance

Casos edge

  1. Chunk sin frases: Split devuelve array vacío → emite el chunk original sin cambios.
  2. Ninguna frase supera threshold: Garantía mínima toma las mejores (por score), evita vaciar.
  3. Recortamos casi nada: Si keep.size ≥ sentences.length, emite original (sin marcar [...] inútil).
  4. Ventana grande: Puede hacer que keep incluya casi todas las frases → emite casi original.

Notas

Véase también

Subir