Vector

Vector#

Rotas /v1/cloudvec/* · owner/admin · gated por CLOUDVEC_ENABLED (off → 404)

O que é#

Busca semântica gerenciada (RAG) sobre seus textos e documentos. Você cria coleções, coloca documentos (ou sobe arquivos que o sistema converte + chunka + indexa), e busca por significado — não por palavra-chave. Por baixo: embeddings da OpenAI + PostgreSQL/pgvector.

O que dá pra fazer#

  • Criar/listar/remover coleções (por projeto).
  • Organizar em pastas hierárquicas (suporte/faturamento/) e escopar a busca por pasta (recursivo).
  • Adicionar documentos por texto (embeda + upserta em lote, até 100/request).
  • Ingerir qualquer arquivo (PDF, docx, xlsx, pptx, imagens, md, txt…): o pipeline converte para Markdown, guarda original + Markdown no Storage, chunka e embeda. Imagens e PDFs escaneados passam por OCR via visão.
  • Buscar por similaridade de cosseno, escopada por pasta; resultados de arquivos apontam de volta para a fonte.

Como funciona por dentro (na prática)#

  • Coleção = control-row + tabela de vetores. Cada coleção é uma linha no tenant DB (vec_collections) + uma tabela no pgvector (namespaced pelo id da coleção).
  • Embedder por-coleção. O modelo de embedding fica gravado em cada coleção (default para novas: text-embedding-3-large, 3072 dims; coleções antigas 3-small/1536 continuam funcionando — modelos coexistem). A busca embeda a query com o modelo da coleção e compara com <=> (cosine distance) no pgvector.
  • Pastas = caminho materializado. Cada documento tem um path. A busca é por prefixo, recursiva: sem path busca tudo; path:"suporte/" busca suporte/
    • subpastas. Só documentos reais entram (placeholders de pasta vazia são excluídos).
  • Ingestão (fontes). Um arquivo → sidecar MarkItDown (+ passe opcional de embelezamento OpenAI) → Storage guarda original + .md espelhando a árvore de pastas (vector/<coleção>/<pasta><arquivo>) → o Markdown é quebrado em chunks embedados. Imagens/PDF escaneado → OCR de visão (modelo gpt-5.4-mini, detail:high, prompt de fidelidade que não inventa valores). Cada arquivo vira uma fonte (VecSource) rastreável; seus chunks não inflam o document_count nem aparecem no GET /documents — ficam no card da fonte, e carregam metadata.source_filename/source_id para o resultado apontar de volta.
  • Busca híbrida e re-rank. Além do cosine puro, a busca aceita fusão híbrida (semântica + lexical via RRF) para não perder termo exato, re-rank LLM opt-in, filtros por metadata/fonte/tipo e modos de retrieval com FAQ cards (qa_first). Detalhes na seção Busca abaixo.

Fluxo típico (end-to-end)#

bash
KEY="X-API-Key: ctc_…" ; B=https://data-api.catcher.one ; JS='Content-Type: application/json'
PID=<project-uuid>

# 1. Cria a coleção  (tenant_id é opcional — isola um cliente final; ver Tenants)
CID=$(curl -sX POST $B/v1/cloudvec/projects/$PID/collections -H "$KEY" -H "$JS" \
  -d '{"name":"docs"}' | jq -r .id)

# 2a. Adiciona documentos por texto (embeda em lote)
curl -sX POST $B/v1/cloudvec/collections/$CID/documents -H "$KEY" -H "$JS" -d '{
  "documents":[{"content":"O servidor tem 12 meses de garantia.","path":"suporte/","metadata":{"src":"faq"}}]}'

# 2b. …ou sobe um arquivo (converte → Storage → chunka → embeda; assíncrono)
curl -sX POST $B/v1/cloudvec/collections/$CID/ingest -H "$KEY" \
  -F "file=@./manual.pdf" -F "path=suporte/"
# → { "id":"…","status":"processing","chunk_count":…,"filename":"manual.pdf" }

# 3. Busca semântica (escopada por pasta, recursiva)
curl -sX POST $B/v1/cloudvec/collections/$CID/search -H "$KEY" -H "$JS" \
  -d '{"query":"quanto tempo de cobertura contra defeitos?","k":5,"path":"suporte/"}'
# → { "results":[{"id","content","metadata","score"}], "count":5 }

Referência de endpoints#

Coleções#

Método Rota O que faz
POST /v1/cloudvec/projects/{pid}/collections Cria ({name} lowercase 2-48, tenant_id?) → 201 (embed_model, dimensions, tenant_id?)
GET /v1/cloudvec/projects/{pid}/collections Lista
GET /v1/cloudvec/collections/{id} Detalhe + document_count vivo
DELETE /v1/cloudvec/collections/{id} Remove (dropa tabela de vetores + control-row) → 204
Isolamento por tenant (end-customers)

Passe tenant_id ao criar a coleção para isolar um cliente final: a coleção passa a viver no schema Postgres dedicado do tenant (t_<id> + role tr_<id>) e a busca naquela coleção só enxerga os vetores dele. Sem tenant_id, a coleção é de nível projeto (schema default public, compartilhada). É o mesmo tenant_id usado no Database, Storage e Cache — ver Tenants. Para isolar ainda mais fino (por usuário final dentro de um tenant) sem multiplicar coleções, use o filter por metadata na busca (abaixo).

Pastas#

Método Rota O que faz
POST /v1/cloudvec/collections/{id}/folders Cria pasta ({path}) → 201 {path:"suporte/faturamento/"}
GET /v1/cloudvec/collections/{id}/folders?parent= Subpastas imediatas
GET /v1/cloudvec/collections/{id}/folders/stats?path= {document_count} recursivo
DELETE /v1/cloudvec/collections/{id}/folders?path= Apaga pasta + conteúdo recursivo → {deleted:N}

Documentos#

Método Rota O que faz
POST /v1/cloudvec/collections/{id}/documents Embeda + upserta ({documents:[{content,path?,id?,metadata?}]}, máx. 100) → 201
GET /v1/cloudvec/collections/{id}/documents?path=&recursive=&limit= Lista (limit ≤ 1000)
DELETE /v1/cloudvec/collections/{id}/documents/{docId} Remove → 204

Busca#

POST /v1/cloudvec/collections/{id}/search{results:[{id,content,metadata,score}], count}

Campo Tipo O que faz
query string A consulta (é embedada — 1 embedding por busca).
k int Máximo de resultados (default 5).
path string Escopo por pasta, recursivo (""/ausente = coleção inteira).
hybrid bool Fusão RRF do braço semântico (cosine) com um braço lexical full-text ANTES do corte top-k — um doc term-exato (ID, SKU, código) que o cosine enterrou ainda aparece. Cada hit ganha metadata.match: "semantic"|"lexical"|"both".
alpha float 0..1 Peso do braço semântico na fusão (lexical = 1−alpha; ausente = 0.5; 0 = lexical puro).
rerank bool Re-rank listwise com LLM após a recuperação (opt-in — custa uma chamada de modelo; use em consultas críticas).
mode string Retrieval com FAQ cards do Refinery: default qa_first (cards antes de chunks; degrada para chunks se não houver cards) · chunks (só chunks) · qa_only.
filter map Pré-filtro de igualdade por metadata (AND), ex.: {"end_user_id":"u_9"}.
source_id string Só chunks de uma fonte específica.
type string Só um tipo de fonte (pdf|word|spreadsheet|markdown|text|…).

score = similaridade de cosseno em [0,1] (1 = idêntico).

Ingestão de arquivos (fontes)#

Método Rota O que faz
POST /v1/cloudvec/collections/{id}/ingest Multipart (file + path?), ≤ 64 MB → {id,status:"processing",chunk_count,…}
GET /v1/cloudvec/collections/{id}/sources?path= Lista fontes em path
GET /v1/cloudvec/collections/{id}/sources/{sid} Detalhe + signed URLs (TTL 30 min) do original + Markdown + os chunks
DELETE /v1/cloudvec/collections/{id}/sources/{sid} Remove fonte + chunks + objetos no Storage → 204

Ingestão de binários requer o sidecar (CLOUDVEC_CONVERTER_URL) + CLOUDSTORE_ENABLED + um GCP project READY no projeto (para o bucket vector-sources). .md/.txt passam direto.

Códigos de erro do módulo#

INVALID_COLLECTION_NAME (400, inclui path inválido) · COLLECTION_ALREADY_EXISTS (409) · COLLECTION_NOT_FOUND (404) · EMPTY_INPUT (400) · BATCH_TOO_LARGE (400,

100 docs) · EMBEDDING_DIMENSION_MISMATCH (400) · SOURCE_NOT_FOUND (404) · CONVERSION_UNAVAILABLE (422, sidecar ausente p/ binário) · NO_READY_PROJECT (409) · MISSING_FIELD (400).

Gotchas#

  • Embeddings custam (OpenAI) — documents cobra por embedar cada content; busca cobra 1 embedding por query. Agrupe inserções em lote (até 100/request).
  • Não misture modelos numa coleção — o modelo é fixado na criação; se mudar o default global, coleções novas usam o novo, as antigas mantêm o seu (sem EMBEDDING_DIMENSION_MISMATCH).
  • Ingestão é assíncronastatus: processingready|failed (last_error); faça poll via GET .../sources/{sid}.
  • Chunks de fontes ≠ documents — não aparecem no GET /documents nem contam no document_count; aparecem nos resultados de busca com metadata.source_filename.
  • Para RAG: ingira os docs em pastas por assunto, depois search com path para focar o contexto e k para o tamanho do contexto.