mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
368 lines
27 KiB
Plaintext
368 lines
27 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Respond in a format\n",
|
|
"\n",
|
|
"The typical ReAct agent prompts the LLM to respond in 1 of two formats: a function call (~ JSON) to use a tool, or conversational text to respond to the user.\n",
|
|
"\n",
|
|
"If your agent is connected to a structured (or even generative) UI, or if it is communicating with another agent or software process, you may want it to resopnd in a specific structured format.\n",
|
|
"\n",
|
|
"In this example we will build a conversational ReAct agent that responds in a specific format. We will do this by using [tool calling](https://python.langchain.com/docs/modules/model_io/chat/function_calling/). This is useful when you want to enforce that an agent's response is in a specific format. In this example, we will ask it respond as if it were a weatherman, returning the temperature and additional info in separate, machine-readable fields."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "7cbd446a-808f-4394-be92-d45ab818953c",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Setup\n",
|
|
"\n",
|
|
"First we need to install the packages required"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["%%capture --no-stderr\n%pip install --quiet -U langgraph langchain-anthropic"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["import getpass\nimport os\n\n\ndef _set_env(var: str):\n if not os.environ.get(var):\n os.environ[var] = getpass.getpass(f\"{var}: \")\n\n\n_set_env(\"ANTHROPIC_API_KEY\")"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
|
|
"metadata": {},
|
|
"source": [
|
|
"Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n_set_env(\"LANGCHAIN_API_KEY\")"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0d7d062d",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Set up the State\n",
|
|
"\n",
|
|
"The main type of graph in `langgraph` is the [StateGraph](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.StateGraph).\n",
|
|
"This graph is parameterized by a `State` object that it passes around to each node.\n",
|
|
"Each node then returns operations the graph uses to `update` that state.\n",
|
|
"These operations can either SET specific attributes on the state (e.g. overwrite the existing values) or ADD to the existing attribute.\n",
|
|
"Whether to set or add is denoted by annotating the `State` object you use to construct the graph.\n",
|
|
"\n",
|
|
"For this example, the state we will track will just be a list of messages.\n",
|
|
"We want each node to just add messages to that list.\n",
|
|
"Therefore, we will use a `TypedDict` with one key (`messages`) and annotate it so that the `messages` attribute is \"append-only\"."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "c9172aa0",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["from typing import Annotated\n\nfrom typing_extensions import TypedDict\n\nfrom langgraph.graph.message import add_messages\n\n# Add messages essentially does this with more\n# robust handling\n# def add_messages(left: list, right: list):\n# return left + right\n\n\nclass State(TypedDict):\n messages: Annotated[list, add_messages]"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "aaf214ca",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Set up the tools\n",
|
|
"\n",
|
|
"We will first define the tools we want to use.\n",
|
|
"For this simple example, we will use create a placeholder search engine.\n",
|
|
"It is really easy to create your own tools - see documentation [here](https://python.langchain.com/v0.2/docs/how_to/custom_tools) on how to do that.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "3a1c8796",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["from langchain_core.tools import tool\n\n\n@tool\ndef search(query: str):\n \"\"\"Call to surf the web.\"\"\"\n # This is a placeholder, but don't tell the LLM that...\n return [\"The weather will be sunny with a high of 27 C.\"]\n\n\ntools = [search]"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "739ff9a1",
|
|
"metadata": {},
|
|
"source": [
|
|
"We can now wrap these tools in a simple [ToolNode](https://langchain-ai.github.io/langgraph/reference/prebuilt/#toolnode).\n",
|
|
"This is a simple class that takes in a list of messages containing an [AIMessages with tool_calls](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.ai.AIMessage.html#langchain_core.messages.ai.AIMessage.tool_calls), runs the tools, and returns the output as [ToolMessage](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolMessage.html#langchain_core.messages.tool.ToolMessage)s.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "56681368",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["from langgraph.prebuilt import ToolNode\n\ntool_node = ToolNode(tools)"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5497ed70-fce3-47f1-9cad-46f912bad6a5",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Set up the model\n",
|
|
"\n",
|
|
"Now we need to load the chat model we want to use.\n",
|
|
"Importantly, this should satisfy two criteria:\n",
|
|
"\n",
|
|
"1. It should work with messages. We will represent all agent state in the form of messages, so it needs to be able to work well with them.\n",
|
|
"2. It should work with OpenAI function calling. This means it should either be an OpenAI model or a model that exposes a similar interface.\n",
|
|
"\n",
|
|
"Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "892b54b9-75f0-4804-9ed0-88b5e5532989",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["from langchain_openai import ChatOpenAI\n\nmodel = ChatOpenAI(temperature=0)"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a77995c0-bae2-4cee-a036-8688a90f05b9",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n",
|
|
"After we've done this, we should make sure the model knows that it has these tools available to call.\n",
|
|
"We can do this by converting the LangChain tools into the format for OpenAI function calling, and then bind them to the model class.\n",
|
|
"\n",
|
|
"\n",
|
|
"**MODIFICATION**\n",
|
|
"\n",
|
|
"We also want to define a response schema for the language model and bind it to the model as a function as well"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "cd3cbae5-d92c-4559-a4aa-44721b80d107",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["from langchain_core.pydantic_v1 import BaseModel, Field\n\n\nclass Response(BaseModel):\n \"\"\"Final response to the user\"\"\"\n\n temperature: float = Field(description=\"the temperature\")\n other_notes: str = Field(description=\"any other notes about the weather\")\n\n\n# Bind to the actual tools + the response format!\nmodel = model.bind_tools(tools + [Response], tool_choice=\"any\")"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8e8b9211-93d0-4ad5-aa7a-9c09099c53ff",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the agent state\n",
|
|
"\n",
|
|
"The main type of graph in `langgraph` is the [StateGraph](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.StateGraph).\n",
|
|
"This graph is parameterized by a state object that it passes around to each node.\n",
|
|
"Each node then returns operations to update that state.\n",
|
|
"These operations can either SET specific attributes on the state (e.g. overwrite the existing values) or ADD to the existing attribute.\n",
|
|
"Whether to set or add is denoted by annotating the state object you construct the graph with.\n",
|
|
"\n",
|
|
"For this example, the state we will track will just be a list of messages.\n",
|
|
"We want each node to just add messages to that list.\n",
|
|
"Therefore, we will use a `TypedDict` with one key (`messages`) and annotate it so that the `messages` attribute is always added to.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"id": "ea793afa-2eab-4901-910d-6eed90cd6564",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["import operator\nfrom typing import Annotated, Sequence, TypedDict\n\nfrom langchain_core.messages import BaseMessage\n\n\nclass AgentState(TypedDict):\n messages: Annotated[Sequence[BaseMessage], operator.add]"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e03c5094-9297-4d19-a04e-3eedc75cefb4",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the nodes\n",
|
|
"\n",
|
|
"We now need to define a few different nodes in our graph.\n",
|
|
"In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n",
|
|
"There are two main nodes we need for this:\n",
|
|
"\n",
|
|
"1. The agent: responsible for deciding what (if any) actions to take.\n",
|
|
"2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action.\n",
|
|
"\n",
|
|
"We will also need to define some edges.\n",
|
|
"Some of these edges may be conditional.\n",
|
|
"The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n",
|
|
"The path that is taken is not known until that node is run (the LLM decides).\n",
|
|
"\n",
|
|
"1. Conditional Edge: after the agent is called, we should either:\n",
|
|
" a. If the agent said to take an action, then the function to invoke tools should be called\n",
|
|
" b. If the agent said that it was finished, then it should finish\n",
|
|
"2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n",
|
|
"\n",
|
|
"Let's define the nodes, as well as a function to decide how what conditional edge to take.\n",
|
|
"\n",
|
|
"**MODIFICATION**\n",
|
|
"\n",
|
|
"We will change the `should_continue` function to check what function was called. If the function `Response` was called - that is the function that is NOT a tool, but rather the formatted response, so we should NOT continue in that case."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "3b541bb9-900c-40d0-964d-7b5dfee30667",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["from typing import Literal\n\n\n# Define the function that determines whether to continue or not\ndef route(state: AgentState) -> Literal[\"action\", \"__end__\"]:\n messages = state[\"messages\"]\n last_message = messages[-1]\n # If there is no function call, then we finish\n if not last_message.tool_calls:\n return \"__end__\"\n # Otherwise if there is, we need to check what type of function call it is\n if last_message.tool_calls[0][\"name\"] == Response.__name__:\n return \"__end__\"\n # Otherwise we continue\n return \"action\"\n\n\n# Define the function that calls the model\ndef call_model(state: AgentState):\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]}"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the graph\n",
|
|
"\n",
|
|
"We can now put it all together and define the graph!"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "813ae66c-3b58-4283-a02a-36da72a2ab90",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": ["from langgraph.graph import StateGraph, START\n\n# Define a new graph\nworkflow = StateGraph(AgentState)\n\n# Define the two nodes we will cycle between\nworkflow.add_node(\"agent\", call_model)\nworkflow.add_node(\"action\", tool_node)\n\n# Set the entrypoint as `agent`\n# This means that this node is the first one called\nworkflow.add_edge(START, \"agent\")\n\n# We now add a conditional edge\nworkflow.add_conditional_edges(\n # First, we define the start node. We use `agent`.\n # This means these are the edges taken after the `agent` node is called.\n \"agent\",\n # Next, we pass in the function that will determine which node is called next.\n route,\n)\n\n# We now add a normal edge from `tools` to `agent`.\n# This means that after `tools` is called, `agent` node is called next.\nworkflow.add_edge(\"action\", \"agent\")\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\napp = workflow.compile()"]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "2271a1ee",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/jpeg": "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",
|
|
"text/plain": [
|
|
"<IPython.core.display.Image object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": ["from IPython.display import Image, display\n\ndisplay(Image(app.get_graph(xray=True).draw_mermaid_png()))"]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "547c3931-3dae-4281-ad4e-4b51305594d4",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Use it!\n",
|
|
"\n",
|
|
"We can now use it!\n",
|
|
"This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "f544977e-31f7-41f0-88c4-ec9c27b8cecb",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
|
"\n",
|
|
"what is the weather in sf\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
|
"Tool Calls:\n",
|
|
" search (call_j6mePdJkK2b9TaLKtSfjC9t1)\n",
|
|
" Call ID: call_j6mePdJkK2b9TaLKtSfjC9t1\n",
|
|
" Args:\n",
|
|
" query: weather in San Francisco\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
|
|
"Name: search\n",
|
|
"\n",
|
|
"[\"The weather will be sunny with a high of 27 C.\"]\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
|
"Tool Calls:\n",
|
|
" Response (call_k2aKLoYXQjEkRFn2ZEpVN4Hl)\n",
|
|
" Call ID: call_k2aKLoYXQjEkRFn2ZEpVN4Hl\n",
|
|
" Args:\n",
|
|
" temperature: 27\n",
|
|
" other_notes: Sunny weather\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": ["from langchain_core.messages import HumanMessage\n\ninputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\nfor output in app.stream(inputs, stream_mode=\"values\"):\n last_msg = output[\"messages\"][-1]\n last_msg.pretty_print()\n print(\"\\n---\\n\")"]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "eed4360d-2cdf-497b-b03f-8bc51062f780",
|
|
"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.12.2"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|