chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
from typing import Annotated
|
||||
|
||||
from semantic_kernel import Kernel
|
||||
from semantic_kernel.connectors.ai import FunctionChoiceBehavior
|
||||
from semantic_kernel.connectors.ai.open_ai import (
|
||||
OpenAIChatCompletion,
|
||||
OpenAIChatPromptExecutionSettings,
|
||||
OpenAITextEmbedding,
|
||||
)
|
||||
from semantic_kernel.connectors.in_memory import InMemoryCollection
|
||||
from semantic_kernel.data.vector import VectorStoreField, vectorstoremodel
|
||||
from semantic_kernel.functions import KernelArguments
|
||||
|
||||
"""
|
||||
This sample shows a really easy way to have RAG with a vector store.
|
||||
It creates a simple datamodel, and then creates a collection with that datamodel.
|
||||
Then we create a function that can search the collection.
|
||||
Finally, in two different ways we call the function to search the collection.
|
||||
"""
|
||||
|
||||
|
||||
# Define a data model for the collection
|
||||
# This model will be used to store the information in the collection
|
||||
@vectorstoremodel(collection_name="budget")
|
||||
@dataclass
|
||||
class BudgetItem:
|
||||
id: Annotated[str, VectorStoreField("key")]
|
||||
text: Annotated[str, VectorStoreField("data")]
|
||||
embedding: Annotated[
|
||||
list[float] | str | None,
|
||||
VectorStoreField("vector", dimensions=1536, embedding_generator=OpenAITextEmbedding()),
|
||||
] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.embedding is None:
|
||||
self.embedding = self.text
|
||||
|
||||
|
||||
async def main():
|
||||
kernel = Kernel()
|
||||
|
||||
kernel.add_service(OpenAIChatCompletion())
|
||||
|
||||
async with InMemoryCollection(record_type=BudgetItem) as collection:
|
||||
await collection.ensure_collection_exists()
|
||||
# Add information to the collection
|
||||
await collection.upsert(
|
||||
[
|
||||
BudgetItem(id="info1", text="My budget for 2022 is $50,000"),
|
||||
BudgetItem(id="info1", text="My budget for 2023 is $75,000"),
|
||||
BudgetItem(id="info1", text="My budget for 2024 is $100,000"),
|
||||
BudgetItem(id="info2", text="My budget for 2025 is $150,000"),
|
||||
],
|
||||
)
|
||||
# Create a function to search the collection
|
||||
# note the string_mapper, this is used to map the result of the search to a string
|
||||
kernel.add_function(
|
||||
"memory",
|
||||
collection.create_search_function(
|
||||
function_name="recall",
|
||||
description="Recalls the budget information.",
|
||||
string_mapper=lambda x: x.record.text,
|
||||
),
|
||||
)
|
||||
|
||||
# Call the search function directly from from a template.
|
||||
result = await kernel.invoke_prompt(
|
||||
function_name="budget",
|
||||
plugin_name="BudgetPlugin",
|
||||
prompt="{{memory.recall 'budget by year'}} What is my budget for 2024?",
|
||||
)
|
||||
print("Called from template")
|
||||
print(result)
|
||||
print("======================")
|
||||
# Let the LLM choose the function to call
|
||||
result = await kernel.invoke_prompt(
|
||||
function_name="budget",
|
||||
plugin_name="BudgetPlugin",
|
||||
prompt="What is my budget for 2024?",
|
||||
arguments=KernelArguments(
|
||||
settings=OpenAIChatPromptExecutionSettings(
|
||||
function_choice_behavior=FunctionChoiceBehavior.Auto(),
|
||||
),
|
||||
),
|
||||
)
|
||||
print("Called from LLM")
|
||||
print(result)
|
||||
|
||||
|
||||
"""
|
||||
Output:
|
||||
|
||||
Called from template
|
||||
Your budget for 2024 is $100,000.
|
||||
======================
|
||||
Called from LLM
|
||||
Your budget for 2024 is $100,000.
|
||||
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,174 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
from textwrap import dedent
|
||||
from typing import Annotated
|
||||
|
||||
from semantic_kernel import Kernel
|
||||
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, OpenAITextEmbedding
|
||||
from semantic_kernel.connectors.azure_ai_search import AzureAISearchCollection
|
||||
from semantic_kernel.contents import ChatHistory
|
||||
from semantic_kernel.data.vector import VectorStoreField, vectorstoremodel
|
||||
from semantic_kernel.functions.kernel_function import KernelFunction
|
||||
|
||||
"""
|
||||
This sample shows a really easy way to perform RAG with a vector store.
|
||||
It creates a simple datamodel, and then creates a collection with that datamodel.
|
||||
Then we create a function that can search the collection.
|
||||
Finally, we use the function in a prompt to get an grounding for a answer.
|
||||
And we then call a function that can check if the answer is grounded or not.
|
||||
"""
|
||||
|
||||
|
||||
# Define a data model for the collection
|
||||
# This model will be used to store the information in the collection
|
||||
@vectorstoremodel(collection_name="generic")
|
||||
@dataclass
|
||||
class InfoItem:
|
||||
key: Annotated[str, VectorStoreField("key")]
|
||||
text: Annotated[str, VectorStoreField("data")]
|
||||
embedding: Annotated[
|
||||
list[float] | str | None,
|
||||
VectorStoreField("vector", dimensions=1536, embedding_generator=OpenAITextEmbedding()),
|
||||
] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.embedding is None:
|
||||
self.embedding = self.text
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
kernel = Kernel()
|
||||
|
||||
async with AzureAISearchCollection(record_type=InfoItem) as collection:
|
||||
# Setting up OpenAI services for text completion and text embedding
|
||||
kernel.add_service(OpenAIChatCompletion())
|
||||
kernel.add_function(
|
||||
plugin_name="memory",
|
||||
function=collection.create_search_function(
|
||||
function_name="recall",
|
||||
description="Search the collection for information.",
|
||||
string_mapper=lambda x: x.record.text,
|
||||
),
|
||||
)
|
||||
|
||||
print("Creating index for memory...")
|
||||
await collection.ensure_collection_deleted()
|
||||
await collection.ensure_collection_exists()
|
||||
|
||||
print("Populating memory...")
|
||||
# Add information to the collection
|
||||
await collection.upsert([
|
||||
InfoItem(key="info1", text="My name is Andrea"),
|
||||
InfoItem(key="info2", text="I currently work as a tour guide"),
|
||||
InfoItem(key="info3", text="I've been living in Seattle since 2005"),
|
||||
InfoItem(key="info4", text="I visited France and Italy five times since 2015"),
|
||||
InfoItem(key="info5", text="My family is from New York"),
|
||||
])
|
||||
|
||||
chat_func: KernelFunction = kernel.add_function( # type: ignore
|
||||
function_name="rag",
|
||||
plugin_name="RagPlugin",
|
||||
prompt=dedent("""
|
||||
Assistant can have a conversation with you about any topic.
|
||||
It can give explicit instructions or say 'I don't know' if it does not have an answer.
|
||||
|
||||
Here is some background information about the user that you should use to answer the question below:
|
||||
Background: {{ memory.recall $question }}
|
||||
User: {{$question}}"""),
|
||||
)
|
||||
self_critique_func: KernelFunction = kernel.add_function( # type: ignore
|
||||
function_name="self_critique_rag",
|
||||
plugin_name="RagPlugin",
|
||||
prompt=dedent("""
|
||||
You will get a question, background information to be used with that question and a answer that was given.
|
||||
You have to answer Grounded or Ungrounded or Unclear.
|
||||
Grounded if the answer is based on the background information and clearly answers the question.
|
||||
Ungrounded if the answer could be true but is not based on the background information.
|
||||
Unclear if the answer does not answer the question at all.
|
||||
Question: {{$question}}
|
||||
Background: {{ memory.recall $question }}
|
||||
Answer: {{ $answer_to_question }}
|
||||
Remember, just answer Grounded or Ungrounded or Unclear: """),
|
||||
)
|
||||
|
||||
print("Asking a question...")
|
||||
question = "Do I live in Seattle?"
|
||||
print(f"Question: {question}")
|
||||
chat_history = ChatHistory()
|
||||
chat_history.add_user_message(question)
|
||||
answer = await kernel.invoke(
|
||||
chat_func,
|
||||
question=question,
|
||||
chat_history=chat_history,
|
||||
)
|
||||
chat_history.add_assistant_message(str(answer))
|
||||
print(f"Answer: {str(answer).strip()}")
|
||||
print("Checking the answer...")
|
||||
check = await kernel.invoke(
|
||||
self_critique_func,
|
||||
question=answer,
|
||||
answer_to_question=answer,
|
||||
chat_history=chat_history,
|
||||
)
|
||||
print(f"The answer was {str(check).strip()}")
|
||||
|
||||
print("-" * 50)
|
||||
print(" Let's pretend the answer was wrong...")
|
||||
wrong_answer = "Yes, you live in New York City."
|
||||
print(f"Question: {question}")
|
||||
print(f"Answer: {str(wrong_answer).strip()}")
|
||||
check = await kernel.invoke(
|
||||
self_critique_func,
|
||||
question=question,
|
||||
answer_to_question=wrong_answer,
|
||||
chat_history=chat_history,
|
||||
)
|
||||
print(f"The answer was {str(check).strip()}")
|
||||
|
||||
print("-" * 50)
|
||||
print(" Let's pretend the answer is not related...")
|
||||
unrelated_answer = "Yes, the earth is not flat."
|
||||
print(f"Question: {question}")
|
||||
print(f"Answer: {str(unrelated_answer).strip()}")
|
||||
check = await kernel.invoke(
|
||||
self_critique_func,
|
||||
question=question,
|
||||
answer_to_question=unrelated_answer,
|
||||
chat_history=chat_history,
|
||||
)
|
||||
print(f"The answer was {str(check).strip()}")
|
||||
|
||||
print("-" * 50)
|
||||
print("Removing collection...")
|
||||
await collection.ensure_collection_deleted()
|
||||
|
||||
|
||||
"""
|
||||
Expected output:
|
||||
--------------------------------------------------
|
||||
Creating index for memory...
|
||||
Populating memory...
|
||||
Asking a question...
|
||||
Question: Do I live in Seattle?
|
||||
Answer: Yes, Andrea, you do live in Seattle.
|
||||
Checking the answer...
|
||||
The answer was Grounded
|
||||
--------------------------------------------------
|
||||
Let's pretend the answer was wrong...
|
||||
Question: Do I live in Seattle?
|
||||
Answer: Yes, you live in New York City.
|
||||
The answer was Ungrounded
|
||||
--------------------------------------------------
|
||||
Let's pretend the answer is not related...
|
||||
Question: Do I live in Seattle?
|
||||
Answer: Yes, the earth is not flat.
|
||||
The answer was Unclear
|
||||
--------------------------------------------------
|
||||
Removing collection...
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user