mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-08 18:57:52 +02:00
docs: update recursion notebook to use RemainingSteps (#2114)
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -70,12 +70,12 @@
|
||||
"source": [
|
||||
"## Without returning state\n",
|
||||
"\n",
|
||||
"We are going to define a dummy graph in this example that will always hit the recursion limit. First, we will implement it without returning the state and show that it hits the recursion limit. This graph is based on the ReACT architecture, but instead of actually making decisions and taking actions it just loops forever."
|
||||
"We are going to define a dummy graph in this example that will always hit the recursion limit. First, we will implement it without returning the state and show that it hits the recursion limit. This graph is based on the ReAct architecture, but instead of actually making decisions and taking actions it just loops forever."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -145,7 +145,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -171,18 +171,18 @@
|
||||
"source": [
|
||||
"## With returning state\n",
|
||||
"\n",
|
||||
"If we wanted to actually return the state, what we are going to do is introduce a new key to our state called `is_last_step` which keeps track of if we are on the last step of our recursion limit. If so, we will bypass all other graph decisions and simply terminate the graph, returning the state to the user without causing an error.\n",
|
||||
"To avoid hitting the recursion limit, we can introduce a new key to our state called `remaining_steps`. It will keep track of number of steps until reaching the recursion limit. We can then check the value of `remaining_steps` to determine whether we should terminate the graph execution and return the state to the user without causing the `RecursionError`.\n",
|
||||
"\n",
|
||||
"We are going to use a `ManagedValue` channel to do this. A `ManagedValue` channel is a state channel that will exist for the duration of our graph run and no longer. Since our `action` node is going to always induce at least 2 extra steps to our graph (since the `action` node ALWAYS calls the `decision` node afterwards), we will use this channel to check if we are within 2 steps of the limit. See the implementation of `IsLastOrSecondToLastStepManager` below.\n",
|
||||
"To do so, we will use a special `RemainingSteps` annotation. Under the hood, it creates a special `ManagedValue` channel -- a state channel that will exist for the duration of our graph run and no longer.\n",
|
||||
"\n",
|
||||
"This implementation very closely mirrors the implementation of `isLastStep` (which you can use by calling `from langgraph.managed import IsLastStep` and then decorating state keys with the `isLastStep` type), but in this case we check if we are on the last OR second-to-last step, instead of just the last step.\n",
|
||||
"Since our `action` node is going to always induce at least 2 extra steps to our graph (since the `action` node ALWAYS calls the `decision` node afterwards), we will use this channel to check if we are within 2 steps of the limit.\n",
|
||||
"\n",
|
||||
"Now, when we run our graph we should receive no errors and instead get the last value of the state before the recursion limit was hit."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -190,24 +190,18 @@
|
||||
"from langgraph.graph import StateGraph\n",
|
||||
"from typing import Annotated\n",
|
||||
"\n",
|
||||
"from langgraph.managed.base import ManagedValue\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class IsLastOrSecondToLastStepManager(ManagedValue[bool]):\n",
|
||||
" def __call__(self, step: int) -> bool:\n",
|
||||
" limit = self.config.get(\"recursion_limit\", 0)\n",
|
||||
" return step >= limit - 2\n",
|
||||
"from langgraph.managed.is_last_step import RemainingSteps\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class State(TypedDict):\n",
|
||||
" value: str\n",
|
||||
" action_result: str\n",
|
||||
" is_last_step: Annotated[bool, IsLastOrSecondToLastStepManager]\n",
|
||||
" remaining_steps: RemainingSteps\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def router(state: State):\n",
|
||||
" # Force the agent to end if it is on the last step\n",
|
||||
" if state[\"is_last_step\"]:\n",
|
||||
" # Force the agent to end\n",
|
||||
" if state[\"remaining_steps\"] <= 2:\n",
|
||||
" return END\n",
|
||||
" if state[\"value\"] == \"end\":\n",
|
||||
" return END\n",
|
||||
@@ -235,7 +229,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -244,7 +238,7 @@
|
||||
"{'value': 'keep going!', 'action_result': 'what a great result!'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -277,7 +271,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
"version": "3.11.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
Reference in New Issue
Block a user