mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-28 01:46:22 +02:00
Structure the tech specs directory (#836)
Tech spec some subdirectories for different languages
This commit is contained in:
parent
48da6c5f8b
commit
e7efb673ef
423 changed files with 0 additions and 0 deletions
135
docs/tech-specs/ru/__TEMPLATE.ru.md
Normal file
135
docs/tech-specs/ru/__TEMPLATE.ru.md
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает интерфейсы командной строки для загрузки знаний в TrustGraph, позволяя пользователям загружать данные из различных источников с помощью инструментов командной строки. Интеграция поддерживает четыре основных сценария использования:
|
||||
|
||||
1. **[Сценарий использования 1]**: [Описание]
|
||||
2. **[Сценарий использования 2]**: [Описание]
|
||||
3. **[Сценарий использования 3]**: [Описание]
|
||||
4. **[Сценарий использования 4]**: [Описание]
|
||||
|
||||
## Цели
|
||||
|
||||
- **[Цель 1]**: [Описание]
|
||||
- **[Цель 2]**: [Описание]
|
||||
- **[Цель 3]**: [Описание]
|
||||
- **[Цель 4]**: [Описание]
|
||||
- **[Цель 5]**: [Описание]
|
||||
- **[Цель 6]**: [Описание]
|
||||
- **[Цель 7]**: [Описание]
|
||||
- **[Цель 8]**: [Описание]
|
||||
|
||||
## Предыстория
|
||||
|
||||
[Опишите текущее состояние и ограничения, которые решает эта спецификация]
|
||||
|
||||
Текущие ограничения включают:
|
||||
- [Ограничение 1]
|
||||
- [Ограничение 2]
|
||||
- [Ограничение 3]
|
||||
- [Ограничение 4]
|
||||
|
||||
Эта спецификация устраняет эти недостатки, [описание]. Благодаря [возможности], TrustGraph может:
|
||||
- [Преимущество 1]
|
||||
- [Преимущество 2]
|
||||
- [Преимущество 3]
|
||||
- [Преимущество 4]
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Архитектура
|
||||
|
||||
Загрузка знаний через командную строку требует следующих технических компонентов:
|
||||
|
||||
1. **[Компонент 1]**
|
||||
- [Описание функциональности компонента]
|
||||
- [Основные характеристики]
|
||||
- [Точки интеграции]
|
||||
|
||||
Модуль: [путь_к_модулю]
|
||||
|
||||
2. **[Компонент 2]**
|
||||
- [Описание функциональности компонента]
|
||||
- [Основные характеристики]
|
||||
- [Точки интеграции]
|
||||
|
||||
Модуль: [путь_к_модулю]
|
||||
|
||||
3. **[Компонент 3]**
|
||||
- [Описание функциональности компонента]
|
||||
- [Основные характеристики]
|
||||
- [Точки интеграции]
|
||||
|
||||
Модуль: [путь_к_модулю]
|
||||
|
||||
### Модели данных
|
||||
|
||||
#### [Модель данных 1]
|
||||
|
||||
[Описание модели данных и ее структуры]
|
||||
|
||||
Пример:
|
||||
```
|
||||
[Example data structure]
|
||||
```
|
||||
|
||||
Этот подход позволяет:
|
||||
- [Преимущество 1]
|
||||
- [Преимущество 2]
|
||||
- [Преимущество 3]
|
||||
- [Преимущество 4]
|
||||
|
||||
### API
|
||||
|
||||
Новые API:
|
||||
- [Описание API 1]
|
||||
- [Описание API 2]
|
||||
- [Описание API 3]
|
||||
|
||||
Измененные API:
|
||||
- [Измененный API 1] - [Описание изменений]
|
||||
- [Измененный API 2] - [Описание изменений]
|
||||
|
||||
### Детали реализации
|
||||
|
||||
[Подход к реализации и соглашения]
|
||||
|
||||
[Дополнительные примечания по реализации]
|
||||
|
||||
## Вопросы безопасности
|
||||
|
||||
[Вопросы безопасности, специфичные для данной реализации]
|
||||
|
||||
## Вопросы производительности
|
||||
|
||||
[Вопросы производительности и потенциальные узкие места]
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
[Подход и стратегия тестирования]
|
||||
|
||||
## План миграции
|
||||
|
||||
[Стратегия миграции, если применимо]
|
||||
|
||||
## Сроки
|
||||
|
||||
[Информация о сроках, если указана]
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- [Открытый вопрос 1]
|
||||
- [Открытый вопрос 2]
|
||||
|
||||
## Ссылки
|
||||
|
||||
[Ссылки, если применимо]
|
||||
280
docs/tech-specs/ru/agent-explainability.ru.md
Normal file
280
docs/tech-specs/ru/agent-explainability.ru.md
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Добавьте регистрацию происхождения в цикл работы агента React, чтобы сеансы работы агента можно было отслеживать и отлаживать с использованием той же инфраструктуры объяснимости, что и в GraphRAG.
|
||||
|
||||
**Принятые решения:**
|
||||
Запись в `urn:graph:retrieval` (общий граф объяснимости)
|
||||
Линейная цепочка зависимостей на данный момент (анализ N → был получен из → анализ N-1)
|
||||
Инструменты являются непрозрачными "черными ящиками" (записывайте только входные и выходные данные)
|
||||
Поддержка DAG отложена до будущей итерации
|
||||
|
||||
## Типы сущностей
|
||||
|
||||
И GraphRAG, и Agent используют PROV-O в качестве базовой онтологии с подтипами, специфичными для TrustGraph:
|
||||
|
||||
### Типы GraphRAG
|
||||
| Сущность | Тип PROV-O | Типы TG | Описание |
|
||||
|--------|-------------|----------|-------------|
|
||||
| Вопрос | `prov:Activity` | `tg:Question`, `tg:GraphRagQuestion` | Запрос пользователя |
|
||||
| Исследование | `prov:Entity` | `tg:Exploration` | Ряды, извлеченные из графа знаний |
|
||||
| Фокус | `prov:Entity` | `tg:Focus` | Выбранные ряды с обоснованием |
|
||||
| Синтез | `prov:Entity` | `tg:Synthesis` | Окончательный ответ |
|
||||
|
||||
### Типы Agent
|
||||
| Сущность | Тип PROV-O | Типы TG | Описание |
|
||||
|--------|-------------|----------|-------------|
|
||||
| Вопрос | `prov:Activity` | `tg:Question`, `tg:AgentQuestion` | Запрос пользователя |
|
||||
| Анализ | `prov:Entity` | `tg:Analysis` | Каждый цикл "думай/действуй/наблюдай" |
|
||||
| Вывод | `prov:Entity` | `tg:Conclusion` | Окончательный ответ |
|
||||
|
||||
### Типы Document RAG
|
||||
| Сущность | Тип PROV-O | Типы TG | Описание |
|
||||
|--------|-------------|----------|-------------|
|
||||
| Вопрос | `prov:Activity` | `tg:Question`, `tg:DocRagQuestion` | Запрос пользователя |
|
||||
| Исследование | `prov:Entity` | `tg:Exploration` | Части, извлеченные из хранилища документов |
|
||||
| Синтез | `prov:Entity` | `tg:Synthesis` | Окончательный ответ |
|
||||
|
||||
**Примечание:** Document RAG использует подмножество типов GraphRAG (нет этапа "Фокус", поскольку нет этапа выбора/обоснования ребер).
|
||||
|
||||
### Подтипы вопросов
|
||||
|
||||
Все сущности "Вопрос" имеют `tg:Question` в качестве базового типа, но имеют определенный подтип для идентификации механизма извлечения:
|
||||
|
||||
| Подтип | Шаблон URI | Механизм |
|
||||
|---------|-------------|-----------|
|
||||
| `tg:GraphRagQuestion` | `urn:trustgraph:question:{uuid}` | RAG на основе графа знаний |
|
||||
| `tg:DocRagQuestion` | `urn:trustgraph:docrag:{uuid}` | RAG на основе документов/частей |
|
||||
| `tg:AgentQuestion` | `urn:trustgraph:agent:{uuid}` | Агент ReAct |
|
||||
|
||||
Это позволяет запрашивать все вопросы через `tg:Question`, одновременно фильтруя по конкретному механизму с помощью подтипа.
|
||||
|
||||
## Модель происхождения
|
||||
|
||||
```
|
||||
Question (urn:trustgraph:agent:{uuid})
|
||||
│
|
||||
│ tg:query = "User's question"
|
||||
│ prov:startedAtTime = timestamp
|
||||
│ rdf:type = prov:Activity, tg:Question
|
||||
│
|
||||
↓ prov:wasDerivedFrom
|
||||
│
|
||||
Analysis1 (urn:trustgraph:agent:{uuid}/i1)
|
||||
│
|
||||
│ tg:thought = "I need to query the knowledge base..."
|
||||
│ tg:action = "knowledge-query"
|
||||
│ tg:arguments = {"question": "..."}
|
||||
│ tg:observation = "Result from tool..."
|
||||
│ rdf:type = prov:Entity, tg:Analysis
|
||||
│
|
||||
↓ prov:wasDerivedFrom
|
||||
│
|
||||
Analysis2 (urn:trustgraph:agent:{uuid}/i2)
|
||||
│ ...
|
||||
↓ prov:wasDerivedFrom
|
||||
│
|
||||
Conclusion (urn:trustgraph:agent:{uuid}/final)
|
||||
│
|
||||
│ tg:answer = "The final response..."
|
||||
│ rdf:type = prov:Entity, tg:Conclusion
|
||||
```
|
||||
|
||||
### Модель происхождения документов RAG
|
||||
|
||||
```
|
||||
Question (urn:trustgraph:docrag:{uuid})
|
||||
│
|
||||
│ tg:query = "User's question"
|
||||
│ prov:startedAtTime = timestamp
|
||||
│ rdf:type = prov:Activity, tg:Question
|
||||
│
|
||||
↓ prov:wasGeneratedBy
|
||||
│
|
||||
Exploration (urn:trustgraph:docrag:{uuid}/exploration)
|
||||
│
|
||||
│ tg:chunkCount = 5
|
||||
│ tg:selectedChunk = "chunk-id-1"
|
||||
│ tg:selectedChunk = "chunk-id-2"
|
||||
│ ...
|
||||
│ rdf:type = prov:Entity, tg:Exploration
|
||||
│
|
||||
↓ prov:wasDerivedFrom
|
||||
│
|
||||
Synthesis (urn:trustgraph:docrag:{uuid}/synthesis)
|
||||
│
|
||||
│ tg:content = "The synthesized answer..."
|
||||
│ rdf:type = prov:Entity, tg:Synthesis
|
||||
```
|
||||
|
||||
## Необходимые изменения
|
||||
|
||||
### 1. Изменения схемы
|
||||
|
||||
**Файл:** `trustgraph-base/trustgraph/schema/services/agent.py`
|
||||
|
||||
Добавить поля `session_id` и `collection` в `AgentRequest`:
|
||||
```python
|
||||
@dataclass
|
||||
class AgentRequest:
|
||||
question: str = ""
|
||||
state: str = ""
|
||||
group: list[str] | None = None
|
||||
history: list[AgentStep] = field(default_factory=list)
|
||||
user: str = ""
|
||||
collection: str = "default" # NEW: Collection for provenance traces
|
||||
streaming: bool = False
|
||||
session_id: str = "" # NEW: For provenance tracking across iterations
|
||||
```
|
||||
|
||||
**Файл:** `trustgraph-base/trustgraph/messaging/translators/agent.py`
|
||||
|
||||
Обновить переводчик для обработки `session_id` и `collection` как в `to_pulsar()`, так и в `from_pulsar()`.
|
||||
|
||||
### 2. Добавить компонент "Explainability Producer" в сервис Agent
|
||||
|
||||
**Файл:** `trustgraph-flow/trustgraph/agent/react/service.py`
|
||||
|
||||
Зарегистрировать компонент "explainability" (в соответствии с тем же шаблоном, что и GraphRAG):
|
||||
```python
|
||||
from ... base import ProducerSpec
|
||||
from ... schema import Triples
|
||||
|
||||
# In __init__:
|
||||
self.register_specification(
|
||||
ProducerSpec(
|
||||
name = "explainability",
|
||||
schema = Triples,
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Генерация триплетов происхождения
|
||||
|
||||
**Файл:** `trustgraph-base/trustgraph/provenance/agent.py`
|
||||
|
||||
Создайте вспомогательные функции (подобные `question_triples`, `exploration_triples` и т.д. в GraphRAG):
|
||||
```python
|
||||
def agent_session_triples(session_uri, query, timestamp):
|
||||
"""Generate triples for agent Question."""
|
||||
return [
|
||||
Triple(s=session_uri, p=RDF_TYPE, o=PROV_ACTIVITY),
|
||||
Triple(s=session_uri, p=RDF_TYPE, o=TG_QUESTION),
|
||||
Triple(s=session_uri, p=TG_QUERY, o=query),
|
||||
Triple(s=session_uri, p=PROV_STARTED_AT_TIME, o=timestamp),
|
||||
]
|
||||
|
||||
def agent_iteration_triples(iteration_uri, parent_uri, thought, action, arguments, observation):
|
||||
"""Generate triples for one Analysis step."""
|
||||
return [
|
||||
Triple(s=iteration_uri, p=RDF_TYPE, o=PROV_ENTITY),
|
||||
Triple(s=iteration_uri, p=RDF_TYPE, o=TG_ANALYSIS),
|
||||
Triple(s=iteration_uri, p=TG_THOUGHT, o=thought),
|
||||
Triple(s=iteration_uri, p=TG_ACTION, o=action),
|
||||
Triple(s=iteration_uri, p=TG_ARGUMENTS, o=json.dumps(arguments)),
|
||||
Triple(s=iteration_uri, p=TG_OBSERVATION, o=observation),
|
||||
Triple(s=iteration_uri, p=PROV_WAS_DERIVED_FROM, o=parent_uri),
|
||||
]
|
||||
|
||||
def agent_final_triples(final_uri, parent_uri, answer):
|
||||
"""Generate triples for Conclusion."""
|
||||
return [
|
||||
Triple(s=final_uri, p=RDF_TYPE, o=PROV_ENTITY),
|
||||
Triple(s=final_uri, p=RDF_TYPE, o=TG_CONCLUSION),
|
||||
Triple(s=final_uri, p=TG_ANSWER, o=answer),
|
||||
Triple(s=final_uri, p=PROV_WAS_DERIVED_FROM, o=parent_uri),
|
||||
]
|
||||
```
|
||||
|
||||
### 4. Определения типов
|
||||
|
||||
**Файл:** `trustgraph-base/trustgraph/provenance/namespaces.py`
|
||||
|
||||
Добавить типы сущностей, обеспечивающих объяснимость, и предикаты агентов:
|
||||
```python
|
||||
# Explainability entity types (used by both GraphRAG and Agent)
|
||||
TG_QUESTION = TG + "Question"
|
||||
TG_EXPLORATION = TG + "Exploration"
|
||||
TG_FOCUS = TG + "Focus"
|
||||
TG_SYNTHESIS = TG + "Synthesis"
|
||||
TG_ANALYSIS = TG + "Analysis"
|
||||
TG_CONCLUSION = TG + "Conclusion"
|
||||
|
||||
# Agent predicates
|
||||
TG_THOUGHT = TG + "thought"
|
||||
TG_ACTION = TG + "action"
|
||||
TG_ARGUMENTS = TG + "arguments"
|
||||
TG_OBSERVATION = TG + "observation"
|
||||
TG_ANSWER = TG + "answer"
|
||||
```
|
||||
|
||||
## Измененные файлы
|
||||
|
||||
| Файл | Изменение |
|
||||
|------|--------|
|
||||
| `trustgraph-base/trustgraph/schema/services/agent.py` | Добавлены session_id и collection в AgentRequest |
|
||||
| `trustgraph-base/trustgraph/messaging/translators/agent.py` | Обновлен переводчик для новых полей |
|
||||
| `trustgraph-base/trustgraph/provenance/namespaces.py` | Добавлены типы сущностей, предикаты агента и предикаты Document RAG |
|
||||
| `trustgraph-base/trustgraph/provenance/triples.py` | Добавлены типы TG для конструкторов троек GraphRAG, добавлены конструкторы троек Document RAG |
|
||||
| `trustgraph-base/trustgraph/provenance/uris.py` | Добавлены генераторы URI для Document RAG |
|
||||
| `trustgraph-base/trustgraph/provenance/__init__.py` | Экспортированы новые типы, предикаты и функции Document RAG |
|
||||
| `trustgraph-base/trustgraph/schema/services/retrieval.py` | Добавлены explain_id и explain_graph в DocumentRagResponse |
|
||||
| `trustgraph-base/trustgraph/messaging/translators/retrieval.py` | Обновлен DocumentRagResponseTranslator для полей, связанных с объяснением |
|
||||
| `trustgraph-flow/trustgraph/agent/react/service.py` | Добавлена логика создания и записи информации о объяснении |
|
||||
| `trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py` | Добавлен колбэк для информации о объяснении и выводятся тройки, содержащие информацию о происхождении |
|
||||
| `trustgraph-flow/trustgraph/retrieval/document_rag/rag.py` | Добавлен генератор информации о объяснении и подключен колбэк |
|
||||
| `trustgraph-cli/trustgraph/cli/show_explain_trace.py` | Обработка типов трассировки агента |
|
||||
| `trustgraph-cli/trustgraph/cli/list_explain_traces.py` | Отображение сессий агента вместе с GraphRAG |
|
||||
|
||||
## Созданные файлы
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|---------|
|
||||
| `trustgraph-base/trustgraph/provenance/agent.py` | Генераторы троек, специфичные для агента |
|
||||
|
||||
## Обновления CLI
|
||||
|
||||
**Обнаружение:** И GraphRAG, и вопросы агента имеют тип `tg:Question`. Отличаются следующим:
|
||||
1. Шаблон URI: `urn:trustgraph:agent:` против `urn:trustgraph:question:`
|
||||
2. Выводимые сущности: `tg:Analysis` (агент) против `tg:Exploration` (GraphRAG)
|
||||
|
||||
**`list_explain_traces.py`:**
|
||||
Отображает столбец "Тип" (Агент против GraphRAG)
|
||||
|
||||
**`show_explain_trace.py`:**
|
||||
Автоматически определяет тип трассировки
|
||||
Отображение информации об агенте: Вопрос → Шаги анализа → Вывод
|
||||
|
||||
## Обратная совместимость
|
||||
|
||||
`session_id` по умолчанию равно `""` - старые запросы работают, но не будут содержать информацию о происхождении
|
||||
`collection` по умолчанию равно `"default"` - разумная альтернатива
|
||||
CLI корректно обрабатывает оба типа трассировки
|
||||
|
||||
## Проверка
|
||||
|
||||
```bash
|
||||
# Run an agent query
|
||||
tg-invoke-agent -q "What is the capital of France?"
|
||||
|
||||
# List traces (should show agent sessions with Type column)
|
||||
tg-list-explain-traces -U trustgraph -C default
|
||||
|
||||
# Show agent trace
|
||||
tg-show-explain-trace "urn:trustgraph:agent:xxx"
|
||||
```
|
||||
|
||||
## Будущие задачи (не входят в этот PR)
|
||||
|
||||
Зависимости DAG (когда анализ N использует результаты нескольких предыдущих анализов)
|
||||
Связь с конкретными инструментами (KnowledgeQuery → его трассировка GraphRAG)
|
||||
Потоковая передача метаданных (отправлять по мере выполнения, а не пакетами в конце)
|
||||
113
docs/tech-specs/ru/architecture-principles.ru.md
Normal file
113
docs/tech-specs/ru/architecture-principles.ru.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
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: Модель графа "Субъект-Предикат-Объект" (SPO)
|
||||
**Решение**: Принять SPO/RDF в качестве основной модели представления знаний.
|
||||
|
||||
**Обоснование**:
|
||||
Обеспечивает максимальную гибкость и совместимость с существующими технологиями графов.
|
||||
Позволяет беспрепятственно переводить в другие языки запросов к графам (например, SPO → Cypher, но не наоборот).
|
||||
Создает основу, которая "открывает множество" возможностей для дальнейшей разработки.
|
||||
Поддерживает как отношения между узлами (SPO), так и отношения между узлами и литералами (RDF).
|
||||
|
||||
**Реализация**:
|
||||
Основная структура данных: `node → edge → {node | literal}`
|
||||
Поддерживать совместимость со стандартами RDF, одновременно поддерживая расширенные операции SPO.
|
||||
|
||||
## Основа 2: Интеграция графа знаний, оптимизированная для LLM
|
||||
**Решение**: Оптимизировать структуру и операции графа знаний для взаимодействия с LLM.
|
||||
|
||||
**Обоснование**:
|
||||
Основной сценарий использования включает взаимодействие LLM с графами знаний.
|
||||
Выбор технологий графов должен отдавать приоритет совместимости с LLM, а не другим соображениям.
|
||||
Обеспечивает потоки обработки естественного языка, использующие структурированные знания.
|
||||
|
||||
**Реализация**:
|
||||
Разрабатывать схемы графов, которые LLM могут эффективно использовать для рассуждений.
|
||||
Оптимизировать для распространенных шаблонов взаимодействия LLM.
|
||||
|
||||
## Основа 3: Навигация по графу на основе встраиваний
|
||||
**Решение**: Реализовать прямое сопоставление между запросами на естественном языке и узлами графа с помощью встраиваний.
|
||||
|
||||
**Обоснование**:
|
||||
Обеспечивает самый простой путь от запроса на естественном языке к навигации по графу.
|
||||
Избегает сложных промежуточных этапов генерации запросов.
|
||||
Предоставляет возможности семантического поиска в структуре графа.
|
||||
|
||||
**Реализация**:
|
||||
`NLP Query → Graph Embeddings → Graph Nodes`
|
||||
Поддерживать представления встраиваний для всех сущностей графа.
|
||||
Поддерживать прямое семантическое сопоставление сходства для разрешения запросов.
|
||||
|
||||
## Основа 4: Распределенное разрешение сущностей с детерминированными идентификаторами
|
||||
**Решение**: Поддерживать параллельное извлечение знаний с детерминированной идентификацией сущностей (правило 80%).
|
||||
|
||||
**Обоснование**:
|
||||
**Идеально**: Извлечение в одном процессе с полной видимостью состояния обеспечивает идеальное разрешение сущностей.
|
||||
**Реальность**: Требования к масштабируемости требуют возможностей параллельной обработки.
|
||||
**Компромисс**: Разработать для детерминированной идентификации сущностей в распределенных процессах.
|
||||
|
||||
**Реализация**:
|
||||
Разработать механизмы для генерации согласованных, уникальных идентификаторов в различных инструментах извлечения знаний.
|
||||
Одна и та же сущность, упомянутая в разных процессах, должна разрешаться в один и тот же идентификатор.
|
||||
Признать, что ~20% крайних случаев могут потребовать альтернативных моделей обработки.
|
||||
Разработать механизмы отката для сложных сценариев разрешения сущностей.
|
||||
|
||||
## Основа 5: Архитектура, управляемая событиями, с публикацией и подпиской
|
||||
**Решение**: Реализовать систему обмена сообщениями pub-sub для координации системы.
|
||||
|
||||
**Обоснование**:
|
||||
Обеспечивает слабую связанность между компонентами извлечения знаний, хранения и запросов.
|
||||
Поддерживает обновления в режиме реального времени и уведомления по всей системе.
|
||||
Облегчает масштабируемые, распределенные рабочие процессы.
|
||||
|
||||
**Реализация**:
|
||||
Координация между компонентами системы с помощью управляемых сообщениями.
|
||||
Потоки событий для обновлений знаний, завершения извлечения и результатов запросов.
|
||||
|
||||
## Основа 6: Взаимодействие агентов с возможностью повторного входа
|
||||
**Решение**: Поддерживать операции pub-sub с возможностью повторного входа для обработки на основе агентов.
|
||||
|
||||
**Обоснование**:
|
||||
Позволяет создавать сложные рабочие процессы агентов, в которых агенты могут инициировать и реагировать друг на друга.
|
||||
Поддерживает сложные, многоступенчатые конвейеры обработки знаний.
|
||||
Позволяет использовать рекурсивные и итеративные шаблоны обработки.
|
||||
|
||||
**Реализация**:
|
||||
Система pub-sub должна безопасно обрабатывать вызовы с повторным входом.
|
||||
Механизмы координации агентов, предотвращающие бесконечные циклы.
|
||||
Поддержка оркестровки рабочих процессов агентов.
|
||||
|
||||
## Основа 7: Интеграция с хранилищем данных в столбцовом формате
|
||||
**Решение**: Обеспечить совместимость запросов с системами хранения данных в столбцовом формате.
|
||||
|
||||
**Обоснование**:
|
||||
Обеспечивает эффективные аналитические запросы к большим наборам данных знаний.
|
||||
Поддерживает сценарии бизнес-аналитики и отчетности.
|
||||
Объединяет представление знаний на основе графов с традиционными аналитическими рабочими процессами.
|
||||
|
||||
**Реализация**:
|
||||
Слой перевода запросов: Запросы графов → Запросы в столбцовом формате.
|
||||
Гибридная стратегия хранения, поддерживающая как операции графов, так и аналитические рабочие нагрузки.
|
||||
Поддерживать производительность запросов в обеих парадигмах.
|
||||
|
||||
--
|
||||
|
||||
## Краткое изложение принципов архитектуры
|
||||
|
||||
1. **Гибкость прежде всего**: Модель SPO/RDF обеспечивает максимальную адаптируемость.
|
||||
2. **Оптимизация для LLM**: Все решения в области проектирования учитывают требования взаимодействия с LLM.
|
||||
3. **Семантическая эффективность**: Прямое сопоставление встраиваний с узлами для оптимальной производительности запросов.
|
||||
4. **Прагматическая масштабируемость**: Баланс между идеальной точностью и практическими возможностями распределенной обработки.
|
||||
5. **Координация, управляемая событиями**: Pub-sub обеспечивает слабую связанность и масштабируемость.
|
||||
6. **Поддержка агентов**: Поддержка сложных рабочих процессов, основанных на нескольких агентах.
|
||||
7. **Совместимость с аналитикой**: Объединение парадигм графов и столбцов для всестороннего запроса.
|
||||
|
||||
Эта архитектура графа знаний сочетает теоретическую строгость с практическими требованиями масштабируемости, оптимизированная для интеграции с LLM и распределенной обработки.
|
||||
339
docs/tech-specs/ru/cassandra-consolidation.ru.md
Normal file
339
docs/tech-specs/ru/cassandra-consolidation.ru.md
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Техническое описание: Консолидация конфигурации Cassandra"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Техническое описание: Консолидация конфигурации Cassandra
|
||||
|
||||
> **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.
|
||||
|
||||
**Статус:** Черновик
|
||||
**Автор:** Assistant
|
||||
**Дата:** 2024-09-03
|
||||
|
||||
## Обзор
|
||||
|
||||
Данное техническое описание посвящено неконсистентности именования и шаблонов конфигурации параметров подключения к Cassandra в кодовой базе TrustGraph. В настоящее время существуют две различные схемы именования параметров (`cassandra_*` против `graph_*`), что приводит к путанице и усложняет обслуживание.
|
||||
|
||||
## Описание проблемы
|
||||
|
||||
В кодовой базе в настоящее время используются два различных набора параметров конфигурации Cassandra:
|
||||
|
||||
1. **Модули Knowledge/Config/Library** используют:
|
||||
`cassandra_host` (список хостов)
|
||||
`cassandra_user`
|
||||
`cassandra_password`
|
||||
|
||||
2. **Модули Graph/Storage** используют:
|
||||
`graph_host` (один хост, иногда преобразуется в список)
|
||||
`graph_username`
|
||||
`graph_password`
|
||||
|
||||
3. **Неконсистентное использование в командной строке**:
|
||||
Некоторые процессоры (например, `kg-store`) не предоставляют параметры Cassandra в качестве аргументов командной строки
|
||||
Другие процессоры предоставляют их с разными именами и форматами
|
||||
Тексты справки не отражают значения переменных окружения по умолчанию
|
||||
|
||||
Оба набора параметров подключаются к одному кластеру Cassandra, но с разными соглашениями об именовании, что приводит к:
|
||||
Путанице в конфигурации для пользователей
|
||||
Увеличенной нагрузке на обслуживание
|
||||
Неконсистентной документации
|
||||
Возможности неправильной конфигурации
|
||||
Невозможности перезаписать настройки через командную строку в некоторых процессорах
|
||||
|
||||
## Предлагаемое решение
|
||||
|
||||
### 1. Стандартизация имен параметров
|
||||
|
||||
Все модули будут использовать согласованные имена параметров `cassandra_*`:
|
||||
`cassandra_host` - Список хостов (хранится внутри как список)
|
||||
`cassandra_username` - Имя пользователя для аутентификации
|
||||
`cassandra_password` - Пароль для аутентификации
|
||||
|
||||
### 2. Аргументы командной строки
|
||||
|
||||
Все процессоры ДОЛЖНЫ предоставлять конфигурацию Cassandra через аргументы командной строки:
|
||||
`--cassandra-host` - Список хостов, разделенный запятыми
|
||||
`--cassandra-username` - Имя пользователя для аутентификации
|
||||
`--cassandra-password` - Пароль для аутентификации
|
||||
|
||||
### 3. Передача через переменные окружения
|
||||
|
||||
Если параметры командной строки не указаны явно, система будет проверять переменные окружения:
|
||||
`CASSANDRA_HOST` - Список хостов, разделенный запятыми
|
||||
`CASSANDRA_USERNAME` - Имя пользователя для аутентификации
|
||||
`CASSANDRA_PASSWORD` - Пароль для аутентификации
|
||||
|
||||
### 4. Значения по умолчанию
|
||||
|
||||
Если ни параметры командной строки, ни переменные окружения не указаны:
|
||||
`cassandra_host` по умолчанию `["cassandra"]`
|
||||
`cassandra_username` по умолчанию `None` (без аутентификации)
|
||||
`cassandra_password` по умолчанию `None` (без аутентификации)
|
||||
|
||||
### 5. Требования к тексту справки
|
||||
|
||||
Вывод команды `--help` должен:
|
||||
Показывать значения переменных окружения в качестве значений по умолчанию, если они установлены
|
||||
Никогда не отображать значения паролей (показывать `****` или `<set>` вместо этого)
|
||||
Четко указывать порядок разрешения в тексте справки
|
||||
|
||||
Пример вывода справки:
|
||||
```
|
||||
--cassandra-host HOST
|
||||
Cassandra host list, comma-separated (default: prod-cluster-1,prod-cluster-2)
|
||||
[from CASSANDRA_HOST environment variable]
|
||||
|
||||
--cassandra-username USERNAME
|
||||
Cassandra username (default: cassandra_user)
|
||||
[from CASSANDRA_USERNAME environment variable]
|
||||
|
||||
--cassandra-password PASSWORD
|
||||
Cassandra password (default: <set from environment>)
|
||||
```
|
||||
|
||||
## Детали реализации
|
||||
|
||||
### Порядок разрешения параметров
|
||||
|
||||
Для каждого параметра Cassandra порядок разрешения будет следующим:
|
||||
1. Значение аргумента командной строки
|
||||
2. Значение переменной окружения (`CASSANDRA_*`)
|
||||
3. Значение по умолчанию
|
||||
|
||||
### Обработка параметров хостов
|
||||
|
||||
Параметр `cassandra_host`:
|
||||
Командная строка принимает строку, разделенную запятыми: `--cassandra-host "host1,host2,host3"`
|
||||
Переменная окружения принимает строку, разделенную запятыми: `CASSANDRA_HOST="host1,host2,host3"`
|
||||
Внутри всегда хранится в виде списка: `["host1", "host2", "host3"]`
|
||||
Один хост: `"localhost"` → преобразуется в `["localhost"]`
|
||||
Уже является списком: `["host1", "host2"]` → используется как есть
|
||||
|
||||
### Логика аутентификации
|
||||
|
||||
Аутентификация будет использоваться, когда указаны и `cassandra_username`, и `cassandra_password`:
|
||||
```python
|
||||
if cassandra_username and cassandra_password:
|
||||
# Use SSL context and PlainTextAuthProvider
|
||||
else:
|
||||
# Connect without authentication
|
||||
```
|
||||
|
||||
## Файлы для изменения
|
||||
|
||||
### Модули, использующие параметры `graph_*` (которые необходимо изменить):
|
||||
`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/objects/cassandra/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/rows/cassandra/write.py`
|
||||
`trustgraph-flow/trustgraph/query/triples/cassandra/service.py`
|
||||
|
||||
### Модули, использующие параметры `cassandra_*` (которые необходимо обновить с использованием резервного варианта из среды):
|
||||
`trustgraph-flow/trustgraph/tables/config.py`
|
||||
`trustgraph-flow/trustgraph/tables/knowledge.py`
|
||||
`trustgraph-flow/trustgraph/tables/library.py`
|
||||
`trustgraph-flow/trustgraph/storage/knowledge/store.py`
|
||||
`trustgraph-flow/trustgraph/cores/knowledge.py`
|
||||
`trustgraph-flow/trustgraph/librarian/librarian.py`
|
||||
`trustgraph-flow/trustgraph/librarian/service.py`
|
||||
`trustgraph-flow/trustgraph/config/service/service.py`
|
||||
`trustgraph-flow/trustgraph/cores/service.py`
|
||||
|
||||
### Тестовые файлы для обновления:
|
||||
`tests/unit/test_cores/test_knowledge_manager.py`
|
||||
`tests/unit/test_storage/test_triples_cassandra_storage.py`
|
||||
`tests/unit/test_query/test_triples_cassandra_query.py`
|
||||
`tests/integration/test_objects_cassandra_integration.py`
|
||||
|
||||
## Стратегия реализации
|
||||
|
||||
### Этап 1: Создание общего вспомогательного класса конфигурации
|
||||
Создайте вспомогательные функции для стандартизации конфигурации Cassandra во всех процессорах:
|
||||
|
||||
```python
|
||||
import os
|
||||
import argparse
|
||||
|
||||
def get_cassandra_defaults():
|
||||
"""Get default values from environment variables or fallback."""
|
||||
return {
|
||||
'host': os.getenv('CASSANDRA_HOST', 'cassandra'),
|
||||
'username': os.getenv('CASSANDRA_USERNAME'),
|
||||
'password': os.getenv('CASSANDRA_PASSWORD')
|
||||
}
|
||||
|
||||
def add_cassandra_args(parser: argparse.ArgumentParser):
|
||||
"""
|
||||
Add standardized Cassandra arguments to an argument parser.
|
||||
Shows environment variable values in help text.
|
||||
"""
|
||||
defaults = get_cassandra_defaults()
|
||||
|
||||
# Format help text with env var indication
|
||||
host_help = f"Cassandra host list, comma-separated (default: {defaults['host']})"
|
||||
if 'CASSANDRA_HOST' in os.environ:
|
||||
host_help += " [from CASSANDRA_HOST]"
|
||||
|
||||
username_help = f"Cassandra username"
|
||||
if defaults['username']:
|
||||
username_help += f" (default: {defaults['username']})"
|
||||
if 'CASSANDRA_USERNAME' in os.environ:
|
||||
username_help += " [from CASSANDRA_USERNAME]"
|
||||
|
||||
password_help = "Cassandra password"
|
||||
if defaults['password']:
|
||||
password_help += " (default: <set>)"
|
||||
if 'CASSANDRA_PASSWORD' in os.environ:
|
||||
password_help += " [from CASSANDRA_PASSWORD]"
|
||||
|
||||
parser.add_argument(
|
||||
'--cassandra-host',
|
||||
default=defaults['host'],
|
||||
help=host_help
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--cassandra-username',
|
||||
default=defaults['username'],
|
||||
help=username_help
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--cassandra-password',
|
||||
default=defaults['password'],
|
||||
help=password_help
|
||||
)
|
||||
|
||||
def resolve_cassandra_config(args) -> tuple[list[str], str|None, str|None]:
|
||||
"""
|
||||
Convert argparse args to Cassandra configuration.
|
||||
|
||||
Returns:
|
||||
tuple: (hosts_list, username, password)
|
||||
"""
|
||||
# Convert host string to list
|
||||
if isinstance(args.cassandra_host, str):
|
||||
hosts = [h.strip() for h in args.cassandra_host.split(',')]
|
||||
else:
|
||||
hosts = args.cassandra_host
|
||||
|
||||
return hosts, args.cassandra_username, args.cassandra_password
|
||||
```
|
||||
|
||||
### Фаза 2: Обновление модулей с использованием параметров `graph_*`
|
||||
1. Изменить имена параметров с `graph_*` на `cassandra_*`
|
||||
2. Заменить собственные методы `add_args()` на стандартизированные методы `add_cassandra_args()`
|
||||
3. Использовать общие вспомогательные функции конфигурации
|
||||
4. Обновить строки документации
|
||||
|
||||
Пример преобразования:
|
||||
```python
|
||||
# OLD CODE
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
parser.add_argument(
|
||||
'-g', '--graph-host',
|
||||
default="localhost",
|
||||
help=f'Graph host (default: localhost)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--graph-username',
|
||||
default=None,
|
||||
help=f'Cassandra username'
|
||||
)
|
||||
|
||||
# NEW CODE
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
FlowProcessor.add_args(parser)
|
||||
add_cassandra_args(parser) # Use standard helper
|
||||
```
|
||||
|
||||
### Фаза 3: Обновление модулей с использованием параметров `cassandra_*`
|
||||
1. Добавить поддержку аргументов командной строки там, где это отсутствует (например, `kg-store`).
|
||||
2. Заменить существующие определения аргументов на `add_cassandra_args()`.
|
||||
3. Использовать `resolve_cassandra_config()` для обеспечения согласованности разрешения.
|
||||
4. Обеспечить согласованную обработку списка хостов.
|
||||
|
||||
### Фаза 4: Обновление тестов и документации
|
||||
1. Обновить все файлы тестов для использования новых имен параметров.
|
||||
2. Обновить документацию CLI.
|
||||
3. Обновить документацию API.
|
||||
4. Добавить документацию по переменным окружения.
|
||||
|
||||
## Обратная совместимость
|
||||
|
||||
Для поддержания обратной совместимости во время перехода:
|
||||
|
||||
1. **Предупреждения об устаревании** для параметров `graph_*`.
|
||||
2. **Псевдонимы параметров** - изначально принимать как старые, так и новые имена.
|
||||
3. **Поэтапное внедрение** в течение нескольких релизов.
|
||||
4. **Обновления документации** с руководством по миграции.
|
||||
|
||||
Пример кода обратной совместимости:
|
||||
```python
|
||||
def __init__(self, **params):
|
||||
# Handle deprecated graph_* parameters
|
||||
if 'graph_host' in params:
|
||||
warnings.warn("graph_host is deprecated, use cassandra_host", DeprecationWarning)
|
||||
params.setdefault('cassandra_host', params.pop('graph_host'))
|
||||
|
||||
if 'graph_username' in params:
|
||||
warnings.warn("graph_username is deprecated, use cassandra_username", DeprecationWarning)
|
||||
params.setdefault('cassandra_username', params.pop('graph_username'))
|
||||
|
||||
# ... continue with standard resolution
|
||||
```
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
1. **Юнит-тесты** для логики разрешения конфигурации
|
||||
2. **Интеграционные тесты** с различными комбинациями конфигурации
|
||||
3. **Тесты переменных окружения**
|
||||
4. **Тесты обратной совместимости** с устаревшими параметрами
|
||||
5. **Тесты Docker Compose** с переменными окружения
|
||||
|
||||
## Обновления документации
|
||||
|
||||
1. Обновить всю документацию по командам CLI
|
||||
2. Обновить документацию API
|
||||
3. Создать руководство по миграции
|
||||
4. Обновить примеры Docker Compose
|
||||
5. Обновить справочную документацию по конфигурации
|
||||
|
||||
## Риски и меры по их снижению
|
||||
|
||||
| Риск | Влияние | Меры по снижению |
|
||||
|------|--------|------------|
|
||||
| Изменения, нарушающие работу пользователей | Высокое | Реализовать период обратной совместимости |
|
||||
| Спутанность конфигурации во время перехода | Среднее | Четкая документация и предупреждения об устаревании |
|
||||
| Сбои в тестах | Среднее | Комплексное обновление тестов |
|
||||
| Проблемы при развертывании Docker | Высокое | Обновить все примеры Docker Compose |
|
||||
|
||||
## Критерии успеха
|
||||
|
||||
[ ] Все модули используют согласованные имена параметров `cassandra_*`
|
||||
[ ] Все процессоры предоставляют настройки Cassandra через аргументы командной строки
|
||||
[ ] Текстовая справка по командной строке показывает значения переменных окружения по умолчанию
|
||||
[ ] Значения паролей никогда не отображаются в справке
|
||||
[ ] Механизм отката к переменным окружения работает правильно
|
||||
[ ] `cassandra_host` последовательно обрабатывается как список внутри
|
||||
[ ] Обратная совместимость поддерживается не менее 2 версий
|
||||
[ ] Все тесты проходят с новой системой конфигурации
|
||||
[ ] Документация полностью обновлена
|
||||
[ ] Примеры Docker Compose работают с переменными окружения
|
||||
|
||||
## План
|
||||
|
||||
**Неделя 1:** Реализовать общий помощник конфигурации и обновить модули `graph_*`
|
||||
**Неделя 2:** Добавить поддержку переменных окружения в существующие модули `cassandra_*`
|
||||
**Неделя 3:** Обновить тесты и документацию
|
||||
**Неделя 4:** Интеграционное тестирование и исправление ошибок
|
||||
|
||||
## Будущие соображения
|
||||
|
||||
Рассмотреть возможность расширения этой модели на другие конфигурации баз данных (например, Elasticsearch)
|
||||
Реализовать проверку конфигурации и улучшенные сообщения об ошибках
|
||||
Добавить поддержку конфигурации пула соединений Cassandra
|
||||
Рассмотреть возможность добавления поддержки файлов конфигурации (.env)
|
||||
687
docs/tech-specs/ru/cassandra-performance-refactor.ru.md
Normal file
687
docs/tech-specs/ru/cassandra-performance-refactor.ru.md
Normal file
|
|
@ -0,0 +1,687 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Технические спецификации: Оптимизация производительности базы знаний Cassandra"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Технические спецификации: Оптимизация производительности базы знаний Cassandra
|
||||
|
||||
> **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.
|
||||
|
||||
**Статус:** Черновик
|
||||
**Автор:** Ассистент
|
||||
**Дата:** 2025-09-18
|
||||
|
||||
## Обзор
|
||||
|
||||
Данная спецификация рассматривает проблемы производительности в реализации базы знаний TrustGraph на Cassandra и предлагает оптимизации для хранения и запросов RDF-тройных.
|
||||
|
||||
## Текущая реализация
|
||||
|
||||
### Структура данных
|
||||
|
||||
Текущая реализация использует структуру данных с одной таблицей в `trustgraph-flow/trustgraph/direct/cassandra_kg.py`:
|
||||
|
||||
```sql
|
||||
CREATE TABLE triples (
|
||||
collection text,
|
||||
s text,
|
||||
p text,
|
||||
o text,
|
||||
PRIMARY KEY (collection, s, p, o)
|
||||
);
|
||||
```
|
||||
|
||||
**Вторичные индексы:**
|
||||
`triples_s` по `s` (субъект)
|
||||
`triples_p` по `p` (предикат)
|
||||
`triples_o` по `o` (объект)
|
||||
|
||||
### Шаблоны запросов
|
||||
|
||||
Текущая реализация поддерживает 8 различных шаблонов запросов:
|
||||
|
||||
1. **get_all(collection, limit=50)** - Получить все тройки для коллекции
|
||||
```sql
|
||||
SELECT s, p, o FROM triples WHERE collection = ? LIMIT 50
|
||||
```
|
||||
|
||||
2. **get_s(collection, s, limit=10)** - Запрос по теме.
|
||||
```sql
|
||||
SELECT p, o FROM triples WHERE collection = ? AND s = ? LIMIT 10
|
||||
```
|
||||
|
||||
3. **get_p(collection, p, limit=10)** - Запрос по предикату.
|
||||
```sql
|
||||
SELECT s, o FROM triples WHERE collection = ? AND p = ? LIMIT 10
|
||||
```
|
||||
|
||||
4. **get_o(collection, o, limit=10)** - Запрос по объекту.
|
||||
```sql
|
||||
SELECT s, p FROM triples WHERE collection = ? AND o = ? LIMIT 10
|
||||
```
|
||||
|
||||
5. **get_sp(collection, s, p, limit=10)** - Запрос по субъекту + предикату.
|
||||
```sql
|
||||
SELECT o FROM triples WHERE collection = ? AND s = ? AND p = ? LIMIT 10
|
||||
```
|
||||
|
||||
6. **get_po(collection, p, o, limit=10)** - Запрос по предикату + объекту ⚠️
|
||||
```sql
|
||||
SELECT s FROM triples WHERE collection = ? AND p = ? AND o = ? LIMIT 10 ALLOW FILTERING
|
||||
```
|
||||
|
||||
7. **get_os(collection, o, s, limit=10)** - Запрос по объекту + субъекту ⚠️
|
||||
```sql
|
||||
SELECT p FROM triples WHERE collection = ? AND o = ? AND s = ? LIMIT 10 ALLOW FILTERING
|
||||
```
|
||||
|
||||
8. **get_spo(collection, s, p, o, limit=10)** - Точное соответствие тройке.
|
||||
```sql
|
||||
SELECT s as x FROM triples WHERE collection = ? AND s = ? AND p = ? AND o = ? LIMIT 10
|
||||
```
|
||||
|
||||
### Текущая архитектура
|
||||
|
||||
**Файл: `trustgraph-flow/trustgraph/direct/cassandra_kg.py`**
|
||||
Один класс `KnowledgeGraph`, обрабатывающий все операции
|
||||
Объединение подключений через глобальный список `_active_clusters`
|
||||
Фиксированное имя таблицы: `"triples"`
|
||||
Пространство ключей для каждой модели пользователя
|
||||
Репликация SimpleStrategy с коэффициентом 1
|
||||
|
||||
**Точки интеграции:**
|
||||
**Путь записи:** `trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
|
||||
**Путь запроса:** `trustgraph-flow/trustgraph/query/triples/cassandra/service.py`
|
||||
**Хранилище знаний:** `trustgraph-flow/trustgraph/tables/knowledge.py`
|
||||
|
||||
## Выявленные проблемы с производительностью
|
||||
|
||||
### Проблемы на уровне схемы
|
||||
|
||||
1. **Неэффективный дизайн первичного ключа**
|
||||
Текущий: `PRIMARY KEY (collection, s, p, o)`
|
||||
Приводит к плохой кластеризации для распространенных шаблонов доступа
|
||||
Заставляет использовать дорогостоящие вторичные индексы
|
||||
|
||||
2. **Чрезмерное использование вторичных индексов** ⚠️
|
||||
Три вторичных индекса на столбцах с высокой кардинальностью (s, p, o)
|
||||
Вторичные индексы в Cassandra дороги и плохо масштабируются
|
||||
Запросы 6 и 7 требуют `ALLOW FILTERING`, что указывает на плохое моделирование данных
|
||||
|
||||
3. **Риск "горячих" разделов**
|
||||
Один ключ раздела `collection` может создавать "горячие" разделы
|
||||
Большие коллекции будут концентрироваться на отдельных узлах
|
||||
Отсутствует стратегия распределения для балансировки нагрузки
|
||||
|
||||
### Проблемы на уровне запросов
|
||||
|
||||
1. **Использование ALLOW FILTERING** ⚠️
|
||||
Два типа запросов (get_po, get_os) требуют `ALLOW FILTERING`
|
||||
Эти запросы сканируют несколько разделов и чрезвычайно дороги
|
||||
Производительность ухудшается линейно с увеличением объема данных
|
||||
|
||||
2. **Неэффективные шаблоны доступа**
|
||||
Отсутствует оптимизация для распространенных шаблонов запросов RDF
|
||||
Отсутствуют составные индексы для часто используемых комбинаций запросов
|
||||
Не учитываются шаблоны обхода графов
|
||||
|
||||
3. **Отсутствие оптимизации запросов**
|
||||
Отсутствует кэширование подготовленных выражений
|
||||
Отсутствуют подсказки или стратегии оптимизации запросов
|
||||
Не учитывается постраничная навигация, кроме простого LIMIT
|
||||
|
||||
## Описание проблемы
|
||||
|
||||
Текущая реализация базы знаний Cassandra имеет два критических узких места в производительности:
|
||||
|
||||
### 1. Неэффективная производительность запроса get_po
|
||||
|
||||
Запрос `get_po(collection, p, o)` крайне неэффективен, поскольку требует `ALLOW FILTERING`:
|
||||
|
||||
```sql
|
||||
SELECT s FROM triples WHERE collection = ? AND p = ? AND o = ? LIMIT 10 ALLOW FILTERING
|
||||
```
|
||||
|
||||
**Почему это является проблемой:**
|
||||
`ALLOW FILTERING` заставляет Cassandra сканировать все разделы внутри коллекции.
|
||||
Производительность ухудшается линейно с увеличением размера данных.
|
||||
Это распространенный шаблон запросов RDF (поиск объектов, имеющих определенную связь "субъект-предикат-объект").
|
||||
Это создает значительную нагрузку на кластер по мере роста данных.
|
||||
|
||||
### 2. Неоптимальная стратегия кластеризации
|
||||
|
||||
Текущий первичный ключ `PRIMARY KEY (collection, s, p, o)` обеспечивает минимальные преимущества кластеризации:
|
||||
|
||||
**Проблемы с текущей кластеризацией:**
|
||||
`collection` в качестве ключа раздела не обеспечивает эффективное распределение данных.
|
||||
Большинство коллекций содержат разнообразные данные, что делает кластеризацию неэффективной.
|
||||
Отсутствует учет типичных шаблонов доступа в запросах RDF.
|
||||
Большие коллекции создают "горячие" разделы на отдельных узлах.
|
||||
Столбцы кластеризации (s, p, o) не оптимизированы для типичных шаблонов обхода графа.
|
||||
|
||||
**Влияние:**
|
||||
Запросы не получают выгоды от локальности данных.
|
||||
Плохое использование кэша.
|
||||
Неравномерное распределение нагрузки между узлами кластера.
|
||||
"Узкие места" масштабируемости по мере роста коллекций.
|
||||
|
||||
## Предлагаемое решение: Стратегия денормализации с использованием 4 таблиц
|
||||
|
||||
### Обзор
|
||||
|
||||
Замените одну таблицу `triples` на четыре специализированные таблицы, каждая из которых оптимизирована для конкретных шаблонов запросов. Это устраняет необходимость во вторичных индексах и использовании ALLOW FILTERING, обеспечивая при этом оптимальную производительность для всех типов запросов. Четвертая таблица обеспечивает эффективное удаление коллекций, несмотря на составные ключи разделов.
|
||||
|
||||
### Новая структура схемы
|
||||
|
||||
**Таблица 1: Запросы, ориентированные на субъекты (triples_s)**
|
||||
```sql
|
||||
CREATE TABLE triples_s (
|
||||
collection text,
|
||||
s text,
|
||||
p text,
|
||||
o text,
|
||||
PRIMARY KEY ((collection, s), p, o)
|
||||
);
|
||||
```
|
||||
**Оптимизирует:** get_s, get_sp, get_os
|
||||
**Ключ разделения:** (collection, s) - Обеспечивает лучшую распределенность, чем только collection.
|
||||
**Кластеризация:** (p, o) - Обеспечивает эффективный поиск по предикатам/объектам для субъекта.
|
||||
|
||||
**Таблица 2: Запросы предикат-объект (triples_p)**
|
||||
```sql
|
||||
CREATE TABLE triples_p (
|
||||
collection text,
|
||||
p text,
|
||||
o text,
|
||||
s text,
|
||||
PRIMARY KEY ((collection, p), o, s)
|
||||
);
|
||||
```
|
||||
**Оптимизирует:** get_p, get_po (исключает ALLOW FILTERING!)
|
||||
**Ключ партиции:** (collection, p) - Прямой доступ по предикату
|
||||
**Кластеризация:** (o, s) - Эффективный обход объектов и субъектов
|
||||
|
||||
**Таблица 3: Объектно-ориентированные запросы (triples_o)**
|
||||
```sql
|
||||
CREATE TABLE triples_o (
|
||||
collection text,
|
||||
o text,
|
||||
s text,
|
||||
p text,
|
||||
PRIMARY KEY ((collection, o), s, p)
|
||||
);
|
||||
```
|
||||
**Оптимизирует:** get_o
|
||||
**Ключ разделения:** (collection, o) - Прямой доступ по объекту
|
||||
**Кластеризация:** (s, p) - Эффективный обход по субъекту-предикату
|
||||
|
||||
**Таблица 4: Управление коллекциями и запросы SPO (triples_collection)**
|
||||
```sql
|
||||
CREATE TABLE triples_collection (
|
||||
collection text,
|
||||
s text,
|
||||
p text,
|
||||
o text,
|
||||
PRIMARY KEY (collection, s, p, o)
|
||||
);
|
||||
```
|
||||
**Оптимизирует:** get_spo, delete_collection
|
||||
**Разделительный ключ:** только коллекция - Обеспечивает эффективные операции на уровне коллекций.
|
||||
**Кластеризация:** (s, p, o) - Стандартный порядок троек.
|
||||
**Назначение:** Двойное использование для точного поиска SPO и в качестве индекса удаления.
|
||||
|
||||
### Отображение запросов
|
||||
|
||||
| Исходный запрос | Целевая таблица | Улучшение производительности |
|
||||
|----------------|-------------|------------------------|
|
||||
| get_all(collection) | triples_s | ALLOW FILTERING (приемлемо для сканирования) |
|
||||
| get_s(collection, s) | triples_s | Прямой доступ к разделу |
|
||||
| get_p(collection, p) | triples_p | Прямой доступ к разделу |
|
||||
| get_o(collection, o) | triples_o | Прямой доступ к разделу |
|
||||
| get_sp(collection, s, p) | triples_s | Раздел + кластеризация |
|
||||
| get_po(collection, p, o) | triples_p | **Больше не требуется ALLOW FILTERING!** |
|
||||
| get_os(collection, o, s) | triples_o | Раздел + кластеризация |
|
||||
| get_spo(collection, s, p, o) | triples_collection | Точный поиск по ключу |
|
||||
| delete_collection(collection) | triples_collection | Чтение индекса, пакетное удаление всех |
|
||||
|
||||
### Стратегия удаления коллекций
|
||||
|
||||
С составными раздельными ключами мы не можем просто выполнить `DELETE FROM table WHERE collection = ?`. Вместо этого:
|
||||
|
||||
1. **Фаза чтения:** Запрос `triples_collection` для перечисления всех троек:
|
||||
```sql
|
||||
SELECT s, p, o FROM triples_collection WHERE collection = ?
|
||||
```
|
||||
Это эффективно, поскольку `collection` является ключом секции для этой таблицы.
|
||||
|
||||
2. **Фаза удаления:** Для каждой тройки (s, p, o) удалите из всех 4 таблиц, используя полные ключи секций:
|
||||
```sql
|
||||
DELETE FROM triples_s WHERE collection = ? AND s = ? AND p = ? AND o = ?
|
||||
DELETE FROM triples_p WHERE collection = ? AND p = ? AND o = ? AND s = ?
|
||||
DELETE FROM triples_o WHERE collection = ? AND o = ? AND s = ? AND p = ?
|
||||
DELETE FROM triples_collection WHERE collection = ? AND s = ? AND p = ? AND o = ?
|
||||
```
|
||||
Обрабатывается пакетами по 100 элементов для повышения эффективности.
|
||||
|
||||
**Анализ компромиссов:**
|
||||
✅ Поддерживает оптимальную производительность запросов с использованием распределенных разделов.
|
||||
✅ Отсутствуют "горячие" разделы для больших коллекций.
|
||||
❌ Более сложная логика удаления (чтение, а затем удаление).
|
||||
❌ Время удаления пропорционально размеру коллекции.
|
||||
|
||||
### Преимущества
|
||||
|
||||
1. **Устраняет ALLOW FILTERING** - Каждый запрос имеет оптимальный путь доступа (за исключением полного сканирования).
|
||||
2. **Не требуются вторичные индексы** - Каждая таблица ЯВЛЯЕТСЯ индексом для своего шаблона запросов.
|
||||
3. **Более равномерное распределение данных** - Композитные ключи разделов эффективно распределяют нагрузку.
|
||||
4. **Предсказуемая производительность** - Время выполнения запроса пропорционально размеру результата, а не общему объему данных.
|
||||
5. **Использует сильные стороны Cassandra** - Разработана с учетом архитектуры Cassandra.
|
||||
6. **Позволяет удалять коллекции** - `triples_collection` служит индексом для удаления.
|
||||
|
||||
## План реализации
|
||||
|
||||
### Файлы, требующие изменений
|
||||
|
||||
#### Основной файл реализации
|
||||
|
||||
**`trustgraph-flow/trustgraph/direct/cassandra_kg.py`** - Требуется полная переработка.
|
||||
|
||||
**Методы, требующие рефакторинга:**
|
||||
```python
|
||||
# Schema initialization
|
||||
def init(self) -> None # Replace single table with three tables
|
||||
|
||||
# Insert operations
|
||||
def insert(self, collection, s, p, o) -> None # Write to all three tables
|
||||
|
||||
# Query operations (API unchanged, implementation optimized)
|
||||
def get_all(self, collection, limit=50) # Use triples_by_subject
|
||||
def get_s(self, collection, s, limit=10) # Use triples_by_subject
|
||||
def get_p(self, collection, p, limit=10) # Use triples_by_po
|
||||
def get_o(self, collection, o, limit=10) # Use triples_by_object
|
||||
def get_sp(self, collection, s, p, limit=10) # Use triples_by_subject
|
||||
def get_po(self, collection, p, o, limit=10) # Use triples_by_po (NO ALLOW FILTERING!)
|
||||
def get_os(self, collection, o, s, limit=10) # Use triples_by_subject
|
||||
def get_spo(self, collection, s, p, o, limit=10) # Use triples_by_subject
|
||||
|
||||
# Collection management
|
||||
def delete_collection(self, collection) -> None # Delete from all three tables
|
||||
```
|
||||
|
||||
#### Интеграционные файлы (изменения в логике не требуются)
|
||||
|
||||
**`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`**
|
||||
Изменения не требуются - используется существующий API KnowledgeGraph.
|
||||
Автоматически получает преимущества от улучшений производительности.
|
||||
|
||||
**`trustgraph-flow/trustgraph/query/triples/cassandra/service.py`**
|
||||
Изменения не требуются - используется существующий API KnowledgeGraph.
|
||||
Автоматически получает преимущества от улучшений производительности.
|
||||
|
||||
### Файлы для тестирования, требующие обновления
|
||||
|
||||
#### Юнит-тесты
|
||||
**`tests/unit/test_storage/test_triples_cassandra_storage.py`**
|
||||
Обновить ожидаемые результаты тестов в связи с изменениями схемы.
|
||||
Добавить тесты для обеспечения согласованности между несколькими таблицами.
|
||||
Проверить отсутствие использования ALLOW FILTERING в планах запросов.
|
||||
|
||||
**`tests/unit/test_query/test_triples_cassandra_query.py`**
|
||||
Обновить утверждения о производительности.
|
||||
Протестировать все 8 шаблонов запросов для новых таблиц.
|
||||
Проверить маршрутизацию запросов к правильным таблицам.
|
||||
|
||||
#### Интеграционные тесты
|
||||
**`tests/integration/test_cassandra_integration.py`**
|
||||
Комплексное тестирование с новой схемой.
|
||||
Сравнение результатов бенчмаркинга производительности.
|
||||
Проверка согласованности данных между таблицами.
|
||||
|
||||
**`tests/unit/test_storage/test_cassandra_config_integration.py`**
|
||||
Обновить тесты проверки схемы.
|
||||
Протестировать сценарии миграции.
|
||||
|
||||
### Стратегия реализации
|
||||
|
||||
#### Фаза 1: Схема и основные методы
|
||||
1. **Переписать метод `init()`** - Создать четыре таблицы вместо одной.
|
||||
2. **Переписать метод `insert()`** - Выполнять пакетные записи во все четыре таблицы.
|
||||
3. **Реализовать подготовленные запросы** - Для оптимальной производительности.
|
||||
4. **Добавить логику маршрутизации таблиц** - Направлять запросы к оптимальным таблицам.
|
||||
5. **Реализовать удаление коллекций** - Читать из triples_collection, пакетно удалять из всех таблиц.
|
||||
|
||||
#### Фаза 2: Оптимизация методов запросов
|
||||
1. **Переписать каждый метод get_*** для использования оптимальной таблицы.
|
||||
2. **Удалить все случаи использования ALLOW FILTERING**.
|
||||
3. **Реализовать эффективное использование ключей кластеризации**.
|
||||
4. **Добавить логирование производительности запросов**.
|
||||
|
||||
#### Фаза 3: Управление коллекциями
|
||||
1. **Обновить `delete_collection()`** - Удалить из всех трех таблиц.
|
||||
2. **Добавить проверку согласованности** - Обеспечить синхронизацию всех таблиц.
|
||||
3. **Реализовать пакетные операции** - Для атомарных операций с несколькими таблицами.
|
||||
|
||||
### Ключевые детали реализации
|
||||
|
||||
#### Стратегия пакетной записи
|
||||
```python
|
||||
def insert(self, collection, s, p, o):
|
||||
batch = BatchStatement()
|
||||
|
||||
# Insert into all four tables
|
||||
batch.add(self.insert_subject_stmt, (collection, s, p, o))
|
||||
batch.add(self.insert_po_stmt, (collection, p, o, s))
|
||||
batch.add(self.insert_object_stmt, (collection, o, s, p))
|
||||
batch.add(self.insert_collection_stmt, (collection, s, p, o))
|
||||
|
||||
self.session.execute(batch)
|
||||
```
|
||||
|
||||
#### Логика маршрутизации запросов
|
||||
```python
|
||||
def get_po(self, collection, p, o, limit=10):
|
||||
# Route to triples_p table - NO ALLOW FILTERING!
|
||||
return self.session.execute(
|
||||
self.get_po_stmt,
|
||||
(collection, p, o, limit)
|
||||
)
|
||||
|
||||
def get_spo(self, collection, s, p, o, limit=10):
|
||||
# Route to triples_collection table for exact SPO lookup
|
||||
return self.session.execute(
|
||||
self.get_spo_stmt,
|
||||
(collection, s, p, o, limit)
|
||||
)
|
||||
```
|
||||
|
||||
#### Логика удаления коллекции
|
||||
```python
|
||||
def delete_collection(self, collection):
|
||||
# Step 1: Read all triples from collection table
|
||||
rows = self.session.execute(
|
||||
f"SELECT s, p, o FROM {self.collection_table} WHERE collection = %s",
|
||||
(collection,)
|
||||
)
|
||||
|
||||
# Step 2: Batch delete from all 4 tables
|
||||
batch = BatchStatement()
|
||||
count = 0
|
||||
|
||||
for row in rows:
|
||||
s, p, o = row.s, row.p, row.o
|
||||
|
||||
# Delete using full partition keys for each table
|
||||
batch.add(SimpleStatement(
|
||||
f"DELETE FROM {self.subject_table} WHERE collection = ? AND s = ? AND p = ? AND o = ?"
|
||||
), (collection, s, p, o))
|
||||
|
||||
batch.add(SimpleStatement(
|
||||
f"DELETE FROM {self.po_table} WHERE collection = ? AND p = ? AND o = ? AND s = ?"
|
||||
), (collection, p, o, s))
|
||||
|
||||
batch.add(SimpleStatement(
|
||||
f"DELETE FROM {self.object_table} WHERE collection = ? AND o = ? AND s = ? AND p = ?"
|
||||
), (collection, o, s, p))
|
||||
|
||||
batch.add(SimpleStatement(
|
||||
f"DELETE FROM {self.collection_table} WHERE collection = ? AND s = ? AND p = ? AND o = ?"
|
||||
), (collection, s, p, o))
|
||||
|
||||
count += 1
|
||||
|
||||
# Execute every 100 triples to avoid oversized batches
|
||||
if count % 100 == 0:
|
||||
self.session.execute(batch)
|
||||
batch = BatchStatement()
|
||||
|
||||
# Execute remaining deletions
|
||||
if count % 100 != 0:
|
||||
self.session.execute(batch)
|
||||
|
||||
logger.info(f"Deleted {count} triples from collection {collection}")
|
||||
```
|
||||
|
||||
#### Оптимизация подготовленных выражений
|
||||
```python
|
||||
def prepare_statements(self):
|
||||
# Cache prepared statements for better performance
|
||||
self.insert_subject_stmt = self.session.prepare(
|
||||
f"INSERT INTO {self.subject_table} (collection, s, p, o) VALUES (?, ?, ?, ?)"
|
||||
)
|
||||
self.insert_po_stmt = self.session.prepare(
|
||||
f"INSERT INTO {self.po_table} (collection, p, o, s) VALUES (?, ?, ?, ?)"
|
||||
)
|
||||
self.insert_object_stmt = self.session.prepare(
|
||||
f"INSERT INTO {self.object_table} (collection, o, s, p) VALUES (?, ?, ?, ?)"
|
||||
)
|
||||
self.insert_collection_stmt = self.session.prepare(
|
||||
f"INSERT INTO {self.collection_table} (collection, s, p, o) VALUES (?, ?, ?, ?)"
|
||||
)
|
||||
# ... query statements
|
||||
```
|
||||
|
||||
## Стратегия миграции
|
||||
|
||||
### Подход к миграции данных
|
||||
|
||||
#### Вариант 1: Развертывание Blue-Green (Рекомендуется)
|
||||
1. **Разверните новую схему параллельно с существующей** - Временно используйте разные имена таблиц.
|
||||
2. **Период двойной записи** - Записывайте данные как в старую, так и в новую схемы во время перехода.
|
||||
3. **Фоновая миграция** - Скопируйте существующие данные в новые таблицы.
|
||||
4. **Перенаправление операций чтения** - Направляйте запросы к новым таблицам после миграции данных.
|
||||
5. **Удаление старых таблиц** - После периода проверки.
|
||||
|
||||
#### Вариант 2: Миграция на месте
|
||||
1. **Добавление схемы** - Создайте новые таблицы в существующем пространстве ключей.
|
||||
2. **Скрипт миграции данных** - Пакетная копия из старой таблицы в новые таблицы.
|
||||
3. **Обновление приложения** - Разверните новый код после завершения миграции.
|
||||
4. **Очистка старой таблицы** - Удалите старую таблицу и индексы.
|
||||
|
||||
### Обратная совместимость
|
||||
|
||||
#### Стратегия развертывания
|
||||
```python
|
||||
# Environment variable to control table usage during migration
|
||||
USE_LEGACY_TABLES = os.getenv('CASSANDRA_USE_LEGACY', 'false').lower() == 'true'
|
||||
|
||||
class KnowledgeGraph:
|
||||
def __init__(self, ...):
|
||||
if USE_LEGACY_TABLES:
|
||||
self.init_legacy_schema()
|
||||
else:
|
||||
self.init_optimized_schema()
|
||||
```
|
||||
|
||||
#### Скрипт миграции
|
||||
```python
|
||||
def migrate_data():
|
||||
# Read from old table
|
||||
old_triples = session.execute("SELECT collection, s, p, o FROM triples")
|
||||
|
||||
# Batch write to new tables
|
||||
for batch in batched(old_triples, 100):
|
||||
batch_stmt = BatchStatement()
|
||||
for row in batch:
|
||||
# Add to all three new tables
|
||||
batch_stmt.add(insert_subject_stmt, row)
|
||||
batch_stmt.add(insert_po_stmt, (row.collection, row.p, row.o, row.s))
|
||||
batch_stmt.add(insert_object_stmt, (row.collection, row.o, row.s, row.p))
|
||||
session.execute(batch_stmt)
|
||||
```
|
||||
|
||||
### Стратегия проверки
|
||||
|
||||
#### Проверки согласованности данных
|
||||
```python
|
||||
def validate_migration():
|
||||
# Count total records in old vs new tables
|
||||
old_count = session.execute("SELECT COUNT(*) FROM triples WHERE collection = ?", (collection,))
|
||||
new_count = session.execute("SELECT COUNT(*) FROM triples_by_subject WHERE collection = ?", (collection,))
|
||||
|
||||
assert old_count == new_count, f"Record count mismatch: {old_count} vs {new_count}"
|
||||
|
||||
# Spot check random samples
|
||||
sample_queries = generate_test_queries()
|
||||
for query in sample_queries:
|
||||
old_result = execute_legacy_query(query)
|
||||
new_result = execute_optimized_query(query)
|
||||
assert old_result == new_result, f"Query results differ for {query}"
|
||||
```
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
### Тестирование производительности
|
||||
|
||||
#### Сценарии для бенчмаркинга
|
||||
1. **Сравнение производительности запросов**
|
||||
Показатели производительности до и после для всех 8 типов запросов
|
||||
Акцент на улучшении производительности запроса `get_po` (удаление `ALLOW FILTERING`)
|
||||
Измерение задержки запросов при различных объемах данных
|
||||
|
||||
2. **Тестирование нагрузки**
|
||||
Одновременное выполнение запросов
|
||||
Скорость записи с использованием пакетных операций
|
||||
Использование памяти и ЦП
|
||||
|
||||
3. **Тестирование масштабируемости**
|
||||
Производительность при увеличении размеров коллекций
|
||||
Распределение запросов по нескольким коллекциям
|
||||
Использование узлов кластера
|
||||
|
||||
#### Наборы тестовых данных
|
||||
**Маленький:** 10 тысяч троек на коллекцию
|
||||
**Средний:** 100 тысяч троек на коллекцию
|
||||
**Большой:** 1 миллион и более троек на коллекцию
|
||||
**Несколько коллекций:** Тестирование распределения партиций
|
||||
|
||||
### Функциональное тестирование
|
||||
|
||||
#### Обновления модульных тестов
|
||||
```python
|
||||
# Example test structure for new implementation
|
||||
class TestCassandraKGPerformance:
|
||||
def test_get_po_no_allow_filtering(self):
|
||||
# Verify get_po queries don't use ALLOW FILTERING
|
||||
with patch('cassandra.cluster.Session.execute') as mock_execute:
|
||||
kg.get_po('test_collection', 'predicate', 'object')
|
||||
executed_query = mock_execute.call_args[0][0]
|
||||
assert 'ALLOW FILTERING' not in executed_query
|
||||
|
||||
def test_multi_table_consistency(self):
|
||||
# Verify all tables stay in sync
|
||||
kg.insert('test', 's1', 'p1', 'o1')
|
||||
|
||||
# Check all tables contain the triple
|
||||
assert_triple_exists('triples_by_subject', 'test', 's1', 'p1', 'o1')
|
||||
assert_triple_exists('triples_by_po', 'test', 'p1', 'o1', 's1')
|
||||
assert_triple_exists('triples_by_object', 'test', 'o1', 's1', 'p1')
|
||||
```
|
||||
|
||||
#### Обновления результатов интеграционного тестирования
|
||||
```python
|
||||
class TestCassandraIntegration:
|
||||
def test_query_performance_regression(self):
|
||||
# Ensure new implementation is faster than old
|
||||
old_time = benchmark_legacy_get_po()
|
||||
new_time = benchmark_optimized_get_po()
|
||||
assert new_time < old_time * 0.5 # At least 50% improvement
|
||||
|
||||
def test_end_to_end_workflow(self):
|
||||
# Test complete write -> query -> delete cycle
|
||||
# Verify no performance degradation in integration
|
||||
```
|
||||
|
||||
### План отката
|
||||
|
||||
#### Быстрая стратегия отката
|
||||
1. **Переключение переменной окружения** - Немедленный возврат к устаревшим таблицам.
|
||||
2. **Сохранение устаревших таблиц** - Не удалять до тех пор, пока производительность не будет подтверждена.
|
||||
3. **Сигналы мониторинга** - Автоматический запуск отката на основе показателей ошибок/задержек.
|
||||
|
||||
#### Проверка отката
|
||||
```python
|
||||
def rollback_to_legacy():
|
||||
# Set environment variable
|
||||
os.environ['CASSANDRA_USE_LEGACY'] = 'true'
|
||||
|
||||
# Restart services to pick up change
|
||||
restart_cassandra_services()
|
||||
|
||||
# Validate functionality
|
||||
run_smoke_tests()
|
||||
```
|
||||
|
||||
## Риски и соображения
|
||||
|
||||
### Риски производительности
|
||||
**Увеличение времени записи** - 4 операции записи на каждую вставку (на 33% больше, чем при использовании 3-х таблиц)
|
||||
**Избыточность хранения** - В 4 раза больше места для хранения (на 33% больше, чем при использовании 3-х таблиц)
|
||||
**Сбои пакетной записи** - Требуется правильная обработка ошибок
|
||||
**Сложность удаления** - Удаление коллекции требует цикла "чтение-удаление"
|
||||
|
||||
### Операционные риски
|
||||
**Сложность миграции** - Миграция данных для больших наборов данных
|
||||
**Проблемы с согласованностью** - Обеспечение синхронизации всех таблиц
|
||||
**Недостаточность мониторинга** - Необходимы новые метрики для операций с несколькими таблицами
|
||||
|
||||
### Стратегии смягчения
|
||||
1. **Постепенное внедрение** - Начните с небольших коллекций
|
||||
2. **Комплексный мониторинг** - Отслеживайте все метрики производительности
|
||||
3. **Автоматизированная проверка** - Постоянная проверка согласованности
|
||||
4. **Возможность быстрого отката** - Выбор таблицы на основе среды
|
||||
|
||||
## Критерии успеха
|
||||
|
||||
### Улучшения производительности
|
||||
[ ] **Устранение ALLOW FILTERING** - Запросы get_po и get_os выполняются без фильтрации
|
||||
[ ] **Сокращение задержки запросов** - Улучшение времени отклика запросов на 50% или более
|
||||
[ ] **Более равномерное распределение нагрузки** - Отсутствие "горячих" партиций, равномерная нагрузка на узлы кластера
|
||||
[ ] **Масштабируемая производительность** - Время запроса пропорционально размеру результата, а не общему объему данных
|
||||
|
||||
### Функциональные требования
|
||||
[ ] **Совместимость API** - Весь существующий код продолжает работать без изменений
|
||||
[ ] **Согласованность данных** - Все три таблицы остаются синхронизированными
|
||||
[ ] **Отсутствие потери данных** - Миграция сохраняет все существующие тройки
|
||||
[ ] **Обратная совместимость** - Возможность отката к устаревшей схеме
|
||||
|
||||
### Операционные требования
|
||||
[ ] **Безопасная миграция** - Развертывание по принципу "синий-зеленый" с возможностью отката
|
||||
[ ] **Покрытие мониторингом** - Комплексные метрики для операций с несколькими таблицами
|
||||
[ ] **Покрытие тестами** - Все шаблоны запросов протестированы с использованием эталонных показателей производительности
|
||||
[ ] **Документация** - Обновленные процедуры развертывания и эксплуатации
|
||||
|
||||
## План
|
||||
|
||||
### Фаза 1: Реализация
|
||||
[ ] Переписать `cassandra_kg.py` с использованием схемы нескольких таблиц
|
||||
[ ] Реализовать пакетные операции записи
|
||||
[ ] Добавить оптимизацию с использованием подготовленных выражений
|
||||
[ ] Обновить модульные тесты
|
||||
|
||||
### Фаза 2: Интеграционное тестирование
|
||||
[ ] Обновить интеграционные тесты
|
||||
[ ] Эталонное тестирование производительности
|
||||
[ ] Тестирование нагрузки с использованием реалистичных объемов данных
|
||||
[ ] Скрипты проверки согласованности данных
|
||||
|
||||
### Фаза 3: Планирование миграции
|
||||
[ ] Скрипты развертывания по принципу "синий-зеленый"
|
||||
[ ] Инструменты миграции данных
|
||||
[ ] Обновления панели мониторинга
|
||||
[ ] Процедуры отката
|
||||
|
||||
### Фаза 4: Развертывание в производственной среде
|
||||
[ ] Поэтапное развертывание в производственной среде
|
||||
[ ] Мониторинг и проверка производительности
|
||||
[ ] Очистка устаревших таблиц
|
||||
[ ] Обновление документации
|
||||
|
||||
## Заключение
|
||||
|
||||
Эта стратегия денормализации с использованием нескольких таблиц напрямую решает две критические проблемы производительности:
|
||||
|
||||
1. **Устраняет дорогостоящий ALLOW FILTERING**, предоставляя оптимальные структуры таблиц для каждого шаблона запроса
|
||||
2. **Улучшает эффективность кластеризации** за счет составных ключей партиций, которые правильно распределяют нагрузку
|
||||
|
||||
Этот подход использует сильные стороны Cassandra, сохраняя при этом полную совместимость API, что обеспечивает автоматическое получение преимуществ от улучшений производительности для существующего кода.
|
||||
410
docs/tech-specs/ru/collection-management.ru.md
Normal file
410
docs/tech-specs/ru/collection-management.ru.md
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает возможности управления коллекциями для TrustGraph, требуя явного создания коллекций и обеспечивая прямой контроль над жизненным циклом коллекций. Коллекции должны быть явно созданы перед использованием, что обеспечивает надлежащую синхронизацию между метаданными библиотекаря и всеми хранилищами. Эта функция поддерживает четыре основных сценария использования:
|
||||
|
||||
1. **Создание коллекции**: Явно создавайте коллекции перед сохранением данных.
|
||||
2. **Перечисление коллекций**: Просматривайте все существующие коллекции в системе.
|
||||
3. **Управление метаданными коллекции**: Обновляйте имена, описания и теги коллекций.
|
||||
4. **Удаление коллекции**: Удаляйте коллекции и связанные с ними данные во всех типах хранилищ.
|
||||
|
||||
## Цели
|
||||
|
||||
**Явное создание коллекции**: Требуйте, чтобы коллекции создавались перед сохранением данных.
|
||||
**Синхронизация хранилищ**: Обеспечьте наличие коллекций во всех хранилищах (векторах, объектах, тройках).
|
||||
**Видимость коллекций**: Предоставьте пользователям возможность перечислять и просматривать все коллекции в их среде.
|
||||
**Очистка коллекций**: Разрешите удаление коллекций, которые больше не нужны.
|
||||
**Организация коллекций**: Поддержка меток и тегов для лучшего отслеживания и обнаружения коллекций.
|
||||
**Управление метаданными**: Связывайте значимые метаданные с коллекциями для обеспечения оперативной ясности.
|
||||
**Обнаружение коллекций**: Облегчите поиск конкретных коллекций с помощью фильтрации и поиска.
|
||||
**Оперативная прозрачность**: Обеспечьте четкую видимость жизненного цикла и использования коллекций.
|
||||
**Управление ресурсами**: Позвольте очищать неиспользуемые коллекции для оптимизации использования ресурсов.
|
||||
**Целостность данных**: Предотвратите появление "сиротских" коллекций в хранилище без отслеживания метаданных.
|
||||
|
||||
## Предыстория
|
||||
|
||||
Ранее коллекции в TrustGraph создавались неявно во время операций загрузки данных, что приводило к проблемам синхронизации, когда коллекции могли существовать в хранилищах без соответствующих метаданных в библиотекаре. Это создавало проблемы управления и потенциальные "сиротские" данные.
|
||||
|
||||
Модель явного создания коллекций решает эти проблемы следующим образом:
|
||||
Требует создания коллекций перед использованием через `tg-set-collection`.
|
||||
Передает информацию о создании коллекции во все хранилища.
|
||||
Поддерживает синхронизированное состояние между метаданными библиотекаря и хранилищем.
|
||||
Предотвращает запись в несуществующие коллекции.
|
||||
Обеспечивает четкое управление жизненным циклом коллекций.
|
||||
|
||||
Эта спецификация определяет модель явного управления коллекциями. Требуя явного создания коллекций, TrustGraph обеспечивает:
|
||||
Отслеживание коллекций в метаданных библиотекаря с момента создания.
|
||||
Знание всеми хранилищами о коллекциях до получения данных.
|
||||
Отсутствие "сиротских" коллекций в хранилище.
|
||||
Четкую оперативную видимость и контроль над жизненным циклом коллекций.
|
||||
Последовательную обработку ошибок при выполнении операций, ссылающихся на несуществующие коллекции.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Архитектура
|
||||
|
||||
Система управления коллекциями будет реализована в существующей инфраструктуре TrustGraph:
|
||||
|
||||
1. **Интеграция с сервисом библиотекаря**
|
||||
Операции управления коллекциями будут добавлены в существующий сервис библиотекаря.
|
||||
Не требуется новый сервис - используется существующая аутентификация и модели доступа.
|
||||
Обрабатывает перечисление коллекций, удаление и управление метаданными.
|
||||
|
||||
Модуль: trustgraph-librarian
|
||||
|
||||
2. **Таблица метаданных коллекций Cassandra**
|
||||
Новая таблица в существующем пространстве ключей библиотекаря.
|
||||
Хранит метаданные коллекций с доступом, специфичным для пользователя.
|
||||
Первичный ключ: (user_id, collection_id) для обеспечения правильной многопользовательской среды.
|
||||
|
||||
Модуль: trustgraph-librarian
|
||||
|
||||
3. **CLI для управления коллекциями**
|
||||
Интерфейс командной строки для операций с коллекциями.
|
||||
Предоставляет команды для перечисления, удаления, добавления меток и тегов.
|
||||
Интегрируется с существующей инфраструктурой CLI.
|
||||
|
||||
Модуль: trustgraph-cli
|
||||
|
||||
### Модели данных
|
||||
|
||||
#### Таблица метаданных коллекций Cassandra
|
||||
|
||||
Метаданные коллекций будут храниться в структурированной таблице Cassandra в пространстве ключей библиотекаря:
|
||||
|
||||
```sql
|
||||
CREATE TABLE collections (
|
||||
user text,
|
||||
collection text,
|
||||
name text,
|
||||
description text,
|
||||
tags set<text>,
|
||||
created_at timestamp,
|
||||
updated_at timestamp,
|
||||
PRIMARY KEY (user, collection)
|
||||
);
|
||||
```
|
||||
|
||||
Структура таблицы:
|
||||
**user** + **collection**: Составной первичный ключ, обеспечивающий изоляцию пользователей
|
||||
**name**: Человекочитаемое имя коллекции
|
||||
**description**: Подробное описание назначения коллекции
|
||||
**tags**: Набор тегов для категоризации и фильтрации
|
||||
**created_at**: Временная метка создания коллекции
|
||||
**updated_at**: Временная метка последнего изменения
|
||||
|
||||
Этот подход позволяет:
|
||||
Управление коллекциями с поддержкой многопользовательского режима и изоляцией пользователей
|
||||
Эффективный поиск по пользователю и коллекции
|
||||
Гибкая система тегирования для организации
|
||||
Отслеживание жизненного цикла для получения оперативной информации
|
||||
|
||||
#### Жизненный цикл коллекции
|
||||
|
||||
Коллекции явно создаются в библиотечнике, прежде чем можно будет выполнять операции с данными:
|
||||
|
||||
1. **Создание коллекции** (Два пути):
|
||||
|
||||
**Путь A: Создание, инициированное пользователем** через `tg-set-collection`:
|
||||
Пользователь предоставляет идентификатор коллекции, имя, описание и теги
|
||||
Библиотечник создает запись метаданных в таблице `collections`
|
||||
Библиотечник отправляет сообщение "create-collection" всем хранилищам данных
|
||||
Все процессоры хранилищ создают коллекцию и подтверждают успешное выполнение
|
||||
Коллекция готова для операций с данными
|
||||
|
||||
**Путь B: Автоматическое создание при отправке документа**:
|
||||
Пользователь отправляет документ, указывающий идентификатор коллекции
|
||||
Библиотечник проверяет, существует ли коллекция в таблице метаданных
|
||||
Если коллекция не существует: Библиотечник создает метаданные с настройками по умолчанию (имя = идентификатор коллекции, пустое описание/теги)
|
||||
Библиотечник отправляет сообщение "create-collection" всем хранилищам данных
|
||||
Все процессоры хранилищ создают коллекцию и подтверждают успешное выполнение
|
||||
Обработка документа продолжается после создания коллекции
|
||||
|
||||
Оба пути обеспечивают существование коллекции в метаданных библиотечника И во всех хранилищах данных перед выполнением операций с данными.
|
||||
|
||||
2. **Проверка хранилища**: Операции записи проверяют существование коллекции:
|
||||
Процессоры хранилищ проверяют состояние коллекции перед принятием записи
|
||||
Записи в несуществующие коллекции возвращают ошибку
|
||||
Это предотвращает прямые записи, обходя логику создания коллекции библиотечником
|
||||
|
||||
3. **Поведение при запросах**: Операции запроса корректно обрабатывают несуществующие коллекции:
|
||||
Запросы к несуществующим коллекциям возвращают пустые результаты
|
||||
Не возникает ошибок для операций запроса
|
||||
Позволяет исследовать данные без необходимости существования коллекции
|
||||
|
||||
4. **Обновление метаданных**: Пользователи могут обновлять метаданные коллекции после ее создания:
|
||||
Обновление имени, описания и тегов через `tg-set-collection`
|
||||
Обновления применяются только к метаданным библиотечника
|
||||
Хранилища данных сохраняют коллекцию, но обновления метаданных не распространяются
|
||||
|
||||
5. **Явное удаление**: Пользователи удаляют коллекции через `tg-delete-collection`:
|
||||
Библиотечник отправляет сообщение "delete-collection" всем хранилищам данных
|
||||
Ожидает подтверждение от всех процессоров хранилищ
|
||||
Удаляет запись метаданных библиотечника только после завершения очистки хранилища
|
||||
Обеспечивает отсутствие "осиротевших" данных в хранилище
|
||||
|
||||
**Ключевой принцип**: Библиотечник является центральной точкой управления созданием коллекций. Независимо от того, инициировано ли это пользовательской командой или отправкой документа, библиотечник обеспечивает правильное отслеживание метаданных и синхронизацию хранилищ данных, прежде чем разрешить операции с данными.
|
||||
|
||||
Требуемые операции:
|
||||
**Создание коллекции**: Операция пользователя через `tg-set-collection` ИЛИ автоматическое создание при отправке документа
|
||||
**Обновление метаданных коллекции**: Операция пользователя для изменения имени, описания и тегов
|
||||
**Удаление коллекции**: Операция пользователя для удаления коллекции и ее данных во всех хранилищах
|
||||
**Список коллекций**: Операция пользователя для просмотра коллекций с фильтрацией по тегам
|
||||
|
||||
#### Управление коллекциями в нескольких хранилищах
|
||||
|
||||
Коллекции существуют в нескольких хранилищах данных в TrustGraph:
|
||||
**Векторные хранилища** (Qdrant, Milvus, Pinecone): Хранят вложения и векторные данные
|
||||
**Объектные хранилища** (Cassandra): Хранят документы и файлы
|
||||
**Графовые хранилища** (Cassandra, Neo4j, Memgraph, FalkorDB): Хранят данные графов/RDF
|
||||
|
||||
Каждый тип хранилища реализует:
|
||||
**Отслеживание состояния коллекций**: Поддержание информации о том, какие коллекции существуют.
|
||||
**Создание коллекций**: Прием и обработка операций "создать коллекцию".
|
||||
**Проверка существования коллекций**: Проверка существования коллекции перед записью данных.
|
||||
**Удаление коллекций**: Удаление всех данных для указанной коллекции.
|
||||
|
||||
Сервис библиотекаря координирует операции с коллекциями во всех типах хранилищ, обеспечивая:
|
||||
Создание коллекций во всех бэкендах перед использованием.
|
||||
Подтверждение создания всеми бэкендами перед возвратом успеха.
|
||||
Синхронизированный жизненный цикл коллекций во всех типах хранилища.
|
||||
Последовательную обработку ошибок, когда коллекции не существуют.
|
||||
|
||||
#### Отслеживание состояния коллекций по типу хранилища
|
||||
|
||||
Каждый бэкенд хранилища отслеживает состояние коллекций по-разному, в зависимости от его возможностей:
|
||||
|
||||
**Triple Store на базе Cassandra:**
|
||||
Использует существующую таблицу `triples_collection`.
|
||||
Создает системный маркер-трипл при создании коллекции.
|
||||
Запрос: `SELECT collection FROM triples_collection WHERE collection = ? LIMIT 1`.
|
||||
Эффективная проверка существования коллекции в одной партиции.
|
||||
|
||||
**Векторные хранилища Qdrant/Milvus/Pinecone:**
|
||||
Нативные API коллекций обеспечивают проверку существования.
|
||||
Коллекции создаются с правильной конфигурацией векторов.
|
||||
Метод `collection_exists()` использует API хранилища.
|
||||
Создание коллекции проверяет требования к размерности.
|
||||
|
||||
**Графовые хранилища Neo4j/Memgraph/FalkorDB:**
|
||||
Используют узлы `:CollectionMetadata` для отслеживания коллекций.
|
||||
Свойства узла: `{user, collection, created_at}`.
|
||||
Запрос: `MATCH (c:CollectionMetadata {user: $user, collection: $collection})`.
|
||||
Отделены от узлов данных для четкого разделения.
|
||||
Обеспечивает эффективное перечисление и проверку коллекций.
|
||||
|
||||
**Объектное хранилище на базе Cassandra:**
|
||||
Использует таблицу метаданных коллекции или маркерные строки.
|
||||
Аналогичный шаблон, что и для triple store.
|
||||
Проверяет существование коллекции перед записью документов.
|
||||
|
||||
### API
|
||||
|
||||
API управления коллекциями (Библиотекарь):
|
||||
**Создание/Обновление коллекции**: Создание новой коллекции или обновление существующих метаданных через `tg-set-collection`.
|
||||
**Список коллекций**: Получение коллекций для пользователя с необязательной фильтрацией по тегам.
|
||||
**Удаление коллекции**: Удаление коллекции и связанных данных, распространяясь на все типы хранилищ.
|
||||
|
||||
API управления хранилищем (Все процессоры хранилища):
|
||||
**Создание коллекции**: Обработка операции "создать коллекцию", создание коллекции в хранилище.
|
||||
**Удаление коллекции**: Обработка операции "удалить коллекцию", удаление всех данных коллекции.
|
||||
**Проверка существования коллекции**: Внутренняя проверка перед принятием операций записи.
|
||||
|
||||
API операций с данными (Измененное поведение):
|
||||
**API записи**: Проверка существования коллекции перед принятием данных, возврат ошибки, если коллекция не существует.
|
||||
**API запросов**: Возврат пустых результатов для несуществующих коллекций без ошибки.
|
||||
|
||||
### Детали реализации
|
||||
|
||||
Реализация будет следовать существующим шаблонам TrustGraph для интеграции служб и структуры командной строки.
|
||||
|
||||
#### Каскадное удаление коллекций
|
||||
|
||||
Когда пользователь инициирует удаление коллекции через сервис библиотекаря:
|
||||
|
||||
1. **Проверка метаданных**: Проверка существования коллекции и наличие у пользователя разрешения на удаление.
|
||||
2. **Каскадное удаление в хранилищах**: Библиотекарь координирует удаление во всех хранилищах записи:
|
||||
Хранилище векторов: Удаление вложений и индексов векторов для пользователя и коллекции.
|
||||
Объектное хранилище: Удаление документов и файлов для пользователя и коллекции.
|
||||
Графовое хранилище: Удаление данных графа и триплетов для пользователя и коллекции.
|
||||
3. **Очистка метаданных**: Удаление записи метаданных коллекции из Cassandra.
|
||||
4. **Обработка ошибок**: Если удаление в каком-либо хранилище не удалось, поддерживайте согласованность с помощью механизмов отката или повторной попытки.
|
||||
|
||||
#### Интерфейс управления коллекциями
|
||||
|
||||
**⚠️ УСТАРЕВШИЙ ПОДХОД - ЗАМЕЩЕН ШАБЛОНОМ НА ОСНОВЕ КОНФИГУРАЦИИ**
|
||||
|
||||
Архитектура на основе очереди, описанная ниже, была заменена подходом на основе конфигурации с использованием `CollectionConfigHandler`. Все бэкенды хранилищ теперь получают обновления коллекций через сообщения push конфигурации, а не через выделенные очереди управления.
|
||||
|
||||
~~Все хранилища записи реализуют стандартизированный интерфейс управления коллекциями с общей схемой:~~
|
||||
|
||||
~~**Схема сообщения (`StorageManagementRequest`):**~~
|
||||
```json
|
||||
{
|
||||
"operation": "create-collection" | "delete-collection",
|
||||
"user": "user123",
|
||||
"collection": "documents-2024"
|
||||
}
|
||||
```
|
||||
|
||||
~~**Архитектура очередей:**~~
|
||||
~~**Очередь управления векторными хранилищами** (`vector-storage-management`): векторные/эмбеддинговые хранилища~~
|
||||
~~**Очередь управления хранилищами объектов** (`object-storage-management`): хранилища объектов/документов~~
|
||||
~~**Очередь управления графовыми хранилищами** (`triples-storage-management`): графовые/RDF хранилища~~
|
||||
~~**Очередь обработки ответов хранилища** (`storage-management-response`): все ответы отправляются сюда~~
|
||||
|
||||
**Текущая реализация:**
|
||||
|
||||
Все бэкенды хранилищ теперь используют `CollectionConfigHandler`:
|
||||
**Интеграция с системой push-уведомлений конфигурации**: службы хранилищ регистрируются для получения уведомлений об изменениях конфигурации
|
||||
**Автоматическая синхронизация**: коллекции создаются/удаляются на основе изменений конфигурации
|
||||
**Декларативная модель**: коллекции определены в службе конфигурации, бэкенды синхронизируются для соответствия
|
||||
**Отсутствие запросов/ответов**: устраняет накладные расходы на координацию и отслеживание ответов
|
||||
**Отслеживание состояния коллекций**: поддерживается через кэш `known_collections`
|
||||
**Идемпотентные операции**: безопасно обрабатывать одну и ту же конфигурацию несколько раз
|
||||
|
||||
Каждый бэкенд хранилища реализует:
|
||||
`create_collection(user: str, collection: str, metadata: dict)` - Создание структур коллекций
|
||||
`delete_collection(user: str, collection: str)` - Удаление всех данных коллекций
|
||||
`collection_exists(user: str, collection: str) -> bool` - Проверка перед записью
|
||||
|
||||
#### Рефакторинг графового хранилища Cassandra
|
||||
|
||||
В рамках этой реализации графовое хранилище Cassandra будет переработано с модели "таблица на коллекцию" на унифицированную модель "одна таблица":
|
||||
|
||||
**Текущая архитектура:**
|
||||
Keyspace на пользователя, отдельная таблица на коллекцию
|
||||
Схема: `(s, p, o)` с `PRIMARY KEY (s, p, o)`
|
||||
Имена таблиц: коллекции пользователя становятся отдельными таблицами Cassandra
|
||||
|
||||
**Новая архитектура:**
|
||||
Keyspace на пользователя, одна таблица "triples" для всех коллекций
|
||||
Схема: `(collection, s, p, o)` с `PRIMARY KEY (collection, s, p, o)`
|
||||
Изоляция коллекций через партиционирование коллекций
|
||||
|
||||
**Необходимые изменения:**
|
||||
|
||||
1. **Рефакторинг класса TrustGraph** (`trustgraph/direct/cassandra.py`):
|
||||
Удалить параметр `table` из конструктора, использовать фиксированную таблицу "triples"
|
||||
Добавить параметр `collection` ко всем методам
|
||||
Обновить схему для включения коллекции в качестве первого столбца
|
||||
**Обновления индексов**: Будут созданы новые индексы для поддержки всех 8 шаблонов запросов:
|
||||
Индекс на `(s)` для запросов на основе субъекта
|
||||
Индекс на `(p)` для запросов на основе предиката
|
||||
Индекс на `(o)` для запросов на основе объекта
|
||||
Обратите внимание: Cassandra не поддерживает многоколоночные вторичные индексы, поэтому это одноколоночные индексы
|
||||
|
||||
**Производительность шаблонов запросов**:
|
||||
✅ `get_all()` - сканирование раздела на `collection`
|
||||
✅ `get_s(s)` - эффективно использует первичный ключ (`collection, s`)
|
||||
✅ `get_p(p)` - использует `idx_p` с фильтрацией `collection`
|
||||
✅ `get_o(o)` - использует `idx_o` с фильтрацией `collection`
|
||||
✅ `get_sp(s, p)` - эффективно использует первичный ключ (`collection, s, p`)
|
||||
⚠️ `get_po(p, o)` - требует `ALLOW FILTERING` (использует либо `idx_p`, либо `idx_o` плюс фильтрацию)
|
||||
✅ `get_os(o, s)` - использует `idx_o` с дополнительной фильтрацией на `s`
|
||||
✅ `get_spo(s, p, o)` - эффективно использует полный первичный ключ
|
||||
|
||||
**Примечание об ALLOW FILTERING**: Шаблон запроса `get_po` требует `ALLOW FILTERING`, поскольку ему необходимы ограничения как предиката, так и объекта без подходящего составного индекса. Это допустимо, поскольку этот шаблон запроса менее распространен, чем запросы на основе субъекта, в типичном использовании хранилища тройных данных.
|
||||
|
||||
2. **Обновления модуля записи данных** (`trustgraph/storage/triples/cassandra/write.py`):
|
||||
Поддерживать одно соединение TrustGraph на пользователя вместо одного соединения на (пользователь, коллекция)
|
||||
Передавать коллекцию в операции вставки
|
||||
Улучшенное использование ресурсов за счет меньшего количества соединений
|
||||
|
||||
3. **Обновления сервиса запросов** (`trustgraph/query/triples/cassandra/service.py`):
|
||||
Одно соединение TrustGraph на пользователя
|
||||
Передавать коллекцию во все операции запросов
|
||||
Сохранять ту же логику запросов с параметром коллекции
|
||||
|
||||
**Преимущества:**
|
||||
**Упрощенное удаление коллекций**: Удаление с использованием ключа раздела `collection` во всех 4 таблицах
|
||||
**Эффективность использования ресурсов**: Меньшее количество соединений с базой данных и объектов таблиц
|
||||
**Операции, охватывающие несколько коллекций**: Легче реализовать операции, охватывающие несколько коллекций
|
||||
**Согласованная архитектура**: Соответствует унифицированному подходу к метаданным коллекций
|
||||
**Проверка коллекций**: Легко проверить существование коллекции через таблицу `triples_collection`
|
||||
|
||||
Операции с коллекциями будут выполняться атомарно, где это возможно, и обеспечивать соответствующую обработку ошибок и проверку.
|
||||
|
||||
## Вопросы безопасности
|
||||
|
||||
Управление коллекциями требует соответствующей авторизации для предотвращения несанкционированного доступа или удаления коллекций. Контроль доступа будет соответствовать существующим моделям безопасности TrustGraph.
|
||||
|
||||
## Вопросы производительности
|
||||
|
||||
Операции перечисления коллекций могут требовать постраничной разбивки для сред с большим количеством коллекций. Запросы метаданных должны быть оптимизированы для распространенных шаблонов фильтрации.
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
Комплексное тестирование будет охватывать:
|
||||
Рабочий процесс создания коллекции от начала до конца
|
||||
Синхронизация с хранилищем
|
||||
Проверка при записи для несуществующих коллекций
|
||||
Обработка запросов для несуществующих коллекций
|
||||
Каскадное удаление коллекций во всех хранилищах
|
||||
Обработка ошибок и сценарии восстановления
|
||||
Юнит-тесты для каждого хранилища
|
||||
Интеграционные тесты для операций между хранилищами
|
||||
|
||||
## Статус реализации
|
||||
|
||||
### ✅ Завершенные компоненты
|
||||
|
||||
1. **Сервис управления коллекциями Librarian** (`trustgraph-flow/trustgraph/librarian/collection_manager.py`)
|
||||
Операции CRUD (создание, чтение, обновление, удаление) метаданных коллекции
|
||||
Интеграция с таблицей метаданных коллекций Cassandra через `LibraryTableStore`
|
||||
Координация каскадного удаления коллекций во всех типах хранилищ
|
||||
Асинхронная обработка запросов/ответов с надлежащим управлением ошибками
|
||||
|
||||
2. **Схема метаданных коллекции** (`trustgraph-base/trustgraph/schema/services/collection.py`)
|
||||
Схемы `CollectionManagementRequest` и `CollectionManagementResponse`
|
||||
Схема `CollectionMetadata` для записей коллекций
|
||||
Определения тем очередей для запросов/ответов коллекции
|
||||
|
||||
3. **Схема управления хранилищем** (`trustgraph-base/trustgraph/schema/services/storage.py`)
|
||||
Схемы `StorageManagementRequest` и `StorageManagementResponse`
|
||||
Определены темы очередей для управления хранилищем
|
||||
Формат сообщения для операций с коллекциями на уровне хранилища
|
||||
|
||||
4. **Схема Cassandra для 4 таблиц** (`trustgraph-flow/trustgraph/direct/cassandra_kg.py`)
|
||||
Составные ключи разделов для повышения производительности запросов
|
||||
Таблица `triples_collection` для запросов SPO и отслеживания удаления
|
||||
Реализовано удаление коллекций с использованием шаблона "чтение-затем-удаление"
|
||||
|
||||
### ✅ Миграция на конфигурационно-ориентированный шаблон - ЗАВЕРШЕНО
|
||||
|
||||
**Все хранилища были перенесены из шаблона на основе очереди на конфигурационно-ориентированный шаблон `CollectionConfigHandler`.**
|
||||
|
||||
Выполненные миграции:
|
||||
✅ `trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/triples/neo4j/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/triples/memgraph/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/triples/falkordb/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/doc_embeddings/qdrant/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/graph_embeddings/qdrant/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/doc_embeddings/milvus/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/graph_embeddings/milvus/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/doc_embeddings/pinecone/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/graph_embeddings/pinecone/write.py`
|
||||
✅ `trustgraph-flow/trustgraph/storage/objects/cassandra/write.py`
|
||||
|
||||
Теперь все хранилища:
|
||||
Наследуют от `CollectionConfigHandler`
|
||||
Регистрируются для получения уведомлений об изменениях конфигурации через `self.register_config_handler(self.on_collection_config)`
|
||||
Реализуют `create_collection(user, collection, metadata)` и `delete_collection(user, collection)`
|
||||
Используют `collection_exists(user, collection)` для проверки перед записью
|
||||
Автоматически синхронизируются с изменениями сервиса конфигурации
|
||||
|
||||
Устаревшая инфраструктура на основе очереди удалена:
|
||||
✅ Удалены схемы `StorageManagementRequest` и `StorageManagementResponse`
|
||||
✅ Удалены определения тем управления хранилищем
|
||||
✅ Удален потребитель/производитель очереди управления хранилищем из всех хранилищ
|
||||
✅ Удалены обработчики `on_storage_management` из всех хранилищ
|
||||
144
docs/tech-specs/ru/document-embeddings-chunk-id.ru.md
Normal file
144
docs/tech-specs/ru/document-embeddings-chunk-id.ru.md
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
В настоящее время хранилище встраиваний документов хранит текст фрагментов непосредственно в полезной нагрузке векторного хранилища, дублируя данные, которые существуют в Garage. Данная спецификация заменяет хранение текста фрагментов ссылками на `chunk_id`.
|
||||
|
||||
## Текущее состояние
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ChunkEmbeddings:
|
||||
chunk: bytes = b""
|
||||
vectors: list[list[float]] = field(default_factory=list)
|
||||
|
||||
@dataclass
|
||||
class DocumentEmbeddingsResponse:
|
||||
error: Error | None = None
|
||||
chunks: list[str] = field(default_factory=list)
|
||||
```
|
||||
|
||||
Структура данных для хранения векторов:
|
||||
```python
|
||||
payload={"doc": chunk} # Duplicates Garage content
|
||||
```
|
||||
|
||||
## Дизайн
|
||||
|
||||
### Изменения схемы
|
||||
|
||||
**ChunkEmbeddings** - заменить "chunk" на "chunk_id":
|
||||
```python
|
||||
@dataclass
|
||||
class ChunkEmbeddings:
|
||||
chunk_id: str = ""
|
||||
vectors: list[list[float]] = field(default_factory=list)
|
||||
```
|
||||
|
||||
**DocumentEmbeddingsResponse** - возвращать chunk_ids вместо фрагментов:
|
||||
```python
|
||||
@dataclass
|
||||
class DocumentEmbeddingsResponse:
|
||||
error: Error | None = None
|
||||
chunk_ids: list[str] = field(default_factory=list)
|
||||
```
|
||||
|
||||
### Структура данных для векторного хранилища
|
||||
|
||||
Все хранилища (Qdrant, Milvus, Pinecone):
|
||||
```python
|
||||
payload={"chunk_id": chunk_id}
|
||||
```
|
||||
|
||||
### Изменения в системе извлечения информации (RAG)
|
||||
|
||||
Модуль обработки документов RAG извлекает содержимое фрагментов из системы Garage:
|
||||
|
||||
```python
|
||||
# Get chunk_ids from embeddings store
|
||||
chunk_ids = await self.rag.doc_embeddings_client.query(...)
|
||||
|
||||
# Fetch chunk content from Garage
|
||||
docs = []
|
||||
for chunk_id in chunk_ids:
|
||||
content = await self.rag.librarian_client.get_document_content(
|
||||
chunk_id, self.user
|
||||
)
|
||||
docs.append(content)
|
||||
```
|
||||
|
||||
### Изменения API/SDK
|
||||
|
||||
**DocumentEmbeddingsClient** возвращает chunk_ids:
|
||||
```python
|
||||
return resp.chunk_ids # Changed from resp.chunks
|
||||
```
|
||||
|
||||
**Формат данных** (DocumentEmbeddingsResponseTranslator):
|
||||
```python
|
||||
result["chunk_ids"] = obj.chunk_ids # Changed from chunks
|
||||
```
|
||||
|
||||
### Изменения в CLI
|
||||
|
||||
Инструмент CLI отображает идентификаторы фрагментов (пользователи могут извлекать контент отдельно, если это необходимо).
|
||||
|
||||
## Файлы для изменения
|
||||
|
||||
### Схема
|
||||
`trustgraph-base/trustgraph/schema/knowledge/embeddings.py` - ChunkEmbeddings
|
||||
`trustgraph-base/trustgraph/schema/services/query.py` - DocumentEmbeddingsResponse
|
||||
|
||||
### Сообщения/Переводчики
|
||||
`trustgraph-base/trustgraph/messaging/translators/embeddings_query.py` - DocumentEmbeddingsResponseTranslator
|
||||
|
||||
### Клиент
|
||||
`trustgraph-base/trustgraph/base/document_embeddings_client.py` - возвращать идентификаторы фрагментов
|
||||
|
||||
### Python SDK/API
|
||||
`trustgraph-base/trustgraph/api/flow.py` - document_embeddings_query
|
||||
`trustgraph-base/trustgraph/api/socket_client.py` - document_embeddings_query
|
||||
`trustgraph-base/trustgraph/api/async_flow.py` - если применимо
|
||||
`trustgraph-base/trustgraph/api/bulk_client.py` - импорт/экспорт векторных представлений документов
|
||||
`trustgraph-base/trustgraph/api/async_bulk_client.py` - импорт/экспорт векторных представлений документов
|
||||
|
||||
### Сервис векторных представлений
|
||||
`trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py` - передавать идентификатор фрагмента
|
||||
|
||||
### Модули записи в хранилище
|
||||
`trustgraph-flow/trustgraph/storage/doc_embeddings/qdrant/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/doc_embeddings/milvus/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/doc_embeddings/pinecone/write.py`
|
||||
|
||||
### Сервисы запросов
|
||||
`trustgraph-flow/trustgraph/query/doc_embeddings/qdrant/service.py`
|
||||
`trustgraph-flow/trustgraph/query/doc_embeddings/milvus/service.py`
|
||||
`trustgraph-flow/trustgraph/query/doc_embeddings/pinecone/service.py`
|
||||
|
||||
### Шлюз
|
||||
`trustgraph-flow/trustgraph/gateway/dispatch/document_embeddings_query.py`
|
||||
`trustgraph-flow/trustgraph/gateway/dispatch/document_embeddings_export.py`
|
||||
`trustgraph-flow/trustgraph/gateway/dispatch/document_embeddings_import.py`
|
||||
|
||||
### Document RAG
|
||||
`trustgraph-flow/trustgraph/retrieval/document_rag/rag.py` - добавить клиент librarian
|
||||
`trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py` - извлекать из Garage
|
||||
|
||||
### CLI
|
||||
`trustgraph-cli/trustgraph/cli/invoke_document_embeddings.py`
|
||||
`trustgraph-cli/trustgraph/cli/save_doc_embeds.py`
|
||||
`trustgraph-cli/trustgraph/cli/load_doc_embeds.py`
|
||||
|
||||
## Преимущества
|
||||
|
||||
1. Единый источник истины - текст фрагментов только в Garage
|
||||
2. Уменьшение объема хранимых векторных представлений
|
||||
3. Обеспечивает отслеживание происхождения данных в момент запроса с помощью идентификатора фрагмента.
|
||||
675
docs/tech-specs/ru/embeddings-batch-processing.ru.md
Normal file
675
docs/tech-specs/ru/embeddings-batch-processing.ru.md
Normal 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)
|
||||
268
docs/tech-specs/ru/entity-centric-graph.ru.md
Normal file
268
docs/tech-specs/ru/entity-centric-graph.ru.md
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Хранение графов знаний, ориентированных на сущности, в Cassandra"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Хранение графов знаний, ориентированных на сущности, в Cassandra
|
||||
|
||||
> **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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Этот документ описывает модель хранения графов знаний в стиле RDF в Apache Cassandra. Модель использует подход, ориентированный на сущности, при котором каждая сущность знает о каждой квадрупле, в которой она участвует, и о роли, которую она играет. Это заменяет традиционный подход с использованием нескольких таблиц и различных перестановок SPO (Subject, Predicate, Object) всего двумя таблицами.
|
||||
|
||||
## Предыстория и мотивация
|
||||
|
||||
### Традиционный подход
|
||||
|
||||
Стандартное хранилище квадруплов RDF в Cassandra требует нескольких денормализованных таблиц для охвата шаблонов запросов — обычно 6 или более таблиц, представляющих различные перестановки Subject, Predicate, Object и Dataset (SPOD). Каждый квадруплет записывается в каждую таблицу, что приводит к значительному увеличению количества операций записи, оперативным издержкам и сложности схемы.
|
||||
|
||||
Кроме того, разрешение меток (получение удобочитаемых имен для сущностей) требует отдельных запросов, что особенно дорого в сценариях использования AI и GraphRAG, где метки необходимы для контекста LLM.
|
||||
|
||||
### Инсайт, ориентированный на сущности
|
||||
|
||||
Каждый квадруплет `(D, S, P, O)` включает до 4 сущностей. Записывая строку для участия каждой сущности в квадруплете, мы гарантируем, что **любой запрос, содержащий хотя бы один известный элемент, попадет в первичный ключ**. Это охватывает все 16 шаблонов запросов с помощью одной таблицы данных.
|
||||
|
||||
Основные преимущества:
|
||||
|
||||
**2 таблицы** вместо 7+
|
||||
**4 записи на квадруплет** вместо 6+
|
||||
**Разрешение меток бесплатно** — метки сущности находятся рядом с ее отношениями, что естественным образом подогревает кэш приложения.
|
||||
**Все 16 шаблонов запросов** обслуживаются с помощью чтения из одной секции.
|
||||
**Более простые операции** — одна таблица данных для настройки, уплотнения и восстановления.
|
||||
|
||||
## Схема
|
||||
|
||||
### Таблица 1: quads_by_entity
|
||||
|
||||
Основная таблица данных. Каждая сущность имеет секцию, содержащую все квадруплеты, в которых она участвует. Названа в соответствии с шаблоном запроса (поиск по сущности).
|
||||
|
||||
```sql
|
||||
CREATE TABLE quads_by_entity (
|
||||
collection text, -- Collection/tenant scope (always specified)
|
||||
entity text, -- The entity this row is about
|
||||
role text, -- 'S', 'P', 'O', 'G' — how this entity participates
|
||||
p text, -- Predicate of the quad
|
||||
otype text, -- 'U' (URI), 'L' (literal), 'T' (triple/reification)
|
||||
s text, -- Subject of the quad
|
||||
o text, -- Object of the quad
|
||||
d text, -- Dataset/graph of the quad
|
||||
dtype text, -- XSD datatype (when otype = 'L'), e.g. 'xsd:string'
|
||||
lang text, -- Language tag (when otype = 'L'), e.g. 'en', 'fr'
|
||||
PRIMARY KEY ((collection, entity), role, p, otype, s, o, d, dtype, lang)
|
||||
);
|
||||
```
|
||||
|
||||
**Ключ секции (Partition key)**: `(collection, entity)` — ограничен областью действия коллекции, одна секция на сущность.
|
||||
|
||||
**Обоснование порядка столбцов кластеризации**:
|
||||
|
||||
1. **role** — большинство запросов начинаются с "где эта сущность является субъектом/объектом"
|
||||
2. **p** — следующий по частоте фильтр, "верните все `knows` отношения"
|
||||
3. **otype** — позволяет фильтровать по отношениям со значениями URI по сравнению с литеральными значениями
|
||||
4. **s, o, d** — оставшиеся столбцы для обеспечения уникальности
|
||||
5. **dtype, lang** — различают литералы с одинаковым значением, но с разными метаданными типа (например, `"thing"` vs `"thing"@en` vs `"thing"^^xsd:string`)
|
||||
|
||||
### Таблица 2: quads_by_collection
|
||||
|
||||
Поддерживает запросы и удаление на уровне коллекции. Предоставляет список всех квадруплетов, принадлежащих коллекции. Название отражает шаблон запроса (поиск по коллекции).
|
||||
|
||||
```sql
|
||||
CREATE TABLE quads_by_collection (
|
||||
collection text,
|
||||
d text, -- Dataset/graph of the quad
|
||||
s text, -- Subject of the quad
|
||||
p text, -- Predicate of the quad
|
||||
o text, -- Object of the quad
|
||||
otype text, -- 'U' (URI), 'L' (literal), 'T' (triple/reification)
|
||||
dtype text, -- XSD datatype (when otype = 'L')
|
||||
lang text, -- Language tag (when otype = 'L')
|
||||
PRIMARY KEY (collection, d, s, p, o, otype, dtype, lang)
|
||||
);
|
||||
```
|
||||
|
||||
Группировка сначала по набору данных, что позволяет удалять данные как на уровне коллекции, так и на уровне набора данных. Столбцы `otype`, `dtype` и `lang` включены в ключ группировки для различения литералов с одинаковым значением, но с разными метаданными типа — в RDF, `"thing"`, `"thing"@en` и `"thing"^^xsd:string` являются семантически различными значениями.
|
||||
|
||||
## Путь записи
|
||||
|
||||
Для каждой входящей квадрупли `(D, S, P, O)` в коллекции `C`, записывайте **4 строки** в `quads_by_entity` и **1 строку** в `quads_by_collection`.
|
||||
|
||||
### Пример
|
||||
|
||||
Для квадрупли в коллекции `tenant1`:
|
||||
|
||||
```
|
||||
Dataset: https://example.org/graph1
|
||||
Subject: https://example.org/Alice
|
||||
Predicate: https://example.org/knows
|
||||
Object: https://example.org/Bob
|
||||
```
|
||||
|
||||
Запишите 4 строки в `quads_by_entity`:
|
||||
|
||||
| collection | entity | role | p | otype | s | o | d |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| tenant1 | https://example.org/graph1 | G | https://example.org/knows | U | https://example.org/Alice | https://example.org/Bob | https://example.org/graph1 |
|
||||
| tenant1 | https://example.org/Alice | S | https://example.org/knows | U | https://example.org/Alice | https://example.org/Bob | https://example.org/graph1 |
|
||||
| tenant1 | https://example.org/knows | P | https://example.org/knows | U | https://example.org/Alice | https://example.org/Bob | https://example.org/graph1 |
|
||||
| tenant1 | https://example.org/Bob | O | https://example.org/knows | U | https://example.org/Alice | https://example.org/Bob | https://example.org/graph1 |
|
||||
|
||||
Запишите 1 строку в `quads_by_collection`:
|
||||
|
||||
| collection | d | s | p | o | otype | dtype | lang |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| tenant1 | https://example.org/graph1 | https://example.org/Alice | https://example.org/knows | https://example.org/Bob | U | | |
|
||||
|
||||
### Пример с буквальным текстом
|
||||
|
||||
Для тройки меток:
|
||||
|
||||
```
|
||||
Dataset: https://example.org/graph1
|
||||
Subject: https://example.org/Alice
|
||||
Predicate: http://www.w3.org/2000/01/rdf-schema#label
|
||||
Object: "Alice Smith" (lang: en)
|
||||
```
|
||||
|
||||
`otype` — это `'L'`, `dtype` — это `'xsd:string'`, а `lang` — это `'en'`. Литеральное значение `"Alice Smith"` хранится в `o`. В `quads_by_entity` требуется всего 3 строки — строка для литерала как сущности не записывается, поскольку литералы не являются независимыми сущностями, к которым можно обращаться.
|
||||
|
||||
## Шаблоны запросов
|
||||
|
||||
### Все 16 шаблонов DSPO
|
||||
|
||||
В таблице ниже "Идеальный префикс" означает, что запрос использует непрерывный префикс столбцов кластеризации. "Сканирование раздела + фильтрация" означает, что Cassandra считывает часть одного раздела и фильтрует в памяти — это эффективно, но не является чистым совпадением префикса.
|
||||
|
||||
| # | Известно | Поиск сущности | Префикс кластеризации | Эффективность |
|
||||
|---|---|---|---|---|
|
||||
| 1 | D,S,P,O | entity=S, role='S', p=P | Полное совпадение | Идеальный префикс |
|
||||
| 2 | D,S,P,? | entity=S, role='S', p=P | Фильтрация по D | Сканирование раздела + фильтрация |
|
||||
| 3 | D,S,?,O | entity=S, role='S' | Фильтрация по D, O | Сканирование раздела + фильтрация |
|
||||
| 4 | D,?,P,O | entity=O, role='O', p=P | Фильтрация по D | Сканирование раздела + фильтрация |
|
||||
| 5 | ?,S,P,O | entity=S, role='S', p=P | Фильтрация по O | Сканирование раздела + фильтрация |
|
||||
| 6 | D,S,?,? | entity=S, role='S' | Фильтрация по D | Сканирование раздела + фильтрация |
|
||||
| 7 | D,?,P,? | entity=P, role='P' | Фильтрация по D | Сканирование раздела + фильтрация |
|
||||
| 8 | D,?,?,O | entity=O, role='O' | Фильтрация по D | Сканирование раздела + фильтрация |
|
||||
| 9 | ?,S,P,? | entity=S, role='S', p=P | — | **Идеальный префикс** |
|
||||
| 10 | ?,S,?,O | entity=S, role='S' | Фильтрация по O | Сканирование раздела + фильтрация |
|
||||
| 11 | ?,?,P,O | entity=O, role='O', p=P | — | **Идеальный префикс** |
|
||||
| 12 | D,?,?,? | entity=D, role='G' | — | **Идеальный префикс** |
|
||||
| 13 | ?,S,?,? | entity=S, role='S' | — | **Идеальный префикс** |
|
||||
| 14 | ?,?,P,? | entity=P, role='P' | — | **Идеальный префикс** |
|
||||
| 15 | ?,?,?,O | entity=O, role='O' | — | **Идеальный префикс** |
|
||||
| 16 | ?,?,?,? | — | Полное сканирование | Только исследование |
|
||||
|
||||
**Ключевой результат**: 7 из 15 нетривиальных шаблонов являются идеальными совпадениями префикса кластеризации. Оставшиеся 8 — это чтение одного раздела с фильтрацией внутри раздела. Каждый запрос, содержащий хотя бы один известный элемент, соответствует ключу раздела.
|
||||
|
||||
Шаблон 16 (?,?,?,?) не встречается на практике, поскольку коллекция всегда указана, что сводит его к шаблону 12.
|
||||
|
||||
### Примеры распространенных запросов
|
||||
|
||||
**Вся информация об сущности:**
|
||||
|
||||
```sql
|
||||
SELECT * FROM quads_by_entity
|
||||
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice';
|
||||
```
|
||||
|
||||
**Все исходящие связи для сущности:**
|
||||
|
||||
```sql
|
||||
SELECT * FROM quads_by_entity
|
||||
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
|
||||
AND role = 'S';
|
||||
```
|
||||
|
||||
**Конкретный предикат для сущности:**
|
||||
|
||||
```sql
|
||||
SELECT * FROM quads_by_entity
|
||||
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
|
||||
AND role = 'S' AND p = 'https://example.org/knows';
|
||||
```
|
||||
|
||||
**Метка для сущности (на конкретном языке):**
|
||||
|
||||
```sql
|
||||
SELECT * FROM quads_by_entity
|
||||
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
|
||||
AND role = 'S' AND p = 'http://www.w3.org/2000/01/rdf-schema#label'
|
||||
AND otype = 'L';
|
||||
```
|
||||
|
||||
Затем, при необходимости, выполните фильтрацию на стороне приложения с использованием `lang = 'en'`.
|
||||
|
||||
**Только связи, имеющие значение URI (ссылки между сущностями):**
|
||||
|
||||
```sql
|
||||
SELECT * FROM quads_by_entity
|
||||
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
|
||||
AND role = 'S' AND p = 'https://example.org/knows' AND otype = 'U';
|
||||
```
|
||||
|
||||
**Обратный поиск — что указывает на эту сущность:**
|
||||
|
||||
```sql
|
||||
SELECT * FROM quads_by_entity
|
||||
WHERE collection = 'tenant1' AND entity = 'https://example.org/Bob'
|
||||
AND role = 'O';
|
||||
```
|
||||
|
||||
## Разрешение меток и предварительная загрузка кэша
|
||||
|
||||
Одним из наиболее значительных преимуществ модели, ориентированной на сущности, является то, что **разрешение меток становится побочным эффектом**.
|
||||
|
||||
В традиционной многотабличной модели получение меток требует отдельных запросов: извлечение троек, определение URI сущностей в результатах, а затем получение `rdfs:label` для каждого. Этот шаблон N+1 дорог.
|
||||
|
||||
В модели, ориентированной на сущности, запрос сущности возвращает **все** ее квадруплеты, включая ее метки, типы и другие свойства. Когда приложение кэширует результаты запросов, метки предварительно загружаются в кэш до того, как кто-либо запросит их.
|
||||
|
||||
Два режима использования подтверждают, что это хорошо работает на практике:
|
||||
|
||||
**Запросы, предназначенные для пользователей**: обычно небольшие наборы результатов, метки необходимы. Чтение сущностей предварительно загружает кэш.
|
||||
**Запросы для ИИ/пакетной обработки**: большие наборы результатов с жесткими ограничениями. Метки либо не нужны, либо необходимы только для курированного подмножества сущностей, которые уже находятся в кэше.
|
||||
|
||||
Теоретическое опасение по поводу разрешения меток для огромных наборов результатов (например, 30 000 сущностей) нивелируется практическим наблюдением, что ни один пользователь или ИИ не обрабатывает такое большое количество меток. Ограничения на запросы на уровне приложения обеспечивают, чтобы нагрузка на кэш оставалась управляемой.
|
||||
|
||||
## Широкие разделы и реификация
|
||||
|
||||
Реификация (утверждения RDF-star об утверждениях) создает центральные сущности, например, документ-источник, который поддерживает тысячи извлеченных фактов. Это может привести к широким разделам.
|
||||
|
||||
Факторы, смягчающие ситуацию:
|
||||
|
||||
**Ограничения на запросы на уровне приложения**: все запросы GraphRAG и предназначенные для пользователей применяют жесткие ограничения, поэтому широкие разделы никогда не сканируются полностью на пути горячего чтения.
|
||||
**Cassandra эффективно обрабатывает частичные чтения**: сканирование кластерной колонки с ранней остановкой происходит быстро даже на больших разделах.
|
||||
**Удаление коллекции** (единственная операция, которая может просматривать полные разделы) является приемлемым фоновым процессом.
|
||||
|
||||
## Удаление коллекции
|
||||
|
||||
Запускается по API-вызову, выполняется в фоновом режиме (с конечной согласованностью).
|
||||
|
||||
1. Чтение `quads_by_collection` для целевой коллекции, чтобы получить все квадруплеты.
|
||||
2. Извлечение уникальных сущностей из квадруплетов (значений s, p, o, d).
|
||||
3. Для каждой уникальной сущности удалите раздел из `quads_by_entity`.
|
||||
4. Удалите строки из `quads_by_collection`.
|
||||
|
||||
Таблица `quads_by_collection` предоставляет индекс, необходимый для поиска всех разделов сущностей без полного сканирования таблицы. Удаление на уровне раздела эффективно, поскольку `(collection, entity)` является ключом раздела.
|
||||
|
||||
## Путь миграции из многотабличной модели
|
||||
|
||||
Модель, ориентированная на сущности, может сосуществовать с существующей многотабличной моделью во время миграции:
|
||||
|
||||
1. Разверните таблицы `quads_by_entity` и `quads_by_collection` рядом с существующими таблицами.
|
||||
2. Записывайте новые квадруплеты одновременно в старые и новые таблицы.
|
||||
3. Заполните существующие данные в новые таблицы.
|
||||
4. Мигрируйте пути запросов по одному шаблону за раз.
|
||||
5. Отключите старые таблицы после миграции всех запросов.
|
||||
|
||||
## Краткое описание
|
||||
|
||||
| Аспект | Традиционная (6 таблиц) | Ориентированная на сущности (2 таблицы) |
|
||||
|---|---|---|
|
||||
| Таблицы | 7+ | 2 |
|
||||
| Записи на квадруплет | 6+ | 5 (4 данных + 1 манифест) |
|
||||
| Разрешение меток | Отдельные запросы | Бесплатно благодаря предварительной загрузке кэша |
|
||||
| Шаблоны запросов | 16 по 6 таблицам | 16 на 1 таблицу |
|
||||
| Сложность схемы | Высокая | Низкая |
|
||||
| Операционные накладные расходы | 6 таблиц для настройки/восстановления | 1 таблица данных |
|
||||
| Поддержка реификации | Дополнительная сложность | Естественная совместимость |
|
||||
| Фильтрация по типу объекта | Недоступно | Нативно (через кластеризацию по типу объекта) |
|
||||
228
docs/tech-specs/ru/explainability-cli.ru.md
Normal file
228
docs/tech-specs/ru/explainability-cli.ru.md
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Техническая спецификация CLI для Explainability"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Техническая спецификация CLI для Explainability
|
||||
|
||||
> **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.
|
||||
|
||||
## Статус
|
||||
|
||||
Черновик
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает инструменты командной строки для отладки и изучения данных explainability в TrustGraph. Эти инструменты позволяют пользователям отслеживать, как были получены ответы, и отлаживать цепочку происхождения от ребер до исходных документов.
|
||||
|
||||
Три инструмента командной строки:
|
||||
|
||||
1. **`tg-show-document-hierarchy`** - Отображение иерархии документ → страница → фрагмент → ребро
|
||||
2. **`tg-list-explain-traces`** - Список всех сессий GraphRAG с вопросами
|
||||
3. **`tg-show-explain-trace`** - Отображение полной цепочки explainability для сессии
|
||||
|
||||
## Цели
|
||||
|
||||
**Отладка**: Предоставить разработчикам возможность просматривать результаты обработки документов.
|
||||
**Прослеживаемость**: Отслеживать любой извлеченный факт до его исходного документа.
|
||||
**Прозрачность**: Показать, как GraphRAG получил ответ.
|
||||
**Удобство использования**: Простой интерфейс командной строки с разумными значениями по умолчанию.
|
||||
|
||||
## Предыстория
|
||||
|
||||
TrustGraph имеет две системы отслеживания происхождения:
|
||||
|
||||
1. **Отслеживание происхождения во время извлечения** (см. `extraction-time-provenance.md`): Записывает отношения документ → страница → фрагмент → ребро во время импорта. Хранится в графе с именем `urn:graph:source`, используя `prov:wasDerivedFrom`.
|
||||
|
||||
2. **Explainability во время запроса** (см. `query-time-explainability.md`): Записывает цепочку вопрос → исследование → фокус → синтез во время запросов GraphRAG. Хранится в графе с именем `urn:graph:retrieval`.
|
||||
|
||||
Текущие ограничения:
|
||||
Нет простого способа визуализации иерархии документов после обработки.
|
||||
Необходимо вручную запрашивать тройки для просмотра данных explainability.
|
||||
Нет единого представления сессии GraphRAG.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Инструмент 1: tg-show-document-hierarchy
|
||||
|
||||
**Назначение**: При заданном идентификаторе документа, обходит и отображает все производные сущности.
|
||||
|
||||
**Использование**:
|
||||
```bash
|
||||
tg-show-document-hierarchy "urn:trustgraph:doc:abc123"
|
||||
tg-show-document-hierarchy --show-content --max-content 500 "urn:trustgraph:doc:abc123"
|
||||
```
|
||||
|
||||
**Аргументы**:
|
||||
| Аргумент | Описание |
|
||||
|-----|-------------|
|
||||
| `document_id` | URI документа (позиционный) |
|
||||
| `-u/--api-url` | URL шлюза (по умолчанию: `$TRUSTGRAPH_URL`) |
|
||||
| `-t/--token` | Токен авторизации (по умолчанию: `$TRUSTGRAPH_TOKEN`) |
|
||||
| `-U/--user` | Идентификатор пользователя (по умолчанию: `trustgraph`) |
|
||||
| `-C/--collection` | Коллекция (по умолчанию: `default`) |
|
||||
| `--show-content` | Включить содержимое блоба/документа |
|
||||
| `--max-content` | Максимальное количество символов на блоб (по умолчанию: 200) |
|
||||
| `--format` | Вывод: `tree` (по умолчанию), `json` |
|
||||
|
||||
**Реализация**:
|
||||
1. Запрос троек: `?child prov:wasDerivedFrom <document_id>` в `urn:graph:source`
|
||||
2. Рекурсивный запрос дочерних элементов каждого результата
|
||||
3. Построение древовидной структуры: Документ → Страницы → Части
|
||||
4. Если `--show-content`, получение содержимого из API librarian
|
||||
5. Отображение в виде отформатированного дерева или JSON
|
||||
|
||||
**Пример вывода**:
|
||||
```
|
||||
Document: urn:trustgraph:doc:abc123
|
||||
Title: "Sample PDF"
|
||||
Type: application/pdf
|
||||
|
||||
└── Page 1: urn:trustgraph:doc:abc123/p1
|
||||
├── Chunk 0: urn:trustgraph:doc:abc123/p1/c0
|
||||
│ Content: "The quick brown fox..." [truncated]
|
||||
└── Chunk 1: urn:trustgraph:doc:abc123/p1/c1
|
||||
Content: "Machine learning is..." [truncated]
|
||||
```
|
||||
|
||||
### Инструмент 2: tg-list-explain-traces
|
||||
|
||||
**Назначение**: Вывести список всех сессий GraphRAG (вопросов) в коллекции.
|
||||
|
||||
**Использование**:
|
||||
```bash
|
||||
tg-list-explain-traces
|
||||
tg-list-explain-traces --limit 20 --format json
|
||||
```
|
||||
|
||||
**Аргументы**:
|
||||
| Аргумент | Описание |
|
||||
|-----|-------------|
|
||||
| `-u/--api-url` | URL шлюза |
|
||||
| `-t/--token` | Токен авторизации |
|
||||
| `-U/--user` | Идентификатор пользователя |
|
||||
| `-C/--collection` | Коллекция |
|
||||
| `--limit` | Максимальное количество результатов (по умолчанию: 50) |
|
||||
| `--format` | Вывод: `table` (по умолчанию), `json` |
|
||||
|
||||
**Реализация**:
|
||||
1. Запрос: `?session tg:query ?text` в `urn:graph:retrieval`
|
||||
2. Запрос временных меток: `?session prov:startedAtTime ?time`
|
||||
3. Отображение в виде таблицы
|
||||
|
||||
**Пример вывода**:
|
||||
```
|
||||
Session ID | Question | Time
|
||||
----------------------------------------------|--------------------------------|---------------------
|
||||
urn:trustgraph:question:abc123 | What was the War on Terror? | 2024-01-15 10:30:00
|
||||
urn:trustgraph:question:def456 | Who founded OpenAI? | 2024-01-15 09:15:00
|
||||
```
|
||||
|
||||
### Инструмент 3: tg-show-explain-trace
|
||||
|
||||
**Назначение**: Отображение полной цепочки объяснений для сеанса GraphRAG.
|
||||
|
||||
**Использование**:
|
||||
```bash
|
||||
tg-show-explain-trace "urn:trustgraph:question:abc123"
|
||||
tg-show-explain-trace --max-answer 1000 --show-provenance "urn:trustgraph:question:abc123"
|
||||
```
|
||||
|
||||
**Аргументы**:
|
||||
| Аргумент | Описание |
|
||||
|-----|-------------|
|
||||
| `question_id` | URI вопроса (позиционный) |
|
||||
| `-u/--api-url` | URL шлюза |
|
||||
| `-t/--token` | Токен авторизации |
|
||||
| `-U/--user` | Идентификатор пользователя |
|
||||
| `-C/--collection` | Коллекция |
|
||||
| `--max-answer` | Максимальное количество символов для ответа (по умолчанию: 500) |
|
||||
| `--show-provenance` | Проследить связи к исходным документам |
|
||||
| `--format` | Вывод: `text` (по умолчанию), `json` |
|
||||
|
||||
**Реализация**:
|
||||
1. Получить текст вопроса из предиката `tg:query`
|
||||
2. Найти исследование: `?exp prov:wasGeneratedBy <question_id>`
|
||||
3. Найти фокус: `?focus prov:wasDerivedFrom <exploration_id>`
|
||||
4. Получить выбранные связи: `<focus_id> tg:selectedEdge ?edge`
|
||||
5. Для каждой связи, получить `tg:edge` (цитируемая тройка) и `tg:reasoning`
|
||||
6. Найти синтез: `?synth prov:wasDerivedFrom <focus_id>`
|
||||
7. Получить ответ из `tg:document` через библиотекаря
|
||||
8. Если `--show-provenance`, проследить связи к исходным документам
|
||||
|
||||
**Пример вывода**:
|
||||
```
|
||||
=== GraphRAG Session: urn:trustgraph:question:abc123 ===
|
||||
|
||||
Question: What was the War on Terror?
|
||||
Time: 2024-01-15 10:30:00
|
||||
|
||||
--- Exploration ---
|
||||
Retrieved 50 edges from knowledge graph
|
||||
|
||||
--- Focus (Edge Selection) ---
|
||||
Selected 12 edges:
|
||||
|
||||
1. (War on Terror, definition, "A military campaign...")
|
||||
Reasoning: Directly defines the subject of the query
|
||||
Source: chunk → page 2 → "Beyond the Vigilant State"
|
||||
|
||||
2. (Guantanamo Bay, part_of, War on Terror)
|
||||
Reasoning: Shows key component of the campaign
|
||||
|
||||
--- Synthesis ---
|
||||
Answer:
|
||||
The War on Terror was a military campaign initiated...
|
||||
[truncated at 500 chars]
|
||||
```
|
||||
|
||||
## Файлы для создания
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|---------|
|
||||
| `trustgraph-cli/trustgraph/cli/show_document_hierarchy.py` | Инструмент 1 |
|
||||
| `trustgraph-cli/trustgraph/cli/list_explain_traces.py` | Инструмент 2 |
|
||||
| `trustgraph-cli/trustgraph/cli/show_explain_trace.py` | Инструмент 3 |
|
||||
|
||||
## Файлы для изменения
|
||||
|
||||
| Файл | Изменение |
|
||||
|------|--------|
|
||||
| `trustgraph-cli/setup.py` | Добавить записи в console_scripts |
|
||||
|
||||
## Замечания по реализации
|
||||
|
||||
1. **Безопасность двоичного содержимого**: Попробуйте декодировать в UTF-8; если не удается, отобразите `[Binary: {size} bytes]`.
|
||||
2. **Усечение**: Соблюдайте `--max-content`/`--max-answer` с индикатором `[truncated]`.
|
||||
3. **Тройки в кавычках**: Разберите формат RDF-star из предиката `tg:edge`.
|
||||
4. **Шаблоны**: Следуйте существующим шаблонам CLI из `query_graph.py`.
|
||||
|
||||
## Вопросы безопасности
|
||||
|
||||
Все запросы соответствуют границам пользователя/коллекции.
|
||||
Поддерживается аутентификация по токену через `--token` или `$TRUSTGRAPH_TOKEN`.
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
Ручная проверка с использованием образцовых данных:
|
||||
```bash
|
||||
# Load a test document
|
||||
tg-load-pdf -f test.pdf -c test-collection
|
||||
|
||||
# Verify hierarchy
|
||||
tg-show-document-hierarchy "urn:trustgraph:doc:test"
|
||||
|
||||
# Run a GraphRAG query with explainability
|
||||
tg-invoke-graph-rag --explainable -q "Test question"
|
||||
|
||||
# List and inspect traces
|
||||
tg-list-explain-traces
|
||||
tg-show-explain-trace "urn:trustgraph:question:xxx"
|
||||
```
|
||||
|
||||
## Ссылки
|
||||
|
||||
Объяснимость во время выполнения запроса: `docs/tech-specs/query-time-explainability.md`
|
||||
Происхождение во время извлечения: `docs/tech-specs/extraction-time-provenance.md`
|
||||
Существующий пример интерфейса командной строки: `trustgraph-cli/trustgraph/cli/invoke_graph_rag.py`
|
||||
355
docs/tech-specs/ru/extraction-flows.ru.md
Normal file
355
docs/tech-specs/ru/extraction-flows.ru.md
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
---
|
||||
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.
|
||||
|
||||
Этот документ описывает, как данные передаются через конвейер извлечения данных TrustGraph, начиная с отправки документов и заканчивая хранением в хранилищах знаний.
|
||||
|
||||
## Обзор
|
||||
|
||||
```
|
||||
┌──────────┐ ┌─────────────┐ ┌─────────┐ ┌────────────────────┐
|
||||
│ Librarian│────▶│ PDF Decoder │────▶│ Chunker │────▶│ Knowledge │
|
||||
│ │ │ (PDF only) │ │ │ │ Extraction │
|
||||
│ │────────────────────────▶│ │ │ │
|
||||
└──────────┘ └─────────────┘ └─────────┘ └────────────────────┘
|
||||
│ │
|
||||
│ ├──▶ Triples
|
||||
│ ├──▶ Entity Contexts
|
||||
│ └──▶ Rows
|
||||
│
|
||||
└──▶ Document Embeddings
|
||||
```
|
||||
|
||||
## Хранение контента
|
||||
|
||||
### Blob-хранилище (S3/Minio)
|
||||
|
||||
Содержимое документов хранится в блочном хранилище, совместимом с S3:
|
||||
Формат пути: `doc/{object_id}`, где object_id - это UUID
|
||||
Все типы документов хранятся здесь: исходные документы, страницы, фрагменты
|
||||
|
||||
### Хранилище метаданных (Cassandra)
|
||||
|
||||
Метаданные документов, хранящиеся в Cassandra, включают:
|
||||
Идентификатор документа, заголовок, тип (MIME-тип)
|
||||
Ссылку `object_id` на блочное хранилище
|
||||
Ссылку `parent_id` для дочерних документов (страниц, фрагментов)
|
||||
Тип `document_type`: "source", "page", "chunk", "answer"
|
||||
|
||||
### Порог для встроенных данных и потоковой передачи
|
||||
|
||||
Передача контента использует стратегию, основанную на размере:
|
||||
**< 2 МБ**: Контент включается непосредственно в сообщение (в кодировке base64)
|
||||
**≥ 2 МБ**: Отправляется только `document_id`; процессор извлекает данные через API librarian
|
||||
|
||||
## Этап 1: Отправка документа (Librarian)
|
||||
|
||||
### Точка входа
|
||||
|
||||
Документы поступают в систему через операцию `add-document` librarian:
|
||||
1. Контент загружается в блочное хранилище
|
||||
2. Создается запись метаданных в Cassandra
|
||||
3. Возвращается идентификатор документа
|
||||
|
||||
### Запуск извлечения
|
||||
|
||||
Операция `add-processing` запускает извлечение:
|
||||
Указывает `document_id`, `flow` (идентификатор конвейера), `collection` (целевое хранилище)
|
||||
Операция `load_document()` librarian извлекает контент и публикует его в очередь ввода конвейера
|
||||
|
||||
### Схема: Document
|
||||
|
||||
```
|
||||
Document
|
||||
├── metadata: Metadata
|
||||
│ ├── id: str # Document identifier
|
||||
│ ├── user: str # Tenant/user ID
|
||||
│ ├── collection: str # Target collection
|
||||
│ └── metadata: list[Triple] # (largely unused, historical)
|
||||
├── data: bytes # PDF content (base64, if inline)
|
||||
└── document_id: str # Librarian reference (if streaming)
|
||||
```
|
||||
|
||||
**Маршрутизация**: Основана на поле `kind`:
|
||||
`application/pdf` → очередь `document-load` → Декодер PDF
|
||||
`text/plain` → очередь `text-load` → Разделитель (Chunker)
|
||||
|
||||
## Этап 2: Декодер PDF
|
||||
|
||||
Преобразует документы PDF в текстовые страницы.
|
||||
|
||||
### Процесс
|
||||
|
||||
1. Получение содержимого (встроенное `data` или через `document_id` от библиотекаря)
|
||||
2. Извлечение страниц с использованием PyPDF
|
||||
3. Для каждой страницы:
|
||||
Сохранение как дочерний документ в библиотеке (`{doc_id}/p{page_num}`)
|
||||
Отправка информации о происхождении (страница получена из документа)
|
||||
Передача разделителю (chunker)
|
||||
|
||||
### Схема: TextDocument
|
||||
|
||||
```
|
||||
TextDocument
|
||||
├── metadata: Metadata
|
||||
│ ├── id: str # Page URI (e.g., https://trustgraph.ai/doc/xxx/p1)
|
||||
│ ├── user: str
|
||||
│ ├── collection: str
|
||||
│ └── metadata: list[Triple]
|
||||
├── text: bytes # Page text content (if inline)
|
||||
└── document_id: str # Librarian reference (e.g., "doc123/p1")
|
||||
```
|
||||
|
||||
## Этап 3: Разделение на фрагменты
|
||||
|
||||
Разделяет текст на фрагменты заданного размера.
|
||||
|
||||
### Параметры (настраиваются в процессе выполнения)
|
||||
|
||||
`chunk_size`: Целевой размер фрагмента в символах (по умолчанию: 2000)
|
||||
`chunk_overlap`: Перекрытие между фрагментами (по умолчанию: 100)
|
||||
|
||||
### Процесс
|
||||
|
||||
1. Получение содержимого текста (встроенного или через библиотеку)
|
||||
2. Разделение с использованием рекурсивного разделителя символов
|
||||
3. Для каждого фрагмента:
|
||||
Сохранение как дочерний документ в библиотеке (`{parent_id}/c{index}`)
|
||||
Вывод информации о происхождении (фрагмент получен из страницы/документа)
|
||||
Передача на этапы обработки извлечения
|
||||
|
||||
### Схема: Фрагмент
|
||||
|
||||
```
|
||||
Chunk
|
||||
├── metadata: Metadata
|
||||
│ ├── id: str # Chunk URI
|
||||
│ ├── user: str
|
||||
│ ├── collection: str
|
||||
│ └── metadata: list[Triple]
|
||||
├── chunk: bytes # Chunk text content
|
||||
└── document_id: str # Librarian chunk ID (e.g., "doc123/p1/c3")
|
||||
```
|
||||
|
||||
### Иерархия идентификаторов документов
|
||||
|
||||
Дочерние документы кодируют свою родословную в идентификаторе:
|
||||
Источник: `doc123`
|
||||
Страница: `doc123/p5`
|
||||
Часть страницы: `doc123/p5/c2`
|
||||
Часть текста: `doc123/c2`
|
||||
|
||||
## Этап 4: Извлечение знаний
|
||||
|
||||
Доступно несколько шаблонов извлечения, выбираемых конфигурацией потока.
|
||||
|
||||
### Шаблон A: Базовый GraphRAG
|
||||
|
||||
Два параллельных процессора:
|
||||
|
||||
**kg-extract-definitions**
|
||||
Вход: Часть текста
|
||||
Выход: Тройки (определения сущностей), Контексты сущностей
|
||||
Извлекает: метки сущностей, определения
|
||||
|
||||
**kg-extract-relationships**
|
||||
Вход: Часть текста
|
||||
Выход: Тройки (отношения), Контексты сущностей
|
||||
Извлекает: отношения субъект-предикат-объект
|
||||
|
||||
### Шаблон B: Основанный на онтологии (kg-extract-ontology)
|
||||
|
||||
Вход: Часть текста
|
||||
Выход: Тройки, Контексты сущностей
|
||||
Использует настроенную онтологию для управления извлечением
|
||||
|
||||
### Шаблон C: Основанный на агентах (kg-extract-agent)
|
||||
|
||||
Вход: Часть текста
|
||||
Выход: Тройки, Контексты сущностей
|
||||
Использует агентную платформу для извлечения
|
||||
|
||||
### Шаблон D: Извлечение строк (kg-extract-rows)
|
||||
|
||||
Вход: Часть текста
|
||||
Выход: Строки (структурированные данные, а не тройки)
|
||||
Использует определение схемы для извлечения структурированных записей
|
||||
|
||||
### Схема: Тройки
|
||||
|
||||
```
|
||||
Triples
|
||||
├── metadata: Metadata
|
||||
│ ├── id: str
|
||||
│ ├── user: str
|
||||
│ ├── collection: str
|
||||
│ └── metadata: list[Triple] # (set to [] by extractors)
|
||||
└── triples: list[Triple]
|
||||
└── Triple
|
||||
├── s: Term # Subject
|
||||
├── p: Term # Predicate
|
||||
├── o: Term # Object
|
||||
└── g: str | None # Named graph
|
||||
```
|
||||
|
||||
### Схема: EntityContexts
|
||||
|
||||
```
|
||||
EntityContexts
|
||||
├── metadata: Metadata
|
||||
└── entities: list[EntityContext]
|
||||
└── EntityContext
|
||||
├── entity: Term # Entity identifier (IRI)
|
||||
├── context: str # Textual description for embedding
|
||||
└── chunk_id: str # Source chunk ID (provenance)
|
||||
```
|
||||
|
||||
### Схема: Строки
|
||||
|
||||
```
|
||||
Rows
|
||||
├── metadata: Metadata
|
||||
├── row_schema: RowSchema
|
||||
│ ├── name: str
|
||||
│ ├── description: str
|
||||
│ └── fields: list[Field]
|
||||
└── rows: list[dict[str, str]] # Extracted records
|
||||
```
|
||||
|
||||
## Этап 5: Генерация векторных представлений (эмбеддингов)
|
||||
|
||||
### Векторные представления графа
|
||||
|
||||
Преобразует контексты сущностей в векторные представления.
|
||||
|
||||
**Процесс:**
|
||||
1. Получение контекстов сущностей (EntityContexts)
|
||||
2. Вызов сервиса генерации векторных представлений с текстом контекста
|
||||
3. Вывод векторных представлений графа (отображение сущности во вектор)
|
||||
|
||||
**Схема: Векторные представления графа (GraphEmbeddings)**
|
||||
|
||||
```
|
||||
GraphEmbeddings
|
||||
├── metadata: Metadata
|
||||
└── entities: list[EntityEmbeddings]
|
||||
└── EntityEmbeddings
|
||||
├── entity: Term # Entity identifier
|
||||
├── vector: list[float] # Embedding vector
|
||||
└── chunk_id: str # Source chunk (provenance)
|
||||
```
|
||||
|
||||
### Векторные представления документов
|
||||
|
||||
Преобразует текстовые фрагменты непосредственно в векторные представления.
|
||||
|
||||
**Процесс:**
|
||||
1. Получение фрагмента текста
|
||||
2. Вызов сервиса векторизации с текстом фрагмента
|
||||
3. Вывод векторного представления документа
|
||||
|
||||
**Схема: Векторное представление документа**
|
||||
|
||||
```
|
||||
DocumentEmbeddings
|
||||
├── metadata: Metadata
|
||||
└── chunks: list[ChunkEmbeddings]
|
||||
└── ChunkEmbeddings
|
||||
├── chunk_id: str # Chunk identifier
|
||||
└── vector: list[float] # Embedding vector
|
||||
```
|
||||
|
||||
### Встраивания строк
|
||||
|
||||
Преобразует поля индекса строк в векторные представления.
|
||||
|
||||
**Процесс:**
|
||||
1. Получение строк
|
||||
2. Встраивание настроенных полей индекса
|
||||
3. Вывод в хранилище векторов строк
|
||||
|
||||
## Этап 6: Хранение
|
||||
|
||||
### Тройной магазин
|
||||
|
||||
Получает: Тройки
|
||||
Хранение: Cassandra (таблицы, ориентированные на сущности)
|
||||
Именованные графы разделяют основные знания от информации об источнике:
|
||||
`""` (по умолчанию): Факты основных знаний
|
||||
`urn:graph:source`: Информация об источнике извлечения
|
||||
`urn:graph:retrieval`: Объяснимость во время запроса
|
||||
|
||||
### Хранилище векторов (Встраивания графов)
|
||||
|
||||
Получает: Встраивания графов
|
||||
Хранение: Qdrant, Milvus или Pinecone
|
||||
Индексируется по: IRI сущности
|
||||
Метаданные: chunk_id для информации об источнике
|
||||
|
||||
### Хранилище векторов (Встраивания документов)
|
||||
|
||||
Получает: Встраивания документов
|
||||
Хранение: Qdrant, Milvus или Pinecone
|
||||
Индексируется по: chunk_id
|
||||
|
||||
### Хранилище строк
|
||||
|
||||
Получает: Строки
|
||||
Хранение: Cassandra
|
||||
Структура таблицы, определяемая схемой
|
||||
|
||||
### Хранилище векторов строк
|
||||
|
||||
Получает: Встраивания строк
|
||||
Хранение: Векторная база данных
|
||||
Индексируется по: полям индекса строк
|
||||
|
||||
## Анализ полей метаданных
|
||||
|
||||
### Активно используемые поля
|
||||
|
||||
| Поле | Использование |
|
||||
|-------|-------|
|
||||
| `metadata.id` | Идентификатор документа/фрагмента, ведение журнала, информация об источнике |
|
||||
| `metadata.user` | Многопользовательский режим, маршрутизация хранения |
|
||||
| `metadata.collection` | Выбор целевой коллекции |
|
||||
| `document_id` | Ссылка на библиотекаря, связывание с информацией об источнике |
|
||||
| `chunk_id` | Отслеживание информации об источнике через конвейер |
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Потенциально избыточные поля
|
||||
|
||||
| Поле | Статус |
|
||||
|-------|--------|
|
||||
| `metadata.metadata` | Устанавливается в `[]` всеми извлекателями; метаданные на уровне документа теперь обрабатываются библиотекарем при отправке |
|
||||
=======
|
||||
### Удаленные поля
|
||||
|
||||
| Поле | Статус |
|
||||
|-------|--------|
|
||||
| `metadata.metadata` | Удалено из класса `Metadata`. Тройки метаданных на уровне документа теперь напрямую выдаются библиотекарем в тройной магазин при отправке, а не передаются через конвейер извлечения. |
|
||||
>>>>>>> e3bcbf73 (The metadata field (list of triples) in the pipeline Metadata class)
|
||||
|
||||
### Шаблон полей байтов
|
||||
|
||||
Все поля содержимого (`data`, `text`, `chunk`) являются `bytes`, но немедленно декодируются в строки UTF-8 всеми процессорами. Ни один процессор не использует необработанные байты.
|
||||
|
||||
## Конфигурация потока
|
||||
|
||||
Потоки определяются внешне и предоставляются библиотекарию через сервис конфигурации. Каждый поток определяет:
|
||||
|
||||
Входные очереди (`text-load`, `document-load`)
|
||||
Цепочка процессоров
|
||||
Параметры (размер фрагмента, метод извлечения и т. д.)
|
||||
|
||||
Пример шаблонов потоков:
|
||||
`pdf-graphrag`: PDF → Decoder → Chunker → Definitions + Relationships → Embeddings
|
||||
`text-graphrag`: Text → Chunker → Definitions + Relationships → Embeddings
|
||||
`pdf-ontology`: PDF → Decoder → Chunker → Ontology Extraction → Embeddings
|
||||
`text-rows`: Text → Chunker → Row Extraction → Row Store
|
||||
268
docs/tech-specs/ru/extraction-provenance-subgraph.ru.md
Normal file
268
docs/tech-specs/ru/extraction-provenance-subgraph.ru.md
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Проблема
|
||||
|
||||
<<<<<<< HEAD
|
||||
В настоящее время механизм отслеживания происхождения, работающий во время извлечения, создает полную реификацию для каждого
|
||||
извлеченного тройства: уникальный `stmt_uri`, `activity_uri` и связанные
|
||||
метаданные PROV-O для каждого отдельного факта знаний. Обработка одного блока
|
||||
данных, который дает 20 отношений, приводит к появлению примерно 220 тройств отслеживания происхождения в дополнение к
|
||||
примерно 20 тройствам знаний — это примерно 10:1 избыточности.
|
||||
|
||||
Это дорого (хранение, индексация, передача) и семантически
|
||||
неточно. Каждый блок обрабатывается одним вызовом LLM, который создает
|
||||
все свои тройства за одну транзакцию. Текущая модель, основанная на тройствах,
|
||||
искажает это, создавая иллюзию 20 независимых событий извлечения.
|
||||
|
||||
|
||||
Кроме того, два из четырех процессоров извлечения (kg-extract-ontology,
|
||||
kg-extract-agent) вообще не имеют информации об отслеживании происхождения, что оставляет пробелы в журнале аудита.
|
||||
|
||||
|
||||
## Решение
|
||||
|
||||
Заменить реификацию на уровне тройств на **модель подграфа**: одна запись отслеживания происхождения для каждого блока извлечения,
|
||||
используемая для всех тройств, созданных из этого блока.
|
||||
|
||||
=======
|
||||
В настоящее время механизм отслеживания происхождения, действующий во время извлечения, создает полную реификацию для каждого
|
||||
извлеченного тройного набора: уникальный `stmt_uri`, `activity_uri` и связанные
|
||||
метаданные PROV-O для каждого отдельного факта знаний. Обработка одного блока,
|
||||
который дает 20 отношений, приводит к появлению примерно 220 тройных наборов отслеживания происхождения, помимо
|
||||
примерно 20 тройных наборов знаний — это примерно 10:1 избыточности.
|
||||
|
||||
Это дорого (хранение, индексация, передача) и семантически
|
||||
неточно. Каждый блок обрабатывается одним вызовом LLM, который создает
|
||||
все свои тройные наборы за одну транзакцию. Текущая модель для каждого тройного набора
|
||||
создает иллюзию 20 независимых событий извлечения.
|
||||
|
||||
|
||||
Кроме того, два из четырех процессоров извлечения (kg-extract-ontology,
|
||||
kg-extract-agent) вообще не имеют информации об отслеживании происхождения, что создает пробелы в
|
||||
журнале аудита.
|
||||
|
||||
## Решение
|
||||
|
||||
Заменить реификацию для каждого тройного набора на **модель подграфа**: один
|
||||
набор отслеживания происхождения для каждого извлеченного блока, который используется
|
||||
для всех тройных наборов, созданных из этого блока.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
### Изменение терминологии
|
||||
|
||||
| Старое | Новое |
|
||||
|-----|-----|
|
||||
| `stmt_uri` (`https://trustgraph.ai/stmt/{uuid}`) | `subgraph_uri` (`https://trustgraph.ai/subgraph/{uuid}`) |
|
||||
| `statement_uri()` | `subgraph_uri()` |
|
||||
| `tg:reifies` (1:1, идентичность) | `tg:contains` (1:много, содержательность) |
|
||||
|
||||
### Целевая структура
|
||||
|
||||
<<<<<<< HEAD
|
||||
Все тройства отслеживания происхождения должны быть помещены в именованный граф `urn:graph:source`.
|
||||
=======
|
||||
Все тройные наборы отслеживания происхождения должны быть помещены в именованный граф `urn:graph:source`.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
```
|
||||
# Subgraph contains each extracted triple (RDF-star quoted triples)
|
||||
<subgraph> tg:contains <<s1 p1 o1>> .
|
||||
<subgraph> tg:contains <<s2 p2 o2>> .
|
||||
<subgraph> tg:contains <<s3 p3 o3>> .
|
||||
|
||||
# Derivation from source chunk
|
||||
<subgraph> prov:wasDerivedFrom <chunk_uri> .
|
||||
<subgraph> prov:wasGeneratedBy <activity> .
|
||||
|
||||
# Activity: one per chunk extraction
|
||||
<activity> rdf:type prov:Activity .
|
||||
<activity> rdfs:label "{component_name} extraction" .
|
||||
<activity> prov:used <chunk_uri> .
|
||||
<activity> prov:wasAssociatedWith <agent> .
|
||||
<activity> prov:startedAtTime "2026-03-13T10:00:00Z" .
|
||||
<activity> tg:componentVersion "0.25.0" .
|
||||
<activity> tg:llmModel "gpt-4" . # if available
|
||||
<activity> tg:ontology <ontology_uri> . # if available
|
||||
|
||||
# Agent: stable per component
|
||||
<agent> rdf:type prov:Agent .
|
||||
<agent> rdfs:label "{component_name}" .
|
||||
```
|
||||
|
||||
### Сравнение объемов
|
||||
|
||||
Для блока, генерирующего N извлеченных троек:
|
||||
|
||||
| | Старый (на тройку) | Новый (подграф) |
|
||||
|---|---|---|
|
||||
| `tg:contains` / `tg:reifies` | N | N |
|
||||
| Тройки активности | ~9 x N | ~9 |
|
||||
| Тройки агентов | 2 x N | 2 |
|
||||
| Метаданные утверждения/подграфа | 2 x N | 2 |
|
||||
<<<<<<< HEAD
|
||||
| **Всего троек происхождения** | **~13N** | **N + 13** |
|
||||
=======
|
||||
| **Общее количество троек происхождения** | **~13N** | **N + 13** |
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
| **Пример (N=20)** | **~260** | **33** |
|
||||
|
||||
## Область применения
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Модули для обновления (существующее происхождение, на тройку)
|
||||
=======
|
||||
### Процессоры для обновления (существующее происхождение, на тройку)
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**kg-extract-definitions**
|
||||
(`trustgraph-flow/trustgraph/extract/kg/definitions/extract.py`)
|
||||
|
||||
В настоящее время вызывает `statement_uri()` + `triple_provenance_triples()` внутри
|
||||
цикла для каждой сущности.
|
||||
|
||||
Изменения:
|
||||
Переместить создание `subgraph_uri()` и `activity_uri()` перед циклом
|
||||
Собирать тройки `tg:contains` внутри цикла
|
||||
Выводить общий блок активности/агента/происхождения один раз после цикла
|
||||
|
||||
**kg-extract-relationships**
|
||||
(`trustgraph-flow/trustgraph/extract/kg/relationships/extract.py`)
|
||||
|
||||
Та же схема, что и для сущностей. Те же изменения.
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Модули для добавления информации о происхождении (в настоящее время отсутствуют)
|
||||
=======
|
||||
### Процессоры для добавления происхождения (в настоящее время отсутствуют)
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**kg-extract-ontology**
|
||||
(`trustgraph-flow/trustgraph/extract/kg/ontology/extract.py`)
|
||||
|
||||
<<<<<<< HEAD
|
||||
В настоящее время генерируются тройки без указания источника. Добавить информацию об источнике подграфа,
|
||||
используя ту же схему: один подграф для каждого фрагмента, `tg:contains` для каждой
|
||||
извлеченной тройки.
|
||||
=======
|
||||
В настоящее время генерируются тройки данных без указания источника. Добавить информацию об источнике подграфа,
|
||||
используя ту же схему: один подграф для каждого блока, `tg:contains` для каждой
|
||||
извлеченной тройки данных.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**kg-extract-agent**
|
||||
(`trustgraph-flow/trustgraph/extract/kg/agent/extract.py`)
|
||||
|
||||
<<<<<<< HEAD
|
||||
В настоящее время генерируются тройки без указания источника. Добавить информацию об источнике подграфа,
|
||||
=======
|
||||
В настоящее время генерируются тройки данных без указания источника. Добавить информацию об источнике подграфа,
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
используя ту же схему.
|
||||
|
||||
### Изменения в общей библиотеке отслеживания происхождения данных
|
||||
|
||||
**`trustgraph-base/trustgraph/provenance/triples.py`**
|
||||
|
||||
Заменить `triple_provenance_triples()` на `subgraph_provenance_triples()`
|
||||
<<<<<<< HEAD
|
||||
Новая функция принимает список извлеченных троек вместо одной
|
||||
Генерирует один `tg:contains` для каждой тройки, общий блок активности/агента
|
||||
=======
|
||||
Новая функция принимает список извлеченных троек данных вместо одной
|
||||
Генерирует один `tg:contains` для каждой тройки данных, общий блок активности/агента
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Удалить старый `triple_provenance_triples()`
|
||||
|
||||
**`trustgraph-base/trustgraph/provenance/uris.py`**
|
||||
|
||||
Заменить `statement_uri()` на `subgraph_uri()`
|
||||
|
||||
**`trustgraph-base/trustgraph/provenance/namespaces.py`**
|
||||
|
||||
Заменить `TG_REIFIES` на `TG_CONTAINS`
|
||||
|
||||
### Не входит в область действия
|
||||
|
||||
**kg-extract-topics**: устаревший процессор, в настоящее время не используется в
|
||||
стандартных процессах.
|
||||
**kg-extract-rows**: генерирует строки, а не тройки, имеет другую модель
|
||||
происхождения.
|
||||
**Происхождение во время запроса** (`urn:graph:retrieval`): отдельная задача,
|
||||
уже использует другую схему (вопрос/исследование/фокус/синтез).
|
||||
**Происхождение документа/страницы/фрагмента** (декодер PDF, разбиение на фрагменты): уже использует
|
||||
`derived_entity_triples()`, который применяется к каждой сущности, а не к каждой тройке — нет
|
||||
проблемы избыточности.
|
||||
|
||||
## Замечания по реализации
|
||||
|
||||
### Реструктуризация цикла процессора
|
||||
|
||||
До (для каждой тройки, в отношениях):
|
||||
```python
|
||||
for rel in rels:
|
||||
# ... build relationship_triple ...
|
||||
stmt_uri = statement_uri()
|
||||
prov_triples = triple_provenance_triples(
|
||||
stmt_uri=stmt_uri,
|
||||
extracted_triple=relationship_triple,
|
||||
...
|
||||
)
|
||||
triples.extend(set_graph(prov_triples, GRAPH_SOURCE))
|
||||
```
|
||||
|
||||
После (подграфа):
|
||||
```python
|
||||
sg_uri = subgraph_uri()
|
||||
|
||||
for rel in rels:
|
||||
# ... build relationship_triple ...
|
||||
extracted_triples.append(relationship_triple)
|
||||
|
||||
prov_triples = subgraph_provenance_triples(
|
||||
subgraph_uri=sg_uri,
|
||||
extracted_triples=extracted_triples,
|
||||
chunk_uri=chunk_uri,
|
||||
component_name=default_ident,
|
||||
component_version=COMPONENT_VERSION,
|
||||
llm_model=llm_model,
|
||||
ontology_uri=ontology_uri,
|
||||
)
|
||||
triples.extend(set_graph(prov_triples, GRAPH_SOURCE))
|
||||
```
|
||||
|
||||
### Новая вспомогательная подпись
|
||||
|
||||
```python
|
||||
def subgraph_provenance_triples(
|
||||
subgraph_uri: str,
|
||||
extracted_triples: List[Triple],
|
||||
chunk_uri: str,
|
||||
component_name: str,
|
||||
component_version: str,
|
||||
llm_model: Optional[str] = None,
|
||||
ontology_uri: Optional[str] = None,
|
||||
timestamp: Optional[str] = None,
|
||||
) -> List[Triple]:
|
||||
"""
|
||||
Build provenance triples for a subgraph of extracted knowledge.
|
||||
|
||||
Creates:
|
||||
- tg:contains link for each extracted triple (RDF-star quoted)
|
||||
- One prov:wasDerivedFrom link to source chunk
|
||||
- One activity with agent metadata
|
||||
"""
|
||||
```
|
||||
|
||||
### Существенное изменение
|
||||
|
||||
Это существенное изменение модели отслеживания происхождения. Отслеживание происхождения еще не было выпущено, поэтому миграция не требуется. Старый код ⟦CODE_0⟧ /
|
||||
`tg:reifies` можно удалить безвозвратно.
|
||||
Код `statement_uri` можно удалить полностью.
|
||||
770
docs/tech-specs/ru/extraction-time-provenance.ru.md
Normal file
770
docs/tech-specs/ru/extraction-time-provenance.ru.md
Normal file
|
|
@ -0,0 +1,770 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Этот документ содержит заметки о происхождении данных во время извлечения для будущих работ по спецификации. Происхождение данных во время извлечения фиксирует "исходный слой" - откуда данные были получены изначально, как они были извлечены и преобразованы.
|
||||
|
||||
Это отличается от происхождения данных во время запроса (см. `query-time-provenance.md`), которое фиксирует логику работы агента.
|
||||
|
||||
## Описание проблемы
|
||||
|
||||
### Текущая реализация
|
||||
|
||||
В настоящее время происхождение данных работает следующим образом:
|
||||
Метаданные документа хранятся в виде RDF-триплетов в графе знаний.
|
||||
Идентификатор документа связывает метаданные с документом, поэтому документ отображается как узел в графе.
|
||||
Когда связи (отношения/факты) извлекаются из документов, связь `subjectOf` связывает извлеченную связь с исходным документом.
|
||||
|
||||
### Проблемы текущего подхода
|
||||
|
||||
1. **Повторная загрузка метаданных:** Метаданные документа группируются и загружаются повторно при каждой партии триплетов, извлеченных из этого документа. Это неэффективно и избыточно - одни и те же метаданные передаются как "груз" с каждым результатом извлечения.
|
||||
|
||||
2. **Поверхностное происхождение данных:** Текущая связь `subjectOf` связывает факты только с верхним уровнем документа. Нет информации о цепочке преобразований - с какой страницы был получен факт, из какого фрагмента, какой метод извлечения был использован.
|
||||
|
||||
### Желаемое состояние
|
||||
|
||||
1. **Загрузка метаданных один раз:** Метаданные документа должны быть загружены один раз и привязаны к узлу верхнего уровня документа, а не повторяться с каждой партией триплетов.
|
||||
|
||||
<<<<<<< HEAD
|
||||
2. **Разветвленная структура данных о происхождении:** Захватите полную цепочку преобразований от исходного документа через все промежуточные артефакты до извлеченных фактов. Например, преобразование PDF-документа:
|
||||
=======
|
||||
2. **Разветвленная структура происхождения данных:** Захватите полную цепочку преобразований от исходного документа через все промежуточные артефакты до извлеченных фактов. Например, преобразование PDF-документа:
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
```
|
||||
PDF file (source document with metadata)
|
||||
→ Page 1 (decoded text)
|
||||
→ Chunk 1
|
||||
→ Extracted edge/fact (via subjectOf)
|
||||
→ Extracted edge/fact
|
||||
→ Chunk 2
|
||||
→ Extracted edge/fact
|
||||
→ Page 2
|
||||
→ Chunk 3
|
||||
→ ...
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
3. **Унифицированное хранилище:** Граф происхождения хранится в той же базе знаний, что и извлеченные знания. Это позволяет запрашивать происхождение так же, как и знания, прослеживая связи обратно по цепочке от любого факта к его точному источнику.
|
||||
=======
|
||||
3. **Унифицированное хранилище:** Граф происхождения хранится в той же базе знаний, что и извлеченные знания. Это позволяет запрашивать происхождение так же, как и знания, прослеживая связи вверх по цепочке от любого факта к его точному источнику.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
4. **Стабильные идентификаторы:** Каждый промежуточный артефакт (страница, фрагмент) имеет стабильный идентификатор в виде узла в графе.
|
||||
|
||||
5. **Связь родитель-потомок:** Производные документы связаны с их родительскими документами до самого верхнего уровня, используя согласованные типы отношений.
|
||||
|
||||
6. **Точное определение источника факта:** Отношение `subjectOf` на извлеченных связях указывает на непосредственный родительский элемент (фрагмент), а не на верхний документ. Полное происхождение восстанавливается путем прохода по графу.
|
||||
|
||||
## Случаи использования
|
||||
|
||||
### UC1: Определение источника в ответах GraphRAG
|
||||
|
||||
**Сценарий:** Пользователь выполняет запрос GraphRAG и получает ответ от агента.
|
||||
|
||||
**Процесс:**
|
||||
1. Пользователь отправляет запрос агенту GraphRAG.
|
||||
2. Агент извлекает соответствующие факты из базы знаний для формирования ответа.
|
||||
<<<<<<< HEAD
|
||||
3. В соответствии со спецификацией происхождения во время запроса, агент сообщает, какие факты внесли вклад в ответ.
|
||||
=======
|
||||
3. В соответствии со спецификацией происхождения для запросов, агент сообщает, какие факты послужили основой для ответа.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
4. Каждый факт связан с его исходным фрагментом через граф происхождения.
|
||||
5. Фрагменты связаны со страницами, страницы связаны с исходными документами.
|
||||
|
||||
**Результат для пользователя:** Интерфейс отображает ответ LLM вместе с указанием источника. Пользователь может:
|
||||
<<<<<<< HEAD
|
||||
Увидеть, какие факты подтверждают ответ.
|
||||
Переходить от фактов к фрагментам, страницам и документам.
|
||||
Просматривать исходные документы для проверки утверждений.
|
||||
Понимать, откуда в документе (на какой странице, в каком разделе) произошел факт.
|
||||
=======
|
||||
Увидеть, какие факты послужили основой для ответа.
|
||||
Переходить от фактов к фрагментам, страницам и документам.
|
||||
Просматривать исходные документы для проверки утверждений.
|
||||
Понимать, откуда в документе (на какой странице, в каком разделе) произошел тот или иной факт.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**Преимущество:** Пользователи могут проверять ответы, сгенерированные ИИ, по сравнению с первичными источниками, что повышает доверие и позволяет проводить проверку фактов.
|
||||
|
||||
### UC2: Отладка качества извлечения
|
||||
|
||||
<<<<<<< HEAD
|
||||
Факт кажется неверным. Проследите обратно через фрагмент, страницу и документ, чтобы увидеть исходный текст. Это была ошибка извлечения, или исходный документ был неверным?
|
||||
|
||||
### UC3: Инкрементное повторное извлечение
|
||||
|
||||
Исходный документ обновлен. Какие фрагменты/факты были получены из него? Отмените и перезапустите только эти, а не перерабатывайте все.
|
||||
|
||||
### UC4: Удаление данных / Право на забвение
|
||||
|
||||
Исходный документ должен быть удален (GDPR, юридические требования и т.д.). Найдите и удалите все производные факты, пройдя по графу.
|
||||
|
||||
### UC5: Разрешение конфликтов
|
||||
|
||||
Два факта противоречат друг другу. Проследите оба обратно к их источникам, чтобы понять, почему, и решить, кому доверять (более авторитетному источнику, более свежему и т.д.).
|
||||
=======
|
||||
Факт кажется неверным. Проследите по цепочке от фрагмента к странице и документу, чтобы увидеть исходный текст. Была ли это ошибка извлечения, или исходный текст был неверным?
|
||||
|
||||
### UC3: Инкрементное повторное извлечение
|
||||
|
||||
Исходный документ был обновлен. Какие фрагменты/факты были получены из него? Отмените и перегенерируйте только эти, а не перерабатывайте все.
|
||||
|
||||
### UC4: Удаление данных / Право на забвение
|
||||
|
||||
Исходный документ должен быть удален (GDPR, юридические требования и т.д.). Пройдите по графу, чтобы найти и удалить все производные факты.
|
||||
|
||||
### UC5: Разрешение конфликтов
|
||||
|
||||
Два факта противоречат друг другу. Проследите оба факта до их источников, чтобы понять причину и решить, кому доверять (более авторитетному источнику, более свежему и т.д.).
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
### UC6: Взвешивание авторитетности источника
|
||||
|
||||
Некоторые источники более авторитетны, чем другие. Факты могут быть взвешены или отфильтрованы на основе авторитетности/качества исходных документов.
|
||||
|
||||
### UC7: Сравнение конвейеров извлечения
|
||||
|
||||
Сравните результаты, полученные с использованием различных методов/версий извлечения. Какой экстрактор выдал лучшие факты из одного и того же источника?
|
||||
|
||||
## Точки интеграции
|
||||
|
||||
### Библиотекарь
|
||||
|
||||
Компонент "библиотекарь" уже предоставляет хранение документов с уникальными идентификаторами документов. Система отслеживания происхождения интегрируется с существующей инфраструктурой.
|
||||
|
||||
#### Существующие возможности (уже реализовано)
|
||||
|
||||
**Связывание документов "родитель-потомок":**
|
||||
Поле `parent_id` в `DocumentMetadata` - связывает дочерний документ с родительским документом.
|
||||
<<<<<<< HEAD
|
||||
Поле `document_type` - значения: `"source"` (оригинальный) или `"extracted"` (производный).
|
||||
=======
|
||||
Поле `document_type` - значения: `"source"` (исходный) или `"extracted"` (производный).
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
API `add-child-document` - создает дочерний документ с автоматическим `document_type = "extracted"`.
|
||||
API `list-children` - извлекает все дочерние документы родительского документа.
|
||||
Каскадное удаление - удаление родительского документа автоматически удаляет все дочерние документы.
|
||||
|
||||
**Идентификация документов:**
|
||||
Идентификаторы документов задаются клиентом (не генерируются автоматически).
|
||||
Документы индексируются по составному ключу `(user, document_id)` в Cassandra.
|
||||
Объектные идентификаторы (UUID) генерируются внутренне для хранения двоичных данных.
|
||||
|
||||
**Поддержка метаданных:**
|
||||
Поле `metadata: list[Triple]` - тройки RDF для структурированных метаданных.
|
||||
`title`, `comments`, `tags` - основные метаданные документа.
|
||||
`time` - метка времени, `kind` - MIME-тип.
|
||||
|
||||
**Архитектура хранения:**
|
||||
Метаданные хранятся в Cassandra (пространство ключей `librarian`, таблица `document`).
|
||||
Содержимое хранится в хранилище двоичных данных MinIO/S3 (контейнер `library`).
|
||||
Интеллектуальная доставка контента: документы размером менее 2 МБ встраиваются, более крупные документы передаются потоком.
|
||||
|
||||
#### Основные файлы
|
||||
|
||||
`trustgraph-flow/trustgraph/librarian/librarian.py` - Основные операции "библиотекаря".
|
||||
`trustgraph-flow/trustgraph/librarian/service.py` - Сервисный процессор, загрузка документов.
|
||||
`trustgraph-flow/trustgraph/tables/library.py` - Хранилище таблиц Cassandra.
|
||||
`trustgraph-base/trustgraph/schema/services/library.py` - Определения схемы.
|
||||
|
||||
<<<<<<< HEAD
|
||||
#### Необходимые улучшения
|
||||
=======
|
||||
#### Проблемы, требующие решения
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
"Библиотекарь" имеет необходимые компоненты, но в настоящее время:
|
||||
1. Связывание "родитель-потомок" ограничено одним уровнем - отсутствуют вспомогательные функции для обхода многоуровневых графов.
|
||||
2. Отсутствует стандартная терминология для типов отношений (например, `derivedFrom`, `extractedFrom`).
|
||||
<<<<<<< HEAD
|
||||
3. Метаданные происхождения (метод извлечения, уверенность, позиция фрагмента) не стандартизированы.
|
||||
=======
|
||||
3. Метаданные происхождения (метод извлечения, достоверность, позиция фрагмента) не стандартизированы.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
4. Отсутствует API запросов для прослеживания полной цепочки происхождения от факта до исходного источника.
|
||||
|
||||
## Проектирование сквозного потока
|
||||
|
||||
Каждый процессор в конвейере следует последовательной схеме:
|
||||
Получает идентификатор документа от предыдущего этапа.
|
||||
Извлекает содержимое из "библиотекаря".
|
||||
Создает дочерние артефакты.
|
||||
Для каждого дочернего элемента: сохраняет в "библиотекаре", создает связь в графе и передает идентификатор на следующий этап.
|
||||
|
||||
### Потоки обработки
|
||||
|
||||
Существуют два потока в зависимости от типа документа:
|
||||
|
||||
#### Поток обработки документов PDF
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Librarian (initiate processing) │
|
||||
│ 1. Emit root document metadata to knowledge graph (once) │
|
||||
│ 2. Send root document ID to PDF extractor │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ PDF Extractor (per page) │
|
||||
│ 1. Fetch PDF content from librarian using document ID │
|
||||
│ 2. Extract pages as text │
|
||||
│ 3. For each page: │
|
||||
│ a. Save page as child document in librarian (parent = root doc) │
|
||||
│ b. Emit parent-child edge to knowledge graph │
|
||||
│ c. Send page document ID to chunker │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Chunker (per chunk) │
|
||||
│ 1. Fetch page content from librarian using document ID │
|
||||
│ 2. Split text into chunks │
|
||||
│ 3. For each chunk: │
|
||||
│ a. Save chunk as child document in librarian (parent = page) │
|
||||
│ b. Emit parent-child edge to knowledge graph │
|
||||
│ c. Send chunk document ID + chunk content to next processor │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
Post-chunker optimization: messages carry both
|
||||
chunk ID (for provenance) and content (to avoid
|
||||
librarian round-trip). Chunks are small (2-4KB).
|
||||
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Knowledge Extractor (per chunk) │
|
||||
│ 1. Receive chunk ID + content directly (no librarian fetch needed) │
|
||||
│ 2. Extract facts/triples and embeddings from chunk content │
|
||||
│ 3. For each triple: │
|
||||
│ a. Emit triple to knowledge graph │
|
||||
│ b. Emit reified edge linking triple → chunk ID (edge pointing │
|
||||
│ to edge - first use of reification support) │
|
||||
│ 4. For each embedding: │
|
||||
│ a. Emit embedding with its entity ID │
|
||||
│ b. Link entity ID → chunk ID in knowledge graph │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Поток текстовых документов
|
||||
|
||||
Текстовые документы пропускают этап извлечения из PDF и сразу поступают в компоновщик:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Librarian (initiate processing) │
|
||||
│ 1. Emit root document metadata to knowledge graph (once) │
|
||||
│ 2. Send root document ID directly to chunker (skip PDF extractor) │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Chunker (per chunk) │
|
||||
│ 1. Fetch text content from librarian using document ID │
|
||||
│ 2. Split text into chunks │
|
||||
│ 3. For each chunk: │
|
||||
│ a. Save chunk as child document in librarian (parent = root doc) │
|
||||
│ b. Emit parent-child edge to knowledge graph │
|
||||
│ c. Send chunk document ID + chunk content to next processor │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ Knowledge Extractor │
|
||||
│ (same as PDF flow) │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Полученный ориентированный ациклический граф (DAG) имеет на один уровень меньше:
|
||||
|
||||
```
|
||||
PDF: Document → Pages → Chunks → Triples/Embeddings
|
||||
Text: Document → Chunks → Triples/Embeddings
|
||||
```
|
||||
|
||||
Дизайн учитывает оба варианта, поскольку компонент, разделяющий текст на части, обрабатывает входные данные универсально - он использует любой идентификатор документа, который он получает, в качестве родительского, независимо от того, является ли это исходным документом или страницей.
|
||||
|
||||
### Схема метаданных (PROV-O)
|
||||
|
||||
Метаданные происхождения используют онтологию W3C PROV-O. Это обеспечивает стандартный словарь и позволяет в будущем осуществлять подпись/аутентификацию результатов извлечения.
|
||||
|
||||
#### Основные концепции PROV-O
|
||||
|
||||
| Тип PROV-O | Использование в TrustGraph |
|
||||
|-------------|------------------|
|
||||
| `prov:Entity` | Документ, страница, фрагмент, тройка, вложение |
|
||||
| `prov:Activity` | Экземпляры операций извлечения |
|
||||
| `prov:Agent` | Компоненты TG (извлечение PDF, компонент, разделяющий текст на части и т.д.) с версиями |
|
||||
|
||||
#### Отношения PROV-O
|
||||
|
||||
| Предикат | Значение | Пример |
|
||||
|-----------|---------|---------|
|
||||
| `prov:wasDerivedFrom` | Сущность, полученная из другой сущности | Страница была получена из документа |
|
||||
| `prov:wasGeneratedBy` | Сущность, сгенерированная действием | Страница была сгенерирована действием извлечения PDF |
|
||||
| `prov:used` | Действие, использующее сущность в качестве входных данных | Действие извлечения PDF использовало документ |
|
||||
| `prov:wasAssociatedWith` | Действие, выполненное агентом | Действие извлечения PDF было связано с tg:PDFExtractor |
|
||||
|
||||
#### Метаданные на каждом уровне
|
||||
|
||||
**Исходный документ (генерируется Librarian):**
|
||||
```
|
||||
doc:123 a prov:Entity .
|
||||
doc:123 dc:title "Research Paper" .
|
||||
doc:123 dc:source <https://example.com/paper.pdf> .
|
||||
doc:123 dc:date "2024-01-15" .
|
||||
doc:123 dc:creator "Author Name" .
|
||||
doc:123 tg:pageCount 42 .
|
||||
doc:123 tg:mimeType "application/pdf" .
|
||||
```
|
||||
|
||||
**Страница (сгенерирована извлечением из PDF):**
|
||||
```
|
||||
page:123-1 a prov:Entity .
|
||||
page:123-1 prov:wasDerivedFrom doc:123 .
|
||||
page:123-1 prov:wasGeneratedBy activity:pdf-extract-456 .
|
||||
page:123-1 tg:pageNumber 1 .
|
||||
|
||||
activity:pdf-extract-456 a prov:Activity .
|
||||
activity:pdf-extract-456 prov:used doc:123 .
|
||||
activity:pdf-extract-456 prov:wasAssociatedWith tg:PDFExtractor .
|
||||
activity:pdf-extract-456 tg:componentVersion "1.2.3" .
|
||||
activity:pdf-extract-456 prov:startedAtTime "2024-01-15T10:30:00Z" .
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
**Раздел (выдан разделителем):**
|
||||
=======
|
||||
**Раздел (выдан Компоновщиком):**
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
```
|
||||
chunk:123-1-1 a prov:Entity .
|
||||
chunk:123-1-1 prov:wasDerivedFrom page:123-1 .
|
||||
chunk:123-1-1 prov:wasGeneratedBy activity:chunk-789 .
|
||||
chunk:123-1-1 tg:chunkIndex 1 .
|
||||
chunk:123-1-1 tg:charOffset 0 .
|
||||
chunk:123-1-1 tg:charLength 2048 .
|
||||
|
||||
activity:chunk-789 a prov:Activity .
|
||||
activity:chunk-789 prov:used page:123-1 .
|
||||
activity:chunk-789 prov:wasAssociatedWith tg:Chunker .
|
||||
activity:chunk-789 tg:componentVersion "1.0.0" .
|
||||
activity:chunk-789 tg:chunkSize 2048 .
|
||||
activity:chunk-789 tg:chunkOverlap 200 .
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
**Жирный шрифт (вывод Knowledge Extractor):**
|
||||
=======
|
||||
**Жирный шрифт (выделен извлечением знаний):**
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
```
|
||||
# The extracted triple (edge)
|
||||
entity:JohnSmith rel:worksAt entity:AcmeCorp .
|
||||
|
||||
# Subgraph containing the extracted triples
|
||||
subgraph:001 tg:contains <<entity:JohnSmith rel:worksAt entity:AcmeCorp>> .
|
||||
subgraph:001 prov:wasDerivedFrom chunk:123-1-1 .
|
||||
subgraph:001 prov:wasGeneratedBy activity:extract-999 .
|
||||
|
||||
activity:extract-999 a prov:Activity .
|
||||
activity:extract-999 prov:used chunk:123-1-1 .
|
||||
activity:extract-999 prov:wasAssociatedWith tg:KnowledgeExtractor .
|
||||
activity:extract-999 tg:componentVersion "2.1.0" .
|
||||
activity:extract-999 tg:llmModel "claude-3" .
|
||||
activity:extract-999 tg:ontology <http://example.org/ontologies/business-v1> .
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
**Встраивание (хранится в векторной базе данных, а не в тройной базе данных):**
|
||||
=======
|
||||
**Встраивания (хранятся в векторной базе данных, а не в тройной базе данных):**
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
Встраивания хранятся в векторной базе данных вместе с метаданными, а не в виде RDF-троек. Каждая запись встраивания содержит:
|
||||
|
||||
| Поле | Описание | Пример |
|
||||
|-------|-------------|---------|
|
||||
| vector | Вектор встраивания | [0.123, -0.456, ...] |
|
||||
| entity | URI узла, который представляет встраивание | `entity:JohnSmith` |
|
||||
| chunk_id | Исходный фрагмент (происхождение) | `chunk:123-1-1` |
|
||||
| model | Используемая модель встраивания | `text-embedding-ada-002` |
|
||||
| component_version | Версия компонента TG | `1.0.0` |
|
||||
|
||||
<<<<<<< HEAD
|
||||
Поле `entity` связывает встраивание с графом знаний (URI узла). Поле `chunk_id` предоставляет информацию о происхождении, указывающую на исходный фрагмент, что позволяет проследить путь до исходного документа.
|
||||
=======
|
||||
Поле `entity` связывает встраивание с графом знаний (URI узла). Поле `chunk_id` предоставляет информацию о происхождении, указывающую на исходный фрагмент, что позволяет проследить путь вверх по DAG к исходному документу.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
#### Расширения пространства имен TrustGraph
|
||||
|
||||
Пользовательские предикаты в пространстве имен `tg:` для метаданных, специфичных для извлечения:
|
||||
|
||||
| Предикат | Область | Описание |
|
||||
|-----------|--------|-------------|
|
||||
| `tg:contains` | Подграф | Указывает на тройку, содержащуюся в этом подграфе извлечения |
|
||||
| `tg:pageCount` | Документ | Общее количество страниц в исходном документе |
|
||||
| `tg:mimeType` | Документ | MIME-тип исходного документа |
|
||||
| `tg:pageNumber` | Страница | Номер страницы в исходном документе |
|
||||
<<<<<<< HEAD
|
||||
| `tg:chunkIndex` | Фрагмент | Индекс фрагмента внутри родительского фрагмента |
|
||||
=======
|
||||
| `tg:chunkIndex` | Фрагмент | Индекс фрагмента внутри родительского элемента |
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
| `tg:charOffset` | Фрагмент | Смещение символов в родительском тексте |
|
||||
| `tg:charLength` | Фрагмент | Длина фрагмента в символах |
|
||||
| `tg:chunkSize` | Действие | Настроенный размер фрагмента |
|
||||
| `tg:chunkOverlap` | Действие | Настроенное перекрытие между фрагментами |
|
||||
| `tg:componentVersion` | Действие | Версия компонента TG |
|
||||
| `tg:llmModel` | Действие | LLM, используемый для извлечения |
|
||||
| `tg:ontology` | Действие | URI онтологии, используемой для управления извлечением |
|
||||
| `tg:embeddingModel` | Действие | Модель, используемая для встраиваний |
|
||||
| `tg:sourceText` | Утверждение | Точный текст, из которого была извлечена тройка |
|
||||
| `tg:sourceCharOffset` | Утверждение | Смещение символов внутри фрагмента, где начинается исходный текст |
|
||||
| `tg:sourceCharLength` | Утверждение | Длина исходного текста в символах |
|
||||
|
||||
#### Начальная загрузка словаря (для каждой коллекции)
|
||||
|
||||
<<<<<<< HEAD
|
||||
Граф знаний является онтологически нейтральным и изначально пуст. При первом добавлении данных о происхождении PROV-O в коллекцию словарь должен быть инициализирован с помощью RDF-меток для всех классов и предикатов. Это обеспечивает удобочитаемое отображение в запросах и пользовательском интерфейсе.
|
||||
=======
|
||||
Граф знаний является онтологически нейтральным и изначально пуст. При записи данных о происхождении PROV-O в коллекцию в первый раз словарь должен быть инициализирован с помощью меток RDF для всех классов и предикатов. Это обеспечивает удобочитаемое отображение в запросах и пользовательском интерфейсе.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**Классы PROV-O:**
|
||||
```
|
||||
prov:Entity rdfs:label "Entity" .
|
||||
prov:Activity rdfs:label "Activity" .
|
||||
prov:Agent rdfs:label "Agent" .
|
||||
```
|
||||
|
||||
**Предикаты PROV-O:**
|
||||
```
|
||||
prov:wasDerivedFrom rdfs:label "was derived from" .
|
||||
prov:wasGeneratedBy rdfs:label "was generated by" .
|
||||
prov:used rdfs:label "used" .
|
||||
prov:wasAssociatedWith rdfs:label "was associated with" .
|
||||
prov:startedAtTime rdfs:label "started at" .
|
||||
```
|
||||
|
||||
**Предикаты TrustGraph:**
|
||||
```
|
||||
tg:contains rdfs:label "contains" .
|
||||
tg:pageCount rdfs:label "page count" .
|
||||
tg:mimeType rdfs:label "MIME type" .
|
||||
tg:pageNumber rdfs:label "page number" .
|
||||
tg:chunkIndex rdfs:label "chunk index" .
|
||||
tg:charOffset rdfs:label "character offset" .
|
||||
tg:charLength rdfs:label "character length" .
|
||||
tg:chunkSize rdfs:label "chunk size" .
|
||||
tg:chunkOverlap rdfs:label "chunk overlap" .
|
||||
tg:componentVersion rdfs:label "component version" .
|
||||
tg:llmModel rdfs:label "LLM model" .
|
||||
tg:ontology rdfs:label "ontology" .
|
||||
tg:embeddingModel rdfs:label "embedding model" .
|
||||
tg:sourceText rdfs:label "source text" .
|
||||
tg:sourceCharOffset rdfs:label "source character offset" .
|
||||
tg:sourceCharLength rdfs:label "source character length" .
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
**Примечания по реализации:** Этот механизм начальной загрузки словаря должен быть идемпотентным, то есть его можно безопасно запускать несколько раз без создания дубликатов. Его можно запускать при первой обработке документа в коллекции или как отдельный этап инициализации коллекции.
|
||||
|
||||
#### Происхождение подраздела (желательно)
|
||||
|
||||
Для более детальной информации о происхождении полезно записывать, из какой именно части фрагмента была извлечена тройка. Это позволяет:
|
||||
=======
|
||||
**Примечания по реализации:** Этот начальный набор словаря должен быть идемпотентным - его можно безопасно запускать несколько раз без создания дубликатов. Его можно запускать при первой обработке документа в коллекции или как отдельный этап инициализации коллекции.
|
||||
|
||||
#### Происхождение под-фрагментов (желательно)
|
||||
|
||||
Для более детальной информации о происхождении, было бы полезно записывать, из какой именно части фрагмента была извлечена тройка. Это позволяет:
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
Выделять точный исходный текст в пользовательском интерфейсе
|
||||
Проверять точность извлечения по отношению к исходному тексту
|
||||
Отлаживать качество извлечения на уровне предложений
|
||||
|
||||
**Пример с отслеживанием позиции:**
|
||||
```
|
||||
# The extracted triple
|
||||
entity:JohnSmith rel:worksAt entity:AcmeCorp .
|
||||
|
||||
# Subgraph with sub-chunk provenance
|
||||
subgraph:001 tg:contains <<entity:JohnSmith rel:worksAt entity:AcmeCorp>> .
|
||||
subgraph:001 prov:wasDerivedFrom chunk:123-1-1 .
|
||||
subgraph:001 tg:sourceText "John Smith has worked at Acme Corp since 2019" .
|
||||
subgraph:001 tg:sourceCharOffset 1547 .
|
||||
subgraph:001 tg:sourceCharLength 46 .
|
||||
```
|
||||
|
||||
**Пример с выделением текста (альтернативный):**
|
||||
```
|
||||
subgraph:001 tg:contains <<entity:JohnSmith rel:worksAt entity:AcmeCorp>> .
|
||||
subgraph:001 prov:wasDerivedFrom chunk:123-1-1 .
|
||||
subgraph:001 tg:sourceRange "1547-1593" .
|
||||
subgraph:001 tg:sourceText "John Smith has worked at Acme Corp since 2019" .
|
||||
```
|
||||
|
||||
**Соображения по реализации:**
|
||||
|
||||
Извлечение на основе LLM может не всегда предоставлять информацию о позициях символов.
|
||||
<<<<<<< HEAD
|
||||
Можно запросить LLM, чтобы он возвращал исходное предложение/фразу вместе с извлеченными тройками.
|
||||
=======
|
||||
Можно запросить LLM, чтобы она возвращала исходное предложение/фразу вместе с извлеченными тройками.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Альтернативно, можно выполнить постобработку для сопоставления извлеченных сущностей с исходным текстом.
|
||||
Компромисс между сложностью извлечения и детализацией происхождения.
|
||||
Может быть проще реализовать с использованием структурированных методов извлечения, чем с использованием LLM для извлечения в свободной форме.
|
||||
|
||||
<<<<<<< HEAD
|
||||
Это обозначено как перспективное направление - сначала следует реализовать базовое отслеживание происхождения на уровне фрагментов, а отслеживание подфрагментов следует рассматривать как улучшение в будущем, если это будет возможно.
|
||||
|
||||
### Двухуровневая модель хранения
|
||||
|
||||
Граф происхождения строится постепенно по мере прохождения документов через конвейер:
|
||||
|
||||
| Хранилище | Что хранится | Назначение |
|
||||
|-------|---------------|---------|
|
||||
| Библиотекарь | Содержимое документа + ссылки "родитель-потомок" | Извлечение содержимого, каскадное удаление |
|
||||
| Граф знаний | Ребра "родитель-потомок" + метаданные | Запросы происхождения, атрибуция фактов |
|
||||
|
||||
Оба хранилища поддерживают одну и ту же структуру графа. Библиотекарь хранит содержимое, а граф хранит отношения и обеспечивает возможности запросов для обхода.
|
||||
=======
|
||||
Это обозначено как перспективное направление - сначала следует реализовать базовое отслеживание происхождения на уровне фрагментов, а отслеживание подфрагментов следует рассматривать как улучшение в будущем, если это возможно.
|
||||
|
||||
### Двухуровневая модель хранения
|
||||
|
||||
Граф происхождения строится постепенно по мере прохождения документов по конвейеру:
|
||||
|
||||
| Хранилище | Что хранится | Назначение |
|
||||
|-------|---------------|---------|
|
||||
| Библиотекарь | Содержимое документа + ссылки родитель-потомок | Извлечение содержимого, каскадное удаление |
|
||||
| Граф знаний | Ребра родитель-потомок + метаданные | Запросы происхождения, атрибуция фактов |
|
||||
|
||||
Оба хранилища поддерживают одну и ту же структуру графа. Библиотекарь хранит содержимое, а граф хранит отношения и обеспечивает возможность выполнения запросов для обхода графа.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
### Основные принципы проектирования
|
||||
|
||||
1. **Идентификатор документа как единица потока данных** - Процессоры передают идентификаторы, а не содержимое. Содержимое извлекается из библиотеки, когда это необходимо.
|
||||
|
||||
<<<<<<< HEAD
|
||||
2. **Отправка данных только в исходной точке** - Метаданные записываются в граф только один раз при начале обработки, а не повторяются далее по цепочке.
|
||||
=======
|
||||
2. **Однократная отправка данных в источнике** - Метаданные записываются в граф один раз при начале обработки, а не повторяются далее по цепочке.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
3. **Единый шаблон для процессоров** - Каждый процессор следует одному и тому же шаблону: прием/извлечение/создание/сохранение/отправка/передача.
|
||||
|
||||
4. **Постепенное построение графа** - Каждый процессор добавляет свой уровень в граф. Полная цепочка происхождения строится постепенно.
|
||||
|
||||
5. **Оптимизация после разбиения на фрагменты** - После разбиения на фрагменты сообщения содержат как идентификатор, так и содержимое. Фрагменты небольшие (2-4 КБ), поэтому включение содержимого позволяет избежать ненужных обращений к библиотеке, сохраняя при этом происхождение с помощью идентификатора.
|
||||
|
||||
## Задачи реализации
|
||||
|
||||
### Изменения в библиотеке
|
||||
|
||||
#### Текущее состояние
|
||||
|
||||
Инициирует обработку документа, отправляя идентификатор документа первому процессору.
|
||||
Нет подключения к хранилищу тройных данных - метаданные добавляются к результатам извлечения.
|
||||
<<<<<<< HEAD
|
||||
`add-child-document` создает одноуровневые ссылки "родитель-потомок".
|
||||
=======
|
||||
`add-child-document` создает одноуровневые ссылки родитель-потомок.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
`list-children` возвращает только непосредственные потомки.
|
||||
|
||||
#### Необходимые изменения
|
||||
|
||||
**1. Новый интерфейс: Подключение к хранилищу тройных данных**
|
||||
|
||||
Библиотекарь должен напрямую отправлять ребра метаданных документа в граф знаний при начале обработки.
|
||||
Добавить клиент/публикатор хранилища тройных данных в сервис библиотеки.
|
||||
При инициации обработки: отправлять метаданные корневого документа в виде ребер графа (один раз).
|
||||
|
||||
**2. Словарь типов документов**
|
||||
|
||||
<<<<<<< HEAD
|
||||
Стандартизировать значения `document_type` для дочерних документов:
|
||||
=======
|
||||
Стандартизировать значения `document_type` для документов-потомков:
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
`source` - исходный загруженный документ.
|
||||
`page` - страница, извлеченная из источника (PDF и т.д.).
|
||||
`chunk` - текстовый фрагмент, полученный из страницы или источника.
|
||||
|
||||
#### Краткое описание изменений интерфейса
|
||||
|
||||
| Интерфейс | Изменение |
|
||||
|-----------|--------|
|
||||
| Хранилище тройных данных | Новый исходящий канал - отправка ребер метаданных документа |
|
||||
| Инициация обработки | Отправка метаданных в граф перед передачей идентификатора документа |
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Изменения в извлечении данных из PDF
|
||||
=======
|
||||
### Изменения в извлечении PDF
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
#### Текущее состояние
|
||||
|
||||
Получает содержимое документа (или потоковую передачу больших документов).
|
||||
Извлекает текст из страниц PDF.
|
||||
Передает содержимое страницы в компоновщик.
|
||||
Не взаимодействует с библиотекой или хранилищем тройных данных.
|
||||
|
||||
#### Необходимые изменения
|
||||
|
||||
**1. Новый интерфейс: Клиент библиотеки**
|
||||
|
||||
<<<<<<< HEAD
|
||||
Извлечение данных из PDF должно сохранять каждую страницу как дочерний документ в библиотеке.
|
||||
Добавить клиент библиотеки в сервис извлечения данных из PDF.
|
||||
=======
|
||||
Извлечение PDF должно сохранять каждую страницу как документ-потомок в библиотеке.
|
||||
Добавить клиент библиотеки в сервис извлечения PDF.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Для каждой страницы: вызвать `add-child-document` с parent = идентификатор корневого документа.
|
||||
|
||||
**2. Новый интерфейс: Подключение к хранилищу тройных данных**
|
||||
|
||||
<<<<<<< HEAD
|
||||
Извлечение данных из PDF должно отправлять ребра "родитель-потомок" в граф знаний.
|
||||
=======
|
||||
Извлечение PDF должно отправлять ребра родитель-потомок в граф знаний.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Добавить клиент/публикатор хранилища тройных данных.
|
||||
Для каждой страницы: отправлять ребро, связывающее документ страницы с родительским документом.
|
||||
|
||||
**3. Изменить формат вывода**
|
||||
|
||||
Вместо прямой передачи содержимого страницы, передавайте идентификатор документа страницы.
|
||||
Chunker будет извлекать содержимое из хранилища, используя идентификатор.
|
||||
|
||||
#### Краткое описание изменений интерфейса
|
||||
|
||||
| Интерфейс | Изменение |
|
||||
|-----------|--------|
|
||||
| Хранилище | Новый исходящий поток - сохранение дочерних документов |
|
||||
| Тройной магазин | Новый исходящий поток - генерация связей родитель-потомок |
|
||||
| Сообщение вывода | Изменение с содержимого на идентификатор документа |
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Изменения Chunker
|
||||
=======
|
||||
### Изменения в Chunker
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
#### Текущее состояние
|
||||
|
||||
Получает содержимое страницы/текста
|
||||
Разбивает на фрагменты
|
||||
Передает содержимое фрагмента обработчикам на последующих этапах
|
||||
Не взаимодействует с хранилищем или тройным магазином
|
||||
|
||||
#### Необходимые изменения
|
||||
|
||||
**1. Изменение обработки входных данных**
|
||||
|
||||
Вместо содержимого получайте идентификатор документа, извлекайте из хранилища.
|
||||
Добавьте клиент хранилища в сервис Chunker
|
||||
Извлекайте содержимое страницы, используя идентификатор документа
|
||||
|
||||
**2. Новый интерфейс: Клиент хранилища (запись)**
|
||||
|
||||
Сохраняйте каждый фрагмент как дочерний документ в хранилище.
|
||||
Для каждого фрагмента: вызывайте `add-child-document` с parent = идентификатор документа страницы
|
||||
|
||||
**3. Новый интерфейс: Подключение к тройному магазину**
|
||||
|
||||
Генерируйте связи родитель-потомок для графа знаний.
|
||||
Добавьте клиент/публикатор тройного магазина
|
||||
Для каждого фрагмента: генерируйте связь, связывающую документ фрагмента с документом страницы
|
||||
|
||||
**4. Изменение формата вывода**
|
||||
|
||||
Передавайте как идентификатор документа фрагмента, так и содержимое фрагмента (оптимизация после обработки фрагмента).
|
||||
Обработчики на последующих этапах получают идентификатор для отслеживания происхождения + содержимое для работы
|
||||
|
||||
#### Краткое описание изменений интерфейса
|
||||
|
||||
| Интерфейс | Изменение |
|
||||
|-----------|--------|
|
||||
| Сообщение ввода | Изменение с содержимого на идентификатор документа |
|
||||
| Хранилище | Новый исходящий поток (чтение + запись) - извлечение содержимого, сохранение дочерних документов |
|
||||
| Тройной магазин | Новый исходящий поток - генерация связей родитель-потомок |
|
||||
| Сообщение вывода | Изменение с содержимого только на идентификатор + содержимое |
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Изменения Knowledge Extractor
|
||||
=======
|
||||
### Изменения в Knowledge Extractor
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
#### Текущее состояние
|
||||
|
||||
Получает содержимое фрагмента
|
||||
Извлекает тройки и вложения
|
||||
<<<<<<< HEAD
|
||||
Передает в тройной магазин и хранилище вложений
|
||||
=======
|
||||
Отправляет в тройной магазин и хранилище вложений
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
`subjectOf` отношение указывает на верхний уровень документа (а не на фрагмент)
|
||||
|
||||
#### Необходимые изменения
|
||||
|
||||
**1. Изменение обработки входных данных**
|
||||
|
||||
<<<<<<< HEAD
|
||||
Получайте идентификатор фрагмента вместе с содержимым.
|
||||
=======
|
||||
Получайте идентификатор документа фрагмента вместе с содержимым.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Используйте идентификатор фрагмента для отслеживания происхождения (содержимое уже включено в соответствии с оптимизацией)
|
||||
|
||||
**2. Обновление отслеживания происхождения тройками**
|
||||
|
||||
Связывайте извлеченные тройки с фрагментом (а не с верхним уровнем документа).
|
||||
Используйте реификацию для создания связи, указывающей на связь
|
||||
`subjectOf` отношение: тройка → идентификатор документа фрагмента
|
||||
Первое использование существующей поддержки реификации
|
||||
|
||||
**3. Обновление отслеживания происхождения вложениями**
|
||||
|
||||
Связывайте идентификаторы сущностей вложений с фрагментом.
|
||||
Генерируйте связь: идентификатор сущности вложения → идентификатор документа фрагмента
|
||||
|
||||
#### Краткое описание изменений интерфейса
|
||||
|
||||
| Интерфейс | Изменение |
|
||||
|-----------|--------|
|
||||
| Сообщение ввода | Ожидается идентификатор фрагмента + содержимое (а не только содержимое) |
|
||||
| Тройной магазин | Используйте реификацию для отслеживания происхождения тройки → фрагмент |
|
||||
<<<<<<< HEAD
|
||||
| Отслеживание происхождения вложениями | Свяжите идентификатор сущности → идентификатор фрагмента |
|
||||
=======
|
||||
| Отслеживание происхождения вложений | Свяжите идентификатор сущности → идентификатор фрагмента |
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
## Ссылки
|
||||
|
||||
Отслеживание происхождения во время запроса: `docs/tech-specs/query-time-provenance.md`
|
||||
Стандарт PROV-O для моделирования происхождения
|
||||
Существующие метаданные источника в графе знаний (требуется аудит)
|
||||
313
docs/tech-specs/ru/flow-class-definition.ru.md
Normal file
313
docs/tech-specs/ru/flow-class-definition.ru.md
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Шаблон потока определяет полный шаблон структуры потока данных в системе TrustGraph. При инстанцировании он создает взаимосвязанную сеть процессоров, которые обрабатывают прием данных, обработку, хранение и запросы как единую систему.
|
||||
|
||||
## Структура
|
||||
|
||||
Определение шаблона потока состоит из пяти основных разделов:
|
||||
|
||||
### 1. Раздел классов
|
||||
Определяет общие сервисные процессоры, которые инстанцируются один раз для каждого шаблона потока. Эти процессоры обрабатывают запросы от всех экземпляров потока этого класса.
|
||||
|
||||
```json
|
||||
"class": {
|
||||
"service-name:{class}": {
|
||||
"request": "queue-pattern:{class}",
|
||||
"response": "queue-pattern:{class}",
|
||||
"settings": {
|
||||
"setting-name": "fixed-value",
|
||||
"parameterized-setting": "{parameter-name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Характеристики:**
|
||||
<<<<<<< HEAD
|
||||
Общие для всех экземпляров потока одного класса.
|
||||
Обычно это дорогие или безсостояниевые сервисы (LLM, модели эмбеддингов).
|
||||
Используйте переменную шаблона `{class}` для именования очереди.
|
||||
Настройки могут быть фиксированными значениями или параметризованы с использованием синтаксиса `{parameter-name}`.
|
||||
=======
|
||||
Распространяются на все экземпляры потока одного и того же класса.
|
||||
Обычно это дорогие или не имеющие состояния сервисы (LLM, модели эмбеддингов).
|
||||
Используйте переменную шаблона `{class}` для именования очереди.
|
||||
Параметры могут быть фиксированными значениями или параметризованы с использованием синтаксиса `{parameter-name}`.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Примеры: `embeddings:{class}`, `text-completion:{class}`, `graph-rag:{class}`.
|
||||
|
||||
### 2. Раздел потока
|
||||
Определяет процессоры, специфичные для потока, которые создаются для каждого отдельного экземпляра потока. Каждый поток получает свой собственный изолированный набор этих процессоров.
|
||||
|
||||
```json
|
||||
"flow": {
|
||||
"processor-name:{id}": {
|
||||
"input": "queue-pattern:{id}",
|
||||
"output": "queue-pattern:{id}",
|
||||
"settings": {
|
||||
"setting-name": "fixed-value",
|
||||
"parameterized-setting": "{parameter-name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Характеристики:**
|
||||
Уникальный экземпляр для каждого потока.
|
||||
Обработка данных и состояния, специфичных для потока.
|
||||
Использование переменной шаблона `{id}` для именования очереди.
|
||||
Настройки могут быть фиксированными значениями или параметризованы с использованием синтаксиса `{parameter-name}`.
|
||||
Примеры: `chunker:{id}`, `pdf-decoder:{id}`, `kg-extract-relationships:{id}`.
|
||||
|
||||
### Раздел "Интерфейсы"
|
||||
Определяет точки входа и контракты взаимодействия для потока. Они формируют API для внешних систем и взаимодействия между внутренними компонентами.
|
||||
|
||||
Интерфейсы могут иметь две формы:
|
||||
|
||||
**Модель "Отправь и забудь"** (одна очередь):
|
||||
```json
|
||||
"interfaces": {
|
||||
"document-load": "persistent://tg/flow/document-load:{id}",
|
||||
"triples-store": "persistent://tg/flow/triples-store:{id}"
|
||||
}
|
||||
```
|
||||
|
||||
**Шаблон "Запрос/Ответ"** (объект с полями запроса/ответа):
|
||||
```json
|
||||
"interfaces": {
|
||||
"embeddings": {
|
||||
"request": "non-persistent://tg/request/embeddings:{class}",
|
||||
"response": "non-persistent://tg/response/embeddings:{class}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Типы интерфейсов:**
|
||||
**Точки входа:** Места, куда внешние системы вводят данные (`document-load`, `agent`)
|
||||
**Интерфейсы сервисов:** Схемы запросов/ответов для сервисов (`embeddings`, `text-completion`)
|
||||
**Интерфейсы данных:** Точки подключения для потоковой передачи данных (`triples-store`, `entity-contexts-load`)
|
||||
|
||||
### 4. Раздел параметров
|
||||
Отображает имена параметров, специфичные для потока, на централизованно хранящиеся определения параметров:
|
||||
|
||||
```json
|
||||
"parameters": {
|
||||
"model": "llm-model",
|
||||
"temp": "temperature",
|
||||
"chunk": "chunk-size"
|
||||
}
|
||||
```
|
||||
|
||||
**Характеристики:**
|
||||
Ключи являются именами параметров, используемыми в настройках процессора (например, `{model}`)
|
||||
Значения ссылаются на определения параметров, хранящиеся в schema/config
|
||||
<<<<<<< HEAD
|
||||
Обеспечивает повторное использование общих определений параметров в различных потоках.
|
||||
=======
|
||||
Позволяет повторно использовать общие определения параметров в различных потоках.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Уменьшает дублирование схем параметров.
|
||||
|
||||
### 5. Метаданные
|
||||
Дополнительная информация о шаблоне потока:
|
||||
|
||||
```json
|
||||
"description": "Human-readable description",
|
||||
"tags": ["capability-1", "capability-2"]
|
||||
```
|
||||
|
||||
## Переменные шаблона
|
||||
|
||||
### Системные переменные
|
||||
|
||||
#### {id}
|
||||
Заменяется уникальным идентификатором экземпляра потока.
|
||||
Создает изолированные ресурсы для каждого потока.
|
||||
Пример: `flow-123`, `customer-A-flow`
|
||||
|
||||
#### {class}
|
||||
Заменяется именем шаблона потока.
|
||||
Создает общие ресурсы для потоков одного и того же класса.
|
||||
Пример: `standard-rag`, `enterprise-rag`
|
||||
|
||||
### Переменные параметров
|
||||
|
||||
#### {parameter-name}
|
||||
Пользовательские параметры, определенные во время запуска потока.
|
||||
Имена параметров соответствуют ключам в разделе `parameters` потока.
|
||||
Используются в настройках процессоров для настройки поведения.
|
||||
Примеры: `{model}`, `{temp}`, `{chunk}`
|
||||
Заменяются значениями, предоставленными при запуске потока.
|
||||
Проверяются на соответствие централизованным определениям параметров.
|
||||
|
||||
## Настройки процессоров
|
||||
|
||||
Настройки предоставляют значения конфигурации процессорам во время их создания. Они могут быть:
|
||||
|
||||
### Фиксированные настройки
|
||||
Прямые значения, которые не изменяются:
|
||||
```json
|
||||
"settings": {
|
||||
"model": "gemma3:12b",
|
||||
"temperature": 0.7,
|
||||
"max_retries": 3
|
||||
}
|
||||
```
|
||||
|
||||
### Параметрические настройки
|
||||
Значения, использующие параметры, предоставленные при запуске потока:
|
||||
```json
|
||||
"settings": {
|
||||
"model": "{model}",
|
||||
"temperature": "{temp}",
|
||||
"endpoint": "https://{region}.api.example.com"
|
||||
}
|
||||
```
|
||||
|
||||
Имена параметров в настройках соответствуют ключам в разделе `parameters` потока.
|
||||
|
||||
### Примеры настроек
|
||||
|
||||
**Обработчик LLM с параметрами:**
|
||||
```json
|
||||
// In parameters section:
|
||||
"parameters": {
|
||||
"model": "llm-model",
|
||||
"temp": "temperature",
|
||||
"tokens": "max-tokens",
|
||||
"key": "openai-api-key"
|
||||
}
|
||||
|
||||
// In processor definition:
|
||||
"text-completion:{class}": {
|
||||
"request": "non-persistent://tg/request/text-completion:{class}",
|
||||
"response": "non-persistent://tg/response/text-completion:{class}",
|
||||
"settings": {
|
||||
"model": "{model}",
|
||||
"temperature": "{temp}",
|
||||
"max_tokens": "{tokens}",
|
||||
"api_key": "{key}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Разбиение на части с фиксированными и параметризованными настройками:**
|
||||
```json
|
||||
// In parameters section:
|
||||
"parameters": {
|
||||
"chunk": "chunk-size"
|
||||
}
|
||||
|
||||
// In processor definition:
|
||||
"chunker:{id}": {
|
||||
"input": "persistent://tg/flow/chunk:{id}",
|
||||
"output": "persistent://tg/flow/chunk-load:{id}",
|
||||
"settings": {
|
||||
"chunk_size": "{chunk}",
|
||||
"chunk_overlap": 100,
|
||||
"encoding": "utf-8"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Паттерны очередей (Pulsar)
|
||||
|
||||
Схемы потоков используют Apache Pulsar для обмена сообщениями. Имена очередей соответствуют формату Pulsar:
|
||||
```
|
||||
<persistence>://<tenant>/<namespace>/<topic>
|
||||
```
|
||||
|
||||
### Компоненты:
|
||||
**постоянство**: `persistent` или `non-persistent` (режим постоянства Pulsar)
|
||||
**арендатор**: `tg` для определений шаблонов потоков, предоставляемых TrustGraph
|
||||
**пространство имен**: Указывает шаблон обмена сообщениями
|
||||
`flow`: Сервисы "отправь и забудь"
|
||||
<<<<<<< HEAD
|
||||
`request`: Запрос в сервисах "запрос/ответ"
|
||||
`response`: Ответ в сервисах "запрос/ответ"
|
||||
=======
|
||||
`request`: Запрос в сервисах "запрос-ответ"
|
||||
`response`: Ответ в сервисах "запрос-ответ"
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
**тема**: Конкретное имя очереди/темы с переменными шаблона
|
||||
|
||||
### Постоянные очереди
|
||||
Шаблон: `persistent://tg/flow/<topic>:{id}`
|
||||
Используется для сервисов "отправь и забудь" и потоков данных с гарантированной доставкой
|
||||
<<<<<<< HEAD
|
||||
Данные сохраняются в хранилище Pulsar после перезагрузок
|
||||
=======
|
||||
Данные сохраняются в хранилище Pulsar при перезапусках
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Пример: `persistent://tg/flow/chunk-load:{id}`
|
||||
|
||||
### Непостоянные очереди
|
||||
Шаблон: `non-persistent://tg/request/<topic>:{class}` или `non-persistent://tg/response/<topic>:{class}`
|
||||
<<<<<<< HEAD
|
||||
Используется для шаблонов обмена сообщениями "запрос/ответ"
|
||||
=======
|
||||
Используется для шаблонов обмена сообщениями "запрос-ответ"
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Непостоянные, не сохраняются на диск Pulsar
|
||||
Меньшая задержка, подходит для коммуникации в стиле RPC
|
||||
Пример: `non-persistent://tg/request/embeddings:{class}`
|
||||
|
||||
## Архитектура потока данных
|
||||
|
||||
Шаблон потока данных создает унифицированный поток, где:
|
||||
|
||||
1. **Конвейер обработки документов**: Поток от получения данных до преобразования и хранения
|
||||
2. **Сервисы запросов**: Интегрированные процессоры, которые запрашивают одни и те же хранилища данных и сервисы
|
||||
3. **Общие сервисы**: Централизованные процессоры, которые могут использоваться всеми потоками
|
||||
4. **Модули записи данных**: Сохраняют обработанные данные в соответствующие хранилища
|
||||
|
||||
Все процессоры (как `{id}`, так и `{class}`) работают вместе как единый граф потока данных, а не как отдельные системы.
|
||||
|
||||
<<<<<<< HEAD
|
||||
## Пример инстанцирования потока
|
||||
=======
|
||||
## Пример инстанциирования потока
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
Дано:
|
||||
Идентификатор экземпляра потока: `customer-A-flow`
|
||||
Шаблон потока: `standard-rag`
|
||||
Отображения параметров потока:
|
||||
`"model": "llm-model"`
|
||||
`"temp": "temperature"`
|
||||
`"chunk": "chunk-size"`
|
||||
Параметры, предоставленные пользователем:
|
||||
`model`: `gpt-4`
|
||||
`temp`: `0.5`
|
||||
`chunk`: `512`
|
||||
|
||||
Расширение шаблонов:
|
||||
`persistent://tg/flow/chunk-load:{id}` → `persistent://tg/flow/chunk-load:customer-A-flow`
|
||||
`non-persistent://tg/request/embeddings:{class}` → `non-persistent://tg/request/embeddings:standard-rag`
|
||||
`"model": "{model}"` → `"model": "gpt-4"`
|
||||
`"temperature": "{temp}"` → `"temperature": "0.5"`
|
||||
`"chunk_size": "{chunk}"` → `"chunk_size": "512"`
|
||||
|
||||
Это создает:
|
||||
Изолированный конвейер обработки документов для `customer-A-flow`
|
||||
Общий сервис внедрения для всех потоков `standard-rag`
|
||||
Полный поток данных от получения документов до запросов
|
||||
Процессоры, настроенные с предоставленными значениями параметров
|
||||
|
||||
## Преимущества
|
||||
|
||||
1. **Эффективность использования ресурсов**: Дорогие сервисы используются совместно несколькими потоками
|
||||
2. **Изоляция потоков**: Каждый поток имеет свой собственный конвейер обработки данных
|
||||
3. **Масштабируемость**: Можно создавать несколько экземпляров потоков из одного шаблона
|
||||
4. **Модульность**: Четкое разделение между общими и специфичными для потока компонентами
|
||||
5. **Унифицированная архитектура**: Запросы и обработка являются частью одного потока данных
|
||||
613
docs/tech-specs/ru/flow-configurable-parameters.ru.md
Normal file
613
docs/tech-specs/ru/flow-configurable-parameters.ru.md
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает реализацию конфигурируемых параметров для шаблонов потоков в TrustGraph. Параметры позволяют пользователям настраивать параметры процессоров при запуске потока, предоставляя значения, которые заменяют заполнители параметров в определении шаблона потока.
|
||||
|
||||
Параметры работают путем подстановки переменных в шаблонах в параметрах процессоров, аналогично тому, как работают переменные `{id}` и `{class}`, но со значениями, предоставленными пользователем.
|
||||
|
||||
Интеграция поддерживает четыре основных сценария использования:
|
||||
|
||||
1. **Выбор модели**: Предоставление пользователям возможности выбора различных моделей LLM (например, `gemma3:8b`, `gpt-4`, `claude-3`) для процессоров.
|
||||
2. **Конфигурация ресурсов**: Настройка параметров процессоров, таких как размеры пакетов, размеры партий и лимиты параллельности.
|
||||
3. **Настройка поведения**: Изменение поведения процессоров с помощью параметров, таких как температура, максимальное количество токенов или пороги извлечения.
|
||||
4. **Параметры, специфичные для среды**: Настройка конечных точек, ключей API или URL-адресов, специфичных для региона, для каждого развертывания.
|
||||
|
||||
## Цели
|
||||
|
||||
**Динамическая конфигурация процессоров**: Обеспечение возможности динамической конфигурации параметров процессоров путем подстановки параметров.
|
||||
**Проверка параметров**: Предоставление проверки типов и валидации параметров при запуске потока.
|
||||
**Значения по умолчанию**: Поддержка разумных значений по умолчанию, позволяя при этом переопределять их для продвинутых пользователей.
|
||||
**Подстановка шаблонов**: Бесшовная замена заполнителей параметров в параметрах процессоров.
|
||||
**Интеграция с пользовательским интерфейсом**: Обеспечение ввода параметров как через API, так и через пользовательский интерфейс.
|
||||
**Безопасность типов**: Обеспечение соответствия типов параметров ожидаемым типам параметров процессора.
|
||||
**Документация**: Самодокументированные схемы параметров в определениях шаблонов потоков.
|
||||
**Обратная совместимость**: Поддержка обратной совместимости с существующими шаблонами потоков, которые не используют параметры.
|
||||
|
||||
## Предыстория
|
||||
|
||||
В шаблонах потоков в TrustGraph теперь поддерживаются параметры процессоров, которые могут содержать либо фиксированные значения, либо заполнители параметров. Это создает возможность для динамической настройки.
|
||||
|
||||
Текущие параметры процессоров поддерживают:
|
||||
Фиксированные значения: `"model": "gemma3:12b"`
|
||||
Заполнители параметров: `"model": "gemma3:{model-size}"`
|
||||
|
||||
Эта спецификация определяет, как параметры:
|
||||
Объявляются в определениях шаблонов потоков.
|
||||
Проверяются при запуске потоков.
|
||||
Подставляются в параметры процессоров.
|
||||
Предоставляются через API и пользовательский интерфейс.
|
||||
|
||||
Используя параметризованные параметры процессоров, TrustGraph может:
|
||||
Уменьшить дублирование шаблонов потоков, используя параметры для вариаций.
|
||||
Позволить пользователям настраивать поведение процессоров без изменения определений.
|
||||
Поддерживать конфигурации, специфичные для среды, с помощью значений параметров.
|
||||
Обеспечивать безопасность типов с помощью проверки схемы параметров.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Архитектура
|
||||
|
||||
Система конфигурируемых параметров требует следующих технических компонентов:
|
||||
|
||||
1. **Определение схемы параметров**
|
||||
Определения параметров на основе JSON-схемы в метаданных шаблона потока.
|
||||
Определения типов, включая типы string, number, boolean, enum и object.
|
||||
Правила валидации, включая минимальные/максимальные значения, шаблоны и обязательные поля.
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/flow/definition.py
|
||||
|
||||
<<<<<<< HEAD
|
||||
2. **Механизм разрешения параметров**
|
||||
Проверка параметров на соответствие схеме во время выполнения.
|
||||
Применение значений по умолчанию для не указанных параметров.
|
||||
Внедрение параметров в контекст выполнения потока.
|
||||
Преобразование типов и приведение типов при необходимости.
|
||||
=======
|
||||
2. **Движок разрешения параметров**
|
||||
Проверка параметров на соответствие схеме во время выполнения.
|
||||
Применение значений по умолчанию для не указанных параметров.
|
||||
Внедрение параметров в контекст выполнения потока.
|
||||
Приведение типов и преобразование при необходимости.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/flow/parameter_resolver.py
|
||||
|
||||
3. **Интеграция с хранилищем параметров**
|
||||
Получение определений параметров из хранилища схем/конфигураций.
|
||||
Кэширование часто используемых определений параметров.
|
||||
Проверка на соответствие централизованным схемам.
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/flow/parameter_store.py
|
||||
|
||||
4. **Расширения для запуска потоков**
|
||||
Расширения API для приема значений параметров при запуске потока.
|
||||
<<<<<<< HEAD
|
||||
Разрешение отображения (имена потоков на имена определений).
|
||||
=======
|
||||
Разрешение сопоставления параметров (имена потоков к именам определений).
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Обработка ошибок для недействительных комбинаций параметров.
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/flow/launcher.py
|
||||
|
||||
5. **Формы параметров пользовательского интерфейса**
|
||||
Динамическая генерация форм из метаданных параметров потока.
|
||||
<<<<<<< HEAD
|
||||
Отображение параметров в упорядоченном виде с использованием поля `order`.
|
||||
=======
|
||||
Отображение параметров в отсортированном порядке с использованием поля `order`.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Описательные метки параметров с использованием поля `description`.
|
||||
Проверка ввода в соответствии с определениями типов параметров.
|
||||
Предустановки и шаблоны параметров.
|
||||
|
||||
Модуль: trustgraph-ui/components/flow-parameters/
|
||||
|
||||
### Модели данных
|
||||
|
||||
#### Определения параметров (хранятся в схеме/конфигурации)
|
||||
|
||||
Определения параметров хранятся централизованно в системе схем и конфигураций с типом "parameter-type":
|
||||
|
||||
```json
|
||||
{
|
||||
"llm-model": {
|
||||
"type": "string",
|
||||
"description": "LLM model to use",
|
||||
"default": "gpt-4",
|
||||
"enum": [
|
||||
{
|
||||
"id": "gpt-4",
|
||||
"description": "OpenAI GPT-4 (Most Capable)"
|
||||
},
|
||||
{
|
||||
"id": "gpt-3.5-turbo",
|
||||
"description": "OpenAI GPT-3.5 Turbo (Fast & Efficient)"
|
||||
},
|
||||
{
|
||||
"id": "claude-3",
|
||||
"description": "Anthropic Claude 3 (Thoughtful & Safe)"
|
||||
},
|
||||
{
|
||||
"id": "gemma3:8b",
|
||||
"description": "Google Gemma 3 8B (Open Source)"
|
||||
}
|
||||
],
|
||||
"required": false
|
||||
},
|
||||
"model-size": {
|
||||
"type": "string",
|
||||
"description": "Model size variant",
|
||||
"default": "8b",
|
||||
"enum": ["2b", "8b", "12b", "70b"],
|
||||
"required": false
|
||||
},
|
||||
"temperature": {
|
||||
"type": "number",
|
||||
"description": "Model temperature for generation",
|
||||
"default": 0.7,
|
||||
"minimum": 0.0,
|
||||
"maximum": 2.0,
|
||||
"required": false
|
||||
},
|
||||
"chunk-size": {
|
||||
"type": "integer",
|
||||
"description": "Document chunk size",
|
||||
"default": 512,
|
||||
"minimum": 128,
|
||||
"maximum": 2048,
|
||||
"required": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Схема потока с ссылками на параметры
|
||||
|
||||
Схемы потока определяют метаданные параметров со ссылками на типы, описаниями и порядком:
|
||||
|
||||
```json
|
||||
{
|
||||
"flow_class": "document-analysis",
|
||||
"parameters": {
|
||||
"llm-model": {
|
||||
"type": "llm-model",
|
||||
"description": "Primary LLM model for text completion",
|
||||
"order": 1
|
||||
},
|
||||
"llm-rag-model": {
|
||||
"type": "llm-model",
|
||||
"description": "LLM model for RAG operations",
|
||||
"order": 2,
|
||||
"advanced": true,
|
||||
"controlled-by": "llm-model"
|
||||
},
|
||||
"llm-temperature": {
|
||||
"type": "temperature",
|
||||
"description": "Generation temperature for creativity control",
|
||||
"order": 3,
|
||||
"advanced": true
|
||||
},
|
||||
"chunk-size": {
|
||||
"type": "chunk-size",
|
||||
"description": "Document chunk size for processing",
|
||||
"order": 4,
|
||||
"advanced": true
|
||||
},
|
||||
"chunk-overlap": {
|
||||
"type": "integer",
|
||||
"description": "Overlap between document chunks",
|
||||
"order": 5,
|
||||
"advanced": true,
|
||||
"controlled-by": "chunk-size"
|
||||
}
|
||||
},
|
||||
"class": {
|
||||
"text-completion:{class}": {
|
||||
"request": "non-persistent://tg/request/text-completion:{class}",
|
||||
"response": "non-persistent://tg/response/text-completion:{class}",
|
||||
"parameters": {
|
||||
"model": "{llm-model}",
|
||||
"temperature": "{llm-temperature}"
|
||||
}
|
||||
},
|
||||
"rag-completion:{class}": {
|
||||
"request": "non-persistent://tg/request/rag-completion:{class}",
|
||||
"response": "non-persistent://tg/response/rag-completion:{class}",
|
||||
"parameters": {
|
||||
"model": "{llm-rag-model}",
|
||||
"temperature": "{llm-temperature}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"flow": {
|
||||
"chunker:{id}": {
|
||||
"input": "persistent://tg/flow/chunk:{id}",
|
||||
"output": "persistent://tg/flow/chunk-load:{id}",
|
||||
"parameters": {
|
||||
"chunk_size": "{chunk-size}",
|
||||
"chunk_overlap": "{chunk-overlap}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Раздел `parameters` сопоставляет имена параметров, специфичные для потока (ключи), с объектами метаданных параметров, содержащими:
|
||||
<<<<<<< HEAD
|
||||
`type`: Ссылка на централизованно определенное определение параметра (например, "llm-model")
|
||||
`description`: Описание, понятное человеку, для отображения в пользовательском интерфейсе
|
||||
=======
|
||||
`type`: Ссылка на центрально определенное определение параметра (например, "llm-model")
|
||||
`description`: Описание, понятное для человека, для отображения в пользовательском интерфейсе
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
`order`: Порядок отображения параметров в формах (меньшие числа отображаются первыми)
|
||||
`advanced` (необязательно): Булевский флаг, указывающий, является ли этот параметр расширенным (по умолчанию: false). Если установлено значение true, пользовательский интерфейс может скрывать этот параметр по умолчанию или помещать его в раздел "Расширенные"
|
||||
`controlled-by` (необязательно): Имя другого параметра, который управляет значением этого параметра в простом режиме. Если указано, этот параметр наследует свое значение от управляющего параметра, если явно не переопределен
|
||||
|
||||
Этот подход позволяет:
|
||||
Повторное использование определений типов параметров в нескольких шаблонах потоков
|
||||
Централизованное управление и проверка типов параметров
|
||||
Описания и порядок параметров, специфичные для потока
|
||||
Улучшенный пользовательский интерфейс с описательными формами параметров
|
||||
Последовательная проверка параметров во всех потоках
|
||||
Легкое добавление новых стандартных типов параметров
|
||||
Упрощенный пользовательский интерфейс с разделением на режимы "базовый/расширенный"
|
||||
Наследование значений параметров для связанных настроек
|
||||
|
||||
#### Запрос запуска потока
|
||||
|
||||
API запуска потока принимает параметры, используя имена параметров, специфичные для потока:
|
||||
|
||||
```json
|
||||
{
|
||||
"flow_class": "document-analysis",
|
||||
"flow_id": "customer-A-flow",
|
||||
"parameters": {
|
||||
"llm-model": "claude-3",
|
||||
"llm-temperature": 0.5,
|
||||
"chunk-size": 1024
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
Примечание: В этом примере, `llm-rag-model` явно не указан, но он унаследует значение "claude-3" из `llm-model` из-за его `controlled-by` связи. Аналогично, `chunk-overlap` может унаследовать вычисленное значение на основе `chunk-size`.
|
||||
|
||||
Система будет:
|
||||
1. Извлекать метаданные параметров из определения схемы потока
|
||||
2. Сопоставлять имена параметров потока с их определениями типов (например, `llm-model` → `llm-model` тип)
|
||||
3. Разрешать отношения "контролируется" (например, `llm-rag-model` наследуется от `llm-model`)
|
||||
=======
|
||||
Примечание: В этом примере `llm-rag-model` явно не указано, но оно наследует значение "claude-3" из `llm-model` из-за своей `controlled-by` связи. Аналогично, `chunk-overlap` может наследовать вычисленное значение на основе `chunk-size`.
|
||||
|
||||
Система будет:
|
||||
1. Извлекать метаданные параметров из определения шаблона потока
|
||||
2. Сопоставлять имена параметров потока с их определениями типов (например, `llm-model` → тип `llm-model`)
|
||||
3. Разрешать зависимости (например, `llm-rag-model` наследует от `llm-model`)
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
4. Проверять предоставленные пользователем и унаследованные значения на соответствие определениям типов параметров
|
||||
5. Подставлять разрешенные значения в параметры процессоров во время создания экземпляра потока
|
||||
|
||||
### Детали реализации
|
||||
|
||||
#### Процесс разрешения параметров
|
||||
|
||||
При запуске потока система выполняет следующие шаги разрешения параметров:
|
||||
|
||||
<<<<<<< HEAD
|
||||
1. **Загрузка схемы потока**: Загрузить определение схемы потока и извлечь метаданные параметров
|
||||
2. **Извлечение метаданных**: Извлечь `type`, `description`, `order`, `advanced` и `controlled-by` для каждого параметра, определенного в разделе `parameters` схемы потока
|
||||
3. **Поиск определения типа**: Для каждого параметра в схеме потока:
|
||||
Получить определение типа параметра из хранилища схем/конфигураций, используя поле `type`
|
||||
Определения типов хранятся с типом "parameter-type" в системе конфигураций
|
||||
Каждое определение типа содержит схему параметра, значение по умолчанию и правила проверки
|
||||
4. **Разрешение значения по умолчанию**:
|
||||
Для каждого параметра, определенного в схеме потока:
|
||||
Проверить, предоставлено ли пользователем значение для этого параметра
|
||||
Если пользовательское значение не предоставлено, использовать значение `default` из определения типа параметра
|
||||
Создать полную карту параметров, содержащую как предоставленные пользователем, так и значения по умолчанию
|
||||
5. **Разрешение наследования параметров** (отношения "контролируется"):
|
||||
Для параметров с полем `controlled-by`, проверить, было ли предоставлено явное значение
|
||||
Если явное значение не предоставлено, унаследовать значение от контролируемого параметра
|
||||
Если контролируемый параметр также не имеет значения, использовать значение по умолчанию из определения типа
|
||||
Проверить отсутствие циклических зависимостей в отношениях `controlled-by`
|
||||
6. **Проверка**: Проверить полный набор параметров (предоставленные пользователем, значения по умолчанию и унаследованные) на соответствие определениям типов
|
||||
7. **Хранение**: Сохранить полный разрешенный набор параметров с экземпляром потока для обеспечения возможности аудита
|
||||
8. **Подстановка шаблонов**: Заменить заполнители параметров в параметрах процессоров разрешенными значениями
|
||||
9. **Создание экземпляров процессоров**: Создать процессоры с подставленными параметрами
|
||||
=======
|
||||
1. **Загрузка шаблона потока**: Загрузить определение шаблона потока и извлечь метаданные параметров
|
||||
2. **Извлечение метаданных**: Извлечь `type`, `description`, `order`, `advanced` и `controlled-by` для каждого параметра, определенного в разделе `parameters` шаблона потока
|
||||
3. **Поиск определения типа**: Для каждого параметра в шаблоне потока:
|
||||
Получить определение типа параметра из хранилища схемы/конфигурации, используя поле `type`
|
||||
Определения типов хранятся с типом "parameter-type" в системе конфигурации
|
||||
Каждое определение типа содержит схему параметра, значение по умолчанию и правила проверки
|
||||
4. **Разрешение значения по умолчанию**:
|
||||
Для каждого параметра, определенного в шаблоне потока:
|
||||
Проверить, предоставлено ли пользователем значение для этого параметра
|
||||
Если значение не предоставлено пользователем, использовать значение `default` из определения типа параметра
|
||||
Создать полную карту параметров, содержащую как предоставленные пользователем, так и значения по умолчанию
|
||||
5. **Разрешение наследования параметров** (зависимости):
|
||||
Для параметров с полем `controlled-by`, проверить, было ли предоставлено явное значение
|
||||
Если явное значение не предоставлено, унаследовать значение от управляющего параметра
|
||||
Если управляющий параметр также не имеет значения, использовать значение по умолчанию из определения типа
|
||||
Убедиться в отсутствии циклических зависимостей в отношениях `controlled-by`
|
||||
6. **Проверка**: Проверить полный набор параметров (предоставленные пользователем, значения по умолчанию и унаследованные) на соответствие определениям типов
|
||||
7. **Хранение**: Сохранить полный набор разрешенных параметров с экземпляром потока для обеспечения возможности аудита
|
||||
8. **Подстановка шаблонов**: Заменить заполнители параметров в параметрах процессоров разрешенными значениями
|
||||
9. **Создание процессоров**: Создать процессоры с подставленными параметрами
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**Важные замечания по реализации**:
|
||||
Сервис потока ДОЛЖЕН объединять параметры, предоставленные пользователем, со значениями по умолчанию из определений типов параметров
|
||||
Полный набор параметров (включая примененные значения по умолчанию) ДОЛЖЕН храниться с потоком для обеспечения отслеживаемости
|
||||
Разрешение параметров происходит при запуске потока, а не при создании экземпляра процессора
|
||||
Отсутствующие обязательные параметры без значений по умолчанию ДОЛЖНЫ приводить к сбою запуска потока с четким сообщением об ошибке
|
||||
|
||||
<<<<<<< HEAD
|
||||
#### Наследование параметров с помощью "контролируется"
|
||||
=======
|
||||
#### Наследование параметров с помощью controlled-by
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
Поле `controlled-by` обеспечивает наследование значений параметров, что особенно полезно для упрощения пользовательских интерфейсов, сохраняя при этом гибкость:
|
||||
|
||||
**Пример сценария**:
|
||||
<<<<<<< HEAD
|
||||
Параметр `llm-model` контролирует основную модель LLM
|
||||
Параметр `llm-rag-model` имеет значение `"controlled-by": "llm-model"`
|
||||
В простом режиме установка `llm-model` в "gpt-4" автоматически устанавливает `llm-rag-model` в "gpt-4"
|
||||
В расширенном режиме пользователи могут переопределить `llm-rag-model` другим значением
|
||||
|
||||
**Правила разрешения**:
|
||||
1. Если параметр имеет явно предоставленное значение, используйте это значение
|
||||
2. Если нет явного значения и `controlled-by` установлено, используйте значение контролируемого параметра
|
||||
3. Если контролируемый параметр не имеет значения, используйте значение по умолчанию из определения типа
|
||||
4. Циклические зависимости в отношениях `controlled-by` приводят к ошибке проверки
|
||||
|
||||
**Поведение пользовательского интерфейса**:
|
||||
В базовом/простом режиме: Параметры с `controlled-by` могут быть скрыты или показаны как доступные только для чтения со значением, полученным по наследству
|
||||
В расширенном режиме: Все параметры отображаются и могут быть настроены индивидуально
|
||||
При изменении контролируемого параметра зависимые параметры автоматически обновляются, если они не были явно переопределены
|
||||
=======
|
||||
Параметр `llm-model` управляет основной моделью LLM
|
||||
Параметр `llm-rag-model` имеет значение `"controlled-by": "llm-model"`
|
||||
В простом режиме установка `llm-model` в "gpt-4" автоматически устанавливает `llm-rag-model` в "gpt-4"
|
||||
В расширенном режиме пользователи могут перезаписать `llm-rag-model` другим значением
|
||||
|
||||
**Правила разрешения**:
|
||||
1. Если параметр имеет явно предоставленное значение, используйте это значение
|
||||
2. Если нет явного значения и `controlled-by` установлено, используйте значение управляющего параметра
|
||||
3. Если управляющий параметр не имеет значения, используйте значение по умолчанию из определения типа
|
||||
4. Циклические зависимости в отношениях `controlled-by` приводят к ошибке проверки
|
||||
|
||||
**Поведение пользовательского интерфейса**:
|
||||
В режимах "базовый/простой": Параметры с `controlled-by` могут быть скрыты или показаны как доступные только для чтения со унаследованным значением
|
||||
В расширенном режиме: Все параметры отображаются и могут быть настроены индивидуально
|
||||
При изменении управляющего параметра зависимые параметры автоматически обновляются, если они не перезаписаны
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
#### Интеграция с Pulsar
|
||||
|
||||
1. **Операция Start-Flow**
|
||||
<<<<<<< HEAD
|
||||
Операция start-flow в Pulsar должна принимать поле `parameters`, содержащее карту значений параметров
|
||||
Схема запроса start-flow в Pulsar должна быть обновлена для включения необязательного поля `parameters`
|
||||
=======
|
||||
Операция Pulsar start-flow должна принимать поле `parameters`, содержащее карту значений параметров
|
||||
Схема Pulsar для запроса start-flow должна быть обновлена для включения необязательного поля `parameters`
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Пример запроса:
|
||||
```json
|
||||
{
|
||||
"flow_class": "document-analysis",
|
||||
"flow_id": "customer-A-flow",
|
||||
"parameters": {
|
||||
"model": "claude-3",
|
||||
"size": "12b",
|
||||
"temp": 0.5,
|
||||
"chunk": 1024
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Операция Get-Flow**
|
||||
Схему Pulsar для ответа get-flow необходимо обновить, чтобы включить поле `parameters`.
|
||||
Это позволяет клиентам получать значения параметров, которые были использованы при запуске потока.
|
||||
Пример ответа:
|
||||
```json
|
||||
{
|
||||
"flow_id": "customer-A-flow",
|
||||
"flow_class": "document-analysis",
|
||||
"status": "running",
|
||||
"parameters": {
|
||||
"model": "claude-3",
|
||||
"size": "12b",
|
||||
"temp": 0.5,
|
||||
"chunk": 1024
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Реализация сервиса потоков
|
||||
|
||||
Сервис конфигурации потоков (`trustgraph-flow/trustgraph/config/service/flow.py`) требует следующих улучшений:
|
||||
|
||||
1. **Функция разрешения параметров**
|
||||
```python
|
||||
async def resolve_parameters(self, flow_class, user_params):
|
||||
"""
|
||||
Resolve parameters by merging user-provided values with defaults.
|
||||
|
||||
Args:
|
||||
flow_class: The flow blueprint definition dict
|
||||
user_params: User-provided parameters dict
|
||||
|
||||
Returns:
|
||||
Complete parameter dict with user values and defaults merged
|
||||
"""
|
||||
```
|
||||
|
||||
Эта функция должна:
|
||||
Извлекать метаданные параметров из раздела `parameters` схемы потока.
|
||||
Для каждого параметра получать определение его типа из хранилища конфигурации.
|
||||
<<<<<<< HEAD
|
||||
Применять значения по умолчанию для любых параметров, которые не предоставлены пользователем.
|
||||
=======
|
||||
Применять значения по умолчанию для любых параметров, не предоставленных пользователем.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Обрабатывать отношения наследования `controlled-by`.
|
||||
Возвращать полный набор параметров.
|
||||
|
||||
2. **Измененный метод `handle_start_flow`**
|
||||
Вызывать `resolve_parameters` после загрузки схемы потока.
|
||||
Использовать полный набор разрешенных параметров для подстановки в шаблоне.
|
||||
Сохранять полный набор параметров (а не только предоставленные пользователем) вместе с потоком.
|
||||
Проверять, что все необходимые параметры имеют значения.
|
||||
|
||||
3. **Получение типа параметра**
|
||||
Определения типов параметров хранятся в конфигурации с типом "parameter-type".
|
||||
Каждое определение типа содержит схему, значение по умолчанию и правила проверки.
|
||||
Кэшировать часто используемые типы параметров для уменьшения количества обращений к конфигурации.
|
||||
|
||||
#### Интеграция с системой конфигурации
|
||||
|
||||
3. **Хранение объектов потока**
|
||||
Когда поток добавляется в систему конфигурации компонентом потока в менеджере конфигурации, объект потока должен включать разрешенные значения параметров.
|
||||
Менеджер конфигурации должен хранить как исходные параметры, предоставленные пользователем, так и разрешенные значения (с применением значений по умолчанию).
|
||||
Объекты потока в системе конфигурации должны включать:
|
||||
<<<<<<< HEAD
|
||||
`parameters`: Конечные разрешенные значения параметров, используемые для потока.
|
||||
=======
|
||||
`parameters`: Окончательные разрешенные значения параметров, используемые для потока.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
#### Интеграция с CLI
|
||||
|
||||
4. **Команды CLI для библиотеки**
|
||||
Команды CLI, которые запускают потоки, должны поддерживать параметры:
|
||||
Принимать значения параметров через флаги командной строки или файлы конфигурации.
|
||||
Проверять параметры на соответствие определениям схемы потока перед отправкой.
|
||||
Поддерживать ввод файлов параметров (JSON/YAML) для сложных наборов параметров.
|
||||
|
||||
Команды CLI, которые отображают потоки, должны отображать информацию о параметрах:
|
||||
Отображать значения параметров, использованные при запуске потока.
|
||||
Отображать доступные параметры для схемы потока.
|
||||
Отображать схемы проверки параметров и значения по умолчанию.
|
||||
|
||||
#### Интеграция с базовым классом процессора
|
||||
|
||||
5. **Поддержка ParameterSpec**
|
||||
Базовые классы процессоров должны поддерживать подстановку параметров с помощью существующего механизма ParametersSpec.
|
||||
Класс ParametersSpec (расположенный в том же модуле, что и ConsumerSpec и ProducerSpec) должен быть расширен, если это необходимо, для поддержки подстановки параметров в шаблонах.
|
||||
Процессоры должны иметь возможность вызывать ParametersSpec для настройки своих параметров со значениями параметров, разрешенными во время запуска потока.
|
||||
Реализация ParametersSpec должна:
|
||||
Принимать конфигурации параметров, содержащие заполнители параметров (например, `{model}`, `{temperature}`).
|
||||
Поддерживать подстановку параметров во время выполнения при создании экземпляра процессора.
|
||||
<<<<<<< HEAD
|
||||
Проверять, соответствуют ли подставленные значения ожидаемым типам и ограничениям.
|
||||
=======
|
||||
Проверять, что подставленные значения соответствуют ожидаемым типам и ограничениям.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Предоставлять обработку ошибок для отсутствующих или недействительных ссылок на параметры.
|
||||
|
||||
#### Правила подстановки
|
||||
|
||||
Параметры используют формат `{parameter-name}` в параметрах процессора.
|
||||
Имена параметров в параметрах соответствуют ключам в разделе `parameters` схемы потока.
|
||||
Подстановка происходит наряду с заменой `{id}` и `{class}`.
|
||||
Недействительные ссылки на параметры приводят к ошибкам во время запуска.
|
||||
Проверка типов происходит на основе централизованно хранящегося определения параметра.
|
||||
**ВАЖНО**: Все значения параметров хранятся и передаются в виде строк.
|
||||
Числа преобразуются в строки (например, `0.7` становится `"0.7"`).
|
||||
Булевы значения преобразуются в строчные буквы (например, `true` становится `"true"`).
|
||||
Это требуется схемой Pulsar, которая определяет `parameters = Map(String())`.
|
||||
|
||||
Пример разрешения:
|
||||
```
|
||||
Flow parameter mapping: "model": "llm-model"
|
||||
Processor parameter: "model": "{model}"
|
||||
User provides: "model": "gemma3:8b"
|
||||
Final parameter: "model": "gemma3:8b"
|
||||
|
||||
Example with type conversion:
|
||||
Parameter type default: 0.7 (number)
|
||||
Stored in flow: "0.7" (string)
|
||||
Substituted in processor: "0.7" (string)
|
||||
```
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
Юнит-тесты для проверки схемы параметров
|
||||
Интеграционные тесты для проверки подстановки параметров в параметры процессора
|
||||
Комплексные тесты для запуска потоков с разными значениями параметров
|
||||
UI-тесты для генерации и проверки форм параметров
|
||||
Тесты производительности для потоков со многими параметрами
|
||||
<<<<<<< HEAD
|
||||
Пограничные случаи: отсутствующие параметры, неверные типы, неопределенные ссылки на параметры
|
||||
=======
|
||||
Краевые случаи: отсутствующие параметры, неверные типы, неопределенные ссылки на параметры
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
## План миграции
|
||||
|
||||
1. Система должна продолжать поддерживать шаблоны потоков без объявленных параметров.
|
||||
2. Система должна продолжать поддерживать потоки без указанных параметров:
|
||||
<<<<<<< HEAD
|
||||
Это работает для потоков без параметров и для потоков с параметрами (у которых есть значения по умолчанию).
|
||||
|
||||
(имеют значения по умолчанию).
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
В: Должны ли параметры поддерживать сложные вложенные объекты или ограничиваться простыми типами?
|
||||
О: Значения параметров будут закодированы в виде строки, поэтому, вероятно, нам стоит
|
||||
ограничиваться строками.
|
||||
|
||||
В: Должны ли быть разрешены заполнители параметров в именах очередей или только в
|
||||
параметрах?
|
||||
О: Только в параметрах, чтобы избежать странных инъекций и пограничных случаев.
|
||||
=======
|
||||
Это работает для потоков без параметров и для потоков с параметрами
|
||||
(у них есть значения по умолчанию).
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
|
||||
В: Должны ли параметры поддерживать сложные вложенные объекты или ограничиваться простыми типами?
|
||||
О: Значения параметров будут закодированы в виде строки, поэтому, вероятно, нам стоит
|
||||
придерживаться строк.
|
||||
|
||||
В: Должны ли быть разрешены заполнители параметров в именах очередей или только в
|
||||
параметрах?
|
||||
О: Только в параметрах, чтобы избежать странных внедрений и пограничных случаев.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
В: Как обрабатывать конфликты между именами параметров и системными переменными, такими как
|
||||
`id` и `class`?
|
||||
О: Недопустимо указывать id и class при запуске потока.
|
||||
|
||||
В: Должны ли мы поддерживать вычисляемые параметры (выводимые из других параметров)?
|
||||
<<<<<<< HEAD
|
||||
О: Только простая подстановка строк, чтобы избежать странных инъекций и пограничных случаев.
|
||||
=======
|
||||
О: Только простая замена строк, чтобы избежать странных внедрений и пограничных случаев.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
## Ссылки
|
||||
|
||||
Спецификация JSON Schema: https://json-schema.org/
|
||||
<<<<<<< HEAD
|
||||
Спецификация определения шаблона потока: docs/tech-specs/flow-class-definition.md
|
||||
=======
|
||||
Спецификация определения схемы потока: docs/tech-specs/flow-class-definition.md
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
415
docs/tech-specs/ru/graph-contexts.ru.md
Normal file
415
docs/tech-specs/ru/graph-contexts.ru.md
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает изменения основных примитивов графа TrustGraph для
|
||||
соответствия RDF 1.2 и поддержки полной семантики набора данных RDF. Это
|
||||
радикальное изменение для серии выпусков 2.x.
|
||||
|
||||
### Версионирование
|
||||
|
||||
- **2.0**: Ранний выпуск для тестирования. Основные функции доступны, но
|
||||
возможно, еще не полностью готовы к производственному использованию.
|
||||
- **2.1 / 2.2**: Промышленный выпуск. Стабильность и полнота проверены.
|
||||
|
||||
Гибкость в отношении зрелости намеренна - ранние пользователи могут получить
|
||||
доступ к новым возможностям, прежде чем все функции будут полностью
|
||||
проверены.
|
||||
|
||||
## Цели
|
||||
|
||||
Основные цели этой работы - обеспечить возможность добавления метаданных к
|
||||
фактам/утверждениям:
|
||||
|
||||
- **Временная информация**: Связывание фактов со временными метаданными
|
||||
- Когда факт считался истинным
|
||||
- Когда факт стал истинным
|
||||
- Когда факт был признан ложным
|
||||
|
||||
- **Происхождение/Источники**: Отслеживание источников, подтверждающих факт
|
||||
- "Этот факт подтверждается источником X"
|
||||
- Связывание фактов с исходными документами
|
||||
|
||||
- **Достоверность/Надежность**: Запись утверждений об истинности
|
||||
- "Пользователь P заявил, что это было правдой"
|
||||
- "Пользователь Q утверждает, что это неправда"
|
||||
- Обеспечение оценки надежности и обнаружения конфликтов
|
||||
|
||||
**Гипотеза**: Реификация (RDF-star / цитируемые тройки) является ключевым
|
||||
механизмом для достижения этих результатов, поскольку все они требуют
|
||||
создания утверждений об утверждениях.
|
||||
|
||||
## Предыстория
|
||||
|
||||
Чтобы выразить фразу "факт (Алиса знает Боба) был обнаружен 15 января 2024 года"
|
||||
или "источник X поддерживает утверждение (Y вызывает Z)", необходимо
|
||||
обращаться к ребру как к чему-то, о чем можно делать утверждения. Стандартные
|
||||
тройки не поддерживают это.
|
||||
|
||||
### Текущие ограничения
|
||||
|
||||
Текущий класс `Value` в `trustgraph-base/trustgraph/schema/core/primitives.py`
|
||||
может представлять:
|
||||
- URI-узлы (`is_uri=True`)
|
||||
- Литеральные значения (`is_uri=False`)
|
||||
|
||||
Поле `type` существует, но не используется для представления типов данных XSD.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Функции RDF для поддержки
|
||||
|
||||
#### Основные функции (связанные с целями реификации)
|
||||
|
||||
Эти функции напрямую связаны с целями временной информации, происхождения и
|
||||
достоверности:
|
||||
|
||||
1. **Цитируемые тройки RDF 1.2 (RDF-star)**
|
||||
- Ребра, указывающие на другие ребра
|
||||
- Тройка может быть субъектом или объектом другой тройки
|
||||
- Позволяет делать утверждения об утверждениях (реификация)
|
||||
- Основной механизм для аннотирования отдельных фактов
|
||||
|
||||
2. **Набор данных RDF / Именованные графы**
|
||||
- Поддержка нескольких именованных графов внутри набора данных
|
||||
- Каждый граф идентифицируется IRI
|
||||
- Переход от троек (s, p, o) к квадам (s, p, o, g)
|
||||
- Включает граф по умолчанию и ноль или более именованных графов
|
||||
- IRI графа может быть субъектом в утверждениях, например:
|
||||
```
|
||||
<graph-source-A> <discoveredOn> "2024-01-15"
|
||||
<graph-source-A> <hasVeracity> "high"
|
||||
```
|
||||
- Обратите внимание: Именованные графы - это отдельная функция от
|
||||
реификации. Они имеют другие применения (разделение, контроль доступа,
|
||||
организация набора данных) и должны рассматриваться как отдельная
|
||||
возможность.
|
||||
|
||||
3. **Анонимные узлы** (Ограниченная поддержка)
|
||||
- Анонимные узлы без глобального URI
|
||||
- Поддерживаются для совместимости при загрузке внешних данных RDF
|
||||
- **Ограниченный статус**: Без гарантий стабильной идентичности после
|
||||
загрузки
|
||||
- Находите их с помощью запросов по подстановочным знакам (сопоставляйте по
|
||||
связям, а не по ID)
|
||||
- Не являются основной функцией - не полагайтесь на точную обработку
|
||||
анонимных узлов
|
||||
|
||||
#### Временные исправления (радикальное изменение в 2.0)
|
||||
|
||||
Эти функции не связаны напрямую с целями реификации, но являются ценными
|
||||
улучшениями, которые следует включить при внесении радикальных изменений:
|
||||
|
||||
4. **Типы литералов**
|
||||
- Правильное использование поля `type` для типов данных XSD
|
||||
- Примеры: xsd:string, xsd:integer, xsd:dateTime и т.д.
|
||||
- Исправляет текущее ограничение: невозможно правильно представлять даты
|
||||
или целые числа
|
||||
|
||||
5. **Языковые теги**
|
||||
- Поддержка языковых атрибутов для строковых литералов (@en, @fr и т.д.)
|
||||
- Обратите внимание: у литерала либо языковой тег, ЛИБО тип данных, но не
|
||||
оба (за исключением rdf:langString)
|
||||
- Важно для сценариев использования ИИ/многоязычности
|
||||
|
||||
### Модели данных
|
||||
|
||||
#### Термин (переименование из Value)
|
||||
|
||||
Класс `Value` будет переименован в `Term`, чтобы лучше отражать терминологию
|
||||
RDF. Это переименование имеет две цели:
|
||||
1. Согласование наименований с концепциями RDF ( "Term" может быть IRI,
|
||||
литералом, анонимным узлом или цитируемой тройкой - а не просто
|
||||
"значением")
|
||||
2. Вызывает проверку кода на интерфейсе радикальных изменений - любой код,
|
||||
который все еще ссылается на `Value`, явно не работает и требует
|
||||
обновления
|
||||
|
||||
Термин может представлять:
|
||||
|
||||
- **IRI/URI** - Именованный узел/ресурс
|
||||
- **Анонимный узел** - Анонимный узел с локальной областью
|
||||
- **Литерал** - Значение данных с одним из следующих:
|
||||
- Тип данных (тип XSD) ИЛИ
|
||||
- Языковой тег
|
||||
- **Цитируемая тройка** - Тройка, используемая в качестве термина (RDF 1.2)
|
||||
|
||||
##### Выбранный подход: Одиночный класс с дискриминатором типа
|
||||
|
||||
Структура определяется требованиями сериализации - в формате передачи данных
|
||||
необходим дискриминатор типа, независимо от представления на Python.
|
||||
Одиночный класс с полем типа - это естественный выбор и соответствует
|
||||
текущему шаблону `Value`.
|
||||
|
||||
Односимвольный код типа обеспечивает компактную сериализацию:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
# Константы типа Term
|
||||
IRI = "i" # IRI/URI узел
|
||||
BLANK = "b" # Анонимный узел
|
||||
LITERAL = "l" # Литеральное значение
|
||||
TRIPLE = "t" # Цитируемая тройка (RDF 1.2)
|
||||
|
||||
@dataclass
|
||||
class Term:
|
||||
type: str = "" # Один из: IRI, BLANK, LITERAL, TRIPLE
|
||||
|
||||
# Для терминов IRI (type == IRI)
|
||||
iri: str = ""
|
||||
|
||||
# Для анонимных узлов (type == BLANK)
|
||||
id: str = ""
|
||||
|
||||
# Для литералов (type == LITERAL)
|
||||
value: str = ""
|
||||
datatype: str = "" # URI типа данных XSD (взаимно исключающееся с language)
|
||||
language: str = "" # Языковой тег (взаимно исключающееся с datatype)
|
||||
|
||||
# Для цитируемых троек (type == TRIPLE)
|
||||
triple: "Triple | None" = None
|
||||
```
|
||||
|
||||
Примеры использования:
|
||||
|
||||
```python
|
||||
# Термин IRI
|
||||
node = Term(type=IRI, iri="http://example.org/Alice")
|
||||
|
||||
# Литерал с типом данных
|
||||
age = Term(type=LITERAL, value="42", datatype="xsd:integer")
|
||||
|
||||
# Литерал с языковым тегом
|
||||
label = Term(type=LITERAL, value="Hello", language="en")
|
||||
|
||||
# Анонимный узел
|
||||
anon = Term(type=BLANK, id="_:b1")
|
||||
|
||||
# Цитируемая тройка (утверждение об утверждении)
|
||||
inner = Triple(
|
||||
s=Term(type=IRI, iri="http://example.org/Alice"),
|
||||
p=Term(type=IRI, iri="http://example.org/knows"),
|
||||
o=Term(type=IRI, iri="http://example.org/Bob"),
|
||||
)
|
||||
reified = Term(type=TRIPLE, triple=inner)
|
||||
```
|
||||
|
||||
##### Рассмотренные альтернативы
|
||||
|
||||
**Вариант B: Объединение специализированных классов** (`Term = IRI | BlankNode | Literal | QuotedTriple`)
|
||||
- Отклонено: Сериализация все равно потребует дискриминатора типа, что
|
||||
увеличивает сложность
|
||||
|
||||
**Вариант C: Базовый класс с подклассами**
|
||||
- Отклонено: Та же проблема с сериализацией, а также особенности
|
||||
наследования dataclass
|
||||
|
||||
#### Тройка / Квад
|
||||
|
||||
Класс `Triple` получает необязательное поле графа, чтобы стать квадом:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Triple:
|
||||
s: Term | None = None # Subject
|
||||
p: Term | None = None # Predicate
|
||||
o: Term | None = None # Object
|
||||
g: str | None = None # Имя графа (IRI), None = граф по умолчанию
|
||||
```
|
||||
|
||||
Решения в дизайне:
|
||||
- **Имя поля**: `g` для согласованности с `s`, `p`, `o`
|
||||
- **Необязательно**: `None` означает граф по умолчанию (неименованный)
|
||||
- **Тип**: Обычная строка (IRI) вместо Term
|
||||
- Имена графов всегда являются IRI
|
||||
- Анонимные узлы в качестве имен графов не допускаются (слишком
|
||||
путано)
|
||||
- Нет необходимости в полной машинерии Term
|
||||
|
||||
Обратите внимание: Имя класса остается `Triple`, даже если технически это
|
||||
квад. Это позволяет избежать изменений и "тройка" по-прежнему является
|
||||
общепринятым термином. Контекст графа является метаданными о том, где
|
||||
находится тройка.
|
||||
|
||||
### Шаблоны запросов
|
||||
|
||||
Текущий движок запросов принимает комбинации терминов S, P, O. С цитируемыми
|
||||
тройками сама тройка может быть допустимым термином в этих позициях. Ниже
|
||||
приведены примеры шаблонов запросов, поддерживающих исходные цели.
|
||||
|
||||
#### Семантика параметров графа
|
||||
|
||||
В соответствии с соглашениями SPARQL для обратной совместимости:
|
||||
|
||||
- **`g` опущено / None**: Запрос только к графу по умолчанию
|
||||
- **`g` = конкретный IRI**: Запрос только к этому именованному графу
|
||||
- **`g` = подстановочный знак / `*`**: Запрос по всем графам (эквивалентно
|
||||
SPARQL `GRAPH ?g { ... }`)
|
||||
|
||||
Это позволяет выполнять простые запросы простыми и делает запросы именованных
|
||||
графов опциональными.
|
||||
|
||||
Запросы между графами (g=подстановочный знак) полностью поддерживаются.
|
||||
Схема Cassandra включает специализированные таблицы (SPOG, POSG, OSPG), где g
|
||||
является столбцом кластеризации, а не ключом раздела, что обеспечивает
|
||||
эффективные запросы по всем графам.
|
||||
|
||||
#### Временные запросы
|
||||
|
||||
**Найти все факты, обнаруженные после определенной даты:**
|
||||
```
|
||||
S: ? # любая цитируемая тройка
|
||||
P: <discoveredOn>
|
||||
O: > "2024-01-15"^^xsd:date # сравнение даты
|
||||
```
|
||||
|
||||
**Найти, когда определенный факт считался истинным:**
|
||||
```
|
||||
S: << <Alice> <knows> <Bob> >> # цитируемая тройка как субъект
|
||||
P: <believedTrueFrom>
|
||||
O: ? # возвращает дату
|
||||
```
|
||||
|
||||
**Найти факты, которые были признаны ложными:**
|
||||
```
|
||||
S: ? # любая цитируемая тройка
|
||||
P: <discoveredFalseOn>
|
||||
O: ? # имеет любое значение (существует)
|
||||
```
|
||||
|
||||
#### Запросы происхождения
|
||||
|
||||
**Найти все факты, подтвержденные определенным источником:**
|
||||
```
|
||||
S: ? # любая цитируемая тройка
|
||||
P: <supportedBy>
|
||||
O: <source:document-123>
|
||||
```
|
||||
|
||||
**Найти, какие источники подтверждают определенный факт:**
|
||||
```
|
||||
S: << <DrugA> <treats> <DiseaseB> >> # цитируемая тройка как субъект
|
||||
P: <supportedBy>
|
||||
O: ? # возвращает IRI источника
|
||||
```
|
||||
|
||||
#### Запросы достоверности
|
||||
|
||||
**Найти утверждения, которые пользователь отметил как истинные:**
|
||||
```
|
||||
S: ? # любая цитируемая тройка
|
||||
P: <assertedTrueBy>
|
||||
O: <person:Alice>
|
||||
```
|
||||
|
||||
**Найти конфликтующие утверждения (один и тот же факт, разная достоверность):**
|
||||
```
|
||||
# Первый запрос: факты, отмеченные как истинные
|
||||
S: ?
|
||||
P: <assertedTrueBy>
|
||||
O: ?
|
||||
|
||||
# Второй запрос: факты, отмеченные как ложные
|
||||
S: ?
|
||||
P: <assertedFalseBy>
|
||||
O: ?
|
||||
|
||||
# Логика приложения: найти пересечение субъектов
|
||||
```
|
||||
|
||||
**Найти факты со значением надежности ниже определенного значения:**
|
||||
- **Vector store impact**
|
||||
```
|
||||
S: ?
|
||||
P: <value>
|
||||
O: <threshold>
|
||||
```
|
||||
|
||||
### Граница хранилища векторов
|
||||
|
||||
Хранилища векторов всегда ссылаются только на IRI:
|
||||
- Никогда на ребра (цитируемые тройки)
|
||||
- Никогда на литеральные значения
|
||||
- Никогда на анонимные узлы
|
||||
|
||||
Это упрощает хранилище векторов - оно обрабатывает семантическое сходство
|
||||
именованных сущностей. Структура графа обрабатывает отношения,
|
||||
реификацию и метаданные. Цитируемые тройки и именованные графы не
|
||||
усложняют операции с векторами.
|
||||
|
||||
## Соображения безопасности
|
||||
|
||||
Именованные графы не являются функцией безопасности. Пользователи и
|
||||
коллекции остаются границами безопасности. Именованные графы предназначены
|
||||
исключительно для организации данных и поддержки реификации.
|
||||
|
||||
## Соображения производительности
|
||||
|
||||
- Цитируемые тройки добавляют глубину вложенности - могут повлиять на
|
||||
производительность запросов
|
||||
- Необходимы стратегии индексирования именованных графов для эффективных
|
||||
запросов, охватывающих графы
|
||||
- Проект схемы Cassandra должен учитывать эффективное хранение квадов.
|
||||
|
||||
### Vector store boundary
|
||||
|
||||
Vector stores always reference IRIs only:
|
||||
- Never edges (quoted triples)
|
||||
- Never literal values
|
||||
- Never blank nodes
|
||||
|
||||
This keeps the vector store simple - it handles semantic similarity of named
|
||||
entities. The graph structure handles relationships, reification, and metadata.
|
||||
Quoted triples and named graphs don't complicate vector operations.
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
Используйте существующую стратегию тестирования. Поскольку это радикальное
|
||||
изменение, особое внимание уделяется комплексному набору тестов для
|
||||
проверки правильности работы новых структур во всех компонентах.
|
||||
|
||||
## План миграции
|
||||
|
||||
- 2.0 - это радикальное изменение; обратная совместимость не требуется
|
||||
- Существующие данные могут потребовать миграции на новую схему (будет
|
||||
определено на основе окончательного проекта)
|
||||
- Рассмотрите возможность использования инструментов миграции для преобразования
|
||||
существующих троек
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- **Анонимные узлы**: Подтверждена ограниченная поддержка. Возможно, потребуется
|
||||
определить стратегию сколемизации (генерировать IRI при загрузке или
|
||||
сохранять идентификаторы анонимных узлов).
|
||||
- **Синтаксис запросов**: Какой конкретный синтаксис используется для
|
||||
указания цитируемых троек в запросах? Необходимо определить API запросов.
|
||||
- ~~**Словарь предикатов**~~: Решено. Допускаются любые допустимые
|
||||
предикаты RDF, включая пользовательские. Минимальные предположения о
|
||||
валидности RDF. Очень мало жестко заданных значений (например,
|
||||
`rdfs:label` используется в некоторых местах). Стратегия: избегать
|
||||
фиксирования чего-либо, если это абсолютно необходимо.
|
||||
- ~~**Влияние на хранилище векторов**~~: Решено. Хранилища векторов
|
||||
всегда указывают только на IRI - никогда на ребра, литеральные значения или
|
||||
анонимные узлы. Цитируемые тройки и реификация не влияют на хранилище
|
||||
векторов.
|
||||
- ~~**Семантика именованных графов**~~: Решено. Запросы по умолчанию
|
||||
выполняются к графу по умолчанию (соответствует поведению SPARQL,
|
||||
обратная совместимость). Требуется явный параметр графа для запросов к
|
||||
именованным графам или ко всем графам.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Концепции RDF 1.2](https://www.w3.org/TR/rdf12-concepts/)
|
||||
- [RDF-star и SPARQL-star](https://w3c.github.io/rdf-star/)
|
||||
- [Набор данных RDF](https://www.w3.org/TR/rdf11-concepts/#section-dataset)
|
||||
```
|
||||
530
docs/tech-specs/ru/graphql-query.ru.md
Normal file
530
docs/tech-specs/ru/graphql-query.ru.md
Normal file
|
|
@ -0,0 +1,530 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Техническая спецификация запросов GraphQL"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Техническая спецификация запросов GraphQL
|
||||
|
||||
> **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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает реализацию интерфейса запросов GraphQL для хранения структурированных данных TrustGraph в Apache Cassandra. Основываясь на возможностях структурированных данных, описанных в спецификации structured-data.md, этот документ подробно описывает, как запросы GraphQL будут выполняться к таблицам Cassandra, содержащим извлеченные и импортированные структурированные объекты.
|
||||
|
||||
Сервис запросов GraphQL предоставит гибкий и типобезопасный интерфейс для запроса структурированных данных, хранящихся в Cassandra. Он будет динамически адаптироваться к изменениям схемы, поддерживать сложные запросы, включая отношения между объектами, и беспрепятственно интегрироваться с существующей архитектурой TrustGraph, основанной на обмене сообщениями.
|
||||
|
||||
## Цели
|
||||
|
||||
**Динамическая поддержка схемы**: Автоматическая адаптация к изменениям схемы в конфигурации без перезапуска сервиса.
|
||||
**Соответствие стандартам GraphQL**: Предоставление стандартного интерфейса GraphQL, совместимого с существующими инструментами и клиентами GraphQL.
|
||||
<<<<<<< HEAD
|
||||
**Эффективные запросы к Cassandra**: Преобразование запросов GraphQL в эффективные запросы CQL к Cassandra, учитывающие первичные ключи и индексы.
|
||||
**Разрешение отношений**: Поддержка решателей полей GraphQL для отношений между различными типами объектов.
|
||||
**Типобезопасность**: Обеспечение типобезопасного выполнения запросов и генерации ответов на основе определений схемы.
|
||||
**Масштабируемая производительность**: Эффективная обработка одновременных запросов с использованием правильного пула соединений и оптимизации запросов.
|
||||
**Интеграция запросов/ответов**: Поддержка существующей архитектуры TrustGraph, основанной на шаблоне запросов/ответов с использованием Pulsar.
|
||||
=======
|
||||
**Эффективные запросы к Cassandra**: Преобразование запросов GraphQL в эффективные запросы CQL для Cassandra, учитывающие первичные ключи и индексы.
|
||||
**Разрешение отношений**: Поддержка решателей полей GraphQL для отношений между различными типами объектов.
|
||||
**Типобезопасность**: Обеспечение типобезопасного выполнения запросов и генерации ответов на основе определений схемы.
|
||||
**Масштабируемая производительность**: Эффективная обработка одновременных запросов с использованием правильного пула соединений и оптимизации запросов.
|
||||
**Интеграция запросов/ответов**: Поддержка существующего шаблона запросов/ответов TrustGraph, основанного на Pulsar.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
**Обработка ошибок**: Предоставление подробной отчетности об ошибках для несоответствий схемы, ошибок запросов и проблем проверки данных.
|
||||
|
||||
## Предыстория
|
||||
|
||||
<<<<<<< HEAD
|
||||
Реализация хранения структурированных данных (trustgraph-flow/trustgraph/storage/objects/cassandra/) записывает объекты в таблицы Cassandra на основе определений схемы, хранящихся в системе конфигурации TrustGraph. Эти таблицы используют структуру составного первичного ключа с коллекциями и первичными ключами, определенными схемой, что обеспечивает эффективные запросы внутри коллекций.
|
||||
=======
|
||||
Реализация хранения структурированных данных (trustgraph-flow/trustgraph/storage/objects/cassandra/) записывает объекты в таблицы Cassandra на основе определений схемы, хранящихся в системе конфигурации TrustGraph. Эти таблицы используют структуру составного первичного ключа с коллекциями и первичными ключами, определенными схемой, что обеспечивает эффективные запросы в пределах коллекций.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
Текущие ограничения, которые эта спецификация решает:
|
||||
Отсутствие интерфейса запросов для структурированных данных, хранящихся в Cassandra.
|
||||
Невозможность использования мощных возможностей запросов GraphQL для структурированных данных.
|
||||
Отсутствие поддержки обхода отношений между связанными объектами.
|
||||
Отсутствие стандартизированного языка запросов для доступа к структурированным данным.
|
||||
|
||||
Сервис запросов GraphQL устранит эти недостатки, предоставив:
|
||||
Стандартный интерфейс GraphQL для запросов к таблицам Cassandra.
|
||||
Динамическую генерацию схем GraphQL из конфигурации TrustGraph.
|
||||
Эффективное преобразование запросов GraphQL в CQL для Cassandra.
|
||||
Поддержку разрешения отношений с помощью решателей полей.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Архитектура
|
||||
|
||||
Сервис запросов GraphQL будет реализован как новый обработчик потоков TrustGraph, следуя установленным шаблонам:
|
||||
|
||||
<<<<<<< HEAD
|
||||
**Местоположение модуля**: `trustgraph-flow/trustgraph/query/objects/cassandra/`
|
||||
=======
|
||||
**Расположение модуля**: `trustgraph-flow/trustgraph/query/objects/cassandra/`
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**Основные компоненты**:
|
||||
|
||||
1. **Обработчик сервиса запросов GraphQL**
|
||||
Расширяет базовый класс FlowProcessor.
|
||||
Реализует шаблон запросов/ответов, аналогичный существующим сервисам запросов.
|
||||
Отслеживает конфигурацию на предмет обновлений схемы.
|
||||
Поддерживает синхронизацию схемы GraphQL с конфигурацией.
|
||||
|
||||
2. **Генератор динамической схемы**
|
||||
<<<<<<< HEAD
|
||||
Преобразует определения RowSchema TrustGraph в типы GraphQL.
|
||||
Создает типы объектов GraphQL с соответствующими определениями полей.
|
||||
Генерирует корневой тип запроса с решателями на основе коллекций.
|
||||
Обновляет схему GraphQL при изменении конфигурации.
|
||||
|
||||
3. **Исполнитель запросов**
|
||||
Анализирует входящие запросы GraphQL с использованием библиотеки Strawberry.
|
||||
=======
|
||||
Преобразует определения схемы TrustGraph в типы GraphQL.
|
||||
Создает типы объектов GraphQL с правильными определениями полей.
|
||||
Генерирует корневой тип Query с решателями на основе коллекций.
|
||||
Обновляет схему GraphQL при изменении конфигурации.
|
||||
|
||||
3. **Исполнитель запросов**
|
||||
Разбирает входящие запросы GraphQL с использованием библиотеки Strawberry.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Проверяет запросы на соответствие текущей схеме.
|
||||
Выполняет запросы и возвращает структурированные ответы.
|
||||
Обрабатывает ошибки с подробными сообщениями об ошибках.
|
||||
|
||||
4. **Транслятор запросов Cassandra**
|
||||
Преобразует выборки GraphQL в запросы CQL.
|
||||
Оптимизирует запросы на основе доступных индексов и первичных ключей.
|
||||
Обрабатывает фильтрацию, пагинацию и сортировку.
|
||||
Управляет пулом соединений и жизненным циклом сессии.
|
||||
|
||||
5. **Разрешитель отношений**
|
||||
Реализует решатели полей для отношений между объектами.
|
||||
Выполняет эффективную пакетную загрузку для предотвращения запросов N+1.
|
||||
Кэширует разрешенные отношения в контексте запроса.
|
||||
Поддерживает как прямой, так и обратный обход отношений.
|
||||
|
||||
### Мониторинг схемы конфигурации
|
||||
|
||||
Сервис зарегистрирует обработчик конфигурации для получения обновлений схемы:
|
||||
|
||||
```python
|
||||
self.register_config_handler(self.on_schema_config)
|
||||
```
|
||||
|
||||
Когда схемы меняются:
|
||||
1. Разбор новых определений схемы из конфигурации
|
||||
2. Регенерация типов GraphQL и решателей
|
||||
3. Обновление исполняемой схемы
|
||||
4. Очистка любых кэшей, зависящих от схемы
|
||||
|
||||
### Генерация схемы GraphQL
|
||||
|
||||
Для каждой RowSchema в конфигурации, генерируется:
|
||||
|
||||
1. **Тип объекта GraphQL**:
|
||||
Отображение типов полей (string → String, integer → Int, float → Float, boolean → Boolean)
|
||||
Отметка обязательных полей как non-nullable в GraphQL
|
||||
Добавление описаний полей из схемы
|
||||
|
||||
2. **Корневые поля запроса**:
|
||||
Запрос коллекции (например, `customers`, `transactions`)
|
||||
Аргументы фильтрации на основе индексированных полей
|
||||
Поддержка пагинации (limit, offset)
|
||||
Варианты сортировки для полей, поддерживающих сортировку
|
||||
|
||||
3. **Поля отношений**:
|
||||
Определение отношений внешнего ключа из схемы
|
||||
Создание решателей полей для связанных объектов
|
||||
Поддержка как одиночных объектов, так и списков отношений
|
||||
|
||||
### Поток выполнения запроса
|
||||
|
||||
1. **Прием запроса**:
|
||||
Получение ObjectsQueryRequest от Pulsar
|
||||
Извлечение строки запроса GraphQL и переменных
|
||||
Определение контекста пользователя и коллекции
|
||||
|
||||
2. **Валидация запроса**:
|
||||
Разбор запроса GraphQL с использованием Strawberry
|
||||
Проверка на соответствие текущей схеме
|
||||
Проверка выбранных полей и типов аргументов
|
||||
|
||||
3. **Генерация CQL**:
|
||||
Анализ выбранных элементов GraphQL
|
||||
<<<<<<< HEAD
|
||||
Создание запроса CQL с правильными предложениями WHERE
|
||||
=======
|
||||
Построение запроса CQL с правильными предложениями WHERE
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Включение коллекции в ключ партиции
|
||||
Применение фильтров на основе аргументов GraphQL
|
||||
|
||||
4. **Выполнение запроса**:
|
||||
Выполнение запроса CQL к Cassandra
|
||||
Отображение результатов в структуру ответа GraphQL
|
||||
Разрешение любых полей отношений
|
||||
Форматирование ответа в соответствии со спецификацией GraphQL
|
||||
|
||||
5. **Доставка ответа**:
|
||||
Создание ObjectsQueryResponse с результатами
|
||||
Включение любых ошибок выполнения
|
||||
Отправка ответа через Pulsar с идентификатором корреляции
|
||||
|
||||
### Модели данных
|
||||
|
||||
> **Примечание**: Существует существующая схема StructuredQueryRequest/Response в `trustgraph-base/trustgraph/schema/services/structured_query.py`. Однако, в ней отсутствуют критические поля (user, collection) и используются неоптимальные типы. Схемы ниже представляют собой рекомендуемую эволюцию, которая должна либо заменить существующие схемы, либо быть создана как новые типы ObjectsQueryRequest/Response.
|
||||
|
||||
#### Схема запроса (ObjectsQueryRequest)
|
||||
|
||||
```python
|
||||
from pulsar.schema import Record, String, Map, Array
|
||||
|
||||
class ObjectsQueryRequest(Record):
|
||||
user = String() # Cassandra keyspace (follows pattern from TriplesQueryRequest)
|
||||
collection = String() # Data collection identifier (required for partition key)
|
||||
query = String() # GraphQL query string
|
||||
variables = Map(String()) # GraphQL variables (consider enhancing to support all JSON types)
|
||||
operation_name = String() # Operation to execute for multi-operation documents
|
||||
```
|
||||
|
||||
**Обоснование изменений по сравнению с существующим запросом StructuredQueryRequest:**
|
||||
Добавлены поля `user` и `collection` для соответствия шаблону других сервисов запросов.
|
||||
Эти поля необходимы для идентификации пространства ключей и коллекции Cassandra.
|
||||
Переменные пока остаются Map(String()), но в идеале должны поддерживать все типы JSON.
|
||||
|
||||
#### Схема ответа (ObjectsQueryResponse)
|
||||
|
||||
```python
|
||||
from pulsar.schema import Record, String, Array
|
||||
from ..core.primitives import Error
|
||||
|
||||
class GraphQLError(Record):
|
||||
message = String()
|
||||
path = Array(String()) # Path to the field that caused the error
|
||||
extensions = Map(String()) # Additional error metadata
|
||||
|
||||
class ObjectsQueryResponse(Record):
|
||||
error = Error() # System-level error (connection, timeout, etc.)
|
||||
data = String() # JSON-encoded GraphQL response data
|
||||
errors = Array(GraphQLError) # GraphQL field-level errors
|
||||
extensions = Map(String()) # Query metadata (execution time, etc.)
|
||||
```
|
||||
|
||||
**Обоснование изменений по сравнению с существующим StructuredQueryResponse:**
|
||||
Различает системные ошибки (`error`) и ошибки GraphQL (`errors`)
|
||||
Использует структурированные объекты GraphQLError вместо массива строк
|
||||
Добавляет поле `extensions` для соответствия спецификации GraphQL
|
||||
Сохраняет данные в виде строки JSON для совместимости, хотя нативные типы были бы предпочтительнее
|
||||
|
||||
### Оптимизация запросов Cassandra
|
||||
|
||||
Сервис будет оптимизировать запросы Cassandra следующим образом:
|
||||
|
||||
1. **Соблюдение ключей разделов (Partition Keys):**
|
||||
Всегда включайте коллекцию в запросы
|
||||
Эффективно используйте первичные ключи, определенные в схеме
|
||||
Избегайте полного сканирования таблицы
|
||||
|
||||
2. **Использование индексов:**
|
||||
Используйте вторичные индексы для фильтрации
|
||||
Объединяйте несколько фильтров, когда это возможно
|
||||
Предупреждайте, когда запросы могут быть неэффективными
|
||||
|
||||
3. **Пакетная загрузка:**
|
||||
Собирайте запросы для получения взаимосвязей
|
||||
Выполняйте их пакетами для уменьшения количества обращений
|
||||
Кэшируйте результаты в контексте запроса
|
||||
|
||||
4. **Управление соединениями:**
|
||||
Поддерживайте постоянные сессии Cassandra
|
||||
Используйте пули соединений
|
||||
Обрабатывайте повторное подключение в случае сбоев
|
||||
|
||||
### Примеры запросов GraphQL
|
||||
|
||||
#### Простой запрос коллекции
|
||||
```graphql
|
||||
{
|
||||
customers(status: "active") {
|
||||
customer_id
|
||||
name
|
||||
email
|
||||
registration_date
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Запрос со связями
|
||||
```graphql
|
||||
{
|
||||
orders(order_date_gt: "2024-01-01") {
|
||||
order_id
|
||||
total_amount
|
||||
customer {
|
||||
name
|
||||
email
|
||||
}
|
||||
items {
|
||||
product_name
|
||||
quantity
|
||||
price
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Разделенная на страницы выборка
|
||||
```graphql
|
||||
{
|
||||
products(limit: 20, offset: 40) {
|
||||
product_id
|
||||
name
|
||||
price
|
||||
category
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Зависимости реализации
|
||||
=======
|
||||
### Реализация и зависимости
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**Strawberry GraphQL**: Для определения схемы GraphQL и выполнения запросов.
|
||||
**Cassandra Driver**: Для подключения к базе данных (уже используется в модуле хранения).
|
||||
**TrustGraph Base**: Для FlowProcessor и определений схем.
|
||||
**Configuration System**: Для мониторинга и обновления схем.
|
||||
|
||||
### Интерфейс командной строки
|
||||
|
||||
Сервис предоставит команду интерфейса командной строки: `kg-query-objects-graphql-cassandra`
|
||||
|
||||
Аргументы:
|
||||
<<<<<<< HEAD
|
||||
`--cassandra-host`: Точка контакта кластера Cassandra.
|
||||
=======
|
||||
`--cassandra-host`: Контактная точка кластера Cassandra.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
`--cassandra-username`: Имя пользователя для аутентификации.
|
||||
`--cassandra-password`: Пароль для аутентификации.
|
||||
`--config-type`: Тип конфигурации для схем (по умолчанию: "schema").
|
||||
Стандартные аргументы FlowProcessor (конфигурация Pulsar и т.д.).
|
||||
|
||||
<<<<<<< HEAD
|
||||
## Интеграция API
|
||||
=======
|
||||
## Интеграция с API
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
### Темы Pulsar
|
||||
|
||||
**Входная тема**: `objects-graphql-query-request`
|
||||
Схема: ObjectsQueryRequest
|
||||
<<<<<<< HEAD
|
||||
Получает GraphQL-запросы от шлюзовых сервисов.
|
||||
=======
|
||||
Получает запросы GraphQL от шлюзовых сервисов.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
**Выходная тема**: `objects-graphql-query-response`
|
||||
Схема: ObjectsQueryResponse
|
||||
Возвращает результаты запросов и ошибки.
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Интеграция с шлюзом
|
||||
|
||||
Шлюз и обратный шлюз потребуют конечных точек для:
|
||||
1. Приема GraphQL-запросов от клиентов.
|
||||
2. Передачи запросов сервису запросов через Pulsar.
|
||||
3. Возврата ответов клиентам.
|
||||
4. Поддержки GraphQL-запросов на интроспекцию.
|
||||
=======
|
||||
### Интеграция со шлюзом
|
||||
|
||||
Шлюз и обратный шлюз потребуют конечных точек для:
|
||||
1. Приема запросов GraphQL от клиентов.
|
||||
2. Передачи запросов сервису запросов через Pulsar.
|
||||
3. Возврата ответов клиентам.
|
||||
4. Поддержки запросов introspection GraphQL.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
### Интеграция с инструментом агента
|
||||
|
||||
Новый класс инструмента агента позволит:
|
||||
<<<<<<< HEAD
|
||||
Генерировать GraphQL-запросы из естественного языка.
|
||||
Выполнять GraphQL-запросы напрямую.
|
||||
Интерпретировать и форматировать результаты.
|
||||
Интегрироваться с потоками принятия решений агентом.
|
||||
|
||||
## Вопросы безопасности
|
||||
|
||||
**Ограничение глубины запросов**: Предотвращает вложенные запросы, которые могут вызвать проблемы с производительностью.
|
||||
**Анализ сложности запросов**: Ограничивает сложность запросов для предотвращения исчерпания ресурсов.
|
||||
**Разрешения на уровне полей**: Будущая поддержка контроля доступа на уровне полей на основе ролей пользователей.
|
||||
**Санитизация входных данных**: Проверяет и очищает все входные данные запросов для предотвращения атак внедрения.
|
||||
**Ограничение скорости**: Реализует ограничение скорости запросов на пользователя/коллекцию.
|
||||
|
||||
## Вопросы производительности
|
||||
|
||||
**Планирование запросов**: Анализирует запросы перед выполнением для оптимизации генерации CQL.
|
||||
=======
|
||||
Преобразование естественного языка в запросы GraphQL.
|
||||
Непосредственное выполнение запросов GraphQL.
|
||||
Интерпретацию и форматирование результатов.
|
||||
Интеграцию с потоками принятия решений агентом.
|
||||
|
||||
## Вопросы безопасности
|
||||
|
||||
**Ограничение глубины запросов**: Предотвращение глубоко вложенных запросов, которые могут вызвать проблемы с производительностью.
|
||||
**Анализ сложности запросов**: Ограничение сложности запросов для предотвращения исчерпания ресурсов.
|
||||
**Разрешения на уровне полей**: Будущая поддержка контроля доступа на уровне полей на основе ролей пользователей.
|
||||
**Санитарная обработка входных данных**: Проверка и санитарная обработка всех входных данных запросов для предотвращения атак внедрения.
|
||||
**Ограничение скорости**: Реализация ограничения скорости запросов на пользователя/коллекцию.
|
||||
|
||||
## Вопросы производительности
|
||||
|
||||
**Планирование запросов**: Анализ запросов перед выполнением для оптимизации генерации CQL.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
**Кэширование результатов**: Рассмотрите возможность кэширования часто используемых данных на уровне решателя полей.
|
||||
**Пул соединений**: Поддерживайте эффективные пулы соединений к Cassandra.
|
||||
**Пакетные операции**: Объединяйте несколько запросов, когда это возможно, для уменьшения задержки.
|
||||
**Мониторинг**: Отслеживайте метрики производительности запросов для оптимизации.
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Unit Tests
|
||||
Генерация схемы на основе определений RowSchema
|
||||
Разбор и проверка запросов GraphQL
|
||||
Логика генерации запросов CQL
|
||||
Реализации решателей полей
|
||||
|
||||
### Contract Tests
|
||||
Соответствие контракту сообщений Pulsar
|
||||
Достоверность схемы GraphQL
|
||||
Проверка формата ответа
|
||||
Проверка структуры ошибок
|
||||
|
||||
### Integration Tests
|
||||
Комплексное выполнение запросов к тестовой инстанции Cassandra
|
||||
Обработка обновлений схемы
|
||||
Разрешение связей
|
||||
Пагинация и фильтрация
|
||||
Сценарии ошибок
|
||||
|
||||
### Performance Tests
|
||||
Производительность запросов при высокой нагрузке
|
||||
Время отклика для различных уровней сложности запросов
|
||||
Использование памяти при работе с большими наборами результатов
|
||||
Эффективность пула соединений
|
||||
=======
|
||||
### Юнит-тесты
|
||||
Генерация схем из определений RowSchema.
|
||||
Разбор и проверка запросов GraphQL.
|
||||
Логика генерации запросов CQL.
|
||||
Реализации решателей полей.
|
||||
|
||||
### Контрактные тесты
|
||||
Соответствие контракту сообщений Pulsar.
|
||||
Достоверность схемы GraphQL.
|
||||
Проверка формата ответа.
|
||||
Проверка структуры ошибок.
|
||||
|
||||
### Интеграционные тесты
|
||||
Комплексное выполнение запросов к тестовой инстанции Cassandra.
|
||||
Обработка обновления схем.
|
||||
Разрешение взаимосвязей.
|
||||
Пагинация и фильтрация.
|
||||
Сценарии ошибок.
|
||||
|
||||
### Тесты производительности
|
||||
Пропускная способность запросов при нагрузке.
|
||||
Время отклика для различных уровней сложности запросов.
|
||||
Использование памяти с большими наборами результатов.
|
||||
Эффективность пула соединений.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
## План миграции
|
||||
|
||||
Миграция не требуется, так как это новая функциональность. Сервис будет:
|
||||
<<<<<<< HEAD
|
||||
1. Считывать существующие схемы из конфигурации
|
||||
2. Подключаться к существующим таблицам Cassandra, созданным модулем хранения
|
||||
3. Начинать принимать запросы сразу после развертывания
|
||||
|
||||
## Сроки
|
||||
|
||||
1-2 неделя: Реализация основного сервиса и генерация схемы
|
||||
3 неделя: Выполнение запросов и трансляция CQL
|
||||
4 неделя: Разрешение связей и оптимизация
|
||||
5 неделя: Тестирование и настройка производительности
|
||||
6 неделя: Интеграция с шлюзом и документация
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
1. **Эволюция схемы**: Как сервис должен обрабатывать запросы во время изменений схемы?
|
||||
Вариант: Помещать запросы в очередь во время обновлений схемы
|
||||
Вариант: Поддерживать несколько версий схемы одновременно
|
||||
|
||||
2. **Стратегия кэширования**: Следует ли кэшировать результаты запросов?
|
||||
Рассмотреть: Временное истечение срока действия
|
||||
Рассмотреть: Инвалидация на основе событий
|
||||
|
||||
3. **Поддержка федерации**: Следует ли сервису поддерживать GraphQL federation для объединения с другими источниками данных?
|
||||
Это позволит выполнять унифицированные запросы к структурированным и графовым данным
|
||||
|
||||
4. **Поддержка подписок**: Следует ли сервису поддерживать GraphQL subscriptions для получения обновлений в реальном времени?
|
||||
Это потребует поддержки WebSocket в шлюзе
|
||||
|
||||
5. **Пользовательские скаляры**: Следует ли поддерживать пользовательские скалярные типы для специфических типов данных?
|
||||
Примеры: DateTime, UUID, JSON-поля
|
||||
=======
|
||||
1. Считывать существующие схемы из конфигурации.
|
||||
Подключаться к существующим таблицам Cassandra, созданным модулем хранения.
|
||||
3. Начинать принимать запросы сразу после развертывания.
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
**Эволюция схемы**: Как сервис должен обрабатывать запросы во время переходов схемы?
|
||||
Вариант: Помещать запросы в очередь во время обновлений схемы.
|
||||
Вариант: Поддерживать несколько версий схемы одновременно.
|
||||
|
||||
**Стратегия кэширования**: Следует ли кэшировать результаты запросов?
|
||||
Рассмотреть: Временное истечение срока действия.
|
||||
Рассмотреть: Инвалидация на основе событий.
|
||||
|
||||
**Поддержка федерации**: Должен ли сервис поддерживать федерацию GraphQL для объединения с другими источниками данных?
|
||||
Это позволит выполнять унифицированные запросы к структурированным и графовым данным.
|
||||
|
||||
**Поддержка подписок**: Должен ли сервис поддерживать подписки GraphQL для получения обновлений в режиме реального времени?
|
||||
Это потребует поддержки WebSocket в шлюзе.
|
||||
|
||||
**Пользовательские скаляры**: Должна ли поддерживаться поддержка пользовательских скалярных типов для специфических типов данных?
|
||||
Примеры: DateTime, UUID, поля JSON.
|
||||
|
||||
## Ссылки
|
||||
|
||||
Техническая спецификация структурированных данных: ⟦CODE_0⟧
|
||||
Документация Strawberry GraphQL: ⟦URL_0⟧
|
||||
Спецификация GraphQL: ⟦URL_0⟧
|
||||
Документация Cassandra Driver: ⟦URL_0⟧
|
||||
Документация Pulsar: ⟦URL_0⟧
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
## Ссылки
|
||||
|
||||
Техническая спецификация структурированных данных: `docs/tech-specs/structured-data.md`
|
||||
Документация Strawberry GraphQL: https://strawberry.rocks/
|
||||
Спецификация GraphQL: https://spec.graphql.org/
|
||||
Справочник Apache Cassandra CQL: https://cassandra.apache.org/doc/stable/cassandra/cql/
|
||||
<<<<<<< HEAD
|
||||
Документация TrustGraph Flow Processor: Внутренняя документация
|
||||
=======
|
||||
Документация процессора потоков TrustGraph: Внутренняя документация
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
745
docs/tech-specs/ru/graphrag-performance-optimization.ru.md
Normal file
745
docs/tech-specs/ru/graphrag-performance-optimization.ru.md
Normal file
|
|
@ -0,0 +1,745 @@
|
|||
---
|
||||
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)
|
||||
717
docs/tech-specs/ru/import-export-graceful-shutdown.ru.md
Normal file
717
docs/tech-specs/ru/import-export-graceful-shutdown.ru.md
Normal file
|
|
@ -0,0 +1,717 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Описание проблемы
|
||||
|
||||
В настоящее время шлюз TrustGraph испытывает потерю сообщений во время закрытия WebSocket как при импорте, так и при экспорте. Это происходит из-за гонок, когда сообщения, находящиеся в процессе передачи, отбрасываются до того, как они достигнут своего назначения (очереди Pulsar для импорта, клиенты WebSocket для экспорта).
|
||||
|
||||
### Проблемы на стороне импорта
|
||||
1. Буфер asyncio.Queue издателя не очищается при завершении работы.
|
||||
2. WebSocket закрывается до того, как будет гарантировано, что сообщения в очереди достигнут Pulsar.
|
||||
3. Отсутствует механизм подтверждения успешной доставки сообщений.
|
||||
|
||||
### Проблемы на стороне экспорта
|
||||
1. Сообщения подтверждаются в Pulsar до успешной доставки клиентам.
|
||||
2. Жестко заданные таймауты приводят к потере сообщений, когда очереди заполнены.
|
||||
3. Отсутствует механизм обратной связи для обработки медленных потребителей.
|
||||
4. Несколько буферных областей, где данные могут быть потеряны.
|
||||
|
||||
## Обзор архитектуры
|
||||
|
||||
```
|
||||
Import Flow:
|
||||
Client -> Websocket -> TriplesImport -> Publisher -> Pulsar Queue
|
||||
|
||||
Export Flow:
|
||||
Pulsar Queue -> Subscriber -> TriplesExport -> Websocket -> Client
|
||||
```
|
||||
|
||||
## Предлагаемые исправления
|
||||
|
||||
### 1. Улучшения для издателя (сторона импорта)
|
||||
|
||||
#### A. Плавная очистка очереди
|
||||
|
||||
**Файл**: `trustgraph-base/trustgraph/base/publisher.py`
|
||||
|
||||
```python
|
||||
class Publisher:
|
||||
def __init__(self, client, topic, schema=None, max_size=10,
|
||||
chunking_enabled=True, drain_timeout=5.0):
|
||||
self.client = client
|
||||
self.topic = topic
|
||||
self.schema = schema
|
||||
self.q = asyncio.Queue(maxsize=max_size)
|
||||
self.chunking_enabled = chunking_enabled
|
||||
self.running = True
|
||||
self.draining = False # New state for graceful shutdown
|
||||
self.task = None
|
||||
self.drain_timeout = drain_timeout
|
||||
|
||||
async def stop(self):
|
||||
"""Initiate graceful shutdown with draining"""
|
||||
self.running = False
|
||||
self.draining = True
|
||||
|
||||
if self.task:
|
||||
# Wait for run() to complete draining
|
||||
await self.task
|
||||
|
||||
async def run(self):
|
||||
"""Enhanced run method with integrated draining logic"""
|
||||
while self.running or self.draining:
|
||||
try:
|
||||
producer = self.client.create_producer(
|
||||
topic=self.topic,
|
||||
schema=JsonSchema(self.schema),
|
||||
chunking_enabled=self.chunking_enabled,
|
||||
)
|
||||
|
||||
drain_end_time = None
|
||||
|
||||
while self.running or self.draining:
|
||||
try:
|
||||
# Start drain timeout when entering drain mode
|
||||
if self.draining and drain_end_time is None:
|
||||
drain_end_time = time.time() + self.drain_timeout
|
||||
logger.info(f"Publisher entering drain mode, timeout={self.drain_timeout}s")
|
||||
|
||||
# Check drain timeout
|
||||
if self.draining and time.time() > drain_end_time:
|
||||
if not self.q.empty():
|
||||
logger.warning(f"Drain timeout reached with {self.q.qsize()} messages remaining")
|
||||
self.draining = False
|
||||
break
|
||||
|
||||
# Calculate wait timeout based on mode
|
||||
if self.draining:
|
||||
# Shorter timeout during draining to exit quickly when empty
|
||||
timeout = min(0.1, drain_end_time - time.time())
|
||||
else:
|
||||
# Normal operation timeout
|
||||
timeout = 0.25
|
||||
|
||||
# Get message from queue
|
||||
id, item = await asyncio.wait_for(
|
||||
self.q.get(),
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
# Send the message (single place for sending)
|
||||
if id:
|
||||
producer.send(item, { "id": id })
|
||||
else:
|
||||
producer.send(item)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
# If draining and queue is empty, we're done
|
||||
if self.draining and self.q.empty():
|
||||
logger.info("Publisher queue drained successfully")
|
||||
self.draining = False
|
||||
break
|
||||
continue
|
||||
|
||||
except asyncio.QueueEmpty:
|
||||
# If draining and queue is empty, we're done
|
||||
if self.draining and self.q.empty():
|
||||
logger.info("Publisher queue drained successfully")
|
||||
self.draining = False
|
||||
break
|
||||
continue
|
||||
|
||||
# Flush producer before closing
|
||||
if producer:
|
||||
producer.flush()
|
||||
producer.close()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Exception in publisher: {e}", exc_info=True)
|
||||
|
||||
if not self.running and not self.draining:
|
||||
return
|
||||
|
||||
# If handler drops out, sleep a retry
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def send(self, id, item):
|
||||
"""Send still works normally - just adds to queue"""
|
||||
if self.draining:
|
||||
# Optionally reject new messages during drain
|
||||
raise RuntimeError("Publisher is shutting down, not accepting new messages")
|
||||
await self.q.put((id, item))
|
||||
```
|
||||
|
||||
**Основные преимущества дизайна:**
|
||||
**Единое место отправки:** Все вызовы `producer.send()` происходят в одном месте внутри метода `run()`.
|
||||
**Чистая конечная машина состояний:** Три четких состояния - работа, слив, остановка.
|
||||
**Защита от таймаута:** Не зависнет бесконечно во время слива.
|
||||
**Улучшенная наблюдаемость:** Четкое логирование хода слива и переходов состояний.
|
||||
**Опциональное отклонение сообщений:** Может отклонять новые сообщения во время фазы завершения работы.
|
||||
|
||||
#### B. Улучшенный порядок завершения работы
|
||||
|
||||
**Файл:** `trustgraph-flow/trustgraph/gateway/dispatch/triples_import.py`
|
||||
|
||||
```python
|
||||
class TriplesImport:
|
||||
async def destroy(self):
|
||||
"""Enhanced destroy with proper shutdown order"""
|
||||
# Step 1: Stop accepting new messages
|
||||
self.running.stop()
|
||||
|
||||
# Step 2: Wait for publisher to drain its queue
|
||||
logger.info("Draining publisher queue...")
|
||||
await self.publisher.stop()
|
||||
|
||||
# Step 3: Close websocket only after queue is drained
|
||||
if self.ws:
|
||||
await self.ws.close()
|
||||
```
|
||||
|
||||
### 2. Улучшения для подписчиков (экспортная сторона)
|
||||
|
||||
#### A. Интегрированная схема отвода
|
||||
|
||||
**Файл**: `trustgraph-base/trustgraph/base/subscriber.py`
|
||||
|
||||
```python
|
||||
class Subscriber:
|
||||
def __init__(self, client, topic, subscription, consumer_name,
|
||||
schema=None, max_size=100, metrics=None,
|
||||
backpressure_strategy="block", drain_timeout=5.0):
|
||||
# ... existing init ...
|
||||
self.backpressure_strategy = backpressure_strategy
|
||||
self.running = True
|
||||
self.draining = False # New state for graceful shutdown
|
||||
self.drain_timeout = drain_timeout
|
||||
self.pending_acks = {} # Track messages awaiting delivery
|
||||
|
||||
async def stop(self):
|
||||
"""Initiate graceful shutdown with draining"""
|
||||
self.running = False
|
||||
self.draining = True
|
||||
|
||||
if self.task:
|
||||
# Wait for run() to complete draining
|
||||
await self.task
|
||||
|
||||
async def run(self):
|
||||
"""Enhanced run method with integrated draining logic"""
|
||||
while self.running or self.draining:
|
||||
if self.metrics:
|
||||
self.metrics.state("stopped")
|
||||
|
||||
try:
|
||||
self.consumer = self.client.subscribe(
|
||||
topic = self.topic,
|
||||
subscription_name = self.subscription,
|
||||
consumer_name = self.consumer_name,
|
||||
schema = JsonSchema(self.schema),
|
||||
)
|
||||
|
||||
if self.metrics:
|
||||
self.metrics.state("running")
|
||||
|
||||
logger.info("Subscriber running...")
|
||||
drain_end_time = None
|
||||
|
||||
while self.running or self.draining:
|
||||
# Start drain timeout when entering drain mode
|
||||
if self.draining and drain_end_time is None:
|
||||
drain_end_time = time.time() + self.drain_timeout
|
||||
logger.info(f"Subscriber entering drain mode, timeout={self.drain_timeout}s")
|
||||
|
||||
# Stop accepting new messages from Pulsar during drain
|
||||
self.consumer.pause_message_listener()
|
||||
|
||||
# Check drain timeout
|
||||
if self.draining and time.time() > drain_end_time:
|
||||
async with self.lock:
|
||||
total_pending = sum(
|
||||
q.qsize() for q in
|
||||
list(self.q.values()) + list(self.full.values())
|
||||
)
|
||||
if total_pending > 0:
|
||||
logger.warning(f"Drain timeout reached with {total_pending} messages in queues")
|
||||
self.draining = False
|
||||
break
|
||||
|
||||
# Check if we can exit drain mode
|
||||
if self.draining:
|
||||
async with self.lock:
|
||||
all_empty = all(
|
||||
q.empty() for q in
|
||||
list(self.q.values()) + list(self.full.values())
|
||||
)
|
||||
if all_empty and len(self.pending_acks) == 0:
|
||||
logger.info("Subscriber queues drained successfully")
|
||||
self.draining = False
|
||||
break
|
||||
|
||||
# Process messages only if not draining
|
||||
if not self.draining:
|
||||
try:
|
||||
msg = await asyncio.to_thread(
|
||||
self.consumer.receive,
|
||||
timeout_millis=250
|
||||
)
|
||||
except _pulsar.Timeout:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"Exception in subscriber receive: {e}", exc_info=True)
|
||||
raise e
|
||||
|
||||
if self.metrics:
|
||||
self.metrics.received()
|
||||
|
||||
# Process the message
|
||||
await self._process_message(msg)
|
||||
else:
|
||||
# During draining, just wait for queues to empty
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Subscriber exception: {e}", exc_info=True)
|
||||
|
||||
finally:
|
||||
# Negative acknowledge any pending messages
|
||||
for msg in self.pending_acks.values():
|
||||
self.consumer.negative_acknowledge(msg)
|
||||
self.pending_acks.clear()
|
||||
|
||||
if self.consumer:
|
||||
self.consumer.unsubscribe()
|
||||
self.consumer.close()
|
||||
self.consumer = None
|
||||
|
||||
if self.metrics:
|
||||
self.metrics.state("stopped")
|
||||
|
||||
if not self.running and not self.draining:
|
||||
return
|
||||
|
||||
# If handler drops out, sleep a retry
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def _process_message(self, msg):
|
||||
"""Process a single message with deferred acknowledgment"""
|
||||
# Store message for later acknowledgment
|
||||
msg_id = str(uuid.uuid4())
|
||||
self.pending_acks[msg_id] = msg
|
||||
|
||||
try:
|
||||
id = msg.properties()["id"]
|
||||
except:
|
||||
id = None
|
||||
|
||||
value = msg.value()
|
||||
delivery_success = False
|
||||
|
||||
async with self.lock:
|
||||
# Deliver to specific subscribers
|
||||
if id in self.q:
|
||||
delivery_success = await self._deliver_to_queue(
|
||||
self.q[id], value
|
||||
)
|
||||
|
||||
# Deliver to all subscribers
|
||||
for q in self.full.values():
|
||||
if await self._deliver_to_queue(q, value):
|
||||
delivery_success = True
|
||||
|
||||
# Acknowledge only on successful delivery
|
||||
if delivery_success:
|
||||
self.consumer.acknowledge(msg)
|
||||
del self.pending_acks[msg_id]
|
||||
else:
|
||||
# Negative acknowledge for retry
|
||||
self.consumer.negative_acknowledge(msg)
|
||||
del self.pending_acks[msg_id]
|
||||
|
||||
async def _deliver_to_queue(self, queue, value):
|
||||
"""Deliver message to queue with backpressure handling"""
|
||||
try:
|
||||
if self.backpressure_strategy == "block":
|
||||
# Block until space available (no timeout)
|
||||
await queue.put(value)
|
||||
return True
|
||||
|
||||
elif self.backpressure_strategy == "drop_oldest":
|
||||
# Drop oldest message if queue full
|
||||
if queue.full():
|
||||
try:
|
||||
queue.get_nowait()
|
||||
if self.metrics:
|
||||
self.metrics.dropped()
|
||||
except asyncio.QueueEmpty:
|
||||
pass
|
||||
await queue.put(value)
|
||||
return True
|
||||
|
||||
elif self.backpressure_strategy == "drop_new":
|
||||
# Drop new message if queue full
|
||||
if queue.full():
|
||||
if self.metrics:
|
||||
self.metrics.dropped()
|
||||
return False
|
||||
await queue.put(value)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to deliver message: {e}")
|
||||
return False
|
||||
```
|
||||
|
||||
**Основные преимущества дизайна (соответствие шаблону издателя):**
|
||||
<<<<<<< HEAD
|
||||
**Единое место обработки сообщений**: Вся обработка сообщений происходит в методе `run()`
|
||||
**Чистая конечная машина состояний**: Три четких состояния - работа, слив, остановка
|
||||
**Приостановка во время слива**: Прекращает прием новых сообщений из Pulsar во время слива существующих очередей
|
||||
**Защита от таймаутов**: Не зависнет бесконечно во время слива
|
||||
**Правильная очистка**: Отправляет отрицательные подтверждения для любых неотправленных сообщений при завершении работы
|
||||
=======
|
||||
**Единое место обработки сообщений**: Вся обработка сообщений происходит в методе `run()`.
|
||||
**Чистый конечный автомат**: Три четких состояния - работа, слив, остановка.
|
||||
**Приостановка во время слива**: Прекращает прием новых сообщений из Pulsar во время слива существующих очередей.
|
||||
**Защита от таймаутов**: Не зависнет бесконечно во время слива.
|
||||
**Правильная очистка**: Отправляет отрицательные подтверждения для любых неотправленных сообщений при завершении работы.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
#### B. Улучшения обработчика экспорта
|
||||
|
||||
**Файл**: `trustgraph-flow/trustgraph/gateway/dispatch/triples_export.py`
|
||||
|
||||
```python
|
||||
class TriplesExport:
|
||||
async def destroy(self):
|
||||
"""Enhanced destroy with graceful shutdown"""
|
||||
# Step 1: Signal stop to prevent new messages
|
||||
self.running.stop()
|
||||
|
||||
# Step 2: Wait briefly for in-flight messages
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
# Step 3: Unsubscribe and stop subscriber (triggers queue drain)
|
||||
if hasattr(self, 'subs'):
|
||||
await self.subs.unsubscribe_all(self.id)
|
||||
await self.subs.stop()
|
||||
|
||||
# Step 4: Close websocket last
|
||||
if self.ws and not self.ws.closed:
|
||||
await self.ws.close()
|
||||
|
||||
async def run(self):
|
||||
"""Enhanced run with better error handling"""
|
||||
self.subs = Subscriber(
|
||||
client = self.pulsar_client,
|
||||
topic = self.queue,
|
||||
consumer_name = self.consumer,
|
||||
subscription = self.subscriber,
|
||||
schema = Triples,
|
||||
backpressure_strategy = "block" # Configurable
|
||||
)
|
||||
|
||||
await self.subs.start()
|
||||
|
||||
self.id = str(uuid.uuid4())
|
||||
q = await self.subs.subscribe_all(self.id)
|
||||
|
||||
consecutive_errors = 0
|
||||
max_consecutive_errors = 5
|
||||
|
||||
while self.running.get():
|
||||
try:
|
||||
resp = await asyncio.wait_for(q.get(), timeout=0.5)
|
||||
await self.ws.send_json(serialize_triples(resp))
|
||||
consecutive_errors = 0 # Reset on success
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
continue
|
||||
|
||||
except queue.Empty:
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Exception sending to websocket: {str(e)}")
|
||||
consecutive_errors += 1
|
||||
|
||||
if consecutive_errors >= max_consecutive_errors:
|
||||
logger.error("Too many consecutive errors, shutting down")
|
||||
break
|
||||
|
||||
# Brief pause before retry
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# Graceful cleanup handled in destroy()
|
||||
```
|
||||
|
||||
### 3. Улучшения на уровне сокетов
|
||||
|
||||
**Файл**: `trustgraph-flow/trustgraph/gateway/endpoint/socket.py`
|
||||
|
||||
```python
|
||||
class SocketEndpoint:
|
||||
async def listener(self, ws, dispatcher, running):
|
||||
"""Enhanced listener with graceful shutdown"""
|
||||
async for msg in ws:
|
||||
if msg.type == WSMsgType.TEXT:
|
||||
await dispatcher.receive(msg)
|
||||
continue
|
||||
elif msg.type == WSMsgType.BINARY:
|
||||
await dispatcher.receive(msg)
|
||||
continue
|
||||
else:
|
||||
# Graceful shutdown on close
|
||||
logger.info("Websocket closing, initiating graceful shutdown")
|
||||
running.stop()
|
||||
|
||||
# Allow time for dispatcher cleanup
|
||||
await asyncio.sleep(1.0)
|
||||
break
|
||||
|
||||
async def handle(self, request):
|
||||
"""Enhanced handler with better cleanup"""
|
||||
# ... existing setup code ...
|
||||
|
||||
try:
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
running = Running()
|
||||
|
||||
dispatcher = await self.dispatcher(
|
||||
ws, running, request.match_info
|
||||
)
|
||||
|
||||
worker_task = tg.create_task(
|
||||
self.worker(ws, dispatcher, running)
|
||||
)
|
||||
|
||||
lsnr_task = tg.create_task(
|
||||
self.listener(ws, dispatcher, running)
|
||||
)
|
||||
|
||||
except ExceptionGroup as e:
|
||||
logger.error("Exception group occurred:", exc_info=True)
|
||||
|
||||
# Attempt graceful dispatcher shutdown
|
||||
try:
|
||||
await asyncio.wait_for(
|
||||
dispatcher.destroy(),
|
||||
timeout=5.0
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning("Dispatcher shutdown timed out")
|
||||
except Exception as de:
|
||||
logger.error(f"Error during dispatcher cleanup: {de}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Socket exception: {e}", exc_info=True)
|
||||
|
||||
finally:
|
||||
# Ensure dispatcher cleanup
|
||||
if dispatcher and hasattr(dispatcher, 'destroy'):
|
||||
try:
|
||||
await dispatcher.destroy()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Ensure websocket is closed
|
||||
if ws and not ws.closed:
|
||||
await ws.close()
|
||||
|
||||
return ws
|
||||
```
|
||||
|
||||
## Варианты конфигурации
|
||||
|
||||
Добавить поддержку конфигурации для настройки поведения:
|
||||
|
||||
```python
|
||||
# config.py
|
||||
class GracefulShutdownConfig:
|
||||
# Publisher settings
|
||||
PUBLISHER_DRAIN_TIMEOUT = 5.0 # Seconds to wait for queue drain
|
||||
PUBLISHER_FLUSH_TIMEOUT = 2.0 # Producer flush timeout
|
||||
|
||||
# Subscriber settings
|
||||
SUBSCRIBER_DRAIN_TIMEOUT = 5.0 # Seconds to wait for queue drain
|
||||
BACKPRESSURE_STRATEGY = "block" # Options: "block", "drop_oldest", "drop_new"
|
||||
SUBSCRIBER_MAX_QUEUE_SIZE = 100 # Maximum queue size before backpressure
|
||||
|
||||
# Socket settings
|
||||
SHUTDOWN_GRACE_PERIOD = 1.0 # Seconds to wait for graceful shutdown
|
||||
MAX_CONSECUTIVE_ERRORS = 5 # Maximum errors before forced shutdown
|
||||
|
||||
# Monitoring
|
||||
LOG_QUEUE_STATS = True # Log queue statistics on shutdown
|
||||
METRICS_ENABLED = True # Enable metrics collection
|
||||
```
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
### Юнит-тесты
|
||||
|
||||
```python
|
||||
async def test_publisher_queue_drain():
|
||||
"""Verify Publisher drains queue on shutdown"""
|
||||
publisher = Publisher(...)
|
||||
|
||||
# Fill queue with messages
|
||||
for i in range(10):
|
||||
await publisher.send(f"id-{i}", {"data": i})
|
||||
|
||||
# Stop publisher
|
||||
await publisher.stop()
|
||||
|
||||
# Verify all messages were sent
|
||||
assert publisher.q.empty()
|
||||
assert mock_producer.send.call_count == 10
|
||||
|
||||
async def test_subscriber_deferred_ack():
|
||||
"""Verify Subscriber only acks on successful delivery"""
|
||||
subscriber = Subscriber(..., backpressure_strategy="drop_new")
|
||||
|
||||
# Fill queue to capacity
|
||||
queue = await subscriber.subscribe("test")
|
||||
for i in range(100):
|
||||
await queue.put({"data": i})
|
||||
|
||||
# Try to add message when full
|
||||
msg = create_mock_message()
|
||||
await subscriber._process_message(msg)
|
||||
|
||||
# Verify negative acknowledgment
|
||||
assert msg.negative_acknowledge.called
|
||||
assert not msg.acknowledge.called
|
||||
```
|
||||
|
||||
### Интеграционные тесты
|
||||
|
||||
```python
|
||||
async def test_import_graceful_shutdown():
|
||||
"""Test import path handles shutdown gracefully"""
|
||||
# Setup
|
||||
import_handler = TriplesImport(...)
|
||||
await import_handler.start()
|
||||
|
||||
# Send messages
|
||||
messages = []
|
||||
for i in range(100):
|
||||
msg = {"metadata": {...}, "triples": [...]}
|
||||
await import_handler.receive(msg)
|
||||
messages.append(msg)
|
||||
|
||||
# Shutdown while messages in flight
|
||||
await import_handler.destroy()
|
||||
|
||||
# Verify all messages reached Pulsar
|
||||
received = await pulsar_consumer.receive_all()
|
||||
assert len(received) == 100
|
||||
|
||||
async def test_export_no_message_loss():
|
||||
"""Test export path doesn't lose acknowledged messages"""
|
||||
# Setup Pulsar with test messages
|
||||
for i in range(100):
|
||||
await pulsar_producer.send({"data": i})
|
||||
|
||||
# Start export handler
|
||||
export_handler = TriplesExport(...)
|
||||
export_task = asyncio.create_task(export_handler.run())
|
||||
|
||||
# Receive some messages
|
||||
received = []
|
||||
for _ in range(50):
|
||||
msg = await websocket.receive()
|
||||
received.append(msg)
|
||||
|
||||
# Force shutdown
|
||||
await export_handler.destroy()
|
||||
|
||||
# Continue receiving until websocket closes
|
||||
while not websocket.closed:
|
||||
try:
|
||||
msg = await websocket.receive()
|
||||
received.append(msg)
|
||||
except:
|
||||
break
|
||||
|
||||
# Verify no acknowledged messages were lost
|
||||
assert len(received) >= 50
|
||||
```
|
||||
|
||||
## План внедрения
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Фаза 1: Критические исправления (Неделя 1)
|
||||
=======
|
||||
### Этап 1: Критические исправления (Неделя 1)
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Исправить время подтверждения подписки (предотвратить потерю сообщений)
|
||||
Добавить очистку очереди издателя
|
||||
Развернуть в тестовой среде
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Фаза 2: Плавное завершение работы (Неделя 2)
|
||||
=======
|
||||
### Этап 2: Плавное завершение работы (Неделя 2)
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Реализовать координацию завершения работы
|
||||
Добавить стратегии обратной связи
|
||||
Тестирование производительности
|
||||
|
||||
<<<<<<< HEAD
|
||||
### Фаза 3: Мониторинг и настройка (Неделя 3)
|
||||
=======
|
||||
### Этап 3: Мониторинг и настройка (Неделя 3)
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Добавить метрики для глубины очередей
|
||||
Добавить оповещения о потере сообщений
|
||||
Настроить значения тайм-аутов на основе данных производственной среды
|
||||
|
||||
## Мониторинг и оповещения
|
||||
|
||||
### Метрики для отслеживания
|
||||
`publisher.queue.depth` - Текущий размер очереди издателя
|
||||
`publisher.messages.dropped` - Сообщения, потерянные во время завершения работы
|
||||
`subscriber.messages.negatively_acknowledged` - Неудачные доставки
|
||||
`websocket.graceful_shutdowns` - Успешные плавные завершения работы
|
||||
`websocket.forced_shutdowns` - Принудительные/тайм-аут завершения работы
|
||||
|
||||
### Оповещения
|
||||
Глубина очереди издателя > 80% от максимальной емкости
|
||||
Любая потеря сообщений во время завершения работы
|
||||
Частота отрицательных подтверждений от подписчика > 1%
|
||||
Превышен тайм-аут завершения работы
|
||||
|
||||
## Обратная совместимость
|
||||
|
||||
<<<<<<< HEAD
|
||||
Все изменения сохраняют обратную совместимость:
|
||||
Поведение по умолчанию не изменено без конфигурации
|
||||
Существующие развертывания продолжают работать
|
||||
Плавное снижение производительности в случае недоступности новых функций
|
||||
=======
|
||||
Все изменения поддерживают обратную совместимость:
|
||||
Поведение по умолчанию не изменено без конфигурации
|
||||
Существующие развертывания продолжают работать
|
||||
Плавное снижение функциональности в случае недоступности новых функций
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
## Вопросы безопасности
|
||||
|
||||
Не введено новых векторов атак
|
||||
Обратная связь предотвращает атаки, приводящие к исчерпанию памяти
|
||||
Настраиваемые лимиты предотвращают злоупотребление ресурсами
|
||||
|
||||
## Влияние на производительность
|
||||
|
||||
Минимальные накладные расходы во время нормальной работы
|
||||
Завершение работы может занять до 5 секунд больше (настраивается)
|
||||
Использование памяти ограничено лимитами размера очереди
|
||||
Влияние на ЦП незначительно (<1% увеличение)
|
||||
491
docs/tech-specs/ru/jsonl-prompt-output.ru.md
Normal file
491
docs/tech-specs/ru/jsonl-prompt-output.ru.md
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Техническая спецификация формата вывода JSONL для запросов"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Техническая спецификация формата вывода JSONL для запросов
|
||||
|
||||
> **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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает реализацию формата вывода JSONL (JSON Lines) для ответов на запросы в TrustGraph. JSONL обеспечивает устойчивость к усечению при извлечении структурированных данных из ответов LLM, решая критические проблемы, связанные с повреждением выходных массивов JSON, когда ответы LLM достигают лимита токенов.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Эта реализация поддерживает следующие сценарии использования:
|
||||
|
||||
1. **Извлечение, устойчивое к усечению**: Извлекайте допустимые частичные результаты, даже когда
|
||||
вывод LLM усекается в середине ответа.
|
||||
<<<<<<< HEAD
|
||||
2. **Извлечение в больших масштабах**: Обрабатывайте извлечение большого количества элементов без риска
|
||||
=======
|
||||
2. **Масштабное извлечение**: Обрабатывайте извлечение большого количества элементов без риска
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
полной сбоя из-за ограничений на количество токенов.
|
||||
3. **Извлечение данных разных типов**: Поддерживайте извлечение нескольких типов сущностей
|
||||
(определения, отношения, сущности, атрибуты) в одном запросе.
|
||||
4. **Вывод, совместимый со стримингом**: Обеспечьте возможность будущей потоковой/инкрементной
|
||||
обработки результатов извлечения.
|
||||
|
||||
## Цели
|
||||
|
||||
**Обратная совместимость**: Существующие запросы, использующие `response-type: "text"` и
|
||||
`response-type: "json"`, продолжают работать без изменений.
|
||||
**Устойчивость к усечению**: Частичные выходные данные LLM приводят к частичным, но допустимым результатам,
|
||||
а не к полному сбою.
|
||||
**Проверка схемы**: Поддержка проверки JSON-схемы для отдельных объектов.
|
||||
**Дискриминированные объединения**: Поддержка выходных данных смешанных типов с использованием поля `type`
|
||||
в качестве дискриминатора.
|
||||
<<<<<<< HEAD
|
||||
**Минимальные изменения API**: Расширение существующей конфигурации запросов с добавлением нового
|
||||
типа ответа и ключа схемы.
|
||||
|
||||
## Обзор
|
||||
=======
|
||||
**Минимальные изменения API**: Расширение существующей конфигурации запросов с помощью нового
|
||||
типа ответа и ключа схемы.
|
||||
|
||||
## Контекст
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
### Текущая архитектура
|
||||
|
||||
Сервис запросов поддерживает два типа ответов:
|
||||
|
||||
1. `response-type: "text"` - Необработанный текстовый ответ, возвращаемый как есть.
|
||||
2. `response-type: "json"` - JSON, полученный из ответа и проверенный на соответствие
|
||||
необязательной `schema`.
|
||||
|
||||
Текущая реализация в `trustgraph-flow/trustgraph/template/prompt_manager.py`:
|
||||
|
||||
```python
|
||||
class Prompt:
|
||||
def __init__(self, template, response_type = "text", terms=None, schema=None):
|
||||
self.template = template
|
||||
self.response_type = response_type
|
||||
self.terms = terms
|
||||
self.schema = schema
|
||||
```
|
||||
|
||||
### Текущие ограничения
|
||||
|
||||
Когда запросы извлечения требуют вывод в виде массивов JSON (`[{...}, {...}, ...]`):
|
||||
|
||||
**Повреждение из-за усечения**: Если языковая модель достигает лимита выходных токенов в середине массива,
|
||||
весь ответ становится недействительным JSON и не может быть проанализирован.
|
||||
**Анализ "все или ничего"**: Необходимо получить полный вывод перед анализом.
|
||||
**Нет частичных результатов**: Усеченный ответ дает нулевые полезные данные.
|
||||
**Ненадежно для больших извлечений**: Чем больше извлеченных элементов, тем выше риск сбоя.
|
||||
|
||||
Данная спецификация решает эти ограничения, вводя формат JSONL для
|
||||
запросов извлечения, где каждый извлеченный элемент является полным JSON-объектом на своей
|
||||
строке.
|
||||
|
||||
## Техническое проектирование
|
||||
|
||||
### Расширение типа ответа
|
||||
|
||||
Добавьте новый тип ответа `"jsonl"` наряду с существующими типами `"text"` и `"json"`.
|
||||
|
||||
#### Изменения конфигурации
|
||||
|
||||
**Новое значение типа ответа:**
|
||||
|
||||
```
|
||||
"response-type": "jsonl"
|
||||
```
|
||||
|
||||
**Интерпретация схемы:**
|
||||
|
||||
Существующий ключ `"schema"` используется как для `"json"`, так и для `"jsonl"` типов ответов.
|
||||
Интерпретация зависит от типа ответа:
|
||||
|
||||
`"json"`: Схема описывает весь ответ (обычно массив или объект).
|
||||
`"jsonl"`: Схема описывает каждую отдельную строку/объект.
|
||||
|
||||
```json
|
||||
{
|
||||
"response-type": "jsonl",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entity": { "type": "string" },
|
||||
"definition": { "type": "string" }
|
||||
},
|
||||
"required": ["entity", "definition"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Это позволяет избежать изменений в инструментах и редакторах конфигурации подсказок.
|
||||
|
||||
### Спецификация формата JSONL
|
||||
|
||||
#### Простое извлечение
|
||||
|
||||
Для подсказок, извлекающих один тип объекта (определения, отношения,
|
||||
темы, строки), вывод представляет собой один объект JSON на строку без обертки:
|
||||
|
||||
**Формат вывода подсказки:**
|
||||
```
|
||||
{"entity": "photosynthesis", "definition": "Process by which plants convert sunlight"}
|
||||
{"entity": "chlorophyll", "definition": "Green pigment in plants"}
|
||||
{"entity": "mitochondria", "definition": "Powerhouse of the cell"}
|
||||
```
|
||||
|
||||
**Контраст с предыдущим форматом JSON-массива:**
|
||||
```json
|
||||
[
|
||||
{"entity": "photosynthesis", "definition": "Process by which plants convert sunlight"},
|
||||
{"entity": "chlorophyll", "definition": "Green pigment in plants"},
|
||||
{"entity": "mitochondria", "definition": "Powerhouse of the cell"}
|
||||
]
|
||||
```
|
||||
|
||||
Если LLM обрезает текст после строки 2, формат массива JSON приводит к недействительному JSON,
|
||||
в то время как JSONL дает два допустимых объекта.
|
||||
|
||||
#### Извлечение данных смешанных типов (дискриминированные объединения)
|
||||
|
||||
Для запросов, извлекающих несколько типов объектов (например, определения и
|
||||
отношения, или сущности, отношения и атрибуты), используйте поле `"type"`
|
||||
в качестве дискриминатора:
|
||||
|
||||
**Формат вывода запроса:**
|
||||
```
|
||||
{"type": "definition", "entity": "DNA", "definition": "Molecule carrying genetic instructions"}
|
||||
{"type": "relationship", "subject": "DNA", "predicate": "located_in", "object": "cell nucleus", "object-entity": true}
|
||||
{"type": "definition", "entity": "RNA", "definition": "Molecule that carries genetic information"}
|
||||
{"type": "relationship", "subject": "RNA", "predicate": "transcribed_from", "object": "DNA", "object-entity": true}
|
||||
```
|
||||
|
||||
**Схема для дискриминированных объединений использует `oneOf`:**
|
||||
```json
|
||||
{
|
||||
"response-type": "jsonl",
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": { "const": "definition" },
|
||||
"entity": { "type": "string" },
|
||||
"definition": { "type": "string" }
|
||||
},
|
||||
"required": ["type", "entity", "definition"]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": { "const": "relationship" },
|
||||
"subject": { "type": "string" },
|
||||
"predicate": { "type": "string" },
|
||||
"object": { "type": "string" },
|
||||
"object-entity": { "type": "boolean" }
|
||||
},
|
||||
"required": ["type", "subject", "predicate", "object", "object-entity"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Извлечение онтологии
|
||||
|
||||
Для извлечения онтологии на основе сущностей, связей и атрибутов:
|
||||
|
||||
**Формат вывода запроса:**
|
||||
```
|
||||
{"type": "entity", "entity": "Cornish pasty", "entity_type": "fo/Recipe"}
|
||||
{"type": "entity", "entity": "beef", "entity_type": "fo/Food"}
|
||||
{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "beef", "object_type": "fo/Food"}
|
||||
{"type": "attribute", "entity": "Cornish pasty", "entity_type": "fo/Recipe", "attribute": "fo/serves", "value": "4 people"}
|
||||
```
|
||||
|
||||
### Детали реализации
|
||||
|
||||
#### Класс Prompt
|
||||
|
||||
Существующий класс `Prompt` не требует изменений. Поле `schema` используется повторно
|
||||
<<<<<<< HEAD
|
||||
для JSONL, а его интерпретация определяется `response_type`:
|
||||
=======
|
||||
для JSONL, и его интерпретация определяется `response_type`:
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
```python
|
||||
class Prompt:
|
||||
def __init__(self, template, response_type="text", terms=None, schema=None):
|
||||
self.template = template
|
||||
self.response_type = response_type
|
||||
self.terms = terms
|
||||
self.schema = schema # Interpretation depends on response_type
|
||||
```
|
||||
|
||||
#### PromptManager.load_config
|
||||
|
||||
Изменения не требуются - существующая загрузка конфигурации уже обрабатывает
|
||||
ключ `schema`.
|
||||
|
||||
#### Разбор JSONL
|
||||
|
||||
Добавлен новый метод разбора ответов в формате JSONL:
|
||||
|
||||
```python
|
||||
def parse_jsonl(self, text):
|
||||
"""
|
||||
Parse JSONL response, returning list of valid objects.
|
||||
|
||||
Invalid lines (malformed JSON, empty lines) are skipped with warnings.
|
||||
This provides truncation resilience - partial output yields partial results.
|
||||
"""
|
||||
results = []
|
||||
|
||||
for line_num, line in enumerate(text.strip().split('\n'), 1):
|
||||
line = line.strip()
|
||||
|
||||
# Skip empty lines
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Skip markdown code fence markers if present
|
||||
if line.startswith('```'):
|
||||
continue
|
||||
|
||||
try:
|
||||
obj = json.loads(line)
|
||||
results.append(obj)
|
||||
except json.JSONDecodeError as e:
|
||||
# Log warning but continue - this provides truncation resilience
|
||||
logger.warning(f"JSONL parse error on line {line_num}: {e}")
|
||||
|
||||
return results
|
||||
```
|
||||
|
||||
#### Изменения в PromptManager.invoke
|
||||
|
||||
Расширить метод invoke для обработки нового типа ответа:
|
||||
|
||||
```python
|
||||
async def invoke(self, id, input, llm):
|
||||
logger.debug("Invoking prompt template...")
|
||||
|
||||
terms = self.terms | self.prompts[id].terms | input
|
||||
resp_type = self.prompts[id].response_type
|
||||
|
||||
prompt = {
|
||||
"system": self.system_template.render(terms),
|
||||
"prompt": self.render(id, input)
|
||||
}
|
||||
|
||||
resp = await llm(**prompt)
|
||||
|
||||
if resp_type == "text":
|
||||
return resp
|
||||
|
||||
if resp_type == "json":
|
||||
try:
|
||||
obj = self.parse_json(resp)
|
||||
except:
|
||||
logger.error(f"JSON parse failed: {resp}")
|
||||
raise RuntimeError("JSON parse fail")
|
||||
|
||||
if self.prompts[id].schema:
|
||||
try:
|
||||
validate(instance=obj, schema=self.prompts[id].schema)
|
||||
logger.debug("Schema validation successful")
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Schema validation fail: {e}")
|
||||
|
||||
return obj
|
||||
|
||||
if resp_type == "jsonl":
|
||||
objects = self.parse_jsonl(resp)
|
||||
|
||||
if not objects:
|
||||
logger.warning("JSONL parse returned no valid objects")
|
||||
return []
|
||||
|
||||
# Validate each object against schema if provided
|
||||
if self.prompts[id].schema:
|
||||
validated = []
|
||||
for i, obj in enumerate(objects):
|
||||
try:
|
||||
validate(instance=obj, schema=self.prompts[id].schema)
|
||||
validated.append(obj)
|
||||
except Exception as e:
|
||||
logger.warning(f"Object {i} failed schema validation: {e}")
|
||||
return validated
|
||||
|
||||
return objects
|
||||
|
||||
raise RuntimeError(f"Response type {resp_type} not known")
|
||||
```
|
||||
|
||||
### Затронутые запросы
|
||||
|
||||
Следующие запросы должны быть перенесены в формат JSONL:
|
||||
|
||||
| Идентификатор запроса | Описание | Тип поля |
|
||||
|-----------|-------------|------------|
|
||||
| `extract-definitions` | Извлечение сущностей/определений | Нет (один тип) |
|
||||
| `extract-relationships` | Извлечение отношений | Нет (один тип) |
|
||||
| `extract-topics` | Извлечение темы/определения | Нет (один тип) |
|
||||
| `extract-rows` | Извлечение структурированных строк | Нет (один тип) |
|
||||
| `agent-kg-extract` | Комбинированное извлечение определений + отношений | Да: `"definition"`, `"relationship"` |
|
||||
| `extract-with-ontologies` / `ontology-extract` | Извлечение на основе онтологии | Да: `"entity"`, `"relationship"`, `"attribute"` |
|
||||
|
||||
### Изменения API
|
||||
|
||||
#### Точка зрения клиента
|
||||
|
||||
Разбор JSONL прозрачен для вызывающих API сервиса запросов. Разбор происходит
|
||||
на стороне сервера в сервисе запросов, и ответ возвращается через стандартное
|
||||
поле `PromptResponse.object` в виде сериализованного массива JSON.
|
||||
|
||||
Когда клиенты вызывают сервис запросов (через `PromptClient.prompt()` или аналогично):
|
||||
|
||||
**`response-type: "json"`** с схемой массива → клиент получает Python `list`
|
||||
**`response-type: "jsonl"`** → клиент получает Python `list`
|
||||
|
||||
С точки зрения клиента, оба возвращают идентичные структуры данных. Разница заключается только в том, как вывод LLM разбирается на стороне сервера:
|
||||
|
||||
|
||||
Формат массива JSON: Один вызов `json.loads()`; полностью завершается с ошибкой, если усечен
|
||||
Формат JSONL: Разбор построчно; дает частичные результаты, если усечен
|
||||
|
||||
Это означает, что существующий клиентский код, ожидающий список от запросов извлечения,
|
||||
не требует изменений при переносе запросов из формата JSON в формат JSONL.
|
||||
|
||||
#### Возвращаемое значение сервера
|
||||
|
||||
Для `response-type: "jsonl"` метод `PromptManager.invoke()` возвращает
|
||||
`list[dict]`, содержащий все успешно разобранные и проверенные объекты. Этот
|
||||
список затем сериализуется в JSON для поля `PromptResponse.object`.
|
||||
|
||||
### Обработка ошибок
|
||||
|
||||
Пустые результаты: Возвращает пустой список `[]` с предупреждением в журнале
|
||||
Частная ошибка разбора: Возвращает список успешно разобранных объектов с
|
||||
предупреждениями в журналах об ошибках
|
||||
Полная ошибка разбора: Возвращает пустой список `[]` с предупреждениями в журналах
|
||||
|
||||
Это отличается от `response-type: "json"`, который вызывает `RuntimeError` при
|
||||
ошибке разбора. Легкое поведение для JSONL намеренно, чтобы обеспечить
|
||||
устойчивость к усечению.
|
||||
|
||||
### Пример конфигурации
|
||||
|
||||
Полный пример конфигурации запроса:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "Extract all entities and their definitions from the following text. Output one JSON object per line.\n\nText:\n{{text}}\n\nOutput format per line:\n{\"entity\": \"<name>\", \"definition\": \"<definition>\"}",
|
||||
"response-type": "jsonl",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entity": {
|
||||
"type": "string",
|
||||
"description": "The entity name"
|
||||
},
|
||||
"definition": {
|
||||
"type": "string",
|
||||
"description": "A clear definition of the entity"
|
||||
}
|
||||
},
|
||||
"required": ["entity", "definition"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Соображения безопасности
|
||||
|
||||
**Проверка входных данных**: Разбор JSON использует стандартную `json.loads()`, что безопасно
|
||||
от атак внедрения.
|
||||
**Проверка схемы**: Используется `jsonschema.validate()` для обеспечения соответствия схеме.
|
||||
**Отсутствие новых уязвимостей**: Разбор JSONL значительно безопаснее, чем разбор JSON-массивов,
|
||||
благодаря обработке построчно.
|
||||
|
||||
## Соображения производительности
|
||||
|
||||
**Память**: Построчный разбор использует меньше пиковой памяти, чем загрузка полных
|
||||
JSON-массивов.
|
||||
**Задержка**: Производительность разбора сопоставима с разбором JSON-массивов.
|
||||
**Проверка**: Проверка схемы выполняется для каждого объекта, что добавляет накладные
|
||||
расходы, но позволяет получать частичные результаты в случае сбоя проверки.
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
### Юнит-тесты
|
||||
|
||||
Разбор JSONL с допустимыми входными данными.
|
||||
Разбор JSONL с пустыми строками.
|
||||
<<<<<<< HEAD
|
||||
Разбор JSONL с блоками форматированного текста Markdown.
|
||||
=======
|
||||
Разбор JSONL с блоками кода Markdown.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
Разбор JSONL с обрезанной последней строкой.
|
||||
Разбор JSONL со строками, содержащими недопустимый JSON.
|
||||
Проверка схемы с использованием `oneOf` для дискриминируемых объединений.
|
||||
Обратная совместимость: существующие `"text"` и `"json"` подсказки не изменены.
|
||||
|
||||
### Интеграционные тесты
|
||||
|
||||
<<<<<<< HEAD
|
||||
Комплексная извлечение данных с использованием подсказок JSONL.
|
||||
Извлечение данных с имитацией усечения (искусственно ограниченный ответ).
|
||||
Извлечение данных смешанных типов с использованием дискриминатора типов.
|
||||
Извлечение данных онтологии со всеми тремя типами.
|
||||
|
||||
### Тесты качества извлечения данных.
|
||||
=======
|
||||
Комплексная извлечение с подсказками JSONL.
|
||||
Извлечение с имитацией усечения (искусственно ограниченный ответ).
|
||||
Извлечение смешанных типов с использованием дискриминатора типа.
|
||||
Извлечение онтологии со всеми тремя типами.
|
||||
|
||||
### Тесты качества извлечения.
|
||||
>>>>>>> 82edf2d (New md files from RunPod)
|
||||
|
||||
Сравнение результатов извлечения: формат JSONL против массива JSON.
|
||||
Проверка устойчивости к усечению: JSONL возвращает частичные результаты, в то время как JSON - нет.
|
||||
|
||||
## План миграции
|
||||
|
||||
### Этап 1: Реализация
|
||||
|
||||
1. Реализовать метод `parse_jsonl()` в `PromptManager`.
|
||||
2. Расширить `invoke()` для обработки `response-type: "jsonl"`.
|
||||
3. Добавить модульные тесты.
|
||||
|
||||
### Этап 2: Миграция подсказок
|
||||
|
||||
1. Обновить подсказку `extract-definitions` и конфигурацию.
|
||||
2. Обновить подсказку `extract-relationships` и конфигурацию.
|
||||
3. Обновить подсказку `extract-topics` и конфигурацию.
|
||||
4. Обновить подсказку `extract-rows` и конфигурацию.
|
||||
5. Обновить подсказку `agent-kg-extract` и конфигурацию.
|
||||
6. Обновить подсказку `extract-with-ontologies` и конфигурацию.
|
||||
|
||||
### Этап 3: Обновления для последующих этапов
|
||||
|
||||
1. Обновить любой код, использующий результаты извлечения, чтобы он мог обрабатывать возвращаемый тип списка.
|
||||
2. Обновить код, который категоризует извлечения смешанных типов, используя поле `type`.
|
||||
3. Обновить тесты, которые проверяют формат выходных данных извлечения.
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
На данный момент нет.
|
||||
|
||||
## Ссылки
|
||||
|
||||
Текущая реализация: `trustgraph-flow/trustgraph/template/prompt_manager.py`
|
||||
Спецификация JSON Lines: https://jsonlines.org/
|
||||
Схема JSON `oneOf`: https://json-schema.org/understanding-json-schema/reference/combining.html#oneof
|
||||
Связанная спецификация: Streaming LLM Responses (`docs/tech-specs/streaming-llm-responses.md`)
|
||||
992
docs/tech-specs/ru/large-document-loading.ru.md
Normal file
992
docs/tech-specs/ru/large-document-loading.ru.md
Normal file
|
|
@ -0,0 +1,992 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация рассматривает проблемы масштабируемости и удобства использования при загрузке
|
||||
больших документов в TrustGraph. Текущая архитектура рассматривает загрузку документов
|
||||
как единую атомарную операцию, что приводит к перегрузке памяти на нескольких этапах
|
||||
процесса и не предоставляет пользователям обратной связи или возможностей восстановления.
|
||||
|
||||
Эта реализация нацелена на следующие сценарии использования:
|
||||
|
||||
1. **Обработка больших PDF-файлов**: Загрузка и обработка PDF-файлов объемом в сотни мегабайт
|
||||
без переполнения памяти
|
||||
2. **Возобновляемая загрузка**: Возможность продолжить прерванную загрузку с того места,
|
||||
где она была остановлена, а не перезапускать ее.
|
||||
3. **Информативность о ходе выполнения**: Предоставление пользователям информации в режиме реального времени
|
||||
о ходе загрузки и обработки.
|
||||
4. **Эффективная обработка памяти**: Обработка документов потоковым способом
|
||||
без хранения целых файлов в памяти.
|
||||
|
||||
## Цели
|
||||
|
||||
**Инкрементная загрузка**: Поддержка загрузки документов по частям через REST и WebSocket
|
||||
**Возобновляемые передачи**: Возможность восстановления после прерванных загрузок
|
||||
**Информативность о ходе выполнения**: Предоставление клиентам информации о ходе загрузки/обработки
|
||||
**Эффективность использования памяти**: Исключение полной буферизации документов на протяжении всего процесса
|
||||
**Обратная совместимость**: Существующие процессы загрузки небольших документов продолжают работать без изменений
|
||||
**Потоковая обработка**: Декодирование PDF и разбиение текста на фрагменты выполняются в потоковом режиме
|
||||
|
||||
## Описание
|
||||
|
||||
### Текущая архитектура
|
||||
|
||||
Процесс отправки документов проходит по следующему пути:
|
||||
|
||||
1. **Клиент** отправляет документ через REST (`POST /api/v1/librarian`) или WebSocket
|
||||
2. **API Gateway** получает полный запрос с содержимым документа в кодировке base64
|
||||
3. **LibrarianRequestor** преобразует запрос в сообщение Pulsar
|
||||
4. **Librarian Service** получает сообщение, декодирует документ в память
|
||||
5. **BlobStore** загружает документ в Garage/S3
|
||||
6. **Cassandra** хранит метаданные со ссылкой на объект
|
||||
7. Для обработки: документ извлекается из S3, декодируется, разбивается на фрагменты — все в памяти
|
||||
|
||||
Основные файлы:
|
||||
Точка входа REST/WebSocket: `trustgraph-flow/trustgraph/gateway/service.py`
|
||||
Основной модуль Librarian: `trustgraph-flow/trustgraph/librarian/librarian.py`
|
||||
Хранилище объектов: `trustgraph-flow/trustgraph/librarian/blob_store.py`
|
||||
Таблицы Cassandra: `trustgraph-flow/trustgraph/tables/library.py`
|
||||
Схема API: `trustgraph-base/trustgraph/schema/services/library.py`
|
||||
|
||||
### Текущие ограничения
|
||||
|
||||
Текущая конструкция имеет несколько взаимосвязанных проблем, связанных с памятью и удобством использования:
|
||||
|
||||
1. **Атомарная операция загрузки**: Весь документ должен быть передан в одном запросе.
|
||||
Загрузка больших документов требует длительных запросов без индикации хода выполнения
|
||||
и без механизма повторной отправки в случае сбоя соединения.
|
||||
|
||||
2. **Дизайн API**: И REST, и WebSocket API ожидают получения всего документа
|
||||
в одном сообщении. Схема (`LibrarianRequest`) имеет одно поле `content`
|
||||
для хранения всего документа в кодировке base64.
|
||||
|
||||
3. **Память Librarian**: Сервис Librarian декодирует весь документ
|
||||
в память перед загрузкой в S3. Для PDF-файла объемом 500 МБ это означает
|
||||
выделение 500 МБ + в оперативной памяти.
|
||||
|
||||
4. **Память декодера PDF**: При начале обработки декодер PDF-файла загружает
|
||||
весь PDF-файл в память для извлечения текста. Такие библиотеки, как PyPDF,
|
||||
обычно требуют полного доступа к документу.
|
||||
|
||||
5. **Память разбивающего на фрагменты**: Разбивающий на фрагменты получает весь извлеченный текст
|
||||
и хранит его в памяти при создании фрагментов.
|
||||
|
||||
**Пример влияния на память** (PDF-файл объемом 500 МБ):
|
||||
Gateway: ~700 МБ (дополнительный объем для кодирования base64)
|
||||
Librarian: ~500 МБ (декодированные байты)
|
||||
Декодер PDF: ~500 МБ + буферы извлечения
|
||||
Разбивающий на фрагменты: извлеченный текст (переменная, потенциально 100 МБ+)
|
||||
|
||||
Общий пиковый объем используемой памяти может превысить 2 ГБ для одного большого документа.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Принципы проектирования
|
||||
|
||||
1. **API Facade**: Все взаимодействия с клиентом осуществляются через API Librarian. Клиенты
|
||||
не имеют прямого доступа к хранилищу S3/Garage и не знают о его существовании.
|
||||
|
||||
2. **Многокомпонентная загрузка в S3**: Используется стандартная многокомпонентная загрузка в S3.
|
||||
Это широко поддерживается в системах, совместимых с S3 (AWS S3, MinIO, Garage,
|
||||
Ceph, DigitalOcean Spaces, Backblaze B2 и т. д.), что обеспечивает переносимость.
|
||||
|
||||
3. **Атомарное завершение**: Многокомпонентные загрузки в S3 по своей природе являются атомарными — загруженные
|
||||
фрагменты невидимы до вызова `CompleteMultipartUpload`. Не требуются временные
|
||||
файлы или операции переименования.
|
||||
|
||||
4. **Отслеживаемое состояние**: Сессии загрузки отслеживаются в Cassandra, что обеспечивает
|
||||
информацию о незавершенных загрузках и позволяет восстановить загрузку.
|
||||
|
||||
### Поток загрузки по частям
|
||||
|
||||
```
|
||||
Client Librarian API S3/Garage
|
||||
│ │ │
|
||||
│── begin-upload ───────────►│ │
|
||||
│ (metadata, size) │── CreateMultipartUpload ────►│
|
||||
│ │◄── s3_upload_id ─────────────│
|
||||
│◄── upload_id ──────────────│ (store session in │
|
||||
│ │ Cassandra) │
|
||||
│ │ │
|
||||
│── upload-chunk ───────────►│ │
|
||||
│ (upload_id, index, data) │── UploadPart ───────────────►│
|
||||
│ │◄── etag ─────────────────────│
|
||||
│◄── ack + progress ─────────│ (store etag in session) │
|
||||
│ ⋮ │ ⋮ │
|
||||
│ (repeat for all chunks) │ │
|
||||
│ │ │
|
||||
│── complete-upload ────────►│ │
|
||||
│ (upload_id) │── CompleteMultipartUpload ──►│
|
||||
│ │ (parts coalesced by S3) │
|
||||
│ │── store doc metadata ───────►│ Cassandra
|
||||
│◄── document_id ────────────│ (delete session) │
|
||||
```
|
||||
|
||||
Клиент никогда не взаимодействует с S3 напрямую. Библиотека преобразует
|
||||
наши API загрузки по частям в многокомпонентные операции S3 внутри себя.
|
||||
|
||||
### Операции API библиотеки
|
||||
|
||||
#### `begin-upload`
|
||||
|
||||
Инициализация сессии загрузки по частям.
|
||||
|
||||
Запрос:
|
||||
```json
|
||||
{
|
||||
"operation": "begin-upload",
|
||||
"document-metadata": {
|
||||
"id": "doc-123",
|
||||
"kind": "application/pdf",
|
||||
"title": "Large Document",
|
||||
"user": "user-id",
|
||||
"tags": ["tag1", "tag2"]
|
||||
},
|
||||
"total-size": 524288000,
|
||||
"chunk-size": 5242880
|
||||
}
|
||||
```
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"upload-id": "upload-abc-123",
|
||||
"chunk-size": 5242880,
|
||||
"total-chunks": 100
|
||||
}
|
||||
```
|
||||
|
||||
Библиотекарь:
|
||||
1. Генерирует уникальный `upload_id` и `object_id` (UUID для хранения объектов)
|
||||
2. Вызывает `CreateMultipartUpload` S3, получает `s3_upload_id`
|
||||
3. Создает запись сессии в Cassandra
|
||||
4. Возвращает `upload_id` клиенту
|
||||
|
||||
#### `upload-chunk`
|
||||
|
||||
Загрузка одного фрагмента.
|
||||
|
||||
Запрос:
|
||||
```json
|
||||
{
|
||||
"operation": "upload-chunk",
|
||||
"upload-id": "upload-abc-123",
|
||||
"chunk-index": 0,
|
||||
"content": "<base64-encoded-chunk>"
|
||||
}
|
||||
```
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"upload-id": "upload-abc-123",
|
||||
"chunk-index": 0,
|
||||
"chunks-received": 1,
|
||||
"total-chunks": 100,
|
||||
"bytes-received": 5242880,
|
||||
"total-bytes": 524288000
|
||||
}
|
||||
```
|
||||
|
||||
Библиотекарь:
|
||||
1. Ищет сессию по `upload_id`
|
||||
2. Проверяет право собственности (пользователь должен совпадать с создателем сессии)
|
||||
3. Вызывает S3 `UploadPart` с данными фрагмента, получает `etag`
|
||||
4. Обновляет запись сессии с индексом фрагмента и etag
|
||||
5. Возвращает прогресс клиенту
|
||||
|
||||
Неудачные фрагменты можно повторить - просто отправьте тот же `chunk-index` снова.
|
||||
|
||||
#### `complete-upload`
|
||||
|
||||
Завершите загрузку и создайте документ.
|
||||
|
||||
Запрос:
|
||||
```json
|
||||
{
|
||||
"operation": "complete-upload",
|
||||
"upload-id": "upload-abc-123"
|
||||
}
|
||||
```
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"document-id": "doc-123",
|
||||
"object-id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
Библиотекарь:
|
||||
1. Ищет сессию, проверяет, все ли фрагменты получены.
|
||||
2. Вызывает S3 `CompleteMultipartUpload` с информацией о частях (S3 объединяет части
|
||||
внутри себя - нулевая стоимость по памяти для библиотекаря).
|
||||
3. Создает запись документа в Cassandra с метаданными и ссылкой на объект.
|
||||
4. Удаляет запись сессии загрузки.
|
||||
5. Возвращает идентификатор документа клиенту.
|
||||
|
||||
#### `abort-upload`
|
||||
|
||||
Отмена загрузки, находящейся в процессе.
|
||||
|
||||
Запрос:
|
||||
```json
|
||||
{
|
||||
"operation": "abort-upload",
|
||||
"upload-id": "upload-abc-123"
|
||||
}
|
||||
```
|
||||
|
||||
Библиотекарь:
|
||||
1. Вызывает S3 `AbortMultipartUpload` для очистки разделов.
|
||||
2. Удаляет запись сессии из Cassandra.
|
||||
|
||||
#### `get-upload-status`
|
||||
|
||||
Проверка статуса загрузки (для возможности возобновления).
|
||||
|
||||
Запрос:
|
||||
```json
|
||||
{
|
||||
"operation": "get-upload-status",
|
||||
"upload-id": "upload-abc-123"
|
||||
}
|
||||
```
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"upload-id": "upload-abc-123",
|
||||
"state": "in-progress",
|
||||
"chunks-received": [0, 1, 2, 5, 6],
|
||||
"missing-chunks": [3, 4, 7, 8],
|
||||
"total-chunks": 100,
|
||||
"bytes-received": 36700160,
|
||||
"total-bytes": 524288000
|
||||
}
|
||||
```
|
||||
|
||||
#### `list-uploads`
|
||||
|
||||
Список неполных загрузок для пользователя.
|
||||
|
||||
Запрос:
|
||||
```json
|
||||
{
|
||||
"operation": "list-uploads"
|
||||
}
|
||||
```
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"uploads": [
|
||||
{
|
||||
"upload-id": "upload-abc-123",
|
||||
"document-metadata": { "title": "Large Document", ... },
|
||||
"progress": { "chunks-received": 43, "total-chunks": 100 },
|
||||
"created-at": "2024-01-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Загрузка данных сессии
|
||||
|
||||
Отслеживание текущих загрузок в Cassandra:
|
||||
|
||||
```sql
|
||||
CREATE TABLE upload_session (
|
||||
upload_id text PRIMARY KEY,
|
||||
user text,
|
||||
document_id text,
|
||||
document_metadata text, -- JSON: title, kind, tags, comments, etc.
|
||||
s3_upload_id text, -- internal, for S3 operations
|
||||
object_id uuid, -- target blob ID
|
||||
total_size bigint,
|
||||
chunk_size int,
|
||||
total_chunks int,
|
||||
chunks_received map<int, text>, -- chunk_index → etag
|
||||
created_at timestamp,
|
||||
updated_at timestamp
|
||||
) WITH default_time_to_live = 86400; -- 24 hour TTL
|
||||
|
||||
CREATE INDEX upload_session_user ON upload_session (user);
|
||||
```
|
||||
|
||||
**Поведение TTL:**
|
||||
Сессии истекают через 24 часа, если не завершены.
|
||||
Когда TTL Cassandra истекает, запись сессии удаляется.
|
||||
Оставшиеся фрагменты S3 очищаются политикой жизненного цикла S3 (настраивается на бакете).
|
||||
|
||||
### Обработка ошибок и атомарность
|
||||
|
||||
**Ошибка загрузки фрагмента:**
|
||||
Клиент повторяет загрузку неудачного фрагмента (с теми же `upload_id` и `chunk-index`).
|
||||
Операция `UploadPart` S3 является идемпотентной для одного и того же номера фрагмента.
|
||||
Сессия отслеживает, какие фрагменты были успешно загружены.
|
||||
|
||||
**Разрыв соединения клиента во время загрузки:**
|
||||
Сессия остается в Cassandra с записью о полученных фрагментах.
|
||||
Клиент может вызвать `get-upload-status`, чтобы узнать, что отсутствует.
|
||||
Возобновление путем загрузки только отсутствующих фрагментов, затем `complete-upload`.
|
||||
|
||||
**Ошибка полной загрузки:**
|
||||
Операция `CompleteMultipartUpload` S3 является атомарной - либо успешно завершается полностью, либо не удается.
|
||||
В случае сбоя фрагменты остаются, и клиент может повторить `complete-upload`.
|
||||
Ни один частичный документ никогда не отображается.
|
||||
|
||||
**Истечение срока действия сессии:**
|
||||
TTL Cassandra удаляет запись сессии через 24 часа.
|
||||
Политика жизненного цикла бакета S3 очищает неполные многокомпонентные загрузки.
|
||||
Не требуется ручная очистка.
|
||||
|
||||
### Атомарность многокомпонентной загрузки S3
|
||||
|
||||
Многокомпонентные загрузки S3 обеспечивают встроенную атомарность:
|
||||
|
||||
1. **Фрагменты невидимы**: Загруженные фрагменты не могут быть доступны в качестве объектов.
|
||||
Они существуют только как части незавершенной многокомпонентной загрузки.
|
||||
|
||||
2. **Атомарное завершение**: `CompleteMultipartUpload` либо успешно завершается (объект
|
||||
появляется атомарно), либо не удается (объект не создается). Отсутствует частичное состояние.
|
||||
|
||||
3. **Не требуется переименование**: Конечный ключ объекта указывается во время
|
||||
`CreateMultipartUpload`. Фрагменты объединяются непосредственно в этот ключ.
|
||||
|
||||
4. **Объединение на стороне сервера**: S3 объединяет фрагменты внутренне. Библиотекарь
|
||||
никогда не считывает фрагменты обратно - нулевые накладные расходы памяти независимо от размера документа.
|
||||
|
||||
### Расширения BlobStore
|
||||
|
||||
**Файл:** `trustgraph-flow/trustgraph/librarian/blob_store.py`
|
||||
|
||||
Добавлены методы многокомпонентной загрузки:
|
||||
|
||||
```python
|
||||
class BlobStore:
|
||||
# Existing methods...
|
||||
|
||||
def create_multipart_upload(self, object_id: UUID, kind: str) -> str:
|
||||
"""Initialize multipart upload, return s3_upload_id."""
|
||||
# minio client: create_multipart_upload()
|
||||
|
||||
def upload_part(
|
||||
self, object_id: UUID, s3_upload_id: str,
|
||||
part_number: int, data: bytes
|
||||
) -> str:
|
||||
"""Upload a single part, return etag."""
|
||||
# minio client: upload_part()
|
||||
# Note: S3 part numbers are 1-indexed
|
||||
|
||||
def complete_multipart_upload(
|
||||
self, object_id: UUID, s3_upload_id: str,
|
||||
parts: List[Tuple[int, str]] # [(part_number, etag), ...]
|
||||
) -> None:
|
||||
"""Finalize multipart upload."""
|
||||
# minio client: complete_multipart_upload()
|
||||
|
||||
def abort_multipart_upload(
|
||||
self, object_id: UUID, s3_upload_id: str
|
||||
) -> None:
|
||||
"""Cancel multipart upload, clean up parts."""
|
||||
# minio client: abort_multipart_upload()
|
||||
```
|
||||
|
||||
### Особенности размера фрагментов
|
||||
|
||||
**Минимальный размер для S3**: 5 МБ на часть (кроме последней части)
|
||||
**Максимальный размер для S3**: 10 000 частей на загрузку
|
||||
**Рекомендуемый размер по умолчанию**: фрагменты по 5 МБ
|
||||
Документ объемом 500 МБ = 100 фрагментов
|
||||
Документ объемом 5 ГБ = 1000 фрагментов
|
||||
**Гранулярность прогресса**: Меньшие фрагменты = более точные обновления прогресса
|
||||
**Эффективность сети**: Большие фрагменты = меньше сетевых запросов
|
||||
|
||||
Размер фрагмента может быть настроен клиентом в пределах заданных ограничений (от 5 МБ до 100 МБ).
|
||||
|
||||
### Обработка документов: Потоковая загрузка
|
||||
|
||||
Процесс загрузки предназначен для эффективной передачи документов в хранилище. Процесс обработки предназначен для извлечения и разделения документов на фрагменты без полной загрузки
|
||||
их в память.
|
||||
|
||||
|
||||
#### Основной принцип: Идентификатор, а не содержимое
|
||||
|
||||
В настоящее время, при запуске обработки, содержимое документа передается через сообщения Pulsar. Это приводит к полной загрузке документов в память. Вместо этого:
|
||||
|
||||
|
||||
Сообщения Pulsar содержат только **идентификатор документа**
|
||||
Обработчики получают содержимое документа непосредственно из хранилища.
|
||||
Получение происходит в виде **потока во временный файл**
|
||||
Разбор документов, специфичный для каждого формата (PDF, текст и т.д.), работает с файлами, а не с буферами памяти.
|
||||
|
||||
Это позволяет хранилищу быть независимым от структуры документа. Разбор PDF, извлечение текста и другая логика, специфичная для формата, остается в соответствующих декодерах.
|
||||
|
||||
|
||||
#### Процесс обработки
|
||||
|
||||
```
|
||||
Pulsar PDF Decoder Librarian S3
|
||||
│ │ │ │
|
||||
│── doc-id ───────────►│ │ │
|
||||
│ (processing msg) │ │ │
|
||||
│ │ │ │
|
||||
│ │── stream-document ──────►│ │
|
||||
│ │ (doc-id) │── GetObject ────►│
|
||||
│ │ │ │
|
||||
│ │◄── chunk ────────────────│◄── stream ───────│
|
||||
│ │ (write to temp file) │ │
|
||||
│ │◄── chunk ────────────────│◄── stream ───────│
|
||||
│ │ (append to temp file) │ │
|
||||
│ │ ⋮ │ ⋮ │
|
||||
│ │◄── EOF ──────────────────│ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────────────────┐ │
|
||||
│ │ │ temp file on disk │ │
|
||||
│ │ │ (memory stays bounded) │ │
|
||||
│ │ └────────────┬─────────────┘ │
|
||||
│ │ │ │
|
||||
│ │ PDF library opens file │
|
||||
│ │ extract page 1 text ──► chunker │
|
||||
│ │ extract page 2 text ──► chunker │
|
||||
│ │ ⋮ │
|
||||
│ │ close file │
|
||||
│ │ delete temp file │
|
||||
```
|
||||
|
||||
#### API потоковой передачи данных для библиотекаря
|
||||
|
||||
Добавить операцию получения документов в режиме потоковой передачи:
|
||||
|
||||
**`stream-document`**
|
||||
|
||||
Запрос:
|
||||
```json
|
||||
{
|
||||
"operation": "stream-document",
|
||||
"document-id": "doc-123"
|
||||
}
|
||||
```
|
||||
|
||||
Ответ: Потоковые двоичные фрагменты (не единый ответ).
|
||||
|
||||
Для REST API это возвращает потоковый ответ с `Transfer-Encoding: chunked`.
|
||||
|
||||
Для внутренних вызовов между сервисами (от процессора к библиотеке), это может быть:
|
||||
Прямая потоковая передача данных через S3 с использованием предварительно подписанного URL (если внутренняя сеть это позволяет).
|
||||
Фрагментированные ответы через протокол сервиса.
|
||||
Специальный конечный пункт для потоковой передачи данных.
|
||||
|
||||
Основное требование: данные передаются фрагментами, никогда полностью не буферизуются в библиотеке.
|
||||
|
||||
#### Изменения декодера PDF
|
||||
|
||||
**Текущая реализация** (требует много памяти):
|
||||
|
||||
```python
|
||||
def decode_pdf(document_content: bytes) -> str:
|
||||
reader = PdfReader(BytesIO(document_content)) # full doc in memory
|
||||
text = ""
|
||||
for page in reader.pages:
|
||||
text += page.extract_text() # accumulating
|
||||
return text # full text in memory
|
||||
```
|
||||
|
||||
**Новая реализация** (временный файл, постепенная):
|
||||
|
||||
```python
|
||||
def decode_pdf_streaming(doc_id: str, librarian_client) -> Iterator[str]:
|
||||
"""Yield extracted text page by page."""
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=True, suffix='.pdf') as tmp:
|
||||
# Stream document to temp file
|
||||
for chunk in librarian_client.stream_document(doc_id):
|
||||
tmp.write(chunk)
|
||||
tmp.flush()
|
||||
|
||||
# Open PDF from file (not memory)
|
||||
reader = PdfReader(tmp.name)
|
||||
|
||||
# Yield pages incrementally
|
||||
for page in reader.pages:
|
||||
yield page.extract_text()
|
||||
|
||||
# tmp file auto-deleted on context exit
|
||||
```
|
||||
|
||||
Профиль использования памяти:
|
||||
Временный файл на диске: размер PDF (диск дешевый).
|
||||
В памяти: текст одной страницы за раз.
|
||||
Максимальный объем памяти: ограничен, не зависит от размера документа.
|
||||
|
||||
#### Изменения в декодере текстовых документов
|
||||
|
||||
Для обычных текстовых документов еще проще - временный файл не нужен:
|
||||
|
||||
```python
|
||||
def decode_text_streaming(doc_id: str, librarian_client) -> Iterator[str]:
|
||||
"""Yield text in chunks as it streams from storage."""
|
||||
|
||||
buffer = ""
|
||||
for chunk in librarian_client.stream_document(doc_id):
|
||||
buffer += chunk.decode('utf-8')
|
||||
|
||||
# Yield complete lines/paragraphs as they arrive
|
||||
while '\n\n' in buffer:
|
||||
paragraph, buffer = buffer.split('\n\n', 1)
|
||||
yield paragraph + '\n\n'
|
||||
|
||||
# Yield remaining buffer
|
||||
if buffer:
|
||||
yield buffer
|
||||
```
|
||||
|
||||
Текстовые документы могут передаваться напрямую без временного файла, поскольку они имеют
|
||||
линейную структуру.
|
||||
|
||||
#### Интеграция с модулем разбиения на части (Chunker)
|
||||
|
||||
Модуль разбиения на части (chunker) получает итератор текстовых данных (страниц или абзацев) и
|
||||
создает фрагменты постепенно:
|
||||
|
||||
```python
|
||||
class StreamingChunker:
|
||||
def __init__(self, chunk_size: int, overlap: int):
|
||||
self.chunk_size = chunk_size
|
||||
self.overlap = overlap
|
||||
|
||||
def process(self, text_stream: Iterator[str]) -> Iterator[str]:
|
||||
"""Yield chunks as text arrives."""
|
||||
buffer = ""
|
||||
|
||||
for text_segment in text_stream:
|
||||
buffer += text_segment
|
||||
|
||||
while len(buffer) >= self.chunk_size:
|
||||
chunk = buffer[:self.chunk_size]
|
||||
yield chunk
|
||||
# Keep overlap for context continuity
|
||||
buffer = buffer[self.chunk_size - self.overlap:]
|
||||
|
||||
# Yield remaining buffer as final chunk
|
||||
if buffer.strip():
|
||||
yield buffer
|
||||
```
|
||||
|
||||
#### Конвейер сквозной обработки
|
||||
|
||||
```python
|
||||
async def process_document(doc_id: str, librarian_client, embedder):
|
||||
"""Process document with bounded memory."""
|
||||
|
||||
# Get document metadata to determine type
|
||||
metadata = await librarian_client.get_document_metadata(doc_id)
|
||||
|
||||
# Select decoder based on document type
|
||||
if metadata.kind == 'application/pdf':
|
||||
text_stream = decode_pdf_streaming(doc_id, librarian_client)
|
||||
elif metadata.kind == 'text/plain':
|
||||
text_stream = decode_text_streaming(doc_id, librarian_client)
|
||||
else:
|
||||
raise UnsupportedDocumentType(metadata.kind)
|
||||
|
||||
# Chunk incrementally
|
||||
chunker = StreamingChunker(chunk_size=1000, overlap=100)
|
||||
|
||||
# Process each chunk as it's produced
|
||||
for chunk in chunker.process(text_stream):
|
||||
# Generate embeddings, store in vector DB, etc.
|
||||
embedding = await embedder.embed(chunk)
|
||||
await store_chunk(doc_id, chunk, embedding)
|
||||
```
|
||||
|
||||
В любой момент времени полный документ или полностью извлеченный текст не хранятся в памяти.
|
||||
|
||||
#### Особенности использования временных файлов
|
||||
|
||||
**Расположение**: Используйте системную временную директорию (`/tmp` или эквивалент). Для
|
||||
контейнеризированных развертываний убедитесь, что временная директория имеет достаточно места
|
||||
и находится на быстром носителе (желательно не на сетевом диске).
|
||||
|
||||
**Очистка**: Используйте контекстные менеджеры (`with tempfile...`) для обеспечения очистки
|
||||
даже в случае возникновения исключений.
|
||||
|
||||
**Параллельная обработка**: Каждый процесс обработки получает свой временный файл.
|
||||
Отсутствуют конфликты между параллельной обработкой документов.
|
||||
|
||||
**Место на диске**: Временные файлы существуют недолго (продолжительность обработки). Для
|
||||
PDF-файла объемом 500 МБ требуется 500 МБ временного пространства во время обработки. Ограничение размера может
|
||||
быть применено во время загрузки, если место на диске ограничено.
|
||||
|
||||
### Унифицированный интерфейс обработки: Дочерние документы
|
||||
|
||||
Извлечение текста из PDF-файлов и обработка текстовых документов должны быть интегрированы в одну
|
||||
общую цепочку обработки (разделение на фрагменты → создание векторных представлений → хранение). Для достижения этого с
|
||||
использованием единого интерфейса "получение по ID", извлеченные текстовые фрагменты сохраняются обратно
|
||||
в систему как дочерние документы.
|
||||
|
||||
#### Поток обработки с использованием дочерних документов
|
||||
|
||||
```
|
||||
PDF Document Text Document
|
||||
│ │
|
||||
▼ │
|
||||
pdf-extractor │
|
||||
│ │
|
||||
│ (stream PDF from librarian) │
|
||||
│ (extract page 1 text) │
|
||||
│ (store as child doc → librarian) │
|
||||
│ (extract page 2 text) │
|
||||
│ (store as child doc → librarian) │
|
||||
│ ⋮ │
|
||||
▼ ▼
|
||||
[child-doc-id, child-doc-id, ...] [doc-id]
|
||||
│ │
|
||||
└─────────────────────┬───────────────────────────────┘
|
||||
▼
|
||||
chunker
|
||||
│
|
||||
│ (receives document ID)
|
||||
│ (streams content from librarian)
|
||||
│ (chunks incrementally)
|
||||
▼
|
||||
[chunks → embedding → storage]
|
||||
```
|
||||
|
||||
Модуль разбиения на фрагменты имеет один унифицированный интерфейс:
|
||||
Получение идентификатора документа (через Pulsar)
|
||||
Получение содержимого от хранилища данных
|
||||
Разбиение на фрагменты
|
||||
|
||||
Он не знает и не заботится о том, относится ли идентификатор к:
|
||||
Текстовому документу, загруженному пользователем
|
||||
Извлеченному текстовому фрагменту из страницы PDF
|
||||
Любому будущему типу документов
|
||||
|
||||
#### Метаданные дочернего документа
|
||||
|
||||
Расширьте схему документа для отслеживания отношений родитель/дочерний элемент:
|
||||
|
||||
```sql
|
||||
-- Add columns to document table
|
||||
ALTER TABLE document ADD parent_id text;
|
||||
ALTER TABLE document ADD document_type text;
|
||||
|
||||
-- Index for finding children of a parent
|
||||
CREATE INDEX document_parent ON document (parent_id);
|
||||
```
|
||||
|
||||
**Типы документов:**
|
||||
|
||||
| `document_type` | Описание |
|
||||
|-----------------|-------------|
|
||||
| `source` | Документ, загруженный пользователем (PDF, текст и т.д.) |
|
||||
| `extracted` | Полученный из исходного документа (например, текст страницы PDF) |
|
||||
|
||||
**Поля метаданных:**
|
||||
|
||||
| Поле | Исходный документ | Извлеченный дочерний элемент |
|
||||
|-------|-----------------|-----------------|
|
||||
| `id` | Предоставлено пользователем или сгенерировано | сгенерировано (например, `{parent-id}-page-{n}`) |
|
||||
| `parent_id` | `NULL` | Идентификатор родительского документа |
|
||||
| `document_type` | `source` | `extracted` |
|
||||
| `kind` | `application/pdf` и т.д. | `text/plain` |
|
||||
| `title` | Предоставлено пользователем | сгенерировано (например, "Страница 3 отчета Report.pdf") |
|
||||
| `user` | Аутентифицированный пользователь | то же, что и у родительского элемента |
|
||||
|
||||
#### API для дочерних документов
|
||||
|
||||
**Создание дочерних документов** (внутреннее, используется pdf-extractor):
|
||||
|
||||
```json
|
||||
{
|
||||
"operation": "add-child-document",
|
||||
"parent-id": "doc-123",
|
||||
"document-metadata": {
|
||||
"id": "doc-123-page-1",
|
||||
"kind": "text/plain",
|
||||
"title": "Page 1"
|
||||
},
|
||||
"content": "<base64-encoded-text>"
|
||||
}
|
||||
```
|
||||
|
||||
Для небольших извлеченных фрагментов текста (обычный текст страницы обычно менее 100 КБ) однокрасная загрузка является приемлемой. Для очень больших извлечений текста можно использовать загрузку по частям.
|
||||
|
||||
**Список дочерних документов** (для отладки/администрирования):
|
||||
|
||||
**Отображение дочерних документов** (для отладки/администрирования):
|
||||
|
||||
```json
|
||||
{
|
||||
"operation": "list-children",
|
||||
"parent-id": "doc-123"
|
||||
}
|
||||
```
|
||||
|
||||
Ответ:
|
||||
```json
|
||||
{
|
||||
"children": [
|
||||
{ "id": "doc-123-page-1", "title": "Page 1", "kind": "text/plain" },
|
||||
{ "id": "doc-123-page-2", "title": "Page 2", "kind": "text/plain" },
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Поведение, видимое пользователю
|
||||
|
||||
**`list-documents` поведение по умолчанию:**
|
||||
|
||||
```sql
|
||||
SELECT * FROM document WHERE user = ? AND parent_id IS NULL;
|
||||
```
|
||||
|
||||
Только документы верхнего уровня отображаются в списке документов пользователя.
|
||||
Дочерние документы по умолчанию отфильтровываются.
|
||||
|
||||
**Необязательный флаг `include-children`** (для администраторов/отладки):
|
||||
|
||||
```json
|
||||
{
|
||||
"operation": "list-documents",
|
||||
"include-children": true
|
||||
}
|
||||
```
|
||||
|
||||
#### Каскадное удаление
|
||||
|
||||
При удалении родительского документа все дочерние элементы должны быть удалены:
|
||||
|
||||
```python
|
||||
def delete_document(doc_id: str):
|
||||
# Find all children
|
||||
children = query("SELECT id, object_id FROM document WHERE parent_id = ?", doc_id)
|
||||
|
||||
# Delete child blobs from S3
|
||||
for child in children:
|
||||
blob_store.delete(child.object_id)
|
||||
|
||||
# Delete child metadata from Cassandra
|
||||
execute("DELETE FROM document WHERE parent_id = ?", doc_id)
|
||||
|
||||
# Delete parent blob and metadata
|
||||
parent = get_document(doc_id)
|
||||
blob_store.delete(parent.object_id)
|
||||
execute("DELETE FROM document WHERE id = ? AND user = ?", doc_id, user)
|
||||
```
|
||||
|
||||
#### Соображения по поводу хранения
|
||||
|
||||
Извлеченные текстовые фрагменты дублируют контент:
|
||||
Оригинальный PDF-файл хранится в хранилище "Garage".
|
||||
Извлеченный текст для каждой страницы также хранится в хранилище "Garage".
|
||||
|
||||
Этот компромисс позволяет:
|
||||
**Единый интерфейс для разбиения на фрагменты**: Разбиватель всегда получает данные по идентификатору.
|
||||
**Возобновление/повтор**: Можно перезапустить на этапе разбиения на фрагменты без повторной извлечения PDF-файла.
|
||||
**Отладка**: Извлеченный текст можно просмотреть.
|
||||
**Разделение ответственности**: Сервисы извлечения PDF и разбиения на фрагменты являются независимыми.
|
||||
|
||||
Для PDF-файла объемом 500 МБ с 200 страницами, в среднем 5 КБ текста на страницу:
|
||||
Хранение PDF: 500 МБ.
|
||||
Хранение извлеченного текста: около 1 МБ в общей сложности.
|
||||
Дополнительные затраты: незначительные.
|
||||
|
||||
#### Вывод извлечения текста из PDF-файла
|
||||
|
||||
Сервис извлечения текста из PDF-файла, после обработки документа:
|
||||
|
||||
1. Получает PDF-файл из хранилища "librarian" во временный файл.
|
||||
2. Извлекает текст страницу за страницей.
|
||||
3. Для каждой страницы сохраняет извлеченный текст как дочерний документ через хранилище "librarian".
|
||||
4. Отправляет идентификаторы дочерних документов в очередь разбивателя на фрагменты.
|
||||
Выходной документ.
|
||||
```python
|
||||
async def extract_pdf(doc_id: str, librarian_client, output_queue):
|
||||
"""Extract PDF pages and store as child documents."""
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=True, suffix='.pdf') as tmp:
|
||||
# Stream PDF to temp file
|
||||
for chunk in librarian_client.stream_document(doc_id):
|
||||
tmp.write(chunk)
|
||||
tmp.flush()
|
||||
|
||||
# Extract pages
|
||||
reader = PdfReader(tmp.name)
|
||||
for page_num, page in enumerate(reader.pages, start=1):
|
||||
text = page.extract_text()
|
||||
|
||||
# Store as child document
|
||||
child_id = f"{doc_id}-page-{page_num}"
|
||||
await librarian_client.add_child_document(
|
||||
parent_id=doc_id,
|
||||
document_id=child_id,
|
||||
kind="text/plain",
|
||||
title=f"Page {page_num}",
|
||||
content=text.encode('utf-8')
|
||||
)
|
||||
|
||||
# Send to chunker queue
|
||||
await output_queue.send(child_id)
|
||||
```
|
||||
|
||||
Модуль, отвечающий за разделение на части, получает эти идентификаторы дочерних элементов и обрабатывает их так же, как он бы обрабатывал текстовый документ, загруженный пользователем.
|
||||
|
||||
### Обновления для клиентов
|
||||
|
||||
#### Python SDK
|
||||
|
||||
|
||||
Python SDK (`trustgraph-base/trustgraph/api/library.py`) должен обрабатывать
|
||||
загрузку данных, разделенных на части, прозрачно. Публичный интерфейс остается неизменным:
|
||||
|
||||
```python
|
||||
# Existing interface - no change for users
|
||||
library.add_document(
|
||||
id="doc-123",
|
||||
title="Large Report",
|
||||
kind="application/pdf",
|
||||
content=large_pdf_bytes, # Can be hundreds of MB
|
||||
tags=["reports"]
|
||||
)
|
||||
```
|
||||
|
||||
Внутри, SDK определяет размер документа и переключает стратегию:
|
||||
|
||||
```python
|
||||
class Library:
|
||||
CHUNKED_UPLOAD_THRESHOLD = 2 * 1024 * 1024 # 2MB
|
||||
|
||||
def add_document(self, id, title, kind, content, tags=None, ...):
|
||||
if len(content) < self.CHUNKED_UPLOAD_THRESHOLD:
|
||||
# Small document: single operation (existing behavior)
|
||||
return self._add_document_single(id, title, kind, content, tags)
|
||||
else:
|
||||
# Large document: chunked upload
|
||||
return self._add_document_chunked(id, title, kind, content, tags)
|
||||
|
||||
def _add_document_chunked(self, id, title, kind, content, tags):
|
||||
# 1. begin-upload
|
||||
session = self._begin_upload(
|
||||
document_metadata={...},
|
||||
total_size=len(content),
|
||||
chunk_size=5 * 1024 * 1024
|
||||
)
|
||||
|
||||
# 2. upload-chunk for each chunk
|
||||
for i, chunk in enumerate(self._chunk_bytes(content, session.chunk_size)):
|
||||
self._upload_chunk(session.upload_id, i, chunk)
|
||||
|
||||
# 3. complete-upload
|
||||
return self._complete_upload(session.upload_id)
|
||||
```
|
||||
|
||||
**Функции обратного вызова для отслеживания прогресса** (необязательное улучшение):
|
||||
|
||||
```python
|
||||
def add_document(self, ..., on_progress=None):
|
||||
"""
|
||||
on_progress: Optional callback(bytes_sent, total_bytes)
|
||||
"""
|
||||
```
|
||||
|
||||
Это позволяет пользовательским интерфейсам отображать ход загрузки без изменения основного API.
|
||||
|
||||
#### Инструменты командной строки
|
||||
|
||||
**`tg-add-library-document`** продолжает работать без изменений:
|
||||
|
||||
```bash
|
||||
# Works transparently for any size - SDK handles chunking internally
|
||||
tg-add-library-document --file large-report.pdf --title "Large Report"
|
||||
```
|
||||
|
||||
Возможно, можно добавить опциональное отображение прогресса:
|
||||
|
||||
```bash
|
||||
tg-add-library-document --file large-report.pdf --title "Large Report" --progress
|
||||
# Output:
|
||||
# Uploading: 45% (225MB / 500MB)
|
||||
```
|
||||
|
||||
**Устаревшие инструменты удалены:**
|
||||
|
||||
`tg-load-pdf` - устарело, используйте `tg-add-library-document`
|
||||
`tg-load-text` - устарело, используйте `tg-add-library-document`
|
||||
|
||||
**Команды администратора/отладки** (опционально, низкий приоритет):
|
||||
|
||||
```bash
|
||||
# List incomplete uploads (admin troubleshooting)
|
||||
tg-add-library-document --list-pending
|
||||
|
||||
# Resume specific upload (recovery scenario)
|
||||
tg-add-library-document --resume upload-abc-123 --file large-report.pdf
|
||||
```
|
||||
|
||||
Это могут быть флаги для существующей команды, а не отдельные инструменты.
|
||||
|
||||
#### Обновления спецификации API
|
||||
|
||||
Спецификация OpenAPI (`specs/api/paths/librarian.yaml`) требует обновлений для:
|
||||
|
||||
**Новые операции:**
|
||||
|
||||
`begin-upload` - Инициализация сессии загрузки по частям
|
||||
`upload-chunk` - Загрузка отдельного фрагмента
|
||||
`complete-upload` - Завершение загрузки
|
||||
`abort-upload` - Отмена загрузки
|
||||
`get-upload-status` - Запрос хода загрузки
|
||||
`list-uploads` - Список незавершенных загрузок для пользователя
|
||||
`stream-document` - Получение документа в режиме потоковой передачи
|
||||
`add-child-document` - Сохранение извлеченного текста (внутреннее использование)
|
||||
`list-children` - Список дочерних документов (для администраторов)
|
||||
|
||||
**Измененные операции:**
|
||||
|
||||
`list-documents` - Добавлен параметр `include-children`
|
||||
|
||||
**Новые схемы:**
|
||||
|
||||
`ChunkedUploadBeginRequest`
|
||||
`ChunkedUploadBeginResponse`
|
||||
`ChunkedUploadChunkRequest`
|
||||
`ChunkedUploadChunkResponse`
|
||||
`UploadSession`
|
||||
`UploadProgress`
|
||||
|
||||
**Обновления спецификации WebSocket** (`specs/websocket/`):
|
||||
|
||||
Отразите операции REST для клиентов WebSocket, обеспечивая обновления хода загрузки в режиме реального времени.
|
||||
|
||||
|
||||
#### Соображения пользовательского интерфейса
|
||||
|
||||
Обновления спецификации API позволяют улучшить пользовательский интерфейс:
|
||||
|
||||
**Интерфейс отображения хода загрузки:**
|
||||
Индикатор прогресса, показывающий загруженные фрагменты
|
||||
Оценка оставшегося времени
|
||||
Возможность приостановки/возобновления
|
||||
|
||||
**Восстановление после ошибок:**
|
||||
Опция "Возобновить загрузку" для прерванных загрузок
|
||||
Список ожидающих загрузок при повторном подключении
|
||||
|
||||
**Обработка больших файлов:**
|
||||
Обнаружение размера файла на стороне клиента
|
||||
Автоматическая загрузка по частям для больших файлов
|
||||
Четкая обратная связь во время длительных загрузок
|
||||
|
||||
Эти улучшения пользовательского интерфейса требуют работы на стороне клиентской части, основанной на обновленной спецификации API.
|
||||
213
docs/tech-specs/ru/logging-strategy.ru.md
Normal file
213
docs/tech-specs/ru/logging-strategy.ru.md
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Стратегия логирования TrustGraph"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Стратегия логирования TrustGraph
|
||||
|
||||
> **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 использует встроенный модуль `logging` Python для всех операций логирования, с централизованной конфигурацией и опциональной интеграцией с Loki для агрегации логов. Это обеспечивает стандартизированный и гибкий подход к логированию для всех компонентов системы.
|
||||
|
||||
## Параметры конфигурации по умолчанию
|
||||
|
||||
### Уровень логирования
|
||||
- **Уровень по умолчанию**: `INFO`
|
||||
- **Указывается через**: аргумент командной строки `--log-level`
|
||||
- **Возможные значения**: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
|
||||
|
||||
### Направления вывода
|
||||
1. **Консоль (stdout)**: Всегда включено - обеспечивает совместимость с контейнерными средами
|
||||
2. **Loki**: Опциональная централизованная агрегация логов (включена по умолчанию, может быть отключена)
|
||||
|
||||
## Модуль централизованного логирования
|
||||
|
||||
Вся конфигурация логирования управляется модулем `trustgraph.base.logging`, который предоставляет:
|
||||
- `add_logging_args(parser)` - Добавляет стандартные аргументы командной строки для логирования
|
||||
- `setup_logging(args)` - Настраивает логирование на основе предоставленных аргументов
|
||||
|
||||
Этот модуль используется всеми серверными компонентами:
|
||||
- Сервисы, основанные на AsyncProcessor
|
||||
- API Gateway
|
||||
- Сервер MCP
|
||||
|
||||
## Рекомендации по реализации
|
||||
|
||||
### 1. Инициализация логгера
|
||||
|
||||
Каждый модуль должен создавать свой собственный логгер, используя `__name__` модуля:
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
```
|
||||
|
||||
Имя логгера автоматически используется как метка в Loki для фильтрации и поиска.
|
||||
|
||||
### 2. Инициализация сервиса
|
||||
|
||||
Все серверные сервисы автоматически получают конфигурацию логирования через централизованный модуль:
|
||||
|
||||
```python
|
||||
from trustgraph.base import add_logging_args, setup_logging
|
||||
import argparse
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
# Добавляем стандартные аргументы логирования (включая конфигурацию Loki)
|
||||
add_logging_args(parser)
|
||||
|
||||
# Добавляем свои аргументы для сервиса
|
||||
parser.add_argument('--port', type=int, default=8080)
|
||||
|
||||
args = parser.parse_args()
|
||||
args = vars(args)
|
||||
|
||||
# Настраиваем логирование в начале запуска
|
||||
setup_logging(args)
|
||||
|
||||
# Остальная часть инициализации сервиса
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info("Сервис запущен...")
|
||||
```
|
||||
|
||||
### 3. Аргументы командной строки
|
||||
|
||||
Все сервисы поддерживают следующие аргументы логирования:
|
||||
|
||||
**Уровень логирования:**
|
||||
```bash
|
||||
--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
|
||||
```
|
||||
|
||||
**Конфигурация Loki:**
|
||||
```bash
|
||||
--loki-enabled # Включить Loki (по умолчанию)
|
||||
--no-loki-enabled # Отключить Loki
|
||||
--loki-url URL # URL для отправки логов в Loki (по умолчанию: http://loki:3100/loki/api/v1/push)
|
||||
--loki-username USERNAME # Необязательное имя пользователя для аутентификации
|
||||
--loki-password PASSWORD # Необязательный пароль для аутентификации
|
||||
```
|
||||
|
||||
**Примеры:**
|
||||
```bash
|
||||
# По умолчанию - уровень INFO, Loki включен
|
||||
./my-service
|
||||
|
||||
# Режим отладки, только консоль
|
||||
./my-service --log-level DEBUG --no-loki-enabled
|
||||
|
||||
# Пользовательский сервер Loki с аутентификацией
|
||||
./my-service --loki-url http://loki.prod:3100/loki/api/v1/push \
|
||||
--loki-username admin --loki-password secret
|
||||
```
|
||||
|
||||
### 4. Переменные окружения
|
||||
|
||||
Конфигурация Loki поддерживает использование переменных окружения:
|
||||
|
||||
```
|
||||
export LOKI_URL=http://loki.prod:3100/loki/api/v1/push
|
||||
export LOKI_USERNAME=admin
|
||||
export LOKI_PASSWORD=secret
|
||||
```
|
||||
|
||||
Аргументы командной строки имеют приоритет над переменными окружения.
|
||||
|
||||
### 5. Рекомендации по логированию
|
||||
|
||||
#### Использование уровней логирования
|
||||
- **DEBUG**: Детальная информация для диагностики проблем (значения переменных, вход/выход функций)
|
||||
- **INFO**: Общие информационные сообщения (запуск сервиса, загрузка конфигурации, этапы обработки)
|
||||
- **WARNING**: Предупреждающие сообщения о потенциально опасных ситуациях (устаревшие функции, восстанавливаемые ошибки)
|
||||
- **ERROR**: Сообщения об ошибках о серьезных проблемах (неудачные операции, исключения)
|
||||
- **CRITICAL**: Критические сообщения о системных сбоях, требующих немедленного внимания
|
||||
|
||||
#### Формат сообщений
|
||||
```python
|
||||
# Хорошо - включает контекст
|
||||
logger.info(f"Обработка документа: {doc_id}, размер: {doc_size} байт")
|
||||
logger.error(f"Не удалось подключиться к базе данных: {error}", exc_info=True)
|
||||
|
||||
# Плохо - не включает контекст
|
||||
logger.info("Обработка документа")
|
||||
logger.error("Не удалось подключиться")
|
||||
```
|
||||
|
||||
#### Соображения производительности
|
||||
```python
|
||||
# Используйте ленивую адресацию для дорогостоящих операций
|
||||
logger.debug("Результат дорогостоящей операции: %s", expensive_function())
|
||||
|
||||
# Проверяйте уровень логирования для очень дорогих операций DEBUG
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
debug_data = compute_expensive_debug_info()
|
||||
logger.debug(f"Данные для отладки: {debug_data}")
|
||||
```
|
||||
|
||||
### 6. Структурированное логирование с использованием Loki
|
||||
|
||||
Для сложных данных используйте структурированное логирование с дополнительными метками для Loki:
|
||||
|
||||
```python
|
||||
logger.info("Запрос обработан", extra={
|
||||
'tags': {
|
||||
'request_id': request_id,
|
||||
'user_id': user_id,
|
||||
'status': 'успешно'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Эти метки становятся поисковыми метками в Loki, а также автоматически создаваемыми:
|
||||
- `severity` - Уровень логирования (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||
- `logger` - Имя модуля (из `__name__`)
|
||||
|
||||
### Зависимости
|
||||
|
||||
Модуль централизованного логирования требует:
|
||||
- `python-logging-loki` - Для интеграции с Loki (необязательно, функционирует без него)
|
||||
|
||||
Уже включен в `trustgraph-base/pyproject.toml` и `requirements.txt`.
|
||||
|
||||
## Переход
|
||||
|
||||
Для существующего кода:
|
||||
|
||||
1. **Сервисы, уже использующие AsyncProcessor**: Не требуется никаких изменений, интеграция с Loki автоматическая
|
||||
2. **Сервисы, которые не используют AsyncProcessor** (api-gateway, mcp-server): Уже обновлены
|
||||
3. **Инструменты командной строки**: Не относится - продолжайте использовать `print()` или простое логирование
|
||||
|
||||
### Переход от `print()` к `logging`:
|
||||
```python
|
||||
# Предыдущий код
|
||||
print(f"Обработка документа {doc_id}")
|
||||
|
||||
# Новый код
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(f"Обработка документа {doc_id}")
|
||||
```
|
||||
|
||||
## Безопасность
|
||||
|
||||
- **Никогда не логируйте конфиденциальную информацию** (пароли, API-ключи, персональные данные, токены)
|
||||
- **Фильтруйте пользовательский ввод** перед логированием
|
||||
- **Используйте плейсхолдеры** для конфиденциальных полей: `user_id=****1234`
|
||||
- **Используйте аутентификацию** для Loki с помощью `--loki-username` и `--loki-password` в производственной среде
|
||||
- **Безопасный транспорт**: Используйте HTTPS для URL Loki в производственной среде: `https://loki.prod:3100/loki/api/v1/push`
|
||||
|
||||
## Обзор конфигурации
|
||||
|
||||
| Аргумент | Значение по умолчанию | Переменная окружения | Описание |
|
||||
|---|---|---|---|
|
||||
| `--log-level` | `INFO` | - | Уровень логирования в консоли и Loki |
|
||||
| `--loki-enabled` | `True` | - | Включить логирование Loki |
|
||||
| `--loki-url` | `http://loki:3100/loki/api/v1/push` | `LOKI_URL` | URL для отправки логов в Loki |
|
||||
| `--loki-username` | `None` | `LOKI_USERNAME` | Имя пользователя для аутентификации Loki |
|
||||
| `--loki-password` | `None` | `LOKI_PASSWORD` | Пароль для аутентификации Loki |
|
||||
128
docs/tech-specs/ru/mcp-tool-arguments.ru.md
Normal file
128
docs/tech-specs/ru/mcp-tool-arguments.ru.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Спецификация аргументов для инструмента MCP"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Спецификация аргументов для инструмента MCP
|
||||
|
||||
> **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.
|
||||
|
||||
## Обзор
|
||||
**Имя функции**: Поддержка аргументов для инструмента MCP
|
||||
**Автор**: Claude Code Assistant
|
||||
**Дата**: 21.08.2025
|
||||
**Статус**: Завершено
|
||||
|
||||
### Краткое описание
|
||||
|
||||
Позволить агентам ReACT использовать инструменты MCP (Протокол контекста модели) с правильно определенными аргументами, добавив поддержку спецификации аргументов в конфигурацию инструмента MCP, аналогично тому, как работают инструменты с шаблонами запросов.
|
||||
|
||||
### Проблема
|
||||
|
||||
В настоящее время инструменты MCP в фреймворке агента ReACT не могут указывать ожидаемые аргументы. Метод `McpToolImpl.get_arguments()` возвращает пустой список, заставляя LLM угадывать правильную структуру параметров, основываясь только на именах и описаниях инструментов. Это приводит к:
|
||||
- Ненадежным вызовам инструментов из-за угадывания параметров
|
||||
- Плохой пользовательской опыту, когда инструменты не работают из-за неправильных аргументов
|
||||
- Отсутствию проверки параметров инструментов перед выполнением
|
||||
- Отсутствия документации параметров в запросах агента
|
||||
|
||||
### Цели
|
||||
|
||||
- [ ] Разрешить конфигурации инструментов MCP указывать ожидаемые аргументы (имя, тип, описание)
|
||||
- [ ] Обновить менеджер агентов, чтобы он предоставлял аргументы инструментов MCP LLM через запросы
|
||||
- [ ] Сохранить обратную совместимость с существующими конфигурациями инструментов MCP
|
||||
- [ ] Поддерживать проверку аргументов, аналогичную инструментам с шаблонами запросов
|
||||
|
||||
### Нецелевые области
|
||||
- Динамическое обнаружение аргументов от серверов MCP (будущее улучшение)
|
||||
- Проверка типов аргументов за пределами базовой структуры
|
||||
- Сложные схемы аргументов (вложенные объекты, массивы)
|
||||
|
||||
## Предыстория и контекст
|
||||
|
||||
### Текущее состояние
|
||||
Инструменты MCP настраиваются в системе агента ReACT с минимальными метаданными:
|
||||
```json
|
||||
{
|
||||
"type": "mcp-tool",
|
||||
"name": "get_bank_balance",
|
||||
"description": "Получить баланс банковского счета",
|
||||
"mcp-tool": "get_bank_balance"
|
||||
}
|
||||
```
|
||||
Метод `McpToolImpl.get_arguments()` возвращает `[]`, поэтому LLM не получают никаких указаний по аргументам в своих запросах.
|
||||
|
||||
### Ограничения
|
||||
|
||||
1. **Отсутствие спецификации аргументов**: Инструменты MCP не могут определять ожидаемые параметры
|
||||
2. **Угадывание параметров LLM**: Агенты должны выводить параметры из имен/описаний инструментов
|
||||
3. **Отсутствие информации в запросах**: Запросы агента не содержат деталей аргументов для инструментов MCP
|
||||
4. **Отсутствие проверки**: Неправильные параметры обнаруживаются только во время выполнения инструмента MCP
|
||||
|
||||
### Связанные компоненты
|
||||
- **trustgraph-flow/agent/react/service.py**: Загрузка конфигурации инструментов и создание AgentManager
|
||||
- **trustgraph-flow/agent/react/tools.py**: Реализация McpToolImpl
|
||||
- **trustgraph-flow/agent/react/agent_manager.py**: Генерация запросов с аргументами инструментов
|
||||
- **tg-invoke-mcp-tool**: Клиентские инструменты для управления инструментами MCP
|
||||
- **Workbench**: Внешний UI для конфигурации инструментов агента
|
||||
|
||||
## Требования
|
||||
|
||||
### Функциональные требования
|
||||
|
||||
1. **Конфигурация аргументов для инструмента MCP**: Конфигурации инструментов MCP ДОЛЖНЫ поддерживать необязательный массив `arguments` с полями `name`, `type` и `description`
|
||||
2. **Предоставление аргументов**: `McpToolImpl.get_arguments()` ДОЛЖЕН возвращать сконфигурированные аргументы, а не пустой список
|
||||
3. **Интеграция с запросами**: Запросы агента ДОЛЖНЫ содержать детали аргументов инструментов MCP, когда аргументы указаны
|
||||
4. **Обратная совместимость**: Существующие конфигурации инструментов MCP без аргументов ДОЛЖНЫ продолжать работать
|
||||
5. **Поддержка CLI**: Существующие CLI инструменты tg-invoke-mcp-tool поддерживают аргументы (уже реализовано)
|
||||
|
||||
### Нефункциональные требования
|
||||
1. **Обратная совместимость**: Без каких-либо изменений для существующих конфигураций инструментов MCP
|
||||
2. **Производительность**: Отсутствие существенного влияния на генерацию запросов агента
|
||||
3. **Согласованность**: Обработка аргументов ДОЛЖНА соответствовать шаблонам инструментов с шаблонами запросов
|
||||
|
||||
### Истории пользователей
|
||||
|
||||
1. Как **разработчик агента**, я хочу указывать аргументы инструментов MCP в конфигурации, чтобы LLM могли вызывать инструменты с правильными параметрами
|
||||
2. Как **пользователь Workbench**, я хочу настраивать аргументы инструментов MCP в UI, чтобы агенты использовали инструменты правильно
|
||||
3. Как **LLM в ReACT агенте**, я хочу видеть спецификации аргументов инструментов в запросах, чтобы я мог предоставлять правильные параметры
|
||||
|
||||
## Проектирование
|
||||
|
||||
### Высокоуровневая архитектура
|
||||
Расширить конфигурацию инструмента MCP, чтобы соответствовать шаблону инструмента с шаблоном запросов, путем:
|
||||
1. Добавления необязательного массива `arguments` в конфигурацию инструментов MCP
|
||||
2. Модификации `McpToolImpl` для приема и возврата сконфигурированных аргументов
|
||||
3. Обновления загрузки конфигурации инструментов для обработки аргументов инструментов MCP
|
||||
4. Обеспечения включения информации об аргументах инструментов MCP в запросы агента
|
||||
|
||||
### Схема конфигурации
|
||||
```json
|
||||
{
|
||||
"type": "mcp-tool",
|
||||
"name": "get_bank_balance",
|
||||
"description": "Get bank account balance",
|
||||
"mcp-tool": "get_bank_balance",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "account_id",
|
||||
"type": "string",
|
||||
"description": "Bank account identifier"
|
||||
},
|
||||
{
|
||||
"name": "date",
|
||||
"type": "string",
|
||||
"description": "Date for balance query (optional, format: YYYY-MM-DD)"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Поток данных
|
||||
1. **Загрузка конфигурации**: Конфигурация инструмента MCP с аргументами загружается с помощью `on_tools_config()`
|
||||
2. **Создание инструмента**: Аргументы парсятся и передаются в `McpToolImpl` через конструктор
|
||||
3. **Генерация запросов**: `agent_manager.py` вызывает `tool.arguments` для включения в LLM запросы
|
||||
4. **Вызов инструмента**: LLM предоставляет параметры, которые передаются инструменту MCP без изменений
|
||||
|
||||
### Изменения API
|
||||
Нет изменений API - это чисто внутренняя конфигурация и обработка аргументов
|
||||
562
docs/tech-specs/ru/mcp-tool-bearer-token.ru.md
Normal file
562
docs/tech-specs/ru/mcp-tool-bearer-token.ru.md
Normal file
|
|
@ -0,0 +1,562 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Спецификация аутентификации токенов Bearer для инструментов MCP"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Спецификация аутентификации токенов Bearer для инструментов MCP
|
||||
|
||||
> **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.
|
||||
|
||||
> **⚠️ ВАЖНО: ТОЛЬКО ДЛЯ ОДНОГО АРЕНДАТОРА**
|
||||
>
|
||||
> Эта спецификация описывает **базовый механизм аутентификации на уровне сервиса** для инструментов MCP. Это **НЕ** полное решение для аутентификации и **НЕ подходит** для:
|
||||
> - Многопользовательских сред
|
||||
> - Многоарендных развертываний
|
||||
> - Федеративной аутентификации
|
||||
> - Распространения контекста пользователя
|
||||
> - Авторизации на уровне пользователя
|
||||
>
|
||||
> Эта функция предоставляет **один статический токен для каждого инструмента MCP**, который используется всеми пользователями и сеансами. Если вам требуется аутентификация на уровне пользователя или арендатора, это не подходящее решение.
|
||||
|
||||
## Обзор
|
||||
**Название функции**: Поддержка аутентификации токенов Bearer для инструментов MCP
|
||||
**Автор**: Claude Code Assistant
|
||||
**Дата**: 2025-11-11
|
||||
**Статус**: В разработке
|
||||
|
||||
### Краткое описание
|
||||
|
||||
Позволяет конфигурациям инструментов MCP указывать необязательные токены Bearer для аутентификации с защищенными серверами MCP. Это позволяет TrustGraph безопасно вызывать инструменты MCP, размещенные на серверах, требующих аутентификации, без изменения интерфейсов агента или вызова инструментов.
|
||||
|
||||
**ВАЖНО**: Это базовый механизм аутентификации, предназначенный для сценариев аутентификации от сервиса к сервису в одноарендной среде. Он **НЕ подходит** для:
|
||||
Многопользовательских сред, где разные пользователи нуждаются в разных учетных данных
|
||||
Многоарендных развертываний, требующих изоляции на уровне арендатора
|
||||
Сценариев федеративной аутентификации
|
||||
Аутентификации или авторизации на уровне пользователя
|
||||
Динамического управления учетными данными или обновления токенов
|
||||
|
||||
Эта функция предоставляет статический, системный токен Bearer для каждой конфигурации инструмента MCP, который используется всеми пользователями и вызовами этого инструмента.
|
||||
|
||||
### Описание проблемы
|
||||
|
||||
В настоящее время инструменты MCP могут подключаться только к общедоступным серверам MCP. Многие производственные развертывания MCP требуют аутентификации с помощью токенов Bearer для обеспечения безопасности. Без поддержки аутентификации:
|
||||
Инструменты MCP не могут подключаться к защищенным серверам MCP
|
||||
Пользователям приходится либо предоставлять доступ к серверам MCP в общедоступном режиме, либо использовать обратные прокси
|
||||
Нет стандартизированного способа передачи учетных данных для подключений MCP
|
||||
Невозможно применять лучшие практики безопасности к конечным точкам MCP
|
||||
|
||||
### Цели
|
||||
|
||||
[ ] Разрешить конфигурациям инструментов MCP указывать необязательный параметр `auth-token`
|
||||
[ ] Обновить сервис инструмента MCP для использования токенов Bearer при подключении к серверам MCP
|
||||
[ ] Обновить инструменты командной строки для поддержки установки/отображения токенов аутентификации
|
||||
[ ] Сохранить обратную совместимость с конфигурациями MCP без аутентификации
|
||||
[ ] Задокументировать соображения безопасности для хранения токенов
|
||||
|
||||
### Не включено
|
||||
Динамическое обновление токенов или потоки OAuth (только статические токены)
|
||||
Шифрование хранимых токенов (безопасность системы конфигураций не входит в область)
|
||||
Альтернативные методы аутентификации (базовая аутентификация, ключи API и т. д.)
|
||||
Проверка срока действия или истечения срока действия токенов
|
||||
**Аутентификация на уровне пользователя**: Эта функция **НЕ поддерживает** учетные данные, специфичные для пользователя
|
||||
**Изоляция на уровне арендатора**: Эта функция **НЕ предоставляет** управление токенами на уровне арендатора
|
||||
**Федеративная аутентификация**: Эта функция **НЕ интегрируется** с поставщиками идентификации (SSO, OAuth, SAML и т. д.)
|
||||
**Аутентификация, зависящая от контекста**: Токены не передаются на основе контекста пользователя или сеанса
|
||||
|
||||
## Предыстория и контекст
|
||||
|
||||
### Текущее состояние
|
||||
Конфигурации инструментов MCP хранятся в группе конфигураций `mcp` со следующей структурой:
|
||||
```json
|
||||
{
|
||||
"remote-name": "tool_name",
|
||||
"url": "http://mcp-server:3000/api"
|
||||
}
|
||||
```
|
||||
|
||||
Сервис инструмента MCP подключается к серверам, используя `streamablehttp_client(url)`, без каких-либо заголовков аутентификации.
|
||||
|
||||
### Ограничения
|
||||
|
||||
**Текущие ограничения системы:**
|
||||
1. **Отсутствие поддержки аутентификации:** Невозможно подключиться к защищенным серверам MCP.
|
||||
2. **Риски безопасности:** Серверы MCP должны быть общедоступными или использовать только сетевую безопасность.
|
||||
3. **Проблемы при развертывании в производственной среде:** Невозможно соблюдать лучшие практики безопасности для API-интерфейсов.
|
||||
|
||||
**Ограничения данного решения:**
|
||||
1. **Только для однопользовательской системы:** Один статический токен для каждого инструмента MCP, общий для всех пользователей.
|
||||
2. **Отсутствие учетных данных для каждого пользователя:** Невозможно аутентифицироваться как разные пользователи или передавать контекст пользователя.
|
||||
3. **Отсутствие поддержки многопользовательской системы:** Невозможно изолировать учетные данные по арендатору или организации.
|
||||
4. **Только статические токены:** Нет поддержки обновления, ротации или обработки истечения срока действия токенов.
|
||||
5. **Аутентификация на уровне сервиса:** Аутентифицируется сервис TrustGraph, а не отдельные пользователи.
|
||||
6. **Общий контекст безопасности:** Все вызовы инструмента MCP используют одни и те же учетные данные.
|
||||
|
||||
### Область применения
|
||||
|
||||
**✅ Подходящие сценарии использования:**
|
||||
Развертывания TrustGraph для одного арендатора.
|
||||
Аутентификация от сервиса к сервису (TrustGraph → MCP Server).
|
||||
Среды разработки и тестирования.
|
||||
Внутренние инструменты MCP, доступные системе TrustGraph.
|
||||
Сценарии, в которых все пользователи имеют одинаковый уровень доступа к инструменту MCP.
|
||||
Статические, долгоживущие учетные данные сервиса.
|
||||
|
||||
**❌ Неподходящие сценарии использования:**
|
||||
Системы с несколькими пользователями, требующие аутентификацию для каждого пользователя.
|
||||
Многопользовательские SaaS-платформы с требованиями к изоляции арендаторов.
|
||||
Сценарии федеративной аутентификации (SSO, OAuth, SAML).
|
||||
Системы, требующие передачу контекста пользователя на серверы MCP.
|
||||
Среды, требующие динамическое обновление токенов или короткоживущие токены.
|
||||
Приложения, в которых разные пользователи должны иметь разные уровни разрешений.
|
||||
Требования соответствия для аудиторских журналов на уровне пользователя.
|
||||
|
||||
**Пример подходящего сценария:**
|
||||
Развертывание TrustGraph для одной организации, в котором все сотрудники используют один и тот же внутренний инструмент MCP (например, поиск информации в корпоративной базе данных). Сервер MCP требует аутентификации для предотвращения внешнего доступа, но все внутренние пользователи имеют одинаковый уровень доступа.
|
||||
|
||||
**Пример неподходящего сценария:**
|
||||
Многопользовательская SaaS-платформа TrustGraph, в которой арендатор A и арендатор B должны получать доступ к своим собственным изолированным серверам MCP с отдельными учетными данными. Эта функция НЕ поддерживает управление токенами на уровне арендатора.
|
||||
|
||||
### Связанные компоненты
|
||||
**trustgraph-flow/trustgraph/agent/mcp_tool/service.py**: Сервис вызова инструмента MCP.
|
||||
**trustgraph-cli/trustgraph/cli/set_mcp_tool.py**: Инструмент командной строки для создания/обновления конфигураций MCP.
|
||||
**trustgraph-cli/trustgraph/cli/show_mcp_tools.py**: Инструмент командной строки для отображения конфигураций MCP.
|
||||
**MCP Python SDK**: `streamablehttp_client` из `mcp.client.streamable_http`.
|
||||
|
||||
## Требования
|
||||
|
||||
### Функциональные требования
|
||||
|
||||
1. **Токен аутентификации для конфигурации MCP:** Конфигурации инструмента MCP ДОЛЖНЫ поддерживать необязательное поле `auth-token`.
|
||||
2. **Использование токена Bearer:** Сервис инструмента MCP ДОЛЖЕН отправлять заголовок `Authorization: Bearer {token}` при настройке аутентификации.
|
||||
3. **Поддержка CLI:** `tg-set-mcp-tool` ДОЛЖЕН принимать необязательный параметр `--auth-token`.
|
||||
4. **Отображение токена:** `tg-show-mcp-tools` ДОЛЖЕН указывать, когда настроена аутентификация (маскируется для безопасности).
|
||||
5. **Обратная совместимость:** Существующие конфигурации инструмента MCP без аутентификации ДОЛЖНЫ продолжать работать.
|
||||
|
||||
### Нефункциональные требования
|
||||
1. **Обратная совместимость:** Отсутствие изменений, нарушающих работу существующих конфигураций инструмента MCP.
|
||||
2. **Производительность:** Отсутствие значительного влияния на производительность вызова инструмента MCP.
|
||||
3. **Безопасность:** Токены хранятся в конфигурации (учитывайте последствия для безопасности).
|
||||
|
||||
### Пользовательские истории
|
||||
|
||||
1. Как **инженеру DevOps**, я хочу настроить токены Bearer для инструментов MCP, чтобы я мог защитить конечные точки серверов MCP.
|
||||
2. Как **пользователю CLI**, я хочу устанавливать токены аутентификации при создании инструментов MCP, чтобы я мог подключаться к защищенным серверам.
|
||||
3. Как **системному администратору**, я хочу видеть, какие инструменты MCP имеют настроенную аутентификацию, чтобы я мог проверять настройки безопасности.
|
||||
|
||||
## Проектирование
|
||||
|
||||
### Архитектура высокого уровня
|
||||
Расширение конфигурации и сервиса инструмента MCP для поддержки аутентификации с помощью токена Bearer:
|
||||
1. Добавление необязательного поля `auth-token` в схему конфигурации инструмента MCP.
|
||||
2. Изменение сервиса инструмента MCP для чтения токена аутентификации и передачи его в HTTP-клиент.
|
||||
3. Обновление инструментов командной строки для поддержки установки и отображения токенов аутентификации.
|
||||
4. Документирование соображений безопасности и лучших практик.
|
||||
|
||||
### Схема конфигурации
|
||||
|
||||
**Текущая схема:**
|
||||
```json
|
||||
{
|
||||
"remote-name": "tool_name",
|
||||
"url": "http://mcp-server:3000/api"
|
||||
}
|
||||
```
|
||||
|
||||
**Новая схема** (с необязательным токеном авторизации):
|
||||
```json
|
||||
{
|
||||
"remote-name": "tool_name",
|
||||
"url": "http://mcp-server:3000/api",
|
||||
"auth-token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
```
|
||||
|
||||
**Описание полей**:
|
||||
`remote-name` (необязательно): Имя, используемое сервером MCP (по умолчанию - ключ конфигурации).
|
||||
`url` (обязательно): URL-адрес конечной точки сервера MCP.
|
||||
`auth-token` (необязательно): Токен для аутентификации.
|
||||
|
||||
### Поток данных
|
||||
|
||||
1. **Хранение конфигурации**: Пользователь запускает `tg-set-mcp-tool --id my-tool --tool-url http://server/api --auth-token xyz123`.
|
||||
2. **Загрузка конфигурации**: Сервис инструмента MCP получает обновление конфигурации через обратный вызов `on_mcp_config()`.
|
||||
3. **Вызов инструмента**: Когда инструмент вызывается:
|
||||
Сервис считывает `auth-token` из конфигурации (если он присутствует).
|
||||
Создает словарь заголовков: `{"Authorization": "Bearer {token}"}`.
|
||||
Передает заголовки в `streamablehttp_client(url, headers=headers)`.
|
||||
Сервер MCP проверяет токен и обрабатывает запрос.
|
||||
|
||||
### Изменения API
|
||||
Отсутствуют внешние изменения API - только расширение схемы конфигурации.
|
||||
|
||||
### Детали компонента
|
||||
|
||||
#### Компонент 1: service.py (Сервис инструмента MCP)
|
||||
**Файл**: `trustgraph-flow/trustgraph/agent/mcp_tool/service.py`.
|
||||
|
||||
**Назначение**: Вызов инструментов MCP на удаленных серверах.
|
||||
|
||||
**Необходимые изменения** (в методе `invoke_tool()`):
|
||||
1. Проверить наличие `auth-token` в конфигурации `self.mcp_services[name]`.
|
||||
2. Создать словарь заголовков с заголовком Authorization, если токен существует.
|
||||
3. Передать заголовки в `streamablehttp_client(url, headers=headers)`.
|
||||
|
||||
**Текущий код** (строки 42-89):
|
||||
```python
|
||||
async def invoke_tool(self, name, parameters):
|
||||
try:
|
||||
if name not in self.mcp_services:
|
||||
raise RuntimeError(f"MCP service {name} not known")
|
||||
if "url" not in self.mcp_services[name]:
|
||||
raise RuntimeError(f"MCP service {name} URL not defined")
|
||||
|
||||
url = self.mcp_services[name]["url"]
|
||||
|
||||
if "remote-name" in self.mcp_services[name]:
|
||||
remote_name = self.mcp_services[name]["remote-name"]
|
||||
else:
|
||||
remote_name = name
|
||||
|
||||
logger.info(f"Invoking {remote_name} at {url}")
|
||||
|
||||
# Connect to a streamable HTTP server
|
||||
async with streamablehttp_client(url) as (
|
||||
read_stream,
|
||||
write_stream,
|
||||
_,
|
||||
):
|
||||
# ... rest of method
|
||||
```
|
||||
|
||||
**Измененный код**:
|
||||
```python
|
||||
async def invoke_tool(self, name, parameters):
|
||||
try:
|
||||
if name not in self.mcp_services:
|
||||
raise RuntimeError(f"MCP service {name} not known")
|
||||
if "url" not in self.mcp_services[name]:
|
||||
raise RuntimeError(f"MCP service {name} URL not defined")
|
||||
|
||||
url = self.mcp_services[name]["url"]
|
||||
|
||||
if "remote-name" in self.mcp_services[name]:
|
||||
remote_name = self.mcp_services[name]["remote-name"]
|
||||
else:
|
||||
remote_name = name
|
||||
|
||||
# Build headers with optional bearer token
|
||||
headers = {}
|
||||
if "auth-token" in self.mcp_services[name]:
|
||||
token = self.mcp_services[name]["auth-token"]
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
|
||||
logger.info(f"Invoking {remote_name} at {url}")
|
||||
|
||||
# Connect to a streamable HTTP server with headers
|
||||
async with streamablehttp_client(url, headers=headers) as (
|
||||
read_stream,
|
||||
write_stream,
|
||||
_,
|
||||
):
|
||||
# ... rest of method (unchanged)
|
||||
```
|
||||
|
||||
#### Компонент 2: set_mcp_tool.py (Инструмент конфигурации командной строки)
|
||||
**Файл**: `trustgraph-cli/trustgraph/cli/set_mcp_tool.py`
|
||||
|
||||
**Назначение**: Создание/обновление конфигураций инструмента MCP.
|
||||
|
||||
**Необходимые изменения**:
|
||||
1. Добавить необязательный аргумент `--auth-token` в argparse.
|
||||
2. Включить `auth-token` в конфигурационный JSON, если он предоставлен.
|
||||
|
||||
**Текущие аргументы**:
|
||||
`--id` (обязательный): Идентификатор инструмента MCP.
|
||||
`--remote-name` (необязательный): Удаленное имя инструмента MCP.
|
||||
`--tool-url` (обязательный): URL-адрес конечной точки инструмента MCP.
|
||||
`-u, --api-url` (необязательный): URL-адрес API TrustGraph.
|
||||
|
||||
**Новый аргумент**:
|
||||
`--auth-token` (необязательный): Токен Bearer для аутентификации.
|
||||
|
||||
**Измененное построение конфигурации**:
|
||||
```python
|
||||
# Build configuration object
|
||||
config = {
|
||||
"url": args.tool_url,
|
||||
}
|
||||
|
||||
if args.remote_name:
|
||||
config["remote-name"] = args.remote_name
|
||||
|
||||
if args.auth_token:
|
||||
config["auth-token"] = args.auth_token
|
||||
|
||||
# Store configuration
|
||||
api.config().put([
|
||||
ConfigValue(type="mcp", key=args.id, value=json.dumps(config))
|
||||
])
|
||||
```
|
||||
|
||||
#### Компонент 3: show_mcp_tools.py (Инструмент отображения командной строки)
|
||||
**Файл**: `trustgraph-cli/trustgraph/cli/show_mcp_tools.py`
|
||||
|
||||
**Назначение**: Отображение конфигураций инструмента MCP.
|
||||
|
||||
**Необходимые изменения**:
|
||||
1. Добавить столбец "Auth" в таблицу вывода.
|
||||
2. Отображать "Да" или "Нет" в зависимости от наличия auth-токена.
|
||||
3. Не отображать фактическое значение токена (безопасность).
|
||||
|
||||
**Текущий вывод**:
|
||||
```
|
||||
ID Remote Name URL
|
||||
---------- ------------- ------------------------
|
||||
my-tool my-tool http://server:3000/api
|
||||
```
|
||||
|
||||
**Новый вывод**:
|
||||
```
|
||||
ID Remote Name URL Auth
|
||||
---------- ------------- ------------------------ ------
|
||||
my-tool my-tool http://server:3000/api Yes
|
||||
other-tool other-tool http://other:3000/api No
|
||||
```
|
||||
|
||||
#### Компонент 4: Документация
|
||||
**Файл**: `docs/cli/tg-set-mcp-tool.md`
|
||||
|
||||
**Необходимые изменения**:
|
||||
1. Документировать новый параметр `--auth-token`
|
||||
2. Предоставить пример использования с аутентификацией
|
||||
3. Документировать соображения безопасности
|
||||
|
||||
## План реализации
|
||||
|
||||
### Фаза 1: Создание технической спецификации
|
||||
[x] Написать подробную техническую спецификацию, документирующую все изменения
|
||||
|
||||
### Фаза 2: Обновление сервиса MCP Tool
|
||||
[ ] Изменить `invoke_tool()` в `service.py` для чтения auth-token из конфигурации
|
||||
[ ] Создать словарь заголовков и передать его в `streamablehttp_client`
|
||||
[ ] Протестировать с аутентифицированным сервером MCP
|
||||
|
||||
### Фаза 3: Обновление инструментов командной строки
|
||||
[ ] Добавить аргумент `--auth-token` в `set_mcp_tool.py`
|
||||
[ ] Включить auth-token в конфигурационный JSON
|
||||
[ ] Добавить столбец "Auth" в вывод `show_mcp_tools.py`
|
||||
[ ] Протестировать изменения в инструментах командной строки
|
||||
|
||||
### Фаза 4: Обновление документации
|
||||
[ ] Документировать параметр `--auth-token` в `tg-set-mcp-tool.md`
|
||||
[ ] Добавить раздел "Соображения безопасности"
|
||||
[ ] Предоставить пример использования
|
||||
|
||||
### Фаза 5: Тестирование
|
||||
[ ] Проверить, что сервис MCP tool успешно подключается с использованием auth-token
|
||||
[ ] Проверить обратную совместимость (инструменты без auth-token по-прежнему работают)
|
||||
[ ] Проверить, что инструменты командной строки правильно принимают и сохраняют auth-token
|
||||
[ ] Проверить, что команда "show" правильно отображает статус аутентификации
|
||||
|
||||
### Краткое описание изменений в коде
|
||||
| Файл | Тип изменения | Строки | Описание |
|
||||
|------|------------|-------|-------------|
|
||||
| `service.py` | Изменено | ~52-66 | Добавлено чтение auth-token и построение заголовков |
|
||||
| `set_mcp_tool.py` | Изменено | ~30-60 | Добавлен аргумент --auth-token и хранение в конфигурации |
|
||||
| `show_mcp_tools.py` | Изменено | ~40-70 | Добавлен столбец Auth в отображение |
|
||||
| `tg-set-mcp-tool.md` | Изменено | Различные | Документирован новый параметр |
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
### Юнит-тесты
|
||||
**Чтение токена аутентификации**: Проверить, что `invoke_tool()` правильно считывает auth-token из конфигурации
|
||||
**Построение заголовков**: Проверить, что заголовок Authorization строится правильно с префиксом Bearer
|
||||
**Обратная совместимость**: Проверить, что инструменты без auth-token работают без изменений
|
||||
**Разбор аргументов командной строки**: Проверить, что аргумент `--auth-token` разбирается правильно
|
||||
|
||||
### Интеграционные тесты
|
||||
**Аутентифицированное подключение**: Проверить, что сервис MCP tool подключается к аутентифицированному серверу
|
||||
**Комплексное тестирование**: Проверить взаимодействие командной строки → хранилище конфигурации → вызов сервиса с auth token
|
||||
**Подключение без токена**: Проверить, что подключение к неаутентифицированному серверу по-прежнему работает
|
||||
|
||||
### Ручное тестирование
|
||||
**Реальный сервер MCP**: Проверить с фактическим сервером MCP, требующим аутентификацию с использованием токена bearer
|
||||
**Рабочий процесс командной строки**: Проверить полный рабочий процесс: настройка инструмента с auth → вызов инструмента → проверка успешного выполнения
|
||||
**Маскировка отображения**: Убедиться, что статус аутентификации отображается, но значение токена не отображается
|
||||
|
||||
## Миграция и развертывание
|
||||
|
||||
### Стратегия миграции
|
||||
Миграция не требуется - это чисто дополнительная функциональность:
|
||||
Существующие конфигурации MCP tool без `auth-token` продолжают работать без изменений
|
||||
Новые конфигурации могут опционально включать поле `auth-token`
|
||||
Инструменты командной строки принимают, но не требуют параметр `--auth-token`
|
||||
|
||||
### План развертывания
|
||||
1. **Фаза 1**: Развернуть основные изменения сервиса в среде разработки/тестирования
|
||||
2. **Фаза 2**: Развернуть обновления инструментов командной строки
|
||||
3. **Фаза 3**: Обновить документацию
|
||||
4. **Фаза 4**: Развертывание в производственной среде с мониторингом
|
||||
|
||||
### План отката
|
||||
Основные изменения обратно совместимы - существующие инструменты не затронуты
|
||||
В случае возникновения проблем обработку auth-token можно отключить, удалив логику построения заголовков
|
||||
Изменения в инструментах командной строки являются независимыми и могут быть отменены отдельно
|
||||
|
||||
## Соображения безопасности
|
||||
|
||||
### ⚠️ Критическое ограничение: Поддержка только однопользовательской аутентификации
|
||||
|
||||
**Этот механизм аутентификации НЕ подходит для многопользовательских или многопользовательских сред.**
|
||||
|
||||
**Общие учетные данные**: Все пользователи и вызовы используют один и тот же токен для каждого инструмента MCP
|
||||
**Отсутствие контекста пользователя**: Сервер MCP не может различать разных пользователей TrustGraph
|
||||
**Отсутствие изоляции арендаторов**: Все арендаторы используют одни и те же учетные данные для каждого инструмента MCP
|
||||
**Ограничение журнала аудита**: Сервер MCP отображает все запросы от одних и тех же учетных данных
|
||||
**Область разрешений**: Невозможно применять разные уровни разрешений для разных пользователей
|
||||
|
||||
**Не используйте эту функцию, если:**
|
||||
Ваша развертка TrustGraph обслуживает несколько организаций (многопользовательская среда)
|
||||
Вам необходимо отслеживать, какой пользователь получил доступ к какому инструменту MCP
|
||||
Разным пользователям требуются разные уровни разрешений
|
||||
Вам необходимо соблюдать требования аудита на уровне пользователей
|
||||
Ваш сервер MCP применяет ограничения скорости или квоты на уровне пользователя
|
||||
|
||||
**Альтернативные решения для сценариев с несколькими пользователями/многопользовательской средой:**
|
||||
Реализовать распространение контекста пользователя через пользовательские заголовки
|
||||
Развернуть отдельные экземпляры TrustGraph для каждого арендатора
|
||||
Использовать сетевую изоляцию (VPCs, service meshes)
|
||||
Реализовать прокси-слой, который обрабатывает аутентификацию для каждого пользователя
|
||||
|
||||
### Хранение токенов
|
||||
**Риск**: Токены аутентификации хранятся в открытом виде в системе конфигурации
|
||||
|
||||
**Меры по снижению риска**:
|
||||
Задокументировать, что токены хранятся без шифрования
|
||||
Рекомендовать использовать токены с коротким сроком действия, когда это возможно
|
||||
Рекомендовать надлежащий контроль доступа к хранилищу конфигурации
|
||||
Рассмотреть возможность будущей реализации для зашифрованного хранения токенов
|
||||
|
||||
### Раскрытие токенов
|
||||
**Риск**: Токены могут быть раскрыты в журналах или в выходных данных командной строки
|
||||
|
||||
**Меры по снижению риска**:
|
||||
Не записывать значения токенов в журналы (записывать только "аутентификация настроена: да/нет")
|
||||
Команда `show` в командной строке отображает только маскированный статус, а не фактический токен
|
||||
Не включать токены в сообщения об ошибках
|
||||
|
||||
### Сетевая безопасность
|
||||
**Риск**: Токены передаются по незашифрованным соединениям
|
||||
|
||||
**Меры по снижению риска**:
|
||||
Рекомендовать использовать HTTPS-URL-адреса для серверов MCP
|
||||
Предупреждать пользователей о риске передачи данных в открытом виде по протоколу HTTP
|
||||
|
||||
### Доступ к конфигурации
|
||||
**Риск**: Несанкционированный доступ к системе конфигурации раскрывает токены
|
||||
|
||||
**Меры по снижению риска**:
|
||||
Подчеркнуть важность защиты доступа к системе конфигурации
|
||||
Рекомендовать принцип наименьших привилегий для доступа к конфигурации
|
||||
Рассмотреть возможность ведения журналов аудита для изменений конфигурации (будущая реализация)
|
||||
|
||||
### Многопользовательские среды
|
||||
**Риск**: В многопользовательских развертываниях все пользователи используют одни и те же учетные данные MCP
|
||||
|
||||
**Понимание риска**:
|
||||
Пользователь A и пользователь B используют один и тот же токен при доступе к инструменту MCP
|
||||
Сервер MCP не может различать разных пользователей TrustGraph
|
||||
Нет возможности применить права доступа или ограничения скорости для каждого пользователя
|
||||
Журналы аудита на сервере MCP показывают все запросы от одних и тех же учетных данных
|
||||
Если сессия одного пользователя скомпрометирована, злоумышленник получает тот же доступ к MCP, что и все пользователи
|
||||
|
||||
**Это НЕ ошибка - это фундаментальное ограничение этой архитектуры.**
|
||||
|
||||
## Влияние на производительность
|
||||
**Минимальные накладные расходы**: Создание заголовка добавляет незначительное время обработки
|
||||
**Влияние на сеть**: Дополнительный HTTP-заголовок добавляет ~50-200 байт на запрос
|
||||
**Использование памяти**: Незначительное увеличение объема памяти для хранения строки токена в конфигурации
|
||||
|
||||
## Документация
|
||||
|
||||
### Пользовательская документация
|
||||
[ ] Обновить `tg-set-mcp-tool.md` с параметром `--auth-token`
|
||||
[ ] Добавить раздел о соображениях безопасности
|
||||
[ ] Предоставить пример использования с токеном типа bearer
|
||||
[ ] Задокументировать последствия хранения токенов
|
||||
|
||||
### Документация для разработчиков
|
||||
[ ] Добавить встроенные комментарии для обработки токенов аутентификации в `service.py`
|
||||
[ ] Задокументировать логику создания заголовков
|
||||
[ ] Обновить документацию схемы конфигурации инструмента MCP
|
||||
|
||||
## Открытые вопросы
|
||||
1. **Шифрование токенов**: Следует ли нам реализовать зашифрованное хранение токенов в системе конфигурации?
|
||||
2. **Обновление токенов**: Будущая поддержка потоков OAuth для обновления токенов или ротации токенов?
|
||||
3. **Альтернативные методы аутентификации**: Следует ли нам поддерживать Basic auth, API-ключи или другие методы?
|
||||
|
||||
## Рассмотренные альтернативы
|
||||
|
||||
1. **Переменные среды для токенов**: Хранить токены в переменных среды вместо конфигурации
|
||||
**Отклонено**: Усложняет развертывание и управление конфигурацией
|
||||
|
||||
2. **Отдельное хранилище секретов**: Использовать специализированную систему управления секретами
|
||||
**Отложено**: Выходит за рамки первоначальной реализации, рассмотреть возможность будущей реализации
|
||||
|
||||
3. **Несколько методов аутентификации**: Поддержка Basic, API-ключей, OAuth и т.д.
|
||||
**Отклонено**: Токены типа bearer охватывают большинство сценариев использования, сохраняем простоту первоначальной реализации
|
||||
|
||||
4. **Зашифрованное хранение токенов**: Шифровать токены в системе конфигурации
|
||||
**Отложено**: Безопасность системы конфигурации является более широкой проблемой, отложить до будущей работы
|
||||
|
||||
5. **Токены для каждого вызова**: Разрешить передачу токенов во время вызова
|
||||
**Отклонено**: Нарушает разделение ответственности, агент не должен обрабатывать учетные данные
|
||||
|
||||
## Ссылки
|
||||
[Спецификация протокола MCP](https://github.com/modelcontextprotocol/spec)
|
||||
[HTTP Bearer Authentication (RFC 6750)](https://tools.ietf.org/html/rfc6750)
|
||||
[Текущий сервис инструмента MCP](../trustgraph-flow/trustgraph/agent/mcp_tool/service.py)
|
||||
[Спецификация аргументов инструмента MCP](./mcp-tool-arguments.md)
|
||||
|
||||
## Приложение
|
||||
|
||||
### Пример использования
|
||||
|
||||
**Настройка инструмента MCP с аутентификацией**:
|
||||
```bash
|
||||
tg-set-mcp-tool \
|
||||
--id secure-tool \
|
||||
--tool-url https://secure-server.example.com/mcp \
|
||||
--auth-token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
```
|
||||
|
||||
**Отображение инструментов MCP:**
|
||||
```bash
|
||||
tg-show-mcp-tools
|
||||
|
||||
ID Remote Name URL Auth
|
||||
----------- ----------- ------------------------------------ ------
|
||||
secure-tool secure-tool https://secure-server.example.com/mcp Yes
|
||||
public-tool public-tool http://localhost:3000/mcp No
|
||||
```
|
||||
|
||||
### Пример конфигурации
|
||||
|
||||
**Хранится в системе конфигурации**:
|
||||
```json
|
||||
{
|
||||
"type": "mcp",
|
||||
"key": "secure-tool",
|
||||
"value": "{\"url\": \"https://secure-server.example.com/mcp\", \"auth-token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"}"
|
||||
}
|
||||
```
|
||||
|
||||
### Лучшие практики безопасности
|
||||
|
||||
1. **Использование HTTPS**: Всегда используйте HTTPS-адреса для серверов MCP с аутентификацией.
|
||||
2. **Кратковременные токены**: Используйте токены с истечением срока действия, когда это возможно.
|
||||
3. **Принцип наименьших привилегий**: Предоставляйте токенам минимально необходимые разрешения.
|
||||
4. **Контроль доступа**: Ограничьте доступ к системе конфигурации.
|
||||
5. **Ротация токенов**: Регулярно обновляйте токены.
|
||||
6. **Журналирование аудита**: Отслеживайте изменения конфигурации для выявления событий безопасности.
|
||||
266
docs/tech-specs/ru/minio-to-s3-migration.ru.md
Normal file
266
docs/tech-specs/ru/minio-to-s3-migration.ru.md
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Технические характеристики: Поддержка хранилища, совместимого с S3"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Технические характеристики: Поддержка хранилища, совместимого с S3
|
||||
|
||||
> **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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Сервис Librarian использует объектное хранилище, совместимое с S3, для хранения двоичных данных документов. Этот документ описывает реализацию, которая обеспечивает поддержку любого хранилища, совместимого с S3, включая MinIO, Ceph RADOS Gateway (RGW), AWS S3, Cloudflare R2, DigitalOcean Spaces и другие.
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Компоненты хранения
|
||||
**Хранилище двоичных данных**: Объектное хранилище, совместимое с S3, через `minio` Python клиентскую библиотеку.
|
||||
**Хранилище метаданных**: Cassandra (хранит сопоставление object_id и метаданные документов).
|
||||
**Затронутый компонент**: Только сервис Librarian.
|
||||
**Схема хранения**: Гибридное хранилище с метаданными в Cassandra и содержимым в хранилище, совместимом с S3.
|
||||
|
||||
### Реализация
|
||||
**Библиотека**: `minio` Python клиент (поддерживает любой API, совместимый с S3).
|
||||
**Расположение**: `trustgraph-flow/trustgraph/librarian/blob_store.py`
|
||||
**Операции**:
|
||||
`add()` - Сохранение двоичных данных с object_id в формате UUID.
|
||||
`get()` - Получение двоичных данных по object_id.
|
||||
`remove()` - Удаление двоичных данных по object_id.
|
||||
`ensure_bucket()` - Создание бакета, если он не существует.
|
||||
**Бакет**: `library`
|
||||
**Путь к объекту**: `doc/{object_id}`
|
||||
**Поддерживаемые типы MIME**: `text/plain`, `application/pdf`
|
||||
|
||||
### Основные файлы
|
||||
1. `trustgraph-flow/trustgraph/librarian/blob_store.py` - Реализация BlobStore.
|
||||
2. `trustgraph-flow/trustgraph/librarian/librarian.py` - Инициализация BlobStore.
|
||||
3. `trustgraph-flow/trustgraph/librarian/service.py` - Конфигурация сервиса.
|
||||
4. `trustgraph-flow/pyproject.toml` - Зависимости (пакет `minio`).
|
||||
5. `docs/apis/api-librarian.md` - Документация API.
|
||||
|
||||
## Поддерживаемые системы хранения
|
||||
|
||||
Реализация работает с любой системой объектного хранения, совместимой с S3:
|
||||
|
||||
### Протестировано/Поддерживается
|
||||
**Ceph RADOS Gateway (RGW)** - Распределенная система хранения с API S3 (конфигурация по умолчанию).
|
||||
**MinIO** - Легковесное объектное хранилище для самостоятельного размещения.
|
||||
**Garage** - Легковесное географически распределенное хранилище, совместимое с S3.
|
||||
|
||||
### Должно работать (совместимо с S3)
|
||||
**AWS S3** - Облачное объектное хранилище от Amazon.
|
||||
**Cloudflare R2** - Хранилище от Cloudflare, совместимое с S3.
|
||||
**DigitalOcean Spaces** - Объектное хранилище от DigitalOcean.
|
||||
**Wasabi** - Облачное хранилище, совместимое с S3.
|
||||
**Backblaze B2** - Хранилище резервных копий, совместимое с S3.
|
||||
Любая другая служба, реализующая REST API S3.
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### Аргументы командной строки
|
||||
|
||||
```bash
|
||||
librarian \
|
||||
--object-store-endpoint <hostname:port> \
|
||||
--object-store-access-key <access_key> \
|
||||
--object-store-secret-key <secret_key> \
|
||||
[--object-store-use-ssl] \
|
||||
[--object-store-region <region>]
|
||||
```
|
||||
|
||||
**Примечание:** Не включайте `http://` или `https://` в конечную точку. Используйте `--object-store-use-ssl` для включения HTTPS.
|
||||
|
||||
### Переменные окружения (Альтернативный способ)
|
||||
|
||||
```bash
|
||||
OBJECT_STORE_ENDPOINT=<hostname:port>
|
||||
OBJECT_STORE_ACCESS_KEY=<access_key>
|
||||
OBJECT_STORE_SECRET_KEY=<secret_key>
|
||||
OBJECT_STORE_USE_SSL=true|false # Optional, default: false
|
||||
OBJECT_STORE_REGION=<region> # Optional
|
||||
```
|
||||
|
||||
### Примеры
|
||||
|
||||
**Ceph RADOS Gateway (по умолчанию):**
|
||||
```bash
|
||||
--object-store-endpoint ceph-rgw:7480 \
|
||||
--object-store-access-key object-user \
|
||||
--object-store-secret-key object-password
|
||||
```
|
||||
|
||||
**MinIO:**
|
||||
```bash
|
||||
--object-store-endpoint minio:9000 \
|
||||
--object-store-access-key minioadmin \
|
||||
--object-store-secret-key minioadmin
|
||||
```
|
||||
|
||||
**Гараж (совместимый с S3):**
|
||||
```bash
|
||||
--object-store-endpoint garage:3900 \
|
||||
--object-store-access-key GK000000000000000000000001 \
|
||||
--object-store-secret-key b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427
|
||||
```
|
||||
|
||||
**AWS S3 с использованием SSL:**
|
||||
```bash
|
||||
--object-store-endpoint s3.amazonaws.com \
|
||||
--object-store-access-key AKIAIOSFODNN7EXAMPLE \
|
||||
--object-store-secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \
|
||||
--object-store-use-ssl \
|
||||
--object-store-region us-east-1
|
||||
```
|
||||
|
||||
## Аутентификация
|
||||
|
||||
Все бэкенды, совместимые с S3, требуют аутентификации AWS Signature Version 4 (или v2):
|
||||
|
||||
**Access Key** - Публичный идентификатор (например, имя пользователя)
|
||||
**Secret Key** - Приватный ключ для подписи (например, пароль)
|
||||
|
||||
Python-клиент MinIO автоматически обрабатывает все вычисления подписи.
|
||||
|
||||
### Создание учетных данных
|
||||
|
||||
**Для MinIO:**
|
||||
```bash
|
||||
# Use default credentials or create user via MinIO Console
|
||||
minioadmin / minioadmin
|
||||
```
|
||||
|
||||
**Для Ceph RGW:**
|
||||
```bash
|
||||
radosgw-admin user create --uid="trustgraph" --display-name="TrustGraph Service"
|
||||
# Returns access_key and secret_key
|
||||
```
|
||||
|
||||
**Для AWS S3:**
|
||||
Создайте пользователя IAM с разрешениями S3
|
||||
Сгенерируйте ключ доступа в консоли AWS
|
||||
|
||||
## Выбор библиотеки: MinIO Python Client
|
||||
|
||||
**Обоснование:**
|
||||
Легковесная (~500 КБ против ~50 МБ у boto3)
|
||||
Совместима с S3 - работает с любым конечным пунктом API S3
|
||||
Более простой API, чем boto3, для основных операций
|
||||
Уже используется, не требуется миграция
|
||||
Проверена в боевых условиях с MinIO и другими системами S3
|
||||
|
||||
## Реализация BlobStore
|
||||
|
||||
**Расположение:** `trustgraph-flow/trustgraph/librarian/blob_store.py`
|
||||
|
||||
```python
|
||||
from minio import Minio
|
||||
import io
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class BlobStore:
|
||||
"""
|
||||
S3-compatible blob storage for document content.
|
||||
Supports MinIO, Ceph RGW, AWS S3, and other S3-compatible backends.
|
||||
"""
|
||||
|
||||
def __init__(self, endpoint, access_key, secret_key, bucket_name,
|
||||
use_ssl=False, region=None):
|
||||
"""
|
||||
Initialize S3-compatible blob storage.
|
||||
|
||||
Args:
|
||||
endpoint: S3 endpoint (e.g., "minio:9000", "ceph-rgw:7480")
|
||||
access_key: S3 access key
|
||||
secret_key: S3 secret key
|
||||
bucket_name: Bucket name for storage
|
||||
use_ssl: Use HTTPS instead of HTTP (default: False)
|
||||
region: S3 region (optional, e.g., "us-east-1")
|
||||
"""
|
||||
self.client = Minio(
|
||||
endpoint=endpoint,
|
||||
access_key=access_key,
|
||||
secret_key=secret_key,
|
||||
secure=use_ssl,
|
||||
region=region,
|
||||
)
|
||||
|
||||
self.bucket_name = bucket_name
|
||||
|
||||
protocol = "https" if use_ssl else "http"
|
||||
logger.info(f"Connected to S3-compatible storage at {protocol}://{endpoint}")
|
||||
|
||||
self.ensure_bucket()
|
||||
|
||||
def ensure_bucket(self):
|
||||
"""Create bucket if it doesn't exist"""
|
||||
found = self.client.bucket_exists(bucket_name=self.bucket_name)
|
||||
if not found:
|
||||
self.client.make_bucket(bucket_name=self.bucket_name)
|
||||
logger.info(f"Created bucket {self.bucket_name}")
|
||||
else:
|
||||
logger.debug(f"Bucket {self.bucket_name} already exists")
|
||||
|
||||
async def add(self, object_id, blob, kind):
|
||||
"""Store blob in S3-compatible storage"""
|
||||
self.client.put_object(
|
||||
bucket_name=self.bucket_name,
|
||||
object_name=f"doc/{object_id}",
|
||||
length=len(blob),
|
||||
data=io.BytesIO(blob),
|
||||
content_type=kind,
|
||||
)
|
||||
logger.debug("Add blob complete")
|
||||
|
||||
async def remove(self, object_id):
|
||||
"""Delete blob from S3-compatible storage"""
|
||||
self.client.remove_object(
|
||||
bucket_name=self.bucket_name,
|
||||
object_name=f"doc/{object_id}",
|
||||
)
|
||||
logger.debug("Remove blob complete")
|
||||
|
||||
async def get(self, object_id):
|
||||
"""Retrieve blob from S3-compatible storage"""
|
||||
resp = self.client.get_object(
|
||||
bucket_name=self.bucket_name,
|
||||
object_name=f"doc/{object_id}",
|
||||
)
|
||||
return resp.read()
|
||||
```
|
||||
|
||||
## Ключевые преимущества
|
||||
|
||||
1. **Отсутствие привязки к конкретному поставщику** - Работает с любым хранилищем, совместимым с S3.
|
||||
2. **Легковесность** - Клиент MinIO занимает всего около 500 КБ.
|
||||
3. **Простая настройка** - Только конечная точка и учетные данные.
|
||||
4. **Отсутствие миграции данных** - Замена между бэкендами без перебоев.
|
||||
5. **Проверено в боевых условиях** - Клиент MinIO работает со всеми основными реализациями S3.
|
||||
|
||||
## Статус реализации
|
||||
|
||||
Весь код был обновлен для использования общих имен параметров S3:
|
||||
|
||||
✅ `blob_store.py` - Обновлено для приема `endpoint`, `access_key`, `secret_key`
|
||||
✅ `librarian.py` - Обновлены имена параметров
|
||||
✅ `service.py` - Обновлены аргументы командной строки и конфигурация
|
||||
✅ Обновлена документация
|
||||
|
||||
## Планируемые улучшения
|
||||
|
||||
1. **Поддержка SSL/TLS** - Добавить флаг `--s3-use-ssl` для HTTPS.
|
||||
2. **Логика повторных попыток** - Реализовать экспоненциальную задержку для временных сбоев.
|
||||
3. **Временные URL-адреса** - Генерировать временные URL-адреса для загрузки/скачивания.
|
||||
4. **Поддержка нескольких регионов** - Репликация объектов между регионами.
|
||||
5. **Интеграция с CDN** - Предоставление объектов через CDN.
|
||||
6. **Классы хранения** - Использование классов хранения S3 для оптимизации затрат.
|
||||
7. **Политики жизненного цикла** - Автоматическое архивирование/удаление.
|
||||
8. **Версионирование** - Хранение нескольких версий объектов.
|
||||
|
||||
## Ссылки
|
||||
|
||||
MinIO Python Client: https://min.io/docs/minio/linux/developers/python/API.html
|
||||
Ceph RGW S3 API: https://docs.ceph.com/en/latest/radosgw/s3/
|
||||
S3 API Reference: https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html
|
||||
210
docs/tech-specs/ru/more-config-cli.ru.md
Normal file
210
docs/tech-specs/ru/more-config-cli.ru.md
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Техническая спецификация CLI для конфигурации"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Техническая спецификация CLI для конфигурации
|
||||
|
||||
> **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, позволяя пользователям управлять отдельными элементами конфигурации с помощью команд CLI с гранулярным уровнем доступа. Интеграция поддерживает четыре основных сценария использования:
|
||||
|
||||
1. **Перечисление элементов конфигурации**: Отображение ключей конфигурации определенного типа
|
||||
2. **Получение элемента конфигурации**: Получение конкретных значений конфигурации
|
||||
3. **Установка элемента конфигурации**: Установка или обновление отдельных элементов конфигурации
|
||||
4. **Удаление элемента конфигурации**: Удаление конкретных элементов конфигурации
|
||||
|
||||
## Цели
|
||||
|
||||
- **Детализированный контроль**: Обеспечение управления отдельными элементами конфигурации, а не пакетными операциями
|
||||
- **Список по типу**: Предоставление пользователям возможности исследовать элементы конфигурации по типу
|
||||
- **Операции для отдельных элементов**: Предоставление команд для получения/установки/удаления отдельных элементов конфигурации
|
||||
- **Интеграция с API**: Использование существующего API для конфигурации для всех операций
|
||||
- **Единый шаблон CLI**: Соблюдение установленных норм и шаблонов CLI для TrustGraph
|
||||
- **Обработка ошибок**: Предоставление понятных сообщений об ошибках для некорректных операций
|
||||
- **Вывод в формате JSON**: Поддержка структурированного вывода для программного использования
|
||||
- **Документация**: Включение подробной справки и примеров использования
|
||||
|
||||
## Предыстория
|
||||
|
||||
TrustGraph в настоящее время предоставляет управление конфигурацией через API для конфигурации и один командный интерфейс `tg-show-config`, который отображает всю конфигурацию. Хотя это работает для просмотра конфигурации, оно не обеспечивает возможности детального управления.
|
||||
|
||||
Текущие ограничения включают:
|
||||
- Отсутствие возможности перечисления элементов конфигурации по типу через командную строку
|
||||
- Отсутствие командной строки для получения конкретных значений конфигурации
|
||||
- Отсутствие командной строки для установки отдельных элементов конфигурации
|
||||
- Отсутствие командной строки для удаления отдельных элементов конфигурации
|
||||
|
||||
Эта спецификация решает эти проблемы, добавляя четыре новые команды CLI, которые обеспечивают детальное управление конфигурацией. Предоставляя отдельные операции API через команды CLI, TrustGraph может:
|
||||
- Обеспечить управление конфигурацией через скрипты
|
||||
- Предоставить пользователям возможность исследовать структуру конфигурации по типу
|
||||
- Обеспечить целевые обновления конфигурации
|
||||
- Обеспечить детальный контроль над конфигурацией
|
||||
|
||||
## Техническое проектирование
|
||||
|
||||
### Архитектура
|
||||
|
||||
Для расширенного CLI необходимо следующее техническое:
|
||||
|
||||
1. **tg-list-config-items**
|
||||
- Перечисление ключей конфигурации для указанного типа
|
||||
- Вызов API-метода Config.list(type)
|
||||
- Вывод списка ключей конфигурации
|
||||
|
||||
Модуль: `trustgraph.cli.list_config_items`
|
||||
|
||||
2. **tg-get-config-item**
|
||||
- Получение конкретного элемента конфигурации(й)
|
||||
- Вызов API-метода Config.get(keys)
|
||||
- Вывод значений конфигурации в формате JSON
|
||||
|
||||
Модуль: `trustgraph.cli.get_config_item`
|
||||
|
||||
3. **tg-put-config-item**
|
||||
- Установка или обновление элемента конфигурации
|
||||
- Вызов API-метода Config.put(values)
|
||||
- Прием параметров type, key и value
|
||||
|
||||
Модуль: `trustgraph.cli.put_config_item`
|
||||
|
||||
4. **tg-delete-config-item**
|
||||
- Удаление элемента конфигурации
|
||||
- Вызов API-метода Config.delete(keys)
|
||||
- Прием параметров type и key
|
||||
|
||||
Модуль: `trustgraph.cli.delete_config_item`
|
||||
|
||||
### Модели данных
|
||||
|
||||
#### ConfigKey и ConfigValue
|
||||
|
||||
Команды используют существующие структуры данных из `trustgraph.api.types`:
|
||||
|
||||
```python
|
||||
@dataclasses.dataclass
|
||||
class ConfigKey:
|
||||
type : str
|
||||
key : str
|
||||
|
||||
@dataclasses.dataclass
|
||||
class ConfigValue:
|
||||
type : str
|
||||
key : str
|
||||
value : str
|
||||
```
|
||||
|
||||
Это обеспечивает:
|
||||
- Последовательную обработку данных в CLI и API
|
||||
- Типобезопасное управление конфигурацией
|
||||
- Структурированные форматы ввода/вывода
|
||||
- Интеграцию с существующим API для конфигурации
|
||||
|
||||
### Спецификации команд CLI
|
||||
|
||||
#### tg-list-config-items
|
||||
```bash
|
||||
tg-list-config-items --type <config-type> [--format text|json] [--api-url <url>]
|
||||
```
|
||||
- **Назначение**: Отобразить все ключи конфигурации для данного типа
|
||||
- **API-вызов**: `Config.list(type)`
|
||||
- **Вывод**:
|
||||
- `text` (по умолчанию): Ключи конфигурации, разделенные новыми строками
|
||||
- `json`: Массив строк JSON с ключами конфигурации
|
||||
|
||||
#### tg-get-config-item
|
||||
```bash
|
||||
tg-get-config-item --type <type> --key <key> [--format text|json] [--api-url <url>]
|
||||
```
|
||||
- **Назначение**: Получить конкретный элемент конфигурации
|
||||
- **API-вызов**: `Config.get([ConfigKey(type, key)])`
|
||||
- **Вывод**:
|
||||
- `text` (по умолчанию): Сырая текстовая строка
|
||||
- `json`: JSON-кодированная строка значения
|
||||
|
||||
#### tg-put-config-item
|
||||
```bash
|
||||
tg-put-config-item --type <type> --key <key> --value <value> [--api-url <url>]
|
||||
tg-put-config-item --type <type> --key <key> --stdin [--api-url <url>]
|
||||
```
|
||||
- **Назначение**: Установить или обновить элемент конфигурации
|
||||
- **API-вызов**: `Config.put([ConfigValue(type, key, value)])`
|
||||
- **Варианты ввода**:
|
||||
- `--value <строка>`: Строковое значение, указанное непосредственно в командной строке
|
||||
- `--stdin`: Чтение всего ввода как значения конфигурации
|
||||
- **Вывод**: Подтверждение успеха
|
||||
|
||||
#### tg-delete-config-item
|
||||
```bash
|
||||
tg-delete-config-item --type <type> --key <key> [--api-url <url>]
|
||||
```
|
||||
- **Назначение**: Удалить элемент конфигурации
|
||||
- **API-вызов**: `Config.delete([ConfigKey(type, key)])`
|
||||
- **Вывод**: Подтверждение успеха
|
||||
|
||||
### Детали реализации
|
||||
|
||||
Все команды следуют установленной схеме CLI для TrustGraph:
|
||||
- Использование `argparse` для разбора командной строки
|
||||
- Импорт и использование `trustgraph.api.Api` для взаимодействия с backend
|
||||
- Соблюдение тех же схем обработки ошибок, что и у существующих CLI
|
||||
- Поддержка стандартного параметра `--api-url` для настройки URL API
|
||||
- Предоставление описательного справки и примеров использования
|
||||
|
||||
#### Обработка форматов вывода
|
||||
|
||||
**Текст:**
|
||||
```bash
|
||||
tg-list-config-items --type prompt
|
||||
template-1
|
||||
template-2
|
||||
system-prompt
|
||||
```
|
||||
|
||||
**JSON:**
|
||||
```bash
|
||||
tg-list-config-items --type prompt --format json
|
||||
["template-1", "template-2", "system-prompt"]
|
||||
```
|
||||
|
||||
**Текст:**
|
||||
```bash
|
||||
tg-get-config-item --type prompt --key template-1
|
||||
You are a helpful assistant. Please respond to: {query}
|
||||
```
|
||||
|
||||
**JSON:**
|
||||
```bash
|
||||
tg-get-config-item --type prompt --key template-1 --format json
|
||||
"You are a helpful assistant. Please respond to: {query}"
|
||||
```
|
||||
|
||||
**Текст:**
|
||||
```bash
|
||||
tg-put-config-item --type prompt --key new-template --value "Custom prompt: {input}"
|
||||
```
|
||||
|
||||
**JSON:**
|
||||
```bash
|
||||
tg-put-config-item --type prompt --key complex-template --stdin < ./prompt-template.txt
|
||||
```
|
||||
|
||||
**Текст:**
|
||||
```bash
|
||||
tg-delete-config-item --type prompt --key old-template
|
||||
```
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- Следует ли командам поддерживать пакетные операции (несколько ключей) помимо отдельных элементов?
|
||||
- Какой формат вывода следует использовать для подтверждений успеха?
|
||||
- Как следует документировать и узнавать пользователями типы конфигурации?
|
||||
|
||||
## Ссылки
|
||||
|
||||
- Существующий API для конфигурации: `trustgraph/api/config.py`
|
||||
- Шаблоны CLI: `trustgraph-cli/trustgraph/cli/show_config.py`
|
||||
- Типы данных: `trustgraph/api/types.py`
|
||||
780
docs/tech-specs/ru/multi-tenant-support.ru.md
Normal file
780
docs/tech-specs/ru/multi-tenant-support.ru.md
Normal file
|
|
@ -0,0 +1,780 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Обеспечьте поддержку многопользовательской среды, устранив несоответствия в именах параметров, которые препятствуют настройке очередей, и добавив параметризацию пространства ключей Cassandra.
|
||||
|
||||
## Контекст архитектуры
|
||||
|
||||
### Разрешение очередей на основе потоков
|
||||
|
||||
Система TrustGraph использует **архитектуру на основе потоков** для динамического разрешения очередей, что изначально поддерживает многопользовательскую среду:
|
||||
|
||||
**Определения потоков** хранятся в Cassandra и указывают имена очередей через определения интерфейсов.
|
||||
**Имена очередей используют шаблоны** с переменными `{id}`, которые заменяются идентификаторами экземпляров потоков.
|
||||
**Сервисы динамически разрешают очереди**, получая конфигурации потоков в момент запроса.
|
||||
**Каждый пользователь может иметь уникальные потоки** с разными именами очередей, что обеспечивает изоляцию.
|
||||
|
||||
Пример определения интерфейса потока:
|
||||
```json
|
||||
{
|
||||
"interfaces": {
|
||||
"triples-store": "persistent://tg/flow/triples-store:{id}",
|
||||
"graph-embeddings-store": "persistent://tg/flow/graph-embeddings-store:{id}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Когда арендатор A запускает поток `tenant-a-prod`, а арендатор B запускает поток `tenant-b-prod`, они автоматически получают изолированные очереди:
|
||||
`persistent://tg/flow/triples-store:tenant-a-prod`
|
||||
`persistent://tg/flow/triples-store:tenant-b-prod`
|
||||
|
||||
**Сервисы, правильно разработанные для многоарендности:**
|
||||
✅ **Knowledge Management (основные компоненты)** - Динамически определяет очереди из конфигурации потока, передаваемой в запросах
|
||||
|
||||
**Сервисы, требующие исправления:**
|
||||
🔴 **Config Service** - Несоответствие имен параметров препятствует настройке очереди
|
||||
🔴 **Librarian Service** - Жестко заданные темы управления хранилищем (описано ниже)
|
||||
🔴 **Все сервисы** - Невозможно настроить пространство ключей Cassandra
|
||||
|
||||
## Описание проблемы
|
||||
|
||||
### Проблема #1: Несоответствие имен параметров в AsyncProcessor
|
||||
**CLI определяет:** `--config-queue` (неясное наименование)
|
||||
**Argparse преобразует в:** `config_queue` (в словаре параметров)
|
||||
**Код ищет:** `config_push_queue`
|
||||
**Результат:** Параметр игнорируется, используется значение по умолчанию `persistent://tg/config/config`
|
||||
**Влияние:** Затрагивает все 32+ сервиса, наследующие от AsyncProcessor
|
||||
**Препятствует:** Многоарендные развертывания не могут использовать очереди, специфичные для арендатора
|
||||
**Решение:** Переименовать параметр CLI в `--config-push-queue` для ясности (разрыв обратной совместимости допустим, так как функция в настоящее время не работает)
|
||||
|
||||
### Проблема #2: Несоответствие имен параметров в Config Service
|
||||
**CLI определяет:** `--push-queue` (двусмысленное наименование)
|
||||
**Argparse преобразует в:** `push_queue` (в словаре параметров)
|
||||
**Код ищет:** `config_push_queue`
|
||||
**Результат:** Параметр игнорируется
|
||||
**Влияние:** Сервис Config не может использовать пользовательскую очередь отправки
|
||||
**Решение:** Переименовать параметр CLI в `--config-push-queue` для согласованности и ясности (разрыв обратной совместимости допустим)
|
||||
|
||||
### Проблема #3: Жестко заданное пространство ключей Cassandra
|
||||
**Текущее состояние:** Пространство ключей жестко задано как `"config"`, `"knowledge"`, `"librarian"` в различных сервисах
|
||||
**Результат:** Невозможно настроить пространство ключей для многоарендных развертываний
|
||||
**Влияние:** Сервисы Config, основные компоненты и Librarian
|
||||
**Препятствует:** Несколько арендаторов не могут использовать отдельные пространства ключей Cassandra
|
||||
|
||||
### Проблема #4: Архитектура управления коллекциями ✅ ВЫПОЛНЕНО
|
||||
**Предыдущее состояние:** Коллекции хранились в пространстве ключей Cassandra Librarian через отдельную таблицу коллекций
|
||||
**Предыдущее состояние:** Librarian использовал 4 жестко заданные темы управления хранилищем для координации создания/удаления коллекций:
|
||||
`vector_storage_management_topic`
|
||||
`object_storage_management_topic`
|
||||
`triples_storage_management_topic`
|
||||
`storage_management_response_topic`
|
||||
**Проблемы (решены):**
|
||||
Жестко заданные темы не могли быть настроены для многоарендных развертываний
|
||||
Сложная асинхронная координация между Librarian и 4+ сервисами хранения
|
||||
Отдельная таблица Cassandra и инфраструктура управления
|
||||
Непостоянные очереди запросов/ответов для критических операций
|
||||
**Решение реализовано:** Коллекции были перенесены в хранилище сервиса Config, используется отправка Config для распространения
|
||||
**Статус:** Все хранилища были перенесены на шаблон `CollectionConfigHandler`
|
||||
|
||||
## Решение
|
||||
|
||||
Эта спецификация решает проблемы #1, #2, #3 и #4.
|
||||
|
||||
### Часть 1: Исправление несоответствий имен параметров
|
||||
|
||||
#### Изменение 1: Базовый класс AsyncProcessor - Переименование параметра CLI
|
||||
**Файл:** `trustgraph-base/trustgraph/base/async_processor.py`
|
||||
**Строка:** 260-264
|
||||
|
||||
**Текущее состояние:**
|
||||
```python
|
||||
parser.add_argument(
|
||||
'--config-queue',
|
||||
default=default_config_queue,
|
||||
help=f'Config push queue {default_config_queue}',
|
||||
)
|
||||
```
|
||||
|
||||
**Исправлено:**
|
||||
```python
|
||||
parser.add_argument(
|
||||
'--config-push-queue',
|
||||
default=default_config_queue,
|
||||
help=f'Config push queue (default: {default_config_queue})',
|
||||
)
|
||||
```
|
||||
|
||||
**Обоснование:**
|
||||
Более понятное и явное наименование
|
||||
Соответствует внутреннему имени переменной `config_push_queue`
|
||||
Изменение, нарушающее обратную совместимость, допустимо, поскольку функция в настоящее время не функционирует
|
||||
Не требуется изменение кода в params.get() - он уже ищет правильное имя
|
||||
|
||||
#### Изменение 2: Сервис конфигурации - Переименование параметра командной строки
|
||||
**Файл:** `trustgraph-flow/trustgraph/config/service/service.py`
|
||||
**Строка:** 276-279
|
||||
|
||||
**Текущее:**
|
||||
```python
|
||||
parser.add_argument(
|
||||
'--push-queue',
|
||||
default=default_config_push_queue,
|
||||
help=f'Config push queue (default: {default_config_push_queue})'
|
||||
)
|
||||
```
|
||||
|
||||
**Исправлено:**
|
||||
```python
|
||||
parser.add_argument(
|
||||
'--config-push-queue',
|
||||
default=default_config_push_queue,
|
||||
help=f'Config push queue (default: {default_config_push_queue})'
|
||||
)
|
||||
```
|
||||
|
||||
**Обоснование:**
|
||||
Более понятное наименование - "config-push-queue" более явно, чем просто "push-queue".
|
||||
Соответствует внутреннему имени переменной `config_push_queue`.
|
||||
Соответствует параметру `--config-push-queue` класса AsyncProcessor.
|
||||
Изменение, нарушающее обратную совместимость, допустимо, поскольку функция в настоящее время не работает.
|
||||
Не требуется изменение кода в params.get() - он уже ищет правильное имя.
|
||||
|
||||
### Часть 2: Добавление параметризации пространства ключей Cassandra
|
||||
|
||||
#### Изменение 3: Добавление параметра пространства ключей в модуль cassandra_config
|
||||
**Файл:** `trustgraph-base/trustgraph/base/cassandra_config.py`
|
||||
|
||||
**Добавление аргумента командной строки** (в функции `add_cassandra_args()`):
|
||||
```python
|
||||
parser.add_argument(
|
||||
'--cassandra-keyspace',
|
||||
default=None,
|
||||
help='Cassandra keyspace (default: service-specific)'
|
||||
)
|
||||
```
|
||||
|
||||
**Добавить поддержку переменных окружения** (в функции `resolve_cassandra_config()`):
|
||||
```python
|
||||
keyspace = params.get(
|
||||
"cassandra_keyspace",
|
||||
os.environ.get("CASSANDRA_KEYSPACE")
|
||||
)
|
||||
```
|
||||
|
||||
**Обновить возвращаемое значение** `resolve_cassandra_config()`:
|
||||
В настоящее время возвращает: `(hosts, username, password)`
|
||||
Изменить, чтобы возвращалось: `(hosts, username, password, keyspace)`
|
||||
|
||||
**Обоснование:**
|
||||
Соответствует существующей схеме конфигурации Cassandra
|
||||
Доступно всем сервисам через `add_cassandra_args()`
|
||||
Поддерживает конфигурацию как через командную строку, так и через переменные окружения
|
||||
|
||||
#### Изменение 4: Сервис конфигурации - Использование параметризованных пространств ключей
|
||||
**Файл:** `trustgraph-flow/trustgraph/config/service/service.py`
|
||||
|
||||
**Строка 30** - Удалить жестко закодированное пространство ключей:
|
||||
```python
|
||||
# DELETE THIS LINE:
|
||||
keyspace = "config"
|
||||
```
|
||||
|
||||
**Строки 69-73** - Обновление разрешения конфигурации Cassandra:
|
||||
|
||||
**Текущая версия:**
|
||||
```python
|
||||
cassandra_host, cassandra_username, cassandra_password = \
|
||||
resolve_cassandra_config(params)
|
||||
```
|
||||
|
||||
**Исправлено:**
|
||||
```python
|
||||
cassandra_host, cassandra_username, cassandra_password, keyspace = \
|
||||
resolve_cassandra_config(params, default_keyspace="config")
|
||||
```
|
||||
|
||||
**Обоснование:**
|
||||
Обеспечивает обратную совместимость с "config" в качестве значения по умолчанию.
|
||||
Позволяет переопределить значение с помощью `--cassandra-keyspace` или `CASSANDRA_KEYSPACE`.
|
||||
|
||||
#### Изменение 5: Основные компоненты/Сервис знаний - Использование параметризованных пространств ключей.
|
||||
**Файл:** `trustgraph-flow/trustgraph/cores/service.py`
|
||||
|
||||
**Строка 37** - Удалить жестко закодированное пространство ключей:
|
||||
```python
|
||||
# DELETE THIS LINE:
|
||||
keyspace = "knowledge"
|
||||
```
|
||||
|
||||
**Обновление разрешения конфигурации Cassandra** (в том же месте, что и служба конфигурации):
|
||||
```python
|
||||
cassandra_host, cassandra_username, cassandra_password, keyspace = \
|
||||
resolve_cassandra_config(params, default_keyspace="knowledge")
|
||||
```
|
||||
|
||||
#### Изменение 6: Сервис библиотекаря - Использование параметризованных пространств ключей.
|
||||
**Файл:** `trustgraph-flow/trustgraph/librarian/service.py`
|
||||
|
||||
**Строка 51** - Удалить жестко закодированное пространство ключей:
|
||||
```python
|
||||
# DELETE THIS LINE:
|
||||
keyspace = "librarian"
|
||||
```
|
||||
|
||||
**Обновление разрешения конфигурации Cassandra** (в том же месте, что и служба конфигурации):
|
||||
```python
|
||||
cassandra_host, cassandra_username, cassandra_password, keyspace = \
|
||||
resolve_cassandra_config(params, default_keyspace="librarian")
|
||||
```
|
||||
|
||||
### Часть 3: Перенос управления коллекциями в службу конфигураций
|
||||
|
||||
#### Обзор
|
||||
Перенесите коллекции из пространства ключей Cassandra librarian в хранилище службы конфигураций. Это устраняет жестко закодированные темы управления хранилищем и упрощает архитектуру, используя существующий механизм распространения конфигураций.
|
||||
|
||||
#### Текущая архитектура
|
||||
```
|
||||
API Request → Gateway → Librarian Service
|
||||
↓
|
||||
CollectionManager
|
||||
↓
|
||||
Cassandra Collections Table (librarian keyspace)
|
||||
↓
|
||||
Broadcast to 4 Storage Management Topics (hardcoded)
|
||||
↓
|
||||
Wait for 4+ Storage Service Responses
|
||||
↓
|
||||
Response to Gateway
|
||||
```
|
||||
|
||||
#### Новая архитектура
|
||||
```
|
||||
API Request → Gateway → Librarian Service
|
||||
↓
|
||||
CollectionManager
|
||||
↓
|
||||
Config Service API (put/delete/getvalues)
|
||||
↓
|
||||
Cassandra Config Table (class='collections', key='user:collection')
|
||||
↓
|
||||
Config Push (to all subscribers on config-push-queue)
|
||||
↓
|
||||
All Storage Services receive config update independently
|
||||
```
|
||||
|
||||
#### Изменение 7: Менеджер коллекций - Использование API сервиса конфигурации
|
||||
**Файл:** `trustgraph-flow/trustgraph/librarian/collection_manager.py`
|
||||
|
||||
**Удалить:**
|
||||
Использование `LibraryTableStore` (строки 33, 40-41)
|
||||
Инициализация производителей управления хранилищем (строки 86-140)
|
||||
Метод `on_storage_response` (строки 400-430)
|
||||
Отслеживание `pending_deletions` (строки 57, 90-96 и использование во всем коде)
|
||||
|
||||
**Добавить:**
|
||||
Клиент сервиса конфигурации для вызовов API (шаблон запрос/ответ)
|
||||
|
||||
**Настройка клиента конфигурации:**
|
||||
```python
|
||||
# In __init__, add config request/response producers/consumers
|
||||
from trustgraph.schema.services.config import ConfigRequest, ConfigResponse
|
||||
|
||||
# Producer for config requests
|
||||
self.config_request_producer = Producer(
|
||||
client=pulsar_client,
|
||||
topic=config_request_queue,
|
||||
schema=ConfigRequest,
|
||||
)
|
||||
|
||||
# Consumer for config responses (with correlation ID)
|
||||
self.config_response_consumer = Consumer(
|
||||
taskgroup=taskgroup,
|
||||
client=pulsar_client,
|
||||
flow=None,
|
||||
topic=config_response_queue,
|
||||
subscriber=f"{id}-config",
|
||||
schema=ConfigResponse,
|
||||
handler=self.on_config_response,
|
||||
)
|
||||
|
||||
# Tracking for pending config requests
|
||||
self.pending_config_requests = {} # request_id -> asyncio.Event
|
||||
```
|
||||
|
||||
**Изменить `list_collections` (строки 145-180):**
|
||||
```python
|
||||
async def list_collections(self, user, tag_filter=None, limit=None):
|
||||
"""List collections from config service"""
|
||||
# Send getvalues request to config service
|
||||
request = ConfigRequest(
|
||||
id=str(uuid.uuid4()),
|
||||
operation='getvalues',
|
||||
type='collections',
|
||||
)
|
||||
|
||||
# Send request and wait for response
|
||||
response = await self.send_config_request(request)
|
||||
|
||||
# Parse collections from response
|
||||
collections = []
|
||||
for key, value_json in response.values.items():
|
||||
if ":" in key:
|
||||
coll_user, collection = key.split(":", 1)
|
||||
if coll_user == user:
|
||||
metadata = json.loads(value_json)
|
||||
collections.append(CollectionMetadata(**metadata))
|
||||
|
||||
# Apply tag filtering in-memory (as before)
|
||||
if tag_filter:
|
||||
collections = [c for c in collections if any(tag in c.tags for tag in tag_filter)]
|
||||
|
||||
# Apply limit
|
||||
if limit:
|
||||
collections = collections[:limit]
|
||||
|
||||
return collections
|
||||
|
||||
async def send_config_request(self, request):
|
||||
"""Send config request and wait for response"""
|
||||
event = asyncio.Event()
|
||||
self.pending_config_requests[request.id] = event
|
||||
|
||||
await self.config_request_producer.send(request)
|
||||
await event.wait()
|
||||
|
||||
return self.pending_config_requests.pop(request.id + "_response")
|
||||
|
||||
async def on_config_response(self, message, consumer, flow):
|
||||
"""Handle config response"""
|
||||
response = message.value()
|
||||
if response.id in self.pending_config_requests:
|
||||
self.pending_config_requests[response.id + "_response"] = response
|
||||
self.pending_config_requests[response.id].set()
|
||||
```
|
||||
|
||||
**Изменить `update_collection` (строки 182-312):**
|
||||
```python
|
||||
async def update_collection(self, user, collection, name, description, tags):
|
||||
"""Update collection via config service"""
|
||||
# Create metadata
|
||||
metadata = CollectionMetadata(
|
||||
user=user,
|
||||
collection=collection,
|
||||
name=name,
|
||||
description=description,
|
||||
tags=tags,
|
||||
)
|
||||
|
||||
# Send put request to config service
|
||||
request = ConfigRequest(
|
||||
id=str(uuid.uuid4()),
|
||||
operation='put',
|
||||
type='collections',
|
||||
key=f'{user}:{collection}',
|
||||
value=json.dumps(metadata.to_dict()),
|
||||
)
|
||||
|
||||
response = await self.send_config_request(request)
|
||||
|
||||
if response.error:
|
||||
raise RuntimeError(f"Config update failed: {response.error.message}")
|
||||
|
||||
# Config service will trigger config push automatically
|
||||
# Storage services will receive update and create collections
|
||||
```
|
||||
|
||||
**Изменить `delete_collection` (строки 314-398):**
|
||||
```python
|
||||
async def delete_collection(self, user, collection):
|
||||
"""Delete collection via config service"""
|
||||
# Send delete request to config service
|
||||
request = ConfigRequest(
|
||||
id=str(uuid.uuid4()),
|
||||
operation='delete',
|
||||
type='collections',
|
||||
key=f'{user}:{collection}',
|
||||
)
|
||||
|
||||
response = await self.send_config_request(request)
|
||||
|
||||
if response.error:
|
||||
raise RuntimeError(f"Config delete failed: {response.error.message}")
|
||||
|
||||
# Config service will trigger config push automatically
|
||||
# Storage services will receive update and delete collections
|
||||
```
|
||||
|
||||
**Формат метаданных коллекции:**
|
||||
Хранится в таблице конфигурации как: `class='collections', key='user:collection'`
|
||||
Значение является JSON-сериализованным объектом CollectionMetadata (без полей времени).
|
||||
Поля: `user`, `collection`, `name`, `description`, `tags`
|
||||
Пример: `class='collections', key='alice:my-docs', value='{"user":"alice","collection":"my-docs","name":"My Documents","description":"...","tags":["work"]}'`
|
||||
|
||||
#### Изменение 8: Сервис Librarian - Удаление инфраструктуры управления хранилищем
|
||||
**Файл:** `trustgraph-flow/trustgraph/librarian/service.py`
|
||||
|
||||
**Удалить:**
|
||||
Модули-производители управления хранилищем (строки 173-190):
|
||||
`vector_storage_management_producer`
|
||||
`object_storage_management_producer`
|
||||
`triples_storage_management_producer`
|
||||
Потребитель ответов хранилища (строки 192-201)
|
||||
Обработчик `on_storage_response` (строки 467-473)
|
||||
|
||||
**Изменить:**
|
||||
Инициализация CollectionManager (строки 215-224) - удалить параметры модуля-производителя хранилища
|
||||
|
||||
**Примечание:** Внешний API для коллекций остается неизменным:
|
||||
`list-collections`
|
||||
`update-collection`
|
||||
`delete-collection`
|
||||
|
||||
#### Изменение 9: Удаление таблицы Collections из LibraryTableStore
|
||||
**Файл:** `trustgraph-flow/trustgraph/tables/library.py`
|
||||
|
||||
**Удалить:**
|
||||
Оператор CREATE для таблицы Collections (строки 114-127)
|
||||
Подготовленные операторы для Collections (строки 205-240)
|
||||
Все методы для работы с коллекциями (строки 578-717):
|
||||
`ensure_collection_exists`
|
||||
`list_collections`
|
||||
`update_collection`
|
||||
`delete_collection`
|
||||
`get_collection`
|
||||
`create_collection`
|
||||
|
||||
**Обоснование:**
|
||||
Коллекции теперь хранятся в таблице конфигурации.
|
||||
Изменение, требующее обновления, приемлемо - миграция данных не требуется.
|
||||
Значительно упрощает сервис Librarian.
|
||||
|
||||
#### Изменение 10: Сервисы хранения - Управление коллекциями на основе конфигурации ✅ ВЫПОЛНЕНО
|
||||
|
||||
**Статус:** Все 11 хранилищ были перенесены на использование `CollectionConfigHandler`.
|
||||
|
||||
**Затронутые сервисы (всего 11):**
|
||||
Векторные представления документов: milvus, pinecone, qdrant
|
||||
Векторные представления графов: milvus, pinecone, qdrant
|
||||
Объектное хранилище: cassandra
|
||||
Хранилище троек: cassandra, falkordb, memgraph, neo4j
|
||||
|
||||
**Файлы:**
|
||||
`trustgraph-flow/trustgraph/storage/doc_embeddings/milvus/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/doc_embeddings/pinecone/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/doc_embeddings/qdrant/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/graph_embeddings/milvus/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/graph_embeddings/pinecone/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/graph_embeddings/qdrant/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/objects/cassandra/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/triples/falkordb/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/triples/memgraph/write.py`
|
||||
`trustgraph-flow/trustgraph/storage/triples/neo4j/write.py`
|
||||
|
||||
**Шаблон реализации (для всех сервисов):**
|
||||
|
||||
1. **Зарегистрировать обработчик конфигурации в `__init__`:**
|
||||
```python
|
||||
# Add after AsyncProcessor initialization
|
||||
self.register_config_handler(self.on_collection_config)
|
||||
self.known_collections = set() # Track (user, collection) tuples
|
||||
```
|
||||
|
||||
2. **Реализовать обработчик конфигурации:**
|
||||
```python
|
||||
async def on_collection_config(self, config, version):
|
||||
"""Handle collection configuration updates"""
|
||||
logger.info(f"Collection config version: {version}")
|
||||
|
||||
if "collections" not in config:
|
||||
return
|
||||
|
||||
# Parse collections from config
|
||||
# Key format: "user:collection" in config["collections"]
|
||||
config_collections = set()
|
||||
for key in config["collections"].keys():
|
||||
if ":" in key:
|
||||
user, collection = key.split(":", 1)
|
||||
config_collections.add((user, collection))
|
||||
|
||||
# Determine changes
|
||||
to_create = config_collections - self.known_collections
|
||||
to_delete = self.known_collections - config_collections
|
||||
|
||||
# Create new collections (idempotent)
|
||||
for user, collection in to_create:
|
||||
try:
|
||||
await self.create_collection_internal(user, collection)
|
||||
self.known_collections.add((user, collection))
|
||||
logger.info(f"Created collection: {user}/{collection}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create {user}/{collection}: {e}")
|
||||
|
||||
# Delete removed collections (idempotent)
|
||||
for user, collection in to_delete:
|
||||
try:
|
||||
await self.delete_collection_internal(user, collection)
|
||||
self.known_collections.discard((user, collection))
|
||||
logger.info(f"Deleted collection: {user}/{collection}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete {user}/{collection}: {e}")
|
||||
```
|
||||
|
||||
3. **Инициализация известных коллекций при запуске:**
|
||||
```python
|
||||
async def start(self):
|
||||
"""Start the processor"""
|
||||
await super().start()
|
||||
await self.sync_known_collections()
|
||||
|
||||
async def sync_known_collections(self):
|
||||
"""Query backend to populate known_collections set"""
|
||||
# Backend-specific implementation:
|
||||
# - Milvus/Pinecone/Qdrant: List collections/indexes matching naming pattern
|
||||
# - Cassandra: Query keyspaces or collection metadata
|
||||
# - Neo4j/Memgraph/FalkorDB: Query CollectionMetadata nodes
|
||||
pass
|
||||
```
|
||||
|
||||
4. **Рефакторинг существующих методов обработчиков:**
|
||||
```python
|
||||
# Rename and remove response sending:
|
||||
# handle_create_collection → create_collection_internal
|
||||
# handle_delete_collection → delete_collection_internal
|
||||
|
||||
async def create_collection_internal(self, user, collection):
|
||||
"""Create collection (idempotent)"""
|
||||
# Same logic as current handle_create_collection
|
||||
# But remove response producer calls
|
||||
# Handle "already exists" gracefully
|
||||
pass
|
||||
|
||||
async def delete_collection_internal(self, user, collection):
|
||||
"""Delete collection (idempotent)"""
|
||||
# Same logic as current handle_delete_collection
|
||||
# But remove response producer calls
|
||||
# Handle "not found" gracefully
|
||||
pass
|
||||
```
|
||||
|
||||
5. **Удаление инфраструктуры управления хранилищем:**
|
||||
Удалить настройку и запуск `self.storage_request_consumer`
|
||||
Удалить настройку `self.storage_response_producer`
|
||||
Удалить метод диспетчера `on_storage_management`
|
||||
Удалить метрики для управления хранилищем
|
||||
Удалить импорты: `StorageManagementRequest`, `StorageManagementResponse`
|
||||
|
||||
**Особенности, специфичные для бэкенда:**
|
||||
|
||||
**Векторные хранилища (Milvus, Pinecone, Qdrant):** Отслеживать логическую `(user, collection)` в `known_collections`, но может создавать несколько бэкенд-коллекций для каждой размерности. Продолжить использование ленивой схемы создания. Операции удаления должны удалять все варианты размерностей.
|
||||
|
||||
**Объекты Cassandra:** Коллекции являются свойствами строк, а не структурами. Отслеживать информацию на уровне пространства ключей.
|
||||
|
||||
**Графовые хранилища (Neo4j, Memgraph, FalkorDB):** Запрашивать узлы `CollectionMetadata` при запуске. Создавать/удалять метаданные узлов при синхронизации.
|
||||
|
||||
**Тройки Cassandra:** Использовать API `KnowledgeGraph` для операций с коллекциями.
|
||||
|
||||
**Основные принципы проектирования:**
|
||||
|
||||
**Итоговая согласованность:** Отсутствует механизм запрос/ответ, конфигурация распространяется широковещанием.
|
||||
**Идемпотентность:** Все операции создания/удаления должны быть безопасными для повторной попытки.
|
||||
**Обработка ошибок:** Регистрировать ошибки, но не блокировать обновления конфигурации.
|
||||
**Самовосстановление:** Неудачные операции будут повторены при следующем распространении конфигурации.
|
||||
**Формат ключа коллекции:** `"user:collection"` в `config["collections"]`
|
||||
|
||||
#### Изменение 11: Обновление схемы коллекции - Удаление временных меток
|
||||
**Файл:** `trustgraph-base/trustgraph/schema/services/collection.py`
|
||||
|
||||
**Изменить CollectionMetadata (строки 13-21):**
|
||||
Удалить поля `created_at` и `updated_at`:
|
||||
```python
|
||||
class CollectionMetadata(Record):
|
||||
user = String()
|
||||
collection = String()
|
||||
name = String()
|
||||
description = String()
|
||||
tags = Array(String())
|
||||
# Remove: created_at = String()
|
||||
# Remove: updated_at = String()
|
||||
```
|
||||
|
||||
**Изменить класс CollectionManagementRequest (строки 25-47):**
|
||||
Удалить поля временной метки:
|
||||
```python
|
||||
class CollectionManagementRequest(Record):
|
||||
operation = String()
|
||||
user = String()
|
||||
collection = String()
|
||||
timestamp = String()
|
||||
name = String()
|
||||
description = String()
|
||||
tags = Array(String())
|
||||
# Remove: created_at = String()
|
||||
# Remove: updated_at = String()
|
||||
tag_filter = Array(String())
|
||||
limit = Integer()
|
||||
```
|
||||
|
||||
**Обоснование:**
|
||||
Временные метки не добавляют ценности для коллекций.
|
||||
Сервис конфигурации самостоятельно отслеживает версии.
|
||||
Упрощает схему и уменьшает объем хранения.
|
||||
|
||||
#### Преимущества миграции на сервис конфигурации
|
||||
|
||||
1. ✅ **Устраняет жестко закодированные темы управления хранилищем** - Решает проблему многопользовательской среды.
|
||||
2. ✅ **Более простая координация** - Нет сложного асинхронного ожидания ответов от 4+ хранилищ.
|
||||
3. ✅ **В конечном итоге согласованность** - Сервисы хранения обновляются независимо с помощью push-конфигурации.
|
||||
4. ✅ **Более высокая надежность** - Постоянная push-конфигурация против неперсистентного запроса/ответа.
|
||||
5. ✅ **Унифицированная модель конфигурации** - Коллекции рассматриваются как конфигурация.
|
||||
6. ✅ **Уменьшает сложность** - Удаляет около 300 строк кода для координации.
|
||||
7. ✅ **Готовность к многопользовательской среде** - Конфигурация уже поддерживает изоляцию пользователей с помощью пространств ключей.
|
||||
8. ✅ **Отслеживание версий** - Механизм версионирования сервиса конфигурации обеспечивает аудит.
|
||||
|
||||
## Замечания по реализации
|
||||
|
||||
### Обратная совместимость
|
||||
|
||||
**Изменения параметров:**
|
||||
Переименование параметров командной строки является серьезным изменением, но допустимо (функция в настоящее время не работает).
|
||||
Сервисы работают без параметров (используются значения по умолчанию).
|
||||
Значения по умолчанию для пространств ключей сохранены: "config", "knowledge", "librarian".
|
||||
Значение по умолчанию для очереди: `persistent://tg/config/config`
|
||||
|
||||
**Управление коллекциями:**
|
||||
**Серьезное изменение:** Таблица коллекций удалена из пространства ключей librarian.
|
||||
**Миграция данных не предусмотрена** - приемлемо для этой фазы.
|
||||
Внешний API для коллекций не изменен (операции list/update/delete).
|
||||
Формат метаданных коллекций упрощен (временные метки удалены).
|
||||
|
||||
### Требования к тестированию
|
||||
|
||||
**Тестирование параметров:**
|
||||
1. Проверить, работает ли параметр `--config-push-queue` для сервиса graph-embeddings.
|
||||
2. Проверить, работает ли параметр `--config-push-queue` для сервиса text-completion.
|
||||
3. Проверить, работает ли параметр `--config-push-queue` для сервиса конфигурации.
|
||||
4. Проверить, работает ли параметр `--cassandra-keyspace` для сервиса конфигурации.
|
||||
5. Проверить, работает ли параметр `--cassandra-keyspace` для сервиса cores.
|
||||
6. Проверить, работает ли параметр `--cassandra-keyspace` для сервиса librarian.
|
||||
7. Проверить, работают ли сервисы без параметров (используются значения по умолчанию).
|
||||
8. Проверить развертывание в многопользовательской среде с использованием пользовательских имен очередей и пространств ключей.
|
||||
|
||||
**Тестирование управления коллекциями:**
|
||||
9. Проверить операцию `list-collections` через сервис конфигурации.
|
||||
10. Проверить, создает ли `update-collection` записи/обновления в таблице конфигурации.
|
||||
11. Проверить, удаляет ли `delete-collection` записи из таблицы конфигурации.
|
||||
12. Проверить, запускается ли push-конфигурации при обновлениях коллекций.
|
||||
13. Проверить, работает ли фильтрация по тегам с использованием хранилища на основе конфигурации.
|
||||
14. Проверить, работают ли операции с коллекциями без полей временных меток.
|
||||
|
||||
### Пример развертывания в многопользовательской среде
|
||||
```bash
|
||||
# Tenant: tg-dev
|
||||
graph-embeddings \
|
||||
-p pulsar+ssl://broker:6651 \
|
||||
--pulsar-api-key <KEY> \
|
||||
--config-push-queue persistent://tg-dev/config/config
|
||||
|
||||
config-service \
|
||||
-p pulsar+ssl://broker:6651 \
|
||||
--pulsar-api-key <KEY> \
|
||||
--config-push-queue persistent://tg-dev/config/config \
|
||||
--cassandra-keyspace tg_dev_config
|
||||
```
|
||||
|
||||
## Анализ влияния
|
||||
|
||||
### Сервисы, затронутые изменением 1-2 (Переименование параметра CLI)
|
||||
Все сервисы, наследующие AsyncProcessor или FlowProcessor:
|
||||
config-service
|
||||
cores-service
|
||||
librarian-service
|
||||
graph-embeddings
|
||||
document-embeddings
|
||||
text-completion-* (все провайдеры)
|
||||
extract-* (все экстракторы)
|
||||
query-* (все сервисы запросов)
|
||||
retrieval-* (все сервисы RAG)
|
||||
storage-* (все сервисы хранения)
|
||||
И еще 20+ сервисов
|
||||
|
||||
### Сервисы, затронутые изменениями 3-6 (Keyspace Cassandra)
|
||||
config-service
|
||||
cores-service
|
||||
librarian-service
|
||||
|
||||
### Сервисы, затронутые изменениями 7-11 (Управление коллекциями)
|
||||
|
||||
**Немедленные изменения:**
|
||||
librarian-service (collection_manager.py, service.py)
|
||||
tables/library.py (удаление таблицы collections)
|
||||
schema/services/collection.py (удаление метки времени)
|
||||
|
||||
**Выполненные изменения (изменение 10):** ✅
|
||||
Все сервисы хранения (всего 11) - перенесены на push-конфигурацию для обновлений коллекций через `CollectionConfigHandler`
|
||||
Схема управления хранилищем удалена из `storage.py`
|
||||
|
||||
## Будущие соображения
|
||||
|
||||
### Модель keyspace на уровне пользователя
|
||||
|
||||
Некоторые сервисы динамически используют **keyspace на уровне пользователя**, когда каждый пользователь получает свой собственный keyspace Cassandra:
|
||||
|
||||
**Сервисы с keyspace на уровне пользователя:**
|
||||
1. **Сервис запросов Triple** (`trustgraph-flow/trustgraph/query/triples/cassandra/service.py:65`)
|
||||
Использует `keyspace=query.user`
|
||||
2. **Сервис запросов объектов** (`trustgraph-flow/trustgraph/query/objects/cassandra/service.py:479`)
|
||||
Использует `keyspace=self.sanitize_name(user)`
|
||||
3. **Прямой доступ к графу знаний** (`trustgraph-flow/trustgraph/direct/cassandra_kg.py:18`)
|
||||
Параметр по умолчанию `keyspace="trustgraph"`
|
||||
|
||||
**Статус:** Эти параметры **не изменяются** в этой спецификации.
|
||||
|
||||
**Требуется дальнейший анализ:**
|
||||
Оценить, создает ли модель keyspace на уровне пользователя проблемы изоляции арендаторов
|
||||
Рассмотреть, нужны ли многопользовательским развертываниям префиксы keyspace (например, `tenant_a_user1`)
|
||||
Проверить на потенциальные конфликты идентификаторов пользователей между арендаторами
|
||||
Оценить, является ли единый общий keyspace на арендатора с изоляцией строк на уровне пользователя более предпочтительным
|
||||
|
||||
**Примечание:** Это не блокирует текущую многопользовательскую реализацию, но должно быть рассмотрено перед производственными многопользовательскими развертываниями.
|
||||
|
||||
## Этапы реализации
|
||||
|
||||
### Этап 1: Исправление параметров (изменения 1-6)
|
||||
Исправить именование параметра `--config-push-queue`
|
||||
Добавить поддержку параметра `--cassandra-keyspace`
|
||||
**Результат:** Многопользовательская очередь и конфигурация keyspace включены
|
||||
|
||||
### Этап 2: Миграция управления коллекциями (изменения 7-9, 11)
|
||||
Перенести хранение коллекций в сервис конфигурации
|
||||
Удалить таблицу collections из librarian
|
||||
Обновить схему коллекций (удалить метки времени)
|
||||
**Результат:** Устраняет жестко закодированные темы управления хранилищем, упрощает librarian
|
||||
|
||||
### Этап 3: Обновления сервисов хранения (изменение 10) ✅ ВЫПОЛНЕНО
|
||||
Обновлены все сервисы хранения для использования push-конфигурации для коллекций через `CollectionConfigHandler`
|
||||
Удалена инфраструктура запросов/ответов управления хранилищем
|
||||
Удалены устаревшие определения схемы
|
||||
**Результат:** Достигнуто полное управление коллекциями на основе конфигурации
|
||||
|
||||
## Ссылки
|
||||
GitHub Issue: https://github.com/trustgraph-ai/trustgraph/issues/582
|
||||
Связанные файлы:
|
||||
`trustgraph-base/trustgraph/base/async_processor.py`
|
||||
`trustgraph-base/trustgraph/base/cassandra_config.py`
|
||||
`trustgraph-base/trustgraph/schema/core/topic.py`
|
||||
`trustgraph-base/trustgraph/schema/services/collection.py`
|
||||
`trustgraph-flow/trustgraph/config/service/service.py`
|
||||
`trustgraph-flow/trustgraph/cores/service.py`
|
||||
`trustgraph-flow/trustgraph/librarian/service.py`
|
||||
`trustgraph-flow/trustgraph/librarian/collection_manager.py`
|
||||
`trustgraph-flow/trustgraph/tables/library.py`
|
||||
223
docs/tech-specs/ru/neo4j-user-collection-isolation.ru.md
Normal file
223
docs/tech-specs/ru/neo4j-user-collection-isolation.ru.md
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Поддержка изоляции пользователя/коллекции в Neo4j"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Поддержка изоляции пользователя/коллекции в Neo4j
|
||||
|
||||
> **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.
|
||||
|
||||
## Формулировка проблемы
|
||||
|
||||
Текущая реализация хранения и запросов в Neo4j не обеспечивает изоляцию пользователя/коллекции, что создает проблему безопасности многопользовательской среды. Все тройки хранятся в одном графе без каких-либо механизмов для предотвращения доступа пользователей к данным других пользователей или смешения коллекций.
|
||||
|
||||
В отличие от других бэкендов хранения в TrustGraph:
|
||||
- **Cassandra**: использует отдельные пространства ключей для каждого пользователя и таблицы для каждой коллекции.
|
||||
- **Vector-хранилища** (Milvus, Qdrant, Pinecone): используют пространства имен, специфичные для коллекции.
|
||||
- **Neo4j**: в настоящее время все данные хранятся в одном графе (уязвимость для безопасности).
|
||||
|
||||
## Текущая архитектура
|
||||
|
||||
### Модель данных
|
||||
- **Узлы**: метка `:Node` с свойством `uri`, метка `:Literal` с свойством `value`
|
||||
- **Отношения**: метка `:Rel` с свойством `uri`
|
||||
- **Индексы**: `Node.uri`, `Literal.value`, `Rel.uri`
|
||||
|
||||
### Поток сообщений
|
||||
- Сообщения `Triples` содержат поля `metadata.user` и `metadata.collection`
|
||||
- Сервис хранения получает информацию о пользователе/коллекции, но игнорирует ее
|
||||
- Сервис запросов ожидает `user` и `collection` в `TriplesQueryRequest`, но игнорирует их
|
||||
|
||||
### Текущая проблема безопасности
|
||||
```cypher
|
||||
# Любой пользователь может запросить любые данные - нет изоляции
|
||||
MATCH (src:Node)-[rel:Rel]->(dest:Node)
|
||||
RETURN src.uri, rel.uri, dest.uri
|
||||
```
|
||||
|
||||
## Предлагаемое решение: Фильтрация на основе свойств (Рекомендуется)
|
||||
|
||||
### Обзор
|
||||
Добавьте свойства `user` и `collection` ко всем узлам и отношениям, а затем фильтруйте все операции по этим свойствам. Этот подход обеспечивает надежную изоляцию, сохраняя при этом гибкость запросов и обратную совместимость.
|
||||
|
||||
### Изменения в модели данных
|
||||
|
||||
#### Улучшенная структура узла
|
||||
```cypher
|
||||
// Узел
|
||||
CREATE (n:Node {
|
||||
uri: "http://example.com/entity1",
|
||||
user: "john_doe",
|
||||
collection: "production_v1"
|
||||
})
|
||||
|
||||
// Литеральные сущности
|
||||
CREATE (n:Literal {
|
||||
value: "literal value",
|
||||
user: "john_doe",
|
||||
collection: "production_v1"
|
||||
})
|
||||
```
|
||||
|
||||
#### Улучшенная структура отношений
|
||||
```cypher
|
||||
// Отношения с свойствами user/collection
|
||||
CREATE (src)-[:Rel {
|
||||
uri: "http://example.com/predicate1",
|
||||
user: "john_doe",
|
||||
collection: "production_v1"
|
||||
}]->(dest)
|
||||
```
|
||||
|
||||
#### Обновленные индексы
|
||||
```cypher
|
||||
// Комплексные индексы для эффективного фильтра
|
||||
CREATE INDEX node_user_collection_uri FOR (n:Node) ON (n.user, n.collection, n.uri);
|
||||
CREATE INDEX literal_user_collection_value FOR (n:Literal) ON (n.user, n.collection, n.value);
|
||||
CREATE INDEX rel_user_collection_uri FOR ()-[r:Rel]-() ON (r.user, r.collection, r.uri);
|
||||
|
||||
// Сохранение существующих индексов для обратной совместимости (необязательно)
|
||||
CREATE INDEX Node_uri FOR (n:Node) ON (n.uri);
|
||||
CREATE INDEX Literal_value FOR (n:Literal) ON (n.value);
|
||||
CREATE INDEX Rel_uri FOR ()-[r:Rel]-() ON (r.uri);
|
||||
```
|
||||
|
||||
### Изменения в реализации
|
||||
|
||||
#### Сервис хранения (`write.py`)
|
||||
|
||||
**Текущий код:**
|
||||
```python
|
||||
def create_node(self, uri):
|
||||
summary = self.io.execute_query(
|
||||
"MERGE (n:Node {uri: $uri})",
|
||||
uri=uri, database_=self.db,
|
||||
).summary
|
||||
```
|
||||
|
||||
**Обновленный код:**
|
||||
```python
|
||||
def create_node(self, uri, user, collection):
|
||||
summary = self.io.execute_query(
|
||||
"MERGE (n:Node {uri: $uri})",
|
||||
uri=uri, user=user, collection=collection, database_=self.db,
|
||||
).summary
|
||||
```
|
||||
|
||||
#### Сервис запросов
|
||||
- Добавьте фильтры для user и collection в запросы, чтобы обеспечить изоляцию.
|
||||
|
||||
### План реализации
|
||||
|
||||
### Этап 1: Основа (Неделя 1)
|
||||
1. [ ] Обновление сервиса хранения для приема и хранения свойств user/collection
|
||||
2. [ ] Создание комплексных индексов для эффективного запроса
|
||||
3. [ ] Реализация обратной совместимости
|
||||
4. [ ] Создание модульных тестов для новой функциональности
|
||||
|
||||
### Этап 2: Обновление запросов (Неделя 2)
|
||||
1. [ ] Обновление всех шаблонов запросов для включения фильтров user/collection
|
||||
2. [ ] Добавление валидации запросов и мер безопасности
|
||||
3. [ ] Обновление интеграционных тестов
|
||||
4. [ ] Тестирование производительности с запросами, отфильтрованными
|
||||
|
||||
### Этап 3: Миграция и развертывание (Неделя 3)
|
||||
1. [ ] Создание скриптов миграции для существующих экземпляров Neo4j
|
||||
2. [ ] Документация и руководства по развертыванию
|
||||
3. [ ] Мониторинг и оповещения об изоляции
|
||||
4. [ ] Тестирование конвейера
|
||||
|
||||
### Этап 4: Усиление (Неделя 4)
|
||||
1. [ ] Удаление режима обратной совместимости
|
||||
2. [ ] Добавление комплексной журнализации
|
||||
3. [ ] Проверка безопасности и тестирование на проникновение
|
||||
4. [ ] Оптимизация производительности
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
### Модульные тесты
|
||||
```python
|
||||
def test_user_collection_isolation():
|
||||
# Храним тройки для user1/collection1
|
||||
processor.store_triples(triples_user1_coll1)
|
||||
|
||||
# Храним тройки для user2/collection2
|
||||
processor.store_triples(triples_user2_coll2)
|
||||
|
||||
# Запрос как user1 должен возвращать только данные user1
|
||||
results = processor.query_triples(query_user1_coll1)
|
||||
assert all_results_belong_to_user1_coll1(results)
|
||||
|
||||
# Запрос как user2 должен возвращать только данные user2
|
||||
results = processor.query_triples(query_user2_coll2)
|
||||
assert all_results_belong_to_user2_coll2(results)
|
||||
```
|
||||
|
||||
### Интеграционные тесты
|
||||
- Сценарии многопользовательского взаимодействия с перекрывающимися данными
|
||||
- Запросы к перекрестным коллекциям (должны завершаться неудачей)
|
||||
- Тестирование миграции с существующими данными
|
||||
- Тестирование производительности с большими наборами данных
|
||||
|
||||
### Тесты безопасности
|
||||
- Попытки запросить данные других пользователей
|
||||
- Атаки SQL injection на параметры user/collection
|
||||
- Проверка полной изоляции при различных шаблонах запросов
|
||||
|
||||
## Соображения производительности
|
||||
|
||||
### Стратегия индексирования
|
||||
- Комплексные индексы на `(user, collection, uri)` для оптимальной фильтрации
|
||||
- Рассмотрите частичные индексы, если некоторые коллекции значительно больше
|
||||
- Отслеживайте использование и производительность индекса
|
||||
|
||||
### Оптимизация запросов
|
||||
- Используйте EXPLAIN для проверки использования индекса в запросах, отфильтрованных по user/collection
|
||||
- Рассмотрите кэширование результатов запросов для часто используемых данных
|
||||
- Профилируйте использование памяти с большим количеством пользователей/коллекций
|
||||
|
||||
### Масштабируемость
|
||||
- Каждая комбинация пользователя/коллекции создает отдельные "острова данных"
|
||||
- Отслеживайте размер базы данных и использование пула соединений
|
||||
- Рассмотрите стратегии горизонтального масштабирования, если необходимо
|
||||
|
||||
## Безопасность и соответствие
|
||||
|
||||
### Гарантии изоляции данных
|
||||
- **Физическая**: Все данные пользователя хранятся с явными свойствами user/collection
|
||||
- **Логическая**: Все запросы фильтруются по контексту user/collection
|
||||
- **Контроль доступа**: Уровень сервиса обеспечивает проверку доступа
|
||||
|
||||
### Требования к отчетности
|
||||
- Отслеживайте доступ к данным с контекстом user/collection
|
||||
- Отслеживайте активности миграции и перемещения данных
|
||||
- Мониторинг попыток нарушить изоляцию
|
||||
|
||||
### Соображения соответствия
|
||||
- GDPR: Улучшенная возможность находить и удалять данные, специфичные для пользователя
|
||||
- SOC2: Четкая изоляция и контроль доступа
|
||||
- HIPAA: Сильная изоляция для данных о здравоохранении
|
||||
|
||||
## Риски и смягчающие факторы
|
||||
|
||||
| Риск | Влияние | Вероятность | Смягчение |
|
||||
|------|--------|------------|------------|
|
||||
| Отсутствие фильтра user/collection в запросе | Высокий | Средняя | Обязательная валидация, комплексное тестирование |
|
||||
| Снижение производительности | Средний | Низкая | Оптимизация индекса, профилирование запросов |
|
||||
| Повреждение данных во время миграции | Высокий | Низкая | Стратегия резервного копирования, процедуры отката |
|
||||
| Уязвимость для безопасности | Высокий | Низкая | Проверка безопасности, тестирование на проникновение |
|
||||
|
||||
## Критерии успеха
|
||||
|
||||
1. **Безопасность**: Полная изоляция данных между пользователями в производственной среде
|
||||
2. **Производительность**: <10% влияния на производительность запросов по сравнению с нефильтрованными запросами
|
||||
3. **Миграция**: 100% данных существующего экземпляра Neo4j успешно мигрированы без потерь
|
||||
4. **Удобство использования**: Все существующие шаблоны запросов работают с контекстом user/collection
|
||||
5. **Соответствие**: Полный журнал доступа к данным с контекстом user/collection
|
||||
|
||||
## Заключение
|
||||
|
||||
Предлагаемый подход на основе фильтрации свойств обеспечивает наилучший баланс между безопасностью, производительностью и удобством обслуживания для добавления изоляции пользователя/коллекции в Neo4j. Он соответствует существующим многопользовательским шаблонам TrustGraph, одновременно используя возможности Neo4j в области запросов и индексирования графов.
|
||||
|
||||
Это решение гарантирует, что бэкенд Neo4j TrustGraph соответствует тем же стандартам безопасности, что и другие бэкенды, обеспечивая при этом изоляцию данных, одновременно сохраняя гибкость и мощь запросов графов.
|
||||
769
docs/tech-specs/ru/ontology-extract-phase-2.ru.md
Normal file
769
docs/tech-specs/ru/ontology-extract-phase-2.ru.md
Normal file
|
|
@ -0,0 +1,769 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Извлечение знаний из онтологий - Фаза 2, рефакторинг"
|
||||
parent: "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.
|
||||
|
||||
**Статус**: Черновик
|
||||
**Автор**: Аналитическая сессия 2025-12-03
|
||||
**Связанные**: `ontology.md`, `ontorag.md`
|
||||
|
||||
## Обзор
|
||||
|
||||
Этот документ выявляет несоответствия в текущей системе извлечения знаний на основе онтологий и предлагает рефакторинг для повышения производительности LLM и снижения потери информации.
|
||||
|
||||
## Текущая реализация
|
||||
|
||||
### Как это работает сейчас
|
||||
|
||||
1. **Загрузка онтологии** (`ontology_loader.py`)
|
||||
Загружает JSON-файл онтологии с ключами, такими как `"fo/Recipe"`, `"fo/Food"`, `"fo/produces"`
|
||||
Идентификаторы классов включают префикс пространства имен в самом ключе
|
||||
Пример из `food.ontology`:
|
||||
```json
|
||||
"classes": {
|
||||
"fo/Recipe": {
|
||||
"uri": "http://purl.org/ontology/fo/Recipe",
|
||||
"rdfs:comment": "A Recipe is a combination..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Построение запроса** (`extract.py:299-307`, `ontology-prompt.md`)
|
||||
Шаблон получает словари `classes`, `object_properties`, `datatype_properties`
|
||||
Шаблон выполняет итерацию: `{% for class_id, class_def in classes.items() %}`
|
||||
LLM видит: `**fo/Recipe**: A Recipe is a combination...`
|
||||
Пример формата вывода показывает:
|
||||
```json
|
||||
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "Recipe"}
|
||||
{"subject": "recipe:cornish-pasty", "predicate": "has_ingredient", "object": "ingredient:flour"}
|
||||
```
|
||||
|
||||
3. **Разбор ответа** (`extract.py:382-428`)
|
||||
Ожидается массив JSON: `[{"subject": "...", "predicate": "...", "object": "..."}]`
|
||||
Проверка на соответствие подмножеству онтологии
|
||||
Расширение URI с помощью `expand_uri()` (extract.py:473-521)
|
||||
|
||||
4. **Расширение URI** (`extract.py:473-521`)
|
||||
Проверяет, присутствует ли значение в словаре `ontology_subset.classes`
|
||||
Если найдено, извлекает URI из определения класса
|
||||
Если не найдено, создает URI: `f"https://trustgraph.ai/ontology/{ontology_id}#{value}"`
|
||||
|
||||
### Пример потока данных
|
||||
|
||||
**JSON онтологии → Загрузчик → Запрос:**
|
||||
```
|
||||
"fo/Recipe" → classes["fo/Recipe"] → LLM sees "**fo/Recipe**"
|
||||
```
|
||||
|
||||
**Большая языковая модель → Парсер → Вывод:**
|
||||
```
|
||||
"Recipe" → not in classes["fo/Recipe"] → constructs URI → LOSES original URI
|
||||
"fo/Recipe" → found in classes → uses original URI → PRESERVES URI
|
||||
```
|
||||
|
||||
## Выявленные проблемы
|
||||
|
||||
### 1. **Несоответствие примеров в запросе**
|
||||
|
||||
**Проблема**: Шаблон запроса показывает идентификаторы классов с префиксами (`fo/Recipe`), но пример вывода использует имена классов без префиксов (`Recipe`).
|
||||
|
||||
**Местоположение**: `ontology-prompt.md:5-52`
|
||||
|
||||
```markdown
|
||||
## Ontology Classes:
|
||||
- **fo/Recipe**: A Recipe is...
|
||||
|
||||
## Example Output:
|
||||
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "Recipe"}
|
||||
```
|
||||
|
||||
**Влияние**: LLM получает противоречивые сигналы о том, какой формат использовать.
|
||||
|
||||
### 2. **Потеря информации при расширении URI**
|
||||
|
||||
**Проблема**: Когда LLM возвращает имена классов без префикса, следуя примеру, `expand_uri()` не может найти их в словаре онтологии и создает резервные URI, теряя исходные правильные URI.
|
||||
|
||||
**Местоположение**: `extract.py:494-500`
|
||||
|
||||
```python
|
||||
if value in ontology_subset.classes: # Looks for "Recipe"
|
||||
class_def = ontology_subset.classes[value] # But key is "fo/Recipe"
|
||||
if isinstance(class_def, dict) and 'uri' in class_def:
|
||||
return class_def['uri'] # Never reached!
|
||||
return f"https://trustgraph.ai/ontology/{ontology_id}#{value}" # Fallback
|
||||
```
|
||||
|
||||
**Влияние:**
|
||||
Исходный URI: `http://purl.org/ontology/fo/Recipe`
|
||||
Сформированный URI: `https://trustgraph.ai/ontology/food#Recipe`
|
||||
Семантическое значение потеряно, нарушается совместимость.
|
||||
|
||||
### 3. **Неоднозначный формат экземпляра сущности**
|
||||
|
||||
**Проблема:** Отсутствуют четкие указания относительно формата URI экземпляра сущности.
|
||||
|
||||
**Примеры в запросе:**
|
||||
`"recipe:cornish-pasty"` (префикс, похожий на пространство имен)
|
||||
`"ingredient:flour"` (другой префикс)
|
||||
|
||||
**Фактическое поведение** (extract.py:517-520):
|
||||
```python
|
||||
# Treat as entity instance - construct unique URI
|
||||
normalized = value.replace(" ", "-").lower()
|
||||
return f"https://trustgraph.ai/{ontology_id}/{normalized}"
|
||||
```
|
||||
|
||||
**Влияние**: LLM должна угадать соглашение о префиксах без контекста онтологии.
|
||||
|
||||
### 4. **Отсутствие рекомендаций по префиксам пространств имен**
|
||||
|
||||
**Проблема**: JSON-файл онтологии содержит определения пространств имен (строки 10-25 в food.ontology):
|
||||
```json
|
||||
"namespaces": {
|
||||
"fo": "http://purl.org/ontology/fo/",
|
||||
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Но эти данные никогда не передаются в языковую модель. Языковая модель не знает:
|
||||
Что означает "fo"
|
||||
Какой префикс использовать для сущностей
|
||||
К каким элементам относится какое пространство имен
|
||||
|
||||
### 5. **Метки, не используемые в запросе**
|
||||
|
||||
**Проблема**: У каждого класса есть поля `rdfs:label` (например, `{"value": "Recipe", "lang": "en-gb"}`), но шаблон запроса их не использует.
|
||||
|
||||
**Текущая ситуация**: Отображаются только `class_id` и `comment`
|
||||
```jinja
|
||||
- **{{class_id}}**{% if class_def.comment %}: {{class_def.comment}}{% endif %}
|
||||
```
|
||||
|
||||
**Доступно, но не используется**:
|
||||
```python
|
||||
"rdfs:label": [{"value": "Recipe", "lang": "en-gb"}]
|
||||
```
|
||||
|
||||
**Влияние**: Может предоставить удобочитаемые имена наряду с техническими идентификаторами.
|
||||
|
||||
## Предлагаемые решения
|
||||
|
||||
### Вариант A: Нормализация до идентификаторов без префиксов
|
||||
|
||||
**Подход**: Удалять префиксы из идентификаторов классов перед отображением LLM.
|
||||
|
||||
**Изменения**:
|
||||
1. Изменить `build_extraction_variables()` для преобразования ключей:
|
||||
```python
|
||||
classes_for_prompt = {
|
||||
k.split('/')[-1]: v # "fo/Recipe" → "Recipe"
|
||||
for k, v in ontology_subset.classes.items()
|
||||
}
|
||||
```
|
||||
|
||||
2. Обновить пример запроса, чтобы он соответствовал (уже использует имена без префиксов).
|
||||
|
||||
3. Изменить `expand_uri()` для обработки обоих форматов:
|
||||
```python
|
||||
# Try exact match first
|
||||
if value in ontology_subset.classes:
|
||||
return ontology_subset.classes[value]['uri']
|
||||
|
||||
# Try with prefix
|
||||
for prefix in ['fo/', 'rdf:', 'rdfs:']:
|
||||
prefixed = f"{prefix}{value}"
|
||||
if prefixed in ontology_subset.classes:
|
||||
return ontology_subset.classes[prefixed]['uri']
|
||||
```
|
||||
|
||||
**Преимущества:**
|
||||
Более понятный и читаемый для человека.
|
||||
Соответствует существующим примерам запросов.
|
||||
Большие языковые модели (LLM) лучше работают с более простыми токенами.
|
||||
|
||||
**Недостатки:**
|
||||
Конфликты имен классов, если несколько онтологий имеют одинаковое имя класса.
|
||||
Потеря информации о пространстве имен.
|
||||
Требуется логика обработки исключений для поиска.
|
||||
|
||||
### Вариант B: Использовать полные префиксные идентификаторы последовательно
|
||||
|
||||
**Подход:** Обновить примеры для использования префиксных идентификаторов, соответствующих тем, которые показаны в списке классов.
|
||||
|
||||
**Изменения:**
|
||||
1. Обновить пример запроса (ontology-prompt.md:46-52):
|
||||
```json
|
||||
[
|
||||
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "fo/Recipe"},
|
||||
{"subject": "recipe:cornish-pasty", "predicate": "rdfs:label", "object": "Cornish Pasty"},
|
||||
{"subject": "recipe:cornish-pasty", "predicate": "fo/produces", "object": "food:cornish-pasty"},
|
||||
{"subject": "food:cornish-pasty", "predicate": "rdf:type", "object": "fo/Food"}
|
||||
]
|
||||
```
|
||||
|
||||
2. Добавьте объяснение пространства имен в запрос:
|
||||
```markdown
|
||||
## Namespace Prefixes:
|
||||
- **fo/**: Food Ontology (http://purl.org/ontology/fo/)
|
||||
- **rdf:**: RDF Schema
|
||||
- **rdfs:**: RDF Schema
|
||||
|
||||
Use these prefixes exactly as shown when referencing classes and properties.
|
||||
```
|
||||
|
||||
3. Оставьте `expand_uri()` без изменений (это работает правильно, когда найдены совпадения).
|
||||
|
||||
**Преимущества:**
|
||||
Согласованность входных и выходных данных.
|
||||
Отсутствие потери информации.
|
||||
Сохраняет семантику пространства имен.
|
||||
Работает с несколькими онтологиями.
|
||||
|
||||
**Недостатки:**
|
||||
Более многословные токены для LLM.
|
||||
Требует от LLM отслеживания префиксов.
|
||||
|
||||
### Вариант C: Гибридный - Отображать и метку, и идентификатор.
|
||||
|
||||
**Подход:** Улучшить запрос, чтобы отображать как читаемые человеком метки, так и технические идентификаторы.
|
||||
|
||||
**Изменения:**
|
||||
1. Обновить шаблон запроса:
|
||||
```jinja
|
||||
{% for class_id, class_def in classes.items() %}
|
||||
- **{{class_id}}** (label: "{{class_def.labels[0].value if class_def.labels else class_id}}"){% if class_def.comment %}: {{class_def.comment}}{% endif %}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
Пример вывода:
|
||||
```markdown
|
||||
- **fo/Recipe** (label: "Recipe"): A Recipe is a combination...
|
||||
```
|
||||
|
||||
2. Инструкции по обновлению:
|
||||
```markdown
|
||||
When referencing classes:
|
||||
- Use the full prefixed ID (e.g., "fo/Recipe") in JSON output
|
||||
- The label (e.g., "Recipe") is for human understanding only
|
||||
```
|
||||
|
||||
**Преимущества:**
|
||||
Наиболее понятный формат для больших языковых моделей (LLM).
|
||||
Сохраняет всю информацию.
|
||||
Явно указывает, что использовать.
|
||||
|
||||
**Недостатки:**
|
||||
Более длинный запрос.
|
||||
Более сложный шаблон.
|
||||
|
||||
## Реализованный подход
|
||||
|
||||
**Упрощенный формат "Сущность-Отношение-Атрибут"** - полностью заменяет старый формат на основе троек.
|
||||
|
||||
Новый подход был выбран, потому что:
|
||||
|
||||
1. **Отсутствие потери информации:** Оригинальные URI сохраняются корректно.
|
||||
2. **Более простая логика:** Не требуется преобразование, прямые запросы к словарям работают.
|
||||
3. **Безопасность пространств имен:** Обрабатывает несколько онтологий без конфликтов.
|
||||
4. **Семантическая корректность:** Сохраняет семантику RDF/OWL.
|
||||
|
||||
## Реализация завершена
|
||||
|
||||
### Что было создано:
|
||||
|
||||
1. **Новый шаблон запроса** (`prompts/ontology-extract-v2.txt`)
|
||||
✅ Четкие разделы: Типы сущностей, Отношения, Атрибуты.
|
||||
✅ Пример использования полных идентификаторов типов (`fo/Recipe`, `fo/has_ingredient`).
|
||||
✅ Инструкции по использованию точных идентификаторов из схемы.
|
||||
✅ Новый формат JSON с массивами сущностей/отношений/атрибутов.
|
||||
|
||||
2. **Нормализация сущностей** (`entity_normalizer.py`)
|
||||
✅ `normalize_entity_name()` - Преобразует имена в формат, безопасный для URI.
|
||||
✅ `normalize_type_identifier()` - Обрабатывает слеши в типах (`fo/Recipe` → `fo-recipe`).
|
||||
✅ `build_entity_uri()` - Создает уникальные URI, используя кортеж (имя, тип).
|
||||
✅ `EntityRegistry` - Отслеживает сущности для исключения дубликатов.
|
||||
|
||||
3. **JSON-парсер** (`simplified_parser.py`)
|
||||
✅ Парсит новый формат: `{entities: [...], relationships: [...], attributes: [...]}`
|
||||
✅ Поддерживает имена полей в формате kebab-case и snake_case.
|
||||
✅ Возвращает структурированные классы данных.
|
||||
✅ Корректная обработка ошибок с ведением журнала.
|
||||
|
||||
4. **Тройной преобразователь** (`triple_converter.py`)
|
||||
✅ `convert_entity()` - Автоматически генерирует тройки типа + метки.
|
||||
✅ `convert_relationship()` - Соединяет URI сущностей через свойства.
|
||||
✅ `convert_attribute()` - Добавляет литеральные значения.
|
||||
✅ Выполняет поиск полных URI из определений онтологии.
|
||||
|
||||
5. **Обновленный основной процессор** (`extract.py`)
|
||||
✅ Удален старый код извлечения на основе троек.
|
||||
✅ Добавлен метод `extract_with_simplified_format()`.
|
||||
✅ Теперь использует только новый упрощенный формат.
|
||||
✅ Вызывает запрос с идентификатором `extract-with-ontologies-v2`.
|
||||
|
||||
## Тестовые примеры
|
||||
|
||||
### Тест 1: Сохранение URI
|
||||
```python
|
||||
# Given ontology class
|
||||
classes = {"fo/Recipe": {"uri": "http://purl.org/ontology/fo/Recipe", ...}}
|
||||
|
||||
# When LLM returns
|
||||
llm_output = {"subject": "x", "predicate": "rdf:type", "object": "fo/Recipe"}
|
||||
|
||||
# Then expanded URI should be
|
||||
assert expanded == "http://purl.org/ontology/fo/Recipe"
|
||||
# Not: "https://trustgraph.ai/ontology/food#Recipe"
|
||||
```
|
||||
|
||||
### Тест 2: Конфликт между несколькими онтологиями
|
||||
```python
|
||||
# Given two ontologies
|
||||
ont1 = {"fo/Recipe": {...}}
|
||||
ont2 = {"cooking/Recipe": {...}}
|
||||
|
||||
# LLM should use full prefix to disambiguate
|
||||
llm_output = {"object": "fo/Recipe"} # Not just "Recipe"
|
||||
```
|
||||
|
||||
### Тест 3: Формат экземпляра сущности
|
||||
```python
|
||||
# Given prompt with food ontology
|
||||
# LLM should create instances like
|
||||
{"subject": "recipe:cornish-pasty"} # Namespace-style
|
||||
{"subject": "food:beef"} # Consistent prefix
|
||||
```
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
1. **Следует ли экземплярам сущностей использовать префиксы пространств имен?**
|
||||
Сейчас: `"recipe:cornish-pasty"` (произвольно)
|
||||
Альтернатива: Использовать префикс онтологии `"fo:cornish-pasty"`?
|
||||
Альтернатива: Без префикса, расширить в URI `"cornish-pasty"` → полный URI?
|
||||
|
||||
2. **Как обрабатывать область определения/область значений в запросе?**
|
||||
В настоящее время отображается: `(Recipe → Food)`
|
||||
Должно ли быть: `(fo/Recipe → fo/Food)`?
|
||||
|
||||
3. **Следует ли нам проверять ограничения области определения/области значений?**
|
||||
TODO комментарий в extract.py:470
|
||||
Это позволило бы выявлять больше ошибок, но было бы сложнее.
|
||||
|
||||
4. **Что касается обратных свойств и эквивалентностей?**
|
||||
В онтологии есть `owl:inverseOf`, `owl:equivalentClass`
|
||||
В настоящее время не используются при извлечении.
|
||||
Следует ли их использовать?
|
||||
|
||||
## Показатели успеха
|
||||
|
||||
✅ Отсутствие потери информации об URI (100% сохранение исходных URI).
|
||||
✅ Формат вывода LLM соответствует формату входных данных.
|
||||
✅ Отсутствие неоднозначных примеров в запросе.
|
||||
✅ Тесты проходят с использованием нескольких онтологий.
|
||||
✅ Улучшенное качество извлечения (измеряется процентом допустимых троек).
|
||||
|
||||
## Альтернативный подход: Упрощенный формат извлечения
|
||||
|
||||
### Философия
|
||||
|
||||
Вместо того, чтобы просить LLM понимать семантику RDF/OWL, попросите его делать то, что он умеет хорошо: **находить сущности и отношения в тексте**.
|
||||
|
||||
Пусть код занимается построением URI, преобразованием в RDF и формальностями семантической паутины.
|
||||
|
||||
### Пример: Классификация сущностей
|
||||
|
||||
**Исходный текст:**
|
||||
```
|
||||
Cornish pasty is a traditional British pastry filled with meat and vegetables.
|
||||
```
|
||||
|
||||
**Схема онтологии (показана LLM):**
|
||||
```markdown
|
||||
## Entity Types:
|
||||
- Recipe: A recipe is a combination of ingredients and a method
|
||||
- Food: A food is something that can be eaten
|
||||
- Ingredient: An ingredient combines a quantity and a food
|
||||
```
|
||||
|
||||
**Что возвращает большая языковая модель (простой JSON):**
|
||||
```json
|
||||
{
|
||||
"entities": [
|
||||
{
|
||||
"entity": "Cornish pasty",
|
||||
"type": "Recipe"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Что генерирует код (тройки RDF):**
|
||||
```python
|
||||
# 1. Normalize entity name + type to ID (type prevents collisions)
|
||||
entity_id = "recipe-cornish-pasty" # normalize("Cornish pasty", "Recipe")
|
||||
entity_uri = "https://trustgraph.ai/food/recipe-cornish-pasty"
|
||||
|
||||
# Note: Same name, different type = different URI
|
||||
# "Cornish pasty" (Recipe) → recipe-cornish-pasty
|
||||
# "Cornish pasty" (Food) → food-cornish-pasty
|
||||
|
||||
# 2. Generate triples
|
||||
triples = [
|
||||
# Type triple
|
||||
Triple(
|
||||
s=Value(value=entity_uri, is_uri=True),
|
||||
p=Value(value="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", is_uri=True),
|
||||
o=Value(value="http://purl.org/ontology/fo/Recipe", is_uri=True)
|
||||
),
|
||||
# Label triple (automatic)
|
||||
Triple(
|
||||
s=Value(value=entity_uri, is_uri=True),
|
||||
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
|
||||
o=Value(value="Cornish pasty", is_uri=False)
|
||||
)
|
||||
]
|
||||
```
|
||||
|
||||
### Преимущества
|
||||
|
||||
1. **LLM (большая языковая модель) не должна:**
|
||||
Понимать синтаксис URI
|
||||
Придумывать префиксы идентификаторов (`recipe:`, `ingredient:`)
|
||||
Знать о `rdf:type` или `rdfs:label`
|
||||
Конструировать идентификаторы семантической паутины
|
||||
|
||||
2. **LLM просто должна:**
|
||||
Находить сущности в тексте
|
||||
Сопоставлять их с классами онтологии
|
||||
Извлекать отношения и атрибуты
|
||||
|
||||
3. **Код обрабатывает:**
|
||||
Нормализацию и построение URI
|
||||
Генерацию триплетов RDF
|
||||
Автоматическое присвоение меток
|
||||
Управление пространствами имен
|
||||
|
||||
### Почему это работает лучше
|
||||
|
||||
**Более простой запрос** = меньше путаницы = меньше ошибок
|
||||
**Согласованные идентификаторы** = код контролирует правила нормализации
|
||||
**Автоматически сгенерированные метки** = нет отсутствующих триплетов rdfs:label
|
||||
**LLM фокусируется на извлечении** = на том, что она действительно хороша
|
||||
|
||||
### Пример: Отношения между сущностями
|
||||
|
||||
**Исходный текст:**
|
||||
```
|
||||
Cornish pasty is a traditional British pastry filled with beef and potatoes.
|
||||
```
|
||||
|
||||
**Схема онтологии (показана LLM):**
|
||||
```markdown
|
||||
## Entity Types:
|
||||
- Recipe: A recipe is a combination of ingredients and a method
|
||||
- Food: A food is something that can be eaten
|
||||
- Ingredient: An ingredient combines a quantity and a food
|
||||
|
||||
## Relationships:
|
||||
- has_ingredient: Relates a recipe to an ingredient it uses (Recipe → Ingredient)
|
||||
- food: Relates an ingredient to the food that is required (Ingredient → Food)
|
||||
```
|
||||
|
||||
**Что возвращает большая языковая модель (простой JSON):**
|
||||
```json
|
||||
{
|
||||
"entities": [
|
||||
{
|
||||
"entity": "Cornish pasty",
|
||||
"type": "Recipe"
|
||||
},
|
||||
{
|
||||
"entity": "beef",
|
||||
"type": "Food"
|
||||
},
|
||||
{
|
||||
"entity": "potatoes",
|
||||
"type": "Food"
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"subject": "Cornish pasty",
|
||||
"subject-type": "Recipe",
|
||||
"relation": "has_ingredient",
|
||||
"object": "beef",
|
||||
"object-type": "Food"
|
||||
},
|
||||
{
|
||||
"subject": "Cornish pasty",
|
||||
"subject-type": "Recipe",
|
||||
"relation": "has_ingredient",
|
||||
"object": "potatoes",
|
||||
"object-type": "Food"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Что генерирует код (тройки RDF):**
|
||||
```python
|
||||
# Normalize entity names to URIs
|
||||
cornish_pasty_uri = "https://trustgraph.ai/food/cornish-pasty"
|
||||
beef_uri = "https://trustgraph.ai/food/beef"
|
||||
potatoes_uri = "https://trustgraph.ai/food/potatoes"
|
||||
|
||||
# Look up relation URI from ontology
|
||||
has_ingredient_uri = "http://purl.org/ontology/fo/ingredients" # from fo/has_ingredient
|
||||
|
||||
triples = [
|
||||
# Entity type triples (as before)
|
||||
Triple(s=cornish_pasty_uri, p=rdf_type, o="http://purl.org/ontology/fo/Recipe"),
|
||||
Triple(s=cornish_pasty_uri, p=rdfs_label, o="Cornish pasty"),
|
||||
|
||||
Triple(s=beef_uri, p=rdf_type, o="http://purl.org/ontology/fo/Food"),
|
||||
Triple(s=beef_uri, p=rdfs_label, o="beef"),
|
||||
|
||||
Triple(s=potatoes_uri, p=rdf_type, o="http://purl.org/ontology/fo/Food"),
|
||||
Triple(s=potatoes_uri, p=rdfs_label, o="potatoes"),
|
||||
|
||||
# Relationship triples
|
||||
Triple(
|
||||
s=Value(value=cornish_pasty_uri, is_uri=True),
|
||||
p=Value(value=has_ingredient_uri, is_uri=True),
|
||||
o=Value(value=beef_uri, is_uri=True)
|
||||
),
|
||||
Triple(
|
||||
s=Value(value=cornish_pasty_uri, is_uri=True),
|
||||
p=Value(value=has_ingredient_uri, is_uri=True),
|
||||
o=Value(value=potatoes_uri, is_uri=True)
|
||||
)
|
||||
]
|
||||
```
|
||||
|
||||
**Основные моменты:**
|
||||
LLM возвращает имена сущностей на естественном языке: `"Cornish pasty"`, `"beef"`, `"potatoes"`
|
||||
LLM включает типы для устранения неоднозначности: `subject-type`, `object-type`
|
||||
LLM использует имя отношения из схемы: `"has_ingredient"`
|
||||
Код генерирует согласованные идентификаторы, используя (имя, тип): `("Cornish pasty", "Recipe")` → `recipe-cornish-pasty`
|
||||
Код ищет URI отношения в онтологии: `fo/has_ingredient` → полный URI
|
||||
Одна и та же (имя, тип) всегда получает один и тот же URI (дедупликация)
|
||||
|
||||
### Пример: Разрешение неоднозначности имени сущности
|
||||
|
||||
**Проблема:** Одно и то же имя может относиться к разным типам сущностей.
|
||||
|
||||
**Реальный пример:**
|
||||
```
|
||||
"Cornish pasty" can be:
|
||||
- A Recipe (instructions for making it)
|
||||
- A Food (the dish itself)
|
||||
```
|
||||
|
||||
**Как это обрабатывается:**
|
||||
|
||||
LLM возвращает оба элемента как отдельные сущности:
|
||||
```json
|
||||
{
|
||||
"entities": [
|
||||
{"entity": "Cornish pasty", "type": "Recipe"},
|
||||
{"entity": "Cornish pasty", "type": "Food"}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"subject": "Cornish pasty",
|
||||
"subject-type": "Recipe",
|
||||
"relation": "produces",
|
||||
"object": "Cornish pasty",
|
||||
"object-type": "Food"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Разрешение кода:**
|
||||
```python
|
||||
# Different types → different URIs
|
||||
recipe_uri = normalize("Cornish pasty", "Recipe")
|
||||
# → "https://trustgraph.ai/food/recipe-cornish-pasty"
|
||||
|
||||
food_uri = normalize("Cornish pasty", "Food")
|
||||
# → "https://trustgraph.ai/food/food-cornish-pasty"
|
||||
|
||||
# Relationship connects them correctly
|
||||
triple = Triple(
|
||||
s=recipe_uri, # The Recipe
|
||||
p="http://purl.org/ontology/fo/produces",
|
||||
o=food_uri # The Food
|
||||
)
|
||||
```
|
||||
|
||||
**Почему это работает:**
|
||||
Тип включен во ВСЕ ссылки (сущности, отношения, атрибуты).
|
||||
Код использует кортеж `(name, type)` в качестве ключа поиска.
|
||||
Отсутствие неоднозначности, отсутствие конфликтов.
|
||||
|
||||
### Пример: Атрибуты сущности
|
||||
|
||||
**Исходный текст:**
|
||||
```
|
||||
This Cornish pasty recipe serves 4-6 people and takes 45 minutes to prepare.
|
||||
```
|
||||
|
||||
**Схема онтологии (показана LLM):**
|
||||
```markdown
|
||||
## Entity Types:
|
||||
- Recipe: A recipe is a combination of ingredients and a method
|
||||
|
||||
## Attributes:
|
||||
- serves: Indicates what the recipe is intended to serve (Recipe → text)
|
||||
- preparation_time: Time needed to prepare the recipe (Recipe → text)
|
||||
```
|
||||
|
||||
**Что возвращает большая языковая модель (простой JSON):**
|
||||
```json
|
||||
{
|
||||
"entities": [
|
||||
{
|
||||
"entity": "Cornish pasty recipe",
|
||||
"type": "Recipe"
|
||||
}
|
||||
],
|
||||
"attributes": [
|
||||
{
|
||||
"entity": "Cornish pasty recipe",
|
||||
"entity-type": "Recipe",
|
||||
"attribute": "serves",
|
||||
"value": "4-6 people"
|
||||
},
|
||||
{
|
||||
"entity": "Cornish pasty recipe",
|
||||
"entity-type": "Recipe",
|
||||
"attribute": "preparation_time",
|
||||
"value": "45 minutes"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Что генерирует код (тройки RDF):**
|
||||
```python
|
||||
# Normalize entity name to URI
|
||||
recipe_uri = "https://trustgraph.ai/food/cornish-pasty-recipe"
|
||||
|
||||
# Look up attribute URIs from ontology
|
||||
serves_uri = "http://purl.org/ontology/fo/serves" # from fo/serves
|
||||
prep_time_uri = "http://purl.org/ontology/fo/preparation_time" # from fo/preparation_time
|
||||
|
||||
triples = [
|
||||
# Entity type triple
|
||||
Triple(
|
||||
s=Value(value=recipe_uri, is_uri=True),
|
||||
p=Value(value=rdf_type, is_uri=True),
|
||||
o=Value(value="http://purl.org/ontology/fo/Recipe", is_uri=True)
|
||||
),
|
||||
|
||||
# Label triple (automatic)
|
||||
Triple(
|
||||
s=Value(value=recipe_uri, is_uri=True),
|
||||
p=Value(value=rdfs_label, is_uri=True),
|
||||
o=Value(value="Cornish pasty recipe", is_uri=False)
|
||||
),
|
||||
|
||||
# Attribute triples (objects are literals, not URIs)
|
||||
Triple(
|
||||
s=Value(value=recipe_uri, is_uri=True),
|
||||
p=Value(value=serves_uri, is_uri=True),
|
||||
o=Value(value="4-6 people", is_uri=False) # Literal value!
|
||||
),
|
||||
Triple(
|
||||
s=Value(value=recipe_uri, is_uri=True),
|
||||
p=Value(value=prep_time_uri, is_uri=True),
|
||||
o=Value(value="45 minutes", is_uri=False) # Literal value!
|
||||
)
|
||||
]
|
||||
```
|
||||
|
||||
**Основные моменты:**
|
||||
LLM извлекает строковые значения: `"4-6 people"`, `"45 minutes"`
|
||||
LLM включает тип сущности для устранения неоднозначности: `entity-type`
|
||||
LLM использует имя атрибута из схемы: `"serves"`, `"preparation_time"`
|
||||
Код ищет URI атрибута из свойств типа данных онтологии
|
||||
**Объект является строковым значением** (`is_uri=False`), а не ссылкой URI
|
||||
Значения остаются в виде обычного текста, нормализация не требуется
|
||||
|
||||
**Различия с отношениями:**
|
||||
Отношения: и субъект, и объект являются сущностями (URI)
|
||||
Атрибуты: субъект является сущностью (URI), объект является строковым значением (строка/число)
|
||||
|
||||
### Полный пример: Сущности + Отношения + Атрибуты
|
||||
|
||||
**Исходный текст:**
|
||||
```
|
||||
Cornish pasty is a savory pastry filled with beef and potatoes.
|
||||
This recipe serves 4 people.
|
||||
```
|
||||
|
||||
**Что возвращает большая языковая модель:**
|
||||
```json
|
||||
{
|
||||
"entities": [
|
||||
{
|
||||
"entity": "Cornish pasty",
|
||||
"type": "Recipe"
|
||||
},
|
||||
{
|
||||
"entity": "beef",
|
||||
"type": "Food"
|
||||
},
|
||||
{
|
||||
"entity": "potatoes",
|
||||
"type": "Food"
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"subject": "Cornish pasty",
|
||||
"subject-type": "Recipe",
|
||||
"relation": "has_ingredient",
|
||||
"object": "beef",
|
||||
"object-type": "Food"
|
||||
},
|
||||
{
|
||||
"subject": "Cornish pasty",
|
||||
"subject-type": "Recipe",
|
||||
"relation": "has_ingredient",
|
||||
"object": "potatoes",
|
||||
"object-type": "Food"
|
||||
}
|
||||
],
|
||||
"attributes": [
|
||||
{
|
||||
"entity": "Cornish pasty",
|
||||
"entity-type": "Recipe",
|
||||
"attribute": "serves",
|
||||
"value": "4 people"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Результат:** Сгенерировано 11 тройных наборов RDF:
|
||||
3 тройных набора, определяющих тип сущности (rdf:type)
|
||||
3 тройных набора, определяющих метку сущности (rdfs:label) - автоматически
|
||||
2 тройных набора, описывающих отношения (has_ingredient)
|
||||
1 тройной набор, описывающий атрибут (serves)
|
||||
|
||||
Все это получено из простых, естественных текстовых извлечений с помощью LLM!
|
||||
|
||||
## Ссылки
|
||||
|
||||
Текущая реализация: `trustgraph-flow/trustgraph/extract/kg/ontology/extract.py`
|
||||
Шаблон запроса: `ontology-prompt.md`
|
||||
Тестовые примеры: `tests/unit/test_extract/test_ontology/`
|
||||
Пример онтологии: `e2e/test-data/food.ontology`
|
||||
160
docs/tech-specs/ru/ontology.ru.md
Normal file
160
docs/tech-specs/ru/ontology.ru.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает структуру и формат онтологий в системе TrustGraph. Онтологии предоставляют формальные модели знаний, определяя классы, свойства и отношения, обеспечивая возможности для рассуждения и вывода. Система использует формат конфигурации, вдохновленный OWL, который широко представляет концепции OWL/RDFS, оптимизированный для требований TrustGraph.
|
||||
|
||||
**Конвенция именования**: Для всех идентификаторов (ключей конфигурации, конечных точек API, названий модулей и т.д.) используется формат kebab-case, а не snake_case.
|
||||
|
||||
## Цели
|
||||
|
||||
- **Управление классами и свойствами**: Определение классов, похожих на OWL, с свойствами, доменами, диапазонами и ограничениями типов.
|
||||
- **Поддержка богатых семантических возможностей**: Обеспечение полных возможностей RDFS/OWL, включая метки, поддержку нескольких языков и формальные ограничения.
|
||||
- **Поддержка нескольких онтологий**: Разрешение одновременного существования и взаимодействия нескольких онтологий.
|
||||
- **Валидация и рассуждение**: Обеспечение соответствия онтологий стандартам, похожим на OWL, с проверкой согласованности и поддержкой вывода.
|
||||
- **Совместимость со стандартами**: Поддержка импорта/экспорта в стандартных форматах (Turtle, RDF/XML, OWL/XML), сохраняя оптимизацию.
|
||||
|
||||
## Предыстория
|
||||
|
||||
TrustGraph хранит онтологии как элементы конфигурации в гибкой системе ключ-значение. Хотя формат вдохновлен OWL (Web Ontology Language), он оптимизирован для конкретных случаев использования TrustGraph и не строго соответствует всем спецификациям OWL.
|
||||
|
||||
Онтологии в TrustGraph обеспечивают:
|
||||
- Определение формальных типов объектов и их свойств.
|
||||
- Спецификацию доменов, диапазонов и ограничений типов свойств.
|
||||
- Логическое рассуждение и вывод.
|
||||
- Сложные отношения и ограничения кардинальности.
|
||||
- Поддержку нескольких языков для международного использования.
|
||||
|
||||
## Структура Онтологии
|
||||
|
||||
### Хранение конфигурации
|
||||
|
||||
Онтологии хранятся как элементы конфигурации с следующим шаблоном:
|
||||
- **Тип**: `ontology`
|
||||
- **Ключ**: Уникальный идентификатор онтологии (например, `natural-world`, `domain-model`)
|
||||
- **Значение**: Полная онтология в формате JSON
|
||||
|
||||
### JSON структура
|
||||
|
||||
Формат JSON для онтологии состоит из четырех основных разделов:
|
||||
|
||||
#### 1. Метаданные
|
||||
|
||||
Содержит административную и описательную информацию об онтологии:
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"name": "The natural world",
|
||||
"description": "Ontology covering the natural order",
|
||||
"version": "1.0.0",
|
||||
"created": "2025-09-20T12:07:37.068Z",
|
||||
"modified": "2025-09-20T12:12:20.725Z",
|
||||
"creator": "current-user",
|
||||
"namespace": "http://trustgraph.ai/ontologies/natural-world",
|
||||
"imports": ["http://www.w3.org/2002/07/owl#"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Поля:**
|
||||
- `name`: Человекочитаемое имя онтологии
|
||||
- `description`: Краткое описание цели онтологии
|
||||
- `version`: Семантическая версия
|
||||
- `created`: Дата создания в формате ISO 8601
|
||||
- `modified`: Дата последнего изменения в формате ISO 8601
|
||||
- `creator`: Идентификатор пользователя/системы, создавшей онтологию
|
||||
- `namespace`: Базовый URI для элементов онтологии
|
||||
- `imports`: Массив URI импортируемых онтологий
|
||||
|
||||
#### 2. Классы
|
||||
|
||||
Определяет типы объектов и их иерархические отношения:
|
||||
|
||||
```json
|
||||
{
|
||||
"classes": {
|
||||
"animal": {
|
||||
"uri": "http://trustgraph.ai/ontologies/natural-world#animal",
|
||||
"type": "owl:Class",
|
||||
"rdfs:label": [{"value": "Animal", "lang": "en"}],
|
||||
"rdfs:comment": "An animal",
|
||||
"rdfs:subClassOf": "lifeform",
|
||||
"owl:equivalentClass": ["creature"],
|
||||
"owl:disjointWith": ["plant"],
|
||||
"dcterms:identifier": "ANI-001"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Поддерживаемые свойства:**
|
||||
- `uri`: Полный URI класса
|
||||
- `type`: Всегда `"owl:Class"`
|
||||
- `rdfs:label`: Массив меток с указанием языка
|
||||
- `rdfs:comment`: Описание класса
|
||||
- `rdfs:subClassOf`: Идентификатор родительского класса (наследование)
|
||||
- `owl:equivalentClass`: Массив идентификаторов эквивалентных классов
|
||||
- `owl:disjointWith`: Массив идентификаторов классов, отличных друг от друга
|
||||
- `dcterms:identifier`: Опциональный внешний идентификатор
|
||||
|
||||
#### 3. Объектные свойства
|
||||
|
||||
Свойства, связывающие экземпляры между собой:
|
||||
|
||||
```json
|
||||
{
|
||||
"objectProperties": {}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Свойства данных
|
||||
|
||||
```json
|
||||
{
|
||||
"datatypeProperties": {
|
||||
"number-of-legs": {
|
||||
"uri": "http://trustgraph.ai/ontologies/natural-world#number-of-legs",
|
||||
"type": "owl:DatatypeProperty",
|
||||
"rdfs:label": [{"value": "number-of-legs", "lang": "en"}],
|
||||
"rdfs:comment": "Count of number of legs of the animal",
|
||||
"rdfs:range": "xsd:nonNegativeInteger",
|
||||
"rdfs:domain": "animal"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Правила валидации
|
||||
|
||||
### Структурная валидация
|
||||
|
||||
1. **Уникальность URI**: Все URI должны соответствовать формату `{namespace}#{identifier}`
|
||||
2. **Иерархия классов**: Не должно быть циклической иерархии в `rdfs:subClassOf`
|
||||
3. **Области/Диапазоны свойств**: Должны ссылаться на существующие классы или допустимые типы XSD
|
||||
4. **Отличные классы**: Не могут быть подклассами друг друга
|
||||
5. **Обратные свойства**: Должны быть двусторонними, если указаны
|
||||
|
||||
### Семантическая валидация
|
||||
|
||||
1. **Уникальные идентификаторы**: Идентификаторы классов и свойств должны быть уникальными в онтологии
|
||||
2. **Языковые теги**: Должны соответствовать формату BCP 47
|
||||
3. **Ограничения кардинальности**: `minCardinality` ≤ `maxCardinality` при указании обоих
|
||||
4. **Функциональные свойства**: Не должны иметь `maxCardinality` > 1
|
||||
|
||||
## Поддержка импорта/экспорта
|
||||
|
||||
Хотя внутренний формат - JSON, система поддерживает преобразование в/из стандартных форматов онтологий:
|
||||
|
||||
- **Turtle (.ttl)** - компактная RDF-сериализация
|
||||
- **RDF/XML (.rdf, .owl)** - стандартный формат W3C
|
||||
- **OWL/XML (.owx)** - XML-формат OWL
|
||||
- **JSON-LD (.jsonld)** - JSON для Linked Data
|
||||
1075
docs/tech-specs/ru/ontorag.ru.md
Normal file
1075
docs/tech-specs/ru/ontorag.ru.md
Normal file
File diff suppressed because it is too large
Load diff
239
docs/tech-specs/ru/openapi-spec.ru.md
Normal file
239
docs/tech-specs/ru/openapi-spec.ru.md
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Спецификация OpenAPI - Техническая спецификация"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Спецификация OpenAPI - Техническая спецификация
|
||||
|
||||
> **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.
|
||||
|
||||
## Цель
|
||||
|
||||
Создать всеобъемлющую, модульную спецификацию OpenAPI 3.1 для шлюза REST API TrustGraph, которая:
|
||||
Документирует все конечные точки REST
|
||||
Использует внешние `$ref` для модульности и удобства обслуживания
|
||||
Непосредственно соответствует коду переводчика сообщений
|
||||
Предоставляет точные схемы запросов/ответов
|
||||
|
||||
## Источник истины
|
||||
|
||||
API определяется следующим:
|
||||
**Переводчики сообщений**: `trustgraph-base/trustgraph/messaging/translators/*.py`
|
||||
**Менеджер диспетчера**: `trustgraph-flow/trustgraph/gateway/dispatch/manager.py`
|
||||
**Менеджер конечных точек**: `trustgraph-flow/trustgraph/gateway/endpoint/manager.py`
|
||||
|
||||
## Структура каталогов
|
||||
|
||||
```
|
||||
openapi/
|
||||
├── openapi.yaml # Main entry point
|
||||
├── paths/
|
||||
│ ├── config.yaml # Global services
|
||||
│ ├── flow.yaml
|
||||
│ ├── librarian.yaml
|
||||
│ ├── knowledge.yaml
|
||||
│ ├── collection-management.yaml
|
||||
│ ├── flow-services/ # Flow-hosted services
|
||||
│ │ ├── agent.yaml
|
||||
│ │ ├── document-rag.yaml
|
||||
│ │ ├── graph-rag.yaml
|
||||
│ │ ├── text-completion.yaml
|
||||
│ │ ├── prompt.yaml
|
||||
│ │ ├── embeddings.yaml
|
||||
│ │ ├── mcp-tool.yaml
|
||||
│ │ ├── triples.yaml
|
||||
│ │ ├── objects.yaml
|
||||
│ │ ├── nlp-query.yaml
|
||||
│ │ ├── structured-query.yaml
|
||||
│ │ ├── structured-diag.yaml
|
||||
│ │ ├── graph-embeddings.yaml
|
||||
│ │ ├── document-embeddings.yaml
|
||||
│ │ ├── text-load.yaml
|
||||
│ │ └── document-load.yaml
|
||||
│ ├── import-export/
|
||||
│ │ ├── core-import.yaml
|
||||
│ │ ├── core-export.yaml
|
||||
│ │ └── flow-import-export.yaml # WebSocket import/export
|
||||
│ ├── websocket.yaml
|
||||
│ └── metrics.yaml
|
||||
├── components/
|
||||
│ ├── schemas/
|
||||
│ │ ├── config/
|
||||
│ │ ├── flow/
|
||||
│ │ ├── librarian/
|
||||
│ │ ├── knowledge/
|
||||
│ │ ├── collection/
|
||||
│ │ ├── ai-services/
|
||||
│ │ ├── common/
|
||||
│ │ └── errors/
|
||||
│ ├── parameters/
|
||||
│ ├── responses/
|
||||
│ └── examples/
|
||||
└── security/
|
||||
└── bearerAuth.yaml
|
||||
```
|
||||
|
||||
## Отображение сервисов
|
||||
|
||||
### Глобальные сервисы (`/api/v1/{kind}`)
|
||||
`config` - Управление конфигурацией
|
||||
`flow` - Жизненный цикл потока
|
||||
`librarian` - Библиотека документов
|
||||
`knowledge` - Базы знаний
|
||||
`collection-management` - Метаданные коллекции
|
||||
|
||||
### Сервисы, размещенные в потоке (`/api/v1/flow/{flow}/service/{kind}`)
|
||||
|
||||
**Запрос/Ответ:**
|
||||
`agent`, `text-completion`, `prompt`, `mcp-tool`
|
||||
`graph-rag`, `document-rag`
|
||||
`embeddings`, `graph-embeddings`, `document-embeddings`
|
||||
`triples`, `objects`, `nlp-query`, `structured-query`, `structured-diag`
|
||||
|
||||
**Отправка и получение (Fire-and-Forget):**
|
||||
`text-load`, `document-load`
|
||||
|
||||
### Импорт/Экспорт
|
||||
`/api/v1/import-core` (POST)
|
||||
`/api/v1/export-core` (GET)
|
||||
`/api/v1/flow/{flow}/import/{kind}` (WebSocket)
|
||||
`/api/v1/flow/{flow}/export/{kind}` (WebSocket)
|
||||
|
||||
### Другое
|
||||
`/api/v1/socket` (Множественный WebSocket)
|
||||
`/api/metrics` (Prometheus)
|
||||
|
||||
## Подход
|
||||
|
||||
### Фаза 1: Настройка
|
||||
1. Создание структуры каталогов
|
||||
2. Создание основного файла `openapi.yaml` с метаданными, серверами, безопасностью
|
||||
3. Создание многократно используемых компонентов (ошибок, общих параметров, схем безопасности)
|
||||
|
||||
### Фаза 2: Общие схемы
|
||||
Создание общих схем, используемых во всех сервисах:
|
||||
`RdfValue`, `Triple` - RDF/структуры троек
|
||||
`ErrorObject` - Ответ об ошибке
|
||||
`DocumentMetadata`, `ProcessingMetadata` - Структуры метаданных
|
||||
Общие параметры: `FlowId`, `User`, `Collection`
|
||||
|
||||
### Фаза 3: Глобальные сервисы
|
||||
Для каждого глобального сервиса (конфигурация, поток, библиотека, знания, управление коллекцией):
|
||||
1. Создание файла пути в `paths/`
|
||||
2. Создание схемы запроса в `components/schemas/{service}/`
|
||||
3. Создание схемы ответа
|
||||
4. Добавление примеров
|
||||
5. Ссылка из основного файла `openapi.yaml`
|
||||
|
||||
### Фаза 4: Сервисы, размещенные в потоке
|
||||
Для каждого сервиса, размещенного в потоке:
|
||||
1. Создание файла пути в `paths/flow-services/`
|
||||
2. Создание схем запроса/ответа в `components/schemas/ai-services/`
|
||||
3. Добавление документации о флаге потоковой передачи, где это применимо
|
||||
4. Ссылка из основного файла `openapi.yaml`
|
||||
|
||||
### Фаза 5: Импорт/Экспорт и WebSocket
|
||||
1. Документирование основных конечных точек импорта/экспорта
|
||||
2. Документирование шаблонов протокола WebSocket
|
||||
3. Документирование конечных точек импорта/экспорта WebSocket на уровне потока
|
||||
|
||||
### Фаза 6: Валидация
|
||||
1. Проверка с помощью инструментов валидации OpenAPI
|
||||
2. Тестирование с помощью Swagger UI
|
||||
3. Убедитесь, что все переводчики охвачены
|
||||
|
||||
## Соглашение об именовании полей
|
||||
|
||||
Все поля JSON используют **kebab-case**:
|
||||
`flow-id`, `blueprint-name`, `doc-limit`, `entity-limit` и т. д.
|
||||
|
||||
## Создание файлов схем
|
||||
|
||||
Для каждого переводчика в `trustgraph-base/trustgraph/messaging/translators/`:
|
||||
|
||||
1. **Прочитать метод переводчика `to_pulsar()`** - Определяет схему запроса
|
||||
2. **Прочитать метод переводчика `from_pulsar()`** - Определяет схему ответа
|
||||
3. **Извлечь имена и типы полей**
|
||||
4. **Создать схему OpenAPI** с:
|
||||
Имена полей (kebab-case)
|
||||
Типы (string, integer, boolean, object, array)
|
||||
Обязательные поля
|
||||
Значения по умолчанию
|
||||
Описания
|
||||
|
||||
### Пример процесса сопоставления
|
||||
|
||||
```python
|
||||
# From retrieval.py DocumentRagRequestTranslator
|
||||
def to_pulsar(self, data: Dict[str, Any]) -> DocumentRagQuery:
|
||||
return DocumentRagQuery(
|
||||
query=data["query"], # required string
|
||||
user=data.get("user", "trustgraph"), # optional string, default "trustgraph"
|
||||
collection=data.get("collection", "default"), # optional string, default "default"
|
||||
doc_limit=int(data.get("doc-limit", 20)), # optional integer, default 20
|
||||
streaming=data.get("streaming", False) # optional boolean, default false
|
||||
)
|
||||
```
|
||||
|
||||
Соответствует:
|
||||
|
||||
```yaml
|
||||
# components/schemas/ai-services/DocumentRagRequest.yaml
|
||||
type: object
|
||||
required:
|
||||
- query
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: Search query
|
||||
user:
|
||||
type: string
|
||||
default: trustgraph
|
||||
collection:
|
||||
type: string
|
||||
default: default
|
||||
doc-limit:
|
||||
type: integer
|
||||
default: 20
|
||||
description: Maximum number of documents to retrieve
|
||||
streaming:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Enable streaming responses
|
||||
```
|
||||
|
||||
## Потоковые ответы
|
||||
|
||||
Сервисы, поддерживающие потоковую передачу, возвращают несколько ответов с флагом `end_of_stream`:
|
||||
`agent`, `text-completion`, `prompt`
|
||||
`document-rag`, `graph-rag`
|
||||
|
||||
Опишите эту схему в схеме ответа каждого сервиса.
|
||||
|
||||
## Ответы об ошибках
|
||||
|
||||
Все сервисы могут возвращать:
|
||||
```yaml
|
||||
error:
|
||||
oneOf:
|
||||
- type: string
|
||||
- $ref: '#/components/schemas/ErrorObject'
|
||||
```
|
||||
|
||||
Где `ErrorObject` находится:
|
||||
```yaml
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
```
|
||||
|
||||
## Ссылки
|
||||
|
||||
Переводчики: `trustgraph-base/trustgraph/messaging/translators/`
|
||||
Отображение диспетчера: `trustgraph-flow/trustgraph/gateway/dispatch/manager.py`
|
||||
Маршрутизация конечных точек: `trustgraph-flow/trustgraph/gateway/endpoint/manager.py`
|
||||
Обзор сервисов: `API_SERVICES_SUMMARY.md`
|
||||
965
docs/tech-specs/ru/pubsub.ru.md
Normal file
965
docs/tech-specs/ru/pubsub.ru.md
Normal file
|
|
@ -0,0 +1,965 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Инфраструктура Pub/Sub"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Инфраструктура Pub/Sub
|
||||
|
||||
> **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 и инфраструктурой pub/sub. В настоящее время система жестко запрограммирована на использование Apache Pulsar. Этот анализ определяет все точки интеграции для будущей рефакторизации в сторону конфигурируемой абстракции pub/sub.
|
||||
|
||||
## Текущее состояние: точки интеграции Pulsar
|
||||
|
||||
### 1. Прямое использование клиента Pulsar
|
||||
|
||||
**Местоположение:** `trustgraph-flow/trustgraph/gateway/service.py`
|
||||
|
||||
API-шлюз напрямую импортирует и создает экземпляр клиента Pulsar:
|
||||
|
||||
**Строка 20:** `import pulsar`
|
||||
**Строки 54-61:** Прямая инициализация `pulsar.Client()` с необязательными параметрами `pulsar.AuthenticationToken()`
|
||||
**Строки 33-35:** Конфигурация хоста Pulsar по умолчанию из переменных окружения
|
||||
**Строки 178-192:** Аргументы командной строки для `--pulsar-host`, `--pulsar-api-key` и `--pulsar-listener`
|
||||
**Строки 78, 124:** Передача `pulsar_client` в `ConfigReceiver` и `DispatcherManager`
|
||||
|
||||
Это единственное место, где напрямую создается экземпляр клиента Pulsar за пределами слоя абстракции.
|
||||
|
||||
### 2. Базовая платформа процессоров
|
||||
|
||||
**Местоположение:** `trustgraph-base/trustgraph/base/async_processor.py`
|
||||
|
||||
Базовый класс для всех процессоров обеспечивает подключение к Pulsar:
|
||||
|
||||
**Строка 9:** `import _pulsar` (для обработки исключений)
|
||||
**Строка 18:** `from . pubsub import PulsarClient`
|
||||
**Строка 38:** Создание `pulsar_client_object = PulsarClient(**params)`
|
||||
**Строки 104-108:** Свойства, предоставляющие доступ к `pulsar_host` и `pulsar_client`
|
||||
**Строка 250:** Статический метод `add_args()` вызывает `PulsarClient.add_args(parser)` для аргументов командной строки
|
||||
**Строки 223-225:** Обработка исключений для `_pulsar.Interrupted`
|
||||
|
||||
Все процессоры наследуются от `AsyncProcessor`, что делает это центральной точкой интеграции.
|
||||
|
||||
### 3. Абстракция потребителя
|
||||
|
||||
**Местоположение:** `trustgraph-base/trustgraph/base/consumer.py`
|
||||
|
||||
Потребляет сообщения из очередей и вызывает функции обработчиков:
|
||||
|
||||
**Импорты Pulsar:**
|
||||
**Строка 12:** `from pulsar.schema import JsonSchema`
|
||||
**Строка 13:** `import pulsar`
|
||||
**Строка 14:** `import _pulsar`
|
||||
|
||||
**Использование, специфичное для Pulsar:**
|
||||
**Строки 100, 102:** `pulsar.InitialPosition.Earliest` / `pulsar.InitialPosition.Latest`
|
||||
**Строка 108:** Обертка `JsonSchema(self.schema)`
|
||||
**Строка 110:** `pulsar.ConsumerType.Shared`
|
||||
**Строки 104-111:** `self.client.subscribe()` с параметрами, специфичными для Pulsar
|
||||
**Строки 143, 150, 65:** Методы `consumer.unsubscribe()` и `consumer.close()`
|
||||
**Строка 162:** Исключение `_pulsar.Timeout`
|
||||
**Строки 182, 205, 232:** `consumer.acknowledge()` / `consumer.negative_acknowledge()`
|
||||
|
||||
**Спецификационный файл:** `trustgraph-base/trustgraph/base/consumer_spec.py`
|
||||
**Строка 22:** Ссылка на `processor.pulsar_client`
|
||||
|
||||
### 4. Абстракция производителя
|
||||
|
||||
**Местоположение:** `trustgraph-base/trustgraph/base/producer.py`
|
||||
|
||||
Отправляет сообщения в очереди:
|
||||
|
||||
**Импорты Pulsar:**
|
||||
**Строка 2:** `from pulsar.schema import JsonSchema`
|
||||
|
||||
**Использование, специфичное для Pulsar:**
|
||||
**Строка 49:** Обертка `JsonSchema(self.schema)`
|
||||
**Строки 47-51:** `self.client.create_producer()` с параметрами, специфичными для Pulsar (тема, схема, включение фрагментации)
|
||||
**Строки 31, 76:** Метод `producer.close()`
|
||||
**Строки 64-65:** `producer.send()` с сообщением и свойствами
|
||||
|
||||
**Спецификационный файл:** `trustgraph-base/trustgraph/base/producer_spec.py`
|
||||
**Строка 18:** Ссылка на `processor.pulsar_client`
|
||||
|
||||
### 5. Абстракция издателя
|
||||
|
||||
**Местоположение:** `trustgraph-base/trustgraph/base/publisher.py`
|
||||
|
||||
Асинхронная публикация сообщений с буферизацией очереди:
|
||||
|
||||
**Импорты Pulsar:**
|
||||
**Строка 2:** `from pulsar.schema import JsonSchema`
|
||||
**Строка 6:** `import pulsar`
|
||||
|
||||
**Использование, специфичное для Pulsar:**
|
||||
**Строка 52:** Обертка `JsonSchema(self.schema)`
|
||||
**Строки 50-54:** `self.client.create_producer()` с параметрами, специфичными для Pulsar
|
||||
**Строки 101, 103:** `producer.send()` с сообщением и необязательными свойствами
|
||||
**Строки 106-107:** Методы `producer.flush()` и `producer.close()`
|
||||
|
||||
### 6. Абстракция подписчика
|
||||
|
||||
**Местоположение:** `trustgraph-base/trustgraph/base/subscriber.py`
|
||||
|
||||
Предоставляет распределение сообщений для нескольких получателей из очередей:
|
||||
|
||||
**Импорт Pulsar:**
|
||||
**Строка 6:** `from pulsar.schema import JsonSchema`
|
||||
**Строка 8:** `import _pulsar`
|
||||
|
||||
**Использование, специфичное для Pulsar:**
|
||||
**Строка 55:** `JsonSchema(self.schema)` wrapper
|
||||
**Строка 57:** `self.client.subscribe(**subscribe_args)`
|
||||
**Строки 101, 136, 160, 167-172:** Исключения Pulsar: `_pulsar.Timeout`, `_pulsar.InvalidConfiguration`, `_pulsar.AlreadyClosed`
|
||||
**Строки 159, 166, 170:** Методы потребителя: `negative_acknowledge()`, `unsubscribe()`, `close()`
|
||||
**Строки 247, 251:** Подтверждение сообщений: `acknowledge()`, `negative_acknowledge()`
|
||||
|
||||
**Файл спецификации:** `trustgraph-base/trustgraph/base/subscriber_spec.py`
|
||||
**Строка 19:** Ссылается на `processor.pulsar_client`
|
||||
|
||||
### 7. Система схем (Heart of Darkness)
|
||||
|
||||
**Расположение:** `trustgraph-base/trustgraph/schema/`
|
||||
|
||||
Каждая схема сообщения в системе определяется с использованием фреймворка схем Pulsar.
|
||||
|
||||
**Основные примитивы:** `schema/core/primitives.py`
|
||||
**Строка 2:** `from pulsar.schema import Record, String, Boolean, Array, Integer`
|
||||
Все схемы наследуются от базового класса Pulsar `Record`
|
||||
Все типы полей являются типами Pulsar: `String()`, `Integer()`, `Boolean()`, `Array()`, `Map()`, `Double()`
|
||||
|
||||
**Примеры схем:**
|
||||
`schema/services/llm.py` (Строка 2): `from pulsar.schema import Record, String, Array, Double, Integer, Boolean`
|
||||
`schema/services/config.py` (Строка 2): `from pulsar.schema import Record, Bytes, String, Boolean, Array, Map, Integer`
|
||||
|
||||
**Именование тем:** `schema/core/topic.py`
|
||||
**Строки 2-3:** Формат темы: `{kind}://{tenant}/{namespace}/{topic}`
|
||||
Эта структура URI специфична для Pulsar (например, `persistent://tg/flow/config`)
|
||||
|
||||
**Влияние:**
|
||||
Все определения сообщений запроса/ответа во всем коде используют схемы Pulsar
|
||||
Это включает в себя сервисы для: config, flow, llm, prompt, query, storage, agent, collection, diagnosis, library, lookup, nlp_query, objects_query, retrieval, structured_query
|
||||
Определения схем импортируются и используются во всех процессорах и сервисах.
|
||||
|
||||
## Краткое описание
|
||||
|
||||
### Зависимости Pulsar по категориям
|
||||
|
||||
1. **Инициализация клиента:**
|
||||
Прямая: `gateway/service.py`
|
||||
Абстрактная: `async_processor.py` → `pubsub.py` (PulsarClient)
|
||||
|
||||
2. **Транспортировка сообщений:**
|
||||
Потребитель: `consumer.py`, `consumer_spec.py`
|
||||
Издатель: `producer.py`, `producer_spec.py`
|
||||
Публикатор: `publisher.py`
|
||||
Подписчик: `subscriber.py`, `subscriber_spec.py`
|
||||
|
||||
3. **Система схем:**
|
||||
Базовые типы: `schema/core/primitives.py`
|
||||
Все схемы сервисов: `schema/services/*.py`
|
||||
Именование тем: `schema/core/topic.py`
|
||||
|
||||
4. **Требуются концепции, специфичные для Pulsar:**
|
||||
Сообщения на основе тем
|
||||
Система схем (Record, типы полей)
|
||||
Общие подписки
|
||||
Подтверждение сообщений (положительное/отрицательное)
|
||||
Позиционирование потребителя (самое начало/последнее)
|
||||
Свойства сообщений
|
||||
Начальные позиции и типы потребителей
|
||||
Поддержка разбиения на части
|
||||
Постоянные и непостоянные темы
|
||||
|
||||
### Проблемы рефакторинга
|
||||
|
||||
Хорошая новость: слой абстракции (Consumer, Producer, Publisher, Subscriber) обеспечивает четкую инкапсуляцию большинства взаимодействий с Pulsar.
|
||||
|
||||
Проблемы:
|
||||
1. **Всепроникающая система схем:** Каждое определение сообщения использует `pulsar.schema.Record` и типы Pulsar.
|
||||
2. **Перечисления, специфичные для Pulsar:** `InitialPosition`, `ConsumerType`
|
||||
3. **Исключения Pulsar:** `_pulsar.Timeout`, `_pulsar.Interrupted`, `_pulsar.InvalidConfiguration`, `_pulsar.AlreadyClosed`
|
||||
4. **Сигнатуры методов:** `acknowledge()`, `negative_acknowledge()`, `subscribe()`, `create_producer()` и т.д.
|
||||
5. **Формат URI темы:** Структура Pulsar `kind://tenant/namespace/topic`
|
||||
|
||||
### Следующие шаги
|
||||
|
||||
Чтобы сделать инфраструктуру публикации/подписки настраиваемой, нам нужно:
|
||||
|
||||
1. Создать интерфейс абстракции для системы клиента/схем.
|
||||
2. Абстрагировать перечисления и исключения, специфичные для Pulsar.
|
||||
3. Создать обертки для схем или альтернативные определения схем.
|
||||
4. Реализовать интерфейс как для Pulsar, так и для альтернативных систем (Kafka, RabbitMQ, Redis Streams и т.д.).
|
||||
5. Обновить `pubsub.py`, чтобы он был настраиваемым и поддерживал несколько бэкендов.
|
||||
6. Предоставить путь миграции для существующих развертываний.
|
||||
|
||||
## Предварительный черновик подхода 1: Шаблон адаптера со слоем перевода схем
|
||||
|
||||
### Ключевое понимание
|
||||
**Система схем** является наиболее глубокой точкой интеграции - от нее зависит все остальное. Мы должны решить эту проблему в первую очередь, иначе нам придется переписывать весь код.
|
||||
|
||||
### Стратегия: Минимальное нарушение с помощью адаптеров
|
||||
|
||||
**1. Сохраняйте схемы Pulsar в качестве внутреннего представления**
|
||||
Не переписывайте все определения схем.
|
||||
Схемы остаются `pulsar.schema.Record` во внутреннем представлении.
|
||||
Используйте адаптеры для преобразования данных на границе между нашим кодом и бэкендом pub/sub.
|
||||
|
||||
**2. Создайте абстрактный слой pub/sub:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Existing Code (unchanged) │
|
||||
│ - Uses Pulsar schemas internally │
|
||||
│ - Consumer/Producer/Publisher │
|
||||
└──────────────┬──────────────────────┘
|
||||
│
|
||||
┌──────────────┴──────────────────────┐
|
||||
│ PubSubFactory (configurable) │
|
||||
│ - Creates backend-specific client │
|
||||
└──────────────┬──────────────────────┘
|
||||
│
|
||||
┌──────┴──────┐
|
||||
│ │
|
||||
┌───────▼─────┐ ┌────▼─────────┐
|
||||
│ PulsarAdapter│ │ KafkaAdapter │ etc...
|
||||
│ (passthrough)│ │ (translates) │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
**3. Определение абстрактных интерфейсов:**
|
||||
`PubSubClient` - клиентское подключение
|
||||
`PubSubProducer` - отправка сообщений
|
||||
`PubSubConsumer` - получение сообщений
|
||||
`SchemaAdapter` - преобразование схем Pulsar в/из JSON или форматы, специфичные для бэкенда
|
||||
|
||||
**4. Детали реализации:**
|
||||
|
||||
Для **адаптера Pulsar**: Практически без изменений, минимальное преобразование.
|
||||
|
||||
Для **других бэкендов** (Kafka, RabbitMQ и т.д.):
|
||||
Сериализация объектов Pulsar Record в JSON/байты.
|
||||
Отображение понятий, таких как:
|
||||
`InitialPosition.Earliest/Latest` → auto.offset.reset в Kafka
|
||||
`acknowledge()` → commit в Kafka
|
||||
`negative_acknowledge()` → Паттерн повторной отправки или DLQ (Dead Letter Queue)
|
||||
URI тем → Имена тем, специфичные для бэкенда.
|
||||
|
||||
### Анализ
|
||||
|
||||
**Преимущества:**
|
||||
✅ Минимальные изменения существующего кода.
|
||||
✅ Схемы остаются без изменений (без необходимости масштабной переработки).
|
||||
✅ Постепенный путь миграции.
|
||||
✅ Пользователи Pulsar не заметят разницы.
|
||||
✅ Новые бэкенды добавляются через адаптеры.
|
||||
|
||||
**Недостатки:**
|
||||
⚠️ Все еще присутствует зависимость от Pulsar (для определений схем).
|
||||
⚠️ Некоторые несовместимости при преобразовании понятий.
|
||||
|
||||
### Альтернативное решение
|
||||
|
||||
Создать **систему схем TrustGraph**, которая является агностической к pub/sub (используя dataclasses или Pydantic), а затем генерировать схемы Pulsar/Kafka/и т.д. из нее. Это требует переписывания каждого файла схемы и может привести к несовместимым изменениям.
|
||||
|
||||
### Рекомендации для версии 1
|
||||
|
||||
Начните с **подхода с использованием адаптеров**, потому что:
|
||||
1. Это практичный подход, который работает с существующим кодом.
|
||||
2. Подтверждает концепцию с минимальным риском.
|
||||
3. Может быть усовершенствован до нативной системы схем в будущем, если это необходимо.
|
||||
4. Управление конфигурацией: одна переменная окружения переключает бэкенды.
|
||||
|
||||
## Подход, версия 2: Система схем, независимая от бэкенда, с использованием dataclasses
|
||||
|
||||
### Основная концепция
|
||||
|
||||
Используйте Python **dataclasses** в качестве нейтрального формата определения схемы. Каждый бэкенд pub/sub предоставляет свой собственный механизм сериализации/десериализации для dataclasses, что устраняет необходимость сохранения схем Pulsar в кодовой базе.
|
||||
|
||||
### Полиморфизм схем на уровне фабрики
|
||||
|
||||
Вместо преобразования схем Pulsar, **каждый бэкенд предоставляет собственную обработку схем**, которая работает со стандартными Python dataclasses.
|
||||
|
||||
### Поток публикации
|
||||
|
||||
```python
|
||||
# 1. Get the configured backend from factory
|
||||
pubsub = get_pubsub() # Returns PulsarBackend, MQTTBackend, etc.
|
||||
|
||||
# 2. Get schema class from the backend
|
||||
# (Can be imported directly - backend-agnostic)
|
||||
from trustgraph.schema.services.llm import TextCompletionRequest
|
||||
|
||||
# 3. Create a producer/publisher for a specific topic
|
||||
producer = pubsub.create_producer(
|
||||
topic="text-completion-requests",
|
||||
schema=TextCompletionRequest # Tells backend what schema to use
|
||||
)
|
||||
|
||||
# 4. Create message instances (same API regardless of backend)
|
||||
request = TextCompletionRequest(
|
||||
system="You are helpful",
|
||||
prompt="Hello world",
|
||||
streaming=False
|
||||
)
|
||||
|
||||
# 5. Send the message
|
||||
producer.send(request) # Backend serializes appropriately
|
||||
```
|
||||
|
||||
### Поток пользователей
|
||||
|
||||
```python
|
||||
# 1. Get the configured backend
|
||||
pubsub = get_pubsub()
|
||||
|
||||
# 2. Create a consumer
|
||||
consumer = pubsub.subscribe(
|
||||
topic="text-completion-requests",
|
||||
schema=TextCompletionRequest # Tells backend how to deserialize
|
||||
)
|
||||
|
||||
# 3. Receive and deserialize
|
||||
msg = consumer.receive()
|
||||
request = msg.value() # Returns TextCompletionRequest dataclass instance
|
||||
|
||||
# 4. Use the data (type-safe access)
|
||||
print(request.system) # "You are helpful"
|
||||
print(request.prompt) # "Hello world"
|
||||
print(request.streaming) # False
|
||||
```
|
||||
|
||||
### Что происходит за кулисами
|
||||
|
||||
**Для бэкенда Pulsar:**
|
||||
`create_producer()` → создает производителя Pulsar с JSON-схемой или динамически генерируемой записью
|
||||
`send(request)` → сериализует dataclass в формат JSON/Pulsar, отправляет в Pulsar
|
||||
`receive()` → получает сообщение Pulsar, десериализует обратно в dataclass
|
||||
|
||||
**Для бэкенда MQTT:**
|
||||
`create_producer()` → подключается к MQTT-брокеру, регистрация схемы не требуется
|
||||
`send(request)` → преобразует dataclass в JSON, публикует в MQTT-топик
|
||||
`receive()` → подписывается на MQTT-топик, десериализует JSON в dataclass
|
||||
|
||||
**Для бэкенда Kafka:**
|
||||
`create_producer()` → создает производителя Kafka, регистрирует схему Avro, если необходимо
|
||||
`send(request)` → сериализует dataclass в формат Avro, отправляет в Kafka
|
||||
`receive()` → получает сообщение Kafka, десериализует Avro обратно в dataclass
|
||||
|
||||
### Ключевые моменты проектирования
|
||||
|
||||
1. **Создание объекта схемы**: Экземпляр dataclass (`TextCompletionRequest(...)`) идентичен независимо от бэкенда
|
||||
2. **Бэкенд обрабатывает кодирование**: Каждый бэкенд знает, как сериализовать свой dataclass в формат для передачи данных
|
||||
3. **Определение схемы при создании**: При создании производителя/потребителя вы указываете тип схемы
|
||||
4. **Сохранение типобезопасности**: Вы получаете правильный объект `TextCompletionRequest`, а не словарь
|
||||
5. **Отсутствие утечек бэкенда**: Код приложения никогда не импортирует библиотеки, специфичные для бэкенда
|
||||
|
||||
### Пример преобразования
|
||||
|
||||
**Текущий (специфичный для Pulsar):**
|
||||
```python
|
||||
# schema/services/llm.py
|
||||
from pulsar.schema import Record, String, Boolean, Integer
|
||||
|
||||
class TextCompletionRequest(Record):
|
||||
system = String()
|
||||
prompt = String()
|
||||
streaming = Boolean()
|
||||
```
|
||||
|
||||
**Новое (независимое от бэкенда):**
|
||||
```python
|
||||
# schema/services/llm.py
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class TextCompletionRequest:
|
||||
system: str
|
||||
prompt: str
|
||||
streaming: bool = False
|
||||
```
|
||||
|
||||
### Интеграция с бэкендом
|
||||
|
||||
Каждый бэкенд отвечает за сериализацию/десериализацию dataclasses:
|
||||
|
||||
**Бэкенд Pulsar:**
|
||||
Динамически генерирует классы `pulsar.schema.Record` из dataclasses
|
||||
Или сериализует dataclasses в JSON и использует JSON-схему Pulsar
|
||||
Обеспечивает совместимость с существующими развертываниями Pulsar
|
||||
|
||||
**Бэкенд MQTT/Redis:**
|
||||
Прямая сериализация в JSON экземпляров dataclass
|
||||
Использует `dataclasses.asdict()` / `from_dict()`
|
||||
Легковесный, не требуется реестр схем
|
||||
|
||||
**Бэкенд Kafka:**
|
||||
Генерирует схемы Avro из определений dataclass
|
||||
Использует реестр схем Confluent
|
||||
Типобезопасная сериализация с поддержкой эволюции схемы
|
||||
|
||||
### Архитектура
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Application Code │
|
||||
│ - Uses dataclass schemas │
|
||||
│ - Backend-agnostic │
|
||||
└──────────────┬──────────────────────┘
|
||||
│
|
||||
┌──────────────┴──────────────────────┐
|
||||
│ PubSubFactory (configurable) │
|
||||
│ - get_pubsub() returns backend │
|
||||
└──────────────┬──────────────────────┘
|
||||
│
|
||||
┌──────┴──────┐
|
||||
│ │
|
||||
┌───────▼─────────┐ ┌────▼──────────────┐
|
||||
│ PulsarBackend │ │ MQTTBackend │
|
||||
│ - JSON schema │ │ - JSON serialize │
|
||||
│ - or dynamic │ │ - Simple queues │
|
||||
│ Record gen │ │ │
|
||||
└─────────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
### Детали реализации
|
||||
|
||||
**1. Определение схем:** Простые классы данных с подсказками типов
|
||||
`str`, `int`, `bool`, `float` для примитивов
|
||||
`list[T]` для массивов
|
||||
`dict[str, T]` для словарей
|
||||
Вложенные классы данных для сложных типов
|
||||
|
||||
**2. Каждый бэкенд предоставляет:**
|
||||
Сериализатор: `dataclass → bytes/wire format`
|
||||
Десериализатор: `bytes/wire format → dataclass`
|
||||
Регистрация схемы (если необходимо, например, для Pulsar/Kafka)
|
||||
|
||||
**3. Абстракция потребителя/производителя:**
|
||||
Уже существует (consumer.py, producer.py)
|
||||
Обновление для использования сериализации бэкенда
|
||||
Удаление прямых импортов Pulsar
|
||||
|
||||
**4. Отображение типов:**
|
||||
Pulsar `String()` → Python `str`
|
||||
Pulsar `Integer()` → Python `int`
|
||||
Pulsar `Boolean()` → Python `bool`
|
||||
Pulsar `Array(T)` → Python `list[T]`
|
||||
Pulsar `Map(K, V)` → Python `dict[K, V]`
|
||||
Pulsar `Double()` → Python `float`
|
||||
Pulsar `Bytes()` → Python `bytes`
|
||||
|
||||
### Путь миграции
|
||||
|
||||
1. **Создайте версии классов данных** для всех схем в `trustgraph/schema/`
|
||||
2. **Обновите классы бэкенда** (Consumer, Producer, Publisher, Subscriber) для использования сериализации, предоставляемой бэкендом
|
||||
3. **Реализуйте PulsarBackend** с использованием JSON-схемы или динамической генерации записей
|
||||
4. **Протестируйте с Pulsar**, чтобы обеспечить обратную совместимость с существующими развертываниями
|
||||
5. **Добавьте новые бэкенды** (MQTT, Kafka, Redis и т. д.) по мере необходимости
|
||||
6. **Удалите импорты Pulsar** из файлов схем
|
||||
|
||||
### Преимущества
|
||||
|
||||
✅ **Отсутствие зависимости от pub/sub** в определениях схем
|
||||
✅ **Стандартный Python** - легко понять, проверить типы, документировать
|
||||
✅ **Современные инструменты** - работает с mypy, автодополнением IDE, линтерами
|
||||
✅ **Оптимизировано для бэкенда** - каждый бэкенд использует собственную сериализацию
|
||||
✅ **Отсутствие накладных расходов на преобразование** - прямая сериализация, без адаптеров
|
||||
✅ **Безопасность типов** - реальные объекты с правильными типами
|
||||
✅ **Простая проверка** - можно использовать Pydantic, если необходимо
|
||||
|
||||
### Проблемы и решения
|
||||
|
||||
**Проблема:** У `Record` Pulsar есть проверка полей во время выполнения
|
||||
**Решение:** Используйте классы данных Pydantic для проверки, если это необходимо, или функции Python 3.10+ для классов данных с `__post_init__`
|
||||
|
||||
**Проблема:** Некоторые специфичные для Pulsar функции (например, тип `Bytes`)
|
||||
**Решение:** Отобразите на тип `bytes` в классе данных, бэкенд обрабатывает кодирование соответствующим образом
|
||||
|
||||
**Проблема:** Именование тем (`persistent://tenant/namespace/topic`)
|
||||
**Решение:** Абстрагируйте имена тем в определениях схем, бэкенд преобразует в правильный формат
|
||||
|
||||
**Проблема:** Эволюция и версионирование схем
|
||||
**Решение:** Каждый бэкенд обрабатывает это в соответствии со своими возможностями (версии схем Pulsar, реестр схем Kafka и т. д.)
|
||||
|
||||
**Проблема:** Вложенные сложные типы
|
||||
**Решение:** Используйте вложенные классы данных, бэкенды рекурсивно сериализуют/десериализуют
|
||||
|
||||
### Принятые решения
|
||||
|
||||
1. **Обычные классы данных или Pydantic?**
|
||||
✅ **Решение: Используйте обычные классы данных Python**
|
||||
Проще, без дополнительных зависимостей
|
||||
Проверка не требуется на практике
|
||||
Легче понять и поддерживать
|
||||
|
||||
2. **Эволюция схемы:**
|
||||
✅ **Решение: Механизм версионирования не требуется**
|
||||
Схемы стабильны и долговечны
|
||||
Обновления обычно добавляют новые поля (обратная совместимость)
|
||||
Бэкенды обрабатывают эволюцию схемы в соответствии со своими возможностями
|
||||
|
||||
3. **Обратная совместимость:**
|
||||
✅ **Решение: Изменение основной версии, обратная совместимость не требуется**
|
||||
Это будет изменение, нарушающее обратную совместимость, с инструкциями по миграции
|
||||
Чистый разрыв позволяет лучше спроектировать систему
|
||||
Будет предоставлено руководство по миграции для существующих развертываний
|
||||
|
||||
4. **Вложенные типы и сложные структуры:**
|
||||
✅ **Решение: Используйте вложенные классы данных естественным образом**
|
||||
Классы данных Python отлично справляются с вложенностью
|
||||
`list[T]` для массивов, `dict[K, V]` для словарей
|
||||
Бэкенды рекурсивно сериализуют/десериализуют
|
||||
Пример:
|
||||
```python
|
||||
@dataclass
|
||||
class Value:
|
||||
value: str
|
||||
is_uri: bool
|
||||
|
||||
@dataclass
|
||||
class Triple:
|
||||
s: Value # Nested dataclass
|
||||
p: Value
|
||||
o: Value
|
||||
|
||||
@dataclass
|
||||
class GraphQuery:
|
||||
triples: list[Triple] # Array of nested dataclasses
|
||||
metadata: dict[str, str]
|
||||
```
|
||||
|
||||
5. **Значения по умолчанию и необязательные поля:**
|
||||
✅ **Решение: Комбинация обязательных, полей со значениями по умолчанию и необязательных полей**
|
||||
Обязательные поля: Не имеют значения по умолчанию.
|
||||
Поля со значениями по умолчанию: Всегда присутствуют, имеют разумные значения по умолчанию.
|
||||
Действительно необязательные поля: `T | None = None`, опускаются при сериализации, когда `None`
|
||||
Пример:
|
||||
```python
|
||||
@dataclass
|
||||
class TextCompletionRequest:
|
||||
system: str # Required, no default
|
||||
prompt: str # Required, no default
|
||||
streaming: bool = False # Optional with default value
|
||||
metadata: dict | None = None # Truly optional, can be absent
|
||||
```
|
||||
|
||||
**Важные семантические аспекты сериализации:**
|
||||
|
||||
Когда `metadata = None`:
|
||||
```json
|
||||
{
|
||||
"system": "...",
|
||||
"prompt": "...",
|
||||
"streaming": false
|
||||
// metadata field NOT PRESENT
|
||||
}
|
||||
```
|
||||
|
||||
Когда `metadata = {}` (явно пусто):
|
||||
```json
|
||||
{
|
||||
"system": "...",
|
||||
"prompt": "...",
|
||||
"streaming": false,
|
||||
"metadata": {} // Field PRESENT but empty
|
||||
}
|
||||
```
|
||||
|
||||
**Ключевое отличие:**
|
||||
`None` → поле отсутствует в JSON (не сериализуется)
|
||||
Пустое значение (`{}`, `[]`, `""`) → поле присутствует со значением "пусто"
|
||||
Это имеет семантическое значение: "не предоставлено" против "явно пусто"
|
||||
Механизмы сериализации должны пропускать поля `None`, а не кодировать их как `null`
|
||||
|
||||
## Предварительный вариант 3: Детали реализации
|
||||
|
||||
### Универсальный формат именования очередей
|
||||
|
||||
Замените специфичные для каждого бэкенда имена очередей на универсальный формат, который бэкенды могут сопоставить соответствующим образом.
|
||||
|
||||
**Формат:** `{qos}/{tenant}/{namespace}/{queue-name}`
|
||||
|
||||
Где:
|
||||
`qos`: Уровень качества обслуживания (QoS)
|
||||
`q0` = best-effort (отправка без подтверждения)
|
||||
`q1` = at-least-once (требуется подтверждение)
|
||||
`q2` = exactly-once (двухфазное подтверждение)
|
||||
`tenant`: Логическая группировка для многопользовательской среды
|
||||
`namespace`: Подгруппа внутри арендатора
|
||||
`queue-name`: Фактическое имя очереди/топика
|
||||
|
||||
**Примеры:**
|
||||
```
|
||||
q1/tg/flow/text-completion-requests
|
||||
q2/tg/config/config-push
|
||||
q0/tg/metrics/stats
|
||||
```
|
||||
|
||||
### Отображение тем для бэкенда
|
||||
|
||||
Каждый бэкенд преобразует общий формат в свой собственный формат:
|
||||
|
||||
**Бэкенд Pulsar:**
|
||||
```python
|
||||
def map_topic(self, generic_topic: str) -> str:
|
||||
# Parse: q1/tg/flow/text-completion-requests
|
||||
qos, tenant, namespace, queue = generic_topic.split('/', 3)
|
||||
|
||||
# Map QoS to persistence
|
||||
persistence = 'persistent' if qos in ['q1', 'q2'] else 'non-persistent'
|
||||
|
||||
# Return Pulsar URI: persistent://tg/flow/text-completion-requests
|
||||
return f"{persistence}://{tenant}/{namespace}/{queue}"
|
||||
```
|
||||
|
||||
**Бэкэнд MQTT:**
|
||||
```python
|
||||
def map_topic(self, generic_topic: str) -> tuple[str, int]:
|
||||
# Parse: q1/tg/flow/text-completion-requests
|
||||
qos, tenant, namespace, queue = generic_topic.split('/', 3)
|
||||
|
||||
# Map QoS level
|
||||
qos_level = {'q0': 0, 'q1': 1, 'q2': 2}[qos]
|
||||
|
||||
# Build MQTT topic including tenant/namespace for proper namespacing
|
||||
mqtt_topic = f"{tenant}/{namespace}/{queue}"
|
||||
|
||||
return mqtt_topic, qos_level
|
||||
```
|
||||
|
||||
### Обновленная вспомогательная функция для темы
|
||||
|
||||
```python
|
||||
# schema/core/topic.py
|
||||
def topic(queue_name, qos='q1', tenant='tg', namespace='flow'):
|
||||
"""
|
||||
Create a generic topic identifier that can be mapped by backends.
|
||||
|
||||
Args:
|
||||
queue_name: The queue/topic name
|
||||
qos: Quality of service
|
||||
- 'q0' = best-effort (no ack)
|
||||
- 'q1' = at-least-once (ack required)
|
||||
- 'q2' = exactly-once (two-phase ack)
|
||||
tenant: Tenant identifier for multi-tenancy
|
||||
namespace: Namespace within tenant
|
||||
|
||||
Returns:
|
||||
Generic topic string: qos/tenant/namespace/queue_name
|
||||
|
||||
Examples:
|
||||
topic('my-queue') # q1/tg/flow/my-queue
|
||||
topic('config', qos='q2', namespace='config') # q2/tg/config/config
|
||||
"""
|
||||
return f"{qos}/{tenant}/{namespace}/{queue_name}"
|
||||
```
|
||||
|
||||
### Конфигурация и инициализация
|
||||
|
||||
**Аргументы командной строки + переменные окружения:**
|
||||
|
||||
```python
|
||||
# In base/async_processor.py - add_args() method
|
||||
@staticmethod
|
||||
def add_args(parser):
|
||||
# Pub/sub backend selection
|
||||
parser.add_argument(
|
||||
'--pubsub-backend',
|
||||
default=os.getenv('PUBSUB_BACKEND', 'pulsar'),
|
||||
choices=['pulsar', 'mqtt'],
|
||||
help='Pub/sub backend (default: pulsar, env: PUBSUB_BACKEND)'
|
||||
)
|
||||
|
||||
# Pulsar-specific configuration
|
||||
parser.add_argument(
|
||||
'--pulsar-host',
|
||||
default=os.getenv('PULSAR_HOST', 'pulsar://localhost:6650'),
|
||||
help='Pulsar host (default: pulsar://localhost:6650, env: PULSAR_HOST)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--pulsar-api-key',
|
||||
default=os.getenv('PULSAR_API_KEY', None),
|
||||
help='Pulsar API key (env: PULSAR_API_KEY)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--pulsar-listener',
|
||||
default=os.getenv('PULSAR_LISTENER', None),
|
||||
help='Pulsar listener name (env: PULSAR_LISTENER)'
|
||||
)
|
||||
|
||||
# MQTT-specific configuration
|
||||
parser.add_argument(
|
||||
'--mqtt-host',
|
||||
default=os.getenv('MQTT_HOST', 'localhost'),
|
||||
help='MQTT broker host (default: localhost, env: MQTT_HOST)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--mqtt-port',
|
||||
type=int,
|
||||
default=int(os.getenv('MQTT_PORT', '1883')),
|
||||
help='MQTT broker port (default: 1883, env: MQTT_PORT)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--mqtt-username',
|
||||
default=os.getenv('MQTT_USERNAME', None),
|
||||
help='MQTT username (env: MQTT_USERNAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--mqtt-password',
|
||||
default=os.getenv('MQTT_PASSWORD', None),
|
||||
help='MQTT password (env: MQTT_PASSWORD)'
|
||||
)
|
||||
```
|
||||
|
||||
**Фабричная функция:**
|
||||
|
||||
```python
|
||||
# In base/pubsub.py or base/pubsub_factory.py
|
||||
def get_pubsub(**config) -> PubSubBackend:
|
||||
"""
|
||||
Create and return a pub/sub backend based on configuration.
|
||||
|
||||
Args:
|
||||
config: Configuration dict from command-line args
|
||||
Must include 'pubsub_backend' key
|
||||
|
||||
Returns:
|
||||
Backend instance (PulsarBackend, MQTTBackend, etc.)
|
||||
"""
|
||||
backend_type = config.get('pubsub_backend', 'pulsar')
|
||||
|
||||
if backend_type == 'pulsar':
|
||||
return PulsarBackend(
|
||||
host=config.get('pulsar_host'),
|
||||
api_key=config.get('pulsar_api_key'),
|
||||
listener=config.get('pulsar_listener'),
|
||||
)
|
||||
elif backend_type == 'mqtt':
|
||||
return MQTTBackend(
|
||||
host=config.get('mqtt_host'),
|
||||
port=config.get('mqtt_port'),
|
||||
username=config.get('mqtt_username'),
|
||||
password=config.get('mqtt_password'),
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unknown pub/sub backend: {backend_type}")
|
||||
```
|
||||
|
||||
**Использование в AsyncProcessor:**
|
||||
|
||||
```python
|
||||
# In async_processor.py
|
||||
class AsyncProcessor:
|
||||
def __init__(self, **params):
|
||||
self.id = params.get("id")
|
||||
|
||||
# Create backend from config (replaces PulsarClient)
|
||||
self.pubsub = get_pubsub(**params)
|
||||
|
||||
# Rest of initialization...
|
||||
```
|
||||
|
||||
### Интерфейс бэкенда
|
||||
|
||||
```python
|
||||
class PubSubBackend(Protocol):
|
||||
"""Protocol defining the interface all pub/sub backends must implement."""
|
||||
|
||||
def create_producer(self, topic: str, schema: type, **options) -> BackendProducer:
|
||||
"""
|
||||
Create a producer for a topic.
|
||||
|
||||
Args:
|
||||
topic: Generic topic format (qos/tenant/namespace/queue)
|
||||
schema: Dataclass type for messages
|
||||
options: Backend-specific options (e.g., chunking_enabled)
|
||||
|
||||
Returns:
|
||||
Backend-specific producer instance
|
||||
"""
|
||||
...
|
||||
|
||||
def create_consumer(
|
||||
self,
|
||||
topic: str,
|
||||
subscription: str,
|
||||
schema: type,
|
||||
initial_position: str = 'latest',
|
||||
consumer_type: str = 'shared',
|
||||
**options
|
||||
) -> BackendConsumer:
|
||||
"""
|
||||
Create a consumer for a topic.
|
||||
|
||||
Args:
|
||||
topic: Generic topic format (qos/tenant/namespace/queue)
|
||||
subscription: Subscription/consumer group name
|
||||
schema: Dataclass type for messages
|
||||
initial_position: 'earliest' or 'latest' (MQTT may ignore)
|
||||
consumer_type: 'shared', 'exclusive', 'failover' (MQTT may ignore)
|
||||
options: Backend-specific options
|
||||
|
||||
Returns:
|
||||
Backend-specific consumer instance
|
||||
"""
|
||||
...
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close the backend connection."""
|
||||
...
|
||||
```
|
||||
|
||||
```python
|
||||
class BackendProducer(Protocol):
|
||||
"""Protocol for backend-specific producer."""
|
||||
|
||||
def send(self, message: Any, properties: dict = {}) -> None:
|
||||
"""Send a message (dataclass instance) with optional properties."""
|
||||
...
|
||||
|
||||
def flush(self) -> None:
|
||||
"""Flush any buffered messages."""
|
||||
...
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close the producer."""
|
||||
...
|
||||
```
|
||||
|
||||
```python
|
||||
class BackendConsumer(Protocol):
|
||||
"""Protocol for backend-specific consumer."""
|
||||
|
||||
def receive(self, timeout_millis: int = 2000) -> Message:
|
||||
"""
|
||||
Receive a message from the topic.
|
||||
|
||||
Raises:
|
||||
TimeoutError: If no message received within timeout
|
||||
"""
|
||||
...
|
||||
|
||||
def acknowledge(self, message: Message) -> None:
|
||||
"""Acknowledge successful processing of a message."""
|
||||
...
|
||||
|
||||
def negative_acknowledge(self, message: Message) -> None:
|
||||
"""Negative acknowledge - triggers redelivery."""
|
||||
...
|
||||
|
||||
def unsubscribe(self) -> None:
|
||||
"""Unsubscribe from the topic."""
|
||||
...
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close the consumer."""
|
||||
...
|
||||
```
|
||||
|
||||
```python
|
||||
class Message(Protocol):
|
||||
"""Protocol for a received message."""
|
||||
|
||||
def value(self) -> Any:
|
||||
"""Get the deserialized message (dataclass instance)."""
|
||||
...
|
||||
|
||||
def properties(self) -> dict:
|
||||
"""Get message properties/metadata."""
|
||||
...
|
||||
```
|
||||
|
||||
### Рефакторинг существующих классов
|
||||
|
||||
Существующие классы `Consumer`, `Producer`, `Publisher`, `Subscriber` остаются в основном без изменений:
|
||||
|
||||
**Текущие обязанности (сохранить):**
|
||||
Асинхронная модель потоков и группы задач
|
||||
Логика повторного подключения и обработка повторных попыток
|
||||
Сбор метрик
|
||||
Ограничение скорости
|
||||
Управление параллелизмом
|
||||
|
||||
**Необходимые изменения:**
|
||||
Удалить прямые импорты Pulsar (`pulsar.schema`, `pulsar.InitialPosition` и т.д.)
|
||||
Принимать `BackendProducer`/`BackendConsumer` вместо клиента Pulsar
|
||||
Передавать фактические операции публикации/подписки на экземпляры бэкенда
|
||||
Отображать общие концепции на вызовы бэкенда
|
||||
|
||||
**Пример рефакторинга:**
|
||||
|
||||
```python
|
||||
# OLD - consumer.py
|
||||
class Consumer:
|
||||
def __init__(self, client, topic, subscriber, schema, ...):
|
||||
self.client = client # Direct Pulsar client
|
||||
# ...
|
||||
|
||||
async def consumer_run(self):
|
||||
# Uses pulsar.InitialPosition, pulsar.ConsumerType
|
||||
self.consumer = self.client.subscribe(
|
||||
topic=self.topic,
|
||||
schema=JsonSchema(self.schema),
|
||||
initial_position=pulsar.InitialPosition.Earliest,
|
||||
consumer_type=pulsar.ConsumerType.Shared,
|
||||
)
|
||||
|
||||
# NEW - consumer.py
|
||||
class Consumer:
|
||||
def __init__(self, backend_consumer, schema, ...):
|
||||
self.backend_consumer = backend_consumer # Backend-specific consumer
|
||||
self.schema = schema
|
||||
# ...
|
||||
|
||||
async def consumer_run(self):
|
||||
# Backend consumer already created with right settings
|
||||
# Just use it directly
|
||||
while self.running:
|
||||
msg = await asyncio.to_thread(
|
||||
self.backend_consumer.receive,
|
||||
timeout_millis=2000
|
||||
)
|
||||
await self.handle_message(msg)
|
||||
```
|
||||
|
||||
### Специфические для бэкенда особенности
|
||||
|
||||
**Бэкенд Pulsar:**
|
||||
Отображает `q0` → `non-persistent://`, `q1`/`q2` → `persistent://`
|
||||
Поддерживает все типы потребителей (shared, exclusive, failover)
|
||||
Поддерживает начальную позицию (earliest/latest)
|
||||
Поддержка подтверждения получения сообщений
|
||||
Поддержка реестра схем
|
||||
|
||||
**Бэкенд MQTT:**
|
||||
Отображает `q0`/`q1`/`q2` → уровни качества обслуживания (QoS) MQTT 0/1/2
|
||||
Включает арендатора/пространство имен в путь темы для обеспечения разделения имен
|
||||
Автоматически генерирует идентификаторы клиентов из имен подписок
|
||||
Игнорирует начальную позицию (нет истории сообщений в базовом MQTT)
|
||||
Игнорирует тип потребителя (MQTT использует идентификаторы клиентов, а не группы потребителей)
|
||||
Простая модель публикации/подписки
|
||||
|
||||
### Краткое описание принятых решений
|
||||
|
||||
1. ✅ **Универсальное именование очередей**: формат `qos/tenant/namespace/queue-name`
|
||||
2. ✅ **QoS в идентификаторе очереди**: определяется определением очереди, а не конфигурацией
|
||||
3. ✅ **Повторное подключение**: обрабатывается классами Consumer/Producer, а не бэкендами
|
||||
4. ✅ **Темы MQTT**: включают арендатора/пространство имен для правильного разделения имен
|
||||
5. ✅ **История сообщений**: MQTT игнорирует параметр `initial_position` (будущее улучшение)
|
||||
6. ✅ **Идентификаторы клиентов**: бэкенд MQTT автоматически генерирует из имени подписки
|
||||
|
||||
### Будущие улучшения
|
||||
|
||||
**История сообщений MQTT:**
|
||||
Можно добавить необязательный слой постоянного хранения (например, сохраненные сообщения, внешний хранилище)
|
||||
Это позволит поддерживать `initial_position='earliest'`
|
||||
Не требуется для первоначальной реализации
|
||||
1516
docs/tech-specs/ru/python-api-refactor.ru.md
Normal file
1516
docs/tech-specs/ru/python-api-refactor.ru.md
Normal file
File diff suppressed because it is too large
Load diff
271
docs/tech-specs/ru/query-time-explainability.ru.md
Normal file
271
docs/tech-specs/ru/query-time-explainability.ru.md
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Статус
|
||||
|
||||
Реализовано
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает, как GraphRAG записывает и передает данные об объяснимости во время выполнения запроса. Цель - полная отслеживаемость: от окончательного ответа до выбранных ребер и исходных документов.
|
||||
|
||||
Объяснимость во время выполнения запроса фиксирует действия, которые выполняет конвейер GraphRAG во время рассуждений. Она связана с информацией о происхождении, которая записывается во время извлечения и фиксирует, откуда взялись факты графа знаний.
|
||||
|
||||
## Терминология
|
||||
|
||||
| Термин | Определение |
|
||||
|------|------------|
|
||||
| **Объяснимость** | Запись о том, как был получен результат |
|
||||
| **Сессия** | Одиночное выполнение запроса GraphRAG |
|
||||
| **Выбор ребра** | Выбор релевантных ребер с использованием LLM и обоснованием |
|
||||
| **Цепь происхождения** | Путь от ребра → фрагмента → страницы → документа |
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Поток объяснимости
|
||||
|
||||
```
|
||||
GraphRAG Query
|
||||
│
|
||||
├─► Session Activity
|
||||
│ └─► Query text, timestamp
|
||||
│
|
||||
├─► Retrieval Entity
|
||||
│ └─► All edges retrieved from subgraph
|
||||
│
|
||||
├─► Selection Entity
|
||||
│ └─► Selected edges with LLM reasoning
|
||||
│ └─► Each edge links to extraction provenance
|
||||
│
|
||||
└─► Answer Entity
|
||||
└─► Reference to synthesized response (in librarian)
|
||||
```
|
||||
|
||||
### Двухэтапная конвейерная обработка GraphRAG
|
||||
|
||||
1. **Выбор ребер**: LLM выбирает релевантные ребра из подграфа, предоставляя обоснование для каждого.
|
||||
2. **Синтез**: LLM генерирует ответ, используя только выбранные ребра.
|
||||
|
||||
Такое разделение обеспечивает объяснимость - мы точно знаем, какие ребра внесли вклад.
|
||||
|
||||
### Хранилище
|
||||
|
||||
Тройки, обеспечивающие объяснимость, хранятся в настраиваемой коллекции (по умолчанию: `explainability`).
|
||||
Используется онтология PROV-O для отношений происхождения.
|
||||
RDF-star для ссылок на ребра.
|
||||
Содержимое ответа хранится в сервисе librarian (не встроено - слишком большой объем).
|
||||
|
||||
### Потоковая передача в реальном времени
|
||||
|
||||
События, обеспечивающие объяснимость, передаются клиенту в процессе выполнения запроса:
|
||||
|
||||
1. Создание сессии → событие отправлено.
|
||||
2. Получение ребер → событие отправлено.
|
||||
3. Выбор ребер с обоснованием → событие отправлено.
|
||||
4. Синтез ответа → событие отправлено.
|
||||
|
||||
Клиент получает `explain_id` и `explain_collection` для получения полной информации.
|
||||
|
||||
## Структура URI
|
||||
|
||||
Все URI используют пространство имен `urn:trustgraph:` с UUID:
|
||||
|
||||
| Сущность | Шаблон URI |
|
||||
|--------|-------------|
|
||||
| Сессия | `urn:trustgraph:session:{uuid}` |
|
||||
| Получение | `urn:trustgraph:prov:retrieval:{uuid}` |
|
||||
| Выбор | `urn:trustgraph:prov:selection:{uuid}` |
|
||||
| Ответ | `urn:trustgraph:prov:answer:{uuid}` |
|
||||
| Выбор ребра | `urn:trustgraph:prov:edge:{uuid}:{index}` |
|
||||
|
||||
## Модель RDF (PROV-O)
|
||||
|
||||
### Сессия активности
|
||||
|
||||
```turtle
|
||||
<session-uri> a prov:Activity ;
|
||||
rdfs:label "GraphRAG query session" ;
|
||||
prov:startedAtTime "2024-01-15T10:30:00Z" ;
|
||||
tg:query "What was the War on Terror?" .
|
||||
```
|
||||
|
||||
### Получение сущности
|
||||
|
||||
```turtle
|
||||
<retrieval-uri> a prov:Entity ;
|
||||
rdfs:label "Retrieved edges" ;
|
||||
prov:wasGeneratedBy <session-uri> ;
|
||||
tg:edgeCount 50 .
|
||||
```
|
||||
|
||||
### Выбор сущности
|
||||
|
||||
```turtle
|
||||
<selection-uri> a prov:Entity ;
|
||||
rdfs:label "Selected edges" ;
|
||||
prov:wasDerivedFrom <retrieval-uri> ;
|
||||
tg:selectedEdge <edge-sel-0> ;
|
||||
tg:selectedEdge <edge-sel-1> .
|
||||
|
||||
<edge-sel-0> tg:edge << <s> <p> <o> >> ;
|
||||
tg:reasoning "This edge establishes the key relationship..." .
|
||||
```
|
||||
|
||||
### Ответная сущность
|
||||
|
||||
```turtle
|
||||
<answer-uri> a prov:Entity ;
|
||||
rdfs:label "GraphRAG answer" ;
|
||||
prov:wasDerivedFrom <selection-uri> ;
|
||||
tg:document <urn:trustgraph:answer:{uuid}> .
|
||||
```
|
||||
|
||||
`tg:document` ссылается на ответ, хранящийся в сервисе librarian.
|
||||
|
||||
## Константы пространства имен
|
||||
|
||||
Определены в `trustgraph-base/trustgraph/provenance/namespaces.py`:
|
||||
|
||||
| Константа | URI |
|
||||
|----------|-----|
|
||||
| `TG_QUERY` | `https://trustgraph.ai/ns/query` |
|
||||
| `TG_EDGE_COUNT` | `https://trustgraph.ai/ns/edgeCount` |
|
||||
| `TG_SELECTED_EDGE` | `https://trustgraph.ai/ns/selectedEdge` |
|
||||
| `TG_EDGE` | `https://trustgraph.ai/ns/edge` |
|
||||
| `TG_REASONING` | `https://trustgraph.ai/ns/reasoning` |
|
||||
| `TG_CONTENT` | `https://trustgraph.ai/ns/content` |
|
||||
| `TG_DOCUMENT` | `https://trustgraph.ai/ns/document` |
|
||||
|
||||
## Схема GraphRagResponse
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class GraphRagResponse:
|
||||
error: Error | None = None
|
||||
response: str = ""
|
||||
end_of_stream: bool = False
|
||||
explain_id: str | None = None
|
||||
explain_collection: str | None = None
|
||||
message_type: str = "" # "chunk" or "explain"
|
||||
end_of_session: bool = False
|
||||
```
|
||||
|
||||
### Типы сообщений
|
||||
|
||||
| message_type | Назначение |
|
||||
|--------------|---------|
|
||||
| `chunk` | Текстовый ответ (потоковый или окончательный) |
|
||||
| `explain` | Событие, связанное с объяснимостью, с ссылкой IRI |
|
||||
|
||||
### Жизненный цикл сессии
|
||||
|
||||
1. Несколько сообщений `explain` (сессия, получение, выбор, ответ)
|
||||
2. Несколько сообщений `chunk` (потоковый ответ)
|
||||
3. Окончательное сообщение `chunk` с `end_of_session=True`
|
||||
|
||||
## Формат выбора ребер
|
||||
|
||||
LLM возвращает JSONL с выбранными ребрами:
|
||||
|
||||
```jsonl
|
||||
{"id": "edge-hash-1", "reasoning": "This edge shows the key relationship..."}
|
||||
{"id": "edge-hash-2", "reasoning": "Provides supporting evidence..."}
|
||||
```
|
||||
|
||||
`id` является хешем `(labeled_s, labeled_p, labeled_o)`, вычисленным с помощью `edge_id()`.
|
||||
|
||||
## Сохранение URI
|
||||
|
||||
### Проблема
|
||||
|
||||
GraphRAG отображает для LLM удобочитаемые метки, но для отслеживания происхождения необходимы исходные URI.
|
||||
|
||||
### Решение
|
||||
|
||||
`get_labelgraph()` возвращает следующее:
|
||||
`labeled_edges`: Список `(label_s, label_p, label_o)` для LLM
|
||||
`uri_map`: Словарь, сопоставляющий `edge_id(labels)` → `(uri_s, uri_p, uri_o)`
|
||||
|
||||
При хранении данных для объяснения используются URI из `uri_map`.
|
||||
|
||||
## Отслеживание происхождения
|
||||
|
||||
### От узла к источнику
|
||||
|
||||
Выбранные узлы можно проследить до исходных документов:
|
||||
|
||||
1. Запрос для получения содержащего подграфа: `?subgraph tg:contains <<s p o>>`
|
||||
2. Переход по цепочке `prov:wasDerivedFrom` к корневому документу
|
||||
3. Каждый шаг в цепочке: фрагмент → страница → документ
|
||||
|
||||
### Поддержка тройных наборов в Cassandra
|
||||
|
||||
Сервис запросов Cassandra поддерживает сопоставление тройных наборов:
|
||||
|
||||
```python
|
||||
# In get_term_value():
|
||||
elif term.type == TRIPLE:
|
||||
return serialize_triple(term.triple)
|
||||
```
|
||||
|
||||
Это позволяет выполнять запросы, такие как:
|
||||
```
|
||||
?subgraph tg:contains <<http://example.org/s http://example.org/p "value">>
|
||||
```
|
||||
|
||||
## Использование интерфейса командной строки
|
||||
|
||||
```bash
|
||||
tg-invoke-graph-rag --explainable -q "What was the War on Terror?"
|
||||
```
|
||||
|
||||
### Формат вывода
|
||||
|
||||
```
|
||||
[session] urn:trustgraph:session:abc123
|
||||
|
||||
[retrieval] urn:trustgraph:prov:retrieval:abc123
|
||||
|
||||
[selection] urn:trustgraph:prov:selection:abc123
|
||||
Selected 12 edge(s)
|
||||
Edge: (Guantanamo, definition, A detention facility...)
|
||||
Reason: Directly connects Guantanamo to the War on Terror
|
||||
Source: Chunk 1 → Page 2 → Beyond the Vigilant State
|
||||
|
||||
[answer] urn:trustgraph:prov:answer:abc123
|
||||
|
||||
Based on the provided knowledge statements...
|
||||
```
|
||||
|
||||
### Особенности
|
||||
|
||||
События объяснения в реальном времени во время запроса.
|
||||
Разрешение меток для компонентов ребер с помощью `rdfs:label`.
|
||||
Отслеживание цепочки источников с помощью `prov:wasDerivedFrom`.
|
||||
Кэширование меток для предотвращения повторных запросов.
|
||||
|
||||
## Реализованные файлы
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|---------|
|
||||
| `trustgraph-base/trustgraph/provenance/uris.py` | Генераторы URI |
|
||||
| `trustgraph-base/trustgraph/provenance/namespaces.py` | Константы пространства имен RDF |
|
||||
| `trustgraph-base/trustgraph/provenance/triples.py` | Конструкторы троек |
|
||||
| `trustgraph-base/trustgraph/schema/services/retrieval.py` | Схема GraphRagResponse |
|
||||
| `trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py` | Основной GraphRAG с сохранением URI |
|
||||
| `trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` | Сервис с интеграцией с библиотекарем |
|
||||
| `trustgraph-flow/trustgraph/query/triples/cassandra/service.py` | Поддержка запросов троек в кавычках |
|
||||
| `trustgraph-cli/trustgraph/cli/invoke_graph_rag.py` | CLI с отображением объяснений |
|
||||
|
||||
## Ссылки
|
||||
|
||||
PROV-O (W3C Provenance Ontology): https://www.w3.org/TR/prov-o/
|
||||
RDF-star: https://w3c.github.io/rdf-star/
|
||||
Происхождение во время извлечения: `docs/tech-specs/extraction-time-provenance.md`
|
||||
180
docs/tech-specs/ru/rag-streaming-support.ru.md
Normal file
180
docs/tech-specs/ru/rag-streaming-support.ru.md
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Техническая спецификация поддержки потоковой передачи для RAG"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Техническая спецификация поддержки потоковой передачи для RAG
|
||||
|
||||
> **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 и DocumentRAG, позволяя получать ответы по частям (токен за токеном) для запросов из графа знаний и документов. Это расширяет существующую архитектуру потоковой передачи, уже реализованную для LLM-сервисов для завершения текста, запросов и агентов.
|
||||
|
||||
## Цели
|
||||
|
||||
- **Консистентный UX потоковой передачи**: Обеспечить одинаковый опыт потоковой передачи для всех сервисов TrustGraph.
|
||||
- **Минимальные изменения API**: Добавить поддержку потоковой передачи с помощью одного флага `streaming`, следуя установленным шаблонам.
|
||||
- **Совместимость со старыми версиями**: Поддерживать существующее поведение без потоковой передачи по умолчанию.
|
||||
- **Использование существующей инфраструктуры**: Использовать существующую функциональность потоковой передачи PromptClient.
|
||||
- **Поддержка Gateways**: Включить потоковую передачу через websocket Gateway для клиентских приложений.
|
||||
|
||||
## Предыстория
|
||||
|
||||
Текущие сервисы, поддерживающие потоковую передачу:
|
||||
- **Сервис завершения текста LLM**: Фаза 1 - потоковая передача от LLM-провайдеров.
|
||||
- **Сервис запросов**: Фаза 2 - потоковая передача через шаблоны запросов.
|
||||
- **Сервис агента**: Фазы 3-4 - потоковая передача ReAct с последовательными частями/данными/ответами.
|
||||
|
||||
Текущие ограничения для сервисов RAG:
|
||||
- GraphRAG и DocumentRAG поддерживают только не потоковые ответы.
|
||||
- Пользователям необходимо ждать полного ответа LLM, прежде чем видеть какой-либо результат.
|
||||
- Плохой UX для длинных ответов из запросов к графу знаний или документам.
|
||||
- Несогласованный опыт по сравнению с другими сервисами TrustGraph.
|
||||
|
||||
Эта спецификация решает эти проблемы, добавляя поддержку потоковой передачи для GraphRAG и DocumentRAG. Благодаря потоковой передаче по частям, TrustGraph может:
|
||||
- Обеспечить консистентный UX потоковой передачи для всех типов запросов.
|
||||
- Снизить воспринимаемую задержку для запросов RAG.
|
||||
- Обеспечить лучший прогресс для длительных запросов.
|
||||
- Поддерживать отображение в реальном времени в клиентских приложениях.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Архитектура
|
||||
|
||||
Реализация потоковой передачи для RAG использует существующую инфраструктуру:
|
||||
|
||||
1. **PromptClient Streaming** (Уже реализовано)
|
||||
- `kg_prompt()` и `document_prompt()` уже принимают параметры `streaming` и `chunk_callback`.
|
||||
- Эти вызывают `prompt()` с поддержкой потоковой передачи.
|
||||
- Изменения не требуются для PromptClient.
|
||||
|
||||
Модуль: `trustgraph-base/trustgraph/base/prompt_client.py`
|
||||
|
||||
2. **Сервис GraphRAG** (Требуется передача параметра `streaming`)
|
||||
- Добавить параметр `streaming` к методу `query()`.
|
||||
- Передавать флаг `streaming` и обратные вызовы в `prompt_client.kg_prompt()`.
|
||||
- Схема GraphRagRequest должна иметь поле `streaming`.
|
||||
|
||||
Модули:
|
||||
- `trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py`
|
||||
- `trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` (Обработчик)
|
||||
- `trustgraph-base/trustgraph/schema/graph_rag.py` (Схема запроса)
|
||||
- `trustgraph-flow/trustgraph/gateway/dispatch/graph_rag.py` (Gateway)
|
||||
|
||||
3. **Сервис DocumentRAG** (Требуется передача параметра `streaming`)
|
||||
- Добавить параметр `streaming` к методу `query()`.
|
||||
- Передавать флаг `streaming` и обратные вызовы в `prompt_client.document_prompt()`.
|
||||
- Схема DocumentRagRequest должна иметь поле `streaming`.
|
||||
|
||||
Модули:
|
||||
- `trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py`
|
||||
- `trustgraph-flow/trustgraph/retrieval/document_rag/rag.py` (Обработчик)
|
||||
- `trustgraph-base/trustgraph/schema/document_rag.py` (Схема запроса)
|
||||
- `trustgraph-flow/trustgraph/gateway/dispatch/document_rag.py` (Gateway)
|
||||
|
||||
### Поток данных
|
||||
|
||||
**Не потоковая передача (текущая)**:
|
||||
```
|
||||
Клиент → Gateway → Сервис RAG → PromptClient.kg_prompt(streaming=False)
|
||||
↓
|
||||
Сервис запросов → LLM
|
||||
↓
|
||||
Полный ответ
|
||||
↓
|
||||
Клиент ← Gateway ← Сервис RAG ← Ответ
|
||||
```
|
||||
|
||||
**Потоковая передача (предлагаемая)**:
|
||||
```
|
||||
Клиент → Gateway → Сервис RAG → PromptClient.kg_prompt(streaming=True, chunk_callback=cb)
|
||||
↓
|
||||
Сервис запросов → LLM (streaming)
|
||||
↓
|
||||
Часть → обратный вызов → Ответ RAG (часть)
|
||||
↓ ↓
|
||||
Клиент ← Gateway ← ────────────────────────────────── Поток ответа
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
**Изменения для GraphRAG**:
|
||||
|
||||
1. **GraphRag.query()** - Добавлены параметры потоковой передачи
|
||||
```python
|
||||
async def query(
|
||||
self, query, user, collection,
|
||||
verbose=False, streaming=False, chunk_callback=None # NEW
|
||||
):
|
||||
# ... существующая работа с сущностями/триплетами ...
|
||||
|
||||
if streaming and chunk_callback:
|
||||
resp = await self.prompt_client.kg_prompt(
|
||||
query, kg,
|
||||
streaming=True,
|
||||
chunk_callback=chunk_callback
|
||||
)
|
||||
else:
|
||||
resp = await self.prompt_client.kg_prompt(query, kg)
|
||||
|
||||
return resp
|
||||
```
|
||||
|
||||
2. **GraphRagRequest schema** - Добавлен параметр `streaming`
|
||||
```python
|
||||
class GraphRagRequest(Record):
|
||||
query = String()
|
||||
user = String()
|
||||
collection = String()
|
||||
streaming = Boolean() # NEW
|
||||
```
|
||||
|
||||
3. **GraphRagResponse schema** - Добавлены поля для потоковой передачи (следовать паттерну Agent)
|
||||
```python
|
||||
class GraphRagResponse(Record):
|
||||
response = String() # Legacy: полный ответ
|
||||
chunk = String() # NEW: часть для потоковой передачи
|
||||
end_of_stream = Boolean() # NEW: указывает, что это последняя часть
|
||||
```
|
||||
|
||||
4. **Обработчик** - Передача потоковой передачи
|
||||
```python
|
||||
async def handle(self, ...):
|
||||
# ... существующий код ...
|
||||
response = await self.query(...)
|
||||
if response and streaming:
|
||||
# ... логика для отправки части
|
||||
else:
|
||||
# ... логика для отправки полного ответа
|
||||
```
|
||||
|
||||
**Изменения для DocumentRAG**: Аналогично GraphRAG.
|
||||
|
||||
## График миграции
|
||||
|
||||
Не требуется миграция:
|
||||
- Поддержка потоковой передачи является опцией (по умолчанию отключена)
|
||||
- Существующие клиенты продолжают работать без изменений.
|
||||
- Новые клиенты могут включить поддержку потоковой передачи.
|
||||
|
||||
## Сроки
|
||||
|
||||
Оценка времени реализации: 4-6 часов
|
||||
- Фаза 1 (2 часа): Поддержка потоковой передачи для GraphRAG.
|
||||
- Фаза 2 (2 часа): Поддержка потоковой передачи для DocumentRAG.
|
||||
- Фаза 3 (1-2 часа): Обновления Gateway и флаги командной строки.
|
||||
- Тестирование: Встроено в каждую фазу.
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- Должна ли также поддерживаться потоковая передача для сервиса NLP Query?
|
||||
- Хотим ли мы передавать только выход LLM (например, "Извлечь сущности...", "Запрос к графу...") или только его?
|
||||
- Должны ли ответы GraphRAG/DocumentRAG содержать метаданные части (например, номер части, общее количество)?
|
||||
|
||||
## Ссылки
|
||||
|
||||
- Существующая реализация: `docs/tech-specs/streaming-llm-responses.md`
|
||||
- Потоковая передача LLM: `trustgraph-flow/trustgraph/agent/react/agent_manager.py`
|
||||
- Потоковая передача PromptClient: `trustgraph-base/trustgraph/base/prompt_client.py`
|
||||
99
docs/tech-specs/ru/schema-refactoring-proposal.ru.md
Normal file
99
docs/tech-specs/ru/schema-refactoring-proposal.ru.md
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
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. **Плоская структура** - Все схемы в одном каталоге затрудняют понимание взаимосвязей
|
||||
2. **Смешанные задачи** - Ядро типов, объекты домена и контракты API смешаны вместе
|
||||
3. **Неясное наименование** - Файлы, такие как "object.py", "types.py", "topic.py", не указывают четко их назначение
|
||||
4. **Отсутствие четкого уровня** - Трудно понять, на что зависит что
|
||||
|
||||
## Предлагаемая структура
|
||||
|
||||
```
|
||||
trustgraph-base/trustgraph/schema/
|
||||
├── __init__.py
|
||||
├── core/ # Основные примитивные типы, используемые везде
|
||||
│ ├── __init__.py
|
||||
│ ├── primitives.py # Ошибка, Значение, Трипл, Поле, Схема строк
|
||||
│ ├── metadata.py # Запись метаданных
|
||||
│ └── topic.py # Утилиты для тем
|
||||
│
|
||||
├── knowledge/ # Модели домена знаний и извлечение
|
||||
│ ├── __init__.py
|
||||
│ ├── graph.py # EntityContext, EntityEmbeddings, Триплы
|
||||
│ ├── document.py # Документ, TextDocument, Chunk
|
||||
│ ├── knowledge.py # Типы извлечения знаний
|
||||
│ ├── embeddings.py # Все типы, связанные с встраиванием (перенесены из нескольких файлов)
|
||||
│ └── nlp.py # Типы Определение, Тема, Отношение, Факты
|
||||
│
|
||||
└── services/ # Контракты запросов/ответов сервисов
|
||||
├── __init__.py
|
||||
├── llm.py # TextCompletion, Embeddings, Запросы/ответы инструментов
|
||||
├── retrieval.py # Запросы/ответы GraphRAG, DocumentRAG
|
||||
├── query.py # Запросы/ответы GraphEmbeddingsRequest/Response, DocumentEmbeddingsRequest/Response
|
||||
├── agent.py # Запросы/ответы агента
|
||||
├── flow.py # Запросы/ответы потока
|
||||
├── prompt.py # Запросы/ответы сервиса подсказок
|
||||
├── config.py # Конфигурационный сервис
|
||||
├── library.py # Сервис библиотека
|
||||
└── lookup.py # Сервис поиска
|
||||
```
|
||||
|
||||
## Ключевые изменения
|
||||
|
||||
1. **Иерархическая организация** - Четкое разделение между основными типами, моделями знаний и контрактами сервисов
|
||||
2. **Улучшенное наименование:**
|
||||
- `types.py` → `core/primitives.py` (более четкое назначение)
|
||||
- `object.py` → Разделение на соответствующие файлы на основе фактического содержимого
|
||||
- `documents.py` → `knowledge/document.py` (одинаковое, согласованное)
|
||||
- `models.py` → `services/llm.py` (более четко, какие модели)
|
||||
- `prompt.py` → Разделение: части сервиса в `services/prompt.py`, типы данных в `knowledge/nlp.py`
|
||||
|
||||
3. **Логическая группировка:**
|
||||
- Все типы встраивания консолидированы в `knowledge/embeddings.py`
|
||||
- Все контракты сервисов LLM в `services/llm.py`
|
||||
- Четкое разделение пар запросов/ответов в каталоге сервисов
|
||||
- Типы извлечения знаний сгруппированы с другими моделями домена знаний
|
||||
|
||||
4. **Ясность зависимостей:**
|
||||
- Основные типы не имеют зависимостей
|
||||
- Модели знаний зависят только от основных
|
||||
- Контракты сервисов могут зависеть как от основных, так и от моделей знаний
|
||||
|
||||
## Преимущества миграции
|
||||
|
||||
1. **Легче навигация** - Разработчики могут быстро найти то, что им нужно
|
||||
2. **Улучшенная модульность** - Четкие границы между разными задачами
|
||||
3. **Проще импорт** - Более интуитивные пути импорта
|
||||
4. **Готовность к будущему** - Легко добавлять новые типы знаний или сервисы, не загромождая
|
||||
|
||||
## Пример изменений импорта
|
||||
|
||||
```python
|
||||
# Изначально
|
||||
from trustgraph.schema import Error, Triple, GraphEmbeddings, TextCompletionRequest
|
||||
|
||||
# После
|
||||
from trustgraph.schema.core import Error, Triple
|
||||
from trustgraph.schema.knowledge import GraphEmbeddings
|
||||
from trustgraph.schema.services import TextCompletionRequest
|
||||
```
|
||||
|
||||
## Заметки по реализации
|
||||
|
||||
1. Сохраняйте обратную совместимость, поддерживая импорт в корневом файле `__init__.py`
|
||||
2. Перемещайте файлы постепенно, обновляя импорты по мере необходимости
|
||||
3. Рассмотрите возможность добавления файла `legacy.py`, который импортирует всё для переходного периода
|
||||
4. Обновите документацию, чтобы отразить новую структуру
|
||||
|
||||
<function_calls>
|
||||
<invoke name="TodoWrite">
|
||||
<parameter name="todos">[{"id": "1", "content": "Оценить текущую структуру каталога схемы", "status": "completed", "priority": "high"}, {"id": "2", "content": "Проанализировать файлы схемы и их назначение", "status": "completed", "priority": "high"}, {"id": "3", "content": "Предложить улучшенное наименование и структуру", "status": "completed", "priority": "high"}]
|
||||
578
docs/tech-specs/ru/streaming-llm-responses.ru.md
Normal file
578
docs/tech-specs/ru/streaming-llm-responses.ru.md
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Техническая спецификация потоковой передачи ответов LLM"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Техническая спецификация потоковой передачи ответов LLM
|
||||
|
||||
> **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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает реализацию поддержки потоковой передачи для ответов LLM
|
||||
в TrustGraph. Потоковая передача обеспечивает доставку сгенерированных
|
||||
токенов в режиме реального времени по мере их создания LLM, а не после
|
||||
завершения полной генерации ответа.
|
||||
|
||||
Эта реализация поддерживает следующие сценарии использования:
|
||||
|
||||
1. **Интерфейсы пользователя в реальном времени**: Передавайте токены в пользовательский интерфейс по мере их генерации,
|
||||
обеспечивая немедленную визуальную обратную связь.
|
||||
2. **Сокращение времени до первого токена**: Пользователи видят вывод сразу,
|
||||
а не после полной генерации.
|
||||
3. **Обработка очень длинных ответов**: Обрабатывайте очень длинные ответы, которые в противном случае
|
||||
могли бы привести к таймаутам или превышению лимитов памяти.
|
||||
4. **Интерактивные приложения**: Обеспечьте отзывчивые чат-интерфейсы и интерфейсы агентов.
|
||||
|
||||
## Цели
|
||||
|
||||
**Обратная совместимость**: Существующие клиенты, не использующие потоковую передачу, продолжают работать
|
||||
без изменений.
|
||||
**Согласованный дизайн API**: Потоковая передача и не потоковая передача используют одни и те же схемы
|
||||
с минимальными отклонениями.
|
||||
**Гибкость для поставщиков**: Поддержка потоковой передачи, где это возможно, и плавный
|
||||
переход к не потоковой передаче, где это невозможно.
|
||||
**Поэтапное внедрение**: Постепенная реализация для снижения рисков.
|
||||
**Комплексная поддержка**: Потоковая передача от поставщика LLM до клиентских
|
||||
приложений через Pulsar, Gateway API и Python API.
|
||||
|
||||
## Обзор
|
||||
|
||||
### Текущая архитектура
|
||||
|
||||
Текущий процесс текстового завершения LLM работает следующим образом:
|
||||
|
||||
1. Клиент отправляет `TextCompletionRequest` с полями `system` и `prompt`.
|
||||
2. Сервис LLM обрабатывает запрос и ожидает полной генерации.
|
||||
3. Возвращается один `TextCompletionResponse` с полной строкой `response`.
|
||||
|
||||
Текущая схема (`trustgraph-base/trustgraph/schema/services/llm.py`):
|
||||
|
||||
```python
|
||||
class TextCompletionRequest(Record):
|
||||
system = String()
|
||||
prompt = String()
|
||||
|
||||
class TextCompletionResponse(Record):
|
||||
error = Error()
|
||||
response = String()
|
||||
in_token = Integer()
|
||||
out_token = Integer()
|
||||
model = String()
|
||||
```
|
||||
|
||||
### Текущие ограничения
|
||||
|
||||
**Задержка**: Пользователи должны ждать завершения генерации, прежде чем увидеть какой-либо результат.
|
||||
**Риск превышения времени ожидания**: Длительная генерация может превысить лимиты времени ожидания клиента.
|
||||
**Плохой пользовательский опыт**: Отсутствие обратной связи во время генерации создает ощущение медленной работы.
|
||||
**Использование ресурсов**: Полные ответы должны быть буферизованы в памяти.
|
||||
|
||||
Эта спецификация решает эти ограничения, обеспечивая постепенную передачу ответа, при этом сохраняя полную обратную совместимость.
|
||||
|
||||
|
||||
## Техническое проектирование
|
||||
|
||||
### Фаза 1: Инфраструктура
|
||||
|
||||
Фаза 1 закладывает основу для потоковой передачи путем изменения схем, API и инструментов командной строки.
|
||||
|
||||
|
||||
#### Изменения в схемах
|
||||
|
||||
##### Схема LLM (`trustgraph-base/trustgraph/schema/services/llm.py`)
|
||||
|
||||
**Изменения в запросах:**
|
||||
|
||||
```python
|
||||
class TextCompletionRequest(Record):
|
||||
system = String()
|
||||
prompt = String()
|
||||
streaming = Boolean() # NEW: Default false for backward compatibility
|
||||
```
|
||||
|
||||
`streaming`: Когда `true`, запрашивается потоковая доставка ответа.
|
||||
По умолчанию: `false` (сохранено существующее поведение).
|
||||
|
||||
**Изменения в ответе:**
|
||||
|
||||
```python
|
||||
class TextCompletionResponse(Record):
|
||||
error = Error()
|
||||
response = String()
|
||||
in_token = Integer()
|
||||
out_token = Integer()
|
||||
model = String()
|
||||
end_of_stream = Boolean() # NEW: Indicates final message
|
||||
```
|
||||
|
||||
`end_of_stream`: Когда `true`, указывает на то, что это окончательный (или единственный) ответ.
|
||||
Для нестриминговых запросов: Один ответ с `end_of_stream=true`.
|
||||
Для стриминговых запросов: Множественные ответы, все с `end_of_stream=false`,
|
||||
за исключением последнего.
|
||||
|
||||
##### Схема запроса (`trustgraph-base/trustgraph/schema/services/prompt.py`)
|
||||
|
||||
Сервис запросов оборачивает завершение текста, поэтому он следует той же схеме:
|
||||
|
||||
**Изменения в запросе:**
|
||||
|
||||
```python
|
||||
class PromptRequest(Record):
|
||||
id = String()
|
||||
terms = Map(String())
|
||||
streaming = Boolean() # NEW: Default false
|
||||
```
|
||||
|
||||
**Изменения в ответе:**
|
||||
|
||||
```python
|
||||
class PromptResponse(Record):
|
||||
error = Error()
|
||||
text = String()
|
||||
object = String()
|
||||
end_of_stream = Boolean() # NEW: Indicates final message
|
||||
```
|
||||
|
||||
#### Изменения API шлюза
|
||||
|
||||
API шлюз должен предоставлять возможности потоковой передачи данных для клиентов HTTP/WebSocket.
|
||||
|
||||
**Обновления REST API:**
|
||||
|
||||
`POST /api/v1/text-completion`: Принимать параметр `streaming` в теле запроса
|
||||
Поведение ответа зависит от флага потоковой передачи:
|
||||
`streaming=false`: Одиночный ответ в формате JSON (текущее поведение)
|
||||
`streaming=true`: Поток событий от сервера (SSE) или сообщения WebSocket
|
||||
|
||||
**Формат ответа (потоковая передача):**
|
||||
|
||||
Каждый фрагмент, передаваемый потоком, имеет одинаковую структуру схемы:
|
||||
```json
|
||||
{
|
||||
"response": "partial text...",
|
||||
"end_of_stream": false,
|
||||
"model": "model-name"
|
||||
}
|
||||
```
|
||||
|
||||
Заключительный раздел:
|
||||
```json
|
||||
{
|
||||
"response": "final text chunk",
|
||||
"end_of_stream": true,
|
||||
"in_token": 150,
|
||||
"out_token": 500,
|
||||
"model": "model-name"
|
||||
}
|
||||
```
|
||||
|
||||
#### Изменения в API Python
|
||||
|
||||
API клиента Python должен поддерживать как потоковый, так и не потоковый режимы,
|
||||
при этом сохраняя обратную совместимость.
|
||||
|
||||
**Обновления LlmClient** (`trustgraph-base/trustgraph/clients/llm_client.py`):
|
||||
|
||||
```python
|
||||
class LlmClient(BaseClient):
|
||||
def request(self, system, prompt, timeout=300, streaming=False):
|
||||
"""
|
||||
Non-streaming request (backward compatible).
|
||||
Returns complete response string.
|
||||
"""
|
||||
# Existing behavior when streaming=False
|
||||
|
||||
async def request_stream(self, system, prompt, timeout=300):
|
||||
"""
|
||||
Streaming request.
|
||||
Yields response chunks as they arrive.
|
||||
"""
|
||||
# New async generator method
|
||||
```
|
||||
|
||||
**Обновления PromptClient** (`trustgraph-base/trustgraph/base/prompt_client.py`):
|
||||
|
||||
Аналогичный шаблон с параметром `streaming` и вариантом асинхронного генератора.
|
||||
|
||||
#### Изменения инструмента командной строки
|
||||
|
||||
**tg-invoke-llm** (`trustgraph-cli/trustgraph/cli/invoke_llm.py`):
|
||||
|
||||
```
|
||||
tg-invoke-llm [system] [prompt] [--no-streaming] [-u URL] [-f flow-id]
|
||||
```
|
||||
|
||||
По умолчанию включен режим потоковой передачи для улучшения интерактивного пользовательского опыта.
|
||||
Флаг `--no-streaming` отключает режим потоковой передачи.
|
||||
В режиме потоковой передачи: выводите токены в стандартный вывод по мере их поступления.
|
||||
В режиме, когда потоковая передача отключена: дождитесь получения полного ответа, а затем выведите его.
|
||||
|
||||
**tg-invoke-prompt** (`trustgraph-cli/trustgraph/cli/invoke_prompt.py`):
|
||||
|
||||
```
|
||||
tg-invoke-prompt [template-id] [var=value...] [--no-streaming] [-u URL] [-f flow-id]
|
||||
```
|
||||
|
||||
Такой же шаблон, как у `tg-invoke-llm`.
|
||||
|
||||
#### Изменения базового класса LLM Service.
|
||||
|
||||
**LlmService** (`trustgraph-base/trustgraph/base/llm_service.py`):
|
||||
|
||||
```python
|
||||
class LlmService(FlowProcessor):
|
||||
async def on_request(self, msg, consumer, flow):
|
||||
request = msg.value()
|
||||
streaming = getattr(request, 'streaming', False)
|
||||
|
||||
if streaming and self.supports_streaming():
|
||||
async for chunk in self.generate_content_stream(...):
|
||||
await self.send_response(chunk, end_of_stream=False)
|
||||
await self.send_response(final_chunk, end_of_stream=True)
|
||||
else:
|
||||
response = await self.generate_content(...)
|
||||
await self.send_response(response, end_of_stream=True)
|
||||
|
||||
def supports_streaming(self):
|
||||
"""Override in subclass to indicate streaming support."""
|
||||
return False
|
||||
|
||||
async def generate_content_stream(self, system, prompt, model, temperature):
|
||||
"""Override in subclass to implement streaming."""
|
||||
raise NotImplementedError()
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
### Фаза 2: Проверка концепции VertexAI
|
||||
|
||||
Фаза 2 реализует потоковую передачу данных в одном провайдере (VertexAI) для проверки
|
||||
инфраструктуры и обеспечения сквозного тестирования.
|
||||
|
||||
#### Реализация VertexAI
|
||||
|
||||
**Модуль:** `trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py`
|
||||
|
||||
**Изменения:**
|
||||
|
||||
1. Переопределить `supports_streaming()` для возврата `True`
|
||||
2. Реализовать асинхронный генератор `generate_content_stream()`
|
||||
3. Поддержка моделей Gemini и Claude (через VertexAI Anthropic API)
|
||||
|
||||
**Потоковая передача данных Gemini:**
|
||||
|
||||
```python
|
||||
async def generate_content_stream(self, system, prompt, model, temperature):
|
||||
model_instance = self.get_model(model, temperature)
|
||||
response = model_instance.generate_content(
|
||||
[system, prompt],
|
||||
stream=True # Enable streaming
|
||||
)
|
||||
for chunk in response:
|
||||
yield LlmChunk(
|
||||
text=chunk.text,
|
||||
in_token=None, # Available only in final chunk
|
||||
out_token=None,
|
||||
)
|
||||
# Final chunk includes token counts from response.usage_metadata
|
||||
```
|
||||
|
||||
**Claude (через VertexAI Anthropic) в режиме потоковой передачи:**
|
||||
|
||||
```python
|
||||
async def generate_content_stream(self, system, prompt, model, temperature):
|
||||
with self.anthropic_client.messages.stream(...) as stream:
|
||||
for text in stream.text_stream:
|
||||
yield LlmChunk(text=text)
|
||||
# Token counts from stream.get_final_message()
|
||||
```
|
||||
|
||||
#### Тестирование
|
||||
|
||||
Юнит-тесты для сборки потоковых ответов
|
||||
Интеграционные тесты с VertexAI (Gemini и Claude)
|
||||
Комплексные тесты: CLI -> Gateway -> Pulsar -> VertexAI -> back
|
||||
Тесты обратной совместимости: Непотоковые запросы по-прежнему работают
|
||||
|
||||
--
|
||||
|
||||
### Фаза 3: Все провайдеры LLM
|
||||
|
||||
Фаза 3 расширяет поддержку потоковой передачи для всех провайдеров LLM в системе.
|
||||
|
||||
#### Статус реализации для каждого провайдера
|
||||
|
||||
Каждый провайдер должен либо:
|
||||
1. **Полная поддержка потоковой передачи**: Реализовать `generate_content_stream()`
|
||||
2. **Режим совместимости**: Правильно обрабатывать флаг `end_of_stream`
|
||||
(возвращать единый ответ с `end_of_stream=true`)
|
||||
|
||||
| Провайдер | Пакет | Поддержка потоковой передачи |
|
||||
|----------|---------|-------------------|
|
||||
| OpenAI | trustgraph-flow | Полная (нативная API потоковой передачи) |
|
||||
| Claude/Anthropic | trustgraph-flow | Полная (нативная API потоковой передачи) |
|
||||
| Ollama | trustgraph-flow | Полная (нативная API потоковой передачи) |
|
||||
| Cohere | trustgraph-flow | Полная (нативная API потоковой передачи) |
|
||||
| Mistral | trustgraph-flow | Полная (нативная API потоковой передачи) |
|
||||
| Azure OpenAI | trustgraph-flow | Полная (нативная API потоковой передачи) |
|
||||
| Google AI Studio | trustgraph-flow | Полная (нативная API потоковой передачи) |
|
||||
| VertexAI | trustgraph-vertexai | Полная (Фаза 2) |
|
||||
| Bedrock | trustgraph-bedrock | Полная (нативная API потоковой передачи) |
|
||||
| LM Studio | trustgraph-flow | Полная (совместима с OpenAI) |
|
||||
| LlamaFile | trustgraph-flow | Полная (совместима с OpenAI) |
|
||||
| vLLM | trustgraph-flow | Полная (совместима с OpenAI) |
|
||||
| TGI | trustgraph-flow | Будет определено |
|
||||
| Azure | trustgraph-flow | Будет определено |
|
||||
|
||||
#### Шаблон реализации
|
||||
|
||||
Для провайдеров, совместимых с OpenAI (OpenAI, LM Studio, LlamaFile, vLLM):
|
||||
|
||||
```python
|
||||
async def generate_content_stream(self, system, prompt, model, temperature):
|
||||
response = await self.client.chat.completions.create(
|
||||
model=model,
|
||||
messages=[
|
||||
{"role": "system", "content": system},
|
||||
{"role": "user", "content": prompt}
|
||||
],
|
||||
temperature=temperature,
|
||||
stream=True
|
||||
)
|
||||
async for chunk in response:
|
||||
if chunk.choices[0].delta.content:
|
||||
yield LlmChunk(text=chunk.choices[0].delta.content)
|
||||
```
|
||||
|
||||
--
|
||||
|
||||
### Фаза 4: API агента
|
||||
|
||||
Фаза 4 расширяет потоковую передачу на API агента. Это более сложный процесс, поскольку
|
||||
API агента изначально предназначен для работы с несколькими сообщениями (мысль → действие → наблюдение
|
||||
→ повтор → окончательный ответ).
|
||||
|
||||
#### Текущая схема агента
|
||||
|
||||
```python
|
||||
class AgentStep(Record):
|
||||
thought = String()
|
||||
action = String()
|
||||
arguments = Map(String())
|
||||
observation = String()
|
||||
user = String()
|
||||
|
||||
class AgentRequest(Record):
|
||||
question = String()
|
||||
state = String()
|
||||
group = Array(String())
|
||||
history = Array(AgentStep())
|
||||
user = String()
|
||||
|
||||
class AgentResponse(Record):
|
||||
answer = String()
|
||||
error = Error()
|
||||
thought = String()
|
||||
observation = String()
|
||||
```
|
||||
|
||||
#### Предлагаемые изменения схемы агента
|
||||
|
||||
**Запрос изменений:**
|
||||
|
||||
```python
|
||||
class AgentRequest(Record):
|
||||
question = String()
|
||||
state = String()
|
||||
group = Array(String())
|
||||
history = Array(AgentStep())
|
||||
user = String()
|
||||
streaming = Boolean() # NEW: Default false
|
||||
```
|
||||
|
||||
**Изменения в ответах:**
|
||||
|
||||
Агент генерирует несколько типов выходных данных в процессе рассуждения:
|
||||
Мысли (рассуждения)
|
||||
Действия (вызовы инструментов)
|
||||
Наблюдения (результаты работы инструментов)
|
||||
Ответ (окончательный ответ)
|
||||
Ошибки
|
||||
|
||||
Поскольку `chunk_type` указывает на тип передаваемого контента, отдельные
|
||||
поля `answer`, `error`, `thought` и `observation` можно объединить в
|
||||
одно поле `content`:
|
||||
|
||||
```python
|
||||
class AgentResponse(Record):
|
||||
chunk_type = String() # "thought", "action", "observation", "answer", "error"
|
||||
content = String() # The actual content (interpretation depends on chunk_type)
|
||||
end_of_message = Boolean() # Current thought/action/observation/answer is complete
|
||||
end_of_dialog = Boolean() # Entire agent dialog is complete
|
||||
```
|
||||
|
||||
**Семантика полей:**
|
||||
|
||||
`chunk_type`: Указывает, какой тип содержимого находится в поле `content`
|
||||
`"thought"`: Рассуждения/мысли агента
|
||||
`"action"`: Используемый инструмент/действие
|
||||
`"observation"`: Результат выполнения инструмента
|
||||
`"answer"`: Окончательный ответ на вопрос пользователя
|
||||
`"error"`: Сообщение об ошибке
|
||||
|
||||
`content`: Фактическое потоковое содержимое, интерпретируемое на основе `chunk_type`
|
||||
|
||||
`end_of_message`: Когда `true`, текущий тип фрагмента завершен
|
||||
Пример: Все токены для текущей мысли были отправлены
|
||||
Позволяет клиентам знать, когда переходить к следующему этапу
|
||||
|
||||
`end_of_dialog`: Когда `true`, все взаимодействие с агентом завершено
|
||||
Это последнее сообщение в потоке
|
||||
|
||||
#### Поведение потоковой передачи агента
|
||||
|
||||
Когда `streaming=true`:
|
||||
|
||||
1. **Потоковая передача мыслей:**
|
||||
Несколько фрагментов с `chunk_type="thought"`, `end_of_message=false`
|
||||
Последний фрагмент мысли содержит `end_of_message=true`
|
||||
2. **Уведомление о действии:**
|
||||
Один фрагмент с `chunk_type="action"`, `end_of_message=true`
|
||||
3. **Наблюдение:**
|
||||
Один или несколько фрагментов с `chunk_type="observation"`, последний содержит `end_of_message=true`
|
||||
4. **Повторяйте** шаги 1-3, пока агент рассуждает
|
||||
5. **Окончательный ответ:**
|
||||
`chunk_type="answer"` с окончательным ответом в `content`
|
||||
Последний фрагмент содержит `end_of_message=true`, `end_of_dialog=true`
|
||||
|
||||
**Пример последовательности потоковой передачи:**
|
||||
|
||||
```
|
||||
{chunk_type: "thought", content: "I need to", end_of_message: false, end_of_dialog: false}
|
||||
{chunk_type: "thought", content: " search for...", end_of_message: true, end_of_dialog: false}
|
||||
{chunk_type: "action", content: "search", end_of_message: true, end_of_dialog: false}
|
||||
{chunk_type: "observation", content: "Found: ...", end_of_message: true, end_of_dialog: false}
|
||||
{chunk_type: "thought", content: "Based on this", end_of_message: false, end_of_dialog: false}
|
||||
{chunk_type: "thought", content: " I can answer...", end_of_message: true, end_of_dialog: false}
|
||||
{chunk_type: "answer", content: "The answer is...", end_of_message: true, end_of_dialog: true}
|
||||
```
|
||||
|
||||
Когда `streaming=false`:
|
||||
Текущее поведение сохранено
|
||||
Единый ответ с полным ответом
|
||||
`end_of_message=true`, `end_of_dialog=true`
|
||||
|
||||
#### Шлюз и Python API
|
||||
|
||||
Шлюз: Новый SSE/WebSocket endpoint для потоковой передачи данных от агента
|
||||
Python API: Новый асинхронный генератор `agent_stream()`
|
||||
|
||||
--
|
||||
|
||||
## Соображения безопасности
|
||||
|
||||
**Отсутствие новых уязвимостей**: Потоковая передача использует ту же аутентификацию/авторизацию
|
||||
**Ограничение скорости**: При необходимости применяйте ограничения скорости на токен или на фрагмент
|
||||
**Обработка соединений**: Правильно завершайте потоки при отключении клиента
|
||||
**Управление временем ожидания**: Запросы потоковой передачи требуют соответствующей обработки времени ожидания
|
||||
|
||||
## Соображения производительности
|
||||
|
||||
**Память**: Потоковая передача снижает пиковое использование памяти (без полной буферизации ответа)
|
||||
**Задержка**: Время до первого токена значительно сокращено
|
||||
**Накладные расходы на соединение**: Соединения SSE/WebSocket имеют накладные расходы на поддержание соединения
|
||||
**Производительность Pulsar**: Несколько небольших сообщений против одного большого сообщения - компромисс
|
||||
tradeoff
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
### Юнит-тесты
|
||||
Сериализация/десериализация схемы с новыми полями
|
||||
Обратная совместимость (отсутствующие поля используют значения по умолчанию)
|
||||
Логика сборки фрагментов
|
||||
|
||||
### Интеграционные тесты
|
||||
Реализация потоковой передачи каждого поставщика LLM
|
||||
Потоковые конечные точки API шлюза
|
||||
Методы потоковой передачи клиента на Python
|
||||
|
||||
### Комплексные тесты
|
||||
Вывод потоковой передачи инструмента командной строки
|
||||
Полный поток: Клиент → Шлюз → Pulsar → LLM → обратно
|
||||
Смешанные потоковые и не потоковые рабочие нагрузки
|
||||
|
||||
### Тесты обратной совместимости
|
||||
Существующие клиенты работают без изменений
|
||||
Запросы без потоковой передачи ведут себя идентично
|
||||
|
||||
## План миграции
|
||||
|
||||
### Фаза 1: Инфраструктура
|
||||
Развертывание изменений схемы (обратная совместимость)
|
||||
Развертывание обновлений API шлюза
|
||||
Развертывание обновлений Python API
|
||||
Выпуск обновлений инструмента командной строки
|
||||
|
||||
### Фаза 2: VertexAI
|
||||
Развернуть поточную реализацию VertexAI.
|
||||
Проверить с помощью тестовых нагрузок.
|
||||
|
||||
### Фаза 3: Все провайдеры
|
||||
Постепенно внедрять обновления для провайдеров.
|
||||
Отслеживать наличие проблем.
|
||||
|
||||
### Фаза 4: API агента
|
||||
Развернуть изменения схемы агента.
|
||||
Развернуть поточную реализацию агента.
|
||||
Обновить документацию.
|
||||
|
||||
## График
|
||||
|
||||
| Фаза | Описание | Зависимости |
|
||||
|-------|-------------|--------------|
|
||||
| Фаза 1 | Инфраструктура | Отсутствуют |
|
||||
| Фаза 2 | VertexAI, пилотный проект | Фаза 1 |
|
||||
| Фаза 3 | Все провайдеры | Фаза 2 |
|
||||
| Фаза 4 | API агента | Фаза 3 |
|
||||
|
||||
## Принятые решения по проектированию
|
||||
|
||||
В процессе разработки спецификации были решены следующие вопросы:
|
||||
|
||||
1. **Количество токенов в потоке**: Количество токенов указывается как разница, а не как текущая сумма.
|
||||
Потребители могут суммировать их, если это необходимо. Это соответствует тому, как большинство провайдеров
|
||||
сообщают об использовании и упрощает реализацию.
|
||||
|
||||
2. **Обработка ошибок в потоках**: В случае возникновения ошибки, поле `error`
|
||||
заполняется, и другие поля не требуются. Ошибка всегда является последним
|
||||
сообщением - после ошибки не допускаются и не ожидаются последующие сообщения.
|
||||
Для потоков LLM/Prompt: `end_of_stream=true`. Для потоков Agent:
|
||||
`chunk_type="error"` с `end_of_dialog=true`.
|
||||
|
||||
3. **Восстановление после частичного ответа**: Протокол обмена сообщениями (Pulsar) устойчив,
|
||||
поэтому повторная отправка сообщений на уровне отдельных сообщений не требуется.
|
||||
Если клиент теряет отслеживание потока или отключается, он должен повторить
|
||||
полный запрос с самого начала.
|
||||
4. **Быстрая потоковая передача**: Потоковая передача поддерживается только для текстовых ответов (`text`).
|
||||
ответы, а не для структурированных (`object`) ответов. Сервис запросов знает заранее,
|
||||
будет ли вывод в формате JSON или текста, в зависимости от шаблона запроса. Если
|
||||
выполняется запрос на потоковую передачу для запроса, предназначенного для вывода JSON,
|
||||
сервис должен либо:
|
||||
Вернуть полный JSON в одном ответе с `end_of_stream=true`, или
|
||||
Отклонить запрос на потоковую передачу с ошибкой.
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
На данный момент их нет.
|
||||
|
||||
## Ссылки
|
||||
|
||||
Текущая схема LLM: `trustgraph-base/trustgraph/schema/services/llm.py`
|
||||
Текущая схема запросов: `trustgraph-base/trustgraph/schema/services/prompt.py`
|
||||
Текущая схема агента: `trustgraph-base/trustgraph/schema/services/agent.py`
|
||||
Базовый URL службы LLM: `trustgraph-base/trustgraph/base/llm_service.py`
|
||||
Провайдер VertexAI: `trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py`
|
||||
API шлюза: `trustgraph-base/trustgraph/api/`
|
||||
Инструменты CLI: `trustgraph-cli/trustgraph/cli/`
|
||||
621
docs/tech-specs/ru/structured-data-2.ru.md
Normal file
621
docs/tech-specs/ru/structured-data-2.ru.md
Normal file
|
|
@ -0,0 +1,621 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Техническая спецификация структурированных данных (часть 2)"
|
||||
parent: "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. Это вызывало проблемы при эволюции схем, поскольку изменения структуры таблицы требовали миграций.
|
||||
|
||||
В новой разработке используется единая унифицированная таблица для всех данных строк:
|
||||
|
||||
```sql
|
||||
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']`
|
||||
|
||||
#### Шаблоны запросов
|
||||
|
||||
Все запросы следуют одной и той же схеме, независимо от того, какой индекс используется:
|
||||
|
||||
```sql
|
||||
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`, которые содержат данные.
|
||||
|
||||
#### Таблица отслеживания разделов
|
||||
|
||||
Вторичная таблица поиска отслеживает, какие разделы существуют:
|
||||
|
||||
```sql
|
||||
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)`, а не для каждой строки.
|
||||
Таблица поиска отражает индексы, которые были активны во время записи данных.
|
||||
Изменения схемы во время импорта обрабатываются правильно.
|
||||
|
||||
#### Операции удаления
|
||||
|
||||
**Удаление коллекции:**
|
||||
```sql
|
||||
-- 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';
|
||||
```
|
||||
|
||||
**Удалить коллекцию и схему:**
|
||||
```sql
|
||||
-- 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 содержит:
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
```sql
|
||||
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)`.
|
||||
Создание коллекции, если это необходимо (создание при первой записи).
|
||||
Добавление точки с вектором и полезной нагрузкой.
|
||||
|
||||
#### Типы сообщений.
|
||||
|
||||
```python
|
||||
@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 векторных представлений обрабатывает семантическую схожесть
|
||||
Рабочий процесс пользователя: нечеткий поиск с помощью векторных представлений для поиска кандидатов, затем точный запрос для получения полных данных строки
|
||||
|
||||
#### Схема запроса/ответа
|
||||
|
||||
```python
|
||||
@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`
|
||||
|
||||
Определение интерфейса в шаблоне потока:
|
||||
```json
|
||||
{
|
||||
"interfaces": {
|
||||
"row-embeddings": {
|
||||
"request": "non-persistent://tg/request/row-embeddings:{id}",
|
||||
"response": "non-persistent://tg/response/row-embeddings:{id}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Поддержка Python SDK
|
||||
|
||||
SDK предоставляет методы для запросов векторных представлений строк:
|
||||
|
||||
```python
|
||||
# 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`
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
#### Типичная схема использования
|
||||
|
||||
Запрос векторных представлений строк обычно используется как часть процесса поиска приблизительного соответствия для точного поиска:
|
||||
|
||||
```python
|
||||
# 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 | Создание таблиц для каждой схемы, вторичные индексы, вставка/удаление |
|
||||
|
||||
**Текущая схема запросов:**
|
||||
```sql
|
||||
SELECT * FROM {keyspace}.o_{schema_name}
|
||||
WHERE collection = 'X' AND email = 'foo@bar.com'
|
||||
ALLOW FILTERING
|
||||
```
|
||||
|
||||
**Новая схема запроса:**
|
||||
```sql
|
||||
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) |
|
||||
|
||||
## Ссылки
|
||||
|
||||
[Техническая спецификация структурированных данных](structured-data.md)
|
||||
567
docs/tech-specs/ru/structured-data-descriptor.ru.md
Normal file
567
docs/tech-specs/ru/structured-data-descriptor.ru.md
Normal file
|
|
@ -0,0 +1,567 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Дескриптор структурированных данных - это язык конфигурации на основе JSON, который описывает, как анализировать, преобразовывать и импортировать структурированные данные в TrustGraph. Он предоставляет декларативный подход к импорту данных, поддерживающий различные форматы входных данных и сложные конвейеры преобразований без необходимости написания пользовательского кода.
|
||||
|
||||
## Основные понятия
|
||||
|
||||
### 1. Определение формата
|
||||
Описывает тип входного файла и параметры анализа. Определяет, какой анализатор использовать и как интерпретировать исходные данные.
|
||||
|
||||
### 2. Отображения полей
|
||||
Отображает пути источника на целевые поля с преобразованиями. Определяет, как данные передаются из исходных источников в поля выходной схемы.
|
||||
|
||||
### 3. Конвейер преобразований
|
||||
Цепочка преобразований данных, которые могут быть применены к значениям полей, включая:
|
||||
Очистка данных (удаление лишних пробелов, нормализация)
|
||||
Преобразование формата (разбор дат, приведение типов)
|
||||
Вычисления (арифметические операции, манипуляции со строками)
|
||||
Поиск (таблицы ссылок, замены)
|
||||
|
||||
### 4. Правила проверки
|
||||
Проверки качества данных, применяемые для обеспечения целостности данных:
|
||||
Проверка типов
|
||||
Проверки диапазонов
|
||||
Сопоставление с образцом (регулярные выражения)
|
||||
Проверка обязательных полей
|
||||
Пользовательская логика проверки
|
||||
|
||||
### 5. Глобальные настройки
|
||||
Конфигурация, применяемая ко всему процессу импорта:
|
||||
Таблицы поиска для обогащения данных
|
||||
Глобальные переменные и константы
|
||||
Спецификации формата вывода
|
||||
Политики обработки ошибок
|
||||
|
||||
## Стратегия реализации
|
||||
|
||||
Реализация импортера следует следующей схеме:
|
||||
|
||||
1. **Анализ конфигурации** - Загрузка и проверка JSON-дескриптора
|
||||
2. **Инициализация анализатора** - Загрузка соответствующего анализатора (CSV, XML, JSON и т.д.) на основе `format.type`
|
||||
3. **Применение предварительной обработки** - Выполнение глобальных фильтров и преобразований
|
||||
4. **Обработка записей** - Для каждой входной записи:
|
||||
Извлечение данных с использованием путей источника (JSONPath, XPath, имена столбцов)
|
||||
Применение преобразований на уровне поля в последовательности
|
||||
Проверка результатов на соответствие определенным правилам
|
||||
Применение значений по умолчанию для отсутствующих данных
|
||||
5. **Применение постобработки** - Выполнение дедупликации, агрегации и т.д.
|
||||
6. **Генерация вывода** - Создание данных в указанном целевом формате
|
||||
|
||||
## Поддержка выражений путей
|
||||
|
||||
Различные форматы входных данных используют соответствующие языки выражений путей:
|
||||
|
||||
**CSV**: Имена столбцов или индексы (`"column_name"` или `"[2]"`)
|
||||
**JSON**: Синтаксис JSONPath (`"$.user.profile.email"`)
|
||||
**XML**: Выражения XPath (`"//product[@id='123']/price"`)
|
||||
**Fixed-width**: Имена полей из определений полей
|
||||
|
||||
## Преимущества
|
||||
|
||||
**Единая кодовая база** - Один импортер обрабатывает несколько форматов входных данных
|
||||
**Удобство использования** - Пользователи, не являющиеся специалистами, могут создавать конфигурации
|
||||
**Повторное использование** - Конфигурации можно обмениваться и версионировать
|
||||
**Гибкость** - Сложные преобразования без пользовательского кода
|
||||
**Надежность** - Встроенная проверка и комплексная обработка ошибок
|
||||
**Поддерживаемость** - Декларативный подход снижает сложность реализации
|
||||
|
||||
## Спецификация языка
|
||||
|
||||
Дескриптор структурированных данных использует формат конфигурации JSON со следующей структурой верхнего уровня:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"metadata": {
|
||||
"name": "Configuration Name",
|
||||
"description": "Description of what this config does",
|
||||
"author": "Author Name",
|
||||
"created": "2024-01-01T00:00:00Z"
|
||||
},
|
||||
"format": { ... },
|
||||
"globals": { ... },
|
||||
"preprocessing": [ ... ],
|
||||
"mappings": [ ... ],
|
||||
"postprocessing": [ ... ],
|
||||
"output": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### Определение формата
|
||||
|
||||
Описывает формат входных данных и параметры разбора:
|
||||
|
||||
```json
|
||||
{
|
||||
"format": {
|
||||
"type": "csv|json|xml|fixed-width|excel|parquet",
|
||||
"encoding": "utf-8",
|
||||
"options": {
|
||||
// Format-specific options
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Варианты формата CSV
|
||||
```json
|
||||
{
|
||||
"format": {
|
||||
"type": "csv",
|
||||
"options": {
|
||||
"delimiter": ",",
|
||||
"quote_char": "\"",
|
||||
"escape_char": "\\",
|
||||
"skip_rows": 1,
|
||||
"has_header": true,
|
||||
"null_values": ["", "NULL", "null", "N/A"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Варианты формата JSON
|
||||
```json
|
||||
{
|
||||
"format": {
|
||||
"type": "json",
|
||||
"options": {
|
||||
"root_path": "$.data",
|
||||
"array_mode": "records|single",
|
||||
"flatten": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Варианты формата XML
|
||||
```json
|
||||
{
|
||||
"format": {
|
||||
"type": "xml",
|
||||
"options": {
|
||||
"root_element": "//records/record",
|
||||
"namespaces": {
|
||||
"ns": "http://example.com/namespace"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Глобальные настройки
|
||||
|
||||
Определите таблицы поиска, переменные и глобальную конфигурацию:
|
||||
|
||||
```json
|
||||
{
|
||||
"globals": {
|
||||
"variables": {
|
||||
"current_date": "2024-01-01",
|
||||
"batch_id": "BATCH_001",
|
||||
"default_confidence": 0.8
|
||||
},
|
||||
"lookup_tables": {
|
||||
"country_codes": {
|
||||
"US": "United States",
|
||||
"UK": "United Kingdom",
|
||||
"CA": "Canada"
|
||||
},
|
||||
"status_mapping": {
|
||||
"1": "active",
|
||||
"0": "inactive"
|
||||
}
|
||||
},
|
||||
"constants": {
|
||||
"source_system": "legacy_crm",
|
||||
"import_type": "full"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Отображение полей
|
||||
|
||||
Определите, как исходные данные сопоставляются с целевыми полями с использованием преобразований:
|
||||
|
||||
```json
|
||||
{
|
||||
"mappings": [
|
||||
{
|
||||
"target_field": "person_name",
|
||||
"source": "$.name",
|
||||
"transforms": [
|
||||
{"type": "trim"},
|
||||
{"type": "title_case"},
|
||||
{"type": "required"}
|
||||
],
|
||||
"validation": [
|
||||
{"type": "min_length", "value": 2},
|
||||
{"type": "max_length", "value": 100},
|
||||
{"type": "pattern", "value": "^[A-Za-z\\s]+$"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_field": "age",
|
||||
"source": "$.age",
|
||||
"transforms": [
|
||||
{"type": "to_int"},
|
||||
{"type": "default", "value": 0}
|
||||
],
|
||||
"validation": [
|
||||
{"type": "range", "min": 0, "max": 150}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_field": "country",
|
||||
"source": "$.country_code",
|
||||
"transforms": [
|
||||
{"type": "lookup", "table": "country_codes"},
|
||||
{"type": "default", "value": "Unknown"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Типы преобразований
|
||||
|
||||
Доступные функции преобразования:
|
||||
|
||||
#### Преобразования строк
|
||||
```json
|
||||
{"type": "trim"},
|
||||
{"type": "upper"},
|
||||
{"type": "lower"},
|
||||
{"type": "title_case"},
|
||||
{"type": "replace", "pattern": "old", "replacement": "new"},
|
||||
{"type": "regex_replace", "pattern": "\\d+", "replacement": "XXX"},
|
||||
{"type": "substring", "start": 0, "end": 10},
|
||||
{"type": "pad_left", "length": 10, "char": "0"}
|
||||
```
|
||||
|
||||
#### Преобразования типов
|
||||
```json
|
||||
{"type": "to_string"},
|
||||
{"type": "to_int"},
|
||||
{"type": "to_float"},
|
||||
{"type": "to_bool"},
|
||||
{"type": "to_date", "format": "YYYY-MM-DD"},
|
||||
{"type": "parse_json"}
|
||||
```
|
||||
|
||||
#### Операции с данными
|
||||
```json
|
||||
{"type": "default", "value": "default_value"},
|
||||
{"type": "lookup", "table": "table_name"},
|
||||
{"type": "concat", "values": ["field1", " - ", "field2"]},
|
||||
{"type": "calculate", "expression": "${field1} + ${field2}"},
|
||||
{"type": "conditional", "condition": "${age} > 18", "true_value": "adult", "false_value": "minor"}
|
||||
```
|
||||
|
||||
### Правила проверки
|
||||
|
||||
Проверки качества данных с настраиваемой обработкой ошибок:
|
||||
|
||||
#### Базовые проверки
|
||||
```json
|
||||
{"type": "required"},
|
||||
{"type": "not_null"},
|
||||
{"type": "min_length", "value": 5},
|
||||
{"type": "max_length", "value": 100},
|
||||
{"type": "range", "min": 0, "max": 1000},
|
||||
{"type": "pattern", "value": "^[A-Z]{2,3}$"},
|
||||
{"type": "in_list", "values": ["active", "inactive", "pending"]}
|
||||
```
|
||||
|
||||
#### Пользовательские проверки
|
||||
```json
|
||||
{
|
||||
"type": "custom",
|
||||
"expression": "${age} >= 18 && ${country} == 'US'",
|
||||
"message": "Must be 18+ and in US"
|
||||
},
|
||||
{
|
||||
"type": "cross_field",
|
||||
"fields": ["start_date", "end_date"],
|
||||
"expression": "${start_date} < ${end_date}",
|
||||
"message": "Start date must be before end date"
|
||||
}
|
||||
```
|
||||
|
||||
### Предварительная обработка и постобработка
|
||||
|
||||
Глобальные операции, применяемые до/после сопоставления полей:
|
||||
|
||||
```json
|
||||
{
|
||||
"preprocessing": [
|
||||
{
|
||||
"type": "filter",
|
||||
"condition": "${status} != 'deleted'"
|
||||
},
|
||||
{
|
||||
"type": "sort",
|
||||
"field": "created_date",
|
||||
"order": "asc"
|
||||
}
|
||||
],
|
||||
"postprocessing": [
|
||||
{
|
||||
"type": "deduplicate",
|
||||
"key_fields": ["email", "phone"]
|
||||
},
|
||||
{
|
||||
"type": "aggregate",
|
||||
"group_by": ["country"],
|
||||
"functions": {
|
||||
"total_count": {"type": "count"},
|
||||
"avg_age": {"type": "avg", "field": "age"}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Настройка вывода
|
||||
|
||||
Определите, как обработанные данные должны быть выведены:
|
||||
|
||||
```json
|
||||
{
|
||||
"output": {
|
||||
"format": "trustgraph-objects",
|
||||
"schema_name": "person",
|
||||
"options": {
|
||||
"batch_size": 1000,
|
||||
"confidence": 0.9,
|
||||
"source_span_field": "raw_text",
|
||||
"metadata": {
|
||||
"source": "crm_import",
|
||||
"version": "1.0"
|
||||
}
|
||||
},
|
||||
"error_handling": {
|
||||
"on_validation_error": "skip|fail|log",
|
||||
"on_transform_error": "skip|fail|default",
|
||||
"max_errors": 100,
|
||||
"error_output": "errors.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Полный пример
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"metadata": {
|
||||
"name": "Customer Import from CRM CSV",
|
||||
"description": "Imports customer data from legacy CRM system",
|
||||
"author": "Data Team",
|
||||
"created": "2024-01-01T00:00:00Z"
|
||||
},
|
||||
"format": {
|
||||
"type": "csv",
|
||||
"encoding": "utf-8",
|
||||
"options": {
|
||||
"delimiter": ",",
|
||||
"has_header": true,
|
||||
"skip_rows": 1
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"variables": {
|
||||
"import_date": "2024-01-01",
|
||||
"default_confidence": 0.85
|
||||
},
|
||||
"lookup_tables": {
|
||||
"country_codes": {
|
||||
"US": "United States",
|
||||
"CA": "Canada",
|
||||
"UK": "United Kingdom"
|
||||
}
|
||||
}
|
||||
},
|
||||
"preprocessing": [
|
||||
{
|
||||
"type": "filter",
|
||||
"condition": "${status} == 'active'"
|
||||
}
|
||||
],
|
||||
"mappings": [
|
||||
{
|
||||
"target_field": "full_name",
|
||||
"source": "customer_name",
|
||||
"transforms": [
|
||||
{"type": "trim"},
|
||||
{"type": "title_case"}
|
||||
],
|
||||
"validation": [
|
||||
{"type": "required"},
|
||||
{"type": "min_length", "value": 2}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_field": "email",
|
||||
"source": "email_address",
|
||||
"transforms": [
|
||||
{"type": "trim"},
|
||||
{"type": "lower"}
|
||||
],
|
||||
"validation": [
|
||||
{"type": "pattern", "value": "^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_field": "age",
|
||||
"source": "age",
|
||||
"transforms": [
|
||||
{"type": "to_int"},
|
||||
{"type": "default", "value": 0}
|
||||
],
|
||||
"validation": [
|
||||
{"type": "range", "min": 0, "max": 120}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_field": "country",
|
||||
"source": "country_code",
|
||||
"transforms": [
|
||||
{"type": "lookup", "table": "country_codes"},
|
||||
{"type": "default", "value": "Unknown"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"format": "trustgraph-objects",
|
||||
"schema_name": "customer",
|
||||
"options": {
|
||||
"confidence": "${default_confidence}",
|
||||
"batch_size": 500
|
||||
},
|
||||
"error_handling": {
|
||||
"on_validation_error": "log",
|
||||
"max_errors": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Подсказка для LLM для генерации описаний
|
||||
|
||||
Следующую подсказку можно использовать, чтобы LLM проанализировал примеры данных и сгенерировал конфигурацию описания:
|
||||
|
||||
```
|
||||
I need you to analyze the provided data sample and create a Structured Data Descriptor configuration in JSON format.
|
||||
|
||||
The descriptor should follow this specification:
|
||||
- version: "1.0"
|
||||
- metadata: Configuration name, description, author, and creation date
|
||||
- format: Input format type and parsing options
|
||||
- globals: Variables, lookup tables, and constants
|
||||
- preprocessing: Filters and transformations applied before mapping
|
||||
- mappings: Field-by-field mapping from source to target with transformations and validations
|
||||
- postprocessing: Operations like deduplication or aggregation
|
||||
- output: Target format and error handling configuration
|
||||
|
||||
ANALYZE THE DATA:
|
||||
1. Identify the format (CSV, JSON, XML, etc.)
|
||||
2. Detect delimiters, encodings, and structure
|
||||
3. Find data types for each field
|
||||
4. Identify patterns and constraints
|
||||
5. Look for fields that need cleaning or transformation
|
||||
6. Find relationships between fields
|
||||
7. Identify lookup opportunities (codes that map to values)
|
||||
8. Detect required vs optional fields
|
||||
|
||||
CREATE THE DESCRIPTOR:
|
||||
For each field in the sample data:
|
||||
- Map it to an appropriate target field name
|
||||
- Add necessary transformations (trim, case conversion, type casting)
|
||||
- Include appropriate validations (required, patterns, ranges)
|
||||
- Set defaults for missing values
|
||||
|
||||
Include preprocessing if needed:
|
||||
- Filters to exclude invalid records
|
||||
- Sorting requirements
|
||||
|
||||
Include postprocessing if beneficial:
|
||||
- Deduplication on key fields
|
||||
- Aggregation for summary data
|
||||
|
||||
Configure output for TrustGraph:
|
||||
- format: "trustgraph-objects"
|
||||
- schema_name: Based on the data entity type
|
||||
- Appropriate error handling
|
||||
|
||||
DATA SAMPLE:
|
||||
[Insert data sample here]
|
||||
|
||||
ADDITIONAL CONTEXT (optional):
|
||||
- Target schema name: [if known]
|
||||
- Business rules: [any specific requirements]
|
||||
- Data quality issues to address: [known problems]
|
||||
|
||||
Generate a complete, valid Structured Data Descriptor configuration that will properly import this data into TrustGraph. Include comments explaining key decisions.
|
||||
```
|
||||
|
||||
### Пример использования запроса
|
||||
|
||||
```
|
||||
I need you to analyze the provided data sample and create a Structured Data Descriptor configuration in JSON format.
|
||||
|
||||
[Standard instructions from above...]
|
||||
|
||||
DATA SAMPLE:
|
||||
```csv
|
||||
CustomerID,Имя,Электронная почта,Возраст,Страна,Статус,Дата присоединения,Общая сумма покупок
|
||||
1001,"Smith, John",john.smith@email.com,35,US,1,2023-01-15,5420.50
|
||||
1002,"doe, jane",JANE.DOE@GMAIL.COM,28,CA,1,2023-03-22,3200.00
|
||||
1003,"Bob Johnson",bob@,62,UK,0,2022-11-01,0
|
||||
1004,"Alice Chen","alice.chen@company.org",41,US,1,2023-06-10,8900.25
|
||||
1005,,invalid-email,25,XX,1,2024-01-01,100
|
||||
```
|
||||
|
||||
ADDITIONAL CONTEXT:
|
||||
- Target schema name: customer
|
||||
- Business rules: Email should be valid and lowercase, names should be title case
|
||||
- Data quality issues: Some emails are invalid, some names are missing, country codes need mapping
|
||||
```
|
||||
|
||||
### Запрос на анализ существующих данных без использования образцов
|
||||
|
||||
```
|
||||
I need you to help me create a Structured Data Descriptor configuration for importing [data type] data.
|
||||
|
||||
The source data has these characteristics:
|
||||
- Format: [CSV/JSON/XML/etc]
|
||||
- Fields: [list the fields]
|
||||
- Data quality issues: [describe any known issues]
|
||||
- Volume: [approximate number of records]
|
||||
|
||||
Requirements:
|
||||
- [List any specific transformation needs]
|
||||
- [List any validation requirements]
|
||||
- [List any business rules]
|
||||
|
||||
Please generate a Structured Data Descriptor configuration that will:
|
||||
1. Parse the input format correctly
|
||||
2. Clean and standardize the data
|
||||
3. Validate according to the requirements
|
||||
4. Handle errors gracefully
|
||||
5. Output in TrustGraph ExtractedObject format
|
||||
|
||||
Focus on making the configuration robust and reusable.
|
||||
```
|
||||
147
docs/tech-specs/ru/structured-data-schemas.ru.md
Normal file
147
docs/tech-specs/ru/structured-data-schemas.ru.md
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
layout: default
|
||||
title: "Структурированные данные Pulsar: Изменения схемы"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# Структурированные данные Pulsar: Изменения схемы
|
||||
|
||||
> **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.
|
||||
|
||||
## Обзор
|
||||
|
||||
На основе спецификации `STRUCTURED_DATA.md`, этот документ предлагает необходимые дополнения и изменения схемы Pulsar для поддержки возможностей работы со структурированными данными в TrustGraph.
|
||||
|
||||
## Необходимые изменения схемы
|
||||
|
||||
### 1. Улучшения основной схемы
|
||||
|
||||
#### Улучшенное определение поля
|
||||
Сущность `Field` в файле `core/primitives.py` требует дополнительных свойств:
|
||||
|
||||
```python
|
||||
class Field(Record):
|
||||
name = String()
|
||||
type = String() # int, string, long, bool, float, double, timestamp
|
||||
size = Integer()
|
||||
primary = Boolean()
|
||||
description = String()
|
||||
# НОВЫЕ ПОЛЯ:
|
||||
required = Boolean() # Требуется ли поле
|
||||
enum_values = Array(String()) # Для полей типа enum
|
||||
indexed = Boolean() # Нужно ли индексировать поле
|
||||
```
|
||||
|
||||
### 2. Новые схемы знаний
|
||||
|
||||
#### 2.1 Отправка структурированных данных
|
||||
Новый файл: `knowledge/structured.py`
|
||||
|
||||
```python
|
||||
from pulsar.schema import Record, String, Bytes, Map
|
||||
from ..core.metadata import Metadata
|
||||
|
||||
class StructuredDataSubmission(Record):
|
||||
metadata = Metadata()
|
||||
format = String() # "json", "csv", "xml"
|
||||
schema_name = String() # Ссылка на схему в конфигурации
|
||||
data = Bytes() # Сырые данные для обработки
|
||||
options = Map(String()) # Опции, специфичные для формата
|
||||
```
|
||||
|
||||
#### 2.2 Структурированный запрос
|
||||
|
||||
#### 3.1 Преобразование NLP в структурированный запрос
|
||||
Новый файл: `services/nlp_query.py`
|
||||
|
||||
```python
|
||||
from pulsar.schema import Record, String, Array, Map, Integer, Double
|
||||
from ..core.primitives import Error
|
||||
|
||||
class NLPToStructuredQueryRequest(Record):
|
||||
natural_language_query = String()
|
||||
max_results = Integer()
|
||||
context_hints = Map(String()) # Дополнительный контекст для генерации запроса
|
||||
|
||||
class NLPToStructuredQueryResponse(Record):
|
||||
error = Error()
|
||||
graphql_query = String() # Сгенерированный GraphQL запрос
|
||||
variables = Map(String()) # Переменные GraphQL, если есть
|
||||
detected_schemas = Array(String()) # Какие схемы затрагивает запрос
|
||||
confidence = Double()
|
||||
```
|
||||
|
||||
#### 3.2 Структурированный запрос
|
||||
Новый файл: `services/structured_query.py`
|
||||
|
||||
```python
|
||||
from pulsar.schema import Record, String, Map, Array
|
||||
from ..core.primitives import Error
|
||||
|
||||
class StructuredQueryRequest(Record):
|
||||
query = String() # GraphQL запрос
|
||||
variables = Map(String()) # Переменные GraphQL
|
||||
operation_name = String() # Дополнительное имя операции для документов с несколькими операциями
|
||||
|
||||
class StructuredQueryResponse(Record):
|
||||
error = Error()
|
||||
data = String() # JSON-кодированный формат данных GraphQL
|
||||
errors = Array(String()) # Ошибки GraphQL, если есть
|
||||
```
|
||||
|
||||
#### 2.2 Вывод извлечения объектов
|
||||
Новый файл: `knowledge/object.py`
|
||||
|
||||
```python
|
||||
from pulsar.schema import Record, String, Map, Double
|
||||
from ..core.metadata import Metadata
|
||||
|
||||
class ExtractedObject(Record):
|
||||
metadata = Metadata()
|
||||
schema_name = String() # Какая схема принадлежит этому объекту
|
||||
values = Map(String()) # Имя поля -> значение
|
||||
confidence = Double()
|
||||
source_span = String() # Текстовый фрагмент, где был найден объект
|
||||
```
|
||||
|
||||
### 4. Улучшенные схемы знаний
|
||||
|
||||
#### 4.1 Улучшение встраивания объектов
|
||||
Обновите `knowledge/embeddings.py` для лучшей поддержки встраивания структурированных объектов:
|
||||
|
||||
```python
|
||||
class StructuredObjectEmbedding(Record):
|
||||
metadata = Metadata()
|
||||
vectors = Array(Array(Double()))
|
||||
schema_name = String()
|
||||
object_id = String() # Основной ключ
|
||||
field_embeddings = Map(Array(Double())) # Встраивание для каждого поля
|
||||
```
|
||||
|
||||
## Точки интеграции
|
||||
|
||||
### Интеграция потоков
|
||||
|
||||
Схемы будут использоваться в новых модулях потоков:
|
||||
- `trustgraph-flow/trustgraph/decoding/structured` - Использует StructuredDataSubmission
|
||||
- `trustgraph-flow/trustgraph/query/nlp_query/cassandra` - Использует схемы запросов NLP
|
||||
- `trustgraph-flow/trustgraph/query/objects/cassandra` - Использует схемы структурированных запросов
|
||||
- `trustgraph-flow/trustgraph/extract/object/row/` - Потребляет Chunk, производит ExtractedObject
|
||||
- `trustgraph-flow/trustgraph/storage/objects/cassandra` - Использует схему Rows
|
||||
- `trustgraph-flow/trustgraph/embeddings/object_embeddings/qdrant` - Использует схемы встраивания объектов
|
||||
|
||||
## Примечания по реализации
|
||||
|
||||
1. **Версионирование схемы**: Рассмотрите возможность добавления поля `version` к схеме RowSchema для поддержки будущих миграций.
|
||||
2. **Система типов**: `Field.type` должна поддерживать все родные типы Cassandra.
|
||||
3. **Операции пакета**: Большинство сервисов должны поддерживать как одиночные, так и пакетные операции.
|
||||
4. **Обработка ошибок**: Необходимо обеспечить единообразное сообщение об ошибках во всех новых сервисах.
|
||||
5. **Совместимость с существующими схемами**: Существующие схемы остаются неизменными, за исключением незначительных улучшений поля.
|
||||
|
||||
## Следующие шаги
|
||||
|
||||
1. Реализуйте файлы схем в новой структуре.
|
||||
2. Обновите существующие сервисы для распознавания новых типов схем.
|
||||
3. Реализуйте модули потоков, использующие эти схемы.
|
||||
4. Добавьте конечные точки gateway/rev-gateway для новых сервисов.
|
||||
5. Создайте модульные тесты для проверки схемы.
|
||||
260
docs/tech-specs/ru/structured-data.ru.md
Normal file
260
docs/tech-specs/ru/structured-data.ru.md
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает интеграцию TrustGraph с потоками структурированных данных, что позволяет системе работать с данными, которые могут быть представлены в виде строк в таблицах или объектов в хранилищах объектов. Интеграция поддерживает четыре основных сценария использования:
|
||||
|
||||
1. **Извлечение неструктурированных данных в структурированные**: Чтение неструктурированных источников данных, выявление и извлечение объектных структур и хранение их в табличном формате.
|
||||
2. **Импорт структурированных данных**: Загрузка данных, которые уже находятся в структурированных форматах, непосредственно в структурированное хранилище вместе с извлеченными данными.
|
||||
3. **Запросы на естественном языке**: Преобразование вопросов, заданных на естественном языке, в структурированные запросы для извлечения соответствующих данных из хранилища.
|
||||
4. **Прямые структурированные запросы**: Выполнение структурированных запросов непосредственно к хранилищу данных для точного извлечения данных.
|
||||
|
||||
## Цели
|
||||
|
||||
**Унифицированный доступ к данным**: Предоставление единого интерфейса для доступа как к структурированным, так и к неструктурированным данным в TrustGraph.
|
||||
**Бесшовная интеграция**: Обеспечение плавного взаимодействия между графовым представлением знаний TrustGraph и традиционными форматами структурированных данных.
|
||||
**Гибкое извлечение**: Поддержка автоматического извлечения структурированных данных из различных неструктурированных источников (документов, текста и т.д.).
|
||||
**Универсальность запросов**: Предоставление пользователям возможности выполнять запросы к данным как на естественном языке, так и с использованием структурированных языков запросов.
|
||||
**Согласованность данных**: Поддержание целостности и согласованности данных в различных представлениях.
|
||||
**Оптимизация производительности**: Обеспечение эффективного хранения и извлечения структурированных данных в масштабе.
|
||||
**Гибкость схемы**: Поддержка как подходов "схема при записи", так и "схема при чтении" для адаптации к различным источникам данных.
|
||||
**Обратная совместимость**: Сохранение существующей функциональности TrustGraph при добавлении возможностей работы со структурированными данными.
|
||||
|
||||
## Предыстория
|
||||
|
||||
В настоящее время TrustGraph отлично справляется с обработкой неструктурированных данных и построением графов знаний из различных источников. Однако, многие корпоративные сценарии использования включают данные, которые изначально являются структурированными - записи о клиентах, журналы транзакций, базы данных инвентаризации и другие табличные наборы данных. Эти структурированные наборы данных часто необходимо анализировать вместе с неструктурированным контентом для получения всесторонних сведений.
|
||||
|
||||
Текущие ограничения включают:
|
||||
Отсутствие встроенной поддержки импорта данных в предварительно структурированных форматах (CSV, массивы JSON, экспорты баз данных).
|
||||
Невозможность сохранения исходной структуры при извлечении табличных данных из документов.
|
||||
Отсутствие эффективных механизмов запросов для структурированных данных.
|
||||
Отсутствие моста между запросами, похожими на SQL, и графовыми запросами TrustGraph.
|
||||
|
||||
Эта спецификация решает эти проблемы, вводя слой структурированных данных, который дополняет существующие возможности TrustGraph. Поддерживая структурированные данные нативно, TrustGraph может:
|
||||
Служить унифицированной платформой для анализа как структурированных, так и неструктурированных данных.
|
||||
Обеспечивать гибридные запросы, охватывающие как графовые отношения, так и табличные данные.
|
||||
Предоставлять знакомые интерфейсы для пользователей, привыкших работать со структурированными данными.
|
||||
Открывать новые сценарии использования в интеграции данных и бизнес-аналитике.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Архитектура
|
||||
|
||||
Интеграция структурированных данных требует следующих технических компонентов:
|
||||
|
||||
1. **Сервис преобразования естественного языка в структурированные запросы**
|
||||
Преобразует вопросы, заданные на естественном языке, в структурированные запросы.
|
||||
Поддерживает несколько целевых языков запросов (изначально синтаксис, похожий на SQL).
|
||||
Интегрируется с существующими возможностями NLP TrustGraph.
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/query/nlp_query/cassandra
|
||||
|
||||
2. **Поддержка схемы конфигурации** ✅ **[ПОЛНО]**
|
||||
Расширенная система конфигурации для хранения схем структурированных данных.
|
||||
Поддержка определения структуры таблиц, типов полей и взаимосвязей.
|
||||
Возможности версионирования и миграции схем.
|
||||
|
||||
3. **Модуль извлечения объектов** ✅ **[ПОЛНО]**
|
||||
Улучшенная интеграция потока извлечения знаний.
|
||||
Определяет и извлекает структурированные объекты из неструктурированных источников.
|
||||
Сохраняет информацию о происхождении и коэффициенты уверенности.
|
||||
Регистрирует обработчик конфигурации (например: trustgraph-flow/trustgraph/prompt/template/service.py) для получения данных конфигурации и декодирования информации о схеме.
|
||||
Получает объекты и декодирует их в объекты ExtractedObject для передачи в очередь Pulsar.
|
||||
ВАЖНО: Существует существующий код по адресу `trustgraph-flow/trustgraph/extract/object/row/`. Это была предыдущая попытка, и его потребуется серьезно переработать, поскольку он не соответствует текущим API. Используйте его, если это полезно, начните с нуля, если нет.
|
||||
Требуется интерфейс командной строки: `kg-extract-objects`
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/extract/kg/objects/
|
||||
|
||||
4. **Модуль записи структурированных данных** ✅ **[ПОЛНО]**
|
||||
Получает объекты в формате ExtractedObject из очередей Pulsar.
|
||||
Первоначальная реализация, ориентированная на Apache Cassandra в качестве хранилища структурированных данных.
|
||||
Обрабатывает динамическое создание таблиц на основе обнаруженных схем.
|
||||
Управляет сопоставлением схем и таблиц Cassandra и преобразованием данных.
|
||||
Предоставляет операции пакетной и потоковой записи для оптимизации производительности.
|
||||
Не имеет выходных данных Pulsar - это терминальный сервис в потоке данных.
|
||||
|
||||
**Обработка схем**:
|
||||
Отслеживает входящие сообщения ExtractedObject на наличие ссылок на схемы.
|
||||
При первом обнаружении новой схемы автоматически создает соответствующую таблицу Cassandra.
|
||||
Поддерживает кэш известных схем, чтобы избежать повторных попыток создания таблиц.
|
||||
Следует рассмотреть, следует ли получать определения схем напрямую или полагаться на имена схем в сообщениях ExtractedObject.
|
||||
|
||||
**Сопоставление таблиц Cassandra**:
|
||||
Имя пространства ключей (keyspace) берется из поля `user` из метаданных объекта ExtractedObject.
|
||||
Имя таблицы берется из поля `schema_name` объекта ExtractedObject.
|
||||
Коллекция из метаданных становится частью ключа секции (partition key) для обеспечения:
|
||||
Естественного распределения данных между узлами Cassandra.
|
||||
Эффективных запросов внутри определенной коллекции.
|
||||
Логической изоляции между различными импортами данных/источниками.
|
||||
Структура первичного ключа: `PRIMARY KEY ((collection, <schema_primary_key_fields>), <clustering_keys>)`.
|
||||
Коллекция всегда является первым компонентом ключа секции.
|
||||
Поля, определенные в схеме, следуют за первичным ключом в виде составного ключа секции.
|
||||
Это требует, чтобы запросы указывали коллекцию, что обеспечивает предсказуемую производительность.
|
||||
Определение полей сопоставляется с столбцами Cassandra с преобразованием типов:
|
||||
`string` → `text`.
|
||||
`integer` → `int` или `bigint` в зависимости от размера.
|
||||
`float` → `float` или `double` в зависимости от требуемой точности.
|
||||
`boolean` → `boolean`.
|
||||
`timestamp` → `timestamp`.
|
||||
`enum` → `text` с проверкой на уровне приложения.
|
||||
Индексированные поля создают вторичные индексы Cassandra (за исключением полей, которые уже находятся в первичном ключе).
|
||||
Обязательные поля проверяются на уровне приложения (Cassandra не поддерживает NOT NULL).
|
||||
|
||||
**Хранилище объектов**:
|
||||
Извлекает значения из карты ExtractedObject.values.
|
||||
Выполняет преобразование типов и проверку перед вставкой.
|
||||
Обрабатывает отсутствующие необязательные поля корректно.
|
||||
Поддерживает метаданные об источнике объекта (исходный документ, показатели достоверности).
|
||||
Поддерживает идемпотентные записи для обработки сценариев повторной отправки сообщений.
|
||||
|
||||
**Примечания к реализации**:
|
||||
Существующий код, расположенный по адресу `trustgraph-flow/trustgraph/storage/objects/cassandra/`, устарел и не соответствует текущим API.
|
||||
Следует использовать `trustgraph-flow/trustgraph/storage/triples/cassandra` в качестве примера работающего процессора хранилища.
|
||||
Необходимо оценить существующий код на предмет наличия компонентов, которые можно повторно использовать, прежде чем принимать решение о рефакторинге или переписывании.
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/storage/objects/cassandra
|
||||
|
||||
5. **Сервис структурированных запросов** ✅ **[ЗАВЕРШЕНО]**
|
||||
Принимает структурированные запросы в определенных форматах.
|
||||
Выполняет запросы к структурированному хранилищу.
|
||||
Возвращает объекты, соответствующие критериям запроса.
|
||||
Поддерживает постраничную выдачу и фильтрацию результатов.
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/query/objects/cassandra
|
||||
|
||||
6. **Интеграция с инструментами агентов**:
|
||||
Новый класс инструмента для фреймворков агентов.
|
||||
Позволяет агентам выполнять запросы к структурированным хранилищам данных.
|
||||
Предоставляет интерфейсы структурированных запросов и запросов на естественном языке.
|
||||
Интегрируется с существующими процессами принятия решений агентами.
|
||||
|
||||
7. **Сервис приема структурированных данных**:
|
||||
Принимает структурированные данные в нескольких форматах (JSON, CSV, XML).
|
||||
Разбирает и проверяет входящие данные в соответствии с определенными схемами.
|
||||
Преобразует данные в нормализованные потоки объектов.
|
||||
Отправляет объекты в соответствующие очереди сообщений для обработки.
|
||||
Поддерживает массовую загрузку и потоковую передачу данных.
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/decoding/structured
|
||||
|
||||
8. **Сервис встраивания объектов**:
|
||||
Генерирует векторные представления для структурированных объектов.
|
||||
Обеспечивает семантический поиск по структурированным данным.
|
||||
Поддерживает гибридный поиск, сочетающий структурированные запросы с семантической схожестью.
|
||||
Интегрируется с существующими векторными хранилищами.
|
||||
|
||||
Модуль: trustgraph-flow/trustgraph/embeddings/object_embeddings/qdrant
|
||||
|
||||
### Модели данных
|
||||
|
||||
#### Механизм хранения схем
|
||||
|
||||
Схемы хранятся в системе конфигурации TrustGraph со следующей структурой:
|
||||
|
||||
**Тип**: `schema` (фиксированное значение для всех схем структурированных данных).
|
||||
**Ключ**: Уникальное имя/идентификатор схемы (например, `customer_records`, `transaction_log`).
|
||||
**Значение**: Определение схемы JSON, содержащее структуру.
|
||||
|
||||
Пример записи конфигурации:
|
||||
```
|
||||
Type: schema
|
||||
Key: customer_records
|
||||
Value: {
|
||||
"name": "customer_records",
|
||||
"description": "Customer information table",
|
||||
"fields": [
|
||||
{
|
||||
"name": "customer_id",
|
||||
"type": "string",
|
||||
"primary_key": true
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "registration_date",
|
||||
"type": "timestamp"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"type": "string",
|
||||
"enum": ["active", "inactive", "suspended"]
|
||||
}
|
||||
],
|
||||
"indexes": ["email", "registration_date"]
|
||||
}
|
||||
```
|
||||
|
||||
Этот подход позволяет:
|
||||
Динамическое определение схемы без внесения изменений в код
|
||||
Простое обновление и версионирование схемы
|
||||
Последовательная интеграция с существующим управлением конфигурацией TrustGraph
|
||||
Поддержка нескольких схем в рамках одного развертывания
|
||||
|
||||
### API
|
||||
|
||||
Новые API:
|
||||
Схемы Pulsar для вышеперечисленных типов
|
||||
Интерфейсы Pulsar в новых потоках
|
||||
Необходим способ указания типов схем в потоках, чтобы потоки знали, какие
|
||||
типы схем загружать
|
||||
API добавлены в шлюз и обратный шлюз
|
||||
|
||||
Измененные API:
|
||||
Конечные точки извлечения знаний - Добавлена опция структурированного объектного вывода
|
||||
Конечные точки агентов - Добавлена поддержка инструментов для структурированных данных
|
||||
|
||||
### Детали реализации
|
||||
|
||||
В соответствии с существующими соглашениями - это просто новые модули обработки.
|
||||
Все находится в пакетах trustgraph-flow, за исключением элементов схемы,
|
||||
находящихся в trustgraph-base.
|
||||
|
||||
Требуется некоторая работа в пользовательском интерфейсе Workbench, чтобы
|
||||
продемонстрировать / протестировать эту
|
||||
возможность.
|
||||
|
||||
## Вопросы безопасности
|
||||
|
||||
Нет дополнительных соображений.
|
||||
|
||||
## Вопросы производительности
|
||||
|
||||
Некоторые вопросы, касающиеся использования запросов и индексов Cassandra, чтобы
|
||||
запросы не замедлялись.
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
Используйте существующую стратегию тестирования, будут созданы модульные,
|
||||
контрактные и интеграционные тесты.
|
||||
|
||||
## План миграции
|
||||
|
||||
Отсутствует.
|
||||
|
||||
## Сроки
|
||||
|
||||
Не указаны.
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
Можно ли сделать так, чтобы это работало с другими типами хранилищ? Мы стремимся
|
||||
использовать интерфейсы, которые делают модули, работающие с одним хранилищем,
|
||||
применимыми к другим хранилищам.
|
||||
|
||||
## Ссылки
|
||||
281
docs/tech-specs/ru/structured-diag-service.ru.md
Normal file
281
docs/tech-specs/ru/structured-diag-service.ru.md
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация описывает новый сервис, который можно вызывать для диагностики и анализа структурированных данных в TrustGraph. Сервис извлекает функциональность из существующего инструмента командной строки `tg-load-structured-data` и предоставляет ее в виде сервиса запросов/ответов, обеспечивая программный доступ к возможностям определения типа данных и генерации описаний.
|
||||
|
||||
Сервис поддерживает три основные операции:
|
||||
|
||||
1. **Определение типа данных**: Анализ образца данных для определения его формата (CSV, JSON или XML).
|
||||
2. **Генерация описания**: Генерация структурированного описания данных TrustGraph для заданного образца данных и типа.
|
||||
3. **Комплексная диагностика**: Выполнение как определения типа данных, так и генерации описания последовательно.
|
||||
|
||||
## Цели
|
||||
|
||||
**Модулизация анализа данных**: Извлечение логики диагностики данных из интерфейса командной строки в многократно используемые компоненты сервиса.
|
||||
**Обеспечение программного доступа**: Предоставление API для доступа к возможностям анализа данных.
|
||||
**Поддержка нескольких форматов данных**: Обработка форматов данных CSV, JSON и XML согласованным образом.
|
||||
**Генерация точных описаний**: Создание структурированных описаний данных, которые точно сопоставляют исходные данные со схемами TrustGraph.
|
||||
**Сохранение обратной совместимости**: Обеспечение продолжения работы существующей функциональности интерфейса командной строки.
|
||||
**Обеспечение возможности объединения сервисов**: Предоставление другим сервисам возможности использования возможностей диагностики данных.
|
||||
**Улучшение тестируемости**: Разделение бизнес-логики от интерфейса командной строки для улучшения тестирования.
|
||||
**Поддержка потоковой обработки**: Обеспечение возможности анализа образцов данных без загрузки целых файлов.
|
||||
|
||||
## Обзор
|
||||
|
||||
В настоящее время команда `tg-load-structured-data` предоставляет комплексную функциональность для анализа структурированных данных и генерации описаний. Однако эта функциональность тесно связана с интерфейсом командной строки, что ограничивает ее повторное использование.
|
||||
|
||||
Существующие ограничения включают:
|
||||
Логика диагностики данных, встроенная в код интерфейса командной строки.
|
||||
Отсутствие программного доступа к определению типа данных и генерации описаний.
|
||||
Сложность интеграции возможностей диагностики в другие сервисы.
|
||||
Ограниченная возможность создания рабочих процессов анализа данных.
|
||||
|
||||
Эта спецификация решает эти проблемы, создавая специальный сервис для диагностики структурированных данных. Предоставляя эти возможности в виде сервиса, TrustGraph может:
|
||||
Обеспечить возможность другим сервисам программного анализа данных.
|
||||
Поддерживать более сложные конвейеры обработки данных.
|
||||
Облегчить интеграцию с внешними системами.
|
||||
Улучшить поддерживаемость за счет разделения ответственности.
|
||||
|
||||
## Технический дизайн
|
||||
|
||||
### Архитектура
|
||||
|
||||
Сервису диагностики структурированных данных требуются следующие технические компоненты:
|
||||
|
||||
1. **Процессор сервиса диагностики**
|
||||
Обрабатывает входящие запросы на диагностику.
|
||||
Координирует определение типа данных и генерацию описаний.
|
||||
Возвращает структурированные ответы с результатами диагностики.
|
||||
|
||||
Модуль: `trustgraph-flow/trustgraph/diagnosis/structured_data/service.py`
|
||||
|
||||
2. **Детектор типа данных**
|
||||
Использует алгоритмическое определение для идентификации формата данных (CSV, JSON, XML).
|
||||
Анализирует структуру данных, разделители и шаблоны синтаксиса.
|
||||
Возвращает определенный формат и коэффициенты достоверности.
|
||||
|
||||
Модуль: `trustgraph-flow/trustgraph/diagnosis/structured_data/type_detector.py`
|
||||
|
||||
3. **Генератор описаний**
|
||||
Использует сервис запросов для генерации описаний.
|
||||
Вызывает специфичные для формата запросы (diagnose-csv, diagnose-json, diagnose-xml).
|
||||
Отображает поля данных на поля схемы TrustGraph с помощью ответов запросов.
|
||||
|
||||
Модуль: `trustgraph-flow/trustgraph/diagnosis/structured_data/descriptor_generator.py`
|
||||
|
||||
### Модели данных
|
||||
|
||||
#### StructuredDataDiagnosisRequest
|
||||
|
||||
Сообщение запроса для операций диагностики структурированных данных:
|
||||
|
||||
```python
|
||||
class StructuredDataDiagnosisRequest:
|
||||
operation: str # "detect-type", "generate-descriptor", or "diagnose"
|
||||
sample: str # Data sample to analyze (text content)
|
||||
type: Optional[str] # Data type (csv, json, xml) - required for generate-descriptor
|
||||
schema_name: Optional[str] # Target schema name for descriptor generation
|
||||
options: Dict[str, Any] # Additional options (e.g., delimiter for CSV)
|
||||
```
|
||||
|
||||
#### StructuredDataDiagnosisResponse
|
||||
|
||||
Сообщение ответа, содержащее результаты диагностики:
|
||||
|
||||
```python
|
||||
class StructuredDataDiagnosisResponse:
|
||||
operation: str # The operation that was performed
|
||||
detected_type: Optional[str] # Detected data type (for detect-type/diagnose)
|
||||
confidence: Optional[float] # Confidence score for type detection
|
||||
descriptor: Optional[Dict] # Generated descriptor (for generate-descriptor/diagnose)
|
||||
error: Optional[str] # Error message if operation failed
|
||||
metadata: Dict[str, Any] # Additional metadata (e.g., field count, sample records)
|
||||
```
|
||||
|
||||
#### Структура дескриптора
|
||||
|
||||
Сгенерированный дескриптор соответствует существующему формату структурированного дескриптора данных:
|
||||
|
||||
```json
|
||||
{
|
||||
"format": {
|
||||
"type": "csv",
|
||||
"encoding": "utf-8",
|
||||
"options": {
|
||||
"delimiter": ",",
|
||||
"has_header": true
|
||||
}
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"source_field": "customer_id",
|
||||
"target_field": "id",
|
||||
"transforms": [
|
||||
{"type": "trim"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
"schema_name": "customer",
|
||||
"options": {
|
||||
"batch_size": 1000,
|
||||
"confidence": 0.9
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Интерфейс сервиса
|
||||
|
||||
Сервис будет предоставлять следующие операции с использованием шаблона запрос/ответ:
|
||||
|
||||
1. **Операция определения типа**
|
||||
Входные данные: Образец данных
|
||||
Обработка: Анализ структуры данных с использованием алгоритмического определения
|
||||
Выходные данные: Определенный тип с уровнем достоверности
|
||||
|
||||
2. **Операция генерации дескриптора**
|
||||
Входные данные: Образец данных, тип, имя целевой схемы
|
||||
Обработка:
|
||||
Вызов сервиса запросов с идентификатором запроса, специфичным для формата (diagnose-csv, diagnose-json или diagnose-xml)
|
||||
Передача образца данных и доступных схем в запрос
|
||||
Получение сгенерированного дескриптора из ответа запроса
|
||||
Выходные данные: Структурированный дескриптор данных
|
||||
|
||||
3. **Комплексная операция диагностики**
|
||||
Входные данные: Образец данных, необязательное имя схемы
|
||||
Обработка:
|
||||
Использование алгоритмического определения для определения формата
|
||||
Выбор соответствующего запроса, специфичного для формата, на основе определенного типа
|
||||
Вызов сервиса запросов для генерации дескриптора
|
||||
Выходные данные: Определенный тип и дескриптор
|
||||
|
||||
### Детали реализации
|
||||
|
||||
Сервис будет соответствовать соглашениям сервиса TrustGraph:
|
||||
|
||||
1. **Регистрация сервиса**
|
||||
Регистрация как сервис типа `structured-diag`
|
||||
Использование стандартных тем запросов/ответов
|
||||
Реализация базового класса FlowProcessor
|
||||
Регистрация PromptClientSpec для взаимодействия с сервисом запросов
|
||||
|
||||
2. **Управление конфигурацией**
|
||||
Доступ к конфигурациям схем через сервис конфигурации
|
||||
Кэширование схем для повышения производительности
|
||||
Динамическая обработка обновлений конфигурации
|
||||
|
||||
3. **Интеграция с сервисом запросов**
|
||||
Использование существующей инфраструктуры сервиса запросов
|
||||
Вызов сервиса запросов с идентификаторами запросов, специфичными для формата:
|
||||
`diagnose-csv`: Для анализа данных CSV
|
||||
`diagnose-json`: Для анализа данных JSON
|
||||
`diagnose-xml`: Для анализа данных XML
|
||||
Запросы настроены в конфигурации запросов, а не жестко закодированы в сервисе
|
||||
Передача схем и образцов данных в качестве переменных запроса
|
||||
Разбор ответов запросов для извлечения дескрипторов
|
||||
|
||||
4. **Обработка ошибок**
|
||||
Проверка входных образцов данных
|
||||
Предоставление описательных сообщений об ошибках
|
||||
Обработка некорректных данных
|
||||
Обработка сбоев сервиса запросов
|
||||
|
||||
5. **Выборка данных**
|
||||
Обработка настраиваемых размеров выборки
|
||||
Правильная обработка неполных записей
|
||||
Поддержание согласованности выборки
|
||||
|
||||
### Интеграция с API
|
||||
|
||||
Сервис будет интегрирован с существующими API TrustGraph:
|
||||
|
||||
Измененные компоненты:
|
||||
`tg-load-structured-data` CLI - Переработан для использования нового сервиса для операций диагностики
|
||||
Flow API - Расширен для поддержки запросов структурированной диагностики
|
||||
|
||||
Новые конечные точки сервиса:
|
||||
`/api/v1/flow/{flow}/diagnose/structured-data` - WebSocket конечная точка для запросов диагностики
|
||||
`/api/v1/diagnose/structured-data` - REST конечная точка для синхронной диагностики
|
||||
|
||||
### Поток сообщений
|
||||
|
||||
```
|
||||
Client → Gateway → Structured Diag Service → Config Service (for schemas)
|
||||
↓
|
||||
Type Detector (algorithmic)
|
||||
↓
|
||||
Prompt Service (diagnose-csv/json/xml)
|
||||
↓
|
||||
Descriptor Generator (parses prompt response)
|
||||
↓
|
||||
Client ← Gateway ← Structured Diag Service (response)
|
||||
```
|
||||
|
||||
## Соображения безопасности
|
||||
|
||||
Проверка входных данных для предотвращения атак внедрения
|
||||
Ограничения на размер образцов данных для предотвращения DoS-атак
|
||||
Очистка сгенерированных описаний
|
||||
Контроль доступа через существующую аутентификацию TrustGraph
|
||||
|
||||
## Соображения производительности
|
||||
|
||||
Кэширование определений схемы для уменьшения количества обращений к сервису конфигурации
|
||||
Ограничение размеров образцов для поддержания отзывчивой производительности
|
||||
Использование потоковой обработки для больших образцов данных
|
||||
Реализация механизмов тайм-аута для длительных анализов
|
||||
|
||||
## Стратегия тестирования
|
||||
|
||||
1. **Модульные тесты**
|
||||
Обнаружение типа для различных форматов данных
|
||||
Точность генерации описаний
|
||||
Сценарии обработки ошибок
|
||||
|
||||
2. **Интеграционные тесты**
|
||||
Поток запросов/ответов сервиса
|
||||
Получение и кэширование схемы
|
||||
Интеграция с CLI
|
||||
|
||||
3. **Тесты производительности**
|
||||
Обработка больших образцов
|
||||
Обработка одновременных запросов
|
||||
Использование памяти при высокой нагрузке
|
||||
|
||||
## План миграции
|
||||
|
||||
1. **Этап 1**: Реализация сервиса с основной функциональностью
|
||||
2. **Этап 2**: Рефакторинг CLI для использования сервиса (с сохранением обратной совместимости)
|
||||
3. **Этап 3**: Добавление REST API
|
||||
4. **Этап 4**: Отказ от встроенной логики CLI (с предварительным уведомлением)
|
||||
|
||||
## График
|
||||
|
||||
Неделя 1-2: Реализация основного сервиса и обнаружения типа
|
||||
Неделя 3-4: Добавление генерации описаний и интеграции
|
||||
Неделя 5: Тестирование и документация
|
||||
Неделя 6: Рефакторинг CLI и миграция
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
Должен ли сервис поддерживать дополнительные форматы данных (например, Parquet, Avro)?
|
||||
Каков должен быть максимальный размер образца для анализа?
|
||||
Следует ли кэшировать результаты диагностики для повторяющихся запросов?
|
||||
Как сервис должен обрабатывать сценарии с несколькими схемами?
|
||||
Должны ли идентификаторы запросов быть настраиваемыми параметрами для сервиса?
|
||||
|
||||
## Ссылки
|
||||
|
||||
[Спецификация структурированного описания данных](structured-data-descriptor.md)
|
||||
[Документация по загрузке структурированных данных](structured-data.md)
|
||||
`tg-load-structured-data` реализация: `trustgraph-cli/trustgraph/cli/load_structured_data.py`
|
||||
499
docs/tech-specs/ru/tool-group.ru.md
Normal file
499
docs/tech-specs/ru/tool-group.ru.md
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
---
|
||||
layout: default
|
||||
title: "TrustGraph Tool Group System"
|
||||
parent: "Russian (Beta)"
|
||||
---
|
||||
|
||||
# TrustGraph Tool Group System
|
||||
|
||||
> **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.
|
||||
## Техническая спецификация v1.0
|
||||
|
||||
### Краткое описание
|
||||
|
||||
Эта спецификация определяет систему группировки инструментов для агентов TrustGraph, которая позволяет осуществлять детальный контроль над тем, какие инструменты доступны для конкретных запросов. Система вводит фильтрацию инструментов на основе групп с помощью конфигурации и указания на уровне запроса, что обеспечивает более строгие границы безопасности, управление ресурсами и функциональное разделение возможностей агентов.
|
||||
|
||||
### 1. Обзор
|
||||
|
||||
#### 1.1 Описание проблемы
|
||||
|
||||
В настоящее время агенты TrustGraph имеют доступ ко всем настроенным инструментам, независимо от контекста запроса или требований безопасности. Это создает несколько проблем:
|
||||
|
||||
**Риск безопасности**: Чувствительные инструменты (например, изменение данных) доступны даже для запросов только для чтения.
|
||||
**Неэффективное использование ресурсов**: Сложные инструменты загружаются даже тогда, когда простые запросы не требуют их.
|
||||
**Функциональная путаница**: Агенты могут выбирать неподходящие инструменты, когда существуют более простые альтернативы.
|
||||
**Изоляция для многопользовательских сред**: Различным группам пользователей требуется доступ к разным наборам инструментов.
|
||||
|
||||
#### 1.2 Обзор решения
|
||||
|
||||
Система группировки инструментов включает в себя:
|
||||
|
||||
1. **Классификация по группам**: Инструменты помечаются принадлежностью к группам во время конфигурации.
|
||||
2. **Фильтрация на уровне запроса**: AgentRequest указывает, какие группы инструментов разрешены.
|
||||
3. **Принудительное исполнение во время выполнения**: Агенты имеют доступ только к инструментам, соответствующим запрошенным группам.
|
||||
4. **Гибкая группировка**: Инструменты могут принадлежать к нескольким группам для сложных сценариев.
|
||||
|
||||
### 2. Изменения схемы
|
||||
|
||||
#### 2.1 Расширение схемы конфигурации инструмента
|
||||
|
||||
Существующая схема конфигурации инструмента расширена с помощью поля `group`:
|
||||
|
||||
**До:**
|
||||
```json
|
||||
{
|
||||
"name": "knowledge-query",
|
||||
"type": "knowledge-query",
|
||||
"description": "Query the knowledge graph"
|
||||
}
|
||||
```
|
||||
|
||||
**После:**
|
||||
```json
|
||||
{
|
||||
"name": "knowledge-query",
|
||||
"type": "knowledge-query",
|
||||
"description": "Query the knowledge graph",
|
||||
"group": ["read-only", "knowledge", "basic"]
|
||||
}
|
||||
```
|
||||
|
||||
**Спецификация поля группы:**
|
||||
`group`: Array(String) - Список групп, к которым принадлежит этот инструмент.
|
||||
**Необязательно**: Инструменты без поля группы принадлежат к группе "по умолчанию".
|
||||
**Множественная принадлежность**: Инструменты могут принадлежать к нескольким группам.
|
||||
**Чувствительность к регистру**: Имена групп должны точно соответствовать строке.
|
||||
|
||||
#### 2.1.2 Улучшение переходов состояния инструмента
|
||||
|
||||
Инструменты могут опционально указывать переходы состояний и доступность, зависящую от состояния:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "knowledge-query",
|
||||
"type": "knowledge-query",
|
||||
"description": "Query the knowledge graph",
|
||||
"group": ["read-only", "knowledge", "basic"],
|
||||
"state": "analysis",
|
||||
"available_in_states": ["undefined", "research"]
|
||||
}
|
||||
```
|
||||
|
||||
**Спецификация состояния:**
|
||||
`state`: String - **Необязательно** - Состояние, в которое система переходит после успешного выполнения инструмента.
|
||||
`available_in_states`: Array(String) - **Необязательно** - Состояния, в которых этот инструмент доступен.
|
||||
**Поведение по умолчанию**: Инструменты без `available_in_states` доступны во всех состояниях.
|
||||
**Переход состояния**: Происходит только после успешного выполнения инструмента.
|
||||
|
||||
#### 2.2 Улучшение схемы AgentRequest
|
||||
|
||||
Схема `AgentRequest` в `trustgraph-base/trustgraph/schema/services/agent.py` улучшена:
|
||||
|
||||
**Текущий AgentRequest:**
|
||||
`question`: String - Пользовательский запрос.
|
||||
`plan`: String - План выполнения (может быть удален).
|
||||
`state`: String - Состояние агента.
|
||||
`history`: Array(AgentStep) - История выполнения.
|
||||
|
||||
**Улучшенный AgentRequest:**
|
||||
`question`: String - Пользовательский запрос.
|
||||
`state`: String - Состояние выполнения агента (теперь активно используется для фильтрации инструментов).
|
||||
`history`: Array(AgentStep) - История выполнения.
|
||||
`group`: Array(String) - **НОВОЕ** - Группы инструментов, разрешенные для этого запроса.
|
||||
|
||||
**Изменения в схеме:**
|
||||
**Удалено**: Поле `plan` больше не требуется и может быть удалено (изначально предназначалось для указания инструментов).
|
||||
**Добавлено**: Поле `group` для указания групп инструментов.
|
||||
**Улучшено**: Поле `state` теперь контролирует доступность инструментов во время выполнения.
|
||||
|
||||
**Поведение полей:**
|
||||
|
||||
**Группа (Group):**
|
||||
**Необязательно**: Если не указано, по умолчанию используется ["default"].
|
||||
**Пересечение**: Доступны только инструменты, соответствующие хотя бы одной указанной группе.
|
||||
**Пустой массив**: Ни один инструмент не доступен (агент может использовать только внутренние рассуждения).
|
||||
**Подстановочный знак**: Специальная группа "*" предоставляет доступ ко всем инструментам.
|
||||
|
||||
**Состояние (State):**
|
||||
**Необязательно**: Если не указано, по умолчанию используется "undefined".
|
||||
**Фильтрация по состоянию**: Доступны только инструменты, доступные в текущем состоянии.
|
||||
**Состояние по умолчанию**: Состояние "undefined" позволяет использовать все инструменты (с учетом фильтрации по группам).
|
||||
**Переходы состояний**: Состояние инструментов может меняться после успешного выполнения.
|
||||
|
||||
### 3. Примеры пользовательских групп
|
||||
|
||||
Организации могут определять группы, специфичные для домена:
|
||||
|
||||
```json
|
||||
{
|
||||
"financial-tools": ["stock-query", "portfolio-analysis"],
|
||||
"medical-tools": ["diagnosis-assist", "drug-interaction"],
|
||||
"legal-tools": ["contract-analysis", "case-search"]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Детали реализации
|
||||
|
||||
#### 4.1 Загрузка и фильтрация инструментов
|
||||
|
||||
**Фаза конфигурации:**
|
||||
1. Все инструменты загружаются из конфигурации вместе с их групповыми назначениями.
|
||||
2. Инструменты, не имеющие явных групповых назначений, назначаются в группу "default".
|
||||
3. Групповая принадлежность проверяется и сохраняется в реестре инструментов.
|
||||
|
||||
**Фаза обработки запросов:**
|
||||
1. Запрос AgentRequest поступает с опциональной спецификацией группы.
|
||||
2. Agent фильтрует доступные инструменты на основе пересечения групп.
|
||||
3. Только соответствующие инструменты передаются в контекст выполнения агента.
|
||||
4. Agent работает с отфильтрованным набором инструментов на протяжении всего жизненного цикла запроса.
|
||||
|
||||
#### 4.2 Логика фильтрации инструментов
|
||||
|
||||
**Комбинированная фильтрация по группам и состоянию:**
|
||||
|
||||
```
|
||||
For each configured tool:
|
||||
tool_groups = tool.group || ["default"]
|
||||
tool_states = tool.available_in_states || ["*"] // Available in all states
|
||||
|
||||
For each request:
|
||||
requested_groups = request.group || ["default"]
|
||||
current_state = request.state || "undefined"
|
||||
|
||||
Tool is available if:
|
||||
// Group filtering
|
||||
(intersection(tool_groups, requested_groups) is not empty OR "*" in requested_groups)
|
||||
AND
|
||||
// State filtering
|
||||
(current_state in tool_states OR "*" in tool_states)
|
||||
```
|
||||
|
||||
**Логика переходов состояний:**
|
||||
|
||||
```
|
||||
After successful tool execution:
|
||||
if tool.state is defined:
|
||||
next_request.state = tool.state
|
||||
else:
|
||||
next_request.state = current_request.state // No change
|
||||
```
|
||||
|
||||
#### 4.3 Точки интеграции агента
|
||||
|
||||
**Агент ReAct:**
|
||||
Фильтрация инструментов происходит в файле agent_manager.py во время создания регистрации инструментов.
|
||||
Список доступных инструментов фильтруется как по группе, так и по состоянию перед генерацией плана.
|
||||
Изменения состояния обновляют поле AgentRequest.state после успешного выполнения инструмента.
|
||||
Следующая итерация использует обновленное состояние для фильтрации инструментов.
|
||||
|
||||
**Агент, основанный на оценке уверенности:**
|
||||
Фильтрация инструментов происходит в файле planner.py во время генерации плана.
|
||||
Проверка ExecutionStep гарантирует, что используются только инструменты, соответствующие группе и состоянию.
|
||||
Контроллер потока обеспечивает доступность инструментов во время выполнения.
|
||||
Изменения состояния управляются контроллером потока между шагами.
|
||||
|
||||
### 5. Примеры конфигурации
|
||||
|
||||
#### 5.1 Конфигурация инструментов с группами и состояниями
|
||||
|
||||
```yaml
|
||||
tool:
|
||||
knowledge-query:
|
||||
type: knowledge-query
|
||||
name: "Knowledge Graph Query"
|
||||
description: "Query the knowledge graph for entities and relationships"
|
||||
group: ["read-only", "knowledge", "basic"]
|
||||
state: "analysis"
|
||||
available_in_states: ["undefined", "research"]
|
||||
|
||||
graph-update:
|
||||
type: graph-update
|
||||
name: "Graph Update"
|
||||
description: "Add or modify entities in the knowledge graph"
|
||||
group: ["write", "knowledge", "admin"]
|
||||
available_in_states: ["analysis", "modification"]
|
||||
|
||||
text-completion:
|
||||
type: text-completion
|
||||
name: "Text Completion"
|
||||
description: "Generate text using language models"
|
||||
group: ["read-only", "text", "basic"]
|
||||
state: "undefined"
|
||||
# No available_in_states = available in all states
|
||||
|
||||
complex-analysis:
|
||||
type: mcp-tool
|
||||
name: "Complex Analysis Tool"
|
||||
description: "Perform complex data analysis"
|
||||
group: ["advanced", "compute", "expensive"]
|
||||
state: "results"
|
||||
available_in_states: ["analysis"]
|
||||
mcp_tool_id: "analysis-server"
|
||||
|
||||
reset-workflow:
|
||||
type: mcp-tool
|
||||
name: "Reset Workflow"
|
||||
description: "Reset to initial state"
|
||||
group: ["admin"]
|
||||
state: "undefined"
|
||||
available_in_states: ["analysis", "results"]
|
||||
```
|
||||
|
||||
#### 5.2 Примеры запросов с рабочими процессами, зависящими от состояния.
|
||||
|
||||
**Первоначальный запрос на исследование:**
|
||||
```json
|
||||
{
|
||||
"question": "What entities are connected to Company X?",
|
||||
"group": ["read-only", "knowledge"],
|
||||
"state": "undefined"
|
||||
}
|
||||
```
|
||||
*Доступные инструменты: knowledge-query, text-completion*
|
||||
*После knowledge-query: состояние → "analysis"*
|
||||
|
||||
**Фаза анализа:**
|
||||
```json
|
||||
{
|
||||
"question": "Continue analysis based on previous results",
|
||||
"group": ["advanced", "compute", "write"],
|
||||
"state": "analysis"
|
||||
}
|
||||
```
|
||||
*Доступные инструменты: complex-analysis, graph-update, reset-workflow*
|
||||
*После complex-analysis: состояние → "results"*
|
||||
|
||||
**Фаза результатов:**
|
||||
```json
|
||||
{
|
||||
"question": "What should I do with these results?",
|
||||
"group": ["admin"],
|
||||
"state": "results"
|
||||
}
|
||||
```
|
||||
*Доступные инструменты: только reset-workflow*
|
||||
*После выполнения reset-workflow: состояние → "undefined"*
|
||||
|
||||
**Пример рабочего процесса - Полный цикл:**
|
||||
1. **Начало (undefined):** Используйте knowledge-query → переходы в состояние "analysis"
|
||||
2. **Состояние анализа:** Используйте complex-analysis → переходы в состояние "results"
|
||||
3. **Состояние результатов:** Используйте reset-workflow → возврат в состояние "undefined"
|
||||
4. **Возврат к началу:** Все начальные инструменты снова доступны
|
||||
|
||||
### 6. Вопросы безопасности
|
||||
|
||||
#### 6.1 Интеграция с контролем доступа
|
||||
|
||||
**Фильтрация на уровне шлюза:**
|
||||
Шлюз может применять ограничения для групп на основе прав доступа пользователя
|
||||
Предотвращение повышения привилегий путем манипулирования запросами
|
||||
Журнал аудита включает запрошенные и предоставленные группы инструментов
|
||||
|
||||
**Пример логики шлюза:**
|
||||
```
|
||||
user_permissions = get_user_permissions(request.user_id)
|
||||
allowed_groups = user_permissions.tool_groups
|
||||
requested_groups = request.group
|
||||
|
||||
# Validate request doesn't exceed permissions
|
||||
if not is_subset(requested_groups, allowed_groups):
|
||||
reject_request("Insufficient permissions for requested tool groups")
|
||||
```
|
||||
|
||||
#### 6.2 Аудит и мониторинг
|
||||
|
||||
**Расширенный журнал аудита:**
|
||||
Регистрировать запрошенные группы инструментов и начальное состояние для каждого запроса.
|
||||
Отслеживать переходы состояний и использование инструментов по принадлежности к группе.
|
||||
Мониторить попытки несанкционированного доступа к группам и недопустимые переходы состояний.
|
||||
Оповещать об необычных шаблонах использования групп или подозрительных рабочих процессах состояний.
|
||||
|
||||
### 7. Стратегия миграции
|
||||
|
||||
#### 7.1 Обратная совместимость
|
||||
|
||||
**Этап 1: Аддитивные изменения**
|
||||
Добавить необязательное поле `group` в конфигурации инструментов.
|
||||
Добавить необязательное поле `group` в схему AgentRequest.
|
||||
Поведение по умолчанию: Все существующие инструменты принадлежат к группе "default".
|
||||
Существующие запросы без поля группы используют группу "default".
|
||||
|
||||
**Сохранение существующего поведения:**
|
||||
Инструменты без конфигурации группы продолжают работать (группа "default").
|
||||
Инструменты без конфигурации состояния доступны во всех состояниях.
|
||||
Запросы без указания группы получают доступ ко всем инструментам (группа "default").
|
||||
Запросы без указания состояния используют состояние "не определено" (доступны все инструменты).
|
||||
Отсутствуют изменения, нарушающие работу существующих развертываний.
|
||||
|
||||
### 8. Мониторинг и наблюдаемость
|
||||
|
||||
#### 8.1 Новые метрики
|
||||
|
||||
**Использование групп инструментов:**
|
||||
`agent_tool_group_requests_total` - Счетчик запросов по группе.
|
||||
`agent_tool_group_availability` - Показатель количества доступных инструментов для каждой группы.
|
||||
`agent_filtered_tools_count` - Гистограмма количества инструментов после фильтрации по группе и состоянию.
|
||||
|
||||
**Метрики рабочих процессов состояний:**
|
||||
`agent_state_transitions_total` - Счетчик переходов состояний по инструменту.
|
||||
`agent_workflow_duration_seconds` - Гистограмма времени, проведенного в каждом состоянии.
|
||||
`agent_state_availability` - Показатель количества доступных инструментов для каждого состояния.
|
||||
|
||||
**Метрики безопасности:**
|
||||
`agent_group_access_denied_total` - Счетчик несанкционированного доступа к группам.
|
||||
`agent_invalid_state_transition_total` - Счетчик недопустимых переходов состояний.
|
||||
`agent_privilege_escalation_attempts_total` - Счетчик подозрительных запросов.
|
||||
|
||||
#### 8.2 Улучшения ведения журнала
|
||||
|
||||
**Ведение журнала запросов:**
|
||||
```json
|
||||
{
|
||||
"request_id": "req-123",
|
||||
"requested_groups": ["read-only", "knowledge"],
|
||||
"initial_state": "undefined",
|
||||
"state_transitions": [
|
||||
{"tool": "knowledge-query", "from": "undefined", "to": "analysis", "timestamp": "2024-01-01T10:00:01Z"}
|
||||
],
|
||||
"available_tools": ["knowledge-query", "text-completion"],
|
||||
"filtered_by_group": ["graph-update", "admin-tool"],
|
||||
"filtered_by_state": [],
|
||||
"execution_time": "1.2s"
|
||||
}
|
||||
```
|
||||
|
||||
### 9. Стратегия тестирования
|
||||
|
||||
#### 9.1 Юнит-тесты
|
||||
|
||||
**Логика фильтрации инструментов:**
|
||||
Расчет пересечений тестовых групп
|
||||
Логика фильтрации на основе состояния теста
|
||||
Проверка назначения групп и состояний по умолчанию
|
||||
Тестирование поведения групп с подстановочными знаками
|
||||
Проверка обработки пустых групп
|
||||
Тестирование сценариев комбинированной фильтрации групп и состояний
|
||||
|
||||
**Проверка конфигурации:**
|
||||
Тестирование загрузки инструментов с различными конфигурациями групп и состояний
|
||||
Проверка валидации схемы для неверных спецификаций групп и состояний
|
||||
Тестирование обратной совместимости с существующими конфигурациями
|
||||
Проверка определений и циклов переходов состояний
|
||||
|
||||
#### 9.2 Интеграционные тесты
|
||||
|
||||
**Поведение агента:**
|
||||
Проверка того, что агенты видят только инструменты, отфильтрованные по группам и состояниям
|
||||
Тестирование выполнения запросов с различными комбинациями групп
|
||||
Тестирование переходов состояний во время выполнения агентом
|
||||
Проверка обработки ошибок, когда ни один инструмент недоступен
|
||||
Тестирование прогресса рабочего процесса через несколько состояний
|
||||
|
||||
**Тестирование безопасности:**
|
||||
Тестирование предотвращения повышения привилегий
|
||||
Проверка точности журнала аудита
|
||||
Тестирование интеграции шлюза с разрешениями пользователей
|
||||
|
||||
#### 9.3 Сценарии сквозного тестирования
|
||||
|
||||
**Многопользовательское использование с рабочими процессами состояний:**
|
||||
```
|
||||
Scenario: Different users with different tool access and workflow states
|
||||
Given: User A has "read-only" permissions, state "undefined"
|
||||
And: User B has "write" permissions, state "analysis"
|
||||
When: Both request knowledge operations
|
||||
Then: User A gets read-only tools available in "undefined" state
|
||||
And: User B gets write tools available in "analysis" state
|
||||
And: State transitions are tracked per user session
|
||||
And: All usage and transitions are properly audited
|
||||
```
|
||||
|
||||
**Прогрессия состояний рабочего процесса:**
|
||||
```
|
||||
Scenario: Complete workflow execution
|
||||
Given: Request with groups ["knowledge", "compute"] and state "undefined"
|
||||
When: Agent executes knowledge-query tool (transitions to "analysis")
|
||||
And: Agent executes complex-analysis tool (transitions to "results")
|
||||
And: Agent executes reset-workflow tool (transitions to "undefined")
|
||||
Then: Each step has correctly filtered available tools
|
||||
And: State transitions are logged with timestamps
|
||||
And: Final state allows initial workflow to repeat
|
||||
```
|
||||
|
||||
### 10. Соображения производительности
|
||||
|
||||
#### 10.1 Влияние загрузки инструментов
|
||||
|
||||
**Загрузка конфигурации:**
|
||||
Метаданные группы и состояния загружаются один раз при запуске
|
||||
Минимальные накладные расходы памяти на каждый инструмент (дополнительные поля)
|
||||
Отсутствие влияния на время инициализации инструмента
|
||||
|
||||
**Обработка запросов:**
|
||||
Объединенная фильтрация группы+состояния выполняется один раз для каждого запроса
|
||||
Сложность O(n), где n = количество настроенных инструментов
|
||||
Переходы состояний добавляют минимальные накладные расходы (назначение строки)
|
||||
Незначительное влияние для типичного количества инструментов (< 100)
|
||||
|
||||
#### 10.2 Стратегии оптимизации
|
||||
|
||||
**Предварительно вычисленные наборы инструментов:**
|
||||
Кэширование наборов инструментов по комбинации группа+состояние
|
||||
Избежание повторной фильтрации для распространенных шаблонов группы/состояния
|
||||
Компромисс между памятью и вычислениями для часто используемых комбинаций
|
||||
|
||||
**Отложенная загрузка:**
|
||||
Загрузка реализаций инструментов только при необходимости
|
||||
Уменьшение времени запуска для развертываний с большим количеством инструментов
|
||||
Динамическая регистрация инструментов на основе требований группы
|
||||
|
||||
### 11. Будущие улучшения
|
||||
|
||||
#### 11.1 Динамическое назначение групп
|
||||
|
||||
**Группировка, учитывающая контекст:**
|
||||
Назначение инструментов группам на основе контекста запроса
|
||||
Доступность групп во времени (только в рабочее время)
|
||||
Ограничения групп на основе нагрузки (дорогие инструменты при низком использовании)
|
||||
|
||||
#### 11.2 Иерархии групп
|
||||
|
||||
**Вложенная структура групп:**
|
||||
```json
|
||||
{
|
||||
"knowledge": {
|
||||
"read": ["knowledge-query", "entity-search"],
|
||||
"write": ["graph-update", "entity-create"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 11.3 Рекомендации по инструментам
|
||||
|
||||
**Предложения, основанные на группах:**
|
||||
Предлагать оптимальные группы инструментов для типов запросов.
|
||||
Учиться на основе моделей использования для улучшения рекомендаций.
|
||||
Предоставлять резервные группы, когда предпочитаемые инструменты недоступны.
|
||||
|
||||
### 12. Открытые вопросы
|
||||
|
||||
1. **Проверка групп**: Должны ли недопустимые имена групп в запросах вызывать критические ошибки или предупреждения?
|
||||
|
||||
2. **Обнаружение групп**: Должна ли система предоставлять API для перечисления доступных групп и их инструментов?
|
||||
|
||||
3. **Динамические группы**: Должны ли группы быть настраиваемыми во время выполнения или только при запуске?
|
||||
|
||||
4. **Наследование групп**: Должны ли инструменты наследовать группы от своих родительских категорий или реализаций?
|
||||
|
||||
5. **Мониторинг производительности**: Какие дополнительные метрики необходимы для эффективного отслеживания использования инструментов на основе групп?
|
||||
|
||||
### 13. Заключение
|
||||
|
||||
Система групп инструментов обеспечивает:
|
||||
|
||||
**Безопасность**: Гранулярный контроль доступа к возможностям агента.
|
||||
**Производительность**: Снижение накладных расходов на загрузку и выбор инструментов.
|
||||
**Гибкость**: Многомерная классификация инструментов.
|
||||
**Совместимость**: Бесшовная интеграция с существующими архитектурами агентов.
|
||||
|
||||
Эта система позволяет развертываниям TrustGraph лучше управлять доступом к инструментам, улучшать границы безопасности и оптимизировать использование ресурсов, сохраняя полную обратную совместимость с существующими конфигурациями и запросами.
|
||||
479
docs/tech-specs/ru/tool-services.ru.md
Normal file
479
docs/tech-specs/ru/tool-services.ru.md
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Статус
|
||||
|
||||
Реализовано
|
||||
|
||||
## Обзор
|
||||
|
||||
Эта спецификация определяет механизм динамически подключаемых инструментов для агентов, называемых "сервисами инструментов". В отличие от существующих встроенных типов инструментов (`KnowledgeQueryImpl`, `McpToolImpl` и т.д.), сервисы инструментов позволяют добавлять новые инструменты путем:
|
||||
|
||||
1. Развертывания нового сервиса на базе Pulsar
|
||||
2. Добавления описания конфигурации, которое сообщает агенту, как его вызывать
|
||||
|
||||
Это обеспечивает расширяемость без изменения основной структуры агента.
|
||||
|
||||
## Терминология
|
||||
|
||||
| Термин | Определение |
|
||||
|------|------------|
|
||||
| **Встроенный инструмент** | Существующие типы инструментов с жестко закодированными реализациями в `tools.py` |
|
||||
| **Сервис инструмента** | Сервис Pulsar, который может быть вызван как инструмент для агента, определенный описанием сервиса |
|
||||
| **Инструмент** | Настроенный экземпляр, который ссылается на сервис инструмента и предоставляется агенту/LLM |
|
||||
|
||||
Это двухзвенная модель, аналогичная инструментам MCP:
|
||||
MCP: MCP-сервер определяет интерфейс инструмента → Конфигурация инструмента ссылается на него
|
||||
Сервисы инструментов: Сервис инструмента определяет интерфейс Pulsar → Конфигурация инструмента ссылается на него
|
||||
|
||||
## Обзор: Существующие инструменты
|
||||
|
||||
### Реализация встроенного инструмента
|
||||
|
||||
Инструменты в настоящее время определены в `trustgraph-flow/trustgraph/agent/react/tools.py` с типизированными реализациями:
|
||||
|
||||
```python
|
||||
class KnowledgeQueryImpl:
|
||||
async def invoke(self, question):
|
||||
client = self.context("graph-rag-request")
|
||||
return await client.rag(question, self.collection)
|
||||
```
|
||||
|
||||
Каждый тип инструмента:
|
||||
Имеет жестко заданный сервис Pulsar, к которому он обращается (например, `graph-rag-request`)
|
||||
Знает точный метод, который нужно вызвать у клиента (например, `client.rag()`)
|
||||
Имеет типизированные аргументы, определенные в реализации
|
||||
|
||||
### Регистрация инструментов (service.py:105-214)
|
||||
|
||||
Инструменты загружаются из конфигурации с помощью поля `type`, которое сопоставляется с реализацией:
|
||||
|
||||
```python
|
||||
if impl_id == "knowledge-query":
|
||||
impl = functools.partial(KnowledgeQueryImpl, collection=data.get("collection"))
|
||||
elif impl_id == "text-completion":
|
||||
impl = TextCompletionImpl
|
||||
# ... etc
|
||||
```
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Двухуровневая модель
|
||||
|
||||
#### Уровень 1: Описание сервиса инструмента
|
||||
|
||||
Сервис инструмента определяет интерфейс сервиса Pulsar. Он объявляет:
|
||||
Очереди Pulsar для запросов/ответов
|
||||
Параметры конфигурации, которые он требует от инструментов, использующих его
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "custom-rag",
|
||||
"request-queue": "non-persistent://tg/request/custom-rag",
|
||||
"response-queue": "non-persistent://tg/response/custom-rag",
|
||||
"config-params": [
|
||||
{"name": "collection", "required": true}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Сервис, который не требует параметров конфигурации:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "calculator",
|
||||
"request-queue": "non-persistent://tg/request/calc",
|
||||
"response-queue": "non-persistent://tg/response/calc",
|
||||
"config-params": []
|
||||
}
|
||||
```
|
||||
|
||||
#### Уровень 2: Описание инструмента
|
||||
|
||||
Инструмент ссылается на сервис инструмента и предоставляет:
|
||||
Значения параметров конфигурации (соответствующие требованиям сервиса)
|
||||
Метаданные инструмента для агента (название, описание)
|
||||
Определения аргументов для LLM
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "tool-service",
|
||||
"name": "query-customers",
|
||||
"description": "Query the customer knowledge base",
|
||||
"service": "custom-rag",
|
||||
"collection": "customers",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "question",
|
||||
"type": "string",
|
||||
"description": "The question to ask about customers"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Несколько инструментов могут ссылаться на один и тот же сервис с разными конфигурациями:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "tool-service",
|
||||
"name": "query-products",
|
||||
"description": "Query the product knowledge base",
|
||||
"service": "custom-rag",
|
||||
"collection": "products",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "question",
|
||||
"type": "string",
|
||||
"description": "The question to ask about products"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Формат запроса
|
||||
|
||||
Когда вызывается инструмент, запрос к службе инструмента включает в себя:
|
||||
`user`: Из запроса агента (многопользовательский режим)
|
||||
`config`: Значения конфигурации, закодированные в формате JSON, из описания инструмента
|
||||
`arguments`: Аргументы, закодированные в формате JSON, из LLM
|
||||
|
||||
```json
|
||||
{
|
||||
"user": "alice",
|
||||
"config": "{\"collection\": \"customers\"}",
|
||||
"arguments": "{\"question\": \"What are the top customer complaints?\"}"
|
||||
}
|
||||
```
|
||||
|
||||
Сервис инструментов получает это в виде разобранных словарей в методе `invoke`.
|
||||
|
||||
### Общая реализация сервиса инструментов
|
||||
|
||||
Класс `ToolServiceImpl` вызывает сервисы инструментов на основе конфигурации:
|
||||
|
||||
```python
|
||||
class ToolServiceImpl:
|
||||
def __init__(self, context, request_queue, response_queue, config_values, arguments, processor):
|
||||
self.request_queue = request_queue
|
||||
self.response_queue = response_queue
|
||||
self.config_values = config_values # e.g., {"collection": "customers"}
|
||||
# ...
|
||||
|
||||
async def invoke(self, **arguments):
|
||||
client = await self._get_or_create_client()
|
||||
response = await client.call(user, self.config_values, arguments)
|
||||
if isinstance(response, str):
|
||||
return response
|
||||
else:
|
||||
return json.dumps(response)
|
||||
```
|
||||
|
||||
## Принятые решения по проектированию
|
||||
|
||||
### Модель конфигурации с двумя уровнями
|
||||
|
||||
Сервисы инструментов следуют двухслойной модели, аналогичной инструментам MCP:
|
||||
|
||||
1. **Сервис инструмента**: Определяет интерфейс сервиса Pulsar (тема, необходимые параметры конфигурации).
|
||||
2. **Инструмент**: Ссылается на сервис инструмента, предоставляет значения конфигурации, определяет аргументы LLM.
|
||||
|
||||
Это разделение позволяет:
|
||||
Использовать один сервис инструмента несколькими инструментами с разными конфигурациями.
|
||||
Четко различать интерфейс сервиса и конфигурацию инструмента.
|
||||
Повторное использование определений сервисов.
|
||||
|
||||
### Отображение запросов: Прямая передача с оболочкой
|
||||
|
||||
Запрос к сервису инструмента представляет собой структурированную оболочку, содержащую:
|
||||
`user`: Передается из запроса агента для поддержки многопользовательской среды.
|
||||
Значения конфигурации: Из описателя инструмента (например, `collection`).
|
||||
`arguments`: Аргументы, предоставленные LLM, передаются в виде словаря.
|
||||
|
||||
Менеджер агента анализирует ответ LLM в `act.arguments` в виде словаря (`agent_manager.py:117-154`). Этот словарь включается в оболочку запроса.
|
||||
|
||||
### Обработка схем: Без типов
|
||||
|
||||
Запросы и ответы используют нетипизированные словари. Отсутствует проверка схемы на уровне агента - сервис инструмента отвечает за проверку своих входных данных. Это обеспечивает максимальную гибкость при определении новых сервисов.
|
||||
|
||||
### Клиентский интерфейс: Прямые темы Pulsar
|
||||
|
||||
Сервисы инструментов используют прямые темы Pulsar без необходимости настройки потоков. Описатель сервиса инструмента указывает полные имена очередей:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "joke-service",
|
||||
"request-queue": "non-persistent://tg/request/joke",
|
||||
"response-queue": "non-persistent://tg/response/joke",
|
||||
"config-params": [...]
|
||||
}
|
||||
```
|
||||
|
||||
Это позволяет размещать сервисы в любом пространстве имен.
|
||||
|
||||
### Обработка ошибок: Стандартная конвенция для ошибок
|
||||
|
||||
Ответы сервисов инструментов соответствуют существующей схеме с полем `error`:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Error:
|
||||
type: str = ""
|
||||
message: str = ""
|
||||
```
|
||||
|
||||
Структура ответа:
|
||||
Успех: `error` имеет значение `None`, ответ содержит результат
|
||||
Ошибка: `error` заполняется значениями `type` и `message`
|
||||
|
||||
Это соответствует шаблону, используемому во всех существующих схемах сервисов (например, `PromptResponse`, `QueryResponse`, `AgentResponse`).
|
||||
|
||||
### Корреляция запросов и ответов
|
||||
|
||||
Корреляция запросов и ответов осуществляется с использованием `id` в свойствах сообщения Pulsar:
|
||||
|
||||
Запрос включает `id` в свойствах: `properties={"id": id}`
|
||||
Ответы включают тот же `id`: `properties={"id": id}`
|
||||
|
||||
Это соответствует существующему шаблону, используемому во всей кодовой базе (например, `agent_service.py`, `llm_service.py`).
|
||||
|
||||
### Поддержка потоковой передачи
|
||||
|
||||
Сервисы инструментов могут возвращать потоковые ответы:
|
||||
|
||||
Несколько сообщений ответа с тем же `id` в свойствах
|
||||
Каждый ответ включает поле `end_of_stream: bool`
|
||||
Заключительный ответ имеет `end_of_stream: True`
|
||||
|
||||
Это соответствует шаблону, используемому в `AgentResponse` и других сервисах потоковой передачи.
|
||||
|
||||
### Обработка ответов: Возврат строки
|
||||
|
||||
Все существующие инструменты следуют одному и тому же шаблону: **получение аргументов в виде словаря, возврат наблюдения в виде строки**.
|
||||
|
||||
| Инструмент | Обработка ответов |
|
||||
|------|------------------|
|
||||
| `KnowledgeQueryImpl` | Возвращает `client.rag()` напрямую (строка) |
|
||||
| `TextCompletionImpl` | Возвращает `client.question()` напрямую (строка) |
|
||||
| `McpToolImpl` | Возвращает строку, или `json.dumps(output)`, если это не строка |
|
||||
| `StructuredQueryImpl` | Форматирует результат в строку |
|
||||
| `PromptImpl` | Возвращает `client.prompt()` напрямую (строка) |
|
||||
|
||||
Сервисы инструментов следуют тому же контракту:
|
||||
Сервис возвращает строковый ответ (наблюдение)
|
||||
Если ответ не является строкой, он преобразуется с помощью `json.dumps()`
|
||||
Не требуется конфигурация извлечения в дескрипторе
|
||||
|
||||
Это упрощает дескриптор и возлагает ответственность на сервис по возврату соответствующего текстового ответа для агента.
|
||||
|
||||
## Руководство по настройке
|
||||
|
||||
Для добавления нового сервиса инструмента требуются два элемента конфигурации:
|
||||
|
||||
### 1. Конфигурация сервиса инструмента
|
||||
|
||||
Хранится под ключом конфигурации `tool-service`. Определяет очереди Pulsar и доступные параметры конфигурации.
|
||||
|
||||
| Поле | Обязательно | Описание |
|
||||
|-------|----------|-------------|
|
||||
| `id` | Да | Уникальный идентификатор для сервиса инструмента |
|
||||
| `request-queue` | Да | Полная тема Pulsar для запросов (например, `non-persistent://tg/request/joke`) |
|
||||
| `response-queue` | Да | Полная тема Pulsar для ответов (например, `non-persistent://tg/response/joke`) |
|
||||
| `config-params` | Нет | Массив параметров конфигурации, которые принимает сервис |
|
||||
|
||||
Каждый параметр конфигурации может указывать:
|
||||
`name`: Имя параметра (обязательно)
|
||||
`required`: Должен ли параметр предоставляться инструментами (по умолчанию: false)
|
||||
|
||||
Пример:
|
||||
```json
|
||||
{
|
||||
"id": "joke-service",
|
||||
"request-queue": "non-persistent://tg/request/joke",
|
||||
"response-queue": "non-persistent://tg/response/joke",
|
||||
"config-params": [
|
||||
{"name": "style", "required": false}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Конфигурация инструмента
|
||||
|
||||
Хранится под ключом конфигурации `tool`. Определяет инструмент, которым может пользоваться агент.
|
||||
|
||||
| Поле | Обязательно | Описание |
|
||||
|-------|----------|-------------|
|
||||
| `type` | Да | Должно быть `"tool-service"` |
|
||||
| `name` | Да | Название инструмента, доступное для LLM |
|
||||
| `description` | Да | Описание того, что делает инструмент (отображается для LLM) |
|
||||
| `service` | Да | ID сервиса инструмента, который необходимо вызвать |
|
||||
| `arguments` | Нет | Массив определений аргументов для LLM |
|
||||
| *(параметры конфигурации)* | Зависит | Любые параметры конфигурации, определенные сервисом |
|
||||
|
||||
Каждый аргумент может содержать:
|
||||
`name`: Имя аргумента (обязательно)
|
||||
`type`: Тип данных, например, `"string"` (обязательно)
|
||||
`description`: Описание, отображаемое для LLM (обязательно)
|
||||
|
||||
Пример:
|
||||
```json
|
||||
{
|
||||
"type": "tool-service",
|
||||
"name": "tell-joke",
|
||||
"description": "Tell a joke on a given topic",
|
||||
"service": "joke-service",
|
||||
"style": "pun",
|
||||
"arguments": [
|
||||
{
|
||||
"name": "topic",
|
||||
"type": "string",
|
||||
"description": "The topic for the joke (e.g., programming, animals, food)"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Загрузка конфигурации
|
||||
|
||||
Используйте `tg-put-config-item` для загрузки конфигураций:
|
||||
|
||||
```bash
|
||||
# Load tool-service config
|
||||
tg-put-config-item tool-service/joke-service < joke-service.json
|
||||
|
||||
# Load tool config
|
||||
tg-put-config-item tool/tell-joke < tell-joke.json
|
||||
```
|
||||
|
||||
Агент-менеджер должен быть перезапущен для применения новых конфигураций.
|
||||
|
||||
## Детали реализации
|
||||
|
||||
### Схема
|
||||
|
||||
Типы запросов и ответов в `trustgraph-base/trustgraph/schema/services/tool_service.py`:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ToolServiceRequest:
|
||||
user: str = "" # User context for multi-tenancy
|
||||
config: str = "" # JSON-encoded config values from tool descriptor
|
||||
arguments: str = "" # JSON-encoded arguments from LLM
|
||||
|
||||
@dataclass
|
||||
class ToolServiceResponse:
|
||||
error: Error | None = None
|
||||
response: str = "" # String response (the observation)
|
||||
end_of_stream: bool = False
|
||||
```
|
||||
|
||||
### Серверная часть: DynamicToolService
|
||||
|
||||
Базовый класс в `trustgraph-base/trustgraph/base/dynamic_tool_service.py`:
|
||||
|
||||
```python
|
||||
class DynamicToolService(AsyncProcessor):
|
||||
"""Base class for implementing tool services."""
|
||||
|
||||
def __init__(self, **params):
|
||||
topic = params.get("topic", default_topic)
|
||||
# Constructs topics: non-persistent://tg/request/{topic}, non-persistent://tg/response/{topic}
|
||||
# Sets up Consumer and Producer
|
||||
|
||||
async def invoke(self, user, config, arguments):
|
||||
"""Override this method to implement the tool's logic."""
|
||||
raise NotImplementedError()
|
||||
```
|
||||
|
||||
### Клиентская часть: ToolServiceImpl
|
||||
|
||||
Реализация в `trustgraph-flow/trustgraph/agent/react/tools.py`:
|
||||
|
||||
```python
|
||||
class ToolServiceImpl:
|
||||
def __init__(self, context, request_queue, response_queue, config_values, arguments, processor):
|
||||
# Uses the provided queue paths directly
|
||||
# Creates ToolServiceClient on first use
|
||||
|
||||
async def invoke(self, **arguments):
|
||||
client = await self._get_or_create_client()
|
||||
response = await client.call(user, config_values, arguments)
|
||||
return response if isinstance(response, str) else json.dumps(response)
|
||||
```
|
||||
|
||||
### Файлы
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|---------|
|
||||
| `trustgraph-base/trustgraph/schema/services/tool_service.py` | Схемы запросов/ответов |
|
||||
| `trustgraph-base/trustgraph/base/tool_service_client.py` | Клиент для вызова сервисов |
|
||||
| `trustgraph-base/trustgraph/base/dynamic_tool_service.py` | Базовый класс для реализации сервиса |
|
||||
| `trustgraph-flow/trustgraph/agent/react/tools.py` | Класс `ToolServiceImpl` |
|
||||
| `trustgraph-flow/trustgraph/agent/react/service.py` | Загрузка конфигурации |
|
||||
|
||||
### Пример: Сервис шуток
|
||||
|
||||
Пример сервиса на `trustgraph-flow/trustgraph/tool_service/joke/`:
|
||||
|
||||
```python
|
||||
class Processor(DynamicToolService):
|
||||
async def invoke(self, user, config, arguments):
|
||||
style = config.get("style", "pun")
|
||||
topic = arguments.get("topic", "")
|
||||
joke = pick_joke(topic, style)
|
||||
return f"Hey {user}! Here's a {style} for you:\n\n{joke}"
|
||||
```
|
||||
|
||||
Конфигурация сервиса инструментов:
|
||||
```json
|
||||
{
|
||||
"id": "joke-service",
|
||||
"request-queue": "non-persistent://tg/request/joke",
|
||||
"response-queue": "non-persistent://tg/response/joke",
|
||||
"config-params": [{"name": "style", "required": false}]
|
||||
}
|
||||
```
|
||||
|
||||
Конфигурация инструмента:
|
||||
```json
|
||||
{
|
||||
"type": "tool-service",
|
||||
"name": "tell-joke",
|
||||
"description": "Tell a joke on a given topic",
|
||||
"service": "joke-service",
|
||||
"style": "pun",
|
||||
"arguments": [
|
||||
{"name": "topic", "type": "string", "description": "The topic for the joke"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Обратная совместимость
|
||||
|
||||
Существующие встроенные типы инструментов продолжают работать без изменений.
|
||||
`tool-service` - это новый тип инструмента, который существует наряду с существующими типами (`knowledge-query`, `mcp-tool` и т.д.).
|
||||
|
||||
## Будущие соображения
|
||||
|
||||
### Сервисы с автоматическим оповещением
|
||||
|
||||
В будущем можно будет реализовать функцию, позволяющую сервисам публиковать свои собственные описания:
|
||||
|
||||
Сервисы публикуют информацию при запуске в определенной теме `tool-descriptors`.
|
||||
Агент подписывается и динамически регистрирует инструменты.
|
||||
Это обеспечивает возможность подключения и использования без изменения конфигурации.
|
||||
|
||||
Это выходит за рамки первоначальной реализации.
|
||||
|
||||
## Ссылки
|
||||
|
||||
Текущая реализация инструментов: `trustgraph-flow/trustgraph/agent/react/tools.py`
|
||||
Регистрация инструментов: `trustgraph-flow/trustgraph/agent/react/service.py:105-214`
|
||||
Схемы агента: `trustgraph-base/trustgraph/schema/services/agent.py`
|
||||
200
docs/tech-specs/ru/universal-decoder.ru.md
Normal file
200
docs/tech-specs/ru/universal-decoder.ru.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Заголовок
|
||||
|
||||
Универсальный декодер документов, работающий на базе `unstructured` — извлекает любые распространенные форматы документов через единый сервис, обеспечивая полную отслеживаемость и интеграцию с библиотекой, регистрируя позиции источника в качестве метаданных графа знаний для обеспечения полной отслеживаемости.
|
||||
|
||||
## Проблема
|
||||
|
||||
В настоящее время TrustGraph имеет декодер, специфичный для PDF. Поддержка дополнительных форматов (DOCX, XLSX, PPTX и т.д.) требует значительных изменений.
|
||||
|
||||
## Решение
|
||||
|
||||
Мы предлагаем универсальный декодер, который будет поддерживать широкий спектр форматов документов. Этот декодер будет использовать библиотеку `unstructured` для извлечения текста и метаданных из документов.
|
||||
|
||||
## Архитектура
|
||||
|
||||
Универсальный декодер будет состоять из следующих компонентов:
|
||||
|
||||
* **Входной компонент:** Принимает документ в качестве входных данных.
|
||||
* **Компонент извлечения:** Использует библиотеку `unstructured` для извлечения текста и метаданных из документа.
|
||||
* **Компонент формирования provenance:** Генерирует provenance-данные, которые содержат информацию о том, откуда взялся каждый фрагмент текста.
|
||||
* **Компонент выходных данных:** Выдает извлеченные данные и provenance-данные.
|
||||
|
||||
## Процесс
|
||||
|
||||
1. Декодер получает документ в качестве входных данных.
|
||||
2. Декодер использует библиотеку `unstructured` для извлечения текста и метаданных из документа.
|
||||
3. Декодер генерирует provenance-данные, которые содержат информацию о том, откуда взялся каждый фрагмент текста.
|
||||
4. Декодер выдает извлеченные данные и provenance-данные.
|
||||
|
||||
## Форматы Документов
|
||||
|
||||
| Формат | MIME-тип | Поддержка страниц | Примечания |
|
||||
|---|---|---|---|
|
||||
| PDF | application/pdf | Да | Группировка по страницам |
|
||||
| DOCX | application/vnd.openxmlformats... | Нет | Использование стратегии секций |
|
||||
| PPTX | application/vnd.openxmlformats... | Да | Группировка по слайдам |
|
||||
| XLSX/XLS | application/vnd.openxmlformats... | Да | Группировка по листам |
|
||||
| HTML | text/html | Нет | Использование стратегии секций |
|
||||
| Markdown | text/markdown | Нет | Использование стратегии секций |
|
||||
| Plain | text/plain | Нет | Использование стратегии секций |
|
||||
| CSV | text/csv | Нет | Использование стратегии секций |
|
||||
| RST | text/x-rst | Нет | Использование стратегии секций |
|
||||
| RTF | application/rtf | Нет | Использование стратегии секций |
|
||||
| ODT | application/vnd.oasis... | Нет | Использование стратегии секций |
|
||||
| TSV | text/tab-separated-values | Нет | Использование стратегии секций |
|
||||
|
||||
## Метаданные Provenance
|
||||
|
||||
Каждая сущность страницы/секции регистрирует метаданные о позиции в виде triple в `GRAPH_SOURCE`, обеспечивая полную отслеживаемость от triple в графе знаний обратно к исходным позициям в документе.
|
||||
|
||||
#### Существующие поля (уже в `derived_entity_triples`)
|
||||
|
||||
* `page_number` — номер страницы/листа/слайда (1-indexed, только для страниц)
|
||||
* `char_offset` — смещение символов для этой страницы/секции в полном тексте
|
||||
* `char_length` — длина текста для этой страницы/секции в документе
|
||||
|
||||
#### Новые поля (расширяем `derived_entity_triples`)
|
||||
|
||||
* `mime_type` — исходный формат документа (например, `application/pdf`)
|
||||
* `element_types` — список категорий элементов `unstructured`, найденных на этой странице/секции (например, "Title,NarrativeText,Table")
|
||||
* `table_count` — количество таблиц на этой странице/секции
|
||||
* `image_count` — количество изображений на этой странице/секции
|
||||
|
||||
Эти требуют новых префиксов пространства имен TG:
|
||||
|
||||
```
|
||||
TG_SECTION_TYPE = "https://trustgraph.ai/ns/Section"
|
||||
TG_IMAGE_TYPE = "https://trustgraph.ai/ns/Image"
|
||||
TG_ELEMENT_TYPES = "https://trustgraph.ai/ns/elementTypes"
|
||||
TG_TABLE_COUNT = "https://trustgraph.ai/ns/tableCount"
|
||||
TG_IMAGE_COUNT = "https://trustgraph.ai/ns/imageCount"
|
||||
```
|
||||
|
||||
Схема URN для изображений: `urn:image:{uuid}`
|
||||
|
||||
(`TG_MIME_TYPE` уже существует.)
|
||||
|
||||
#### Новый тип сущности
|
||||
|
||||
Для не-страничных форматов (DOCX, HTML, Markdown и т.д.), где декодер выдает весь документ в виде одной сущности, тип сущности становится:
|
||||
|
||||
```
|
||||
TG_SECTION_TYPE = "https://trustgraph.ai/ns/Section"
|
||||
```
|
||||
|
||||
Это отличает секции от страниц при запросе provenance:
|
||||
|
||||
| Entity | Type | Когда используется |
|
||||
|---|---|---|
|
||||
| Document | `tg:Document` | Оригинальный загруженный файл |
|
||||
| Page | `tg:Page` | Для page-based форматов (PDF, PPTX, XLSX) |
|
||||
| Section | `tg:Section` | Для не-page форматов (DOCX, HTML, MD и т.д.) |
|
||||
| Image | `tg:Image` | Встроенные изображения (сохранены, не обрабатываются) |
|
||||
| Chunk | `tg:Chunk` | Выход из chunker |
|
||||
| Subgraph | `tg:Subgraph` | Выход из extraции KG |
|
||||
|
||||
Тип устанавливается декодером в зависимости от группировки по страницам или выдачи всей документа в виде одной сущности. `derived_entity_triples` добавляет необязательный параметр `section` — если он установлен, сущность имеет тип `tg:Section` вместо `tg:Page`.
|
||||
|
||||
#### Полная цепочка provenance
|
||||
|
||||
```
|
||||
Triple KG
|
||||
→ subgraph (provenance)
|
||||
→ chunk (char_offset, char_length within page)
|
||||
→ page/section (page_number, char_offset, char_length within doc, mime_type, element_types)
|
||||
→ document (оригинальный файл в librarian)
|
||||
```
|
||||
|
||||
Каждый линк — это набор triple в графе `GRAPH_SOURCE`.
|
||||
|
||||
## Конфигурация Сервиса
|
||||
|
||||
Аргументы командной строки:
|
||||
|
||||
```
|
||||
--strategy Стратегия партиционирования: auto, hi_res, fast (по умолчанию: auto)
|
||||
--languages Разделенные кодами OCR (по умолчанию: eng)
|
||||
--section-strategy Группировка секций: whole-document, heading, element-type,
|
||||
count, size (по умолчанию: whole-document)
|
||||
--section-element-count Элементы на секцию для 'count' strategy (по умолчанию: 20)
|
||||
--section-max-size Макс. символов на секцию для 'size' strategy (по умолчанию: 4000)
|
||||
--section-within-pages Применение стратегии секций также для страниц (по умолчанию: false)
|
||||
```
|
||||
|
||||
Кроме стандартных аргументов для `FlowProcessor` и очереди librarian.
|
||||
|
||||
## Интеграция с Flow
|
||||
|
||||
Универсальный декодер занимает ту же позицию в потоке обработки, что и существующий декодер PDF:
|
||||
|
||||
```
|
||||
Document → [universal-decoder] → TextDocument → [chunker] → Chunk → ...
|
||||
```
|
||||
|
||||
Он регистрирует:
|
||||
* Консумент `input` (схема Document)
|
||||
* Производитель `output` (схема TextDocument)
|
||||
* Производитель `triples` (схема)
|
||||
* Запросы/отзывы к librarian (для fetch и хранения child document)
|
||||
|
||||
## Развертывание
|
||||
|
||||
* Новый контейнер: `trustgraph-flow-universal-decoder`
|
||||
* Зависимость: `unstructured[all-docs]` (включает PDF, DOCX, PPTX и т.д.)
|
||||
* Можно запускать параллельно или заменять существующий декодер PDF, в зависимости от конфигурации потока
|
||||
* Существующий декодер PDF остается доступным, если зависимости `unstructured` слишком велики
|
||||
|
||||
## Изменения
|
||||
|
||||
| Компонент | Изменение |
|
||||
|---|---|
|
||||
| `provenance/namespaces.py` | Добавить `TG_SECTION_TYPE`, `TG_IMAGE_TYPE`, `TG_ELEMENT_TYPES`, `TG_TABLE_COUNT`, `TG_IMAGE_COUNT` |
|
||||
| `provenance/triples.py` | Добавить параметры `mime_type`, `element_types`, `table_count`, `image_count` |
|
||||
| `provenance/__init__.py` | Экспортировать новые константы |
|
||||
| Новый: `decoding/universal/` | Новый модуль сервиса |
|
||||
| `setup.cfg` / `pyproject` | Добавить зависимость `unstructured[all-docs]` |
|
||||
| Docker | Новый образ контейнера |
|
||||
| Flow definitions | |
|
||||
|
||||
## Что не меняется
|
||||
|
||||
* Chunker (принимает TextDocument, работает как раньше)
|
||||
* Downstream extractors (принимают Chunk, без изменений)
|
||||
* Librarian (хранит child document, без изменений)
|
||||
* Схема (Document, TextDocument, Chunk без изменений)
|
||||
* Запрос provenance (без изменений)
|
||||
|
||||
## Риски
|
||||
|
||||
* `unstructured[all-docs]` имеет тяжелые зависимости (poppler, tesseract, libreoffice для некоторых форматов)
|
||||
* Необходимы значительные изменения.
|
||||
|
||||
## Преимущества
|
||||
|
||||
* Поддержка широкого спектра форматов документов
|
||||
* Использование библиотеки `unstructured` для извлечения текста и метаданных
|
||||
* Генерация provenance-данных
|
||||
* Возможность интеграции с существующей системой обработки документов
|
||||
|
||||
## Дополнительные Замечания
|
||||
|
||||
* Эта архитектура требует тщательного тестирования и оптимизации.
|
||||
* Необходимо учитывать производительность при обработке больших документов.
|
||||
* Важно обеспечить совместимость с существующими системами обработки документов.
|
||||
* Следует использовать возможности библиотеки `unstructured` для оптимизации извлечения текста.
|
||||
* Необходимо провести анализ требований к производительности и выбрать оптимальную конфигурацию.
|
||||
* Необходимо обеспечить безопасность при обработке конфиденциальных данных.
|
||||
|
||||
## Заключение
|
||||
|
||||
Предлагаемая архитектура универсального декодера представляет собой мощное решение для обработки широкого спектра форматов документов. Она обеспечивает гибкость, масштабируемость и возможность интеграции с существующими системами. При правильной реализации и тестировании, эта архитектура позволит эффективно извлекать данные из различных источников и использовать их в аналитических целях.
|
||||
307
docs/tech-specs/ru/vector-store-lifecycle.ru.md
Normal file
307
docs/tech-specs/ru/vector-store-lifecycle.ru.md
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
---
|
||||
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.
|
||||
|
||||
## Обзор
|
||||
|
||||
Этот документ описывает, как TrustGraph управляет коллекциями векторных хранилищ в различных реализациях бэкенда (Qdrant, Pinecone, Milvus). Проект решает проблему поддержки вложений с разными размерностями без жесткой кодировки значений размерностей.
|
||||
|
||||
## Описание проблемы
|
||||
|
||||
Векторные хранилища требуют указания размерности вложений при создании коллекций/индексов. Однако:
|
||||
Разные модели вложений генерируют разные размерности (например, 384, 768, 1536).
|
||||
Размерность неизвестна до генерации первого вложения.
|
||||
Одна коллекция TrustGraph может получать вложения из нескольких моделей.
|
||||
Жесткая кодировка размерности (например, 384) приводит к сбоям при использовании других размеров вложений.
|
||||
|
||||
## Принципы проектирования
|
||||
|
||||
1. **Отложенное создание**: Коллекции создаются по запросу при первой записи, а не во время операций управления коллекциями.
|
||||
2. **Именование на основе размерности**: Имена коллекций включают размерность вложения в качестве суффикса.
|
||||
3. **Плавное снижение функциональности**: Запросы к несуществующим коллекциям возвращают пустые результаты, а не ошибки.
|
||||
4. **Поддержка нескольких размерностей**: Одна логическая коллекция может иметь несколько физических коллекций (по одной для каждой размерности).
|
||||
|
||||
## Архитектура
|
||||
|
||||
### Соглашение об именовании коллекций
|
||||
|
||||
Коллекции векторных хранилищ используют суффиксы, указывающие размерность, для поддержки нескольких размеров вложений:
|
||||
|
||||
**Вложения документов:**
|
||||
Qdrant: `d_{user}_{collection}_{dimension}`
|
||||
Pinecone: `d-{user}-{collection}-{dimension}`
|
||||
Milvus: `doc_{user}_{collection}_{dimension}`
|
||||
|
||||
**Вложения графов:**
|
||||
Qdrant: `t_{user}_{collection}_{dimension}`
|
||||
Pinecone: `t-{user}-{collection}-{dimension}`
|
||||
Milvus: `entity_{user}_{collection}_{dimension}`
|
||||
|
||||
Примеры:
|
||||
`d_alice_papers_384` - Коллекция статей Алисы с вложениями размерностью 384.
|
||||
`d_alice_papers_768` - Та же логическая коллекция с вложениями размерностью 768.
|
||||
`t_bob_knowledge_1536` - Граф знаний Боба с вложениями размерностью 1536.
|
||||
|
||||
### Фазы жизненного цикла
|
||||
|
||||
#### 1. Запрос на создание коллекции
|
||||
|
||||
**Поток запросов:**
|
||||
```
|
||||
User/System → Librarian → Storage Management Topic → Vector Stores
|
||||
```
|
||||
|
||||
**Поведение:**
|
||||
Библиотекарь отправляет запросы `create-collection` всем хранилищам данных.
|
||||
Процессоры векторных хранилищ подтверждают запрос, но **не создают физические коллекции**.
|
||||
Ответ возвращается немедленно с сообщением об успехе.
|
||||
Фактическое создание коллекции откладывается до первой записи.
|
||||
|
||||
**Обоснование:**
|
||||
Размерность неизвестна на момент создания.
|
||||
Предотвращает создание коллекций с неправильными размерами.
|
||||
Упрощает логику управления коллекциями.
|
||||
|
||||
#### 2. Операции записи (отложенное создание)
|
||||
|
||||
**Поток записи:**
|
||||
```
|
||||
Data → Storage Processor → Check Collection → Create if Needed → Insert
|
||||
```
|
||||
|
||||
**Поведение:**
|
||||
1. Извлечь размерность вложения из вектора: `dim = len(vector)`
|
||||
2. Сформировать имя коллекции с суффиксом, указывающим на размерность.
|
||||
3. Проверить, существует ли коллекция с указанной размерностью.
|
||||
4. Если коллекция не существует:
|
||||
Создать коллекцию с правильной размерностью.
|
||||
Записать в лог: `"Lazily creating collection {name} with dimension {dim}"`
|
||||
5. Вставить вложение в коллекцию, специфичную для данной размерности.
|
||||
|
||||
**Пример сценария:**
|
||||
```
|
||||
1. User creates collection "papers"
|
||||
→ No physical collections created yet
|
||||
|
||||
2. First document with 384-dim embedding arrives
|
||||
→ Creates d_user_papers_384
|
||||
→ Inserts data
|
||||
|
||||
3. Second document with 768-dim embedding arrives
|
||||
→ Creates d_user_papers_768
|
||||
→ Inserts data
|
||||
|
||||
Result: Two physical collections for one logical collection
|
||||
```
|
||||
|
||||
#### 3. Операции запросов
|
||||
|
||||
**Поток запросов:**
|
||||
```
|
||||
Query Vector → Determine Dimension → Check Collection → Search or Return Empty
|
||||
```
|
||||
|
||||
**Поведение:**
|
||||
1. Извлечение размерности из вектора запроса: `dim = len(vector)`
|
||||
2. Формирование имени коллекции с суффиксом, указывающим размерность.
|
||||
3. Проверка наличия коллекции.
|
||||
4. Если коллекция существует:
|
||||
Выполнение поиска по схожести.
|
||||
Возврат результатов.
|
||||
5. Если коллекция не существует:
|
||||
Запись в журнал: `"Collection {name} does not exist, returning empty results"`
|
||||
Возврат пустого списка (без возникновения ошибки).
|
||||
|
||||
**Несколько размерностей в одном запросе:**
|
||||
Если запрос содержит векторы разных размерностей.
|
||||
Для каждой размерности выполняется поиск в соответствующей коллекции.
|
||||
Результаты объединяются.
|
||||
Отсутствующие коллекции пропускаются (не рассматриваются как ошибки).
|
||||
|
||||
**Обоснование:**
|
||||
Запрос к пустой коллекции является допустимым сценарием использования.
|
||||
Возврат пустых результатов является семантически правильным.
|
||||
Предотвращает ошибки при запуске системы или до загрузки данных.
|
||||
|
||||
#### 4. Удаление коллекции
|
||||
|
||||
**Процесс удаления:**
|
||||
```
|
||||
Delete Request → List All Collections → Filter by Prefix → Delete All Matches
|
||||
```
|
||||
|
||||
**Поведение:**
|
||||
1. Создание префиксного шаблона: `d_{user}_{collection}_` (обратите внимание на завершающий символ подчеркивания)
|
||||
2. Перечисление всех коллекций в векторной базе данных.
|
||||
3. Фильтрация коллекций, соответствующих префиксу.
|
||||
4. Удаление всех соответствующих коллекций.
|
||||
5. Запись каждого удаления в журнал: `"Deleted collection {name}"`
|
||||
6. Итоговый журнал: `"Deleted {count} collection(s) for {user}/{collection}"`
|
||||
|
||||
**Пример:**
|
||||
```
|
||||
Collections in store:
|
||||
- d_alice_papers_384
|
||||
- d_alice_papers_768
|
||||
- d_alice_reports_384
|
||||
- d_bob_papers_384
|
||||
|
||||
Delete "papers" for alice:
|
||||
→ Deletes: d_alice_papers_384, d_alice_papers_768
|
||||
→ Keeps: d_alice_reports_384, d_bob_papers_384
|
||||
```
|
||||
|
||||
**Обоснование:**
|
||||
Обеспечивает полную очистку всех вариантов измерений.
|
||||
Сопоставление с образцом предотвращает случайное удаление несвязанных коллекций.
|
||||
Атомарная операция с точки зрения пользователя (все измерения удаляются вместе).
|
||||
|
||||
## Поведенческие характеристики
|
||||
|
||||
### Нормальная работа
|
||||
|
||||
**Создание коллекции:**
|
||||
✓ Немедленно возвращает успешный результат.
|
||||
✓ Не выделяется физическое хранилище.
|
||||
✓ Быстрая операция (без операций ввода-вывода на стороне сервера).
|
||||
|
||||
**Первая запись:**
|
||||
✓ Создает коллекцию с правильным измерением.
|
||||
✓ Немного медленнее из-за накладных расходов на создание коллекции.
|
||||
✓ Последующие записи в то же измерение выполняются быстро.
|
||||
|
||||
**Запросы до любых записей:**
|
||||
✓ Возвращает пустые результаты.
|
||||
✓ Без ошибок или исключений.
|
||||
✓ Система остается стабильной.
|
||||
|
||||
**Смешанные записи измерений:**
|
||||
✓ Автоматически создает отдельные коллекции для каждого измерения.
|
||||
✓ Каждое измерение изолировано в своей коллекции.
|
||||
✓ Отсутствуют конфликты измерений или ошибки схемы.
|
||||
|
||||
**Удаление коллекции:**
|
||||
✓ Удаляет все варианты измерений.
|
||||
✓ Полная очистка.
|
||||
✓ Отсутствуют "осиротевшие" коллекции.
|
||||
|
||||
### Крайние случаи
|
||||
|
||||
**Несколько моделей встраивания:**
|
||||
```
|
||||
Scenario: User switches from model A (384-dim) to model B (768-dim)
|
||||
Behavior:
|
||||
- Both dimensions coexist in separate collections
|
||||
- Old data (384-dim) remains queryable with 384-dim vectors
|
||||
- New data (768-dim) queryable with 768-dim vectors
|
||||
- Cross-dimension queries return results only for matching dimension
|
||||
```
|
||||
|
||||
**Одновременные первые записи:**
|
||||
```
|
||||
Scenario: Multiple processes write to same collection simultaneously
|
||||
Behavior:
|
||||
- Each process checks for existence before creating
|
||||
- Most vector stores handle concurrent creation gracefully
|
||||
- If race condition occurs, second create is typically idempotent
|
||||
- Final state: Collection exists and both writes succeed
|
||||
```
|
||||
|
||||
**Миграция измерений:**
|
||||
```
|
||||
Scenario: User wants to migrate from 384-dim to 768-dim embeddings
|
||||
Behavior:
|
||||
- No automatic migration
|
||||
- Old collection (384-dim) persists
|
||||
- New collection (768-dim) created on first new write
|
||||
- Both dimensions remain accessible
|
||||
- Manual deletion of old dimension collections possible
|
||||
```
|
||||
|
||||
**Запросы к пустой коллекции:**
|
||||
```
|
||||
Scenario: Query a collection that has never received data
|
||||
Behavior:
|
||||
- Collection doesn't exist (never created)
|
||||
- Query returns empty list
|
||||
- No error state
|
||||
- System logs: "Collection does not exist, returning empty results"
|
||||
```
|
||||
|
||||
## Заметки об реализации
|
||||
|
||||
### Особенности используемого хранилища данных
|
||||
|
||||
**Qdrant:**
|
||||
Использует `collection_exists()` для проверок существования
|
||||
Использует `get_collections()` для перечисления при удалении
|
||||
Создание коллекции требует `VectorParams(size=dim, distance=Distance.COSINE)`
|
||||
|
||||
**Pinecone:**
|
||||
Использует `has_index()` для проверок существования
|
||||
Использует `list_indexes()` для перечисления при удалении
|
||||
Создание индекса требует ожидания статуса "ready"
|
||||
Serverless режим конфигурируется с указанием облака/региона
|
||||
|
||||
**Milvus:**
|
||||
Прямые классы (`DocVectors`, `EntityVectors`) управляют жизненным циклом
|
||||
Внутренний кэш `self.collections[(dim, user, collection)]` для повышения производительности
|
||||
Имена коллекций очищаются (только буквенно-цифровые символы и подчеркивание)
|
||||
Поддерживает схему с автоматически увеличивающимися идентификаторами
|
||||
|
||||
### Вопросы производительности
|
||||
|
||||
**Задержка при первой записи:**
|
||||
Дополнительные накладные расходы, связанные с созданием коллекции
|
||||
Qdrant: ~100-500 мс
|
||||
Pinecone: ~10-30 секунд (подготовка serverless режима)
|
||||
Milvus: ~500-2000 мс (включая индексацию)
|
||||
|
||||
**Производительность запросов:**
|
||||
Проверка существования добавляет минимальные накладные расходы (~1-10 мс)
|
||||
Отсутствие влияния на производительность после создания коллекции
|
||||
Каждая коллекция, соответствующая определенной размерности, оптимизируется независимо
|
||||
|
||||
**Накладные расходы на хранение:**
|
||||
Минимальный объем метаданных на коллекцию
|
||||
Основные накладные расходы связаны с хранением данных для каждой размерности
|
||||
Компромисс: объем хранимых данных против гибкости размерностей
|
||||
|
||||
## Будущие улучшения
|
||||
|
||||
**Автоматическая консолидация размерностей:**
|
||||
Можно добавить фоновый процесс для выявления и объединения неиспользуемых вариантов размерностей
|
||||
Это потребует повторной встраиваемости или уменьшения размерности
|
||||
|
||||
**Обнаружение размерностей:**
|
||||
Можно предоставить API для перечисления всех используемых размерностей для коллекции
|
||||
Полезно для администрирования и мониторинга
|
||||
|
||||
**Предпочтительная размерность по умолчанию:**
|
||||
Можно отслеживать "основную" размерность для каждой коллекции
|
||||
Использовать для запросов, когда контекст размерности недоступен
|
||||
|
||||
**Квоты на хранение:**
|
||||
Возможно, потребуются ограничения на количество размерностей на коллекцию
|
||||
Предотвращение чрезмерного количества вариантов размерностей
|
||||
|
||||
## Примечания по миграции
|
||||
|
||||
**Из системы с суффиксом размерности:**
|
||||
Старые коллекции: `d_{user}_{collection}` (без суффикса размерности)
|
||||
Новые коллекции: `d_{user}_{collection}_{dim}` (с суффиксом размерности)
|
||||
Отсутствует автоматическая миграция - старые коллекции остаются доступными
|
||||
Рассмотрите возможность использования скрипта для ручной миграции, если это необходимо
|
||||
Можно использовать обе схемы именования одновременно
|
||||
|
||||
## Ссылки
|
||||
|
||||
Управление коллекциями: `docs/tech-specs/collection-management.md`
|
||||
Схема хранения: `trustgraph-base/trustgraph/schema/services/storage.py`
|
||||
Сервис Librarian: `trustgraph-flow/trustgraph/librarian/service.py`
|
||||
Loading…
Add table
Add a link
Reference in a new issue