mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-09 11:17:53 +02:00
@@ -59,7 +59,7 @@ from langchain_core.messages import HumanMessage
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_core.tools import tool
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.graph import END, StateGraph, MessagesState
|
||||
from langgraph.graph import END, START, StateGraph, MessagesState
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ workflow.add_node("tools", tool_node)
|
||||
|
||||
# Set the entrypoint as `agent`
|
||||
# This means that this node is the first one called
|
||||
workflow.set_entry_point("agent")
|
||||
workflow.add_edge(START, "agent")
|
||||
|
||||
# We now add a conditional edge
|
||||
workflow.add_conditional_edges(
|
||||
|
||||
@@ -28,7 +28,7 @@ In the standard LangGraph API configuration, the server uses the compiled graph
|
||||
|
||||
```python
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, MessageGraph
|
||||
from langgraph.graph import END, START, MessageGraph
|
||||
|
||||
model = ChatOpenAI(temperature=0)
|
||||
|
||||
@@ -36,7 +36,7 @@ graph_workflow = MessageGraph()
|
||||
|
||||
graph_workflow.add_node("agent", model)
|
||||
graph_workflow.add_edge("agent", END)
|
||||
graph_workflow.set_entry_point("agent")
|
||||
graph_workflow.add_edge(START, "agent")
|
||||
|
||||
agent = graph_workflow.compile()
|
||||
```
|
||||
@@ -60,7 +60,7 @@ To make your graph rebuild on each new run with custom configuration, you need t
|
||||
```python
|
||||
from typing import Annotated, TypedDict
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import END, MessageGraph
|
||||
from langgraph.graph import END, START, MessageGraph
|
||||
from langgraph.graph.state import StateGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
from langgraph.prebuilt import ToolNode
|
||||
@@ -83,7 +83,7 @@ def make_default_graph():
|
||||
|
||||
graph_workflow.add_node("agent", call_model)
|
||||
graph_workflow.add_edge("agent", END)
|
||||
graph_workflow.set_entry_point("agent")
|
||||
graph_workflow.add_edge(START, "agent")
|
||||
|
||||
agent = graph_workflow.compile()
|
||||
return agent
|
||||
@@ -113,7 +113,7 @@ def make_alternative_graph():
|
||||
graph_workflow.add_node("agent", call_model)
|
||||
graph_workflow.add_node("tools", tool_node)
|
||||
graph_workflow.add_edge("tools", "agent")
|
||||
graph_workflow.set_entry_point("agent")
|
||||
graph_workflow.add_edge(START, "agent")
|
||||
graph_workflow.add_conditional_edges("agent", should_continue)
|
||||
|
||||
agent = graph_workflow.compile()
|
||||
|
||||
@@ -103,7 +103,7 @@ Example `agent.py` file, which shows how to import from other modules you define
|
||||
# my_agent/agent.py
|
||||
from typing import TypedDict, Literal
|
||||
|
||||
from langgraph.graph import StateGraph, END
|
||||
from langgraph.graph import StateGraph, END, START
|
||||
from my_agent.utils.nodes import call_model, should_continue, tool_node # import nodes
|
||||
from my_agent.utils.state import AgentState # import state
|
||||
|
||||
@@ -114,7 +114,7 @@ class GraphConfig(TypedDict):
|
||||
workflow = StateGraph(AgentState, config_schema=GraphConfig)
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("action", tool_node)
|
||||
workflow.set_entry_point("agent")
|
||||
workflow.add_edge(START, "agent")
|
||||
workflow.add_conditional_edges(
|
||||
"agent",
|
||||
should_continue,
|
||||
|
||||
@@ -111,7 +111,7 @@ Example `agent.py` file, which shows how to import from other modules you define
|
||||
# my_agent/agent.py
|
||||
from typing import TypedDict, Literal
|
||||
|
||||
from langgraph.graph import StateGraph, END
|
||||
from langgraph.graph import StateGraph, END, START
|
||||
from my_agent.utils.nodes import call_model, should_continue, tool_node # import nodes
|
||||
from my_agent.utils.state import AgentState # import state
|
||||
|
||||
@@ -122,7 +122,7 @@ class GraphConfig(TypedDict):
|
||||
workflow = StateGraph(AgentState, config_schema=GraphConfig)
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.add_node("action", tool_node)
|
||||
workflow.set_entry_point("agent")
|
||||
workflow.add_edge(START, "agent")
|
||||
workflow.add_conditional_edges(
|
||||
"agent",
|
||||
should_continue,
|
||||
|
||||
@@ -30,10 +30,7 @@
|
||||
"id": "47f79af8-58d8-4a48-8d9a-88823d88701f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%capture --no-stderr\n",
|
||||
"%pip install -U langgraph openai"
|
||||
]
|
||||
"source": ["%%capture --no-stderr\n%pip install -U langgraph openai"]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -49,18 +46,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _set_env(var: str):\n",
|
||||
" if not os.environ.get(var):\n",
|
||||
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"_set_env(\"OPENAI_API_KEY\")"
|
||||
]
|
||||
"source": ["import getpass\nimport os\n\n\ndef _set_env(var: str):\n if not os.environ.get(var):\n os.environ[var] = getpass.getpass(f\"{var}: \")\n\n\n_set_env(\"OPENAI_API_KEY\")"]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -84,94 +70,7 @@
|
||||
"id": "d59234f9-173e-469d-a725-c13e0979663e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from openai import AsyncOpenAI\n",
|
||||
"from langchain_core.language_models.chat_models import ChatGenerationChunk\n",
|
||||
"from langchain_core.messages import AIMessageChunk\n",
|
||||
"from langchain_core.runnables.config import (\n",
|
||||
" ensure_config,\n",
|
||||
" get_callback_manager_for_config,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"openai_client = AsyncOpenAI()\n",
|
||||
"# define tool schema for openai tool calling\n",
|
||||
"\n",
|
||||
"tool = {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_items\",\n",
|
||||
" \"description\": \"Use this tool to look up which items are in the given place.\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\"place\": {\"type\": \"string\"}},\n",
|
||||
" \"required\": [\"place\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"async def call_model(state, config=None):\n",
|
||||
" config = ensure_config(config | {\"tags\": [\"agent_llm\"]})\n",
|
||||
" callback_manager = get_callback_manager_for_config(config)\n",
|
||||
" messages = state[\"messages\"]\n",
|
||||
"\n",
|
||||
" llm_run_manager = callback_manager.on_chat_model_start({}, [messages])[0]\n",
|
||||
" response = await openai_client.chat.completions.create(\n",
|
||||
" messages=messages, model=\"gpt-3.5-turbo\", tools=[tool], stream=True\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" response_content = \"\"\n",
|
||||
" role = None\n",
|
||||
"\n",
|
||||
" tool_call_id = None\n",
|
||||
" tool_call_function_name = None\n",
|
||||
" tool_call_function_arguments = \"\"\n",
|
||||
" async for chunk in response:\n",
|
||||
" delta = chunk.choices[0].delta\n",
|
||||
" if delta.role is not None:\n",
|
||||
" role = delta.role\n",
|
||||
"\n",
|
||||
" if delta.content:\n",
|
||||
" response_content += delta.content\n",
|
||||
" llm_run_manager.on_llm_new_token(delta.content)\n",
|
||||
"\n",
|
||||
" if delta.tool_calls:\n",
|
||||
" # note: for simplicity we're only handling a single tool call here\n",
|
||||
" if delta.tool_calls[0].function.name is not None:\n",
|
||||
" tool_call_function_name = delta.tool_calls[0].function.name\n",
|
||||
" tool_call_id = delta.tool_calls[0].id\n",
|
||||
"\n",
|
||||
" # note: we're wrapping the tools calls in ChatGenerationChunk so that the events from .astream_events in the graph can render tool calls correctly\n",
|
||||
" tool_call_chunk = ChatGenerationChunk(\n",
|
||||
" message=AIMessageChunk(\n",
|
||||
" content=\"\",\n",
|
||||
" additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]},\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" llm_run_manager.on_llm_new_token(\"\", chunk=tool_call_chunk)\n",
|
||||
" tool_call_function_arguments += delta.tool_calls[0].function.arguments\n",
|
||||
"\n",
|
||||
" if tool_call_function_name is not None:\n",
|
||||
" tool_calls = [\n",
|
||||
" {\n",
|
||||
" \"id\": tool_call_id,\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": tool_call_function_name,\n",
|
||||
" \"arguments\": tool_call_function_arguments,\n",
|
||||
" },\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" else:\n",
|
||||
" tool_calls = None\n",
|
||||
"\n",
|
||||
" response_message = {\n",
|
||||
" \"role\": role,\n",
|
||||
" \"content\": response_content,\n",
|
||||
" \"tool_calls\": tool_calls,\n",
|
||||
" }\n",
|
||||
" return {\"messages\": [response_message]}"
|
||||
]
|
||||
"source": ["from openai import AsyncOpenAI\nfrom langchain_core.language_models.chat_models import ChatGenerationChunk\nfrom langchain_core.messages import AIMessageChunk\nfrom langchain_core.runnables.config import (\n ensure_config,\n get_callback_manager_for_config,\n)\n\nopenai_client = AsyncOpenAI()\n# define tool schema for openai tool calling\n\ntool = {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_items\",\n \"description\": \"Use this tool to look up which items are in the given place.\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\"place\": {\"type\": \"string\"}},\n \"required\": [\"place\"],\n },\n },\n}\n\n\nasync def call_model(state, config=None):\n config = ensure_config(config | {\"tags\": [\"agent_llm\"]})\n callback_manager = get_callback_manager_for_config(config)\n messages = state[\"messages\"]\n\n llm_run_manager = callback_manager.on_chat_model_start({}, [messages])[0]\n response = await openai_client.chat.completions.create(\n messages=messages, model=\"gpt-3.5-turbo\", tools=[tool], stream=True\n )\n\n response_content = \"\"\n role = None\n\n tool_call_id = None\n tool_call_function_name = None\n tool_call_function_arguments = \"\"\n async for chunk in response:\n delta = chunk.choices[0].delta\n if delta.role is not None:\n role = delta.role\n\n if delta.content:\n response_content += delta.content\n llm_run_manager.on_llm_new_token(delta.content)\n\n if delta.tool_calls:\n # note: for simplicity we're only handling a single tool call here\n if delta.tool_calls[0].function.name is not None:\n tool_call_function_name = delta.tool_calls[0].function.name\n tool_call_id = delta.tool_calls[0].id\n\n # note: we're wrapping the tools calls in ChatGenerationChunk so that the events from .astream_events in the graph can render tool calls correctly\n tool_call_chunk = ChatGenerationChunk(\n message=AIMessageChunk(\n content=\"\",\n additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]},\n )\n )\n llm_run_manager.on_llm_new_token(\"\", chunk=tool_call_chunk)\n tool_call_function_arguments += delta.tool_calls[0].function.arguments\n\n if tool_call_function_name is not None:\n tool_calls = [\n {\n \"id\": tool_call_id,\n \"function\": {\n \"name\": tool_call_function_name,\n \"arguments\": tool_call_function_arguments,\n },\n \"type\": \"function\",\n }\n ]\n else:\n tool_calls = None\n\n response_message = {\n \"role\": role,\n \"content\": response_content,\n \"tool_calls\": tool_calls,\n }\n return {\"messages\": [response_message]}"]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -187,62 +86,7 @@
|
||||
"id": "b90941d8-afe4-42ec-9262-9c3b87c3b1ec",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from langchain_core.callbacks import adispatch_custom_event\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"async def get_items(place: str) -> str:\n",
|
||||
" \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n",
|
||||
"\n",
|
||||
" # this can be replaced with any actual streaming logic that you might have\n",
|
||||
" def stream(place: str):\n",
|
||||
" if \"bed\" in place: # For under the bed\n",
|
||||
" yield from [\"socks\", \"shoes\", \"dust bunnies\"]\n",
|
||||
" elif \"shelf\" in place: # For 'shelf'\n",
|
||||
" yield from [\"books\", \"penciles\", \"pictures\"]\n",
|
||||
" else: # if the agent decides to ask about a different place\n",
|
||||
" yield \"cat snacks\"\n",
|
||||
"\n",
|
||||
" tokens = []\n",
|
||||
" for token in stream(place):\n",
|
||||
" await adispatch_custom_event(\n",
|
||||
" # this will allow you to filter events by name\n",
|
||||
" \"tool_call_token_stream\",\n",
|
||||
" {\n",
|
||||
" \"function_name\": \"get_items\",\n",
|
||||
" \"arguments\": {\"place\": place},\n",
|
||||
" \"tool_output_token\": token,\n",
|
||||
" },\n",
|
||||
" # this will allow you to filter events by tags\n",
|
||||
" config={\"tags\": [\"tool_call\"]},\n",
|
||||
" )\n",
|
||||
" tokens.append(token)\n",
|
||||
"\n",
|
||||
" return \", \".join(tokens)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# define mapping to look up functions when running tools\n",
|
||||
"function_name_to_function = {\"get_items\": get_items}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"async def call_tools(state):\n",
|
||||
" messages = state[\"messages\"]\n",
|
||||
"\n",
|
||||
" tool_call = messages[-1][\"tool_calls\"][0]\n",
|
||||
" function_name = tool_call[\"function\"][\"name\"]\n",
|
||||
" function_arguments = tool_call[\"function\"][\"arguments\"]\n",
|
||||
" arguments = json.loads(function_arguments)\n",
|
||||
"\n",
|
||||
" function_response = await function_name_to_function[function_name](**arguments)\n",
|
||||
" tool_message = {\n",
|
||||
" \"tool_call_id\": tool_call[\"id\"],\n",
|
||||
" \"role\": \"tool\",\n",
|
||||
" \"name\": function_name,\n",
|
||||
" \"content\": function_response,\n",
|
||||
" }\n",
|
||||
" return {\"messages\": [tool_message]}"
|
||||
]
|
||||
"source": ["import json\nfrom langchain_core.callbacks import adispatch_custom_event\n\n\nasync def get_items(place: str) -> str:\n \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n\n # this can be replaced with any actual streaming logic that you might have\n def stream(place: str):\n if \"bed\" in place: # For under the bed\n yield from [\"socks\", \"shoes\", \"dust bunnies\"]\n elif \"shelf\" in place: # For 'shelf'\n yield from [\"books\", \"penciles\", \"pictures\"]\n else: # if the agent decides to ask about a different place\n yield \"cat snacks\"\n\n tokens = []\n for token in stream(place):\n await adispatch_custom_event(\n # this will allow you to filter events by name\n \"tool_call_token_stream\",\n {\n \"function_name\": \"get_items\",\n \"arguments\": {\"place\": place},\n \"tool_output_token\": token,\n },\n # this will allow you to filter events by tags\n config={\"tags\": [\"tool_call\"]},\n )\n tokens.append(token)\n\n return \", \".join(tokens)\n\n\n# define mapping to look up functions when running tools\nfunction_name_to_function = {\"get_items\": get_items}\n\n\nasync def call_tools(state):\n messages = state[\"messages\"]\n\n tool_call = messages[-1][\"tool_calls\"][0]\n function_name = tool_call[\"function\"][\"name\"]\n function_arguments = tool_call[\"function\"][\"arguments\"]\n arguments = json.loads(function_arguments)\n\n function_response = await function_name_to_function[function_name](**arguments)\n tool_message = {\n \"tool_call_id\": tool_call[\"id\"],\n \"role\": \"tool\",\n \"name\": function_name,\n \"content\": function_response,\n }\n return {\"messages\": [tool_message]}"]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -258,33 +102,7 @@
|
||||
"id": "228260be-1f9a-4195-80e0-9604f8a5dba6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import operator\n",
|
||||
"from typing import Annotated, TypedDict, Literal\n",
|
||||
"\n",
|
||||
"from langgraph.graph import StateGraph, END\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class State(TypedDict):\n",
|
||||
" messages: Annotated[list, operator.add]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def should_continue(state) -> Literal[\"tools\", END]:\n",
|
||||
" messages = state[\"messages\"]\n",
|
||||
" last_message = messages[-1]\n",
|
||||
" if last_message[\"tool_calls\"]:\n",
|
||||
" return \"tools\"\n",
|
||||
" return END\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"workflow = StateGraph(State)\n",
|
||||
"workflow.set_entry_point(\"model\")\n",
|
||||
"workflow.add_node(\"model\", call_model) # i.e. our \"agent\"\n",
|
||||
"workflow.add_node(\"tools\", call_tools)\n",
|
||||
"workflow.add_conditional_edges(\"model\", should_continue)\n",
|
||||
"workflow.add_edge(\"tools\", \"model\")\n",
|
||||
"graph = workflow.compile()"
|
||||
]
|
||||
"source": ["import operator\nfrom typing import Annotated, TypedDict, Literal\n\nfrom langgraph.graph import StateGraph, END, START\n\n\nclass State(TypedDict):\n messages: Annotated[list, operator.add]\n\n\ndef should_continue(state) -> Literal[\"tools\", END]:\n messages = state[\"messages\"]\n last_message = messages[-1]\n if last_message[\"tool_calls\"]:\n return \"tools\"\n return END\n\n\nworkflow = StateGraph(State)\nworkflow.add_edge(START, \"model\")\nworkflow.add_node(\"model\", call_model) # i.e. our \"agent\"\nworkflow.add_node(\"tools\", call_tools)\nworkflow.add_conditional_edges(\"model\", should_continue)\nworkflow.add_edge(\"tools\", \"model\")\ngraph = workflow.compile()"]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -318,14 +136,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"async for event in graph.astream_events(\n",
|
||||
" {\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"\n",
|
||||
"):\n",
|
||||
" tags = event.get(\"tags\", [])\n",
|
||||
" if event[\"event\"] == \"on_custom_event\" and \"tool_call\" in tags:\n",
|
||||
" print(\"Tool token\", event[\"data\"][\"tool_output_token\"])"
|
||||
]
|
||||
"source": ["async for event in graph.astream_events(\n {\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"\n):\n tags = event.get(\"tags\", [])\n if event[\"event\"] == \"on_custom_event\" and \"tool_call\" in tags:\n print(\"Tool token\", event[\"data\"][\"tool_output_token\"])"]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -30,10 +30,7 @@
|
||||
"id": "47f79af8-58d8-4a48-8d9a-88823d88701f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%capture --no-stderr\n",
|
||||
"%pip install -U langgraph openai"
|
||||
]
|
||||
"source": ["%%capture --no-stderr\n%pip install -U langgraph openai"]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -49,18 +46,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _set_env(var: str):\n",
|
||||
" if not os.environ.get(var):\n",
|
||||
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"_set_env(\"OPENAI_API_KEY\")"
|
||||
]
|
||||
"source": ["import getpass\nimport os\n\n\ndef _set_env(var: str):\n if not os.environ.get(var):\n os.environ[var] = getpass.getpass(f\"{var}: \")\n\n\n_set_env(\"OPENAI_API_KEY\")"]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -84,94 +70,7 @@
|
||||
"id": "d59234f9-173e-469d-a725-c13e0979663e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from openai import AsyncOpenAI\n",
|
||||
"from langchain_core.language_models.chat_models import ChatGenerationChunk\n",
|
||||
"from langchain_core.messages import AIMessageChunk\n",
|
||||
"from langchain_core.runnables.config import (\n",
|
||||
" ensure_config,\n",
|
||||
" get_callback_manager_for_config,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"openai_client = AsyncOpenAI()\n",
|
||||
"# define tool schema for openai tool calling\n",
|
||||
"\n",
|
||||
"tool = {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"get_items\",\n",
|
||||
" \"description\": \"Use this tool to look up which items are in the given place.\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\"place\": {\"type\": \"string\"}},\n",
|
||||
" \"required\": [\"place\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"async def call_model(state, config=None):\n",
|
||||
" config = ensure_config(config | {\"tags\": [\"agent_llm\"]})\n",
|
||||
" callback_manager = get_callback_manager_for_config(config)\n",
|
||||
" messages = state[\"messages\"]\n",
|
||||
"\n",
|
||||
" llm_run_manager = callback_manager.on_chat_model_start({}, [messages])[0]\n",
|
||||
" response = await openai_client.chat.completions.create(\n",
|
||||
" messages=messages, model=\"gpt-3.5-turbo\", tools=[tool], stream=True\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" response_content = \"\"\n",
|
||||
" role = None\n",
|
||||
"\n",
|
||||
" tool_call_id = None\n",
|
||||
" tool_call_function_name = None\n",
|
||||
" tool_call_function_arguments = \"\"\n",
|
||||
" async for chunk in response:\n",
|
||||
" delta = chunk.choices[0].delta\n",
|
||||
" if delta.role is not None:\n",
|
||||
" role = delta.role\n",
|
||||
"\n",
|
||||
" if delta.content:\n",
|
||||
" response_content += delta.content\n",
|
||||
" llm_run_manager.on_llm_new_token(delta.content)\n",
|
||||
"\n",
|
||||
" if delta.tool_calls:\n",
|
||||
" # note: for simplicity we're only handling a single tool call here\n",
|
||||
" if delta.tool_calls[0].function.name is not None:\n",
|
||||
" tool_call_function_name = delta.tool_calls[0].function.name\n",
|
||||
" tool_call_id = delta.tool_calls[0].id\n",
|
||||
"\n",
|
||||
" # note: we're wrapping the tools calls in ChatGenerationChunk so that the events from .astream_events in the graph can render tool calls correctly\n",
|
||||
" tool_call_chunk = ChatGenerationChunk(\n",
|
||||
" message=AIMessageChunk(\n",
|
||||
" content=\"\",\n",
|
||||
" additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]},\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" llm_run_manager.on_llm_new_token(\"\", chunk=tool_call_chunk)\n",
|
||||
" tool_call_function_arguments += delta.tool_calls[0].function.arguments\n",
|
||||
"\n",
|
||||
" if tool_call_function_name is not None:\n",
|
||||
" tool_calls = [\n",
|
||||
" {\n",
|
||||
" \"id\": tool_call_id,\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": tool_call_function_name,\n",
|
||||
" \"arguments\": tool_call_function_arguments,\n",
|
||||
" },\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" else:\n",
|
||||
" tool_calls = None\n",
|
||||
"\n",
|
||||
" response_message = {\n",
|
||||
" \"role\": role,\n",
|
||||
" \"content\": response_content,\n",
|
||||
" \"tool_calls\": tool_calls,\n",
|
||||
" }\n",
|
||||
" return {\"messages\": [response_message]}"
|
||||
]
|
||||
"source": ["from openai import AsyncOpenAI\nfrom langchain_core.language_models.chat_models import ChatGenerationChunk\nfrom langchain_core.messages import AIMessageChunk\nfrom langchain_core.runnables.config import (\n ensure_config,\n get_callback_manager_for_config,\n)\n\nopenai_client = AsyncOpenAI()\n# define tool schema for openai tool calling\n\ntool = {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_items\",\n \"description\": \"Use this tool to look up which items are in the given place.\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\"place\": {\"type\": \"string\"}},\n \"required\": [\"place\"],\n },\n },\n}\n\n\nasync def call_model(state, config=None):\n config = ensure_config(config | {\"tags\": [\"agent_llm\"]})\n callback_manager = get_callback_manager_for_config(config)\n messages = state[\"messages\"]\n\n llm_run_manager = callback_manager.on_chat_model_start({}, [messages])[0]\n response = await openai_client.chat.completions.create(\n messages=messages, model=\"gpt-3.5-turbo\", tools=[tool], stream=True\n )\n\n response_content = \"\"\n role = None\n\n tool_call_id = None\n tool_call_function_name = None\n tool_call_function_arguments = \"\"\n async for chunk in response:\n delta = chunk.choices[0].delta\n if delta.role is not None:\n role = delta.role\n\n if delta.content:\n response_content += delta.content\n llm_run_manager.on_llm_new_token(delta.content)\n\n if delta.tool_calls:\n # note: for simplicity we're only handling a single tool call here\n if delta.tool_calls[0].function.name is not None:\n tool_call_function_name = delta.tool_calls[0].function.name\n tool_call_id = delta.tool_calls[0].id\n\n # note: we're wrapping the tools calls in ChatGenerationChunk so that the events from .astream_events in the graph can render tool calls correctly\n tool_call_chunk = ChatGenerationChunk(\n message=AIMessageChunk(\n content=\"\",\n additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]},\n )\n )\n llm_run_manager.on_llm_new_token(\"\", chunk=tool_call_chunk)\n tool_call_function_arguments += delta.tool_calls[0].function.arguments\n\n if tool_call_function_name is not None:\n tool_calls = [\n {\n \"id\": tool_call_id,\n \"function\": {\n \"name\": tool_call_function_name,\n \"arguments\": tool_call_function_arguments,\n },\n \"type\": \"function\",\n }\n ]\n else:\n tool_calls = None\n\n response_message = {\n \"role\": role,\n \"content\": response_content,\n \"tool_calls\": tool_calls,\n }\n return {\"messages\": [response_message]}"]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -187,41 +86,7 @@
|
||||
"id": "b756ea32",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"async def get_items(place: str) -> str:\n",
|
||||
" \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n",
|
||||
" if \"bed\" in place: # For under the bed\n",
|
||||
" return \"socks, shoes and dust bunnies\"\n",
|
||||
" if \"shelf\" in place: # For 'shelf'\n",
|
||||
" return \"books, penciles and pictures\"\n",
|
||||
" else: # if the agent decides to ask about a different place\n",
|
||||
" return \"cat snacks\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# define mapping to look up functions when running tools\n",
|
||||
"function_name_to_function = {\"get_items\": get_items}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"async def call_tools(state):\n",
|
||||
" messages = state[\"messages\"]\n",
|
||||
"\n",
|
||||
" tool_call = messages[-1][\"tool_calls\"][0]\n",
|
||||
" function_name = tool_call[\"function\"][\"name\"]\n",
|
||||
" function_arguments = tool_call[\"function\"][\"arguments\"]\n",
|
||||
" arguments = json.loads(function_arguments)\n",
|
||||
"\n",
|
||||
" function_response = await function_name_to_function[function_name](**arguments)\n",
|
||||
" tool_message = {\n",
|
||||
" \"tool_call_id\": tool_call[\"id\"],\n",
|
||||
" \"role\": \"tool\",\n",
|
||||
" \"name\": function_name,\n",
|
||||
" \"content\": function_response,\n",
|
||||
" }\n",
|
||||
" return {\"messages\": [tool_message]}"
|
||||
]
|
||||
"source": ["import json\n\n\nasync def get_items(place: str) -> str:\n \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n if \"bed\" in place: # For under the bed\n return \"socks, shoes and dust bunnies\"\n if \"shelf\" in place: # For 'shelf'\n return \"books, penciles and pictures\"\n else: # if the agent decides to ask about a different place\n return \"cat snacks\"\n\n\n# define mapping to look up functions when running tools\nfunction_name_to_function = {\"get_items\": get_items}\n\n\nasync def call_tools(state):\n messages = state[\"messages\"]\n\n tool_call = messages[-1][\"tool_calls\"][0]\n function_name = tool_call[\"function\"][\"name\"]\n function_arguments = tool_call[\"function\"][\"arguments\"]\n arguments = json.loads(function_arguments)\n\n function_response = await function_name_to_function[function_name](**arguments)\n tool_message = {\n \"tool_call_id\": tool_call[\"id\"],\n \"role\": \"tool\",\n \"name\": function_name,\n \"content\": function_response,\n }\n return {\"messages\": [tool_message]}"]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -237,33 +102,7 @@
|
||||
"id": "228260be-1f9a-4195-80e0-9604f8a5dba6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import operator\n",
|
||||
"from typing import Annotated, TypedDict, Literal\n",
|
||||
"\n",
|
||||
"from langgraph.graph import StateGraph, END\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class State(TypedDict):\n",
|
||||
" messages: Annotated[list, operator.add]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def should_continue(state) -> Literal[\"tools\", END]:\n",
|
||||
" messages = state[\"messages\"]\n",
|
||||
" last_message = messages[-1]\n",
|
||||
" if last_message[\"tool_calls\"]:\n",
|
||||
" return \"tools\"\n",
|
||||
" return END\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"workflow = StateGraph(State)\n",
|
||||
"workflow.set_entry_point(\"model\")\n",
|
||||
"workflow.add_node(\"model\", call_model) # i.e. our \"agent\"\n",
|
||||
"workflow.add_node(\"tools\", call_tools)\n",
|
||||
"workflow.add_conditional_edges(\"model\", should_continue)\n",
|
||||
"workflow.add_edge(\"tools\", \"model\")\n",
|
||||
"graph = workflow.compile()"
|
||||
]
|
||||
"source": ["import operator\nfrom typing import Annotated, TypedDict, Literal\n\nfrom langgraph.graph import StateGraph, END, START\n\n\nclass State(TypedDict):\n messages: Annotated[list, operator.add]\n\n\ndef should_continue(state) -> Literal[\"tools\", END]:\n messages = state[\"messages\"]\n last_message = messages[-1]\n if last_message[\"tool_calls\"]:\n return \"tools\"\n return END\n\n\nworkflow = StateGraph(State)\nworkflow.add_edge(START, \"model\")\nworkflow.add_node(\"model\", call_model) # i.e. our \"agent\"\nworkflow.add_node(\"tools\", call_tools)\nworkflow.add_conditional_edges(\"model\", should_continue)\nworkflow.add_edge(\"tools\", \"model\")\ngraph = workflow.compile()"]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -328,14 +167,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"async for event in graph.astream_events(\n",
|
||||
" {\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"\n",
|
||||
"):\n",
|
||||
" tags = event.get(\"tags\", [])\n",
|
||||
" if event[\"event\"] == \"on_chat_model_stream\" and \"agent_llm\" in tags:\n",
|
||||
" print(\"LLM token\", event[\"data\"][\"chunk\"].dict())"
|
||||
]
|
||||
"source": ["async for event in graph.astream_events(\n {\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"\n):\n tags = event.get(\"tags\", [])\n if event[\"event\"] == \"on_chat_model_stream\" and \"agent_llm\" in tags:\n print(\"LLM token\", event[\"data\"][\"chunk\"].dict())"]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -343,7 +175,7 @@
|
||||
"id": "adb0f7bc-6e51-478e-bd32-8f72df072d6c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"source": [""]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -355,7 +355,7 @@
|
||||
"workflow.add_node(\"web_search\", web_search) # web search\n",
|
||||
"\n",
|
||||
"# Build graph\n",
|
||||
"workflow.set_entry_point(\"retrieve\")\n",
|
||||
"workflow.add_edge(START, retrieve)\n",
|
||||
"workflow.add_edge(\"retrieve\", \"grade_documents\")\n",
|
||||
"workflow.add_conditional_edges(\n",
|
||||
" \"grade_documents\",\n",
|
||||
|
||||
@@ -457,13 +457,13 @@
|
||||
"source": [
|
||||
"from langchain_core.runnables import RunnableLambda\n",
|
||||
"\n",
|
||||
"from langgraph.graph import END, StateGraph\n",
|
||||
"from langgraph.graph import END, START, StateGraph\n",
|
||||
"\n",
|
||||
"graph_builder = StateGraph(AgentState)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"graph_builder.add_node(\"agent\", agent)\n",
|
||||
"graph_builder.set_entry_point(\"agent\")\n",
|
||||
"graph_builder.add_edge(START, \"agent\")\n",
|
||||
"\n",
|
||||
"graph_builder.add_node(\"update_scratchpad\", update_scratchpad)\n",
|
||||
"graph_builder.add_edge(\"update_scratchpad\", \"agent\")\n",
|
||||
|
||||
@@ -59,7 +59,7 @@ from langchain_core.messages import HumanMessage
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_core.tools import tool
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.graph import END, StateGraph, MessagesState
|
||||
from langgraph.graph import END, START, StateGraph, MessagesState
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ workflow.add_node("tools", tool_node)
|
||||
|
||||
# Set the entrypoint as `agent`
|
||||
# This means that this node is the first one called
|
||||
workflow.set_entry_point("agent")
|
||||
workflow.add_edge(START, "agent")
|
||||
|
||||
# We now add a conditional edge
|
||||
workflow.add_conditional_edges(
|
||||
|
||||
Reference in New Issue
Block a user