mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 16:36:21 +02:00
676 lines
22 KiB
Markdown
676 lines
22 KiB
Markdown
|
|
---
|
||
|
|
layout: default
|
||
|
|
title: "Especificação Técnica de Processamento em Lote de Embeddings"
|
||
|
|
parent: "Portuguese (Beta)"
|
||
|
|
---
|
||
|
|
|
||
|
|
# Especificação Técnica de Processamento em Lote de Embeddings
|
||
|
|
|
||
|
|
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
|
||
|
|
|
||
|
|
## Visão Geral
|
||
|
|
|
||
|
|
Esta especificação descreve otimizações para o serviço de embeddings para suportar o processamento em lote de vários textos em um único pedido. A implementação atual processa um texto por vez, perdendo os benefícios de desempenho significativos que os modelos de embedding oferecem ao processar lotes.
|
||
|
|
|
||
|
|
1. **Ineficiência no Processamento de Texto Único**: A implementação atual envolve textos únicos em uma lista, subutilizando as capacidades de lote do FastEmbed.
|
||
|
|
2. **Sobrecarga de Pedido por Texto**: Cada texto requer uma viagem de ida e volta separada do Pulsar.
|
||
|
|
3. **Ineficiência na Inferência do Modelo**: Os modelos de embedding têm uma sobrecarga fixa por lote; lotes pequenos desperdiçam recursos de GPU/CPU.
|
||
|
|
4. **Processamento Serial nos Clientes**: Serviços importantes fazem loop sobre itens e chamam embeddings um de cada vez.
|
||
|
|
|
||
|
|
## Objetivos
|
||
|
|
|
||
|
|
**Suporte para API de Lote**: Permitir o processamento de vários textos em um único pedido.
|
||
|
|
**Compatibilidade com Versões Anteriores**: Manter o suporte para pedidos de texto único.
|
||
|
|
**Melhora Significativa no Desempenho**: Almejar uma melhora de desempenho de 5 a 10 vezes para operações em lote.
|
||
|
|
**Latência Reduzida por Texto**: Diminuir a latência amortizada ao incorporar vários textos.
|
||
|
|
**Eficiência de Memória**: Processar lotes sem consumo excessivo de memória.
|
||
|
|
**Independente do Provedor**: Suportar o processamento em lote em FastEmbed, Ollama e outros provedores.
|
||
|
|
**Migração do Cliente**: Atualizar todos os clientes de embedding para usar a API de lote sempre que for benéfico.
|
||
|
|
|
||
|
|
## Contexto
|
||
|
|
|
||
|
|
### Implementação Atual - Serviço de Embeddings
|
||
|
|
|
||
|
|
A implementação de embeddings em `trustgraph-flow/trustgraph/embeddings/fastembed/processor.py` apresenta uma ineficiência de desempenho significativa:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# fastembed/processor.py line 56
|
||
|
|
async def on_embeddings(self, text, model=None):
|
||
|
|
use_model = model or self.default_model
|
||
|
|
self._load_model(use_model)
|
||
|
|
|
||
|
|
vecs = self.embeddings.embed([text]) # Single text wrapped in list
|
||
|
|
|
||
|
|
return [v.tolist() for v in vecs]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problemas:**
|
||
|
|
|
||
|
|
1. **Tamanho do Lote 1**: O método `embed()` do FastEmbed é otimizado para processamento em lote, mas sempre o chamamos com `[text]` - um lote de tamanho 1.
|
||
|
|
|
||
|
|
2. **Sobrecarga por Requisição**: Cada solicitação de incorporação incorre em:
|
||
|
|
Serialização/desserialização de mensagens Pulsar
|
||
|
|
Latência de ida e volta da rede
|
||
|
|
Sobrecarga de inicialização da inferência do modelo
|
||
|
|
Sobrecarga de agendamento assíncrono do Python
|
||
|
|
|
||
|
|
3. **Limitação do Esquema**: O esquema `EmbeddingsRequest` suporta apenas um único texto:
|
||
|
|
```python
|
||
|
|
@dataclass
|
||
|
|
class EmbeddingsRequest:
|
||
|
|
text: str = "" # Single text only
|
||
|
|
```
|
||
|
|
|
||
|
|
### Clientes Atuais - Processamento Serial
|
||
|
|
|
||
|
|
#### 1. Gateway de API
|
||
|
|
|
||
|
|
**Arquivo:** `trustgraph-flow/trustgraph/gateway/dispatch/embeddings.py`
|
||
|
|
|
||
|
|
O gateway aceita solicitações de incorporação de texto único via HTTP/WebSocket e as encaminha para o serviço de incorporação. Atualmente, não existe um endpoint para processamento em lote.
|
||
|
|
|
||
|
|
```python
|
||
|
|
class EmbeddingsRequestor(ServiceRequestor):
|
||
|
|
# Handles single EmbeddingsRequest -> EmbeddingsResponse
|
||
|
|
request_schema=EmbeddingsRequest, # Single text only
|
||
|
|
response_schema=EmbeddingsResponse,
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impacto:** Clientes externos (aplicativos web, scripts) devem fazer N requisições HTTP para incorporar N textos.
|
||
|
|
|
||
|
|
#### 2. Serviço de Incorporação de Documentos
|
||
|
|
|
||
|
|
**Arquivo:** `trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py`
|
||
|
|
|
||
|
|
Processa blocos de documentos um de cada vez:
|
||
|
|
|
||
|
|
```python
|
||
|
|
async def on_message(self, msg, consumer, flow):
|
||
|
|
v = msg.value()
|
||
|
|
|
||
|
|
# Single chunk per request
|
||
|
|
resp = await flow("embeddings-request").request(
|
||
|
|
EmbeddingsRequest(text=v.chunk)
|
||
|
|
)
|
||
|
|
vectors = resp.vectors
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impacto:** Cada trecho de documento requer uma chamada de embedding separada. Um documento com 100 trechos = 100 requisições de embedding.
|
||
|
|
|
||
|
|
#### 3. Serviço de Embeddings de Grafos
|
||
|
|
|
||
|
|
**Arquivo:** `trustgraph-flow/trustgraph/embeddings/graph_embeddings/embeddings.py`
|
||
|
|
|
||
|
|
Percorre as entidades e incorpora cada uma sequencialmente:
|
||
|
|
|
||
|
|
```python
|
||
|
|
async def on_message(self, msg, consumer, flow):
|
||
|
|
for entity in v.entities:
|
||
|
|
# Serial embedding - one entity at a time
|
||
|
|
vectors = await flow("embeddings-request").embed(
|
||
|
|
text=entity.context
|
||
|
|
)
|
||
|
|
entities.append(EntityEmbeddings(
|
||
|
|
entity=entity.entity,
|
||
|
|
vectors=vectors,
|
||
|
|
chunk_id=entity.chunk_id,
|
||
|
|
))
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impacto:** Uma mensagem com 50 entidades = 50 solicitações de incorporação serial. Isso é um grande gargalo durante a construção do grafo de conhecimento.
|
||
|
|
|
||
|
|
#### 4. Serviço de Incorporação de Linhas
|
||
|
|
|
||
|
|
**Arquivo:** `trustgraph-flow/trustgraph/embeddings/row_embeddings/embeddings.py`
|
||
|
|
|
||
|
|
Percorre textos únicos e incorpora cada um serialmente:
|
||
|
|
|
||
|
|
```python
|
||
|
|
async def on_message(self, msg, consumer, flow):
|
||
|
|
for text, (index_name, index_value) in texts_to_embed.items():
|
||
|
|
# Serial embedding - one text at a time
|
||
|
|
vectors = await flow("embeddings-request").embed(text=text)
|
||
|
|
|
||
|
|
embeddings_list.append(RowIndexEmbedding(
|
||
|
|
index_name=index_name,
|
||
|
|
index_value=index_value,
|
||
|
|
text=text,
|
||
|
|
vectors=vectors
|
||
|
|
))
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impacto:** Processar uma tabela com 100 valores indexados únicos = 100 solicitações de incorporação seriais.
|
||
|
|
|
||
|
|
#### 5. EmbeddingsClient (Cliente Base)
|
||
|
|
|
||
|
|
**Arquivo:** `trustgraph-base/trustgraph/base/embeddings_client.py`
|
||
|
|
|
||
|
|
O cliente usado por todos os processadores de fluxo suporta apenas a incorporação de texto único:
|
||
|
|
|
||
|
|
```python
|
||
|
|
class EmbeddingsClient(RequestResponse):
|
||
|
|
async def embed(self, text, timeout=30):
|
||
|
|
resp = await self.request(
|
||
|
|
EmbeddingsRequest(text=text), # Single text
|
||
|
|
timeout=timeout
|
||
|
|
)
|
||
|
|
return resp.vectors
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impacto:** Todos os clientes que utilizam este componente estão limitados a operações de texto único.
|
||
|
|
|
||
|
|
#### 6. Ferramentas de Linha de Comando
|
||
|
|
|
||
|
|
**Arquivo:** `trustgraph-cli/trustgraph/cli/invoke_embeddings.py`
|
||
|
|
|
||
|
|
A ferramenta de linha de comando aceita um único argumento de texto:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def query(url, flow_id, text, token=None):
|
||
|
|
result = flow.embeddings(text=text) # Single text
|
||
|
|
vectors = result.get("vectors", [])
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impacto:** Os usuários não podem realizar incorporações em lote a partir da linha de comando. O processamento de um arquivo de textos requer N invocações.
|
||
|
|
|
||
|
|
#### 7. SDK Python
|
||
|
|
|
||
|
|
O SDK Python fornece duas classes de cliente para interagir com os serviços da TrustGraph. Ambas suportam apenas a incorporação de um único texto.
|
||
|
|
|
||
|
|
**Arquivo:** `trustgraph-base/trustgraph/api/flow.py`
|
||
|
|
|
||
|
|
```python
|
||
|
|
class FlowInstance:
|
||
|
|
def embeddings(self, text):
|
||
|
|
"""Get embeddings for a single text"""
|
||
|
|
input = {"text": text}
|
||
|
|
return self.request("service/embeddings", input)["vectors"]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Arquivo:** `trustgraph-base/trustgraph/api/socket_client.py`
|
||
|
|
|
||
|
|
```python
|
||
|
|
class SocketFlowInstance:
|
||
|
|
def embeddings(self, text: str, **kwargs: Any) -> Dict[str, Any]:
|
||
|
|
"""Get embeddings for a single text via WebSocket"""
|
||
|
|
request = {"text": text}
|
||
|
|
return self.client._send_request_sync(
|
||
|
|
"embeddings", self.flow_id, request, False
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impacto:** Desenvolvedores Python que usam o SDK precisam iterar sobre textos e fazer N chamadas de API separadas. Não existe suporte para processamento em lote de embeddings para usuários do SDK.
|
||
|
|
|
||
|
|
### Impacto no Desempenho
|
||
|
|
|
||
|
|
Para ingestão típica de documentos (1000 trechos de texto):
|
||
|
|
**Atual:** 1000 requisições separadas, 1000 chamadas de inferência de modelo
|
||
|
|
**Em lote (batch_size=32):** 32 requisições, 32 chamadas de inferência de modelo (redução de 96,8%)
|
||
|
|
|
||
|
|
Para embedding de grafos (mensagem com 50 entidades):
|
||
|
|
**Atual:** 50 chamadas `await` sequenciais, ~5-10 segundos
|
||
|
|
**Em lote:** 1-2 chamadas em lote, ~0,5-1 segundo (melhora de 5-10x)
|
||
|
|
|
||
|
|
Bibliotecas como FastEmbed e similares alcançam escalabilidade quase linear no throughput com o tamanho do lote, até os limites do hardware (tipicamente 32-128 textos por lote).
|
||
|
|
|
||
|
|
## Design Técnico
|
||
|
|
|
||
|
|
### Arquitetura
|
||
|
|
|
||
|
|
A otimização de processamento em lote de embeddings requer alterações nos seguintes componentes:
|
||
|
|
|
||
|
|
#### 1. **Aprimoramento do Esquema**
|
||
|
|
Estender `EmbeddingsRequest` para suportar múltiplos textos
|
||
|
|
Estender `EmbeddingsResponse` para retornar múltiplos conjuntos de vetores
|
||
|
|
Manter a compatibilidade retroativa com requisições de texto único
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/schema/services/llm.py`
|
||
|
|
|
||
|
|
#### 2. **Aprimoramento do Serviço Base**
|
||
|
|
Atualizar `EmbeddingsService` para lidar com requisições em lote
|
||
|
|
Adicionar configuração do tamanho do lote
|
||
|
|
Implementar tratamento de requisições com suporte a lote
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/base/embeddings_service.py`
|
||
|
|
|
||
|
|
#### 3. **Atualizações do Processador do Provedor**
|
||
|
|
Atualizar o processador FastEmbed para passar o lote completo para `embed()`
|
||
|
|
Atualizar o processador Ollama para lidar com lotes (se suportado)
|
||
|
|
Adicionar processamento sequencial como fallback para provedores sem suporte a lote
|
||
|
|
|
||
|
|
Módulos:
|
||
|
|
`trustgraph-flow/trustgraph/embeddings/fastembed/processor.py`
|
||
|
|
`trustgraph-flow/trustgraph/embeddings/ollama/processor.py`
|
||
|
|
|
||
|
|
#### 4. **Aprimoramento do Cliente**
|
||
|
|
Adicionar método de embedding em lote para `EmbeddingsClient`
|
||
|
|
Suportar APIs de texto único e em lote
|
||
|
|
Adicionar agrupamento automático para grandes entradas
|
||
|
|
|
||
|
|
Módulo: `trustgraph-base/trustgraph/base/embeddings_client.py`
|
||
|
|
|
||
|
|
#### 5. **Atualizações do Chamador - Processadores de Fluxo**
|
||
|
|
Atualizar `graph_embeddings` para agrupar contextos de entidades
|
||
|
|
Atualizar `row_embeddings` para agrupar textos de índice
|
||
|
|
Atualizar `document_embeddings` se o agrupamento de mensagens for viável
|
||
|
|
|
||
|
|
Módulos:
|
||
|
|
`trustgraph-flow/trustgraph/embeddings/graph_embeddings/embeddings.py`
|
||
|
|
`trustgraph-flow/trustgraph/embeddings/row_embeddings/embeddings.py`
|
||
|
|
`trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py`
|
||
|
|
|
||
|
|
#### 6. **Aprimoramento do Gateway de API**
|
||
|
|
Adicionar endpoint de embedding em lote
|
||
|
|
Suportar array de textos no corpo da requisição
|
||
|
|
|
||
|
|
Módulo: `trustgraph-flow/trustgraph/gateway/dispatch/embeddings.py`
|
||
|
|
|
||
|
|
#### 7. **Aprimoramento da Ferramenta de Linha de Comando (CLI)**
|
||
|
|
Adicionar suporte para múltiplos textos ou entrada de arquivo
|
||
|
|
Adicionar parâmetro de tamanho do lote
|
||
|
|
|
||
|
|
Módulo: `trustgraph-cli/trustgraph/cli/invoke_embeddings.py`
|
||
|
|
|
||
|
|
#### 8. **Aprimoramento do SDK Python**
|
||
|
|
Adicionar método `embeddings_batch()` para `FlowInstance`
|
||
|
|
Adicionar método `embeddings_batch()` para `SocketFlowInstance`
|
||
|
|
Suportar APIs de texto único e em lote para usuários do SDK
|
||
|
|
|
||
|
|
Módulos:
|
||
|
|
`trustgraph-base/trustgraph/api/flow.py`
|
||
|
|
`trustgraph-base/trustgraph/api/socket_client.py`
|
||
|
|
|
||
|
|
### Modelos de Dados
|
||
|
|
|
||
|
|
#### EmbeddingsRequest
|
||
|
|
|
||
|
|
```python
|
||
|
|
@dataclass
|
||
|
|
class EmbeddingsRequest:
|
||
|
|
texts: list[str] = field(default_factory=list)
|
||
|
|
```
|
||
|
|
|
||
|
|
Uso:
|
||
|
|
Texto único: `EmbeddingsRequest(texts=["hello world"])`
|
||
|
|
Lote: `EmbeddingsRequest(texts=["text1", "text2", "text3"])`
|
||
|
|
|
||
|
|
#### EmbeddingsResponse
|
||
|
|
|
||
|
|
```python
|
||
|
|
@dataclass
|
||
|
|
class EmbeddingsResponse:
|
||
|
|
error: Error | None = None
|
||
|
|
vectors: list[list[list[float]]] = field(default_factory=list)
|
||
|
|
```
|
||
|
|
|
||
|
|
Estrutura da resposta:
|
||
|
|
`vectors[i]` contém o conjunto de vetores para `texts[i]`
|
||
|
|
Cada conjunto de vetores é `list[list[float]]` (os modelos podem retornar vários vetores por texto)
|
||
|
|
Exemplo: 3 textos → `vectors` tem 3 entradas, cada uma contendo os embeddings desse texto
|
||
|
|
|
||
|
|
### APIs
|
||
|
|
|
||
|
|
#### EmbeddingsClient
|
||
|
|
|
||
|
|
```python
|
||
|
|
class EmbeddingsClient(RequestResponse):
|
||
|
|
async def embed(
|
||
|
|
self,
|
||
|
|
texts: list[str],
|
||
|
|
timeout: float = 300,
|
||
|
|
) -> list[list[list[float]]]:
|
||
|
|
"""
|
||
|
|
Embed one or more texts in a single request.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
texts: List of texts to embed
|
||
|
|
timeout: Timeout for the operation
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List of vector sets, one per input text
|
||
|
|
"""
|
||
|
|
resp = await self.request(
|
||
|
|
EmbeddingsRequest(texts=texts),
|
||
|
|
timeout=timeout
|
||
|
|
)
|
||
|
|
if resp.error:
|
||
|
|
raise RuntimeError(resp.error.message)
|
||
|
|
return resp.vectors
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Endpoint de Incorporação do Gateway de API
|
||
|
|
|
||
|
|
Endpoint atualizado que suporta incorporação única ou em lote:
|
||
|
|
|
||
|
|
```
|
||
|
|
POST /api/v1/embeddings
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"texts": ["text1", "text2", "text3"],
|
||
|
|
"flow_id": "default"
|
||
|
|
}
|
||
|
|
|
||
|
|
Response:
|
||
|
|
{
|
||
|
|
"vectors": [
|
||
|
|
[[0.1, 0.2, ...]],
|
||
|
|
[[0.3, 0.4, ...]],
|
||
|
|
[[0.5, 0.6, ...]]
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Detalhes de Implementação
|
||
|
|
|
||
|
|
#### Fase 1: Alterações no Esquema
|
||
|
|
|
||
|
|
**EmbeddingsRequest:**
|
||
|
|
```python
|
||
|
|
@dataclass
|
||
|
|
class EmbeddingsRequest:
|
||
|
|
texts: list[str] = field(default_factory=list)
|
||
|
|
```
|
||
|
|
|
||
|
|
**EmbeddingsResponse:**
|
||
|
|
```python
|
||
|
|
@dataclass
|
||
|
|
class EmbeddingsResponse:
|
||
|
|
error: Error | None = None
|
||
|
|
vectors: list[list[list[float]]] = field(default_factory=list)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Atualizado EmbeddingsService.on_request:**
|
||
|
|
```python
|
||
|
|
async def on_request(self, msg, consumer, flow):
|
||
|
|
request = msg.value()
|
||
|
|
id = msg.properties()["id"]
|
||
|
|
model = flow("model")
|
||
|
|
|
||
|
|
vectors = await self.on_embeddings(request.texts, model=model)
|
||
|
|
response = EmbeddingsResponse(error=None, vectors=vectors)
|
||
|
|
|
||
|
|
await flow("response").send(response, properties={"id": id})
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Fase 2: Atualização do Processador FastEmbed
|
||
|
|
|
||
|
|
**Atual (Ineficiente):**
|
||
|
|
```python
|
||
|
|
async def on_embeddings(self, text, model=None):
|
||
|
|
use_model = model or self.default_model
|
||
|
|
self._load_model(use_model)
|
||
|
|
vecs = self.embeddings.embed([text]) # Batch of 1
|
||
|
|
return [v.tolist() for v in vecs]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Atualizado:**
|
||
|
|
```python
|
||
|
|
async def on_embeddings(self, texts: list[str], model=None):
|
||
|
|
"""Embed texts - processes all texts in single model call"""
|
||
|
|
if not texts:
|
||
|
|
return []
|
||
|
|
|
||
|
|
use_model = model or self.default_model
|
||
|
|
self._load_model(use_model)
|
||
|
|
|
||
|
|
# FastEmbed handles the full batch efficiently
|
||
|
|
all_vecs = list(self.embeddings.embed(texts))
|
||
|
|
|
||
|
|
# Return list of vector sets, one per input text
|
||
|
|
return [[v.tolist()] for v in all_vecs]
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Fase 3: Atualização do Serviço de Incorporação de Grafos
|
||
|
|
|
||
|
|
**Atual (Serial):**
|
||
|
|
```python
|
||
|
|
async def on_message(self, msg, consumer, flow):
|
||
|
|
entities = []
|
||
|
|
for entity in v.entities:
|
||
|
|
vectors = await flow("embeddings-request").embed(text=entity.context)
|
||
|
|
entities.append(EntityEmbeddings(...))
|
||
|
|
```
|
||
|
|
|
||
|
|
**Atualizado (Lote):**
|
||
|
|
```python
|
||
|
|
async def on_message(self, msg, consumer, flow):
|
||
|
|
# Collect all contexts
|
||
|
|
contexts = [entity.context for entity in v.entities]
|
||
|
|
|
||
|
|
# Single batch embedding call
|
||
|
|
all_vectors = await flow("embeddings-request").embed(texts=contexts)
|
||
|
|
|
||
|
|
# Pair results with entities
|
||
|
|
entities = [
|
||
|
|
EntityEmbeddings(
|
||
|
|
entity=entity.entity,
|
||
|
|
vectors=vectors[0], # First vector from the set
|
||
|
|
chunk_id=entity.chunk_id,
|
||
|
|
)
|
||
|
|
for entity, vectors in zip(v.entities, all_vectors)
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Fase 4: Atualização do Serviço de Incorporação de Dados
|
||
|
|
|
||
|
|
**Atual (Serial):**
|
||
|
|
```python
|
||
|
|
for text, (index_name, index_value) in texts_to_embed.items():
|
||
|
|
vectors = await flow("embeddings-request").embed(text=text)
|
||
|
|
embeddings_list.append(RowIndexEmbedding(...))
|
||
|
|
```
|
||
|
|
|
||
|
|
**Atualizado (Lote):**
|
||
|
|
```python
|
||
|
|
# Collect texts and metadata
|
||
|
|
texts = list(texts_to_embed.keys())
|
||
|
|
metadata = list(texts_to_embed.values())
|
||
|
|
|
||
|
|
# Single batch embedding call
|
||
|
|
all_vectors = await flow("embeddings-request").embed(texts=texts)
|
||
|
|
|
||
|
|
# Pair results
|
||
|
|
embeddings_list = [
|
||
|
|
RowIndexEmbedding(
|
||
|
|
index_name=meta[0],
|
||
|
|
index_value=meta[1],
|
||
|
|
text=text,
|
||
|
|
vectors=vectors[0] # First vector from the set
|
||
|
|
)
|
||
|
|
for text, meta, vectors in zip(texts, metadata, all_vectors)
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Fase 5: Melhoria da Ferramenta de Linha de Comando (CLI)
|
||
|
|
|
||
|
|
**CLI Atualizado:**
|
||
|
|
```python
|
||
|
|
def main():
|
||
|
|
parser = argparse.ArgumentParser(...)
|
||
|
|
|
||
|
|
parser.add_argument(
|
||
|
|
'text',
|
||
|
|
nargs='*', # Zero or more texts
|
||
|
|
help='Text(s) to convert to embedding vectors',
|
||
|
|
)
|
||
|
|
|
||
|
|
parser.add_argument(
|
||
|
|
'-f', '--file',
|
||
|
|
help='File containing texts (one per line)',
|
||
|
|
)
|
||
|
|
|
||
|
|
parser.add_argument(
|
||
|
|
'--batch-size',
|
||
|
|
type=int,
|
||
|
|
default=32,
|
||
|
|
help='Batch size for processing (default: 32)',
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
Uso:
|
||
|
|
```bash
|
||
|
|
# Single text (existing)
|
||
|
|
tg-invoke-embeddings "hello world"
|
||
|
|
|
||
|
|
# Multiple texts
|
||
|
|
tg-invoke-embeddings "text one" "text two" "text three"
|
||
|
|
|
||
|
|
# From file
|
||
|
|
tg-invoke-embeddings -f texts.txt --batch-size 64
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Fase 6: Melhoria do SDK Python
|
||
|
|
|
||
|
|
**FlowInstance (cliente HTTP):**
|
||
|
|
|
||
|
|
```python
|
||
|
|
class FlowInstance:
|
||
|
|
def embeddings(self, texts: list[str]) -> list[list[list[float]]]:
|
||
|
|
"""
|
||
|
|
Get embeddings for one or more texts.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
texts: List of texts to embed
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List of vector sets, one per input text
|
||
|
|
"""
|
||
|
|
input = {"texts": texts}
|
||
|
|
return self.request("service/embeddings", input)["vectors"]
|
||
|
|
```
|
||
|
|
|
||
|
|
**SocketFlowInstance (cliente WebSocket):**
|
||
|
|
|
||
|
|
```python
|
||
|
|
class SocketFlowInstance:
|
||
|
|
def embeddings(self, texts: list[str], **kwargs: Any) -> list[list[list[float]]]:
|
||
|
|
"""
|
||
|
|
Get embeddings for one or more texts via WebSocket.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
texts: List of texts to embed
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List of vector sets, one per input text
|
||
|
|
"""
|
||
|
|
request = {"texts": texts}
|
||
|
|
response = self.client._send_request_sync(
|
||
|
|
"embeddings", self.flow_id, request, False
|
||
|
|
)
|
||
|
|
return response["vectors"]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Exemplos de uso do SDK:**
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Single text
|
||
|
|
vectors = flow.embeddings(["hello world"])
|
||
|
|
print(f"Dimensions: {len(vectors[0][0])}")
|
||
|
|
|
||
|
|
# Batch embedding
|
||
|
|
texts = ["text one", "text two", "text three"]
|
||
|
|
all_vectors = flow.embeddings(texts)
|
||
|
|
|
||
|
|
# Process results
|
||
|
|
for text, vecs in zip(texts, all_vectors):
|
||
|
|
print(f"{text}: {len(vecs[0])} dimensions")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Considerações de Segurança
|
||
|
|
|
||
|
|
**Limites de Tamanho da Requisição**: Impor o tamanho máximo do lote para evitar o esgotamento de recursos.
|
||
|
|
**Tratamento de Tempo Limite (Timeout)**: Ajustar os tempos limite de acordo com o tamanho do lote.
|
||
|
|
**Limites de Memória**: Monitorar o uso de memória para grandes lotes.
|
||
|
|
**Validação de Entrada**: Validar todos os textos no lote antes do processamento.
|
||
|
|
|
||
|
|
## Considerações de Desempenho
|
||
|
|
|
||
|
|
### Melhorias Esperadas
|
||
|
|
|
||
|
|
**Taxa de Transferência (Throughput):**
|
||
|
|
Texto único: ~10-50 textos/segundo (dependendo do modelo)
|
||
|
|
Lote (tamanho 32): ~200-500 textos/segundo (melhora de 5 a 10 vezes)
|
||
|
|
|
||
|
|
**Latência por Texto:**
|
||
|
|
Texto único: 50-200ms por texto
|
||
|
|
Lote (tamanho 32): 5-20ms por texto (média)
|
||
|
|
|
||
|
|
**Melhorias Específicas do Serviço:**
|
||
|
|
|
||
|
|
| Serviço | Atual | Em Lote | Melhoria |
|
||
|
|
|---------|---------|---------|-------------|
|
||
|
|
| Incorporações de Grafos (50 entidades) | 5-10s | 0,5-1s | 5-10x |
|
||
|
|
| Incorporações de Linhas (100 textos) | 10-20s | 1-2s | 5-10x |
|
||
|
|
| Ingestão de Documentos (1000 fragmentos) | 100-200s | 10-30s | 5-10x |
|
||
|
|
|
||
|
|
### Parâmetros de Configuração
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Recommended defaults
|
||
|
|
DEFAULT_BATCH_SIZE = 32
|
||
|
|
MAX_BATCH_SIZE = 128
|
||
|
|
BATCH_TIMEOUT_MULTIPLIER = 2.0
|
||
|
|
```
|
||
|
|
|
||
|
|
## Estratégia de Testes
|
||
|
|
|
||
|
|
### Testes Unitários
|
||
|
|
Incorporação de texto única (compatibilidade com versões anteriores)
|
||
|
|
Tratamento de lotes vazios
|
||
|
|
Imposição do tamanho máximo do lote
|
||
|
|
Tratamento de erros para falhas parciais do lote
|
||
|
|
|
||
|
|
### Testes de Integração
|
||
|
|
Incorporação de lote de ponta a ponta através do Pulsar
|
||
|
|
Processamento de lote do serviço de incorporação de grafos
|
||
|
|
Processamento de lote do serviço de incorporação de linhas
|
||
|
|
Endpoint de lote do gateway de API
|
||
|
|
|
||
|
|
### Testes de Desempenho
|
||
|
|
Comparação de benchmark de taxa de transferência única versus lote
|
||
|
|
Uso de memória sob vários tamanhos de lote
|
||
|
|
Análise da distribuição de latência
|
||
|
|
|
||
|
|
## Plano de Migração
|
||
|
|
|
||
|
|
Esta é uma versão que introduz alterações significativas. Todas as fases são implementadas juntas.
|
||
|
|
|
||
|
|
### Fase 1: Alterações no Esquema
|
||
|
|
Substituir `text: str` por `texts: list[str]` em EmbeddingsRequest
|
||
|
|
Alterar o tipo de `vectors` para `list[list[list[float]]]` em EmbeddingsResponse
|
||
|
|
|
||
|
|
### Fase 2: Atualizações do Processador
|
||
|
|
Atualizar a assinatura de `on_embeddings` nos processadores FastEmbed e Ollama
|
||
|
|
Processar o lote completo em uma única chamada de modelo
|
||
|
|
|
||
|
|
### Fase 3: Atualizações do Cliente
|
||
|
|
Atualizar `EmbeddingsClient.embed()` para aceitar `texts: list[str]`
|
||
|
|
|
||
|
|
### Fase 4: Atualizações do Chamador
|
||
|
|
Atualizar graph_embeddings para incorporar contextos de entidades em lote
|
||
|
|
Atualizar row_embeddings para incorporar textos de índice em lote
|
||
|
|
Atualizar document_embeddings para usar o novo esquema
|
||
|
|
Atualizar a ferramenta de linha de comando (CLI)
|
||
|
|
|
||
|
|
### Fase 5: Gateway de API
|
||
|
|
Atualizar o endpoint de incorporação para o novo esquema
|
||
|
|
|
||
|
|
### Fase 6: SDK Python
|
||
|
|
Atualizar a assinatura de `FlowInstance.embeddings()`
|
||
|
|
Atualizar a assinatura de `SocketFlowInstance.embeddings()`
|
||
|
|
|
||
|
|
## Perguntas Abertas
|
||
|
|
|
||
|
|
**Incorporação de Lotes Grandes em Streaming**: Devemos suportar o streaming de resultados para lotes muito grandes (>100 textos)?
|
||
|
|
**Limites Específicos do Provedor**: Como devemos lidar com provedores com tamanhos máximos de lote diferentes?
|
||
|
|
**Tratamento de Falhas Parciais**: Se um texto em um lote falhar, devemos falhar o lote inteiro ou retornar resultados parciais?
|
||
|
|
**Incorporação em Lote de Documentos**: Devemos incorporar em lote em várias mensagens de Chunk ou manter o processamento por mensagem?
|
||
|
|
|
||
|
|
## Referências
|
||
|
|
|
||
|
|
[Documentação do FastEmbed](https://github.com/qdrant/fastembed)
|
||
|
|
[API de Incorporação do Ollama](https://github.com/ollama/ollama)
|
||
|
|
[Implementação do Serviço de Incorporação](trustgraph-base/trustgraph/base/embeddings_service.py)
|
||
|
|
[Otimização de Desempenho do GraphRAG](graphrag-performance-optimization.md)
|