diff --git a/docs/_scripts/copy_notebooks.py b/docs/_scripts/copy_notebooks.py index 962ece629..90790392d 100644 --- a/docs/_scripts/copy_notebooks.py +++ b/docs/_scripts/copy_notebooks.py @@ -15,6 +15,7 @@ _MANUAL = { "how-tos": [ "async.ipynb", "streaming-tokens.ipynb", + "streaming-content.ipynb", "human-in-the-loop.ipynb", "persistence.ipynb", "time-travel.ipynb", @@ -30,8 +31,10 @@ _MANUAL = { "dynamically-returning-directly.ipynb", "configuration.ipynb", "map-reduce.ipynb", - "extraction/retries.ipynb", "create-react-agent.ipynb", + "create-react-agent-system-prompt.ipynb", + "create-react-agent-memory.ipynb", + "create-react-agent-hitl.ipynb", ], "tutorials": [ "introduction.ipynb", diff --git a/docs/docs/concepts/index.md b/docs/docs/concepts/index.md index e23686978..3364f2f4e 100644 --- a/docs/docs/concepts/index.md +++ b/docs/docs/concepts/index.md @@ -30,6 +30,7 @@ Low Level Concepts - [Conditional Entry Point](low_level#conditional-entry-point) - [Send](low_level#send) - [Checkpointer](low_level#checkpointer) +- [Threads](low_level#threads) - [Checkpointer states](low_level#checkpointer-state) - [Get state](low_level#get-state) - [Get state history](low_level#get-state-history) diff --git a/docs/docs/concepts/low_level.md b/docs/docs/concepts/low_level.md index f39266d78..e8aa73f6c 100644 --- a/docs/docs/concepts/low_level.md +++ b/docs/docs/concepts/low_level.md @@ -226,8 +226,6 @@ graph.set_conditional_entry_point(routing_function, {True: "node_b", False: "nod ## `Send` -[`Send`](https://langchain-ai.github.io/langgraph/reference/graphs/#send) is a special type of edge. - By default, `Nodes` and `Edges` are defined ahead of time and operate on the same shared state. However, there can be cases where the exact edges are not known ahead of time and/or you may want different versions of `State` to exist at the same time. A common of example of this is with `map-reduce` design patterns. In this design pattern, a first node may generate a list of objects, and you may want to apply some other node to all those objects. The number of objects may be unknown ahead of time (meaning the number of edges may not be known) and the input `State` to the downstream `Node` should be different (one for each generated object). To support this design pattern, LangGraph supports returning [`Send`](https://langchain-ai.github.io/langgraph/reference/graphs/#send) objects from conditional edges. `Send` takes two arguments: first is the name of the node, and second is the state to pass to that node. @@ -249,18 +247,35 @@ First, it allows for human-in-the-loop workflows, as it allows humans to inspect Second, it allows for "memory" between interactions. You can use checkpointers to create threads and save the state of a thread after a graph executes. In the case of repeated human interactions (like conversations) any follow up messages can be sent to that checkpoint, which will retain its memory of previous ones. +## Threads + +When using a checkpointer, you must specify a `thread_id` or `thread_ts` when running the graph. +Threads are used to checkpoint multiple different runs. This can be used to enable a multi-tenant chat applications. + +`thread_id` is simply the ID of a thread. This is always required + +`thread_ts` can optionally be passed. This identifier refers to a specific checkpoint within a thread. This can be used to kick of a run of a graph from some point halfway through a thread. + +You must pass these when invoking the graph as part of the configurable part of the config. + +```python +config = {"configurable": {"thread_id": "a"}} +graph.invoke(inputs, config=config) +``` + ## Checkpointer state When you use a checkpointer with a graph, you can interact with the state of that graph. This usually done when enabling different human-in-the-loop interaction patterns. +When interacting with the checkpointer state, you must specify [thread identifiers](#threads) ### Get state -You can get the state of a checkpointer by calling `graph.get_state(config)`. The config commonly contains things like the `thread_id` of a particular thread to get the state for. +You can get the state of a checkpointer by calling `graph.get_state(config)`. The config should contain `thread_id`, and the state will be fetched for that thread. ### Get state history -You can also call `graph.get_state_history(config)` to get a list of the history of the graph. The config commonly contains things like the `thread_id` of a particular thread to get the state for. +You can also call `graph.get_state_history(config)` to get a list of the history of the graph. The config should contain `thread_id`, and the state history will be fetched for that thread. ### Update state @@ -273,7 +288,7 @@ You can also interact with the state directly and update it. This takes three di **config** -The config commonly contains things like `thread_id` specifying which thread to update. +The config should contain `thread_id` specifying which thread to update. **values** diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md index 9adde1a14..126542350 100644 --- a/docs/docs/how-tos/index.md +++ b/docs/docs/how-tos/index.md @@ -7,36 +7,44 @@ hide: Welcome to the LangGraph how-to guides! These guides provide practical, step-by-step instructions for accomplishing key tasks in LangGraph. -## Basics +## Controllability -These guides show how to address common needs when building out AI workflows, with special focus placed on [ReAct](https://arxiv.org/abs/2210.03629)-style agents with [tool calling](https://python.langchain.com/docs/modules/model_io/chat/function_calling/) (agents that Reason and **Act** to accomplish tasks). - -- [How to create a ReAct agent](create-react-agent.ipynb) -- [How to add persistence ("memory") to your graph](persistence.ipynb) -- [How to view and update graph state](time-travel.ipynb) -- [How to run graph asynchronously](async.ipynb) -- [How to stream graph responses](streaming-tokens.ipynb) -- [How to visualize your graph](visualization.ipynb) -- [How to add runtime configuration to your graph](configuration.ipynb) - -### Design patterns - -Recipes showing how to apply common design patterns in your workflows: +LangGraph is known for being a highly controllable agent framework. +These how-to guides show how to achieve that controllability. - [How to create subgraphs](subgraph.ipynb) - [How to create branches for parallel execution](branching.ipynb) - [How to create map-reduce branches for parallel execution](map-reduce.ipynb) + +## Human in the Loop + +One of LangGraph's main benefits is that it makes human-in-the-loop workflows easy. +These guides cover common examples of that. + +- [How to add persistence ("memory") to your graph](persistence.ipynb) +- [How to view and update graph state](time-travel.ipynb) - [How to add human-in-the-loop](human-in-the-loop.ipynb) -The following examples are useful especially if you are used to LangChain's `AgentExecutor` configurations. +## Streaming -- [How to force an agent to call a tool](force-calling-a-tool-first.ipynb) -- [How to pass runtime values to tools](pass-run-time-values-to-tools.ipynb) -- [How to let agent return tool results directly](dynamically-returning-directly.ipynb) -- [How to have agent respond in structured format](respond-in-format.ipynb) -- [How to manage agent steps](managing-agent-steps.ipynb) +LangGraph is built to be streaming first. +These guides show how to use different streaming modes. -### Advanced +- [How to stream LLM tokens](streaming-tokens.ipynb) +- [How to stream arbitrarily nested content](streaming-content.ipynb) +## Other +- [How to run graph asynchronously](async.ipynb) +- [How to visualize your graph](visualization.ipynb) +- [How to add runtime configuration to your graph](configuration.ipynb) - [How to use a Pydantic model as your state](state-model.ipynb) -- [How to extract structured output with re-prompting](./extraction/retries.ipynb) \ No newline at end of file + +## Prebuilt ReAct Agent + +These guides show how to use the prebuilt ReAct agent. +Please note that here will we use a **prebuilt agent**. One of the big benefits of LangGraph is that you can easily create your own agent architectures. So while it's fine to start here to build an agent quickly, we would strongly recommend learning how to build your own agent so that you can take full advantage of LangGraph. + +- [How to create a ReAct agent](create-react-agent.ipynb) +- [How to add memory to a ReAct agent](create-react-agent-memory.ipynb) +- [How to add a custom system prompt to a ReAct agent](create-react-agent-system-prompt.ipynb) +- [How to add human-in-the-loop processes to a ReAct agent](create-react-agent-hitl.ipynb) diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md index ae82b7e99..a5aa052dc 100644 --- a/docs/docs/tutorials/index.md +++ b/docs/docs/tutorials/index.md @@ -63,4 +63,6 @@ Learn from example implementations of graphs designed for specific scenarios and - [Web Research (STORM)](storm/storm.ipynb): Generate Wikipedia-like articles via research and multi-perspective QA - [TNT-LLM](tnt-llm/tnt-llm.ipynb): Build rich, interpretable taxonomies of user intentand using the classification system developed by Microsoft for their Bing Copilot application. - [Web Navigation](web-navigation/web_voyager.ipynb): Build an agent that can navigate and interact with websites -- [Competitive Programming](usaco/usaco.ipynb): Build an agent with few-shot "episodic memory" and human-in-the-loop collaboration to solve problems from the USA Computing Olympiad; adapted from the ["Can Language Models Solve Olympiad Programming?"](https://arxiv.org/abs/2404.10952v1) paper by Shi, Tang, Narasimhan, and Yao. \ No newline at end of file +- [Competitive Programming](usaco/usaco.ipynb): Build an agent with few-shot "episodic memory" and human-in-the-loop collaboration to solve problems from the USA Computing Olympiad; adapted from the ["Can Language Models Solve Olympiad Programming?"](https://arxiv.org/abs/2404.10952v1) paper by Shi, Tang, Narasimhan, and Yao. +- [Complex data extraction](extraction/retries.ipynb): Build an agent that can use function calling to do complex extraction tasks +- \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index c880ba9c4..4e4f1078d 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -130,32 +130,36 @@ nav: - TNT-LLM: tutorials/tnt-llm/tnt-llm.ipynb - Web Navigation: tutorials/web-navigation/web_voyager.ipynb - Competitive Programming: tutorials/usaco/usaco.ipynb + - Extract structured output: tutorials/extraction/retries.ipynb - "How-to Guides": - 'how-tos/index.md' - - Basics: - - Create a ReAct agent: how-tos/create-react-agent.ipynb - - Add persistence ("memory"): how-tos/persistence.ipynb - - View and update graph state: how-tos/time-travel.ipynb - - Run graph asynchronously: how-tos/async.ipynb - - Stream graph responses: how-tos/streaming-tokens.ipynb - - Visualize your graph: how-tos/visualization.ipynb - - Add runtime configuration: how-tos/configuration.ipynb - - Design Patterns: + - Controllability: - Create subgraphs: how-tos/subgraph.ipynb - Create branches for parallel execution: how-tos/branching.ipynb - Create map-reduce branches for parallel execution: how-tos/map-reduce.ipynb + - Human-in-the-loop: + - Add persistence ("memory"): how-tos/persistence.ipynb + - View and update graph state: how-tos/time-travel.ipynb - Add human-in-the-loop: how-tos/human-in-the-loop.ipynb - - Force an agent to call a tool: how-tos/force-calling-a-tool-first.ipynb - - Pass runtime values to tools: how-tos/pass-run-time-values-to-tools.ipynb - - Let agent return tool results directly: how-tos/dynamically-returning-directly.ipynb - - Have agent respond in structured format: how-tos/respond-in-format.ipynb - - Manage agent steps: how-tos/managing-agent-steps.ipynb - - Advanced: + - Streaming: + - Stream LLM tokens: how-tos/streaming-tokens.ipynb + - Stream Arbitrarily Nested Content: how-tos/streaming-content.ipynb + - Other: + - Run graph asynchronously: how-tos/async.ipynb + - Visualize your graph: how-tos/visualization.ipynb + - Add runtime configuration: how-tos/configuration.ipynb - Use Pydantic model as state: how-tos/state-model.ipynb - - Extract structured output with re-prompting: how-tos/extraction/retries.ipynb + - Prebuilt ReAct Agent: + - Create a ReAct agent: how-tos/create-react-agent.ipynb + - Add Memory to a ReAct agent: how-tos/create-react-agent-memory.ipynb + - Add a System Prompt to a ReAct agent: how-tos/create-react-agent-system-prompt.ipynb + - Add Human-in-the-Loop to a ReAct agent: how-tos/create-react-agent-hitl.ipynb - 'Conceptual Guides': - 'concepts/index.md' + - LangGraph for Agentic Applications: concepts/high_level.md + - Low Level LangGraph Concepts: concepts/high_level.md + - Common Agentic Patterns: concepts/high_level.md - Reference: - Graphs: reference/graphs.md - Checkpointing: reference/checkpoints.md diff --git a/examples/create-react-agent-hitl.ipynb b/examples/create-react-agent-hitl.ipynb new file mode 100644 index 000000000..21ac07472 --- /dev/null +++ b/examples/create-react-agent-hitl.ipynb @@ -0,0 +1,245 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4", + "metadata": {}, + "source": [ + "# How to add human-in-the-loop processes to the prebuilt ReAct agent\n", + "\n", + "This tutorial will show how to add human-in-the-loop processes to the prebuilt ReAct agent. Please see [this tutorial](create-react-agent) for how to get started with the prebuilt ReAct agent\n", + "\n", + "You can add a a breakpoint before tools are called by passing `interrupt_before=[\"tools\"]` to `create_react_agent`. Note that you need to be using a checkpointer for this to work." + ] + }, + { + "cell_type": "markdown", + "id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a213e11a-5c62-4ddb-a707-490d91add383", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph langchain-openai" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "23a1885c-04ab-4750-aefa-105891fddf3e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OPENAI_API_KEY: ········\n" + ] + } + ], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "\n", + "def _set_env(var: str):\n", + " if not os.environ.get(var):\n", + " os.environ[var] = getpass.getpass(f\"{var}: \")\n", + "\n", + "\n", + "_set_env(\"OPENAI_API_KEY\")\n", + "\n", + "# Recommended\n", + "_set_env(\"LANGCHAIN_API_KEY\")\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\"" + ] + }, + { + "cell_type": "markdown", + "id": "03c0f089-070c-4cd4-87e0-6c51f2477b82", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7a154152-973e-4b5d-aa13-48c617744a4c", + "metadata": {}, + "outputs": [], + "source": [ + "# First we initialize the model we want to use.\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", + "\n", + "\n", + "# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n", + "\n", + "from typing import Literal\n", + "\n", + "from langchain_core.tools import tool\n", + "\n", + "\n", + "@tool\n", + "def get_weather(city: Literal[\"nyc\", \"sf\"]):\n", + " \"\"\"Use this to get weather information.\"\"\"\n", + " if city == \"nyc\":\n", + " return \"It might be cloudy in nyc\"\n", + " elif city == \"sf\":\n", + " return \"It's always sunny in sf\"\n", + " else:\n", + " raise AssertionError(\"Unknown city\")\n", + "\n", + "\n", + "tools = [get_weather]\n", + "\n", + "# We need a checkpointer to enable human-in-the-loop patterns\n", + "from langgraph.checkpoint import MemorySaver\n", + "\n", + "memory = MemorySaver()\n", + "\n", + "# Define the graph\n", + "\n", + "from langgraph.prebuilt import create_react_agent\n", + "\n", + "graph = create_react_agent(model, tools=tools, interrupt_before=[\"tools\"], checkpointer=memory)" + ] + }, + { + "cell_type": "markdown", + "id": "00407425-506d-4ffd-9c86-987921d8c844", + "metadata": {}, + "source": [ + "## Usage\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28", + "metadata": {}, + "outputs": [], + "source": [ + "def print_stream(stream):\n", + " for s in stream:\n", + " message = s[\"messages\"][-1]\n", + " if isinstance(message, tuple):\n", + " print(message)\n", + " else:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's the weather in SF?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_0OMmuTLec9t8kxMVkllZCSxo)\n", + " Call ID: call_0OMmuTLec9t8kxMVkllZCSxo\n", + " Args:\n", + " city: sf\n" + ] + } + ], + "source": [ + "config = {\"configurable\": {\"thread_id\": \"42\"}}\n", + "inputs = {\"messages\": [(\"user\", \"What's the weather in SF?\")]}\n", + "\n", + "print_stream(graph.stream(inputs, config, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3decf001-7228-4ed5-8779-2b9ed98a74ea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Next step: ('tools',)\n" + ] + } + ], + "source": [ + "snapshot = graph.get_state(config)\n", + "print(\"Next step: \", snapshot.next)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "83148e08-63e8-49e5-a08b-02dc907bed1d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It's always sunny in sf\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The weather in San Francisco is currently sunny.\n" + ] + } + ], + "source": [ + "print_stream(graph.stream(None, config, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f6f8965-b016-4e25-be63-31c00fc0a6de", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/create-react-agent-memory.ipynb b/examples/create-react-agent-memory.ipynb new file mode 100644 index 000000000..1588084c3 --- /dev/null +++ b/examples/create-react-agent-memory.ipynb @@ -0,0 +1,255 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4", + "metadata": {}, + "source": [ + "# How to add memory to the prebuilt ReAct agent\n", + "\n", + "This tutorial will show how to add memory to the prebuilt ReAct agent. Please see [this tutorial](create-react-agent) for how to get started with the prebuilt ReAct agent\n", + "\n", + "All we need to do to enable memory is pass in a checkpointer to `create_react_agents`" + ] + }, + { + "cell_type": "markdown", + "id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a213e11a-5c62-4ddb-a707-490d91add383", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph langchain-openai" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "23a1885c-04ab-4750-aefa-105891fddf3e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OPENAI_API_KEY: ········\n" + ] + } + ], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "\n", + "def _set_env(var: str):\n", + " if not os.environ.get(var):\n", + " os.environ[var] = getpass.getpass(f\"{var}: \")\n", + "\n", + "\n", + "_set_env(\"OPENAI_API_KEY\")\n", + "\n", + "# Recommended\n", + "_set_env(\"LANGCHAIN_API_KEY\")\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\"" + ] + }, + { + "cell_type": "markdown", + "id": "03c0f089-070c-4cd4-87e0-6c51f2477b82", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7a154152-973e-4b5d-aa13-48c617744a4c", + "metadata": {}, + "outputs": [], + "source": [ + "# First we initialize the model we want to use.\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", + "\n", + "\n", + "# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n", + "\n", + "from typing import Literal\n", + "\n", + "from langchain_core.tools import tool\n", + "\n", + "\n", + "@tool\n", + "def get_weather(city: Literal[\"nyc\", \"sf\"]):\n", + " \"\"\"Use this to get weather information.\"\"\"\n", + " if city == \"nyc\":\n", + " return \"It might be cloudy in nyc\"\n", + " elif city == \"sf\":\n", + " return \"It's always sunny in sf\"\n", + " else:\n", + " raise AssertionError(\"Unknown city\")\n", + "\n", + "\n", + "tools = [get_weather]\n", + "\n", + "# We can add \"chat memory\" to the graph with LangGraph's checkpointer\n", + "# to retain the chat context between interactions\n", + "from langgraph.checkpoint import MemorySaver\n", + "\n", + "memory = MemorySaver()\n", + "\n", + "# Define the graph\n", + "\n", + "from langgraph.prebuilt import create_react_agent\n", + "\n", + "graph = create_react_agent(model, tools=tools, checkpointer=memory)" + ] + }, + { + "cell_type": "markdown", + "id": "00407425-506d-4ffd-9c86-987921d8c844", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "Let's interact with it multiple times to show that it can remember" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28", + "metadata": {}, + "outputs": [], + "source": [ + "def print_stream(stream):\n", + " for s in stream:\n", + " message = s[\"messages\"][-1]\n", + " if isinstance(message, tuple):\n", + " print(message)\n", + " else:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's the weather in NYC?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_mdovy4yXSSYrmSlnlVSUacVn)\n", + " Call ID: call_mdovy4yXSSYrmSlnlVSUacVn\n", + " Args:\n", + " city: nyc\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It might be cloudy in nyc\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "The weather in NYC might be cloudy.\n" + ] + } + ], + "source": [ + "config = {\"configurable\": {\"thread_id\": \"1\"}}\n", + "inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n", + "\n", + "print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "markdown", + "id": "838a043f-90ad-4e69-9d1d-6e22db2c346c", + "metadata": {}, + "source": [ + "Notice that when we pass the same the same thread ID, the chat history is preserved" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "187479f9-32fa-4611-9487-cf816ba2e147", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's it known for?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "New York City (NYC) is known for many things, including:\n", + "\n", + "1. **Landmarks and Attractions**: The Statue of Liberty, Times Square, Central Park, Empire State Building, and Brooklyn Bridge.\n", + "2. **Cultural Institutions**: Broadway theaters, Metropolitan Museum of Art, Museum of Modern Art (MoMA), and the American Museum of Natural History.\n", + "3. **Diverse Neighborhoods**: Areas like Chinatown, Little Italy, Harlem, and Greenwich Village.\n", + "4. **Financial Hub**: Wall Street and the New York Stock Exchange.\n", + "5. **Cuisine**: A melting pot of global cuisines, famous for its pizza, bagels, and street food.\n", + "6. **Media and Entertainment**: Home to major media companies, TV networks, and film studios.\n", + "7. **Fashion**: A global fashion capital, hosting New York Fashion Week.\n", + "8. **Sports**: Teams like the New York Yankees, New York Mets, New York Knicks, and New York Rangers.\n", + "9. **Public Transportation**: An extensive subway system and iconic yellow taxis.\n", + "10. **Events**: New Year's Eve celebration in Times Square, Macy's Thanksgiving Day Parade, and various cultural festivals.\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [(\"user\", \"What's it known for?\")]}\n", + "print_stream(graph.stream(inputs, config=config, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3decf001-7228-4ed5-8779-2b9ed98a74ea", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/create-react-agent-system-prompt.ipynb b/examples/create-react-agent-system-prompt.ipynb new file mode 100644 index 000000000..4b664412d --- /dev/null +++ b/examples/create-react-agent-system-prompt.ipynb @@ -0,0 +1,207 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4", + "metadata": {}, + "source": [ + "# How to add a custom system prompt to the prebuilt ReAct agent\n", + "\n", + "This tutorial will show how to add a custom system prompt to the prebuilt ReAct agent. Please see [this tutorial](create-react-agent) for how to get started with the prebuilt ReAct agent\n", + "\n", + "You can add a custom system prompt by passing a string to the `messages_modifier` param." + ] + }, + { + "cell_type": "markdown", + "id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a213e11a-5c62-4ddb-a707-490d91add383", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph langchain-openai" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "23a1885c-04ab-4750-aefa-105891fddf3e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OPENAI_API_KEY: ········\n" + ] + } + ], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "\n", + "def _set_env(var: str):\n", + " if not os.environ.get(var):\n", + " os.environ[var] = getpass.getpass(f\"{var}: \")\n", + "\n", + "\n", + "_set_env(\"OPENAI_API_KEY\")\n", + "\n", + "# Recommended\n", + "_set_env(\"LANGCHAIN_API_KEY\")\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\"" + ] + }, + { + "cell_type": "markdown", + "id": "03c0f089-070c-4cd4-87e0-6c51f2477b82", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7a154152-973e-4b5d-aa13-48c617744a4c", + "metadata": {}, + "outputs": [], + "source": [ + "# First we initialize the model we want to use.\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", + "\n", + "\n", + "# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n", + "\n", + "from typing import Literal\n", + "\n", + "from langchain_core.tools import tool\n", + "\n", + "\n", + "@tool\n", + "def get_weather(city: Literal[\"nyc\", \"sf\"]):\n", + " \"\"\"Use this to get weather information.\"\"\"\n", + " if city == \"nyc\":\n", + " return \"It might be cloudy in nyc\"\n", + " elif city == \"sf\":\n", + " return \"It's always sunny in sf\"\n", + " else:\n", + " raise AssertionError(\"Unknown city\")\n", + "\n", + "\n", + "tools = [get_weather]\n", + "\n", + "# We can add our system prompt here\n", + "\n", + "prompt = \"Respond in Italian\"\n", + "\n", + "# Define the graph\n", + "\n", + "from langgraph.prebuilt import create_react_agent\n", + "\n", + "graph = create_react_agent(model, tools=tools, messages_modifier=prompt)" + ] + }, + { + "cell_type": "markdown", + "id": "00407425-506d-4ffd-9c86-987921d8c844", + "metadata": {}, + "source": [ + "## Usage\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28", + "metadata": {}, + "outputs": [], + "source": [ + "def print_stream(stream):\n", + " for s in stream:\n", + " message = s[\"messages\"][-1]\n", + " if isinstance(message, tuple):\n", + " print(message)\n", + " else:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "What's the weather in NYC?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " get_weather (call_b02uzBRrIm2uciJa8zDXCDxT)\n", + " Call ID: call_b02uzBRrIm2uciJa8zDXCDxT\n", + " Args:\n", + " city: nyc\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: get_weather\n", + "\n", + "It might be cloudy in nyc\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "A New York potrebbe essere nuvoloso.\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [(\"user\", \"What's the weather in NYC?\")]}\n", + "\n", + "print_stream(graph.stream(inputs, stream_mode=\"values\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3decf001-7228-4ed5-8779-2b9ed98a74ea", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/create-react-agent.ipynb b/examples/create-react-agent.ipynb index 07498ff90..eb3b44a06 100644 --- a/examples/create-react-agent.ipynb +++ b/examples/create-react-agent.ipynb @@ -5,7 +5,7 @@ "id": "992c4695-ec4f-428d-bd05-fb3b5fbd70f4", "metadata": {}, "source": [ - "# How to create a ReAct agent" + "# How to use the prebuilt ReAct agent" ] }, { @@ -16,7 +16,14 @@ "In this how-to we'll create a simple [ReAct](https://arxiv.org/abs/2210.03629) agent app that can check the weather. The app consists of an agent (LLM) and tools. As we interact with the app, we will first call the agent (LLM) to decide if we should use tools. Then we will run a loop: \n", "\n", "1. If the agent said to take an action (i.e. call tool), we'll run the tools and pass the results back to the agent\n", - "2. If the agent did not ask to run tools, we will finish (respond to the user)" + "2. If the agent did not ask to run tools, we will finish (respond to the user)\n", + "\n", + "
Prebuilt Agent
\n", + "\n", + "Please note that here will we use a prebuilt agent. One of the big benefits of LangGraph is that you can easily create your own agent architectures. So while it's fine to start here to build an agent quickly, we would strongly recommend learning how to build your own agent so that you can take full advantage of LangGraph.\n", + "
\n", + "