mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 16:36:21 +02:00
746 lines
48 KiB
Markdown
746 lines
48 KiB
Markdown
|
|
---
|
|||
|
|
layout: default
|
|||
|
|
title: "Техническая спецификация оптимизации производительности GraphRAG"
|
|||
|
|
parent: "Russian (Beta)"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Техническая спецификация оптимизации производительности GraphRAG
|
|||
|
|
|
|||
|
|
> **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.
|
|||
|
|
|
|||
|
|
## Обзор
|
|||
|
|
|
|||
|
|
Эта спецификация описывает комплексные оптимизации производительности для алгоритма GraphRAG (Graph Retrieval-Augmented Generation) в TrustGraph. Текущая реализация страдает от значительных узких мест в производительности, которые ограничивают масштабируемость и время отклика. Эта спецификация охватывает четыре основные области оптимизации:
|
|||
|
|
|
|||
|
|
1. **Оптимизация обхода графа**: Исключение неэффективных рекурсивных запросов к базе данных и реализация пакетной обработки графа.
|
|||
|
|
2. **Оптимизация разрешения меток**: Замена последовательной загрузки меток параллельными/пакетными операциями.
|
|||
|
|
3. **Улучшение стратегии кэширования**: Реализация интеллектуального кэширования с вытеснением по принципу LRU и предварительной загрузкой.
|
|||
|
|
4. **Оптимизация запросов**: Добавление мемоизации результатов и кэширования вложений для повышения скорости отклика.
|
|||
|
|
|
|||
|
|
## Цели
|
|||
|
|
|
|||
|
|
**Сокращение объема запросов к базе данных**: Достижение снижения общего количества запросов к базе данных на 50-80% за счет пакетной обработки и кэширования.
|
|||
|
|
**Улучшение времени отклика**: Целевое увеличение скорости построения подграфов в 3-5 раз и ускорение разрешения меток в 2-3 раза.
|
|||
|
|
**Повышение масштабируемости**: Поддержка более крупных графов знаний с улучшением управления памятью.
|
|||
|
|
**Сохранение точности**: Сохранение существующей функциональности GraphRAG и качества результатов.
|
|||
|
|
**Обеспечение параллельности**: Улучшение возможностей параллельной обработки для нескольких одновременных запросов.
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
**Уменьшение объема памяти**: Реализация эффективных структур данных и управления памятью.
|
|||
|
|
=======
|
|||
|
|
**Уменьшение объема используемой памяти**: Реализация эффективных структур данных и управления памятью.
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
**Добавление возможностей мониторинга**: Включение показателей производительности и возможностей мониторинга.
|
|||
|
|
**Обеспечение надежности**: Добавление надлежащей обработки ошибок и механизмов таймаута.
|
|||
|
|
|
|||
|
|
## Предыстория
|
|||
|
|
|
|||
|
|
Текущая реализация GraphRAG в `trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py` имеет несколько критических проблем с производительностью, которые серьезно влияют на масштабируемость системы:
|
|||
|
|
|
|||
|
|
### Текущие проблемы с производительностью
|
|||
|
|
|
|||
|
|
**1. Неэффективный обход графа (функция `follow_edges`, строки 79-127)**
|
|||
|
|
Выполняет 3 отдельных запроса к базе данных для каждой сущности на каждом уровне глубины.
|
|||
|
|
Шаблон запроса: запросы на основе субъекта, запросы на основе предиката и запросы на основе объекта для каждой сущности.
|
|||
|
|
Без пакетной обработки: Каждый запрос обрабатывает только одну сущность за раз.
|
|||
|
|
Без обнаружения циклов: Может повторно посещать одни и те же узлы несколько раз.
|
|||
|
|
Рекурсивная реализация без мемоизации приводит к экспоненциальной сложности.
|
|||
|
|
Временная сложность: O(entities × max_path_length × triple_limit³)
|
|||
|
|
|
|||
|
|
**2. Последовательное разрешение меток (функция `get_labelgraph`, строки 144-171)**
|
|||
|
|
Обрабатывает каждый компонент тройки (субъект, предикат, объект) последовательно.
|
|||
|
|
Каждый вызов `maybe_label` потенциально вызывает запрос к базе данных.
|
|||
|
|
Без параллельного выполнения или пакетной обработки запросов меток.
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
В результате получается до 3 × subgraph_size отдельных вызовов базы данных.
|
|||
|
|
=======
|
|||
|
|
Приводит до 3 × subgraph_size отдельных вызовов базы данных.
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
**3. Примитивная стратегия кэширования (функция `maybe_label`, строки 62-77)**
|
|||
|
|
Простой кэш в виде словаря без ограничений размера или TTL.
|
|||
|
|
Отсутствие политики вытеснения кэша приводит к неограниченному росту памяти.
|
|||
|
|
Пропуски кэша вызывают отдельные запросы к базе данных.
|
|||
|
|
Без предварительной загрузки или интеллектуального подогрева кэша.
|
|||
|
|
|
|||
|
|
**4. Субоптимальные шаблоны запросов**
|
|||
|
|
Запросы на сравнение векторного сходства сущностей не кэшируются между похожими запросами.
|
|||
|
|
Без мемоизации результатов для повторяющихся шаблонов запросов.
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Отсутствие оптимизации запросов для распространенных шаблонов доступа.
|
|||
|
|
=======
|
|||
|
|
Отсутствует оптимизация запросов для распространенных шаблонов доступа.
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
**5. Критические проблемы с жизненным циклом объектов (`rag.py:96-102`)**
|
|||
|
|
**Объект GraphRag создается для каждого запроса**: Новый экземпляр создается для каждого запроса, что приводит к потере всех преимуществ кэша.
|
|||
|
|
**Объект запроса имеет очень короткий срок службы**: Создается и уничтожается в течение выполнения одного запроса (строки 201-207).
|
|||
|
|
**Кэш меток сбрасывается для каждого запроса**: Подогрев кэша и накопленные знания теряются между запросами.
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
**Накладные расходы на повторное создание клиента**: Клиенты базы данных потенциально повторно устанавливаются для каждого запроса.
|
|||
|
|
**Без оптимизации между запросами**: Невозможно извлечь выгоду из шаблонов запросов или совместного использования результатов.
|
|||
|
|
=======
|
|||
|
|
**Накладные расходы на повторное создание клиента**: Клиенты базы данных потенциально пересоздаются для каждого запроса.
|
|||
|
|
**Отсутствие оптимизации между запросами**: Невозможно извлечь выгоду из шаблонов запросов или обмена результатами.
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
### Анализ влияния на производительность
|
|||
|
|
|
|||
|
|
Текущий наихудший сценарий для типичного запроса:
|
|||
|
|
**Извлечение сущности**: 1 запрос на сравнение векторного сходства.
|
|||
|
|
**Обход графа**: entities × max_path_length × 3 × triple_limit запросов.
|
|||
|
|
**Разрешение меток**: subgraph_size × 3 отдельных запросов на разрешение меток.
|
|||
|
|
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Для параметров по умолчанию (50 сущностей, длина пути 2, ограничение в 30 тройки, размер подграфа 150):
|
|||
|
|
**Минимальное количество запросов**: 1 + (50 × 2 × 3 × 30) + (150 × 3) = **9451 запрос к базе данных**
|
|||
|
|
**Время отклика**: 15-30 секунд для графов среднего размера
|
|||
|
|
**Использование памяти**: Неограниваемый рост кэша со временем
|
|||
|
|
=======
|
|||
|
|
Для параметров по умолчанию (50 сущностей, длина пути 2, ограничение в 30 троек, размер подграфа 150):
|
|||
|
|
**Минимальное количество запросов**: 1 + (50 × 2 × 3 × 30) + (150 × 3) = **9451 запрос к базе данных**
|
|||
|
|
**Время отклика**: 15-30 секунд для графов среднего размера
|
|||
|
|
**Использование памяти**: Неограниченный рост кэша со временем
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
**Эффективность кэша**: 0% - кэши сбрасываются при каждом запросе
|
|||
|
|
**Накладные расходы на создание объектов**: Объекты GraphRag + Query создаются/удаляются для каждого запроса
|
|||
|
|
|
|||
|
|
Эта спецификация решает эти проблемы, реализуя пакетные запросы, интеллектуальное кэширование и параллельную обработку. Оптимизируя шаблоны запросов и доступ к данным, TrustGraph может:
|
|||
|
|
Поддерживать графы знаний корпоративного уровня с миллионами сущностей
|
|||
|
|
Обеспечивать время отклика менее 1 секунды для типичных запросов
|
|||
|
|
Обрабатывать сотни одновременных запросов GraphRAG
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Эффективно масштабироваться в зависимости от размера и сложности графа
|
|||
|
|
=======
|
|||
|
|
Эффективно масштабироваться с увеличением размера и сложности графа
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
## Технический дизайн
|
|||
|
|
|
|||
|
|
### Архитектура
|
|||
|
|
|
|||
|
|
Оптимизация производительности GraphRAG требует следующих технических компонентов:
|
|||
|
|
|
|||
|
|
#### 1. **Архитектурная реорганизация жизненного цикла объектов**
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
**Сделать GraphRag долгоживущим**: Переместить экземпляр GraphRag на уровень Processor для сохранения между запросами
|
|||
|
|
**Сохранять кэши**: Поддерживать кэш меток, кэш вложений и кэш результатов запросов между запросами
|
|||
|
|
**Оптимизировать объект Query**: Переработать Query как легковесный контекст выполнения, а не контейнер данных
|
|||
|
|
**Сохранять подключения к базе данных**: Поддерживать подключения к базе данных между запросами
|
|||
|
|
=======
|
|||
|
|
**Сделать GraphRag долгоживущим**: Переместить экземпляр GraphRag на уровень Processor для сохранения данных между запросами
|
|||
|
|
**Сохранять кэши**: Поддерживать кэш меток, кэш вложений и кэш результатов запросов между запросами
|
|||
|
|
**Оптимизировать объект Query**: Переработать Query как легковесный контекст выполнения, а не контейнер данных
|
|||
|
|
**Сохранять соединения с базой данных**: Поддерживать соединения с базой данных между запросами
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` (изменен)
|
|||
|
|
|
|||
|
|
#### 2. **Оптимизированный движок обхода графа**
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Заменить рекурсивную `follow_edges` на итеративный поиск в ширину
|
|||
|
|
Реализовать пакетную обработку сущностей на каждом уровне обхода
|
|||
|
|
Добавить обнаружение циклов с помощью отслеживания посещенных узлов
|
|||
|
|
=======
|
|||
|
|
Заменить рекурсивную функцию `follow_edges` на итеративный поиск в ширину
|
|||
|
|
Реализовать пакетную обработку сущностей на каждом уровне обхода
|
|||
|
|
Добавить обнаружение циклов с использованием отслеживания посещенных узлов
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
Включить раннее завершение при достижении лимитов
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-flow/trustgraph/retrieval/graph_rag/optimized_traversal.py`
|
|||
|
|
|
|||
|
|
#### 3. **Параллельная система разрешения меток**
|
|||
|
|
Пакетные запросы меток для нескольких сущностей одновременно
|
|||
|
|
Реализовать шаблоны async/await для параллельного доступа к базе данных
|
|||
|
|
Добавить интеллектуальную предварительную загрузку для распространенных шаблонов меток
|
|||
|
|
Включить стратегии предварительного заполнения кэша меток
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-flow/trustgraph/retrieval/graph_rag/label_resolver.py`
|
|||
|
|
|
|||
|
|
#### 4. **Консервативный слой кэширования меток**
|
|||
|
|
Кэш LRU с коротким TTL только для меток (5 минут) для баланса между производительностью и согласованностью
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Мониторинг метрик кэша и коэффициента попадания
|
|||
|
|
**Без кэширования вложений**: Уже кэшируются для каждого запроса, нет преимуществ для межзапросных данных
|
|||
|
|
=======
|
|||
|
|
Мониторинг метрик кэша и коэффициента попаданий
|
|||
|
|
**Без кэширования вложений**: Уже кэшируются для каждого запроса, нет преимуществ для межзапросных операций
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
**Без кэширования результатов запросов**: Из-за проблем согласованности изменений графа
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-flow/trustgraph/retrieval/graph_rag/cache_manager.py`
|
|||
|
|
|
|||
|
|
#### 5. **Фреймворк оптимизации запросов**
|
|||
|
|
Анализ шаблонов запросов и предложения по оптимизации
|
|||
|
|
Пакетный координатор запросов для доступа к базе данных
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Управление пулами соединений и временем ожидания запросов
|
|||
|
|
=======
|
|||
|
|
Управление пулом соединений и временем ожидания запросов
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
Мониторинг производительности и сбор метрик
|
|||
|
|
|
|||
|
|
Модуль: `trustgraph-flow/trustgraph/retrieval/graph_rag/query_optimizer.py`
|
|||
|
|
|
|||
|
|
### Модели данных
|
|||
|
|
|
|||
|
|
#### Оптимизированное состояние обхода графа
|
|||
|
|
|
|||
|
|
Движок обхода поддерживает состояние для предотвращения избыточных операций:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class TraversalState:
|
|||
|
|
visited_entities: Set[str]
|
|||
|
|
current_level_entities: Set[str]
|
|||
|
|
next_level_entities: Set[str]
|
|||
|
|
subgraph: Set[Tuple[str, str, str]]
|
|||
|
|
depth: int
|
|||
|
|
query_batch: List[TripleQuery]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Этот подход позволяет:
|
|||
|
|
Эффективное обнаружение циклов за счет отслеживания посещенных сущностей.
|
|||
|
|
Подготовку запросов пакетами на каждом уровне обхода.
|
|||
|
|
Экономичное использование памяти для управления состоянием.
|
|||
|
|
Раннее завершение, когда достигнуты ограничения по размеру.
|
|||
|
|
|
|||
|
|
#### Улучшенная структура кэша
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class CacheEntry:
|
|||
|
|
value: Any
|
|||
|
|
timestamp: float
|
|||
|
|
access_count: int
|
|||
|
|
ttl: Optional[float]
|
|||
|
|
|
|||
|
|
class CacheManager:
|
|||
|
|
label_cache: LRUCache[str, CacheEntry]
|
|||
|
|
embedding_cache: LRUCache[str, CacheEntry]
|
|||
|
|
query_result_cache: LRUCache[str, CacheEntry]
|
|||
|
|
cache_stats: CacheStatistics
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Структуры пакетных запросов
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class BatchTripleQuery:
|
|||
|
|
entities: List[str]
|
|||
|
|
query_type: QueryType # SUBJECT, PREDICATE, OBJECT
|
|||
|
|
limit_per_entity: int
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class BatchLabelQuery:
|
|||
|
|
entities: List[str]
|
|||
|
|
predicate: str = LABEL
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### API
|
|||
|
|
|
|||
|
|
#### Новые API:
|
|||
|
|
|
|||
|
|
**API GraphTraversal**
|
|||
|
|
```python
|
|||
|
|
async def optimized_follow_edges_batch(
|
|||
|
|
entities: List[str],
|
|||
|
|
max_depth: int,
|
|||
|
|
triple_limit: int,
|
|||
|
|
max_subgraph_size: int
|
|||
|
|
) -> Set[Tuple[str, str, str]]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**API для разрешения меток пакетов**
|
|||
|
|
```python
|
|||
|
|
async def resolve_labels_batch(
|
|||
|
|
entities: List[str],
|
|||
|
|
cache_manager: CacheManager
|
|||
|
|
) -> Dict[str, str]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**API управления кэшем**
|
|||
|
|
```python
|
|||
|
|
class CacheManager:
|
|||
|
|
async def get_or_fetch_label(self, entity: str) -> str
|
|||
|
|
async def get_or_fetch_embeddings(self, query: str) -> List[float]
|
|||
|
|
async def cache_query_result(self, query_hash: str, result: Any, ttl: int)
|
|||
|
|
def get_cache_statistics(self) -> CacheStatistics
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Измененные API:
|
|||
|
|
|
|||
|
|
**GraphRag.query()** - Улучшено с оптимизациями производительности:
|
|||
|
|
Добавлен параметр cache_manager для управления кэшем.
|
|||
|
|
Добавлено возвращаемое значение performance_metrics.
|
|||
|
|
Добавлен параметр query_timeout для повышения надежности.
|
|||
|
|
|
|||
|
|
**Класс Query** - Рефакторинг для пакетной обработки:
|
|||
|
|
Замена обработки отдельных сущностей на пакетные операции.
|
|||
|
|
Добавлены асинхронные контекстные менеджеры для очистки ресурсов.
|
|||
|
|
Добавлены обратные вызовы для отслеживания прогресса длительных операций.
|
|||
|
|
|
|||
|
|
### Детали реализации
|
|||
|
|
|
|||
|
|
#### Фаза 0: Критическая архитектурная реорганизация жизненного цикла
|
|||
|
|
|
|||
|
|
**Текущая проблемная реализация:**
|
|||
|
|
```python
|
|||
|
|
# INEFFICIENT: GraphRag recreated every request
|
|||
|
|
class Processor(FlowProcessor):
|
|||
|
|
async def on_request(self, msg, consumer, flow):
|
|||
|
|
# PROBLEM: New GraphRag instance per request!
|
|||
|
|
self.rag = GraphRag(
|
|||
|
|
embeddings_client = flow("embeddings-request"),
|
|||
|
|
graph_embeddings_client = flow("graph-embeddings-request"),
|
|||
|
|
triples_client = flow("triples-request"),
|
|||
|
|
prompt_client = flow("prompt-request"),
|
|||
|
|
verbose=True,
|
|||
|
|
)
|
|||
|
|
# Cache starts empty every time - no benefit from previous requests
|
|||
|
|
response = await self.rag.query(...)
|
|||
|
|
|
|||
|
|
# VERY SHORT-LIVED: Query object created/destroyed per request
|
|||
|
|
class GraphRag:
|
|||
|
|
async def query(self, query, user="trustgraph", collection="default", ...):
|
|||
|
|
q = Query(rag=self, user=user, collection=collection, ...) # Created
|
|||
|
|
kg = await q.get_labelgraph(query) # Used briefly
|
|||
|
|
# q automatically destroyed when function exits
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Оптимизированная архитектура с длительным сроком службы:**
|
|||
|
|
```python
|
|||
|
|
class Processor(FlowProcessor):
|
|||
|
|
def __init__(self, **params):
|
|||
|
|
super().__init__(**params)
|
|||
|
|
self.rag_instance = None # Will be initialized once
|
|||
|
|
self.client_connections = {}
|
|||
|
|
|
|||
|
|
async def initialize_rag(self, flow):
|
|||
|
|
"""Initialize GraphRag once, reuse for all requests"""
|
|||
|
|
if self.rag_instance is None:
|
|||
|
|
self.rag_instance = LongLivedGraphRag(
|
|||
|
|
embeddings_client=flow("embeddings-request"),
|
|||
|
|
graph_embeddings_client=flow("graph-embeddings-request"),
|
|||
|
|
triples_client=flow("triples-request"),
|
|||
|
|
prompt_client=flow("prompt-request"),
|
|||
|
|
verbose=True,
|
|||
|
|
)
|
|||
|
|
return self.rag_instance
|
|||
|
|
|
|||
|
|
async def on_request(self, msg, consumer, flow):
|
|||
|
|
# REUSE the same GraphRag instance - caches persist!
|
|||
|
|
rag = await self.initialize_rag(flow)
|
|||
|
|
|
|||
|
|
# Query object becomes lightweight execution context
|
|||
|
|
response = await rag.query_with_context(
|
|||
|
|
query=v.query,
|
|||
|
|
execution_context=QueryContext(
|
|||
|
|
user=v.user,
|
|||
|
|
collection=v.collection,
|
|||
|
|
entity_limit=entity_limit,
|
|||
|
|
# ... other params
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
class LongLivedGraphRag:
|
|||
|
|
def __init__(self, ...):
|
|||
|
|
# CONSERVATIVE caches - balance performance vs consistency
|
|||
|
|
self.label_cache = LRUCacheWithTTL(max_size=5000, ttl=300) # 5min TTL for freshness
|
|||
|
|
# Note: No embedding cache - already cached per-query, no cross-query benefit
|
|||
|
|
# Note: No query result cache due to consistency concerns
|
|||
|
|
self.performance_metrics = PerformanceTracker()
|
|||
|
|
|
|||
|
|
async def query_with_context(self, query: str, context: QueryContext):
|
|||
|
|
# Use lightweight QueryExecutor instead of heavyweight Query object
|
|||
|
|
executor = QueryExecutor(self, context) # Minimal object
|
|||
|
|
return await executor.execute(query)
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class QueryContext:
|
|||
|
|
"""Lightweight execution context - no heavy operations"""
|
|||
|
|
user: str
|
|||
|
|
collection: str
|
|||
|
|
entity_limit: int
|
|||
|
|
triple_limit: int
|
|||
|
|
max_subgraph_size: int
|
|||
|
|
max_path_length: int
|
|||
|
|
|
|||
|
|
class QueryExecutor:
|
|||
|
|
"""Lightweight execution context - replaces old Query class"""
|
|||
|
|
def __init__(self, rag: LongLivedGraphRag, context: QueryContext):
|
|||
|
|
self.rag = rag
|
|||
|
|
self.context = context
|
|||
|
|
# No heavy initialization - just references
|
|||
|
|
|
|||
|
|
async def execute(self, query: str):
|
|||
|
|
# All heavy lifting uses persistent rag caches
|
|||
|
|
return await self.rag.execute_optimized_query(query, self.context)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Это архитектурное изменение обеспечивает:
|
|||
|
|
**Сокращение количества запросов к базе данных на 10-20%** для графов с общими связями (по сравнению с текущими 0%)
|
|||
|
|
**Устранение накладных расходов на создание объектов** для каждого запроса
|
|||
|
|
**Постоянное использование пула соединений и повторное использование клиентов**
|
|||
|
|
**Оптимизация между запросами** в пределах временных окон TTL кэша
|
|||
|
|
|
|||
|
|
**Важное ограничение согласованности кэша:**
|
|||
|
|
Долгосрочное кэширование создает риск устаревания данных, когда сущности/метки удаляются или изменяются в базовом графе. Кэш LRU с TTL обеспечивает баланс между повышением производительности и актуальностью данных, но не может обнаруживать изменения в графе в режиме реального времени.
|
|||
|
|
|
|||
|
|
#### Фаза 1: Оптимизация обхода графа
|
|||
|
|
|
|||
|
|
**Проблемы текущей реализации:**
|
|||
|
|
```python
|
|||
|
|
# INEFFICIENT: 3 queries per entity per level
|
|||
|
|
async def follow_edges(self, ent, subgraph, path_length):
|
|||
|
|
# Query 1: s=ent, p=None, o=None
|
|||
|
|
res = await self.rag.triples_client.query(s=ent, p=None, o=None, limit=self.triple_limit)
|
|||
|
|
# Query 2: s=None, p=ent, o=None
|
|||
|
|
res = await self.rag.triples_client.query(s=None, p=ent, o=None, limit=self.triple_limit)
|
|||
|
|
# Query 3: s=None, p=None, o=ent
|
|||
|
|
res = await self.rag.triples_client.query(s=None, p=None, o=ent, limit=self.triple_limit)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Оптимизированная реализация:**
|
|||
|
|
```python
|
|||
|
|
async def optimized_traversal(self, entities: List[str], max_depth: int) -> Set[Triple]:
|
|||
|
|
visited = set()
|
|||
|
|
current_level = set(entities)
|
|||
|
|
subgraph = set()
|
|||
|
|
|
|||
|
|
for depth in range(max_depth):
|
|||
|
|
if not current_level or len(subgraph) >= self.max_subgraph_size:
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
# Batch all queries for current level
|
|||
|
|
batch_queries = []
|
|||
|
|
for entity in current_level:
|
|||
|
|
if entity not in visited:
|
|||
|
|
batch_queries.extend([
|
|||
|
|
TripleQuery(s=entity, p=None, o=None),
|
|||
|
|
TripleQuery(s=None, p=entity, o=None),
|
|||
|
|
TripleQuery(s=None, p=None, o=entity)
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
# Execute all queries concurrently
|
|||
|
|
results = await self.execute_batch_queries(batch_queries)
|
|||
|
|
|
|||
|
|
# Process results and prepare next level
|
|||
|
|
next_level = set()
|
|||
|
|
for result in results:
|
|||
|
|
subgraph.update(result.triples)
|
|||
|
|
next_level.update(result.new_entities)
|
|||
|
|
|
|||
|
|
visited.update(current_level)
|
|||
|
|
current_level = next_level - visited
|
|||
|
|
|
|||
|
|
return subgraph
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Фаза 2: Параллельное разрешение меток
|
|||
|
|
|
|||
|
|
**Текущая последовательная реализация:**
|
|||
|
|
```python
|
|||
|
|
# INEFFICIENT: Sequential processing
|
|||
|
|
for edge in subgraph:
|
|||
|
|
s = await self.maybe_label(edge[0]) # Individual query
|
|||
|
|
p = await self.maybe_label(edge[1]) # Individual query
|
|||
|
|
o = await self.maybe_label(edge[2]) # Individual query
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Оптимизированная параллельная реализация:**
|
|||
|
|
```python
|
|||
|
|
async def resolve_labels_parallel(self, subgraph: List[Triple]) -> List[Triple]:
|
|||
|
|
# Collect all unique entities needing labels
|
|||
|
|
entities_to_resolve = set()
|
|||
|
|
for s, p, o in subgraph:
|
|||
|
|
entities_to_resolve.update([s, p, o])
|
|||
|
|
|
|||
|
|
# Remove already cached entities
|
|||
|
|
uncached_entities = [e for e in entities_to_resolve if e not in self.label_cache]
|
|||
|
|
|
|||
|
|
# Batch query for all uncached labels
|
|||
|
|
if uncached_entities:
|
|||
|
|
label_results = await self.batch_label_query(uncached_entities)
|
|||
|
|
self.label_cache.update(label_results)
|
|||
|
|
|
|||
|
|
# Apply labels to subgraph
|
|||
|
|
return [
|
|||
|
|
(self.label_cache.get(s, s), self.label_cache.get(p, p), self.label_cache.get(o, o))
|
|||
|
|
for s, p, o in subgraph
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Фаза 3: Продвинутая стратегия кэширования
|
|||
|
|
|
|||
|
|
**Кэш LRU с TTL:**
|
|||
|
|
```python
|
|||
|
|
class LRUCacheWithTTL:
|
|||
|
|
def __init__(self, max_size: int, default_ttl: int = 3600):
|
|||
|
|
self.cache = OrderedDict()
|
|||
|
|
self.max_size = max_size
|
|||
|
|
self.default_ttl = default_ttl
|
|||
|
|
self.access_times = {}
|
|||
|
|
|
|||
|
|
async def get(self, key: str) -> Optional[Any]:
|
|||
|
|
if key in self.cache:
|
|||
|
|
# Check TTL expiration
|
|||
|
|
if time.time() - self.access_times[key] > self.default_ttl:
|
|||
|
|
del self.cache[key]
|
|||
|
|
del self.access_times[key]
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
# Move to end (most recently used)
|
|||
|
|
self.cache.move_to_end(key)
|
|||
|
|
return self.cache[key]
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
async def put(self, key: str, value: Any):
|
|||
|
|
if key in self.cache:
|
|||
|
|
self.cache.move_to_end(key)
|
|||
|
|
else:
|
|||
|
|
if len(self.cache) >= self.max_size:
|
|||
|
|
# Remove least recently used
|
|||
|
|
oldest_key = next(iter(self.cache))
|
|||
|
|
del self.cache[oldest_key]
|
|||
|
|
del self.access_times[oldest_key]
|
|||
|
|
|
|||
|
|
self.cache[key] = value
|
|||
|
|
self.access_times[key] = time.time()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### Фаза 4: Оптимизация запросов и мониторинг
|
|||
|
|
|
|||
|
|
**Сбор показателей производительности:**
|
|||
|
|
```python
|
|||
|
|
@dataclass
|
|||
|
|
class PerformanceMetrics:
|
|||
|
|
total_queries: int
|
|||
|
|
cache_hits: int
|
|||
|
|
cache_misses: int
|
|||
|
|
avg_response_time: float
|
|||
|
|
subgraph_construction_time: float
|
|||
|
|
label_resolution_time: float
|
|||
|
|
total_entities_processed: int
|
|||
|
|
memory_usage_mb: float
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Тайм-аут запроса и предохранитель:**
|
|||
|
|
```python
|
|||
|
|
async def execute_with_timeout(self, query_func, timeout: int = 30):
|
|||
|
|
try:
|
|||
|
|
return await asyncio.wait_for(query_func(), timeout=timeout)
|
|||
|
|
except asyncio.TimeoutError:
|
|||
|
|
logger.error(f"Query timeout after {timeout}s")
|
|||
|
|
raise GraphRagTimeoutError(f"Query exceeded timeout of {timeout}s")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Соображения по обеспечению согласованности кэша
|
|||
|
|
|
|||
|
|
**Компромиссы между актуальностью данных:**
|
|||
|
|
**Кэш меток (TTL 5 минут):** Риск предоставления устаревших меток сущностей (удаленных или переименованных).
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
**Отсутствие кэширования вложений:** Не требуется, так как вложения уже кэшируются для каждого запроса.
|
|||
|
|
=======
|
|||
|
|
**Отсутствие кэширования вложений:** Не требуется - вложения уже кэшируются для каждого запроса.
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
**Отсутствие кэширования результатов:** Предотвращает получение устаревших результатов подграфов из-за удаленных сущностей/связей.
|
|||
|
|
|
|||
|
|
**Стратегии смягчения:**
|
|||
|
|
**Консервативные значения TTL:** Баланс между приростом производительности (10-20%) и актуальностью данных.
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
**Хуки для аннулирования кэша:** Необязательная интеграция с событиями изменения графа.
|
|||
|
|
**Панели мониторинга:** Отслеживание показателей попадания в кэш по сравнению с инцидентами устаревания данных.
|
|||
|
|
**Настраиваемые политики кэширования:** Возможность тонкой настройки для каждого развертывания в зависимости от частоты изменений.
|
|||
|
|
|
|||
|
|
**Рекомендуемая конфигурация кэша в зависимости от частоты изменений графа:**
|
|||
|
|
**Высокая частота изменений (>100 изменений/час):** TTL=60 секунд, меньшие размеры кэша.
|
|||
|
|
**Средняя частота изменений (10-100 изменений/час):** TTL=300 секунд (по умолчанию).
|
|||
|
|
**Низкая частота изменений (<10 изменений/час):** TTL=600 секунд, большие размеры кэша.
|
|||
|
|
=======
|
|||
|
|
**Механизмы аннулирования кэша:** Необязательная интеграция с событиями изменения графа.
|
|||
|
|
**Информационные панели мониторинга:** Отслеживание показателей попадания в кэш по сравнению с инцидентами устаревания данных.
|
|||
|
|
**Настраиваемые политики кэширования:** Возможность тонкой настройки для каждого развертывания в зависимости от частоты изменений.
|
|||
|
|
|
|||
|
|
**Рекомендуемая конфигурация кэша в зависимости от скорости изменений графа:**
|
|||
|
|
**Высокая скорость изменений (>100 изменений/час):** TTL=60 секунд, меньшие размеры кэша.
|
|||
|
|
**Средняя скорость изменений (10-100 изменений/час):** TTL=300 секунд (по умолчанию).
|
|||
|
|
**Низкая скорость изменений (<10 изменений/час):** TTL=600 секунд, большие размеры кэша.
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
## Соображения безопасности
|
|||
|
|
|
|||
|
|
**Предотвращение внедрения запросов:**
|
|||
|
|
Проверка всех идентификаторов сущностей и параметров запроса.
|
|||
|
|
Использование параметризованных запросов для всех взаимодействий с базой данных.
|
|||
|
|
Реализация ограничений на сложность запросов для предотвращения атак типа "отказ в обслуживании" (DoS).
|
|||
|
|
|
|||
|
|
**Защита ресурсов:**
|
|||
|
|
Применение ограничений на максимальный размер подграфа.
|
|||
|
|
Реализация таймаутов запросов для предотвращения исчерпания ресурсов.
|
|||
|
|
Добавление мониторинга и ограничений использования памяти.
|
|||
|
|
|
|||
|
|
**Контроль доступа:**
|
|||
|
|
Поддержание существующей изоляции пользователей и коллекций.
|
|||
|
|
Добавление ведения журнала аудита для операций, влияющих на производительность.
|
|||
|
|
Реализация ограничения скорости для дорогостоящих операций.
|
|||
|
|
|
|||
|
|
## Соображения производительности
|
|||
|
|
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
### Ожидаемые улучшения производительности
|
|||
|
|
|
|||
|
|
**Сокращение количества запросов:**
|
|||
|
|
Сейчас: ~9000+ запросов для типичного запроса.
|
|||
|
|
Оптимизировано: ~50-100 пакетных запросов (снижение на 98%).
|
|||
|
|
=======
|
|||
|
|
### Ожидаемое повышение производительности
|
|||
|
|
|
|||
|
|
**Сокращение количества запросов:**
|
|||
|
|
Текущее: ~9000+ запросов для типичного запроса.
|
|||
|
|
Оптимизированное: ~50-100 пакетных запросов (снижение на 98%).
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
**Улучшение времени отклика:**
|
|||
|
|
Обход графа: 15-20 секунд → 3-5 секунд (в 4-5 раза быстрее).
|
|||
|
|
Разрешение меток: 8-12 секунд → 2-4 секунды (в 3 раза быстрее).
|
|||
|
|
Общий запрос: 25-35 секунд → 6-10 секунд (улучшение в 3-4 раза).
|
|||
|
|
|
|||
|
|
**Эффективность использования памяти:**
|
|||
|
|
Ограниченные размеры кэша предотвращают утечки памяти.
|
|||
|
|
Эффективные структуры данных уменьшают объем используемой памяти примерно на 40%.
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Улучшен сбор мусора благодаря правильной очистке ресурсов.
|
|||
|
|
=======
|
|||
|
|
Улучшенная сборка мусора благодаря правильной очистке ресурсов.
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
**Реалистичные ожидания производительности:**
|
|||
|
|
**Кэш меток:** Уменьшение количества запросов на 10-20% для графов с общими связями.
|
|||
|
|
**Оптимизация пакетной обработки:** Уменьшение количества запросов на 50-80% (основная оптимизация).
|
|||
|
|
**Оптимизация времени жизни объектов:** Исключение накладных расходов на создание объектов для каждого запроса.
|
|||
|
|
**Общее улучшение:** Улучшение времени отклика в 3-4 раза, в основном за счет пакетной обработки.
|
|||
|
|
|
|||
|
|
**Улучшения масштабируемости:**
|
|||
|
|
Поддержка графов знаний в 3-5 раза большего размера (ограничено потребностями согласованности кэша).
|
|||
|
|
Увеличение количества одновременных запросов в 3-5 раза.
|
|||
|
|
Лучшее использование ресурсов благодаря повторному использованию соединений.
|
|||
|
|
|
|||
|
|
### Мониторинг производительности
|
|||
|
|
|
|||
|
|
**Метрики в реальном времени:**
|
|||
|
|
Время выполнения запросов по типу операции.
|
|||
|
|
Показатели попадания в кэш и его эффективность.
|
|||
|
|
Использование пула соединений с базой данных.
|
|||
|
|
Использование памяти и влияние сборки мусора.
|
|||
|
|
|
|||
|
|
**Бенчмаркинг производительности:**
|
|||
|
|
Автоматизированное регрессионное тестирование производительности
|
|||
|
|
Тестирование нагрузки с использованием реалистичных объемов данных
|
|||
|
|
Сравнительные тесты с текущей реализацией
|
|||
|
|
|
|||
|
|
## Стратегия тестирования
|
|||
|
|
|
|||
|
|
### Модульное тестирование
|
|||
|
|
Тестирование отдельных компонентов для обхода графа, кэширования и разрешения меток
|
|||
|
|
Эмуляция взаимодействия с базой данных для тестирования производительности
|
|||
|
|
Тестирование вытеснения из кэша и истечения срока действия TTL
|
|||
|
|
Обработка ошибок и сценарии таймаутов
|
|||
|
|
|
|||
|
|
### Интеграционное тестирование
|
|||
|
|
Комплексное тестирование запросов GraphRAG с оптимизациями
|
|||
|
|
Тестирование взаимодействия с базой данных с использованием реальных данных
|
|||
|
|
Обработка одновременных запросов и управление ресурсами
|
|||
|
|
Обнаружение утечек памяти и проверка очистки ресурсов
|
|||
|
|
|
|||
|
|
### Тестирование производительности
|
|||
|
|
Тестирование производительности по сравнению с текущей реализацией
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Тестирование нагрузки с различными размерами и сложностью графов
|
|||
|
|
=======
|
|||
|
|
Тестирование нагрузки с графами различного размера и сложности
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
Стресс-тестирование для проверки лимитов памяти и соединений
|
|||
|
|
Регрессионное тестирование для проверки улучшений производительности
|
|||
|
|
|
|||
|
|
### Тестирование совместимости
|
|||
|
|
Проверка совместимости существующего API GraphRAG
|
|||
|
|
Тестирование с различными бэкендами графовых баз данных
|
|||
|
|
Проверка точности результатов по сравнению с текущей реализацией
|
|||
|
|
|
|||
|
|
## План реализации
|
|||
|
|
|
|||
|
|
### Прямой подход к реализации
|
|||
|
|
Поскольку API могут изменяться, реализуйте оптимизации напрямую без сложности миграции:
|
|||
|
|
|
|||
|
|
1. **Замените метод `follow_edges`**: Перепишите с использованием пакетного итеративного обхода
|
|||
|
|
2. **Оптимизируйте `get_labelgraph`**: Реализуйте параллельное разрешение меток
|
|||
|
|
3. **Добавьте долгоживущий GraphRag**: Измените Processor для поддержания постоянной инстанции
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
4. **Реализуйте кэширование меток**: Добавьте кэш LRU с TTL в класс GraphRag
|
|||
|
|
=======
|
|||
|
|
4. **Реализуйте кэширование меток**: Добавьте кэш LRU со сроком действия TTL в класс GraphRag
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
### Область изменений
|
|||
|
|
**Класс запроса**: Замените ~50 строк в `follow_edges`, добавьте ~30 строк для обработки пакетов
|
|||
|
|
**Класс GraphRag**: Добавьте слой кэширования (~40 строк)
|
|||
|
|
**Класс Processor**: Измените для использования постоянной инстанции GraphRag (~20 строк)
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
**Всего**: ~140 строк целенаправленных изменений, в основном в существующих классах
|
|||
|
|
=======
|
|||
|
|
**Всего**: ~140 строк изменений, в основном в существующих классах
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
|
|||
|
|
## Временная шкала
|
|||
|
|
|
|||
|
|
**Неделя 1: Основная реализация**
|
|||
|
|
Замените `follow_edges` пакетным итеративным обходом
|
|||
|
|
Реализуйте параллельное разрешение меток в `get_labelgraph`
|
|||
|
|
Добавьте долгоживущую инстанцию GraphRag в Processor
|
|||
|
|
Реализуйте слой кэширования меток
|
|||
|
|
|
|||
|
|
**Неделя 2: Тестирование и интеграция**
|
|||
|
|
Модульные тесты для новой логики обхода и кэширования
|
|||
|
|
Бенчмаркинг производительности по сравнению с текущей реализацией
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Интеграционное тестирование с реальными данными графа
|
|||
|
|
=======
|
|||
|
|
Интеграционное тестирование с реальными графовыми данными
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
Проверка кода и оптимизация
|
|||
|
|
|
|||
|
|
**Неделя 3: Развертывание**
|
|||
|
|
Разверните оптимизированную реализацию
|
|||
|
|
Отслеживайте улучшения производительности
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
Тонкая настройка TTL кэша и размеров пакетов на основе реального использования
|
|||
|
|
|
|||
|
|
## Открытые вопросы
|
|||
|
|
|
|||
|
|
**Пул соединений с базой данных**: Следует ли нам реализовать собственный пул соединений или использовать существующий пул соединений от клиента базы данных?
|
|||
|
|
**Постоянство кэша**: Должны ли кэши меток и внедрений сохраняться после перезапуска службы?
|
|||
|
|
**Распределенное кэширование**: Для развернутых в нескольких экземплярах систем следует ли нам реализовать распределенное кэширование с использованием Redis/Memcached?
|
|||
|
|
**Формат результата запроса**: Следует ли нам оптимизировать внутреннее представление тройки для повышения эффективности использования памяти?
|
|||
|
|
=======
|
|||
|
|
Тонкая настройка срока действия TTL кэша и размеров пакетов на основе реального использования
|
|||
|
|
|
|||
|
|
## Открытые вопросы
|
|||
|
|
|
|||
|
|
**Пул соединений с базой данных**: Следует ли нам реализовывать собственный пул соединений или использовать существующий пул соединений от клиента базы данных?
|
|||
|
|
**Постоянство кэша**: Должны ли кэши меток и вложений сохраняться после перезапуска сервиса?
|
|||
|
|
**Распределенное кэширование**: Для развернутых в нескольких инстанциях систем следует ли реализовывать распределенное кэширование с использованием Redis/Memcached?
|
|||
|
|
**Формат результата запроса**: Следует ли оптимизировать внутреннее представление тройки для повышения эффективности использования памяти?
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
**Интеграция мониторинга**: Какие метрики следует предоставлять существующим системам мониторинга (Prometheus и т. д.)?
|
|||
|
|
|
|||
|
|
## Ссылки
|
|||
|
|
|
|||
|
|
<<<<<<< HEAD
|
|||
|
|
[Оригинальная реализация GraphRAG](trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py)
|
|||
|
|
=======
|
|||
|
|
[Исходная реализация GraphRAG](trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py)
|
|||
|
|
>>>>>>> 82edf2d (New md files from RunPod)
|
|||
|
|
[Принципы архитектуры TrustGraph](architecture-principles.md)
|
|||
|
|
[Спецификация управления коллекциями](collection-management.md)
|