Busca semântica com embeddings — faça chunking de textos, gere embeddings via OpenAI (ou qualquer API compatível) e execute buscas por similaridade usando cosine similarity em memória ou pgvector no PostgreSQL.
npm install embed-search
# com suporte a pgvector
npm install embed-search pgimport { chunkDocument } from 'embed-search/chunker';
import { Embedder, cosineSimilarity } from 'embed-search/embedder';
import { InMemoryVectorStore } from 'embed-search';
const embedder = new Embedder({
apiKey: process.env.OPENAI_API_KEY!,
model: 'text-embedding-3-small',
});
// 1. Dividir documento em chunks
const chunks = chunkDocument({
id: 'doc-1',
content: `Texto longo do seu documento aqui...
Pode ser um artigo, contrato, FAQ ou qualquer texto.`,
metadata: { fonte: 'manual', idioma: 'pt-BR' },
});
// 2. Gerar embeddings para todos os chunks
const chunksComEmbeddings = await embedder.embedChunks(chunks);
// 3. Armazenar
const store = new InMemoryVectorStore();
await store.upsert(chunksComEmbeddings);
// 4. Buscar por similaridade
const queryEmbedding = await embedder.embedText('como faço para cancelar minha conta?');
const resultados = await store.query(queryEmbedding, { topK: 3, minScore: 0.75 });
for (const r of resultados) {
console.log(`Score: ${r.score.toFixed(3)} | ${r.chunk.text.slice(0, 100)}...`);
}import { chunkText, chunkByTokenEstimate, splitByMarkdownHeadings } from 'embed-search/chunker';
// Chunking por caracteres com overlap
const chunks = chunkText(textoLongo, {
chunkSize: 512,
chunkOverlap: 64,
separator: '\n\n',
minChunkSize: 50,
});
// Chunking estimado por tokens (1 token ≈ 4 chars)
const chunksTokens = chunkByTokenEstimate(texto, 256, 20);
// Dividir markdown por headings
const secoes = splitByMarkdownHeadings(conteudoMarkdown);import { PgVectorStore } from 'embed-search';
const store = new PgVectorStore({
connectionString: process.env.DATABASE_URL!,
tableName: 'embeddings',
dimensions: 1536,
});
// SQL para criar a tabela (rodar uma vez)
console.log(PgVectorStore.createTableSQL('embeddings', 1536));
await store.upsert(chunksComEmbeddings);
const resultados = await store.query(queryEmbedding, { topK: 5, minScore: 0.7 });import { rerank } from 'embed-search';
const rerankeados = rerank(resultados, 'cancelar conta assinatura');import { cosineSimilarity, dotProduct, normalizeVector } from 'embed-search/embedder';
const similaridade = cosineSimilarity(embeddingA, embeddingB); // 0 a 1
const vetorNormalizado = normalizeVector(embedding);MIT © 2026 Tox
built by tox