diff --git a/docs/modules/indexes/retrievers/examples/time_weighted_vectorstore.ipynb b/docs/modules/indexes/retrievers/examples/time_weighted_vectorstore.ipynb index c29773cc27e..9afbb4fb3e7 100644 --- a/docs/modules/indexes/retrievers/examples/time_weighted_vectorstore.ipynb +++ b/docs/modules/indexes/retrievers/examples/time_weighted_vectorstore.ipynb @@ -46,7 +46,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 2, "id": "c10e7696", "metadata": {}, "outputs": [], @@ -62,17 +62,17 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 3, "id": "86dbadb9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['e1990f14-c66c-4de1-961e-b35d251dbc24']" + "['73f8f585-9536-4240-aef7-cea205b336bd']" ] }, - "execution_count": 28, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -85,29 +85,24 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 4, "id": "a580be32", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.999943851187518\n", - "1.7783825087097196\n", - "0.9997499080672996\n", - "1.997134532390176\n" + "ename": "ValueError", + "evalue": "normalize_score_fn must be provided to FAISS constructor to normalize scores", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[4], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mretriever\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_relevant_documents\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mhello world\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/workplace/langchain/langchain/retrievers/time_weighted_retriever.py:94\u001b[0m, in \u001b[0;36mTimeWeightedVectorStoreRetriever.get_relevant_documents\u001b[0;34m(self, query)\u001b[0m\n\u001b[1;32m 89\u001b[0m docs_and_scores \u001b[38;5;241m=\u001b[39m {\n\u001b[1;32m 90\u001b[0m doc\u001b[38;5;241m.\u001b[39mmetadata[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbuffer_idx\u001b[39m\u001b[38;5;124m\"\u001b[39m]: (doc, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdefault_salience)\n\u001b[1;32m 91\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m doc \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmemory_stream[\u001b[38;5;241m-\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mk :]\n\u001b[1;32m 92\u001b[0m }\n\u001b[1;32m 93\u001b[0m \u001b[38;5;66;03m# If a doc is considered salient, update the salience score\u001b[39;00m\n\u001b[0;32m---> 94\u001b[0m docs_and_scores\u001b[38;5;241m.\u001b[39mupdate(\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_salient_docs\u001b[49m\u001b[43m(\u001b[49m\u001b[43mquery\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 95\u001b[0m rescored_docs \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 96\u001b[0m (doc, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_combined_score(doc, salience, current_time))\n\u001b[1;32m 97\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m doc, salience \u001b[38;5;129;01min\u001b[39;00m docs_and_scores\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[1;32m 98\u001b[0m ]\n\u001b[1;32m 99\u001b[0m rescored_docs\u001b[38;5;241m.\u001b[39msort(key\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mlambda\u001b[39;00m x: x[\u001b[38;5;241m1\u001b[39m], reverse\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n", + "File \u001b[0;32m~/workplace/langchain/langchain/retrievers/time_weighted_retriever.py:75\u001b[0m, in \u001b[0;36mTimeWeightedVectorStoreRetriever.get_salient_docs\u001b[0;34m(self, query)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Return documents that are salient to the query.\"\"\"\u001b[39;00m\n\u001b[1;32m 73\u001b[0m docs_and_scores: List[Tuple[Document, \u001b[38;5;28mfloat\u001b[39m]]\n\u001b[1;32m 74\u001b[0m docs_and_scores \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m---> 75\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvectorstore\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msimilarity_search_with_normalized_similarities\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 76\u001b[0m \u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msearch_kwargs\u001b[49m\n\u001b[1;32m 77\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 78\u001b[0m )\n\u001b[1;32m 79\u001b[0m results \u001b[38;5;241m=\u001b[39m {}\n\u001b[1;32m 80\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m fetched_doc, cosine_distance \u001b[38;5;129;01min\u001b[39;00m docs_and_scores:\n", + "File \u001b[0;32m~/workplace/langchain/langchain/vectorstores/base.py:94\u001b[0m, in \u001b[0;36mVectorStore.similarity_search_with_normalized_similarities\u001b[0;34m(self, query, k, **kwargs)\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msimilarity_search_with_normalized_similarities\u001b[39m(\n\u001b[1;32m 85\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 86\u001b[0m query: \u001b[38;5;28mstr\u001b[39m,\n\u001b[1;32m 87\u001b[0m k: \u001b[38;5;28mint\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m4\u001b[39m,\n\u001b[1;32m 88\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[1;32m 89\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m List[Tuple[Document, \u001b[38;5;28mfloat\u001b[39m]]:\n\u001b[1;32m 90\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Return docs and similarity scores, normalized on a scale from 0 to 1.\u001b[39;00m\n\u001b[1;32m 91\u001b[0m \n\u001b[1;32m 92\u001b[0m \u001b[38;5;124;03m 0 is dissimilar, 1 is most similar.\u001b[39;00m\n\u001b[1;32m 93\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m---> 94\u001b[0m docs_and_similarities \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_similarity_search_with_normalized_similarities\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 95\u001b[0m \u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\n\u001b[1;32m 96\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 97\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28many\u001b[39m(\n\u001b[1;32m 98\u001b[0m similarity \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m0.0\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m similarity \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1.0\u001b[39m\n\u001b[1;32m 99\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m _, similarity \u001b[38;5;129;01min\u001b[39;00m docs_and_similarities\n\u001b[1;32m 100\u001b[0m ):\n\u001b[1;32m 101\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 102\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNormalized similarity scores must be between\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 103\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m 0 and 1, got \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mdocs_and_similarities\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 104\u001b[0m )\n", + "File \u001b[0;32m~/workplace/langchain/langchain/vectorstores/faiss.py:435\u001b[0m, in \u001b[0;36mFAISS._similarity_search_with_normalized_similarities\u001b[0;34m(self, query, k, **kwargs)\u001b[0m\n\u001b[1;32m 433\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Return docs and their similarity scores on a scale from 0 to 1.\"\"\"\u001b[39;00m\n\u001b[1;32m 434\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mnormalize_score_fn \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 435\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 436\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnormalize_score_fn must be provided to\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 437\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m FAISS constructor to normalize scores\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 438\u001b[0m )\n\u001b[1;32m 439\u001b[0m docs_and_scores \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msimilarity_search_with_score(query, k\u001b[38;5;241m=\u001b[39mk)\n\u001b[1;32m 440\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [(doc, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mnormalize_score_fn(score)) \u001b[38;5;28;01mfor\u001b[39;00m doc, score \u001b[38;5;129;01min\u001b[39;00m docs_and_scores]\n", + "\u001b[0;31mValueError\u001b[0m: normalize_score_fn must be provided to FAISS constructor to normalize scores" ] - }, - { - "data": { - "text/plain": [ - "[Document(page_content='hello world', metadata={'last_accessed_at': datetime.datetime(2023, 4, 13, 23, 35, 40, 308626), 'created_at': datetime.datetime(2023, 4, 13, 23, 35, 38, 583497), 'buffer_idx': 0})]" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ diff --git a/docs/use_cases/agents/characters.ipynb b/docs/use_cases/agents/characters.ipynb index 2945dfee6a1..20876155739 100644 --- a/docs/use_cases/agents/characters.ipynb +++ b/docs/use_cases/agents/characters.ipynb @@ -188,8 +188,10 @@ " new_insights.extend(insights)\n", " return new_insights\n", " \n", - " def _score_memory_importance(self, memory_content: str) -> float:\n", + " def _score_memory_importance(self, memory_content: str, weight: float = 0.15) -> float:\n", " \"\"\"Score the absolute importance of the given memory.\"\"\"\n", + " # A weight of 0.25 makes this less important than it\n", + " # would be otherwise, relative to salience and time\n", " prompt = PromptTemplate.from_template(\n", " \"On the scale of 1 to 10, where 1 is purely mundane\"\n", " +\" (e.g., brushing teeth, making bed) and 10 is\"\n", @@ -203,7 +205,7 @@ " score = chain.run(memory_content=memory_content).strip()\n", " match = re.search(r\"^\\D*(\\d+)\", score)\n", " if match:\n", - " return float(score[0]) / 10\n", + " return (float(score[0]) / 10) * weight\n", " else:\n", " return 0.0\n", "\n", @@ -353,9 +355,10 @@ " self.add_memory(f\"{self.name} observed {observation} and reacted by {result}\")\n", " if \"REACT:\" in result:\n", " reaction = result.split(\"REACT:\")[-1].strip()\n", - " return False, reaction\n", + " return False, f\"{self.name} {reaction}\"\n", " if \"SAY:\" in result:\n", - " return True, result.split(\"SAY:\")[-1].strip()\n", + " said_value = result.split(\"SAY:\")[-1].strip()\n", + " return True, f\"{self.name} said {said_value}\"\n", " else:\n", " return False, result\n", "\n", @@ -369,11 +372,11 @@ " if \"GOODBYE:\" in result:\n", " farewell = result.split(\"GOODBYE:\")[-1].strip()\n", " self.add_memory(f\"{self.name} observed {observation} and said {farewell}\")\n", - " return False, farewell\n", + " return False, f\"{self.name} said {farewell}\"\n", " if \"SAY:\" in result:\n", " response_text = result.split(\"SAY:\")[-1].strip()\n", " self.add_memory(f\"{self.name} observed {observation} and said {response_text}\")\n", - " return True, response_text\n", + " return True, f\"{self.name} said {response_text}\"\n", " else:\n", " return False, result" ] @@ -381,7 +384,10 @@ { "cell_type": "markdown", "id": "361bd49e", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true, + "tags": [] + }, "source": [ "## Memory Lifecycle\n", "\n", @@ -406,24 +412,32 @@ "source": [ "## Create a Generative Character\n", "\n", + "\n", + "\n", "Now that we've walked through the definition, we will create two characters named \"Tommie\" and \"Eve\"." ] }, { "cell_type": "code", "execution_count": 5, - "id": "608c0272", + "id": "ee9c1a1d-c311-4f1c-8131-75fccd9025b1", "metadata": { "tags": [] }, "outputs": [], "source": [ + "import math\n", "import faiss\n", "\n", - "def interview_agent(agent: GenerativeAgent, message: str) -> str:\n", - " \"\"\"Help the notebook user interact with the agent.\"\"\"\n", - " new_message = f\"{USER_NAME} says {message}\"\n", - " return agent.generate_dialogue_response(new_message)[1]\n", + "def normalize_score_fn(score: float) -> float:\n", + " \"\"\"Return a similarity score on a scale [0, 1].\"\"\"\n", + " # This will differ depending on a few things:\n", + " # - the distance / similarity metric used by the VectorStore\n", + " # - the scale of your embeddings (OpenAI's are unit norm. Many others are not!)\n", + " # This function converts the euclidean norm of normalized embeddings\n", + " # (0 is most similar, sqrt(2) most dissimilar)\n", + " # to a similarity function (0 to 1)\n", + " return 1.0 - score / math.sqrt(2)\n", "\n", "def create_new_memory_retriever():\n", " \"\"\"Create a new vector store retriever unique to the agent.\"\"\"\n", @@ -432,8 +446,8 @@ " # Initialize the vectorstore as empty\n", " embedding_size = 1536\n", " index = faiss.IndexFlatL2(embedding_size)\n", - " vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})\n", - " return TimeWeightedVectorStoreRetriever(vectorstore=vectorstore, other_score_keys=[\"importance\"]) " + " vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}, normalize_score_fn=normalize_score_fn)\n", + " return TimeWeightedVectorStoreRetriever(vectorstore=vectorstore, other_score_keys=[\"importance\"], k=15) " ] }, { @@ -472,7 +486,7 @@ "text": [ "Name: Tommie (age: 25)\n", "Innate traits: anxious, likes design\n", - "Unfortunately, there are no statements provided to summarize Tommie's core characteristics.\n" + "No statements or information about Tommie have been provided. Therefore, it is not possible to summarize Tommie's core characteristics.\n" ] } ], @@ -519,7 +533,7 @@ "text": [ "Name: Tommie (age: 25)\n", "Innate traits: anxious, likes design\n", - "Tommie is a person who is currently on a road trip and experiencing various sensory stimuli such as noise from the road at night, memories of his childhood dog, observing a new home and neighbors with a cat. He is also experiencing physical sensations such as fatigue from driving and hunger. Tommie attempts to rest to alleviate his tiredness and hunger.\n" + "Tommie is observant, has a sentimental side, gets hungry and tired like any person, and is affected by his surroundings such as the noise level.\n" ] } ], @@ -543,6 +557,21 @@ { "cell_type": "code", "execution_count": 10, + "id": "eaf125d8-f54c-4c5f-b6af-32789b1f7d3a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def interview_agent(agent: GenerativeAgent, message: str) -> str:\n", + " \"\"\"Help the notebook user interact with the agent.\"\"\"\n", + " new_message = f\"{USER_NAME} says {message}\"\n", + " return agent.generate_dialogue_response(new_message)[1]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, "id": "54024d41-6e83-4914-91e5-73140e2dd9c8", "metadata": { "tags": [] @@ -551,10 +580,10 @@ { "data": { "text/plain": [ - "'\"I really enjoy design. I find it calming and a way to express my creativity. But right now, I\\'m mostly focused on finding a job to support myself. How about you, Person A? What do you like to do?\"'" + "'Tommie said \"I really enjoy design, specifically graphic design. I find it fulfilling to create something visually appealing while also communicating a message. How about you?\"'" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -565,7 +594,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "71e2e8cc-921e-4816-82f1-66962b2c1055", "metadata": { "tags": [] @@ -574,10 +603,10 @@ { "data": { "text/plain": [ - "'\"Well, since it\\'s already late at night, I\\'m mostly looking forward to getting some rest and starting fresh in the morning. How about you, Person A? What are you looking forward to doing tomorrow?\"'" + "'Tommie said \"I\\'m actually on the job hunt right now, so I\\'m hoping to find some promising leads today. How about you?\"'" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -588,7 +617,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "id": "a2521ffc-7050-4ac3-9a18-4cccfc798c31", "metadata": { "tags": [] @@ -597,10 +626,10 @@ { "data": { "text/plain": [ - "'\"Honestly, I\\'ve been feeling pretty anxious lately about finding a job and making ends meet. But I\\'m trying to stay positive and keep pushing forward. How about you, Person A? What worries you?\"'" + "'Tommie said \"Honestly, I\\'m feeling anxious about finding a job. It\\'s been a bit of a struggle lately. But I\\'m trying to stay positive and keep working at it. How about you, what are you most worried about today?\"'" ] }, - "execution_count": 12, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -619,7 +648,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "id": "154dee3d-bfe0-4828-b963-ed7e885799b3", "metadata": { "tags": [] @@ -656,20 +685,12 @@ " \"Tommie calls his best friend to vent about his struggles.\",\n", " \"Tommie's friend offers some words of encouragement and tells him to keep trying.\",\n", " \"Tommie feels slightly better after talking to his friend.\",\n", - " \"Tommie decides to go for a jog to clear his mind.\",\n", - " \"Tommie jogs through the city and sees some interesting sights.\",\n", - " \"Tommie stops to take a picture of a street mural.\",\n", - " \"Tommie runs into an old friend from college who now lives in the city.\",\n", - " \"They catch up for a few minutes, but Tommie's friend has to leave to attend a meeting.\",\n", - " \"Tommie thanks his friend and feels hopeful again.\",\n", - " \"Tommie heads back to his apartment to rest and prepare for his upcoming interviews.\",\n", - " \"Tommie spends the evening rehearsing his interview pitch.\"\n", "]\n" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "id": "238be49c-edb3-4e26-a2b6-98777ba8de86", "metadata": { "tags": [] @@ -679,49 +700,40 @@ "name": "stdout", "output_type": "stream", "text": [ - "\u001b[32mTommie wakes up to the sound of a noisy construction site outside his window.\u001b[0m Tommie covers his head with a pillow to block out the noise.\n", - "\u001b[32mTommie gets out of bed and heads to the kitchen to make himself some coffee.\u001b[0m Tommie starts brewing his coffee and enjoys the smell.\n", - "\u001b[32mTommie realizes he forgot to buy coffee filters and starts rummaging through his moving boxes to find some.\u001b[0m Tommie sighs in frustration and continues to search for the filters.\n", - "\u001b[32mTommie finally finds the filters and makes himself a cup of coffee.\u001b[0m Tommie takes a sip of his coffee and feels relieved.\n", - "\u001b[32mThe coffee tastes bitter, and Tommie regrets not buying a better brand.\u001b[0m Tommie grimaces at the bitter taste of the coffee and makes a mental note to buy a better brand next time.\n", - "\u001b[32mTommie checks his email and sees that he has no job offers yet.\u001b[0m Tommie feels disappointed and anxious.\n", - "\u001b[32mTommie spends some time updating his resume and cover letter.\u001b[0m Tommie feels determined to continue job searching and improving his resume and cover letter.\n", - "\u001b[32mTommie heads out to explore the city and look for job openings.\u001b[0m Tommie feels excited to explore the city and hopeful about finding job openings.\n", - "\u001b[32mTommie sees a sign for a job fair and decides to attend.\u001b[0m \"That job fair looks promising. I'll definitely check it out.\"\n", - "\u001b[32mThe line to get in is long, and Tommie has to wait for an hour.\u001b[0m Tommie feels impatient and frustrated by the long wait in line.\n", - "\u001b[32mTommie meets several potential employers at the job fair but doesn't receive any offers.\u001b[0m Tommie feels disappointed but determined to keep looking for job opportunities.\n", - "\u001b[32mTommie leaves the job fair feeling disappointed.\u001b[0m Tommie feels discouraged but still determined to keep searching for job opportunities.\n", - "\u001b[32mTommie stops by a local diner to grab some lunch.\u001b[0m \"Hi, can I get a menu please?\"\n", - "\u001b[32mThe service is slow, and Tommie has to wait for 30 minutes to get his food.\u001b[0m Tommie sighs in frustration and looks around for something to occupy his time while he waits.\n", - "\u001b[32mTommie overhears a conversation at the next table about a job opening.\u001b[0m \"Excuse me, I couldn't help but overhear about the job opening. Could you tell me more about it?\"\n", - "\u001b[32mTommie asks the diners about the job opening and gets some information about the company.\u001b[0m \"Thank you for the information. Do you happen to have any contact information for the company or know where I can apply?\"\n", - "\u001b[34mCharacter Tommie is reflecting\u001b[0m\n", - "\u001b[32mTommie decides to apply for the job and sends his resume and cover letter.\u001b[0m \"Thank you for the information. Do you happen to have any contact information for the company or know where I can apply?\"\n", - "\u001b[32mTommie continues his search for job openings and drops off his resume at several local businesses.\u001b[0m Tommie feels hopeful but also anxious about the possibility of not hearing back from any of the businesses.\n", - "\u001b[32mTommie takes a break from his job search to go for a walk in a nearby park.\u001b[0m Tommie feels a sense of calm and relaxation as he takes a break from his job search to enjoy nature.\n", - "\u001b[32mA dog approaches and licks Tommie's feet, and he pets it for a few minutes.\u001b[0m Tommie feels a brief moment of happiness and connection with the dog before continuing on with his day.\n", + "\u001b[32mTommie wakes up to the sound of a noisy construction site outside his window.\u001b[0m Tommie Tommie covers his head with a pillow to try to block out the noise.\n", + "\u001b[32mTommie gets out of bed and heads to the kitchen to make himself some coffee.\u001b[0m Tommie Tommie makes himself a cup of coffee.\n", + "\u001b[32mTommie realizes he forgot to buy coffee filters and starts rummaging through his moving boxes to find some.\u001b[0m Tommie Tommie sighs and continues to search through the boxes for coffee filters.\n", + "\u001b[32mTommie finally finds the filters and makes himself a cup of coffee.\u001b[0m Tommie Tommie takes a sip of his coffee and feels a sense of relief.\n", + "\u001b[32mThe coffee tastes bitter, and Tommie regrets not buying a better brand.\u001b[0m Tommie Tommie grimaces and adds sugar to his coffee to try and improve the taste.\n", + "\u001b[32mTommie checks his email and sees that he has no job offers yet.\u001b[0m Tommie Tommie sighs and feels discouraged.\n", + "\u001b[32mTommie spends some time updating his resume and cover letter.\u001b[0m Tommie Tommie nods and feels productive.\n", + "\u001b[32mTommie heads out to explore the city and look for job openings.\u001b[0m Tommie Tommie feels excited to explore the city and find job openings.\n", + "\u001b[32mTommie sees a sign for a job fair and decides to attend.\u001b[0m Tommie Tommie's eyes light up with hope and he feels a surge of optimism.\n", + "\u001b[32mThe line to get in is long, and Tommie has to wait for an hour.\u001b[0m Tommie Tommie taps his foot impatiently while waiting in line.\n", + "\u001b[32mTommie meets several potential employers at the job fair but doesn't receive any offers.\u001b[0m Tommie Tommie feels disappointed but tries to stay positive and continues to search for job openings.\n", + "\u001b[32mTommie leaves the job fair feeling disappointed.\u001b[0m Tommie Tommie feels discouraged but tries to stay optimistic and continues to search for job openings.\n", + "\u001b[32mTommie stops by a local diner to grab some lunch.\u001b[0m Tommie Tommie feels relieved to take a break and enjoy some food.\n", + "\u001b[32mThe service is slow, and Tommie has to wait for 30 minutes to get his food.\u001b[0m Tommie Tommie taps his foot impatiently while waiting for his food.\n", + "\u001b[32mTommie overhears a conversation at the next table about a job opening.\u001b[0m Tommie said \"Excuse me, I couldn't help but overhear about the job opening. Can you tell me more about it?\"\n", + "\u001b[32mTommie asks the diners about the job opening and gets some information about the company.\u001b[0m Tommie said \"Can you tell me more about it?\"\n", + "\u001b[32mTommie decides to apply for the job and sends his resume and cover letter.\u001b[0m Tommie said \"Thank you for the information. I will definitely apply for the job.\"\n", + "\u001b[32mTommie continues his search for job openings and drops off his resume at several local businesses.\u001b[0m Tommie Tommie feels hopeful about the job search and continues to actively search for opportunities.\n", + "\u001b[32mTommie takes a break from his job search to go for a walk in a nearby park.\u001b[0m Tommie Tommie takes a deep breath and enjoys the fresh air.\n", + "\u001b[32mA dog approaches and licks Tommie's feet, and he pets it for a few minutes.\u001b[0m Tommie Tommie smiles and pets the dog for a few minutes before continuing his walk.\n", "****************************************\n", "\u001b[34mAfter 20 observations, Tommie's summary is:\n", "Name: Tommie (age: 25)\n", "Innate traits: anxious, likes design\n", - "Tommie is a determined and persistent job seeker who experiences a range of emotions, including disappointment, anxiety, determination, and excitement. He is proactive in his search and seeks out opportunities to explore the city and meet potential employers. Despite facing setbacks and challenges, he remains hopeful and continues to update his resume and cover letter.\u001b[0m\n", + "Tommie is a determined and optimistic individual who is actively searching for job opportunities in the city. He feels excited and hopeful when he finds potential job openings, but also experiences disappointment and discouragement when his efforts do not yield immediate results. He tries to stay positive and continues to actively search for opportunities, while also taking breaks and enjoying simple pleasures like walking in the park and interacting with dogs. Tommie is interested in graphic design and values productive activities that contribute to his job search.\u001b[0m\n", "****************************************\n", - "\u001b[32mTommie sees a group of people playing frisbee and decides to join in.\u001b[0m \"Hi, can I play with you guys?\"\n", - "\u001b[32mTommie has fun playing frisbee but gets hit in the face with the frisbee and hurts his nose.\u001b[0m Tommie winces in pain and holds his nose, but continues to play through the discomfort.\n", - "\u001b[32mTommie goes back to his apartment to rest for a bit.\u001b[0m Tommie sighs and takes a deep breath, feeling tired from the day's events.\n", - "\u001b[32mA raccoon tore open the trash bag outside his apartment, and the garbage is all over the floor.\u001b[0m Tommie cleans up the garbage and secures the trash bag to prevent future incidents.\n", - "\u001b[32mTommie starts to feel frustrated with his job search.\u001b[0m Tommie takes a deep breath and reminds himself to stay positive and persistent in his job search.\n", - "\u001b[32mTommie calls his best friend to vent about his struggles.\u001b[0m \"Hey, thanks for listening. It feels good to talk about it and get some support.\"\n", - "\u001b[32mTommie's friend offers some words of encouragement and tells him to keep trying.\u001b[0m \"Thanks for the encouragement, I really appreciate it.\"\n", - "\u001b[32mTommie feels slightly better after talking to his friend.\u001b[0m Tommie nods and smiles, feeling grateful for his friend's support.\n", - "\u001b[32mTommie decides to go for a jog to clear his mind.\u001b[0m Tommie puts on his running shoes and heads out the door.\n", - "\u001b[32mTommie jogs through the city and sees some interesting sights.\u001b[0m Tommie feels a sense of wonder and appreciation for the beauty of the city.\n", - "\u001b[32mTommie stops to take a picture of a street mural.\u001b[0m Tommie feels inspired and appreciative of the creativity and beauty of the street mural.\n", - "\u001b[32mTommie runs into an old friend from college who now lives in the city.\u001b[0m \"Hey! It's great to see you! What have you been up to?\"\n", - "\u001b[32mThey catch up for a few minutes, but Tommie's friend has to leave to attend a meeting.\u001b[0m \"It was great seeing you, let's catch up again soon!\"\n", - "\u001b[32mTommie thanks his friend and feels hopeful again.\u001b[0m Tommie nods and smiles, feeling grateful for his friend's support.\n", - "\u001b[32mTommie heads back to his apartment to rest and prepare for his upcoming interviews.\u001b[0m Tommie takes a deep breath and feels a sense of relief knowing that he has upcoming interviews.\n", - "\u001b[32mTommie spends the evening rehearsing his interview pitch.\u001b[0m Tommie feels focused and determined to do well in his upcoming interviews.\n" + "\u001b[32mTommie sees a group of people playing frisbee and decides to join in.\u001b[0m Tommie Tommie watches the group play for a few minutes but decides not to join in and continues his walk.\n", + "\u001b[32mTommie has fun playing frisbee but gets hit in the face with the frisbee and hurts his nose.\u001b[0m Tommie Tommie touches his nose and winces in pain.\n", + "\u001b[32mTommie goes back to his apartment to rest for a bit.\u001b[0m Tommie Tommie sits down on his couch and takes a deep breath to rest his sore nose.\n", + "\u001b[32mA raccoon tore open the trash bag outside his apartment, and the garbage is all over the floor.\u001b[0m Tommie Tommie sighs and grabs some gloves and a trash bag to clean up the mess.\n", + "\u001b[32mTommie starts to feel frustrated with his job search.\u001b[0m Tommie Tommie takes a deep breath and tries to stay positive, reminding himself that the right opportunity will come along eventually.\n", + "\u001b[32mTommie calls his best friend to vent about his struggles.\u001b[0m Tommie said \"Hey, can we talk? I'm feeling frustrated with my job search and could use someone to vent to.\"\n", + "\u001b[32mTommie's friend offers some words of encouragement and tells him to keep trying.\u001b[0m Tommie said \"Thanks for the encouragement, it means a lot to me.\"\n", + "\u001b[32mTommie feels slightly better after talking to his friend.\u001b[0m Tommie said \"Thank you for being there for me, it really helps to have your support.\"\n" ] } ], @@ -746,7 +758,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "6336ab5d-3074-4831-951f-c9e2cba5dfb5", "metadata": { "tags": [] @@ -755,10 +767,10 @@ { "data": { "text/plain": [ - "'\"Well, it\\'s been a bit of a rollercoaster, to be honest. I\\'ve been job searching all day and it\\'s been a mix of excitement, disappointment, and anxiety. But I\\'m trying to stay positive and keep pushing forward.\"'" + "'Tommie said \"It\\'s been a bit of a mixed day. I went to a job fair and met some potential employers, but didn\\'t receive any offers. I also applied for a job that I heard about at a diner, so I\\'m hoping that turns into something. But overall, it\\'s been a bit frustrating. How about you?\"'" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -769,7 +781,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "809ac906-69b7-4326-99ec-af638d32bb20", "metadata": { "tags": [] @@ -778,10 +790,10 @@ { "data": { "text/plain": [ - "'\"I actually really like coffee, it helps me stay focused during my job search. How about you?\"'" + "'Tommie said \"I enjoy drinking coffee, but I recently bought a bitter brand and regretted it. I\\'ve been trying to improve the taste by adding sugar, but I also need to remember to buy coffee filters.\"'" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -792,7 +804,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "f733a431-19ea-421a-9101-ae2593a8c626", "metadata": { "tags": [] @@ -801,16 +813,16 @@ { "data": { "text/plain": [ - "'Tommie would say: \"I actually really like coffee, it helps me stay focused during my job search. How about you?\" '" + "'Tommie said \"Oh, I had a dog named Bruno when I was a kid. He was a loyal and loving companion. I haven\\'t had a pet since then, but I hope to adopt one in the future.\"'" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "interview_agent(tommie, \"What are your thoughts on coffee\")" + "interview_agent(tommie, \"Tell me about your childhood dog!\")" ] }, { @@ -825,7 +837,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "id": "ec8bbe18-a021-419c-bf1f-23d34732cd99", "metadata": { "tags": [] @@ -847,7 +859,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "1e2745f5-e0da-4abd-98b4-830802ce6698", "metadata": { "tags": [] @@ -870,7 +882,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "id": "de4726e3-4bb1-47da-8fd9-f317a036fe0f", "metadata": { "tags": [] @@ -882,7 +894,7 @@ "text": [ "Name: Eve (age: 34)\n", "Innate traits: curious, helpful\n", - "Eve is attentive to her colleagues, enjoys physical activity, helpful to coworkers, responsible in waking up to her alarm, and eats breakfast.\n" + "Eve is helpful towards her coworkers, enjoys playing tennis with friends, eats breakfast, is attentive to conversations around her, and is punctual in waking up for work.\n" ] } ], @@ -903,7 +915,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "id": "6cda916d-800c-47bc-a7f9-6a2f19187472", "metadata": { "tags": [] @@ -912,10 +924,10 @@ { "data": { "text/plain": [ - "'\"I\\'m feeling curious about what we have planned for today. How about you?\"'" + "'Eve said \"I\\'m feeling pretty good, thanks for asking! How about you?\"'" ] }, - "execution_count": 21, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -926,7 +938,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "id": "448ae644-0a66-4eb2-a03a-319f36948b37", "metadata": { "tags": [] @@ -935,10 +947,10 @@ { "data": { "text/plain": [ - "'\"I overheard someone mention that Tommie is hard to work with, but I don\\'t know much else about them. Why do you ask?\"'" + "'Eve said \"I overheard someone say that Tommie is hard to work with, but I don\\'t really know much beyond that. Have you worked with Tommie before?\"'" ] }, - "execution_count": 22, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -949,7 +961,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "id": "493fc5b8-8730-4ef8-9820-0f1769ce1691", "metadata": { "tags": [] @@ -958,10 +970,10 @@ { "data": { "text/plain": [ - "'\"That\\'s interesting, I didn\\'t know Tommie was looking for a job. What kind of job is he looking for? Maybe I can keep an eye out for any openings that might be a good fit for him.\"'" + "'Eve said \"That\\'s interesting, what kind of job is Tommie looking for?\"'" ] }, - "execution_count": 23, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -972,7 +984,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "id": "4b46452a-6c54-4db2-9d87-18597f70fec8", "metadata": { "tags": [] @@ -981,10 +993,10 @@ { "data": { "text/plain": [ - "'\"Sure, I\\'d be happy to talk to him and see if I can help in any way. Do you have any specific questions you\\'d like me to ask him?\"'" + "'Eve said \"Sure thing, I\\'ll do my best to make him feel comfortable and ask him some questions about his job search. Thanks for the heads up.\"'" ] }, - "execution_count": 24, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -1005,7 +1017,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "id": "042ea271-4bf1-4247-9082-239a6fea43b8", "metadata": { "tags": [] @@ -1015,14 +1027,14 @@ "def run_conversation(agents: List[GenerativeAgent], initial_observation: str, max_turns:int = 25) -> None:\n", " \"\"\"Runs a conversation between agents.\"\"\"\n", " _, observation = agents[1].generate_reaction(initial_observation)\n", - " print(f\"{agents[1].name}: {observation}\")\n", + " print(observation)\n", " turns = 0\n", " while True:\n", " break_dialogue = False\n", " for agent in agents:\n", - " stay_in_dialogue, reaction = agent.generate_dialogue_response(observation)\n", - " print(f\"{agent.name}: {reaction}\")\n", - " observation = f\"{agent.name} said {reaction}\"\n", + " stay_in_dialogue, observation = agent.generate_dialogue_response(observation)\n", + " print(observation)\n", + " # observation = f\"{agent.name} said {reaction}\"\n", " if not stay_in_dialogue:\n", " break_dialogue = True \n", " if break_dialogue:\n", @@ -1032,7 +1044,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "id": "d5462b14-218e-4d85-b035-df57ea8e0f80", "metadata": { "tags": [] @@ -1042,20 +1054,21 @@ "name": "stdout", "output_type": "stream", "text": [ - "Eve: \"Sure, I'd love to share my story with you and offer any advice I can. Where would you like me to start?\"\n", - "Tommie: \"I've had some ups and downs, but overall I'm feeling hopeful about my job search. I've been updating my resume and cover letter, and I have a few upcoming interviews that I'm excited about. How about you, how has your day been going?\"\n", - "Eve: \"That's great to hear, Tommie. I'm glad you're feeling hopeful and have some upcoming interviews. Is there anything specific you're looking for in a job? Maybe I can keep an eye out for any opportunities that might be a good fit for you.\"\n", - "\u001b[34mCharacter Tommie is reflecting\u001b[0m\n", - "Tommie: \"Thank you so much, Eve! I really appreciate your offer to keep an eye out for job opportunities. Right now, I'm looking for a job in the design field, preferably in a creative agency or startup. But I'm open to exploring different options as well.\"\n", - "Eve: \"That's great to know, Tommie. I'll definitely keep an eye out for any opportunities in the design field, whether it's in a creative agency or startup. And if I come across anything that might be a good fit, I'll let you know right away. Good luck with your upcoming interviews and let me know if there's anything else I can do to help!\"\n", - "Tommie: \"Thank you so much, Eve! I really appreciate your willingness to help me find job opportunities. Have a great day!\"\n", - "Eve: \"You're welcome, Tommie. It was great talking with you and I wish you the best of luck in your job search. Have a great day!\"\n" + "Eve said \"Of course, I'd be happy to share my experience with you. I started in the industry after completing my degree in business management. How about you, what inspired you to pursue this field?\"\n", + "Tommie said \"I've always been interested in design and the creative process. It's something that I've enjoyed doing since I was a kid, so I decided to pursue it in college. I'm still trying to find my footing in the industry, but it's been a challenging and rewarding experience so far.\"\n", + "Eve said \"That's great to hear that you've been pursuing your passion. It can definitely be challenging, but it sounds like you're on the right track. Have you had any particular projects or experiences that have stood out to you so far?\"\n", + "Tommie said \"Thank you, Eve. I've had some interesting projects, like designing a logo for a local business and creating graphics for a social media campaign. It's been a great learning experience so far.\"\n", + "Eve said \"Those sound like great experiences, Tommie. Have you been able to apply any of those skills to your current work? I'd love to hear more about what you're currently working on.\"\n", + "Tommie said \"Yes, I've been updating my resume and cover letter to showcase my design skills. Right now, I'm actively searching for job opportunities in the design industry. It's been a bit challenging, but I'm hopeful that something will come along soon.\"\n", + "Eve said \"That's great to hear that you're actively searching for job opportunities, Tommie. Have you considered any specific companies or positions that you're interested in? I might be able to offer some advice or suggestions.\"\n", + "Tommie said \"Thank you for your offer, Eve. I've been exploring different companies and positions in the design industry, but I'm open to any suggestions or advice you may have.\"\n", + "Eve said \"Well, it was great chatting with you, Tommie. Best of luck with your job search and feel free to reach out if you ever need any advice or support.\"\n" ] } ], "source": [ "agents = [tommie, eve]\n", - "run_conversation(agents, \"Hi, Eve. Thanks for agreeing to share your story with me and give me advice. Maybe we can start with how you got into the industry.\")" + "run_conversation(agents, \"Tommie said: Hi, Eve. Thanks for agreeing to share your story with me and give me advice. Maybe we can start with how you got into the industry.\")" ] }, { @@ -1072,7 +1085,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "id": "c4d252f3-fcc1-474c-846e-a7605a6b4ce7", "metadata": { "tags": [] @@ -1084,7 +1097,7 @@ "text": [ "Name: Tommie (age: 25)\n", "Innate traits: anxious, likes design\n", - "Tommie is a determined and persistent individual who seeks support from friends during his job search. He finds inspiration in the creativity and beauty around him, and experiences a range of emotions including disappointment, anxiety, determination, and excitement. He copes with setbacks by remaining hopeful and proactive in his search, exploring the city and seeking out potential employers. He values the support and understanding of his friends throughout the process.\n" + "Tommie is a determined and optimistic individual who is actively searching for job opportunities. He feels excited about exploring new places and meeting potential employers but also experiences disappointment and frustration when things don't go as planned. He seeks support from his friends and tries to stay positive even in challenging situations. Tommie has a passion for graphic design and finds fulfillment in creating visually appealing designs that convey a message.\n" ] } ], @@ -1095,7 +1108,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "id": "c04db9a4", "metadata": {}, "outputs": [ @@ -1105,19 +1118,18 @@ "text": [ "Name: Eve (age: 34)\n", "Innate traits: curious, helpful\n", - "Eve is a helpful and attentive person who is willing to offer assistance and keep an eye out for job opportunities. She is curious and interested in others' experiences and is a team player, willing to assist her colleagues with their tasks. She is also a tennis player and enjoys a bowl of porridge for breakfast. However, she is not immune to workplace gossip and occasionally overhears negative comments about others.\n" + "Eve is a helpful and friendly coworker who is attentive to her colleagues' needs and interests. She enjoys playing tennis and eating breakfast before work, and is always willing to engage in conversation and provide support to those around her. She is also curious and interested in learning about others' experiences and providing advice and suggestions when possible.\n" ] } ], "source": [ - "\n", "# We can see a current \"Summary\" of a character based on their own perception of self\n", "print(eve.get_summary(force_refresh=True))" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "id": "71762558-8fb6-44d7-8483-f5b47fb2a862", "metadata": { "tags": [] @@ -1126,10 +1138,10 @@ { "data": { "text/plain": [ - "'\"It was really helpful actually. Eve offered to keep an eye out for any job opportunities in the design field, which is exactly what I\\'m looking for. It\\'s great to have supportive friends like her.\"'" + "'Tommie said \"It was really helpful actually. Eve offered some great advice and suggestions for my job search. I\\'m feeling more optimistic about finding the right opportunity now.\"'" ] }, - "execution_count": 29, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -1140,7 +1152,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "id": "085af3d8-ac21-41ea-8f8b-055c56976a67", "metadata": { "tags": [] @@ -1149,10 +1161,10 @@ { "data": { "text/plain": [ - "'\"It was really nice! Tommie seems like a great person and I\\'m happy to help them with their job search. Is there anything else you wanted to know about our conversation?\"'" + "'Eve said \"It was really nice chatting with Tommie. He\\'s a talented designer and seems really passionate about his work. I offered some advice and suggestions for his job search, so hopefully that helps him out. How about you, Person A? How\\'s your day going?\"'" ] }, - "execution_count": 30, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -1163,7 +1175,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "id": "5b439f3c-7849-4432-a697-2bcc85b89dae", "metadata": { "tags": [] @@ -1172,10 +1184,10 @@ { "data": { "text/plain": [ - "'\"I think our conversation went well, but if there was one thing I wish I had said to Tommie, it would be to ask if they have any specific companies or agencies in mind that they would love to work for. That way, I can keep an even closer eye out for any job opportunities that might be a perfect fit for them. But overall, I think we covered a lot of ground and I\\'m excited to help Tommie in any way I can.\"'" + "'Eve said \"It sounds like you have a good grasp on what you\\'re looking for, Tommie. Keep up the great work and don\\'t hesitate to reach out if you need any more advice or support. Have a great day!\"'" ] }, - "execution_count": 31, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1186,11 +1198,26 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "52eb7532-e38f-4278-8dc4-5b95df6c8a17", - "metadata": {}, - "outputs": [], - "source": [] + "execution_count": 40, + "id": "526e8863-8b32-4216-8e61-2dfe82e3fb47", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Tommie said \"Oh, I bought a bitter brand and regretted it. I need to remember to buy filters next time. But overall, it\\'s been okay. How about you?\"'" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interview_agent(tommie, \"What happened with your coffee this morning?\")" + ] } ], "metadata": { diff --git a/langchain/retrievers/time_weighted_retriever.py b/langchain/retrievers/time_weighted_retriever.py index 5f98e79cead..d53189e6a4a 100644 --- a/langchain/retrievers/time_weighted_retriever.py +++ b/langchain/retrievers/time_weighted_retriever.py @@ -1,9 +1,9 @@ """Retriever that combines embedding similarity with recency in retrieving values.""" from copy import deepcopy from datetime import datetime -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field from langchain.schema import BaseRetriever, Document from langchain.vectorstores.faiss import FAISS @@ -11,7 +11,7 @@ from langchain.vectorstores.faiss import FAISS def _get_hours_passed(time: datetime, ref_time: datetime) -> float: """Get the hours passed between two datetime objects.""" - return (time - ref_time).total_seconds() / 60 + return (time - ref_time).total_seconds() / 3600 class TimeWeightedVectorStoreRetriever(BaseRetriever, BaseModel): @@ -30,7 +30,7 @@ class TimeWeightedVectorStoreRetriever(BaseRetriever, BaseModel): decay_factor: float = Field(default=0.99) """The exponential decay factor used as decay_factor ** (hrs_passed).""" - k: int = 15 + k: int = 4 """The maximum number of documents to retrieve in a given call.""" other_score_keys: List[str] = [] @@ -68,18 +68,13 @@ class TimeWeightedVectorStoreRetriever(BaseRetriever, BaseModel): print(score) return score - @property - def _similarity_search_with_score( - self, - ) -> Callable[[str], List[Tuple[Document, float]]]: - """Search the vector store for related docs and their similarities.""" - return self.vectorstore.similarity_search_with_score # type: ignore - def get_salient_docs(self, query: str) -> Dict[int, Tuple[Document, float]]: """Return documents that are salient to the query.""" docs_and_scores: List[Tuple[Document, float]] - docs_and_scores = self.vectorstore.similarity_search_with_score( - query, **self.search_kwargs + docs_and_scores = ( + self.vectorstore.similarity_search_with_normalized_similarities( + query, **self.search_kwargs + ) ) results = {} for fetched_doc, cosine_distance in docs_and_scores: @@ -106,8 +101,10 @@ class TimeWeightedVectorStoreRetriever(BaseRetriever, BaseModel): # Ensure frequently accessed memories aren't forgotten current_time = datetime.now() for doc, _ in rescored_docs[: self.k]: - doc.metadata["last_accessed_at"] = current_time - result.append(doc) + # TODO: Update vector store doc once `update` method is exposed. + buffered_doc = self.memory_stream[doc.metadata["buffer_idx"]] + buffered_doc.metadata["last_accessed_at"] = current_time + result.append(buffered_doc) return result async def aget_relevant_documents(self, query: str) -> List[Document]: diff --git a/langchain/vectorstores/base.py b/langchain/vectorstores/base.py index f995d3dda31..5f618dce0f0 100644 --- a/langchain/vectorstores/base.py +++ b/langchain/vectorstores/base.py @@ -4,7 +4,7 @@ from __future__ import annotations import asyncio from abc import ABC, abstractmethod from functools import partial -from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar +from typing import Any, Dict, Iterable, List, Optional, Tuple, Type, TypeVar from pydantic import BaseModel, Field, root_validator @@ -81,6 +81,41 @@ class VectorStore(ABC): ) -> List[Document]: """Return docs most similar to query.""" + def similarity_search_with_normalized_similarities( + self, + query: str, + k: int = 4, + **kwargs: Any, + ) -> List[Tuple[Document, float]]: + """Return docs and similarity scores, normalized on a scale from 0 to 1. + + 0 is dissimilar, 1 is most similar. + """ + docs_and_similarities = self._similarity_search_with_normalized_similarities( + query, k=k, **kwargs + ) + if any( + similarity < 0.0 or similarity > 1.0 + for _, similarity in docs_and_similarities + ): + raise ValueError( + "Normalized similarity scores must be between" + f" 0 and 1, got {docs_and_similarities}" + ) + return docs_and_similarities + + def _similarity_search_with_normalized_similarities( + self, + query: str, + k: int = 4, + **kwargs: Any, + ) -> List[Tuple[Document, float]]: + """Return docs and similarity scores, normalized on a scale from 0 to 1. + + 0 is dissimilar, 1 is most similar. + """ + raise NotImplementedError + async def asimilarity_search( self, query: str, k: int = 4, **kwargs: Any ) -> List[Document]: diff --git a/langchain/vectorstores/faiss.py b/langchain/vectorstores/faiss.py index 9b139807a51..3d76b377cb6 100644 --- a/langchain/vectorstores/faiss.py +++ b/langchain/vectorstores/faiss.py @@ -48,12 +48,14 @@ class FAISS(VectorStore): index: Any, docstore: Docstore, index_to_docstore_id: Dict[int, str], + normalize_score_fn: Optional[Callable[[float], float]] = None, ): """Initialize with necessary components.""" self.embedding_function = embedding_function self.index = index self.docstore = docstore self.index_to_docstore_id = index_to_docstore_id + self.normalize_score_fn = normalize_score_fn def __add( self, @@ -314,7 +316,7 @@ class FAISS(VectorStore): docstore = InMemoryDocstore( {index_to_id[i]: doc for i, doc in enumerate(documents)} ) - return cls(embedding.embed_query, index, docstore, index_to_id) + return cls(embedding.embed_query, index, docstore, index_to_id, **kwargs) @classmethod def from_texts( @@ -342,7 +344,13 @@ class FAISS(VectorStore): faiss = FAISS.from_texts(texts, embeddings) """ embeddings = embedding.embed_documents(texts) - return cls.__from(texts, embeddings, embedding, metadatas, **kwargs) + return cls.__from( + texts, + embeddings, + embedding, + metadatas, + **kwargs, + ) @classmethod def from_embeddings( @@ -371,7 +379,13 @@ class FAISS(VectorStore): """ texts = [t[0] for t in text_embeddings] embeddings = [t[1] for t in text_embeddings] - return cls.__from(texts, embeddings, embedding, metadatas, **kwargs) + return cls.__from( + texts, + embeddings, + embedding, + metadatas, + **kwargs, + ) def save_local(self, folder_path: str) -> None: """Save FAISS index, docstore, and index_to_docstore_id to disk. @@ -409,3 +423,18 @@ class FAISS(VectorStore): with open(path / "index.pkl", "rb") as f: docstore, index_to_docstore_id = pickle.load(f) return cls(embeddings.embed_query, index, docstore, index_to_docstore_id) + + def _similarity_search_with_normalized_similarities( + self, + query: str, + k: int = 4, + **kwargs: Any, + ) -> List[Tuple[Document, float]]: + """Return docs and their similarity scores on a scale from 0 to 1.""" + if self.normalize_score_fn is None: + raise ValueError( + "normalize_score_fn must be provided to" + " FAISS constructor to normalize scores" + ) + docs_and_scores = self.similarity_search_with_score(query, k=k) + return [(doc, self.normalize_score_fn(score)) for doc, score in docs_and_scores] diff --git a/tests/integration_tests/vectorstores/test_faiss.py b/tests/integration_tests/vectorstores/test_faiss.py index d1fc9c5e886..e5b27532e44 100644 --- a/tests/integration_tests/vectorstores/test_faiss.py +++ b/tests/integration_tests/vectorstores/test_faiss.py @@ -1,4 +1,5 @@ """Test FAISS functionality.""" +import math import tempfile import pytest @@ -109,3 +110,37 @@ def test_faiss_local_save_load() -> None: docsearch.save_local(temp_file.name) new_docsearch = FAISS.load_local(temp_file.name, FakeEmbeddings()) assert new_docsearch.index is not None + + +def test_faiss_similarity_search_with_normalized_similarities() -> None: + """Test the similarity search with normalized similarities.""" + texts = ["foo", "bar", "baz"] + docsearch = FAISS.from_texts( + texts, + FakeEmbeddings(), + normalize_score_fn=lambda score: 1.0 - score / math.sqrt(2), + ) + outputs = docsearch.similarity_search_with_normalized_similarities("foo", k=1) + output, score = outputs[0] + assert output == Document(page_content="foo") + assert score == 1.0 + + +def test_faiss_invalid_normalize_fn() -> None: + """Test the similarity search with normalized similarities.""" + texts = ["foo", "bar", "baz"] + docsearch = FAISS.from_texts( + texts, FakeEmbeddings(), normalize_score_fn=lambda _: 2.0 + ) + with pytest.raises( + ValueError, match="Normalized similarity scores must be between 0 and 1" + ): + docsearch.similarity_search_with_normalized_similarities("foo", k=1) + + +def test_missing_normalize_score_fn() -> None: + """Test doesn't perform similarity search without a normalize score function.""" + with pytest.raises(ValueError): + texts = ["foo", "bar", "baz"] + faiss_instance = FAISS.from_texts(texts, FakeEmbeddings()) + faiss_instance.similarity_search_with_normalized_similarities("foo", k=2) diff --git a/tests/unit_tests/retrievers/test_time_weighted_retriever.py b/tests/unit_tests/retrievers/test_time_weighted_retriever.py index f7b397a7fe4..a99ab9e17fd 100644 --- a/tests/unit_tests/retrievers/test_time_weighted_retriever.py +++ b/tests/unit_tests/retrievers/test_time_weighted_retriever.py @@ -1,19 +1,33 @@ """Tests for the time-weighted retriever class.""" -from typing import Any, Iterable, List, Optional, Type +from datetime import datetime +from typing import Any, Iterable, List, Optional, Tuple, Type import pytest -from pydantic import ValidationError from langchain.embeddings.base import Embeddings from langchain.retrievers.time_weighted_retriever import ( TimeWeightedVectorStoreRetriever, + _get_hours_passed, ) from langchain.schema import Document from langchain.vectorstores.base import VectorStore -class MockInvalidVectorStore(VectorStore): +def _get_example_memories(k: int = 4) -> List[Document]: + return [ + Document( + page_content="foo", + metadata={ + "buffer_idx": i, + "last_accessed_at": datetime(2023, 4, 14, 12, 0), + }, + ) + for i in range(k) + ] + + +class MockVectorStore(VectorStore): """Mock invalid vector store.""" def add_texts( @@ -32,7 +46,7 @@ class MockInvalidVectorStore(VectorStore): Returns: List of ids from adding the texts into the vectorstore. """ - return [] + return list(texts) async def aadd_texts( self, @@ -51,11 +65,11 @@ class MockInvalidVectorStore(VectorStore): @classmethod def from_documents( - cls: Type["MockInvalidVectorStore"], + cls: Type["MockVectorStore"], documents: List[Document], embedding: Embeddings, **kwargs: Any, - ) -> "MockInvalidVectorStore": + ) -> "MockVectorStore": """Return VectorStore initialized from documents and embeddings.""" texts = [d.page_content for d in documents] metadatas = [d.metadata for d in documents] @@ -63,20 +77,87 @@ class MockInvalidVectorStore(VectorStore): @classmethod def from_texts( - cls: Type["MockInvalidVectorStore"], + cls: Type["MockVectorStore"], texts: List[str], embedding: Embeddings, metadatas: Optional[List[dict]] = None, **kwargs: Any, - ) -> "MockInvalidVectorStore": + ) -> "MockVectorStore": """Return VectorStore initialized from texts and embeddings.""" return cls() + def _similarity_search_with_normalized_similarities( + self, + query: str, + k: int = 4, + **kwargs: Any, + ) -> List[Tuple[Document, float]]: + """Return docs and similarity scores, normalized on a scale from 0 to 1. -def test_time_weighted_retriever_on_invalid_vector_store() -> None: - vectorstore = MockInvalidVectorStore() - with pytest.raises( - ValidationError, - match="Required method 'similarity_search_with_score not implemented", - ): - TimeWeightedVectorStoreRetriever(vectorstore=vectorstore) + 0 is dissimilar, 1 is most similar. + """ + return [(doc, 0.5) for doc in _get_example_memories()] + + +@pytest.fixture +def time_weighted_retriever() -> TimeWeightedVectorStoreRetriever: + vectorstore = MockVectorStore() + return TimeWeightedVectorStoreRetriever( + vectorstore=vectorstore, memory_stream=_get_example_memories() + ) + + +def test__get_hours_passed() -> None: + time1 = datetime(2023, 4, 14, 14, 30) + time2 = datetime(2023, 4, 14, 12, 0) + expected_hours_passed = 2.5 + hours_passed = _get_hours_passed(time1, time2) + assert hours_passed == expected_hours_passed + + +def test_get_combined_score( + time_weighted_retriever: TimeWeightedVectorStoreRetriever, +) -> None: + document = Document( + page_content="Test document", + metadata={"last_accessed_at": datetime(2023, 4, 14, 12, 0)}, + ) + vector_salience = 0.7 + expected_hours_passed = 2.5 + current_time = datetime(2023, 4, 14, 14, 30) + combined_score = time_weighted_retriever._get_combined_score( + document, vector_salience, current_time + ) + expected_score = ( + time_weighted_retriever.decay_factor**expected_hours_passed + vector_salience + ) + assert combined_score == pytest.approx(expected_score) + + +def test_get_salient_docs( + time_weighted_retriever: TimeWeightedVectorStoreRetriever, +) -> None: + query = "Test query" + docs_and_scores = time_weighted_retriever.get_salient_docs(query) + assert isinstance(docs_and_scores, dict) + + +def test_get_relevant_documents( + time_weighted_retriever: TimeWeightedVectorStoreRetriever, +) -> None: + query = "Test query" + relevant_documents = time_weighted_retriever.get_relevant_documents(query) + assert isinstance(relevant_documents, list) + + +def test_add_documents( + time_weighted_retriever: TimeWeightedVectorStoreRetriever, +) -> None: + documents = [Document(page_content="test_add_documents document")] + added_documents = time_weighted_retriever.add_documents(documents) + assert isinstance(added_documents, list) + assert len(added_documents) == 1 + assert ( + time_weighted_retriever.memory_stream[-1].page_content + == documents[0].page_content + )