mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-03 05:28:43 +02:00
Also, remove comment from bash script that makes insertion of `uv` harder
49 KiB
49 KiB
In [1]:
%%capture --no-stderr
%pip install --quiet -U langgraph langchain_anthropicIn [ ]:
import getpass
import os
def _set_env(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"{var}: ")
_set_env("ANTHROPIC_API_KEY")In [3]:
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
# highlight-next-line
from langgraph.types import Command, interrupt
from langgraph.checkpoint.memory import InMemorySaver
from IPython.display import Image, display
class State(TypedDict):
input: str
user_feedback: str
def step_1(state):
print("---Step 1---")
pass
def human_feedback(state):
print("---human_feedback---")
# highlight-next-line
feedback = interrupt("Please provide feedback:")
return {"user_feedback": feedback}
def step_3(state):
print("---Step 3---")
pass
builder = StateGraph(State)
builder.add_node("step_1", step_1)
builder.add_node("human_feedback", human_feedback)
builder.add_node("step_3", step_3)
builder.add_edge(START, "step_1")
builder.add_edge("step_1", "human_feedback")
builder.add_edge("human_feedback", "step_3")
builder.add_edge("step_3", END)
# Set up memory
memory = InMemorySaver()
# Add
graph = builder.compile(checkpointer=memory)
# View
display(Image(graph.get_graph().draw_mermaid_png()))In [4]:
# Input
initial_input = {"input": "hello world"}
# Thread
thread = {"configurable": {"thread_id": "1"}}
# Run the graph until the first interruption
for event in graph.stream(initial_input, thread, stream_mode="updates"):
print(event)
print("\n")---Step 1---
{'step_1': None}
---human_feedback---
{'__interrupt__': (Interrupt(value='Please provide feedback:', resumable=True, ns=['human_feedback:c723d73a-d2cb-32cf-452f-e147367868bd']),)}
In [5]:
# Continue the graph execution
for event in graph.stream(
# highlight-next-line
Command(resume="go to step 3!"),
thread,
stream_mode="updates",
):
print(event)
print("\n")---human_feedback---
{'human_feedback': {'user_feedback': 'go to step 3!'}}
---Step 3---
{'step_3': None}
In [6]:
graph.get_state(thread).valuesOut [6]:
{'input': 'hello world', 'user_feedback': 'go to step 3!'}In [7]:
# Set up the state
from langgraph.graph import MessagesState, START
# Set up the tool
# We will have one real tool - a search tool
# We'll also have one "fake" tool - a "ask_human" tool
# Here we define any ACTUAL tools
from langchain_core.tools import tool
from langgraph.prebuilt import ToolNode
@tool
def search(query: str):
"""Call to surf the web."""
# This is a placeholder for the actual implementation
# Don't let the LLM know this though 😊
return f"I looked up: {query}. Result: It's sunny in San Francisco, but you better look out if you're a Gemini 😈."
tools = [search]
tool_node = ToolNode(tools)
# Set up the model
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(model="claude-3-5-sonnet-latest")
from pydantic import BaseModel
# We are going "bind" all tools to the model
# We have the ACTUAL tools from above, but we also need a mock tool to ask a human
# Since `bind_tools` takes in tools but also just tool definitions,
# We can define a tool definition for `ask_human`
class AskHuman(BaseModel):
"""Ask the human a question"""
question: str
model = model.bind_tools(tools + [AskHuman])
# Define nodes and conditional edges
# Define the function that determines whether to continue or not
def should_continue(state):
messages = state["messages"]
last_message = messages[-1]
# If there is no function call, then we finish
if not last_message.tool_calls:
return END
# If tool call is asking Human, we return that node
# You could also add logic here to let some system know that there's something that requires Human input
# For example, send a slack message, etc
elif last_message.tool_calls[0]["name"] == "AskHuman":
return "ask_human"
# Otherwise if there is, we continue
else:
return "action"
# Define the function that calls the model
def call_model(state):
messages = state["messages"]
response = model.invoke(messages)
# We return a list, because this will get added to the existing list
return {"messages": [response]}
# We define a fake node to ask the human
def ask_human(state):
tool_call_id = state["messages"][-1].tool_calls[0]["id"]
ask = AskHuman.model_validate(state["messages"][-1].tool_calls[0]["args"])
# highlight-next-line
location = interrupt(ask.question)
tool_message = [{"tool_call_id": tool_call_id, "type": "tool", "content": location}]
return {"messages": tool_message}
# Build the graph
from langgraph.graph import END, StateGraph
# Define a new graph
workflow = StateGraph(MessagesState)
# Define the three nodes we will cycle between
workflow.add_node("agent", call_model)
workflow.add_node("action", tool_node)
workflow.add_node("ask_human", ask_human)
# Set the entrypoint as `agent`
# This means that this node is the first one called
workflow.add_edge(START, "agent")
# We now add a conditional edge
workflow.add_conditional_edges(
# First, we define the start node. We use `agent`.
# This means these are the edges taken after the `agent` node is called.
"agent",
# Next, we pass in the function that will determine which node is called next.
should_continue,
path_map=["ask_human", "action", END],
)
# We now add a normal edge from `tools` to `agent`.
# This means that after `tools` is called, `agent` node is called next.
workflow.add_edge("action", "agent")
# After we get back the human response, we go back to the agent
workflow.add_edge("ask_human", "agent")
# Set up memory
from langgraph.checkpoint.memory import InMemorySaver
memory = InMemorySaver()
# Finally, we compile it!
# This compiles it into a LangChain Runnable,
# meaning you can use it as you would any other runnable
app = workflow.compile(checkpointer=memory)
display(Image(app.get_graph().draw_mermaid_png()))In [8]:
config = {"configurable": {"thread_id": "2"}}
for event in app.stream(
{
"messages": [
(
"user",
"Ask the user where they are, then look up the weather there",
)
]
},
config,
stream_mode="values",
):
if "messages" in event:
event["messages"][-1].pretty_print()================================[1m Human Message [0m================================= Ask the user where they are, then look up the weather there ==================================[1m Ai Message [0m================================== [{'text': "I'll help you with that. Let me first ask the user about their location.", 'type': 'text'}, {'id': 'toolu_012Z9yyZjvH8xKgMShgwpQZ9', 'input': {'question': 'Where are you located?'}, 'name': 'AskHuman', 'type': 'tool_use'}] Tool Calls: AskHuman (toolu_012Z9yyZjvH8xKgMShgwpQZ9) Call ID: toolu_012Z9yyZjvH8xKgMShgwpQZ9 Args: question: Where are you located?
In [9]:
app.get_state(config).nextOut [9]:
('ask_human',)In [10]:
for event in app.stream(
# highlight-next-line
Command(resume="san francisco"),
config,
stream_mode="values",
):
if "messages" in event:
event["messages"][-1].pretty_print()==================================[1m Ai Message [0m================================== [{'text': "I'll help you with that. Let me first ask the user about their location.", 'type': 'text'}, {'id': 'toolu_012Z9yyZjvH8xKgMShgwpQZ9', 'input': {'question': 'Where are you located?'}, 'name': 'AskHuman', 'type': 'tool_use'}] Tool Calls: AskHuman (toolu_012Z9yyZjvH8xKgMShgwpQZ9) Call ID: toolu_012Z9yyZjvH8xKgMShgwpQZ9 Args: question: Where are you located? =================================[1m Tool Message [0m================================= san francisco ==================================[1m Ai Message [0m================================== [{'text': "Now I'll search for the weather in San Francisco.", 'type': 'text'}, {'id': 'toolu_01QrWBCDouvBuJPZa4veepLw', 'input': {'query': 'current weather in san francisco'}, 'name': 'search', 'type': 'tool_use'}] Tool Calls: search (toolu_01QrWBCDouvBuJPZa4veepLw) Call ID: toolu_01QrWBCDouvBuJPZa4veepLw Args: query: current weather in san francisco =================================[1m Tool Message [0m================================= Name: search I looked up: current weather in san francisco. Result: It's sunny in San Francisco, but you better look out if you're a Gemini 😈. ==================================[1m Ai Message [0m================================== Based on the search results, it's currently sunny in San Francisco. Would you like more specific weather details?