mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-22 15:42:25 +02:00
stash
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "aa9f9110-ea74-43af-aa72-6b45518abd6e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"from langgraph.prebuilt.messages_executor import create_messages_executor\n",
|
||||
"from langchain_core.messages import HumanMessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "0f005b2d-ddc0-4d60-8af9-9eb3dbeb45b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tools = [TavilySearchResults(max_results=1)]\n",
|
||||
"model = ChatOpenAI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "02831581-f1ba-4701-ba33-f0f68468907d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.tools.render import format_tool_to_openai_function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "a5c0ca12-4922-461c-b740-62ff99f8ae56",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = model.bind_functions([format_tool_to_openai_function(t) for t in tools])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "57547622-ddd8-4179-aa4a-e1b69ca4523c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.prebuilt.tool_executor import ToolExecutor\n",
|
||||
"tool_executor = ToolExecutor(tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "96141ebd-32af-4c9d-a0b0-f46482b8bb88",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import TypedDict, Annotated, Sequence\n",
|
||||
"import operator\n",
|
||||
"from langchain_core.messages import BaseMessage\n",
|
||||
"# We create the AgentState that we will pass around\n",
|
||||
"# This simply involves a list of messages\n",
|
||||
"# We want steps to return messages to append to the list\n",
|
||||
"# So we annotate the messages attribute with operator.add\n",
|
||||
"class AgentState(TypedDict):\n",
|
||||
" messages: Annotated[Sequence[BaseMessage], operator.add]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "f422dc99-672e-414f-88b3-1a6dd21b6882",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.agents import AgentAction\n",
|
||||
"import json\n",
|
||||
"from langchain_core.messages import FunctionMessage\n",
|
||||
"\n",
|
||||
"# Define the function that determines whether to continue or not\n",
|
||||
"def should_continue(state):\n",
|
||||
" messages = state['messages']\n",
|
||||
" last_message = messages[-1]\n",
|
||||
" # If there is no function call, then we finish\n",
|
||||
" if \"function_call\" not in last_message.additional_kwargs:\n",
|
||||
" return \"end\"\n",
|
||||
" # Otherwise if there is, we continue\n",
|
||||
" else:\n",
|
||||
" return \"continue\"\n",
|
||||
"\n",
|
||||
"# Define the function that calls the model\n",
|
||||
"def call_model(state):\n",
|
||||
" messages = state['messages']\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",
|
||||
"# Define the function to execute tools\n",
|
||||
"def call_tool(state):\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 AgentAction from the function_call\n",
|
||||
" action = AgentAction(\n",
|
||||
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
|
||||
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
|
||||
" log=\"\",\n",
|
||||
" )\n",
|
||||
" # We call the tool_executor and get back a response\n",
|
||||
" response = tool_executor.invoke(action)\n",
|
||||
" # We use the response to create a FunctionMessage\n",
|
||||
" function_message = FunctionMessage(content=str(response), name=action.tool)\n",
|
||||
" # We return a list, because this will get added to the existing list\n",
|
||||
" return {\"messages\": [function_message]}\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "1133ec83-7af9-4444-9f88-c793fbdce214",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.graph import StateGraph, END\n",
|
||||
"# Define a new graph\n",
|
||||
"workflow = StateGraph(AgentState)\n",
|
||||
"\n",
|
||||
"# Define the two nodes we will cycle between\n",
|
||||
"workflow.add_node(\"agent\", call_model)\n",
|
||||
"workflow.add_node(\"action\", call_tool)\n",
|
||||
"\n",
|
||||
"# Set the entrypoint as `agent`\n",
|
||||
"# This means that this node is the first one called\n",
|
||||
"workflow.set_entry_point(\"agent\")\n",
|
||||
"\n",
|
||||
"# We now add a conditional edge\n",
|
||||
"workflow.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.\n",
|
||||
"workflow.add_edge('action', 'agent')\n",
|
||||
"\n",
|
||||
"# Finally, we compile it!\n",
|
||||
"# This compiles it into a LangChain Runnable,\n",
|
||||
"# meaning you can use it as you would any other runnable\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "289c648f-bfc6-464f-8df9-50be8b1b9e48",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}})]}\n",
|
||||
"----\n",
|
||||
"{'messages': [FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [AIMessage(content='The weather in San Francisco is currently not available. However, you can check the weather history for January 2024 in San Francisco on [weatherspark.com](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [HumanMessage(content='what is the weather in sf'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}}), FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json'), AIMessage(content='The weather in San Francisco is currently not available. However, you can check the weather history for January 2024 in San Francisco on [weatherspark.com](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).')]}\n",
|
||||
"----\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
||||
"for s in app.stream(inputs):\n",
|
||||
" print(list(s.values())[0])\n",
|
||||
" print(\"----\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4cd4b39f-831a-4818-bf56-99cf301b0555",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "aa9f9110-ea74-43af-aa72-6b45518abd6e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"from langgraph.prebuilt.messages_executor import create_messages_executor\n",
|
||||
"from langchain_core.messages import HumanMessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "75bbcaa8-b23a-409c-9745-08d3aca7c3cb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"\n",
|
||||
"class SearchTool(search_tool.args_schema):\n",
|
||||
" \"\"\"Look up things online, optionally returning directly\"\"\"\n",
|
||||
" query: str = Field(description=\"query to look up online\")\n",
|
||||
" return_direct: bool = Field(\n",
|
||||
" description=\"Whether or the result of this should be returned directly to the user without you seeing what it is\", \n",
|
||||
" default = False\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "0c93c6e1-532b-472e-b9f7-d374b9d3c89e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"search_tool = TavilySearchResults(max_results=1, args_schema=SearchTool)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "0f005b2d-ddc0-4d60-8af9-9eb3dbeb45b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tools = [search_tool]\n",
|
||||
"model = ChatOpenAI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "02831581-f1ba-4701-ba33-f0f68468907d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.tools.render import format_tool_to_openai_function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "a5c0ca12-4922-461c-b740-62ff99f8ae56",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = model.bind_functions([format_tool_to_openai_function(t) for t in tools])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "57547622-ddd8-4179-aa4a-e1b69ca4523c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.prebuilt.tool_executor import ToolExecutor\n",
|
||||
"tool_executor = ToolExecutor(tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "96141ebd-32af-4c9d-a0b0-f46482b8bb88",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import TypedDict, Annotated, Sequence\n",
|
||||
"import operator\n",
|
||||
"from langchain_core.messages import BaseMessage\n",
|
||||
"# We create the AgentState that we will pass around\n",
|
||||
"# This simply involves a list of messages\n",
|
||||
"# We want steps to return messages to append to the list\n",
|
||||
"# So we annotate the messages attribute with operator.add\n",
|
||||
"class AgentState(TypedDict):\n",
|
||||
" messages: Annotated[Sequence[BaseMessage], operator.add]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "f422dc99-672e-414f-88b3-1a6dd21b6882",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.agents import AgentAction\n",
|
||||
"import json\n",
|
||||
"from langchain_core.messages import FunctionMessage\n",
|
||||
"\n",
|
||||
"# Define the function that determines whether to continue or not\n",
|
||||
"def should_continue(state):\n",
|
||||
" messages = state['messages']\n",
|
||||
" last_message = messages[-1]\n",
|
||||
" # If there is no function call, then we finish\n",
|
||||
" if \"function_call\" not in last_message.additional_kwargs:\n",
|
||||
" return \"end\"\n",
|
||||
" # Otherwise if there is, we check if it's suppose to return direct\n",
|
||||
" else:\n",
|
||||
" arguments = json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"])\n",
|
||||
" if arguments.get(\"return_direct\", False):\n",
|
||||
" return \"final\"\n",
|
||||
" else:\n",
|
||||
" return \"continue\"\n",
|
||||
"\n",
|
||||
"# Define the function that calls the model\n",
|
||||
"def call_model(state):\n",
|
||||
" messages = state['messages']\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",
|
||||
"# Define the function to execute tools\n",
|
||||
"def call_tool(state):\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",
|
||||
" tool_name = last_message.additional_kwargs[\"function_call\"][\"name\"]\n",
|
||||
" arguments = json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"])\n",
|
||||
" if tool_name == \"tavily_search_results_json\":\n",
|
||||
" if \"return_direct\" in arguments:\n",
|
||||
" del arguments[\"return_direct\"]\n",
|
||||
" # We construct an AgentAction from the function_call\n",
|
||||
" action = AgentAction(\n",
|
||||
" tool=tool_name,\n",
|
||||
" tool_input=arguments,\n",
|
||||
" log=\"\",\n",
|
||||
" )\n",
|
||||
" # We call the tool_executor and get back a response\n",
|
||||
" response = tool_executor.invoke(action)\n",
|
||||
" # We use the response to create a FunctionMessage\n",
|
||||
" function_message = FunctionMessage(content=str(response), name=action.tool)\n",
|
||||
" # We return a list, because this will get added to the existing list\n",
|
||||
" return {\"messages\": [function_message]}\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "1133ec83-7af9-4444-9f88-c793fbdce214",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.graph import StateGraph, END\n",
|
||||
"# Define a new graph\n",
|
||||
"workflow = StateGraph(AgentState)\n",
|
||||
"\n",
|
||||
"# Define the two nodes we will cycle between\n",
|
||||
"workflow.add_node(\"agent\", call_model)\n",
|
||||
"workflow.add_node(\"action\", call_tool)\n",
|
||||
"workflow.add_node(\"final\", call_tool)\n",
|
||||
"\n",
|
||||
"# Set the entrypoint as `agent`\n",
|
||||
"# This means that this node is the first one called\n",
|
||||
"workflow.set_entry_point(\"agent\")\n",
|
||||
"\n",
|
||||
"# We now add a conditional edge\n",
|
||||
"workflow.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",
|
||||
" # Final call\n",
|
||||
" \"final\": \"final\",\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.\n",
|
||||
"workflow.add_edge('action', 'agent')\n",
|
||||
"workflow.add_edge('final', END)\n",
|
||||
"\n",
|
||||
"# Finally, we compile it!\n",
|
||||
"# This compiles it into a LangChain Runnable,\n",
|
||||
"# meaning you can use it as you would any other runnable\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "289c648f-bfc6-464f-8df9-50be8b1b9e48",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}})]}\n",
|
||||
"----\n",
|
||||
"{'messages': [FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [AIMessage(content='The current weather in San Francisco is not available. However, you can check the weather history for January 2024 in San Francisco [here](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [HumanMessage(content='what is the weather in sf'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}}), FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json'), AIMessage(content='The current weather in San Francisco is not available. However, you can check the weather history for January 2024 in San Francisco [here](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).')]}\n",
|
||||
"----\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
||||
"for s in app.stream(inputs):\n",
|
||||
" print(list(s.values())[0])\n",
|
||||
" print(\"----\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"id": "4cd4b39f-831a-4818-bf56-99cf301b0555",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\",\\n \"return_direct\": true\\n}', 'name': 'tavily_search_results_json'}})]}\n",
|
||||
"----\n",
|
||||
"{'messages': [FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [HumanMessage(content='what is the weather in sf? return this result directly by setting return_direct = True'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\",\\n \"return_direct\": true\\n}', 'name': 'tavily_search_results_json'}}), FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf? return this result directly by setting return_direct = True\")]}\n",
|
||||
"for s in app.stream(inputs):\n",
|
||||
" print(list(s.values())[0])\n",
|
||||
" print(\"----\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "2d2e89d8-0e35-465c-8984-1aa37a132b03",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "aa9f9110-ea74-43af-aa72-6b45518abd6e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"from langgraph.prebuilt.messages_executor import create_messages_executor\n",
|
||||
"from langchain_core.messages import HumanMessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "0f005b2d-ddc0-4d60-8af9-9eb3dbeb45b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tools = [TavilySearchResults(max_results=1)]\n",
|
||||
"model = ChatOpenAI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "02831581-f1ba-4701-ba33-f0f68468907d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.tools.render import format_tool_to_openai_function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "a5c0ca12-4922-461c-b740-62ff99f8ae56",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = model.bind_functions([format_tool_to_openai_function(t) for t in tools])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "57547622-ddd8-4179-aa4a-e1b69ca4523c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.prebuilt.tool_executor import ToolExecutor\n",
|
||||
"tool_executor = ToolExecutor(tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "96141ebd-32af-4c9d-a0b0-f46482b8bb88",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import TypedDict, Annotated, Sequence\n",
|
||||
"import operator\n",
|
||||
"from langchain_core.messages import BaseMessage\n",
|
||||
"# We create the AgentState that we will pass around\n",
|
||||
"# This simply involves a list of messages\n",
|
||||
"# We want steps to return messages to append to the list\n",
|
||||
"# So we annotate the messages attribute with operator.add\n",
|
||||
"class AgentState(TypedDict):\n",
|
||||
" messages: Annotated[Sequence[BaseMessage], operator.add]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f422dc99-672e-414f-88b3-1a6dd21b6882",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.agents import AgentAction\n",
|
||||
"import json\n",
|
||||
"from langchain_core.messages import FunctionMessage\n",
|
||||
"\n",
|
||||
"# Define the function that determines whether to continue or not\n",
|
||||
"def should_continue(state):\n",
|
||||
" messages = state['messages']\n",
|
||||
" last_message = messages[-1]\n",
|
||||
" # If there is no function call, then we finish\n",
|
||||
" if \"function_call\" not in last_message.additional_kwargs:\n",
|
||||
" return \"end\"\n",
|
||||
" # Otherwise if there is, we continue\n",
|
||||
" else:\n",
|
||||
" return \"continue\"\n",
|
||||
"\n",
|
||||
"# Define the function that calls the model\n",
|
||||
"def call_model(state):\n",
|
||||
" messages = state['messages']\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",
|
||||
"# Define the function to execute tools\n",
|
||||
"def call_tool(state):\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 AgentAction from the function_call\n",
|
||||
" action = AgentAction(\n",
|
||||
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
|
||||
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
|
||||
" log=\"\",\n",
|
||||
" )\n",
|
||||
" # We call the tool_executor and get back a response\n",
|
||||
" response = tool_executor.invoke(action)\n",
|
||||
" # We use the response to create a FunctionMessage\n",
|
||||
" function_message = FunctionMessage(content=str(response), name=action.tool)\n",
|
||||
" # We return a list, because this will get added to the existing list\n",
|
||||
" return {\"messages\": [function_message]}\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "47bf79f1-652c-43dc-aeb3-0f0de4400539",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'tavily_search_results_json'"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tools[0].name"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "ab62da37-9632-4dc8-833b-feb4c62bab37",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This is the new first - the first call of the model we want to explicitly hard-code some action\n",
|
||||
"from langchain_core.messages import AIMessage\n",
|
||||
"import json\n",
|
||||
"\n",
|
||||
"def first_model(state):\n",
|
||||
" human_input = state['messages'][-1].content\n",
|
||||
" return {\"messages\": [AIMessage(\n",
|
||||
" content=\"\", \n",
|
||||
" additional_kwargs={\n",
|
||||
" \"function_call\": {\n",
|
||||
" \"name\": \"tavily_search_results_json\", \n",
|
||||
" \"arguments\": json.dumps({\"query\": human_input})\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
" ]}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "1133ec83-7af9-4444-9f88-c793fbdce214",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.graph import StateGraph, END\n",
|
||||
"# Define a new graph\n",
|
||||
"workflow = StateGraph(AgentState)\n",
|
||||
"\n",
|
||||
"# Define the new entrypoint\n",
|
||||
"workflow.add_node(\"first_agent\", first_model)\n",
|
||||
"\n",
|
||||
"# Define the two nodes we will cycle between\n",
|
||||
"workflow.add_node(\"agent\", call_model)\n",
|
||||
"workflow.add_node(\"action\", call_tool)\n",
|
||||
"\n",
|
||||
"# Set the entrypoint as `agent`\n",
|
||||
"# This means that this node is the first one called\n",
|
||||
"workflow.set_entry_point(\"first_agent\")\n",
|
||||
"\n",
|
||||
"# We now add a conditional edge\n",
|
||||
"workflow.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.\n",
|
||||
"workflow.add_edge('action', 'agent')\n",
|
||||
"\n",
|
||||
"# After we call the first agent, we know we want to go to action\n",
|
||||
"workflow.add_edge('first_agent', 'action')\n",
|
||||
"\n",
|
||||
"# Finally, we compile it!\n",
|
||||
"# This compiles it into a LangChain Runnable,\n",
|
||||
"# meaning you can use it as you would any other runnable\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "289c648f-bfc6-464f-8df9-50be8b1b9e48",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'name': 'tavily_search_results_json', 'arguments': '{\"query\": \"what is the weather in sf\"}'}})]}\n",
|
||||
"----\n",
|
||||
"{'messages': [FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [AIMessage(content=\"I'm sorry, but I couldn't find the current weather in San Francisco. However, you can visit [this link](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States) to see the historical weather data for January 2024 in San Francisco.\")]}\n",
|
||||
"----\n",
|
||||
"{'messages': [HumanMessage(content='what is the weather in sf'), AIMessage(content='', additional_kwargs={'function_call': {'name': 'tavily_search_results_json', 'arguments': '{\"query\": \"what is the weather in sf\"}'}}), FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json'), AIMessage(content=\"I'm sorry, but I couldn't find the current weather in San Francisco. However, you can visit [this link](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States) to see the historical weather data for January 2024 in San Francisco.\")]}\n",
|
||||
"----\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
||||
"for s in app.stream(inputs):\n",
|
||||
" print(list(s.values())[0])\n",
|
||||
" print(\"----\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4cd4b39f-831a-4818-bf56-99cf301b0555",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8bcd1a3d-7c50-4f58-be4e-1ed654aa33be",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Chat Executor: with function calling\n",
|
||||
"\n",
|
||||
"This notebook walks through an example creating a chat executor that uses function calling.\n",
|
||||
"This is useful for getting started quickly.\n",
|
||||
"However, it is highly likely you will want to customize the logic - for information on that, check out the other examples in this folder."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e130cf70-a30e-47d7-8fd5-464f1a92e374",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Set up the chat model and tools\n",
|
||||
"\n",
|
||||
"Here we will define the chat model and tools that we want to use.\n",
|
||||
"Importantly, this model MUST support OpenAI function calling."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "efb7e3c0-c63f-40f6-93ce-19681d650fc2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"from langgraph.prebuilt import chat_executor\n",
|
||||
"from langchain_core.messages import HumanMessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "a7025f33-3160-41cf-868b-17ebc916fb1d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tools = [TavilySearchResults(max_results=1)]\n",
|
||||
"model = ChatOpenAI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "43064805-2ac9-4b5a-850c-a68dd7282350",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Create executor\n",
|
||||
"\n",
|
||||
"We can now use the high level interface to create the executor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "32b4ae66-f667-4a8b-a602-503fd0effcd9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"app = chat_executor.create_function_calling_executor(model, tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d63dbfc7-a5c1-4a03-991c-f0789ba52c52",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can now invoke this executor. The input to this must be a dictionary with a single `messsages` key that contains a list of messages."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "0abc5655-d772-450c-832f-1fee1111a5f6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}})]}\n",
|
||||
"----\n",
|
||||
"{'messages': [FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San FranciscoThis report shows the past weather for San Francisco, providing a weather history for January 2024. It features all historical weather data series we have available, including the San Francisco temperature history for January 2024. You can drill down from year to month and even day level reports by clicking on the graphs.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [AIMessage(content=\"I couldn't find the current weather in San Francisco. However, you can check historical weather data for January 2024 [here](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).\")]}\n",
|
||||
"----\n",
|
||||
"{'messages': [HumanMessage(content='what is the weather in sf'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}}), FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San FranciscoThis report shows the past weather for San Francisco, providing a weather history for January 2024. It features all historical weather data series we have available, including the San Francisco temperature history for January 2024. You can drill down from year to month and even day level reports by clicking on the graphs.'}]\", name='tavily_search_results_json'), AIMessage(content=\"I couldn't find the current weather in San Francisco. However, you can check historical weather data for January 2024 [here](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).\")]}\n",
|
||||
"----\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
||||
"for s in app.stream(inputs):\n",
|
||||
" print(list(s.values())[0])\n",
|
||||
" print(\"----\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "87f147e3-f96f-4b96-a3cc-ec7affd7a57f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "aa9f9110-ea74-43af-aa72-6b45518abd6e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"from langgraph.prebuilt.messages_executor import create_messages_executor\n",
|
||||
"from langchain_core.messages import HumanMessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "0f005b2d-ddc0-4d60-8af9-9eb3dbeb45b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tools = [TavilySearchResults(max_results=1)]\n",
|
||||
"model = ChatOpenAI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "02831581-f1ba-4701-ba33-f0f68468907d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.tools.render import format_tool_to_openai_function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "a5c0ca12-4922-461c-b740-62ff99f8ae56",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = model.bind_functions([format_tool_to_openai_function(t) for t in tools])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "57547622-ddd8-4179-aa4a-e1b69ca4523c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.prebuilt.tool_executor import ToolExecutor\n",
|
||||
"tool_executor = ToolExecutor(tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "96141ebd-32af-4c9d-a0b0-f46482b8bb88",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import TypedDict, Annotated, Sequence\n",
|
||||
"import operator\n",
|
||||
"from langchain_core.messages import BaseMessage\n",
|
||||
"# We create the AgentState that we will pass around\n",
|
||||
"# This simply involves a list of messages\n",
|
||||
"# We want steps to return messages to append to the list\n",
|
||||
"# So we annotate the messages attribute with operator.add\n",
|
||||
"class AgentState(TypedDict):\n",
|
||||
" messages: Annotated[Sequence[BaseMessage], operator.add]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "f422dc99-672e-414f-88b3-1a6dd21b6882",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.agents import AgentAction\n",
|
||||
"import json\n",
|
||||
"from langchain_core.messages import FunctionMessage\n",
|
||||
"\n",
|
||||
"# Define the function that determines whether to continue or not\n",
|
||||
"def should_continue(state):\n",
|
||||
" messages = state['messages']\n",
|
||||
" last_message = messages[-1]\n",
|
||||
" # If there is no function call, then we finish\n",
|
||||
" if \"function_call\" not in last_message.additional_kwargs:\n",
|
||||
" return \"end\"\n",
|
||||
" # Otherwise if there is, we continue\n",
|
||||
" else:\n",
|
||||
" return \"continue\"\n",
|
||||
"\n",
|
||||
"# Define the function that calls the model\n",
|
||||
"def call_model(state):\n",
|
||||
" messages = state['messages']\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",
|
||||
"# Define the function to execute tools\n",
|
||||
"# Here we add some lines to add a human-in-the-loop\n",
|
||||
"def call_tool(state):\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 AgentAction from the function_call\n",
|
||||
" action = AgentAction(\n",
|
||||
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
|
||||
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
|
||||
" log=\"\",\n",
|
||||
" )\n",
|
||||
" response = input(prompt=f\"[y/n] continue with: {action}?\")\n",
|
||||
" if response == \"n\":\n",
|
||||
" raise ValueError\n",
|
||||
" # We call the tool_executor and get back a response\n",
|
||||
" response = tool_executor.invoke(action)\n",
|
||||
" # We use the response to create a FunctionMessage\n",
|
||||
" function_message = FunctionMessage(content=str(response), name=action.tool)\n",
|
||||
" # We return a list, because this will get added to the existing list\n",
|
||||
" return {\"messages\": [function_message]}\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "1133ec83-7af9-4444-9f88-c793fbdce214",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.graph import StateGraph, END\n",
|
||||
"# Define a new graph\n",
|
||||
"workflow = StateGraph(AgentState)\n",
|
||||
"\n",
|
||||
"# Define the two nodes we will cycle between\n",
|
||||
"workflow.add_node(\"agent\", call_model)\n",
|
||||
"workflow.add_node(\"action\", call_tool)\n",
|
||||
"\n",
|
||||
"# Set the entrypoint as `agent`\n",
|
||||
"# This means that this node is the first one called\n",
|
||||
"workflow.set_entry_point(\"agent\")\n",
|
||||
"\n",
|
||||
"# We now add a conditional edge\n",
|
||||
"workflow.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.\n",
|
||||
"workflow.add_edge('action', 'agent')\n",
|
||||
"\n",
|
||||
"# Finally, we compile it!\n",
|
||||
"# This compiles it into a LangChain Runnable,\n",
|
||||
"# meaning you can use it as you would any other runnable\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "289c648f-bfc6-464f-8df9-50be8b1b9e48",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}})]}\n",
|
||||
"----\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdin",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[y/n] continue with: tool='tavily_search_results_json' tool_input={'query': 'weather in San Francisco'} log=''? y\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [AIMessage(content='The weather in San Francisco is currently not available. However, you can check the historical weather data for San Francisco in January 2024 [here](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [HumanMessage(content='what is the weather in sf'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}}), FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json'), AIMessage(content='The weather in San Francisco is currently not available. However, you can check the historical weather data for San Francisco in January 2024 [here](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).')]}\n",
|
||||
"----\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
||||
"for s in app.stream(inputs):\n",
|
||||
" print(list(s.values())[0])\n",
|
||||
" print(\"----\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4cd4b39f-831a-4818-bf56-99cf301b0555",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "aa9f9110-ea74-43af-aa72-6b45518abd6e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"from langgraph.prebuilt.messages_executor import create_messages_executor\n",
|
||||
"from langchain_core.messages import HumanMessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "0f005b2d-ddc0-4d60-8af9-9eb3dbeb45b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tools = [TavilySearchResults(max_results=1)]\n",
|
||||
"model = ChatOpenAI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "02831581-f1ba-4701-ba33-f0f68468907d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.tools.render import format_tool_to_openai_function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "a5c0ca12-4922-461c-b740-62ff99f8ae56",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = model.bind_functions([format_tool_to_openai_function(t) for t in tools])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "57547622-ddd8-4179-aa4a-e1b69ca4523c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.prebuilt.tool_executor import ToolExecutor\n",
|
||||
"tool_executor = ToolExecutor(tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "96141ebd-32af-4c9d-a0b0-f46482b8bb88",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import TypedDict, Annotated, Sequence\n",
|
||||
"import operator\n",
|
||||
"from langchain_core.messages import BaseMessage\n",
|
||||
"# We create the AgentState that we will pass around\n",
|
||||
"# This simply involves a list of messages\n",
|
||||
"# We want steps to return messages to append to the list\n",
|
||||
"# So we annotate the messages attribute with operator.add\n",
|
||||
"class AgentState(TypedDict):\n",
|
||||
" messages: Annotated[Sequence[BaseMessage], operator.add]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "f422dc99-672e-414f-88b3-1a6dd21b6882",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.agents import AgentAction\n",
|
||||
"import json\n",
|
||||
"from langchain_core.messages import FunctionMessage\n",
|
||||
"\n",
|
||||
"# Define the function that determines whether to continue or not\n",
|
||||
"def should_continue(state):\n",
|
||||
" messages = state['messages']\n",
|
||||
" last_message = messages[-1]\n",
|
||||
" # If there is no function call, then we finish\n",
|
||||
" if \"function_call\" not in last_message.additional_kwargs:\n",
|
||||
" return \"end\"\n",
|
||||
" # Otherwise if there is, we continue\n",
|
||||
" else:\n",
|
||||
" return \"continue\"\n",
|
||||
"\n",
|
||||
"# Define the function that calls the model\n",
|
||||
"def call_model(state):\n",
|
||||
" messages = state['messages']\n",
|
||||
" if len(messages) > 10:\n",
|
||||
" messages = messages[-10:]\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",
|
||||
"# Define the function to execute tools\n",
|
||||
"def call_tool(state):\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 AgentAction from the function_call\n",
|
||||
" action = AgentAction(\n",
|
||||
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
|
||||
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
|
||||
" log=\"\",\n",
|
||||
" )\n",
|
||||
" # We call the tool_executor and get back a response\n",
|
||||
" response = tool_executor.invoke(action)\n",
|
||||
" # We use the response to create a FunctionMessage\n",
|
||||
" function_message = FunctionMessage(content=str(response), name=action.tool)\n",
|
||||
" # We return a list, because this will get added to the existing list\n",
|
||||
" return {\"messages\": [function_message]}\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "1133ec83-7af9-4444-9f88-c793fbdce214",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.graph import StateGraph, END\n",
|
||||
"# Define a new graph\n",
|
||||
"workflow = StateGraph(AgentState)\n",
|
||||
"\n",
|
||||
"# Define the two nodes we will cycle between\n",
|
||||
"workflow.add_node(\"agent\", call_model)\n",
|
||||
"workflow.add_node(\"action\", call_tool)\n",
|
||||
"\n",
|
||||
"# Set the entrypoint as `agent`\n",
|
||||
"# This means that this node is the first one called\n",
|
||||
"workflow.set_entry_point(\"agent\")\n",
|
||||
"\n",
|
||||
"# We now add a conditional edge\n",
|
||||
"workflow.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.\n",
|
||||
"workflow.add_edge('action', 'agent')\n",
|
||||
"\n",
|
||||
"# Finally, we compile it!\n",
|
||||
"# This compiles it into a LangChain Runnable,\n",
|
||||
"# meaning you can use it as you would any other runnable\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "289c648f-bfc6-464f-8df9-50be8b1b9e48",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}})]}\n",
|
||||
"----\n",
|
||||
"{'messages': [FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [AIMessage(content='The weather in San Francisco is currently not available. However, you can check the weather history for January 2024 in San Francisco on [weatherspark.com](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [HumanMessage(content='what is the weather in sf'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}}), FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json'), AIMessage(content='The weather in San Francisco is currently not available. However, you can check the weather history for January 2024 in San Francisco on [weatherspark.com](https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States).')]}\n",
|
||||
"----\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
||||
"for s in app.stream(inputs):\n",
|
||||
" print(list(s.values())[0])\n",
|
||||
" print(\"----\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4cd4b39f-831a-4818-bf56-99cf301b0555",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "aa9f9110-ea74-43af-aa72-6b45518abd6e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain import hub\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
|
||||
"from langgraph.prebuilt.messages_executor import create_messages_executor\n",
|
||||
"from langchain_core.messages import HumanMessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "0f005b2d-ddc0-4d60-8af9-9eb3dbeb45b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tools = [TavilySearchResults(max_results=1)]\n",
|
||||
"model = ChatOpenAI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "02831581-f1ba-4701-ba33-f0f68468907d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.tools.render import format_tool_to_openai_function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "a3bdf328-f34c-421e-9771-85f2740fbad6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Here we bind an additional function beside just tools - the response format\n",
|
||||
"functions = [format_tool_to_openai_function(t) for t in tools]\n",
|
||||
"\n",
|
||||
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
|
||||
"\n",
|
||||
"class Response(BaseModel):\n",
|
||||
" \"\"\"Final response to the user\"\"\"\n",
|
||||
" temperature: float = Field(description=\"the temperature\")\n",
|
||||
" other_notes: str = Field(description=\"any other notes about the weather\")\n",
|
||||
"\n",
|
||||
"from langchain_core.utils.function_calling import convert_pydantic_to_openai_function"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "a5c0ca12-4922-461c-b740-62ff99f8ae56",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = model.bind_functions(functions + [convert_pydantic_to_openai_function(Response)])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "57547622-ddd8-4179-aa4a-e1b69ca4523c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.prebuilt.tool_executor import ToolExecutor\n",
|
||||
"tool_executor = ToolExecutor(tools)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "96141ebd-32af-4c9d-a0b0-f46482b8bb88",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import TypedDict, Annotated, Sequence\n",
|
||||
"import operator\n",
|
||||
"from langchain_core.messages import BaseMessage\n",
|
||||
"# We create the AgentState that we will pass around\n",
|
||||
"# This simply involves a list of messages\n",
|
||||
"# We want steps to return messages to append to the list\n",
|
||||
"# So we annotate the messages attribute with operator.add\n",
|
||||
"class AgentState(TypedDict):\n",
|
||||
" messages: Annotated[Sequence[BaseMessage], operator.add]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "0759bddc-010a-4e3f-9f41-3a51c1ea5144",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.agents import AgentAction\n",
|
||||
"import json\n",
|
||||
"from langchain_core.messages import FunctionMessage\n",
|
||||
"\n",
|
||||
"# Define the function that determines whether to continue or not\n",
|
||||
"# This needs to get updated\n",
|
||||
"def should_continue(state):\n",
|
||||
" messages = state['messages']\n",
|
||||
" last_message = messages[-1]\n",
|
||||
" # If there is no function call, then we finish\n",
|
||||
" if \"function_call\" not in last_message.additional_kwargs:\n",
|
||||
" return \"end\"\n",
|
||||
" # Otherwise if there is, we need to check what type of function call it is\n",
|
||||
" elif last_message.additional_kwargs[\"function_call\"][\"name\"] == \"Response\":\n",
|
||||
" return \"end\"\n",
|
||||
" else:\n",
|
||||
" return \"continue\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "2a048e72-525a-4dcb-a91d-efacaddd8848",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define the function that calls the model\n",
|
||||
"def call_model(state):\n",
|
||||
" messages = state['messages']\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",
|
||||
"# Define the function to execute tools\n",
|
||||
"def call_tool(state):\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 AgentAction from the function_call\n",
|
||||
" action = AgentAction(\n",
|
||||
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
|
||||
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
|
||||
" log=\"\",\n",
|
||||
" )\n",
|
||||
" # We call the tool_executor and get back a response\n",
|
||||
" response = tool_executor.invoke(action)\n",
|
||||
" # We use the response to create a FunctionMessage\n",
|
||||
" function_message = FunctionMessage(content=str(response), name=action.tool)\n",
|
||||
" # We return a list, because this will get added to the existing list\n",
|
||||
" return {\"messages\": [function_message]}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "1133ec83-7af9-4444-9f88-c793fbdce214",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langgraph.graph import StateGraph, END\n",
|
||||
"# Define a new graph\n",
|
||||
"workflow = StateGraph(AgentState)\n",
|
||||
"\n",
|
||||
"# Define the two nodes we will cycle between\n",
|
||||
"workflow.add_node(\"agent\", call_model)\n",
|
||||
"workflow.add_node(\"action\", call_tool)\n",
|
||||
"\n",
|
||||
"# Set the entrypoint as `agent`\n",
|
||||
"# This means that this node is the first one called\n",
|
||||
"workflow.set_entry_point(\"agent\")\n",
|
||||
"\n",
|
||||
"# We now add a conditional edge\n",
|
||||
"workflow.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.\n",
|
||||
"workflow.add_edge('action', 'agent')\n",
|
||||
"\n",
|
||||
"# Finally, we compile it!\n",
|
||||
"# This compiles it into a LangChain Runnable,\n",
|
||||
"# meaning you can use it as you would any other runnable\n",
|
||||
"app = workflow.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "289c648f-bfc6-464f-8df9-50be8b1b9e48",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}})]}\n",
|
||||
"----\n",
|
||||
"{'messages': [FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json')]}\n",
|
||||
"----\n",
|
||||
"{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"temperature\": 80,\\n \"other_notes\": \"Mostly cloudy\"\\n}', 'name': 'Response'}})]}\n",
|
||||
"----\n",
|
||||
"{'messages': [HumanMessage(content='what is the weather in sf'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"query\": \"weather in San Francisco\"\\n}', 'name': 'tavily_search_results_json'}}), FunctionMessage(content=\"[{'url': 'https://weatherspark.com/h/m/557/2024/1/Historical-Weather-in-January-2024-in-San-Francisco-California-United-States', 'content': 'January 2024 Weather History in San Francisco California, United States Daily Precipitation in January 2024 in San Francisco Observed Weather in January 2024 in San Francisco San Francisco Temperature History January 2024 Hourly Temperature in January 2024 in San Francisco Hours of Daylight and Twilight in January 2024 in San Francisco80 deg, E Cloud Cover Mostly Cloudy 18,000 ft Mostly Clear 4,000 ft Partly Cloudy 15,000 ft Raw: KSFO 121956Z 08006KT 10SM FEW040 SCT150 BKN180 11/07 A3028 RMK AO2 SLP254 T01110067 This report shows the past weather for San Francisco, providing a weather history for January 2024.'}]\", name='tavily_search_results_json'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n \"temperature\": 80,\\n \"other_notes\": \"Mostly cloudy\"\\n}', 'name': 'Response'}})]}\n",
|
||||
"----\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n",
|
||||
"for s in app.stream(inputs):\n",
|
||||
" print(list(s.values())[0])\n",
|
||||
" print(\"----\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4cd4b39f-831a-4818-bf56-99cf301b0555",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user