diff --git a/README.md b/README.md index 8fb1923a31..ddf9dcadd6 100644 --- a/README.md +++ b/README.md @@ -34,34 +34,40 @@ conda install langchain -c conda-forge ## πŸ€” What is LangChain? -**LangChain** is a framework for developing applications powered by language models. It enables applications that: -- **Are context-aware**: connect a language model to sources of context (prompt instructions, few shot examples, content to ground its response in, etc.) -- **Reason**: rely on a language model to reason (about how to answer based on provided context, what actions to take, etc.) - -This framework consists of several parts. -- **LangChain Libraries**: The Python and JavaScript libraries. Contains interfaces and integrations for a myriad of components, a basic run time for combining these components into chains and agents, and off-the-shelf implementations of chains and agents. -- **[LangChain Templates](templates)**: A collection of easily deployable reference architectures for a wide variety of tasks. -- **[LangServe](https://github.com/langchain-ai/langserve)**: A library for deploying LangChain chains as a REST API. -- **[LangSmith](https://smith.langchain.com)**: A developer platform that lets you debug, test, evaluate, and monitor chains built on any LLM framework and seamlessly integrates with LangChain. -- **[LangGraph](https://python.langchain.com/docs/langgraph)**: LangGraph is a library for building stateful, multi-actor applications with LLMs, built on top of (and intended to be used with) LangChain. It extends the LangChain Expression Language with the ability to coordinate multiple chains (or actors) across multiple steps of computation in a cyclic manner. - -The LangChain libraries themselves are made up of several different packages. -- **[`langchain-core`](libs/core)**: Base abstractions and LangChain Expression Language. -- **[`langchain-community`](libs/community)**: Third party integrations. -- **[`langchain`](libs/langchain)**: Chains, agents, and retrieval strategies that make up an application's cognitive architecture. +**LangChain** is a framework for developing applications powered by large language models (LLMs). + +For these applications, LangChain simplifies the entire application lifecycle: + +- **Open-source libraries**: Build your applications using LangChain's [modular building blocks](https://python.langchain.com/docs/expression_language/) and [components](https://python.langchain.com/docs/modules/). Integrate with hundreds of [third-party providers](https://python.langchain.com/docs/integrations/platforms/). +- **Productionization**: Inspect, monitor, and evaluate your apps with [LangSmith](https://python.langchain.com/docs/langsmith/) so that you can constantly optimize and deploy with confidence. +- **Deployment**: Turn any chain into a REST API with [LangServe](https://python.langchain.com/docs/langserve). + +### Open-source libraries +- **`langchain-core`**: Base abstractions and LangChain Expression Language. +- **`langchain-community`**: Third party integrations. + - Some integrations have been further split into **partner packages** that only rely on **`langchain-core`**. Examples include **`langchain_openai`** and **`langchain_anthropic`**. +- **`langchain`**: Chains, agents, and retrieval strategies that make up an application's cognitive architecture. +- **`[LangGraph](https://python.langchain.com/docs/langgraph)`**: A library for building robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph. + +### Productionization: +- **[LangSmith](https://python.langchain.com/docs/langsmith)**: A developer platform that lets you debug, test, evaluate, and monitor chains built on any LLM framework and seamlessly integrates with LangChain. + +### Deployment: +- **[LangServe](https://python.langchain.com/docs/langserve)**: A library for deploying LangChain chains as REST APIs. ![Diagram outlining the hierarchical organization of the LangChain framework, displaying the interconnected parts across multiple layers.](docs/static/svg/langchain_stack.svg "LangChain Architecture Overview") ## 🧱 What can you build with LangChain? -**❓ Retrieval augmented generation** + +**❓ Question answering with RAG** - [Documentation](https://python.langchain.com/docs/use_cases/question_answering/) - End-to-end Example: [Chat LangChain](https://chat.langchain.com) and [repo](https://github.com/langchain-ai/chat-langchain) -**πŸ’¬ Analyzing structured data** +**🧱 Extracting structured output** -- [Documentation](https://python.langchain.com/docs/use_cases/qa_structured/sql) -- End-to-end Example: [SQL Llama2 Template](https://github.com/langchain-ai/langchain/tree/master/templates/sql-llama2) +- [Documentation](https://python.langchain.com/docs/use_cases/extraction/) +- End-to-end Example: [SQL Llama2 Template](https://github.com/langchain-ai/langchain-extract/) **πŸ€– Chatbots** @@ -72,34 +78,51 @@ And much more! Head to the [Use cases](https://python.langchain.com/docs/use_cas ## πŸš€ How does LangChain help? The main value props of the LangChain libraries are: -1. **Components**: composable tools and integrations for working with language models. Components are modular and easy-to-use, whether you are using the rest of the LangChain framework or not +1. **Components**: composable building blocks, tools and integrations for working with language models. Components are modular and easy-to-use, whether you are using the rest of the LangChain framework or not 2. **Off-the-shelf chains**: built-in assemblages of components for accomplishing higher-level tasks Off-the-shelf chains make it easy to get started. Components make it easy to customize existing chains and build new ones. +## LangChain Expression Language (LCEL) + +LCEL is the foundation of many of LangChain's components, and is a declarative way to compose chains. LCEL was designed from day 1 to support putting prototypes in production, with no code changes, from the simplest β€œprompt + LLM” chain to the most complex chains. + +- **[Overview](https://python.langchain.com/docs/expression_language/)**: LCEL and its benefits +- **[Interface](https://python.langchain.com/docs/expression_language/interface)**: The standard interface for LCEL objects +- **[Primitives](https://python.langchain.com/docs/expression_language/primitives)**: More on the primitives LCEL includes + +## Components + Components fall into the following **modules**: **πŸ“ƒ Model I/O:** -This includes prompt management, prompt optimization, a generic interface for all LLMs, and common utilities for working with LLMs. +This includes [prompt management](https://python.langchain.com/docs/modules/model_io/prompts/), [prompt optimization](https://python.langchain.com/docs/modules/model_io/prompts/example_selectors/), a generic interface for [chat models](https://python.langchain.com/docs/modules/model_io/chat/) and [LLMs](https://python.langchain.com/docs/modules/model_io/llms/), and common utilities for working with [model outputs](https://python.langchain.com/docs/modules/model_io/output_parsers/). **πŸ“š Retrieval:** -Data Augmented Generation involves specific types of chains that first interact with an external data source to fetch data for use in the generation step. Examples include summarization of long pieces of text and question/answering over specific data sources. +Retrieval Augmented Generation involves [loading data](https://python.langchain.com/docs/modules/data_connection/document_loaders/) from a variety of sources, [preparing it](https://python.langchain.com/docs/modules/data_connection/document_loaders/), [then retrieving it](https://python.langchain.com/docs/modules/data_connection/retrievers/) for use in the generation step. **πŸ€– Agents:** -Agents involve an LLM making decisions about which Actions to take, taking that Action, seeing an Observation, and repeating that until done. LangChain provides a standard interface for agents, a selection of agents to choose from, and examples of end-to-end agents. +Agents allow an LLM autonomy over how a task is accomplished. Agents make decisions about which Actions to take, then take that Action, observe the result, and repeat until the task is complete done. LangChain provides a [standard interface for agents](https://python.langchain.com/docs/modules/agents/), a [selection of agents](https://python.langchain.com/docs/modules/agents/agent_types/) to choose from, and examples of end-to-end agents. ## πŸ“– Documentation Please see [here](https://python.langchain.com) for full documentation, which includes: - [Getting started](https://python.langchain.com/docs/get_started/introduction): installation, setting up the environment, simple examples -- Overview of the [interfaces](https://python.langchain.com/docs/expression_language/), [modules](https://python.langchain.com/docs/modules/), and [integrations](https://python.langchain.com/docs/integrations/providers) -- [Use case](https://python.langchain.com/docs/use_cases/qa_structured/sql) walkthroughs and best practice [guides](https://python.langchain.com/docs/guides/adapters/openai) -- [LangSmith](https://python.langchain.com/docs/langsmith/), [LangServe](https://python.langchain.com/docs/langserve), and [LangChain Template](https://python.langchain.com/docs/templates/) overviews -- [Reference](https://api.python.langchain.com): full API docs +- [Use case](https://python.langchain.com/docs/use_cases/) walkthroughs and best practice [guides](https://python.langchain.com/docs/guides/) +- Overviews of the [interfaces](https://python.langchain.com/docs/expression_language/), [components](https://python.langchain.com/docs/modules/), and [integrations](https://python.langchain.com/docs/integrations/providers) + +You can also check out the full [API Reference docs](https://api.python.langchain.com). + +## 🌐 Ecosystem + +- [πŸ¦œπŸ› οΈ LangSmith](https://python.langchain.com/docs/langsmith/): Tracing and evaluating your language model applications and intelligent agents to help you move from prototype to production. +- [πŸ¦œπŸ•ΈοΈ LangGraph](https://python.langchain.com/docs/langgraph): Creating stateful, multi-actor applications with LLMs, built on top of (and intended to be used with) LangChain primitives. +- [πŸ¦œπŸ“ LangServe](https://python.langchain.com/docs/langserve): Deploying LangChain runnables and chains as REST APIs. + - [LangChain Templates](https://python.langchain.com/docs/templates/): Example applications hosted with LangServe. ## πŸ’ Contributing diff --git a/cookbook/LLaMA2_sql_chat.ipynb b/cookbook/LLaMA2_sql_chat.ipynb index 3b697f314d..375c6740fd 100644 --- a/cookbook/LLaMA2_sql_chat.ipynb +++ b/cookbook/LLaMA2_sql_chat.ipynb @@ -38,9 +38,9 @@ "\n", "To run locally, we use Ollama.ai. \n", "\n", - "See [here](https://python.langchain.com/docs/integrations/chat/ollama) for details on installation and setup.\n", + "See [here](/docs/integrations/chat/ollama) for details on installation and setup.\n", "\n", - "Also, see [here](https://python.langchain.com/docs/guides/local_llms) for our full guide on local LLMs.\n", + "Also, see [here](/docs/guides/development/local_llms) for our full guide on local LLMs.\n", " \n", "To use an external API, which is not private, we can use Replicate." ] diff --git a/cookbook/Semi_structured_multi_modal_RAG_LLaMA2.ipynb b/cookbook/Semi_structured_multi_modal_RAG_LLaMA2.ipynb index 19b9218ae7..eb72ef730c 100644 --- a/cookbook/Semi_structured_multi_modal_RAG_LLaMA2.ipynb +++ b/cookbook/Semi_structured_multi_modal_RAG_LLaMA2.ipynb @@ -191,15 +191,15 @@ "source": [ "## Multi-vector retriever\n", "\n", - "Use [multi-vector-retriever](https://python.langchain.com/docs/modules/data_connection/retrievers/multi_vector#summary).\n", + "Use [multi-vector-retriever](/docs/modules/data_connection/retrievers/multi_vector#summary).\n", "\n", "Summaries are used to retrieve raw tables and / or raw chunks of text.\n", "\n", "### Text and Table summaries\n", "\n", - "Here, we use ollama.ai to run LLaMA2 locally. \n", + "Here, we use Ollama to run LLaMA2 locally. \n", "\n", - "See details on installation [here](https://python.langchain.com/docs/guides/local_llms)." + "See details on installation [here](/docs/guides/development/local_llms)." ] }, { diff --git a/docs/docs/additional_resources/tutorials.mdx b/docs/docs/additional_resources/tutorials.mdx index bbb792b737..2aa3a64c35 100644 --- a/docs/docs/additional_resources/tutorials.mdx +++ b/docs/docs/additional_resources/tutorials.mdx @@ -21,10 +21,10 @@ ### Featured courses on Deeplearning.AI -- [LangChain for LLM Application Development](https://learn.deeplearning.ai/langchain) -- [LangChain Chat with Your Data](https://learn.deeplearning.ai/langchain-chat-with-your-data) -- [Functions, Tools and Agents with LangChain](https://learn.deeplearning.ai/functions-tools-agents-langchain) -- [Build LLM Apps with LangChain.js](https://learn.deeplearning.ai/courses/build-llm-apps-with-langchain-js) +- [LangChain for LLM Application Development](https://www.deeplearning.ai/short-courses/langchain-for-llm-application-development/) +- [LangChain Chat with Your Data](https://www.deeplearning.ai/short-courses/langchain-chat-with-your-data/) +- [Functions, Tools and Agents with LangChain](https://www.deeplearning.ai/short-courses/functions-tools-agents-langchain/) +- [Build LLM Apps with LangChain.js](https://www.deeplearning.ai/short-courses/build-llm-apps-with-langchain-js/) ### Online courses diff --git a/docs/docs/contributing/documentation/_category_.yml b/docs/docs/contributing/documentation/_category_.yml new file mode 100644 index 0000000000..7a89d51116 --- /dev/null +++ b/docs/docs/contributing/documentation/_category_.yml @@ -0,0 +1,2 @@ +label: 'Documentation' +position: 3 \ No newline at end of file diff --git a/docs/docs/contributing/documentation/style_guide.mdx b/docs/docs/contributing/documentation/style_guide.mdx new file mode 100644 index 0000000000..e8da942595 --- /dev/null +++ b/docs/docs/contributing/documentation/style_guide.mdx @@ -0,0 +1,138 @@ +--- +sidebar_label: "Style guide" +--- + +# LangChain Documentation Style Guide + +## Introduction + +As LangChain continues to grow, the surface area of documentation required to cover it continues to grow too. +This page provides guidelines for anyone writing documentation for LangChain, as well as some of our philosophies around +organization and structure. + +## Philosophy + +LangChain's documentation aspires to follow the [Diataxis framework](https://diataxis.fr). +Under this framework, all documentation falls under one of four categories: + +- **Tutorials**: Lessons that take the reader by the hand through a series of conceptual steps to complete a project. + - An example of this is our [LCEL streaming guide](/docs/expression_language/streaming). + - Our guides on [custom components](/docs/modules/model_io/chat/custom_chat_model) is another one. +- **How-to guides**: Guides that take the reader through the steps required to solve a real-world problem. + - The clearest examples of this are our [Use case](/docs/use_cases/) quickstart pages. +- **Reference**: Technical descriptions of the machinery and how to operate it. + - Our [Runnable interface](/docs/expression_language/interface) page is an example of this. + - The [API reference pages](https://api.python.langchain.com/) are another. +- **Explanation**: Explanations that clarify and illuminate a particular topic. + - The [LCEL primitives pages](/docs/expression_language/primitives/sequence) are an example of this. + +Each category serves a distinct purpose and requires a specific approach to writing and structuring the content. + +## Taxonomy + +Keeping the above in mind, we have sorted LangChain's docs into categories. It is helpful to think in these terms +when contributing new documentation: + +### Getting started + +The [getting started section](/docs/get_started/introduction) includes a high-level introduction to LangChain, a quickstart that +tours LangChain's various features, and logistical instructions around installation and project setup. + +It contains elements of **How-to guides** and **Explanations**. + +### Use cases + +[Use cases](/docs/use_cases/) are guides that are meant to show how to use LangChain to accomplish a specific task (RAG, information extraction, etc.). +The quickstarts should be good entrypoints for first-time LangChain developers who prefer to learn by getting something practical prototyped, +then taking the pieces apart retrospectively. These should mirror what LangChain is good at. + +The quickstart pages here should fit the **How-to guide** category, with the other pages intended to be **Explanations** of more +in-depth concepts and strategies that accompany the main happy paths. + +:::note +The below sections are listed roughly in order of increasing level of abstraction. +::: + +### Expression Language + +[LangChain Expression Language (LCEL)](/docs/expression_language/) is the fundamental way that most LangChain components fit together, and this section is designed to teach +developers how to use it to build with LangChain's primitives effectively. + +This section should contains **Tutorials** that teach how to stream and use LCEL primitives for more abstract tasks, **Explanations** of specific behaviors, +and some **References** for how to use different methods in the Runnable interface. + +### Components + +The [components section](/docs/modules) covers concepts one level of abstraction higher than LCEL. +Abstract base classes like `BaseChatModel` and `BaseRetriever` should be covered here, as well as core implementations of these base classes, +such as `ChatPromptTemplate` and `RecursiveCharacterTextSplitter`. Customization guides belong here too. + +This section should contain mostly conceptual **Tutorials**, **References**, and **Explanations** of the components they cover. + +:::note +As a general rule of thumb, everything covered in the `Expression Language` and `Components` sections (with the exception of the `Composition` section of components) should +cover only components that exist in `langchain_core`. +::: + +### Integrations + +The [integrations](/docs/integrations/platforms/) are specific implementations of components. These often involve third-party APIs and services. +If this is the case, as a general rule, these are maintained by the third-party partner. + +This section should contain mostly **Explanations** and **References**, though the actual content here is more flexible than other sections and more at the +discretion of the third-party provider. + +:::note +Concepts covered in `Integrations` should generally exist in `langchain_community` or specific partner packages. +::: + +### Guides and Ecosystem + +The [Guides](/docs/guides) and [Ecosystem](/docs/langsmith/) sections should contain guides that address higher-level problems than the sections above. +This includes, but is not limited to, considerations around productionization and development workflows. + +These should contain mostly **How-to guides**, **Explanations**, and **Tutorials**. + +### API references + +LangChain's API references. Should act as **References** (as the name implies) with some **Explanation**-focused content as well. + +## Sample developer journey + +We have set up our docs to assist a new developer to LangChain. Let's walk through the intended path: + +- The developer lands on https://python.langchain.com, and reads through the introduction and the diagram. +- If they are just curious, they may be drawn to the [Quickstart](/docs/get_started/quickstart) to get a high-level tour of what LangChain contains. +- If they have a specific task in mind that they want to accomplish, they will be drawn to the Use-Case section. The use-case should provide a good, concrete hook that shows the value LangChain can provide them and be a good entrypoint to the framework. +- They can then move to learn more about the fundamentals of LangChain through the Expression Language sections. +- Next, they can learn about LangChain's various components and integrations. +- Finally, they can get additional knowledge through the Guides. + +This is only an ideal of course - sections will inevitably reference lower or higher-level concepts that are documented in other sections. + +## Guidelines + +Here are some other guidelines you should think about when writing and organizing documentation. + +### Linking to other sections + +Because sections of the docs do not exist in a vacuum, it is important to link to other sections as often as possible +to allow a developer to learn more about an unfamiliar topic inline. + +This includes linking to the API references as well as conceptual sections! + +### Conciseness + +In general, take a less-is-more approach. If a section with a good explanation of a concept already exists, you should link to it rather than +re-explain it, unless the concept you are documenting presents some new wrinkle. + +Be concise, including in code samples. + +### General style + +- Use active voice and present tense whenever possible. +- Use examples and code snippets to illustrate concepts and usage. +- Use appropriate header levels (`#`, `##`, `###`, etc.) to organize the content hierarchically. +- Use bullet points and numbered lists to break down information into easily digestible chunks. +- Use tables (especially for **Reference** sections) and diagrams often to present information visually. +- Include the table of contents for longer documentation pages to help readers navigate the content, but hide it for shorter pages. diff --git a/docs/docs/contributing/documentation.mdx b/docs/docs/contributing/documentation/technical_logistics.mdx similarity index 98% rename from docs/docs/contributing/documentation.mdx rename to docs/docs/contributing/documentation/technical_logistics.mdx index b7bba374d1..4dbb0204df 100644 --- a/docs/docs/contributing/documentation.mdx +++ b/docs/docs/contributing/documentation/technical_logistics.mdx @@ -1,7 +1,4 @@ ---- -sidebar_position: 3 ---- -# Contribute Documentation +# Technical logistics LangChain documentation consists of two components: diff --git a/docs/docs/contributing/index.mdx b/docs/docs/contributing/index.mdx index d25212f286..95783cae45 100644 --- a/docs/docs/contributing/index.mdx +++ b/docs/docs/contributing/index.mdx @@ -12,7 +12,7 @@ As an open-source project in a rapidly developing field, we are extremely open t There are many ways to contribute to LangChain. Here are some common ways people contribute: -- [**Documentation**](./documentation.mdx): Help improve our docs, including this one! +- [**Documentation**](/docs/contributing/documentation/style_guide): Help improve our docs, including this one! - [**Code**](./code.mdx): Help us write code, fix bugs, or improve our infrastructure. - [**Integrations**](integrations.mdx): Help us integrate with your favorite vendors and tools. - [**Discussions**](https://github.com/langchain-ai/langchain/discussions): Help answer usage questions and discuss issues with users. diff --git a/docs/docs/contributing/repo_structure.mdx b/docs/docs/contributing/repo_structure.mdx index 90f212265c..fc055e3d0a 100644 --- a/docs/docs/contributing/repo_structure.mdx +++ b/docs/docs/contributing/repo_structure.mdx @@ -41,7 +41,7 @@ There are other files in the root directory level, but their presence should be The `/docs` directory contains the content for the documentation that is shown at https://python.langchain.com/ and the associated API Reference https://api.python.langchain.com/en/latest/langchain_api_reference.html. -See the [documentation](./documentation) guidelines to learn how to contribute to the documentation. +See the [documentation](/docs/contributing/documentation/style_guide) guidelines to learn how to contribute to the documentation. ## Code diff --git a/docs/docs/expression_language/cookbook/agent.ipynb b/docs/docs/expression_language/cookbook/agent.ipynb deleted file mode 100644 index a2f518ab7c..0000000000 --- a/docs/docs/expression_language/cookbook/agent.ipynb +++ /dev/null @@ -1,205 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "e89f490d", - "metadata": {}, - "source": [ - "# Agents\n", - "\n", - "You can pass a Runnable into an agent. Make sure you have `langchainhub` installed: `pip install langchainhub`" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "af4381de", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain import hub\n", - "from langchain.agents import AgentExecutor, tool\n", - "from langchain.agents.output_parsers import XMLAgentOutputParser\n", - "from langchain_community.chat_models import ChatAnthropic" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "24cc8134", - "metadata": {}, - "outputs": [], - "source": [ - "model = ChatAnthropic(model=\"claude-2\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "67c0b0e4", - "metadata": {}, - "outputs": [], - "source": [ - "@tool\n", - "def search(query: str) -> str:\n", - " \"\"\"Search things about current events.\"\"\"\n", - " return \"32 degrees\"" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "7203b101", - "metadata": {}, - "outputs": [], - "source": [ - "tool_list = [search]" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "b68e756d", - "metadata": {}, - "outputs": [], - "source": [ - "# Get the prompt to use - you can modify this!\n", - "prompt = hub.pull(\"hwchase17/xml-agent-convo\")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "61ab3e9a", - "metadata": {}, - "outputs": [], - "source": [ - "# Logic for going from intermediate steps to a string to pass into model\n", - "# This is pretty tied to the prompt\n", - "def convert_intermediate_steps(intermediate_steps):\n", - " log = \"\"\n", - " for action, observation in intermediate_steps:\n", - " log += (\n", - " f\"{action.tool}{action.tool_input}\"\n", - " f\"{observation}\"\n", - " )\n", - " return log\n", - "\n", - "\n", - "# Logic for converting tools to string to go in prompt\n", - "def convert_tools(tools):\n", - " return \"\\n\".join([f\"{tool.name}: {tool.description}\" for tool in tools])" - ] - }, - { - "cell_type": "markdown", - "id": "260f5988", - "metadata": {}, - "source": [ - "Building an agent from a runnable usually involves a few things:\n", - "\n", - "1. Data processing for the intermediate steps. These need to be represented in a way that the language model can recognize them. This should be pretty tightly coupled to the instructions in the prompt\n", - "\n", - "2. The prompt itself\n", - "\n", - "3. The model, complete with stop tokens if needed\n", - "\n", - "4. The output parser - should be in sync with how the prompt specifies things to be formatted." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "e92f1d6f", - "metadata": {}, - "outputs": [], - "source": [ - "agent = (\n", - " {\n", - " \"input\": lambda x: x[\"input\"],\n", - " \"agent_scratchpad\": lambda x: convert_intermediate_steps(\n", - " x[\"intermediate_steps\"]\n", - " ),\n", - " }\n", - " | prompt.partial(tools=convert_tools(tool_list))\n", - " | model.bind(stop=[\"\", \"\"])\n", - " | XMLAgentOutputParser()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "6ce6ec7a", - "metadata": {}, - "outputs": [], - "source": [ - "agent_executor = AgentExecutor(agent=agent, tools=tool_list, verbose=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "fb5cb2e3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", - "\u001b[32;1m\u001b[1;3m searchweather in New York\u001b[0m\u001b[36;1m\u001b[1;3m32 degrees\u001b[0m\u001b[32;1m\u001b[1;3m search\n", - "weather in New York\u001b[0m\u001b[36;1m\u001b[1;3m32 degrees\u001b[0m\u001b[32;1m\u001b[1;3m The weather in New York is 32 degrees\u001b[0m\n", - "\n", - "\u001b[1m> Finished chain.\u001b[0m\n" - ] - }, - { - "data": { - "text/plain": [ - "{'input': 'whats the weather in New york?',\n", - " 'output': 'The weather in New York is 32 degrees'}" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "agent_executor.invoke({\"input\": \"whats the weather in New york?\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bce86dd8", - "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.10.1" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/expression_language/cookbook/code_writing.ipynb b/docs/docs/expression_language/cookbook/code_writing.ipynb index f8892dc175..731cba6f56 100644 --- a/docs/docs/expression_language/cookbook/code_writing.ipynb +++ b/docs/docs/expression_language/cookbook/code_writing.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "raw", + "id": "1e997ab7", + "metadata": {}, + "source": [ + "---\n", + "sidebar_class_name: hidden\n", + "---" + ] + }, { "cell_type": "markdown", "id": "f09fd305", diff --git a/docs/docs/expression_language/cookbook/embedding_router.ipynb b/docs/docs/expression_language/cookbook/embedding_router.ipynb deleted file mode 100644 index 17bb0e3111..0000000000 --- a/docs/docs/expression_language/cookbook/embedding_router.ipynb +++ /dev/null @@ -1,163 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "cf4fb76d-c534-485b-8b51-a0714ee3b82e", - "metadata": {}, - "source": [ - "# Routing by semantic similarity\n", - "\n", - "With LCEL you can easily add [custom routing logic](/docs/expression_language/how_to/routing#using-a-custom-function) to your chain to dynamically determine the chain logic based on user input. All you need to do is define a function that given an input returns a `Runnable`.\n", - "\n", - "One especially useful technique is to use embeddings to route a query to the most relevant prompt. Here's a very simple example." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b793a0aa", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain-core langchain langchain-openai" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "eef9020a-5f7c-4291-98eb-fa73f17d4b92", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.utils.math import cosine_similarity\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.prompts import PromptTemplate\n", - "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n", - "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n", - "\n", - "physics_template = \"\"\"You are a very smart physics professor. \\\n", - "You are great at answering questions about physics in a concise and easy to understand manner. \\\n", - "When you don't know the answer to a question you admit that you don't know.\n", - "\n", - "Here is a question:\n", - "{query}\"\"\"\n", - "\n", - "math_template = \"\"\"You are a very good mathematician. You are great at answering math questions. \\\n", - "You are so good because you are able to break down hard problems into their component parts, \\\n", - "answer the component parts, and then put them together to answer the broader question.\n", - "\n", - "Here is a question:\n", - "{query}\"\"\"\n", - "\n", - "embeddings = OpenAIEmbeddings()\n", - "prompt_templates = [physics_template, math_template]\n", - "prompt_embeddings = embeddings.embed_documents(prompt_templates)\n", - "\n", - "\n", - "def prompt_router(input):\n", - " query_embedding = embeddings.embed_query(input[\"query\"])\n", - " similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]\n", - " most_similar = prompt_templates[similarity.argmax()]\n", - " print(\"Using MATH\" if most_similar == math_template else \"Using PHYSICS\")\n", - " return PromptTemplate.from_template(most_similar)\n", - "\n", - "\n", - "chain = (\n", - " {\"query\": RunnablePassthrough()}\n", - " | RunnableLambda(prompt_router)\n", - " | ChatOpenAI()\n", - " | StrOutputParser()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "4d22b0f3-24f2-4a47-9440-065b57ebcdbd", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Using PHYSICS\n", - "A black hole is a region in space where gravity is extremely strong, so strong that nothing, not even light, can escape its gravitational pull. It is formed when a massive star collapses under its own gravity during a supernova explosion. The collapse causes an incredibly dense mass to be concentrated in a small volume, creating a gravitational field that is so intense that it warps space and time. Black holes have a boundary called the event horizon, which marks the point of no return for anything that gets too close. Beyond the event horizon, the gravitational pull is so strong that even light cannot escape, hence the name \"black hole.\" While we have a good understanding of black holes, there is still much to learn, especially about what happens inside them.\n" - ] - } - ], - "source": [ - "print(chain.invoke(\"What's a black hole\"))" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "f261910d-1de1-4a01-8c8a-308db02b81de", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Using MATH\n", - "Thank you for your kind words! I will do my best to break down the concept of a path integral for you.\n", - "\n", - "In mathematics and physics, a path integral is a mathematical tool used to calculate the probability amplitude or wave function of a particle or system of particles. It was introduced by Richard Feynman and is an integral over all possible paths that a particle can take to go from an initial state to a final state.\n", - "\n", - "To understand the concept better, let's consider an example. Suppose we have a particle moving from point A to point B in space. Classically, we would describe this particle's motion using a definite trajectory, but in quantum mechanics, particles can simultaneously take multiple paths from A to B.\n", - "\n", - "The path integral formalism considers all possible paths that the particle could take and assigns a probability amplitude to each path. These probability amplitudes are then added up, taking into account the interference effects between different paths.\n", - "\n", - "To calculate a path integral, we need to define an action, which is a mathematical function that describes the behavior of the system. The action is usually expressed in terms of the particle's position, velocity, and time.\n", - "\n", - "Once we have the action, we can write down the path integral as an integral over all possible paths. Each path is weighted by a factor determined by the action and the principle of least action, which states that a particle takes a path that minimizes the action.\n", - "\n", - "Mathematically, the path integral is expressed as:\n", - "\n", - "∫ e^(iS/Δ§) D[x(t)]\n", - "\n", - "Here, S is the action, Δ§ is the reduced Planck's constant, and D[x(t)] represents the integration over all possible paths x(t) of the particle.\n", - "\n", - "By evaluating this integral, we can obtain the probability amplitude for the particle to go from the initial state to the final state. The absolute square of this amplitude gives us the probability of finding the particle in a particular state.\n", - "\n", - "Path integrals have proven to be a powerful tool in various areas of physics, including quantum mechanics, quantum field theory, and statistical mechanics. They allow us to study complex systems and calculate probabilities that are difficult to obtain using other methods.\n", - "\n", - "I hope this explanation helps you understand the concept of a path integral. If you have any further questions, feel free to ask!\n" - ] - } - ], - "source": [ - "print(chain.invoke(\"What's a path integral\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f0c1732a-01ca-4d10-977c-29ed7480972b", - "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/docs/expression_language/cookbook/index.mdx b/docs/docs/expression_language/cookbook/index.mdx deleted file mode 100644 index 6effbfe904..0000000000 --- a/docs/docs/expression_language/cookbook/index.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -sidebar_position: 3 ---- - -# 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/docs/expression_language/cookbook/memory.ipynb b/docs/docs/expression_language/cookbook/memory.ipynb deleted file mode 100644 index c128d498e3..0000000000 --- a/docs/docs/expression_language/cookbook/memory.ipynb +++ /dev/null @@ -1,194 +0,0 @@ -{ - "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": null, - "id": "18753dee", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain langchain-openai" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "7998efd8", - "metadata": {}, - "outputs": [], - "source": [ - "from operator import itemgetter\n", - "\n", - "from langchain.memory import ConversationBufferMemory\n", - "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", - "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n", - "from langchain_openai import ChatOpenAI\n", - "\n", - "model = ChatOpenAI()\n", - "prompt = ChatPromptTemplate.from_messages(\n", - " [\n", - " (\"system\", \"You are a helpful chatbot\"),\n", - " MessagesPlaceholder(variable_name=\"history\"),\n", - " (\"human\", \"{input}\"),\n", - " ]\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 = (\n", - " RunnablePassthrough.assign(\n", - " history=RunnableLambda(memory.load_memory_variables) | itemgetter(\"history\")\n", - " )\n", - " | prompt\n", - " | model\n", - ")" - ] - }, - { - "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/docs/expression_language/cookbook/prompt_size.ipynb b/docs/docs/expression_language/cookbook/prompt_size.ipynb index 9a73e5d2e7..8d6aa2a2d4 100644 --- a/docs/docs/expression_language/cookbook/prompt_size.ipynb +++ b/docs/docs/expression_language/cookbook/prompt_size.ipynb @@ -34,7 +34,7 @@ "from langchain.agents import AgentExecutor, load_tools\n", "from langchain.agents.format_scratchpad import format_to_openai_function_messages\n", "from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser\n", - "from langchain.tools import WikipediaQueryRun\n", + "from langchain_community.tools import WikipediaQueryRun\n", "from langchain_community.utilities import WikipediaAPIWrapper\n", "from langchain_core.prompt_values import ChatPromptValue\n", "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", diff --git a/docs/docs/expression_language/cookbook/retrieval.ipynb b/docs/docs/expression_language/cookbook/retrieval.ipynb deleted file mode 100644 index 89df3b6a0c..0000000000 --- a/docs/docs/expression_language/cookbook/retrieval.ipynb +++ /dev/null @@ -1,492 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "id": "abe47592-909c-4844-bf44-9e55c2fb4bfa", - "metadata": {}, - "source": [ - "---\n", - "sidebar_position: 1\n", - "title: RAG\n", - "---\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": 4, - "id": "7f25d9e9-d192-42e9-af50-5660a4bfb0d9", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain langchain-openai faiss-cpu tiktoken" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "33be32af", - "metadata": {}, - "outputs": [], - "source": [ - "from operator import itemgetter\n", - "\n", - "from langchain_community.vectorstores import FAISS\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n", - "from langchain_openai import ChatOpenAI, OpenAIEmbeddings" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "bfc47ec1", - "metadata": {}, - "outputs": [], - "source": [ - "vectorstore = FAISS.from_texts(\n", - " [\"harrison worked at kensho\"], embedding=OpenAIEmbeddings()\n", - ")\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": 3, - "id": "eae31755", - "metadata": {}, - "outputs": [], - "source": [ - "chain = (\n", - " {\"context\": retriever, \"question\": RunnablePassthrough()}\n", - " | prompt\n", - " | model\n", - " | StrOutputParser()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "f3040b0c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Harrison worked at Kensho.'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke(\"where did harrison work?\")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "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", - " {\n", - " \"context\": itemgetter(\"question\") | retriever,\n", - " \"question\": itemgetter(\"question\"),\n", - " \"language\": itemgetter(\"language\"),\n", - " }\n", - " | prompt\n", - " | model\n", - " | StrOutputParser()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "7ee8b2d4", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Harrison ha lavorato a Kensho.'" - ] - }, - "execution_count": 6, - "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": 21, - "id": "3f30c348", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.messages import AIMessage, HumanMessage, get_buffer_string\n", - "from langchain_core.prompts import format_document\n", - "from langchain_core.runnables import RunnableParallel" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "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": 9, - "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": 10, - "id": "f60a5d0f", - "metadata": {}, - "outputs": [], - "source": [ - "DEFAULT_DOCUMENT_PROMPT = PromptTemplate.from_template(template=\"{page_content}\")\n", - "\n", - "\n", - "def _combine_documents(\n", - " docs, document_prompt=DEFAULT_DOCUMENT_PROMPT, document_separator=\"\\n\\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": 11, - "id": "5c32cc89", - "metadata": {}, - "outputs": [], - "source": [ - "_inputs = RunnableParallel(\n", - " standalone_question=RunnablePassthrough.assign(\n", - " chat_history=lambda x: get_buffer_string(x[\"chat_history\"])\n", - " )\n", - " | CONDENSE_QUESTION_PROMPT\n", - " | ChatOpenAI(temperature=0)\n", - " | StrOutputParser(),\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": 12, - "id": "135c8205", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='Harrison was employed at Kensho.')" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "conversational_qa_chain.invoke(\n", - " {\n", - " \"question\": \"where did harrison work?\",\n", - " \"chat_history\": [],\n", - " }\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "424e7e7a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='Harrison worked at Kensho.')" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "conversational_qa_chain.invoke(\n", - " {\n", - " \"question\": \"where did he work?\",\n", - " \"chat_history\": [\n", - " HumanMessage(content=\"Who wrote this notebook?\"),\n", - " AIMessage(content=\"Harrison\"),\n", - " ],\n", - " }\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": 14, - "id": "e31dd17c", - "metadata": {}, - "outputs": [], - "source": [ - "from operator import itemgetter\n", - "\n", - "from langchain.memory import ConversationBufferMemory" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "d4bffe94", - "metadata": {}, - "outputs": [], - "source": [ - "memory = ConversationBufferMemory(\n", - " return_messages=True, output_key=\"answer\", input_key=\"question\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "733be985", - "metadata": {}, - "outputs": [], - "source": [ - "# First we add a step to load memory\n", - "# This adds a \"memory\" key to the input object\n", - "loaded_memory = RunnablePassthrough.assign(\n", - " chat_history=RunnableLambda(memory.load_memory_variables) | itemgetter(\"history\"),\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: get_buffer_string(x[\"chat_history\"]),\n", - " }\n", - " | CONDENSE_QUESTION_PROMPT\n", - " | ChatOpenAI(temperature=0)\n", - " | 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 | standalone_question | retrieved_documents | answer" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "806e390c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'answer': AIMessage(content='Harrison was employed at Kensho.'),\n", - " 'docs': [Document(page_content='harrison worked at kensho')]}" - ] - }, - "execution_count": 17, - "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": 18, - "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": 19, - "id": "f94f7de4", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'history': [HumanMessage(content='where did harrison work?'),\n", - " AIMessage(content='Harrison was employed at Kensho.')]}" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "memory.load_memory_variables({})" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "88f2b7cd", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'answer': AIMessage(content='Harrison actually worked at Kensho.'),\n", - " 'docs': [Document(page_content='harrison worked at kensho')]}" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "inputs = {\"question\": \"but where did he really work?\"}\n", - "result = final_chain.invoke(inputs)\n", - "result" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "207a2782", - "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.10.1" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/expression_language/cookbook/sql_db.ipynb b/docs/docs/expression_language/cookbook/sql_db.ipynb deleted file mode 100644 index b7085c2f26..0000000000 --- a/docs/docs/expression_language/cookbook/sql_db.ipynb +++ /dev/null @@ -1,225 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "id": "c14da114-1a4a-487d-9cff-e0e8c30ba366", - "metadata": {}, - "source": [ - "---\n", - "sidebar_position: 3\n", - "title: Querying a SQL DB\n", - "---\n" - ] - }, - { - "cell_type": "markdown", - "id": "506e9636", - "metadata": {}, - "source": [ - "We can replicate our SQLDatabaseChain with Runnables." - ] - }, - { - "cell_type": "code", - "id": "b3121aa8", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain langchain-openai" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "7a927516", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.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_community.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 langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.runnables import RunnablePassthrough\n", - "from langchain_openai import ChatOpenAI\n", - "\n", - "model = ChatOpenAI()\n", - "\n", - "sql_response = (\n", - " RunnablePassthrough.assign(schema=get_schema)\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", - " RunnablePassthrough.assign(query=sql_response).assign(\n", - " schema=get_schema,\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/docs/expression_language/cookbook/tools.ipynb b/docs/docs/expression_language/cookbook/tools.ipynb deleted file mode 100644 index d214e8791c..0000000000 --- a/docs/docs/expression_language/cookbook/tools.ipynb +++ /dev/null @@ -1,122 +0,0 @@ -{ - "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 --upgrade --quiet langchain langchain-openai duckduckgo-search" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "9232d2a9", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.tools import DuckDuckGoSearchRun\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_openai import ChatOpenAI" - ] - }, - { - "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/docs/expression_language/get_started.ipynb b/docs/docs/expression_language/get_started.ipynb index 16d10e379d..7d1a3e0c33 100644 --- a/docs/docs/expression_language/get_started.ipynb +++ b/docs/docs/expression_language/get_started.ipynb @@ -509,7 +509,7 @@ "source": [ "## Next steps\n", "\n", - "We recommend reading our [Why use LCEL](/docs/expression_language/why) section next to see a side-by-side comparison of the code needed to produce common functionality with and without LCEL." + "We recommend reading our [Advantages of LCEL](/docs/expression_language/why) section next to see a side-by-side comparison of the code needed to produce common functionality with and without LCEL." ] } ], diff --git a/docs/docs/expression_language/how_to/decorator.ipynb b/docs/docs/expression_language/how_to/decorator.ipynb index e01acfafc4..eccbfd708d 100644 --- a/docs/docs/expression_language/how_to/decorator.ipynb +++ b/docs/docs/expression_language/how_to/decorator.ipynb @@ -5,9 +5,9 @@ "id": "b45110ef", "metadata": {}, "source": [ - "# Create a runnable with the `@chain` decorator\n", + "# Create a runnable with the @chain decorator\n", "\n", - "You can also turn an arbitrary function into a chain by adding a `@chain` decorator. This is functionaly equivalent to wrapping in a [`RunnableLambda`](./functions).\n", + "You can also turn an arbitrary function into a chain by adding a `@chain` decorator. This is functionaly equivalent to wrapping in a [`RunnableLambda`](/docs/expression_language/primitives/functions).\n", "\n", "This will have the benefit of improved observability by tracing your chain correctly. Any calls to runnables inside this function will be traced as nested childen.\n", "\n", diff --git a/docs/docs/expression_language/how_to/fallbacks.ipynb b/docs/docs/expression_language/how_to/fallbacks.ipynb deleted file mode 100644 index de915b3240..0000000000 --- a/docs/docs/expression_language/how_to/fallbacks.ipynb +++ /dev/null @@ -1,310 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "19c9cbd6", - "metadata": {}, - "source": [ - "# Add fallbacks\n", - "\n", - "There are many possible points of failure in an LLM application, whether that be issues with LLM API's, poor model outputs, issues with other integrations, etc. Fallbacks help you gracefully handle and isolate these issues.\n", - "\n", - "Crucially, fallbacks can be applied not only on the LLM level but on the whole runnable level." - ] - }, - { - "cell_type": "markdown", - "id": "a6bb9ba9", - "metadata": {}, - "source": [ - "## Handling LLM API Errors\n", - "\n", - "This is maybe the most common use case for fallbacks. A request to an LLM API can fail for a variety of reasons - the API could be down, you could have hit rate limits, any number of things. Therefore, using fallbacks can help protect against these types of things.\n", - "\n", - "IMPORTANT: By default, a lot of the LLM wrappers catch errors and retry. You will most likely want to turn those off when working with fallbacks. Otherwise the first wrapper will keep on retrying and not failing." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ebb61b1f", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain langchain-openai" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "d3e893bf", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_community.chat_models import ChatAnthropic\n", - "from langchain_openai import ChatOpenAI" - ] - }, - { - "cell_type": "markdown", - "id": "4847c82d", - "metadata": {}, - "source": [ - "First, let's mock out what happens if we hit a RateLimitError from OpenAI" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "dfdd8bf5", - "metadata": {}, - "outputs": [], - "source": [ - "from unittest.mock import patch\n", - "\n", - "import httpx\n", - "from openai import RateLimitError\n", - "\n", - "request = httpx.Request(\"GET\", \"/\")\n", - "response = httpx.Response(200, request=request)\n", - "error = RateLimitError(\"rate limit\", response=response, body=\"\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e6fdffc1", - "metadata": {}, - "outputs": [], - "source": [ - "# Note that we set max_retries = 0 to avoid retrying on RateLimits, etc\n", - "openai_llm = ChatOpenAI(max_retries=0)\n", - "anthropic_llm = ChatAnthropic()\n", - "llm = openai_llm.with_fallbacks([anthropic_llm])" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "584461ab", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hit error\n" - ] - } - ], - "source": [ - "# Let's use just the OpenAI LLm first, to show that we run into an error\n", - "with patch(\"openai.resources.chat.completions.Completions.create\", side_effect=error):\n", - " try:\n", - " print(openai_llm.invoke(\"Why did the chicken cross the road?\"))\n", - " except RateLimitError:\n", - " print(\"Hit error\")" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "4fc1e673", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "content=' I don\\'t actually know why the chicken crossed the road, but here are some possible humorous answers:\\n\\n- To get to the other side!\\n\\n- It was too chicken to just stand there. \\n\\n- It wanted a change of scenery.\\n\\n- It wanted to show the possum it could be done.\\n\\n- It was on its way to a poultry farmers\\' convention.\\n\\nThe joke plays on the double meaning of \"the other side\" - literally crossing the road to the other side, or the \"other side\" meaning the afterlife. So it\\'s an anti-joke, with a silly or unexpected pun as the answer.' additional_kwargs={} example=False\n" - ] - } - ], - "source": [ - "# Now let's try with fallbacks to Anthropic\n", - "with patch(\"openai.resources.chat.completions.Completions.create\", side_effect=error):\n", - " try:\n", - " print(llm.invoke(\"Why did the chicken cross the road?\"))\n", - " except RateLimitError:\n", - " print(\"Hit error\")" - ] - }, - { - "cell_type": "markdown", - "id": "f00bea25", - "metadata": {}, - "source": [ - "We can use our \"LLM with Fallbacks\" as we would a normal LLM." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "4f8eaaa0", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "content=\" I don't actually know why the kangaroo crossed the road, but I'm happy to take a guess! Maybe the kangaroo was trying to get to the other side to find some tasty grass to eat. Or maybe it was trying to get away from a predator or other danger. Kangaroos do need to cross roads and other open areas sometimes as part of their normal activities. Whatever the reason, I'm sure the kangaroo looked both ways before hopping across!\" additional_kwargs={} example=False\n" - ] - } - ], - "source": [ - "from langchain_core.prompts import ChatPromptTemplate\n", - "\n", - "prompt = ChatPromptTemplate.from_messages(\n", - " [\n", - " (\n", - " \"system\",\n", - " \"You're a nice assistant who always includes a compliment in your response\",\n", - " ),\n", - " (\"human\", \"Why did the {animal} cross the road\"),\n", - " ]\n", - ")\n", - "chain = prompt | llm\n", - "with patch(\"openai.resources.chat.completions.Completions.create\", side_effect=error):\n", - " try:\n", - " print(chain.invoke({\"animal\": \"kangaroo\"}))\n", - " except RateLimitError:\n", - " print(\"Hit error\")" - ] - }, - { - "cell_type": "markdown", - "id": "ef9f0f39-0b9f-4723-a394-f61c98c75d41", - "metadata": {}, - "source": [ - "### Specifying errors to handle\n", - "\n", - "We can also specify the errors to handle if we want to be more specific about when the fallback is invoked:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "e4069ca4-1c16-4915-9a8c-b2732869ae27", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hit error\n" - ] - } - ], - "source": [ - "llm = openai_llm.with_fallbacks(\n", - " [anthropic_llm], exceptions_to_handle=(KeyboardInterrupt,)\n", - ")\n", - "\n", - "chain = prompt | llm\n", - "with patch(\"openai.resources.chat.completions.Completions.create\", side_effect=error):\n", - " try:\n", - " print(chain.invoke({\"animal\": \"kangaroo\"}))\n", - " except RateLimitError:\n", - " print(\"Hit error\")" - ] - }, - { - "cell_type": "markdown", - "id": "8d62241b", - "metadata": {}, - "source": [ - "## Fallbacks for Sequences\n", - "\n", - "We can also create fallbacks for sequences, that are sequences themselves. Here we do that with two different models: ChatOpenAI and then normal OpenAI (which does not use a chat model). Because OpenAI is NOT a chat model, you likely want a different prompt." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "6d0b8056", - "metadata": {}, - "outputs": [], - "source": [ - "# First let's create a chain with a ChatModel\n", - "# We add in a string output parser here so the outputs between the two are the same type\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "\n", - "chat_prompt = ChatPromptTemplate.from_messages(\n", - " [\n", - " (\n", - " \"system\",\n", - " \"You're a nice assistant who always includes a compliment in your response\",\n", - " ),\n", - " (\"human\", \"Why did the {animal} cross the road\"),\n", - " ]\n", - ")\n", - "# Here we're going to use a bad model name to easily create a chain that will error\n", - "chat_model = ChatOpenAI(model_name=\"gpt-fake\")\n", - "bad_chain = chat_prompt | chat_model | StrOutputParser()" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "id": "8d1fc2a5", - "metadata": {}, - "outputs": [], - "source": [ - "# Now lets create a chain with the normal OpenAI model\n", - "from langchain.prompts import PromptTemplate\n", - "from langchain_openai import OpenAI\n", - "\n", - "prompt_template = \"\"\"Instructions: You should always include a compliment in your response.\n", - "\n", - "Question: Why did the {animal} cross the road?\"\"\"\n", - "prompt = PromptTemplate.from_template(prompt_template)\n", - "llm = OpenAI()\n", - "good_chain = prompt | llm" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "283bfa44", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'\\n\\nAnswer: The turtle crossed the road to get to the other side, and I have to say he had some impressive determination.'" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# We can now create a final chain which combines the two\n", - "chain = bad_chain.with_fallbacks([good_chain])\n", - "chain.invoke({\"animal\": \"turtle\"})" - ] - } - ], - "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/docs/expression_language/how_to/functions.ipynb b/docs/docs/expression_language/how_to/functions.ipynb deleted file mode 100644 index 9c69d2deed..0000000000 --- a/docs/docs/expression_language/how_to/functions.ipynb +++ /dev/null @@ -1,206 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "id": "ce0e08fd", - "metadata": {}, - "source": [ - "---\n", - "sidebar_position: 2\n", - "title: \"RunnableLambda: Run Custom Functions\"\n", - "keywords: [RunnableLambda, LCEL]\n", - "---" - ] - }, - { - "cell_type": "markdown", - "id": "fbc4bf6e", - "metadata": {}, - "source": [ - "# Run custom 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": "raw", - "id": "9a5fe916", - "metadata": {}, - "source": [ - "%pip install --upgrade --quiet langchain langchain-openai" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "6bb221b3", - "metadata": {}, - "outputs": [], - "source": [ - "from operator import itemgetter\n", - "\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_core.runnables import RunnableLambda\n", - "from langchain_openai import ChatOpenAI\n", - "\n", - "\n", - "def length_function(text):\n", - " return len(text)\n", - "\n", - "\n", - "def _multiple_length_function(text1, text2):\n", - " return len(text1) * len(text2)\n", - "\n", - "\n", - "def multiple_length_function(_dict):\n", - " return _multiple_length_function(_dict[\"text1\"], _dict[\"text2\"])\n", - "\n", - "\n", - "prompt = ChatPromptTemplate.from_template(\"what is {a} + {b}\")\n", - "model = ChatOpenAI()\n", - "\n", - "chain1 = prompt | model\n", - "\n", - "chain = (\n", - " {\n", - " \"a\": itemgetter(\"foo\") | RunnableLambda(length_function),\n", - " \"b\": {\"text1\": itemgetter(\"foo\"), \"text2\": itemgetter(\"bar\")}\n", - " | RunnableLambda(multiple_length_function),\n", - " }\n", - " | prompt\n", - " | model\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "5488ec85", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content='3 + 9 equals 12.')" - ] - }, - "execution_count": 2, - "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/runnables/langchain_core.runnables.config.RunnableConfig.html#langchain_core.runnables.config.RunnableConfig), which they can use to pass callbacks, tags, and other configuration information to nested runs." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "80b3b5f6-5d58-44b9-807e-cce9a46bf49f", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.runnables import RunnableConfig" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "ff0daf0c-49dd-4d21-9772-e5fa133c5f36", - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "\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": 5, - "id": "1a5e709e-9d75-48c7-bb9c-503251990505", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'foo': 'bar'}\n", - "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", - " output = RunnableLambda(parse_or_fix).invoke(\n", - " \"{foo: bar}\", {\"tags\": [\"my-tag\"], \"callbacks\": [cb]}\n", - " )\n", - " print(output)\n", - " print(cb)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29f55c38", - "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.10.1" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/expression_language/how_to/index.mdx b/docs/docs/expression_language/how_to/index.mdx deleted file mode 100644 index 0f0ef5d973..0000000000 --- a/docs/docs/expression_language/how_to/index.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -sidebar_position: 2 ---- - -# How to - -import DocCardList from "@theme/DocCardList"; - - \ No newline at end of file diff --git a/docs/docs/expression_language/how_to/inspect.ipynb b/docs/docs/expression_language/how_to/inspect.ipynb index 8c3bae4727..5e7e7f7f7e 100644 --- a/docs/docs/expression_language/how_to/inspect.ipynb +++ b/docs/docs/expression_language/how_to/inspect.ipynb @@ -30,9 +30,9 @@ "outputs": [], "source": [ "from langchain.prompts import ChatPromptTemplate\n", - "from langchain.vectorstores import FAISS\n", + "from langchain_community.vectorstores import FAISS\n", "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n", + "from langchain_core.runnables import RunnablePassthrough\n", "from langchain_openai import ChatOpenAI, OpenAIEmbeddings" ] }, diff --git a/docs/docs/expression_language/how_to/routing.ipynb b/docs/docs/expression_language/how_to/routing.ipynb index b555a161e5..ba5485de32 100644 --- a/docs/docs/expression_language/how_to/routing.ipynb +++ b/docs/docs/expression_language/how_to/routing.ipynb @@ -7,7 +7,7 @@ "source": [ "---\n", "sidebar_position: 3\n", - "title: \"RunnableBranch: Dynamically route logic based on input\"\n", + "title: \"Route logic based on input\"\n", "keywords: [RunnableBranch, LCEL]\n", "---" ] @@ -25,7 +25,7 @@ "\n", "There are two ways to perform routing:\n", "\n", - "1. Conditionally return runnables from a [`RunnableLambda`](./functions) (recommended)\n", + "1. Conditionally return runnables from a [`RunnableLambda`](/docs/expression_language/primitives/functions) (recommended)\n", "2. Using a `RunnableBranch`.\n", "\n", "We'll illustrate both methods using a two step sequence where the first step classifies an input question as being about `LangChain`, `Anthropic`, or `Other`, then routes to a corresponding prompt chain." @@ -42,22 +42,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "8a8a1967", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "' Anthropic'" + "'Anthropic'" ] }, + "execution_count": 1, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ - "from langchain_community.chat_models import ChatAnthropic\n", + "from langchain_anthropic import ChatAnthropic\n", "from langchain_core.output_parsers import StrOutputParser\n", "from langchain_core.prompts import PromptTemplate\n", "\n", @@ -73,7 +74,7 @@ "\n", "Classification:\"\"\"\n", " )\n", - " | ChatAnthropic()\n", + " | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n", " | StrOutputParser()\n", ")\n", "\n", @@ -90,42 +91,33 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "89d7722d", "metadata": {}, "outputs": [], "source": [ - "langchain_chain = (\n", - " PromptTemplate.from_template(\n", - " \"\"\"You are an expert in langchain. \\\n", + "langchain_chain = PromptTemplate.from_template(\n", + " \"\"\"You are an expert in langchain. \\\n", "Always answer questions starting with \"As Harrison Chase told me\". \\\n", "Respond to the following question:\n", "\n", "Question: {question}\n", "Answer:\"\"\"\n", - " )\n", - " | ChatAnthropic()\n", - ")\n", - "anthropic_chain = (\n", - " PromptTemplate.from_template(\n", - " \"\"\"You are an expert in anthropic. \\\n", + ") | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n", + "anthropic_chain = PromptTemplate.from_template(\n", + " \"\"\"You are an expert in anthropic. \\\n", "Always answer questions starting with \"As Dario Amodei told me\". \\\n", "Respond to the following question:\n", "\n", "Question: {question}\n", "Answer:\"\"\"\n", - " )\n", - " | ChatAnthropic()\n", - ")\n", - "general_chain = (\n", - " PromptTemplate.from_template(\n", - " \"\"\"Respond to the following question:\n", + ") | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n", + "general_chain = PromptTemplate.from_template(\n", + " \"\"\"Respond to the following question:\n", "\n", "Question: {question}\n", "Answer:\"\"\"\n", - " )\n", - " | ChatAnthropic()\n", - ")" + ") | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")" ] }, { @@ -140,7 +132,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 4, "id": "687492da", "metadata": {}, "outputs": [], @@ -156,7 +148,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 5, "id": "02a33c86", "metadata": {}, "outputs": [], @@ -170,17 +162,17 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 6, "id": "c2e977a4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content=' As Dario Amodei told me, to use Anthropic IPC you first need to import it:\\n\\n```python\\nfrom anthroipc import ic\\n```\\n\\nThen you can create a client and connect to the server:\\n\\n```python \\nclient = ic.connect()\\n```\\n\\nAfter that, you can call methods on the client and get responses:\\n\\n```python\\nresponse = client.ask(\"What is the meaning of life?\")\\nprint(response)\\n```\\n\\nYou can also register callbacks to handle events: \\n\\n```python\\ndef on_poke(event):\\n print(\"Got poked!\")\\n\\nclient.on(\\'poke\\', on_poke)\\n```\\n\\nAnd that\\'s the basics of using the Anthropic IPC client library for Python! Let me know if you have any other questions!', additional_kwargs={}, example=False)" + "AIMessage(content=\"As Dario Amodei told me, to use Anthropic, you can start by exploring the company's website and learning about their mission, values, and the different services and products they offer. Anthropic is focused on developing safe and ethical AI systems, so they have a strong emphasis on transparency and responsible AI development. \\n\\nDepending on your specific needs, you can look into Anthropic's AI research and development services, which cover areas like natural language processing, computer vision, and reinforcement learning. They also offer consulting and advisory services to help organizations navigate the challenges and opportunities of AI integration.\\n\\nAdditionally, Anthropic has released some open-source AI models and tools that you can explore and experiment with. These can be a great way to get hands-on experience with Anthropic's approach to AI development.\\n\\nOverall, Anthropic aims to be a reliable and trustworthy partner in the AI space, so I'd encourage you to reach out to them directly to discuss how they can best support your specific requirements.\", response_metadata={'id': 'msg_01CtLFgFSwvTaJomrihE87Ra', 'content': [ContentBlock(text=\"As Dario Amodei told me, to use Anthropic, you can start by exploring the company's website and learning about their mission, values, and the different services and products they offer. Anthropic is focused on developing safe and ethical AI systems, so they have a strong emphasis on transparency and responsible AI development. \\n\\nDepending on your specific needs, you can look into Anthropic's AI research and development services, which cover areas like natural language processing, computer vision, and reinforcement learning. They also offer consulting and advisory services to help organizations navigate the challenges and opportunities of AI integration.\\n\\nAdditionally, Anthropic has released some open-source AI models and tools that you can explore and experiment with. These can be a great way to get hands-on experience with Anthropic's approach to AI development.\\n\\nOverall, Anthropic aims to be a reliable and trustworthy partner in the AI space, so I'd encourage you to reach out to them directly to discuss how they can best support your specific requirements.\", type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=53, output_tokens=219)})" ] }, - "execution_count": 12, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -191,17 +183,17 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 7, "id": "48913dc6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content=' As Harrison Chase told me, to use LangChain you first need to sign up for an API key at platform.langchain.com. Once you have your API key, you can install the Python library and write a simple Python script to call the LangChain API. Here is some sample code to get started:\\n\\n```python\\nimport langchain\\n\\napi_key = \"YOUR_API_KEY\"\\n\\nlangchain.set_key(api_key)\\n\\nresponse = langchain.ask(\"What is the capital of France?\")\\n\\nprint(response.response)\\n```\\n\\nThis will send the question \"What is the capital of France?\" to the LangChain API and print the response. You can customize the request by providing parameters like max_tokens, temperature, etc. The LangChain Python library documentation has more details on the available options. The key things are getting an API key and calling langchain.ask() with your question text. Let me know if you have any other questions!', additional_kwargs={}, example=False)" + "AIMessage(content=\"As Harrison Chase told me, using LangChain involves a few key steps:\\n\\n1. **Set up your environment**: Install the necessary Python packages, including the LangChain library itself, as well as any other dependencies your application might require, such as language models or other integrations.\\n\\n2. **Understand the core concepts**: LangChain revolves around a few core concepts, like Agents, Chains, and Tools. Familiarize yourself with these concepts and how they work together to build powerful language-based applications.\\n\\n3. **Identify your use case**: Determine what kind of task or application you want to build using LangChain, such as a chatbot, a question-answering system, or a document summarization tool.\\n\\n4. **Choose the appropriate components**: Based on your use case, select the right LangChain components, such as agents, chains, and tools, to build your application.\\n\\n5. **Integrate with language models**: LangChain is designed to work seamlessly with various language models, such as OpenAI's GPT-3 or Anthropic's models. Connect your chosen language model to your LangChain application.\\n\\n6. **Implement your application logic**: Use LangChain's building blocks to implement the specific functionality of your application, such as prompting the language model, processing the response, and integrating with other services or data sources.\\n\\n7. **Test and iterate**: Thoroughly test your application, gather feedback, and iterate on your design and implementation to improve its performance and user experience.\\n\\nAs Harrison Chase emphasized, LangChain provides a flexible and powerful framework for building language-based applications, making it easier to leverage the capabilities of modern language models. By following these steps, you can get started with LangChain and create innovative solutions tailored to your specific needs.\", response_metadata={'id': 'msg_01H3UXAAHG4TwxJLpxwuuVU7', 'content': [ContentBlock(text=\"As Harrison Chase told me, using LangChain involves a few key steps:\\n\\n1. **Set up your environment**: Install the necessary Python packages, including the LangChain library itself, as well as any other dependencies your application might require, such as language models or other integrations.\\n\\n2. **Understand the core concepts**: LangChain revolves around a few core concepts, like Agents, Chains, and Tools. Familiarize yourself with these concepts and how they work together to build powerful language-based applications.\\n\\n3. **Identify your use case**: Determine what kind of task or application you want to build using LangChain, such as a chatbot, a question-answering system, or a document summarization tool.\\n\\n4. **Choose the appropriate components**: Based on your use case, select the right LangChain components, such as agents, chains, and tools, to build your application.\\n\\n5. **Integrate with language models**: LangChain is designed to work seamlessly with various language models, such as OpenAI's GPT-3 or Anthropic's models. Connect your chosen language model to your LangChain application.\\n\\n6. **Implement your application logic**: Use LangChain's building blocks to implement the specific functionality of your application, such as prompting the language model, processing the response, and integrating with other services or data sources.\\n\\n7. **Test and iterate**: Thoroughly test your application, gather feedback, and iterate on your design and implementation to improve its performance and user experience.\\n\\nAs Harrison Chase emphasized, LangChain provides a flexible and powerful framework for building language-based applications, making it easier to leverage the capabilities of modern language models. By following these steps, you can get started with LangChain and create innovative solutions tailored to your specific needs.\", type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=50, output_tokens=400)})" ] }, - "execution_count": 13, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -212,17 +204,17 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 8, "id": "a14d0dca", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content=' 4', additional_kwargs={}, example=False)" + "AIMessage(content='4', response_metadata={'id': 'msg_01UAKP81jTZu9fyiyFYhsbHc', 'content': [ContentBlock(text='4', type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=28, output_tokens=5)})" ] }, - "execution_count": 14, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -249,18 +241,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "2a101418", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content=\" As Dario Amodei told me, here are some ways to use Anthropic:\\n\\n- Sign up for an account on Anthropic's website to access tools like Claude, Constitutional AI, and Writer. \\n\\n- Use Claude for tasks like email generation, customer service chat, and QA. Claude can understand natural language prompts and provide helpful responses.\\n\\n- Use Constitutional AI if you need an AI assistant that is harmless, honest, and helpful. It is designed to be safe and aligned with human values.\\n\\n- Use Writer to generate natural language content for things like marketing copy, stories, reports, and more. Give it a topic and prompt and it will create high-quality written content.\\n\\n- Check out Anthropic's documentation and blog for tips, tutorials, examples, and announcements about new capabilities as they continue to develop their AI technology.\\n\\n- Follow Anthropic on social media or subscribe to their newsletter to stay up to date on new features and releases.\\n\\n- For most people, the easiest way to leverage Anthropic's technology is through their website - just create an account to get started!\", additional_kwargs={}, example=False)" + "AIMessage(content=\"As Dario Amodei told me, to use Anthropic, you should first familiarize yourself with our mission and principles. Anthropic is committed to developing safe and beneficial artificial intelligence that can help solve important problems facing humanity. \\n\\nTo get started, I recommend exploring the resources on our website, which cover our research, products, and approach to AI development. You can also reach out to our team to learn more about how Anthropic's technology and services can support your specific needs.\\n\\nThe key is to engage with us in a way that aligns with our values of transparency, ethical AI, and a commitment to the wellbeing of humanity. We're here to collaborate and help you harness the power of advanced AI responsibly.\", response_metadata={'id': 'msg_0187BVnpniPDJnVvwf3M1LdY', 'content': [ContentBlock(text=\"As Dario Amodei told me, to use Anthropic, you should first familiarize yourself with our mission and principles. Anthropic is committed to developing safe and beneficial artificial intelligence that can help solve important problems facing humanity. \\n\\nTo get started, I recommend exploring the resources on our website, which cover our research, products, and approach to AI development. You can also reach out to our team to learn more about how Anthropic's technology and services can support your specific needs.\\n\\nThe key is to engage with us in a way that aligns with our values of transparency, ethical AI, and a commitment to the wellbeing of humanity. We're here to collaborate and help you harness the power of advanced AI responsibly.\", type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=53, output_tokens=160)})" ] }, + "execution_count": 9, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ @@ -277,18 +270,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "8d8caf9b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content=' As Harrison Chase told me, here is how you use LangChain:\\n\\nLangChain is an AI assistant that can have conversations, answer questions, and generate text. To use LangChain, you simply type or speak your input and LangChain will respond. \\n\\nYou can ask LangChain questions, have discussions, get summaries or explanations about topics, and request it to generate text on a subject. Some examples of interactions:\\n\\n- Ask general knowledge questions and LangChain will try to answer factually. For example \"What is the capital of France?\"\\n\\n- Have conversations on topics by taking turns speaking. You can prompt the start of a conversation by saying something like \"Let\\'s discuss machine learning\"\\n\\n- Ask for summaries or high-level explanations on subjects. For example \"Can you summarize the main themes in Shakespeare\\'s Hamlet?\" \\n\\n- Give creative writing prompts or requests to have LangChain generate text in different styles. For example \"Write a short children\\'s story about a mouse\" or \"Generate a poem in the style of Robert Frost about nature\"\\n\\n- Correct LangChain if it makes an inaccurate statement and provide the right information. This helps train it.\\n\\nThe key is interacting naturally and giving it clear prompts and requests', additional_kwargs={}, example=False)" + "AIMessage(content=\"As Harrison Chase told me, using LangChain involves several key steps. First, you'll need to install the LangChain library and import the necessary modules. Then, you'll want to define your language model, any data sources you plan to use, and the specific tasks you want to accomplish, such as question answering, text generation, or agent-based reasoning. \\n\\nLangChain provides a flexible framework for building applications that leverage large language models. It includes abstractions for things like retrievers, prompts, and chains, which allow you to compose different components together to create powerful workflows. \\n\\nThe documentation on the LangChain website is excellent and covers many common use cases in detail. I'd recommend starting there to get a solid understanding of the core concepts and how to apply them to your specific needs. And of course, feel free to reach out if you have any other questions - I'm always happy to share more insights from my conversations with Harrison.\", response_metadata={'id': 'msg_01T1naS99wGPkEAP4LME8iAv', 'content': [ContentBlock(text=\"As Harrison Chase told me, using LangChain involves several key steps. First, you'll need to install the LangChain library and import the necessary modules. Then, you'll want to define your language model, any data sources you plan to use, and the specific tasks you want to accomplish, such as question answering, text generation, or agent-based reasoning. \\n\\nLangChain provides a flexible framework for building applications that leverage large language models. It includes abstractions for things like retrievers, prompts, and chains, which allow you to compose different components together to create powerful workflows. \\n\\nThe documentation on the LangChain website is excellent and covers many common use cases in detail. I'd recommend starting there to get a solid understanding of the core concepts and how to apply them to your specific needs. And of course, feel free to reach out if you have any other questions - I'm always happy to share more insights from my conversations with Harrison.\", type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=50, output_tokens=205)})" ] }, + "execution_count": 10, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ @@ -297,23 +291,150 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "26159af7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "AIMessage(content=' 2 + 2 = 4', additional_kwargs={}, example=False)" + "AIMessage(content='4', response_metadata={'id': 'msg_01T6T3TS6hRCtU8JayN93QEi', 'content': [ContentBlock(text='4', type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=28, output_tokens=5)})" ] }, + "execution_count": 11, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ "full_chain.invoke({\"question\": \"whats 2 + 2\"})" ] + }, + { + "cell_type": "markdown", + "id": "fa0f589d", + "metadata": {}, + "source": [ + "# Routing by semantic similarity\n", + "\n", + "One especially useful technique is to use embeddings to route a query to the most relevant prompt. Here's an example." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a23457d7", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.utils.math import cosine_similarity\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.prompts import PromptTemplate\n", + "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n", + "from langchain_openai import OpenAIEmbeddings\n", + "\n", + "physics_template = \"\"\"You are a very smart physics professor. \\\n", + "You are great at answering questions about physics in a concise and easy to understand manner. \\\n", + "When you don't know the answer to a question you admit that you don't know.\n", + "\n", + "Here is a question:\n", + "{query}\"\"\"\n", + "\n", + "math_template = \"\"\"You are a very good mathematician. You are great at answering math questions. \\\n", + "You are so good because you are able to break down hard problems into their component parts, \\\n", + "answer the component parts, and then put them together to answer the broader question.\n", + "\n", + "Here is a question:\n", + "{query}\"\"\"\n", + "\n", + "embeddings = OpenAIEmbeddings()\n", + "prompt_templates = [physics_template, math_template]\n", + "prompt_embeddings = embeddings.embed_documents(prompt_templates)\n", + "\n", + "\n", + "def prompt_router(input):\n", + " query_embedding = embeddings.embed_query(input[\"query\"])\n", + " similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]\n", + " most_similar = prompt_templates[similarity.argmax()]\n", + " print(\"Using MATH\" if most_similar == math_template else \"Using PHYSICS\")\n", + " return PromptTemplate.from_template(most_similar)\n", + "\n", + "\n", + "chain = (\n", + " {\"query\": RunnablePassthrough()}\n", + " | RunnableLambda(prompt_router)\n", + " | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n", + " | StrOutputParser()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "664bb851", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using PHYSICS\n", + "As a physics professor, I would be happy to provide a concise and easy-to-understand explanation of what a black hole is.\n", + "\n", + "A black hole is an incredibly dense region of space-time where the gravitational pull is so strong that nothing, not even light, can escape from it. This means that if you were to get too close to a black hole, you would be pulled in and crushed by the intense gravitational forces.\n", + "\n", + "The formation of a black hole occurs when a massive star, much larger than our Sun, reaches the end of its life and collapses in on itself. This collapse causes the matter to become extremely dense, and the gravitational force becomes so strong that it creates a point of no return, known as the event horizon.\n", + "\n", + "Beyond the event horizon, the laws of physics as we know them break down, and the intense gravitational forces create a singularity, which is a point of infinite density and curvature in space-time.\n", + "\n", + "Black holes are fascinating and mysterious objects, and there is still much to be learned about their properties and behavior. If I were unsure about any specific details or aspects of black holes, I would readily admit that I do not have a complete understanding and would encourage further research and investigation.\n" + ] + } + ], + "source": [ + "print(chain.invoke(\"What's a black hole\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "df34e469", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using MATH\n", + "A path integral is a powerful mathematical concept in physics, particularly in the field of quantum mechanics. It was developed by the renowned physicist Richard Feynman as an alternative formulation of quantum mechanics.\n", + "\n", + "In a path integral, instead of considering a single, definite path that a particle might take from one point to another, as in classical mechanics, the particle is considered to take all possible paths simultaneously. Each path is assigned a complex-valued weight, and the total probability amplitude for the particle to go from one point to another is calculated by summing (integrating) over all possible paths.\n", + "\n", + "The key ideas behind the path integral formulation are:\n", + "\n", + "1. Superposition principle: In quantum mechanics, particles can exist in a superposition of multiple states or paths simultaneously.\n", + "\n", + "2. Probability amplitude: The probability amplitude for a particle to go from one point to another is calculated by summing the complex-valued weights of all possible paths.\n", + "\n", + "3. Weighting of paths: Each path is assigned a weight based on the action (the time integral of the Lagrangian) along that path. Paths with lower action have a greater weight.\n", + "\n", + "4. Feynman's approach: Feynman developed the path integral formulation as an alternative to the traditional wave function approach in quantum mechanics, providing a more intuitive and conceptual understanding of quantum phenomena.\n", + "\n", + "The path integral approach is particularly useful in quantum field theory, where it provides a powerful framework for calculating transition probabilities and understanding the behavior of quantum systems. It has also found applications in various areas of physics, such as condensed matter, statistical mechanics, and even in finance (the path integral approach to option pricing).\n", + "\n", + "The mathematical construction of the path integral involves the use of advanced concepts from functional analysis and measure theory, making it a powerful and sophisticated tool in the physicist's arsenal.\n" + ] + } + ], + "source": [ + "print(chain.invoke(\"What's a path integral\"))" + ] + }, + { + "cell_type": "markdown", + "id": "927b7498", + "metadata": {}, + "source": [] } ], "metadata": { @@ -332,7 +453,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.1" + "version": "3.10.5" } }, "nbformat": 4, diff --git a/docs/docs/expression_language/index.mdx b/docs/docs/expression_language/index.mdx index 083e899433..e204d29ce0 100644 --- a/docs/docs/expression_language/index.mdx +++ b/docs/docs/expression_language/index.mdx @@ -7,27 +7,27 @@ sidebar_class_name: hidden LangChain Expression Language, or LCEL, is a declarative way to easily compose chains together. LCEL was designed from day 1 to **support putting prototypes in production, with no code changes**, from the simplest β€œprompt + LLM” chain to the most complex chains (we’ve seen folks successfully run LCEL chains with 100s of steps in production). To highlight a few of the reasons you might want to use LCEL: -**Streaming support** +[**First-class streaming support**](/docs/expression_language/streaming) When you build your chains with LCEL you get the best possible time-to-first-token (time elapsed until the first chunk of output comes out). For some chains this means eg. we stream tokens straight from an LLM to a streaming output parser, and you get back parsed, incremental chunks of output at the same rate as the LLM provider outputs the raw tokens. -**Async support** +[**Async support**](/docs/expression_language/interface) Any chain built with LCEL can be called both with the synchronous API (eg. in your Jupyter notebook while prototyping) as well as with the asynchronous API (eg. in a [LangServe](/docs/langsmith) server). This enables using the same code for prototypes and in production, with great performance, and the ability to handle many concurrent requests in the same server. -**Optimized parallel execution** +[**Optimized parallel execution**](/docs/expression_language/primitives/parallel) Whenever your LCEL chains have steps that can be executed in parallel (eg if you fetch documents from multiple retrievers) we automatically do it, both in the sync and the async interfaces, for the smallest possible latency. -**Retries and fallbacks** +[**Retries and fallbacks**](/docs/guides/productionization/fallbacks) Configure retries and fallbacks for any part of your LCEL chain. This is a great way to make your chains more reliable at scale. We’re currently working on adding streaming support for retries/fallbacks, so you can get the added reliability without any latency cost. -**Access intermediate results** +[**Access intermediate results**](/docs/expression_language/interface#async-stream-events-beta) For more complex chains it’s often very useful to access the results of intermediate steps even before the final output is produced. This can be used to let end-users know something is happening, or even just to debug your chain. You can stream intermediate results, and it’s available on every [LangServe](/docs/langserve) server. -**Input and output schemas** +[**Input and output schemas**](/docs/expression_language/interface#input-schema) Input and output schemas give every LCEL chain Pydantic and JSONSchema schemas inferred from the structure of your chain. This can be used for validation of inputs and outputs, and is an integral part of LangServe. -**Seamless LangSmith tracing integration** +[**Seamless LangSmith tracing**](/docs/langsmith) As your chains get more and more complex, it becomes increasingly important to understand what exactly is happening at every step. With LCEL, **all** steps are automatically logged to [LangSmith](/docs/langsmith/) for maximum observability and debuggability. -**Seamless LangServe deployment integration** +[**Seamless LangServe deployment**](/docs/langserve) Any chain created with LCEL can be easily deployed using [LangServe](/docs/langserve). diff --git a/docs/docs/expression_language/interface.ipynb b/docs/docs/expression_language/interface.ipynb index a0e63966af..8cb8c8b8a7 100644 --- a/docs/docs/expression_language/interface.ipynb +++ b/docs/docs/expression_language/interface.ipynb @@ -7,7 +7,7 @@ "source": [ "---\n", "sidebar_position: 1\n", - "title: Interface\n", + "title: Runnable interface\n", "---" ] }, @@ -16,7 +16,8 @@ "id": "9a9acd2e", "metadata": {}, "source": [ - "To make it as easy as possible to create custom chains, we've implemented a [\"Runnable\"](https://api.python.langchain.com/en/stable/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable) protocol. The `Runnable` protocol is implemented for most components. \n", + "To make it as easy as possible to create custom chains, we've implemented a [\"Runnable\"](https://api.python.langchain.com/en/stable/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable) protocol. Many LangChain components implement the `Runnable` protocol, including chat models, LLMs, output parsers, retrievers, prompt templates, and more. There are also several useful primitives for working with runnables, which you can read about [in this section](/docs/expression_language/primitives).\n", + "\n", "This is a standard interface, which makes it easy to define custom chains as well as invoke them in a standard way. \n", "The standard interface includes:\n", "\n", @@ -52,9 +53,11 @@ ] }, { - "cell_type": "raw", + "cell_type": "code", + "execution_count": null, "id": "57768739", "metadata": {}, + "outputs": [], "source": [ "%pip install --upgrade --quiet langchain-core langchain-community langchain-openai" ] diff --git a/docs/docs/expression_language/primitives/assign.ipynb b/docs/docs/expression_language/primitives/assign.ipynb new file mode 100644 index 0000000000..f99d39ca15 --- /dev/null +++ b/docs/docs/expression_language/primitives/assign.ipynb @@ -0,0 +1,180 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 6\n", + "title: \"Assign: Add values to state\"\n", + "keywords: [RunnablePassthrough, assign, LCEL]\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Adding values to chain state\n", + "\n", + "The `RunnablePassthrough.assign(...)` static method takes an input value and adds the extra arguments passed to the assign function.\n", + "\n", + "This is useful when additively creating a dictionary to use as input to a later step, which is a common LCEL pattern.\n", + "\n", + "Here's an example:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mWARNING: You are using pip version 22.0.4; however, version 24.0 is available.\n", + "You should consider upgrading via the '/Users/jacoblee/.pyenv/versions/3.10.5/bin/python -m pip install --upgrade pip' command.\u001b[0m\u001b[33m\n", + "\u001b[0mNote: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install --upgrade --quiet langchain langchain-openai" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'extra': {'num': 1, 'mult': 3}, 'modified': 2}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n", + "\n", + "runnable = RunnableParallel(\n", + " extra=RunnablePassthrough.assign(mult=lambda x: x[\"num\"] * 3),\n", + " modified=lambda x: x[\"num\"] + 1,\n", + ")\n", + "\n", + "runnable.invoke({\"num\": 1})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's break down what's happening here.\n", + "\n", + "- The input to the chain is `{\"num\": 1}`. This is passed into a `RunnableParallel`, which invokes the runnables it is passed in parallel with that input.\n", + "- The value under the `extra` key is invoked. `RunnablePassthrough.assign()` keeps the original keys in the input dict (`{\"num\": 1}`), and assigns a new key called `mult`. The value is `lambda x: x[\"num\"] * 3)`, which is `3`. Thus, the result is `{\"num\": 1, \"mult\": 3}`.\n", + "- `{\"num\": 1, \"mult\": 3}` is returned to the `RunnableParallel` call, and is set as the value to the key `extra`.\n", + "- At the same time, the `modified` key is called. The result is `2`, since the lambda extracts a key called `\"num\"` from its input and adds one.\n", + "\n", + "Thus, the result is `{'extra': {'num': 1, 'mult': 3}, 'modified': 2}`.\n", + "\n", + "## Streaming\n", + "\n", + "One nice feature of this method is that it allows values to pass through as soon as they are available. To show this off, we'll use `RunnablePassthrough.assign()` to immediately return source docs in a retrieval chain:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'question': 'where did harrison work?'}\n", + "{'context': [Document(page_content='harrison worked at kensho')]}\n", + "{'output': ''}\n", + "{'output': 'H'}\n", + "{'output': 'arrison'}\n", + "{'output': ' worked'}\n", + "{'output': ' at'}\n", + "{'output': ' Kens'}\n", + "{'output': 'ho'}\n", + "{'output': '.'}\n", + "{'output': ''}\n" + ] + } + ], + "source": [ + "from langchain_community.vectorstores import FAISS\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnablePassthrough\n", + "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n", + "\n", + "vectorstore = FAISS.from_texts(\n", + " [\"harrison worked at kensho\"], embedding=OpenAIEmbeddings()\n", + ")\n", + "retriever = vectorstore.as_retriever()\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", + "model = ChatOpenAI()\n", + "\n", + "generation_chain = prompt | model | StrOutputParser()\n", + "\n", + "retrieval_chain = {\n", + " \"context\": retriever,\n", + " \"question\": RunnablePassthrough(),\n", + "} | RunnablePassthrough.assign(output=generation_chain)\n", + "\n", + "stream = retrieval_chain.stream(\"where did harrison work?\")\n", + "\n", + "for chunk in stream:\n", + " print(chunk)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the first chunk contains the original `\"question\"` since that is immediately available. The second chunk contains `\"context\"` since the retriever finishes second. Finally, the output from the `generation_chain` streams in chunks as soon as it is available." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/docs/expression_language/how_to/binding.ipynb b/docs/docs/expression_language/primitives/binding.ipynb similarity index 95% rename from docs/docs/expression_language/how_to/binding.ipynb rename to docs/docs/expression_language/primitives/binding.ipynb index fe25f1a3bc..2961107fbc 100644 --- a/docs/docs/expression_language/how_to/binding.ipynb +++ b/docs/docs/expression_language/primitives/binding.ipynb @@ -1,13 +1,25 @@ { "cells": [ + { + "cell_type": "raw", + "id": "fe63ffaf", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 2\n", + "title: \"Binding: Attach runtime args\"\n", + "keywords: [RunnableBinding, LCEL]\n", + "---" + ] + }, { "cell_type": "markdown", "id": "711752cb-4f15-42a3-9838-a0c67f397771", "metadata": {}, "source": [ - "# Bind runtime args\n", + "# Binding: Attach runtime args\n", "\n", - "Sometimes we want to invoke a Runnable within a Runnable sequence with constant arguments that are not part of the output of the preceding Runnable in the sequence, and which are not part of the user input. We can use `Runnable.bind()` to easily pass these arguments in.\n", + "Sometimes we want to invoke a Runnable within a Runnable sequence with constant arguments that are not part of the output of the preceding Runnable in the sequence, and which are not part of the user input. We can use `Runnable.bind()` to pass these arguments in.\n", "\n", "Suppose we have a simple prompt + model sequence:" ] diff --git a/docs/docs/expression_language/how_to/configure.ipynb b/docs/docs/expression_language/primitives/configure.ipynb similarity index 98% rename from docs/docs/expression_language/how_to/configure.ipynb rename to docs/docs/expression_language/primitives/configure.ipynb index f0521d102a..f5e04a3041 100644 --- a/docs/docs/expression_language/how_to/configure.ipynb +++ b/docs/docs/expression_language/primitives/configure.ipynb @@ -1,5 +1,17 @@ { "cells": [ + { + "cell_type": "raw", + "id": "9ede5870", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 7\n", + "title: \"Configure runtime chain internals\"\n", + "keywords: [ConfigurableField, configurable_fields, ConfigurableAlternatives, configurable_alternatives, LCEL]\n", + "---" + ] + }, { "cell_type": "markdown", "id": "39eaf61b", diff --git a/docs/docs/expression_language/how_to/generators.ipynb b/docs/docs/expression_language/primitives/functions.ipynb similarity index 52% rename from docs/docs/expression_language/how_to/generators.ipynb rename to docs/docs/expression_language/primitives/functions.ipynb index e43f607444..93dc7c7bc2 100644 --- a/docs/docs/expression_language/how_to/generators.ipynb +++ b/docs/docs/expression_language/primitives/functions.ipynb @@ -1,52 +1,207 @@ { "cells": [ + { + "cell_type": "raw", + "id": "ce0e08fd", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 3\n", + "title: \"Lambda: Run custom functions\"\n", + "keywords: [RunnableLambda, LCEL]\n", + "---" + ] + }, { "cell_type": "markdown", + "id": "fbc4bf6e", "metadata": {}, "source": [ - "# Stream custom generator functions\n", + "# Run custom functions\n", "\n", - "You can use generator functions (ie. functions that use the `yield` keyword, and behave like iterators) in a LCEL pipeline.\n", + "You can use arbitrary functions in the pipeline.\n", "\n", - "The signature of these generators should be `Iterator[Input] -> Iterator[Output]`. Or for async generators: `AsyncIterator[Input] -> AsyncIterator[Output]`.\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": "raw", + "id": "9a5fe916", + "metadata": {}, + "source": [ + "%pip install --upgrade --quiet langchain langchain-openai" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6bb221b3", + "metadata": {}, + "outputs": [], + "source": [ + "from operator import itemgetter\n", + "\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnableLambda\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "\n", + "def length_function(text):\n", + " return len(text)\n", + "\n", + "\n", + "def _multiple_length_function(text1, text2):\n", + " return len(text1) * len(text2)\n", "\n", - "These are useful for:\n", - "- implementing a custom output parser\n", - "- modifying the output of a previous step, while preserving streaming capabilities\n", "\n", - "Let's implement a custom output parser for comma-separated lists." + "def multiple_length_function(_dict):\n", + " return _multiple_length_function(_dict[\"text1\"], _dict[\"text2\"])\n", + "\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\"what is {a} + {b}\")\n", + "model = ChatOpenAI()\n", + "\n", + "chain1 = prompt | model\n", + "\n", + "chain = (\n", + " {\n", + " \"a\": itemgetter(\"foo\") | RunnableLambda(length_function),\n", + " \"b\": {\"text1\": itemgetter(\"foo\"), \"text2\": itemgetter(\"bar\")}\n", + " | RunnableLambda(multiple_length_function),\n", + " }\n", + " | prompt\n", + " | model\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "5488ec85", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='3 + 9 = 12', response_metadata={'token_usage': {'completion_tokens': 7, 'prompt_tokens': 14, 'total_tokens': 21}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None}, id='run-bd204541-81fd-429a-ad92-dd1913af9b1c-0')" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"foo\": \"bar\", \"bar\": \"gah\"})" ] }, { "cell_type": "markdown", + "id": "4728ddd9-914d-42ce-ae9b-72c9ce8ec940", "metadata": {}, "source": [ - "## Sync version" + "## Accepting a Runnable Config\n", + "\n", + "Runnable lambdas can optionally accept a [RunnableConfig](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.config.RunnableConfig.html#langchain_core.runnables.config.RunnableConfig), which they can use to pass callbacks, tags, and other configuration information to nested runs." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, + "id": "80b3b5f6-5d58-44b9-807e-cce9a46bf49f", "metadata": {}, "outputs": [], "source": [ - "%pip install --upgrade --quiet langchain langchain-openai" + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.runnables import RunnableConfig" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 4, + "id": "ff0daf0c-49dd-4d21-9772-e5fa133c5f36", "metadata": {}, "outputs": [], "source": [ - "from typing import Iterator, List\n", + "import json\n", "\n", - "from langchain.prompts.chat import ChatPromptTemplate\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_openai import ChatOpenAI\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": 5, + "id": "1a5e709e-9d75-48c7-bb9c-503251990505", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'foo': 'bar'}\n", + "Tokens Used: 62\n", + "\tPrompt Tokens: 56\n", + "\tCompletion Tokens: 6\n", + "Successful Requests: 1\n", + "Total Cost (USD): $9.6e-05\n" + ] + } + ], + "source": [ + "from langchain_community.callbacks import get_openai_callback\n", + "\n", + "with get_openai_callback() as cb:\n", + " output = RunnableLambda(parse_or_fix).invoke(\n", + " \"{foo: bar}\", {\"tags\": [\"my-tag\"], \"callbacks\": [cb]}\n", + " )\n", + " print(output)\n", + " print(cb)" + ] + }, + { + "cell_type": "markdown", + "id": "922b48bd", + "metadata": {}, + "source": [ + "# Streaming\n", + "\n", + "You can use generator functions (ie. functions that use the `yield` keyword, and behave like iterators) in a LCEL pipeline.\n", + "\n", + "The signature of these generators should be `Iterator[Input] -> Iterator[Output]`. Or for async generators: `AsyncIterator[Input] -> AsyncIterator[Output]`.\n", + "\n", + "These are useful for:\n", + "- implementing a custom output parser\n", + "- modifying the output of a previous step, while preserving streaming capabilities\n", + "\n", + "Here's an example of a custom output parser for comma-separated lists:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "29f55c38", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Iterator, List\n", "\n", "prompt = ChatPromptTemplate.from_template(\n", - " \"Write a comma-separated list of 5 animals similar to: {animal}\"\n", + " \"Write a comma-separated list of 5 animals similar to: {animal}. Do not include numbers\"\n", ")\n", "model = ChatOpenAI(temperature=0.0)\n", "\n", @@ -55,7 +210,8 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 7, + "id": "75aa946b", "metadata": {}, "outputs": [ { @@ -73,7 +229,8 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, + "id": "d002a7fe", "metadata": {}, "outputs": [ { @@ -82,7 +239,7 @@ "'lion, tiger, wolf, gorilla, panda'" ] }, - "execution_count": 3, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -93,7 +250,8 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 9, + "id": "f08b8a5b", "metadata": {}, "outputs": [], "source": [ @@ -119,7 +277,8 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 10, + "id": "02e414aa", "metadata": {}, "outputs": [], "source": [ @@ -128,7 +287,8 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 11, + "id": "7ed8799d", "metadata": {}, "outputs": [ { @@ -150,16 +310,17 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 12, + "id": "9ea4ddc6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['lion', 'tiger', 'wolf', 'gorilla', 'panda']" + "['lion', 'tiger', 'wolf', 'gorilla', 'elephant']" ] }, - "execution_count": 7, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -170,6 +331,7 @@ }, { "cell_type": "markdown", + "id": "96e320ed", "metadata": {}, "source": [ "## Async version" @@ -177,7 +339,8 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 13, + "id": "569dbbef", "metadata": {}, "outputs": [], "source": [ @@ -204,7 +367,8 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 14, + "id": "7a76b713", "metadata": {}, "outputs": [ { @@ -226,7 +390,8 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 15, + "id": "3a650482", "metadata": {}, "outputs": [ { @@ -235,7 +400,7 @@ "['lion', 'tiger', 'wolf', 'gorilla', 'panda']" ] }, - "execution_count": 10, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -261,9 +426,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.10.5" } }, "nbformat": 4, - "nbformat_minor": 4 + "nbformat_minor": 5 } diff --git a/docs/docs/expression_language/primitives/index.mdx b/docs/docs/expression_language/primitives/index.mdx new file mode 100644 index 0000000000..ecf99c2fbc --- /dev/null +++ b/docs/docs/expression_language/primitives/index.mdx @@ -0,0 +1,15 @@ +--- +sidebar_class_name: hidden +--- + +# Primitives + +In addition to various [components](/docs/modules) that are usable with LCEL, LangChain also includes various primitives +that help pass around and format data, bind arguments, invoke custom logic, and more. + +This section goes into greater depth on where and how some of these components are useful. + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from '@docusaurus/theme-common'; + + item.href !== "/docs/expression_language/primitives/")} /> \ No newline at end of file diff --git a/docs/docs/expression_language/how_to/map.ipynb b/docs/docs/expression_language/primitives/parallel.ipynb similarity index 92% rename from docs/docs/expression_language/how_to/map.ipynb rename to docs/docs/expression_language/primitives/parallel.ipynb index 67eefe5897..8e3f636fd7 100644 --- a/docs/docs/expression_language/how_to/map.ipynb +++ b/docs/docs/expression_language/primitives/parallel.ipynb @@ -6,8 +6,8 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 0\n", - "title: \"RunnableParallel: Manipulating data\"\n", + "sidebar_position: 1\n", + "title: \"Parallel: Format data\"\n", "keywords: [RunnableParallel, RunnableMap, LCEL]\n", "---" ] @@ -17,13 +17,13 @@ "id": "b022ab74-794d-4c54-ad47-ff9549ddb9d2", "metadata": {}, "source": [ - "# Manipulating inputs & output\n", + "# Formatting inputs & output\n", "\n", - "RunnableParallel can be useful for manipulating the output of one Runnable to match the input format of the next Runnable in a sequence.\n", + "The `RunnableParallel` primitive is essentially a dict whose values are runnables (or things that can be coerced to runnables, like functions). It runs all of its values in parallel, and each value is called with the overall input of the `RunnableParallel`. The final return value is a dict with the results of each value under its appropriate key.\n", "\n", - "Here the input to prompt is expected to be a map with keys \"context\" and \"question\". The user input is just the question. So we need to get the context using our retriever and passthrough the user input under the \"question\" key.\n", + "It is useful for parallelizing operations, but can also be useful for manipulating the output of one Runnable to match the input format of the next Runnable in a sequence.\n", "\n", - "\n" + "Here the input to prompt is expected to be a map with keys \"context\" and \"question\". The user input is just the question. So we need to get the context using our retriever and passthrough the user input under the \"question\" key.\n" ] }, { diff --git a/docs/docs/expression_language/how_to/passthrough.ipynb b/docs/docs/expression_language/primitives/passthrough.ipynb similarity index 75% rename from docs/docs/expression_language/how_to/passthrough.ipynb rename to docs/docs/expression_language/primitives/passthrough.ipynb index d5dba8a2cb..b21d04317a 100644 --- a/docs/docs/expression_language/how_to/passthrough.ipynb +++ b/docs/docs/expression_language/primitives/passthrough.ipynb @@ -1,14 +1,14 @@ { "cells": [ { - "cell_type": "markdown", + "cell_type": "raw", "id": "d35de667-0352-4bfb-a890-cebe7f676fe7", "metadata": {}, "source": [ "---\n", - "sidebar_position: 1\n", - "title: \"RunnablePassthrough: Passing data through\"\n", - "keywords: [RunnablePassthrough, RunnableParallel, LCEL]\n", + "sidebar_position: 5\n", + "title: \"Passthrough: Pass through inputs\"\n", + "keywords: [RunnablePassthrough, LCEL]\n", "---" ] }, @@ -19,11 +19,7 @@ "source": [ "# Passing data through\n", "\n", - "RunnablePassthrough allows to pass inputs unchanged or with the addition of extra keys. This typically is used in conjuction with RunnableParallel to assign data to a new key in the map. \n", - "\n", - "RunnablePassthrough() called on it's own, will simply take the input and pass it through. \n", - "\n", - "RunnablePassthrough called with assign (`RunnablePassthrough.assign(...)`) will take the input, and will add the extra arguments passed to the assign function. \n", + "RunnablePassthrough on its own allows you to pass inputs unchanged. This typically is used in conjuction with RunnableParallel to pass data through to a new key in the map. \n", "\n", "See the example below:" ] @@ -60,7 +56,6 @@ "\n", "runnable = RunnableParallel(\n", " passed=RunnablePassthrough(),\n", - " extra=RunnablePassthrough.assign(mult=lambda x: x[\"num\"] * 3),\n", " modified=lambda x: x[\"num\"] + 1,\n", ")\n", "\n", @@ -74,9 +69,7 @@ "source": [ "As seen above, `passed` key was called with `RunnablePassthrough()` and so it simply passed on `{'num': 1}`. \n", "\n", - "In the second line, we used `RunnablePastshrough.assign` with a lambda that multiplies the numerical value by 3. In this cased, `extra` was set with `{'num': 1, 'mult': 3}` which is the original value with the `mult` key added. \n", - "\n", - "Finally, we also set a third key in the map with `modified` which uses a lambda to set a single value adding 1 to the num, which resulted in `modified` key with the value of `2`." + "We also set a second key in the map with `modified`. This uses a lambda to set a single value adding 1 to the num, which resulted in `modified` key with the value of `2`." ] }, { @@ -86,7 +79,7 @@ "source": [ "## Retrieval Example\n", "\n", - "In the example below, we see a use case where we use RunnablePassthrough along with RunnableMap. " + "In the example below, we see a use case where we use `RunnablePassthrough` along with `RunnableParallel`. " ] }, { diff --git a/docs/docs/expression_language/primitives/sequence.ipynb b/docs/docs/expression_language/primitives/sequence.ipynb new file mode 100644 index 0000000000..8aec2b496c --- /dev/null +++ b/docs/docs/expression_language/primitives/sequence.ipynb @@ -0,0 +1,243 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 0\n", + "title: \"Sequences: Chaining runnables\"\n", + "keywords: [Runnable, Runnables, LCEL]\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chaining runnables\n", + "\n", + "One key advantage of the `Runnable` interface is that any two runnables can be \"chained\" together into sequences. The output of the previous runnable's `.invoke()` call is passed as input to the next runnable. This can be done using the pipe operator (`|`), or the more explicit `.pipe()` method, which does the same thing. The resulting `RunnableSequence` is itself a runnable, which means it can be invoked, streamed, or piped just like any other runnable.\n", + "\n", + "## The pipe operator\n", + "\n", + "To show off how this works, let's go through an example. We'll walk through a common pattern in LangChain: using a [prompt template](/docs/modules/model_io/prompts/) to format input into a [chat model](/docs/modules/model_io/chat/), and finally converting the chat message output into a string with an [output parser](/docs/modules/model_io/output_parsers/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%pip install --upgrade --quiet langchain langchain-anthropic" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_anthropic import ChatAnthropic\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\"tell me a joke about {topic}\")\n", + "model = ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n", + "\n", + "chain = prompt | model | StrOutputParser()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Prompts and models are both runnable, and the output type from the prompt call is the same as the input type of the chat model, so we can chain them together. We can then invoke the resulting sequence like any other runnable:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Here's a bear joke for you:\\n\\nWhy don't bears wear socks? \\nBecause they have bear feet!\\n\\nHow's that? I tried to keep it light and silly. Bears can make for some fun puns and jokes. Let me know if you'd like to hear another one!\"" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke({\"topic\": \"bears\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Coercion\n", + "\n", + "We can even combine this chain with more runnables to create another chain. This may involve some input/output formatting using other types of runnables, depending on the required inputs and outputs of the chain components.\n", + "\n", + "For example, let's say we wanted to compose the joke generating chain with another chain that evaluates whether or not the generated joke was funny.\n", + "\n", + "We would need to be careful with how we format the input into the next chain. In the below example, the dict in the chain is automatically parsed and converted into a [`RunnableParallel`](/docs/expression_language/primitives/parallel), which runs all of its values in parallel and returns a dict with the results.\n", + "\n", + "This happens to be the same format the next prompt template expects. Here it is in action:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.output_parsers import StrOutputParser\n", + "\n", + "analysis_prompt = ChatPromptTemplate.from_template(\"is this a funny joke? {joke}\")\n", + "\n", + "composed_chain = {\"joke\": chain} | analysis_prompt | model | StrOutputParser()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"That's a pretty classic and well-known bear pun joke. Whether it's considered funny is quite subjective, as humor is very personal. Some people may find that type of pun-based joke amusing, while others may not find it that humorous. Ultimately, the funniness of a joke is in the eye (or ear) of the beholder. If you enjoyed the joke and got a chuckle out of it, then that's what matters most.\"" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "composed_chain.invoke({\"topic\": \"bears\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Functions will also be coerced into runnables, so you can add custom logic to your chains too. The below chain results in the same logical flow as before:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "composed_chain_with_lambda = (\n", + " chain\n", + " | (lambda input: {\"joke\": input})\n", + " | analysis_prompt\n", + " | model\n", + " | StrOutputParser()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'I appreciate the effort, but I have to be honest - I didn\\'t find that joke particularly funny. Beet-themed puns can be quite hit-or-miss, and this one falls more on the \"miss\" side for me. The premise is a bit too straightforward and predictable. While I can see the logic behind it, the punchline just doesn\\'t pack much of a comedic punch. \\n\\nThat said, I do admire your willingness to explore puns and wordplay around vegetables. Cultivating a good sense of humor takes practice, and not every joke is going to land. The important thing is to keep experimenting and finding what works. Maybe try for a more unexpected or creative twist on beet-related humor next time. But thanks for sharing - I always appreciate when humans test out jokes on me, even if they don\\'t always make me laugh out loud.'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "composed_chain_with_lambda.invoke({\"topic\": \"beets\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, keep in mind that using functions like this may interfere with operations like streaming. See [this section](/docs/expression_language/primitives/functions) for more information." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The `.pipe()` method\n", + "\n", + "We could also compose the same sequence using the `.pipe()` method. Here's what that looks like:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.runnables import RunnableParallel\n", + "\n", + "composed_chain_with_pipe = (\n", + " RunnableParallel({\"joke\": chain})\n", + " .pipe(analysis_prompt)\n", + " .pipe(model)\n", + " .pipe(StrOutputParser())\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'That\\'s a pretty good Battlestar Galactica-themed pun! I appreciated the clever play on words with \"Centurion\" and \"center on.\" It\\'s the kind of nerdy, science fiction-inspired humor that fans of the show would likely enjoy. The joke is clever and demonstrates a good understanding of the Battlestar Galactica universe. I\\'d be curious to hear any other Battlestar-related jokes you might have up your sleeve. As long as they don\\'t reproduce copyrighted material, I\\'m happy to provide my thoughts on the humor and appeal for fans of the show.'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "composed_chain_with_pipe.invoke({\"topic\": \"battlestar galactica\"})" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/docs/expression_language/streaming.ipynb b/docs/docs/expression_language/streaming.ipynb index facb83d462..5e2df61fcf 100644 --- a/docs/docs/expression_language/streaming.ipynb +++ b/docs/docs/expression_language/streaming.ipynb @@ -201,13 +201,23 @@ " print(chunk, end=\"|\", flush=True)" ] }, + { + "cell_type": "markdown", + "id": "868bc412", + "metadata": {}, + "source": [ + "You might notice above that `parser` actually doesn't block the streaming output from the model, and instead processes each chunk individually. Many of the [LCEL primitives](/docs/expression_language/primitives) also support this kind of transform-style passthrough streaming, which can be very convenient when constructing apps.\n", + "\n", + "Certain runnables, like [prompt templates](/docs/modules/model_io/prompts) and [chat models](/docs/modules/model_io/chat), cannot process individual chunks and instead aggregate all previous steps. This will interrupt the streaming process. Custom functions can be [designed to return generators](/docs/expression_language/primitives/functions#streaming), which" + ] + }, { "cell_type": "markdown", "id": "1b399fb4-5e3c-4581-9570-6df9b42b623d", "metadata": {}, "source": [ ":::{.callout-note}\n", - "You do not have to use the `LangChain Expression Language` to use LangChain and can instead rely on a standard **imperative** programming approach by\n", + "If the above functionality is not relevant to what you're building, you do not have to use the `LangChain Expression Language` to use LangChain and can instead rely on a standard **imperative** programming approach by\n", "caling `invoke`, `batch` or `stream` on each component individually, assigning the results to variables and then using them downstream as you see fit.\n", "\n", "If that works for your needs, then that's fine by us πŸ‘Œ!\n", diff --git a/docs/docs/expression_language/why.ipynb b/docs/docs/expression_language/why.ipynb index be492c448d..018d6b0537 100644 --- a/docs/docs/expression_language/why.ipynb +++ b/docs/docs/expression_language/why.ipynb @@ -1,1210 +1,1209 @@ { - "cells": [ - { - "cell_type": "raw", - "id": "bc346658-6820-413a-bd8f-11bd3082fe43", - "metadata": {}, - "source": [ - "---\n", - "sidebar_position: 0.5\n", - "title: Why use LCEL\n", - "---\n", - "\n", - "```{=mdx}\n", - "import { ColumnContainer, Column } from \"@theme/Columns\";\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "919a5ae2-ed21-4923-b98f-723c111bac67", - "metadata": {}, - "source": [ - ":::{.callout-tip} \n", - "We recommend reading the LCEL [Get started](/docs/expression_language/get_started) section first.\n", - ":::" - ] - }, - { - "cell_type": "markdown", - "id": "f331037f-be3f-4782-856f-d55dab952488", - "metadata": {}, - "source": [ - "LCEL makes it easy to build complex chains from basic components. It does this by providing:\n", - "1. **A unified interface**: Every LCEL object implements the `Runnable` interface, which defines a common set of invocation methods (`invoke`, `batch`, `stream`, `ainvoke`, ...). This makes it possible for chains of LCEL objects to also automatically support these invocations. That is, every chain of LCEL objects is itself an LCEL object.\n", - "2. **Composition primitives**: LCEL provides a number of primitives that make it easy to compose chains, parallelize components, add fallbacks, dynamically configure chain internal, and more.\n", - "\n", - "To better understand the value of LCEL, it's helpful to see it in action and think about how we might recreate similar functionality without it. In this walkthrough we'll do just that with our [basic example](/docs/expression_language/get_started#basic_example) from the get started section. We'll take our simple prompt + model chain, which under the hood already defines a lot of functionality, and see what it would take to recreate all of it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b99b47ec", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain-core langchain-openai langchain-anthropic" - ] - }, - { - "cell_type": "markdown", - "id": "e3621b62-a037-42b8-8faa-59575608bb8b", - "metadata": {}, - "source": [ - "## Invoke\n", - "In the simplest case, we just want to pass in a topic string and get back a joke string:\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e628905c-430e-4e4a-9d7c-c91d2f42052e", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import List\n", - "\n", - "import openai\n", - "\n", - "\n", - "prompt_template = \"Tell me a short joke about {topic}\"\n", - "client = openai.OpenAI()\n", - "\n", - "def call_chat_model(messages: List[dict]) -> str:\n", - " response = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\", \n", - " messages=messages,\n", - " )\n", - " return response.choices[0].message.content\n", - "\n", - "def invoke_chain(topic: str) -> str:\n", - " prompt_value = prompt_template.format(topic=topic)\n", - " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", - " return call_chat_model(messages)\n", - "\n", - "invoke_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "cdc3b527-c09e-4c77-9711-c3cc4506cd95", - "metadata": {}, - "source": [ - "\n", - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0d2a7cf8-1bc7-405c-bb0d-f2ab2ba3b6ab", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_openai import ChatOpenAI\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.runnables import RunnablePassthrough\n", - "\n", - "\n", - "prompt = ChatPromptTemplate.from_template(\n", - " \"Tell me a short joke about {topic}\"\n", - ")\n", - "output_parser = StrOutputParser()\n", - "model = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", - "chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt\n", - " | model\n", - " | output_parser\n", - ")\n", - "\n", - "chain.invoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "3c0b0513-77b8-4371-a20e-3e487cec7e7f", - "metadata": {}, - "source": [ - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "## Stream\n", - "If we want to stream results instead, we'll need to change our function:\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4f2cc6dc-d70a-4c13-9258-452f14290da6", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import Iterator\n", - "\n", - "\n", - "def stream_chat_model(messages: List[dict]) -> Iterator[str]:\n", - " stream = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=messages,\n", - " stream=True,\n", - " )\n", - " for response in stream:\n", - " content = response.choices[0].delta.content\n", - " if content is not None:\n", - " yield content\n", - "\n", - "def stream_chain(topic: str) -> Iterator[str]:\n", - " prompt_value = prompt.format(topic=topic)\n", - " return stream_chat_model([{\"role\": \"user\", \"content\": prompt_value}])\n", - "\n", - "\n", - "for chunk in stream_chain(\"ice cream\"):\n", - " print(chunk, end=\"\", flush=True)" - ] - }, - { - "cell_type": "markdown", - "id": "f8e36b0e-c7dc-4130-a51b-189d4b756c7f", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "173e1a9c-2a18-4669-b0de-136f39197786", - "metadata": {}, - "outputs": [], - "source": [ - "for chunk in chain.stream(\"ice cream\"):\n", - " print(chunk, end=\"\", flush=True)" - ] - }, - { - "cell_type": "markdown", - "id": "b9b41e78-ddeb-44d0-a58b-a0ea0c99a761", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "## Batch\n", - "\n", - "If we want to run on a batch of inputs in parallel, we'll again need a new function:\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b492f13-73a6-48ed-8d4f-9ad634da9988", - "metadata": {}, - "outputs": [], - "source": [ - "from concurrent.futures import ThreadPoolExecutor\n", - "\n", - "\n", - "def batch_chain(topics: list) -> list:\n", - " with ThreadPoolExecutor(max_workers=5) as executor:\n", - " return list(executor.map(invoke_chain, topics))\n", - "\n", - "batch_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "9b3e9d34-6775-43c1-93d8-684b58e341ab", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8f55b292-4e97-4d09-8e71-c71b4d853526", - "metadata": {}, - "outputs": [], - "source": [ - "chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "cc5ba36f-eec1-4fc1-8cfe-fa242a7f7809", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "## Async\n", - "\n", - "If we need an asynchronous version:\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eabe6621-e815-41e3-9c9d-5aa561a69835", - "metadata": {}, - "outputs": [], - "source": [ - "async_client = openai.AsyncOpenAI()\n", - "\n", - "async def acall_chat_model(messages: List[dict]) -> str:\n", - " response = await async_client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\", \n", - " messages=messages,\n", - " )\n", - " return response.choices[0].message.content\n", - "\n", - "async def ainvoke_chain(topic: str) -> str:\n", - " prompt_value = prompt_template.format(topic=topic)\n", - " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", - " return await acall_chat_model(messages)\n", - "\n", - "\n", - "await ainvoke_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "2f209290-498c-4c17-839e-ee9002919846", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4d009781-7307-48a4-8439-f9d3dd015560", - "metadata": {}, - "outputs": [], - "source": [ - "await chain.ainvoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "1f282129-99a3-40f4-b67f-2d0718b1bea9", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "## Async Batch\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1933f39d-7bd7-45fa-a6a5-5fb7be8e31ec", - "metadata": {}, - "outputs": [], - "source": [ - "import asyncio\n", - "import openai\n", - "\n", - "\n", - "async def abatch_chain(topics: list) -> list:\n", - " coros = map(ainvoke_chain, topics)\n", - " return await asyncio.gather(*coros)\n", - "\n", - "\n", - "await abatch_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "90691048-17ae-479d-83c2-859e33ddf3eb", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "947dad23-3443-40eb-a03b-7840c261e261", - "metadata": {}, - "outputs": [], - "source": [ - "await chain.abatch([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "f6888245-1ebe-4768-a53b-e1fef6a8b379", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "## LLM instead of chat model\n", - "\n", - "If we want to use a completion endpoint instead of a chat endpoint: \n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9aca946b-acaa-4f7e-a3d0-ad8e3225e7f2", - "metadata": {}, - "outputs": [], - "source": [ - "def call_llm(prompt_value: str) -> str:\n", - " response = client.completions.create(\n", - " model=\"gpt-3.5-turbo-instruct\",\n", - " prompt=prompt_value,\n", - " )\n", - " return response.choices[0].text\n", - "\n", - "def invoke_llm_chain(topic: str) -> str:\n", - " prompt_value = prompt_template.format(topic=topic)\n", - " return call_llm(prompt_value)\n", - "\n", - "invoke_llm_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "45342cd6-58c2-4543-9392-773e05ef06e7", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d56efc0c-88e0-4cf8-a46a-e8e9b9cd6805", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_openai import OpenAI\n", - "\n", - "llm = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n", - "llm_chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt\n", - " | llm\n", - " | output_parser\n", - ")\n", - "\n", - "llm_chain.invoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "ca115eaf-59ef-45c1-aac1-e8b0ce7db250", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "## Different model provider\n", - "\n", - "If we want to use Anthropic instead of OpenAI: \n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cde2ceb0-f65e-487b-9a32-137b0e9d79d5", - "metadata": {}, - "outputs": [], - "source": [ - "import anthropic\n", - "\n", - "anthropic_template = f\"Human:\\n\\n{prompt_template}\\n\\nAssistant:\"\n", - "anthropic_client = anthropic.Anthropic()\n", - "\n", - "def call_anthropic(prompt_value: str) -> str:\n", - " response = anthropic_client.completions.create(\n", - " model=\"claude-2\",\n", - " prompt=prompt_value,\n", - " max_tokens_to_sample=256,\n", - " )\n", - " return response.completion \n", - "\n", - "def invoke_anthropic_chain(topic: str) -> str:\n", - " prompt_value = anthropic_template.format(topic=topic)\n", - " return call_anthropic(prompt_value)\n", - "\n", - "invoke_anthropic_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "52a0c9f8-e316-42e1-af85-cabeba4b7059", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b3b800d1-5954-41a4-80b0-f00a7908961e", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_anthropic import ChatAnthropic\n", - "\n", - "anthropic = ChatAnthropic(model=\"claude-2\")\n", - "anthropic_chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt \n", - " | anthropic\n", - " | output_parser\n", - ")\n", - "\n", - "anthropic_chain.invoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "d7a91eee-d017-420d-b215-f663dcbf8ed2", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "## Runtime configurability\n", - "\n", - "If we wanted to make the choice of chat model or LLM configurable at runtime:\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0ef10e4-8e8e-463a-bd0f-59b0715e79b6", - "metadata": {}, - "outputs": [], - "source": [ - "def invoke_configurable_chain(\n", - " topic: str, \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> str:\n", - " if model == \"chat_openai\":\n", - " return invoke_chain(topic)\n", - " elif model == \"openai\":\n", - " return invoke_llm_chain(topic)\n", - " elif model == \"anthropic\":\n", - " return invoke_anthropic_chain(topic)\n", - " else:\n", - " raise ValueError(\n", - " f\"Received invalid model '{model}'.\"\n", - " \" Expected one of chat_openai, openai, anthropic\"\n", - " )\n", - "\n", - "def stream_configurable_chain(\n", - " topic: str, \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> Iterator[str]:\n", - " if model == \"chat_openai\":\n", - " return stream_chain(topic)\n", - " elif model == \"openai\":\n", - " # Note we haven't implemented this yet.\n", - " return stream_llm_chain(topic)\n", - " elif model == \"anthropic\":\n", - " # Note we haven't implemented this yet\n", - " return stream_anthropic_chain(topic)\n", - " else:\n", - " raise ValueError(\n", - " f\"Received invalid model '{model}'.\"\n", - " \" Expected one of chat_openai, openai, anthropic\"\n", - " )\n", - "\n", - "def batch_configurable_chain(\n", - " topics: List[str], \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> List[str]:\n", - " # You get the idea\n", - " ...\n", - "\n", - "async def abatch_configurable_chain(\n", - " topics: List[str], \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> List[str]:\n", - " ...\n", - "\n", - "invoke_configurable_chain(\"ice cream\", model=\"openai\")\n", - "stream = stream_configurable_chain(\n", - " \"ice_cream\", \n", - " model=\"anthropic\"\n", - ")\n", - "for chunk in stream:\n", - " print(chunk, end=\"\", flush=True)\n", - "\n", - "# batch_configurable_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])\n", - "# await ainvoke_configurable_chain(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "d1530c5c-6635-4599-9483-6df357ca2d64", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### With LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "76809d14-e77a-4125-a2ea-efbebf0b47cc", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.runnables import ConfigurableField\n", - "\n", - "\n", - "configurable_model = model.configurable_alternatives(\n", - " ConfigurableField(id=\"model\"), \n", - " default_key=\"chat_openai\", \n", - " openai=llm,\n", - " anthropic=anthropic,\n", - ")\n", - "configurable_chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt \n", - " | configurable_model \n", - " | output_parser\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4a3d94d0-cd42-4195-80b8-ef2e12503d6f", - "metadata": {}, - "outputs": [], - "source": [ - "configurable_chain.invoke(\n", - " \"ice cream\", \n", - " config={\"model\": \"openai\"}\n", - ")\n", - "stream = configurable_chain.stream(\n", - " \"ice cream\", \n", - " config={\"model\": \"anthropic\"}\n", - ")\n", - "for chunk in stream:\n", - " print(chunk, end=\"\", flush=True)\n", - "\n", - "configurable_chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])\n", - "\n", - "# await configurable_chain.ainvoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "370dd4d7-b825-40c4-ae3c-2693cba2f22a", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "## Logging\n", - "\n", - "If we want to log our intermediate results:\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n", - "We'll `print` intermediate steps for illustrative purposes\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "383a3c51-926d-48c6-b9ae-42bf8f14ecc8", - "metadata": {}, - "outputs": [], - "source": [ - "def invoke_anthropic_chain_with_logging(topic: str) -> str:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = anthropic_template.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " output = call_anthropic(prompt_value)\n", - " print(f\"Output: {output}\")\n", - " return output\n", - "\n", - "invoke_anthropic_chain_with_logging(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "16bd20fd-43cd-4aaf-866f-a53d1f20312d", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### LCEL\n", - "Every component has built-in integrations with LangSmith. If we set the following two environment variables, all chain traces are logged to LangSmith.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6204f21-d2e7-4ac6-871f-b60b34e5bd36", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"LANGCHAIN_API_KEY\"] = \"...\"\n", - "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", - "\n", - "anthropic_chain.invoke(\"ice cream\")" - ] - }, - { - "cell_type": "markdown", - "id": "db37c922-e641-45e4-86fe-9ed7ef468fd8", - "metadata": {}, - "source": [ - "Here's what our LangSmith trace looks like: https://smith.langchain.com/public/e4de52f8-bcd9-4732-b950-deee4b04e313/r" - ] - }, - { - "cell_type": "markdown", - "id": "e25ce3c5-27a7-4954-9f0e-b94313597135", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "## Fallbacks\n", - "\n", - "If we wanted to add fallback logic, in case one model API is down:\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2e49d512-bc83-4c5f-b56e-934b8343b0fe", - "metadata": {}, - "outputs": [], - "source": [ - "def invoke_chain_with_fallback(topic: str) -> str:\n", - " try:\n", - " return invoke_chain(topic)\n", - " except Exception:\n", - " return invoke_anthropic_chain(topic)\n", - "\n", - "async def ainvoke_chain_with_fallback(topic: str) -> str:\n", - " try:\n", - " return await ainvoke_chain(topic)\n", - " except Exception:\n", - " # Note: we haven't actually implemented this.\n", - " return await ainvoke_anthropic_chain(topic)\n", - "\n", - "async def batch_chain_with_fallback(topics: List[str]) -> str:\n", - " try:\n", - " return batch_chain(topics)\n", - " except Exception:\n", - " # Note: we haven't actually implemented this.\n", - " return batch_anthropic_chain(topics)\n", - "\n", - "invoke_chain_with_fallback(\"ice cream\")\n", - "# await ainvoke_chain_with_fallback(\"ice cream\")\n", - "batch_chain_with_fallback([\"ice cream\", \"spaghetti\", \"dumplings\"]))" - ] - }, - { - "cell_type": "markdown", - "id": "f7ef59b5-2ce3-479e-a7ac-79e1e2f30e9c", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3d0d8a0f-66eb-4c35-9529-74bec44ce4b8", - "metadata": {}, - "outputs": [], - "source": [ - "fallback_chain = chain.with_fallbacks([anthropic_chain])\n", - "\n", - "fallback_chain.invoke(\"ice cream\")\n", - "# await fallback_chain.ainvoke(\"ice cream\")\n", - "fallback_chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])" - ] - }, - { - "cell_type": "markdown", - "id": "3af52d36-37c6-4d89-b515-95d7270bb96a", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "f58af836-26bd-4eab-97a0-76dd56d53430", - "metadata": {}, - "source": [ - "## Full code comparison\n", - "\n", - "Even in this simple case, our LCEL chain succinctly packs in a lot of functionality. As chains become more complex, this becomes especially valuable.\n", - "\n", - "```{=mdx}\n", - "\n", - "\n", - "```\n", - "\n", - "#### Without LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8684690a-e450-4ba7-8509-e9815a42ff1c", - "metadata": {}, - "outputs": [], - "source": [ - "from concurrent.futures import ThreadPoolExecutor\n", - "from typing import Iterator, List, Tuple\n", - "\n", - "import anthropic\n", - "import openai\n", - "\n", - "\n", - "prompt_template = \"Tell me a short joke about {topic}\"\n", - "anthropic_template = f\"Human:\\n\\n{prompt_template}\\n\\nAssistant:\"\n", - "client = openai.OpenAI()\n", - "async_client = openai.AsyncOpenAI()\n", - "anthropic_client = anthropic.Anthropic()\n", - "\n", - "def call_chat_model(messages: List[dict]) -> str:\n", - " response = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\", \n", - " messages=messages,\n", - " )\n", - " return response.choices[0].message.content\n", - "\n", - "def invoke_chain(topic: str) -> str:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = prompt_template.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", - " output = call_chat_model(messages)\n", - " print(f\"Output: {output}\")\n", - " return output\n", - "\n", - "def stream_chat_model(messages: List[dict]) -> Iterator[str]:\n", - " stream = client.chat.completions.create(\n", - " model=\"gpt-3.5-turbo\",\n", - " messages=messages,\n", - " stream=True,\n", - " )\n", - " for response in stream:\n", - " content = response.choices[0].delta.content\n", - " if content is not None:\n", - " yield content\n", - "\n", - "def stream_chain(topic: str) -> Iterator[str]:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = prompt.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " stream = stream_chat_model([{\"role\": \"user\", \"content\": prompt_value}])\n", - " for chunk in stream:\n", - " print(f\"Token: {chunk}\", end=\"\")\n", - " yield chunk\n", - "\n", - "def batch_chain(topics: list) -> list:\n", - " with ThreadPoolExecutor(max_workers=5) as executor:\n", - " return list(executor.map(invoke_chain, topics))\n", - "\n", - "def call_llm(prompt_value: str) -> str:\n", - " response = client.completions.create(\n", - " model=\"gpt-3.5-turbo-instruct\",\n", - " prompt=prompt_value,\n", - " )\n", - " return response.choices[0].text\n", - "\n", - "def invoke_llm_chain(topic: str) -> str:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = promtp_template.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " output = call_llm(prompt_value)\n", - " print(f\"Output: {output}\")\n", - " return output\n", - "\n", - "def call_anthropic(prompt_value: str) -> str:\n", - " response = anthropic_client.completions.create(\n", - " model=\"claude-2\",\n", - " prompt=prompt_value,\n", - " max_tokens_to_sample=256,\n", - " )\n", - " return response.completion \n", - "\n", - "def invoke_anthropic_chain(topic: str) -> str:\n", - " print(f\"Input: {topic}\")\n", - " prompt_value = anthropic_template.format(topic=topic)\n", - " print(f\"Formatted prompt: {prompt_value}\")\n", - " output = call_anthropic(prompt_value)\n", - " print(f\"Output: {output}\")\n", - " return output\n", - "\n", - "async def ainvoke_anthropic_chain(topic: str) -> str:\n", - " ...\n", - "\n", - "def stream_anthropic_chain(topic: str) -> Iterator[str]:\n", - " ...\n", - "\n", - "def batch_anthropic_chain(topics: List[str]) -> List[str]:\n", - " ...\n", - "\n", - "def invoke_configurable_chain(\n", - " topic: str, \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> str:\n", - " if model == \"chat_openai\":\n", - " return invoke_chain(topic)\n", - " elif model == \"openai\":\n", - " return invoke_llm_chain(topic)\n", - " elif model == \"anthropic\":\n", - " return invoke_anthropic_chain(topic)\n", - " else:\n", - " raise ValueError(\n", - " f\"Received invalid model '{model}'.\"\n", - " \" Expected one of chat_openai, openai, anthropic\"\n", - " )\n", - "\n", - "def stream_configurable_chain(\n", - " topic: str, \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> Iterator[str]:\n", - " if model == \"chat_openai\":\n", - " return stream_chain(topic)\n", - " elif model == \"openai\":\n", - " # Note we haven't implemented this yet.\n", - " return stream_llm_chain(topic)\n", - " elif model == \"anthropic\":\n", - " # Note we haven't implemented this yet\n", - " return stream_anthropic_chain(topic)\n", - " else:\n", - " raise ValueError(\n", - " f\"Received invalid model '{model}'.\"\n", - " \" Expected one of chat_openai, openai, anthropic\"\n", - " )\n", - "\n", - "def batch_configurable_chain(\n", - " topics: List[str], \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> List[str]:\n", - " ...\n", - "\n", - "async def abatch_configurable_chain(\n", - " topics: List[str], \n", - " *, \n", - " model: str = \"chat_openai\"\n", - ") -> List[str]:\n", - " ...\n", - "\n", - "def invoke_chain_with_fallback(topic: str) -> str:\n", - " try:\n", - " return invoke_chain(topic)\n", - " except Exception:\n", - " return invoke_anthropic_chain(topic)\n", - "\n", - "async def ainvoke_chain_with_fallback(topic: str) -> str:\n", - " try:\n", - " return await ainvoke_chain(topic)\n", - " except Exception:\n", - " return await ainvoke_anthropic_chain(topic)\n", - "\n", - "async def batch_chain_with_fallback(topics: List[str]) -> str:\n", - " try:\n", - " return batch_chain(topics)\n", - " except Exception:\n", - " return batch_anthropic_chain(topics)" - ] - }, - { - "cell_type": "markdown", - "id": "9fb3d71d-8c69-4dc4-81b7-95cd46b271c2", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "#### LCEL\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "715c469a-545e-434e-bd6e-99745dd880a7", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "from langchain_anthropic import ChatAnthropic\n", - "from langchain_openai import ChatOpenAI\n", - "from langchain_openai import OpenAI\n", - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_core.runnables import RunnablePassthrough, ConfigurableField\n", - "\n", - "os.environ[\"LANGCHAIN_API_KEY\"] = \"...\"\n", - "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", - "\n", - "prompt = ChatPromptTemplate.from_template(\n", - " \"Tell me a short joke about {topic}\"\n", - ")\n", - "chat_openai = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", - "openai = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n", - "anthropic = ChatAnthropic(model=\"claude-2\")\n", - "model = (\n", - " chat_openai\n", - " .with_fallbacks([anthropic])\n", - " .configurable_alternatives(\n", - " ConfigurableField(id=\"model\"),\n", - " default_key=\"chat_openai\",\n", - " openai=openai,\n", - " anthropic=anthropic,\n", - " )\n", - ")\n", - "\n", - "chain = (\n", - " {\"topic\": RunnablePassthrough()} \n", - " | prompt \n", - " | model \n", - " | StrOutputParser()\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "e3637d39", - "metadata": {}, - "source": [ - "```{=mdx}\n", - "\n", - "\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "5e47e773-d0f1-42b5-b509-896807b65c9c", - "metadata": {}, - "source": [ - "## Next steps\n", - "\n", - "To continue learning about LCEL, we recommend:\n", - "- Reading up on the full LCEL [Interface](/docs/expression_language/interface), which we've only partially covered here.\n", - "- Exploring the [How-to](/docs/expression_language/how_to) section to learn about additional composition primitives that LCEL provides.\n", - "- Looking through the [Cookbook](/docs/expression_language/cookbook) section to see LCEL in action for common use cases. A good next use case to look at would be [Retrieval-augmented generation](/docs/expression_language/cookbook/retrieval)." - ] - } - ], - "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.6" - } + "cells": [ + { + "cell_type": "raw", + "id": "bc346658-6820-413a-bd8f-11bd3082fe43", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 0.5\n", + "title: Advantages of LCEL\n", + "---\n", + "\n", + "```{=mdx}\n", + "import { ColumnContainer, Column } from \"@theme/Columns\";\n", + "```" + ] }, - "nbformat": 4, - "nbformat_minor": 5 - } - \ No newline at end of file + { + "cell_type": "markdown", + "id": "919a5ae2-ed21-4923-b98f-723c111bac67", + "metadata": {}, + "source": [ + ":::{.callout-tip} \n", + "We recommend reading the LCEL [Get started](/docs/expression_language/get_started) section first.\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "id": "f331037f-be3f-4782-856f-d55dab952488", + "metadata": {}, + "source": [ + "LCEL is designed to streamline the process of building useful apps with LLMs and combining related components. It does this by providing:\n", + "\n", + "1. **A unified interface**: Every LCEL object implements the `Runnable` interface, which defines a common set of invocation methods (`invoke`, `batch`, `stream`, `ainvoke`, ...). This makes it possible for chains of LCEL objects to also automatically support useful operations like batching and streaming of intermediate steps, since every chain of LCEL objects is itself an LCEL object.\n", + "2. **Composition primitives**: LCEL provides a number of primitives that make it easy to compose chains, parallelize components, add fallbacks, dynamically configure chain internals, and more.\n", + "\n", + "To better understand the value of LCEL, it's helpful to see it in action and think about how we might recreate similar functionality without it. In this walkthrough we'll do just that with our [basic example](/docs/expression_language/get_started#basic_example) from the get started section. We'll take our simple prompt + model chain, which under the hood already defines a lot of functionality, and see what it would take to recreate all of it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b99b47ec", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install --upgrade --quiet langchain-core langchain-openai langchain-anthropic" + ] + }, + { + "cell_type": "markdown", + "id": "e3621b62-a037-42b8-8faa-59575608bb8b", + "metadata": {}, + "source": [ + "## Invoke\n", + "In the simplest case, we just want to pass in a topic string and get back a joke string:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e628905c-430e-4e4a-9d7c-c91d2f42052e", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List\n", + "\n", + "import openai\n", + "\n", + "\n", + "prompt_template = \"Tell me a short joke about {topic}\"\n", + "client = openai.OpenAI()\n", + "\n", + "def call_chat_model(messages: List[dict]) -> str:\n", + " response = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\", \n", + " messages=messages,\n", + " )\n", + " return response.choices[0].message.content\n", + "\n", + "def invoke_chain(topic: str) -> str:\n", + " prompt_value = prompt_template.format(topic=topic)\n", + " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", + " return call_chat_model(messages)\n", + "\n", + "invoke_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "cdc3b527-c09e-4c77-9711-c3cc4506cd95", + "metadata": {}, + "source": [ + "\n", + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d2a7cf8-1bc7-405c-bb0d-f2ab2ba3b6ab", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_openai import ChatOpenAI\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.runnables import RunnablePassthrough\n", + "\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\n", + " \"Tell me a short joke about {topic}\"\n", + ")\n", + "output_parser = StrOutputParser()\n", + "model = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", + "chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt\n", + " | model\n", + " | output_parser\n", + ")\n", + "\n", + "chain.invoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "3c0b0513-77b8-4371-a20e-3e487cec7e7f", + "metadata": {}, + "source": [ + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "## Stream\n", + "If we want to stream results instead, we'll need to change our function:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f2cc6dc-d70a-4c13-9258-452f14290da6", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Iterator\n", + "\n", + "\n", + "def stream_chat_model(messages: List[dict]) -> Iterator[str]:\n", + " stream = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=messages,\n", + " stream=True,\n", + " )\n", + " for response in stream:\n", + " content = response.choices[0].delta.content\n", + " if content is not None:\n", + " yield content\n", + "\n", + "def stream_chain(topic: str) -> Iterator[str]:\n", + " prompt_value = prompt.format(topic=topic)\n", + " return stream_chat_model([{\"role\": \"user\", \"content\": prompt_value}])\n", + "\n", + "\n", + "for chunk in stream_chain(\"ice cream\"):\n", + " print(chunk, end=\"\", flush=True)" + ] + }, + { + "cell_type": "markdown", + "id": "f8e36b0e-c7dc-4130-a51b-189d4b756c7f", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "173e1a9c-2a18-4669-b0de-136f39197786", + "metadata": {}, + "outputs": [], + "source": [ + "for chunk in chain.stream(\"ice cream\"):\n", + " print(chunk, end=\"\", flush=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b9b41e78-ddeb-44d0-a58b-a0ea0c99a761", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Batch\n", + "\n", + "If we want to run on a batch of inputs in parallel, we'll again need a new function:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b492f13-73a6-48ed-8d4f-9ad634da9988", + "metadata": {}, + "outputs": [], + "source": [ + "from concurrent.futures import ThreadPoolExecutor\n", + "\n", + "\n", + "def batch_chain(topics: list) -> list:\n", + " with ThreadPoolExecutor(max_workers=5) as executor:\n", + " return list(executor.map(invoke_chain, topics))\n", + "\n", + "batch_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "9b3e9d34-6775-43c1-93d8-684b58e341ab", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f55b292-4e97-4d09-8e71-c71b4d853526", + "metadata": {}, + "outputs": [], + "source": [ + "chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "cc5ba36f-eec1-4fc1-8cfe-fa242a7f7809", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "## Async\n", + "\n", + "If we need an asynchronous version:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eabe6621-e815-41e3-9c9d-5aa561a69835", + "metadata": {}, + "outputs": [], + "source": [ + "async_client = openai.AsyncOpenAI()\n", + "\n", + "async def acall_chat_model(messages: List[dict]) -> str:\n", + " response = await async_client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\", \n", + " messages=messages,\n", + " )\n", + " return response.choices[0].message.content\n", + "\n", + "async def ainvoke_chain(topic: str) -> str:\n", + " prompt_value = prompt_template.format(topic=topic)\n", + " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", + " return await acall_chat_model(messages)\n", + "\n", + "\n", + "await ainvoke_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "2f209290-498c-4c17-839e-ee9002919846", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d009781-7307-48a4-8439-f9d3dd015560", + "metadata": {}, + "outputs": [], + "source": [ + "await chain.ainvoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "1f282129-99a3-40f4-b67f-2d0718b1bea9", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "## Async Batch\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1933f39d-7bd7-45fa-a6a5-5fb7be8e31ec", + "metadata": {}, + "outputs": [], + "source": [ + "import asyncio\n", + "import openai\n", + "\n", + "\n", + "async def abatch_chain(topics: list) -> list:\n", + " coros = map(ainvoke_chain, topics)\n", + " return await asyncio.gather(*coros)\n", + "\n", + "\n", + "await abatch_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "90691048-17ae-479d-83c2-859e33ddf3eb", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "947dad23-3443-40eb-a03b-7840c261e261", + "metadata": {}, + "outputs": [], + "source": [ + "await chain.abatch([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "f6888245-1ebe-4768-a53b-e1fef6a8b379", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## LLM instead of chat model\n", + "\n", + "If we want to use a completion endpoint instead of a chat endpoint: \n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9aca946b-acaa-4f7e-a3d0-ad8e3225e7f2", + "metadata": {}, + "outputs": [], + "source": [ + "def call_llm(prompt_value: str) -> str:\n", + " response = client.completions.create(\n", + " model=\"gpt-3.5-turbo-instruct\",\n", + " prompt=prompt_value,\n", + " )\n", + " return response.choices[0].text\n", + "\n", + "def invoke_llm_chain(topic: str) -> str:\n", + " prompt_value = prompt_template.format(topic=topic)\n", + " return call_llm(prompt_value)\n", + "\n", + "invoke_llm_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "45342cd6-58c2-4543-9392-773e05ef06e7", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d56efc0c-88e0-4cf8-a46a-e8e9b9cd6805", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_openai import OpenAI\n", + "\n", + "llm = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n", + "llm_chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt\n", + " | llm\n", + " | output_parser\n", + ")\n", + "\n", + "llm_chain.invoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "ca115eaf-59ef-45c1-aac1-e8b0ce7db250", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Different model provider\n", + "\n", + "If we want to use Anthropic instead of OpenAI: \n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cde2ceb0-f65e-487b-9a32-137b0e9d79d5", + "metadata": {}, + "outputs": [], + "source": [ + "import anthropic\n", + "\n", + "anthropic_template = f\"Human:\\n\\n{prompt_template}\\n\\nAssistant:\"\n", + "anthropic_client = anthropic.Anthropic()\n", + "\n", + "def call_anthropic(prompt_value: str) -> str:\n", + " response = anthropic_client.completions.create(\n", + " model=\"claude-2\",\n", + " prompt=prompt_value,\n", + " max_tokens_to_sample=256,\n", + " )\n", + " return response.completion \n", + "\n", + "def invoke_anthropic_chain(topic: str) -> str:\n", + " prompt_value = anthropic_template.format(topic=topic)\n", + " return call_anthropic(prompt_value)\n", + "\n", + "invoke_anthropic_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "52a0c9f8-e316-42e1-af85-cabeba4b7059", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3b800d1-5954-41a4-80b0-f00a7908961e", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_anthropic import ChatAnthropic\n", + "\n", + "anthropic = ChatAnthropic(model=\"claude-2\")\n", + "anthropic_chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt \n", + " | anthropic\n", + " | output_parser\n", + ")\n", + "\n", + "anthropic_chain.invoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "d7a91eee-d017-420d-b215-f663dcbf8ed2", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Runtime configurability\n", + "\n", + "If we wanted to make the choice of chat model or LLM configurable at runtime:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0ef10e4-8e8e-463a-bd0f-59b0715e79b6", + "metadata": {}, + "outputs": [], + "source": [ + "def invoke_configurable_chain(\n", + " topic: str, \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> str:\n", + " if model == \"chat_openai\":\n", + " return invoke_chain(topic)\n", + " elif model == \"openai\":\n", + " return invoke_llm_chain(topic)\n", + " elif model == \"anthropic\":\n", + " return invoke_anthropic_chain(topic)\n", + " else:\n", + " raise ValueError(\n", + " f\"Received invalid model '{model}'.\"\n", + " \" Expected one of chat_openai, openai, anthropic\"\n", + " )\n", + "\n", + "def stream_configurable_chain(\n", + " topic: str, \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> Iterator[str]:\n", + " if model == \"chat_openai\":\n", + " return stream_chain(topic)\n", + " elif model == \"openai\":\n", + " # Note we haven't implemented this yet.\n", + " return stream_llm_chain(topic)\n", + " elif model == \"anthropic\":\n", + " # Note we haven't implemented this yet\n", + " return stream_anthropic_chain(topic)\n", + " else:\n", + " raise ValueError(\n", + " f\"Received invalid model '{model}'.\"\n", + " \" Expected one of chat_openai, openai, anthropic\"\n", + " )\n", + "\n", + "def batch_configurable_chain(\n", + " topics: List[str], \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> List[str]:\n", + " # You get the idea\n", + " ...\n", + "\n", + "async def abatch_configurable_chain(\n", + " topics: List[str], \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> List[str]:\n", + " ...\n", + "\n", + "invoke_configurable_chain(\"ice cream\", model=\"openai\")\n", + "stream = stream_configurable_chain(\n", + " \"ice_cream\", \n", + " model=\"anthropic\"\n", + ")\n", + "for chunk in stream:\n", + " print(chunk, end=\"\", flush=True)\n", + "\n", + "# batch_configurable_chain([\"ice cream\", \"spaghetti\", \"dumplings\"])\n", + "# await ainvoke_configurable_chain(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "d1530c5c-6635-4599-9483-6df357ca2d64", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### With LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76809d14-e77a-4125-a2ea-efbebf0b47cc", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.runnables import ConfigurableField\n", + "\n", + "\n", + "configurable_model = model.configurable_alternatives(\n", + " ConfigurableField(id=\"model\"), \n", + " default_key=\"chat_openai\", \n", + " openai=llm,\n", + " anthropic=anthropic,\n", + ")\n", + "configurable_chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt \n", + " | configurable_model \n", + " | output_parser\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a3d94d0-cd42-4195-80b8-ef2e12503d6f", + "metadata": {}, + "outputs": [], + "source": [ + "configurable_chain.invoke(\n", + " \"ice cream\", \n", + " config={\"model\": \"openai\"}\n", + ")\n", + "stream = configurable_chain.stream(\n", + " \"ice cream\", \n", + " config={\"model\": \"anthropic\"}\n", + ")\n", + "for chunk in stream:\n", + " print(chunk, end=\"\", flush=True)\n", + "\n", + "configurable_chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])\n", + "\n", + "# await configurable_chain.ainvoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "370dd4d7-b825-40c4-ae3c-2693cba2f22a", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Logging\n", + "\n", + "If we want to log our intermediate results:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n", + "We'll `print` intermediate steps for illustrative purposes\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "383a3c51-926d-48c6-b9ae-42bf8f14ecc8", + "metadata": {}, + "outputs": [], + "source": [ + "def invoke_anthropic_chain_with_logging(topic: str) -> str:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = anthropic_template.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " output = call_anthropic(prompt_value)\n", + " print(f\"Output: {output}\")\n", + " return output\n", + "\n", + "invoke_anthropic_chain_with_logging(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "16bd20fd-43cd-4aaf-866f-a53d1f20312d", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "Every component has built-in integrations with LangSmith. If we set the following two environment variables, all chain traces are logged to LangSmith.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6204f21-d2e7-4ac6-871f-b60b34e5bd36", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"...\"\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "\n", + "anthropic_chain.invoke(\"ice cream\")" + ] + }, + { + "cell_type": "markdown", + "id": "db37c922-e641-45e4-86fe-9ed7ef468fd8", + "metadata": {}, + "source": [ + "Here's what our LangSmith trace looks like: https://smith.langchain.com/public/e4de52f8-bcd9-4732-b950-deee4b04e313/r" + ] + }, + { + "cell_type": "markdown", + "id": "e25ce3c5-27a7-4954-9f0e-b94313597135", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "## Fallbacks\n", + "\n", + "If we wanted to add fallback logic, in case one model API is down:\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e49d512-bc83-4c5f-b56e-934b8343b0fe", + "metadata": {}, + "outputs": [], + "source": [ + "def invoke_chain_with_fallback(topic: str) -> str:\n", + " try:\n", + " return invoke_chain(topic)\n", + " except Exception:\n", + " return invoke_anthropic_chain(topic)\n", + "\n", + "async def ainvoke_chain_with_fallback(topic: str) -> str:\n", + " try:\n", + " return await ainvoke_chain(topic)\n", + " except Exception:\n", + " # Note: we haven't actually implemented this.\n", + " return await ainvoke_anthropic_chain(topic)\n", + "\n", + "async def batch_chain_with_fallback(topics: List[str]) -> str:\n", + " try:\n", + " return batch_chain(topics)\n", + " except Exception:\n", + " # Note: we haven't actually implemented this.\n", + " return batch_anthropic_chain(topics)\n", + "\n", + "invoke_chain_with_fallback(\"ice cream\")\n", + "# await ainvoke_chain_with_fallback(\"ice cream\")\n", + "batch_chain_with_fallback([\"ice cream\", \"spaghetti\", \"dumplings\"]))" + ] + }, + { + "cell_type": "markdown", + "id": "f7ef59b5-2ce3-479e-a7ac-79e1e2f30e9c", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d0d8a0f-66eb-4c35-9529-74bec44ce4b8", + "metadata": {}, + "outputs": [], + "source": [ + "fallback_chain = chain.with_fallbacks([anthropic_chain])\n", + "\n", + "fallback_chain.invoke(\"ice cream\")\n", + "# await fallback_chain.ainvoke(\"ice cream\")\n", + "fallback_chain.batch([\"ice cream\", \"spaghetti\", \"dumplings\"])" + ] + }, + { + "cell_type": "markdown", + "id": "3af52d36-37c6-4d89-b515-95d7270bb96a", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "f58af836-26bd-4eab-97a0-76dd56d53430", + "metadata": {}, + "source": [ + "## Full code comparison\n", + "\n", + "Even in this simple case, our LCEL chain succinctly packs in a lot of functionality. As chains become more complex, this becomes especially valuable.\n", + "\n", + "```{=mdx}\n", + "\n", + "\n", + "```\n", + "\n", + "#### Without LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8684690a-e450-4ba7-8509-e9815a42ff1c", + "metadata": {}, + "outputs": [], + "source": [ + "from concurrent.futures import ThreadPoolExecutor\n", + "from typing import Iterator, List, Tuple\n", + "\n", + "import anthropic\n", + "import openai\n", + "\n", + "\n", + "prompt_template = \"Tell me a short joke about {topic}\"\n", + "anthropic_template = f\"Human:\\n\\n{prompt_template}\\n\\nAssistant:\"\n", + "client = openai.OpenAI()\n", + "async_client = openai.AsyncOpenAI()\n", + "anthropic_client = anthropic.Anthropic()\n", + "\n", + "def call_chat_model(messages: List[dict]) -> str:\n", + " response = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\", \n", + " messages=messages,\n", + " )\n", + " return response.choices[0].message.content\n", + "\n", + "def invoke_chain(topic: str) -> str:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = prompt_template.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " messages = [{\"role\": \"user\", \"content\": prompt_value}]\n", + " output = call_chat_model(messages)\n", + " print(f\"Output: {output}\")\n", + " return output\n", + "\n", + "def stream_chat_model(messages: List[dict]) -> Iterator[str]:\n", + " stream = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=messages,\n", + " stream=True,\n", + " )\n", + " for response in stream:\n", + " content = response.choices[0].delta.content\n", + " if content is not None:\n", + " yield content\n", + "\n", + "def stream_chain(topic: str) -> Iterator[str]:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = prompt.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " stream = stream_chat_model([{\"role\": \"user\", \"content\": prompt_value}])\n", + " for chunk in stream:\n", + " print(f\"Token: {chunk}\", end=\"\")\n", + " yield chunk\n", + "\n", + "def batch_chain(topics: list) -> list:\n", + " with ThreadPoolExecutor(max_workers=5) as executor:\n", + " return list(executor.map(invoke_chain, topics))\n", + "\n", + "def call_llm(prompt_value: str) -> str:\n", + " response = client.completions.create(\n", + " model=\"gpt-3.5-turbo-instruct\",\n", + " prompt=prompt_value,\n", + " )\n", + " return response.choices[0].text\n", + "\n", + "def invoke_llm_chain(topic: str) -> str:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = promtp_template.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " output = call_llm(prompt_value)\n", + " print(f\"Output: {output}\")\n", + " return output\n", + "\n", + "def call_anthropic(prompt_value: str) -> str:\n", + " response = anthropic_client.completions.create(\n", + " model=\"claude-2\",\n", + " prompt=prompt_value,\n", + " max_tokens_to_sample=256,\n", + " )\n", + " return response.completion \n", + "\n", + "def invoke_anthropic_chain(topic: str) -> str:\n", + " print(f\"Input: {topic}\")\n", + " prompt_value = anthropic_template.format(topic=topic)\n", + " print(f\"Formatted prompt: {prompt_value}\")\n", + " output = call_anthropic(prompt_value)\n", + " print(f\"Output: {output}\")\n", + " return output\n", + "\n", + "async def ainvoke_anthropic_chain(topic: str) -> str:\n", + " ...\n", + "\n", + "def stream_anthropic_chain(topic: str) -> Iterator[str]:\n", + " ...\n", + "\n", + "def batch_anthropic_chain(topics: List[str]) -> List[str]:\n", + " ...\n", + "\n", + "def invoke_configurable_chain(\n", + " topic: str, \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> str:\n", + " if model == \"chat_openai\":\n", + " return invoke_chain(topic)\n", + " elif model == \"openai\":\n", + " return invoke_llm_chain(topic)\n", + " elif model == \"anthropic\":\n", + " return invoke_anthropic_chain(topic)\n", + " else:\n", + " raise ValueError(\n", + " f\"Received invalid model '{model}'.\"\n", + " \" Expected one of chat_openai, openai, anthropic\"\n", + " )\n", + "\n", + "def stream_configurable_chain(\n", + " topic: str, \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> Iterator[str]:\n", + " if model == \"chat_openai\":\n", + " return stream_chain(topic)\n", + " elif model == \"openai\":\n", + " # Note we haven't implemented this yet.\n", + " return stream_llm_chain(topic)\n", + " elif model == \"anthropic\":\n", + " # Note we haven't implemented this yet\n", + " return stream_anthropic_chain(topic)\n", + " else:\n", + " raise ValueError(\n", + " f\"Received invalid model '{model}'.\"\n", + " \" Expected one of chat_openai, openai, anthropic\"\n", + " )\n", + "\n", + "def batch_configurable_chain(\n", + " topics: List[str], \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> List[str]:\n", + " ...\n", + "\n", + "async def abatch_configurable_chain(\n", + " topics: List[str], \n", + " *, \n", + " model: str = \"chat_openai\"\n", + ") -> List[str]:\n", + " ...\n", + "\n", + "def invoke_chain_with_fallback(topic: str) -> str:\n", + " try:\n", + " return invoke_chain(topic)\n", + " except Exception:\n", + " return invoke_anthropic_chain(topic)\n", + "\n", + "async def ainvoke_chain_with_fallback(topic: str) -> str:\n", + " try:\n", + " return await ainvoke_chain(topic)\n", + " except Exception:\n", + " return await ainvoke_anthropic_chain(topic)\n", + "\n", + "async def batch_chain_with_fallback(topics: List[str]) -> str:\n", + " try:\n", + " return batch_chain(topics)\n", + " except Exception:\n", + " return batch_anthropic_chain(topics)" + ] + }, + { + "cell_type": "markdown", + "id": "9fb3d71d-8c69-4dc4-81b7-95cd46b271c2", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "\n", + "```\n", + "\n", + "#### LCEL\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "715c469a-545e-434e-bd6e-99745dd880a7", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "from langchain_anthropic import ChatAnthropic\n", + "from langchain_openai import ChatOpenAI\n", + "from langchain_openai import OpenAI\n", + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnablePassthrough, ConfigurableField\n", + "\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = \"...\"\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\n", + " \"Tell me a short joke about {topic}\"\n", + ")\n", + "chat_openai = ChatOpenAI(model=\"gpt-3.5-turbo\")\n", + "openai = OpenAI(model=\"gpt-3.5-turbo-instruct\")\n", + "anthropic = ChatAnthropic(model=\"claude-2\")\n", + "model = (\n", + " chat_openai\n", + " .with_fallbacks([anthropic])\n", + " .configurable_alternatives(\n", + " ConfigurableField(id=\"model\"),\n", + " default_key=\"chat_openai\",\n", + " openai=openai,\n", + " anthropic=anthropic,\n", + " )\n", + ")\n", + "\n", + "chain = (\n", + " {\"topic\": RunnablePassthrough()} \n", + " | prompt \n", + " | model \n", + " | StrOutputParser()\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e3637d39", + "metadata": {}, + "source": [ + "```{=mdx}\n", + "\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "5e47e773-d0f1-42b5-b509-896807b65c9c", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "To continue learning about LCEL, we recommend:\n", + "- Reading up on the full LCEL [Interface](/docs/expression_language/interface), which we've only partially covered here.\n", + "- Exploring the [primitives](/docs/expression_language/primitives) to learn more about what LCEL provides." + ] + } + ], + "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.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/get_started/installation.mdx b/docs/docs/get_started/installation.mdx index aaee3c247c..e84ff56460 100644 --- a/docs/docs/get_started/installation.mdx +++ b/docs/docs/get_started/installation.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 2 +--- + # Installation ## Official release @@ -29,18 +33,18 @@ If you want to install from source, you can do so by cloning the repo and be sur pip install -e . ``` -## LangChain community -The `langchain-community` package contains third-party integrations. It is automatically installed by `langchain`, but can also be used separately. Install with: +## LangChain core +The `langchain-core` package contains base abstractions that the rest of the LangChain ecosystem uses, along with the LangChain Expression Language. It is automatically installed by `langchain`, but can also be used separately. Install with: ```bash -pip install langchain-community +pip install langchain-core ``` -## LangChain core -The `langchain-core` package contains base abstractions that the rest of the LangChain ecosystem uses, along with the LangChain Expression Language. It is automatically installed by `langchain`, but can also be used separately. Install with: +## LangChain community +The `langchain-community` package contains third-party integrations. It is automatically installed by `langchain`, but can also be used separately. Install with: ```bash -pip install langchain-core +pip install langchain-community ``` ## LangChain experimental @@ -51,6 +55,13 @@ Install with: pip install langchain-experimental ``` +## LangGraph +`langgraph` is a library for building stateful, multi-actor applications with LLMs, built on top of (and intended to be used with) LangChain. +Install with: + +```bash +pip install langgraph +``` ## LangServe LangServe helps developers deploy LangChain runnables and chains as a REST API. LangServe is automatically installed by LangChain CLI. diff --git a/docs/docs/get_started/introduction.mdx b/docs/docs/get_started/introduction.mdx index c10e01f2c3..5a2b528509 100644 --- a/docs/docs/get_started/introduction.mdx +++ b/docs/docs/get_started/introduction.mdx @@ -1,18 +1,16 @@ --- sidebar_position: 0 +sidebar_class_name: hidden --- # Introduction -**LangChain** is a framework for developing applications powered by language models. It enables applications that: -- **Are context-aware**: connect a language model to sources of context (prompt instructions, few shot examples, content to ground its response in, etc.) -- **Reason**: rely on a language model to reason (about how to answer based on provided context, what actions to take, etc.) +**LangChain** is a framework for developing applications powered by large language models (LLMs). -This framework consists of several parts. -- **LangChain Libraries**: The Python and JavaScript libraries. Contains interfaces and integrations for a myriad of components, a basic run time for combining these components into chains and agents, and off-the-shelf implementations of chains and agents. -- **[LangChain Templates](/docs/templates)**: A collection of easily deployable reference architectures for a wide variety of tasks. -- **[LangServe](/docs/langserve)**: A library for deploying LangChain chains as a REST API. -- **[LangSmith](/docs/langsmith)**: A developer platform that lets you debug, test, evaluate, and monitor chains built on any LLM framework and seamlessly integrates with LangChain. +LangChain simplifies every stage of the LLM application lifecycle: +- **Development**: Build your applications using LangChain's open-source [building blocks](/docs/expression_language/) and [components](/docs/modules/). Hit the ground running using [third-party integrations](/docs/integrations/platforms/) and [Templates](/docs/templates). +- **Productionization**: Use [LangSmith](/docs/langsmith/) to inspect, monitor and evaluate your chains, so that you can continuously optimize and deploy with confidence. +- **Deployment**: Turn any chain into an API with [LangServe](/docs/langserve). import ThemedImage from '@theme/ThemedImage'; @@ -25,31 +23,24 @@ import ThemedImage from '@theme/ThemedImage'; title="LangChain Framework Overview" /> -Together, these products simplify the entire application lifecycle: -- **Develop**: Write your applications in LangChain/LangChain.js. Hit the ground running using Templates for reference. -- **Productionize**: Use LangSmith to inspect, test and monitor your chains, so that you can constantly improve and deploy with confidence. -- **Deploy**: Turn any chain into an API with LangServe. +Concretely, the framework consists of the following open-source libraries: -## LangChain Libraries - -The main value props of the LangChain packages are: -1. **Components**: composable tools and integrations for working with language models. Components are modular and easy-to-use, whether you are using the rest of the LangChain framework or not -2. **Off-the-shelf chains**: built-in assemblages of components for accomplishing higher-level tasks - -Off-the-shelf chains make it easy to get started. Components make it easy to customize existing chains and build new ones. - -The LangChain libraries themselves are made up of several different packages. - **`langchain-core`**: Base abstractions and LangChain Expression Language. - **`langchain-community`**: Third party integrations. + - Partner packages (e.g. **`langchain-openai`**, **`langchain-anthropic`**, etc.): Some integrations have been further split into their own lightweight packages that only depend on **`langchain-core`**. - **`langchain`**: Chains, agents, and retrieval strategies that make up an application's cognitive architecture. +- **[langgraph](/docs/langgraph)**: Build robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph. +- **[langserve](/docs/langserve)**: Deploy LangChain chains as REST APIs. -## Get started +The broader ecosystem includes: -[Here’s](/docs/get_started/installation) how to install LangChain, set up your environment, and start building. +- **[LangSmith](/docs/langsmith)**: A developer platform that lets you debug, test, evaluate, and monitor LLM applications and seamlessly integrates with LangChain. + +## Get started We recommend following our [Quickstart](/docs/get_started/quickstart) guide to familiarize yourself with the framework by building your first LangChain application. -Read up on our [Security](/docs/security) best practices to make sure you're developing safely with LangChain. +[See here](/docs/get_started/installation) for instructions on how to install LangChain, set up your environment, and start building. :::note @@ -57,48 +48,53 @@ These docs focus on the Python LangChain library. [Head here](https://js.langcha ::: -## LangChain Expression Language (LCEL) +## Use cases -LCEL is a declarative way to compose chains. LCEL was designed from day 1 to support putting prototypes in production, with no code changes, from the simplest β€œprompt + LLM” chain to the most complex chains. +If you're looking to build something specific or are more of a hands-on learner, check out our [use-cases](/docs/use_cases). +They're walkthroughs and techniques for common end-to-end tasks, such as: -- **[Overview](/docs/expression_language/)**: LCEL and its benefits -- **[Interface](/docs/expression_language/interface)**: The standard interface for LCEL objects -- **[How-to](/docs/expression_language/how_to)**: Key features of LCEL -- **[Cookbook](/docs/expression_language/cookbook)**: Example code for accomplishing common tasks +- [Question answering with RAG](/docs/use_cases/question_answering/) +- [Extracting structured output](/docs/use_cases/extraction/) +- [Chatbots](/docs/use_cases/chatbots/) +- and more! -## Modules +## Expression Language -LangChain provides standard, extendable interfaces and integrations for the following modules: +LangChain Expression Language (LCEL) is the foundation of many of LangChain's components, and is a declarative way to compose chains. LCEL was designed from day 1 to support putting prototypes in production, with no code changes, from the simplest β€œprompt + LLM” chain to the most complex chains. -#### [Model I/O](/docs/modules/model_io/) -Interface with language models +- **[Get started](/docs/expression_language/)**: LCEL and its benefits +- **[Runnable interface](/docs/expression_language/interface)**: The standard interface for LCEL objects +- **[Primitives](/docs/expression_language/primitives)**: More on the primitives LCEL includes +- and more! -#### [Retrieval](/docs/modules/data_connection/) -Interface with application-specific data +## Ecosystem -#### [Agents](/docs/modules/agents/) -Let models choose which tools to use given high-level directives +### [πŸ¦œπŸ› οΈ LangSmith](/docs/langsmith) +Trace and evaluate your language model applications and intelligent agents to help you move from prototype to production. +### [πŸ¦œπŸ•ΈοΈ LangGraph](/docs/langgraph) +Build stateful, multi-actor applications with LLMs, built on top of (and intended to be used with) LangChain primitives. -## Examples, ecosystem, and resources +### [πŸ¦œπŸ“ LangServe](/docs/langserve) +Deploy LangChain runnables and chains as REST APIs. -### [Use cases](/docs/use_cases/question_answering/) -Walkthroughs and techniques for common end-to-end use cases, like: -- [Document question answering](/docs/use_cases/question_answering/) -- [Chatbots](/docs/use_cases/chatbots/) -- [Analyzing structured data](/docs/use_cases/sql/) -- and much more... +## [Security](/docs/security) +Read up on our [Security](/docs/security) best practices to make sure you're developing safely with LangChain. + +## Additional resources + +### [Components](/docs/modules/) +LangChain provides standard, extendable interfaces and integrations for many different components, including: ### [Integrations](/docs/integrations/providers/) LangChain is part of a rich ecosystem of tools that integrate with our framework and build on top of it. Check out our growing list of [integrations](/docs/integrations/providers/). -### [Guides](../guides/debugging.md) +### [Guides](/docs/guides/) Best practices for developing with LangChain. ### [API reference](https://api.python.langchain.com) Head to the reference section for full documentation of all classes and methods in the LangChain and LangChain Experimental Python packages. -### [Developer's guide](/docs/contributing) +### [Contributing](/docs/contributing) Check out the developer's guide for guidelines on contributing and help getting your dev environment set up. - diff --git a/docs/docs/get_started/quickstart.mdx b/docs/docs/get_started/quickstart.mdx index de49f48c9f..de63efd0c2 100644 --- a/docs/docs/get_started/quickstart.mdx +++ b/docs/docs/get_started/quickstart.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 1 +--- + # Quickstart In this quickstart we'll show you how to: diff --git a/docs/docs/guides/debugging.md b/docs/docs/guides/development/debugging.md similarity index 99% rename from docs/docs/guides/debugging.md rename to docs/docs/guides/development/debugging.md index 140e26fb5f..e8ca2622ec 100644 --- a/docs/docs/guides/debugging.md +++ b/docs/docs/guides/development/debugging.md @@ -8,11 +8,11 @@ Here are a few different tools and functionalities to aid in debugging. ## Tracing -Platforms with tracing capabilities like [LangSmith](/docs/langsmith/) and [WandB](/docs/integrations/providers/wandb_tracing) are the most comprehensive solutions for debugging. These platforms make it easy to not only log and visualize LLM apps, but also to actively debug, test and refine them. +Platforms with tracing capabilities like [LangSmith](/docs/langsmith/) are the most comprehensive solutions for debugging. These platforms make it easy to not only log and visualize LLM apps, but also to actively debug, test and refine them. -For anyone building production-grade LLM applications, we highly recommend using a platform like this. +When building production-grade LLM applications, platforms like this are essential. -![Screenshot of the LangSmith debugging interface showing an AgentExecutor run with input and output details, and a run tree visualization.](../../static/img/run_details.png "LangSmith Debugging Interface") +![Screenshot of the LangSmith debugging interface showing an AgentExecutor run with input and output details, and a run tree visualization.](../../../static/img/run_details.png "LangSmith Debugging Interface") ## `set_debug` and `set_verbose` diff --git a/docs/docs/guides/extending_langchain.mdx b/docs/docs/guides/development/extending_langchain.mdx similarity index 100% rename from docs/docs/guides/extending_langchain.mdx rename to docs/docs/guides/development/extending_langchain.mdx diff --git a/docs/docs/guides/development/index.mdx b/docs/docs/guides/development/index.mdx new file mode 100644 index 0000000000..6525ac294b --- /dev/null +++ b/docs/docs/guides/development/index.mdx @@ -0,0 +1,13 @@ +--- +sidebar_position: 1 +sidebar_class_name: hidden +--- + +# Development + +This section contains guides with general information around building apps with LangChain. + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from '@docusaurus/theme-common'; + + item.href !== "/docs/guides/development/")} /> diff --git a/docs/docs/guides/local_llms.ipynb b/docs/docs/guides/development/local_llms.ipynb similarity index 98% rename from docs/docs/guides/local_llms.ipynb rename to docs/docs/guides/development/local_llms.ipynb index fef32b9785..6f70187183 100644 --- a/docs/docs/guides/local_llms.ipynb +++ b/docs/docs/guides/development/local_llms.ipynb @@ -9,7 +9,7 @@ "\n", "## Use case\n", "\n", - "The popularity of projects like [PrivateGPT](https://github.com/imartinez/privateGPT), [llama.cpp](https://github.com/ggerganov/llama.cpp), [GPT4All](https://github.com/nomic-ai/gpt4all), and [llamafile](https://github.com/Mozilla-Ocho/llamafile) underscore the demand to run LLMs locally (on your own device).\n", + "The popularity of projects like [PrivateGPT](https://github.com/imartinez/privateGPT), [llama.cpp](https://github.com/ggerganov/llama.cpp), [Ollama](https://github.com/ollama/ollama), [GPT4All](https://github.com/nomic-ai/gpt4all), [llamafile](https://github.com/Mozilla-Ocho/llamafile), and others underscore the demand to run LLMs locally (on your own device).\n", "\n", "This has at least two important benefits:\n", "\n", @@ -32,7 +32,7 @@ "1. `Base model`: What is the base-model and how was it trained?\n", "2. `Fine-tuning approach`: Was the base-model fine-tuned and, if so, what [set of instructions](https://cameronrwolfe.substack.com/p/beyond-llama-the-power-of-open-llms#%C2%A7alpaca-an-instruction-following-llama-model) was used?\n", "\n", - "![Image description](../../static/img/OSS_LLM_overview.png)\n", + "![Image description](../../../static/img/OSS_LLM_overview.png)\n", "\n", "The relative performance of these models can be assessed using several leaderboards, including:\n", "\n", @@ -56,7 +56,7 @@ "\n", "In particular, see [this excellent post](https://finbarr.ca/how-is-llama-cpp-possible/) on the importance of quantization.\n", "\n", - "![Image description](../../static/img/llama-memory-weights.png)\n", + "![Image description](../../../static/img/llama-memory-weights.png)\n", "\n", "With less precision, we radically decrease the memory needed to store the LLM in memory.\n", "\n", @@ -64,7 +64,7 @@ "\n", "A Mac M2 Max is 5-6x faster than a M1 for inference due to the larger GPU memory bandwidth.\n", "\n", - "![Image description](../../static/img/llama_t_put.png)\n", + "![Image description](../../../static/img/llama_t_put.png)\n", "\n", "## Quickstart\n", "\n", diff --git a/docs/docs/guides/pydantic_compatibility.md b/docs/docs/guides/development/pydantic_compatibility.md similarity index 100% rename from docs/docs/guides/pydantic_compatibility.md rename to docs/docs/guides/development/pydantic_compatibility.md diff --git a/docs/docs/guides/index.mdx b/docs/docs/guides/index.mdx new file mode 100644 index 0000000000..e77238cd48 --- /dev/null +++ b/docs/docs/guides/index.mdx @@ -0,0 +1,3 @@ +# Guides + +This section contains deeper dives into the LangChain framework and how to apply it. diff --git a/docs/docs/guides/model_laboratory.ipynb b/docs/docs/guides/model_laboratory.ipynb deleted file mode 100644 index 5e87c0102f..0000000000 --- a/docs/docs/guides/model_laboratory.ipynb +++ /dev/null @@ -1,283 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "920a3c1a", - "metadata": {}, - "source": [ - "# Model comparison\n", - "\n", - "Constructing your language model application will likely involved choosing between many different options of prompts, models, and even chains to use. When doing so, you will want to compare these different options on different inputs in an easy, flexible, and intuitive way. \n", - "\n", - "LangChain provides the concept of a ModelLaboratory to test out and try different models." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12ebae56", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain langchain-openai" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "ab9e95ad", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.model_laboratory import ModelLaboratory\n", - "from langchain_community.llms import Cohere, HuggingFaceHub\n", - "from langchain_core.prompts import PromptTemplate\n", - "from langchain_openai import OpenAI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3dd69cb4", - "metadata": {}, - "outputs": [], - "source": [ - "import getpass\n", - "import os\n", - "\n", - "# get a new token: https://dashboard.cohere.ai/\n", - "os.environ[\"COHERE_API_KEY\"] = getpass.getpass(\"Cohere API Key:\")\n", - "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"Open API Key:\")\n", - "os.environ[\"HUGGINGFACEHUB_API_TOKEN\"] = getpass.getpass(\"Hugging Face API Key:\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "32cb94e6", - "metadata": {}, - "outputs": [], - "source": [ - "llms = [\n", - " OpenAI(temperature=0),\n", - " Cohere(temperature=0),\n", - " HuggingFaceHub(repo_id=\"google/flan-t5-xl\", model_kwargs={\"temperature\": 1}),\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "14cde09d", - "metadata": {}, - "outputs": [], - "source": [ - "model_lab = ModelLaboratory.from_llms(llms)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "f186c741", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[1mInput:\u001b[0m\n", - "What color is a flamingo?\n", - "\n", - "\u001b[1mOpenAI\u001b[0m\n", - "Params: {'model': 'text-davinci-002', 'temperature': 0.0, 'max_tokens': 256, 'top_p': 1, 'frequency_penalty': 0, 'presence_penalty': 0, 'n': 1, 'best_of': 1}\n", - "\u001b[36;1m\u001b[1;3m\n", - "\n", - "Flamingos are pink.\u001b[0m\n", - "\n", - "\u001b[1mCohere\u001b[0m\n", - "Params: {'model': 'command-xlarge-20221108', 'max_tokens': 20, 'temperature': 0.0, 'k': 0, 'p': 1, 'frequency_penalty': 0, 'presence_penalty': 0}\n", - "\u001b[33;1m\u001b[1;3m\n", - "\n", - "Pink\u001b[0m\n", - "\n", - "\u001b[1mHuggingFaceHub\u001b[0m\n", - "Params: {'repo_id': 'google/flan-t5-xl', 'temperature': 1}\n", - "\u001b[38;5;200m\u001b[1;3mpink\u001b[0m\n", - "\n" - ] - } - ], - "source": [ - "model_lab.compare(\"What color is a flamingo?\")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "248b652a", - "metadata": {}, - "outputs": [], - "source": [ - "prompt = PromptTemplate(\n", - " template=\"What is the capital of {state}?\", input_variables=[\"state\"]\n", - ")\n", - "model_lab_with_prompt = ModelLaboratory.from_llms(llms, prompt=prompt)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "f64377ac", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[1mInput:\u001b[0m\n", - "New York\n", - "\n", - "\u001b[1mOpenAI\u001b[0m\n", - "Params: {'model': 'text-davinci-002', 'temperature': 0.0, 'max_tokens': 256, 'top_p': 1, 'frequency_penalty': 0, 'presence_penalty': 0, 'n': 1, 'best_of': 1}\n", - "\u001b[36;1m\u001b[1;3m\n", - "\n", - "The capital of New York is Albany.\u001b[0m\n", - "\n", - "\u001b[1mCohere\u001b[0m\n", - "Params: {'model': 'command-xlarge-20221108', 'max_tokens': 20, 'temperature': 0.0, 'k': 0, 'p': 1, 'frequency_penalty': 0, 'presence_penalty': 0}\n", - "\u001b[33;1m\u001b[1;3m\n", - "\n", - "The capital of New York is Albany.\u001b[0m\n", - "\n", - "\u001b[1mHuggingFaceHub\u001b[0m\n", - "Params: {'repo_id': 'google/flan-t5-xl', 'temperature': 1}\n", - "\u001b[38;5;200m\u001b[1;3mst john s\u001b[0m\n", - "\n" - ] - } - ], - "source": [ - "model_lab_with_prompt.compare(\"New York\")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "54336dbf", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.agents.self_ask_with_search.base import SelfAskWithSearchChain\n", - "from langchain_community.utilities import SerpAPIWrapper\n", - "\n", - "open_ai_llm = OpenAI(temperature=0)\n", - "search = SerpAPIWrapper()\n", - "self_ask_with_search_openai = SelfAskWithSearchChain(\n", - " llm=open_ai_llm, search_chain=search, verbose=True\n", - ")\n", - "\n", - "cohere_llm = Cohere(temperature=0)\n", - "search = SerpAPIWrapper()\n", - "self_ask_with_search_cohere = SelfAskWithSearchChain(\n", - " llm=cohere_llm, search_chain=search, verbose=True\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "6a50a9f1", - "metadata": {}, - "outputs": [], - "source": [ - "chains = [self_ask_with_search_openai, self_ask_with_search_cohere]\n", - "names = [str(open_ai_llm), str(cohere_llm)]" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "d3549e99", - "metadata": {}, - "outputs": [], - "source": [ - "model_lab = ModelLaboratory(chains, names=names)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "362f7f57", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[1mInput:\u001b[0m\n", - "What is the hometown of the reigning men's U.S. Open champion?\n", - "\n", - "\u001b[1mOpenAI\u001b[0m\n", - "Params: {'model': 'text-davinci-002', 'temperature': 0.0, 'max_tokens': 256, 'top_p': 1, 'frequency_penalty': 0, 'presence_penalty': 0, 'n': 1, 'best_of': 1}\n", - "\n", - "\n", - "\u001b[1m> Entering new chain...\u001b[0m\n", - "What is the hometown of the reigning men's U.S. Open champion?\n", - "Are follow up questions needed here:\u001b[32;1m\u001b[1;3m Yes.\n", - "Follow up: Who is the reigning men's U.S. Open champion?\u001b[0m\n", - "Intermediate answer: \u001b[33;1m\u001b[1;3mCarlos Alcaraz.\u001b[0m\u001b[32;1m\u001b[1;3m\n", - "Follow up: Where is Carlos Alcaraz from?\u001b[0m\n", - "Intermediate answer: \u001b[33;1m\u001b[1;3mEl Palmar, Spain.\u001b[0m\u001b[32;1m\u001b[1;3m\n", - "So the final answer is: El Palmar, Spain\u001b[0m\n", - "\u001b[1m> Finished chain.\u001b[0m\n", - "\u001b[36;1m\u001b[1;3m\n", - "So the final answer is: El Palmar, Spain\u001b[0m\n", - "\n", - "\u001b[1mCohere\u001b[0m\n", - "Params: {'model': 'command-xlarge-20221108', 'max_tokens': 256, 'temperature': 0.0, 'k': 0, 'p': 1, 'frequency_penalty': 0, 'presence_penalty': 0}\n", - "\n", - "\n", - "\u001b[1m> Entering new chain...\u001b[0m\n", - "What is the hometown of the reigning men's U.S. Open champion?\n", - "Are follow up questions needed here:\u001b[32;1m\u001b[1;3m Yes.\n", - "Follow up: Who is the reigning men's U.S. Open champion?\u001b[0m\n", - "Intermediate answer: \u001b[33;1m\u001b[1;3mCarlos Alcaraz.\u001b[0m\u001b[32;1m\u001b[1;3m\n", - "So the final answer is:\n", - "\n", - "Carlos Alcaraz\u001b[0m\n", - "\u001b[1m> Finished chain.\u001b[0m\n", - "\u001b[33;1m\u001b[1;3m\n", - "So the final answer is:\n", - "\n", - "Carlos Alcaraz\u001b[0m\n", - "\n" - ] - } - ], - "source": [ - "model_lab.compare(\"What is the hometown of the reigning men's U.S. Open champion?\")" - ] - } - ], - "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.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/guides/privacy/_category_.yml b/docs/docs/guides/privacy/_category_.yml deleted file mode 100644 index 3459827572..0000000000 --- a/docs/docs/guides/privacy/_category_.yml +++ /dev/null @@ -1 +0,0 @@ -label: 'Privacy' diff --git a/docs/docs/guides/deployments/index.mdx b/docs/docs/guides/productionization/deployments/index.mdx similarity index 100% rename from docs/docs/guides/deployments/index.mdx rename to docs/docs/guides/productionization/deployments/index.mdx diff --git a/docs/docs/guides/deployments/template_repos.mdx b/docs/docs/guides/productionization/deployments/template_repos.mdx similarity index 100% rename from docs/docs/guides/deployments/template_repos.mdx rename to docs/docs/guides/productionization/deployments/template_repos.mdx diff --git a/docs/docs/guides/evaluation/comparison/custom.ipynb b/docs/docs/guides/productionization/evaluation/comparison/custom.ipynb similarity index 100% rename from docs/docs/guides/evaluation/comparison/custom.ipynb rename to docs/docs/guides/productionization/evaluation/comparison/custom.ipynb diff --git a/docs/docs/guides/evaluation/comparison/index.mdx b/docs/docs/guides/productionization/evaluation/comparison/index.mdx similarity index 100% rename from docs/docs/guides/evaluation/comparison/index.mdx rename to docs/docs/guides/productionization/evaluation/comparison/index.mdx diff --git a/docs/docs/guides/evaluation/comparison/pairwise_embedding_distance.ipynb b/docs/docs/guides/productionization/evaluation/comparison/pairwise_embedding_distance.ipynb similarity index 100% rename from docs/docs/guides/evaluation/comparison/pairwise_embedding_distance.ipynb rename to docs/docs/guides/productionization/evaluation/comparison/pairwise_embedding_distance.ipynb diff --git a/docs/docs/guides/evaluation/comparison/pairwise_string.ipynb b/docs/docs/guides/productionization/evaluation/comparison/pairwise_string.ipynb similarity index 100% rename from docs/docs/guides/evaluation/comparison/pairwise_string.ipynb rename to docs/docs/guides/productionization/evaluation/comparison/pairwise_string.ipynb diff --git a/docs/docs/guides/evaluation/examples/comparisons.ipynb b/docs/docs/guides/productionization/evaluation/examples/comparisons.ipynb similarity index 100% rename from docs/docs/guides/evaluation/examples/comparisons.ipynb rename to docs/docs/guides/productionization/evaluation/examples/comparisons.ipynb diff --git a/docs/docs/guides/evaluation/examples/index.mdx b/docs/docs/guides/productionization/evaluation/examples/index.mdx similarity index 100% rename from docs/docs/guides/evaluation/examples/index.mdx rename to docs/docs/guides/productionization/evaluation/examples/index.mdx diff --git a/docs/docs/guides/evaluation/index.mdx b/docs/docs/guides/productionization/evaluation/index.mdx similarity index 73% rename from docs/docs/guides/evaluation/index.mdx rename to docs/docs/guides/productionization/evaluation/index.mdx index 4603a40e53..6731344743 100644 --- a/docs/docs/guides/evaluation/index.mdx +++ b/docs/docs/guides/productionization/evaluation/index.mdx @@ -7,18 +7,19 @@ Building applications with language models involves many moving parts. One of th The guides in this section review the APIs and functionality LangChain provides to help you better evaluate your applications. Evaluation and testing are both critical when thinking about deploying LLM applications, since production environments require repeatable and useful outcomes. LangChain offers various types of evaluators to help you measure performance and integrity on diverse data, and we hope to encourage the community to create and share other useful evaluators so everyone can improve. These docs will introduce the evaluator types, how to use them, and provide some examples of their use in real-world scenarios. +These built-in evaluators all integrate smoothly with [LangSmith](/docs/langsmith), and allow you to create feedback loops that improve your application over time and prevent regressions. Each evaluator type in LangChain comes with ready-to-use implementations and an extensible API that allows for customization according to your unique requirements. Here are some of the types of evaluators we offer: -- [String Evaluators](/docs/guides/evaluation/string/): These evaluators assess the predicted string for a given input, usually comparing it against a reference string. -- [Trajectory Evaluators](/docs/guides/evaluation/trajectory/): These are used to evaluate the entire trajectory of agent actions. -- [Comparison Evaluators](/docs/guides/evaluation/comparison/): These evaluators are designed to compare predictions from two runs on a common input. +- [String Evaluators](/docs/guides/productionization/evaluation/string/): These evaluators assess the predicted string for a given input, usually comparing it against a reference string. +- [Trajectory Evaluators](/docs/guides/productionization/evaluation/trajectory/): These are used to evaluate the entire trajectory of agent actions. +- [Comparison Evaluators](/docs/guides/productionization/evaluation/comparison/): These evaluators are designed to compare predictions from two runs on a common input. These evaluators can be used across various scenarios and can be applied to different chain and LLM implementations in the LangChain library. We also are working to share guides and cookbooks that demonstrate how to use these evaluators in real-world scenarios, such as: -- [Chain Comparisons](/docs/guides/evaluation/examples/comparisons): This example uses a comparison evaluator to predict the preferred output. It reviews ways to measure confidence intervals to select statistically significant differences in aggregate preference scores across different models or prompts. +- [Chain Comparisons](/docs/guides/productionization/evaluation/examples/comparisons): This example uses a comparison evaluator to predict the preferred output. It reviews ways to measure confidence intervals to select statistically significant differences in aggregate preference scores across different models or prompts. ## LangSmith Evaluation diff --git a/docs/docs/guides/evaluation/string/criteria_eval_chain.ipynb b/docs/docs/guides/productionization/evaluation/string/criteria_eval_chain.ipynb similarity index 100% rename from docs/docs/guides/evaluation/string/criteria_eval_chain.ipynb rename to docs/docs/guides/productionization/evaluation/string/criteria_eval_chain.ipynb diff --git a/docs/docs/guides/evaluation/string/custom.ipynb b/docs/docs/guides/productionization/evaluation/string/custom.ipynb similarity index 100% rename from docs/docs/guides/evaluation/string/custom.ipynb rename to docs/docs/guides/productionization/evaluation/string/custom.ipynb diff --git a/docs/docs/guides/evaluation/string/embedding_distance.ipynb b/docs/docs/guides/productionization/evaluation/string/embedding_distance.ipynb similarity index 100% rename from docs/docs/guides/evaluation/string/embedding_distance.ipynb rename to docs/docs/guides/productionization/evaluation/string/embedding_distance.ipynb diff --git a/docs/docs/guides/evaluation/string/exact_match.ipynb b/docs/docs/guides/productionization/evaluation/string/exact_match.ipynb similarity index 100% rename from docs/docs/guides/evaluation/string/exact_match.ipynb rename to docs/docs/guides/productionization/evaluation/string/exact_match.ipynb diff --git a/docs/docs/guides/evaluation/string/index.mdx b/docs/docs/guides/productionization/evaluation/string/index.mdx similarity index 100% rename from docs/docs/guides/evaluation/string/index.mdx rename to docs/docs/guides/productionization/evaluation/string/index.mdx diff --git a/docs/docs/guides/evaluation/string/json.ipynb b/docs/docs/guides/productionization/evaluation/string/json.ipynb similarity index 100% rename from docs/docs/guides/evaluation/string/json.ipynb rename to docs/docs/guides/productionization/evaluation/string/json.ipynb diff --git a/docs/docs/guides/evaluation/string/regex_match.ipynb b/docs/docs/guides/productionization/evaluation/string/regex_match.ipynb similarity index 100% rename from docs/docs/guides/evaluation/string/regex_match.ipynb rename to docs/docs/guides/productionization/evaluation/string/regex_match.ipynb diff --git a/docs/docs/guides/evaluation/string/scoring_eval_chain.ipynb b/docs/docs/guides/productionization/evaluation/string/scoring_eval_chain.ipynb similarity index 100% rename from docs/docs/guides/evaluation/string/scoring_eval_chain.ipynb rename to docs/docs/guides/productionization/evaluation/string/scoring_eval_chain.ipynb diff --git a/docs/docs/guides/evaluation/string/string_distance.ipynb b/docs/docs/guides/productionization/evaluation/string/string_distance.ipynb similarity index 100% rename from docs/docs/guides/evaluation/string/string_distance.ipynb rename to docs/docs/guides/productionization/evaluation/string/string_distance.ipynb diff --git a/docs/docs/guides/evaluation/trajectory/custom.ipynb b/docs/docs/guides/productionization/evaluation/trajectory/custom.ipynb similarity index 100% rename from docs/docs/guides/evaluation/trajectory/custom.ipynb rename to docs/docs/guides/productionization/evaluation/trajectory/custom.ipynb diff --git a/docs/docs/guides/evaluation/trajectory/index.mdx b/docs/docs/guides/productionization/evaluation/trajectory/index.mdx similarity index 100% rename from docs/docs/guides/evaluation/trajectory/index.mdx rename to docs/docs/guides/productionization/evaluation/trajectory/index.mdx diff --git a/docs/docs/guides/evaluation/trajectory/trajectory_eval.ipynb b/docs/docs/guides/productionization/evaluation/trajectory/trajectory_eval.ipynb similarity index 100% rename from docs/docs/guides/evaluation/trajectory/trajectory_eval.ipynb rename to docs/docs/guides/productionization/evaluation/trajectory/trajectory_eval.ipynb diff --git a/docs/docs/guides/fallbacks.ipynb b/docs/docs/guides/productionization/fallbacks.ipynb similarity index 100% rename from docs/docs/guides/fallbacks.ipynb rename to docs/docs/guides/productionization/fallbacks.ipynb diff --git a/docs/docs/guides/productionization/index.mdx b/docs/docs/guides/productionization/index.mdx new file mode 100644 index 0000000000..ff2fa00c1e --- /dev/null +++ b/docs/docs/guides/productionization/index.mdx @@ -0,0 +1,15 @@ +--- +sidebar_position: 1 +sidebar_class_name: hidden +--- + +# Productionization + +After you've developed a prototype of your language model application, the next step is to prepare it for production. +This section contains guides around best practices for getting and keeping your application production-ready, +ensuring it's ready for real-world use. + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from '@docusaurus/theme-common'; + + item.href !== "/docs/guides/productionization/")} /> diff --git a/docs/docs/guides/productionization/safety/_category_.yml b/docs/docs/guides/productionization/safety/_category_.yml new file mode 100644 index 0000000000..38afda5252 --- /dev/null +++ b/docs/docs/guides/productionization/safety/_category_.yml @@ -0,0 +1 @@ +label: 'Privacy & Safety' diff --git a/docs/docs/guides/safety/amazon_comprehend_chain.ipynb b/docs/docs/guides/productionization/safety/amazon_comprehend_chain.ipynb similarity index 100% rename from docs/docs/guides/safety/amazon_comprehend_chain.ipynb rename to docs/docs/guides/productionization/safety/amazon_comprehend_chain.ipynb diff --git a/docs/docs/guides/safety/constitutional_chain.mdx b/docs/docs/guides/productionization/safety/constitutional_chain.mdx similarity index 100% rename from docs/docs/guides/safety/constitutional_chain.mdx rename to docs/docs/guides/productionization/safety/constitutional_chain.mdx diff --git a/docs/docs/guides/safety/hugging_face_prompt_injection.ipynb b/docs/docs/guides/productionization/safety/hugging_face_prompt_injection.ipynb similarity index 100% rename from docs/docs/guides/safety/hugging_face_prompt_injection.ipynb rename to docs/docs/guides/productionization/safety/hugging_face_prompt_injection.ipynb diff --git a/docs/docs/guides/productionization/safety/index.mdx b/docs/docs/guides/productionization/safety/index.mdx new file mode 100644 index 0000000000..dbdfec93d6 --- /dev/null +++ b/docs/docs/guides/productionization/safety/index.mdx @@ -0,0 +1,11 @@ +# Privacy & Safety + +One of the key concerns with using LLMs is that they may misuse private data or generate harmful or unethical text. This is an area of active research in the field. Here we present some built-in chains inspired by this research, which are intended to make the outputs of LLMs safer. + +- [Amazon Comprehend moderation chain](/docs/guides/productionization/safety/amazon_comprehend_chain): Use [Amazon Comprehend](https://aws.amazon.com/comprehend/) to detect and handle Personally Identifiable Information (PII) and toxicity. +- [Constitutional chain](/docs/guides/productionization/safety/constitutional_chain): Prompt the model with a set of principles which should guide the model behavior. +- [Hugging Face prompt injection identification](/docs/guides/productionization/safety/hugging_face_prompt_injection): Detect and handle prompt injection attacks. +- [Layerup Security](/docs/guides/productionization/safety/layerup_security): Easily mask PII & sensitive data, detect and mitigate 10+ LLM-based threat vectors, including PII & sensitive data, prompt injection, hallucination, abuse, and more. +- [Logical Fallacy chain](/docs/guides/productionization/safety/logical_fallacy_chain): Checks the model output against logical fallacies to correct any deviation. +- [Moderation chain](/docs/guides/productionization/safety/moderation): Check if any output text is harmful and flag it. +- [Presidio data anonymization](/docs/guides/productionization/safety/presidio_data_anonymization): Helps to ensure sensitive data is properly managed and governed. diff --git a/docs/docs/guides/safety/layerup_security.mdx b/docs/docs/guides/productionization/safety/layerup_security.mdx similarity index 100% rename from docs/docs/guides/safety/layerup_security.mdx rename to docs/docs/guides/productionization/safety/layerup_security.mdx diff --git a/docs/docs/guides/safety/logical_fallacy_chain.mdx b/docs/docs/guides/productionization/safety/logical_fallacy_chain.mdx similarity index 100% rename from docs/docs/guides/safety/logical_fallacy_chain.mdx rename to docs/docs/guides/productionization/safety/logical_fallacy_chain.mdx diff --git a/docs/docs/expression_language/cookbook/moderation.ipynb b/docs/docs/guides/productionization/safety/moderation.ipynb similarity index 73% rename from docs/docs/expression_language/cookbook/moderation.ipynb rename to docs/docs/guides/productionization/safety/moderation.ipynb index 3377b9c167..515f5024f5 100644 --- a/docs/docs/expression_language/cookbook/moderation.ipynb +++ b/docs/docs/guides/productionization/safety/moderation.ipynb @@ -5,9 +5,19 @@ "id": "4927a727-b4c8-453c-8c83-bd87b4fcac14", "metadata": {}, "source": [ - "# Adding moderation\n", + "# Moderation chain\n", "\n", - "This shows how to add in moderation (or other safeguards) around your LLM application." + "This notebook walks through examples of how to use a moderation chain, and several common ways for doing so. \n", + "Moderation chains are useful for detecting text that could be hateful, violent, etc. This can be useful to apply on both user input, but also on the output of a Language Model. \n", + "Some API providers specifically prohibit you, or your end users, from generating some \n", + "types of harmful content. To comply with this (and to just generally prevent your application from being harmful) \n", + "you may want to add a moderation chain to your sequences in order to make sure any output \n", + "the LLM generates is not harmful.\n", + "\n", + "If the content passed into the moderation chain is harmful, there is not one best way to handle it.\n", + "It probably depends on your application. Sometimes you may want to throw an error \n", + "(and have your application handle that). Other times, you may want to return something to \n", + "the user explaining that the text was harmful." ] }, { diff --git a/docs/docs/guides/privacy/presidio_data_anonymization/index.ipynb b/docs/docs/guides/productionization/safety/presidio_data_anonymization/index.ipynb similarity index 100% rename from docs/docs/guides/privacy/presidio_data_anonymization/index.ipynb rename to docs/docs/guides/productionization/safety/presidio_data_anonymization/index.ipynb diff --git a/docs/docs/guides/privacy/presidio_data_anonymization/multi_language.ipynb b/docs/docs/guides/productionization/safety/presidio_data_anonymization/multi_language.ipynb similarity index 100% rename from docs/docs/guides/privacy/presidio_data_anonymization/multi_language.ipynb rename to docs/docs/guides/productionization/safety/presidio_data_anonymization/multi_language.ipynb diff --git a/docs/docs/guides/privacy/presidio_data_anonymization/qa_privacy_protection.ipynb b/docs/docs/guides/productionization/safety/presidio_data_anonymization/qa_privacy_protection.ipynb similarity index 99% rename from docs/docs/guides/privacy/presidio_data_anonymization/qa_privacy_protection.ipynb rename to docs/docs/guides/productionization/safety/presidio_data_anonymization/qa_privacy_protection.ipynb index 431e06d778..0791996598 100644 --- a/docs/docs/guides/privacy/presidio_data_anonymization/qa_privacy_protection.ipynb +++ b/docs/docs/guides/productionization/safety/presidio_data_anonymization/qa_privacy_protection.ipynb @@ -24,7 +24,7 @@ "\n", "\n", "\n", - "In the following notebook, we will not go into the details of how the anonymizer works. If you are interested, please visit [this part of the documentation](/docs/guides/privacy/presidio_data_anonymization/).\n", + "In the following notebook, we will not go into the details of how the anonymizer works. If you are interested, please visit [this part of the documentation](/docs/guides/productionization/safety/presidio_data_anonymization/).\n", "\n", "## Quickstart\n", "\n", diff --git a/docs/docs/guides/privacy/presidio_data_anonymization/reversible.ipynb b/docs/docs/guides/productionization/safety/presidio_data_anonymization/reversible.ipynb similarity index 100% rename from docs/docs/guides/privacy/presidio_data_anonymization/reversible.ipynb rename to docs/docs/guides/productionization/safety/presidio_data_anonymization/reversible.ipynb diff --git a/docs/docs/guides/safety/_category_.yml b/docs/docs/guides/safety/_category_.yml deleted file mode 100644 index 8631f769dc..0000000000 --- a/docs/docs/guides/safety/_category_.yml +++ /dev/null @@ -1 +0,0 @@ -label: 'Safety' diff --git a/docs/docs/guides/safety/index.mdx b/docs/docs/guides/safety/index.mdx deleted file mode 100644 index 644c2cc4ca..0000000000 --- a/docs/docs/guides/safety/index.mdx +++ /dev/null @@ -1,10 +0,0 @@ -# Safety - -One of the key concerns with using LLMs is that they may generate harmful or unethical text. This is an area of active research in the field. Here we present some built-in chains inspired by this research, which are intended to make the outputs of LLMs safer. - -- [Amazon Comprehend moderation chain](/docs/guides/safety/amazon_comprehend_chain): Use [Amazon Comprehend](https://aws.amazon.com/comprehend/) to detect and handle Personally Identifiable Information (PII) and toxicity. -- [Constitutional chain](/docs/guides/safety/constitutional_chain): Prompt the model with a set of principles which should guide the model behavior. -- [Hugging Face prompt injection identification](/docs/guides/safety/hugging_face_prompt_injection): Detect and handle prompt injection attacks. -- [Layerup Security](/docs/guides/safety/layerup_security): Easily mask PII & sensitive data, detect and mitigate 10+ LLM-based threat vectors, including PII & sensitive data, prompt injection, hallucination, abuse, and more. -- [Logical Fallacy chain](/docs/guides/safety/logical_fallacy_chain): Checks the model output against logical fallacies to correct any deviation. -- [Moderation chain](/docs/guides/safety/moderation): Check if any output text is harmful and flag it. diff --git a/docs/docs/guides/safety/moderation.mdx b/docs/docs/guides/safety/moderation.mdx deleted file mode 100644 index 4afda1556f..0000000000 --- a/docs/docs/guides/safety/moderation.mdx +++ /dev/null @@ -1,267 +0,0 @@ -# Moderation chain - -This notebook walks through examples of how to use a moderation chain, and several common ways for doing so. -Moderation chains are useful for detecting text that could be hateful, violent, etc. This can be useful to apply on both user input, but also on the output of a Language Model. -Some API providers, like OpenAI, [specifically prohibit](https://beta.openai.com/docs/usage-policies/use-case-policy) you, or your end users, from generating some -types of harmful content. To comply with this (and to just generally prevent your application from being harmful) -you may often want to append a moderation chain to any LLMChains, in order to make sure any output -the LLM generates is not harmful. - -If the content passed into the moderation chain is harmful, there is not one best way to handle it, -it probably depends on your application. Sometimes you may want to throw an error in the Chain -(and have your application handle that). Other times, you may want to return something to -the user explaining that the text was harmful. There could be other ways to handle it. -We will cover all these ways in this walkthrough. - -We'll show: - -1. How to run any piece of text through a moderation chain. -2. How to append a Moderation chain to an LLMChain. - - - - -```python -from langchain_openai import OpenAI -from langchain.chains import OpenAIModerationChain, SequentialChain, LLMChain, SimpleSequentialChain -from langchain_core.prompts import PromptTemplate -``` - -## How to use the moderation chain - -Here's an example of using the moderation chain with default settings (will return a string -explaining stuff was flagged). - - -```python -moderation_chain = OpenAIModerationChain() - -moderation_chain.run("This is okay") -``` - - - -``` - 'This is okay' -``` - - - - -```python -moderation_chain.run("I will kill you") -``` - - - -``` - "Text was found that violates OpenAI's content policy." -``` - - - -Here's an example of using the moderation chain to throw an error. - - -```python -moderation_chain_error = OpenAIModerationChain(error=True) - -moderation_chain_error.run("This is okay") -``` - - - -``` - 'This is okay' -``` - - - - -```python -moderation_chain_error.run("I will kill you") -``` - - - -``` - --------------------------------------------------------------------------- - - ValueError Traceback (most recent call last) - - Cell In[7], line 1 - ----> 1 moderation_chain_error.run("I will kill you") - - - File ~/workplace/langchain/langchain/chains/base.py:138, in Chain.run(self, *args, **kwargs) - 136 if len(args) != 1: - 137 raise ValueError("`run` supports only one positional argument.") - --> 138 return self(args[0])[self.output_keys[0]] - 140 if kwargs and not args: - 141 return self(kwargs)[self.output_keys[0]] - - - File ~/workplace/langchain/langchain/chains/base.py:112, in Chain.__call__(self, inputs, return_only_outputs) - 108 if self.verbose: - 109 print( - 110 f"\n\n\033[1m> Entering new {self.__class__.__name__} chain...\033[0m" - 111 ) - --> 112 outputs = self._call(inputs) - 113 if self.verbose: - 114 print(f"\n\033[1m> Finished {self.__class__.__name__} chain.\033[0m") - - - File ~/workplace/langchain/langchain/chains/moderation.py:81, in OpenAIModerationChain._call(self, inputs) - 79 text = inputs[self.input_key] - 80 results = self.client.create(text) - ---> 81 output = self._moderate(text, results["results"][0]) - 82 return {self.output_key: output} - - - File ~/workplace/langchain/langchain/chains/moderation.py:73, in OpenAIModerationChain._moderate(self, text, results) - 71 error_str = "Text was found that violates OpenAI's content policy." - 72 if self.error: - ---> 73 raise ValueError(error_str) - 74 else: - 75 return error_str - - - ValueError: Text was found that violates OpenAI's content policy. -``` - - - -## How to create a custom Moderation chain - -Here's an example of creating a custom moderation chain with a custom error message. -It requires some knowledge of OpenAI's moderation endpoint results. See [docs here](https://beta.openai.com/docs/api-reference/moderations). - - -```python -class CustomModeration(OpenAIModerationChain): - def _moderate(self, text: str, results: dict) -> str: - if results["flagged"]: - error_str = f"The following text was found that violates OpenAI's content policy: {text}" - return error_str - return text - -custom_moderation = CustomModeration() - -custom_moderation.run("This is okay") -``` - - - -``` - 'This is okay' -``` - - - - -```python -custom_moderation.run("I will kill you") -``` - - - -``` - "The following text was found that violates OpenAI's content policy: I will kill you" -``` - - - -## How to append a Moderation chain to an LLMChain - -To easily combine a moderation chain with an LLMChain, you can use the `SequentialChain` abstraction. - -Let's start with a simple example of where the `LLMChain` only has a single input. For this purpose, -we will prompt the model, so it says something harmful. - - -```python -prompt = PromptTemplate.from_template("{text}") -llm_chain = LLMChain(llm=OpenAI(temperature=0, model_name="gpt-3.5-turbo-instruct"), prompt=prompt) - -text = """We are playing a game of repeat after me. - -Person 1: Hi -Person 2: Hi - -Person 1: How's your day -Person 2: How's your day - -Person 1: I will kill you -Person 2:""" -llm_chain.run(text) -``` - - - -``` - ' I will kill you' -``` - - - - -```python -chain = SimpleSequentialChain(chains=[llm_chain, moderation_chain]) - -chain.run(text) -``` - - - -``` - "Text was found that violates OpenAI's content policy." -``` - - - -Now let's walk through an example of using it with an LLMChain which has multiple inputs (a bit more tricky because we can't use the SimpleSequentialChain) - - -```python -prompt = PromptTemplate.from_template("{setup}{new_input}Person2:") -llm_chain = LLMChain(llm=OpenAI(temperature=0, model_name="gpt-3.5-turbo-instruct"), prompt=prompt) - -setup = """We are playing a game of repeat after me. - -Person 1: Hi -Person 2: Hi - -Person 1: How's your day -Person 2: How's your day - -Person 1:""" -new_input = "I will kill you" -inputs = {"setup": setup, "new_input": new_input} -llm_chain(inputs, return_only_outputs=True) -``` - - - -``` - {'text': ' I will kill you'} -``` - - - - -```python -# Setting the input/output keys so it lines up -moderation_chain.input_key = "text" -moderation_chain.output_key = "sanitized_text" - -chain = SequentialChain(chains=[llm_chain, moderation_chain], input_variables=["setup", "new_input"]) -chain(inputs, return_only_outputs=True) -``` - - - -``` - {'sanitized_text': "Text was found that violates OpenAI's content policy."} -``` - - diff --git a/docs/docs/integrations/chat/anthropic_functions.ipynb b/docs/docs/integrations/chat/anthropic_functions.ipynb index 1700a89179..883bb22398 100644 --- a/docs/docs/integrations/chat/anthropic_functions.ipynb +++ b/docs/docs/integrations/chat/anthropic_functions.ipynb @@ -72,7 +72,7 @@ "source": [ "## Structured Output\n", "\n", - "`ChatAnthropicTools` also implements the [`with_structured_output` spec](/docs/guides/structured_output) for extracting values. Note: this may not be as stable as with models that explicitly offer tool calling." + "`ChatAnthropicTools` also implements the [`with_structured_output` spec](/docs/modules/model_io/chat/structured_output) for extracting values. Note: this may not be as stable as with models that explicitly offer tool calling." ] }, { diff --git a/docs/docs/integrations/platforms/aws.mdx b/docs/docs/integrations/platforms/aws.mdx index b6b7aa9ab6..ffd8f08084 100644 --- a/docs/docs/integrations/platforms/aws.mdx +++ b/docs/docs/integrations/platforms/aws.mdx @@ -283,7 +283,7 @@ We need to install the `boto3` and `nltk` libraries. pip install boto3 nltk ``` -See a [usage example](/docs/guides/safety/amazon_comprehend_chain). +See a [usage example](/docs/guides/productionization/safety/amazon_comprehend_chain). ```python from langchain_experimental.comprehend_moderation import AmazonComprehendModerationChain diff --git a/docs/docs/integrations/platforms/microsoft.mdx b/docs/docs/integrations/platforms/microsoft.mdx index a18bdcd816..57a55b0bbd 100644 --- a/docs/docs/integrations/platforms/microsoft.mdx +++ b/docs/docs/integrations/platforms/microsoft.mdx @@ -346,7 +346,7 @@ pip install langchain-experimental openai presidio-analyzer presidio-anonymizer python -m spacy download en_core_web_lg ``` -See [usage examples](/docs/guides/privacy/presidio_data_anonymization/). +See [usage examples](/docs/guides/productionization/safety/presidio_data_anonymization/). ```python from langchain_experimental.data_anonymizer import PresidioAnonymizer, PresidioReversibleAnonymizer diff --git a/docs/docs/integrations/platforms/openai.mdx b/docs/docs/integrations/platforms/openai.mdx index 0e8f25d266..f0b33faabb 100644 --- a/docs/docs/integrations/platforms/openai.mdx +++ b/docs/docs/integrations/platforms/openai.mdx @@ -111,7 +111,7 @@ For a more detailed walkthrough of this, see [this notebook](/docs/modules/data_ ## Chain -See a [usage example](/docs/guides/safety/moderation). +See a [usage example](/docs/guides/productionization/safety/moderation). ```python from langchain.chains import OpenAIModerationChain diff --git a/docs/docs/integrations/providers/golden.mdx b/docs/docs/integrations/providers/golden.mdx index 3be3e017ce..793958de34 100644 --- a/docs/docs/integrations/providers/golden.mdx +++ b/docs/docs/integrations/providers/golden.mdx @@ -31,4 +31,4 @@ from langchain.agents import load_tools tools = load_tools(["golden-query"]) ``` -For more information on tools, see [this page](/docs/modules/agents/tools/). +For more information on tools, see [this page](/docs/modules/tools/). diff --git a/docs/docs/integrations/providers/google_serper.mdx b/docs/docs/integrations/providers/google_serper.mdx index ef07f017fe..c960e1df6a 100644 --- a/docs/docs/integrations/providers/google_serper.mdx +++ b/docs/docs/integrations/providers/google_serper.mdx @@ -70,4 +70,4 @@ from langchain.agents import load_tools tools = load_tools(["google-serper"]) ``` -For more information on tools, see [this page](/docs/modules/agents/tools/). +For more information on tools, see [this page](/docs/modules/tools/). diff --git a/docs/docs/integrations/providers/ollama.mdx b/docs/docs/integrations/providers/ollama.mdx index 86f981cf95..c7d5464f29 100644 --- a/docs/docs/integrations/providers/ollama.mdx +++ b/docs/docs/integrations/providers/ollama.mdx @@ -7,7 +7,7 @@ >It optimizes setup and configuration details, including GPU usage. >For a complete list of supported models and model variants, see the [Ollama model library](https://ollama.ai/library). -See [this guide](/docs/guides/local_llms#quickstart) for more details +See [this guide](/docs/guides/development/local_llms#quickstart) for more details on how to use `Ollama` with LangChain. ## Installation and Setup diff --git a/docs/docs/integrations/providers/openweathermap.mdx b/docs/docs/integrations/providers/openweathermap.mdx index 35344e2a11..434d3358f3 100644 --- a/docs/docs/integrations/providers/openweathermap.mdx +++ b/docs/docs/integrations/providers/openweathermap.mdx @@ -41,4 +41,4 @@ from langchain.agents import load_tools tools = load_tools(["openweathermap-api"]) ``` -For more information on tools, see [this page](/docs/modules/agents/tools/). +For more information on tools, see [this page](/docs/modules/tools/). diff --git a/docs/docs/integrations/providers/searchapi.mdx b/docs/docs/integrations/providers/searchapi.mdx index f5e55ca390..9912d9ed3b 100644 --- a/docs/docs/integrations/providers/searchapi.mdx +++ b/docs/docs/integrations/providers/searchapi.mdx @@ -77,4 +77,4 @@ from langchain.agents import load_tools tools = load_tools(["searchapi"]) ``` -For more information on tools, see [this page](/docs/modules/agents/tools/). +For more information on tools, see [this page](/docs/modules/tools/). diff --git a/docs/docs/integrations/providers/searx.mdx b/docs/docs/integrations/providers/searx.mdx index 6bc01913d4..b8fec71e34 100644 --- a/docs/docs/integrations/providers/searx.mdx +++ b/docs/docs/integrations/providers/searx.mdx @@ -87,4 +87,4 @@ arxiv_tool = SearxSearchResults(name="Arxiv", wrapper=wrapper, }) ``` -For more information on tools, see [this page](/docs/modules/agents/tools/). +For more information on tools, see [this page](/docs/modules/tools/). diff --git a/docs/docs/integrations/providers/serpapi.mdx b/docs/docs/integrations/providers/serpapi.mdx index 5bf5ec7fa6..e102b740a7 100644 --- a/docs/docs/integrations/providers/serpapi.mdx +++ b/docs/docs/integrations/providers/serpapi.mdx @@ -28,4 +28,4 @@ from langchain.agents import load_tools tools = load_tools(["serpapi"]) ``` -For more information on this, see [this page](/docs/modules/agents/tools) +For more information on this, see [this page](/docs/modules/tools) diff --git a/docs/docs/integrations/providers/stackexchange.mdx b/docs/docs/integrations/providers/stackexchange.mdx index 666b4337e4..5d3407c024 100644 --- a/docs/docs/integrations/providers/stackexchange.mdx +++ b/docs/docs/integrations/providers/stackexchange.mdx @@ -33,4 +33,4 @@ from langchain.agents import load_tools tools = load_tools(["stackexchange"]) ``` -For more information on tools, see [this page](/docs/modules/agents/tools/). +For more information on tools, see [this page](/docs/modules/tools/). diff --git a/docs/docs/integrations/providers/wolfram_alpha.mdx b/docs/docs/integrations/providers/wolfram_alpha.mdx index 24ae6d68f4..f5b24fa0ac 100644 --- a/docs/docs/integrations/providers/wolfram_alpha.mdx +++ b/docs/docs/integrations/providers/wolfram_alpha.mdx @@ -36,4 +36,4 @@ from langchain.agents import load_tools tools = load_tools(["wolfram-alpha"]) ``` -For more information on tools, see [this page](/docs/modules/agents/tools/). +For more information on tools, see [this page](/docs/modules/tools/). diff --git a/docs/docs/langsmith/index.md b/docs/docs/langsmith/index.md index fa0a6a451a..0dd63cf10a 100644 --- a/docs/docs/langsmith/index.md +++ b/docs/docs/langsmith/index.md @@ -2,7 +2,7 @@ sidebar_class_name: hidden --- -# LangSmith +# πŸ¦œπŸ› οΈ LangSmith [LangSmith](https://smith.langchain.com) helps you trace and evaluate your language model applications and intelligent agents to help you move from prototype to production. diff --git a/docs/docs/modules/agents/agent_types/index.mdx b/docs/docs/modules/agents/agent_types/index.mdx index 67cac7f324..ed61cf8209 100644 --- a/docs/docs/modules/agents/agent_types/index.mdx +++ b/docs/docs/modules/agents/agent_types/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 2 +title: Types --- # Agent Types diff --git a/docs/docs/modules/agents/agent_types/openai_assistants.ipynb b/docs/docs/modules/agents/agent_types/openai_assistants.ipynb index 01b1a5314e..d169f0c0cb 100644 --- a/docs/docs/modules/agents/agent_types/openai_assistants.ipynb +++ b/docs/docs/modules/agents/agent_types/openai_assistants.ipynb @@ -100,7 +100,7 @@ "source": [ "import getpass\n", "\n", - "from langchain.tools import DuckDuckGoSearchRun, E2BDataAnalysisTool\n", + "from langchain_community.tools import DuckDuckGoSearchRun, E2BDataAnalysisTool\n", "\n", "tools = [E2BDataAnalysisTool(api_key=getpass.getpass()), DuckDuckGoSearchRun()]" ] diff --git a/docs/docs/modules/agents/agent_types/openai_functions_agent.ipynb b/docs/docs/modules/agents/agent_types/openai_functions_agent.ipynb index c3a8b93ed2..59b4860044 100644 --- a/docs/docs/modules/agents/agent_types/openai_functions_agent.ipynb +++ b/docs/docs/modules/agents/agent_types/openai_functions_agent.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 0\n", + "sidebar_position: 1\n", "---" ] }, diff --git a/docs/docs/modules/agents/agent_types/xml_agent.ipynb b/docs/docs/modules/agents/agent_types/xml_agent.ipynb index 8558d96757..a8f29dd424 100644 --- a/docs/docs/modules/agents/agent_types/xml_agent.ipynb +++ b/docs/docs/modules/agents/agent_types/xml_agent.ipynb @@ -36,7 +36,7 @@ "source": [ "from langchain import hub\n", "from langchain.agents import AgentExecutor, create_xml_agent\n", - "from langchain_community.chat_models import ChatAnthropic\n", + "from langchain_anthropic.chat_models import ChatAnthropic\n", "from langchain_community.tools.tavily_search import TavilySearchResults" ] }, @@ -65,7 +65,9 @@ "id": "6b300d66", "metadata": {}, "source": [ - "## Create Agent" + "## Create Agent\n", + "\n", + "Below we will use LangChain's built-in [create_xml_agent](https://api.python.langchain.com/en/latest/agents/langchain.agents.xml.base.create_xml_agent.html) constructor." ] }, { @@ -87,7 +89,7 @@ "outputs": [], "source": [ "# Choose the LLM that will drive the agent\n", - "llm = ChatAnthropic(model=\"claude-2\")\n", + "llm = ChatAnthropic(model=\"claude-2.1\")\n", "\n", "# Construct the XML agent\n", "agent = create_xml_agent(llm, tools, prompt)" @@ -199,10 +201,149 @@ ")" ] }, + { + "cell_type": "markdown", + "id": "eb5dd54e-4081-4869-aad1-c4253f58486a", + "metadata": {}, + "source": [ + "# Custom XML Agents\n", + "\n", + "**Note:** For greater customizability, we recommend checking out [LangGraph](/docs/langgraph).\n", + "\n", + "Here we provide an example of a custom XML Agent implementation, to give a sense for what `create_xml_agent` is doing under the hood." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "d5965644-660b-4c2c-82c6-370e409fdad1", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.agents.output_parsers import XMLAgentOutputParser" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "59cbc911-42a3-4763-ac75-7d26f913d869", + "metadata": {}, + "outputs": [], + "source": [ + "# Logic for going from intermediate steps to a string to pass into model\n", + "# This is pretty tied to the prompt\n", + "def convert_intermediate_steps(intermediate_steps):\n", + " log = \"\"\n", + " for action, observation in intermediate_steps:\n", + " log += (\n", + " f\"{action.tool}{action.tool_input}\"\n", + " f\"{observation}\"\n", + " )\n", + " return log\n", + "\n", + "\n", + "# Logic for converting tools to string to go in prompt\n", + "def convert_tools(tools):\n", + " return \"\\n\".join([f\"{tool.name}: {tool.description}\" for tool in tools])" + ] + }, + { + "cell_type": "markdown", + "id": "a7702330-af4c-49e8-8af6-0bd98bd85ff4", + "metadata": {}, + "source": [ + "Building an agent from a runnable usually involves a few things:\n", + "\n", + "1. Data processing for the intermediate steps. These need to be represented in a way that the language model can recognize them. This should be pretty tightly coupled to the instructions in the prompt\n", + "\n", + "2. The prompt itself\n", + "\n", + "3. The model, complete with stop tokens if needed\n", + "\n", + "4. The output parser - should be in sync with how the prompt specifies things to be formatted." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7908fd6b-f153-48a3-9337-a4babfc2b8bb", + "metadata": {}, + "outputs": [], + "source": [ + "agent = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: convert_intermediate_steps(\n", + " x[\"intermediate_steps\"]\n", + " ),\n", + " }\n", + " | prompt.partial(tools=convert_tools(tools))\n", + " | llm.bind(stop=[\"\", \"\"])\n", + " | XMLAgentOutputParser()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "c44f0d72-629c-44bc-bd46-1a4ba9180b26", + "metadata": {}, + "outputs": [], + "source": [ + "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "343b8709-8b7a-49f4-99c2-1cd5e4d3cae0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3mtavily_search_results_json\n", + "what is LangChain\u001b[0m\u001b[36;1m\u001b[1;3m[{'url': 'https://www.techtarget.com/searchEnterpriseAI/definition/LangChain', 'content': \"Everything you need to know\\nWhat are the features of LangChain?\\nLangChain is made up of the following modules that ensure the multiple components needed to make an effective NLP app can run smoothly:\\nWhat are the integrations of LangChain?\\nLangChain typically builds applications using integrations with LLM providers and external sources where data can be found and stored. What is synthetic data?\\nExamples and use cases for LangChain\\nThe LLM-based applications LangChain is capable of building can be applied to multiple advanced use cases within various industries and vertical markets, such as the following:\\nReaping the benefits of NLP is a key of why LangChain is important. As the airline giant moves more of its data workloads to the cloud, tools from Intel's Granulate are making platforms such as ...\\nThe vendor's new platform, now in beta testing, combines its existing lakehouse with AI to better enable users to manage and ...\\n The following steps are required to use this:\\nIn this scenario, the language model would be expected to take the two input variables -- the adjective and the content -- and produce a fascinating fact about zebras as its output.\\n The goal of LangChain is to link powerful LLMs, such as OpenAI's GPT-3.5 and GPT-4, to an array of external data sources to create and reap the benefits of natural language processing (NLP) applications.\\n\"}]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "LangChain is a platform developed by Anthropic that enables users to build NLP applications by linking large language models like GPT-3.5 and GPT-4 to external data sources. It provides modules for managing and integrating different components needed for NLP apps.\n", + "\n", + "Some key capabilities and features of LangChain:\n", + "\n", + "- Allows linking LLMs to external data sources to create customized NLP apps\n", + "- Provides modules to manage integration of LLMs, data sources, storage etc. \n", + "- Enables building conversational AI apps, summarization, search, and other NLP capabilities\n", + "- Helps users reap benefits of NLP and LLMs for use cases across industries\n", + "\n", + "So in summary, it is a platform to build and deploy advanced NLP models by leveraging capabilities of large language models in a more customizable and scalable way.\n", + "\n", + "\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + }, + { + "data": { + "text/plain": [ + "{'input': 'what is LangChain?',\n", + " 'output': '\\nLangChain is a platform developed by Anthropic that enables users to build NLP applications by linking large language models like GPT-3.5 and GPT-4 to external data sources. It provides modules for managing and integrating different components needed for NLP apps.\\n\\nSome key capabilities and features of LangChain:\\n\\n- Allows linking LLMs to external data sources to create customized NLP apps\\n- Provides modules to manage integration of LLMs, data sources, storage etc. \\n- Enables building conversational AI apps, summarization, search, and other NLP capabilities\\n- Helps users reap benefits of NLP and LLMs for use cases across industries\\n\\nSo in summary, it is a platform to build and deploy advanced NLP models by leveraging capabilities of large language models in a more customizable and scalable way.\\n\\n'}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent_executor.invoke({\"input\": \"what is LangChain?\"})" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "53ad1a2c", + "id": "ae96c1b2-a3ce-48ec-a488-0c095e8e4971", "metadata": {}, "outputs": [], "source": [] @@ -224,7 +365,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/docs/docs/modules/agents/how_to/custom_agent.ipynb b/docs/docs/modules/agents/how_to/custom_agent.ipynb index feb6b96a56..e49200da22 100644 --- a/docs/docs/modules/agents/how_to/custom_agent.ipynb +++ b/docs/docs/modules/agents/how_to/custom_agent.ipynb @@ -56,7 +56,7 @@ "Next, let's define some tools to use.\n", "Let's write a really simple Python function to calculate the length of a word that is passed in.\n", "\n", - "Note that here the function docstring that we use is pretty important. Read more about why this is the case [here](/docs/modules/agents/tools/custom_tools)" + "Note that here the function docstring that we use is pretty important. Read more about why this is the case [here](/docs/modules/tools/custom_tools)" ] }, { diff --git a/docs/docs/modules/agents/how_to/structured_tools.ipynb b/docs/docs/modules/agents/how_to/structured_tools.ipynb deleted file mode 100644 index ee9293e826..0000000000 --- a/docs/docs/modules/agents/how_to/structured_tools.ipynb +++ /dev/null @@ -1,142 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "id": "473081cc", - "metadata": {}, - "source": [ - "---\n", - "sidebar_position: 1\n", - "---" - ] - }, - { - "cell_type": "markdown", - "id": "16ee4216", - "metadata": {}, - "source": [ - "# Structured Tools" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "670078c4", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import List\n", - "\n", - "from langchain_core.tools import tool\n", - "\n", - "\n", - "@tool\n", - "def get_data(n: int) -> List[dict]:\n", - " \"\"\"Get n datapoints.\"\"\"\n", - " return [{\"name\": \"foo\", \"value\": \"bar\"}] * n\n", - "\n", - "\n", - "tools = [get_data]" - ] - }, - { - "cell_type": "markdown", - "id": "5e04164b", - "metadata": {}, - "source": [ - "We will use a prompt from the hub - you can inspect the prompt more at [https://smith.langchain.com/hub/hwchase17/openai-functions-agent](https://smith.langchain.com/hub/hwchase17/openai-functions-agent)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "d8c5d907", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain import hub\n", - "from langchain.agents import AgentExecutor, create_openai_functions_agent\n", - "from langchain_openai import ChatOpenAI\n", - "\n", - "# Get the prompt to use - you can modify this!\n", - "# If you want to see the prompt in full, you can at: https://smith.langchain.com/hub/hwchase17/openai-functions-agent\n", - "prompt = hub.pull(\"hwchase17/openai-functions-agent\")\n", - "\n", - "llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n", - "\n", - "agent = create_openai_functions_agent(llm, tools, prompt)\n", - "agent_executor = AgentExecutor(agent=agent, tools=tools)" - ] - }, - { - "cell_type": "markdown", - "id": "cba9a9eb", - "metadata": {}, - "source": [ - "## Stream intermediate steps\n", - "\n", - "Let's look at how to stream intermediate steps. We can do this easily by just using the `.stream` method on the AgentExecutor\n", - "\n", - "We can then parse the results to get actions (tool inputs) and observtions (tool outputs)." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "b6bd9bf2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Calling Tool ```get_data``` with input ```{'n': 3}```\n", - "Got result: ```[{'name': 'foo', 'value': 'bar'}, {'name': 'foo', 'value': 'bar'}, {'name': 'foo', 'value': 'bar'}]```\n" - ] - } - ], - "source": [ - "for chunk in agent_executor.stream({\"input\": \"get me three datapoints\"}):\n", - " # Agent Action\n", - " if \"actions\" in chunk:\n", - " for action in chunk[\"actions\"]:\n", - " print(\n", - " f\"Calling Tool ```{action.tool}``` with input ```{action.tool_input}```\"\n", - " )\n", - " # Observation\n", - " elif \"steps\" in chunk:\n", - " for step in chunk[\"steps\"]:\n", - " print(f\"Got result: ```{step.observation}```\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "af9e32fe", - "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.10.1" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/modules/agents/index.ipynb b/docs/docs/modules/agents/index.ipynb index 7f009d17bd..11f121772a 100644 --- a/docs/docs/modules/agents/index.ipynb +++ b/docs/docs/modules/agents/index.ipynb @@ -35,9 +35,9 @@ "\n", "There are many different types of agents to use. For a overview of the different types and when to use them, please check out [this section](/docs/modules/agents/agent_types/).\n", "\n", - "## [Tools](/docs/modules/agents/tools/)\n", + "## [Tools](/docs/modules/tools/)\n", "\n", - "Agents are only as good as the tools they have. For a comprehensive guide on tools, please see [this section](/docs/modules/agents/tools/).\n", + "Agents are only as good as the tools they have. For a comprehensive guide on tools, please see [this section](/docs/modules/tools/).\n", "\n", "## How To Guides\n", "\n", diff --git a/docs/docs/modules/callbacks/token_counting.ipynb b/docs/docs/modules/callbacks/token_counting.ipynb index 8bc7a4f091..fc748f099a 100644 --- a/docs/docs/modules/callbacks/token_counting.ipynb +++ b/docs/docs/modules/callbacks/token_counting.ipynb @@ -11,26 +11,26 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "id": "195fd686", "metadata": {}, "outputs": [], "source": [ "import asyncio\n", "\n", - "from langchain.callbacks import get_openai_callback\n", + "from langchain_community.callbacks import get_openai_callback\n", "from langchain_openai import OpenAI\n", "\n", "llm = OpenAI(temperature=0)\n", "with get_openai_callback() as cb:\n", - " llm(\"What is the square root of 4?\")\n", + " llm.invoke(\"What is the square root of 4?\")\n", "\n", "total_tokens = cb.total_tokens\n", "assert total_tokens > 0\n", "\n", "with get_openai_callback() as cb:\n", - " llm(\"What is the square root of 4?\")\n", - " llm(\"What is the square root of 4?\")\n", + " llm.invoke(\"What is the square root of 4?\")\n", + " llm.invoke(\"What is the square root of 4?\")\n", "\n", "assert cb.total_tokens == total_tokens * 2\n", "\n", @@ -50,21 +50,13 @@ "await task\n", "assert cb.total_tokens == total_tokens" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e94e0d3", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "venv", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "venv" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -76,7 +68,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/docs/docs/modules/composition.mdx b/docs/docs/modules/composition.mdx new file mode 100644 index 0000000000..05de6bbc46 --- /dev/null +++ b/docs/docs/modules/composition.mdx @@ -0,0 +1,26 @@ +--- +sidebar_class_name: hidden +--- + +# Composition + +This section contains higher-level components that combine other arbitrary systems (e.g. external APIs and services) and/or LangChain primitives together. + +A good primer for this section would be reading the sections on [LangChain Expression Language](/docs/expression_language/get_started) and becoming +familiar with constructing sequences via piping and the various primitives offered. + +The components covered in this section are: + +## [Tools](/docs/modules/tools/) + +Tools provide an interface for LLMs and other components to interact with other systems. Examples include Wikipedia, a calculator, and a Python REPL. + +## [Agents](/docs/modules/agents) + +Agents use a language model to decide actions to take, often defined by a tool. They require an `executor`, which is the runtime for the agent. +The executor is what actually calls the agent, executes the tools it chooses, passes the action outputs back to the agent, and repeats. +The agent is responsible for parsing output from the previous results and choosing the next steps. + +## [Chains](/docs/modules/chains) + +Building block-style compositions of other primitives and components. diff --git a/docs/docs/modules/data_connection/document_loaders/index.mdx b/docs/docs/modules/data_connection/document_loaders/index.mdx index 0c9d7489c8..bb12e97e6b 100644 --- a/docs/docs/modules/data_connection/document_loaders/index.mdx +++ b/docs/docs/modules/data_connection/document_loaders/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 0 +sidebar_class_name: hidden --- # Document loaders diff --git a/docs/docs/modules/data_connection/document_transformers/index.mdx b/docs/docs/modules/data_connection/document_transformers/index.mdx index c6d891a960..1efc1b021e 100644 --- a/docs/docs/modules/data_connection/document_transformers/index.mdx +++ b/docs/docs/modules/data_connection/document_transformers/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 1 +sidebar_class_name: hidden --- # Text Splitters diff --git a/docs/docs/modules/data_connection/index.mdx b/docs/docs/modules/data_connection/index.mdx index 385b46a5f0..78806d1749 100644 --- a/docs/docs/modules/data_connection/index.mdx +++ b/docs/docs/modules/data_connection/index.mdx @@ -16,21 +16,21 @@ This encompasses several key modules. ![Illustrative diagram showing the data connection process with steps: Source, Load, Transform, Embed, Store, and Retrieve.](/img/data_connection.jpg "Data Connection Process Diagram") -**[Document loaders](/docs/modules/data_connection/document_loaders/)** +## [Document loaders](/docs/modules/data_connection/document_loaders/) **Document loaders** load documents from many different sources. LangChain provides over 100 different document loaders as well as integrations with other major providers in the space, like AirByte and Unstructured. LangChain provides integrations to load all types of documents (HTML, PDF, code) from all types of locations (private S3 buckets, public websites). -**[Text Splitting](/docs/modules/data_connection/document_transformers/)** +## [Text Splitting](/docs/modules/data_connection/document_transformers/) A key part of retrieval is fetching only the relevant parts of documents. This involves several transformation steps to prepare the documents for retrieval. One of the primary ones here is splitting (or chunking) a large document into smaller chunks. LangChain provides several transformation algorithms for doing this, as well as logic optimized for specific document types (code, markdown, etc). -**[Text embedding models](/docs/modules/data_connection/text_embedding/)** +## [Text embedding models](/docs/modules/data_connection/text_embedding/) Another key part of retrieval is creating embeddings for documents. Embeddings capture the semantic meaning of the text, allowing you to quickly and @@ -40,14 +40,14 @@ from open-source to proprietary API, allowing you to choose the one best suited for your needs. LangChain provides a standard interface, allowing you to easily swap between models. -**[Vector stores](/docs/modules/data_connection/vectorstores/)** +## [Vector stores](/docs/modules/data_connection/vectorstores/) With the rise of embeddings, there has emerged a need for databases to support efficient storage and searching of these embeddings. LangChain provides integrations with over 50 different vectorstores, from open-source local ones to cloud-hosted proprietary ones, allowing you to choose the one best suited for your needs. LangChain exposes a standard interface, allowing you to easily swap between vector stores. -**[Retrievers](/docs/modules/data_connection/retrievers/)** +## [Retrievers](/docs/modules/data_connection/retrievers/) Once the data is in the database, you still need to retrieve it. LangChain supports many different retrieval algorithms and is one of the places where we add the most value. @@ -60,7 +60,7 @@ These include: - [Ensemble Retriever](/docs/modules/data_connection/retrievers/ensemble): Sometimes you may want to retrieve documents from multiple different sources, or using multiple different algorithms. The ensemble retriever allows you to easily do this. - And more! -**[Indexing](/docs/modules/data_connection/indexing)** +## [Indexing](/docs/modules/data_connection/indexing) The LangChain **Indexing API** syncs your data from any source into a vector store, helping you: @@ -69,4 +69,4 @@ helping you: - Avoid re-writing unchanged content - Avoid re-computing embeddings over unchanged content -All of which should save you time and money, as well as improve your vector search results. \ No newline at end of file +All of which should save you time and money, as well as improve your vector search results. diff --git a/docs/docs/modules/data_connection/retrievers/ensemble.ipynb b/docs/docs/modules/data_connection/retrievers/ensemble.ipynb index 961819b956..12f81ffa57 100644 --- a/docs/docs/modules/data_connection/retrievers/ensemble.ipynb +++ b/docs/docs/modules/data_connection/retrievers/ensemble.ipynb @@ -28,7 +28,8 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.retrievers import BM25Retriever, EnsembleRetriever\n", + "from langchain.retrievers import EnsembleRetriever\n", + "from langchain_community.retrievers import BM25Retriever\n", "from langchain_community.vectorstores import FAISS\n", "from langchain_openai import OpenAIEmbeddings" ] diff --git a/docs/docs/modules/data_connection/retrievers/index.mdx b/docs/docs/modules/data_connection/retrievers/index.mdx index 40e3efa229..5ec7fbe0e4 100644 --- a/docs/docs/modules/data_connection/retrievers/index.mdx +++ b/docs/docs/modules/data_connection/retrievers/index.mdx @@ -1,6 +1,7 @@ --- sidebar_position: 4 title: Retrievers +sidebar_class_name: hidden --- # Retrievers diff --git a/docs/docs/modules/data_connection/retrievers/time_weighted_vectorstore.ipynb b/docs/docs/modules/data_connection/retrievers/time_weighted_vectorstore.ipynb index bc334cb640..5006792f20 100644 --- a/docs/docs/modules/data_connection/retrievers/time_weighted_vectorstore.ipynb +++ b/docs/docs/modules/data_connection/retrievers/time_weighted_vectorstore.ipynb @@ -28,8 +28,8 @@ "from datetime import datetime, timedelta\n", "\n", "import faiss\n", - "from langchain.docstore import InMemoryDocstore\n", "from langchain.retrievers import TimeWeightedVectorStoreRetriever\n", + "from langchain_community.docstore import InMemoryDocstore\n", "from langchain_community.vectorstores import FAISS\n", "from langchain_core.documents import Document\n", "from langchain_openai import OpenAIEmbeddings" diff --git a/docs/docs/modules/data_connection/text_embedding/caching_embeddings.ipynb b/docs/docs/modules/data_connection/text_embedding/caching_embeddings.ipynb index 079fa3ce1d..639bf0489a 100644 --- a/docs/docs/modules/data_connection/text_embedding/caching_embeddings.ipynb +++ b/docs/docs/modules/data_connection/text_embedding/caching_embeddings.ipynb @@ -1,21 +1,11 @@ { "cells": [ - { - "cell_type": "raw", - "id": "8baf0f21", - "metadata": {}, - "source": [ - "--\n", - "sidebar_label: Caching\n", - "--" - ] - }, { "cell_type": "markdown", "id": "bf4061ce", "metadata": {}, "source": [ - "# CacheBackedEmbeddings\n", + "# Caching\n", "\n", "Embeddings can be stored or temporarily cached to avoid needing to recompute them.\n", "\n", diff --git a/docs/docs/modules/data_connection/text_embedding/index.mdx b/docs/docs/modules/data_connection/text_embedding/index.mdx index 7e473ff674..dd0ff96177 100644 --- a/docs/docs/modules/data_connection/text_embedding/index.mdx +++ b/docs/docs/modules/data_connection/text_embedding/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 2 +sidebar_class_name: hidden --- # Text embedding models diff --git a/docs/docs/modules/data_connection/vectorstores/index.mdx b/docs/docs/modules/data_connection/vectorstores/index.mdx index 4f750338c7..532a26fd4e 100644 --- a/docs/docs/modules/data_connection/vectorstores/index.mdx +++ b/docs/docs/modules/data_connection/vectorstores/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 3 +sidebar_class_name: hidden --- # Vector stores diff --git a/docs/docs/modules/index.mdx b/docs/docs/modules/index.mdx index 4b77c175d2..c8992959c9 100644 --- a/docs/docs/modules/index.mdx +++ b/docs/docs/modules/index.mdx @@ -2,21 +2,56 @@ sidebar_class_name: hidden --- -# Modules +# Components -LangChain provides standard, extendable interfaces and external integrations for the following main modules: +LangChain provides standard, extendable interfaces and external integrations for the following main components: -#### [Model I/O](/docs/modules/model_io/) -Interface with language models -#### [Retrieval](/docs/modules/data_connection/) -Interface with application-specific data -#### [Agents](/docs/modules/agents/) -Let chains choose which tools to use given high-level directives +## [Model I/O](/docs/modules/model_io/) +Formatting and managing language model input and output + +### [Prompts](/docs/modules/model_io/prompts) +Formatting for LLM inputs that guide generation + +### [Chat models](/docs/modules/model_io/chat) +Interfaces for language models that use chat messages as inputs and returns chat messages as outputs (as opposed to using plain text). + +### [LLMs](/docs/modules/model_io/llms) +Interfaces for language models that use plain text as input and output + +## [Retrieval](/docs/modules/data_connection/) +Interface with application-specific data for e.g. RAG + +### [Document loaders](/docs/modules/data_connection/document_loaders/) +Load data from a source as `Documents` for later processing + +### [Text splitters](/docs/modules/data_connection/document_transformers/) +Transform source documents to better suit your application + +### [Embedding models](/docs/modules/data_connection/text_embedding/) +Create vector representations of a piece of text, allowing for natural language search + +### [Vectorstores](/docs/modules/data_connection/vectorstores/) +Interfaces for specialized databases that can search over unstructured data with natural language + +### [Retrievers](/docs/modules/data_connection/retrievers/) +More generic interfaces that return documents given an unstructured query + +## [Composition](/docs/modules/composition/) +Higher-level components that combine other arbitrary systems and/or or LangChain primitives together + +### [Tools](/docs/modules/tools/) +Interfaces that allow an LLM to interact with external systems + +### [Agents](/docs/modules/agents/) +Constructs that choose which tools to use given high-level directives + +### [Chains](/docs/modules/chains/) +Building block-style compositions of other runnables ## Additional -#### [Chains](/docs/modules/chains/) -Common, building block compositions -#### [Memory](/docs/modules/memory/) + +### [Memory](/docs/modules/memory/) Persist application state between runs of a chain -#### [Callbacks](/docs/modules/callbacks/) + +### [Callbacks](/docs/modules/callbacks/) Log and stream intermediate steps of any chain diff --git a/docs/docs/modules/memory/types/index.mdx b/docs/docs/modules/memory/types/index.mdx index 16e9a34cde..9e9ceeb135 100644 --- a/docs/docs/modules/memory/types/index.mdx +++ b/docs/docs/modules/memory/types/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 2 +sidebar_class_name: hidden --- # Memory types diff --git a/docs/docs/modules/model_io/chat/function_calling.mdx b/docs/docs/modules/model_io/chat/function_calling.mdx index 2307c2b8b1..d06c52bf11 100644 --- a/docs/docs/modules/model_io/chat/function_calling.mdx +++ b/docs/docs/modules/model_io/chat/function_calling.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 1 +sidebar_position: 2 title: Function calling --- @@ -30,7 +30,7 @@ We’ll focus here on the first two points. For a detailed guide on output parsing check out the [OpenAI Tools output parsers](../../../../docs/modules/model_io/output_parsers/types/openai_tools) and to see the structured output chains check out the [Structured output -guide](../../../../docs/guides/structured_output). +guide](/docs/modules/model_io/chat/structured_output). Before getting started make sure you have `langchain-core` installed. @@ -298,13 +298,13 @@ print(json.dumps(convert_to_openai_tool(Multiply()), indent=2)) ## Next steps - **Output parsing**: See [OpenAI Tools output - parsers](../../../../docs/modules/model_io/output_parsers/types/openai_tools) - and [OpenAI Functions output - parsers](../../../../docs/modules/model_io/output_parsers/types/openai_functions) - to learn about extracting the function calling API responses into - various formats. -- **Structured output chains**: [Some models have constructors](../../../../docs/guides/structured_output) that - handle creating a structured output chain for you. + parsers](../../../../docs/modules/model_io/output_parsers/types/openai_tools) + and [OpenAI Functions output + parsers](../../../../docs/modules/model_io/output_parsers/types/openai_functions) + to learn about extracting the function calling API responses into + various formats. +- **Structured output chains**: [Some models have constructors](/docs/modules/model_io/chat/structured_output) that + handle creating a structured output chain for you. - **Tool use**: See how to construct chains and agents that actually - call the invoked tools in [these - guides](../../../../docs/use_cases/tool_use/). + call the invoked tools in [these + guides](../../../../docs/use_cases/tool_use/). diff --git a/docs/docs/modules/model_io/chat/index.mdx b/docs/docs/modules/model_io/chat/index.mdx index 058192951d..9fc2752b43 100644 --- a/docs/docs/modules/model_io/chat/index.mdx +++ b/docs/docs/modules/model_io/chat/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 3 +sidebar_class_name: hidden --- # Chat Models diff --git a/docs/docs/modules/model_io/chat/message_types.mdx b/docs/docs/modules/model_io/chat/message_types.mdx new file mode 100644 index 0000000000..f796f27871 --- /dev/null +++ b/docs/docs/modules/model_io/chat/message_types.mdx @@ -0,0 +1,33 @@ +--- +sidebar_position: 1 +title: Message types +--- + +# Message types + +ChatModels take a list of messages as input and return a message. There are a few different types of messages. All messages have a `role` and a `content` property. The `role` describes WHO is saying the message. LangChain has different message classes for different roles. The `content` property describes the content of the message. This can be a few different things: + +- A string (most models deal this type of content) +- A List of dictionaries (this is used for multi-modal input, where the dictionary contains information about that input type and that input location) + +In addition, messages have an `additional_kwargs` property. This is where additional information about messages can be passed. This is largely used for input parameters that are *provider specific* and not general. The best known example of this is `function_call` from OpenAI. + +### HumanMessage + +This represents a message from the user. Generally consists only of content. + +### AIMessage + +This represents a message from the model. This may have `additional_kwargs` in it - for example `tool_calls` if using OpenAI tool calling. + +### SystemMessage + +This represents a system message, which tells the model how to behave. This generally only consists of content. Not every model supports this. + +### FunctionMessage + +This represents the result of a function call. In addition to `role` and `content`, this message has a `name` parameter which conveys the name of the function that was called to produce this result. + +### ToolMessage + +This represents the result of a tool call. This is distinct from a FunctionMessage in order to match OpenAI's `function` and `tool` message types. In addition to `role` and `content`, this message has a `tool_call_id` parameter which conveys the id of the call to the tool that was called to produce this result. diff --git a/docs/docs/guides/structured_output.ipynb b/docs/docs/modules/model_io/chat/structured_output.ipynb similarity index 98% rename from docs/docs/guides/structured_output.ipynb rename to docs/docs/modules/model_io/chat/structured_output.ipynb index 54baeeccd7..76ac1dd13b 100644 --- a/docs/docs/guides/structured_output.ipynb +++ b/docs/docs/modules/model_io/chat/structured_output.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "raw", + "id": "27598444", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 3\n", + "---" + ] + }, { "cell_type": "markdown", "id": "6e3f0f72", diff --git a/docs/docs/modules/model_io/chat/token_usage_tracking.ipynb b/docs/docs/modules/model_io/chat/token_usage_tracking.ipynb index d334fb8705..8f30037cf5 100644 --- a/docs/docs/modules/model_io/chat/token_usage_tracking.ipynb +++ b/docs/docs/modules/model_io/chat/token_usage_tracking.ipynb @@ -19,7 +19,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.callbacks import get_openai_callback\n", + "from langchain_community.callbacks import get_openai_callback\n", "from langchain_openai import ChatOpenAI" ] }, diff --git a/docs/docs/modules/model_io/concepts.mdx b/docs/docs/modules/model_io/concepts.mdx index 4189b99429..0074ce24b4 100644 --- a/docs/docs/modules/model_io/concepts.mdx +++ b/docs/docs/modules/model_io/concepts.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 1 +sidebar_class_name: hidden --- # Concepts @@ -72,7 +73,7 @@ ChatModels and LLMs take different input types. PromptValue is a class designed ### MessagePromptTemplate -[This](/docs/modules/model_io/prompts/message_prompts) is an example of a prompt template. This consists of a template **message** - meaning a specific role and a PromptTemplate. This PromptTemplate is then formatted with user inputs to produce a final string that becomes the `content` of this message. +This type of template consists of a template **message** - meaning a specific role and a PromptTemplate. This PromptTemplate is then formatted with user inputs to produce a final string that becomes the `content` of this message. #### HumanMessagePromptTemplate diff --git a/docs/docs/modules/model_io/index.mdx b/docs/docs/modules/model_io/index.mdx index a5cf4e81fd..6ffacb4aff 100644 --- a/docs/docs/modules/model_io/index.mdx +++ b/docs/docs/modules/model_io/index.mdx @@ -11,27 +11,274 @@ The core element of any language model application is...the model. LangChain giv ![Flowchart illustrating the Model I/O process with steps Format, Predict, and Parse, showing the transformation from input variables to structured output.](/img/model_io.jpg "Model Input/Output Process Diagram") -## [Conceptual Guide](/docs/modules/model_io/concepts) +# Quickstart -A conceptual explanation of messages, prompts, LLMs vs ChatModels, and output parsers. You should read this before getting started. +The below quickstart will cover the basics of using LangChain's Model I/O components. It will introduce the two different types of models - LLMs and Chat Models. It will then cover how to use Prompt Templates to format the inputs to these models, and how to use Output Parsers to work with the outputs. -## [Quickstart](/docs/modules/model_io/quick_start) +Language models in LangChain come in two flavors: -Covers the basics of getting started working with different types of models. You should walk through [this section](/docs/modules/model_io/quick_start) if you want to get an overview of the functionality. +### [ChatModels](/docs/modules/model_io/chat/) -## [Prompts](/docs/modules/model_io/prompts/) +[Chat models](/docs/modules/model_io/chat/) are often backed by LLMs but tuned specifically for having conversations. +Crucially, their provider APIs use a different interface than pure text completion models. Instead of a single string, +they take a list of chat messages as input and they return an AI message as output. See the section below for more details on what exactly a message consists of. GPT-4 and Anthropic's Claude-2 are both implemented as chat models. -[This section](/docs/modules/model_io/prompts/) deep dives into the different types of prompt templates and how to use them. +### [LLMs](/docs/modules/model_io/llms/) -## [LLMs](/docs/modules/model_io/llms/) +[LLMs](/docs/modules/model_io/llms/) in LangChain refer to pure text completion models. +The APIs they wrap take a string prompt as input and output a string completion. OpenAI's GPT-3 is implemented as an LLM. -[This section](/docs/modules/model_io/llms/) covers functionality related to the LLM class. This is a type of model that takes a text string as input and returns a text string. +These two API types have different input and output schemas. -## [ChatModels](/docs/modules/model_io/chat/) +Additionally, not all models are the same. Different models have different prompting strategies that work best for them. For example, Anthropic's models work best with XML while OpenAI's work best with JSON. You should keep this in mind when designing your apps. -[This section](/docs/modules/model_io/chat/) covers functionality related to the ChatModel class. This is a type of model that takes a list of messages as input and returns a message. +For this getting started guide, we will use chat models and will provide a few options: using an API like Anthropic or OpenAI, or using a local open source model via Ollama. -## [Output Parsers](/docs/modules/model_io/output_parsers/) +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from "@theme/CodeBlock"; -Output parsers are responsible for transforming the output of LLMs and ChatModels into more structured data. [This section](/docs/modules/model_io/output_parsers/) covers the different types of output parsers. + + +First we'll need to install their partner package: + +```shell +pip install langchain-openai +``` + +Accessing the API requires an API key, which you can get by creating an account and heading [here](https://platform.openai.com/account/api-keys). Once we have a key we'll want to set it as an environment variable by running: + +```shell +export OPENAI_API_KEY="..." +``` + +We can then initialize the model: + +```python +from langchain_openai import ChatOpenAI +from langchain_openai import OpenAI + +llm = OpenAI() +chat_model = ChatOpenAI(model="gpt-3.5-turbo-0125") +``` + +If you'd prefer not to set an environment variable you can pass the key in directly via the `openai_api_key` named parameter when initiating the OpenAI LLM class: + +```python +from langchain_openai import ChatOpenAI +llm = ChatOpenAI(openai_api_key="...") +``` + + + + +[Ollama](https://ollama.ai/) allows you to run open-source large language models, such as Llama 2, locally. + +First, follow [these instructions](https://github.com/jmorganca/ollama) to set up and run a local Ollama instance: + +* [Download](https://ollama.ai/download) +* Fetch a model via `ollama pull llama2` + +Then, make sure the Ollama server is running. After that, you can do: +```python +from langchain_community.llms import Ollama +from langchain_community.chat_models import ChatOllama + +llm = Ollama(model="llama2") +chat_model = ChatOllama() +``` + + + + +First we'll need to import the LangChain x Anthropic package. + +```shell +pip install langchain-anthropic +``` + +Accessing the API requires an API key, which you can get by creating an account [here](https://claude.ai/login). Once we have a key we'll want to set it as an environment variable by running: + +```shell +export ANTHROPIC_API_KEY="..." +``` + +We can then initialize the model: + +```python +from langchain_anthropic import ChatAnthropic + +chat_model = ChatAnthropic(model="claude-3-sonnet-20240229", temperature=0.2, max_tokens=1024) +``` + +If you'd prefer not to set an environment variable you can pass the key in directly via the `anthropic_api_key` named parameter when initiating the Anthropic Chat Model class: + +```python +chat_model = ChatAnthropic(anthropic_api_key="...") +``` + + + + +First we'll need to install their partner package: + +```shell +pip install langchain-cohere +``` + +Accessing the API requires an API key, which you can get by creating an account and heading [here](https://dashboard.cohere.com/api-keys). Once we have a key we'll want to set it as an environment variable by running: + +```shell +export COHERE_API_KEY="..." +``` + +We can then initialize the model: + +```python +from langchain_cohere import ChatCohere + +chat_model = ChatCohere() +``` + +If you'd prefer not to set an environment variable you can pass the key in directly via the `cohere_api_key` named parameter when initiating the Cohere LLM class: + +```python +from langchain_cohere import ChatCohere + +chat_model = ChatCohere(cohere_api_key="...") +``` + + + + +Both `llm` and `chat_model` are objects that represent configuration for a particular model. +You can initialize them with parameters like `temperature` and others, and pass them around. +The main difference between them is their input and output schemas. +The LLM objects take string as input and output string. +The ChatModel objects take a list of messages as input and output a message. +For a deeper conceptual explanation of this difference please see [this documentation](./concepts) + +We can see the difference between an LLM and a ChatModel when we invoke it. + +```python +from langchain_core.messages import HumanMessage + +text = "What would be a good company name for a company that makes colorful socks?" +messages = [HumanMessage(content=text)] + +llm.invoke(text) +# >> Feetful of Fun + +chat_model.invoke(messages) +# >> AIMessage(content="Socks O'Color") +``` + +The LLM returns a string, while the ChatModel returns a message. + +## Prompt Templates + +Most LLM applications do not pass user input directly into an LLM. Usually they will add the user input to a larger piece of text, called a prompt template, that provides additional context on the specific task at hand. + +In the previous example, the text we passed to the model contained instructions to generate a company name. For our application, it would be great if the user only had to provide the description of a company/product without worrying about giving the model instructions. + +PromptTemplates help with exactly this! +They bundle up all the logic for going from user input into a fully formatted prompt. +This can start off very simple - for example, a prompt to produce the above string would just be: + +```python +from langchain.prompts import PromptTemplate + +prompt = PromptTemplate.from_template("What is a good name for a company that makes {product}?") +prompt.format(product="colorful socks") +``` + +```python +What is a good name for a company that makes colorful socks? +``` + +There are several advantages of using these over raw string formatting. +You can "partial" out variables - e.g. you can format only some of the variables at a time. +You can compose them together, easily combining different templates into a single prompt. +For explanations of these functionalities, see the [section on prompts](/docs/modules/model_io/prompts) for more detail. + +`PromptTemplate`s can also be used to produce a list of messages. +In this case, the prompt not only contains information about the content, but also each message (its role, its position in the list, etc.). +Here, what happens most often is a `ChatPromptTemplate` is a list of `ChatMessageTemplates`. +Each `ChatMessageTemplate` contains instructions for how to format that `ChatMessage` - its role, and then also its content. +Let's take a look at this below: + +```python +from langchain.prompts.chat import ChatPromptTemplate + +template = "You are a helpful assistant that translates {input_language} to {output_language}." +human_template = "{text}" + +chat_prompt = ChatPromptTemplate.from_messages([ + ("system", template), + ("human", human_template), +]) + +chat_prompt.format_messages(input_language="English", output_language="French", text="I love programming.") +``` + +```pycon +[ + SystemMessage(content="You are a helpful assistant that translates English to French.", additional_kwargs={}), + HumanMessage(content="I love programming.") +] +``` + + +ChatPromptTemplates can also be constructed in other ways - see the [section on prompts](/docs/modules/model_io/prompts) for more detail. + +## Output parsers + +`OutputParser`s convert the raw output of a language model into a format that can be used downstream. +There are a few main types of `OutputParser`s, including: + +- Convert text from `LLM` into structured information (e.g. JSON) +- Convert a `ChatMessage` into just a string +- Convert the extra information returned from a call besides the message (like OpenAI function invocation) into a string. + +For full information on this, see the [section on output parsers](/docs/modules/model_io/output_parsers). + +In this getting started guide, we use a simple one that parses a list of comma separated values. + +```python +from langchain.output_parsers import CommaSeparatedListOutputParser + +output_parser = CommaSeparatedListOutputParser() +output_parser.parse("hi, bye") +# >> ['hi', 'bye'] +``` + +## Composing with LCEL + +We can now combine all these into one chain. +This chain will take input variables, pass those to a prompt template to create a prompt, pass the prompt to a language model, and then pass the output through an (optional) output parser. +This is a convenient way to bundle up a modular piece of logic. +Let's see it in action! + +```python +template = "Generate a list of 5 {text}.\n\n{format_instructions}" + +chat_prompt = ChatPromptTemplate.from_template(template) +chat_prompt = chat_prompt.partial(format_instructions=output_parser.get_format_instructions()) +chain = chat_prompt | chat_model | output_parser +chain.invoke({"text": "colors"}) +# >> ['red', 'blue', 'green', 'yellow', 'orange'] +``` + +Note that we are using the `|` syntax to join these components together. +This `|` syntax is powered by the LangChain Expression Language (LCEL) and relies on the universal `Runnable` interface that all of these objects implement. +To learn more about LCEL, read the documentation [here](/docs/expression_language). + +## Conclusion + +That's it for getting started with prompts, models, and output parsers! This just covered the surface of what there is to learn. For more information, check out: + +- The [prompts section](./prompts) for information on how to work with prompt templates +- The [ChatModel section](./chat) for more information on the ChatModel interface +- The [LLM section](./llms) for more information on the LLM interface +- The [output parser section](./output_parsers) for information about the different types of output parsers. diff --git a/docs/docs/modules/model_io/llms/index.mdx b/docs/docs/modules/model_io/llms/index.mdx index ead4a3e1b9..7965c48d0e 100644 --- a/docs/docs/modules/model_io/llms/index.mdx +++ b/docs/docs/modules/model_io/llms/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 4 +sidebar_class_name: hidden --- # LLMs diff --git a/docs/docs/modules/model_io/llms/token_usage_tracking.ipynb b/docs/docs/modules/model_io/llms/token_usage_tracking.ipynb index 5bf73d3cea..2ce62ddc28 100644 --- a/docs/docs/modules/model_io/llms/token_usage_tracking.ipynb +++ b/docs/docs/modules/model_io/llms/token_usage_tracking.ipynb @@ -19,7 +19,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.callbacks import get_openai_callback\n", + "from langchain_community.callbacks import get_openai_callback\n", "from langchain_openai import OpenAI" ] }, diff --git a/docs/docs/modules/model_io/output_parsers/index.mdx b/docs/docs/modules/model_io/output_parsers/index.mdx index 936efd6e14..3841b51c8e 100644 --- a/docs/docs/modules/model_io/output_parsers/index.mdx +++ b/docs/docs/modules/model_io/output_parsers/index.mdx @@ -1,6 +1,7 @@ --- sidebar_position: 5 hide_table_of_contents: true +sidebar_class_name: hidden --- # Output Parsers diff --git a/docs/docs/modules/model_io/output_parsers/types/_category_.yml b/docs/docs/modules/model_io/output_parsers/types/_category_.yml index 8638cc3b00..51eeb1c6a0 100644 --- a/docs/docs/modules/model_io/output_parsers/types/_category_.yml +++ b/docs/docs/modules/model_io/output_parsers/types/_category_.yml @@ -1 +1 @@ -label: 'Types' \ No newline at end of file +label: 'Built-in parsers' diff --git a/docs/docs/modules/model_io/prompts/composition.ipynb b/docs/docs/modules/model_io/prompts/composition.ipynb index cdd01c29e8..fe81807cb3 100644 --- a/docs/docs/modules/model_io/prompts/composition.ipynb +++ b/docs/docs/modules/model_io/prompts/composition.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "raw", + "id": "02a1c8fb", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 5\n", + "---" + ] + }, { "cell_type": "markdown", "id": "4de4e022", @@ -22,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "69b17f05", "metadata": {}, "outputs": [], @@ -53,7 +63,7 @@ { "data": { "text/plain": [ - "PromptTemplate(input_variables=['language', 'topic'], output_parser=None, partial_variables={}, template='Tell me a joke about {topic}, make it funny\\n\\nand in {language}', template_format='f-string', validate_template=True)" + "PromptTemplate(input_variables=['language', 'topic'], template='Tell me a joke about {topic}, make it funny\\n\\nand in {language}')" ] }, "execution_count": 3, @@ -303,13 +313,162 @@ "chain.run(\"i said hi\")" ] }, + { + "cell_type": "markdown", + "id": "0e1d47e3-b05a-4aef-a58c-3057fa628c1c", + "metadata": {}, + "source": [ + "## Using PipelinePrompt" + ] + }, + { + "cell_type": "markdown", + "id": "0a5892f9-e4d8-4b7c-b6a5-4651539b9734", + "metadata": {}, + "source": [ + "LangChain includes an abstraction [PipelinePromptTemplate](https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.pipeline.PipelinePromptTemplate.html), which can be useful when you want to reuse parts of prompts. A PipelinePrompt consists of two main parts:\n", + "\n", + "- Final prompt: The final prompt that is returned\n", + "- Pipeline prompts: A list of tuples, consisting of a string name and a prompt template. Each prompt template will be formatted and then passed to future prompt templates as a variable with the same name." + ] + }, { "cell_type": "code", - "execution_count": null, - "id": "58196f6b", + "execution_count": 1, + "id": "f5bae2d5-268b-4d75-a935-826f48b607a0", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.prompts.pipeline import PipelinePromptTemplate\n", + "from langchain.prompts.prompt import PromptTemplate" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4face631-74d7-49ca-93b1-1e6e66fa58e2", + "metadata": {}, + "outputs": [], + "source": [ + "full_template = \"\"\"{introduction}\n", + "\n", + "{example}\n", + "\n", + "{start}\"\"\"\n", + "full_prompt = PromptTemplate.from_template(full_template)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f4b88551-f0a6-4ed2-8893-ed4c4d146d1f", + "metadata": {}, + "outputs": [], + "source": [ + "introduction_template = \"\"\"You are impersonating {person}.\"\"\"\n", + "introduction_prompt = PromptTemplate.from_template(introduction_template)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f41d3712-dc5e-4554-a59c-d5e048a88ef2", + "metadata": {}, + "outputs": [], + "source": [ + "example_template = \"\"\"Here's an example of an interaction:\n", + "\n", + "Q: {example_q}\n", + "A: {example_a}\"\"\"\n", + "example_prompt = PromptTemplate.from_template(example_template)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "bd9a4dbf-a735-4758-a255-aee58ce8a6be", + "metadata": {}, + "outputs": [], + "source": [ + "start_template = \"\"\"Now, do this for real!\n", + "\n", + "Q: {input}\n", + "A:\"\"\"\n", + "start_prompt = PromptTemplate.from_template(start_template)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b2ce3fa9-2df5-4186-a83b-a674c8925bba", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "input_prompts = [\n", + " (\"introduction\", introduction_prompt),\n", + " (\"example\", example_prompt),\n", + " (\"start\", start_prompt),\n", + "]\n", + "pipeline_prompt = PipelinePromptTemplate(\n", + " final_prompt=full_prompt, pipeline_prompts=input_prompts\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6fd182b1-9048-4544-808f-55780c43682c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['example_q', 'person', 'input', 'example_a']" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pipeline_prompt.input_variables" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c6cabb16-ea30-4de0-8548-dcce84df8421", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You are impersonating Elon Musk.\n", + "\n", + "Here's an example of an interaction:\n", + "\n", + "Q: What's your favorite car?\n", + "A: Tesla\n", + "\n", + "Now, do this for real!\n", + "\n", + "Q: What's your favorite social media site?\n", + "A:\n" + ] + } + ], + "source": [ + "print(\n", + " pipeline_prompt.format(\n", + " person=\"Elon Musk\",\n", + " example_q=\"What's your favorite car?\",\n", + " example_a=\"Tesla\",\n", + " input=\"What's your favorite social media site?\",\n", + " )\n", + ")" + ] } ], "metadata": { @@ -328,7 +487,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.1" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/docs/docs/modules/model_io/prompts/example_selector_types/index.mdx b/docs/docs/modules/model_io/prompts/example_selector_types/index.mdx deleted file mode 100644 index c33662988b..0000000000 --- a/docs/docs/modules/model_io/prompts/example_selector_types/index.mdx +++ /dev/null @@ -1,8 +0,0 @@ -# Example Selector Types - -| Name | Description | -|------------|---------------------------------------------------------------------------------------------| -| Similarity | Uses semantic similarity between inputs and examples to decide which examples to choose. | -| MMR | Uses Max Marginal Relevance between inputs and examples to decide which examples to choose. | -| Length | Selects examples based on how many can fit within a certain length | -| Ngram | Uses ngram overlap between inputs and examples to decide which examples to choose. | \ No newline at end of file diff --git a/docs/docs/modules/model_io/prompts/example_selectors.ipynb b/docs/docs/modules/model_io/prompts/example_selectors/index.ipynb similarity index 86% rename from docs/docs/modules/model_io/prompts/example_selectors.ipynb rename to docs/docs/modules/model_io/prompts/example_selectors/index.ipynb index b86e2cfb79..bed87e2f1c 100644 --- a/docs/docs/modules/model_io/prompts/example_selectors.ipynb +++ b/docs/docs/modules/model_io/prompts/example_selectors/index.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "raw", + "id": "af408f61", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 1\n", + "---" + ] + }, { "cell_type": "markdown", "id": "1a65e4c9", @@ -26,7 +36,7 @@ "\n", "The only method it needs to define is a ``select_examples`` method. This takes in the input variables and then returns a list of examples. It is up to each specific implementation as to how those examples are selected.\n", "\n", - "LangChain has a few different types of example selectors. For an overview of all these types, see [this documentation](./example_selector_types).\n", + "LangChain has a few different types of example selectors. For an overview of all these types, see the below table.\n", "\n", "In this guide, we will walk through creating a custom example selector." ] @@ -219,6 +229,21 @@ "print(prompt.format(input=\"word\"))" ] }, + { + "cell_type": "markdown", + "id": "e767f69d", + "metadata": {}, + "source": [ + "## Example Selector Types\n", + "\n", + "| Name | Description |\n", + "|------------|---------------------------------------------------------------------------------------------|\n", + "| Similarity | Uses semantic similarity between inputs and examples to decide which examples to choose. |\n", + "| MMR | Uses Max Marginal Relevance between inputs and examples to decide which examples to choose. |\n", + "| Length | Selects examples based on how many can fit within a certain length |\n", + "| Ngram | Uses ngram overlap between inputs and examples to decide which examples to choose. |" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/docs/docs/modules/model_io/prompts/example_selector_types/length_based.ipynb b/docs/docs/modules/model_io/prompts/example_selectors/length_based.ipynb similarity index 100% rename from docs/docs/modules/model_io/prompts/example_selector_types/length_based.ipynb rename to docs/docs/modules/model_io/prompts/example_selectors/length_based.ipynb diff --git a/docs/docs/modules/model_io/prompts/example_selector_types/mmr.ipynb b/docs/docs/modules/model_io/prompts/example_selectors/mmr.ipynb similarity index 100% rename from docs/docs/modules/model_io/prompts/example_selector_types/mmr.ipynb rename to docs/docs/modules/model_io/prompts/example_selectors/mmr.ipynb diff --git a/docs/docs/modules/model_io/prompts/example_selector_types/ngram_overlap.ipynb b/docs/docs/modules/model_io/prompts/example_selectors/ngram_overlap.ipynb similarity index 100% rename from docs/docs/modules/model_io/prompts/example_selector_types/ngram_overlap.ipynb rename to docs/docs/modules/model_io/prompts/example_selectors/ngram_overlap.ipynb diff --git a/docs/docs/modules/model_io/prompts/example_selector_types/similarity.ipynb b/docs/docs/modules/model_io/prompts/example_selectors/similarity.ipynb similarity index 100% rename from docs/docs/modules/model_io/prompts/example_selector_types/similarity.ipynb rename to docs/docs/modules/model_io/prompts/example_selectors/similarity.ipynb diff --git a/docs/docs/modules/model_io/prompts/few_shot_examples.ipynb b/docs/docs/modules/model_io/prompts/few_shot_examples.ipynb index 1fd69e1a29..8b603315b3 100644 --- a/docs/docs/modules/model_io/prompts/few_shot_examples.ipynb +++ b/docs/docs/modules/model_io/prompts/few_shot_examples.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "raw", + "id": "94c3ad61", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 3\n", + "---" + ] + }, { "cell_type": "markdown", "id": "b91e03f1", diff --git a/docs/docs/modules/model_io/prompts/few_shot_examples_chat.ipynb b/docs/docs/modules/model_io/prompts/few_shot_examples_chat.ipynb index 909e43c3d9..526bff05f0 100644 --- a/docs/docs/modules/model_io/prompts/few_shot_examples_chat.ipynb +++ b/docs/docs/modules/model_io/prompts/few_shot_examples_chat.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "raw", + "id": "beba2e0e", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 2\n", + "---" + ] + }, { "cell_type": "markdown", "id": "bb0735c0", diff --git a/docs/docs/modules/model_io/prompts/index.mdx b/docs/docs/modules/model_io/prompts/index.mdx index 52a5a34bd9..459f766e2e 100644 --- a/docs/docs/modules/model_io/prompts/index.mdx +++ b/docs/docs/modules/model_io/prompts/index.mdx @@ -1,5 +1,6 @@ --- sidebar_position: 2 +sidebar_class_name: hidden --- # Prompts @@ -20,10 +21,8 @@ We have many how-to guides for working with prompts. These include: - [How to use few-shot examples with chat models](./few_shot_examples_chat) - [How to use example selectors](./example_selectors) - [How to partial prompts](./partial) -- [How to work with message prompts](./message_prompts) - [How to compose prompts together](./composition) -- [How to create a pipeline prompt](./pipeline) -## [Example Selector Types](./example_selector_types) +## [Example Selector Types](./example_selectors) -LangChain has a few different types of example selectors you can use off the shelf. You can explore those types [here](./example_selector_types) +LangChain has a few different types of example selectors you can use off the shelf. You can explore those types [here](./example_selectors) diff --git a/docs/docs/modules/model_io/prompts/message_prompts.ipynb b/docs/docs/modules/model_io/prompts/message_prompts.ipynb deleted file mode 100644 index 206433b97a..0000000000 --- a/docs/docs/modules/model_io/prompts/message_prompts.ipynb +++ /dev/null @@ -1,140 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "592be667", - "metadata": {}, - "source": [ - "# Types of `MessagePromptTemplate`\n", - "\n", - "LangChain provides different types of `MessagePromptTemplate`. The most commonly used are `AIMessagePromptTemplate`, `SystemMessagePromptTemplate` and `HumanMessagePromptTemplate`, which create an AI message, system message and human message respectively.\n", - "\n", - "However, in cases where the chat model supports taking chat message with arbitrary role, you can use `ChatMessagePromptTemplate`, which allows user to specify the role name." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "3993c10e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ChatMessage(content='May the force be with you', role='Jedi')" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from langchain.prompts import ChatMessagePromptTemplate\n", - "\n", - "prompt = \"May the {subject} be with you\"\n", - "\n", - "chat_message_prompt = ChatMessagePromptTemplate.from_template(\n", - " role=\"Jedi\", template=prompt\n", - ")\n", - "chat_message_prompt.format(subject=\"force\")" - ] - }, - { - "cell_type": "markdown", - "id": "4fc61017", - "metadata": {}, - "source": [ - "LangChain also provides `MessagesPlaceholder`, which gives you full control of what messages to be rendered during formatting. This can be useful when you are uncertain of what role you should be using for your message prompt templates or when you wish to insert a list of messages during formatting.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "0469ee30", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.prompts import (\n", - " ChatPromptTemplate,\n", - " HumanMessagePromptTemplate,\n", - " MessagesPlaceholder,\n", - ")\n", - "\n", - "human_prompt = \"Summarize our conversation so far in {word_count} words.\"\n", - "human_message_template = HumanMessagePromptTemplate.from_template(human_prompt)\n", - "\n", - "chat_prompt = ChatPromptTemplate.from_messages(\n", - " [MessagesPlaceholder(variable_name=\"conversation\"), human_message_template]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "b57a5e29", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[HumanMessage(content='What is the best way to learn programming?'),\n", - " AIMessage(content='1. Choose a programming language: Decide on a programming language that you want to learn.\\n\\n2. Start with the basics: Familiarize yourself with the basic programming concepts such as variables, data types and control structures.\\n\\n3. Practice, practice, practice: The best way to learn programming is through hands-on experience'),\n", - " HumanMessage(content='Summarize our conversation so far in 10 words.')]" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from langchain_core.messages import AIMessage, HumanMessage\n", - "\n", - "human_message = HumanMessage(content=\"What is the best way to learn programming?\")\n", - "ai_message = AIMessage(\n", - " content=\"\"\"\\\n", - "1. Choose a programming language: Decide on a programming language that you want to learn.\n", - "\n", - "2. Start with the basics: Familiarize yourself with the basic programming concepts such as variables, data types and control structures.\n", - "\n", - "3. Practice, practice, practice: The best way to learn programming is through hands-on experience\\\n", - "\"\"\"\n", - ")\n", - "\n", - "chat_prompt.format_prompt(\n", - " conversation=[human_message, ai_message], word_count=\"10\"\n", - ").to_messages()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7158dce4", - "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.10.1" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/modules/model_io/prompts/partial.ipynb b/docs/docs/modules/model_io/prompts/partial.ipynb index 274cc6fb72..e34fb7d43f 100644 --- a/docs/docs/modules/model_io/prompts/partial.ipynb +++ b/docs/docs/modules/model_io/prompts/partial.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "raw", + "id": "45e0127d", + "metadata": {}, + "source": [ + "---\n", + "sidebar_position: 4\n", + "---" + ] + }, { "cell_type": "markdown", "id": "d8ca736e", diff --git a/docs/docs/modules/model_io/prompts/pipeline.ipynb b/docs/docs/modules/model_io/prompts/pipeline.ipynb deleted file mode 100644 index ec9fb6469d..0000000000 --- a/docs/docs/modules/model_io/prompts/pipeline.ipynb +++ /dev/null @@ -1,184 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "aeb01f8f", - "metadata": {}, - "source": [ - "# Pipeline\n", - "\n", - "This notebook goes over how to compose multiple prompts together. This can be useful when you want to reuse parts of prompts. This can be done with a PipelinePrompt. A PipelinePrompt consists of two main parts:\n", - "\n", - "- Final prompt: The final prompt that is returned\n", - "- Pipeline prompts: A list of tuples, consisting of a string name and a prompt template. Each prompt template will be formatted and then passed to future prompt templates as a variable with the same name." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "4044608f", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.prompts.pipeline import PipelinePromptTemplate\n", - "from langchain.prompts.prompt import PromptTemplate" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "e315c5bf", - "metadata": {}, - "outputs": [], - "source": [ - "full_template = \"\"\"{introduction}\n", - "\n", - "{example}\n", - "\n", - "{start}\"\"\"\n", - "full_prompt = PromptTemplate.from_template(full_template)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "33a2ce2b", - "metadata": {}, - "outputs": [], - "source": [ - "introduction_template = \"\"\"You are impersonating {person}.\"\"\"\n", - "introduction_prompt = PromptTemplate.from_template(introduction_template)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "180b7432", - "metadata": {}, - "outputs": [], - "source": [ - "example_template = \"\"\"Here's an example of an interaction:\n", - "\n", - "Q: {example_q}\n", - "A: {example_a}\"\"\"\n", - "example_prompt = PromptTemplate.from_template(example_template)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "583f7188", - "metadata": {}, - "outputs": [], - "source": [ - "start_template = \"\"\"Now, do this for real!\n", - "\n", - "Q: {input}\n", - "A:\"\"\"\n", - "start_prompt = PromptTemplate.from_template(start_template)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "e40edd5c", - "metadata": {}, - "outputs": [], - "source": [ - "input_prompts = [\n", - " (\"introduction\", introduction_prompt),\n", - " (\"example\", example_prompt),\n", - " (\"start\", start_prompt),\n", - "]\n", - "pipeline_prompt = PipelinePromptTemplate(\n", - " final_prompt=full_prompt, pipeline_prompts=input_prompts\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "7957de13", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['example_q', 'example_a', 'input', 'person']" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pipeline_prompt.input_variables" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "a0d87803", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You are impersonating Elon Musk.\n", - "\n", - "Here's an example of an interaction:\n", - "\n", - "Q: What's your favorite car?\n", - "A: Tesla\n", - "\n", - "Now, do this for real!\n", - "\n", - "Q: What's your favorite social media site?\n", - "A:\n" - ] - } - ], - "source": [ - "print(\n", - " pipeline_prompt.format(\n", - " person=\"Elon Musk\",\n", - " example_q=\"What's your favorite car?\",\n", - " example_a=\"Tesla\",\n", - " input=\"What's your favorite social media site?\",\n", - " )\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "399a1687", - "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.10.1" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/modules/model_io/prompts/quick_start.ipynb b/docs/docs/modules/model_io/prompts/quick_start.ipynb index 7b393c3687..9eabff4d36 100644 --- a/docs/docs/modules/model_io/prompts/quick_start.ipynb +++ b/docs/docs/modules/model_io/prompts/quick_start.ipynb @@ -7,7 +7,7 @@ "source": [ "---\n", "sidebar_position: 0\n", - "title: Quick Start\n", + "title: Quick reference\n", "---" ] }, @@ -16,7 +16,8 @@ "id": "2d98412d-fc53-42c1-aed8-f1f8eb9ada58", "metadata": {}, "source": [ - "# Quick Start\n", + "# Quick reference\n", + "\n", "Prompt templates are predefined recipes for generating prompts for language models.\n", "\n", "A template may include instructions, few-shot examples, and specific context and\n", @@ -105,7 +106,7 @@ "\n", "## `ChatPromptTemplate`\n", "\n", - "The prompt to [chat models](../chat) is a list of chat messages.\n", + "The prompt to [chat models](../chat) is a list of [chat messages](/docs/modules/model_io/chat/message_types).\n", "\n", "Each chat message is associated with content, and an additional parameter called `role`.\n", "For example, in the OpenAI [Chat Completions API](https://platform.openai.com/docs/guides/chat/introduction), a chat message can be associated with an AI assistant, a human or a system role.\n", @@ -115,7 +116,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "d088d53c-0e20-4fb9-9d54-b0e989b998b0", "metadata": {}, "outputs": [], @@ -134,12 +135,52 @@ "messages = chat_template.format_messages(name=\"Bob\", user_input=\"What is your name?\")" ] }, + { + "cell_type": "markdown", + "id": "0eee13f0", + "metadata": {}, + "source": [ + "Piping these formatted messages into LangChain's `ChatOpenAI` chat model class is roughly equivalent to the following with using the OpenAI client directly:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6bb2a72", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install openai" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "49aebba3", + "metadata": {}, + "outputs": [], + "source": [ + "from openai import OpenAI\n", + "\n", + "client = OpenAI()\n", + "\n", + "response = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": \"You are a helpful AI bot. Your name is Bob.\"},\n", + " {\"role\": \"user\", \"content\": \"Hello, how are you doing?\"},\n", + " {\"role\": \"assistant\", \"content\": \"I'm doing well, thanks!\"},\n", + " {\"role\": \"user\", \"content\": \"What is your name?\"},\n", + " ],\n", + ")" + ] + }, { "cell_type": "markdown", "id": "d1e7e3ef-ba7d-4ca5-a95c-a0488c9679e5", "metadata": {}, "source": [ - "`ChatPromptTemplate.from_messages` accepts a variety of message representations.\n", + "The `ChatPromptTemplate.from_messages` static method accepts a variety of message representations and is a convenient way to format input to chat models with exactly the messages you want.\n", "\n", "For example, in addition to using the 2-tuple representation of (type, content) used\n", "above, you could pass in an instance of `MessagePromptTemplate` or `BaseMessage`." @@ -162,7 +203,6 @@ "source": [ "from langchain.prompts import HumanMessagePromptTemplate\n", "from langchain_core.messages import SystemMessage\n", - "from langchain_openai import ChatOpenAI\n", "\n", "chat_template = ChatPromptTemplate.from_messages(\n", " [\n", @@ -189,7 +229,136 @@ }, { "cell_type": "markdown", - "id": "3a5fe78c-572c-4e87-b02f-7d33126fb605", + "id": "9305b5ae", + "metadata": {}, + "source": [ + "## Message Prompts" + ] + }, + { + "cell_type": "markdown", + "id": "8513963e", + "metadata": {}, + "source": [ + "LangChain provides different types of `MessagePromptTemplate`. The most commonly used are `AIMessagePromptTemplate`, `SystemMessagePromptTemplate` and `HumanMessagePromptTemplate`, which create an AI message, system message and human message respectively. You can read more about the [different types of messages here](/docs/modules/model_io/chat/message_types).\n", + "\n", + "In cases where the chat model supports taking chat message with arbitrary role, you can use `ChatMessagePromptTemplate`, which allows user to specify the role name." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "71aab8e7-3236-43b6-b516-a76a6cfdc39f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ChatMessage(content='May the force be with you', role='Jedi')" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain.prompts import ChatMessagePromptTemplate\n", + "\n", + "prompt = \"May the {subject} be with you\"\n", + "\n", + "chat_message_prompt = ChatMessagePromptTemplate.from_template(\n", + " role=\"Jedi\", template=prompt\n", + ")\n", + "chat_message_prompt.format(subject=\"force\")" + ] + }, + { + "cell_type": "markdown", + "id": "ebbe2a21-c893-46cf-9fc7-a7f90c09695a", + "metadata": {}, + "source": [ + "## `MessagesPlaceholder`\n", + "\n", + "LangChain also provides `MessagesPlaceholder`, which gives you full control of what messages to be rendered during formatting. This can be useful when you are uncertain of what role you should be using for your message prompt templates or when you wish to insert a list of messages during formatting." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "98a3e39d-7c7e-4a89-80a7-74ea4e6cf177", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.prompts import (\n", + " ChatPromptTemplate,\n", + " HumanMessagePromptTemplate,\n", + " MessagesPlaceholder,\n", + ")\n", + "\n", + "human_prompt = \"Summarize our conversation so far in {word_count} words.\"\n", + "human_message_template = HumanMessagePromptTemplate.from_template(human_prompt)\n", + "\n", + "chat_prompt = ChatPromptTemplate.from_messages(\n", + " [MessagesPlaceholder(variable_name=\"conversation\"), human_message_template]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a92fd952-d96f-4606-8a50-6077ea8ddef4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[HumanMessage(content='What is the best way to learn programming?'),\n", + " AIMessage(content='1. Choose a programming language: Decide on a programming language that you want to learn.\\n\\n2. Start with the basics: Familiarize yourself with the basic programming concepts such as variables, data types and control structures.\\n\\n3. Practice, practice, practice: The best way to learn programming is through hands-on experience'),\n", + " HumanMessage(content='Summarize our conversation so far in 10 words.')]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.messages import AIMessage, HumanMessage\n", + "\n", + "human_message = HumanMessage(content=\"What is the best way to learn programming?\")\n", + "ai_message = AIMessage(\n", + " content=\"\"\"\\\n", + "1. Choose a programming language: Decide on a programming language that you want to learn.\n", + "\n", + "2. Start with the basics: Familiarize yourself with the basic programming concepts such as variables, data types and control structures.\n", + "\n", + "3. Practice, practice, practice: The best way to learn programming is through hands-on experience\\\n", + "\"\"\"\n", + ")\n", + "\n", + "chat_prompt.format_prompt(\n", + " conversation=[human_message, ai_message], word_count=\"10\"\n", + ").to_messages()" + ] + }, + { + "cell_type": "markdown", + "id": "86202814-3539-4a94-8698-73426240516e", + "metadata": {}, + "source": [ + "The full list of message prompt template types includes:\n", + "\n", + "* [AIMessagePromptTemplate](https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.chat.AIMessagePromptTemplate.html), for AI assistant messages;\n", + "* [SystemMessagePromptTemplate](https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.chat.SystemMessagePromptTemplate.html), for system messages;\n", + "* [HumanMessagePromptTemplate](https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.chat.HumanMessagePromptTemplate.html), for user messages;\n", + "* [ChatMessagePromptTemplate](https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.chat.ChatMessagePromptTemplate.html), for messages with arbitrary roles;\n", + "* [MessagesPlaceholder](https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.chat.MessagesPlaceholder.html), which accommodates a list of messages." + ] + }, + { + "cell_type": "markdown", + "id": "1a68e9ae", "metadata": {}, "source": [ "## LCEL\n", @@ -201,39 +370,43 @@ }, { "cell_type": "code", - "execution_count": 24, - "id": "0f0e860b-95e0-4653-8bab-c5d58b0f7d67", + "execution_count": 10, + "id": "a2e02bf4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "StringPromptValue(text='Tell me a joke')" + "StringPromptValue(text='Tell me a funny joke about chickens.')" ] }, - "execution_count": 24, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "prompt_template = PromptTemplate.from_template(\n", + " \"Tell me a {adjective} joke about {content}.\"\n", + ")\n", + "\n", "prompt_val = prompt_template.invoke({\"adjective\": \"funny\", \"content\": \"chickens\"})\n", "prompt_val" ] }, { "cell_type": "code", - "execution_count": 25, - "id": "c0dac782-5144-4489-8d77-eba47f1cd1c4", + "execution_count": 11, + "id": "b60a44b7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'Tell me a joke'" + "'Tell me a funny joke about chickens.'" ] }, - "execution_count": 25, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -244,17 +417,17 @@ }, { "cell_type": "code", - "execution_count": 26, - "id": "a8e3ac32-f690-4d3d-bcb2-27b7931beab2", + "execution_count": 12, + "id": "1366e47b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[HumanMessage(content='Tell me a joke')]" + "[HumanMessage(content='Tell me a funny joke about chickens.')]" ] }, - "execution_count": 26, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -265,18 +438,30 @@ }, { "cell_type": "code", - "execution_count": 30, - "id": "4516257f-0c3b-4851-9e82-8c9e09111444", + "execution_count": 13, + "id": "e335131e", "metadata": {}, "outputs": [], "source": [ + "chat_template = ChatPromptTemplate.from_messages(\n", + " [\n", + " SystemMessage(\n", + " content=(\n", + " \"You are a helpful assistant that re-writes the user's text to \"\n", + " \"sound more upbeat.\"\n", + " )\n", + " ),\n", + " HumanMessagePromptTemplate.from_template(\"{text}\"),\n", + " ]\n", + ")\n", + "\n", "chat_val = chat_template.invoke({\"text\": \"i dont like eating tasty things.\"})" ] }, { "cell_type": "code", - "execution_count": 31, - "id": "7adfe927-ba1d-425f-904c-0328e1a10c18", + "execution_count": 14, + "id": "44924df6", "metadata": {}, "outputs": [ { @@ -286,7 +471,7 @@ " HumanMessage(content='i dont like eating tasty things.')]" ] }, - "execution_count": 31, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -297,8 +482,8 @@ }, { "cell_type": "code", - "execution_count": 32, - "id": "37c9e2e4-a2e8-48a9-a732-01c025a21362", + "execution_count": 15, + "id": "a313f987", "metadata": {}, "outputs": [ { @@ -307,7 +492,7 @@ "\"System: You are a helpful assistant that re-writes the user's text to sound more upbeat.\\nHuman: i dont like eating tasty things.\"" ] }, - "execution_count": 32, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -315,6 +500,14 @@ "source": [ "chat_val.to_string()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c25e59ac", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -333,7 +526,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.1" + "version": "3.10.5" } }, "nbformat": 4, diff --git a/docs/docs/modules/agents/tools/custom_tools.ipynb b/docs/docs/modules/tools/custom_tools.ipynb similarity index 100% rename from docs/docs/modules/agents/tools/custom_tools.ipynb rename to docs/docs/modules/tools/custom_tools.ipynb diff --git a/docs/docs/modules/agents/tools/index.ipynb b/docs/docs/modules/tools/index.ipynb similarity index 98% rename from docs/docs/modules/agents/tools/index.ipynb rename to docs/docs/modules/tools/index.ipynb index 282217d425..c6bf74cbac 100644 --- a/docs/docs/modules/agents/tools/index.ipynb +++ b/docs/docs/modules/tools/index.ipynb @@ -7,6 +7,7 @@ "source": [ "---\n", "sidebar_position: 4\n", + "sidebar_class_name: hidden\n", "---" ] }, @@ -17,7 +18,7 @@ "source": [ "# Tools\n", "\n", - "Tools are interfaces that an agent can use to interact with the world.\n", + "Tools are interfaces that an agent, chain, or LLM can use to interact with the world.\n", "They combine a few things:\n", "\n", "1. The name of the tool\n", @@ -30,7 +31,7 @@ "\n", "The simpler the input to a tool is, the easier it is for an LLM to be able to use it.\n", "Many agents will only work with tools that have a single string input.\n", - "For a list of agent types and which ones work with more complicated inputs, please see [this documentation](../agent_types)\n", + "For a list of agent types and which ones work with more complicated inputs, please see [this documentation](../agents/agent_types)\n", "\n", "Importantly, the name, description, and JSON schema (if used) are all used in the prompt. Therefore, it is really important that they are clear and describe exactly how the tool should be used. You may need to change the default name, description, or JSON schema if the LLM is not understanding how to use the tool.\n", "\n", diff --git a/docs/docs/modules/agents/tools/toolkits.mdx b/docs/docs/modules/tools/toolkits.mdx similarity index 68% rename from docs/docs/modules/agents/tools/toolkits.mdx rename to docs/docs/modules/tools/toolkits.mdx index aabe9172cc..fed302b811 100644 --- a/docs/docs/modules/agents/tools/toolkits.mdx +++ b/docs/docs/modules/tools/toolkits.mdx @@ -4,8 +4,8 @@ sidebar_position: 3 # Toolkits -Toolkits are collections of tools that are designed to be used together for specific tasks and have convenient loading methods. -For a complete list of these, visit [Integrations](/docs/integrations/toolkits/). +Toolkits are collections of tools that are designed to be used together for specific tasks. They have convenient loading methods. +For a complete list of available ready-made toolkits, visit [Integrations](/docs/integrations/toolkits/). All Toolkits expose a `get_tools` method which returns a list of tools. You can therefore do: diff --git a/docs/docs/modules/agents/tools/tools_as_openai_functions.ipynb b/docs/docs/modules/tools/tools_as_openai_functions.ipynb similarity index 100% rename from docs/docs/modules/agents/tools/tools_as_openai_functions.ipynb rename to docs/docs/modules/tools/tools_as_openai_functions.ipynb diff --git a/docs/docs/use_cases/apis.ipynb b/docs/docs/use_cases/apis.ipynb index 02415c48bc..6e038c6dbd 100644 --- a/docs/docs/use_cases/apis.ipynb +++ b/docs/docs/use_cases/apis.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 1\n", + "sidebar_class_name: hidden\n", "title: Interacting with APIs\n", "---" ] diff --git a/docs/docs/use_cases/chatbots/index.ipynb b/docs/docs/use_cases/chatbots/index.ipynb index 99523b8ef4..c4b52cbc85 100644 --- a/docs/docs/use_cases/chatbots/index.ipynb +++ b/docs/docs/use_cases/chatbots/index.ipynb @@ -1,5 +1,14 @@ { "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "---\n", + "sidebar_class_name: hidden\n", + "---" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/docs/use_cases/chatbots/quickstart.ipynb b/docs/docs/use_cases/chatbots/quickstart.ipynb index 72600a195f..2875a094e2 100644 --- a/docs/docs/use_cases/chatbots/quickstart.ipynb +++ b/docs/docs/use_cases/chatbots/quickstart.ipynb @@ -13,9 +13,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "[![](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/use_cases/chatbots.ipynb)\n", + "# Quickstart\n", "\n", - "# Quickstart" + "[![](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/use_cases/chatbots.ipynb)" ] }, { @@ -494,7 +494,7 @@ "\n", "### Handling documents\n", "\n", - "Let's modify our previous prompt to accept documents as context. We'll use a `create_stuff_documents_chain` helper function to \"stuff\" all of the input documents into the prompt, which also conveniently handles formatting. Other arguments (like `messages`) will be passed directly through into the prompt:" + "Let's modify our previous prompt to accept documents as context. We'll use a [`create_stuff_documents_chain`](https://api.python.langchain.com/en/latest/chains/langchain.chains.combine_documents.stuff.create_stuff_documents_chain.html#langchain.chains.combine_documents.stuff.create_stuff_documents_chain) helper function to \"stuff\" all of the input documents into the prompt, which also conveniently handles formatting. We use the [`ChatPromptTemplate.from_messages`](/docs/modules/model_io/prompts/quick_start#chatprompttemplate) method to format the message input we want to pass to the model, including a [`MessagesPlaceholder`](/docs/modules/model_io/prompts/quick_start#messagesplaceholder) where chat history messages will be directly injected:" ] }, { @@ -568,7 +568,7 @@ "\n", "Next, let's integrate our retriever into the chain. Our retriever should retrieve information relevant to the last message we pass in from the user, so we extract it and use that as input to fetch relevant docs, which we add to the current chain as `context`. We pass `context` plus the previous `messages` into our document chain to generate a final answer.\n", "\n", - "We also use the `RunnablePassthrough.assign()` method to pass intermediate steps through at each invocation. Here's what it looks like:" + "We also use the [`RunnablePassthrough.assign()`](/docs/expression_language/primitives/assign) method to pass intermediate steps through at each invocation. Here's what it looks like:" ] }, { diff --git a/docs/docs/use_cases/code_understanding.ipynb b/docs/docs/use_cases/code_understanding.ipynb index 019318cee4..3ab6957a46 100644 --- a/docs/docs/use_cases/code_understanding.ipynb +++ b/docs/docs/use_cases/code_understanding.ipynb @@ -6,6 +6,7 @@ "source": [ "---\n", "title: Code understanding\n", + "sidebar_class_name: hidden\n", "---" ] }, @@ -40,11 +41,11 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "%pip install --upgrade --quiet langchain-openai tiktoken chromadb langchain\n", + "%pip install --upgrade --quiet langchain-openai tiktoken chromadb langchain git\n", "\n", "# Set env var OPENAI_API_KEY or load from a .env file\n", "# import dotenv\n", @@ -73,11 +74,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "# from git import Repo\n", + "from git import Repo\n", "from langchain_community.document_loaders.generic import GenericLoader\n", "from langchain_community.document_loaders.parsers import LanguageParser\n", "from langchain_text_splitters import Language" @@ -90,8 +91,8 @@ "outputs": [], "source": [ "# Clone\n", - "repo_path = \"/Users/rlm/Desktop/test_repo\"\n", - "# repo = Repo.clone_from(\"https://github.com/langchain-ai/langchain\", to_path=repo_path)" + "repo_path = \"/Users/jacoblee/Desktop/test_repo\"\n", + "repo = Repo.clone_from(\"https://github.com/langchain-ai/langchain\", to_path=repo_path)" ] }, { @@ -113,7 +114,7 @@ { "data": { "text/plain": [ - "1293" + "295" ] }, "execution_count": 4, @@ -124,7 +125,7 @@ "source": [ "# Load\n", "loader = GenericLoader.from_filesystem(\n", - " repo_path + \"/libs/langchain/langchain\",\n", + " repo_path + \"/libs/core/langchain_core\",\n", " glob=\"**/*\",\n", " suffixes=[\".py\"],\n", " exclude=[\"**/non-utf8-encoding.py\"],\n", @@ -153,7 +154,7 @@ { "data": { "text/plain": [ - "3748" + "898" ] }, "execution_count": 5, @@ -227,105 +228,97 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "from langchain.chains import ConversationalRetrievalChain\n", - "from langchain.memory import ConversationSummaryMemory\n", + "from langchain.chains import create_history_aware_retriever, create_retrieval_chain\n", + "from langchain.chains.combine_documents import create_stuff_documents_chain\n", + "from langchain_core.prompts import ChatPromptTemplate\n", "from langchain_openai import ChatOpenAI\n", "\n", "llm = ChatOpenAI(model_name=\"gpt-4\")\n", - "memory = ConversationSummaryMemory(\n", - " llm=llm, memory_key=\"chat_history\", return_messages=True\n", + "\n", + "# First we need a prompt that we can pass into an LLM to generate this search query\n", + "\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"placeholder\", \"{chat_history}\"),\n", + " (\"user\", \"{input}\"),\n", + " (\n", + " \"user\",\n", + " \"Given the above conversation, generate a search query to look up to get information relevant to the conversation\",\n", + " ),\n", + " ]\n", ")\n", - "qa = ConversationalRetrievalChain.from_llm(llm, retriever=retriever, memory=memory)" + "\n", + "retriever_chain = create_history_aware_retriever(llm, retriever, prompt)\n", + "\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"Answer the user's questions based on the below context:\\n\\n{context}\",\n", + " ),\n", + " (\"placeholder\", \"{chat_history}\"),\n", + " (\"user\", \"{input}\"),\n", + " ]\n", + ")\n", + "document_chain = create_stuff_documents_chain(llm, prompt)\n", + "\n", + "qa = create_retrieval_chain(retriever_chain, document_chain)" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'To initialize a ReAct agent, you need to follow these steps:\\n\\n1. Initialize a language model `llm` of type `BaseLanguageModel`.\\n\\n2. Initialize a document store `docstore` of type `Docstore`.\\n\\n3. Create a `DocstoreExplorer` with the initialized `docstore`. The `DocstoreExplorer` is used to search for and look up terms in the document store.\\n\\n4. Create an array of `Tool` objects. The `Tool` objects represent the actions that the agent can perform. In the case of `ReActDocstoreAgent`, the tools must be \"Search\" and \"Lookup\" with their corresponding functions from the `DocstoreExplorer`.\\n\\n5. Initialize the `ReActDocstoreAgent` using the `from_llm_and_tools` method with the `llm` (language model) and `tools` as parameters.\\n\\n6. Initialize the `ReActChain` (which is the `AgentExecutor`) using the `ReActDocstoreAgent` and `tools` as parameters.\\n\\nHere is an example of how to do this:\\n\\n```python\\nfrom langchain.chains import ReActChain, OpenAI\\nfrom langchain.docstore.base import Docstore\\nfrom langchain.docstore.document import Document\\nfrom langchain_core.tools import BaseTool\\n\\n# Initialize the LLM and a docstore\\nllm = OpenAI()\\ndocstore = Docstore()\\n\\ndocstore_explorer = DocstoreExplorer(docstore)\\ntools = [\\n Tool(\\n name=\"Search\",\\n func=docstore_explorer.search,\\n description=\"Search for a term in the docstore.\",\\n ),\\n Tool(\\n name=\"Lookup\",\\n func=docstore_explorer.lookup,\\n description=\"Lookup a term in the docstore.\",\\n ),\\n]\\nagent = ReActDocstoreAgent.from_llm_and_tools(llm, tools)\\nreact = ReActChain(agent=agent, tools=tools)\\n```\\n\\nKeep in mind that this is a simplified example and you might need to adapt it to your specific needs.'" + "'A RunnableBinding is a class in the LangChain library that is used to bind arguments to a Runnable. This is useful when a runnable in a chain requires an argument that is not in the output of the previous runnable or included in the user input. It returns a new Runnable with the bound arguments and configuration. The bind method in the RunnableBinding class is used to perform this operation.'" ] }, - "execution_count": 43, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "question = \"How can I initialize a ReAct agent?\"\n", - "result = qa(question)\n", + "question = \"What is a RunnableBinding?\"\n", + "result = qa.invoke({\"input\": question})\n", "result[\"answer\"]" ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "-> **Question**: What is the class hierarchy? \n", - "\n", - "**Answer**: The class hierarchy in object-oriented programming is the structure that forms when classes are derived from other classes. The derived class is a subclass of the base class also known as the superclass. This hierarchy is formed based on the concept of inheritance in object-oriented programming where a subclass inherits the properties and functionalities of the superclass. \n", - "\n", - "In the given context, we have the following examples of class hierarchies:\n", - "\n", - "1. `BaseCallbackHandler --> CallbackHandler` means `BaseCallbackHandler` is a base class and `CallbackHandler` (like `AimCallbackHandler`, `ArgillaCallbackHandler` etc.) are derived classes that inherit from `BaseCallbackHandler`.\n", - "\n", - "2. `BaseLoader --> Loader` means `BaseLoader` is a base class and `Loader` (like `TextLoader`, `UnstructuredFileLoader` etc.) are derived classes that inherit from `BaseLoader`.\n", - "\n", - "3. `ToolMetaclass --> BaseTool --> Tool` means `ToolMetaclass` is a base class, `BaseTool` is a derived class that inherits from `ToolMetaclass`, and `Tool` (like `AIPluginTool`, `BaseGraphQLTool` etc.) are further derived classes that inherit from `BaseTool`. \n", + "-> **Question**: What classes are derived from the Runnable class? \n", "\n", - "-> **Question**: What classes are derived from the Chain class? \n", + "**Answer**: The classes derived from the `Runnable` class as mentioned in the context are: `RunnableLambda`, `RunnableLearnable`, `RunnableSerializable`, `RunnableWithFallbacks`. \n", "\n", - "**Answer**: The classes that are derived from the Chain class are:\n", + "-> **Question**: What one improvement do you propose in code in relation to the class hierarchy for the Runnable class? \n", "\n", - "1. LLMSummarizationCheckerChain\n", - "2. MapReduceChain\n", - "3. OpenAIModerationChain\n", - "4. NatBotChain\n", - "5. QAGenerationChain\n", - "6. QAWithSourcesChain\n", - "7. RetrievalQAWithSourcesChain\n", - "8. VectorDBQAWithSourcesChain\n", - "9. RetrievalQA\n", - "10. VectorDBQA\n", - "11. LLMRouterChain\n", - "12. MultiPromptChain\n", - "13. MultiRetrievalQAChain\n", - "14. MultiRouteChain\n", - "15. RouterChain\n", - "16. SequentialChain\n", - "17. SimpleSequentialChain\n", - "18. TransformChain\n", - "19. BaseConversationalRetrievalChain\n", - "20. ConstitutionalChain \n", - "\n", - "-> **Question**: What one improvement do you propose in code in relation to the class hierarchy for the Chain class? \n", - "\n", - "**Answer**: As an AI model, I don't have personal opinions. However, one suggestion could be to improve the documentation of the Chain class hierarchy. The current comments and docstrings provide some details but it could be helpful to include more explicit explanations about the hierarchy, roles of each subclass, and their relationships with one another. Also, incorporating UML diagrams or other visuals could help developers better understand the structure and interactions of the classes. \n", + "**Answer**: One potential improvement could be the introduction of abstract base classes (ABCs) or interfaces for different types of Runnable classes. Currently, it seems like there are lots of different Runnable types, like RunnableLambda, RunnableParallel, etc., each with their own methods and attributes. By defining a common interface or ABC for all these classes, we can ensure consistency and better organize the codebase. It would also make it easier to add new types of Runnable classes in the future, as they would just need to implement the methods defined in the interface or ABC. \n", "\n" ] } ], "source": [ "questions = [\n", - " \"What is the class hierarchy?\",\n", - " \"What classes are derived from the Chain class?\",\n", - " \"What one improvement do you propose in code in relation to the class hierarchy for the Chain class?\",\n", + " \"What classes are derived from the Runnable class?\",\n", + " \"What one improvement do you propose in code in relation to the class hierarchy for the Runnable class?\",\n", "]\n", "\n", "for question in questions:\n", - " result = qa(question)\n", + " result = qa.invoke({\"input\": question})\n", " print(f\"-> **Question**: {question} \\n\")\n", " print(f\"**Answer**: {result['answer']} \\n\")" ] @@ -334,7 +327,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The can look at the [LangSmith trace](https://smith.langchain.com/public/2b23045f-4e49-4d2d-8980-dec85259af36/r) to see what is happening under the hood:\n", + "Then we can look at the [LangSmith trace](https://smith.langchain.com/public/616f6620-f49f-46c7-8f4b-dae847705c5d/r) to see what is happening under the hood:\n", "\n", "* In particular, the code well structured and kept together in the retrieval output\n", "* The retrieved code and chat history are passed to the LLM for answer distillation\n", @@ -348,221 +341,157 @@ "source": [ "### Open source LLMs\n", "\n", - "We can use [Code LLaMA](https://about.fb.com/news/2023/08/code-llama-ai-for-coding/) via LLamaCPP or [Ollama integration](https://ollama.ai/blog/run-code-llama-locally).\n", - "\n", - "Note: be sure to upgrade `llama-cpp-python` in order to use the new `gguf` [file format](https://github.com/abetlen/llama-cpp-python/pull/633).\n", + "We'll use LangChain's [Ollama integration](https://ollama.com/) to query a local OSS model.\n", "\n", - "```\n", - "CMAKE_ARGS=\"-DLLAMA_METAL=on\" FORCE_CMAKE=1 /Users/rlm/miniforge3/envs/llama2/bin/pip install -U llama-cpp-python --no-cache-dir\n", - "```\n", - " \n", - "Check out the latest code-llama models [here](https://huggingface.co/TheBloke/CodeLlama-13B-Instruct-GGUF/tree/main)." + "Check out the latest available models [here](https://ollama.com/library)." ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "from langchain.callbacks.manager import CallbackManager\n", - "from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler\n", - "from langchain.chains import ConversationalRetrievalChain, LLMChain\n", - "from langchain.memory import ConversationSummaryMemory\n", - "from langchain.prompts import PromptTemplate\n", - "from langchain_community.llms import LlamaCpp" + "%pip install --upgrade --quiet langchain-community" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ - "callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])\n", - "llm = LlamaCpp(\n", - " model_path=\"/Users/rlm/Desktop/Code/llama/code-llama/codellama-13b-instruct.Q4_K_M.gguf\",\n", - " n_ctx=5000,\n", - " n_gpu_layers=1,\n", - " n_batch=512,\n", - " f16_kv=True, # MUST set to True, otherwise you will run into problem after a couple of calls\n", - " callback_manager=callback_manager,\n", - " verbose=True,\n", - ")" + "from langchain_community.chat_models.ollama import ChatOllama\n", + "\n", + "llm = ChatOllama(model=\"codellama\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's run it with a generic coding question to test its knowledge:" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 12, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Llama.generate: prefix-match hit\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - " You can use the find command with a few options to this task. Here is an example of how you might go about it:\n", - "\n", - "find . -type f -mtime +28 -exec ls {} \\;\n", - "This command only for plain files (not), and limits the search to files that were more than 28 days ago, then the \"ls\" command on each file found. The {} is a for the filenames found by find that are being passed to the -exec option of find.\n", "\n", - "You can also use find in with other unix utilities like sort and grep to the list of files before they are:\n", + "You can use the `find` command with the `-mtime` option to find all the text files in the current directory that have been modified in the last month. Here's an example command:\n", + "```bash\n", + "find . -type f -name \"*.txt\" -mtime -30\n", + "```\n", + "This will list all the text files in the current directory (`.`) that have been modified in the last 30 days. The `-type f` option ensures that only regular files are matched, and not directories or other types of files. The `-name \"*.txt\"` option restricts the search to files with a `.txt` extension. Finally, the `-mtime -30` option specifies that we want to find files that have been modified in the last 30 days.\n", "\n", - "find . -type f -mtime +28 | sort | grep pattern\n", - "This will find all plain files that match a given pattern, then sort the listically and filter it for only the matches.\n", + "You can also use `find` command with `-mmin` option to find all the text files in the current directory that have been modified within the last month. Here's an example command:\n", + "```bash\n", + "find . -type f -name \"*.txt\" -mmin -4320\n", + "```\n", + "This will list all the text files in the current directory (`.`) that have been modified within the last 30 days. The `-type f` option ensures that only regular files are matched, and not directories or other types of files. The `-name \"*.txt\"` option restricts the search to files with a `.txt` extension. Finally, the `-mmin -4320` option specifies that we want to find files that have been modified within the last 4320 minutes (which is equivalent to one month).\n", "\n", - "Answer: `find` is pretty with its search. The should work as well:\n", + "You can also use `ls` command with `-l` option and pipe it to `grep` command to filter out the text files. Here's an example command:\n", + "```bash\n", + "ls -l | grep \"*.txt\"\n", + "```\n", + "This will list all the text files in the current directory (`.`) that have been modified within the last 30 days. The `-l` option of `ls` command lists the files in a long format, including the modification time, and the `grep` command filters out the files that do not match the specified pattern.\n", "\n", - "\\begin{code}\n", - "ls -l $(find . -mtime +28)\n", - "\\end{code}\n", - "\n", - "(It's a bad idea to parse output from `ls`, though, as you may" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "llama_print_timings: load time = 1074.43 ms\n", - "llama_print_timings: sample time = 180.71 ms / 256 runs ( 0.71 ms per token, 1416.67 tokens per second)\n", - "llama_print_timings: prompt eval time = 0.00 ms / 1 tokens ( 0.00 ms per token, inf tokens per second)\n", - "llama_print_timings: eval time = 9593.04 ms / 256 runs ( 37.47 ms per token, 26.69 tokens per second)\n", - "llama_print_timings: total time = 10139.91 ms\n" + "Please note that these commands are case-sensitive, so if you have any files with different extensions (e.g., `.TXT`), they will not be matched by these commands.\n", + "{'model': 'codellama', 'created_at': '2024-04-03T00:41:44.014203Z', 'message': {'role': 'assistant', 'content': ''}, 'done': True, 'total_duration': 27078466916, 'load_duration': 12947208, 'prompt_eval_count': 44, 'prompt_eval_duration': 11497468000, 'eval_count': 510, 'eval_duration': 15548191000}\n" ] - }, - { - "data": { - "text/plain": [ - "' You can use the find command with a few options to this task. Here is an example of how you might go about it:\\n\\nfind . -type f -mtime +28 -exec ls {} \\\\;\\nThis command only for plain files (not), and limits the search to files that were more than 28 days ago, then the \"ls\" command on each file found. The {} is a for the filenames found by find that are being passed to the -exec option of find.\\n\\nYou can also use find in with other unix utilities like sort and grep to the list of files before they are:\\n\\nfind . -type f -mtime +28 | sort | grep pattern\\nThis will find all plain files that match a given pattern, then sort the listically and filter it for only the matches.\\n\\nAnswer: `find` is pretty with its search. The should work as well:\\n\\n\\\\begin{code}\\nls -l $(find . -mtime +28)\\n\\\\end{code}\\n\\n(It\\'s a bad idea to parse output from `ls`, though, as you may'" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ - "llm(\n", - " \"Question: In bash, how do I list all the text files in the current directory that have been modified in the last month? Answer:\"\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.chains.question_answering import load_qa_chain\n", + "response_message = llm.invoke(\n", + " \"In bash, how do I list all the text files in the current directory that have been modified in the last month?\"\n", + ")\n", "\n", - "# Prompt\n", - "template = \"\"\"Use the following pieces of context to answer the question at the end. \n", - "If you don't know the answer, just say that you don't know, don't try to make up an answer. \n", - "Use three sentences maximum and keep the answer as concise as possible. \n", - "{context}\n", - "Question: {question}\n", - "Helpful Answer:\"\"\"\n", - "QA_CHAIN_PROMPT = PromptTemplate(\n", - " input_variables=[\"context\", \"question\"],\n", - " template=template,\n", - ")" + "print(response_message.content)\n", + "print(response_message.response_metadata)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We can also use the LangChain Prompt Hub to store and fetch prompts.\n", - "\n", - "This will work with your [LangSmith API key](https://docs.smith.langchain.com/).\n", + "Looks reasonable! Now let's set it up with our previously loaded vectorstore.\n", "\n", - "Let's try with a default RAG prompt, [here](https://smith.langchain.com/hub/rlm/rag-prompt)." + "We omit the conversational aspect to keep things more manageable for the lower-powered local model:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ - "from langchain import hub\n", + "# from langchain.chains.question_answering import load_qa_chain\n", + "\n", + "# # Prompt\n", + "# template = \"\"\"Use the following pieces of context to answer the question at the end.\n", + "# If you don't know the answer, just say that you don't know, don't try to make up an answer.\n", + "# Use three sentences maximum and keep the answer as concise as possible.\n", + "# {context}\n", + "# Question: {question}\n", + "# Helpful Answer:\"\"\"\n", + "# QA_CHAIN_PROMPT = PromptTemplate(\n", + "# input_variables=[\"context\", \"question\"],\n", + "# template=template,\n", + "# )\n", + "\n", + "system_template = \"\"\"\n", + "Answer the user's questions based on the below context.\n", + "If you don't know the answer, just say that you don't know, don't try to make up an answer. \n", + "Use three sentences maximum and keep the answer as concise as possible:\n", "\n", - "QA_CHAIN_PROMPT = hub.pull(\"rlm/rag-prompt-default\")" + "{context}\n", + "\"\"\"\n", + "\n", + "# First we need a prompt that we can pass into an LLM to generate this search query\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"system\", system_template),\n", + " (\"user\", \"{input}\"),\n", + " ]\n", + ")\n", + "document_chain = create_stuff_documents_chain(llm, prompt)\n", + "\n", + "qa_chain = create_retrieval_chain(retriever, document_chain)" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 14, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Llama.generate: prefix-match hit\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - " You can use the `ReActAgent` class and pass it the desired tools as, for example, you would do like this to create an agent with the `Lookup` and `Search` tool:\n", - "```python\n", - "from langchain.agents.react import ReActAgent\n", - "from langchain_community.tools.lookup import Lookup\n", - "from langchain_community.tools.search import Search\n", - "ReActAgent(Lookup(), Search())\n", - "```" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "llama_print_timings: load time = 1074.43 ms\n", - "llama_print_timings: sample time = 65.46 ms / 94 runs ( 0.70 ms per token, 1435.95 tokens per second)\n", - "llama_print_timings: prompt eval time = 15975.57 ms / 1408 tokens ( 11.35 ms per token, 88.13 tokens per second)\n", - "llama_print_timings: eval time = 4772.57 ms / 93 runs ( 51.32 ms per token, 19.49 tokens per second)\n", - "llama_print_timings: total time = 20959.57 ms\n" - ] - }, { "data": { "text/plain": [ - "{'output_text': ' You can use the `ReActAgent` class and pass it the desired tools as, for example, you would do like this to create an agent with the `Lookup` and `Search` tool:\\n```python\\nfrom langchain.agents.react import ReActAgent\\nfrom langchain_community.tools.lookup import Lookup\\nfrom langchain_community.tools.search import Search\\nReActAgent(Lookup(), Search())\\n```'}" + "\"A RunnableBinding is a high-level class in the LangChain framework. It's an abstraction layer that sits between a program and an LLM or other data source.\\n\\nThe main goal of a RunnableBinding is to enable a program, which may be a chat bot or a backend service, to fetch responses from an LLM or other data sources in a way that is easy for both the program and the data sources to use. This is achieved through a set of predefined protocols that are implemented by the RunnableBinding.\\n\\nThe protocols defined by a RunnableBinding include:\\n\\n1. Fetching inputs from the program. The RunnableBinding should be able to receive inputs from the program and translate them into a format that can be processed by the LLM or other data sources.\\n2. Translating outputs from the LLM or other data sources into something that can be returned to the program. This includes converting the raw output of an LLM into something that is easier for the program to process, such as text or a structured object.\\n3. Handling errors that may arise during the fetching, processing, and returning of responses from the LLM or other data sources. The RunnableBinding should be able to catch exceptions and errors that occur during these operations and return a suitable error message or response to the program.\\n4. Managing concurrency and parallelism in the communication with the LLM or other data sources. This may include things like allowing multiple requests to be sent to the LLM or other data sources simultaneously, handling the responses asynchronously, and retrying failed requests.\\n5. Providing a way for the program to set configuration options that affect how the RunnableBinding interacts with the LLM or other data sources. This could include things like setting up credentials, providing additional contextual information to the LLM or other data sources, and controlling logging or error handling behavior.\\n\\nIn summary, a RunnableBinding provides a way for a program to easily communicate with an LLM or other data sources without having to know about the details of how they work. By providing a consistent interface between the program and the data sources, the RunnableBinding enables more robust and scalable communication protocols that are easier for both parties to use.\\n\\nIn the context of the chatbot tutorial, a RunnableBinding may be used to fetch responses from an LLM and return them as output for the bot to process. The RunnableBinding could also be used to handle errors that occur during this process, such as providing error messages or retrying failed requests to the LLM.\\n\\nTo summarize:\\n\\n* A RunnableBinding provides a way for a program to communicate with an LLM or other data sources without having to know about the details of how they work.\\n* It enables more robust and scalable communication protocols that are easier for both parties to use.\\n* It manages concurrency and parallelism in the communication with the LLM or other data sources.\\n* It provides a way for the program to set configuration options that affect how the RunnableBinding interacts with the LLM or other data sources.\"" ] }, - "execution_count": 29, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# Docs\n", - "question = \"How can I initialize a ReAct agent?\"\n", - "docs = retriever.get_relevant_documents(question)\n", - "\n", - "# Chain\n", - "chain = load_qa_chain(llm, chain_type=\"stuff\", prompt=QA_CHAIN_PROMPT)\n", - "\n", - "# Run\n", - "chain({\"input_documents\": docs, \"question\": question}, return_only_outputs=True)" + "# Run, only returning the value under the answer key for readability\n", + "qa_chain.pick(\"answer\").invoke({\"input\": \"What is a RunnableBinding?\"})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here's the trace [RAG](https://smith.langchain.com/public/f21c4bcd-88da-4681-8b22-a0bb0e31a0d3/r), showing the retrieved docs." + "Not perfect, but it did pick up on the fact that it lets the developer set configuration option!\n", + "\n", + "Here's the [LangSmith trace](https://smith.langchain.com/public/d8bb2af8-99cd-406b-a870-f255f4a2423c/r) showing the retrieved docs used as context." ] } ], @@ -582,7 +511,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.8" + "version": "3.10.5" } }, "nbformat": 4, diff --git a/docs/docs/use_cases/data_generation.ipynb b/docs/docs/use_cases/data_generation.ipynb index 5ac66c6081..8329f7251f 100644 --- a/docs/docs/use_cases/data_generation.ipynb +++ b/docs/docs/use_cases/data_generation.ipynb @@ -7,6 +7,7 @@ "source": [ "---\n", "title: Synthetic data generation\n", + "sidebar_class_name: hidden\n", "---" ] }, diff --git a/docs/docs/use_cases/extraction/how_to/handle_files.ipynb b/docs/docs/use_cases/extraction/how_to/handle_files.ipynb index b94c7e6030..d0dc85e5a8 100644 --- a/docs/docs/use_cases/extraction/how_to/handle_files.ipynb +++ b/docs/docs/use_cases/extraction/how_to/handle_files.ipynb @@ -25,9 +25,9 @@ "\n", "## MIME type based parsing\n", "\n", - "For basic parsing exmaples take a look [at document loaders](/docs/modules/data_connection/document_loaders/).\n", + "For basic parsing examples take a look [at document loaders](/docs/modules/data_connection/document_loaders/).\n", "\n", - "Here, we'll be looking at mime-type based parsing which is often useful for extraction based applications if you're writing server code that accepts user uploaded files.\n", + "Here, we'll be looking at MIME-type based parsing which is often useful for extraction based applications if you're writing server code that accepts user uploaded files.\n", "\n", "In this case, it's best to assume that the file extension of the file provided by the user is wrong and instead infer the mimetype from the binary content of the file.\n", "\n", diff --git a/docs/docs/use_cases/extraction/index.ipynb b/docs/docs/use_cases/extraction/index.ipynb index 3122130a8a..d20d2e3a23 100644 --- a/docs/docs/use_cases/extraction/index.ipynb +++ b/docs/docs/use_cases/extraction/index.ipynb @@ -6,8 +6,8 @@ "metadata": {}, "source": [ "---\n", - "title: Extraction\n", - "sidebar_position: 0.05\n", + "title: Extracting structured output\n", + "sidebar_class_name: hidden\n", "---" ] }, diff --git a/docs/docs/use_cases/extraction/quickstart.ipynb b/docs/docs/use_cases/extraction/quickstart.ipynb index b69974d7b5..fa8587ab39 100644 --- a/docs/docs/use_cases/extraction/quickstart.ipynb +++ b/docs/docs/use_cases/extraction/quickstart.ipynb @@ -16,7 +16,7 @@ "id": "d28530a6-ddfd-49c0-85dc-b723551f6614", "metadata": {}, "source": [ - "In this quick start, we will use LLMs that are capable of **function/tool calling** to extract information from text.\n", + "In this quick start, we will use [chat models](/docs/modules/model_io/chat/) that are capable of **function/tool calling** to extract information from text.\n", "\n", ":::{.callout-important}\n", "Extraction using **function/tool calling** only works with [models that support **function/tool calling**](/docs/modules/model_io/chat/function_calling).\n", @@ -30,7 +30,7 @@ "source": [ "## Set up\n", "\n", - "We will use the new [structured output](/docs/guides/structured_output) method available on LLMs that are capable of **function/tool calling**. \n", + "We will use the [structured output](/docs/modules/model_io/chat/structured_output) method available on LLMs that are capable of **function/tool calling**. \n", "\n", "Select a model, install the dependencies for it and set up API keys!" ] @@ -158,7 +158,7 @@ "source": [ "We need to use a model that supports function/tool calling.\n", "\n", - "Please review [structured output](/docs/guides/structured_output) for list of some models that can be used with this API." + "Please review [structured output](/docs/modules/model_io/chat/structured_output) for list of some models that can be used with this API." ] }, { diff --git a/docs/docs/use_cases/graph/index.ipynb b/docs/docs/use_cases/graph/index.ipynb index 2a8de5e0f4..d01058c400 100644 --- a/docs/docs/use_cases/graph/index.ipynb +++ b/docs/docs/use_cases/graph/index.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 0.4\n", + "sidebar_class_name: hidden\n", "---" ] }, diff --git a/docs/docs/use_cases/graph/integrations/neptune_sparql_qa.ipynb b/docs/docs/use_cases/graph/integrations/neptune_sparql_qa.ipynb index adad232ad6..8d37a91c32 100644 --- a/docs/docs/use_cases/graph/integrations/neptune_sparql_qa.ipynb +++ b/docs/docs/use_cases/graph/integrations/neptune_sparql_qa.ipynb @@ -248,8 +248,7 @@ "source": [ "import boto3\n", "from langchain.chains.graph_qa.neptune_sparql import NeptuneSparqlQAChain\n", - "from langchain.chat_models import BedrockChat\n", - "from langchain.llms import Bedrock\n", + "from langchain_community.chat_models import BedrockChat\n", "from langchain_community.graphs import NeptuneRdfGraph\n", "\n", "host = \"\"\n", diff --git a/docs/docs/use_cases/index.mdx b/docs/docs/use_cases/index.mdx new file mode 100644 index 0000000000..389a10c98a --- /dev/null +++ b/docs/docs/use_cases/index.mdx @@ -0,0 +1,19 @@ +--- +sidebar_class_name: hidden +--- + +# Use cases + +This section contains walkthroughs and techniques for common end-to-end use tasks. + +If you're looking to build something specific or are more of a hands-on learner, try one out! +While they reference building blocks that are explained in greater detail in other sections, we absolutely +encourage folks to get started by going through them and picking apart the code in a real-world context. + +Or, if you prefer to look at the fundamentals first, you can check out the sections on [Expression Language](/docs/expression_language/get_started) +and the various [components](/docs/modules) LangChain provides for more background knowledge. + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from '@docusaurus/theme-common'; + + diff --git a/docs/docs/use_cases/query_analysis/index.ipynb b/docs/docs/use_cases/query_analysis/index.ipynb index aa95fdff49..d4433c9862 100644 --- a/docs/docs/use_cases/query_analysis/index.ipynb +++ b/docs/docs/use_cases/query_analysis/index.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 0.3\n", + "sidebar_class_name: hidden\n", "---" ] }, diff --git a/docs/docs/use_cases/query_analysis/quickstart.ipynb b/docs/docs/use_cases/query_analysis/quickstart.ipynb index f74b923a48..f5d383e1c1 100644 --- a/docs/docs/use_cases/query_analysis/quickstart.ipynb +++ b/docs/docs/use_cases/query_analysis/quickstart.ipynb @@ -384,7 +384,7 @@ "source": [ "### Query generation\n", "\n", - "To convert user questions to structured queries we'll make use of OpenAI's tool-calling API. Specifically we'll use the new [ChatModel.with_structured_output()](/docs/guides/structured_output) constructor to handle passing the schema to the model and parsing the output." + "To convert user questions to structured queries we'll make use of OpenAI's tool-calling API. Specifically we'll use the new [ChatModel.with_structured_output()](/docs/modules/model_io/chat/structured_output) constructor to handle passing the schema to the model and parsing the output." ] }, { diff --git a/docs/docs/use_cases/question_answering/chat_history.ipynb b/docs/docs/use_cases/question_answering/chat_history.ipynb index 265bc5912d..45b2e3869d 100644 --- a/docs/docs/use_cases/question_answering/chat_history.ipynb +++ b/docs/docs/use_cases/question_answering/chat_history.ipynb @@ -322,6 +322,49 @@ ":::" ] }, + { + "cell_type": "markdown", + "id": "68ff9dd9-8492-418e-a889-c4d670c47d0b", + "metadata": {}, + "source": [ + "### Returning sources" + ] + }, + { + "cell_type": "markdown", + "id": "eff75637-ac5c-41c7-955a-8f58f9d2a89e", + "metadata": {}, + "source": [ + "Often in Q&A applications it's important to show users the sources that were used to generate the answer. LangChain's built-in `create_retrieval_chain` will propagate retrieved source documents through to the output in the `\"context\"` key:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "dc0eeca7-883a-4d67-a9fc-b0fa746e3bb2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "page_content='Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.\\nTask decomposition can be done (1) by LLM with simple prompting like \"Steps for XYZ.\\\\n1.\", \"What are the subgoals for achieving XYZ?\", (2) by using task-specific instructions; e.g. \"Write a story outline.\" for writing a novel, or (3) with human inputs.' metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}\n", + "\n", + "page_content='Fig. 1. Overview of a LLM-powered autonomous agent system.\\nComponent One: Planning#\\nA complicated task usually involves many steps. An agent needs to know what they are and plan ahead.\\nTask Decomposition#\\nChain of thought (CoT; Wei et al. 2022) has become a standard prompting technique for enhancing model performance on complex tasks. The model is instructed to β€œthink step by step” to utilize more test-time computation to decompose hard tasks into smaller and simpler steps. CoT transforms big tasks into multiple manageable tasks and shed lights into an interpretation of the model’s thinking process.' metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}\n", + "\n", + "page_content='Resources:\\n1. Internet access for searches and information gathering.\\n2. Long Term memory management.\\n3. GPT-3.5 powered Agents for delegation of simple tasks.\\n4. File output.\\n\\nPerformance Evaluation:\\n1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.\\n2. Constructively self-criticize your big-picture behavior constantly.\\n3. Reflect on past decisions and strategies to refine your approach.\\n4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.' metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}\n", + "\n", + "page_content='Fig. 11. Illustration of how HuggingGPT works. (Image source: Shen et al. 2023)\\nThe system comprises of 4 stages:\\n(1) Task planning: LLM works as the brain and parses the user requests into multiple tasks. There are four attributes associated with each task: task type, ID, dependencies, and arguments. They use few-shot examples to guide LLM to do task parsing and planning.\\nInstruction:' metadata={'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}\n", + "\n" + ] + } + ], + "source": [ + "for document in ai_msg_2[\"context\"]:\n", + " print(document)\n", + " print()" + ] + }, { "cell_type": "markdown", "id": "0ab1ded4-76d9-453f-9b9b-db9a4560c737", diff --git a/docs/docs/use_cases/question_answering/index.ipynb b/docs/docs/use_cases/question_answering/index.ipynb index af23b8e3aa..e1ac8f8d65 100644 --- a/docs/docs/use_cases/question_answering/index.ipynb +++ b/docs/docs/use_cases/question_answering/index.ipynb @@ -6,8 +6,7 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 0\n", - "collapsed: true\n", + "sidebar_class_name: hidden\n", "---" ] }, diff --git a/docs/docs/use_cases/question_answering/local_retrieval_qa.ipynb b/docs/docs/use_cases/question_answering/local_retrieval_qa.ipynb index 1a3ca0a8b2..aff5c342c1 100644 --- a/docs/docs/use_cases/question_answering/local_retrieval_qa.ipynb +++ b/docs/docs/use_cases/question_answering/local_retrieval_qa.ipynb @@ -11,7 +11,7 @@ "\n", "LangChain has [integrations](https://integrations.langchain.com/) with many open-source LLMs that can be run locally.\n", "\n", - "See [here](/docs/guides/local_llms) for setup instructions for these LLMs. \n", + "See [here](/docs/guides/development/local_llms) for setup instructions for these LLMs. \n", "\n", "For example, here we show how to run `GPT4All` or `LLaMA2` locally (e.g., on your laptop) using local embeddings and a local LLM.\n", "\n", @@ -145,7 +145,7 @@ " \n", "And / or, you can download a GGUF converted model (e.g., [here](https://huggingface.co/TheBloke)).\n", "\n", - "Finally, as noted in detail [here](/docs/guides/local_llms) install `llama-cpp-python`" + "Finally, as noted in detail [here](/docs/guides/development/local_llms) install `llama-cpp-python`" ] }, { diff --git a/docs/docs/use_cases/question_answering/per_user.ipynb b/docs/docs/use_cases/question_answering/per_user.ipynb index dbc50e223b..cc30729b86 100644 --- a/docs/docs/use_cases/question_answering/per_user.ipynb +++ b/docs/docs/use_cases/question_answering/per_user.ipynb @@ -27,7 +27,7 @@ "\n", "**Step 2: Add that parameter as a configurable field for the chain**\n", "\n", - "This will let you easily call the chain and configure any relevant flags at runtime. See [this documentation](/docs/expression_language/how_to/configure) for more information on configuration.\n", + "This will let you easily call the chain and configure any relevant flags at runtime. See [this documentation](/docs/expression_language/primitives/configure) for more information on configuration.\n", "\n", "**Step 3: Call the chain with that configurable field**\n", "\n", diff --git a/docs/docs/use_cases/question_answering/quickstart.mdx b/docs/docs/use_cases/question_answering/quickstart.mdx index d0c1d70f75..9ccce7eaae 100644 --- a/docs/docs/use_cases/question_answering/quickstart.mdx +++ b/docs/docs/use_cases/question_answering/quickstart.mdx @@ -5,8 +5,6 @@ title: Quickstart # Quickstart -[![](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/docs/use_cases/question_answering/quickstart.ipynb) - LangChain has a number of components designed to help build question-answering applications, and RAG applications more generally. To familiarize ourselves with these, we’ll build a simple Q&A application @@ -32,7 +30,7 @@ passes that to the model. The full sequence from raw data to answer will look like: -#### Indexing +### Indexing 1. **Load**: First we need to load our data. We’ll use [DocumentLoaders](../../../docs/modules/data_connection/document_loaders/) @@ -50,7 +48,7 @@ The full sequence from raw data to answer will look like: [Embeddings](../../../docs/modules/data_connection/text_embedding/) model. -#### Retrieval and generation +### Retrieval and generation 1. **Retrieve**: Given a user input, relevant splits are retrieved from storage using a diff --git a/docs/docs/use_cases/csv.ipynb b/docs/docs/use_cases/sql/csv.ipynb similarity index 99% rename from docs/docs/use_cases/csv.ipynb rename to docs/docs/use_cases/sql/csv.ipynb index fa8cbdbcb4..a3d1c7e496 100644 --- a/docs/docs/use_cases/csv.ipynb +++ b/docs/docs/use_cases/sql/csv.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 0.3\n", + "sidebar_position: 5\n", "---" ] }, diff --git a/docs/docs/use_cases/sql/index.ipynb b/docs/docs/use_cases/sql/index.ipynb index 9b415ce954..1d80832fb8 100644 --- a/docs/docs/use_cases/sql/index.ipynb +++ b/docs/docs/use_cases/sql/index.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 0.1\n", + "sidebar_class_name: hidden\n", "---" ] }, diff --git a/docs/docs/use_cases/sql/quickstart.ipynb b/docs/docs/use_cases/sql/quickstart.ipynb index ba94f8866d..084fb66fc4 100644 --- a/docs/docs/use_cases/sql/quickstart.ipynb +++ b/docs/docs/use_cases/sql/quickstart.ipynb @@ -51,7 +51,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We default to OpenAI models in this guide." + "We will use an OpenAI model in this guide." ] }, { @@ -129,7 +129,7 @@ "\n", "### Convert question to SQL query\n", "\n", - "The first step in a SQL chain or agent is to take the user input and convert it to a SQL query. LangChain comes with a built-in chain for this: [create_sql_query_chain](https://api.python.langchain.com/en/latest/chains/langchain.chains.sql_database.query.create_sql_query_chain.html)" + "The first step in a SQL chain or agent is to take the user input and convert it to a SQL query. LangChain comes with a built-in chain for this: [create_sql_query_chain](https://api.python.langchain.com/en/latest/chains/langchain.chains.sql_database.query.create_sql_query_chain.html)." ] }, { diff --git a/docs/docs/use_cases/summarization.ipynb b/docs/docs/use_cases/summarization.ipynb index c637e0fa87..75684b006b 100644 --- a/docs/docs/use_cases/summarization.ipynb +++ b/docs/docs/use_cases/summarization.ipynb @@ -7,6 +7,7 @@ "source": [ "---\n", "title: Summarization\n", + "sidebar_class_name: hidden\n", "---" ] }, diff --git a/docs/docs/use_cases/tagging.ipynb b/docs/docs/use_cases/tagging.ipynb index 5d0c86de1a..2685e3d8b0 100644 --- a/docs/docs/use_cases/tagging.ipynb +++ b/docs/docs/use_cases/tagging.ipynb @@ -7,6 +7,7 @@ "source": [ "---\n", "title: Tagging\n", + "sidebar_class_name: hidden\n", "---" ] }, @@ -38,7 +39,7 @@ "\n", "## Quickstart\n", "\n", - "Let's see a very straightforward example of how we can use OpenAI functions for tagging in LangChain." + "Let's see a very straightforward example of how we can use OpenAI tool calling for tagging in LangChain. We'll use the [`with_structured_output`](/docs/modules/model_io/chat/structured_output) method supported by OpenAI models:" ] }, { @@ -48,95 +49,111 @@ "metadata": {}, "outputs": [], "source": [ - "%pip install --upgrade --quiet langchain langchain-openai\n", + "%pip install --upgrade --quiet langchain langchain-openai\n", "\n", "# Set env var OPENAI_API_KEY or load from a .env file:\n", "# import dotenv\n", "# dotenv.load_dotenv()" ] }, - { - "cell_type": "code", - "execution_count": 1, - "id": "bafb496a", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain.chains import create_tagging_chain, create_tagging_chain_pydantic\n", - "from langchain_openai import ChatOpenAI" - ] - }, { "cell_type": "markdown", "id": "b8ca3f93", "metadata": {}, "source": [ - "We specify a few properties with their expected type in our schema." + "Let's specify a Pydantic model with a few properties and their expected type in our schema." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "id": "39f3ce3e", "metadata": {}, "outputs": [], "source": [ - "# Schema\n", - "schema = {\n", - " \"properties\": {\n", - " \"sentiment\": {\"type\": \"string\"},\n", - " \"aggressiveness\": {\"type\": \"integer\"},\n", - " \"language\": {\"type\": \"string\"},\n", - " }\n", - "}\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.pydantic_v1 import BaseModel, Field\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "tagging_prompt = ChatPromptTemplate.from_template(\n", + " \"\"\"\n", + "Extract the desired information from the following passage.\n", + "\n", + "Only extract the properties mentioned in the 'Classification' function.\n", + "\n", + "Passage:\n", + "{input}\n", + "\"\"\"\n", + ")\n", + "\n", + "\n", + "class Classification(BaseModel):\n", + " sentiment: str = Field(description=\"The sentiment of the text\")\n", + " aggressiveness: int = Field(\n", + " description=\"How aggressive the text is on a scale from 1 to 10\"\n", + " )\n", + " language: str = Field(description=\"The language the text is written in\")\n", + "\n", "\n", "# LLM\n", - "llm = ChatOpenAI(temperature=0, model=\"gpt-3.5-turbo-0613\")\n", - "chain = create_tagging_chain(schema, llm)" + "llm = ChatOpenAI(temperature=0, model=\"gpt-3.5-turbo-0125\").with_structured_output(\n", + " Classification\n", + ")\n", + "\n", + "tagging_chain = tagging_prompt | llm" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "id": "5509b6a6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'sentiment': 'positive', 'language': 'Spanish'}" + "Classification(sentiment='positive', aggressiveness=1, language='Spanish')" ] }, - "execution_count": 4, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inp = \"Estoy increiblemente contento de haberte conocido! Creo que seremos muy buenos amigos!\"\n", - "chain.run(inp)" + "tagging_chain.invoke({\"input\": inp})" + ] + }, + { + "cell_type": "markdown", + "id": "ff3cf30d", + "metadata": {}, + "source": [ + "If we want JSON output, we can just call `.dict()`" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 13, "id": "9154474c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'sentiment': 'enojado', 'aggressiveness': 1, 'language': 'es'}" + "{'sentiment': 'negative', 'aggressiveness': 8, 'language': 'Spanish'}" ] }, - "execution_count": 5, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inp = \"Estoy muy enojado con vos! Te voy a dar tu merecido!\"\n", - "chain.run(inp)" + "res = tagging_chain.invoke({\"input\": inp})\n", + "res.dict()" ] }, { @@ -146,7 +163,7 @@ "source": [ "As we can see in the examples, it correctly interprets what we want.\n", "\n", - "The results vary so that we get, for example, sentiments in different languages ('positive', 'enojado' etc.).\n", + "The results vary so that we may get, for example, sentiments in different languages ('positive', 'enojado' etc.).\n", "\n", "We will see how to control these results in the next section." ] @@ -172,40 +189,51 @@ "id": "69ef0b9a", "metadata": {}, "source": [ - "Here is an example of how we can use `_enum_`, `_description_`, and `_required_` to control for each of the previously mentioned aspects:" + "Let's redeclare our Pydantic model to control for each of the previously mentioned aspects using enums:" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 14, "id": "6a5f7961", "metadata": {}, "outputs": [], "source": [ - "schema = {\n", - " \"properties\": {\n", - " \"aggressiveness\": {\n", - " \"type\": \"integer\",\n", - " \"enum\": [1, 2, 3, 4, 5],\n", - " \"description\": \"describes how aggressive the statement is, the higher the number the more aggressive\",\n", - " },\n", - " \"language\": {\n", - " \"type\": \"string\",\n", - " \"enum\": [\"spanish\", \"english\", \"french\", \"german\", \"italian\"],\n", - " },\n", - " },\n", - " \"required\": [\"language\", \"sentiment\", \"aggressiveness\"],\n", - "}" + "class Classification(BaseModel):\n", + " sentiment: str = Field(..., enum=[\"happy\", \"neutral\", \"sad\"])\n", + " aggressiveness: int = Field(\n", + " ...,\n", + " description=\"describes how aggressive the statement is, the higher the number the more aggressive\",\n", + " enum=[1, 2, 3, 4, 5],\n", + " )\n", + " language: str = Field(\n", + " ..., enum=[\"spanish\", \"english\", \"french\", \"german\", \"italian\"]\n", + " )" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 15, "id": "e5a5881f", "metadata": {}, "outputs": [], "source": [ - "chain = create_tagging_chain(schema, llm)" + "tagging_prompt = ChatPromptTemplate.from_template(\n", + " \"\"\"\n", + "Extract the desired information from the following passage.\n", + "\n", + "Only extract the properties mentioned in the 'Classification' function.\n", + "\n", + "Passage:\n", + "{input}\n", + "\"\"\"\n", + ")\n", + "\n", + "llm = ChatOpenAI(temperature=0, model=\"gpt-3.5-turbo-0125\").with_structured_output(\n", + " Classification\n", + ")\n", + "\n", + "chain = tagging_prompt | llm" ] }, { @@ -213,73 +241,73 @@ "id": "5ded2332", "metadata": {}, "source": [ - "Now the answers are much better!" + "Now the answers will be restricted in a way we expect!" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 17, "id": "d9b9d53d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'aggressiveness': 0, 'language': 'spanish'}" + "Classification(sentiment='happy', aggressiveness=1, language='spanish')" ] }, - "execution_count": 10, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inp = \"Estoy increiblemente contento de haberte conocido! Creo que seremos muy buenos amigos!\"\n", - "chain.run(inp)" + "chain.invoke({\"input\": inp})" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 18, "id": "1c12fa00", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'aggressiveness': 5, 'language': 'spanish'}" + "Classification(sentiment='sad', aggressiveness=5, language='spanish')" ] }, - "execution_count": 11, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inp = \"Estoy muy enojado con vos! Te voy a dar tu merecido!\"\n", - "chain.run(inp)" + "chain.invoke({\"input\": inp})" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 19, "id": "0bdfcb05", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'aggressiveness': 0, 'language': 'english'}" + "Classification(sentiment='neutral', aggressiveness=2, language='english')" ] }, - "execution_count": 12, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inp = \"Weather is ok here, I can go outside without much more than a coat\"\n", - "chain.run(inp)" + "chain.invoke({\"input\": inp})" ] }, { @@ -287,105 +315,11 @@ "id": "cf6b7389", "metadata": {}, "source": [ - "The [LangSmith trace](https://smith.langchain.com/public/311e663a-bbe8-4053-843e-5735055c032d/r) lets us peek under the hood:\n", - "\n", - "* As with [extraction](/docs/use_cases/extraction), we call the `information_extraction` function [here](https://github.com/langchain-ai/langchain/blob/269f85b7b7ffd74b38cd422d4164fc033388c3d0/libs/langchain/langchain/chains/openai_functions/extraction.py#L20) on the input string.\n", - "* This OpenAI function extraction information based upon the provided schema.\n", + "The [LangSmith trace](https://smith.langchain.com/public/38294e04-33d8-4c5a-ae92-c2fe68be8332/r) lets us peek under the hood:\n", "\n", "![Image description](../../static/img/tagging_trace.png)" ] }, - { - "cell_type": "markdown", - "id": "e68ad17e", - "metadata": {}, - "source": [ - "## Pydantic" - ] - }, - { - "cell_type": "markdown", - "id": "2f5970ec", - "metadata": {}, - "source": [ - "We can also use a Pydantic schema to specify the required properties and types. \n", - "\n", - "We can also send other arguments, such as `enum` or `description`, to each field.\n", - "\n", - "This lets us specify our schema in the same manner that we would a new class or function in Python with purely Pythonic types." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "bf1f367e", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.pydantic_v1 import BaseModel, Field" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "83a2e826", - "metadata": {}, - "outputs": [], - "source": [ - "class Tags(BaseModel):\n", - " sentiment: str = Field(..., enum=[\"happy\", \"neutral\", \"sad\"])\n", - " aggressiveness: int = Field(\n", - " ...,\n", - " description=\"describes how aggressive the statement is, the higher the number the more aggressive\",\n", - " enum=[1, 2, 3, 4, 5],\n", - " )\n", - " language: str = Field(\n", - " ..., enum=[\"spanish\", \"english\", \"french\", \"german\", \"italian\"]\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "6e404892", - "metadata": {}, - "outputs": [], - "source": [ - "chain = create_tagging_chain_pydantic(Tags, llm)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "b5fc43c4", - "metadata": {}, - "outputs": [], - "source": [ - "inp = \"Estoy muy enojado con vos! Te voy a dar tu merecido!\"\n", - "res = chain.run(inp)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "5074bcc3", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Tags(sentiment='sad', aggressiveness=5, language='spanish')" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "res" - ] - }, { "cell_type": "markdown", "id": "29346d09", @@ -414,7 +348,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.1" + "version": "3.10.5" } }, "nbformat": 4, diff --git a/docs/docs/use_cases/tool_use/agents.ipynb b/docs/docs/use_cases/tool_use/agents.ipynb index 77cbef71d6..eede7740d6 100644 --- a/docs/docs/use_cases/tool_use/agents.ipynb +++ b/docs/docs/use_cases/tool_use/agents.ipynb @@ -80,7 +80,7 @@ "source": [ "## Create tools\n", "\n", - "First, we need to create some tool to call. For this example, we will create custom tools from functions. For more information on creating custom tools, please see [this guide](/docs/modules/agents/tools/)." + "First, we need to create some tool to call. For this example, we will create custom tools from functions. For more information on creating custom tools, please see [this guide](/docs/modules/tools/)." ] }, { diff --git a/docs/docs/use_cases/tool_use/index.ipynb b/docs/docs/use_cases/tool_use/index.ipynb index 0f18e5f9d3..c5e71acf4e 100644 --- a/docs/docs/use_cases/tool_use/index.ipynb +++ b/docs/docs/use_cases/tool_use/index.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "source": [ "---\n", - "sidebar_position: 0.2\n", + "sidebar_class_name: hidden\n", "---" ] }, @@ -15,7 +15,7 @@ "id": "14b94240", "metadata": {}, "source": [ - "# Tool use\n", + "# Tool use and agents\n", "\n", "An exciting use case for LLMs is building natural language interfaces for other \"tools\", whether those are APIs, functions, databases, etc. LangChain is great for building such interfaces because it has:\n", "\n", diff --git a/docs/docs/use_cases/tool_use/prompting.ipynb b/docs/docs/use_cases/tool_use/prompting.ipynb index 6cd877fe2e..09dcf0b460 100644 --- a/docs/docs/use_cases/tool_use/prompting.ipynb +++ b/docs/docs/use_cases/tool_use/prompting.ipynb @@ -72,7 +72,7 @@ "source": [ "## Create a tool\n", "\n", - "First, we need to create a tool to call. For this example, we will create a custom tool from a function. For more information on all details related to creating custom tools, please see [this guide](/docs/modules/agents/tools/)." + "First, we need to create a tool to call. For this example, we will create a custom tool from a function. For more information on all details related to creating custom tools, please see [this guide](/docs/modules/tools/)." ] }, { diff --git a/docs/docs/use_cases/tool_use/quickstart.ipynb b/docs/docs/use_cases/tool_use/quickstart.ipynb index d8b335920f..d363ab853a 100644 --- a/docs/docs/use_cases/tool_use/quickstart.ipynb +++ b/docs/docs/use_cases/tool_use/quickstart.ipynb @@ -72,7 +72,7 @@ "source": [ "## Create a tool\n", "\n", - "First, we need to create a tool to call. For this example, we will create a custom tool from a function. For more information on creating custom tools, please see [this guide](/docs/modules/agents/tools/)." + "First, we need to create a tool to call. For this example, we will create a custom tool from a function. For more information on creating custom tools, please see [this guide](/docs/modules/tools/)." ] }, { diff --git a/docs/docs/use_cases/web_scraping.ipynb b/docs/docs/use_cases/web_scraping.ipynb index a8524061a7..f62aaeea1f 100644 --- a/docs/docs/use_cases/web_scraping.ipynb +++ b/docs/docs/use_cases/web_scraping.ipynb @@ -7,6 +7,7 @@ "source": [ "---\n", "title: Web scraping\n", + "sidebar_class_name: hidden\n", "---" ] }, diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 6b0bbf85fa..6fdc10f949 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -149,16 +149,10 @@ const config = { logo: {src: "img/brand/wordmark.png", srcDark: "img/brand/wordmark-dark.png"}, items: [ { - to: "/docs/get_started/introduction", - label: "Docs", + to: "/docs/modules", + label: "Components", position: "left", }, - { - type: "docSidebar", - position: "left", - sidebarId: "use_cases", - label: "Use cases", - }, { type: "docSidebar", position: "left", @@ -166,14 +160,13 @@ const config = { label: "Integrations", }, { - type: "docSidebar", - position: "left", - sidebarId: "guides", + to: "/docs/guides", label: "Guides", + position: "left", }, { href: "https://api.python.langchain.com", - label: "API", + label: "API Reference", position: "left", }, { @@ -189,11 +182,6 @@ const config = { to: "/docs/packages", label: "Versioning", }, - { - type: "docSidebar", - sidebarId: "changelog", - label: "Changelog", - }, { to: "/docs/contributing", label: "Contributing", diff --git a/docs/sidebars.js b/docs/sidebars.js index aa28a21dfd..802178d557 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -18,7 +18,7 @@ Create as many sidebars as you want. */ -module.exports = { + module.exports = { // By default, Docusaurus generates a sidebar from the docs folder structure docs: [ { @@ -26,59 +26,241 @@ module.exports = { label: "Get started", collapsed: false, collapsible: false, - items: [{ type: "autogenerated", dirName: "get_started" }, "security"], + items: [{ type: "autogenerated", dirName: "get_started" }], link: { - type: 'generated-index', - description: 'Get started with LangChain', - slug: "get_started", + type: 'doc', + id: "get_started/introduction" }, }, { type: "category", - label: "LangChain Expression Language", + label: "Use cases", collapsed: false, - items: [{ type: "autogenerated", dirName: "expression_language" } ], + collapsible: false, + items: [ + { + type: "category", + label: "Q&A with RAG", + collapsed: true, + items: [ + "use_cases/question_answering/quickstart", + "use_cases/question_answering/chat_history", + "use_cases/question_answering/streaming", + "use_cases/question_answering/sources", + "use_cases/question_answering/citations", + { + type: "category", + label: "More", + collapsed: true, + items: [ + "use_cases/question_answering/per_user", + "use_cases/question_answering/conversational_retrieval_agents", + "use_cases/question_answering/local_retrieval_qa", + ] + }, + ], + link: { type: "doc", id: "use_cases/question_answering/index" } + }, + { + type: "category", + label: "Extracting structured output", + link: { type: "doc", id: "use_cases/extraction/index" }, + collapsed: true, + items: [ + "use_cases/extraction/quickstart", + "use_cases/extraction/guidelines", + "use_cases/extraction/how_to/examples", + { + type: "category", + label: "More", + collapsed: true, + items: [ + "use_cases/extraction/how_to/handle_long_text", + "use_cases/extraction/how_to/handle_files", + "use_cases/extraction/how_to/parse", + ] + }, + ] + }, + { type: "category", label: "Chatbots", collapsed: true, items: [{ type: "autogenerated", dirName: "use_cases/chatbots" }], link: { type: "doc", id: "use_cases/chatbots/index" } }, + { type: "category", label: "Tool use and agents", collapsed: true, items: [{ type: "autogenerated", dirName: "use_cases/tool_use" }], link: { type: "doc", id: "use_cases/tool_use/index" } }, + { type: "category", label: "Query analysis", collapsed: true, items: [{ type: "autogenerated", dirName: "use_cases/query_analysis" }], link: { type: "doc", id: "use_cases/query_analysis/index" } }, + { type: "category", label: "Q&A over SQL + CSV", collapsed: true, items: [{ type: "autogenerated", dirName: "use_cases/sql" }], link: { type: "doc", id: "use_cases/sql/index" } }, + { + type: "category", + label: "More", + collapsed: true, + items: [ + // "use_cases/apis", + { type: "category", label: "Graphs", collapsed: true, items: [{ type: "autogenerated", dirName: "use_cases/graph", }], link: { type: "doc", id: "use_cases/graph/index" } }, + "use_cases/code_understanding", + "use_cases/data_generation", + "use_cases/tagging", + "use_cases/summarization", + "use_cases/web_scraping" + ] + } + ], link: { type: 'doc', - id: "expression_language/index" + id: "use_cases/index" }, }, { type: "category", - label: "Modules", + label: "Expression Language", collapsed: false, + collapsible: false, items: [ - { type: "category", label: "Model I/O", collapsed: true, items: [{type:"autogenerated", dirName: "modules/model_io" }], link: { type: 'doc', id: "modules/model_io/index" }}, - { type: "category", label: "Retrieval", collapsed: true, items: [{type:"autogenerated", dirName: "modules/data_connection" }], link: { type: 'doc', id: "modules/data_connection/index" }}, - { type: "category", label: "Agents", collapsed: true, items: [{type:"autogenerated", dirName: "modules/agents" }], link: { type: 'doc', id: "modules/agents/index" }}, - "modules/chains", + "expression_language/get_started", + "expression_language/interface", + { type: "category", label: "Primitives", collapsed: true, items: [{ type: "autogenerated", dirName: "expression_language/primitives", }], link: { type: "doc", id: "expression_language/primitives/index" } }, + "expression_language/why", + "expression_language/streaming", + "expression_language/how_to/message_history", { type: "category", label: "More", collapsed: true, items: [ - { type: "category", label: "Memory", collapsed: true, items: [{type:"autogenerated", dirName: "modules/memory" }], link: { type: 'doc', id: "modules/memory/index" }}, - { type: "category", label: "Callbacks", collapsed: true, items: [{type:"autogenerated", dirName: "modules/callbacks" }], link: { type: 'doc', id: "modules/callbacks/index" }}, + "expression_language/how_to/routing", + "expression_language/how_to/inspect", + "expression_language/how_to/decorator", + "expression_language/cookbook/prompt_size", + "expression_language/cookbook/multiple_chains", ] - } + }, ], link: { type: 'doc', - id: "modules/index" + id: "expression_language/index" }, }, - {type: "doc", id: "langserve", label: "LangServe"}, { type: "category", - label: "LangSmith", + label: "Ecosystem", + collapsed: false, + collapsible: false, + items: [ + { + type: "category", + label: "πŸ¦œπŸ› οΈ LangSmith", + collapsed: true, + items: [{ type: "autogenerated", dirName: "langsmith" } ], + link: { + type: 'doc', + id: "langsmith/index" + }, + }, + "langgraph", + "langserve", + ] + }, + "security" + ], + components: [ + { + type: "category", + label: "Model I/O", + collapsed: false, + collapsible: false, + link: { type: "doc", id: "modules/model_io/index" }, + items: [ + { + type: "category", + label: "Prompts", + items: [{ type: "autogenerated", dirName: "modules/model_io/prompts" }], + link: { type: "doc", id: "modules/model_io/prompts/index" } + }, + { + type: "category", + label: "Chat models", + items: [{ type: "autogenerated", dirName: "modules/model_io/chat" }], + link: { type: "doc", id: "modules/model_io/chat/index" } + }, + { + type: "category", + label: "LLMs", + items: [{ type: "autogenerated", dirName: "modules/model_io/llms" }], + link: { type: "doc", id: "modules/model_io/llms/index" } + }, + { + type: "category", + label: "Output parsers", + items: [{ type: "autogenerated", dirName: "modules/model_io/output_parsers" }], + link: { type: "doc", id: "modules/model_io/output_parsers/index" } + }, + ], + }, + { + type: "category", + label: "Retrieval", + collapsed: false, + collapsible: false, + link: { type: "doc", id: "modules/data_connection/index" }, + items: [ + { + type: "category", + label: "Document loaders", + items: [{ type: "autogenerated", dirName: "modules/data_connection/document_loaders" }], + link: { type: "doc", id: "modules/data_connection/document_loaders/index" } + }, + { + type: "category", + label: "Text splitters", + items: [{ type: "autogenerated", dirName: "modules/data_connection/document_transformers" }], + link: { type: "doc", id: "modules/data_connection/document_transformers/index" } + }, + { + type: "category", + label: "Embedding models", + items: [{ type: "autogenerated", dirName: "modules/data_connection/text_embedding" }], + link: { type: "doc", id: "modules/data_connection/text_embedding/index" } + }, + "modules/data_connection/vectorstores/index", + { + type: "category", + label: "Retrievers", + items: [{ type: "autogenerated", dirName: "modules/data_connection/retrievers" }], + link: { type: "doc", id: "modules/data_connection/retrievers/index" } + }, + "modules/data_connection/indexing" + ], + }, + { + type: "category", + label: "Composition", + collapsed: false, + collapsible: false, + items: [ + { type: "category", label: "Tools", collapsed: true, items: [{ type: "autogenerated", dirName: "modules/tools" }], link: { type: 'doc', id: "modules/tools/index" }}, + { type: "category", label: "Agents", collapsed: true, items: [{ type: "autogenerated", dirName: "modules/agents" }], link: { type: 'doc', id: "modules/agents/index" }}, + "modules/chains" + ], + link: { type: "doc", id: "modules/composition" } + }, + { + type: "category", + label: "More", collapsed: true, - items: [{ type: "autogenerated", dirName: "langsmith" } ], - link: { - type: 'doc', - id: "langsmith/index" - }, + items: [ + { type: "category", label: "Memory", collapsed: true, items: [ + "modules/memory/chat_messages/index", + {type: "category", label: "Memory classes [BETA]", collapsed: true, items: [ + "modules/memory/adding_memory", + "modules/memory/adding_memory_chain_multiple_inputs", + "modules/memory/agent_with_memory", + "modules/memory/agent_with_memory_in_db", + "modules/memory/conversational_customization", + "modules/memory/custom_memory", + "modules/memory/multiple_memory", + { type: "category", label: "Types", collapsed: true, items: [{ type: "autogenerated", dirName: "modules/memory/types" }]} + ]} + ], link: { type: 'doc', id: "modules/memory/index" }}, + { type: "category", label: "Callbacks", collapsed: true, items: [{type:"autogenerated", dirName: "modules/callbacks" }], link: { type: 'doc', id: "modules/callbacks/index" }}, + ] }, - {type: "doc", id: "langgraph", label: "LangGraph"}, + { type: "doc", id: "modules/index", className: "hidden" }, ], integrations: [ { @@ -99,8 +281,8 @@ module.exports = { label: "Components", collapsible: false, items: [ - { type: "category", label: "LLMs", collapsed: true, items: [{type:"autogenerated", dirName: "integrations/llms" }], link: { type: 'doc', id: "integrations/llms/index"}}, { type: "category", label: "Chat models", collapsed: true, items: [{type:"autogenerated", dirName: "integrations/chat" }], link: { type: 'doc', id: "integrations/chat/index"}}, + { type: "category", label: "LLMs", collapsed: true, items: [{type:"autogenerated", dirName: "integrations/llms" }], link: { type: 'doc', id: "integrations/llms/index"}}, { type: "category", label: "Embedding models", collapsed: true, items: [{type: "autogenerated", dirName: "integrations/text_embedding" }], link: {type: "generated-index", slug: "integrations/text_embedding" }}, { type: "category", label: "Document loaders", collapsed: true, items: [{type:"autogenerated", dirName: "integrations/document_loaders" }], link: {type: "generated-index", slug: "integrations/document_loaders" }}, { type: "category", label: "Document transformers", collapsed: true, items: [{type: "autogenerated", dirName: "integrations/document_transformers" }], link: {type: "generated-index", slug: "integrations/document_transformers" }}, @@ -120,18 +302,22 @@ module.exports = { }, }, ], - use_cases: [ + guides: [ + { + type: "category", label: "Development", collapsible: false, items: [{type: "autogenerated", dirName: "guides/development" }], + link: { type: "doc", id: "guides/development/index"}, + }, { type: "category", - label: "Use cases", - items: [ - { type: "autogenerated", dirName: "use_cases" }, - ], - link: { type: 'generated-index', slug: "use_cases"} + label: "Productionization", + collapsible: false, + items: [{type: "autogenerated", dirName: "guides/productionization" }], + link: { + type: "doc", + id: "guides/productionization/index", + } }, - ], - guides: [ - {type: "autogenerated", dirName: "guides" } + { type: "doc", id: "guides/index", className: "hidden" }, ], templates: [ { @@ -143,15 +329,7 @@ module.exports = { link: { type: 'doc', id: "templates/index" } }, ], - changelog: [ - { - type: "category", - label: "Changelog", - items: [{ type: "autogenerated", dirName: "changelog" }], - link: { type: 'generated-index', slug: "changelog"} - }, - ], contributing: [ - {type: "autogenerated", dirName: "contributing" } + { type: "category", label: "Contributing", items: [{type: "autogenerated", dirName: "contributing" }] } ], }; diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index b433f41aa4..d486a725b5 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -12,6 +12,16 @@ * bundles Infima by default. Infima is a CSS framework designed to * work well for content-centric websites. */ + + @font-face { + font-family: 'Manrope'; + src: url('/fonts/Manrope-VariableFont_wght.ttf') format('truetype'); +} + +@font-face { + font-family: 'Public Sans'; + src: url('/fonts/PublicSans-VariableFont_wght.ttf') format('truetype'); +} /* You can override the default Infima variables here. */ :root { @@ -22,7 +32,12 @@ --ifm-color-primary-light: #33925d; --ifm-color-primary-lighter: #359962; --ifm-color-primary-lightest: #3cad6e; + --ifm-font-weight-bold: 600; --ifm-code-font-size: 95%; + --ifm-font-family-base: 'Public Sans'; + --ifm-menu-link-padding-horizontal: 0.5rem; + --ifm-menu-link-padding-vertical: 0.375rem; + --doc-sidebar-width: 275px !important; } /* For readability concerns, you should choose a lighter palette in dark mode. */ @@ -36,6 +51,10 @@ --ifm-color-primary-lightest: #4fddbf; } +nav, h1, h2, h3, h4 { + font-family: 'Manrope'; +} + .footer__links { margin-top: 1rem; margin-bottom: 3rem; @@ -83,6 +102,86 @@ font-size: 0.85rem; } +/* .theme-code-block.language-python::before { + content: ""; + padding: 2px 12px; + background-color: var(--ifm-color-primary-light); + color: #ffffff; + font-weight: bold; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + display: block; + margin-bottom: 12px; + font-size: 0.6em; + width: 100%; + box-sizing: border-box; + position: relative; +} */ + +.theme-code-block.language-python, +.theme-code-block.language-javascript, +.theme-code-block.language-js, +.theme-code-block.language-typescript, +.theme-code-block.language-ts { + position: relative; /* Ensure this is set so the ::before pseudo-element is positioned relative to this element */ + padding-left: 4px; + border: 1px solid var(--ifm-color-primary-darkest); +} + +.theme-code-block.language-python::before, +.theme-code-block.language-javascript::before, +.theme-code-block.language-js::before, +.theme-code-block.language-typescript::before, +.theme-code-block.language-ts::before { + content: ""; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 3px; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + background-color: var(--ifm-color-primary-light); + z-index: 1; +} + +.theme-doc-sidebar-menu > .theme-doc-sidebar-item-category:not(:first-of-type), +.theme-doc-sidebar-menu > .theme-doc-sidebar-item-link, +.theme-doc-sidebar-menu > .theme-doc-sidebar-item-link.theme-doc-sidebar-item-link-level-1 { + margin-top: 1rem; +} + +.theme-doc-sidebar-menu .theme-doc-sidebar-item-link, +.theme-doc-sidebar-menu .theme-doc-sidebar-item-category { + margin-top: 0; + padding-bottom: 0; + padding-top: 0; +} + +.theme-doc-sidebar-menu .theme-doc-sidebar-item-category > ul { + margin-top: 0; +} + +.theme-doc-sidebar-menu .theme-doc-sidebar-item-link a, +.theme-doc-sidebar-menu .theme-doc-sidebar-item-category a { + margin-top: 0; +} + +.theme-doc-sidebar-item-category, .theme-doc-sidebar-menu > .theme-doc-sidebar-item-link { + font-size: 1rem; + font-weight: 700; +} + +.theme-doc-sidebar-item-category button:before { + height: 1rem; + width: 1.25rem; +} + +.theme-doc-sidebar-item-link, .theme-doc-sidebar-item-category .theme-doc-sidebar-item-category { + font-size: .9rem; + font-weight: 500; +} + .theme-doc-sidebar-item-category > div > a { flex: 1 1 0; overflow: hidden; @@ -93,6 +192,11 @@ opacity: 0.5; } +/* Hack for "More" style caret buttons */ +.theme-doc-sidebar-item-category > div > a::after { + opacity: 0.5; +} + .markdown > h2 { margin-top: 2rem; border-bottom-color: var(--ifm-color-primary); @@ -119,7 +223,7 @@ } .hidden { - display: none !important; + display: none !important; } .header-github-link:hover { @@ -139,5 +243,3 @@ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; } - - diff --git a/docs/static/fonts/Manrope-VariableFont_wght.ttf b/docs/static/fonts/Manrope-VariableFont_wght.ttf new file mode 100644 index 0000000000..f39ca39c96 Binary files /dev/null and b/docs/static/fonts/Manrope-VariableFont_wght.ttf differ diff --git a/docs/static/fonts/PublicSans-VariableFont_wght.ttf b/docs/static/fonts/PublicSans-VariableFont_wght.ttf new file mode 100644 index 0000000000..bc53efc12a Binary files /dev/null and b/docs/static/fonts/PublicSans-VariableFont_wght.ttf differ diff --git a/docs/static/img/code_retrieval.png b/docs/static/img/code_retrieval.png index 20250135d5..85bc26575e 100644 Binary files a/docs/static/img/code_retrieval.png and b/docs/static/img/code_retrieval.png differ diff --git a/docs/static/img/tagging_trace.png b/docs/static/img/tagging_trace.png index 3cc1231d86..f7fa158d61 100644 Binary files a/docs/static/img/tagging_trace.png and b/docs/static/img/tagging_trace.png differ diff --git a/docs/vercel.json b/docs/vercel.json index b77af82516..b52dabe02e 100644 --- a/docs/vercel.json +++ b/docs/vercel.json @@ -152,10 +152,6 @@ "source": "/docs/modules/data_connection/document_transformers/text_splitters/:path*", "destination": "/docs/modules/data_connection/document_transformers/:path*" }, - { - "source": "/docs/modules/model_io/prompts/example_selectors/:path*", - "destination": "/docs/modules/model_io/prompts/example_selector_types/:path*" - }, { "source": "/docs/modules/model_io/prompts/prompt_templates(/?)", "destination": "/docs/modules/model_io/prompts/" @@ -974,8 +970,144 @@ "destination": "/docs/integrations/platforms/google#google-document-ai" }, { - "source": "/docs/integrations/tools/metaphor_search", + "source": "/docs/integrations/tools/metaphor_search(/?)", "destination": "/docs/integrations/tools/exa_search" + }, + { + "source": "/docs/expression_language/how_to/fallbacks(/?)", + "destination": "/docs/guides/productionization/fallbacks" + }, + { + "source": "/docs/expression_language/cookbook/retrieval(/?)", + "destination": "/docs/use_cases/question_answering" + }, + { + "source": "/docs/expression_language/cookbook/agent(/?)", + "destination": "/docs/modules/agents/agent_types/xml_agent" + }, + { + "source": "/docs/modules/model_io/prompts/message_prompts(/?)", + "destination": "/docs/modules/model_io/prompts/quick_start#message-prompts" + }, + { + "source": "/docs/modules/model_io/prompts/pipeline(/?)", + "destination": "/docs/modules/model_io/prompts/composition#using-pipelineprompt" + }, + { + "source": "/docs/expression_language/cookbook/memory(/?)", + "destination": "/docs/modules/memory" + }, + { + "source": "/docs/expression_language/cookbook/tools(/?)", + "destination": "/docs/use_cases/tool_use/quickstart" + }, + { + "source": "/docs/expression_language/cookbook/sql_db(/?)", + "destination": "/docs/use_cases/sql/quickstart" + }, + { + "source": "/docs/expression_language/cookbook/moderation(/?)", + "destination": "/docs/guides/productionization/safety/moderation" + }, + { + "source": "/docs/expression_language/cookbook/embedding_router(/?)", + "destination": "/docs/expression_language/how_to/routing" + }, + { + "source": "/docs/modules/model_io/prompts/example_selector_types/:path*", + "destination": "/docs/modules/model_io/prompts/example_selectors/:path*" + }, + { + "source": "/docs/modules/agents/tools/:path*", + "destination": "/docs/modules/tools/:path*" + }, + { + "source": "/docs/guides/structured_output(/?)", + "destination": "/docs/modules/model_io/chat/structured_output" + }, + { + "source": "/docs/modules/agents/how_to/structured_tools(/?)", + "destination": "/docs/modules/tools" + }, + { + "source": "/docs/use_cases/csv(/?)", + "destination": "/docs/use_cases/sql/csv" + }, + { + "source": "/docs/guides/debugging(/?)", + "destination": "/docs/guides/development/debugging" + }, + { + "source": "/docs/guides/deployments/:path*", + "destination": "/docs/guides/productionization/deployments/:path*" + }, + { + "source": "/docs/guides/evaluation/:path*", + "destination": "/docs/guides/productionization/evaluation/:path*" + }, + { + "source": "/docs/guides/extending_langchain(/?)", + "destination": "/docs/guides/development/extending_langchain" + }, + { + "source": "/docs/guides/fallbacks(/?)", + "destination": "/docs/guides/productionization/fallbacks" + }, + { + "source": "/docs/guides/privacy/:path*", + "destination": "/docs/guides/productionization/safety/:path*" + }, + { + "source": "/docs/guides/safety/:path*", + "destination": "/docs/guides/productionization/safety/:path*" + }, + { + "source": "/docs/guides/model_laboratory(/?)", + "destination": "/docs/guides/productionization/evaluation" + }, + { + "source": "/docs/guides/pydantic_compatibility(/?)", + "destination": "/docs/guides/development/pydantic_compatibility" + }, + { + "source": "/docs/guides/local_llms(/?)", + "destination": "/docs/guides/development/local_llms" + }, + { + "source": "/docs/modules/model_io/quick_start(/?)", + "destination": "/docs/modules/model_io" + }, + { + "source": "/docs/expression_language/how_to/generators(/?)", + "destination": "/docs/expression_language/primitives/functions" + }, + { + "source": "/docs/expression_language/how_to/functions(/?)", + "destination": "/docs/expression_language/primitives/functions" + }, + { + "source": "/docs/expression_language/how_to/passthrough(/?)", + "destination": "/docs/expression_language/primitives/passthrough" + }, + { + "source": "/docs/expression_language/how_to/map(/?)", + "destination": "/docs/expression_language/primitives/parallel" + }, + { + "source": "/docs/expression_language/how_to/binding(/?)", + "destination": "/docs/expression_language/primitives/binding" + }, + { + "source": "/docs/expression_language/how_to/configure(/?)", + "destination": "/docs/expression_language/primitives/configure" + }, + { + "source": "/docs/expression_language/cookbook/prompt_llm_parser(/?)", + "destination": "/docs/expression_language/get_started" + }, + { + "source": "/docs/contributing/documentation(/?)", + "destination": "/docs/contributing/documentation/technical_logistics" } ] } diff --git a/libs/langchain/README.md b/libs/langchain/README.md index bd16bdf6e6..d6eae5e11d 100644 --- a/libs/langchain/README.md +++ b/libs/langchain/README.md @@ -34,20 +34,20 @@ Large language models (LLMs) are emerging as a transformative technology, enabli This library aims to assist in the development of those types of applications. Common examples of these applications include: -**❓ Question Answering over specific documents** +**❓ Question answering with RAG** - [Documentation](https://python.langchain.com/docs/use_cases/question_answering/) -- End-to-end Example: [Question Answering over Notion Database](https://github.com/hwchase17/notion-qa) +- End-to-end Example: [Chat LangChain](https://chat.langchain.com) and [repo](https://github.com/langchain-ai/chat-langchain) -**πŸ’¬ Chatbots** +**🧱 Extracting structured output** -- [Documentation](https://python.langchain.com/docs/use_cases/chatbots/) -- End-to-end Example: [Chat-LangChain](https://github.com/langchain-ai/chat-langchain) +- [Documentation](https://python.langchain.com/docs/use_cases/extraction/) +- End-to-end Example: [SQL Llama2 Template](https://github.com/langchain-ai/langchain-extract/) -**πŸ€– Agents** +**πŸ€– Chatbots** -- [Documentation](https://python.langchain.com/docs/modules/agents/) -- End-to-end Example: [GPT+WolframAlpha](https://huggingface.co/spaces/JavaFXpert/Chat-GPT-LangChain) +- [Documentation](https://python.langchain.com/docs/use_cases/chatbots) +- End-to-end Example: [Web LangChain (web researcher chatbot)](https://weblangchain.vercel.app) and [repo](https://github.com/langchain-ai/weblangchain) ## πŸ“– Documentation @@ -60,29 +60,25 @@ Please see [here](https://python.langchain.com) for full documentation on: ## πŸš€ What can this help with? -There are six main areas that LangChain is designed to help with. +There are five main areas that LangChain is designed to help with. These are, in increasing order of complexity: -**πŸ“ƒ LLMs and Prompts:** +**πŸ“ƒ Models and Prompts:** -This includes prompt management, prompt optimization, a generic interface for all LLMs, and common utilities for working with LLMs. +This includes prompt management, prompt optimization, a generic interface for all LLMs, and common utilities for working with chat models and LLMs. **πŸ”— Chains:** Chains go beyond a single LLM call and involve sequences of calls (whether to an LLM or a different utility). LangChain provides a standard interface for chains, lots of integrations with other tools, and end-to-end chains for common applications. -**πŸ“š Data Augmented Generation:** +**πŸ“š Retrieval Augmented Generation:** -Data Augmented Generation involves specific types of chains that first interact with an external data source to fetch data for use in the generation step. Examples include summarization of long pieces of text and question/answering over specific data sources. +Retrieval Augmented Generation involves specific types of chains that first interact with an external data source to fetch data for use in the generation step. Examples include summarization of long pieces of text and question/answering over specific data sources. **πŸ€– Agents:** Agents involve an LLM making decisions about which Actions to take, taking that Action, seeing an Observation, and repeating that until done. LangChain provides a standard interface for agents, a selection of agents to choose from, and examples of end-to-end agents. -**🧠 Memory:** - -Memory refers to persisting state between calls of a chain/agent. LangChain provides a standard interface for memory, a collection of memory implementations, and examples of chains/agents that use memory. - **🧐 Evaluation:** [BETA] Generative models are notoriously hard to evaluate with traditional metrics. One new way of evaluating them is using language models themselves to do the evaluation. LangChain provides some prompts/chains for assisting in this.