Graph RAG per Agents

Dec 20, 2025 · 13 min read

Una guida pratica all’utilizzo di Graph RAG con sistemi agentici, mostrando come i knowledge graph superano le limitazioni della semplice ricerca semantica quando si lavora con dati strutturati e non strutturati.

Introduzione

Graph RAG rappresenta un’evoluzione fondamentale nell’architettura dei sistemi agentici. Invece di affidarsi esclusivamente alla ricerca vettoriale su documenti non strutturati, Graph RAG integra un knowledge graph che permette agli agent di eseguire query complesse, aggregazioni e analisi relazionali.

Questa presentazione esplora l’implementazione pratica di Graph RAG attraverso un caso d’uso reale: un assistente per l’analisi delle competenze che deve:

  • Identificare skill e gap nel team
  • Trovare similarità tra professionisti
  • Analizzare collaborazioni su progetti
  • Aggregare dati su competenze tecniche

L’esempio utilizza Neo4j come graph database e Google ADK (Agent Development Kit) per costruire l’agent, ma i principi si applicano a qualsiasi stack tecnologico.

Architettura Generale

L’architettura di Graph RAG si posiziona strategicamente tra gli agent e le fonti dati:

┌─────────┐     ┌──────────────────┐     ┌─────────────┐
│ Agents  │────▶│ Knowledge Graph  │◀────│   Tools     │
└─────────┘     └──────────────────┘     └─────────────┘
                ┌───────┴────────┐
                │                │
         ┌──────────┐    ┌──────────────┐
         │   ETL    │    │   Document   │
         │Structured│    │  Extraction  │
         │   Data   │    │ Unstructured │
         └──────────┘    └──────────────┘

Componenti Chiave

  1. Knowledge Graph Centrale: Repository unificato per dati strutturati ed estratti
  2. Document Extraction: Pipeline per estrarre entità da documenti non strutturati
  3. ETL Strutturato: Processi standard per ingestione di database relazionali, API, sistemi HR
  4. Agent Tools: Interfacce che permettono agli agent di interrogare il grafo
Note

Un anno fa, l’approccio standard per coding agent prevedeva indicizzazione dell’intera codebase, creazione di embedding e sistemi RAG sofisticati. Graph RAG adotta una filosofia diversa: nessuna indicizzazione preventiva, ma esplorazione iterativa come farebbe uno sviluppatore che entra in un nuovo team.

Perché un Knowledge Graph

Vantaggi per Workflow Agentici

Con l’evoluzione dei sistemi di reasoning, gli agent non eseguono più semplici query di retrieval. Oggi decomponendo domande complesse in multiple query parallele, richiedendo:

  • Aggregazioni: COUNT, SUM, AVG su entità correlate
  • Traversal multi-hop: Relazioni indirette tra entità
  • Spiegabilità: Tracciabilità del percorso di reasoning
  • Flessibilità dello schema: Adattamento rapido a nuovi tipi di dati

Data Model Espressivo

Il vantaggio principale è la capacità di esprimere un modello dati semplice e comprensibile all’agent:

// Schema base
(Person)-[:KNOWS]->(Skill)
(Person)-[:ACCOMPLISHED]->(Thing)
(Thing)-[:IN_DOMAIN]->(Domain)
(Thing)-[:TYPE_OF]->(WorkType)

Questo schema permette all’agent di:

  1. Comprendere rapidamente le relazioni disponibili
  2. Decomporre domande in query strutturate
  3. Espandere il modello aggiungendo nuovi nodi/relazioni senza refactoring

Il Problema della Ricerca Semantica Pura

Demo: Knowledge Assistant con Solo Documenti

Iniziamo con un approccio tradizionale: cartella di résumé come documenti embeddate.

# Setup iniziale: documenti con embedding
for resume in resume_folder:
    create_document_node(
        text=resume.content,
        embedding=embed(resume.content),
        metadata={...}
    )

L’agent ha un solo tool: search_documents(query, k=5)

Query 1: Aggregazioni Fallite

User: "Quanti sviluppatori Python ho nel team?"

Agent Response: "Hai 5 sviluppatori Python"

Problema: La risposta è semplicemente k=5 - il numero di documenti recuperati. L’agent non può contare le occorrenze effettive perché non ha accesso a entità strutturate.

Query 2: Similarità Superficiale

User: "Chi è più simile a Lucas Martinez in termini di skill?"

Agent Logic:
1. search_documents("Lucas Martinez")
2. search_documents("full stack AI engineer Python JavaScript ML")
3. Restituisci i documenti con similarity score più alto

Problema: La ricerca semantica trova testo simile, ma non può calcolare overlap esatto di competenze o fare confronti precisi.

Query 3: Aggregazioni Impossibili

User: "Riassumi la distribuzione delle competenze tecniche nel team"

Agent Response: "Non posso fornire statistiche aggregate.
Posso solo cercare termini nei résumé."

Problema: Senza struttura, l’agent non può eseguire GROUP BY, COUNT, o altre operazioni analitiche.

Tip

La ricerca semantica eccelle nel trovare contenuto rilevante, ma fallisce quando servono conteggi esatti, relazioni esplicite o aggregazioni. Graph RAG risolve questo problema combinando ricerca con struttura.

Entity Extraction e Data Modeling

Definire lo Schema

Il primo passo è modellare i dati per il nostro use case. Per un assistente HR:

from pydantic import BaseModel, Field
from enum import Enum

class AccomplishmentType(str, Enum):
    PUBLISHED = "published"
    BUILT = "built"
    LED = "led"
    MANAGED = "managed"
    OPTIMIZED = "optimized"
    SHIPPED = "shipped"

class Domain(str, Enum):
    AI_ML = "AI/ML"
    DATA_ENGINEERING = "data_engineering"
    BACKEND = "backend"
    FRONTEND = "frontend"
    DEVOPS = "devops"
    ANALYTICS = "analytics"

class WorkType(str, Enum):
    SYSTEM = "system"
    FEATURE = "feature"
    TOOL = "tool"
    INFRASTRUCTURE = "infrastructure"

class Thing(BaseModel):
    """Cosa ha realizzato una persona"""
    name: str = Field(description="Nome del progetto/deliverable")
    type: AccomplishmentType
    domain: Domain
    work_type: WorkType

class Accomplishment(BaseModel):
    """Relazione tra persona e cosa realizzata"""
    person_id: str
    thing: Thing
    description: str = Field(description="Descrizione dettagliata")

class Person(BaseModel):
    """Professionista con skills e accomplishments"""
    id: str
    name: str
    skills: list[str] = Field(description="Lista di competenze tecniche")
    accomplishments: list[Accomplishment]

Pipeline di Estrazione

Utilizzando LangChain o framework simili per decomporre documenti:

from langchain.chains import create_extraction_chain

# Chain per estrarre entità strutturate
extraction_chain = create_extraction_chain(
    llm=llm,
    schema=Person
)

# Per ogni résumé
for resume_path in resumes:
    resume_text = read_file(resume_path)

    # Estrazione entità
    extracted = extraction_chain.run(resume_text)

    # Output JSON strutturato
    person_data = {
        "id": generate_id(extracted.name),
        "name": extracted.name,
        "skills": extracted.skills,  # ["Python", "React", "Docker", ...]
        "accomplishments": [
            {
                "thing": "Microservices Platform",
                "type": "built",
                "domain": "backend",
                "work_type": "system"
            },
            # ...
        ]
    }

Caricamento nel Graph

// Creare nodo Person
CREATE (p:Person {
  id: $person_id,
  name: $name
})

// Collegare Skills
UNWIND $skills AS skill
MERGE (s:Skill {name: skill})
CREATE (p)-[:KNOWS]->(s)

// Collegare Accomplishments
UNWIND $accomplishments AS acc
MERGE (t:Thing {name: acc.thing.name})
SET t.type = acc.type,
    t.domain = acc.domain,
    t.work_type = acc.work_type

CREATE (p)-[:ACCOMPLISHED {
  description: acc.description
}]->(t)

// Collegare a domini di alto livello
MERGE (d:Domain {name: acc.domain})
CREATE (t)-[:IN_DOMAIN]->(d)

Grafo Risultante

Il modello dati ora appare così:

      ┌─────────┐
      │ Person  │
      └────┬────┘
    ┌──────┴──────┐
    │             │
[:KNOWS]    [:ACCOMPLISHED]
    │             │
    ▼             ▼
┌────────┐    ┌───────┐
│ Skill  │    │ Thing │
└────────┘    └───┬───┘
           [:IN_DOMAIN]
             ┌─────────┐
             │ Domain  │
             └─────────┘
Note

Questo schema è espandibile senza refactoring. Se domani aggiungiamo (Person)-[:COLLABORATES_WITH]->(Person), non dobbiamo ricostruire le relazioni esistenti - caratteristica unica dei graph database.

Query Avanzate con Cypher

Agent con MCP Server Neo4j

Ora equipaggiamo l’agent con strumenti per interrogare il grafo:

from google.adk import Agent

agent = Agent(
    instructions="""
    Sei un assistente HR specializzato in analisi delle competenze.
    Hai accesso a un knowledge graph con questo schema:
    - Person: professionisti
    - Skill: competenze tecniche
    - Thing: progetti/accomplishments
    - Domain: aree tecnologiche

    Usa Cypher per query precise e spiega il tuo reasoning.
    """,
    tools=[
        mcp_neo4j_server.read_schema(),
        mcp_neo4j_server.run_cypher()
    ]
)

Query 1: Conteggio Accurato

User: "Quanti sviluppatori Python ho?"

Agent Cypher:
MATCH (p:Person)-[:KNOWS]->(s:Skill {name: 'Python'})
RETURN COUNT(DISTINCT p) AS python_developers

Result: 28 sviluppatori Python

Differenza chiave: Conteggio esatto basato su relazioni esplicite, non approssimazioni da vector search.

Query 2: Similarità con Spiegabilità

User: "Chi è più simile a Lucas Martinez e perché?"

Agent Reasoning:
1. Trova skill di Lucas
2. Calcola overlap con altri professionisti
3. Ordina per similarità
// Step 1: ID di Lucas
MATCH (lucas:Person {name: 'Lucas Martinez'})

// Step 2: Sue skill
MATCH (lucas)-[:KNOWS]->(lucas_skill:Skill)
WITH lucas, COLLECT(lucas_skill.name) AS lucas_skills

// Step 3: Altri con skill in comune
MATCH (other:Person)-[:KNOWS]->(skill:Skill)
WHERE other <> lucas
  AND skill.name IN lucas_skills

// Step 4: Calcola overlap
WITH other, lucas_skills,
     COLLECT(DISTINCT skill.name) AS common_skills
WITH other,
     SIZE(common_skills) AS overlap,
     SIZE(lucas_skills) AS lucas_total,
     common_skills
ORDER BY overlap DESC
LIMIT 1

RETURN other.name AS most_similar,
       overlap,
       lucas_total,
       common_skills,
       ROUND(100.0 * overlap / lucas_total, 2) AS similarity_pct
Result:
most_similar: "Sarah Chen"
overlap: 12
lucas_total: 15
common_skills: ["Python", "JavaScript", "React", "Docker",
                "AWS", "PostgreSQL", "Redis", "Git",
                "TensorFlow", "FastAPI", "TypeScript", "Node.js"]
similarity_pct: 80.0

Vantaggio: L’agent può spiegare esattamente perché Sarah è la più simile, con percentuali precise e lista di skill condivise.

Query 3: Aggregazioni Complesse

User: "Riassumi la distribuzione delle competenze tecniche"

Agent Cypher:
MATCH (p:Person)-[:KNOWS]->(s:Skill)
WITH s.name AS skill, COUNT(DISTINCT p) AS num_people
ORDER BY num_people DESC
RETURN skill, num_people
LIMIT 20
Results:
skill          | num_people
---------------|------------
Python         | 28
JavaScript     | 24
Git            | 22
Docker         | 20
AWS            | 18
React          | 15
PostgreSQL     | 14
...

L’agent può anche fare breakdown per domain:

MATCH (p:Person)-[:ACCOMPLISHED]->(t:Thing)-[:IN_DOMAIN]->(d:Domain)
WITH d.name AS domain, COUNT(DISTINCT p) AS contributors
RETURN domain, contributors
ORDER BY contributors DESC
domain              | contributors
--------------------|-------------
AI/ML               | 18
Backend             | 25
Data Engineering    | 12
Frontend            | 15
DevOps              | 10
Tip

Con query Cypher, l’agent non si limita a “trovare documenti rilevanti”. Può calcolare statistiche esatte, fare ranking precisi e spiegare ogni numero con riferimenti al grafo sottostante.

Query Avanzate: Traversal Multi-Hop

Tool Personalizzati per Similarità

Oltre a Cypher generato on-the-fly, possiamo dare all’agent tool pre-costruiti per operazioni comuni:

def find_similar_people(person_id: str, max_hops: int = 3) -> list[dict]:
    """
    Trova persone simili attraversando:
    - Skill condivise
    - Domini comuni
    - Tipi di progetti simili

    Args:
        person_id: ID della persona di riferimento
        max_hops: Profondità massima di traversal (0-3)
    """
    query = f"""
    MATCH (p1:Person {{id: $person_id}})
    MATCH (p2:Person)
    WHERE p1 <> p2

    // Traversal flessibile: 0-{max_hops} hops
    MATCH path = (p1)-[*0..{max_hops}]-(p2)

    // Calcola score basato su:
    // - Skill overlap
    // - Domain overlap
    // - Accomplishment type overlap
    WITH p2,
         COUNT(DISTINCT [r in relationships(path)
                         WHERE type(r) = 'KNOWS']) AS skill_overlap,
         COUNT(DISTINCT [r in relationships(path)
                         WHERE type(r) IN ['IN_DOMAIN', 'TYPE_OF']]) AS domain_overlap
    WHERE skill_overlap > 0 OR domain_overlap > 0

    WITH p2,
         skill_overlap * 3 + domain_overlap AS similarity_score
    ORDER BY similarity_score DESC
    LIMIT 10

    // Ritorna dettagli
    MATCH (p2)-[:KNOWS]->(s:Skill)
    MATCH (p2)-[:ACCOMPLISHED]->(t:Thing)-[:IN_DOMAIN]->(d:Domain)

    RETURN p2.name AS name,
           similarity_score,
           COLLECT(DISTINCT s.name) AS skills,
           COLLECT(DISTINCT d.name) AS domains,
           COLLECT(DISTINCT t.type) AS accomplishment_types
    """

    return execute_cypher(query, {"person_id": person_id})

Vantaggio del traversal: Se aggiungiamo nuove relazioni al grafo (es. CERTIFIED_IN, MENTORED, COLLABORATED_WITH), il traversal le include automaticamente senza modificare la query.

Esempio Conversazione

User: "Chi è più simile a Lucas Martinez e perché?"

Agent:
Sto usando il tool find_similar_people con max_hops=3 per
trovare colleghi con skill e background simili a Lucas...

[Esegue query]

Result:
La persona più simile a Lucas Martinez è **Sarah Chen** con
similarity score di 42:

Skills condivise (12):
- Python, JavaScript, React, Docker, AWS, PostgreSQL, Redis,
  Git, TensorFlow, FastAPI, TypeScript, Node.js

Domini comuni (3):
- AI/ML, Backend, Frontend

Tipi di accomplishment simili (4):
- built, shipped, optimized, led

Sarah ha lavorato principalmente su sistemi AI/ML e backend,
proprio come Lucas. Entrambi hanno esperienza nel delivery
di prodotti completi (full-stack) con focus su ML.

Spiegabilità: L’agent può decostruire il similarity score mostrando esattamente quali fattori hanno contribuito.

Espandere il Data Model

Il Problema delle Join Table

Nell’esempio iniziale, quando estraiamo résumé, assumiamo:

Person --(1:N)--> Accomplishment

Ogni accomplishment appartiene a una sola persona perché è listato sul loro résumé individuale.

Ma cosa succede quando integriamo dati da un sistema HR interno che mostra collaborazioni?

Dati Strutturati: HR System

{
  "projects": [
    {
      "id": "proj_001",
      "name": "AI-Powered Recommendation Engine",
      "domain": "AI/ML",
      "contributors": [
        "lucas_martinez",
        "sarah_chen",
        "amanda_rodriguez"
      ],
      "deliverable_type": "system"
    },
    {
      "id": "proj_002",
      "name": "Supply Chain Optimization Tool",
      "domain": "analytics",
      "contributors": [
        "sarah_chen",
        "amanda_rodriguez"
      ],
      "deliverable_type": "tool"
    }
  ]
}

Refactoring in Database Relazionali

In un RDBMS, questo richiederebbe:

-- Tabella originale
CREATE TABLE accomplishments (
    id INT PRIMARY KEY,
    person_id INT,  -- FK a people
    thing_name VARCHAR,
    ...
);

-- Nuovo schema: join table necessaria!
CREATE TABLE people (id INT PRIMARY KEY, name VARCHAR);
CREATE TABLE things (id INT PRIMARY KEY, name VARCHAR, type VARCHAR);

CREATE TABLE person_thing (  -- JOIN TABLE
    person_id INT REFERENCES people(id),
    thing_id INT REFERENCES things(id),
    PRIMARY KEY (person_id, thing_id)
);

-- Migrazione dati
-- 1. Estrarre things unici da accomplishments
-- 2. Popolare person_thing
-- 3. Droppare vecchia tabella

Problema: Refactoring strutturale completo. Tutte le query esistenti vanno riscritte.

Soluzione Graph: Zero Refactoring

In Neo4j, aggiungiamo semplicemente nuove relazioni:

// Carica progetti dal sistema HR
UNWIND $projects AS proj

// Merge thing (se esiste già da résumé extraction, riutilizza)
MERGE (t:Thing {name: proj.name})
ON CREATE SET
    t.id = proj.id,
    t.type = proj.deliverable_type

// Collega domain
MERGE (d:Domain {name: proj.domain})
MERGE (t)-[:IN_DOMAIN]->(d)

// Per ogni contributor
UNWIND proj.contributors AS contributor_id
MATCH (p:Person {id: contributor_id})

// Crea relazione COLLABORATED_ON
MERGE (p)-[:COLLABORATED_ON]->(t)

Risultato:

  • Nessuna modifica alle relazioni esistenti [:ACCOMPLISHED]
  • Le nuove relazioni [:COLLABORATED_ON] convivono pacificamente
  • Query vecchie continuano a funzionare
  • Nuove query possono sfruttare le collaborazioni

Grafo Esteso

     ┌─────────┐
     │ Person  │
     └────┬────┘
    ┌─────┴──────────┐
    │                │
[:KNOWS]      [:ACCOMPLISHED]
    │          [:COLLABORATED_ON]  ← NUOVO
    ▼                │
┌────────┐          ▼
│ Skill  │      ┌───────┐
└────────┘      │ Thing │
                └───┬───┘
             [:IN_DOMAIN]
               ┌─────────┐
               │ Domain  │
               └─────────┘

Nuove Query Possibili

User: "Quali persone hanno collaborato di più su progetti AI?"

Agent Cypher:
MATCH (p1:Person)-[:COLLABORATED_ON]->(t:Thing)-[:IN_DOMAIN]->(d:Domain {name: 'AI/ML'})
MATCH (p2:Person)-[:COLLABORATED_ON]->(t)
WHERE p1.id < p2.id  // Evita duplicati

WITH p1, p2, COUNT(DISTINCT t) AS num_projects
ORDER BY num_projects DESC
LIMIT 5

RETURN p1.name AS person1,
       p2.name AS person2,
       num_projects,
       COLLECT(t.name) AS shared_projects
person1          | person2            | num_projects | shared_projects
-----------------|--------------------|--------------|-----------------
Sarah Chen       | Amanda Rodriguez   | 4            | [AI Rec Engine, Supply Chain, ...]
Lucas Martinez   | Sarah Chen         | 3            | [AI Rec Engine, ML Pipeline, ...]
Note

La flessibilità dei graph database è cruciale in contesti agentici dove i requisiti cambiano rapidamente. Passare da one-to-many a many-to-many, aggiungere nuovi tipi di relazioni o entità richiede zero refactoring dello schema esistente.

Tool Composizione per Analisi Avanzate

Tool: Find Collaborators

def find_collaborators(
    person_id: str,
    domain: str | None = None,
    min_projects: int = 1
) -> list[dict]:
    """
    Trova con chi ha collaborato una persona.

    Args:
        person_id: ID della persona
        domain: Filtra per dominio specifico (opzionale)
        min_projects: Minimo numero di progetti condivisi
    """
    query = """
    MATCH (p1:Person {id: $person_id})-[:COLLABORATED_ON]->(t:Thing)
    """

    if domain:
        query += """
        MATCH (t)-[:IN_DOMAIN]->(d:Domain {name: $domain})
        """

    query += """
    MATCH (p2:Person)-[:COLLABORATED_ON]->(t)
    WHERE p1 <> p2

    WITH p2, COLLECT(DISTINCT t.name) AS projects
    WHERE SIZE(projects) >= $min_projects

    RETURN p2.name AS collaborator,
           SIZE(projects) AS num_projects,
           projects
    ORDER BY num_projects DESC
    """

    return execute_cypher(query, {
        "person_id": person_id,
        "domain": domain,
        "min_projects": min_projects
    })

Agent con Tool Multipli

agent = Agent(
    instructions="""
    Sei un assistente HR con questi tool:
    - find_similar_people: trova persone con skill simili
    - find_collaborators: trova collaboratori su progetti
    - run_cypher: query personalizzate per analisi complesse
    - read_schema: esplora lo schema del knowledge graph

    Scomponi domande complesse in query incrementali.
    """,
    tools=[
        find_similar_people,
        find_collaborators,
        mcp_neo4j.run_cypher,
        mcp_neo4j.read_schema
    ]
)

Esempio: Query Composita

User: "Trova persone che hanno collaborato insieme per delivery
      di almeno 3 progetti AI/ML"

Agent Reasoning:
1. Cerco tutte le persone che hanno lavorato su progetti AI/ML
2. Per ogni coppia, conto i progetti condivisi
3. Filtro per >= 3 progetti
4. Ritorno le coppie più collaborative

[Esegue find_collaborators con domain='AI/ML', min_projects=3]

Results:
- **Sarah Chen & Amanda Rodriguez**: 4 progetti AI/ML
  - AI-Powered Recommendation Engine
  - ML Model Deployment Pipeline
  - Real-time Fraud Detection
  - Customer Segmentation System

- **Lucas Martinez & Sarah Chen**: 3 progetti AI/ML
  - AI-Powered Recommendation Engine
  - ML Model Deployment Pipeline
  - Automated Data Labeling Tool

Vantaggio: Tool specifici sono più performanti di Cypher generato on-the-fly e forniscono interfacce più prevedibili per l’agent.

Conclusioni

Sintesi dei Vantaggi

Graph RAG offre benefici decisivi per sistemi agentici rispetto a vector search puro:

  1. Aggregazioni Precise: COUNT, SUM, AVG su entità strutturate
  2. Relazioni Esplicite: Traversal multi-hop per trovare connessioni indirette
  3. Spiegabilità: Ogni risposta è tracciabile al percorso nel grafo
  4. Flessibilità dello Schema: Espansione senza refactoring (one-to-many → many-to-many)
  5. Query Composte: Decomposizione di domande in sub-query parallele
  6. Performance: Ottimizzazione per traversal su grafi densi

Quando Usare Graph RAG

Scenari ideali:

  • Dati con relazioni complesse (org charts, supply chain, collaboration networks)
  • Necessità di aggregazioni e analytics oltre al retrieval
  • Multi-source: combinare dati strutturati (DB, API) e non strutturati (documenti)
  • Schema evolutivo: requisiti che cambiano rapidamente

Quando vector search è sufficiente:

  • Solo retrieval semantico senza aggregazioni
  • Dati completamente non strutturati senza entità chiare
  • Nessuna necessità di relazioni esplicite

Pattern Architetturale

L’architettura ideale combina entrambi gli approcci:

Agent
  ├─ Vector Search: retrieval semantico su documenti lunghi
  └─ Graph RAG:
       ├─ Aggregazioni e analytics
       ├─ Traversal relazionale
       └─ Structured queries
Tip

Non pensare a Graph RAG come sostituto di vector search, ma come complemento. Vector search trova contenuto rilevante; Graph RAG struttura, conta, aggrega e traccia relazioni su quel contenuto.

Prossimi Passi

Per implementare Graph RAG nel tuo sistema:

  1. Identifica entità core nel tuo dominio (Person, Project, Skill, …)
  2. Modella relazioni chiave usando Pydantic o schema simili
  3. Costruisci extraction pipeline per documenti non strutturati
  4. Setup ETL per dati strutturati esistenti
  5. Crea tool specifici per query comuni (find_similar, find_collaborators)
  6. Integra con agent framework (ADK, LangGraph, custom)

Risorse:


Graph RAG rappresenta l’evoluzione naturale dei sistemi agentici verso maggiore precisione, spiegabilità e capacità analitiche. La combinazione di ricerca semantica per il retrieval e grafi per struttura relazionale apre nuove possibilità per applicazioni enterprise che richiedono sia flessibilità che rigore.

Questa è una sintesi del video di YouTube presente in testa alla pagina. Spero sia stato tradotto in modo accettabile. Tutto il merito e la gloria agli autori originali.