Estrarre dati strutturati utilizzando le chiamate di funzione

Visualizza sull'IA di Google Prova un blocco note di Colab Visualizza blocco note su GitHub

In questo tutorial imparerai a conoscere un esempio di estrazione di dati strutturati, utilizzando l'API Gemini per estrarre elenchi di caratteri, relazioni, cose e luoghi da una storia.

Configurazione

pip install -U -q google-generativeai
import pathlib
import textwrap

import google.generativeai as genai


from IPython.display import display
from IPython.display import Markdown

from google.api_core import retry

def to_markdown(text):
  text = text.replace('•', '  *')
  return Markdown(textwrap.indent(text, '> ', predicate=lambda _: True))

Una volta ottenuta la chiave API, passala all'SDK. A tale scopo, puoi procedere in uno dei due seguenti modi:

  • Inserisci la chiave nella variabile di ambiente GOOGLE_API_KEY (l'SDK la acquisirà automaticamente da lì).
  • Passa la chiave a genai.configure(api_key=...)
genai.configure(api_key=GOOGLE_API_KEY)

Attività di esempio

Per questo tutorial, estrarrai entità da storie in linguaggio naturale. Come ad esempio, di seguito è riportata una storia scritta da Gemini.

new_story = False

if new_story:
  model = genai.GenerativeModel(model_name='models/gemini-1.5-pro-latest')

  response = model.generate_content("""
      Write a long story about a girl with magic backpack, her family, and at
      least one other charater. Make sure everyone has names. Don't forget to
      describe the contents of the backpack, and where everyone and everything
      starts and ends up.""", request_options={'retry': retry.Retry()})
  story = response.text
  print(response.candidates[0].citation_metadata)
else:
  story = """In the quaint town of Willow Creek, nestled amidst rolling hills and whispering willows, resided a young girl named Anya. As she stepped out of the creaky wooden door of her modest cottage, her heart skipped a beat with excitement and anticipation. Today was her first day of school, and she couldn't wait to show off her prized possession - a magical backpack.\n\nHanded down to her from her grandmother, the backpack was no ordinary satchel. Its soft, emerald-green fabric shimmered with an ethereal glow, and its leather straps held secrets that only Anya knew. Within its capacious interior lay an enchanted world, filled with wonders that would ignite her imagination and change her life forever.\n\nAnya's parents, kind-hearted Elise and wise-bearded Edward, bid her farewell with warm embraces. "Remember, my dear," whispered her mother, "use your magic wisely and for good." Her father added, "Always seek knowledge, and let the backpack be your trusted companion."\n\nWith a skip in her step, Anya set off towards the town's only schoolhouse. On her way, she passed her best friend, Samuel, a curious and adventurous boy with a mischievous grin. "Hey, Anya," he called out. "Can I see your backpack?"\n\nAnya hesitated for a moment before unzipping the flap and revealing its contents. Samuel's eyes widened in amazement as he peered inside. There, nestled amidst pencils and notebooks, were a shimmering sword, a book of ancient spells, a tiny compass that always pointed north, and a magical key that could open any lock.\n\nTogether, they marveled at the backpack's wonders, promising to keep its secrets safe. As they approached the schoolhouse, Anya noticed a group of older children huddled together, their faces etched with fear. Curiosity getting the better of her, she cautiously approached.\n\n"What's wrong?" she asked.\n\nA tall, lanky boy stepped forward. "There's a monster in the forest," he stammered. "It's been terrorizing the town, attacking animals and even people."\n\nAnya's heart sank. The town of Willow Creek was small and peaceful, and the thought of a monster brought a shiver down her spine. She knew she had to do something to protect her family and friends.\n\nWithout a moment's hesitation, Anya opened her backpack and retrieved the shimmering sword. With a determined gleam in her eye, she turned to her terrified peers. "Don't worry," she said, her voice steady. "I'll take care of it."\n\nWith Samuel close behind her, Anya ventured into the shadowy depths of the forest. The trees seemed to whisper secrets as she passed, and the undergrowth rustled with unseen creatures. As they walked deeper into the forest, the air grew heavy and the ground beneath their feet trembled.\n\nSuddenly, they came to a clearing, and there before their eyes was the monster - a massive beast with sharp teeth, glowing red eyes, and claws that could crush a human with ease. The creature roared, a thunderous sound that shook the forest to its core.\n\nFear surged through Anya, but she refused to let it consume her. She drew the sword from its sheath and charged towards the monster. The blade shimmered in the sunlight, and as it struck the beast's hide, a blinding light erupted, enveloping everything in its radiance.\n\nWhen the light faded, the monster was gone, and in its place was a pile of shattered crystals. Anya had defeated the creature with the magic of her backpack, proving that even the smallest of objects could hold the greatest of powers.\n\nAs she and Samuel returned to the town, they were greeted as heroes. The people of Willow Creek rejoiced, and the legend of Anya, the girl with the magic backpack, was passed down through generations. And so, Anya continued her adventures, using the backpack's wonders to make the world a better place, one magical step at a time."""
to_markdown(story)

Nella pittoresca cittadina di Willow Creek, incastonata tra dolci colline e sussurri salici, viveva una ragazzina di nome Anya. Mentre usciva dalla porta di legno scricchiolante del suo modesto cottage, il suo cuore saltò per l'entusiasmo e l'attesa. Oggi era il suo primo giorno di scuola e non vedeva l'ora di mettere in mostra il suo prezioso possesso: uno zaino magico.

Regalato alla nonna, lo zaino non era una cartella normale. Il morbido tessuto verde smeraldo brillava di un bagliore etereo, mentre i cinturini di pelle contenevano segreti che solo Anya conosceva. All'interno del suo capiente interno c'era un mondo incantato, pieno di meraviglie che avrebbero acceso la sua immaginazione e cambiato la sua vita per sempre.

I genitori di Anya, la gentile Elise e la saggia barba Edoardo, la salutano con affettuosi abbracci. "Ricorda, tesoro", sussurrò la madre: "Usa la tua magia saggiamente e per sempre". Suo padre aggiunse: "Cerca sempre la conoscenza e lascia che lo zaino sia il tuo compagno fidato".

Con un salto nel passo, Anya è partita verso l'unica scuola della città. Lungo la strada, ha incontrato il suo migliore amico, Samuel, un ragazzo curioso e avventuroso con un sorriso dispettoso. "Ciao, Anya" ha dichiarato. "Posso vedere il tuo zaino?"

Anya esitò un momento prima di aprire il legaccio e rivelarne il contenuto. Gli occhi di Samuel si spalancò per lo stupore mentre scrutava all'interno. Lì, incastonata tra matite e taccuini, c'erano una spada scintillante, un libro di incantesimi antichi, una piccola bussola che puntava sempre a nord e una chiave magica che poteva aprire qualsiasi serratura.

Insieme, hanno ammirato le meraviglie dello zaino, promettendo di proteggerne i segreti. Mentre si avvicinavano alla scuola, Anya ha notato un gruppo di bambini più grandi radunati con i loro volti incisi dalla paura. Per curiosità, si avvicinò con cautela.

"Qual è il problema?" ha chiesto.

Un ragazzo alto e smilzo si è fatto avanti. "C'è un mostro nella foresta", balbò. "Sta terrorizzando la città, attaccando animali e persino persone."

Il cuore di Anya affondò. La città di Willow Creek era piccola e tranquilla e l'idea di un mostro le faceva venire un brivido alla schiena. Sapeva di dover fare qualcosa per proteggere la sua famiglia e i suoi amici.

Senza un attimo di esitazione, Anya apre lo zaino e recupera la spada luccicante. Con un bagliore determinato negli occhi, si rivolse ai suoi coetanei terrorizzati. "Non preoccuparti", disse, con la voce ferma. "Me ne occupo io."

Con Samuel alle spalle, Anya si è avventurata nelle profondità ombre della foresta. Al suo passaggio, gli alberi sembravano sussurrare segreti e il sottobosco frusciava di creature invisibili. Quando si addentravano nella foresta, l'aria si fece pesante e il terreno sotto i loro piedi tremava.

All'improvviso arrivarono a una radura e lì davanti ai loro occhi c'era il mostro: un'enorme bestia con denti affilati, occhi rossi luminosi e artigli che potevano schiacciare un essere umano con facilità. La creatura ruggise, un suono fragoroso che scosse la foresta fino in fondo.

La paura attraversava Anya, ma si rifiutò di lasciarla consumata. Tratte la spada dal fodero e si avviò verso il mostro. La lama brillava alla luce del sole e, non appena colpiva la pelle dell'animale, si schiantò una luce accecante, avvolgendo tutto nel suo splendore.

Quando la luce sbiadì, il mostro non c'era più e al suo posto c'era un cumulo di cristalli in frantumi. Anya aveva sconfitto la creatura con la magia del suo zaino, dimostrando che anche gli oggetti più piccoli potevano contenere i poteri più grandi.

Al ritorno in città, lei e Samuel sono stati accolti come eroi. Gli abitanti di Willow Creek gioirono e la leggenda di Anya, la ragazza con lo zaino magico, fu tramandata di generazione in generazione. E così, Anya ha continuato le sue avventure, usando le meraviglie dello zaino per rendere il mondo un posto migliore, un passo magico alla volta.

Utilizzo di Natural Language

I modelli linguistici di grandi dimensioni (LLM) sono strumenti avanzati per il multitasking. Spesso basta chiedere a Gemini quello che vuoi e va bene.

L'API Gemini non ha una modalità JSON, quindi ci sono alcuni aspetti da tenere presenti quando si generano strutture di dati in questo modo:

  • A volte l'analisi non riesce.
  • Lo schema non può essere applicato in modo rigoroso.

Questo problema verrà risolto nella prossima sezione. Innanzitutto, prova un semplice prompt in linguaggio naturale con lo schema scritto come testo. Questa impostazione non è stata ottimizzata:

model = genai.GenerativeModel(
    model_name='models/gemini-1.5-pro-latest')

response = model.generate_content(
  textwrap.dedent("""\
    Please return JSON describing the the people, places, things and relationships from this story using the following schema:

    {"people": list[PERSON], "places":list[PLACE], "things":list[THING], "relationships": list[RELATIONSHIP]}

    PERSON = {"name": str, "description": str, "start_place_name": str, "end_place_name": str}
    PLACE = {"name": str, "description": str}
    THING = {"name": str, "description": str, "start_place_name": str, "end_place_name": str}
    RELATIONSHIP = {"person_1_name": str, "person_2_name": str, "relationship": str}

    All fields are required.

    Important: Only return a single piece of valid JSON text.

    Here is the story:

    """) + story,
  generation_config={'response_mime_type':'application/json'}
)
response.text
'{"people": [\n    {\n        "name": "Anya",\n        "description": "A young girl who lives in the town of Willow Creek with her parents, Elise and Edward. She possesses a magical backpack that was handed down to her from her grandmother.",\n        "start_place_name": "Willow Creek",\n        "end_place_name": "Willow Creek"\n    },\n    {\n        "name": "Elise",\n        "description": "Anya\'s kind-hearted mother",\n        "start_place_name": "Willow Creek",\n        "end_place_name": "Willow Creek"\n    },\n    {\n        "name": "Edward",\n        "description": "Anya\'s wise-bearded father",\n        "start_place_name": "Willow Creek",\n        "end_place_name": "Willow Creek"\n    },\n    {\n        "name": "Samuel",\n        "description": "Anya\'s best friend, a curious and adventurous boy with a mischievous grin.",\n        "start_place_name": "Willow Creek",\n        "end_place_name": "Willow Creek"\n    },\n    {\n        "name": "Monster",\n        "description": "A massive beast with sharp teeth, glowing red eyes, and claws that could crush a human with ease.",\n        "start_place_name": "Forest",\n        "end_place_name": "Forest"\n    }\n], "places": [\n    {\n        "name": "Willow Creek",\n        "description": "A quaint town nestled amidst rolling hills and whispering willows."\n    },\n    {\n        "name": "Forest",\n        "description": "A shadowy place with rustling undergrowth and whispering trees."\n    },\n    {\n        "name": "Schoolhouse",\n        "description": "The only school in the town of Willow Creek."\n    },\n    {\n        "name": "Anya\'s home",\n        "description": "A modest cottage with a creaky wooden door."\n    }\n], "things": [\n    {\n        "name": "Magic backpack",\n        "description": "A magical backpack that was handed down to Anya from her grandmother. Its soft, emerald-green fabric shimmered with an ethereal glow, and its leather straps held secrets that only Anya knew.",\n        "start_place_name": "Anya\'s home",\n        "end_place_name": "Forest"\n    },\n    {\n        "name": "Shimmering sword",\n        "description": "A sword that shimmered in the sunlight and could strike with blinding light.",\n        "start_place_name": "Magic backpack",\n        "end_place_name": "Forest"\n    },\n    {\n        "name": "Book of ancient spells",\n        "description": "A book that contained ancient spells.",\n        "start_place_name": "Magic backpack",\n        "end_place_name": "Forest"\n    },\n    {\n        "name": "Tiny compass",\n        "description": "A compass that always pointed north.",\n        "start_place_name": "Magic backpack",\n        "end_place_name": "Forest"\n    },\n    {\n        "name": "Magical key",\n        "description": "A key that could open any lock.",\n        "start_place_name": "Magic backpack",\n        "end_place_name": "Forest"\n    },\n    {\n        "name": "Shattered crystals",\n        "description": "The remains of the monster after it was defeated by Anya\'s magic backpack.",\n        "start_place_name": "Forest",\n        "end_place_name": "Forest"\n    }\n], "relationships": [\n    {\n        "person_1_name": "Anya",\n        "person_2_name": "Elise",\n        "relationship": "mother-daughter"\n    },\n    {\n        "person_1_name": "Anya",\n        "person_2_name": "Edward",\n        "relationship": "father-daughter"\n    },\n    {\n        "person_1_name": "Anya",\n        "person_2_name": "Samuel",\n        "relationship": "best friends"\n    }\n]}'

Questo ha restituito una stringa json. Prova ad analizzarlo:

import json

print(json.dumps(json.loads(response.text), indent=4))
{
    "people": [
        {
            "name": "Anya",
            "description": "A young girl who lives in the town of Willow Creek with her parents, Elise and Edward. She possesses a magical backpack that was handed down to her from her grandmother.",
            "start_place_name": "Willow Creek",
            "end_place_name": "Willow Creek"
        },
        {
            "name": "Elise",
            "description": "Anya's kind-hearted mother",
            "start_place_name": "Willow Creek",
            "end_place_name": "Willow Creek"
        },
        {
            "name": "Edward",
            "description": "Anya's wise-bearded father",
            "start_place_name": "Willow Creek",
            "end_place_name": "Willow Creek"
        },
        {
            "name": "Samuel",
            "description": "Anya's best friend, a curious and adventurous boy with a mischievous grin.",
            "start_place_name": "Willow Creek",
            "end_place_name": "Willow Creek"
        },
        {
            "name": "Monster",
            "description": "A massive beast with sharp teeth, glowing red eyes, and claws that could crush a human with ease.",
            "start_place_name": "Forest",
            "end_place_name": "Forest"
        }
    ],
    "places": [
        {
            "name": "Willow Creek",
            "description": "A quaint town nestled amidst rolling hills and whispering willows."
        },
        {
            "name": "Forest",
            "description": "A shadowy place with rustling undergrowth and whispering trees."
        },
        {
            "name": "Schoolhouse",
            "description": "The only school in the town of Willow Creek."
        },
        {
            "name": "Anya's home",
            "description": "A modest cottage with a creaky wooden door."
        }
    ],
    "things": [
        {
            "name": "Magic backpack",
            "description": "A magical backpack that was handed down to Anya from her grandmother. Its soft, emerald-green fabric shimmered with an ethereal glow, and its leather straps held secrets that only Anya knew.",
            "start_place_name": "Anya's home",
            "end_place_name": "Forest"
        },
        {
            "name": "Shimmering sword",
            "description": "A sword that shimmered in the sunlight and could strike with blinding light.",
            "start_place_name": "Magic backpack",
            "end_place_name": "Forest"
        },
        {
            "name": "Book of ancient spells",
            "description": "A book that contained ancient spells.",
            "start_place_name": "Magic backpack",
            "end_place_name": "Forest"
        },
        {
            "name": "Tiny compass",
            "description": "A compass that always pointed north.",
            "start_place_name": "Magic backpack",
            "end_place_name": "Forest"
        },
        {
            "name": "Magical key",
            "description": "A key that could open any lock.",
            "start_place_name": "Magic backpack",
            "end_place_name": "Forest"
        },
        {
            "name": "Shattered crystals",
            "description": "The remains of the monster after it was defeated by Anya's magic backpack.",
            "start_place_name": "Forest",
            "end_place_name": "Forest"
        }
    ],
    "relationships": [
        {
            "person_1_name": "Anya",
            "person_2_name": "Elise",
            "relationship": "mother-daughter"
        },
        {
            "person_1_name": "Anya",
            "person_2_name": "Edward",
            "relationship": "father-daughter"
        },
        {
            "person_1_name": "Anya",
            "person_2_name": "Samuel",
            "relationship": "best friends"
        }
    ]
}

Si tratta di una procedura relativamente semplice e spesso funziona, ma puoi potenzialmente renderla più rigorosa/robusta definendo lo schema con la funzionalità di chiamata di funzione dell'API.

Usa la chiamata di funzione

Se non hai ancora seguito il tutorial Nozioni di base sulle chiamate di funzione, assicurati di farlo prima.

Con la chiamata di funzione della funzione e i suoi parametri vengono descritti all'API come genai.protos.FunctionDeclaration. Nei casi di base, l'SDK è in grado di creare FunctionDeclaration dalla funzione e dalle relative annotazioni. Quindi... per il momento devi definirli esplicitamente.

Definisci lo schema

Per iniziare, definisci person come oggetto con i campi di stringa name, description, start_place_name, end_place_name.

person = genai.protos.Schema(
    type = genai.protos.Type.OBJECT,
    properties = {
        'name':  genai.protos.Schema(type=genai.protos.Type.STRING),
        'description':  genai.protos.Schema(type=genai.protos.Type.STRING),
        'start_place_name': genai.protos.Schema(type=genai.protos.Type.STRING),
        'end_place_name': genai.protos.Schema(type=genai.protos.Type.STRING)
    },
    required=['name', 'description', 'start_place_name', 'end_place_name']
)

Quindi definisci le persone come un ARRAY di person oggetti:

people = genai.protos.Schema(
    type=genai.protos.Type.ARRAY,
    items=person
)

Poi ripeti la stessa operazione per ciascuna delle entità che stai tentando di estrarre:

place = genai.protos.Schema(
    type = genai.protos.Type.OBJECT,
    properties = {
        'name':  genai.protos.Schema(type=genai.protos.Type.STRING),
        'description':  genai.protos.Schema(type=genai.protos.Type.STRING),
    }
)

places = genai.protos.Schema(
    type=genai.protos.Type.ARRAY,
    items=place
)
thing = genai.protos.Schema(
  type = genai.protos.Type.OBJECT,
  properties = {
      'name':  genai.protos.Schema(type=genai.protos.Type.STRING),
      'description':  genai.protos.Schema(type=genai.protos.Type.STRING),
  }
)

things = genai.protos.Schema(
    type=genai.protos.Type.ARRAY,
    items=thing
)
relationship = genai.protos.Schema(
    type = genai.protos.Type.OBJECT,
    properties = {
        'person_1_name':  genai.protos.Schema(type=genai.protos.Type.STRING),
        'person_2_name':  genai.protos.Schema(type=genai.protos.Type.STRING),
        'relationship':  genai.protos.Schema(type=genai.protos.Type.STRING),
    }
)

relationships = genai.protos.Schema(
    type=genai.protos.Type.ARRAY,
    items=relationship
)

Ora crea il FunctionDeclaration:

add_to_database = genai.protos.FunctionDeclaration(
    name="add_to_database",
    description=textwrap.dedent("""\
        Adds entities to the database.
        """),
    parameters=genai.protos.Schema(
        type=genai.protos.Type.OBJECT,
        properties = {
            'people': people,
            'places': places,
            'things': things,
            'relationships': relationships
        }
    )
)

Chiama l'API

Come hai visto in Nozioni di base sulle chiamate di funzione, ora puoi passare FunctionDeclaration all'argomento tools del costruttore genai.GenerativeModel (il costruttore accetterà anche una rappresentazione JSON equivalente della dichiarazione di funzione):

model = genai.GenerativeModel(
    model_name='models/gemini-1.5-pro-latest',
    tools = [add_to_database])

Ogni volta che chiami l'API, l'SDK invia gli strumenti insieme al tuo prompt e il modello deve chiamare la funzione che hai definito:

result = model.generate_content(f"""
Please add the people, places, things, and relationships from this story to the database:

{story}
""",
# Force a function call
tool_config={'function_calling_config':'ANY'})

Ora non c'è testo da analizzare. Il risultato è una struttura di dati.

'text' in result.candidates[0].content.parts[0]
False
'function_call' in result.candidates[0].content.parts[0]
True
fc = result.candidates[0].content.parts[0].function_call
print(type(fc))
<class 'google.ai.generativelanguage_v1beta.types.content.FunctionCall'>

La classe genai.protos.FunctionCall si basa sui buffer di protocollo di Google, convertirlo in un oggetto compatibile con JSON più familiare:

print(json.dumps(type(fc).to_dict(fc), indent=4))
{
    "name": "add_to_database",
    "args": {
        "things": [
            {
                "name": "Magical Backpack",
                "description": "Anya's prized possession, the Magical Backpack, is no ordinary satchel. Its soft, emerald-green fabric shimmers with an ethereal glow, and its leather straps have secrets that only Anya knows. Within its capacious interior lay an enchanted world, filled with wonders that would ignite her imagination and change her life forever."
            },
            {
                "name": "Shimmering Sword",
                "description": "Among the wonders in Anya's Magical Backpack, lies a shimmering sword. With a determined gleam in her eye, she retrieved the shimmering sword and charged towards the monster."
            },
            {
                "description": "Residing within the Magical Backpack, the Book of Ancient Spells holds secrets untold.",
                "name": "Book of Ancient Spells"
            },
            {
                "description": "Tucked away in the Magical Backpack is a tiny compass that always points north.",
                "name": "Tiny Compass that Always Points North"
            },
            {
                "description": "Hidden within the Magical Backpack is a magical key that can open any lock.",
                "name": "Magical Key that Can Open Any Lock"
            }
        ],
        "relationships": [
            {
                "relationship": "Mother-Daughter",
                "person_1_name": "Anya",
                "person_2_name": "Elise"
            },
            {
                "person_2_name": "Edward",
                "relationship": "Father-Daughter",
                "person_1_name": "Anya"
            },
            {
                "person_2_name": "Samuel",
                "person_1_name": "Anya",
                "relationship": "Best Friends"
            }
        ],
        "people": [
            {
                "name": "Anya",
                "description": "Anya, the main character of the story, is a young girl with a magical backpack.",
                "start_place_name": "Willow Creek",
                "end_place_name": "Unknown"
            },
            {
                "name": "Elise",
                "description": "Anya's mother, Elise is a kind-hearted woman.",
                "end_place_name": "Unknown",
                "start_place_name": "Willow Creek"
            },
            {
                "start_place_name": "Willow Creek",
                "end_place_name": "Unknown",
                "name": "Edward",
                "description": "Anya's father, Edward is a wise-bearded man."
            },
            {
                "end_place_name": "Unknown",
                "start_place_name": "Willow Creek",
                "description": "Anya's best friend, Samuel is a curious and adventurous boy with a mischievous grin.",
                "name": "Samuel"
            }
        ],
        "places": [
            {
                "description": "The quaint town of Willow Creek is nestled amidst rolling hills and whispering willows.",
                "name": "Willow Creek"
            },
            {
                "description": "The town's only schoolhouse.",
                "name": "Schoolhouse"
            },
            {
                "description": "A shadowy place filled with secrets and dangers, the Forest is home to a terrifying monster.",
                "name": "Forest"
            }
        ]
    }
}

Conclusione

Sebbene l'API sia in grado di gestire i problemi di estrazione di dati strutturati con input di testo puro e output di testo, l'utilizzo della chiamata di funzione è probabilmente più affidabile in quanto consente di definire uno schema rigido ed elimina un passaggio di analisi potenzialmente soggetto a errori.