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.
36 KiB
| 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 вставок (по одной на каждое индексированное поле) Дополнительные затраты памяти из-за дублирования данных строк Информация о типах хранится в конфигурации схемы, преобразование происходит на уровне приложения
Модель согласованности
В проекте приняты определенные упрощения:
-
Отсутствие обновлений строк: Система работает только на добавление данных. Это устраняет проблемы согласованности, связанные с обновлением нескольких копий одной и той же строки.
-
Толерантность к изменениям схемы: При изменении схем (например, при добавлении/удалении индексов) существующие строки сохраняют свою первоначальную индексацию. Старые строки не будут доступны через новые индексы. Пользователи могут удалить и пересоздать схему для обеспечения согласованности, если это необходимо.
Отслеживание и удаление разделов
Проблема
При использовании ключа раздела (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). При обработке строки:
- Проверьте, есть ли
(collection, schema_name)в кэше. - Если ⟦CODE_0⟧ не в кэше (первая строка для этой пары):
Найдите конфигурацию схемы, чтобы получить все имена индексов.
Вставьте записи в
row_partitionsдля каждого(collection, schema_name, index_name). Добавьте пару в кэш. - Перейдите к записи данных строки.
Модуль записи строк также отслеживает события изменения конфигурации схемы. При изменении схемы соответствующие записи кэша очищаются, чтобы следующая строка вызвала повторную регистрацию с обновленными именами индексов.
Этот подход обеспечивает:
Записи в таблицу поиска происходят только один раз для каждой пары (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.
Поток запросов
- Пользователь запрашивает "Chestnut Street" для пользователя U, коллекции X, схемы Y.
- Вставьте текст запроса.
- Определите имя(и) коллекции Qdrant, соответствующие префиксу
rows_U_X_Y_. - Выполните поиск в соответствующих коллекциях Qdrant для поиска ближайших векторов.
- Получите соответствующие точки с полезными нагрузками, содержащими
index_nameиindex_value. - Запрос к Cassandra:
SELECT * FROM rows WHERE collection = 'X' AND schema_name = 'Y' AND index_name = '<from payload>' AND index_value = <from payload> - Возврат соответствующих строк.
Необязательно: Фильтрация по имени индекса.
Запросы могут опционально фильтровать данные по 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:
- Получение схемы для поиска индексированных полей.
- Для каждого индексированного поля: Создание текстового представления значения индекса. Вычисление векторного представления через сервис векторных представлений.
- Вывод сообщения
RowEmbeddings, содержащего все вычисленные векторы.
Этап 2 (запись векторных представлений в Qdrant): При получении RowEmbeddings:
- Для каждого векторного представления в сообщении:
Определение коллекции 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):
- Перечислить все коллекции Qdrant, соответствующие префиксу
rows_{user}_{collection}_ - Удалить каждую соответствующую коллекцию
- Удалить разделы строк Cassandra (как описано выше)
- Очистить записи
row_partitions
Удаление (user, collection, schema_name):
- Перечислить все коллекции Qdrant, соответствующие префиксу
rows_{user}_{collection}_{schema_name}_ - Удалить каждую соответствующую коллекцию (обрабатывает несколько измерений)
- Удалить разделы строк Cassandra
- Очистить
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
Обработчик:
- Получает
RowEmbeddingsRequestс векторами запросов - Находит соответствующую коллекцию Qdrant путем сопоставления префикса
- Ищет ближайшие векторы с необязательным фильтром
index_name - Возвращает
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']
Ключевые изменения
-
Упрощение семантики запросов: Новая схема поддерживает только точное соответствие для
index_value. Текущие фильтры GraphQL (gt,lt,containsи т.д.) либо: Становятся постобработкой возвращаемых данных (если это все еще необходимо) Удаляются в пользу использования API для работы с эмбеддингами для нечеткого сопоставления. -
Код 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) |