Feat: TrustGraph i18n & Documentation Translation Updates (#781)

Native CLI i18n: The TrustGraph CLI has built-in translation support
that dynamically loads language strings. You can test and use
different languages by simply passing the --lang flag (e.g., --lang
es for Spanish, --lang ru for Russian) or by configuring your
environment's LANG variable.

Automated Docs Translations: This PR introduces autonomously
translated Markdown documentation into several target languages,
including Spanish, Swahili, Portuguese, Turkish, Hindi, Hebrew,
Arabic, Simplified Chinese, and Russian.
This commit is contained in:
Alex Jenkins 2026-04-14 07:07:58 -04:00 committed by GitHub
parent 19f73e4cdc
commit f95fd4f052
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
560 changed files with 236300 additions and 99 deletions

View file

@ -0,0 +1,675 @@
---
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)