539 lines
19 KiB
Plaintext
539 lines
19 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Introduction\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"This notebook shows how to replace the `VolatileMemoryStore` memory storage used in a [previous notebook](./06-memory-and-embeddings.ipynb) with a `WeaviateMemoryStore`.\n",
|
||
"\n",
|
||
"`WeaviateMemoryStore` is an example of a persistent (i.e. long-term) memory store backed by the Weaviate vector database.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Configuring the Kernel\n",
|
||
"\n",
|
||
"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.\n",
|
||
"\n",
|
||
"**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.**\n",
|
||
"\n",
|
||
"#### Option 1: using OpenAI\n",
|
||
"\n",
|
||
"Add your [OpenAI Key](https://platform.openai.com/docs/overview) key to your `.env` file (org Id only if you have multiple orgs):\n",
|
||
"\n",
|
||
"```\n",
|
||
"GLOBAL_LLM_SERVICE=\"OpenAI\"\n",
|
||
"OPENAI_API_KEY=\"sk-...\"\n",
|
||
"OPENAI_ORG_ID=\"\"\n",
|
||
"OPENAI_CHAT_MODEL_ID=\"\"\n",
|
||
"OPENAI_TEXT_MODEL_ID=\"\"\n",
|
||
"OPENAI_EMBEDDING_MODEL_ID=\"\"\n",
|
||
"```\n",
|
||
"The names should match the names used in the `.env` file, as shown above.\n",
|
||
"\n",
|
||
"#### Option 2: using Azure OpenAI\n",
|
||
"\n",
|
||
"Add your [Azure Open AI Service key](https://learn.microsoft.com/azure/cognitive-services/openai/quickstart?pivots=programming-language-studio) settings to the `.env` file in the same folder:\n",
|
||
"\n",
|
||
"```\n",
|
||
"GLOBAL_LLM_SERVICE=\"AzureOpenAI\"\n",
|
||
"AZURE_OPENAI_API_KEY=\"...\"\n",
|
||
"AZURE_OPENAI_ENDPOINT=\"https://...\"\n",
|
||
"AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=\"...\"\n",
|
||
"AZURE_OPENAI_TEXT_DEPLOYMENT_NAME=\"...\"\n",
|
||
"AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME=\"...\"\n",
|
||
"AZURE_OPENAI_API_VERSION=\"...\"\n",
|
||
"```\n",
|
||
"The names should match the names used in the `.env` file, as shown above.\n",
|
||
"\n",
|
||
"For more advanced configuration, please follow the steps outlined in the [setup guide](./CONFIGURING_THE_KERNEL.md)."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# About Weaviate\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"[Weaviate](https://weaviate.io/) 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).\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"You can run Weaviate in 5 ways:\n",
|
||
"\n",
|
||
"- **SaaS** – with [Weaviate Cloud Services (WCS)](https://weaviate.io/pricing).\n",
|
||
"\n",
|
||
" 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.\n",
|
||
"\n",
|
||
" To set up a SaaS Weaviate instance with WCS:\n",
|
||
"\n",
|
||
" 1. Navigate to [Weaviate Cloud Console](https://console.weaviate.cloud/).\n",
|
||
" 2. Register or sign in to your WCS account.\n",
|
||
" 3. Create a new cluster with the following settings:\n",
|
||
" - `Subscription Tier` – Free sandbox for a free trial, or contact [hello@weaviate.io](mailto:hello@weaviate.io) for other options.\n",
|
||
" - `Cluster name` – a unique name for your cluster. The name will become part of the URL used to access this instance.\n",
|
||
" - `Enable Authentication?` – Enabled by default. This will generate a static API key that you can use to authenticate.\n",
|
||
" 4. Wait for a few minutes until your cluster is ready. You will see a green tick ✔️ when it's done. Copy your cluster URL.\n",
|
||
"\n",
|
||
"- **Hybrid SaaS**\n",
|
||
"\n",
|
||
" > 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.\n",
|
||
"\n",
|
||
" The Weaviate Hybrid SaaS is a custom solution. If you are interested in this option, please reach out to [hello@weaviate.io](mailto:hello@weaviate.io).\n",
|
||
"\n",
|
||
"- **Self-hosted** – with a Docker container\n",
|
||
"\n",
|
||
" To set up a Weaviate instance with Docker:\n",
|
||
"\n",
|
||
" 1. [Install Docker](https://docs.docker.com/engine/install/) on your local machine if it is not already installed.\n",
|
||
" 2. [Install the Docker Compose Plugin](https://docs.docker.com/compose/install/)\n",
|
||
" 3. Download a `docker-compose.yml` file with this `curl` command:\n",
|
||
"\n",
|
||
" ```\n",
|
||
" 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\"\n",
|
||
" ```\n",
|
||
"\n",
|
||
" Alternatively, you can use Weaviate's docker compose [configuration tool](https://weaviate.io/developers/weaviate/installation/docker-compose) to generate your own `docker-compose.yml` file.\n",
|
||
"\n",
|
||
" 4. Run `docker compose up -d` to spin up a Weaviate instance.\n",
|
||
"\n",
|
||
" > To shut it down, run `docker compose down`.\n",
|
||
"\n",
|
||
"- **Self-hosted** – with a Kubernetes cluster\n",
|
||
"\n",
|
||
" To configure a self-hosted instance with Kubernetes, follow Weaviate's [documentation](https://weaviate.io/developers/weaviate/installation/kubernetes).|\n",
|
||
"\n",
|
||
"- **Embedded** - start a weaviate instance right from your application code using the client library\n",
|
||
"\n",
|
||
" This code snippet shows how to instantiate an embedded weaviate instance and upload a document:\n",
|
||
"\n",
|
||
" ```python\n",
|
||
" import weaviate\n",
|
||
" from weaviate.embedded import EmbeddedOptions\n",
|
||
"\n",
|
||
" client = weaviate.Client(\n",
|
||
" embedded_options=EmbeddedOptions()\n",
|
||
" )\n",
|
||
"\n",
|
||
" data_obj = {\n",
|
||
" \"name\": \"Chardonnay\",\n",
|
||
" \"description\": \"Goes with fish\"\n",
|
||
" }\n",
|
||
"\n",
|
||
" client.data_object.create(data_obj, \"Wine\")\n",
|
||
" ```\n",
|
||
"\n",
|
||
" Refer to the [documentation](https://weaviate.io/developers/weaviate/installation/embedded) for more details about this deployment method.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Setup\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Note: if using a virtual environment, do not run this cell\n",
|
||
"%pip install -U semantic-kernel[weaviate]\n",
|
||
"from semantic_kernel import __version__\n",
|
||
"\n",
|
||
"__version__"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## OS-specific notes:\n",
|
||
"\n",
|
||
"- if you run into SSL errors when connecting to OpenAI on macOS, see this issue for a [potential solution](https://github.com/microsoft/semantic-kernel/issues/627#issuecomment-1580912248)\n",
|
||
"- on Windows, you may need to run Docker Desktop as administrator\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"First, we instantiate the Weaviate memory store. Uncomment ONE of the options below, depending on how you want to use Weaviate:\n",
|
||
"\n",
|
||
"- from a Docker instance\n",
|
||
"- from WCS\n",
|
||
"- directly from the client (embedded Weaviate), which works on Linux only at the moment\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from semantic_kernel.connectors.memory.weaviate import WeaviateMemoryStore\n",
|
||
"\n",
|
||
"# Note the Weaviate Config values need to be either configured as environment variables\n",
|
||
"# or in the .env file, as a back up. When creating the instance of the `weaviate_memory_store`\n",
|
||
"# pass in `env_file_path=<path_to_file>` to read the config values from the `.env` file, otherwise\n",
|
||
"# the values will be read from environment variables.\n",
|
||
"# Env variables or .env file config should look like:\n",
|
||
"# WEAVIATE_URL=\"http://localhost:8080\"\n",
|
||
"# WEAVIATE_API_KEY=\"\"\n",
|
||
"# WEAVIATE_USE_EMBED=True|False\n",
|
||
"\n",
|
||
"store = WeaviateMemoryStore()\n",
|
||
"store.client.schema.delete_all()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Then, we register the memory store to the kernel:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from services import Service\n",
|
||
"\n",
|
||
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
|
||
"selectedService = Service.OpenAI"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, OpenAITextEmbedding\n",
|
||
"from semantic_kernel.core_plugins.text_memory_plugin import TextMemoryPlugin\n",
|
||
"from semantic_kernel.kernel import Kernel\n",
|
||
"from semantic_kernel.memory.semantic_text_memory import SemanticTextMemory\n",
|
||
"from semantic_kernel.memory.volatile_memory_store import VolatileMemoryStore\n",
|
||
"\n",
|
||
"kernel = Kernel()\n",
|
||
"\n",
|
||
"chat_service_id = \"chat\"\n",
|
||
"if selectedService == Service.OpenAI:\n",
|
||
" oai_chat_service = OpenAIChatCompletion(\n",
|
||
" service_id=chat_service_id,\n",
|
||
" ai_model_id=\"gpt-3.5-turbo\",\n",
|
||
" )\n",
|
||
" embedding_gen = OpenAITextEmbedding(ai_model_id=\"text-embedding-ada-002\")\n",
|
||
" kernel.add_service(oai_chat_service)\n",
|
||
" kernel.add_service(embedding_gen)\n",
|
||
"\n",
|
||
"memory = SemanticTextMemory(storage=VolatileMemoryStore(), embeddings_generator=embedding_gen)\n",
|
||
"kernel.add_plugin(TextMemoryPlugin(memory), \"TextMemoryPlugin\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Manually adding memories\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Let's create some initial memories \"About Me\". We can add memories to our weaviate memory store by using `save_information`\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"collection_id = \"generic\"\n",
|
||
"\n",
|
||
"\n",
|
||
"async def populate_memory(memory: SemanticTextMemory) -> None:\n",
|
||
" # Add some documents to the semantic memory\n",
|
||
" await memory.save_information(collection=collection_id, id=\"info1\", text=\"Your budget for 2024 is $100,000\")\n",
|
||
" await memory.save_information(collection=collection_id, id=\"info2\", text=\"Your savings from 2023 are $50,000\")\n",
|
||
" await memory.save_information(collection=collection_id, id=\"info3\", text=\"Your investments are $80,000\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"await populate_memory(memory)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Searching is done through `search`:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"async def search_memory_examples(memory: SemanticTextMemory) -> None:\n",
|
||
" questions = [\"What is my budget for 2024?\", \"What are my savings from 2023?\", \"What are my investments?\"]\n",
|
||
"\n",
|
||
" for question in questions:\n",
|
||
" print(f\"Question: {question}\")\n",
|
||
" result = await memory.search(collection_id, question)\n",
|
||
" print(f\"Answer: {result[0].text}\\n\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"await search_memory_examples(memory)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Here's how to use the weaviate memory store in a chat application:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from semantic_kernel.functions.kernel_function import KernelFunction\n",
|
||
"from semantic_kernel.prompt_template import PromptTemplateConfig\n",
|
||
"\n",
|
||
"\n",
|
||
"async def setup_chat_with_memory(\n",
|
||
" kernel: Kernel,\n",
|
||
" service_id: str,\n",
|
||
") -> KernelFunction:\n",
|
||
" prompt = \"\"\"\n",
|
||
" ChatBot can have a conversation with you about any topic.\n",
|
||
" It can give explicit instructions or say 'I don't know' if\n",
|
||
" it does not have an answer.\n",
|
||
"\n",
|
||
" Information about me, from previous conversations:\n",
|
||
" - {{recall 'budget by year'}} What is my budget for 2024?\n",
|
||
" - {{recall 'savings from previous year'}} What are my savings from 2023?\n",
|
||
" - {{recall 'investments'}} What are my investments?\n",
|
||
"\n",
|
||
" {{$request}}\n",
|
||
" \"\"\".strip()\n",
|
||
"\n",
|
||
" prompt_template_config = PromptTemplateConfig(\n",
|
||
" template=prompt,\n",
|
||
" execution_settings={\n",
|
||
" service_id: kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)\n",
|
||
" },\n",
|
||
" )\n",
|
||
"\n",
|
||
" return kernel.add_function(\n",
|
||
" function_name=\"chat_with_memory\",\n",
|
||
" plugin_name=\"TextMemoryPlugin\",\n",
|
||
" prompt_template_config=prompt_template_config,\n",
|
||
" )"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"async def chat(kernel: Kernel, chat_func: KernelFunction) -> bool:\n",
|
||
" try:\n",
|
||
" user_input = input(\"User:> \")\n",
|
||
" except KeyboardInterrupt:\n",
|
||
" print(\"\\n\\nExiting chat...\")\n",
|
||
" return False\n",
|
||
" except EOFError:\n",
|
||
" print(\"\\n\\nExiting chat...\")\n",
|
||
" return False\n",
|
||
"\n",
|
||
" if user_input == \"exit\":\n",
|
||
" print(\"\\n\\nExiting chat...\")\n",
|
||
" return False\n",
|
||
"\n",
|
||
" answer = await kernel.invoke(chat_func, request=user_input)\n",
|
||
"\n",
|
||
" print(f\"ChatBot:> {answer}\")\n",
|
||
" return True"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"print(\"Populating memory...\")\n",
|
||
"await populate_memory(memory)\n",
|
||
"\n",
|
||
"print(\"Asking questions... (manually)\")\n",
|
||
"await search_memory_examples(memory)\n",
|
||
"\n",
|
||
"print(\"Setting up a chat (with memory!)\")\n",
|
||
"chat_func = await setup_chat_with_memory(kernel, chat_service_id)\n",
|
||
"\n",
|
||
"print(\"Begin chatting (type 'exit' to exit):\\n\")\n",
|
||
"print(\n",
|
||
" \"Welcome to the chat bot!\\\n",
|
||
" \\n Type 'exit' to exit.\\\n",
|
||
" \\n Try asking a question about your finances (i.e. \\\"talk to me about my finances\\\").\"\n",
|
||
")\n",
|
||
"chatting = True\n",
|
||
"while chatting:\n",
|
||
" chatting = await chat(kernel, chat_func)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Adding documents to your memory\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Create a dictionary to hold some files. The key is the hyperlink to the file and the value is the file's content:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"github_files = {}\n",
|
||
"github_files[\"https://github.com/microsoft/semantic-kernel/blob/main/README.md\"] = (\n",
|
||
" \"README: Installation, getting started, and how to contribute\"\n",
|
||
")\n",
|
||
"github_files[\n",
|
||
" \"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/02-running-prompts-from-file.ipynb\"\n",
|
||
"] = \"Jupyter notebook describing how to pass prompts from a file to a semantic plugin or function\"\n",
|
||
"github_files[\"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/00-getting-started.ipynb\"] = (\n",
|
||
" \"Jupyter notebook describing how to get started with the Semantic Kernel\"\n",
|
||
")\n",
|
||
"github_files[\"https://github.com/microsoft/semantic-kernel/tree/main/samples/plugins/ChatPlugin/ChatGPT\"] = (\n",
|
||
" \"Sample demonstrating how to create a chat plugin interfacing with ChatGPT\"\n",
|
||
")\n",
|
||
"github_files[\n",
|
||
" \"https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel/Memory/Volatile/VolatileMemoryStore.cs\"\n",
|
||
"] = \"C# class that defines a volatile embedding store\""
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Use `save_reference` to save the file:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"COLLECTION = \"SKGitHub\"\n",
|
||
"\n",
|
||
"print(\"Adding some GitHub file URLs and their descriptions to a volatile Semantic Memory.\")\n",
|
||
"for index, (entry, value) in enumerate(github_files.items()):\n",
|
||
" await memory.save_reference(\n",
|
||
" collection=COLLECTION,\n",
|
||
" description=value,\n",
|
||
" text=value,\n",
|
||
" external_id=entry,\n",
|
||
" external_source_name=\"GitHub\",\n",
|
||
" )\n",
|
||
" print(\" URL {} saved\".format(index))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Use `search` to ask a question:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"ask = \"I love Jupyter notebooks, how should I get started?\"\n",
|
||
"print(\"===========================\\n\" + \"Query: \" + ask + \"\\n\")\n",
|
||
"\n",
|
||
"memories = await memory.search(COLLECTION, ask, limit=5, min_relevance_score=0.77)\n",
|
||
"\n",
|
||
"for index, memory in enumerate(memories):\n",
|
||
" print(f\"Result {index}:\")\n",
|
||
" print(\" URL: : \" + memory.id)\n",
|
||
" print(\" Title : \" + memory.description)\n",
|
||
" print(\" Relevance: \" + str(memory.relevance))\n",
|
||
" print()"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Python 3 (ipykernel)",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.9.13"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 2
|
||
}
|