mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-20 22:52:29 +02:00
921 lines
50 KiB
Plaintext
921 lines
50 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
|
||
"metadata": {},
|
||
"source": [
|
||
"# How to view and update graph state\n",
|
||
"\n",
|
||
"Once you start [checkpointing](./persistence.ipynb) your graphs, you can easily **get** or **update** the state of the agent at any point in time. This permits a few things:\n",
|
||
"\n",
|
||
"1. You can surface a state during an interrupt to a user to let them accept an action.\n",
|
||
"2. You can **rewind** the graph to reproduce or avoid issues.\n",
|
||
"3. You can **modify** the state to embed your agent into a larger system, or to let the user better control its actions.\n",
|
||
"\n",
|
||
"The key methods used for this functionality are:\n",
|
||
"\n",
|
||
"- [get_state](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.graph.CompiledGraph.get_state): fetch the values from the target config\n",
|
||
"- [update_state](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.graph.CompiledGraph.update_state): apply the given values to the target state\n",
|
||
"\n",
|
||
"**Note:** this requires passing in a checkpointer.\n",
|
||
"\n",
|
||
"Below is a quick example.\n",
|
||
"\n",
|
||
"<div class=\"admonition tip\">\n",
|
||
" <p class=\"admonition-title\">Note:</p>\n",
|
||
" <p>\n",
|
||
" In this how-to, we will create our agent from scratch to be transparent (but verbose). You can accomplish similar functionality using the <code>create_react_agent(model, tools=tool, checkpointer=checkpointer)</code> (<a href=\"https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent\">API doc</a>) constructor. This may be more appropriate if you are used to LangChain’s <a href=\"https://python.langchain.com/v0.2/docs/how_to/agent_executor/#concepts\">AgentExecutor</a> class.\n",
|
||
" </p>\n",
|
||
"</div> "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "7cbd446a-808f-4394-be92-d45ab818953c",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Setup\n",
|
||
"\n",
|
||
"First we need to install the packages required"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["%%capture --no-stderr\n%pip install --quiet -U langgraph langchain_openai"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
|
||
"metadata": {},
|
||
"source": [
|
||
"Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["import getpass\nimport os\n\n\ndef _set_env(var: str):\n if not os.environ.get(var):\n os.environ[var] = getpass.getpass(f\"{var}: \")\n\n\n_set_env(\"ANTHROPIC_API_KEY\")"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
|
||
"metadata": {},
|
||
"source": [
|
||
"Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n_set_env(\"LANGCHAIN_API_KEY\")"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "e36f89e5",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Set up the State\n",
|
||
"\n",
|
||
"The state is the interface for all the nodes."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"id": "f5319e01",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["from typing import Annotated\n\nfrom typing_extensions import TypedDict\n\nfrom langgraph.graph.message import add_messages\n\n# `add_messages`` essentially does this\n# (with more robust handling)\n# def add_messages(left: list, right: list):\n# return left + right\n\n\nclass State(TypedDict):\n messages: Annotated[list, add_messages]"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "21ac643b-cb06-4724-a80c-2862ba4773f1",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Set up the tools\n",
|
||
"\n",
|
||
"We will first define the tools we want to use.\n",
|
||
"For this simple example, we will use create a placeholder search engine.\n",
|
||
"However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"id": "d7ef57dd-5d6e-4ad3-9377-a92201c1310e",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["from langchain_core.tools import tool\n\n\n@tool\ndef search(query: str):\n \"\"\"Call to surf the web.\"\"\"\n # This is a placeholder for the actual implementation\n return [\"The weather is cloudy with a chance of meatballs.\"]\n\n\ntools = [search]"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "01885785-b71a-44d1-b1d6-7b5b14d53b58",
|
||
"metadata": {},
|
||
"source": [
|
||
"We can now wrap these tools in a simple ToolNode.\n",
|
||
"This is a prebuilt node that extracts tool calls from the most recent AIMessage, executes them, and returns a ToolMessage with the results.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"id": "5cf3331e-ccb3-41c8-aeb9-a840a94d41e7",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["from langgraph.prebuilt import ToolNode\n\ntool_node = ToolNode(tools)"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "5497ed70-fce3-47f1-9cad-46f912bad6a5",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Set up the model\n",
|
||
"\n",
|
||
"Now we need to load the chat model we want to use.\n",
|
||
"Importantly, this should satisfy two criteria:\n",
|
||
"\n",
|
||
"1. It should work with messages. We will represent all agent state in the form of messages, so it needs to be able to work well with them.\n",
|
||
"2. It should work with OpenAI function calling. This means it should either be an OpenAI model or a model that exposes a similar interface.\n",
|
||
"\n",
|
||
"Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"id": "892b54b9-75f0-4804-9ed0-88b5e5532989",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["from langchain_openai import ChatOpenAI\n\nmodel = ChatOpenAI(temperature=0)"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "a77995c0-bae2-4cee-a036-8688a90f05b9",
|
||
"metadata": {},
|
||
"source": [
|
||
"\n",
|
||
"After we've done this, we should make sure the model knows that it has these tools available to call.\n",
|
||
"We can do this using the `.bind_tools()` method, common to many of LangChain's chat models.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"id": "cd3cbae5-d92c-4559-a4aa-44721b80d107",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["model = model.bind_tools(tools)"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "e03c5094-9297-4d19-a04e-3eedc75cefb4",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Define the nodes\n",
|
||
"\n",
|
||
"We now need to define a few different nodes in our graph.\n",
|
||
"In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n",
|
||
"There are two main nodes we need for this:\n",
|
||
"\n",
|
||
"1. The agent: responsible for deciding what (if any) actions to take.\n",
|
||
"2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action.\n",
|
||
"\n",
|
||
"We will also need to define some edges.\n",
|
||
"Some of these edges may be conditional.\n",
|
||
"The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n",
|
||
"The path that is taken is not known until that node is run (the LLM decides).\n",
|
||
"\n",
|
||
"1. Conditional Edge: after the agent is called, we should either:\n",
|
||
" a. If the agent said to take an action, then the function to invoke tools should be called\n",
|
||
" b. If the agent said that it was finished, then it should finish\n",
|
||
"2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n",
|
||
"\n",
|
||
"Let's define the nodes, as well as a function to decide how what conditional edge to take."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"id": "3b541bb9-900c-40d0-964d-7b5dfee30667",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["from typing import Literal\n\n\n# Define the function that determines whether to continue or not\ndef should_continue(state: State) -> Literal[\"continue\", \"end\"]:\n last_message = state[\"messages\"][-1]\n # If there is no function call, then we finish\n if not last_message.tool_calls:\n return \"end\"\n # Otherwise if there is, we continue\n else:\n return \"continue\""]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Define the graph\n",
|
||
"\n",
|
||
"We can now put it all together and define the graph!"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"id": "812b4e70-4956-4415-8880-db48b3dcbad2",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["from langgraph.graph import END, StateGraph, START\n\n# Define a new graph\nworkflow = StateGraph(State)\n\n\n# Define the two nodes we will cycle between\ndef call_model(state: State) -> State:\n return {\"messages\": model.invoke(state[\"messages\"])}\n\n\nworkflow.add_node(\"agent\", call_model)\nworkflow.add_node(\"action\", tool_node)\n\n# Set the entrypoint as `agent`\n# This means that this node is the first one called\nworkflow.add_edge(START, \"agent\")\n\n# We now add a conditional edge\nworkflow.add_conditional_edges(\n # First, we define the start node. We use `agent`.\n # This means these are the edges taken after the `agent` node is called.\n \"agent\",\n # Next, we pass in the function that will determine which node is called next.\n should_continue,\n # Finally we pass in a mapping.\n # The keys are strings, and the values are other nodes.\n # END is a special node marking that the graph should finish.\n # What will happen is we will call `should_continue`, and then the output of that\n # will be matched against the keys in this mapping.\n # Based on which one it matches, that node will then be called.\n {\n # If `tools`, then we call the tool node.\n \"continue\": \"action\",\n # Otherwise we finish.\n \"end\": END,\n },\n)\n\n# We now add a normal edge from `tools` to `agent`.\n# This means that after `tools` is called, `agent` node is called next.\nworkflow.add_edge(\"action\", \"agent\")"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "bc9c8536-f90b-44fa-958d-5df016c66d8f",
|
||
"metadata": {},
|
||
"source": [
|
||
"**Persistence**\n",
|
||
"\n",
|
||
"To add in persistence, we pass in a checkpoint when compiling the graph"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"id": "6845ed6a-d155-4105-9160-28849877248b",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["from langgraph.checkpoint.sqlite import SqliteSaver\n\nmemory = SqliteSaver.from_conn_string(\":memory:\")"]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"id": "79d29875-8aa8-434c-9f20-1c58346a6249",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["# Finally, we compile it!\n# This compiles it into a LangChain Runnable,\n# meaning you can use it as you would any other runnable\napp = workflow.compile(checkpointer=memory)"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "e8aff75b-563e-42b1-969b-742201514fc3",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Preview the graph"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"id": "c9ab60eb-679b-4eef-9e64-5ffbf3dffc70",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCADtAOgDASIAAhEBAxEB/8QAHQABAAIDAAMBAAAAAAAAAAAAAAYHBAUIAgMJAf/EAFYQAAEDBAADAgcICg8FCQAAAAECAwQABQYRBxIhEzEIFBUiQVXRFjJRVnOTlJUXI1NUYXGBorThJDQ1Njc4QkNSdZGhsbKzCXSCktQYJTNiZYOGlsH/xAAbAQEAAgMBAQAAAAAAAAAAAAAAAgMBBAUGB//EADsRAAIBAgIFCAcIAgMAAAAAAAABAgMRBCEVMUFRkQUSExRSYaHRIlNicZKx8DIzNEKBwdLhI6JjcsL/2gAMAwEAAhEDEQA/APqnSlKAUpSgFKUoBSlKAw5t5t9tcSiXOjRVqHMEvPJQSPh0TWP7qrL64gfSUe2oDmUCLP4luCTGZkBNoY5Q6gK19ue7t16fc9a/VsP5hHsrTxONo4Wp0cotuyezarnTo4LpYKfO1lie6qy+uIH0lHtp7qrL64gfSUe2q79z1r9Ww/mEeynuetfq2H8wj2Vq6Vw/YlxRdo72vAsT3VWX1xA+ko9tPdVZfXED6Sj21Xfuetfq2H8wj2U9z1r9Ww/mEeymlcP2JcUNHe14Fie6qy+uIH0lHtp7qrL64gfSUe2q79z1r9Ww/mEeynuetfq2H8wj2U0rh+xLiho72vAsT3VWX1xA+ko9tPdVZfXED6Sj21Xfuetfq2H8wj2U9z1r9Ww/mEeymlcP2JcUNHe14FjsZFaZTyGmbnDddWdJQiQhSlH4AAa2NUxcLRBh3PH3GIUdhwXaKAttpKT7/wCECrnrpUqsK9JVYJpNtZ9xoYij0ElG9xSlKsNUUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoCtMo/hMe/qhj/Werzrwyj+Ex7+qGP9Z6vOvMcrfin7o/JHp8J9zEVHc24g2Dh3b48y/wA/xJqS8I7CG2XH3XnCCeVDbaVLUdAnoDoCpFVYcebdbpllsz8uBkzs2HO7aBcsTiqkS7c92ax2pQkHaCCUFJSoHm6j0jlU0pSSZszbUW0YeTeEbj+P5VhlubZmzrdkUSRNE6Nb5bpbbQAEBLaGVKUVKJ2OhQEgqACgakV8424VjWVDHbnevE7p2rTCkuRXuxQ44AW0rfCOzSVBSdBSh3iqq8pZrHc4N5vluOXSdOgR7nGu7NpgF6S0X0oDDi47eynmS0CsJ6JUrXSonxvgZdm0DiFBm2nNrhclOtrx632pp1u1+JJQ05zuFJCHHuYO7Q4VL5gkIT3VuKjCTSf6599vruNV1ZpN/t3HQ9z4wYnactdxd+5Orv7RYC4EeDIfcSHujajyNkcndte+VOxzEbG9Nwi4423izNv0ONDmwpFtnyYyEvQpKEOMtOBAcLjjSUpWonZa3zp9I6E1j8P7VKHHHiTenbdKjwp8GzCLKkxltB0Jbf50pKgOqSpPMnvBI3qsLgs/OxjJ83xm52O7RX5ORXC7xrgqEswXo7yw4gpfHmc2laKd7BB6VS4QUXbXZbeJYpSclfVn/RcNKUrVNk1d4/b2P/1tF/z1blVHeP29j/8AW0X/AD1blex5O/Bx97/Y8/yh94vcKUpW+cwUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoCtMo/hMe/qhj/WeqO5Tw1xPOJLMnIcatV7kMo7Np24Q23lITvegVA6G6sfIcAgZHdk3F2VOiSgwI5VDf7MKQFKUARo+lRrX/Yqg+uL39N/VXPxWB6zV6WNTm5JansVjr0cXThTUJK5V54A8NCgIOA44UAkhPkxnQJ1s+9/AP7K3uK8PMXwVclWO49bLGqSEh82+KhkuhO+Xm5QN62db+E1M/sVQfXF7+m/qp9iqD64vf039Vaj5Lm1Z1vmXLG0FmomtpWy+xVB9cXv6b+qqi4jRZuM+ELwfw+Fe7omzZOi8KuCFyOZajGjJca5Va83zid/DUND/APKuDJ6QpbmWXWsyHGrTltrctt7tsW7W9wpUuLNZS62og7BKVAjoetSX7FUH1xe/pv6qfYqg+uL39N/VWVyQ1mqq4Mx1+k8mmVengBw0QdpwHHAdEbFrZ7j3/wAms2x8HMExm6sXO0YdY7ZcY5JalRLe0262SCDyqCdjYJH5asP7FUH1xe/pv6qfYqg+uL39N/VU3yXN5Ot8yPXaC/L4IjN4/b2P/wBbRf8APVuVDY3C22sTokpdwuspUV5L7bciVzI50nYJGutTKurQorD0VS517NvjY52KrRrTUoilKVaaYpSlAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAK534zfxwfB0+SyP9CRXRFc78Zv44Pg6fJZH+hIoDoilKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAK534zfxwfB0+SyP9CRXRFc78Zv44Pg6fJZH+hIoDoilKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApWhyPMoOOONx1pemXB1PM3Cip5nCneuZXcEJ2D5yiB0IGz0qLO53k8ghTFptcNB7kyJbjqx+PlQB/YTVqpSau7Jd7L4UalRXiibZHYIWV49dLJc2vGLbcorsKU1vXO04goWnf4UqIr4UcX+E114R8Vr5g8xtcmbAl9gwtCCTJbVpTK0gf00KSdDu3rvFfZ73Z5d97WT/AJnqqPP+DJ4jcZMP4j3OJafK+ODzGEFzspRSSpgudN7bWSoa7zoHoNVLol2lxLOqVtxY3gocFUcBuCVjxx1ATd3k+P3VQIO5bgTzjY6HkAQ2D6Q2D6auCq192eXfe1k/5nq/UZtliCCqDZnx6Uh91v8Av5Vf4U6L2lxHVK24smlRGx8Q48+UzCucN2zTnSEthxXaMOq7uVDo6b+BKglR9ANS6q5QlDWa8oSg7SVhSlKgQFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFaXMMgONWF+W02l6WopZisrOg48s8qAT6Bs7J9ABrdVAuJ61G4Yq0f8AwjOdWd+lQjucv+JP5KupJOeey74K5bSipzUXtNJBg+KB1xx1UmZIX2siU5795fwn4ABoADokAAaAFZVK5p4zcYMmxO/ZFdsYvl0uttx1+OifbWbNGNtjk9n2jL0pag6pwhfN9q3yc6QR3mtaUnN3es9JKUaUdWR0tSuZOM/FfKoF34ls2zMmMTexiLF8l2kxGHXbst5oL59uAqO1qLSA33KT133Vl3/iTxCybN8hs2NtX+IxjqI0ZSrXAtslT8lyOl1RkGS82Qkc4SEtJTvSjzDYAjYg60U7W+vpHSFfinEoUlKlBJWdJBPedb0PyA/2VQtuyHiRmmewccl3o4LKOIxbtOixIceUtqcqQ80sJU4FjkPKnY87okaKTsmHIu2RcWbrwCvjmRybBdLgxdEOu26NHWlDzbC0uOoS62sefykaOwAegB60DqrYvq6X7nU8qKzOjuMPtpdZcHKpCh0IqR8P76/IEyzTnlPyoAQpl9xXMt+OoEJUo+lQUlaSfTypUeqq0CQQkAnmIHefTTHVqa4kWwI/nbbLS4Nd6Q5HIP5Cdf8AEa2aPpXpvc3+qV/krFOLgpUnJ60WfSlKrPPilKUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQCovxEsz90sKX4ba351ueTNZZQdKd5QQtsfhUhS0j8JFSilThLmSUiUZOLUlsKsiympsZuQw4l1lxIUhae4iquyvwcsfy13I237xf4Vqv7pkz7RBmpaiuSORKe31yFQV5iDrm5CUjaTVgZzerJj3E+z4vaphbyjIEOzRZvFnXGFoRzFb6nEJIjlRBBUdpUrvAJKqynXLzEITKxi6JX6THDT6D+IpXv+0Cs9C5Z03dfpf69x3o4ilWj6Rz1xa4aZpM4gP3jGbdfpNzTDZZg3pu62zsG3UI0FONSGC42nmJKgydK2SACasGdwPbvlxbvz2RXrHcmmwWI16k41LEdq4LbRrmUlSFaI2oJUnlUEnW6sHyhP+Ll6+ifrrVz86iWu926zTYkuJd7kFmDb3w2iRK5BtfZNlfMvlHU8oOh306vV3Ek6N23LX3njBwGBAzY5SiRMcuJtLVmKHXApsstuLcCjscxWVLOyVaPTpvrUVPg+2JrEsXscO63q2u4286/bbtDkNomNFznDgKi2UFKg4oEFHdqrA8oT/i5evon66/Uy7m6QlrGby4o+gsob/vWsD++nV6u75E3OjtaMmKwYsVlkuuPltAQXXSCtehraiNdT3mtpw8gquN4n30g+LIb8Rhq3sODYU64PwFQQn/2j6CKjWUh7F8SueTZeh6245bme3lQbY2uXMdRsAglv3qevnBO+mzzpANWdid6tWR4vabpYnEO2SZFbfhLbaLSFMKSCgpQQCkcutAgdKkkqSavdvw/X6yOfisTGcejgbalKVScoUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlYkq6xIUqNFeksomSgvxeMp1KXH+UcyghJI5tDqdd3poDLqrLnmFy4n3PiBgViZyDDZ1sjNsNZe5BT4v4w4nmIY5z5/KkoOxroo6KSEqOEzjFy8IXDLe5nljvWACHefHWbPCvPK7KZaO2fGVM60CrS+RKtgoSQqrgoDS4jjfuVxu02py4TL0/b4qIpudyWHJUgJABU4sAbUdAk+k1uqUoDxddQy2txxaW20AqUtR0Egd5Jr4weEb4TV14g+Es5nlhmKYj2CU2zYVpPRLLCyUrI6b7RXMsg+hfKegr7E5jjEbNsRvmOzHpEaJd4L9veeiLCXm0OtqQpSCQQFAKJBII3roa+e3ED/Z/wDD3FePXCnCYl5yZy1ZYi7KnPPyo5fbMWOl1vsiGAkbUo83MlWx3a76A7x4RcS7bxh4bWDMLV5sS6xg6WubZZcBKXGifSULCkk/gqYVXXAvgXYPB8xKbjmNS7nJtci4O3BKLm+l5TClpQkttlKU6QOQEA7OyokndWLQCoXd+HD1w4i49lUTJrxambVGciO2KK6nyfMbUDrnbKTpSVcpCh10kDpU0pQED4Z8SLjmNouEjJMUn4HOiXNdtES7OtlMg7HZrZcSdOJUFJAI6c3MAVa3U8qK8SuF+M8XsXcx/K7Yi6WxTqX0oK1IW06nfK4haSFJUNnqD3EjuJFap685bi+cXuTeG7HH4Ww7T40zPQ64mbFcaSC6HUEFKkFPMQU9wR6SdUBP6VosJziw8R8Zg5DjVzYu9mmp52ZUc7SrR0QQeqVAggpIBBBBANb2gFKUoBSlKAUpSgFKUoBWP5Qi/fLPzgrIrntjjhbrnlMiz2fH8hvseJO8my7zboSVwY8gEJWhS1LCjyE6UUJUE9dnoaAvzyhF++WfnBTyhF++WfnBXOs/wjsbt91mNKt16dscG4C1zMmaiJNtjyecIKFuc/PoLUEFYQUAnRVXrvfhIWGxSshD1jyF63Y9O8Qu11YhoVFhq0g86lFwKUjTiSeRKlJHVSQCCQOjvKEX75Z+cFPKEX75Z+cFc4M8X7yvj9c8IGMz5Vmj26JIRPjIZ0hTq3Ap5xSngex0gJHKgq5kr2NcpOsx3j7Ft2N3a83prIJfaZY5YY9tctjCZUR0pTyRwhl1QcSDsBe+YlfUaG6AvzPslvEPFL4cOjQbplMeMHIUS4uqYjOuKJABd1o6AUeUEfyQSkKCq0lj4d2a53/Gc5y+HaJXEe22pMBy4RHVKYZWQS6WUqPTalLAURzcqiN6NV0rwg7HFsd/n3C0Xq1y7FMhQp9plsNCU2qU422wscrhbUhRcB2FnolXTY1W/wAh4sWDFMmnWa6uPQjCsi7/ACJriR4u3GQ52ahsHmK99dBPUenfSgLpbmR3VhKH21qPclKwSa91Udw4402/I84tdkl2DIMZm3Fp563eXISWUzUoRzL7MpWrSgk83IvlVrZ10NXjQClKUArnbjN/HC8HT5LI/wBCRXRNc7cZv44Xg6fJZH+hIoDomlKUApSlAK/O+v2lAV/l/Da6zJWJLw7JV4RDs1wMmXbIMJpUW4sLVt1paNDlUdrIUO5SySCdEZeJcUI+UZVlVhesl4scmwSENKkXWL2UeahfNyOx3NkLSeRXwEa6gVNaiHFXLbbg2HSL1dnVtQoziAQ22XHHFqPIhtCB1UtSlJSEjvJFASfyhF++WfnBTyhF++WfnBXOq/CIssCBfnrxYchx6baLU7el225xG0PyYjfRa2eVxSFaJSCkrBBUNgbrZY3xstGQZCm0SLZd8feegLukN+8x0MNTIqCkLcQQslPLzoJS4EKAUCU0BfKZ0ZaglMhpSidABY2a99cl/wDaIfynOuGLGNW2+26xXrIRHVdp9vbREucUMPK00pRKwCpKFAlKCoAkbG660oBSlKAUpSgFcp8N7bnfCd6XibWHJv8AY3LzIlxsgZujLKURpEhTqu2bX9sLiO0UNJSQrQ6jvrqytf5Bg/cPz1e2gONrtwtzz7HuRcJomPMu2S7XWQ61lZntBpiG/KMhfOyT2peSFKQAE8pPKeYCt5fOFuTS+GfHe0s2ztJ+TXOXItLRkNfsltcOO2hXMVaRtbax55B6b7iK6t8gwfuH56vbTyDB+4fnq9tAc4rx/KsW41x8ig48q+We62OFaJbrMxlpdvcZfcUpxSXFDnRyvE+Zs7QRrqDUYVwoyopcHkvqeKKcjH7Ia/c8FP27334D5nvv/LXWvkGD9w/PV7ahHEjK7NwufsUq42+6z4l7ukazIEJsONQ3HCrTzmiFhJ7ifOHROgNkkChuJHCLKMou3Fp+3wmiLs1j79qU9IQlMp2E8p5xs6JKN6SnagB5w9AOtVn3C7NON2R5Q7Px04hAuOHqtERyZOYkLEoS0PpDqWlK0k8veObzQd9Ty12L5Bg/cPz1e2nkGD9w/PV7aA5z4AYExb83t82bwVseCT4cZz/viG9EcUp8p5FJYDQKwhSVOecspOtDR2ddOVhsWmJGdS601yrT3HmJ/wD2sygFKVr7/f7di1knXi7zGrfbILKpEmU+rlQ02kbKifxUBpeJ/Eyw8IcIueVZJLES2QUcx11ceWfettp/lLUegH9ugCaqDgLw6yLOcxXxp4lRlQ8imMqZx3HlklNhgLHcQf59xJ8862ASOmylOg4cWG5eFjn8HijlcN2Fw3srxXh2Oyk6M1wHXlGQn0719rSe7vHQEudUUApSlAKUpQClKUAqoPCo4bzOKPCZ6025ER+4MT4twjxZ/wC1pSmXA52LvQ+asAp/KN9Kt+vTJiNTGwh5HOkHetkdfyUBxvK4UOXrhrn8W08GLXgV9nWJ+BCVHkwlPy3HEKCm+ZrzUo2G9FShv0gaqT5/wqvGZZJhzaWSxbmsYvFnnzA4jcZySzHbb83m5ldUL6p2By9SNiul/IMH7h+er21DOH2L5RGumXKy52FKgu3ZxdiTEJCmoPKORDmgPP3zd+/x0Bz7i2N8RrnP4NWW74Qi2sYddo5m3Vi6R3WH2mojrCXGmwoL5VFSTogKGwNEbI7FrBbssNpxK0s6Ukgg8yuh/trOoBSlKAUpSgFKUoBSlKAVj3BqQ/AktxXxFlLbUlp9SOcNrIPKop9Ojo69NZFQPjhh+V5zw2u1owrK38OyJ5siPcGUIIVtJSptaihSmwoKOnGuVxCglQPQpUBA7l4RVs8Hzhhavs1ZRaFZ4iMVSLfYz20iYr7YW1IZCUlPOG+XnUENBexzAEVddkvMPIrNAu1ue8Yt8+O3KjvcpTztrSFIVpQBGwQdEA18IeMXDvNOGmd3K3Z5DmMZA84qS9KmOF0zCtRJeDuz2vMdkq2eu99Qa+2/BQa4N4GP/QIH6O3QE0pSlAeLjiWkKWtQQhIJUpR0APhNcnTXZPhu5+uBGcdZ4FY1MAlvtkp9081s77NJHfGQdEke+7x1KSjN4uZbdvCY4gTODWDT3YOLW8gZtk0Q+8Rv9z2FdxcXohfwaIOwlaT0fiWJ2jBcat2P2GC1bbRb2UsRorI81CR/eSTsknqSSTsmgNlGjMwozUeO0hhhpAbbaaSEpQkDQSAOgAHTVe2lKAUpSgFKUoBSlKAUpSgI9l3EXFOH6YqsoyezY2mWVCObvcGooe5dc3J2ihza5k713bHw1TnC3irwUxC9Z1Kt/F2xzHb1fHLhKRc7ww0hl1SUgoYKikLa6DSklQ7+tbPwyOBo48cD7vaorPaX+3DylaiBtSn20nbQ+USVI13bKSe6vmX4FvAhXHLjjbIE6L2uO2g+UbsFp8xTaCOVk+g86+VJHfy85HdQH2lpSlAKUpQClKUApSlAKUrDu90j2O0zbjLUURYjK33VAbISlJUdfkFZScnZAwMlyuLjTbSVtuy5r+/F4ccbcc1rZ2dBKRsbUogdQO8gGGv5Plk9RWJFutLZ0Qy0wqSsfDtxSkg/kQPy1g21MmQXblcAPKk7TkjRJDfTzWk7/koB0O7Z5lEbUd5tWSqKm+bBJ9+vhst4/I7tHBwjG882QLi1wqa43455EzGXGukVCudlzxBCHo6/6TbiSFJPdvR0e4gipVZWcgx2zQLVb763HgQY7cWOz4ilXI2hIShO1KJOgANkk1s6w5d5gQLhAgyZjDE2epaYsdxwBx8oSVr5E950kEnXdWOsT3L4Y+RsdXo9k93lPLPjG39Xt+2sO8HKrzaZsBWVuRUymVsl+JDQ282FAgqQsHaVDfQjuPWvFOTW1eTuY8JBN4bhpnqj9mvowpZQFc2uX3ySNb307tVtKdYnuXwx8jHV6PZIfwpwiTwQxNjHsTet6ba0tTy25kNRckOq98446lwEqOu8g6AAA0ABa2N5w3d5Yt8+Kq13MglDalhbUgDqS0sa3odSlQSrvOtDdRmsefBbuEfsnOZJCgttxB0ttYO0rSfQoHqDRVlPKol70rW4WTKqmEpyXoqzLWpUfwa/u5DjzT0ooNwYWqLL7MaSXkHSlAegK6KA+BQqQVGUXCTi9hwWnF2YpSlRMClKUBCOIN8u9vu9jg2uaiCJaZC3XFMB0nkCNAA93vjWl8fyv4yN/V7ftrYcRP33Yv8AIzP8Gq9Fa2LxNShzI07Zrcnte9Hl+UsZXoV+ZTlZW7jG8fyv4yN/V7ftp4/lfxkb+r2/bWTStHSGI3r4Y+RytJYvt+C8jG8fyv4yN/V7ftqE8O+FCeFd0ye441cGoEvI5xuFwc8RQrncOzpOz5qAVLISOgK1aqf0ppDEb18MfIaSxfb8F5GN4/lfxkb+r2/bTx/K/jI39Xt+2sK95Va8cmWeLcZXi793l+IwkdmtXavci3OXaQQnzW1natDp37IrbU6/iN6+GPkZ0ji1nz/BeRjeP5X8ZG/q9v21hXq/Zba7NPmoyFpao0dx4JNvb0SlJOu/8FbatRl/7071/uT/APpqq6hjq8qsYtqza/LHf7idPlHFOcU57dy8i14DypEGM6vXO42lR18JANK8LT+5UL5FH+UUrpy1s9yZdKUqIFRLitz+4G6cm/5rn1/Q7VHP+bupbWJd7XHvlqm26Wkriy2VsOpB0ShSSk/3GraUlCpGT2NEouzTK8qluNbszJsztOJ2FzIV31FvduLjdqvxtEZpgrDaXXnUoWpaucEJQEke+KhrVW5bVSYxdttwI8qQeVuRoEBwa811O/5KwNjv0eZO9pOtBmPCrFs+nxJt8tnjUuK2plt5uQ6wotKIKm1ltSedBIG0K2n8FUTi4ScWeml/kh6O0onCc0yPi1B4SY9dsin2hm7WOZcrjOtb/i0m4vR3ENJaS6nRR0UXFcmidegVueI3DKGnilwdtD1+ySQ2V3VnxtV7kIk6EdTg+2oUlXN15eb3xSkAk1ZsvgVgs3GYGPuWBCbVb5DkmE0zIeaXEcWoqWWXErC2wSo+ahQHo1qvObwRwu4Yxb8ffsxNst8hUuIES30PMvKKipaXkrDnMorVs83XZ3uoFXRStZ56vCxUXGDMb7w0zniVNst0uDhawqNcmIsuU4/GiyFSnGC820olKeVCEqIA0Skk72a881TdeEV8gWy15ffr4xfsbvDsrypcFyVtPR4yXG5bKidtEqUU6QQnzk6AI3V4fY7x03F6c5bEPyX7UiyOl9xbqXISSpQaUlRKVDa17JGzvqTWlx7gTg2LIuKbdY+zM+Gq3PLelvvLEZQ0WW1OLUW0dfeoKR0HwCsmXTk3rKzwh2747kXBaarJb3dVZfbnhdmbnOU+y4sQRIStDZ81ohSSPMA2D12etdE1HmuH9gZcxhaIHKrGWy1aT2zn7GSWexI995/2s8vn7+Hv61uZ85u3sdo5zKKlBDbaBtbiydJQkelRPQCiTk7LWWwjzE7/AFkbvhfz+NZV39l5SRy7/peKsb1+Du/Lup3WgwewO49jzTMkIE99apUvsztPbLO1JB9IT0SD8CRW/raqtOeWyy4Kx5urJTm5IUpSqSoUpSgK+4ifvuxf5GZ/g1Xor38RP33Yv8jM/wAGqjeVRslksMDG7jarc8FHtlXWA7LSpOugSEPNcp36STXN5Q+1T/6/+pHjOV1fEpX2L9ze1V/hIZld8I4XyJlkdTFnyp0S3iYtwNpioefQ2pwrKVBGgogKKVcpIOjrR2QtnFHkIOS4jz7Gj7npWtdd9PHvxV7o2KZLkEebbM4mY1kGPzI6mXoESzPMKWSRrmU5JdBGt9OXe9EEarmxtFpt3OVBRhJSk00tmfkUrkVh4m8PsE4g3KRcpFvsreMTFtpcyh+6y2pqRtt5p1xhpbQ5ecEBRG+UgDVba6Xm88Jcqs0uJe7xkKLtid1ucqFdpipCHJUVth1C20no1zdopJS2Ep0R5o1Vm2ngRg9ksd7tEWzueIXmL4lOQ/PkvLdY0oBsLW4VJSAtWgkjWzqpG9hVlkXmz3VyEFz7RGeiQnS4vTTToQHE8u9K2G0dVAka6a2d2dJE2HiIPJq6z2a8str2nOEbGZQd4EZZPyy9ZHcr5eGZcoS5pXDC3YEhzbLPvWgnqkcuuhO9nu6rqtLb4O2B47cI1ystiRAuUF9cu3lUqSuPFfUlSeZDPahCU+edoSAD+MAjL8l8U/jLiB/+Oyv+uqM2p6mQrTjWas9W9W291ywK1GX/AL071/uT/wDpqqLeS+Kfxmw//wCuyv8ArqlOW79yV633+Ivd3yaqnQVq0M9q+ZVCKVSNnfNFqWn9yoXyKP8AKKUtP7lQvkUf5RSvQy+0z6QZdKUqIFKUoDSZLikTJW2lOLdizWObxeZHOnGt62OuwpJ0NpUCDoHWwCIa/jGWQFFKY9uu7YIAeafVGWR6dtqSoD8iz+SrNpVqqZc2STXf/WZfTr1KWUWVX5Myv4uN/WDfsp5Myv4uN/WDfsq1KVLnw9Wv9vMv67VKr8mZX8XG/rBv2U8mZX8XG/rBv2ValKc+Hq1/t5jrtUq9uw5dKVyptUCED/OypxVy/wDChB3+LY/HUoxvB27PKE+dKVdLmAQh1SAhpgHoQ0gb1sdCpRUrqRvR1UopWHUytGKXu/u7KqmIqVFaTyFKUqk1hSlKAUpSgIRxBsd2uF3sc61wkThETIQ62p8NEc4Rognv96a0viGV/FxH1g37KtGlJxp1EukgnbLbvvsa3mjWwVDES59SN372Vd4hlfxcR9YN+yniGV/FxH1g37KtGlQ6HD+qXGX8ijReE7Hi/Mq7xDK/i4j6wb9lPEMr+LiPrBv2VaNKdDh/VLjL+Q0XhOx4vzKu8Qyv4uI+sG/ZTxDK/i4j6wb9lWjSnQ4f1S4y/kNF4TseL8yrvEMr+LiPrBv2Vh3qw5ZdLNPhIx5tCpMdxkKM9vQKkkb7vw1btKlGnQhJSVNXXfL+RlcmYWLTUfF+ZjwGVR4MZpeudttKTr4QAKVkUqTd3c6h/9k=",
|
||
"text/plain": [
|
||
"<IPython.core.display.Image object>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": ["from IPython.display import Image, display\n\ntry:\n display(Image(app.get_graph().draw_mermaid_png()))\nexcept Exception:\n # This requires some extra dependencies and is optional\n pass"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Interacting with the Agent\n",
|
||
"\n",
|
||
"We can now interact with the agent. Between interactions you can get and update state.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"id": "cfd140f0-a5a6-4697-8115-322242f197b5",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||
"\n",
|
||
"hi! I'm bob\n",
|
||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||
"\n",
|
||
"Hello Bob! How can I assist you today?\n"
|
||
]
|
||
}
|
||
],
|
||
"source": ["from langchain_core.messages import HumanMessage\n\nconfig = {\"configurable\": {\"thread_id\": \"2\"}}\ninput_message = HumanMessage(content=\"hi! I'm bob\")\nfor event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n event[\"messages\"][-1].pretty_print()"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "9230c0a7-bab8-4327-a9b4-f21e7a28d32a",
|
||
"metadata": {},
|
||
"source": [
|
||
"See LangSmith example run here https://smith.langchain.com/public/01c1d61c-6943-4db1-8afe-5366f083caf3/r\n",
|
||
"\n",
|
||
"Here you can see the \"agent\" node ran, and then \"should_continue\" returned \"end\" so the graph stopped execution there."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "c161d511-0dc3-4824-b89e-0dbcf9a7a759",
|
||
"metadata": {},
|
||
"source": [
|
||
"Let's now get the current state"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"id": "23433643-d35d-4df4-80fe-a3002323cd4f",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"{'messages': [HumanMessage(content=\"hi! I'm bob\", id='cd7df241-189c-46a6-b822-69fcfafd8ad4'),\n",
|
||
" AIMessage(content='Hello Bob! How can I assist you today?', response_metadata={'finish_reason': 'stop', 'logprobs': None, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'token_usage': {'completion_tokens': 11, 'prompt_tokens': 54, 'total_tokens': 65}}, id='run-cc3e7ee7-208e-446e-80cb-0349fe75319b-0')]}"
|
||
]
|
||
},
|
||
"execution_count": 15,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["app.get_state(config).values"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "b894d739-d8b1-485b-841a-5cd997c111f9",
|
||
"metadata": {},
|
||
"source": [
|
||
"The current state is the two messages we've seen above, 1. the HumanMessage we sent in, 2. the AIMessage we got back from the model.\n",
|
||
"\n",
|
||
"The `next` values are empty since the graph has terminated (transitioned to the `__end__`)."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"id": "e2c2531f-6dda-444b-b2f5-bbd607251776",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"()"
|
||
]
|
||
},
|
||
"execution_count": 16,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["app.get_state(config).next"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "98a953a5-7c04-4210-a6ea-bd42e8abd81c",
|
||
"metadata": {},
|
||
"source": [
|
||
"The graph got to the end without interruptions, so the list of next nodes is empty."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "7f117f21-cabc-4c6a-b89a-e673085bc067",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Let's get it to execute a tool"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"id": "73eb35a7-b705-4d7b-9e4b-28f7e2130358",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||
"\n",
|
||
"what is the weather in sf currently\n",
|
||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||
"Tool Calls:\n",
|
||
" search (call_UVPlm7YZ0xksC2VsYsPxN5ag)\n",
|
||
" Call ID: call_UVPlm7YZ0xksC2VsYsPxN5ag\n",
|
||
" Args:\n",
|
||
" query: weather in San Francisco\n",
|
||
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
|
||
"Name: search\n",
|
||
"\n",
|
||
"[\"The weather is cloudy with a chance of meatballs.\"]\n",
|
||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||
"\n",
|
||
"The weather in San Francisco is currently cloudy with a chance of meatballs.\n"
|
||
]
|
||
}
|
||
],
|
||
"source": ["config = {\"configurable\": {\"thread_id\": \"2\"}}\ninput_message = HumanMessage(content=\"what is the weather in sf currently\")\nfor event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n event[\"messages\"][-1].pretty_print()"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "c0ebc4b8-b2cc-4394-bb5a-8964bafa8d2d",
|
||
"metadata": {},
|
||
"source": [
|
||
"See LangSmith example run here https://smith.langchain.com/public/c33c04c5-f1f2-4977-9d7d-c48f28be7be2/r"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "18d3141b-9f9a-4ad8-b475-400674848b16",
|
||
"metadata": {},
|
||
"source": [
|
||
"We can see it planned the tool execution (ie the \"agent\" node), then \"should_continue\" edge returned \"continue\" so we proceeded to \"action\" node, which executed the tool, and then \"agent\" node emitted the final response, which made \"should_continue\" edge return \"end\". Let's see how we can have more control over this."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "1c5d69cf-6de5-4a52-976a-3799aa39d6d5",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Pause before tools"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "06aaa787-32fe-404b-abeb-5b2ed94309fd",
|
||
"metadata": {},
|
||
"source": [
|
||
"If you notice below, we now will add `interrupt_before=[\"action\"]` - this means that before any actions are taken we pause. This is a great moment to allow the user to correct and update the state! This is very useful when you want to have a human-in-the-loop to validate (and potentially change) the action to take. "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 18,
|
||
"id": "5a68afc0-606f-4294-a872-b2b563be0d69",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["app_w_interrupt = workflow.compile(checkpointer=memory, interrupt_before=[\"action\"])"]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"id": "08ae8246-11d5-40e1-8567-361e5bef8917",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||
"\n",
|
||
"what is the weather in sf currently\n",
|
||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||
"Tool Calls:\n",
|
||
" search (call_sxtKypVZlFrjzdOFYiCh8kin)\n",
|
||
" Call ID: call_sxtKypVZlFrjzdOFYiCh8kin\n",
|
||
" Args:\n",
|
||
" query: weather in San Francisco\n"
|
||
]
|
||
}
|
||
],
|
||
"source": ["config = {\"configurable\": {\"thread_id\": \"4\"}}\ninput_message = HumanMessage(content=\"what is the weather in sf currently\")\nfor event in app_w_interrupt.stream(\n {\"messages\": [input_message]}, config, stream_mode=\"values\"\n):\n event[\"messages\"][-1].pretty_print()"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "c53352c8-dcf7-48b5-940c-f75a279cb42c",
|
||
"metadata": {},
|
||
"source": [
|
||
"See LangSmith example run here https://smith.langchain.com/public/22402055-a50e-4d82-8b3e-733c9d752bc5/r\n",
|
||
"This time it executed the \"agent\" node same as before, and you can see in the LangSmith trace that \"should_continue\" returned \"continue\", but it paused execution per our setting above."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "7d9fa4f0-fb7d-47a7-b77e-464e8b619273",
|
||
"metadata": {},
|
||
"source": [
|
||
"Notice that this time, the `next` value is populated with `action`. That means that if we resume the graph, it will start at the `action` node."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 20,
|
||
"id": "5a53df78-6c25-4176-b049-02a02a713771",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"('action',)"
|
||
]
|
||
},
|
||
"execution_count": 20,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["current_values = app_w_interrupt.get_state(config)\ncurrent_values.next"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "a901e2d1-dcf4-470c-88c0-2e4c91307f69",
|
||
"metadata": {},
|
||
"source": [
|
||
"Because we asked to interrupt the graph before getting to the action node, the next node to execute, if we were to resume, would be the \"action\" node."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 21,
|
||
"id": "9bad3f92-a3b9-4370-a1f5-0b78d1b67602",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"[{'name': 'search',\n",
|
||
" 'args': {'query': 'weather in San Francisco'},\n",
|
||
" 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin'}]"
|
||
]
|
||
},
|
||
"execution_count": 21,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["current_values.values[\"messages\"][-1].tool_calls"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "b2b7af00-035c-44e6-a76a-a4b83340b32b",
|
||
"metadata": {},
|
||
"source": [
|
||
"Let's update the search string before proceeding"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 22,
|
||
"id": "060e2e33-1f6a-40ef-850e-161b308986fb",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["current_values.values[\"messages\"][-1].tool_calls[0][\"args\"][\n \"query\"\n] = \"weather in San Francisco today\""]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"id": "3210d003-e20e-47fa-b4f4-402fed7409f1",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"{'configurable': {'thread_id': '4',\n",
|
||
" 'thread_ts': '2024-05-07T17:30:25.205012+00:00'}}"
|
||
]
|
||
},
|
||
"execution_count": 23,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["app_w_interrupt.update_state(config, current_values.values)"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0a21b87a-319c-4a93-9ac1-a3c6ad3f5400",
|
||
"metadata": {},
|
||
"source": [
|
||
"This actually produces a LangSmith run too! See it here https://smith.langchain.com/public/9d86718b-333e-4175-bec0-9a64cdd01dc3/r\n",
|
||
"\n",
|
||
"This is a shorter run that allows you to inspect the edges that reacted to the state update, you can see \"should_continue\" returned \"continue\" as before, given this is still a function call."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "3613f445-cf46-46a0-b7c1-e83a02aae96f",
|
||
"metadata": {},
|
||
"source": [
|
||
"The current state now reflects our updated search query!"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 24,
|
||
"id": "4a7dfaab-bfa7-47c7-8b93-2b06b3560809",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"{'messages': [HumanMessage(content='what is the weather in sf currently', id='7e198f29-a371-49d5-86df-7e0e0b5a9144'),\n",
|
||
" AIMessage(content='', additional_kwargs={'tool_calls': [{'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'search'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin', 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'logprobs': None, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'token_usage': {'completion_tokens': 16, 'prompt_tokens': 56, 'total_tokens': 72}}, id='run-0e6d8103-a92e-461d-aa99-8a68f4c99366-0', tool_calls=[{'name': 'search', 'args': {'query': 'weather in San Francisco today'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin'}])]}"
|
||
]
|
||
},
|
||
"execution_count": 24,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["app_w_interrupt.get_state(config).values"]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 25,
|
||
"id": "77d73f33-93cb-41db-b6bb-81697569fdb0",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"('action',)"
|
||
]
|
||
},
|
||
"execution_count": 25,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["app_w_interrupt.get_state(config).next"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "27304085-4e44-48bc-afa5-d7ab6da4bb8c",
|
||
"metadata": {},
|
||
"source": [
|
||
"If we start the agent again it will pick up from the state we updated."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 26,
|
||
"id": "273d56a8-f40f-4a51-a27f-7c6bb2bda0ba",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{'messages': [ToolMessage(content='[\"The weather is cloudy with a chance of meatballs.\"]', name='search', id='9dce802a-9811-491f-a1d5-ace400fbcba0', tool_call_id='call_sxtKypVZlFrjzdOFYiCh8kin')]}\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{'messages': AIMessage(content='The weather in San Francisco is currently cloudy with a chance of meatballs.', response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 92, 'total_tokens': 108}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-269cd84d-c5ba-438b-9abf-1d1389bed733-0')}\n"
|
||
]
|
||
}
|
||
],
|
||
"source": ["for event in app_w_interrupt.stream(None, config):\n for v in event.values():\n print(v)"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "14d288a5-1027-4a12-898c-1c6bfcc19bb6",
|
||
"metadata": {},
|
||
"source": [
|
||
"See this run in LangSmith here https://smith.langchain.com/public/8262c0f9-0701-4d73-95f6-2a32f6d3f96a/r\n",
|
||
"\n",
|
||
"This continues where we left off, with \"action\" node, followed by \"agent\" node, which terminates the execution."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "1c38c505-6cee-427f-9dcd-493a2ade7ebb",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Checking history\n",
|
||
"\n",
|
||
"Let's browse the history of this thread, from newest to oldest."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 27,
|
||
"id": "8578a66d-6489-4e03-8c23-fd0530278455",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"StateSnapshot(values={'messages': [HumanMessage(content='what is the weather in sf currently', id='7e198f29-a371-49d5-86df-7e0e0b5a9144'), AIMessage(content='', additional_kwargs={'tool_calls': [{'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'search'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin', 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'logprobs': None, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'token_usage': {'completion_tokens': 16, 'prompt_tokens': 56, 'total_tokens': 72}}, id='run-0e6d8103-a92e-461d-aa99-8a68f4c99366-0', tool_calls=[{'name': 'search', 'args': {'query': 'weather in San Francisco today'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin'}]), ToolMessage(content='[\"The weather is cloudy with a chance of meatballs.\"]', name='search', id='9dce802a-9811-491f-a1d5-ace400fbcba0', tool_call_id='call_sxtKypVZlFrjzdOFYiCh8kin'), AIMessage(content='The weather in San Francisco is currently cloudy with a chance of meatballs.', response_metadata={'finish_reason': 'stop', 'logprobs': None, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'token_usage': {'completion_tokens': 16, 'prompt_tokens': 92, 'total_tokens': 108}}, id='run-269cd84d-c5ba-438b-9abf-1d1389bed733-0')]}, next=(), config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:25.872512+00:00'}}, metadata={'source': 'loop', 'step': 4}, parent_config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:25.228389+00:00'}})\n",
|
||
"--\n",
|
||
"StateSnapshot(values={'messages': [HumanMessage(content='what is the weather in sf currently', id='7e198f29-a371-49d5-86df-7e0e0b5a9144'), AIMessage(content='', additional_kwargs={'tool_calls': [{'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'search'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin', 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'logprobs': None, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'token_usage': {'completion_tokens': 16, 'prompt_tokens': 56, 'total_tokens': 72}}, id='run-0e6d8103-a92e-461d-aa99-8a68f4c99366-0', tool_calls=[{'name': 'search', 'args': {'query': 'weather in San Francisco today'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin'}]), ToolMessage(content='[\"The weather is cloudy with a chance of meatballs.\"]', name='search', id='9dce802a-9811-491f-a1d5-ace400fbcba0', tool_call_id='call_sxtKypVZlFrjzdOFYiCh8kin')]}, next=('agent',), config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:25.228389+00:00'}}, metadata={'source': 'loop', 'step': 3}, parent_config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:25.205012+00:00'}})\n",
|
||
"--\n",
|
||
"StateSnapshot(values={'messages': [HumanMessage(content='what is the weather in sf currently', id='7e198f29-a371-49d5-86df-7e0e0b5a9144'), AIMessage(content='', additional_kwargs={'tool_calls': [{'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'search'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin', 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'logprobs': None, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'token_usage': {'completion_tokens': 16, 'prompt_tokens': 56, 'total_tokens': 72}}, id='run-0e6d8103-a92e-461d-aa99-8a68f4c99366-0', tool_calls=[{'name': 'search', 'args': {'query': 'weather in San Francisco today'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin'}])]}, next=('action',), config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:25.205012+00:00'}}, metadata={'source': 'update', 'step': 2}, parent_config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:25.186985+00:00'}})\n",
|
||
"--\n",
|
||
"StateSnapshot(values={'messages': [HumanMessage(content='what is the weather in sf currently', id='7e198f29-a371-49d5-86df-7e0e0b5a9144'), AIMessage(content='', additional_kwargs={'tool_calls': [{'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'search'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin', 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'logprobs': None, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'token_usage': {'completion_tokens': 16, 'prompt_tokens': 56, 'total_tokens': 72}}, id='run-0e6d8103-a92e-461d-aa99-8a68f4c99366-0', tool_calls=[{'name': 'search', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin'}])]}, next=('action',), config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:25.186985+00:00'}}, metadata={'source': 'loop', 'step': 1}, parent_config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:24.675950+00:00'}})\n",
|
||
"--\n",
|
||
"StateSnapshot(values={'messages': [HumanMessage(content='what is the weather in sf currently', id='7e198f29-a371-49d5-86df-7e0e0b5a9144')]}, next=('agent',), config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:24.675950+00:00'}}, metadata={'source': 'loop', 'step': 0}, parent_config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:24.672976+00:00'}})\n",
|
||
"--\n",
|
||
"StateSnapshot(values={'messages': []}, next=('__start__',), config={'configurable': {'thread_id': '4', 'thread_ts': '2024-05-07T17:30:24.672976+00:00'}}, metadata={'source': 'input', 'step': -1}, parent_config=None)\n",
|
||
"--\n"
|
||
]
|
||
}
|
||
],
|
||
"source": ["for state in app_w_interrupt.get_state_history(config):\n print(state)\n print(\"--\")\n if len(state.values[\"messages\"]) == 2:\n to_replay = state"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0ec41c37-7c09-4cc7-8475-bf373fe66584",
|
||
"metadata": {},
|
||
"source": [
|
||
"We can go back to any of these states and restart the agent from there!"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 28,
|
||
"id": "21e7fc18-6fd9-4e11-a84b-e0325c9640c8",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"{'messages': [HumanMessage(content='what is the weather in sf currently', id='7e198f29-a371-49d5-86df-7e0e0b5a9144'),\n",
|
||
" AIMessage(content='', additional_kwargs={'tool_calls': [{'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'search'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin', 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'logprobs': None, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'token_usage': {'completion_tokens': 16, 'prompt_tokens': 56, 'total_tokens': 72}}, id='run-0e6d8103-a92e-461d-aa99-8a68f4c99366-0', tool_calls=[{'name': 'search', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_sxtKypVZlFrjzdOFYiCh8kin'}])]}"
|
||
]
|
||
},
|
||
"execution_count": 28,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["to_replay.values"]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 29,
|
||
"id": "d4b01634-0041-4632-8d1f-5464580e54f5",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"('action',)"
|
||
]
|
||
},
|
||
"execution_count": 29,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["to_replay.next"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "29da43ea-9295-43e2-b164-0eb28d96749c",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Replay a past state\n",
|
||
"\n",
|
||
"To replay from this place we just need to pass its config back to the agent."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 30,
|
||
"id": "e986f94f-706f-4b6f-b3c4-f95483b9e9b8",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{'messages': [ToolMessage(content='[\"The weather is cloudy with a chance of meatballs.\"]', name='search', id='71e6f2b9-46cf-4629-a0e2-fda37da9a3bb', tool_call_id='call_sxtKypVZlFrjzdOFYiCh8kin')]}\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{'messages': AIMessage(content='The weather in San Francisco is currently cloudy with a chance of meatballs.', response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 91, 'total_tokens': 107}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-d2ed2496-271f-4353-8f9c-3fb3157a4f63-0')}\n"
|
||
]
|
||
}
|
||
],
|
||
"source": ["for event in app_w_interrupt.stream(None, to_replay.config):\n for v in event.values():\n print(v)"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "8b4b81dd-1108-4ab7-b680-3f641b3efa70",
|
||
"metadata": {},
|
||
"source": [
|
||
"See this run in LangSmith here https://smith.langchain.com/public/f26e9e1d-16df-48ae-98f7-c823d6942bf7/r\n",
|
||
"\n",
|
||
"This is similar to the previous run, this time with the original search query, instead of our modified one. "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "59910951-fae1-4475-8511-f622439b590d",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Branch off a past state\n",
|
||
"\n",
|
||
"Using LangGraph's checkpointing, you can do more than just replay past states. You can branch off previous locations to let the agent explore alternate trajectories or to let a user \"version control\" changes in a workflow."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 31,
|
||
"id": "b084f141-5800-487b-b115-d2e58421b963",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["from langchain_core.messages import AIMessage\n\nbranch_config = app_w_interrupt.update_state(\n to_replay.config,\n {\n \"messages\": [\n AIMessage(content=\"All done here!\", id=to_replay.values[\"messages\"][-1].id)\n ]\n },\n)"]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 32,
|
||
"id": "1a7cfcd4-289e-419e-8b49-dfaef4f88641",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": ["branch_state = app_w_interrupt.get_state(branch_config)"]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 33,
|
||
"id": "5198f9c1-d2d4-458a-993d-3caa55810b1e",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"{'messages': [HumanMessage(content='what is the weather in sf currently', id='7e198f29-a371-49d5-86df-7e0e0b5a9144'),\n",
|
||
" AIMessage(content='All done here!', id='run-0e6d8103-a92e-461d-aa99-8a68f4c99366-0')]}"
|
||
]
|
||
},
|
||
"execution_count": 33,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["branch_state.values"]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 34,
|
||
"id": "5d89d55d-db84-4c2d-828b-64a29a69947b",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"()"
|
||
]
|
||
},
|
||
"execution_count": 34,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": ["branch_state.next"]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "cc168c90-a374-4280-a9a6-8bc232dbb006",
|
||
"metadata": {},
|
||
"source": [
|
||
"You can see the snapshot was updated and now correctly reflects that there is no next step.\n",
|
||
"\n",
|
||
"You can see this in LangSmith update run here https://smith.langchain.com/public/65104717-6eda-4a0f-93c1-4755c6f929ed/r\n",
|
||
"\n",
|
||
"This shows the \"should_continue\" edge now reacting to this replaced message, and now changing the outcome to \"end\" which finishes the computation."
|
||
]
|
||
}
|
||
],
|
||
"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.12.2"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|