From b7ec64df8ddfb522c61c8a0585309d2b64964415 Mon Sep 17 00:00:00 2001 From: William FH <13333726+hinthornw@users.noreply.github.com> Date: Fri, 3 May 2024 15:48:10 -0700 Subject: [PATCH] [Notebooks Pt 1/N] Fixup some Tool Calling + Import formatting + prebuilt usage (#388) --- README.md | 55 +--- docs/docs/reference/graphs.md | 12 +- examples/agent_executor/base.ipynb | 3 +- .../force-calling-a-tool-first.ipynb | 3 +- examples/agent_executor/high-level.ipynb | 3 +- .../agent_executor/human-in-the-loop.ipynb | 3 +- .../agent_executor/managing-agent-steps.ipynb | 3 +- examples/async.ipynb | 3 +- .../anthropic.ipynb | 3 +- .../base.ipynb | 3 +- .../dynamically-returning-directly.ipynb | 3 +- .../force-calling-a-tool-first.ipynb | 3 +- .../human-in-the-loop.ipynb | 3 +- .../managing-agent-steps.ipynb | 3 +- .../prebuilt-tool-node.ipynb | 3 +- .../respond-in-format.ipynb | 3 +- examples/docs/quickstart.ipynb | 168 +++++----- examples/human-in-the-loop.ipynb | 3 +- .../multi-agent-collaboration.ipynb | 3 +- examples/persistence.ipynb | 3 +- examples/persistence_postgres.ipynb | 3 +- .../plan-and-execute/plan-and-execute.ipynb | 3 +- examples/rag/langgraph_agentic_rag.ipynb | 295 ++++++++---------- examples/state-model.ipynb | 3 +- examples/streaming-tokens.ipynb | 3 +- examples/time-travel.ipynb | 3 +- examples/visualization.ipynb | 9 +- 27 files changed, 306 insertions(+), 299 deletions(-) diff --git a/README.md b/README.md index 969b651cd..2affb96d7 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## Overview -[LangGraph](https://langchain-ai.github.io/langgraph/) is a library for building stateful, multi-actor applications with LLMs, built on top of (and intended to be used with) [LangChain](https://github.com/langchain-ai/langchain). +[LangGraph](https://langchain-ai.github.io/langgraph/) is a library for building stateful, multi-actor applications with LLMs. It extends the [LangChain Expression Language](https://python.langchain.com/docs/expression_language/) with the ability to coordinate multiple chains (or actors) across multiple steps of computation in a cyclic manner. It is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The current interface exposed is one inspired by [NetworkX](https://networkx.org/documentation/latest/). @@ -127,13 +127,11 @@ graph.add_node("oracle", chain) Now, let's move onto something a little bit less trivial. Because math can be difficult for LLMs, let's allow the LLM to conditionally call a `"multiply"` node using tool calling. We'll recreate our graph with an additional `"multiply"` that will take the result of the most recent message, if it is a tool call, and calculate the result. -We'll also bind the calculator to the OpenAI model as a tool to allow the model to optionally use the tool necessary to respond to the current state: +We'll also [bind](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html#langchain_openai.chat_models.base.ChatOpenAI.bind_tools) the calculator to the OpenAI model as a tool to allow the model to optionally use the tool necessary to respond to the current state: ```python -import json -from langchain_core.messages import ToolMessage from langchain_core.tools import tool -from langchain_core.utils.function_calling import convert_to_openai_tool +from langgraph.prebuilt import ToolNode @tool def multiply(first_number: int, second_number: int): @@ -141,36 +139,14 @@ def multiply(first_number: int, second_number: int): return first_number * second_number model = ChatOpenAI(temperature=0) -model_with_tools = model.bind(tools=[convert_to_openai_tool(multiply)]) +model_with_tools = model.bind_tools([multiply]) graph = MessageGraph() -def invoke_model(state: List[BaseMessage]): - return model_with_tools.invoke(state) +graph.add_node("oracle", model_with_tools) -graph.add_node("oracle", invoke_model) - -def invoke_tool(state: List[BaseMessage]): - tool_calls = state[-1].additional_kwargs.get("tool_calls", []) - multiply_call = None - - for tool_call in tool_calls: - if tool_call.get("function").get("name") == "multiply": - multiply_call = tool_call - - if multiply_call is None: - raise Exception("No adder input found.") - - res = multiply.invoke( - json.loads(multiply_call.get("function").get("arguments")) - ) - - return ToolMessage( - tool_call_id=multiply_call.get("id"), - content=res - ) - -graph.add_node("multiply", invoke_tool) +tool_node = ToolNode([multiply]) +graph.add_node("multiply", tool_node) graph.add_edge("multiply", END) @@ -289,13 +265,10 @@ model = ChatOpenAI(temperature=0, streaming=True) ``` After we've done this, we should make sure the model knows that it has these tools available to call. -We can do this by converting the LangChain tools into the format for OpenAI function calling, and then bind them to the model class. +We can do this by converting the LangChain tools into the format for OpenAI tool calling using the [`bind_tools()`](https://api.python.langchain.com/en/latest/chat_models/langchain_openai.chat_models.base.ChatOpenAI.html#langchain_openai.chat_models.base.ChatOpenAI.bind_tools) method. ```python -from langchain.tools.render import format_tool_to_openai_function - -functions = [format_tool_to_openai_function(t) for t in tools] -model = model.bind_functions(functions) +model = model.bind_tools(tools) ``` ### Define the agent state @@ -309,15 +282,17 @@ Whether to set or add is denoted by annotating the state object you construct th For this example, the state we will track will just be a list of messages. We want each node to just add messages to that list. Therefore, we will use a `TypedDict` with one key (`messages`) and annotate it so that the `messages` attribute is always added to with the second parameter (`operator.add`). +(Note: the state can be any [type](https://docs.python.org/3/library/stdtypes.html#type-objects), including [pydantic BaseModel's](https://docs.pydantic.dev/latest/api/base_model/)). ```python -from typing import TypedDict, Annotated, Sequence -import operator -from langchain_core.messages import BaseMessage +from typing import TypedDict, Annotated +from langgraph.graph.message import add_messages class AgentState(TypedDict): - messages: Annotated[Sequence[BaseMessage], operator.add] + # The `add_messages` function within the annotation defines + # *how* updates should be merged into the state. + messages: Annotated[list, add_messages] ``` You can think of the `MessageGraph` used in the initial example as a preconfigured version of this graph, where the state is directly an array of messages, diff --git a/docs/docs/reference/graphs.md b/docs/docs/reference/graphs.md index 70127f505..f52579368 100644 --- a/docs/docs/reference/graphs.md +++ b/docs/docs/reference/graphs.md @@ -9,4 +9,14 @@ handler: python members: - get_graph - - invoke \ No newline at end of file + - invoke + + +## MessageGraph + +::: langgraph.graph.message.MessageGraph + + +## add_messages + +::: ::: langgraph.graph.message.add_messages \ No newline at end of file diff --git a/examples/agent_executor/base.ipynb b/examples/agent_executor/base.ipynb index 6a40577ed..c9854acfc 100644 --- a/examples/agent_executor/base.ipynb +++ b/examples/agent_executor/base.ipynb @@ -26,7 +26,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai langchainhub tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai langchainhub tavily-python" ] }, { diff --git a/examples/agent_executor/force-calling-a-tool-first.ipynb b/examples/agent_executor/force-calling-a-tool-first.ipynb index 74ca4664d..8464331e1 100644 --- a/examples/agent_executor/force-calling-a-tool-first.ipynb +++ b/examples/agent_executor/force-calling-a-tool-first.ipynb @@ -31,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/agent_executor/high-level.ipynb b/examples/agent_executor/high-level.ipynb index ec3082221..0850f4d34 100644 --- a/examples/agent_executor/high-level.ipynb +++ b/examples/agent_executor/high-level.ipynb @@ -29,7 +29,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/agent_executor/human-in-the-loop.ipynb b/examples/agent_executor/human-in-the-loop.ipynb index ea3cf4705..6971d270c 100644 --- a/examples/agent_executor/human-in-the-loop.ipynb +++ b/examples/agent_executor/human-in-the-loop.ipynb @@ -31,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/agent_executor/managing-agent-steps.ipynb b/examples/agent_executor/managing-agent-steps.ipynb index 31847fe67..a97b8e7c5 100644 --- a/examples/agent_executor/managing-agent-steps.ipynb +++ b/examples/agent_executor/managing-agent-steps.ipynb @@ -31,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/async.ipynb b/examples/async.ipynb index 7c9381db9..980f2b334 100644 --- a/examples/async.ipynb +++ b/examples/async.ipynb @@ -37,7 +37,8 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/anthropic.ipynb b/examples/chat_agent_executor_with_function_calling/anthropic.ipynb index 68cb0af95..3d9441dca 100644 --- a/examples/chat_agent_executor_with_function_calling/anthropic.ipynb +++ b/examples/chat_agent_executor_with_function_calling/anthropic.ipynb @@ -28,7 +28,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_anthropic tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_anthropic tavily-python" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/base.ipynb b/examples/chat_agent_executor_with_function_calling/base.ipynb index a76a06ae3..d086f9c7e 100644 --- a/examples/chat_agent_executor_with_function_calling/base.ipynb +++ b/examples/chat_agent_executor_with_function_calling/base.ipynb @@ -27,7 +27,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb b/examples/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb index b83c06c30..46944b781 100644 --- a/examples/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb +++ b/examples/chat_agent_executor_with_function_calling/dynamically-returning-directly.ipynb @@ -31,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\",\n", + " \"%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb b/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb index 56cca9ce1..58c56c3a9 100644 --- a/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb +++ b/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb @@ -31,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb b/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb index 91b2af705..7587752a5 100644 --- a/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb +++ b/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb @@ -31,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb b/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb index e21431101..70e9a18ae 100644 --- a/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb +++ b/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb @@ -31,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb b/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb index f85dcc1c4..6ef8c198d 100644 --- a/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb +++ b/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb @@ -28,7 +28,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/chat_agent_executor_with_function_calling/respond-in-format.ipynb b/examples/chat_agent_executor_with_function_calling/respond-in-format.ipynb index 2e64f0fba..4c3051551 100644 --- a/examples/chat_agent_executor_with_function_calling/respond-in-format.ipynb +++ b/examples/chat_agent_executor_with_function_calling/respond-in-format.ipynb @@ -31,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/docs/quickstart.ipynb b/examples/docs/quickstart.ipynb index db9f9a3a4..6fd526e63 100644 --- a/examples/docs/quickstart.ipynb +++ b/examples/docs/quickstart.ipynb @@ -1,22 +1,10 @@ { "cells": [ { - "cell_type": "code", - "execution_count": 1, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" - ] - } - ], "source": [ - "!pip install --quiet -U langchain_openai" + "# Quickstart\n" ] }, { @@ -24,6 +12,16 @@ "execution_count": 2, "metadata": {}, "outputs": [], + "source": [ + "%%capture --no-stderr\",\n", + " \"%pip install install --quiet -U langgraph langchain_openai" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], "source": [ "import os\n", "import getpass\n", @@ -33,12 +31,10 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "from typing import List\n", - "\n", "from langchain_openai import ChatOpenAI\n", "from langchain_core.messages import BaseMessage, HumanMessage\n", "from langgraph.graph import END, MessageGraph\n", @@ -47,10 +43,7 @@ "\n", "graph = MessageGraph()\n", "\n", - "def invoke_model(state: List[BaseMessage]):\n", - " return model.invoke(state)\n", - "\n", - "graph.add_node(\"oracle\", invoke_model)\n", + "graph.add_node(\"oracle\", model)\n", "graph.add_edge(\"oracle\", END)\n", "\n", "graph.set_entry_point(\"oracle\")\n", @@ -60,16 +53,43 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "try:\n", + " display(Image(runnable.get_graph(xray=True).draw_mermaid_png()))\n", + "except:\n", + " # This requires some extra dependencies and is optional\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[HumanMessage(content='What is 1 + 1?'), AIMessage(content='1 + 1 equals 2.')]" + "[HumanMessage(content='What is 1 + 1?', id='125ef1e2-d3ca-48b5-b35d-f592f520c01c'),\n", + " AIMessage(content='1 + 1 equals 2.', response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 15, 'total_tokens': 23}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_a450710239', 'finish_reason': 'stop', 'logprobs': None}, id='run-13375378-12e3-404f-a6b8-dded28584b06-0')]" ] }, - "execution_count": 12, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -80,51 +100,29 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "import json\n", - "from langchain_core.messages import ToolMessage\n", "from langchain_core.tools import tool\n", - "from langchain_core.utils.function_calling import convert_to_openai_tool\n", + "from langgraph.prebuilt import ToolNode\n", + "\n", "\n", "@tool\n", "def multiply(first_number: int, second_number: int):\n", " \"\"\"Multiplies two numbers together.\"\"\"\n", " return first_number * second_number\n", "\n", + "\n", "model = ChatOpenAI(temperature=0)\n", - "model_with_tools = model.bind(tools=[convert_to_openai_tool(multiply)])\n", + "model_with_tools = model.bind_tools(tools=[multiply])\n", "\n", "graph = MessageGraph()\n", "\n", - "def invoke_model(state: List[BaseMessage]):\n", - " return model_with_tools.invoke(state)\n", + "graph.add_node(\"oracle\", model_with_tools)\n", "\n", - "graph.add_node(\"oracle\", invoke_model)\n", - "\n", - "def invoke_tool(state: List[BaseMessage]):\n", - " tool_calls = state[-1].additional_kwargs.get(\"tool_calls\", [])\n", - " multiply_call = None\n", - "\n", - " for tool_call in tool_calls:\n", - " if tool_call.get(\"function\").get(\"name\") == \"multiply\":\n", - " multiply_call = tool_call\n", - "\n", - " if multiply_call is None:\n", - " raise Exception(\"No adder input found.\")\n", - "\n", - " res = multiply.invoke(\n", - " json.loads(multiply_call.get(\"function\").get(\"arguments\"))\n", - " )\n", - "\n", - " return ToolMessage(\n", - " tool_call_id=multiply_call.get(\"id\"),\n", - " content=res\n", - " )\n", - "\n", - "graph.add_node(\"multiply\", invoke_tool)\n", + "tool_node = ToolNode([multiply])\n", + "graph.add_node(\"multiply\", tool_node)\n", "\n", "graph.add_edge(\"multiply\", END)\n", "\n", @@ -133,60 +131,84 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "def router(state: List[BaseMessage]):\n", + "from typing import Literal\n", + "\n", + "\n", + "def router(state: list[BaseMessage]) -> Literal[\"multiply\", \"__end__\"]:\n", " tool_calls = state[-1].additional_kwargs.get(\"tool_calls\", [])\n", " if len(tool_calls):\n", " return \"multiply\"\n", " else:\n", - " return \"end\"\n", + " return END\n", "\n", - "graph.add_conditional_edges(\"oracle\", router, {\n", - " \"multiply\": \"multiply\",\n", - " \"end\": END,\n", - "})" + "\n", + "graph.add_conditional_edges(\"oracle\", router)\n", + "runnable = graph.compile()" ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAEuAIkDASIAAhEBAxEB/8QAHQABAAIDAQEBAQAAAAAAAAAAAAYHBAUIAwkBAv/EAFEQAAEDBAADAgkGCAgNBQAAAAECAwQABQYRBxIhEzEIFBUWIkFVlNEXN1FhdZMkMkJWcYGy0gkjNVJzkbG0MzY4Q1NUYnKWo7PB1DSEkpWi/8QAGwEBAAIDAQEAAAAAAAAAAAAAAAEDAgQFBgf/xAAzEQACAQICBwUIAgMAAAAAAAAAAQIDEQSREhMVITFBURRSYaHRBUJiY3Gx4fAiMzKywf/aAAwDAQACEQMRAD8A+qdKUoBSlKAV4ypjEFhT0l9uOynW3HVhKR9HU17VCuLrSHsTYbcQlxtVzghSVDYI8Zb6EVnBJySZnCOlJR6kg86rJ7Yge9I+NPOqye2IHvSPjVeeb9r9mw/uE/Cnm/a/ZsP7hPwrjbVw/clmjrbO+LyLD86rJ7Yge9I+NPOqye2IHvSPjVeeb9r9mw/uE/Cnm/a/ZsP7hPwptXD9yWaGzvi8iw/Oqye2IHvSPjTzqsntiB70j41Xnm/a/ZsP7hPwp5v2v2bD+4T8KbVw/clmhs74vIsPzqsntiB70j4086rJ7Yge9I+NV55v2v2bD+4T8Keb9r9mw/uE/Cm1cP3JZobO+LyLD86rJ7Yge9I+NPOqye2IHvSPjVeeb9r9mw/uE/Cnm/a/ZsP7hPwptXD9yWaGzvi8i1WXm5DSHWlpdaWApK0HaVD1EEd9f3UV4VgDhrjAHQeTmP2BUqrtVI6E5Q6M47VnYUpSqyBSlKAVDOLH+K8b7Ug/3lupnUM4sf4rxvtSD/eW6zh/ki2l/ZH6o1VKwL3frZjNseuV4uMS1W9nl7SXOfSy0jZCRzLUQBskAbPeRUXTxy4cLOk8QMWJ0ToXqN3Dv/Lr58oylvSPWOSXFkpvl5iY5Zbhdp7vYwYEdyVId0TyNoSVKOh1OgDVP5l4Qs1jgtk2ZWLEr5Cfgwm5UI3qI2hp5DoJQ8OV70kADahsKAKfR61MpPF/Br5FkW+05TjGQ3OS0tqNaUXiMozHCk8rOuY/jH0e49/caqC38HswvOB8S8djWV3DMeu9pTHs+O3C6omojzNLLimlIUoNMq/iwEb6HZCU91bNKEVvqK29cehRUlJ7oeJcMridLg4nBvDuEZS7KkvdgLRHjMOy0aBPOvleLaUEJ6Er9YHedVppfhF41Fxax3sQLy+Ltd1WJFvahjxxiakOczLrZUOVQU0pPQnqUnu2RGM6tOa8Q7ViEq64JKXbYMt0XjERd4wVOHYpDLpWHA2ttDnMezUob6Eg61Ufwvg9lVmj45GcxiPaI8DiA9fzGhzGnGI8F2K8E8h2knkW4lspCQdjYBT1rNU6ejeT3/X6mLnO9lw+hM5XHC/N8WsfxxOD3xEC4Wd6c8wtEXxppwSGmwtR8Z5A2hKiVa2r00a31Aueqp4gWXJrTxbxzM7DYDk0Zm1S7RLhszGo7rXaOsuodBdUlKk7aIIB2NggGpC9xu4dRnnGXs+xhp1tRQttd5jBSSDoggr6GqZx0lFwWRZGWi2pMmtKg5458NwfnBxX/wC6jfv1Mocxi4RGJUV9uTFfQl1p9lYWhxChtKkqHQggggiqXGUeKLVJPgyS8LPm2xj7OY/YFSmotws+bbGPs5j9gVKa+i4j+6f1f3PIS4sUpSqDEUpSgFQzix/ivG+1IP8AeW6mdarJcci5ValQJinm2i426FsL5FpUhYWkg/pArODSkrmcJaMlJ8iFONodSUrSFpPeFDYrx8Qi/wCrM/8AwFbr5KYPti9++n4U+SmD7Yvfvp+Fee2R81ZM7nb6XRmnTCjoUFJYaSodQQgAivatl8lMH2xe/fT8KfJTB9sXv30/Cmx/mrJjaFLozW0qFeEXYXuGnBDMcosl7uzd1tcFT8dT0rnQFhQHVOuvfUww/h3HvWJWS4SbzeTJlwWH3SmZoFam0qOhrp1Jpsf5qyZO0KXRnrXgYEYnZjtE/wC4K3PyUwfbF799Pwp8lMH2xe/fT8KbI+asmRtCl0ZpRb4oH/pmfuxXulISAAAAOgA9VbP5KYPti9++n4U+SmD7Yvfvp+FNkfNWTHb6XRmbws+bbGPs5j9gVKawbHZ4+P2eFbInMIsRlLDXOrmVypGhs+s1nV6OrJTqSkuDbOC3d3FKUqogUpSgFKUoBSlKAUpSgKY8Mr/Je4j/AGWr9pNWLw5+b3F/suL/ANJNV14ZX+S9xH+y1ftJqxeHPze4v9lxf+kmgJFSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgKY8Mr/Je4j/AGWr9pNWLw5+b3F/suL/ANJNUZ4YfGHApXAPiRjrOb449kAhORTaW7tHVLDyVgKb7IL5ucEEFOtjRq0ODHEjEstw/HrfY8pst6uDNqZLsW33Bl91vs22kucyEKJHKXEA7HQrSD3igLFpSlAKUpQClKUApSlAKUpQClKUApStHkmYQMZ7Jt8PSprwJZhREc7zgHedbASn/aUUp3ob2RWUYubtElJydkbylVy5nmSyTzMWi2wm/UJMtbrn6wlAAP1BR/TXn55Zd/q9l/req3VdZLP0NpYWs/dPn1/CZ8CxhHEuJn1rjdnZ8m2mXyD0W56B6R+rtEAK+tSXDVl/wV3BmTb4eQ8T5yXGUTmlWa2pPQOtBaFvufWOdttIP0ocFdBcZ8QncdsAm4jksa2Jt8lbbofhrWh9hxCgUrbUpKgFd46g7ClD11usEReeHGG2bGLJCszNqtUZEVhK1OlRSkfjKI1tSjtROupJNNUu8syeyVuhdFKrXzyy7/V7L/W9XszxAyCIsGZY4k1nfU2+WUugfUhxISf1rHwaq/CSz9SHhay90sSlavH8lgZNFW9BdUVNnkdYdQW3WVfzVoPUH6PUR1BI61tKqlFxdpLearTTsxSlKxIFKUoBSlKAUpSgNVlN+TjVhl3BTfbLbAS0zvXauqUEto36uZakjf11XsOM42XZEp0yZ8g88h9X5SvoT9CBshKfUPrJJ3vFVauyxpr/ADTt3SHPo0mO+tP/AO0IrV1bP+FOKXvb/wDiXkzs4GC0XPmKVz/fWbnn+bcVPG8uvONsYk0w1bGLVOVFbaKogkGS8kdHdqUQAvaeVBGt7Nafh5cb3xkz6yPXq/3u2Qn8EtV2kW61z3Ybbktx5/bv8WQUghPcCOYcoOwkCtU3tbvtY6YpXKWRZdfhmMHMsbl5CMeVl7Fmefud9JiyUqlCO82zACCkNg8wSsqSsFO9Gp1hFqn8XMozS8XXK7/bTZcikWmFa7RcFRWI7Mfl5VONp6Oqc2Vkr2NKAAFLEKrd2SLesGVWvJ3Lqi2SvGVWuau3TB2a0dlIQlKlI9IDegtJ2Njr399bWuWbpmV2wnAOLcuyOpiz5PEI2/xxbgbEVDwhtqcKylQRoKICilXKSDo60bD4TYfxAxjMn13iQpOMOwVJXEm5I9eXxKC0lDiFux21ISU9oFJ2Rvl0BqgjVu7WLYlLk2x9N2tyVKuEZO+xSrlElsdS0r1HfXlJ/FVo/SDZ9suMe8W2JPiOB2LKaQ+0sflIUAUn+oiq7rfcJlqVg0VJ/FakzGW/6NEp1KP1cqU1tL+VK75NLO/oaGOglaaJhSlKqOQKUpQClKUApSlAR3PrG/fcbdRETzz4ziJcZO9c7jagrk36ucBSN/Qo1DYUxufFbkMklCx3HoUnuKSPUQdgj1EEValQ3JMHeeluXGxvMRZjh5n4r6SGJJ/nEpG0L/2wFbHek9CLVapHQbs1w9DoYXEKk9GXBlW5jwVwzP7r5SvtlTMmlpLDriJDzIfaBJCHUtrSHUgk+isKHWt7Cw2zW7InL7FgoYubkFq2l1tSgkRm1KU22Eb5QAVq6gb663oCsDOc7b4W2F685hbpFktjK0trmlxp1kqUdJCSlfMST3ApB+rVbVi7zZLDbzeOXtTbiQtJMTWwRsdCajs9XkvNHVVSjxTRDrh4PnD+6XGZOk4+FyJUnx1fLLfShEjmCy82gOBLThUNlaAlR67PU7ysg4HYRlGRu364WMLuj3J27rEp5hMjk/ELqG1pS7rQA5weg1Uq8oXD82737qP3qeULh+bd791H71Oz1eg06HVeRpJHCnEpczIpL9jjvLyJtLV1Q4VKalhI0kqbJ5ebX5QAV0HXoK/MG4VYxw4XKXj9vciOSUIbdcflvyVlCN8iAp1aiEjmVpI0Bs1mZDmHmrZJ14u1lvEK2QWlPyZK4m0tNpG1KOiToDqa98Yvc7O7NBuuN2hydbJzYej3GRIaajLQe5WwpS/6kHuO9U7PU5rzXqNZRW+6M65zHIkb8Ha8YmOqDUaPvRddP4qf0esn1AE9wqxcXsicbx632xLheMZlLa3T3uL16Sz9albP661eLYZ5If8AH7i+i4XUpKUuJb5G46T3paSSSN+tRJKvqGkiUVLajHQi79f3wOPiq6rO0eCFKUqo0hSlKAUpSgFKUoBWqynKrPhFgmXu/XGPabTDRzvzJSwhtsbAGyfWSQAO8kgCs24XGJaYT0ydJZhRGU87siQ4G220/SpR6Afpquzj974kZLldnz7ErDIwOJJirsaXV+MvS3EacU84k+ikBRSkJ0D6KweZJ2QM6wWPJr1luTTskulmvOES/FVWC0x4oc7NKAHC+64oektSyCNbACEKGjU+r8A0NDur9oBSlKA/l1pDza23EJcbWClSFDYUD3giq5uqr3w9y+RfZl/stq4SQLHyPW56OGHLe80r0VtrSNFtSDylJ7uRISOpqyKx7hb4t2gSYM2O1LhyW1MvR30BbbqFDSkqSehBBIINAfluuMW72+NOgyWpkKS2l5iQwsLbdQobSpKh0IIIIIrJqtbW3feHmYxLFAsNitXCCBYytE9uR2DsB9pXVC0H0S2UHfN01yrKlb0DY7TqHm0ONrS42sBSVpOwoHuINAf3SlKAUpSgFKUoBSlKAqLIbB8vs7L8KzXCJkLCbZLhmJPfmls3Z1BDqylts/4EHkGyo72egUk8tssMNRWG2WW0MstpCENtpCUpSBoAAdwA9VQPgdaptmwRMa4ZojPpHjslflht3tAUl1RDW+ZX+DHo636u4VYFAKUpQClKUApSlAazJsatmZY9cbHeYbdwtVwYXGkxnR6Ljahoj6R9RHUHqOtQXDRO4b5VYuG1rxCccGh2MGFknjnbpadbVylh4L9JJKSkpIJ31AACTy2bVf8AE+1TblfsEciZojFG4t5Q8/DW7yG8I5D+CgcyeYnv1pXd3UBYFKUoBSlKAUrzfktRWy486hlsd63FBI/rNa45ZYwf5Zt/vSPjWahKXBA2tck+G54T/Ezwabvj0rGrJj9wxa6MKaVKucaQ663MQolSCW3kJCS2UFII2Slzr06dQedlj9s2/wB6R8aq7wmMFxvjzwbv2KKu9rFxW34zbHnJTemZbYJbO99AeqCf5q1VOrn3WTZnE3gk+GZnTmb4pw0sGHY2zZ7reAp9MVqWp5ltx4OSnQpySr8VvtVDfQaB6gaP1Ar5/fwbXBNrh+rIM6zFLdlvjpNrt0K5KDLrTI0p13kUdjnISlJ0OiV94VXdfnZY/bNv96R8aaufdYszbUrU+dlj9s2/3pHxrLh3aDcSREmR5RA2exdSv+w1DhJK7QszLpSlYEClKUAqqeNUrCY+UcMU5ZDmyri7kTaLCuISEtTeRXKpzShtOt94P6KtaoPxFuuUW69YY3j2PRb3Ck3ZLV2kSNc0CLynb6NqHpA6Hr7+6gJxSlKAVEcuy5+JL8k2nkNwKQt+S4OZuIg93T8pxX5Ke4AFSvyUrlUh9EWO684dNtpK1H6gNmqhxpbku1N3F/Rl3I+OvqGztSwCB19SU8qR9SRVsbRi6j5bl9fx6cjdwtFVZ/y4ILxqDKe8YuDZu8sjRk3DTyz130BGkj6kgD6q9vINs9nRPuE/CtBn/EmBw7NkRMg3G4v3iYYESPbWUuuLe7JbgBBUnQIbI33AkEkJ2oQtPhO2NEa4SZWNZRAiWqWmFeJEmC2EWtwlIHbEOnmGlpVtrnASoE62KrdWpLjJnb0oQ3cC0/INs9nRPuE/CnkG2ezon3CfhUdPFC1B/OWvF5nNiCUqnnkRp0GMmSOy9Lr6CgPS5ev1daiuO8UZ+QcS5wiJlS8fXhsG/Q7UlpoSFOvOv9ASR6akoQnlK+UEd46msdZPqyXOK3FmeQbZ7OifcJ+FPINs9nRPuE/CqCgeENfLt4Ns3NLlZb1YpyIwWbrboMSQ2nmWsdsyy5I9JCOUAhwpO1DQPUiw7/xsjWDJ7tjzGM5HkM+0RI8yY5aYrLiEtuhfKoczqST/ABavRA2fyQrR01k+rIVSL3k68g2z2dE+4T8K8JGKWaUQpy1xCtPVLiWUpWk/SlQGwfrBqt7ZxcXkXE62Kskty7YnNw1++sRIzKO0feTJbSkpKgFBfKpSeQqA2euiNiS2/jBYbra8HnQ0ypKcwWEW9ptCe0QOxW8tTo5vRCEoIVokhWho1Kq1Iu6k8yVKLJ1Z8km4k4lMyTIuVkJ0pT57R6GP53P+Mtsevm2od+yBoWUhaXEJUlQUlQ2FA7BFVmpIUCCAQehB9dbzhZMK7BJtqjvyRLXBR39G+VLjSev0NuIT+qrr62Lk+K811/eNzlYyhGFpxJlSlKpOWKr/AIn2qbcr9gjkTNEYo3FvKHn4a3eQ3hHIfwUDmTzE9+tK7u6rAqqeNUrCY+UcMU5ZDmyri7kTaLCuISEtTeRXKpzShtOt94P6KAtalKUBjXGIJ9vlRSdB9pTZP0bBH/eqlxVxS8ctocSpDrbCWXUKGilaByrB/QpJFXHVdZXYXcduMm6xGFPWqWvtZjbQJXGd0AXQn1tq16WuqVelohSim6K04OmuPFen70sdDB1VTm1LmVrxOxW6ZDlXDiZb4vjEa0X1UyavtEJ7JnxR9vm0ogq9JaRpOz17tbqvsw4V5RdOHnHO2RbX2s7JbmqRamvGGh4y2Y0ZG9lWkek2saWQen1ir+jSmZsdt+O82+w4OZDjSgpKh9II6GvStXenZnZdNSv4+lih8uxTNLTf+K7dkxgX6LmcRsxJiZ7LCIrqYQjKQ8lagr8gKBQFA82iU99ZvDrBMnxDP8XuEi1Ictr2GQLHPeTKb54EqN2iyFJ36aVFzlBQToj6OtXXSoI1ave5zUzw8zk+DJknDJ3Fim5Q7e7CgTUT46mbkS8opKAVgt+iQT2gT1rZP5Dk2PeEBxIONYkvKpLtos6SkT2YqWFgSuQrLhG0nat8uyOXuO66DrEYtEGNcpVwZhR2p8pCG5EpDSUuvJRvkStYG1BPMrQJ6cx130uY6q1rPh+fUozhlwgyXhXk/D54RWLxEj49Js12fYfS34o87JTKLiUr0Vt8wUgAde46FYPAvFhI4y5lIizY9yxHF5D8awKjLC2mnZxbky0JIJG21aR07gsiujO+sOz2S3Y7bmoFqgRbZBaGm4sNlLTSP0JSAB+qhKpJNW4IzK2/CuOow77OIPZzbmtTexr0W222D+rmaWf11HowfySa5bbQ4lTiFckuYOqIg9Y33FzXcj9BOh32babXGslsiwIbfZRYzaWm0b3pIGhs+s/X6624p04O/GX24+ljn42qmlTRl0pSqTkCoPxFuuUW69YY3j2PRb3Ck3ZLV2kSNc0CLynb6NqHpA6Hr7+6pxVf8T7VNuV+wRyJmiMUbi3lDz8NbvIbwjkP4KBzJ5ie/Wld3dQFgUpSgFKUoCMXThxYbpJck+LOwZTh2t6BIcjlZ3slQQQFHfrINa/5J7d7WvQ/98fhU3pV6r1Fu0ixVZx3KTIR8k9v9rXv34/CnyT2/wBrXv34/CpvSp19TqZa6p3mc1eCxCl8WeFCb/kF6ujtxNznReZiT2aeRp9SEdAO/QHWre+Se3+1r378fhVW+Ab8wLf23dP705XRNNfU6jXVO8yEfJPb/a179+Pwr0b4T2MqBlOXK4JH+bk3B0tn9KEqCVfoIIqZ0qNfV5SIdWo/eZ4QYMa2RGosOO1EjNDlbZYQEIQPoCR0Fe9KVS227sqFKUqAKqnjVKwmPlHDFOWQ5sq4u5E2iwriEhLU3kVyqc0obTrfeD+irWqD8RbrlFuvWGN49j0W9wpN2S1dpEjXNAi8p2+jah6QOh6+/uoCcUpSgFKUoBSlKAUpSgOdvAN+YFv7bun96cromuWuBd8f8GzOn+DeYJQxbbvOkXDE8jAKWLiHXCtcVzZ0h9KldB3K2AO9PN1LQClKUApSlAKUpQCq/wCJ9qm3K/YI5EzRGKNxbyh5+Gt3kN4RyH8FA5k8xPfrSu7uqwKqeexbeLfFddqu+H3hqPgkuNcrdkEhSmI0iapBJS0nYLoShQ2rqnZIOtJ5gLYpSlAKUpQClKUApSlAQvi7wlsHGrCJmM5CwpcZ3TjEpk8r8N9P4jzSvyVpP9YJB2CRVYcC+Ld+x7LHOD3FF9PnvBaLlpvRHK1kUJO+V1BP+eSAedHf0J66VXQdcg/wi/EbC8S4e2+DdIdycz0OpnYxNgNOMKhPJV6T4l8nJpHKnnaSSs8zWwkKS4kDq3zitPnB5B8pw/Lnivj3kzxhHjPi/Pydt2W+bs+f0ebWt9N7rY18cvBM463pHhhWDK8ou79ynZJLVbbjLkEbd7dPZtjp0ShK+x0kAJSlAAAAGvsbQClKUArXQsjtNyu9ytMS6QpV0tnZePQWJCFvxO0SVN9qgHmRzpBKeYDYGxus9a0tIUtaghCRsqUdAD6TXw9yzwisrb495hxFxK+zLJPu0qUhp+OQFGIs8qGlAghQCEt62OikJUNKSCAPrHl+Q2/jnNznhXYr/f8AGLvaERPKV7tkYthsOnnUw28ofjlsdSNdFggq0oVbcKKIMNiMlx11LLaWw48srWrQ1tSj1JPrJ76qnwaOPth8ITh8xfLQzPjzmEts3VqbFUns5QTpSQ+G0tPHSQrbfUJW3zJbKgkW5QClKUApSlAKUpQClKUBW7+dZJIu11ZgxrWmNDlrjJMguFauUDqddPXWuyCXecstT9svdmxm7218adiTmHHmlj60q2DX5bP5XyT7Ve/sTWyrUxOMnRrSpxSsvA8jivaWIpV5wi9yfRHKWaeAZjd+vDN3x0N4RcGVh5vyTIdWyHAoKC+R0KIII6BKkgfRXVgy7LwP8DZD998a/aVq7Qq9Fkau1sV1WSHndl/+hsn/ADvjTzuy/wD0Nk/53xpSm0KvRZDa2K6rJGnzGdmGW4je7GH7VbTc4L8LxyP2vaMdo2pHOnfTmTzbH1iueOGHgNYhw8kNzJ9ot2Y3BtXOly+uurZSdd3YoCUKH1LC66JtmVWu83y82eHK7a42dbSJzPZrT2KnWw42OYgBW0kH0SdevRrbVO0Ky5LIl+1cWuL8kY8C/ZLaobUSFAx6HFaHK2ww26hCB9ASOgFbGw5pfn8pt9suce3dhLQ6oLiFzmSUJB/K6aO6xqxrf84OOf0cr9hNbWFxc69XVzStZ8uibNzBe0cRXxEac3ud+S6Fo0pSto9WKUpQClKUApSlAVLbP5XyT7Ve/sTWyrW2z+V8k+1Xv7E1H73b+IL10kLtF+xqJbSR2LM2ySH3kjQ3zLTLQFdd9yR/3rjY1XxMz5/jEniql3beyZVz5xXueW5bxsRhVlU8i3wrE3djHjZA7ZnJDi31tlfatMOLWlAQkcg5RtezzdALENr4perJcQ/4dlf+dX5duElvz+Db3M9jQbxeoKl9hcLOmRbVNoV3pSpL6nACNbHPo/RWrFqLuyqk4UpaUnfP8fcq1NozhWVcLcSy7JrhGcls3zxxVlui0rlMNmOqMl15KGypxKVAFxKUKPpaI5lb1VvyzI7k3ZeH68mucSLKza62J2/+Mfh/icVtbzbPbHqHF+ijtPxtJPrNdA2/htjdqk46/EtiWHMfYejWwodc1HbdCQ4Nc2lc3InqrZ6dO81gXXgzhl7slxtE6yIkQLhcl3h9Cn3QvxxR2XkLCuZtX+4U62da2az1kea/d5csRDmvJcbvf5rIg/AvH04vxW4vW1E+4XJDMu2csi6SVSJBBhJVpTiuqtb0CdnQHU1dtV5B4UpwJiYrh55OsU64vNu3B+8NyriJHIjkSeslCgrWvS5jv1gnrX9+S+Kf5zYf/wAOyv8AzqwlaTvcoqONWWkpdOPgkvEsCsa3/ODjn9HK/YTUexmFm0e4qVkN5sE+B2ZAatlpfiu8+xo865Lg1rfTl+jr9Mht/wA4OOf0cr9hNbmA3YhfSX+rNv2crYuFnfj9mWjSlK657sUpSgFKUoBSlKAqFSLlab3fkqsN0kIeuDj7bsdgLQtCgnRB39VenlKf+bd791H71W1SsalKhVk5zhvficqr7Mw9WbqSvd+JUvlKf+bd791H71PKU/8ANu9+6j96rapVfZsN3HmVbIwvjn+CpfKU/wDNu9+6j96nlKf+bd791H71W1SnZsN3HmNkYXxz/BUvlKf+bd791H71PKU/82737qP3qtqlOzYbuPMbIwvjn+CpfKU/82737qP3q98eauNwzizSVWa4wo0ZuR2j0tkISOZIAHeatOlW06dGlLThHfv59VYuo+zqFCoqkL3XiKUpUnUP/9k=", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "try:\n", + " display(Image(runnable.get_graph(xray=True).draw_mermaid_png()))\n", + "except:\n", + " # This requires some extra dependencies and is optional\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[HumanMessage(content='What is 123 * 456?'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_cKnzV4f7sEjnuetuyW8glC7u', 'function': {'arguments': '{\"first_number\":123,\"second_number\":456}', 'name': 'multiply'}, 'type': 'function'}]}),\n", - " ToolMessage(content='56088', tool_call_id='call_cKnzV4f7sEjnuetuyW8glC7u')]" + "[HumanMessage(content='What is 123 * 456?', id='7aec9537-0618-4b77-8cbc-527283fc1c19'),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_n0CYzEKaI5j9XGyd10wCEesR', 'function': {'arguments': '{\"first_number\":123,\"second_number\":456}', 'name': 'multiply'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 69, 'total_tokens': 88}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_a450710239', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-0037ebc2-4418-4498-b81a-9b6854ca69ac-0', tool_calls=[{'name': 'multiply', 'args': {'first_number': 123, 'second_number': 456}, 'id': 'call_n0CYzEKaI5j9XGyd10wCEesR'}]),\n", + " ToolMessage(content='56088', name='multiply', id='5a663378-2893-4059-ae7a-947ab0bc1ef4', tool_call_id='call_n0CYzEKaI5j9XGyd10wCEesR')]" ] }, - "execution_count": 22, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "runnable = graph.compile()\n", - "\n", "runnable.invoke(HumanMessage(\"What is 123 * 456?\"))" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[HumanMessage(content='What is your name?'),\n", - " AIMessage(content='My name is Assistant. How can I assist you today?')]" + "[HumanMessage(content='What is your name?', id='2913f6c4-8f3e-4273-bfb9-01a6bae7251c'),\n", + " AIMessage(content='My name is Assistant. How can I assist you today?', response_metadata={'token_usage': {'completion_tokens': 13, 'prompt_tokens': 66, 'total_tokens': 79}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_3b956da36b', 'finish_reason': 'stop', 'logprobs': None}, id='run-8b2e0a09-3365-4651-b256-72a9d3a8fb75-0')]" ] }, - "execution_count": 24, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -219,7 +241,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.11.2" } }, "nbformat": 4, diff --git a/examples/human-in-the-loop.ipynb b/examples/human-in-the-loop.ipynb index f13c24a0f..ce8ef1702 100644 --- a/examples/human-in-the-loop.ipynb +++ b/examples/human-in-the-loop.ipynb @@ -43,7 +43,8 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/multi_agent/multi-agent-collaboration.ipynb b/examples/multi_agent/multi-agent-collaboration.ipynb index 6fee1e7b2..03f023a29 100644 --- a/examples/multi_agent/multi-agent-collaboration.ipynb +++ b/examples/multi_agent/multi-agent-collaboration.ipynb @@ -27,7 +27,8 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install -U langchain langchain_openai langsmith pandas langchain_experimental matplotlib langgraph langchain_core" + "%%capture --no-stderr\n", + "%pip install install -U langchain langchain_openai langsmith pandas langchain_experimental matplotlib langgraph langchain_core" ] }, { diff --git a/examples/persistence.ipynb b/examples/persistence.ipynb index 2cb1ee988..f27af88e7 100644 --- a/examples/persistence.ipynb +++ b/examples/persistence.ipynb @@ -37,7 +37,8 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/persistence_postgres.ipynb b/examples/persistence_postgres.ipynb index 706b43780..8add3b956 100644 --- a/examples/persistence_postgres.ipynb +++ b/examples/persistence_postgres.ipynb @@ -41,7 +41,8 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python langchain-postgres" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python langchain-postgres" ] }, { diff --git a/examples/plan-and-execute/plan-and-execute.ipynb b/examples/plan-and-execute/plan-and-execute.ipynb index 6c312d149..085c9ad27 100644 --- a/examples/plan-and-execute/plan-and-execute.ipynb +++ b/examples/plan-and-execute/plan-and-execute.ipynb @@ -56,7 +56,8 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/rag/langgraph_agentic_rag.ipynb b/examples/rag/langgraph_agentic_rag.ipynb index b45d6b469..40f6c3da3 100644 --- a/examples/rag/langgraph_agentic_rag.ipynb +++ b/examples/rag/langgraph_agentic_rag.ipynb @@ -1,15 +1,5 @@ { "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "625868e8-46cb-4232-99de-e95aee53c3a3", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph" - ] - }, { "cell_type": "markdown", "id": "425fb020-e864-40ce-a31f-8da40c73d14b", @@ -21,8 +11,47 @@ "\n", "To implement a retrieval agent, we simple need to give an LLM access to a retriever tool.\n", "\n", - "We can incorperate this into [LangGraph](https://python.langchain.com/docs/langgraph).\n", + "We can incorperate this into [LangGraph](https://python.langchain.com/docs/langgraph)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "969fb438", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\",\n", + " \"%pip install\n", + "%pip install -U --quiet langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e4958a8c", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import getpass\n", "\n", + "def _set_env(key: str):\n", + " if key not in os.environ:\n", + " os.environ[key] = getpass.getpass(f\"{key}:\")\n", + "\n", + "_set_env(\"OPENAI_API_KEY\")\n", + "\n", + "# (Optional) For tracing\n", + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "_set_env(\"LANGCHAIN_API_KEY\")" + ] + }, + { + "cell_type": "markdown", + "id": "c74e4532", + "metadata": {}, + "source": [ "## Retriever\n", "\n", "First, we index 3 blog posts." @@ -30,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "e50c9efe-4abe-42fa-b35a-05eeeede9ec6", "metadata": {}, "outputs": [], @@ -73,20 +102,20 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "0b97bdd8-d7e3-444d-ac96-5ef4725f9048", "metadata": {}, "outputs": [], "source": [ "from langchain.tools.retriever import create_retriever_tool\n", "\n", - "tool = create_retriever_tool(\n", + "retriever_tool = create_retriever_tool(\n", " retriever,\n", " \"retrieve_blog_posts\",\n", " \"Search and return information about Lilian Weng blog posts on LLM agents, prompt engineering, and adversarial attacks on LLMs.\",\n", ")\n", "\n", - "tools = [tool]\n", + "tools = [retriever_tool]\n", "\n", "from langgraph.prebuilt import ToolExecutor\n", "\n", @@ -111,19 +140,21 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "0e378706-47d5-425a-8ba0-57b9acffbd0c", "metadata": {}, "outputs": [], "source": [ - "import operator\n", "from typing import Annotated, Sequence, TypedDict\n", "\n", "from langchain_core.messages import BaseMessage\n", + "from langgraph.graph.message import add_messages\n", "\n", "\n", "class AgentState(TypedDict):\n", - " messages: Annotated[Sequence[BaseMessage], operator.add]" + " # The add_messages function defines how an update should be processed\n", + " # Default is to replace. add_messages says \"append\"\n", + " messages: Annotated[Sequence[BaseMessage], add_messages]" ] }, { @@ -149,59 +180,38 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 17, "id": "278d1d83-dda6-4de4-bf8b-be9965c227fa", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "********************Prompt[rlm/rag-prompt]********************\n", + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\n", + "Question: \u001b[33;1m\u001b[1;3m{question}\u001b[0m \n", + "Context: \u001b[33;1m\u001b[1;3m{context}\u001b[0m \n", + "Answer:\n" + ] + } + ], "source": [ - "import json\n", - "import operator\n", - "from typing import Annotated, Sequence, TypedDict\n", + "from typing import Annotated, Literal, Sequence, TypedDict\n", "\n", "from langchain import hub\n", - "from langchain.output_parsers import PydanticOutputParser\n", "from langchain.prompts import PromptTemplate\n", - "from langchain.tools.render import format_tool_to_openai_function\n", - "from langchain_core.utils.function_calling import convert_to_openai_tool\n", - "from langchain_core.messages import BaseMessage, FunctionMessage\n", - "from langchain.output_parsers.openai_tools import PydanticToolsParser\n", + "from langchain_core.messages import BaseMessage, HumanMessage\n", "from langchain_core.pydantic_v1 import BaseModel, Field\n", "from langchain_openai import ChatOpenAI\n", - "from langgraph.prebuilt import ToolInvocation\n", + "from langgraph.prebuilt import tools_condition\n", "from langchain_core.output_parsers import StrOutputParser\n", "\n", "### Edges\n", "\n", - "\n", - "def should_retrieve(state):\n", - " \"\"\"\n", - " Decides whether the agent should retrieve more information or end the process.\n", - "\n", - " This function checks the last message in the state for a function call. If a function call is\n", - " present, the process continues to retrieve information. Otherwise, it ends the process.\n", - "\n", - " Args:\n", - " state (messages): The current state\n", - "\n", - " Returns:\n", - " str: A decision to either \"continue\" the retrieval process or \"end\" it\n", - " \"\"\"\n", - " \n", - " print(\"---DECIDE TO RETRIEVE---\")\n", - " messages = state[\"messages\"]\n", - " last_message = messages[-1]\n", - " \n", - " # If there is no function call, then we finish\n", - " if \"function_call\" not in last_message.additional_kwargs:\n", - " print(\"---DECISION: DO NOT RETRIEVE / DONE---\")\n", - " return \"end\"\n", - " # Otherwise there is a function call, so we continue\n", - " else:\n", - " print(\"---DECISION: RETRIEVE---\")\n", - " return \"continue\"\n", - "\n", - "\n", - "def grade_documents(state):\n", + "def grade_documents(state) -> Literal[\"generate\", \"rewrite\"]:\n", " \"\"\"\n", " Determines whether the retrieved documents are relevant to the question.\n", "\n", @@ -223,17 +233,8 @@ " # LLM\n", " model = ChatOpenAI(temperature=0, model=\"gpt-4-0125-preview\", streaming=True)\n", "\n", - " # Tool\n", - " grade_tool_oai = convert_to_openai_tool(grade)\n", - "\n", - " # LLM with tool and enforce invocation\n", - " llm_with_tool = model.bind(\n", - " tools=[convert_to_openai_tool(grade_tool_oai)],\n", - " tool_choice={\"type\": \"function\", \"function\": {\"name\": \"grade\"}},\n", - " )\n", - "\n", - " # Parser\n", - " parser_tool = PydanticToolsParser(tools=[grade])\n", + " # LLM with tool and validation\n", + " llm_with_tool = model.with_structured_output(grade)\n", "\n", " # Prompt\n", " prompt = PromptTemplate(\n", @@ -246,7 +247,7 @@ " )\n", "\n", " # Chain\n", - " chain = prompt | llm_with_tool | parser_tool\n", + " chain = prompt | llm_with_tool\n", "\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", @@ -254,21 +255,21 @@ " question = messages[0].content\n", " docs = last_message.content\n", " \n", - " score = chain.invoke(\n", + " scored_result = chain.invoke(\n", " {\"question\": question, \n", " \"context\": docs}\n", " )\n", " \n", - " grade = score[0].binary_score\n", + " score = scored_result.binary_score\n", "\n", - " if grade == \"yes\":\n", + " if score == \"yes\":\n", " print(\"---DECISION: DOCS RELEVANT---\")\n", - " return \"yes\"\n", + " return \"generate\"\n", "\n", " else:\n", " print(\"---DECISION: DOCS NOT RELEVANT---\")\n", - " print(grade)\n", - " return \"no\"\n", + " print(score)\n", + " return \"rewrite\"\n", "\n", "\n", "### Nodes\n", @@ -287,42 +288,12 @@ " \"\"\"\n", " print(\"---CALL AGENT---\")\n", " messages = state[\"messages\"]\n", - " model = ChatOpenAI(temperature=0, streaming=True, model=\"gpt-4-0125-preview\")\n", - " functions = [format_tool_to_openai_function(t) for t in tools]\n", - " model = model.bind_functions(functions)\n", + " model = ChatOpenAI(temperature=0, streaming=True, model=\"gpt-4-turbo\")\n", + " model = model.bind_tools(tools)\n", " response = model.invoke(messages)\n", " # We return a list, because this will get added to the existing list\n", " return {\"messages\": [response]}\n", "\n", - "def retrieve(state):\n", - " \"\"\"\n", - " Uses tool to execute retrieval.\n", - "\n", - " Args:\n", - " state (messages): The current state\n", - "\n", - " Returns:\n", - " dict: The updated state with retrieved docs\n", - " \"\"\"\n", - " print(\"---EXECUTE RETRIEVAL---\")\n", - " messages = state[\"messages\"]\n", - " # Based on the continue condition\n", - " # we know the last message involves a function call\n", - " last_message = messages[-1]\n", - " # We construct an ToolInvocation from the function_call\n", - " action = ToolInvocation(\n", - " tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n", - " tool_input=json.loads(\n", - " last_message.additional_kwargs[\"function_call\"][\"arguments\"]\n", - " ),\n", - " )\n", - " # We call the tool_executor and get back a response\n", - " response = tool_executor.invoke(action)\n", - " function_message = FunctionMessage(content=str(response), name=action.tool)\n", - "\n", - " # We return a list, because this will get added to the existing list\n", - " return {\"messages\": [function_message]}\n", - "\n", "def rewrite(state):\n", " \"\"\"\n", " Transform the query to produce a better question.\n", @@ -386,7 +357,11 @@ "\n", " # Run\n", " response = rag_chain.invoke({\"context\": docs, \"question\": question})\n", - " return {\"messages\": [response]}" + " return {\"messages\": [response]}\n", + "\n", + "\n", + "print(\"*\"*20 + \"Prompt[rlm/rag-prompt]\" + \"*\"*20)\n", + "prompt = hub.pull(\"rlm/rag-prompt\").pretty_print() # Show what the prompt looks like" ] }, { @@ -404,30 +379,23 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 18, "id": "8718a37f-83c2-4f16-9850-e61e0f49c3d4", "metadata": {}, "outputs": [], "source": [ "from langgraph.graph import END, StateGraph\n", + "from langgraph.prebuilt import ToolNode\n", "\n", "# Define a new graph\n", "workflow = StateGraph(AgentState)\n", "\n", "# Define the nodes we will cycle between\n", "workflow.add_node(\"agent\", agent) # agent\n", + "retrieve = ToolNode([retriever_tool])\n", "workflow.add_node(\"retrieve\", retrieve) # retrieval\n", - "workflow.add_node(\"rewrite\", rewrite) # retrieval\n", - "workflow.add_node(\"generate\", generate) # retrieval" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "b2158218-b21f-491b-853c-876c1afe9ba6", - "metadata": {}, - "outputs": [], - "source": [ + "workflow.add_node(\"rewrite\", rewrite) # Re-writing the question\n", + "workflow.add_node(\"generate\", generate) # Generating a response after we know the documents are relevant\n", "# Call agent node to decide to retrieve or not\n", "workflow.set_entry_point(\"agent\")\n", "\n", @@ -435,11 +403,11 @@ "workflow.add_conditional_edges(\n", " \"agent\",\n", " # Assess agent decision\n", - " should_retrieve,\n", + " tools_condition,\n", " {\n", - " # Call tool node\n", - " \"continue\": \"retrieve\",\n", - " \"end\": END,\n", + " # Translate the condition outputs to nodes in our graph\n", + " \"action\": \"retrieve\",\n", + " END: END,\n", " },\n", ")\n", "\n", @@ -448,21 +416,44 @@ " \"retrieve\",\n", " # Assess agent decision\n", " grade_documents,\n", - " {\n", - " \"yes\": \"generate\",\n", - " \"no\": \"rewrite\", \n", - " },\n", ")\n", "workflow.add_edge(\"generate\", END)\n", "workflow.add_edge(\"rewrite\", \"agent\")\n", "\n", "# Compile\n", - "app = workflow.compile()" + "graph = workflow.compile()" ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 19, + "id": "7b5a1d35", + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "try:\n", + " display(Image(graph.get_graph(xray=True).draw_mermaid_png()))\n", + "except:\n", + " # This requires some extra dependencies and is optional\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 20, "id": "7649f05a-cb67-490d-b24a-74d41895139a", "metadata": {}, "outputs": [ @@ -473,50 +464,34 @@ "---CALL AGENT---\n", "\"Output from node 'agent':\"\n", "'---'\n", - "{ 'messages': [ AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"types of agent memory\"}', 'name': 'retrieve_blog_posts'}})]}\n", - "'\\n---\\n'\n", - "---DECIDE TO RETRIEVE---\n", - "---DECISION: RETRIEVE---\n", - "---EXECUTE RETRIEVAL---\n", - "\"Output from node 'retrieve':\"\n", - "'---'\n", - "{ 'messages': [ FunctionMessage(content='Table of Contents\\n\\n\\n\\nAgent System Overview\\n\\nComponent One: Planning\\n\\nTask Decomposition\\n\\nSelf-Reflection\\n\\n\\nComponent Two: Memory\\n\\nTypes of Memory\\n\\nMaximum Inner Product Search (MIPS)\\n\\n\\nComponent Three: Tool Use\\n\\nCase Studies\\n\\nScientific Discovery Agent\\n\\nGenerative Agents Simulation\\n\\nProof-of-Concept Examples\\n\\n\\nChallenges\\n\\nCitation\\n\\nReferences\\n\\nPlanning\\n\\nSubgoal and decomposition: The agent breaks down large tasks into smaller, manageable subgoals, enabling efficient handling of complex tasks.\\nReflection and refinement: The agent can do self-criticism and self-reflection over past actions, learn from mistakes and refine them for future steps, thereby improving the quality of final results.\\n\\n\\nMemory\\n\\nMemory\\n\\nShort-term memory: I would consider all the in-context learning (See Prompt Engineering) as utilizing short-term memory of the model to learn.\\nLong-term memory: This provides the agent with the capability to retain and recall (infinite) information over extended periods, often by leveraging an external vector store and fast retrieval.\\n\\n\\nTool use\\n\\nThe design of generative agents combines LLM with memory, planning and reflection mechanisms to enable agents to behave conditioned on past experience, as well as to interact with other agents.', name='retrieve_blog_posts')]}\n", + "{ 'messages': [ AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_z36oPZN8l1UC6raxrebqc1bH', 'function': {'arguments': '{\"query\":\"types of agent memory\"}', 'name': 'retrieve_blog_posts'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls'}, id='run-2bad2518-8187-4d8f-8e23-2b9501becb6f-0', tool_calls=[{'name': 'retrieve_blog_posts', 'args': {'query': 'types of agent memory'}, 'id': 'call_z36oPZN8l1UC6raxrebqc1bH'}])]}\n", "'\\n---\\n'\n", "---CHECK RELEVANCE---\n", "---DECISION: DOCS RELEVANT---\n", + "\"Output from node 'retrieve':\"\n", + "'---'\n", + "{ 'messages': [ ToolMessage(content='Table of Contents\\n\\n\\n\\nAgent System Overview\\n\\nComponent One: Planning\\n\\nTask Decomposition\\n\\nSelf-Reflection\\n\\n\\nComponent Two: Memory\\n\\nTypes of Memory\\n\\nMaximum Inner Product Search (MIPS)\\n\\n\\nComponent Three: Tool Use\\n\\nCase Studies\\n\\nScientific Discovery Agent\\n\\nGenerative Agents Simulation\\n\\nProof-of-Concept Examples\\n\\n\\nChallenges\\n\\nCitation\\n\\nReferences\\n\\nPlanning\\n\\nSubgoal and decomposition: The agent breaks down large tasks into smaller, manageable subgoals, enabling efficient handling of complex tasks.\\nReflection and refinement: The agent can do self-criticism and self-reflection over past actions, learn from mistakes and refine them for future steps, thereby improving the quality of final results.\\n\\n\\nMemory\\n\\nMemory\\n\\nShort-term memory: I would consider all the in-context learning (See Prompt Engineering) as utilizing short-term memory of the model to learn.\\nLong-term memory: This provides the agent with the capability to retain and recall (infinite) information over extended periods, often by leveraging an external vector store and fast retrieval.\\n\\n\\nTool use\\n\\nThe design of generative agents combines LLM with memory, planning and reflection mechanisms to enable agents to behave conditioned on past experience, as well as to interact with other agents.', name='retrieve_blog_posts', id='d815f283-868c-4660-a1c6-5f6e5373ca06', tool_call_id='call_z36oPZN8l1UC6raxrebqc1bH')]}\n", + "'\\n---\\n'\n", "---GENERATE---\n", "\"Output from node 'generate':\"\n", "'---'\n", - "{ 'messages': [ 'Lilian Weng mentions two types of agent memory: short-term '\n", - " 'memory and long-term memory. Short-term memory is used for '\n", - " 'in-context learning, while long-term memory allows the agent '\n", - " 'to retain and recall information over extended periods.']}\n", - "'\\n---\\n'\n", - "\"Output from node '__end__':\"\n", - "'---'\n", - "{ 'messages': [ HumanMessage(content='What does Lilian Weng say about the types of agent memory?'),\n", - " AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"types of agent memory\"}', 'name': 'retrieve_blog_posts'}}),\n", - " FunctionMessage(content='Table of Contents\\n\\n\\n\\nAgent System Overview\\n\\nComponent One: Planning\\n\\nTask Decomposition\\n\\nSelf-Reflection\\n\\n\\nComponent Two: Memory\\n\\nTypes of Memory\\n\\nMaximum Inner Product Search (MIPS)\\n\\n\\nComponent Three: Tool Use\\n\\nCase Studies\\n\\nScientific Discovery Agent\\n\\nGenerative Agents Simulation\\n\\nProof-of-Concept Examples\\n\\n\\nChallenges\\n\\nCitation\\n\\nReferences\\n\\nPlanning\\n\\nSubgoal and decomposition: The agent breaks down large tasks into smaller, manageable subgoals, enabling efficient handling of complex tasks.\\nReflection and refinement: The agent can do self-criticism and self-reflection over past actions, learn from mistakes and refine them for future steps, thereby improving the quality of final results.\\n\\n\\nMemory\\n\\nMemory\\n\\nShort-term memory: I would consider all the in-context learning (See Prompt Engineering) as utilizing short-term memory of the model to learn.\\nLong-term memory: This provides the agent with the capability to retain and recall (infinite) information over extended periods, often by leveraging an external vector store and fast retrieval.\\n\\n\\nTool use\\n\\nThe design of generative agents combines LLM with memory, planning and reflection mechanisms to enable agents to behave conditioned on past experience, as well as to interact with other agents.', name='retrieve_blog_posts'),\n", - " 'Lilian Weng mentions two types of agent memory: short-term '\n", - " 'memory and long-term memory. Short-term memory is used for '\n", - " 'in-context learning, while long-term memory allows the agent '\n", - " 'to retain and recall information over extended periods.']}\n", + "{ 'messages': [ 'Lilian Weng discusses short-term and long-term memory in '\n", + " 'agent systems. Short-term memory is used for in-context '\n", + " 'learning, while long-term memory allows agents to retain and '\n", + " 'recall information over extended periods.']}\n", "'\\n---\\n'\n" ] } ], "source": [ "import pprint\n", - "from langchain_core.messages import HumanMessage\n", "\n", "inputs = {\n", " \"messages\": [\n", - " HumanMessage(\n", - " content=\"What does Lilian Weng say about the types of agent memory?\"\n", - " )\n", + " (\"user\", \"What does Lilian Weng say about the types of agent memory?\"),\n", " ]\n", "}\n", - "for output in app.stream(inputs):\n", + "for output in graph.stream(inputs):\n", " for key, value in output.items():\n", " pprint.pprint(f\"Output from node '{key}':\")\n", " pprint.pprint(\"---\")\n", @@ -549,7 +524,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.2" } }, "nbformat": 4, diff --git a/examples/state-model.ipynb b/examples/state-model.ipynb index 931d3c30a..dcc0ab275 100644 --- a/examples/state-model.ipynb +++ b/examples/state-model.ipynb @@ -37,7 +37,8 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/streaming-tokens.ipynb b/examples/streaming-tokens.ipynb index 8231182b2..145751d96 100644 --- a/examples/streaming-tokens.ipynb +++ b/examples/streaming-tokens.ipynb @@ -42,7 +42,8 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/time-travel.ipynb b/examples/time-travel.ipynb index fed24cd44..51b226585 100644 --- a/examples/time-travel.ipynb +++ b/examples/time-travel.ipynb @@ -39,7 +39,8 @@ } ], "source": [ - "!pip install --quiet -U langchain langchain_openai tavily-python" + "%%capture --no-stderr\n", + "%pip install install --quiet -U langchain langchain_openai tavily-python" ] }, { diff --git a/examples/visualization.ipynb b/examples/visualization.ipynb index bcca44734..57c1ec8e2 100644 --- a/examples/visualization.ipynb +++ b/examples/visualization.ipynb @@ -326,7 +326,8 @@ }, "outputs": [], "source": [ - "#!pip install pygraphviz" + "#%%capture --no-stderr\n", + "%pip install install pygraphviz" ] }, { @@ -380,8 +381,10 @@ }, "outputs": [], "source": [ - "# !pip install pyppeteer\n", - "# !pip install nest_asyncio" + "# %%capture --no-stderr\n", + "%pip install install pyppeteer\n", + "# %%capture --no-stderr\n", + "%pip install install nest_asyncio" ] }, {