使用函式呼叫擷取結構化資料

在 Google AI 中查看 在 Google Colab 中執行 在 GitHub 上查看原始碼

本教學課程將逐步說明如何使用 Gemini API 擷取故事中的角色、關係、事物和地點清單,

設定

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

import google.generativeai as genai
import google.ai.generativelanguage as glm


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

取得 API 金鑰後,請傳遞至 SDK。操作方式有以下兩種:

  • 將金鑰放入 GOOGLE_API_KEY 環境變數中 (SDK 會自動從該處取得金鑰)。
  • 將金鑰傳送至 genai.configure(api_key=...)
try:
    # Used to securely store your API key
    from google.colab import userdata

    # Or use `os.getenv('API_KEY')` to fetch an environment variable.
    GOOGLE_API_KEY=userdata.get('GOOGLE_API_KEY')
except ImportError:
    import os
    GOOGLE_API_KEY = os.environ['GOOGLE_API_KEY']

genai.configure(api_key=GOOGLE_API_KEY)

範例工作

在這個教學課程中,您將從自然語言故事中擷取實體。舉例來說,以下是由 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)

在威洛溪 (Willow Creek) 的古雅小鎮中,連綿起伏的丘陵和茂密的柳樹之間隱匿許多,並居住於一個名為 Anya 的女孩。當她從這間樸實的小屋中踏出古怪的木門時,心中跳動著充滿興奮感和期待感的節奏,今天是她第一天上學,她迫不及待要展現珍貴寶藏,也就是神奇的背包。

後背包與祖母親近距離,當時背包並不常見。這款錶帶柔軟的翡翠綠布料會閃閃發光,而皮革背帶則是 Anya 難以辨識的秘密。充滿魅力的華麗室內世界,充滿奇妙的世界,各種奇觀將激發想像力,為自己的人生帶來永久改變。

小艾的爸媽和善良的愛莉絲和聰明的愛德華,用暖心擁抱的力量來爭執。「記住,我的親,」對她的母親輕聲尖叫,「請善用您的魔法,以求很好」。她的父親新增了「爸爸隨時在尋找知識,讓後背包成為你信任的人」。

Anya 趁著跳躍的腳步,踏出去鄉鎮唯一的校舍。在路上,她帶著她的好朋友 Samuel,這個充滿好奇心又充滿冒險精神的男孩有著調皮的笑容。他說:「嗨,小艾!」「可以看到你的背包嗎?」

在將蓋子解開並揭曉其內容之前,花了些時間感到意猶未盡。Samuel 的雙眼一向獨自奮力,眼花撩亂,藏身在鉛筆和筆記本間,像是閃閃發光的劍、古代魔法書、一本一律指向北方的小型指南針,以及可用來打開任何鎖的魔法鑰匙。

他們就在背包的奇遇中解開了謎團,承諾守護其秘密的安全。接近學校後,Anya 注意到一群年紀較大的孩子一起閒晃,臉上有恐懼。好奇心逐漸養成,她謹慎地往前看。

她問道:「哪裡出了問題?

一名閃閃發光的高聳男孩向前行。「森林裡有怪物」。「這太驚人了整座城鎮,攻擊動物甚至還有人類。」

安安的心臟睡覺。威洛溪 (Willow Creek) 的小鎮都很小,平靜,怪物的思緒使她們的脊椎發光發光。她知道自己必須採取行動保護親朋好友。

Anya 打開後背包,擷取了閃閃發光的劍,不會浪費時間。她眼神的眼光深遠,後來竟陷入了她們棘手的同儕。她說:「別擔心,聲音保持穩定。「我會妥善處理。」

Samuel 背負著她,不惜一切進入森林的陰影深處。這些樹木在她過世時似乎悄悄悄悄溝通,而成長下的生物也充滿了不為人知的生物。隨著人們深入森林,空氣變得更加大,而腳下方的地面又會進行重訓。

結果突然間,他們找到了空地,眼睛是怪物這種巨大的猛獸、發亮的紅色眼睛,還有能夠輕鬆襲擊人類的爪爪。憤怒的火焰聲波,讓森林陷入混亂。

害怕 Anya 遭人報復,但她拒絕讓它取回她。她從剪刀手將刀劍匯出,向怪物指揮。葉片在陽光下閃閃發亮,打著東獸的藏身,閃爍的光芒發光,吸收所有物體。

光線逐漸淡出後,怪物就去了,原來那裡是一堆打碎的水晶。Anya 以自己背包的魔法擊敗生物,證明即使只有最小的物體也能保有最大的力量。

隨著她和薩姆爾回到這個小鎮,她們成為了英雄的好夥伴。Willow Creek 的人民非常開心,而擁有魔法背包的女孩 Anya 則經歷傳奇世紀的歷史。因此,Anya 繼續冒險,運用背包的奇觀讓世界變得更美好,一步一腳印。

使用自然語言

大型語言模型是功能強大的多工工具。您通常可以直接向 Gemini 索取所需資訊,這樣也能處理好。

以這種方式產生資料結構時,有幾點需要注意:

  • 有時剖析失敗。
  • 無法嚴格強制執行結構定義。

您將在下一節解決這些問題。首先,請試著用簡單的自然語言提示,將結構定義以文字寫成。目前尚未最佳化:

model = model = 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]}'

這會傳回 JSON 字串。請嘗試剖析模型:

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"
        }
    ]
}

這是相對簡單的做法,而且通常有效,但您可以使用 API 的函式呼叫功能定義結構定義,讓這個做法更加嚴格/完善。

使用函式呼叫

如果您尚未閱讀「函式呼叫基本」教學課程,請務必先完成此步驟。

透過呼叫函式及其參數的函式,系統會以 glm.FunctionDeclaration 描述 API。基本情況下,SDK 可以從函式及其註解建構 FunctionDeclaration。SDK 目前無法處理巢狀 OBJECT (dict) 參數的說明。因此,目前您必須明確定義這些事件。

定義結構定義

首先,將 person 定義為含有字串欄位 namedescriptionstart_place_nameend_place_name 的物件。

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

然後將人員定義為 person 物件的 ARRAY

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

接著,針對要擷取的每個實體執行相同步驟:

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

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

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

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

現在請建構 FunctionDeclaration

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

呼叫 API

如「函式呼叫基本概念」一文所述,您現在可以將此 FunctionDeclaration 傳遞至 genai.GenerativeModel 建構函式的 tools 引數 (建構函式也接受函式宣告的對等 JSON 表示法):

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

每次呼叫 API 時,SDK 都會連同提示一併傳送工具,而模型應呼叫您定義的函式:

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'})

現在沒有可剖析的文字。結果「是」資料結構。

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

glm.FunctionCall 類別是以 Google 通訊協定緩衝區為基礎,並轉換為更熟悉的 JSON 相容物件:

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"
            }
        ]
    }
}

結論

雖然這個 API 可以處理純文字輸入和文字輸出的結構化資料擷取問題,但函式呼叫可讓您定義更嚴格的結構定義,並且排除容易出錯的剖析步驟,因此使用函式呼叫會比較可靠。