mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 08:26:21 +02:00
676 lines
30 KiB
Markdown
676 lines
30 KiB
Markdown
|
|
---
|
|||
|
|
layout: default
|
|||
|
|
title: "Техническая спецификация пакетной обработки эмбеддингов"
|
|||
|
|
parent: "Russian (Beta)"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Техническая спецификация пакетной обработки эмбеддингов
|
|||
|
|
|
|||
|
|
> **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.
|
|||
|
|
|
|||
|
|
## Обзор
|
|||
|
|
|
|||
|
|
Эта спецификация описывает оптимизации для сервиса эмбеддингов, предназначенные для поддержки пакетной обработки нескольких текстов в одном запросе. Текущая реализация обрабатывает один текст за раз, что не позволяет использовать значительные преимущества, которые предоставляют модели эмбеддингов при обработке пакетов.
|
|||
|
|
|
|||
|
|
1. **Неэффективность обработки одного текста**: Текущая реализация оборачивает отдельные тексты в список, что не позволяет в полной мере использовать возможности пакетной обработки FastEmbed.
|
|||
|
|
2. **Накладные расходы на запрос для каждого текста**: Для каждого текста требуется отдельная передача сообщения Pulsar.
|
|||
|
|
3. **Неэффективность вывода модели**: Модели эмбеддингов имеют фиксированные накладные расходы на пакет; небольшие пакеты приводят к неэффективному использованию ресурсов GPU/CPU.
|
|||
|
|
4. **Последовательная обработка в вызывающих сервисах**: Основные сервисы перебирают элементы и вызывают эмбеддинги по одному.
|
|||
|
|
|
|||
|
|
## Цели
|
|||
|
|
|
|||
|
|
**Поддержка пакетного API**: Обеспечить возможность обработки нескольких текстов в одном запросе.
|
|||
|
|
**Обратная совместимость**: Сохранить поддержку запросов для обработки одного текста.
|
|||
|
|
**Значительное повышение производительности**: Стремиться к увеличению производительности в 5-10 раз для массовых операций.
|
|||
|
|
**Снижение задержки на текст**: Уменьшить среднюю задержку при создании эмбеддингов для нескольких текстов.
|
|||
|
|
**Эффективность использования памяти**: Обрабатывать пакеты без чрезмерного потребления памяти.
|
|||
|
|
**Независимость от провайдера**: Поддерживать пакетную обработку для FastEmbed, Ollama и других провайдеров.
|
|||
|
|
**Миграция вызывающих сервисов**: Обновить все сервисы, вызывающие эмбеддинги, для использования пакетного API, где это целесообразно.
|
|||
|
|
|
|||
|
|
## Контекст
|
|||
|
|
|
|||
|
|
### Текущая реализация - Сервис эмбеддингов
|
|||
|
|
|
|||
|
|
Реализация эмбеддингов в `trustgraph-flow/trustgraph/embeddings/fastembed/processor.py` демонстрирует значительную неэффективность:
|
|||
|
|
|
|||
|
|
```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]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Проблемы:**
|
|||
|
|
|
|||
|
|
1. **Размер пакета 1**: Метод `embed()` FastEmbed оптимизирован для пакетной обработки, но мы всегда вызываем его с `[text]` - пакетом размера 1.
|
|||
|
|
|
|||
|
|
2. **Накладные расходы на каждый запрос**: Каждый запрос на получение эмбеддингов влечет за собой:
|
|||
|
|
Сериализацию/десериализацию сообщения Pulsar.
|
|||
|
|
Задержку сетевого обмена.
|
|||
|
|
Накладные расходы на запуск инференса модели.
|
|||
|
|
Накладные расходы на асинхронное планирование Python.
|
|||
|
|
|
|||
|
|
3. **Ограничение схемы**: Схема `EmbeddingsRequest` поддерживает только один текстовый фрагмент:
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class EmbeddingsRequest:
|
|||
|
|
text: str = "" # Single text only
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Текущие вызывающие стороны - последовательная обработка
|
|||
|
|
|
|||
|
|
#### 1. API-шлюз
|
|||
|
|
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/gateway/dispatch/embeddings.py`
|
|||
|
|
|
|||
|
|
Шлюз принимает запросы на однострочное создание векторных представлений через HTTP/WebSocket и перенаправляет их в сервис создания векторных представлений. В настоящее время нет пакетного интерфейса.
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class EmbeddingsRequestor(ServiceRequestor):
|
|||
|
|
# Handles single EmbeddingsRequest -> EmbeddingsResponse
|
|||
|
|
request_schema=EmbeddingsRequest, # Single text only
|
|||
|
|
response_schema=EmbeddingsResponse,
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Влияние:** Внешние клиенты (веб-приложения, скрипты) должны выполнять N HTTP-запросов для встраивания N текстов.
|
|||
|
|
|
|||
|
|
#### 2. Сервис встраивания документов
|
|||
|
|
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py`
|
|||
|
|
|
|||
|
|
Обрабатывает фрагменты документов по одному.
|
|||
|
|
|
|||
|
|
```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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Влияние:** Каждый фрагмент документа требует отдельного вызова для создания эмбеддинга. Документ, состоящий из 100 фрагментов, = 100 запросов на создание эмбеддингов.
|
|||
|
|
|
|||
|
|
#### 3. Сервис создания графовых эмбеддингов
|
|||
|
|
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/embeddings/graph_embeddings/embeddings.py`
|
|||
|
|
|
|||
|
|
Выполняет итерации по сущностям и создает эмбеддинг для каждой из них последовательно:
|
|||
|
|
|
|||
|
|
```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,
|
|||
|
|
))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Влияние:** Сообщение, содержащее 50 сущностей, приводит к 50 последовательным запросам на создание векторных представлений. Это серьезное препятствие при построении графа знаний.
|
|||
|
|
|
|||
|
|
#### 4. Сервис создания векторных представлений строк
|
|||
|
|
|
|||
|
|
**Файл:** `trustgraph-flow/trustgraph/embeddings/row_embeddings/embeddings.py`
|
|||
|
|
|
|||
|
|
Выполняет итерации по уникальным текстам и создает векторное представление для каждого из них последовательно:
|
|||
|
|
|
|||
|
|
```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
|
|||
|
|
))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Влияние:** Обработка таблицы с 100 уникальными индексированными значениями = 100 последовательных запросов на создание векторных представлений.
|
|||
|
|
|
|||
|
|
#### 5. EmbeddingsClient (Базовый клиент)
|
|||
|
|
|
|||
|
|
**Файл:** `trustgraph-base/trustgraph/base/embeddings_client.py`
|
|||
|
|
|
|||
|
|
Клиент, используемый всеми процессорами потоков, поддерживает только создание векторных представлений для одного текстового фрагмента:
|
|||
|
|
|
|||
|
|
```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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Влияние:** Все клиенты, использующие этот компонент, ограничены операциями с одним текстовым фрагментом.
|
|||
|
|
|
|||
|
|
#### 6. Инструменты командной строки
|
|||
|
|
|
|||
|
|
**Файл:** `trustgraph-cli/trustgraph/cli/invoke_embeddings.py`
|
|||
|
|
|
|||
|
|
Инструмент командной строки принимает один текстовый аргумент:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def query(url, flow_id, text, token=None):
|
|||
|
|
result = flow.embeddings(text=text) # Single text
|
|||
|
|
vectors = result.get("vectors", [])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Влияние:** Пользователи не могут выполнять пакетную вставку из командной строки. Обработка файла текстов требует N вызовов.
|
|||
|
|
|
|||
|
|
#### 7. Python SDK
|
|||
|
|
|
|||
|
|
Python SDK предоставляет два клиентских класса для взаимодействия со службами TrustGraph. Оба поддерживают только вставку одного текста.
|
|||
|
|
|
|||
|
|
**Файл:** `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"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Файл:** `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
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Влияние:** Разработчикам Python, использующим SDK, необходимо перебирать тексты и выполнять N отдельных API-запросов. Поддержка пакетной обработки векторов для пользователей SDK отсутствует.
|
|||
|
|
|
|||
|
|
### Влияние на производительность
|
|||
|
|
|
|||
|
|
Для типичного извлечения данных (1000 текстовых фрагментов):
|
|||
|
|
**Текущая ситуация:** 1000 отдельных запросов, 1000 вызовов модели для получения векторов.
|
|||
|
|
**Пакетная обработка (batch_size=32):** 32 запроса, 32 вызова модели для получения векторов (снижение на 96,8%).
|
|||
|
|
|
|||
|
|
Для получения векторов графа (сообщение с 50 сущностями):
|
|||
|
|
**Текущая ситуация:** 50 последовательных вызовов `await`, ~5-10 секунд.
|
|||
|
|
**Пакетная обработка:** 1-2 пакетных вызова, ~0,5-1 секунда (улучшение в 5-10 раз).
|
|||
|
|
|
|||
|
|
Библиотеки FastEmbed и аналогичные достигают почти линейного увеличения производительности при увеличении размера пакета до пределов аппаратного обеспечения (обычно 32-128 текстов на пакет).
|
|||
|
|
|
|||
|
|
## Техническое проектирование
|
|||
|
|
|
|||
|
|
### Архитектура
|
|||
|
|
|
|||
|
|
Оптимизация пакетной обработки векторов требует изменений в следующих компонентах:
|
|||
|
|
|
|||
|
|
#### 1. **Улучшение схемы данных**
|
|||
|
|
Расширить `EmbeddingsRequest` для поддержки нескольких текстов.
|
|||
|
|
Расширить `EmbeddingsResponse` для возврата нескольких наборов векторов.
|
|||
|
|
Сохранить обратную совместимость с запросами для одного текста.
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-base/trustgraph/schema/services/llm.py`
|
|||
|
|
|
|||
|
|
#### 2. **Улучшение базового сервиса**
|
|||
|
|
Обновить `EmbeddingsService` для обработки пакетных запросов.
|
|||
|
|
Добавить конфигурацию размера пакета.
|
|||
|
|
Реализовать обработку запросов с учетом пакетной обработки.
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-base/trustgraph/base/embeddings_service.py`
|
|||
|
|
|
|||
|
|
#### 3. **Обновления для процессоров провайдеров**
|
|||
|
|
Обновить процессор FastEmbed для передачи полного пакета в `embed()`.
|
|||
|
|
Обновить процессор Ollama для обработки пакетов (если поддерживается).
|
|||
|
|
Добавить последовательную обработку в качестве запасного варианта для провайдеров, не поддерживающих пакетную обработку.
|
|||
|
|
|
|||
|
|
Модули:
|
|||
|
|
`trustgraph-flow/trustgraph/embeddings/fastembed/processor.py`
|
|||
|
|
`trustgraph-flow/trustgraph/embeddings/ollama/processor.py`
|
|||
|
|
|
|||
|
|
#### 4. **Улучшение клиентской библиотеки**
|
|||
|
|
Добавить метод пакетной обработки векторов в `EmbeddingsClient`.
|
|||
|
|
Поддерживать как одиночные, так и пакетные API.
|
|||
|
|
Добавить автоматическую пакетную обработку для больших входных данных.
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-base/trustgraph/base/embeddings_client.py`
|
|||
|
|
|
|||
|
|
#### 5. **Обновления для вызывающего кода - процессоров потоков**
|
|||
|
|
Обновить `graph_embeddings` для пакетной обработки контекстов сущностей.
|
|||
|
|
Обновить `row_embeddings` для пакетной обработки текстов для индексации.
|
|||
|
|
Обновить `document_embeddings`, если пакетная обработка сообщений возможна.
|
|||
|
|
|
|||
|
|
Модули:
|
|||
|
|
`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. **Улучшение API-шлюза**
|
|||
|
|
Добавить конечную точку для пакетной обработки векторов.
|
|||
|
|
Поддерживать массив текстов в теле запроса.
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-flow/trustgraph/gateway/dispatch/embeddings.py`
|
|||
|
|
|
|||
|
|
#### 7. **Улучшение инструмента командной строки (CLI)**
|
|||
|
|
Добавить поддержку нескольких текстов или ввода из файла.
|
|||
|
|
Добавить параметр размера пакета.
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-cli/trustgraph/cli/invoke_embeddings.py`
|
|||
|
|
|
|||
|
|
#### 8. **Улучшение Python SDK**
|
|||
|
|
Добавить метод `embeddings_batch()` в `FlowInstance`.
|
|||
|
|
Добавить метод `embeddings_batch()` в `SocketFlowInstance`.
|
|||
|
|
Поддерживать как одиночные, так и пакетные API для пользователей SDK.
|
|||
|
|
|
|||
|
|
Модули:
|
|||
|
|
`trustgraph-base/trustgraph/api/flow.py`
|
|||
|
|
`trustgraph-base/trustgraph/api/socket_client.py`
|
|||
|
|
|
|||
|
|
### Модели данных
|
|||
|
|
|
|||
|
|
#### EmbeddingsRequest
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class EmbeddingsRequest:
|
|||
|
|
texts: list[str] = field(default_factory=list)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Использование:
|
|||
|
|
Одиночный текст: `EmbeddingsRequest(texts=["hello world"])`
|
|||
|
|
Пакетный режим: `EmbeddingsRequest(texts=["text1", "text2", "text3"])`
|
|||
|
|
|
|||
|
|
#### EmbeddingsResponse
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class EmbeddingsResponse:
|
|||
|
|
error: Error | None = None
|
|||
|
|
vectors: list[list[list[float]]] = field(default_factory=list)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Структура ответа:
|
|||
|
|
`vectors[i]` содержит набор векторов для `texts[i]`
|
|||
|
|
Каждый набор векторов имеет размер `list[list[float]]` (модели могут возвращать несколько векторов для одного текста)
|
|||
|
|
Пример: 3 текста → `vectors` имеет 3 записи, каждая из которых содержит векторные представления этого текста
|
|||
|
|
|
|||
|
|
### API
|
|||
|
|
|
|||
|
|
#### 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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Конечная точка API Gateway для встраиваемых объектов
|
|||
|
|
|
|||
|
|
Обновленная конечная точка, поддерживающая однократную или пакетную генерацию встраиваемых объектов:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
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, ...]]
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Детали реализации
|
|||
|
|
|
|||
|
|
#### Этап 1: Изменения схемы
|
|||
|
|
|
|||
|
|
**EmbeddingsRequest:**
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class EmbeddingsRequest:
|
|||
|
|
texts: list[str] = field(default_factory=list)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ответ Embeddings:**
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class EmbeddingsResponse:
|
|||
|
|
error: Error | None = None
|
|||
|
|
vectors: list[list[list[float]]] = field(default_factory=list)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновлен класс 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})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Фаза 2: Обновление процессора FastEmbed
|
|||
|
|
|
|||
|
|
**Текущая (неэффективная):**
|
|||
|
|
```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]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновлено:**
|
|||
|
|
```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]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Фаза 3: Обновление сервиса графовых вложений
|
|||
|
|
|
|||
|
|
**Текущая (последовательная):**
|
|||
|
|
```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(...))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновлено (пакетно):**
|
|||
|
|
```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)
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Фаза 4: Обновление сервиса встраивания данных.
|
|||
|
|
|
|||
|
|
**Текущая (последовательная):**
|
|||
|
|
```python
|
|||
|
|
for text, (index_name, index_value) in texts_to_embed.items():
|
|||
|
|
vectors = await flow("embeddings-request").embed(text=text)
|
|||
|
|
embeddings_list.append(RowIndexEmbedding(...))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Обновлено (пакетно):**
|
|||
|
|
```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)
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Фаза 5: Улучшение инструмента командной строки (CLI).
|
|||
|
|
|
|||
|
|
**Обновленный CLI:**
|
|||
|
|
```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)',
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Использование:
|
|||
|
|
```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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Фаза 6: Улучшение SDK для Python
|
|||
|
|
|
|||
|
|
**FlowInstance (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 (клиент 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"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Примеры использования 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")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Соображения безопасности
|
|||
|
|
|
|||
|
|
**Ограничения на размер запроса**: Установите максимальный размер пакета для предотвращения исчерпания ресурсов.
|
|||
|
|
**Обработка таймаутов**: Адаптируйте таймауты в соответствии с размером пакета.
|
|||
|
|
**Ограничения памяти**: Отслеживайте использование памяти для больших пакетов.
|
|||
|
|
**Проверка входных данных**: Проверяйте все тексты в пакете перед обработкой.
|
|||
|
|
|
|||
|
|
## Соображения производительности
|
|||
|
|
|
|||
|
|
### Ожидаемые улучшения
|
|||
|
|
|
|||
|
|
**Производительность:**
|
|||
|
|
Для одного текста: ~10-50 текстов/секунду (в зависимости от модели).
|
|||
|
|
Для пакета (размер 32): ~200-500 текстов/секунду (улучшение в 5-10 раз).
|
|||
|
|
|
|||
|
|
**Задержка на текст:**
|
|||
|
|
Для одного текста: 50-200 мс на текст.
|
|||
|
|
Для пакета (размер 32): 5-20 мс на текст (в среднем).
|
|||
|
|
|
|||
|
|
**Улучшения для конкретных сервисов:**
|
|||
|
|
|
|||
|
|
| Сервис | Текущее значение | Пакетный режим | Улучшение |
|
|||
|
|
|---------|---------|---------|-------------|
|
|||
|
|
| Векторные представления графов (50 сущностей) | 5-10 секунд | 0.5-1 секунда | 5-10x |
|
|||
|
|
| Векторные представления строк (100 текстов) | 10-20 секунд | 1-2 секунды | 5-10x |
|
|||
|
|
| Импорт документов (1000 фрагментов) | 100-200 секунд | 10-30 секунд | 5-10x |
|
|||
|
|
|
|||
|
|
### Параметры конфигурации
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Recommended defaults
|
|||
|
|
DEFAULT_BATCH_SIZE = 32
|
|||
|
|
MAX_BATCH_SIZE = 128
|
|||
|
|
BATCH_TIMEOUT_MULTIPLIER = 2.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Стратегия тестирования
|
|||
|
|
|
|||
|
|
### Модульное тестирование
|
|||
|
|
Обработка однострочных вложений (обратная совместимость)
|
|||
|
|
Обработка пустых пакетов
|
|||
|
|
Применение максимального размера пакета
|
|||
|
|
Обработка ошибок при частичных сбоях пакета
|
|||
|
|
|
|||
|
|
### Интеграционное тестирование
|
|||
|
|
Полная обработка пакетов через Pulsar
|
|||
|
|
Обработка пакетов сервисом графовых вложений
|
|||
|
|
Обработка пакетов сервисом строковых вложений
|
|||
|
|
API-шлюз для пакетных операций
|
|||
|
|
|
|||
|
|
### Тестирование производительности
|
|||
|
|
Сравнение производительности при обработке отдельных элементов и пакетов
|
|||
|
|
Использование памяти при различных размерах пакетов
|
|||
|
|
Анализ распределения задержек
|
|||
|
|
|
|||
|
|
## План миграции
|
|||
|
|
|
|||
|
|
Это версия с критическими изменениями. Все этапы реализованы одновременно.
|
|||
|
|
|
|||
|
|
### Этап 1: Изменения схемы
|
|||
|
|
Заменить `text: str` на `texts: list[str]` в EmbeddingsRequest
|
|||
|
|
Изменить тип `vectors` на `list[list[list[float]]]` в EmbeddingsResponse
|
|||
|
|
|
|||
|
|
### Этап 2: Обновление процессоров
|
|||
|
|
Обновить сигнатуру `on_embeddings` в процессорах FastEmbed и Ollama
|
|||
|
|
Обрабатывать полный пакет за один вызов модели
|
|||
|
|
|
|||
|
|
### Этап 3: Обновление клиентской части
|
|||
|
|
Обновить `EmbeddingsClient.embed()` для приема `texts: list[str]`
|
|||
|
|
|
|||
|
|
### Этап 4: Обновление вызывающего кода
|
|||
|
|
Обновить graph_embeddings для пакетной обработки контекстов сущностей
|
|||
|
|
Обновить row_embeddings для пакетной обработки текстов индекса
|
|||
|
|
Обновить document_embeddings для использования новой схемы
|
|||
|
|
Обновить инструмент командной строки
|
|||
|
|
|
|||
|
|
### Этап 5: API-шлюз
|
|||
|
|
Обновить конечную точку для вложений в соответствии с новой схемой
|
|||
|
|
|
|||
|
|
### Этап 6: Python SDK
|
|||
|
|
Обновить сигнатуру `FlowInstance.embeddings()`
|
|||
|
|
Обновить сигнатуру `SocketFlowInstance.embeddings()`
|
|||
|
|
|
|||
|
|
## Открытые вопросы
|
|||
|
|
|
|||
|
|
**Потоковая передача больших пакетов**: Следует ли нам поддерживать потоковую передачу результатов для очень больших пакетов (>100 текстов)?
|
|||
|
|
**Ограничения, специфичные для поставщиков**: Как нам обрабатывать поставщиков с разными максимальными размерами пакетов?
|
|||
|
|
**Обработка частичных сбоев**: Если один текст в пакете завершается сбоем, следует ли нам завершать весь пакет или возвращать частичные результаты?
|
|||
|
|
**Пакетная обработка документов**: Следует ли нам выполнять пакетную обработку по нескольким сообщениям Chunk или сохранять обработку для каждого сообщения?
|
|||
|
|
|
|||
|
|
## Ссылки
|
|||
|
|
|
|||
|
|
[Документация FastEmbed](https://github.com/qdrant/fastembed)
|
|||
|
|
[API вложений Ollama](https://github.com/ollama/ollama)
|
|||
|
|
[Реализация сервиса вложений](trustgraph-base/trustgraph/base/embeddings_service.py)
|
|||
|
|
[Оптимизация производительности GraphRAG](graphrag-performance-optimization.md)
|