This notebook shows how to replace the VolatileMemoryStore
memory storage used in a previous notebook with a WeaviateMemoryStore
.
WeaviateMemoryStore
is an example of a persistent (i.e. long-term) memory store backed by the Weaviate vector database.
Let's get started with the necessary configuration to run Semantic Kernel. For Notebooks, we require a .env
file with the proper settings for the model you use. Create a new file named .env
and place it in this directory. Copy the contents of the .env.example
file from this directory and paste it into the .env
file that you just created.
NOTE: Please make sure to include GLOBAL_LLM_SERVICE
set to either OpenAI, AzureOpenAI, or HuggingFace in your .env file. If this setting is not included, the Service will default to AzureOpenAI.
Add your OpenAI Key key to your .env
file (org Id only if you have multiple orgs):
GLOBAL_LLM_SERVICE="OpenAI"
OPENAI_API_KEY="sk-..."
OPENAI_ORG_ID=""
OPENAI_CHAT_MODEL_ID=""
OPENAI_TEXT_MODEL_ID=""
OPENAI_EMBEDDING_MODEL_ID=""
The names should match the names used in the .env
file, as shown above.
Add your Azure Open AI Service key settings to the .env
file in the same folder:
GLOBAL_LLM_SERVICE="AzureOpenAI"
AZURE_OPENAI_API_KEY="..."
AZURE_OPENAI_ENDPOINT="https://..."
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="..."
AZURE_OPENAI_TEXT_DEPLOYMENT_NAME="..."
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME="..."
AZURE_OPENAI_API_VERSION="..."
The names should match the names used in the .env
file, as shown above.
For more advanced configuration, please follow the steps outlined in the setup guide.
Weaviate is an open-source vector database designed to scale seamlessly into billions of data objects. This implementation supports hybrid search out-of-the-box (meaning it will perform better for keyword searches).
You can run Weaviate in 5 ways:
SaaS – with Weaviate Cloud Services (WCS).
WCS is a fully managed service that takes care of hosting, scaling, and updating your Weaviate instance. You can try it out for free with a sandbox that lasts for 14 days.
To set up a SaaS Weaviate instance with WCS:
Subscription Tier
– Free sandbox for a free trial, or contact hello@weaviate.io for other options.Cluster name
– a unique name for your cluster. The name will become part of the URL used to access this instance.Enable Authentication?
– Enabled by default. This will generate a static API key that you can use to authenticate.Hybrid SaaS
If you need to keep your data on-premise for security or compliance reasons, Weaviate also offers a Hybrid SaaS option: Weaviate runs within your cloud instances, but the cluster is managed remotely by Weaviate. This gives you the benefits of a managed service without sending data to an external party.
The Weaviate Hybrid SaaS is a custom solution. If you are interested in this option, please reach out to hello@weaviate.io.
Self-hosted – with a Docker container
To set up a Weaviate instance with Docker:
Install Docker on your local machine if it is not already installed.
Download a docker-compose.yml
file with this curl
command:
curl -o docker-compose.yml "https://configuration.weaviate.io/v2/docker-compose/docker-compose.yml?modules=standalone&runtime=docker-compose&weaviate_version=v1.19.6"
Alternatively, you can use Weaviate's docker compose configuration tool to generate your own docker-compose.yml
file.
Run docker compose up -d
to spin up a Weaviate instance.
To shut it down, run
docker compose down
.
Self-hosted – with a Kubernetes cluster
To configure a self-hosted instance with Kubernetes, follow Weaviate's documentation.|
Embedded - start a weaviate instance right from your application code using the client library
This code snippet shows how to instantiate an embedded weaviate instance and upload a document:
import weaviate
from weaviate.embedded import EmbeddedOptions
client = weaviate.Client(
embedded_options=EmbeddedOptions()
)
data_obj = {
"name": "Chardonnay",
"description": "Goes with fish"
}
client.data_object.create(data_obj, "Wine")
Refer to the documentation for more details about this deployment method.
# Note: if using a virtual environment, do not run this cell
%pip install -U semantic-kernel[weaviate]
from semantic_kernel import __version__
__version__
First, we instantiate the Weaviate memory store. Uncomment ONE of the options below, depending on how you want to use Weaviate:
from semantic_kernel.connectors.memory.weaviate import WeaviateMemoryStore
# Note the Weaviate Config values need to be either configured as environment variables
# or in the .env file, as a back up. When creating the instance of the `weaviate_memory_store`
# pass in `env_file_path=<path_to_file>` to read the config values from the `.env` file, otherwise
# the values will be read from environment variables.
# Env variables or .env file config should look like:
# WEAVIATE_URL="http://localhost:8080"
# WEAVIATE_API_KEY=""
# WEAVIATE_USE_EMBED=True|False
store = WeaviateMemoryStore()
store.client.schema.delete_all()
Then, we register the memory store to the kernel:
from services import Service
# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)
selectedService = Service.OpenAI
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, OpenAITextEmbedding
from semantic_kernel.core_plugins.text_memory_plugin import TextMemoryPlugin
from semantic_kernel.kernel import Kernel
from semantic_kernel.memory.semantic_text_memory import SemanticTextMemory
from semantic_kernel.memory.volatile_memory_store import VolatileMemoryStore
kernel = Kernel()
chat_service_id = "chat"
if selectedService == Service.OpenAI:
oai_chat_service = OpenAIChatCompletion(
service_id=chat_service_id,
ai_model_id="gpt-3.5-turbo",
)
embedding_gen = OpenAITextEmbedding(ai_model_id="text-embedding-ada-002")
kernel.add_service(oai_chat_service)
kernel.add_service(embedding_gen)
memory = SemanticTextMemory(storage=VolatileMemoryStore(), embeddings_generator=embedding_gen)
kernel.add_plugin(TextMemoryPlugin(memory), "TextMemoryPlugin")
Let's create some initial memories "About Me". We can add memories to our weaviate memory store by using save_information
collection_id = "generic"
async def populate_memory(memory: SemanticTextMemory) -> None:
# Add some documents to the semantic memory
await memory.save_information(collection=collection_id, id="info1", text="Your budget for 2024 is $100,000")
await memory.save_information(collection=collection_id, id="info2", text="Your savings from 2023 are $50,000")
await memory.save_information(collection=collection_id, id="info3", text="Your investments are $80,000")
await populate_memory(memory)
Searching is done through search
:
async def search_memory_examples(memory: SemanticTextMemory) -> None:
questions = ["What is my budget for 2024?", "What are my savings from 2023?", "What are my investments?"]
for question in questions:
print(f"Question: {question}")
result = await memory.search(collection_id, question)
print(f"Answer: {result[0].text}\n")
await search_memory_examples(memory)
Here's how to use the weaviate memory store in a chat application:
from semantic_kernel.functions.kernel_function import KernelFunction
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
async def setup_chat_with_memory(
kernel: Kernel,
service_id: str,
) -> KernelFunction:
prompt = """
ChatBot 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.
Information about me, from previous conversations:
- {{recall 'budget by year'}} What is my budget for 2024?
- {{recall 'savings from previous year'}} What are my savings from 2023?
- {{recall 'investments'}} What are my investments?
{{$request}}
""".strip()
prompt_template_config = PromptTemplateConfig(
template=prompt,
execution_settings={
service_id: kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
},
)
return kernel.add_function(
function_name="chat_with_memory",
plugin_name="TextMemoryPlugin",
prompt_template_config=prompt_template_config,
)
async def chat(kernel: Kernel, chat_func: KernelFunction) -> bool:
try:
user_input = input("User:> ")
except KeyboardInterrupt:
print("\n\nExiting chat...")
return False
except EOFError:
print("\n\nExiting chat...")
return False
if user_input == "exit":
print("\n\nExiting chat...")
return False
answer = await kernel.invoke(chat_func, request=user_input)
print(f"ChatBot:> {answer}")
return True
print("Populating memory...")
await populate_memory(memory)
print("Asking questions... (manually)")
await search_memory_examples(memory)
print("Setting up a chat (with memory!)")
chat_func = await setup_chat_with_memory(kernel, chat_service_id)
print("Begin chatting (type 'exit' to exit):\n")
print(
"Welcome to the chat bot!\
\n Type 'exit' to exit.\
\n Try asking a question about your finances (i.e. \"talk to me about my finances\")."
)
chatting = True
while chatting:
chatting = await chat(kernel, chat_func)
Create a dictionary to hold some files. The key is the hyperlink to the file and the value is the file's content:
github_files = {}
github_files["https://github.com/microsoft/semantic-kernel/blob/main/README.md"] = (
"README: Installation, getting started, and how to contribute"
)
github_files[
"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/02-running-prompts-from-file.ipynb"
] = "Jupyter notebook describing how to pass prompts from a file to a semantic plugin or function"
github_files["https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/00-getting-started.ipynb"] = (
"Jupyter notebook describing how to get started with the Semantic Kernel"
)
github_files["https://github.com/microsoft/semantic-kernel/tree/main/samples/plugins/ChatPlugin/ChatGPT"] = (
"Sample demonstrating how to create a chat plugin interfacing with ChatGPT"
)
github_files[
"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel/Memory/Volatile/VolatileMemoryStore.cs"
] = "C# class that defines a volatile embedding store"
Use save_reference
to save the file:
COLLECTION = "SKGitHub"
print("Adding some GitHub file URLs and their descriptions to a volatile Semantic Memory.")
for index, (entry, value) in enumerate(github_files.items()):
await memory.save_reference(
collection=COLLECTION,
description=value,
text=value,
external_id=entry,
external_source_name="GitHub",
)
print(" URL {} saved".format(index))
Use search
to ask a question:
ask = "I love Jupyter notebooks, how should I get started?"
print("===========================\n" + "Query: " + ask + "\n")
memories = await memory.search(COLLECTION, ask, limit=5, min_relevance_score=0.77)
for index, memory in enumerate(memories):
print(f"Result {index}:")
print(" URL: : " + memory.id)
print(" Title : " + memory.description)
print(" Relevance: " + str(memory.relevance))
print()