תחילת העבודה עם אחזור סמנטי

להצגה ב-ai.google.dev כדאי לנסות notebook של Colab הצגת notebook ב-GitHub

סקירה כללית

מודלים גדולים של שפה (LLMs) יכולים ללמוד יכולות חדשות בלי לאמן אותם ישירות. עם זאת, מודלים גדולים של שפה נקראו כשהם התבקשו לתת תשובות לשאלות שהם לא אומנו. אחת הסיבות לכך היא שמודלים מסוג LLM לא מודעים לאירועים אחרי האימון. בנוסף, קשה מאוד לזהות את המקורות שמהם מודלים מסוג LLM מקבלים את התשובות שלהם. באפליקציות אמינות וניתנות להתאמה, חשוב ש-LLM יענה על בסיס עובדות ויוכל לצטט את מקורות המידע שלו.

שיטה נפוצה שמשמשת כדי להתגבר על המגבלות האלה נקראת Retrieval Augmented Generation (RAG), שמרחיבה את ההנחיה שנשלחת ל-LLM בנתונים רלוונטיים שאוחזרו ממאגר ידע חיצוני באמצעות מנגנון לאחזור מידע (IR). מאגר הידע יכול להיות מאגר משלכם של מסמכים, מסדי נתונים או ממשקי API.

ה-notebook הזה ידריך אתכם בתהליך העבודה לשיפור התשובה של מודל שפה גדול (LLM), על ידי הרחבת הידע שלו בעזרת מאגר טקסט חיצוני ואחזור של מידע סמנטי כדי לענות על שאלות באמצעות הכלי האחזור הסמנטי והשאלה המיוחסת, ממשקי API של תשובות (AQA) ב-Generative Language API.

הגדרה

ייבוא של Generative Language API

# Install the Client library (Semantic Retriever is only supported for versions >0.4.0)
pip install -U google.ai.generativelanguage

אמת

באמצעות ממשק ה-API של האחזור הסמנטי תוכלו לבצע חיפוש סמנטי על הנתונים שלכם. מכיוון שאלו הנתונים שלך, אמצעי בקרת גישה מחמירים יותר ממפתחות API. כדאי לבצע אימות באמצעות OAuth באמצעות חשבונות שירות או באמצעות פרטי הכניסה של המשתמש.

המדריך למתחילים הזה משתמש בגישת אימות פשוטה שמיועדת לסביבת בדיקה, ובדרך כלל קל יותר להתחיל בהגדרה של חשבונות השירות. בסביבת ייצור מומלץ לעיין במידע על אימות והרשאה לפני שבוחרים פרטי כניסה לגישה שמתאימים לאפליקציה.

הגדרת OAuth באמצעות חשבונות שירות

כדי להגדיר OAuth באמצעות חשבונות שירות:

  1. מפעילים את Generative Language API.

  1. יוצרים את חשבון השירות לפי מסמכי התיעוד.

    • אחרי יצירת חשבון השירות, יוצרים מפתח לחשבון השירות.

  1. כדי להעלות את קובץ חשבון השירות, לוחצים על סמל הקובץ בסרגל הצד שמימין ואז על סמל ההעלאה, כמו בצילום המסך שמוצג בהמשך.

    • משנים את השם של הקובץ שהועלה ל-service_account_key.json או משנים את המשתנה service_account_file_name בקוד שלמטה.

pip install -U google-auth-oauthlib
service_account_file_name = 'service_account_key.json'

from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file(service_account_file_name)

scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/generative-language.retriever'])

מפעילים את ספריית הלקוח באמצעות פרטי הכניסה של חשבון השירות.

import google.ai.generativelanguage as glm
generative_service_client = glm.GenerativeServiceClient(credentials=scoped_credentials)
retriever_service_client = glm.RetrieverServiceClient(credentials=scoped_credentials)
permission_service_client = glm.PermissionServiceClient(credentials=scoped_credentials)

יצירת קורפוס

ממשק ה-API של האחזור הסמנטי מאפשר להגדיר עד 5 מאגרי טקסט מותאמים אישית לכל פרויקט. ניתן לציין כל אחד מהשדות הבאים בזמן הגדרת הקורפורה:

  • name: שם המשאב Corpus (מזהה). חייב להכיל עד 40 תווים אלפאנומריים. אם השדה name ריק בזמן היצירה, המערכת תיצור שם ייחודי באורך מקסימלי של 40 תווים עם קידומת מ-display_name וסיומת אקראית של 12 תווים.
  • display_name: השם המוצג של Corpus בפורמט קריא לאנשים. חייב להכיל עד 512 תווים, כולל תווים אלפאנומריים, רווחים ומקפים.
example_corpus = glm.Corpus(display_name="Google for Developers Blog")
create_corpus_request = glm.CreateCorpusRequest(corpus=example_corpus)

# Make the request
create_corpus_response = retriever_service_client.create_corpus(create_corpus_request)

# Set the `corpus_resource_name` for subsequent sections.
corpus_resource_name = create_corpus_response.name
print(create_corpus_response)
name: "corpora/google-for-developers-blog-dqrtz8rs0jg"
display_name: "Google for Developers Blog"
create_time {
  seconds: 1713497533
  nanos: 587977000
}
update_time {
  seconds: 1713497533
  nanos: 587977000
}

קבלת הקורפוס שנוצר

משתמשים בשיטה GetCorpusRequest כדי לגשת באופן פרוגרמטי ל-Corpus שיצרתם למעלה. הערך של הפרמטר name מתייחס לשם המשאב המלא של Corpus, והוא מוגדר בתא שלמעלה כ-corpus_resource_name. הפורמט הנדרש הוא corpora/corpus-123.

get_corpus_request = glm.GetCorpusRequest(name=corpus_resource_name)

# Make the request
get_corpus_response = retriever_service_client.get_corpus(get_corpus_request)

# Print the response
print(get_corpus_response)

יצירת מסמך

Corpus יכול להכיל עד 10,000 Document. במהלך הגדרת המסמכים, ניתן לציין כל אחד מהשדות הבאים:

  • name: שם המשאב Document (מזהה). חייב להכיל עד 40 תווים (אלפאנומריים או מקפים בלבד). המזהה לא יכול להתחיל או להסתיים ב- מקף. אם השם ריק ביצירה, נגזר שם ייחודי display_name עם סיומת אקראית באורך 12 תווים.
  • display_name: השם המוצג בפורמט קריא לאנשים. חייב להכיל עד 512 תווים, כולל תווים אלפאנומריים, רווחים ומקפים.

רכיבי Document תומכים גם ב-20 שדות של custom_metadata שצוינו על ידי המשתמש, המוגדרים כצמדי מפתח-ערך. מטא-נתונים בהתאמה אישית יכולים להיות מחרוזות, רשימות של מחרוזות או מספרים. שימו לב שרשימות של מחרוזות יכולות לתמוך ב-10 ערכים לכל היותר, וערכים מספריים מיוצגים כמספרים בנקודה צפה ב-API.

# Create a document with a custom display name.
example_document = glm.Document(display_name="Introducing Project IDX, An Experiment to Improve Full-stack, Multiplatform App Development")

# Add metadata.
# Metadata also supports numeric values not specified here
document_metadata = [
    glm.CustomMetadata(key="url", string_value="https://developers.googleblog.com/2023/08/introducing-project-idx-experiment-to-improve-full-stack-multiplatform-app-development.html")]
example_document.custom_metadata.extend(document_metadata)

# Make the request
# corpus_resource_name is a variable set in the "Create a corpus" section.
create_document_request = glm.CreateDocumentRequest(parent=corpus_resource_name, document=example_document)
create_document_response = retriever_service_client.create_document(create_document_request)

# Set the `document_resource_name` for subsequent sections.
document_resource_name = create_document_response.name
print(create_document_response)

לקבלת המסמך שנוצר

משתמשים בשיטה GetDocumentRequest כדי לגשת באופן פרוגרמטי למסמך שיצרתם למעלה. הערך של הפרמטר name מתייחס לשם המלא של המשאב במסמך והוא מוגדר בתא שלמעלה כ-document_resource_name. הפורמט הנדרש הוא corpora/corpus-123/documents/document-123.

get_document_request = glm.GetDocumentRequest(name=document_resource_name)

# Make the request
# document_resource_name is a variable set in the "Create a document" section.
get_document_response = retriever_service_client.get_document(get_document_request)

# Print the response
print(get_document_response)

הטמעה ו יצירת מקטע של מסמך

כדי לשפר את הרלוונטיות של תוכן שמוחזר על ידי מסד נתונים וקטורי במהלך אחזור סמנטי, מומלץ לחלק מסמכים גדולים לחלקים או למקטעים קטנים יותר בזמן הטמעת המסמך.

Chunk הוא תת-חלק של Document שמתייחס כיחידה עצמאית למטרות ייצוג ואחסון של וקטורים. השדה Chunk יכול להכיל עד 2,043 אסימונים. Corpus יכול להיות עד מיליון Chunk.

בדומה לשדות Document, השדה Chunks תומך גם ב-20 שדות של custom_metadata שצוינו על ידי המשתמש, המוגדרים כצמדי מפתח-ערך. מטא-נתונים בהתאמה אישית יכולים להיות מחרוזות, רשימות של מחרוזות או מספרים. שימו לב שרשימות של מחרוזות יכולות לתמוך ב-10 ערכים לכל היותר, וערכים מספריים מיוצגים כמספרים בנקודה צפה ב-API.

במדריך הזה נעשה שימוש ב-Open Source HtmlChunker של Google.

מקטעי נתונים אחרים שבהם אפשר להשתמש: LangChain או LlamaIndex.

הטמעת נתוני HTML ומקטע נתונים באמצעות HtmlChunker

!pip install google-labs-html-chunker

from google_labs_html_chunker.html_chunker import HtmlChunker

from urllib.request import urlopen

מקבלים את ה-DOM DOM של אתר. כאן, קוד ה-HTML נקרא ישירות, עדיף לקבל HTML אחרי עיבוד כדי לכלול HTML שהוצב ב-JavaScript. כמו document.documentElement.innerHTML.

with(urlopen("https://developers.googleblog.com/2023/08/introducing-project-idx-experiment-to-improve-full-stack-multiplatform-app-development.html")) as f:
  html = f.read().decode("utf-8")

צריך לפצל את מסמך הטקסט לפסקאות וליצור Chunk פסקאות. בשלב הזה יוצרים את האובייקטים Chunk עצמם, ובקטע הבא הם מעלים אותם אל ה-API של האחזור הסמנטי.

# Chunk the file using HtmlChunker
chunker = HtmlChunker(
    max_words_per_aggregate_passage=200,
    greedily_aggregate_sibling_nodes=True,
    html_tags_to_exclude={"noscript", "script", "style"},
)
passages = chunker.chunk(html)
print(passages)


# Create `Chunk` entities.
chunks = []
for passage in passages:
    chunk = glm.Chunk(data={'string_value': passage})
    # Optionally, you can add metadata to a chunk
    chunk.custom_metadata.append(glm.CustomMetadata(key="tags",
                                                    string_list_value=glm.StringList(
                                                        values=["Google For Developers", "Project IDX", "Blog", "Announcement"])))
    chunk.custom_metadata.append(glm.CustomMetadata(key="chunking_strategy",
                                                    string_value="greedily_aggregate_sibling_nodes"))
    chunk.custom_metadata.append(glm.CustomMetadata(key = "publish_date",
                                                    numeric_value = 20230808))
    chunks.append(chunk)
print(chunks)

יצירת מקטעי נתונים

יצירת מקטעים בכמות גדולה. אפשר לציין עד 100 מקטעים לכל בקשה באצווה.

כדי ליצור מקטע יחיד, צריך להשתמש ב-CreateChunk().

# Option 1: Use HtmlChunker in the section above.
# `chunks` is the variable set from the section above.
create_chunk_requests = []
for chunk in chunks:
  create_chunk_requests.append(glm.CreateChunkRequest(parent=document_resource_name, chunk=chunk))

# Make the request
request = glm.BatchCreateChunksRequest(parent=document_resource_name, requests=create_chunk_requests)
response = retriever_service_client.batch_create_chunks(request)
print(response)

לחלופין, אפשר ליצור מקטעים בלי להשתמש ב-HtmlChunker.

# Add up to 100 CreateChunk requests per batch request.
# document_resource_name is a variable set in the "Create a document" section.
chunks = []
chunk_1 = glm.Chunk(data={'string_value': "Chunks support user specified metadata."})
chunk_1.custom_metadata.append(glm.CustomMetadata(key="section",
                                                  string_value="Custom metadata filters"))
chunk_2 = glm.Chunk(data={'string_value': "The maximum number of metadata supported is 20"})
chunk_2.custom_metadata.append(glm.CustomMetadata(key = "num_keys",
                                                  numeric_value = 20))
chunks = [chunk_1, chunk_2]
create_chunk_requests = []
for chunk in chunks:
  create_chunk_requests.append(glm.CreateChunkRequest(parent=document_resource_name, chunk=chunk))

# Make the request
request = glm.BatchCreateChunksRequest(parent=document_resource_name, requests=create_chunk_requests)
response = retriever_service_client.batch_create_chunks(request)
print(response)

הצגת רשימה של Chunk וקבלת מצב

אפשר להשתמש בשיטה ListChunksRequest כדי לקבל את כל הפריטים מסוג 'Chunk' כרשימה בחלוקה לדפים, עם מגבלת גודל של 100 שניות Chunk בכל דף, שממוינים בסדר עולה לפי Chunk.create_time. אם לא מציינים מגבלה, מוחזרים עד 10 יחידות מסוג Chunk.

צריך לספק את ה-next_page_token שהוחזר בתשובה של ListChunksRequest כארגומנט לבקשה הבאה כדי לאחזר את הדף הבא. חשוב לשים לב שכאשר החלוקה לדפים, כל שאר הפרמטרים שסופקו ל-ListChunks חייבים להתאים לקריאה שסיפקה את אסימון הדף.

כל הערכים של 'Chunk' מחזירים state. אפשר להשתמש במדד הזה כדי לבדוק את המצב של Chunks לפני שליחת שאילתה על Corpus. המצבים של Chunk כוללים את – UNSPECIFIED, PENDING_PROCESSING, ACTIVE ו-FAILED. אפשר להריץ רק שאילתה על ACTIVE Chunk.

# Make the request
request = glm.ListChunksRequest(parent=document_resource_name)
list_chunks_response = retriever_service_client.list_chunks(request)
for index, chunks in enumerate(list_chunks_response.chunks):
  print(f'\nChunk # {index + 1}')
  print(f'Resource Name: {chunks.name}')
  # Only ACTIVE chunks can be queried.
  print(f'State: {glm.Chunk.State(chunks.state).name}')

להטמיע מסמך אחר

צריך להוסיף עוד Document דרך HtmlChunker ולהוסיף פילטרים.

# Create a document with a custom display name.
example_document = glm.Document(display_name="How it’s Made: Interacting with Gemini through multimodal prompting")

# Add document metadata.
# Metadata also supports numeric values not specified here
document_metadata = [
    glm.CustomMetadata(key="url", string_value="https://developers.googleblog.com/2023/12/how-its-made-gemini-multimodal-prompting.html")]
example_document.custom_metadata.extend(document_metadata)

# Make the CreateDocument request
# corpus_resource_name is a variable set in the "Create a corpus" section.
create_document_request = glm.CreateDocumentRequest(parent=corpus_resource_name, document=example_document)
create_document_response = retriever_service_client.create_document(create_document_request)

# Set the `document_resource_name` for subsequent sections.
document_resource_name = create_document_response.name
print(create_document_response)

# Chunks - add another webpage from Google for Developers
with(urlopen("https://developers.googleblog.com/2023/12/how-its-made-gemini-multimodal-prompting.html")) as f:
  html = f.read().decode("utf-8")

# Chunk the file using HtmlChunker
chunker = HtmlChunker(
    max_words_per_aggregate_passage=100,
    greedily_aggregate_sibling_nodes=False,
)
passages = chunker.chunk(html)

# Create `Chunk` entities.
chunks = []
for passage in passages:
    chunk = glm.Chunk(data={'string_value': passage})
    chunk.custom_metadata.append(glm.CustomMetadata(key="tags",
                                                    string_list_value=glm.StringList(
                                                        values=["Google For Developers", "Gemini API", "Blog", "Announcement"])))
    chunk.custom_metadata.append(glm.CustomMetadata(key="chunking_strategy",
                                                    string_value="no_aggregate_sibling_nodes"))
    chunk.custom_metadata.append(glm.CustomMetadata(key = "publish_date",
                                                    numeric_value = 20231206))
    chunks.append(chunk)

# Make the request
create_chunk_requests = []
for chunk in chunks:
  create_chunk_requests.append(glm.CreateChunkRequest(parent=document_resource_name, chunk=chunk))
request = glm.BatchCreateChunksRequest(parent=document_resource_name, requests=create_chunk_requests)
response = retriever_service_client.batch_create_chunks(request)
print(response)

שליחת שאילתה לקורפוס

אפשר להשתמש בשיטה QueryCorpusRequest כדי לבצע חיפוש סמנטי כדי לקבל פסקאות רלוונטיות.

  • results_count: ציון מספר הפסקאות שצריך להחזיר. המקסימום הוא 100. אם לא צוין אחרת, ה-API יחזיר עד 10 שניות Chunk.
  • metadata_filters: סינון לפי chunk_metadata או document_metadata. כל MetadataFilter צריך להתאים למפתח ייחודי. אם יש כמה אובייקטים מסוג MetadataFilter שמחוברים אליהם באמצעות תווי AND לוגיים. תנאים דומים לסינון מטא-נתונים מצורפים באמצעות תווי OR לוגיים. מספר דוגמאות:
(year >= 2020 OR year < 2010) AND (genre = drama OR genre = action)

metadata_filter = [
  {
    key = "document.custom_metadata.year"
    conditions = [
      {int_value = 2020, operation = GREATER_EQUAL},
      {int_value = 2010, operation = LESS}]
  },
  {
    key = "document.custom_metadata.genre"
    conditions = [
      {string_value = "drama", operation = EQUAL},
      {string_value = "action", operation = EQUAL} }]
  }]

הערה: רק ערכים מספריים תומכים בסימן 'AND' עבור אותו מפתח. מחרוזת (String) תומכים רק בערכי 'OR' עבור אותו מפתח.

("Google for Developers" in tags) and (20230314 > publish_date)

metadata_filter = [
 {
    key = "chunk.custom_metadata.tags"
    conditions = [
    {string_value = 'Google for Developers', operation = INCLUDES},
  },
  {
    key = "chunk.custom_metadata.publish_date"
    conditions = [
    {numeric_value = 20230314, operation = GREATER_EQUAL}]
  }]
user_query = "What is the purpose of Project IDX?"
results_count = 5

# Add metadata filters for both chunk and document.
chunk_metadata_filter = glm.MetadataFilter(key='chunk.custom_metadata.tags',
                                           conditions=[glm.Condition(
                                              string_value='Google For Developers',
                                              operation=glm.Condition.Operator.INCLUDES)])

# Make the request
# corpus_resource_name is a variable set in the "Create a corpus" section.
request = glm.QueryCorpusRequest(name=corpus_resource_name,
                                 query=user_query,
                                 results_count=results_count,
                                 metadata_filters=[chunk_metadata_filter])
query_corpus_response = retriever_service_client.query_corpus(request)
print(query_corpus_response)

מענה על שאלות משויכות

משתמשים בשיטה GenerateAnswer כדי לבצע מענה לשאלות משויכות במסמך, במאגר הנתונים או בקבוצת פסקאות.

שיוך (AQA) למענה על שאלות(AQA) מתייחס למענה על שאלות שמתבססות על הקשר נתון ולמתן קרדיטים, תוך צמצום ההזות.

ל-GenerateAnswer יש כמה יתרונות על פני שימוש ב-LLM לא מכוונן, במקרים שבהם רוצים לבצע בקרת איכות:

  • המודל הבסיסי אומן להחזיר רק תשובות שמתבססות על ההקשר שסופק.
  • היא מציינת את הייחוסים (קטעים מההקשר שסופק). ייחוס מאפשר למשתמש לאמת את התשובה.
  • היא מבצעת הערכה של answerable_probability עבור צמד נתון (שאלה, הקשר), וכך מאפשרת לכם להפנות את התנהגות המוצר בהתאם הסבירות שהתשובה שמוחזרת תהיה מבוססת ונכונה.

answerable_probability והבעיה 'אין לי מושג'

במקרים מסוימים, התשובה הטובה ביותר לשאלה היא למעשה "אין לי מושג". לדוגמה, אם ההקשר שצוין לא מכיל תשובה לשאלה, השאלה תיחשב כ'לא ניתנת לתשובה'.

למודל ה-AQA, יש מיומנות גבוהה בזיהוי מקרים כאלה. הוא יכול אפילו להבחין בין דרגות של עונים על בעיות ואי-יכולת לענות עליהן.

עם זאת, בזכות ה-API של GenerateAnswer, כוח קבלת ההחלטות הסופי נמצא בידיים שלך:

  • תמיד ניסיון להחזיר תשובה בסיסית – גם אם סביר יחסית שהתשובה הזו מבוססת על תשובה נכונה ונכונה.
  • החזרת ערך answerable_probability – האומדן של המודל להסתברות, שהתשובה מבוססת על ביסוס ונכונה.

ערך נמוך של answerable_probability עשוי לנבוע מאחד או יותר מהגורמים הבאים:

  • המודל לא בטוח שהתשובה שלו נכונה.
  • המודל לא בטוח שהתשובה שלו מבוססת על הפסקאות המצוטטות. ייתכן שהתשובה נגזרת מידע בעולם. לדוגמה: question="1+1=?", passages=["2+2=4”]answer=2, answerable_probability=0.02
  • המודל סיפק מידע רלוונטי שלא נתן תשובה מלאה לשאלה. דוגמה: question="Is it available in my size?, passages=["Available in sizes 5-11"]answer="Yes it is available in sizes 5-11", answerable_probability=0.03"
  • לא התקבלה שאלה בפורמט תקין ב-GenerateAnswerRequest.

מכיוון שערך answerable_probability נמוך מצביע על כך שסביר להניח שה-GenerateAnswerResponse.answer שגוי או שאין בו יסוד כלשהו, מומלץ מאוד לעבד את התשובה על ידי בדיקה של answerable_probability.

כאשר הערך של answerable_probability נמוך, ייתכן שלקוחות מסוימים ירצו:

  • הצגת הודעה עם הכיתוב 'לא ניתן לענות על השאלה הזו' למשתמש הקצה.
  • נחזור ל-LLM לשימוש כללי שיענה על השאלה מתוך הידע העולמי. הסף והאופי של חלופות כאלה תלויים בתרחישים שונים לדוגמה. ערך של answerable_probability <= 0.5 הוא סף התחלה טוב.

טיפים מועילים בנושא AQA

המפרטים המלאים של ה-API זמינים בחומר העזר בנושא API של GenerateAnswerRequest.

  • אורך המעבר: מומלץ להשתמש ב-300 אסימונים לכל פסקה.
  • מיון המעבר:
    • אם מציינים GenerateAnswerRequest.inline_passages, צריך למיין את הפסקאות בסדר יורד לפי הרלוונטיות שלהן לשאילתה. אם המודל חורג ממגבלת אורך ההקשר, הפסקאות האחרונות (הכי פחות רלוונטיות) יושמטו.
    • אם תוסיפו GenerateAnswerRequest.semantic_retriever, המיון של הרלוונטיות יתבצע עבורכם באופן אוטומטי.
  • מגבלות: מודל ה-AQA מתמחה במענה לשאלות. עבור תרחישים אחרים לדוגמה כמו כתיבה יצירתית, סיכום וכו', יש להפעיל מודל לשימוש כללי באמצעות GenerateContent.
    • Chat: אם ידוע שהקלט של המשתמש הוא שאלה שעשויה להיות תשובה מהקשר מסוים, AQA יכול לענות על שאילתות צ'אט. עם זאת, אם קלט של משתמשים הוא סוג כלשהו של ערך, עדיף להשתמש במודל לשימוש כללי.
  • טמפרטורה:
    • באופן כללי, מומלץ להשתמש בטמפרטורה נמוכה יחסית (כ-0.2) כדי ליצור בקרת איכות מדויקת.
    • אם התרחיש לדוגמה שלכם מסתמך על פלט דטרמיניסטי, מגדירים את הטמפרטורה=0.
user_query = "What is the purpose of Project IDX?"
answer_style = "ABSTRACTIVE" # Or VERBOSE, EXTRACTIVE
MODEL_NAME = "models/aqa"

# Make the request
# corpus_resource_name is a variable set in the "Create a corpus" section.
content = glm.Content(parts=[glm.Part(text=user_query)])
retriever_config = glm.SemanticRetrieverConfig(source=corpus_resource_name, query=content)
req = glm.GenerateAnswerRequest(model=MODEL_NAME,
                                contents=[content],
                                semantic_retriever=retriever_config,
                                answer_style=answer_style)
aqa_response = generative_service_client.generate_answer(req)
print(aqa_response)
# Get the metadata from the first attributed passages for the source
chunk_resource_name = aqa_response.answer.grounding_attributions[0].source_id.semantic_retriever_chunk.chunk
get_chunk_response = retriever_service_client.get_chunk(name=chunk_resource_name)
print(get_chunk_response)

אפשרויות נוספות: AQA (בקרת איכות) באמצעות מעברים מוטבעים

לחלופין, אפשר להשתמש ישירות בנקודת הקצה AQA, בלי להשתמש ב-Semant Retriever API על ידי העברת inline_passages.

user_query = "What is AQA from Google?"
user_query_content = glm.Content(parts=[glm.Part(text=user_query)])
answer_style = "VERBOSE" # or ABSTRACTIVE, EXTRACTIVE
MODEL_NAME = "models/aqa"

# Create the grounding inline passages
grounding_passages = glm.GroundingPassages()
passage_a = glm.Content(parts=[glm.Part(text="Attributed Question and Answering (AQA) refers to answering questions grounded to a given corpus and providing citation")])
grounding_passages.passages.append(glm.GroundingPassage(content=passage_a, id="001"))
passage_b = glm.Content(parts=[glm.Part(text="An LLM is not designed to generate content grounded in a set of passages. Although instructing an LLM to answer questions only based on a set of passages reduces hallucination, hallucination still often occurs when LLMs generate responses unsupported by facts provided by passages")])
grounding_passages.passages.append(glm.GroundingPassage(content=passage_b, id="002"))
passage_c = glm.Content(parts=[glm.Part(text="Hallucination is one of the biggest problems in Large Language Models (LLM) development. Large Language Models (LLMs) could produce responses that are fictitious and incorrect, which significantly impacts the usefulness and trustworthiness of applications built with language models.")])
grounding_passages.passages.append(glm.GroundingPassage(content=passage_c, id="003"))

# Create the request
req = glm.GenerateAnswerRequest(model=MODEL_NAME,
                                contents=[user_query_content],
                                inline_passages=grounding_passages,
                                answer_style=answer_style)
aqa_response = generative_service_client.generate_answer(req)
print(aqa_response)

שיתוף הקורפוס

אפשר לשתף את הקורפוס עם אחרים באמצעות API של CreatePermissionRequest.

מגבלות:

  • יש 2 תפקידים שאפשר לשתף: READER ו-EDITOR.
    • READER יכול לשלוח שאילתה לקורפוס.
    • ל-WRITER יש הרשאות קורא וגם אפשרות לערוך ולשתף את התוכן.
  • אוסף יכול להיות ציבורי על ידי הענקת גישה ל-EVERYONE כ-user_type גישת קריאה.
# Replace your-email@gmail.com with the email added as a test user in the OAuth Quickstart
shared_user_email = "TODO-your-email@gmail.com" #  @param {type:"string"}
user_type = "USER"
role = "READER"

# Make the request
# corpus_resource_name is a variable set in the "Create a corpus" section.
request = glm.CreatePermissionRequest(
    parent=corpus_resource_name,
    permission=glm.Permission(grantee_type=user_type,
                              email_address=shared_user_email,
                              role=role))
create_permission_response = permission_service_client.create_permission(request)
print(create_permission_response)

מחיקת הקורפוס

שימוש ב-DeleteCorpusRequest כדי למחוק קורפוס של משתמשים ואת כל קובצי ה-Document המשויכים וגם Chunks.

חשוב לשים לב שאוסף שאינו ריק יקפיץ שגיאה בלי לציין דגל force=True. אם מגדירים את force=True, כל האובייקטים מסוג Chunk והאובייקטים שקשורים לDocument הזה יימחקו גם הם.

אם force=False (ברירת המחדל) ו-Document מכילים פונקציות Chunk כלשהן, תוחזר שגיאה מסוג FAILED_PRECONDITION.

# Set force to False if you don't want to delete non-empty corpora.
req = glm.DeleteCorpusRequest(name=corpus_resource_name, force=True)
delete_corpus_response = retriever_service_client.delete_corpus(req)
print("Successfully deleted corpus: " + corpus_resource_name)

סיכום וקריאה נוספת

במדריך הזה הצגנו את השאלה הסמנטית ואת השאלה המיוחסת, תשובות בממשקי API של Generative Language API והראו איך אפשר להשתמש בהם כדי לבצע אחזור סמנטי של הנתונים מהטקסט המותאם אישית. שימו לב שה-API הזה פועל גם עם מסגרת הנתונים LlamaIndex. מידע נוסף זמין במדריך.

מידע נוסף על הפונקציות הזמינות האחרות זמין גם במסמכי ה-API.

נספח: הגדרת OAuth עם פרטי כניסה של משתמשים

כדי להגדיר אימות OAuth, יש לבצע את השלבים הבאים מהמדריך למתחילים של OAuth.

  1. מגדירים את מסך ההסכמה של OAuth.

  2. מתן הרשאה לפרטי כניסה של אפליקציה למחשב. כדי להריץ את ה-notebook הזה ב-Colab, צריך קודם לשנות את השם של קובץ פרטי הכניסה (בדרך כלל client_secret_*.json) ל-client_secret.json בלבד. לאחר מכן מעלים את הקובץ באמצעות סמל הקובץ בסרגל הצד שמימין, ואז על סמל ההעלאה, כפי שמוצג בצילום המסך למטה.

# Replace TODO-your-project-name with the project used in the OAuth Quickstart
project_name = "TODO-your-project-name" #  @param {type:"string"}
# Replace TODO-your-email@gmail.com with the email added as a test user in the OAuth Quickstart
email = "TODO-your-email@gmail.com" #  @param {type:"string"}
# Rename the uploaded file to `client_secret.json` OR
# Change the variable `client_file_name` in the code below.
client_file_name = "client_secret.json"

# IMPORTANT: Follow the instructions from the output - you must copy the command
# to your terminal and copy the output after authentication back here.
!gcloud config set project $project_name
!gcloud config set account $email

# NOTE: The simplified project setup in this tutorial triggers a "Google hasn't verified this app." dialog.
# This is normal, click "Advanced" -> "Go to [app name] (unsafe)"
!gcloud auth application-default login --no-browser --client-id-file=$client_file_name --scopes="https://www.googleapis.com/auth/generative-language.retriever,https://www.googleapis.com/auth/cloud-platform"

לאתחל את ספריית הלקוח ולהריץ מחדש את ה-notebook החל מיצירת קורפוס.

import google.ai.generativelanguage as glm

generative_service_client = glm.GenerativeServiceClient()
retriever_service_client = glm.RetrieverServiceClient()
permission_service_client = glm.PermissionServiceClient()