trustgraph/docs/tech-specs/ontology-extract-phase-2.ru.md
Alex Jenkins 8954fa3ad7 Feat: TrustGraph i18n & Documentation Translation Updates (#781)
Native CLI i18n: The TrustGraph CLI has built-in translation support
that dynamically loads language strings. You can test and use
different languages by simply passing the --lang flag (e.g., --lang
es for Spanish, --lang ru for Russian) or by configuring your
environment's LANG variable.

Automated Docs Translations: This PR introduces autonomously
translated Markdown documentation into several target languages,
including Spanish, Swahili, Portuguese, Turkish, Hindi, Hebrew,
Arabic, Simplified Chinese, and Russian.
2026-04-14 12:08:32 +01:00

769 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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`