From 49e0c83126b9c77ed5d113f96ad12f944439b087 Mon Sep 17 00:00:00 2001 From: Bagatur <22008038+baskaryan@users.noreply.github.com> Date: Thu, 7 Sep 2023 14:56:38 -0700 Subject: [PATCH] Split LCEL cookbook (#10342) --- .../extras/expression_language/cookbook.ipynb | 1831 ----------------- .../cookbook/code_writing.ipynb | 119 ++ .../expression_language/cookbook/index.mdx | 11 + .../expression_language/cookbook/memory.ipynb | 180 ++ .../cookbook/moderation.ipynb | 133 ++ .../cookbook/multiple_chains.ipynb | 240 +++ .../cookbook/prompt_llm_parser.ipynb | 431 ++++ .../cookbook/retrieval.ipynb | 461 +++++ .../expression_language/cookbook/sql_db.ipynb | 227 ++ .../expression_language/cookbook/tools.ipynb | 122 ++ .../expression_language/how_to/_category_.yml | 2 + .../how_to/functions.ipynb | 158 ++ .../expression_language/interface.ipynb | 15 +- 13 files changed, 2096 insertions(+), 1834 deletions(-) delete mode 100644 docs/extras/expression_language/cookbook.ipynb create mode 100644 docs/extras/expression_language/cookbook/code_writing.ipynb create mode 100644 docs/extras/expression_language/cookbook/index.mdx create mode 100644 docs/extras/expression_language/cookbook/memory.ipynb create mode 100644 docs/extras/expression_language/cookbook/moderation.ipynb create mode 100644 docs/extras/expression_language/cookbook/multiple_chains.ipynb create mode 100644 docs/extras/expression_language/cookbook/prompt_llm_parser.ipynb create mode 100644 docs/extras/expression_language/cookbook/retrieval.ipynb create mode 100644 docs/extras/expression_language/cookbook/sql_db.ipynb create mode 100644 docs/extras/expression_language/cookbook/tools.ipynb create mode 100644 docs/extras/expression_language/how_to/_category_.yml create mode 100644 docs/extras/expression_language/how_to/functions.ipynb diff --git a/docs/extras/expression_language/cookbook.ipynb b/docs/extras/expression_language/cookbook.ipynb deleted file mode 100644 index c10d0a7672..0000000000 --- a/docs/extras/expression_language/cookbook.ipynb +++ /dev/null @@ -1,1831 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "9a9acd2e", - "metadata": {}, - "source": [ - "# Cookbook\n", - "\n", - "In this notebook we'll take a look at a few common types of sequences to create." - ] - }, - { - "cell_type": "markdown", - "id": "93aa2c87", - "metadata": {}, - "source": [ - "## PromptTemplate + LLM\n", - "\n", - "A PromptTemplate -> LLM is a core chain that is used in most other larger chains/systems." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "466b65b3", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.prompts import ChatPromptTemplate\n", - "from langchain.chat_models import ChatOpenAI" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "3c634ef0", - "metadata": {}, - "outputs": [], - "source": [ - "model = ChatOpenAI()" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "d1850a1f", - "metadata": {}, - "outputs": [], - "source": [ - "prompt = ChatPromptTemplate.from_template(\"tell me a joke about {foo}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "56d0669f", - "metadata": {}, - "outputs": [], - "source": [ - "chain = prompt | model" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "e3d0a6cd", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content=\"Why don't bears wear shoes?\\n\\nBecause they have bear feet!\", additional_kwargs={}, example=False)" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"foo\": \"bears\"})" - ] - }, - { - "cell_type": "markdown", - "id": "7eb9ef50", - "metadata": {}, - "source": [ - "Often times we want to attach kwargs to the model that's passed in. Here's a few examples of that:" - ] - }, - { - "cell_type": "markdown", - "id": "0b1d8f88", - "metadata": {}, - "source": [ - "### Attaching Stop Sequences" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "562a06bf", - "metadata": {}, - "outputs": [], - "source": [ - "chain = prompt | model.bind(stop=[\"\\n\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "43f5d04c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content=\"Why don't bears wear shoes?\", additional_kwargs={}, example=False)" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"foo\": \"bears\"})" - ] - }, - { - "cell_type": "markdown", - "id": "f3eaf88a", - "metadata": {}, - "source": [ - "### Attaching Function Call information" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "f94b71b2", - "metadata": {}, - "outputs": [], - "source": [ - "functions = [\n", - " {\n", - " \"name\": \"joke\",\n", - " \"description\": \"A joke\",\n", - " \"parameters\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"setup\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"The setup for the joke\"\n", - " },\n", - " \"punchline\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"The punchline for the joke\"\n", - " }\n", - " },\n", - " \"required\": [\"setup\", \"punchline\"]\n", - " }\n", - " }\n", - " ]\n", - "chain = prompt | model.bind(function_call= {\"name\": \"joke\"}, functions= functions)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "decf7710", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='', additional_kwargs={'function_call': {'name': 'joke', 'arguments': '{\\n \"setup\": \"Why don\\'t bears wear shoes?\",\\n \"punchline\": \"Because they have bear feet!\"\\n}'}}, example=False)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"foo\": \"bears\"}, config={})" - ] - }, - { - "cell_type": "markdown", - "id": "9098c5ed", - "metadata": {}, - "source": [ - "## PromptTemplate + LLM + OutputParser\n", - "\n", - "We can also add in an output parser to easily trasform the raw LLM/ChatModel output into a more workable format" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "f799664d", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.schema.output_parser import StrOutputParser" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "cc194c78", - "metadata": {}, - "outputs": [], - "source": [ - "chain = prompt | model | StrOutputParser()" - ] - }, - { - "cell_type": "markdown", - "id": "77acf448", - "metadata": {}, - "source": [ - "Notice that this now returns a string - a much more workable format for downstream tasks" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "e3d69a18", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"Sure, here's a bear joke for you:\\n\\nWhy don't bears like fast food?\\n\\nBecause they can't catch it!\"" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"foo\": \"bears\"})" - ] - }, - { - "cell_type": "markdown", - "id": "c01864e5", - "metadata": {}, - "source": [ - "### Functions Output Parser\n", - "\n", - "When you specify the function to return, you may just want to parse that directly" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "ad0dd88e", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser\n", - "chain = (\n", - " prompt \n", - " | model.bind(function_call= {\"name\": \"joke\"}, functions= functions) \n", - " | JsonOutputFunctionsParser()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "1e7aa8eb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'setup': \"Why don't bears wear shoes?\",\n", - " 'punchline': 'Because they have bear feet!'}" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"foo\": \"bears\"})" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "d4aa1a01", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.output_parsers.openai_functions import JsonKeyOutputFunctionsParser\n", - "chain = (\n", - " prompt \n", - " | model.bind(function_call= {\"name\": \"joke\"}, functions= functions) \n", - " | JsonKeyOutputFunctionsParser(key_name=\"setup\")\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "8b6df9ba", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"Why don't bears wear shoes?\"" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"foo\": \"bears\"})" - ] - }, - { - "cell_type": "markdown", - "id": "2ed58136", - "metadata": {}, - "source": [ - "## Passthroughs and itemgetter\n", - "\n", - "Often times when constructing a chain you may want to pass along original input variables to future steps in the chain. How exactly you do this depends on what exactly the input is:\n", - "\n", - "- If the original input was a string, then you likely just want to pass along the string. This can be done with `RunnablePassthrough`. For an example of this, see `LLMChain + Retriever`\n", - "- If the original input was a dictionary, then you likely want to pass along specific keys. This can be done with `itemgetter`. For an example of this see `Multiple LLM Chains`" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "5d3d8ffe", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.schema.runnable import RunnablePassthrough\n", - "from operator import itemgetter" - ] - }, - { - "cell_type": "markdown", - "id": "91c5ef3d", - "metadata": {}, - "source": [ - "## LLMChain + Retriever\n", - "\n", - "Let's now look at adding in a retrieval step, which adds up to a \"retrieval-augmented generation\" chain" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "33be32af", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.vectorstores import Chroma\n", - "from langchain.embeddings import OpenAIEmbeddings\n", - "from langchain.schema.runnable import RunnablePassthrough" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "df3f3fa2", - "metadata": {}, - "outputs": [], - "source": [ - "# Create the retriever\n", - "vectorstore = Chroma.from_texts([\"harrison worked at kensho\"], embedding=OpenAIEmbeddings())\n", - "retriever = vectorstore.as_retriever()" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "bfc47ec1", - "metadata": {}, - "outputs": [], - "source": [ - "template = \"\"\"Answer the question based only on the following context:\n", - "{context}\n", - "\n", - "Question: {question}\n", - "\"\"\"\n", - "prompt = ChatPromptTemplate.from_template(template)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "eae31755", - "metadata": {}, - "outputs": [], - "source": [ - "chain = (\n", - " {\"context\": retriever, \"question\": RunnablePassthrough()} \n", - " | prompt \n", - " | model \n", - " | StrOutputParser()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "f3040b0c", - "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": [ - "'Harrison worked at Kensho.'" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke(\"where did harrison work?\")" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "e1d20c7c", - "metadata": {}, - "outputs": [], - "source": [ - "template = \"\"\"Answer the question based only on the following context:\n", - "{context}\n", - "\n", - "Question: {question}\n", - "\n", - "Answer in the following language: {language}\n", - "\"\"\"\n", - "prompt = ChatPromptTemplate.from_template(template)\n", - "\n", - "chain = {\n", - " \"context\": itemgetter(\"question\") | retriever, \n", - " \"question\": itemgetter(\"question\"), \n", - " \"language\": itemgetter(\"language\")\n", - "} | prompt | model | StrOutputParser()" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "7ee8b2d4", - "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": [ - "'Harrison ha lavorato a Kensho.'" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"question\": \"where did harrison work\", \"language\": \"italian\"})" - ] - }, - { - "cell_type": "markdown", - "id": "f007669c", - "metadata": {}, - "source": [ - "## Conversational Retrieval Chain\n", - "\n", - "We can easily add in conversation history. This primarily means adding in chat_message_history" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "3f30c348", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.schema.runnable import RunnableMap\n", - "from langchain.schema import format_document" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "64ab1dbf", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.prompts.prompt import PromptTemplate\n", - "\n", - "_template = \"\"\"Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question, in its original language.\n", - "\n", - "Chat History:\n", - "{chat_history}\n", - "Follow Up Input: {question}\n", - "Standalone question:\"\"\"\n", - "CONDENSE_QUESTION_PROMPT = PromptTemplate.from_template(_template)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "7d628c97", - "metadata": {}, - "outputs": [], - "source": [ - "template = \"\"\"Answer the question based only on the following context:\n", - "{context}\n", - "\n", - "Question: {question}\n", - "\"\"\"\n", - "ANSWER_PROMPT = ChatPromptTemplate.from_template(template)" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "f60a5d0f", - "metadata": {}, - "outputs": [], - "source": [ - "DEFAULT_DOCUMENT_PROMPT = PromptTemplate.from_template(template=\"{page_content}\")\n", - "def _combine_documents(docs, document_prompt = DEFAULT_DOCUMENT_PROMPT, document_separator=\"\\n\\n\"):\n", - " doc_strings = [format_document(doc, document_prompt) for doc in docs]\n", - " return document_separator.join(doc_strings)" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "id": "7d007db6", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import Tuple, List\n", - "def _format_chat_history(chat_history: List[Tuple]) -> str:\n", - " buffer = \"\"\n", - " for dialogue_turn in chat_history:\n", - " human = \"Human: \" + dialogue_turn[0]\n", - " ai = \"Assistant: \" + dialogue_turn[1]\n", - " buffer += \"\\n\" + \"\\n\".join([human, ai])\n", - " return buffer" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "5c32cc89", - "metadata": {}, - "outputs": [], - "source": [ - "_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", - "}\n", - "conversational_qa_chain = _inputs | _context | ANSWER_PROMPT | ChatOpenAI()" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "id": "135c8205", - "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": [ - "AIMessage(content='Harrison was employed at Kensho.', additional_kwargs={}, example=False)" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "conversational_qa_chain.invoke({\n", - " \"question\": \"where did harrison work?\",\n", - " \"chat_history\": [],\n", - "})" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "424e7e7a", - "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": [ - "AIMessage(content='Harrison worked at Kensho.', additional_kwargs={}, example=False)" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "conversational_qa_chain.invoke({\n", - " \"question\": \"where did he work?\",\n", - " \"chat_history\": [(\"Who wrote this notebook?\", \"Harrison\")],\n", - "})" - ] - }, - { - "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": 33, - "id": "e31dd17c", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.memory import ConversationBufferMemory" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "d4bffe94", - "metadata": {}, - "outputs": [], - "source": [ - "memory = ConversationBufferMemory(return_messages=True, output_key=\"answer\", input_key=\"question\")" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "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": 36, - "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": 36, - "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": 37, - "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": 38, - "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": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "memory.load_memory_variables({})" - ] - }, - { - "cell_type": "markdown", - "id": "0f2bf8d3", - "metadata": {}, - "source": [ - "## Multiple LLM Chains\n", - "\n", - "This can also be used to string together multiple LLMChains" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "id": "d65d4e9e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'El país en el que se encuentra la ciudad de Honolulu, Hawái, donde nació Barack Obama, el 44º presidente de los Estados Unidos, es Estados Unidos.'" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from operator import itemgetter\n", - "\n", - "prompt1 = ChatPromptTemplate.from_template(\"what is the city {person} is from?\")\n", - "prompt2 = ChatPromptTemplate.from_template(\"what country is the city {city} in? respond in {language}\")\n", - "\n", - "chain1 = prompt1 | model | StrOutputParser()\n", - "\n", - "chain2 = {\"city\": chain1, \"language\": itemgetter(\"language\")} | prompt2 | model | StrOutputParser()\n", - "\n", - "chain2.invoke({\"person\": \"obama\", \"language\": \"spanish\"})" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "878f8176", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.schema.runnable import RunnableMap\n", - "prompt1 = ChatPromptTemplate.from_template(\"generate a random color\")\n", - "prompt2 = ChatPromptTemplate.from_template(\"what is a fruit of color: {color}\")\n", - "prompt3 = ChatPromptTemplate.from_template(\"what is countries flag that has the color: {color}\")\n", - "prompt4 = ChatPromptTemplate.from_template(\"What is the color of {fruit} and {country}\")\n", - "chain1 = prompt1 | model | StrOutputParser()\n", - "chain2 = RunnableMap(steps={\"color\": chain1}) | {\n", - " \"fruit\": prompt2 | model | StrOutputParser(),\n", - " \"country\": prompt3 | model | StrOutputParser(),\n", - "} | prompt4" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "d621a870", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ChatPromptValue(messages=[HumanMessage(content=\"What is the color of A fruit that is of color #FF4500 is typically an orange fruit. and The country's flag that has the color #FF4500 is the flag of India.\", additional_kwargs={}, example=False)])" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain2.invoke({})" - ] - }, - { - "cell_type": "markdown", - "id": "6d75a313-f1c8-4e94-9a17-24e0bf4a2bdc", - "metadata": {}, - "source": [ - "### Branching and Merging\n", - "\n", - "You may want the output of one component to be processed by 2 or more other components. [RunnableMaps](https://api.python.langchain.com/en/latest/schema/langchain.schema.runnable.base.RunnableMap.html) let you split or fork the chain so multiple components can process the input in parallel. Later, other components can join or merge the results to synthesize a final response. This type of chain creates a computation graph that looks like the following:\n", - "\n", - "```text\n", - " Input\n", - " / \\\n", - " / \\\n", - " Branch1 Branch2\n", - " \\ /\n", - " \\ /\n", - " Combine\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "id": "247fa0bd-4596-4063-8cb3-1d7fc119d982", - "metadata": {}, - "outputs": [], - "source": [ - "planner = (\n", - " ChatPromptTemplate.from_template(\n", - " \"Generate an argument about: {input}\"\n", - " )\n", - " | ChatOpenAI()\n", - " | StrOutputParser()\n", - " | {\"base_response\": RunnablePassthrough()}\n", - ")\n", - "\n", - "arguments_for = (\n", - " ChatPromptTemplate.from_template(\n", - " \"List the pros or positive aspects of {base_response}\"\n", - " )\n", - " | ChatOpenAI()\n", - " | StrOutputParser()\n", - ")\n", - "arguments_against = (\n", - " ChatPromptTemplate.from_template(\n", - " \"List the cons or negative aspects of {base_response}\"\n", - " )\n", - " | ChatOpenAI()\n", - " | StrOutputParser()\n", - ")\n", - "\n", - "final_responder = (\n", - " ChatPromptTemplate.from_messages(\n", - " [\n", - " (\"ai\", \"{original_response}\"),\n", - " (\"human\", \"Pros:\\n{results_1}\\n\\nCons:\\n{results_2}\"),\n", - " (\"system\", \"Generate a final response given the critique\"),\n", - " ]\n", - " )\n", - " | ChatOpenAI()\n", - " | StrOutputParser()\n", - ")\n", - "\n", - "chain = (\n", - " planner \n", - " | {\n", - " \"results_1\": arguments_for,\n", - " \"results_2\": arguments_against,\n", - " \"original_response\": itemgetter(\"base_response\"),\n", - " }\n", - " | final_responder\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "id": "2564f310-0674-4bb1-9c4e-d7848ca73511", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"While Scrum has its limitations and potential drawbacks, it is important to note that these can be mitigated with proper understanding, implementation, and adaptation. Here are some ways to address the critique:\\n\\n1. Lack of structure: While Scrum promotes self-organization, it is essential to provide clear guidelines, define roles and responsibilities, and establish a shared understanding of the project's goals and expectations. This can be achieved through effective communication and regular alignment meetings.\\n\\n2. Time and resource constraints: Proper planning, prioritization, and resource allocation are crucial in managing the sprint cycles effectively. Teams can leverage tools and techniques such as backlog refinement, sprint planning, and capacity planning to ensure that workloads are manageable and realistic.\\n\\n3. Managing large teams: Scaling frameworks like Scrum of Scrums or LeSS (Large-Scale Scrum) can be implemented to coordinate the efforts of multiple Scrum teams. These frameworks provide mechanisms for communication, synchronization, and alignment across teams.\\n\\n4. Limited documentation: Although Scrum emphasizes working software over comprehensive documentation, it is important to strike a balance. Teams can adopt lightweight documentation practices such as user stories, acceptance criteria, and sprint reviews to capture relevant information and promote knowledge transfer.\\n\\n5. Resolving conflicts and fostering collaboration: Conflict resolution techniques and team-building activities can help address conflicts and foster a collaborative environment. Encouraging open and honest communication, promoting a culture of trust and respect, and providing opportunities for team members to share ideas and perspectives can contribute to better team dynamics.\\n\\n6. Long-term planning: While Scrum focuses on short-term goals, it is still important to have a long-term vision and roadmap. Teams can incorporate longer-term planning activities, such as release planning or product roadmapping, to align the project with broader strategic objectives and ensure a balance between adaptability and long-term goals.\\n\\n7. Skilled Scrum Master: Investing in the training and development of a skilled Scrum Master is crucial. Organizations can provide training and support for Scrum Masters to enhance their understanding of Scrum principles, facilitation skills, and ability to address challenges effectively.\\n\\n8. Scope management: To prevent scope creep, teams should establish a well-defined product backlog and prioritize requirements based on value and feasibility. Regular backlog refinement and stakeholder engagement can help ensure that changes are evaluated and incorporated in a controlled manner.\\n\\n9. Applicability to different domains: While Scrum originated in software development, it has been successfully applied in various industries and domains. Organizations can tailor Scrum practices to suit their specific needs, making necessary adaptations and incorporating domain-specific practices as required.\\n\\nBy addressing these concerns and adapting Scrum to the specific context, organizations can maximize the benefits of Scrum while mitigating potential drawbacks. It is important to continuously evaluate and improve the implementation to ensure the best outcomes for the project and the team.\"" - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"input\": \"scrum\"})" - ] - }, - { - "cell_type": "markdown", - "id": "d094d637", - "metadata": {}, - "source": [ - "## Router\n", - "\n", - "You can also use the router runnable to conditionally route inputs to different runnables." - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "id": "252625fd", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.chains import create_tagging_chain_pydantic\n", - "from pydantic import BaseModel, Field\n", - "\n", - "class PromptToUse(BaseModel):\n", - " \"\"\"Used to determine which prompt to use to answer the user's input.\"\"\"\n", - " \n", - " name: str = Field(description=\"Should be one of `math` or `english`\")" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "id": "57886e84", - "metadata": {}, - "outputs": [], - "source": [ - "tagger = create_tagging_chain_pydantic(PromptToUse, ChatOpenAI(temperature=0))" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "id": "a303b089", - "metadata": {}, - "outputs": [], - "source": [ - "chain1 = ChatPromptTemplate.from_template(\"You are a math genius. Answer the question: {question}\") | ChatOpenAI()\n", - "chain2 = ChatPromptTemplate.from_template(\"You are an english major. Answer the question: {question}\") | ChatOpenAI()" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "id": "7aa9ea06", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.schema.runnable import RouterRunnable\n", - "router = RouterRunnable({\"math\": chain1, \"english\": chain2})" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "id": "6a3d3f5d", - "metadata": {}, - "outputs": [], - "source": [ - "chain = {\n", - " \"key\": {\"input\": lambda x: x[\"question\"]} | tagger | (lambda x: x['text'].name),\n", - " \"input\": {\"question\": lambda x: x[\"question\"]}\n", - "} | router" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "id": "8aeda930", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='Thank you for the compliment! The sum of 2 and 2 is 4.', additional_kwargs={}, example=False)" - ] - }, - "execution_count": 71, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"question\": \"whats 2 + 2\"})" - ] - }, - { - "cell_type": "markdown", - "id": "29781123", - "metadata": {}, - "source": [ - "## Tools\n", - "\n", - "You can use any LangChain tool easily." - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "id": "9232d2a9", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.tools import DuckDuckGoSearchRun" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "id": "a0c64d2c", - "metadata": {}, - "outputs": [], - "source": [ - "search = DuckDuckGoSearchRun()" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "id": "391969b6", - "metadata": {}, - "outputs": [], - "source": [ - "template = \"\"\"turn the following user input into a search query for a search engine:\n", - "\n", - "{input}\"\"\"\n", - "prompt = ChatPromptTemplate.from_template(template)" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "id": "e3d9d20d", - "metadata": {}, - "outputs": [], - "source": [ - "chain = prompt | model | StrOutputParser() | search" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "id": "55f2967d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"What sports games are on TV today & tonight? Watch and stream live sports on TV today, tonight, tomorrow. Today's 2023 sports TV schedule includes football, basketball, baseball, hockey, motorsports, soccer and more. Watch on TV or stream online on ESPN, FOX, FS1, CBS, NBC, ABC, Peacock, Paramount+, fuboTV, local channels and many other networks. MLB Games Tonight: How to Watch on TV, Streaming & Odds - Wednesday, September 6. Texas Rangers second baseman Marcus Semien, left, tags out Houston Astros' Jose Altuve (27) who was attempting to stretch out a single in the seventh inning of a baseball game, Monday, Sept. 4, 2023, in Arlington, Texas. (AP Photo/Tony Gutierrez) (APMedia) There ... MLB Games Tonight: How to Watch on TV, Streaming & Odds - Sunday, September 3. Los Angeles Dodgers right fielder Mookie Betts, left, gives a thumbs up to Vanessa Bryant, right, widow of Kobe ... WEEK 16 NFL TV SCHEDULE. NFL Games Thursday, 12/21/23. TIME ET. TV. New Orleans at LA Rams. 8:15pm. AMZN. NFL Games Saturday, 12/23/23. TIME ET. The second half of tonight's college football schedule still has some good games remaining to watch on your television.. We've already seen an exciting one when Colorado upset TCU. And we saw some ...\"" - ] - }, - "execution_count": 76, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"input\": \"I'd like to figure out what games are tonight\"})" - ] - }, - { - "cell_type": "markdown", - "id": "fbc4bf6e", - "metadata": {}, - "source": [ - "## Arbitrary Functions\n", - "\n", - "You can use arbitrary functions in the pipeline\n", - "\n", - "Note that all inputs to these functions need to be a SINGLE argument. If you have a function that accepts multiple arguments, you should write a wrapper that accepts a single input and unpacks it into multiple argument." - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "id": "6bb221b3", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.schema.runnable import RunnableLambda\n", - "\n", - "def length_function(text):\n", - " return len(text)\n", - "\n", - "def _multiple_length_function(text1, text2):\n", - " return len(text1) * len(text2)\n", - "\n", - "def multiple_length_function(_dict):\n", - " return _multiple_length_function(_dict[\"text1\"], _dict[\"text2\"])\n", - "\n", - "prompt = ChatPromptTemplate.from_template(\"what is {a} + {b}\")\n", - "\n", - "chain1 = prompt | model\n", - "\n", - "chain = {\n", - " \"a\": itemgetter(\"foo\") | RunnableLambda(length_function),\n", - " \"b\": {\"text1\": itemgetter(\"foo\"), \"text2\": itemgetter(\"bar\")} | RunnableLambda(multiple_length_function)\n", - "} | prompt | model" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "id": "5488ec85", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='3 + 9 equals 12.', additional_kwargs={}, example=False)" - ] - }, - "execution_count": 78, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"foo\": \"bar\", \"bar\": \"gah\"})" - ] - }, - { - "cell_type": "markdown", - "id": "4728ddd9-914d-42ce-ae9b-72c9ce8ec940", - "metadata": {}, - "source": [ - "## Accepting a Runnable Config\n", - "\n", - "Runnable lambdas can optionally accept a [RunnableConfig](https://api.python.langchain.com/en/latest/schema/langchain.schema.runnable.config.RunnableConfig.html?highlight=runnableconfig#langchain.schema.runnable.config.RunnableConfig), which they can use to pass callbacks, tags, and other configuration information to nested runs." - ] - }, - { - "cell_type": "code", - "execution_count": 139, - "id": "80b3b5f6-5d58-44b9-807e-cce9a46bf49f", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.schema.runnable import RunnableConfig" - ] - }, - { - "cell_type": "code", - "execution_count": 149, - "id": "ff0daf0c-49dd-4d21-9772-e5fa133c5f36", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "\n", - "def parse_or_fix(text: str, config: RunnableConfig):\n", - " fixing_chain = (\n", - " ChatPromptTemplate.from_template(\n", - " \"Fix the following text:\\n\\n```text\\n{input}\\n```\\nError: {error}\"\n", - " \" Don't narrate, just respond with the fixed data.\"\n", - " )\n", - " | ChatOpenAI()\n", - " | StrOutputParser()\n", - " )\n", - " for _ in range(3):\n", - " try:\n", - " return json.loads(text)\n", - " except Exception as e:\n", - " text = fixing_chain.invoke({\"input\": text, \"error\": e}, config)\n", - " return \"Failed to parse\"" - ] - }, - { - "cell_type": "code", - "execution_count": 152, - "id": "1a5e709e-9d75-48c7-bb9c-503251990505", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Tokens Used: 65\n", - "\tPrompt Tokens: 56\n", - "\tCompletion Tokens: 9\n", - "Successful Requests: 1\n", - "Total Cost (USD): $0.00010200000000000001\n" - ] - } - ], - "source": [ - "from langchain.callbacks import get_openai_callback\n", - "\n", - "with get_openai_callback() as cb:\n", - " RunnableLambda(parse_or_fix).invoke(\"{foo: bar}\", {\"tags\": [\"my-tag\"], \"callbacks\": [cb]})\n", - " print(cb)" - ] - }, - { - "cell_type": "markdown", - "id": "506e9636", - "metadata": {}, - "source": [ - "## SQL Database\n", - "\n", - "We can also try to replicate our SQLDatabaseChain using this style." - ] - }, - { - "cell_type": "code", - "execution_count": 106, - "id": "7a927516", - "metadata": {}, - "outputs": [], - "source": [ - "template = \"\"\"Based on the table schema below, write a SQL query that would answer the user's question:\n", - "{schema}\n", - "\n", - "Question: {question}\n", - "SQL Query:\"\"\"\n", - "prompt = ChatPromptTemplate.from_template(template)" - ] - }, - { - "cell_type": "code", - "execution_count": 107, - "id": "3f51f386", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.utilities import SQLDatabase" - ] - }, - { - "cell_type": "code", - "execution_count": 111, - "id": "2ccca6fc", - "metadata": {}, - "outputs": [], - "source": [ - "db = SQLDatabase.from_uri(\"sqlite:///../../../../notebooks/Chinook.db\")" - ] - }, - { - "cell_type": "code", - "execution_count": 109, - "id": "05ba88ee", - "metadata": {}, - "outputs": [], - "source": [ - "def get_schema(_):\n", - " return db.get_table_info()" - ] - }, - { - "cell_type": "code", - "execution_count": 112, - "id": "a4eda902", - "metadata": {}, - "outputs": [], - "source": [ - "def run_query(query):\n", - " return db.run(query)" - ] - }, - { - "cell_type": "code", - "execution_count": 113, - "id": "5046cb17", - "metadata": {}, - "outputs": [], - "source": [ - "inputs = {\n", - " \"schema\": RunnableLambda(get_schema),\n", - " \"question\": itemgetter(\"question\")\n", - "}\n", - "sql_response = (\n", - " RunnableMap(inputs)\n", - " | prompt\n", - " | model.bind(stop=[\"\\nSQLResult:\"])\n", - " | StrOutputParser()\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 114, - "id": "a5552039", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'SELECT COUNT(EmployeeId) FROM Employee'" - ] - }, - "execution_count": 114, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sql_response.invoke({\"question\": \"How many employees are there?\"})" - ] - }, - { - "cell_type": "code", - "execution_count": 115, - "id": "d6fee130", - "metadata": {}, - "outputs": [], - "source": [ - "template = \"\"\"Based on the table schema below, question, sql query, and sql response, write a natural language response:\n", - "{schema}\n", - "\n", - "Question: {question}\n", - "SQL Query: {query}\n", - "SQL Response: {response}\"\"\"\n", - "prompt_response = ChatPromptTemplate.from_template(template)" - ] - }, - { - "cell_type": "code", - "execution_count": 116, - "id": "923aa634", - "metadata": {}, - "outputs": [], - "source": [ - "full_chain = (\n", - " RunnableMap({\n", - " \"question\": itemgetter(\"question\"),\n", - " \"query\": sql_response,\n", - " }) \n", - " | {\n", - " \"schema\": RunnableLambda(get_schema),\n", - " \"question\": itemgetter(\"question\"),\n", - " \"query\": itemgetter(\"query\"),\n", - " \"response\": lambda x: db.run(x[\"query\"]) \n", - " } \n", - " | prompt_response \n", - " | model\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 117, - "id": "e94963d8", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='There are 8 employees.', additional_kwargs={}, example=False)" - ] - }, - "execution_count": 117, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "full_chain.invoke({\"question\": \"How many employees are there?\"})" - ] - }, - { - "cell_type": "markdown", - "id": "f09fd305", - "metadata": {}, - "source": [ - "## Code Writing" - ] - }, - { - "cell_type": "code", - "execution_count": 118, - "id": "bd7c259a", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.utilities import PythonREPL\n", - "from langchain.prompts import SystemMessagePromptTemplate, HumanMessagePromptTemplate" - ] - }, - { - "cell_type": "code", - "execution_count": 119, - "id": "73795d2d", - "metadata": {}, - "outputs": [], - "source": [ - "template = \"\"\"Write some python code to solve the user's problem. \n", - "\n", - "Return only python code in Markdown format, e.g.:\n", - "\n", - "```python\n", - "....\n", - "```\"\"\"\n", - "prompt = ChatPromptTemplate(messages=[\n", - " SystemMessagePromptTemplate.from_template(template),\n", - " HumanMessagePromptTemplate.from_template(\"{input}\")\n", - "])" - ] - }, - { - "cell_type": "code", - "execution_count": 120, - "id": "42859e8a", - "metadata": {}, - "outputs": [], - "source": [ - "def _sanitize_output(text: str):\n", - " _, after = text.split(\"```python\")\n", - " return after.split(\"```\")[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 121, - "id": "5ded1a86", - "metadata": {}, - "outputs": [], - "source": [ - "chain = prompt | model | StrOutputParser() | _sanitize_output | PythonREPL().run" - ] - }, - { - "cell_type": "code", - "execution_count": 122, - "id": "208c2b75", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Python REPL can execute arbitrary code. Use with caution.\n" - ] - }, - { - "data": { - "text/plain": [ - "'4\\n'" - ] - }, - "execution_count": 122, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"input\": \"whats 2 plus 2\"})" - ] - }, - { - "cell_type": "markdown", - "id": "5062941a", - "metadata": {}, - "source": [ - "## Memory\n", - "\n", - "This shows how to add memory to an arbitrary chain. Right now, you can use the memory classes but need to hook it up manually" - ] - }, - { - "cell_type": "code", - "execution_count": 123, - "id": "7998efd8", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.memory import ConversationBufferMemory\n", - "from langchain.schema.runnable import RunnableMap\n", - "from langchain.prompts import MessagesPlaceholder\n", - "model = ChatOpenAI()\n", - "prompt = ChatPromptTemplate.from_messages([\n", - " (\"system\", \"You are a helpful chatbot\"),\n", - " MessagesPlaceholder(variable_name=\"history\"),\n", - " (\"human\", \"{input}\")\n", - "])" - ] - }, - { - "cell_type": "code", - "execution_count": 124, - "id": "fa0087f3", - "metadata": {}, - "outputs": [], - "source": [ - "memory = ConversationBufferMemory(return_messages=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 125, - "id": "06b531ae", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'history': []}" - ] - }, - "execution_count": 125, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "memory.load_memory_variables({})" - ] - }, - { - "cell_type": "code", - "execution_count": 126, - "id": "d9437af6", - "metadata": {}, - "outputs": [], - "source": [ - "chain = RunnableMap({\n", - " \"input\": lambda x: x[\"input\"],\n", - " \"memory\": memory.load_memory_variables\n", - "}) | {\n", - " \"input\": lambda x: x[\"input\"],\n", - " \"history\": lambda x: x[\"memory\"][\"history\"]\n", - "} | prompt | model" - ] - }, - { - "cell_type": "code", - "execution_count": 127, - "id": "bed1e260", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='Hello Bob! How can I assist you today?', additional_kwargs={}, example=False)" - ] - }, - "execution_count": 127, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inputs = {\"input\": \"hi im bob\"}\n", - "response = chain.invoke(inputs)\n", - "response" - ] - }, - { - "cell_type": "code", - "execution_count": 128, - "id": "890475b4", - "metadata": {}, - "outputs": [], - "source": [ - "memory.save_context(inputs, {\"output\": response.content})" - ] - }, - { - "cell_type": "code", - "execution_count": 129, - "id": "e8fcb77f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'history': [HumanMessage(content='hi im bob', additional_kwargs={}, example=False),\n", - " AIMessage(content='Hello Bob! How can I assist you today?', additional_kwargs={}, example=False)]}" - ] - }, - "execution_count": 129, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "memory.load_memory_variables({})" - ] - }, - { - "cell_type": "code", - "execution_count": 130, - "id": "d837d5c3", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='Your name is Bob.', additional_kwargs={}, example=False)" - ] - }, - "execution_count": 130, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inputs = {\"input\": \"whats my name\"}\n", - "response = chain.invoke(inputs)\n", - "response" - ] - }, - { - "cell_type": "markdown", - "id": "4927a727-b4c8-453c-8c83-bd87b4fcac14", - "metadata": {}, - "source": [ - "## Moderation\n", - "\n", - "This shows how to add in moderation (or other safeguards) around your LLM application." - ] - }, - { - "cell_type": "code", - "execution_count": 131, - "id": "4f5f6449-940a-4f5c-97c0-39b71c3e2a68", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.chains import OpenAIModerationChain\n", - "from langchain.llms import OpenAI" - ] - }, - { - "cell_type": "code", - "execution_count": 132, - "id": "fcb8312b-7e7a-424f-a3ec-76738c9a9d21", - "metadata": {}, - "outputs": [], - "source": [ - "moderate = OpenAIModerationChain()" - ] - }, - { - "cell_type": "code", - "execution_count": 133, - "id": "b24b9148-f6b0-4091-8ea8-d3fb281bd950", - "metadata": {}, - "outputs": [], - "source": [ - "model = OpenAI()\n", - "prompt = ChatPromptTemplate.from_messages([\n", - " (\"system\", \"repeat after me: {input}\")\n", - "])" - ] - }, - { - "cell_type": "code", - "execution_count": 134, - "id": "1c8ed87c-9ca6-4559-bf60-d40e94a0af08", - "metadata": {}, - "outputs": [], - "source": [ - "chain = prompt | model" - ] - }, - { - "cell_type": "code", - "execution_count": 135, - "id": "5256b9bd-381a-42b0-bfa8-7e6d18f853cb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'\\n\\nYou are stupid.'" - ] - }, - "execution_count": 135, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"input\": \"you are stupid\"})" - ] - }, - { - "cell_type": "code", - "execution_count": 136, - "id": "fe6e3b33-dc9a-49d5-b194-ba750c58a628", - "metadata": {}, - "outputs": [], - "source": [ - "moderated_chain = chain | moderate" - ] - }, - { - "cell_type": "code", - "execution_count": 137, - "id": "d8ba0cbd-c739-4d23-be9f-6ae092bd5ffb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'input': '\\n\\nYou are stupid.',\n", - " 'output': \"Text was found that violates OpenAI's content policy.\"}" - ] - }, - "execution_count": 137, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "moderated_chain.invoke({\"input\": \"you are stupid\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f07b5300-8676-48ee-ab77-3f2dc2ecd415", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "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.11.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/extras/expression_language/cookbook/code_writing.ipynb b/docs/extras/expression_language/cookbook/code_writing.ipynb new file mode 100644 index 0000000000..25b039ce44 --- /dev/null +++ b/docs/extras/expression_language/cookbook/code_writing.ipynb @@ -0,0 +1,119 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f09fd305", + "metadata": {}, + "source": [ + "# Code writing\n", + "\n", + "Example of how to use LCEL to write Python code." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "bd7c259a", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.chat_models import ChatOpenAI\n", + "from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate\n", + "from langchain.schema.output_parser import StrOutputParser\n", + "from langchain.utilities import PythonREPL" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "73795d2d", + "metadata": {}, + "outputs": [], + "source": [ + "template = \"\"\"Write some python code to solve the user's problem. \n", + "\n", + "Return only python code in Markdown format, e.g.:\n", + "\n", + "```python\n", + "....\n", + "```\"\"\"\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [(\"system\", template), (\"human\", \"{input}\")]\n", + ")\n", + "\n", + "model = ChatOpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "42859e8a", + "metadata": {}, + "outputs": [], + "source": [ + "def _sanitize_output(text: str):\n", + " _, after = text.split(\"```python\")\n", + " return after.split(\"```\")[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "5ded1a86", + "metadata": {}, + "outputs": [], + "source": [ + "chain = prompt | model | StrOutputParser() | _sanitize_output | PythonREPL().run" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "208c2b75", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Python REPL can execute arbitrary code. Use with caution.\n" + ] + }, + { + "data": { + "text/plain": [ + "'4\\n'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"input\": \"whats 2 plus 2\"})" + ] + } + ], + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/cookbook/index.mdx b/docs/extras/expression_language/cookbook/index.mdx new file mode 100644 index 0000000000..6310fd50b9 --- /dev/null +++ b/docs/extras/expression_language/cookbook/index.mdx @@ -0,0 +1,11 @@ +--- +sidebar_position: 2 +--- + +# Cookbook + +import DocCardList from "@theme/DocCardList"; + +Example code for accomplishing common tasks with the LangChain Expression Language (LCEL). These examples show how to compose different Runnable (the core LCEL interface) components to achieve various tasks. If you're just getting acquainted with LCEL, the [Prompt + LLM](/docs/expression_language/cookbook/prompt_llm_parser) page is a good place to start. + + \ No newline at end of file diff --git a/docs/extras/expression_language/cookbook/memory.ipynb b/docs/extras/expression_language/cookbook/memory.ipynb new file mode 100644 index 0000000000..bef7e5ed01 --- /dev/null +++ b/docs/extras/expression_language/cookbook/memory.ipynb @@ -0,0 +1,180 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5062941a", + "metadata": {}, + "source": [ + "# Adding memory\n", + "\n", + "This shows how to add memory to an arbitrary chain. Right now, you can use the memory classes but need to hook it up manually" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7998efd8", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.chat_models import ChatOpenAI\n", + "from langchain.memory import ConversationBufferMemory\n", + "from langchain.schema.runnable import RunnableMap\n", + "from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "\n", + "model = ChatOpenAI()\n", + "prompt = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"You are a helpful chatbot\"),\n", + " MessagesPlaceholder(variable_name=\"history\"),\n", + " (\"human\", \"{input}\")\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fa0087f3", + "metadata": {}, + "outputs": [], + "source": [ + "memory = ConversationBufferMemory(return_messages=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "06b531ae", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'history': []}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory.load_memory_variables({})" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d9437af6", + "metadata": {}, + "outputs": [], + "source": [ + "chain = RunnableMap({\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"memory\": memory.load_memory_variables\n", + "}) | {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"history\": lambda x: x[\"memory\"][\"history\"]\n", + "} | prompt | model" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "bed1e260", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='Hello Bob! How can I assist you today?', additional_kwargs={}, example=False)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inputs = {\"input\": \"hi im bob\"}\n", + "response = chain.invoke(inputs)\n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "890475b4", + "metadata": {}, + "outputs": [], + "source": [ + "memory.save_context(inputs, {\"output\": response.content})" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e8fcb77f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'history': [HumanMessage(content='hi im bob', additional_kwargs={}, example=False),\n", + " AIMessage(content='Hello Bob! How can I assist you today?', additional_kwargs={}, example=False)]}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory.load_memory_variables({})" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d837d5c3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='Your name is Bob.', additional_kwargs={}, example=False)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "inputs = {\"input\": \"whats my name\"}\n", + "response = chain.invoke(inputs)\n", + "response" + ] + } + ], + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/cookbook/moderation.ipynb b/docs/extras/expression_language/cookbook/moderation.ipynb new file mode 100644 index 0000000000..cb4114d8e9 --- /dev/null +++ b/docs/extras/expression_language/cookbook/moderation.ipynb @@ -0,0 +1,133 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4927a727-b4c8-453c-8c83-bd87b4fcac14", + "metadata": {}, + "source": [ + "# Adding moderation\n", + "\n", + "This shows how to add in moderation (or other safeguards) around your LLM application." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "4f5f6449-940a-4f5c-97c0-39b71c3e2a68", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.chains import OpenAIModerationChain\n", + "from langchain.llms import OpenAI\n", + "from langchain.prompts import ChatPromptTemplate" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "fcb8312b-7e7a-424f-a3ec-76738c9a9d21", + "metadata": {}, + "outputs": [], + "source": [ + "moderate = OpenAIModerationChain()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "b24b9148-f6b0-4091-8ea8-d3fb281bd950", + "metadata": {}, + "outputs": [], + "source": [ + "model = OpenAI()\n", + "prompt = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"repeat after me: {input}\")\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1c8ed87c-9ca6-4559-bf60-d40e94a0af08", + "metadata": {}, + "outputs": [], + "source": [ + "chain = prompt | model" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "5256b9bd-381a-42b0-bfa8-7e6d18f853cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\n\\nYou are stupid.'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"input\": \"you are stupid\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "fe6e3b33-dc9a-49d5-b194-ba750c58a628", + "metadata": {}, + "outputs": [], + "source": [ + "moderated_chain = chain | moderate" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "d8ba0cbd-c739-4d23-be9f-6ae092bd5ffb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'input': '\\n\\nYou are stupid',\n", + " 'output': \"Text was found that violates OpenAI's content policy.\"}" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "moderated_chain.invoke({\"input\": \"you are stupid\"})" + ] + } + ], + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/cookbook/multiple_chains.ipynb b/docs/extras/expression_language/cookbook/multiple_chains.ipynb new file mode 100644 index 0000000000..7db06a85f5 --- /dev/null +++ b/docs/extras/expression_language/cookbook/multiple_chains.ipynb @@ -0,0 +1,240 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "877102d1-02ea-4fa3-8ec7-a08e242b95b3", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 2\n", + "title: Multiple chains\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "0f2bf8d3", + "metadata": {}, + "source": [ + "Runnables can easily be used to string together multiple Chains" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d65d4e9e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'El país donde se encuentra la ciudad de Honolulu, donde nació Barack Obama, el 44º Presidente de los Estados Unidos, es Estados Unidos. Honolulu se encuentra en la isla de Oahu, en el estado de Hawái.'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from operator import itemgetter\n", + "\n", + "from langchain.chat_models import ChatOpenAI\n", + "from langchain.prompts import ChatPromptTemplate\n", + "from langchain.schema import StrOutputParser\n", + "\n", + "prompt1 = ChatPromptTemplate.from_template(\"what is the city {person} is from?\")\n", + "prompt2 = ChatPromptTemplate.from_template(\"what country is the city {city} in? respond in {language}\")\n", + "\n", + "model = ChatOpenAI()\n", + "\n", + "chain1 = prompt1 | model | StrOutputParser()\n", + "\n", + "chain2 = {\"city\": chain1, \"language\": itemgetter(\"language\")} | prompt2 | model | StrOutputParser()\n", + "\n", + "chain2.invoke({\"person\": \"obama\", \"language\": \"spanish\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "878f8176", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.schema.runnable import RunnableMap, RunnablePassthrough\n", + "\n", + "prompt1 = ChatPromptTemplate.from_template(\"generate a {attribute} color. Return the name of the color and nothing else:\")\n", + "prompt2 = ChatPromptTemplate.from_template(\"what is a fruit of color: {color}. Return the name of the fruit and nothing else:\")\n", + "prompt3 = ChatPromptTemplate.from_template(\"what is a country with a flag that has the color: {color}. Return the name of the country and nothing else:\")\n", + "prompt4 = ChatPromptTemplate.from_template(\"What is the color of {fruit} and the flag of {country}?\")\n", + "\n", + "model_parser = model | StrOutputParser()\n", + "\n", + "color_generator = {\"attribute\": RunnablePassthrough()} | prompt1 | {\"color\": model_parser}\n", + "color_to_fruit = prompt2 | model_parser\n", + "color_to_country = prompt3 | model_parser\n", + "question_generator = color_generator | {\"fruit\": color_to_fruit, \"country\": color_to_country} | prompt4" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d621a870", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ChatPromptValue(messages=[HumanMessage(content='What is the color of strawberry and the flag of China?', additional_kwargs={}, example=False)])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "question_generator.invoke({\"warm\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "b4a9812b-bead-4fd9-ae27-0b8be57e5dc1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='The color of an apple is typically red or green. The flag of China is predominantly red with a large yellow star in the upper left corner and four smaller yellow stars surrounding it.', additional_kwargs={}, example=False)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "prompt = question_generator.invoke({\"warm\"})\n", + "model.invoke(prompt)" + ] + }, + { + "cell_type": "markdown", + "id": "6d75a313-f1c8-4e94-9a17-24e0bf4a2bdc", + "metadata": {}, + "source": [ + "### Branching and Merging\n", + "\n", + "You may want the output of one component to be processed by 2 or more other components. [RunnableMaps](https://api.python.langchain.com/en/latest/schema/langchain.schema.runnable.base.RunnableMap.html) let you split or fork the chain so multiple components can process the input in parallel. Later, other components can join or merge the results to synthesize a final response. This type of chain creates a computation graph that looks like the following:\n", + "\n", + "```text\n", + " Input\n", + " / \\\n", + " / \\\n", + " Branch1 Branch2\n", + " \\ /\n", + " \\ /\n", + " Combine\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "247fa0bd-4596-4063-8cb3-1d7fc119d982", + "metadata": {}, + "outputs": [], + "source": [ + "planner = (\n", + " ChatPromptTemplate.from_template(\n", + " \"Generate an argument about: {input}\"\n", + " )\n", + " | ChatOpenAI()\n", + " | StrOutputParser()\n", + " | {\"base_response\": RunnablePassthrough()}\n", + ")\n", + "\n", + "arguments_for = (\n", + " ChatPromptTemplate.from_template(\n", + " \"List the pros or positive aspects of {base_response}\"\n", + " )\n", + " | ChatOpenAI()\n", + " | StrOutputParser()\n", + ")\n", + "arguments_against = (\n", + " ChatPromptTemplate.from_template(\n", + " \"List the cons or negative aspects of {base_response}\"\n", + " )\n", + " | ChatOpenAI()\n", + " | StrOutputParser()\n", + ")\n", + "\n", + "final_responder = (\n", + " ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"ai\", \"{original_response}\"),\n", + " (\"human\", \"Pros:\\n{results_1}\\n\\nCons:\\n{results_2}\"),\n", + " (\"system\", \"Generate a final response given the critique\"),\n", + " ]\n", + " )\n", + " | ChatOpenAI()\n", + " | StrOutputParser()\n", + ")\n", + "\n", + "chain = (\n", + " planner \n", + " | {\n", + " \"results_1\": arguments_for,\n", + " \"results_2\": arguments_against,\n", + " \"original_response\": itemgetter(\"base_response\"),\n", + " }\n", + " | final_responder\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "2564f310-0674-4bb1-9c4e-d7848ca73511", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'While Scrum has its potential cons and challenges, many organizations have successfully embraced and implemented this project management framework to great effect. The cons mentioned above can be mitigated or overcome with proper training, support, and a commitment to continuous improvement. It is also important to note that not all cons may be applicable to every organization or project.\\n\\nFor example, while Scrum may be complex initially, with proper training and guidance, teams can quickly grasp the concepts and practices. The lack of predictability can be mitigated by implementing techniques such as velocity tracking and release planning. The limited documentation can be addressed by maintaining a balance between lightweight documentation and clear communication among team members. The dependency on team collaboration can be improved through effective communication channels and regular team-building activities.\\n\\nScrum can be scaled and adapted to larger projects by using frameworks like Scrum of Scrums or LeSS (Large Scale Scrum). Concerns about speed versus quality can be addressed by incorporating quality assurance practices, such as continuous integration and automated testing, into the Scrum process. Scope creep can be managed by having a well-defined and prioritized product backlog, and a strong product owner can be developed through training and mentorship.\\n\\nResistance to change can be overcome by providing proper education and communication to stakeholders and involving them in the decision-making process. Ultimately, the cons of Scrum can be seen as opportunities for growth and improvement, and with the right mindset and support, they can be effectively managed.\\n\\nIn conclusion, while Scrum may have its challenges and potential cons, the benefits and advantages it offers in terms of collaboration, flexibility, adaptability, transparency, and customer satisfaction make it a widely adopted and successful project management framework. With proper implementation and continuous improvement, organizations can leverage Scrum to drive innovation, efficiency, and project success.'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"input\": \"scrum\"})" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "poetry-venv", + "language": "python", + "name": "poetry-venv" + }, + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/cookbook/prompt_llm_parser.ipynb b/docs/extras/expression_language/cookbook/prompt_llm_parser.ipynb new file mode 100644 index 0000000000..1b670904d5 --- /dev/null +++ b/docs/extras/expression_language/cookbook/prompt_llm_parser.ipynb @@ -0,0 +1,431 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "abf7263d-3a62-4016-b5d5-b157f92f2070", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 0\n", + "title: Prompt + LLM\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "9a434f2b-9405-468c-9dfd-254d456b57a6", + "metadata": {}, + "source": [ + "The most common and valuable composition is taking:\n", + "\n", + "``PromptTemplate`` / ``ChatPromptTemplate`` -> ``LLM`` / ``ChatModel`` -> ``OutputParser``\n", + "\n", + "Almost any other chains you build will use this building block." + ] + }, + { + "cell_type": "markdown", + "id": "93aa2c87", + "metadata": {}, + "source": [ + "## PromptTemplate + LLM\n", + "\n", + "The simplest composition is just combing a prompt and model to create a chain that takes user input, adds it to a prompt, passes it to a model, and returns the raw model input.\n", + "\n", + "Note, you can mix and match PromptTemplate/ChatPromptTemplates and LLMs/ChatModels as you like here." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "466b65b3", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.prompts import ChatPromptTemplate\n", + "from langchain.chat_models import ChatOpenAI\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\"tell me a joke about {foo}\")\n", + "model = ChatOpenAI()\n", + "chain = prompt | model" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "e3d0a6cd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content=\"Why don't bears wear shoes?\\n\\nBecause they have bear feet!\", additional_kwargs={}, example=False)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"foo\": \"bears\"})" + ] + }, + { + "cell_type": "markdown", + "id": "7eb9ef50", + "metadata": {}, + "source": [ + "Often times we want to attach kwargs that'll be passed to each model call. Here's a few examples of that:" + ] + }, + { + "cell_type": "markdown", + "id": "0b1d8f88", + "metadata": {}, + "source": [ + "### Attaching Stop Sequences" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "562a06bf", + "metadata": {}, + "outputs": [], + "source": [ + "chain = prompt | model.bind(stop=[\"\\n\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "43f5d04c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='Why did the bear never wear shoes?', additional_kwargs={}, example=False)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"foo\": \"bears\"})" + ] + }, + { + "cell_type": "markdown", + "id": "f3eaf88a", + "metadata": {}, + "source": [ + "### Attaching Function Call information" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f94b71b2", + "metadata": {}, + "outputs": [], + "source": [ + "functions = [\n", + " {\n", + " \"name\": \"joke\",\n", + " \"description\": \"A joke\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"setup\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The setup for the joke\"\n", + " },\n", + " \"punchline\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The punchline for the joke\"\n", + " }\n", + " },\n", + " \"required\": [\"setup\", \"punchline\"]\n", + " }\n", + " }\n", + " ]\n", + "chain = prompt | model.bind(function_call= {\"name\": \"joke\"}, functions= functions)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "decf7710", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='', additional_kwargs={'function_call': {'name': 'joke', 'arguments': '{\\n \"setup\": \"Why don\\'t bears wear shoes?\",\\n \"punchline\": \"Because they have bear feet!\"\\n}'}}, example=False)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"foo\": \"bears\"}, config={})" + ] + }, + { + "cell_type": "markdown", + "id": "9098c5ed", + "metadata": {}, + "source": [ + "## PromptTemplate + LLM + OutputParser\n", + "\n", + "We can also add in an output parser to easily trasform the raw LLM/ChatModel output into a more workable format" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cc194c78", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.schema.output_parser import StrOutputParser\n", + "\n", + "chain = prompt | model | StrOutputParser()" + ] + }, + { + "cell_type": "markdown", + "id": "77acf448", + "metadata": {}, + "source": [ + "Notice that this now returns a string - a much more workable format for downstream tasks" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e3d69a18", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Why don't bears wear shoes?\\n\\nBecause they have bear feet!\"" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"foo\": \"bears\"})" + ] + }, + { + "cell_type": "markdown", + "id": "c01864e5", + "metadata": {}, + "source": [ + "### Functions Output Parser\n", + "\n", + "When you specify the function to return, you may just want to parse that directly" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "ad0dd88e", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser\n", + "\n", + "chain = (\n", + " prompt \n", + " | model.bind(function_call= {\"name\": \"joke\"}, functions= functions) \n", + " | JsonOutputFunctionsParser()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "1e7aa8eb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'setup': \"Why don't bears like fast food?\",\n", + " 'punchline': \"Because they can't catch it!\"}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"foo\": \"bears\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "d4aa1a01", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.output_parsers.openai_functions import JsonKeyOutputFunctionsParser\n", + "\n", + "chain = (\n", + " prompt \n", + " | model.bind(function_call= {\"name\": \"joke\"}, functions= functions) \n", + " | JsonKeyOutputFunctionsParser(key_name=\"setup\")\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8b6df9ba", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Why don't bears wear shoes?\"" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"foo\": \"bears\"})" + ] + }, + { + "cell_type": "markdown", + "id": "023fbccb-ef7d-489e-a9ba-f98e17283d51", + "metadata": {}, + "source": [ + "## Simplifying input\n", + "\n", + "To make invocation even simpler, we can add a `RunnableMap` to take care of creating the prompt input dict for us:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "9601c0f0-71f9-4bd4-a672-7bd04084b018", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.schema.runnable import RunnableMap, RunnablePassthrough\n", + "\n", + "map_ = RunnableMap({\"foo\": RunnablePassthrough()})\n", + "chain = (\n", + " map_ \n", + " | prompt\n", + " | model.bind(function_call= {\"name\": \"joke\"}, functions= functions) \n", + " | JsonKeyOutputFunctionsParser(key_name=\"setup\")\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "7ec4f154-fda5-4847-9220-41aa902fdc33", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Why don't bears wear shoes?\"" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke(\"bears\")" + ] + }, + { + "cell_type": "markdown", + "id": "def00bfe-0f83-4805-8c8f-8a53f99fa8ea", + "metadata": {}, + "source": [ + "Since we're composing our map with another Runnable, we can even use some syntactic sugar and just use a dict:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "7bf3846a-02ee-41a3-ba1b-a708827d4f3a", + "metadata": {}, + "outputs": [], + "source": [ + "chain = (\n", + " {\"foo\": RunnablePassthrough()} \n", + " | prompt\n", + " | model.bind(function_call= {\"name\": \"joke\"}, functions= functions) \n", + " | JsonKeyOutputFunctionsParser(key_name=\"setup\")\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e566d6a1-538d-4cb5-a210-a63e082e4c74", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Why don't bears like fast food?\"" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke(\"bears\")" + ] + } + ], + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/cookbook/retrieval.ipynb b/docs/extras/expression_language/cookbook/retrieval.ipynb new file mode 100644 index 0000000000..6579b1c7f3 --- /dev/null +++ b/docs/extras/expression_language/cookbook/retrieval.ipynb @@ -0,0 +1,461 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "abe47592-909c-4844-bf44-9e55c2fb4bfa", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 1\n", + "title: RAG\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "91c5ef3d", + "metadata": {}, + "source": [ + "Let's look at adding in a retrieval step to a prompt and LLM, which adds up to a \"retrieval-augmented generation\" chain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f25d9e9-d192-42e9-af50-5660a4bfb0d9", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install langchain openai faiss-cpu" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "33be32af", + "metadata": {}, + "outputs": [], + "source": [ + "from operator import itemgetter\n", + "\n", + "from langchain.prompts import ChatPromptTemplate\n", + "from langchain.chat_models import ChatOpenAI\n", + "from langchain.embeddings import OpenAIEmbeddings\n", + "from langchain.schema.output_parser import StrOutputParser\n", + "from langchain.schema.runnable import RunnablePassthrough\n", + "from langchain.vectorstores import FAISS" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "bfc47ec1", + "metadata": {}, + "outputs": [], + "source": [ + "vectorstore = FAISS.from_texts([\"harrison worked at kensho\"], embedding=OpenAIEmbeddings())\n", + "retriever = vectorstore.as_retriever()\n", + "\n", + "template = \"\"\"Answer the question based only on the following context:\n", + "{context}\n", + "\n", + "Question: {question}\n", + "\"\"\"\n", + "prompt = ChatPromptTemplate.from_template(template)\n", + "\n", + "model = ChatOpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "eae31755", + "metadata": {}, + "outputs": [], + "source": [ + "chain = (\n", + " {\"context\": retriever, \"question\": RunnablePassthrough()} \n", + " | prompt \n", + " | model \n", + " | StrOutputParser()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f3040b0c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Harrison worked at Kensho.'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke(\"where did harrison work?\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e1d20c7c", + "metadata": {}, + "outputs": [], + "source": [ + "template = \"\"\"Answer the question based only on the following context:\n", + "{context}\n", + "\n", + "Question: {question}\n", + "\n", + "Answer in the following language: {language}\n", + "\"\"\"\n", + "prompt = ChatPromptTemplate.from_template(template)\n", + "\n", + "chain = {\n", + " \"context\": itemgetter(\"question\") | retriever, \n", + " \"question\": itemgetter(\"question\"), \n", + " \"language\": itemgetter(\"language\")\n", + "} | prompt | model | StrOutputParser()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "7ee8b2d4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Harrison ha lavorato a Kensho.'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"question\": \"where did harrison work\", \"language\": \"italian\"})" + ] + }, + { + "cell_type": "markdown", + "id": "f007669c", + "metadata": {}, + "source": [ + "## Conversational Retrieval Chain\n", + "\n", + "We can easily add in conversation history. This primarily means adding in chat_message_history" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "3f30c348", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.schema.runnable import RunnableMap\n", + "from langchain.schema import format_document" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "64ab1dbf", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.prompts.prompt import PromptTemplate\n", + "\n", + "_template = \"\"\"Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question, in its original language.\n", + "\n", + "Chat History:\n", + "{chat_history}\n", + "Follow Up Input: {question}\n", + "Standalone question:\"\"\"\n", + "CONDENSE_QUESTION_PROMPT = PromptTemplate.from_template(_template)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "7d628c97", + "metadata": {}, + "outputs": [], + "source": [ + "template = \"\"\"Answer the question based only on the following context:\n", + "{context}\n", + "\n", + "Question: {question}\n", + "\"\"\"\n", + "ANSWER_PROMPT = ChatPromptTemplate.from_template(template)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f60a5d0f", + "metadata": {}, + "outputs": [], + "source": [ + "DEFAULT_DOCUMENT_PROMPT = PromptTemplate.from_template(template=\"{page_content}\")\n", + "def _combine_documents(docs, document_prompt = DEFAULT_DOCUMENT_PROMPT, document_separator=\"\\n\\n\"):\n", + " doc_strings = [format_document(doc, document_prompt) for doc in docs]\n", + " return document_separator.join(doc_strings)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "7d007db6", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Tuple, List\n", + "def _format_chat_history(chat_history: List[Tuple]) -> str:\n", + " buffer = \"\"\n", + " for dialogue_turn in chat_history:\n", + " human = \"Human: \" + dialogue_turn[0]\n", + " ai = \"Assistant: \" + dialogue_turn[1]\n", + " buffer += \"\\n\" + \"\\n\".join([human, ai])\n", + " return buffer" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "5c32cc89", + "metadata": {}, + "outputs": [], + "source": [ + "_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", + "}\n", + "conversational_qa_chain = _inputs | _context | ANSWER_PROMPT | ChatOpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "135c8205", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='Harrison was employed at Kensho.', additional_kwargs={}, example=False)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "conversational_qa_chain.invoke({\n", + " \"question\": \"where did harrison work?\",\n", + " \"chat_history\": [],\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "424e7e7a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='Harrison worked at Kensho.', additional_kwargs={}, example=False)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "conversational_qa_chain.invoke({\n", + " \"question\": \"where did he work?\",\n", + " \"chat_history\": [(\"Who wrote this notebook?\", \"Harrison\")],\n", + "})" + ] + }, + { + "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": 16, + "id": "e31dd17c", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.memory import ConversationBufferMemory" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "d4bffe94", + "metadata": {}, + "outputs": [], + "source": [ + "memory = ConversationBufferMemory(return_messages=True, output_key=\"answer\", input_key=\"question\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "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": 19, + "id": "806e390c", + "metadata": {}, + "outputs": [ + { + "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": 19, + "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": 20, + "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": 21, + "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": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory.load_memory_variables({})" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "poetry-venv", + "language": "python", + "name": "poetry-venv" + }, + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/cookbook/sql_db.ipynb b/docs/extras/expression_language/cookbook/sql_db.ipynb new file mode 100644 index 0000000000..0cf0748009 --- /dev/null +++ b/docs/extras/expression_language/cookbook/sql_db.ipynb @@ -0,0 +1,227 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "c14da114-1a4a-487d-9cff-e0e8c30ba366", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 3\n", + "title: Querying a SQL DB\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "506e9636", + "metadata": {}, + "source": [ + "We can replicate our SQLDatabaseChain with Runnables." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7a927516", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.prompts import ChatPromptTemplate\n", + "\n", + "template = \"\"\"Based on the table schema below, write a SQL query that would answer the user's question:\n", + "{schema}\n", + "\n", + "Question: {question}\n", + "SQL Query:\"\"\"\n", + "prompt = ChatPromptTemplate.from_template(template)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "3f51f386", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.utilities import SQLDatabase" + ] + }, + { + "cell_type": "markdown", + "id": "7c3449d6-684b-416e-ba16-90a035835a88", + "metadata": {}, + "source": [ + "We'll need the Chinook sample DB for this example. There's many places to download it from, e.g. https://database.guide/2-sample-databases-sqlite/" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "2ccca6fc", + "metadata": {}, + "outputs": [], + "source": [ + "db = SQLDatabase.from_uri(\"sqlite:///./Chinook.db\")" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "05ba88ee", + "metadata": {}, + "outputs": [], + "source": [ + "def get_schema(_):\n", + " return db.get_table_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a4eda902", + "metadata": {}, + "outputs": [], + "source": [ + "def run_query(query):\n", + " return db.run(query)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "5046cb17", + "metadata": {}, + "outputs": [], + "source": [ + "from operator import itemgetter\n", + "\n", + "from langchain.chat_models import ChatOpenAI\n", + "from langchain.schema.output_parser import StrOutputParser\n", + "from langchain.schema.runnable import RunnableLambda, RunnableMap\n", + "\n", + "model = ChatOpenAI()\n", + "\n", + "inputs = {\n", + " \"schema\": RunnableLambda(get_schema),\n", + " \"question\": itemgetter(\"question\")\n", + "}\n", + "sql_response = (\n", + " RunnableMap(inputs)\n", + " | prompt\n", + " | model.bind(stop=[\"\\nSQLResult:\"])\n", + " | StrOutputParser()\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "a5552039", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'SELECT COUNT(*) FROM Employee'" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sql_response.invoke({\"question\": \"How many employees are there?\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "d6fee130", + "metadata": {}, + "outputs": [], + "source": [ + "template = \"\"\"Based on the table schema below, question, sql query, and sql response, write a natural language response:\n", + "{schema}\n", + "\n", + "Question: {question}\n", + "SQL Query: {query}\n", + "SQL Response: {response}\"\"\"\n", + "prompt_response = ChatPromptTemplate.from_template(template)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "923aa634", + "metadata": {}, + "outputs": [], + "source": [ + "full_chain = (\n", + " RunnableMap({\n", + " \"question\": itemgetter(\"question\"),\n", + " \"query\": sql_response,\n", + " }) \n", + " | {\n", + " \"schema\": RunnableLambda(get_schema),\n", + " \"question\": itemgetter(\"question\"),\n", + " \"query\": itemgetter(\"query\"),\n", + " \"response\": lambda x: db.run(x[\"query\"]) \n", + " } \n", + " | prompt_response \n", + " | model\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "e94963d8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='There are 8 employees.', additional_kwargs={}, example=False)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "full_chain.invoke({\"question\": \"How many employees are there?\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f358d7b-a721-4db3-9f92-f06913428afc", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/cookbook/tools.ipynb b/docs/extras/expression_language/cookbook/tools.ipynb new file mode 100644 index 0000000000..d13dece3c9 --- /dev/null +++ b/docs/extras/expression_language/cookbook/tools.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "29781123", + "metadata": {}, + "source": [ + "# Using tools\n", + "\n", + "You can use any Tools with Runnables easily." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5c579dd-2e22-41b0-a789-346dfdecb5a2", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install duckduckgo-search" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9232d2a9", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.chat_models import ChatOpenAI\n", + "from langchain.prompts import ChatPromptTemplate\n", + "from langchain.schema.output_parser import StrOutputParser\n", + "from langchain.tools import DuckDuckGoSearchRun" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a0c64d2c", + "metadata": {}, + "outputs": [], + "source": [ + "search = DuckDuckGoSearchRun()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "391969b6", + "metadata": {}, + "outputs": [], + "source": [ + "template = \"\"\"turn the following user input into a search query for a search engine:\n", + "\n", + "{input}\"\"\"\n", + "prompt = ChatPromptTemplate.from_template(template)\n", + "\n", + "model = ChatOpenAI()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e3d9d20d", + "metadata": {}, + "outputs": [], + "source": [ + "chain = prompt | model | StrOutputParser() | search" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "55f2967d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'What sports games are on TV today & tonight? Watch and stream live sports on TV today, tonight, tomorrow. Today\\'s 2023 sports TV schedule includes football, basketball, baseball, hockey, motorsports, soccer and more. Watch on TV or stream online on ESPN, FOX, FS1, CBS, NBC, ABC, Peacock, Paramount+, fuboTV, local channels and many other networks. MLB Games Tonight: How to Watch on TV, Streaming & Odds - Thursday, September 7. Seattle Mariners\\' Julio Rodriguez greets teammates in the dugout after scoring against the Oakland Athletics in a ... Circle - Country Music and Lifestyle. Live coverage of all the MLB action today is available to you, with the information provided below. The Brewers will look to pick up a road win at PNC Park against the Pirates on Wednesday at 12:35 PM ET. Check out the latest odds and with BetMGM Sportsbook. Use bonus code \"GNPLAY\" for special offers! MLB Games Tonight: How to Watch on TV, Streaming & Odds - Tuesday, September 5. Houston Astros\\' Kyle Tucker runs after hitting a double during the fourth inning of a baseball game against the Los Angeles Angels, Sunday, Aug. 13, 2023, in Houston. (AP Photo/Eric Christian Smith) (APMedia) The Houston Astros versus the Texas Rangers is one of ... The second half of tonight\\'s college football schedule still has some good games remaining to watch on your television.. We\\'ve already seen an exciting one when Colorado upset TCU. And we saw some ...'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"input\": \"I'd like to figure out what games are tonight\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a16949cf-00ea-43c6-a6aa-797ad4f6918d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "poetry-venv", + "language": "python", + "name": "poetry-venv" + }, + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/how_to/_category_.yml b/docs/extras/expression_language/how_to/_category_.yml new file mode 100644 index 0000000000..39fa22bfbf --- /dev/null +++ b/docs/extras/expression_language/how_to/_category_.yml @@ -0,0 +1,2 @@ +label: 'How to' +position: 1 \ No newline at end of file diff --git a/docs/extras/expression_language/how_to/functions.ipynb b/docs/extras/expression_language/how_to/functions.ipynb new file mode 100644 index 0000000000..fc2f0a2962 --- /dev/null +++ b/docs/extras/expression_language/how_to/functions.ipynb @@ -0,0 +1,158 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fbc4bf6e", + "metadata": {}, + "source": [ + "# Run arbitrary functions\n", + "\n", + "You can use arbitrary functions in the pipeline\n", + "\n", + "Note that all inputs to these functions need to be a SINGLE argument. If you have a function that accepts multiple arguments, you should write a wrapper that accepts a single input and unpacks it into multiple argument." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "6bb221b3", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.schema.runnable import RunnableLambda\n", + "\n", + "def length_function(text):\n", + " return len(text)\n", + "\n", + "def _multiple_length_function(text1, text2):\n", + " return len(text1) * len(text2)\n", + "\n", + "def multiple_length_function(_dict):\n", + " return _multiple_length_function(_dict[\"text1\"], _dict[\"text2\"])\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\"what is {a} + {b}\")\n", + "\n", + "chain1 = prompt | model\n", + "\n", + "chain = {\n", + " \"a\": itemgetter(\"foo\") | RunnableLambda(length_function),\n", + " \"b\": {\"text1\": itemgetter(\"foo\"), \"text2\": itemgetter(\"bar\")} | RunnableLambda(multiple_length_function)\n", + "} | prompt | model" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "5488ec85", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='3 + 9 equals 12.', additional_kwargs={}, example=False)" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"foo\": \"bar\", \"bar\": \"gah\"})" + ] + }, + { + "cell_type": "markdown", + "id": "4728ddd9-914d-42ce-ae9b-72c9ce8ec940", + "metadata": {}, + "source": [ + "## Accepting a Runnable Config\n", + "\n", + "Runnable lambdas can optionally accept a [RunnableConfig](https://api.python.langchain.com/en/latest/schema/langchain.schema.runnable.config.RunnableConfig.html?highlight=runnableconfig#langchain.schema.runnable.config.RunnableConfig), which they can use to pass callbacks, tags, and other configuration information to nested runs." + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "id": "80b3b5f6-5d58-44b9-807e-cce9a46bf49f", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.schema.runnable import RunnableConfig" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "id": "ff0daf0c-49dd-4d21-9772-e5fa133c5f36", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "def parse_or_fix(text: str, config: RunnableConfig):\n", + " fixing_chain = (\n", + " ChatPromptTemplate.from_template(\n", + " \"Fix the following text:\\n\\n```text\\n{input}\\n```\\nError: {error}\"\n", + " \" Don't narrate, just respond with the fixed data.\"\n", + " )\n", + " | ChatOpenAI()\n", + " | StrOutputParser()\n", + " )\n", + " for _ in range(3):\n", + " try:\n", + " return json.loads(text)\n", + " except Exception as e:\n", + " text = fixing_chain.invoke({\"input\": text, \"error\": e}, config)\n", + " return \"Failed to parse\"" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "id": "1a5e709e-9d75-48c7-bb9c-503251990505", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tokens Used: 65\n", + "\tPrompt Tokens: 56\n", + "\tCompletion Tokens: 9\n", + "Successful Requests: 1\n", + "Total Cost (USD): $0.00010200000000000001\n" + ] + } + ], + "source": [ + "from langchain.callbacks import get_openai_callback\n", + "\n", + "with get_openai_callback() as cb:\n", + " RunnableLambda(parse_or_fix).invoke(\"{foo: bar}\", {\"tags\": [\"my-tag\"], \"callbacks\": [cb]})\n", + " print(cb)" + ] + } + ], + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/extras/expression_language/interface.ipynb b/docs/extras/expression_language/interface.ipynb index cf19bfe4db..c47800ecad 100644 --- a/docs/extras/expression_language/interface.ipynb +++ b/docs/extras/expression_language/interface.ipynb @@ -1,12 +1,21 @@ { "cells": [ + { + "cell_type": "raw", + "id": "366a0e68-fd67-4fe5-a292-5c33733339ea", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 0\n", + "title: Interface\n", + "---" + ] + }, { "cell_type": "markdown", "id": "9a9acd2e", "metadata": {}, "source": [ - "# Interface\n", - "\n", "In an effort to make it as easy as possible to create custom chains, we've implemented a [\"Runnable\"](https://api.python.langchain.com/en/latest/schema/langchain.schema.runnable.Runnable.html#langchain.schema.runnable.Runnable) protocol that most components implement. This is a standard interface with a few different methods, which makes it easy to define custom chains as well as making it possible to invoke them in a standard way. The standard interface exposed includes:\n", "\n", "- `stream`: stream back chunks of the response\n", @@ -429,7 +438,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.1" + "version": "3.9.1" } }, "nbformat": 4,