trustgraph/docs/tech-specs/ontology-extract-phase-2.pt.md

770 lines
24 KiB
Markdown
Raw Normal View History

---
layout: default
title: "Extração de Conhecimento de Ontologias - Fase 2 de Refatoração"
parent: "Portuguese (Beta)"
---
# Extração de Conhecimento de Ontologias - Fase 2 de Refatoração
> **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.
**Status**: Rascunho
**Autor**: Sessão de Análise 2025-12-03
**Relacionado**: `ontology.md`, `ontorag.md`
## Visão Geral
Este documento identifica inconsistências no sistema atual de extração de conhecimento baseado em ontologias e propõe uma refatoração para melhorar o desempenho do LLM e reduzir a perda de informações.
## Implementação Atual
### Como Funciona Atualmente
1. **Carregamento da Ontologia** (`ontology_loader.py`)
Carrega o arquivo JSON da ontologia com chaves como `"fo/Recipe"`, `"fo/Food"`, `"fo/produces"`
Os IDs das classes incluem o prefixo do namespace na própria chave
Exemplo de `food.ontology`:
```json
"classes": {
"fo/Recipe": {
"uri": "http://purl.org/ontology/fo/Recipe",
"rdfs:comment": "A Recipe is a combination..."
}
}
```
2. **Construção do Prompt** (`extract.py:299-307`, `ontology-prompt.md`)
O modelo recebe os dicionários `classes`, `object_properties`, `datatype_properties`
O modelo itera: `{% for class_id, class_def in classes.items() %}`
O LLM vê: `**fo/Recipe**: A Recipe is a combination...`
O formato de saída de exemplo mostra:
```json
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "Recipe"}
{"subject": "recipe:cornish-pasty", "predicate": "has_ingredient", "object": "ingredient:flour"}
```
3. **Análise da Resposta** (`extract.py:382-428`)
Espera um array JSON: `[{"subject": "...", "predicate": "...", "object": "..."}]`
Valida em relação a um subconjunto da ontologia
Expande URIs via `expand_uri()` (extract.py:473-521)
4. **Expansão de URIs** (`extract.py:473-521`)
Verifica se o valor está no dicionário `ontology_subset.classes`
Se encontrado, extrai o URI da definição da classe
Se não encontrado, constrói o URI: `f"https://trustgraph.ai/ontology/{ontology_id}#{value}"`
### Exemplo de Fluxo de Dados
**JSON da Ontologia → Loader → Prompt:**
```
"fo/Recipe" → classes["fo/Recipe"] → LLM sees "**fo/Recipe**"
```
**LLM → Parser → Output:**
```
"Recipe" → not in classes["fo/Recipe"] → constructs URI → LOSES original URI
"fo/Recipe" → found in classes → uses original URI → PRESERVES URI
```
## Problemas Identificados
### 1. **Exemplos Inconsistentes no Prompt**
**Problema**: O modelo de prompt mostra IDs de classe com prefixos (`fo/Recipe`), mas a saída de exemplo usa nomes de classe sem prefixo (`Recipe`).
**Localização**: `ontology-prompt.md:5-52`
```markdown
## Ontology Classes:
- **fo/Recipe**: A Recipe is...
## Example Output:
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "Recipe"}
```
**Impacto**: O LLM recebe sinais conflitantes sobre qual formato usar.
### 2. **Perda de Informação na Expansão de URIs**
**Problema**: Quando o LLM retorna nomes de classe sem prefixo, seguindo o exemplo, `expand_uri()` não consegue encontrá-los no dicionário de ontologias e constrói URIs de fallback, perdendo os URIs originais corretos.
**Localização**: `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
```
**Impacto:**
URI original: `http://purl.org/ontology/fo/Recipe`
URI construído: `https://trustgraph.ai/ontology/food#Recipe`
Perda de significado semântico, quebra a interoperabilidade
### 3. **Formato Ambíguo de Instância de Entidade**
**Problema:** Não há orientação clara sobre o formato do URI da instância de entidade.
**Exemplos no prompt:**
`"recipe:cornish-pasty"` (prefixo semelhante a um namespace)
`"ingredient:flour"` (prefixo diferente)
**Comportamento real** (extract.py:517-520):
```python
# Treat as entity instance - construct unique URI
normalized = value.replace(" ", "-").lower()
return f"https://trustgraph.ai/{ontology_id}/{normalized}"
```
**Impacto**: O modelo de linguagem deve adivinhar a convenção de prefixo sem contexto ontológico.
### 4. **Sem Orientação de Prefixo de Namespace**
**Problema**: O JSON da ontologia contém definições de namespace (linha 10-25 em food.ontology):
```json
"namespaces": {
"fo": "http://purl.org/ontology/fo/",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
...
}
```
Mas estas informações nunca são transmitidas para o LLM. O LLM não sabe:
O que "fo" significa
Qual prefixo usar para entidades
Qual namespace se aplica a quais elementos
### 5. **Rótulos Não Utilizados no Prompt**
**Problema**: Cada classe tem campos `rdfs:label` (por exemplo, `{"value": "Recipe", "lang": "en-gb"}`), mas o modelo de prompt não os utiliza.
**Atual**: Mostra apenas `class_id` e `comment`
```jinja
- **{{class_id}}**{% if class_def.comment %}: {{class_def.comment}}{% endif %}
```
**Disponível, mas não utilizado**:
```python
"rdfs:label": [{"value": "Recipe", "lang": "en-gb"}]
```
**Impacto**: Poderia fornecer nomes legíveis para humanos, juntamente com IDs técnicos.
## Soluções Propostas
### Opção A: Normalizar para IDs sem Prefixo
**Abordagem**: Remover os prefixos dos IDs de classe antes de exibi-los para o LLM.
**Alterações**:
1. Modificar `build_extraction_variables()` para transformar chaves:
```python
classes_for_prompt = {
k.split('/')[-1]: v # "fo/Recipe" → "Recipe"
for k, v in ontology_subset.classes.items()
}
```
2. Atualizar o exemplo de prompt para corresponder (já usa nomes sem prefixo).
3. Modificar `expand_uri()` para lidar com ambos os formatos:
```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']
```
**Prós:**
Mais limpo, mais legível para humanos
Compatível com exemplos de prompts existentes
Modelos de linguagem grandes (LLMs) funcionam melhor com tokens mais simples
**Contras:**
Colisões de nomes de classe se múltiplas ontologias tiverem o mesmo nome de classe
Perde informações de namespace
Requer lógica de fallback para pesquisas
### Opção B: Usar IDs com Prefixo Completo Consistentemente
**Abordagem:** Atualizar exemplos para usar IDs com prefixo correspondentes ao que é mostrado na lista de classes.
**Mudanças:**
1. Atualizar exemplo de prompt (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. Adicionar explicação do namespace ao prompt:
```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. Mantenha `expand_uri()` como está (funciona corretamente quando as correspondências são encontradas).
**Prós:**
Consistência entre entrada e saída.
Sem perda de informação.
Preserva a semântica do namespace.
Funciona com múltiplas ontologias.
**Contras:**
Tokens mais verbosos para o LLM.
Requer que o LLM rastreie os prefixos.
### Opção C: Híbrida - Mostrar Tanto o Rótulo quanto o ID
**Abordagem:** Aprimorar o prompt para mostrar tanto os rótulos legíveis por humanos quanto os IDs técnicos.
**Alterações:**
1. Atualizar o modelo do prompt:
```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 %}
```
Exemplo de saída:
```markdown
- **fo/Recipe** (label: "Recipe"): A Recipe is a combination...
```
2. Instruções de atualização:
```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
```
**Prós**:
Mais claro para LLM
Preserva todas as informações
Explícito sobre o que usar
**Contras**:
Prompt mais longo
Modelo mais complexo
## Abordagem Implementada
**Formato Simplificado de Entidade-Relacionamento-Atributo** - substitui completamente o formato antigo baseado em triplas.
A nova abordagem foi escolhida porque:
1. **Nenhuma Perda de Informação**: URIs originais preservados corretamente
2. **Lógica Mais Simples**: Nenhuma transformação necessária, pesquisas diretas em dicionários funcionam
3. **Segurança de Namespace**: Lida com múltiplas ontologias sem colisões
4. **Correção Semântica**: Mantém a semântica RDF/OWL
## Implementação Completa
### O Que Foi Construído:
1. **Novo Modelo de Prompt** (`prompts/ontology-extract-v2.txt`)
✅ Seções claras: Tipos de Entidade, Relacionamentos, Atributos
✅ Exemplo usando identificadores de tipo completos (`fo/Recipe`, `fo/has_ingredient`)
✅ Instruções para usar identificadores exatos do esquema
✅ Novo formato JSON com arrays de entidades/relacionamentos/atributos
2. **Normalização de Entidade** (`entity_normalizer.py`)
`normalize_entity_name()` - Converte nomes para formato seguro para URI
`normalize_type_identifier()` - Lida com barras em tipos (`fo/Recipe``fo-recipe`)
`build_entity_uri()` - Cria URIs únicos usando a tupla (nome, tipo)
`EntityRegistry` - Rastreia entidades para desduplicação
3. **Analisador JSON** (`simplified_parser.py`)
✅ Analisa o novo formato: `{entities: [...], relationships: [...], attributes: [...]}`
✅ Suporta nomes de campos em kebab-case e snake_case
✅ Retorna dataclasses estruturadas
✅ Tratamento de erros elegante com registro
4. **Conversor de Triplas** (`triple_converter.py`)
`convert_entity()` - Gera automaticamente triplas de tipo + rótulo
`convert_relationship()` - Conecta URIs de entidade via propriedades
`convert_attribute()` - Adiciona valores literais
✅ Consulta URIs completos a partir de definições de ontologia
5. **Processador Principal Atualizado** (`extract.py`)
✅ Removeu o código antigo de extração baseado em triplas
✅ Adicionado método `extract_with_simplified_format()`
✅ Agora usa exclusivamente o novo formato simplificado
✅ Chama o prompt com o ID `extract-with-ontologies-v2`
## Casos de Teste
### Teste 1: Preservação de 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"
```
### Teste 2: Colisão Multi-Ontologia
```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"
```
### Teste 3: Formato de Instância de Entidade
```python
# Given prompt with food ontology
# LLM should create instances like
{"subject": "recipe:cornish-pasty"} # Namespace-style
{"subject": "food:beef"} # Consistent prefix
```
## Perguntas Abertas
1. **As instâncias de entidades devem usar prefixos de namespace?**
Atual: `"recipe:cornish-pasty"` (arbitrário)
Alternativa: Usar o prefixo da ontologia `"fo:cornish-pasty"`?
Alternativa: Sem prefixo, expandir no URI `"cornish-pasty"` → URI completo?
2. **Como lidar com o domínio/alcance no prompt?**
Atualmente mostra: `(Recipe → Food)`
Deveria ser: `(fo/Recipe → fo/Food)`?
3. **Devemos validar as restrições de domínio/alcance?**
TODO: comentário em extract.py:470
Detectaria mais erros, mas seria mais complexo
4. **O que dizer das propriedades inversas e equivalências?**
A ontologia tem `owl:inverseOf`, `owl:equivalentClass`
Não está atualmente sendo usado na extração
Deveria estar?
## Métricas de Sucesso
✅ Perda zero de informações de URI (100% de preservação dos URIs originais)
✅ O formato de saída do LLM corresponde ao formato de entrada
✅ Nenhum exemplo ambíguo no prompt
✅ Os testes passam com múltiplas ontologias
✅ Qualidade de extração aprimorada (medida pela porcentagem de triplas válidas)
## Abordagem Alternativa: Formato de Extração Simplificado
### Filosofia
Em vez de pedir ao LLM para entender a semântica RDF/OWL, peça para ele fazer o que ele faz de melhor: **encontrar entidades e relacionamentos no texto**.
Deixe que o código lide com a construção de URIs, a conversão RDF e as formalidades da web semântica.
### Exemplo: Classificação de Entidades
**Texto de Entrada:**
```
Cornish pasty is a traditional British pastry filled with meat and vegetables.
```
**Esquema de Ontologia (mostrado ao 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
```
**O que o LLM retorna (JSON simples):**
```json
{
"entities": [
{
"entity": "Cornish pasty",
"type": "Recipe"
}
]
}
```
**O que o Código Produz (Triplas 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)
)
]
```
### Benefícios
1. **O LLM não precisa de:**
Entender a sintaxe de URI
Inventar prefixos de identificadores (`recipe:`, `ingredient:`)
Saber sobre `rdf:type` ou `rdfs:label`
Construir identificadores da web semântica
2. **O LLM precisa apenas de:**
Encontrar entidades no texto
Mapeá-las para classes de ontologia
Extrair relacionamentos e atributos
3. **O código lida com:**
Normalização e construção de URI
Geração de triplas RDF
Atribuição automática de rótulos
Gerenciamento de namespaces
### Por que Isso Funciona Melhor
**Prompt mais simples** = menos confusão = menos erros
**IDs consistentes** = o código controla as regras de normalização
**Rótulos gerados automaticamente** = sem triplas rdfs:label ausentes
**O LLM se concentra na extração** = o que ele realmente faz bem
### Exemplo: Relacionamentos de Entidades
**Texto de entrada:**
```
Cornish pasty is a traditional British pastry filled with beef and potatoes.
```
**Esquema de Ontologia (mostrado ao 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)
```
**O que o LLM retorna (JSON simples):**
```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"
}
]
}
```
**O que o Código Produz (Triplas 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)
)
]
```
**Pontos-chave:**
O LLM retorna nomes de entidades em linguagem natural: `"Cornish pasty"`, `"beef"`, `"potatoes"`
O LLM inclui tipos para disambiguar: `subject-type`, `object-type`
O LLM usa o nome da relação do esquema: `"has_ingredient"`
O código deriva IDs consistentes usando (nome, tipo): `("Cornish pasty", "Recipe")``recipe-cornish-pasty`
O código pesquisa o URI da relação na ontologia: `fo/has_ingredient` → URI completo
A mesma tupla (nome, tipo) sempre obtém o mesmo URI (desduplicação)
### Exemplo: Desambiguação de Nomes de Entidades
**Problema:** O mesmo nome pode se referir a diferentes tipos de entidade.
**Caso real:**
```
"Cornish pasty" can be:
- A Recipe (instructions for making it)
- A Food (the dish itself)
```
**Como é tratado:**
O LLM retorna ambos como entidades separadas:
```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"
}
]
}
```
**Resolução de Código:**
```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
)
```
**Por que isso funciona:**
O tipo está incluído em TODAS as referências (entidades, relacionamentos, atributos).
O código usa a tupla `(name, type)` como chave de pesquisa.
Sem ambiguidades, sem colisões.
### Exemplo: Atributos de Entidade
**Texto de entrada:**
```
This Cornish pasty recipe serves 4-6 people and takes 45 minutes to prepare.
```
**Esquema de Ontologia (mostrado ao 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)
```
**O que o LLM retorna (JSON simples):**
```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"
}
]
}
```
**O que o Código Produz (Triplas 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!
)
]
```
**Pontos-chave:**
O LLM extrai valores literais: `"4-6 people"`, `"45 minutes"`
O LLM inclui o tipo de entidade para desambiguação: `entity-type`
O LLM usa o nome do atributo do esquema: `"serves"`, `"preparation_time"`
O código pesquisa o URI do atributo a partir das propriedades do tipo de dados da ontologia
**O objeto é literal** (`is_uri=False`), não uma referência de URI
Os valores permanecem como texto natural, sem necessidade de normalização
**Diferença em relação a Relacionamentos:**
Relacionamentos: tanto o sujeito quanto o objeto são entidades (URIs)
Atributos: o sujeito é uma entidade (URI), o objeto é um valor literal (string/número)
### Exemplo Completo: Entidades + Relacionamentos + Atributos
**Texto de Entrada:**
```
Cornish pasty is a savory pastry filled with beef and potatoes.
This recipe serves 4 people.
```
**O que o LLM retorna:**
```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"
}
]
}
```
**Resultado:** 11 triplas RDF geradas:
3 triplas de tipo de entidade (rdf:type)
3 triplas de rótulo de entidade (rdfs:label) - automático
2 triplas de relacionamento (has_ingredient)
1 tripla de atributo (serves)
Tudo isso a partir de extrações simples e em linguagem natural pelo LLM!
## Referências
Implementação atual: `trustgraph-flow/trustgraph/extract/kg/ontology/extract.py`
Modelo de prompt: `ontology-prompt.md`
Casos de teste: `tests/unit/test_extract/test_ontology/`
Ontologia de exemplo: `e2e/test-data/food.ontology`