trustgraph/docs/tech-specs/structured-data-2.ru.md
Alex Jenkins 8954fa3ad7 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.
2026-04-14 12:08:32 +01:00

36 KiB
Raw Blame History

layout title parent
default Техническая спецификация структурированных данных (часть 2) Russian (Beta)

Техническая спецификация структурированных данных (часть 2)

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.

Обзор

Эта спецификация рассматривает проблемы и недостатки, выявленные в процессе первоначальной реализации интеграции структурированных данных TrustGraph, как описано в structured-data.md.

Описание проблем

1. Несоответствие в наименованиях: "Объект" против "Строка"

В текущей реализации используется терминология "объект" во всем коде (например, ExtractedObject, извлечение объектов, векторные представления объектов). Это наименование слишком общее и вызывает путаницу:

Термин "объект" является перегруженным в программном обеспечении (объекты Python, объекты JSON и т.д.) Данные, которые обрабатываются, по сути являются табличными - строки в таблицах с определенными схемами. "Строка" более точно описывает модель данных и соответствует терминологии баз данных.

Это несоответствие проявляется в названиях модулей, названиях классов, типах сообщений и документации.

2. Ограничения запросов к хранилищу строк

Текущая реализация хранилища строк имеет значительные ограничения по запросам:

Несоответствие с естественным языком: Запросы испытывают трудности с вариациями реальных данных. Например: Сложно найти базу данных улиц, содержащую "CHESTNUT ST", при запросе информации о "Chestnut Street". Аббревиатуры, различия в регистре и вариации форматирования нарушают запросы точного соответствия. Пользователи ожидают семантического понимания, но хранилище обеспечивает только буквальное сопоставление.

Проблемы с эволюцией схемы: Изменение схем вызывает проблемы: Существующие данные могут не соответствовать обновленным схемам. Изменения структуры таблицы могут нарушить запросы и целостность данных. Отсутствует четкий путь миграции для обновлений схемы.

3. Требуются векторные представления строк

В связи с проблемой 2, системе требуются векторные представления данных строк для обеспечения:

Семантического поиска по структурированным данным (поиск "Chestnut Street", когда данные содержат "CHESTNUT ST"). Сопоставления по сходству для нечетких запросов. Гибридного поиска, сочетающего структурированные фильтры с семантическим сходством. Лучшей поддержки запросов на естественном языке.

Сервис создания векторных представлений был определен, но не реализован.

4. Неполная загрузка данных строк

Конвейер загрузки структурированных данных не полностью функционирует:

Существуют диагностические подсказки для классификации форматов входных данных (CSV, JSON и т.д.). Сервис загрузки, использующий эти подсказки, не подключен к системе. Отсутствует сквозной путь для загрузки предварительно структурированных данных в хранилище строк.

Цели

Гибкость схемы: Обеспечить эволюцию схемы без нарушения существующих данных или необходимости миграций. Согласованное наименование: Стандартизировать использование термина "строка" во всем коде. Семантическая запросаемость: Поддержка нечеткого/семантического сопоставления с помощью векторных представлений строк. Полный конвейер загрузки: Предоставить сквозной путь для загрузки структурированных данных.

Технический дизайн

Унифицированная схема хранения строк

В предыдущей реализации для каждой схемы создавалась отдельная таблица Cassandra. Это вызывало проблемы при эволюции схем, поскольку изменения структуры таблицы требовали миграций.

В новой разработке используется единая унифицированная таблица для всех данных строк:

CREATE TABLE rows (
    collection text,
    schema_name text,
    index_name text,
    index_value frozen<list<text>>,
    data map<text, text>,
    source text,
    PRIMARY KEY ((collection, schema_name, index_name), index_value)
)

Описание столбцов

Столбец Тип Описание
collection text Идентификатор сбора/импорта данных (из метаданных)
schema_name text Название схемы, которой соответствует эта строка
index_name text Название индексируемого поля (полей), объединенное запятыми для составных индексов
index_value frozen<list<text>> Значение(я) индекса в виде списка
data map<text, text> Данные строки в виде пар ключ-значение
source text Необязательный URI, ссылающийся на информацию об источнике в графе знаний. Пустая строка или NULL указывает на отсутствие источника.

Обработка индексов

Каждая строка хранится несколько раз - один раз для каждого индексируемого поля, определенного в схеме. Первичные ключи рассматриваются как индекс без специального маркера, что обеспечивает гибкость в будущем.

Пример индекса для одного поля: Схема определяет email как индексируемое index_name = "email" index_value = ['foo@bar.com']

Пример составного индекса: Схема определяет составной индекс для region и status index_name = "region,status" (названия полей отсортированы и объединены запятыми) index_value = ['US', 'active'] (значения в том же порядке, что и названия полей)

Пример первичного ключа: Схема определяет customer_id как первичный ключ index_name = "customer_id" index_value = ['CUST001']

Шаблоны запросов

Все запросы следуют одной и той же схеме, независимо от того, какой индекс используется:

SELECT * FROM rows
WHERE collection = 'import_2024'
  AND schema_name = 'customers'
  AND index_name = 'email'
  AND index_value = ['foo@bar.com']

Компромиссы в проектировании

Преимущества: Изменения схемы не требуют изменений структуры таблицы Данные строк не видны Cassandra - добавление/удаление полей прозрачно Единый шаблон запросов для всех методов доступа Отсутствуют вторичные индексы Cassandra (которые могут быть медленными при больших масштабах) Использование нативных типов Cassandra (map, frozen<list>)

Компромиссы: Увеличение количества операций записи: каждая вставка строки = N вставок (по одной на каждое индексированное поле) Дополнительные затраты памяти из-за дублирования данных строк Информация о типах хранится в конфигурации схемы, преобразование происходит на уровне приложения

Модель согласованности

В проекте приняты определенные упрощения:

  1. Отсутствие обновлений строк: Система работает только на добавление данных. Это устраняет проблемы согласованности, связанные с обновлением нескольких копий одной и той же строки.

  2. Толерантность к изменениям схемы: При изменении схем (например, при добавлении/удалении индексов) существующие строки сохраняют свою первоначальную индексацию. Старые строки не будут доступны через новые индексы. Пользователи могут удалить и пересоздать схему для обеспечения согласованности, если это необходимо.

Отслеживание и удаление разделов

Проблема

При использовании ключа раздела (collection, schema_name, index_name), для эффективного удаления необходимо знать все ключи разделов, которые нужно удалить. Удаление только по collection или collection + schema_name требует знания всех значений index_name, которые содержат данные.

Таблица отслеживания разделов

Вторичная таблица поиска отслеживает, какие разделы существуют:

CREATE TABLE row_partitions (
    collection text,
    schema_name text,
    index_name text,
    PRIMARY KEY ((collection), schema_name, index_name)
)

Это обеспечивает эффективное обнаружение разделов для операций удаления.

Поведение модуля записи строк

Модуль записи строк поддерживает кэш в памяти, содержащий зарегистрированные пары (collection, schema_name). При обработке строки:

  1. Проверьте, есть ли (collection, schema_name) в кэше.
  2. Если ⟦CODE_0⟧ не в кэше (первая строка для этой пары): Найдите конфигурацию схемы, чтобы получить все имена индексов. Вставьте записи в row_partitions для каждого (collection, schema_name, index_name). Добавьте пару в кэш.
  3. Перейдите к записи данных строки.

Модуль записи строк также отслеживает события изменения конфигурации схемы. При изменении схемы соответствующие записи кэша очищаются, чтобы следующая строка вызвала повторную регистрацию с обновленными именами индексов.

Этот подход обеспечивает: Записи в таблицу поиска происходят только один раз для каждой пары (collection, schema_name), а не для каждой строки. Таблица поиска отражает индексы, которые были активны во время записи данных. Изменения схемы во время импорта обрабатываются правильно.

Операции удаления

Удаление коллекции:

-- 1. Discover all partitions
SELECT schema_name, index_name FROM row_partitions WHERE collection = 'X';

-- 2. Delete each partition from rows table
DELETE FROM rows WHERE collection = 'X' AND schema_name = '...' AND index_name = '...';
-- (repeat for each discovered partition)

-- 3. Clean up the lookup table
DELETE FROM row_partitions WHERE collection = 'X';

Удалить коллекцию и схему:

-- 1. Discover partitions for this schema
SELECT index_name FROM row_partitions WHERE collection = 'X' AND schema_name = 'Y';

-- 2. Delete each partition from rows table
DELETE FROM rows WHERE collection = 'X' AND schema_name = 'Y' AND index_name = '...';
-- (repeat for each discovered partition)

-- 3. Clean up the lookup table entries
DELETE FROM row_partitions WHERE collection = 'X' AND schema_name = 'Y';

Встраивания строк

Встраивания строк обеспечивают семантическое/приближенное сопоставление проиндексированных значений, решая проблему несовместимости естественного языка (например, поиск "CHESTNUT ST", когда выполняется запрос "Chestnut Street").

Обзор архитектуры

Каждое проиндексированное значение преобразуется во встраивание и хранится в векторной базе данных (Qdrant). Во время запроса запрос также преобразуется во встраивание, находятся похожие векторы, и связанная метаинформация используется для поиска фактических строк в Cassandra.

Структура коллекции Qdrant

Одна коллекция Qdrant для каждой кортежи (user, collection, schema_name, dimension):

Именование коллекции: rows_{user}_{collection}_{schema_name}_{dimension} Имена очищаются (небуквенно-цифровые символы заменяются на _, приводятся к нижнему регистру, числовые префиксы получают префикс r_) Обоснование: Позволяет чисто удалить экземпляр (user, collection, schema_name) путем удаления соответствующих коллекций Qdrant; суффикс измерения позволяет различным моделям встраивания сосуществовать.

Что подвергается встраиванию

Текстовое представление значений индекса:

Тип индекса Пример index_value Текст для встраивания
Одиночное поле ['foo@bar.com'] "foo@bar.com"
Композитный ['US', 'active'] "US active" (объединенные пробелами)

Структура точки

Каждая точка Qdrant содержит:

{
  "id": "<uuid>",
  "vector": [0.1, 0.2, ...],
  "payload": {
    "index_name": "street_name",
    "index_value": ["CHESTNUT ST"],
    "text": "CHESTNUT ST"
  }
}
Поле полезной нагрузки Описание
index_name Индексированные поля, которые представляет эта вставка.
index_value Исходный список значений (для поиска в Cassandra).
text Текст, который был вставлен (для отладки/отображения).

Обратите внимание: user, collection и schema_name определяются неявно из имени коллекции Qdrant.

Поток запросов

  1. Пользователь запрашивает "Chestnut Street" для пользователя U, коллекции X, схемы Y.
  2. Вставьте текст запроса.
  3. Определите имя(и) коллекции Qdrant, соответствующие префиксу rows_U_X_Y_.
  4. Выполните поиск в соответствующих коллекциях Qdrant для поиска ближайших векторов.
  5. Получите соответствующие точки с полезными нагрузками, содержащими index_name и index_value.
  6. Запрос к Cassandra:
    SELECT * FROM rows
    WHERE collection = 'X'
      AND schema_name = 'Y'
      AND index_name = '<from payload>'
      AND index_value = <from payload>
    
  7. Возврат соответствующих строк.

Необязательно: Фильтрация по имени индекса.

Запросы могут опционально фильтровать данные по index_name в Qdrant, чтобы искать только определенные поля:

"Найти любое поле, соответствующее 'Chestnut'" → поиск по всем векторам в коллекции. "Найти поле street_name, соответствующее 'Chestnut'" → фильтрация по payload.index_name = 'street_name'.

Архитектура.

Векторные представления строк следуют двухступенчатой схеме, используемой в GraphRAG (graph-embeddings, document-embeddings):

Этап 1: Вычисление векторных представлений (trustgraph-flow/trustgraph/embeddings/row_embeddings/) - Использует ExtractedObject, вычисляет векторные представления через сервис векторных представлений, выдает RowEmbeddings. Этап 2: Хранение векторных представлений (trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/) - Использует RowEmbeddings, записывает векторы в Qdrant.

Модуль записи строк в Cassandra является отдельным параллельным компонентом:

Модуль записи строк в Cassandra (trustgraph-flow/trustgraph/storage/rows/cassandra) - Использует ExtractedObject, записывает строки в Cassandra.

Все три компонента используют один и тот же поток данных, что обеспечивает их независимость. Это позволяет: Независимое масштабирование записи в Cassandra по сравнению с генерацией векторных представлений и хранением векторов. Отключение сервисов генерации векторных представлений, если они не требуются. Отказы в одном компоненте не влияют на другие. Согласованная архитектура с конвейерами GraphRAG.

Путь записи.

Этап 1 (процессор векторных представлений строк): При получении ExtractedObject:

  1. Получение схемы для поиска индексированных полей.
  2. Для каждого индексированного поля: Создание текстового представления значения индекса. Вычисление векторного представления через сервис векторных представлений.
  3. Вывод сообщения RowEmbeddings, содержащего все вычисленные векторы.

Этап 2 (запись векторных представлений в Qdrant): При получении RowEmbeddings:

  1. Для каждого векторного представления в сообщении: Определение коллекции Qdrant из (user, collection, schema_name, dimension). Создание коллекции, если это необходимо (создание при первой записи). Добавление точки с вектором и полезной нагрузкой.

Типы сообщений.

@dataclass
class RowIndexEmbedding:
    index_name: str              # The indexed field name(s)
    index_value: list[str]       # The field value(s)
    text: str                    # Text that was embedded
    vectors: list[list[float]]   # Computed embedding vectors

@dataclass
class RowEmbeddings:
    metadata: Metadata
    schema_name: str
    embeddings: list[RowIndexEmbedding]

Интеграция удаления

Коллекции Qdrant обнаруживаются путем сопоставления префикса с шаблоном имени коллекции:

Удаление (user, collection):

  1. Перечислить все коллекции Qdrant, соответствующие префиксу rows_{user}_{collection}_
  2. Удалить каждую соответствующую коллекцию
  3. Удалить разделы строк Cassandra (как описано выше)
  4. Очистить записи row_partitions

Удаление (user, collection, schema_name):

  1. Перечислить все коллекции Qdrant, соответствующие префиксу rows_{user}_{collection}_{schema_name}_
  2. Удалить каждую соответствующую коллекцию (обрабатывает несколько измерений)
  3. Удалить разделы строк Cassandra
  4. Очистить row_partitions

Расположение модулей

Этап Модуль Точка входа
Этап 1 trustgraph-flow/trustgraph/embeddings/row_embeddings/ row-embeddings
Этап 2 trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/ row-embeddings-write-qdrant

API запросов для векторных представлений строк

API запросов для векторных представлений строк является отдельным API от сервиса запросов строк GraphQL:

API Назначение Бэкенд
Запрос строк (GraphQL) Точное сопоставление с индексированными полями Cassandra
Запрос векторных представлений строк Нечеткое/семантическое сопоставление Qdrant

Это разделение позволяет четко разделить функциональность: Сервис GraphQL фокусируется на точных, структурированных запросах API векторных представлений обрабатывает семантическую схожесть Рабочий процесс пользователя: нечеткий поиск с помощью векторных представлений для поиска кандидатов, затем точный запрос для получения полных данных строки

Схема запроса/ответа

@dataclass
class RowEmbeddingsRequest:
    vectors: list[list[float]]    # Query vectors (pre-computed embeddings)
    user: str = ""
    collection: str = ""
    schema_name: str = ""
    index_name: str = ""          # Optional: filter to specific index
    limit: int = 10               # Max results per vector

@dataclass
class RowIndexMatch:
    index_name: str = ""          # The matched index field(s)
    index_value: list[str] = []   # The matched value(s)
    text: str = ""                # Original text that was embedded
    score: float = 0.0            # Similarity score

@dataclass
class RowEmbeddingsResponse:
    error: Error | None = None
    matches: list[RowIndexMatch] = []

Обработчик запросов

Модуль: trustgraph-flow/trustgraph/query/row_embeddings/qdrant

Точка входа: row-embeddings-query-qdrant

Обработчик:

  1. Получает RowEmbeddingsRequest с векторами запросов
  2. Находит соответствующую коллекцию Qdrant путем сопоставления префикса
  3. Ищет ближайшие векторы с необязательным фильтром index_name
  4. Возвращает RowEmbeddingsResponse с информацией об индексе, соответствующем запросу

Интеграция с API-шлюзом

Шлюз предоставляет запросы на получение векторных представлений строк через стандартный шаблон запроса/ответа:

Компонент Местоположение
Диспетчер trustgraph-flow/trustgraph/gateway/dispatch/row_embeddings_query.py
Регистрация Добавьте "row-embeddings" в request_response_dispatchers в manager.py

Имя интерфейса потока: row-embeddings

Определение интерфейса в шаблоне потока:

{
  "interfaces": {
    "row-embeddings": {
      "request": "non-persistent://tg/request/row-embeddings:{id}",
      "response": "non-persistent://tg/response/row-embeddings:{id}"
    }
  }
}

Поддержка Python SDK

SDK предоставляет методы для запросов векторных представлений строк:

# Flow-scoped query (preferred)
api = Api(url)
flow = api.flow().id("default")

# Query with text (SDK computes embeddings)
matches = flow.row_embeddings_query(
    text="Chestnut Street",
    collection="my_collection",
    schema_name="addresses",
    index_name="street_name",  # Optional filter
    limit=10
)

# Query with pre-computed vectors
matches = flow.row_embeddings_query(
    vectors=[[0.1, 0.2, ...]],
    collection="my_collection",
    schema_name="addresses"
)

# Each match contains:
for match in matches:
    print(match.index_name)   # e.g., "street_name"
    print(match.index_value)  # e.g., ["CHESTNUT ST"]
    print(match.text)         # e.g., "CHESTNUT ST"
    print(match.score)        # e.g., 0.95

Утилита командной строки

Команда: tg-invoke-row-embeddings

# Query by text (computes embedding automatically)
tg-invoke-row-embeddings \
  --text "Chestnut Street" \
  --collection my_collection \
  --schema addresses \
  --index street_name \
  --limit 10

# Query by vector file
tg-invoke-row-embeddings \
  --vectors vectors.json \
  --collection my_collection \
  --schema addresses

# Output formats
tg-invoke-row-embeddings --text "..." --format json
tg-invoke-row-embeddings --text "..." --format table

Типичная схема использования

Запрос векторных представлений строк обычно используется как часть процесса поиска приблизительного соответствия для точного поиска:

# Step 1: Fuzzy search via embeddings
matches = flow.row_embeddings_query(
    text="chestnut street",
    collection="geo",
    schema_name="streets"
)

# Step 2: Exact lookup via GraphQL for full row data
for match in matches:
    query = f'''
    query {{
        streets(where: {{ {match.index_name}: {{ eq: "{match.index_value[0]}" }} }}) {{
            street_name
            city
            zip_code
        }}
    }}
    '''
    rows = flow.rows_query(query, collection="geo")

Эта двухэтапная схема позволяет: Находить "CHESTNUT ST", когда пользователь ищет "Chestnut Street" Получать полные данные строки со всеми полями Объединять семантическую схожесть с доступом к структурированным данным

Импорт данных строк

Отложено до последующей фазы. Будет разработано вместе с другими изменениями импорта.

Влияние на реализацию

Анализ текущего состояния

Существующая реализация имеет два основных компонента:

Компонент Местоположение Строк Описание
Сервис запросов trustgraph-flow/trustgraph/query/objects/cassandra/service.py ~740 Монолит: генерация схемы GraphQL, разбор фильтров, запросы Cassandra, обработка запросов
Записывающий модуль trustgraph-flow/trustgraph/storage/objects/cassandra/write.py ~540 Создание таблиц для каждой схемы, вторичные индексы, вставка/удаление

Текущая схема запросов:

SELECT * FROM {keyspace}.o_{schema_name}
WHERE collection = 'X' AND email = 'foo@bar.com'
ALLOW FILTERING

Новая схема запроса:

SELECT * FROM {keyspace}.rows
WHERE collection = 'X' AND schema_name = 'customers'
  AND index_name = 'email' AND index_value = ['foo@bar.com']

Ключевые изменения

  1. Упрощение семантики запросов: Новая схема поддерживает только точное соответствие для index_value. Текущие фильтры GraphQL (gt, lt, contains и т.д.) либо: Становятся постобработкой возвращаемых данных (если это все еще необходимо) Удаляются в пользу использования API для работы с эмбеддингами для нечеткого сопоставления.

  2. Код GraphQL тесно связан: Текущие сборки service.py включают генерацию типов Strawberry, разбор фильтров и запросы, специфичные для Cassandra. Добавление еще одного бэкенда для хранения данных потребует дублирования примерно 400 строк кода GraphQL.

Предлагаемая реструктуризация

Реструктуризация состоит из двух частей:

1. Выделение кода GraphQL

Извлеките многократно используемые компоненты GraphQL в общий модуль:

trustgraph-flow/trustgraph/query/graphql/
├── __init__.py
├── types.py        # Filter types (IntFilter, StringFilter, FloatFilter)
├── schema.py       # Dynamic schema generation from RowSchema
└── filters.py      # Filter parsing utilities

Это позволяет: Повторное использование в различных системах хранения данных. Более четкое разделение ответственности. Более простое тестирование логики GraphQL независимо.

2. Реализация новой схемы таблицы

Переработайте код, специфичный для Cassandra, для использования унифицированной таблицы:

Записывающий модуль (trustgraph-flow/trustgraph/storage/rows/cassandra/): Одна rows таблица вместо таблиц для каждой схемы. Запись N копий на строку (по одной для каждого индекса). Регистрация в row_partitions таблице. Более простое создание таблицы (единоразовая настройка).

Сервис запросов (trustgraph-flow/trustgraph/query/rows/cassandra/): Запрос унифицированной rows таблицы. Использование извлеченного модуля GraphQL для генерации схемы. Упрощенная обработка фильтров (только точное соответствие на уровне базы данных).

Переименование модулей

В рамках очистки наименований "object" → "row":

Текущее Новое
storage/objects/cassandra/ storage/rows/cassandra/
query/objects/cassandra/ query/rows/cassandra/
embeddings/object_embeddings/ embeddings/row_embeddings/

Новые модули

Модуль Назначение
trustgraph-flow/trustgraph/query/graphql/ Общие утилиты GraphQL
trustgraph-flow/trustgraph/query/row_embeddings/qdrant/ API запросов для векторных представлений строк
trustgraph-flow/trustgraph/embeddings/row_embeddings/ Вычисление векторных представлений строк (Этап 1)
trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/ Хранение векторных представлений строк (Этап 2)

Ссылки

Техническая спецификация структурированных данных