From 2bb1d256f3a5b882da8a488d728d78ae16443b51 Mon Sep 17 00:00:00 2001 From: Harrison Chase Date: Sun, 6 Aug 2023 15:25:12 -0700 Subject: [PATCH] add example of memory and returning retrieved docs (#8830) --- .../guides/expression_language/cookbook.ipynb | 197 +++++++++++++++--- 1 file changed, 173 insertions(+), 24 deletions(-) diff --git a/docs/extras/guides/expression_language/cookbook.ipynb b/docs/extras/guides/expression_language/cookbook.ipynb index 57c74ff31..a189926c3 100644 --- a/docs/extras/guides/expression_language/cookbook.ipynb +++ b/docs/extras/guides/expression_language/cookbook.ipynb @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 1, "id": "466b65b3", "metadata": {}, "outputs": [], @@ -202,7 +202,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "id": "f799664d", "metadata": {}, "outputs": [], @@ -347,7 +347,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "id": "5d3d8ffe", "metadata": {}, "outputs": [], @@ -368,7 +368,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 2, "id": "33be32af", "metadata": {}, "outputs": [], @@ -380,7 +380,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 3, "id": "df3f3fa2", "metadata": {}, "outputs": [], @@ -515,7 +515,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 4, "id": "3f30c348", "metadata": {}, "outputs": [], @@ -526,7 +526,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 5, "id": "64ab1dbf", "metadata": {}, "outputs": [], @@ -544,7 +544,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 6, "id": "7d628c97", "metadata": {}, "outputs": [], @@ -559,7 +559,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 7, "id": "f60a5d0f", "metadata": {}, "outputs": [], @@ -572,7 +572,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 8, "id": "7d007db6", "metadata": {}, "outputs": [], @@ -589,25 +589,29 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 16, "id": "5c32cc89", "metadata": {}, "outputs": [], "source": [ - "conversational_qa_chain = RunnableMap({\n", - " \"standalone_question\": {\n", - " \"question\": lambda x: x[\"question\"],\n", - " \"chat_history\": lambda x: _format_chat_history(x['chat_history'])\n", - " } | CONDENSE_QUESTION_PROMPT | ChatOpenAI(temperature=0) | StrOutputParser(),\n", - "}) | {\n", + "_inputs = RunnableMap(\n", + " {\n", + " \"standalone_question\": {\n", + " \"question\": lambda x: x[\"question\"],\n", + " \"chat_history\": lambda x: _format_chat_history(x['chat_history'])\n", + " } | CONDENSE_QUESTION_PROMPT | ChatOpenAI(temperature=0) | StrOutputParser(),\n", + " }\n", + ")\n", + "_context = {\n", " \"context\": itemgetter(\"standalone_question\") | retriever | _combine_documents,\n", " \"question\": lambda x: x[\"standalone_question\"]\n", - "} | ANSWER_PROMPT | ChatOpenAI()" + "}\n", + "conversational_qa_chain = _inputs | _context | ANSWER_PROMPT | ChatOpenAI()" ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 17, "id": "135c8205", "metadata": {}, "outputs": [ @@ -624,7 +628,7 @@ "AIMessage(content='Harrison was employed at Kensho.', additional_kwargs={}, example=False)" ] }, - "execution_count": 71, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -638,7 +642,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 15, "id": "424e7e7a", "metadata": {}, "outputs": [ @@ -655,7 +659,7 @@ "AIMessage(content='Harrison worked at Kensho.', additional_kwargs={}, example=False)" ] }, - "execution_count": 62, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -667,6 +671,149 @@ "})" ] }, + { + "cell_type": "markdown", + "id": "c5543183", + "metadata": {}, + "source": [ + "### With Memory and returning source documents\n", + "\n", + "This shows how to use memory with the above. For memory, we need to manage that outside at the memory. For returning the retrieved documents, we just need to pass them through all the way." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "e31dd17c", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.memory import ConversationBufferMemory" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "d4bffe94", + "metadata": {}, + "outputs": [], + "source": [ + "memory = ConversationBufferMemory(return_messages=True, output_key=\"answer\", input_key=\"question\")" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "733be985", + "metadata": {}, + "outputs": [], + "source": [ + "# First we add a step to load memory\n", + "# This needs to be a RunnableMap because its the first input\n", + "loaded_memory = RunnableMap(\n", + " {\n", + " \"question\": itemgetter(\"question\"),\n", + " \"memory\": memory.load_memory_variables,\n", + " }\n", + ")\n", + "# Next we add a step to expand memory into the variables\n", + "expanded_memory = {\n", + " \"question\": itemgetter(\"question\"),\n", + " \"chat_history\": lambda x: x[\"memory\"][\"history\"]\n", + "}\n", + "\n", + "# Now we calculate the standalone question\n", + "standalone_question = {\n", + " \"standalone_question\": {\n", + " \"question\": lambda x: x[\"question\"],\n", + " \"chat_history\": lambda x: _format_chat_history(x['chat_history'])\n", + " } | CONDENSE_QUESTION_PROMPT | ChatOpenAI(temperature=0) | StrOutputParser(),\n", + "}\n", + "# Now we retrieve the documents\n", + "retrieved_documents = {\n", + " \"docs\": itemgetter(\"standalone_question\") | retriever,\n", + " \"question\": lambda x: x[\"standalone_question\"]\n", + "}\n", + "# Now we construct the inputs for the final prompt\n", + "final_inputs = {\n", + " \"context\": lambda x: _combine_documents(x[\"docs\"]),\n", + " \"question\": itemgetter(\"question\")\n", + "}\n", + "# And finally, we do the part that returns the answers\n", + "answer = {\n", + " \"answer\": final_inputs | ANSWER_PROMPT | ChatOpenAI(),\n", + " \"docs\": itemgetter(\"docs\"),\n", + "}\n", + "# And now we put it all together!\n", + "final_chain = loaded_memory | expanded_memory | standalone_question | retrieved_documents | answer" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "806e390c", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Number of requested results 4 is greater than number of elements in index 1, updating n_results = 1\n" + ] + }, + { + "data": { + "text/plain": [ + "{'answer': AIMessage(content='Harrison was employed at Kensho.', additional_kwargs={}, example=False),\n", + " 'docs': [Document(page_content='harrison worked at kensho', metadata={})]}" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inputs = {\"question\": \"where did harrison work?\"}\n", + "result = final_chain.invoke(inputs)\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "977399fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Note that the memory does not save automatically\n", + "# This will be improved in the future\n", + "# For now you need to save it yourself\n", + "memory.save_context(inputs, {\"answer\": result[\"answer\"].content})" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "f94f7de4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'history': [HumanMessage(content='where did harrison work?', additional_kwargs={}, example=False),\n", + " AIMessage(content='Harrison was employed at Kensho.', additional_kwargs={}, example=False)]}" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory.load_memory_variables({})" + ] + }, { "cell_type": "markdown", "id": "0f2bf8d3", @@ -1397,7 +1544,9 @@ "id": "179d3c03", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "## Conversational Retrieval With Memory" + ] } ], "metadata": { @@ -1416,7 +1565,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.1" + "version": "3.10.1" } }, "nbformat": 4,