# 03. Building an Agent using LangGraph

In this tutorial, we add an Agent that performs web browsing capabilities to chatbots through a web search tool.

Bind the tool to LLM to build an Agent that calls the web search tool (Tool) when needed according to the request entered in LLM.

Not only that, let's learn how to route to different nodes depending on whether the tool is called through a conditional edge.

```python
# Configuration file for managing API keys as environment variables
from dotenv import load_dotenv

# Load API key information
load_dotenv()
```

```
 True 
```

```python
# LangSmith set up tracking. https://smith.langchain.com
# !pip install -qU langchain-teddynote
from langchain_teddynote import logging

# Enter a project name.
logging.langsmith("CH17-LangGraph")
```

```
 Start tracking LangSmith. 
[Project name] 
CH17-LangGraph 
```

### Using Tools <a href="#tool" id="tool"></a>

**Reference**

* [Tools](https://wikidocs.net/262582)

We will incorporate a web search tool to handle questions that chatbots cannot answer in "Remember". You can use this tool to find relevant information and provide better responses.

#### Search API tools <a href="#api" id="api"></a>

A tool that implements search capabilities by utilizing the Tavily search API. This tool offers two main classes: `TavilySearchResults` Wow `TavilyAnswer` .

**API key issuance address** -<https://app.tavily.com/>

Set the API key issued to the environment variable.

`.env` Set to the file as below.

```ini
TAVILY_API_KEY=tvly-abcdefghijklmnopqrstuvwxyz
```

#### TavilySearchResults <a href="#tavilysearchresults" id="tavilysearchresults"></a>

**Explanation** -Query Tavily search API and return results in JSON format. -A search engine optimized for comprehensive, accurate and reliable results. -Useful when answering questions about current events. Next, web search tool `TavilySearchResults` Generate.

```python
# !pip install -U langchain-teddynote
```

```python
from langchain_teddynote.tools.tavily import TavilySearch

# Create a search tool
tool = TavilySearch(max_results=3)

# Add to tools list
tools = [tool]

# Run the tool
tool.invoke("Teddy Note Langchain Tutorial")
```

```
 '[{"url": "https://teddylee777.github.io/langchain/langchain-tutorial-04/", "content": "{\\"title\\": \\" Langchain + orthopedic data (CSV, Excel) - ChatGPT based data analysis (4) - Tedinot\\", \"cont ② LangChain Korean Tutorial Shortcuts 👀 ③ Langchain Notes Free Electronic Books (wikidocs) Shortcuts 🙌 ④ RAG Unlawful Notes LangChain Lecture Open Shortcuts 🙌 ⑤ Seoul PyTorch Deep-Running Lecture Shortcut < Langchain + Orthopedic Data (CSV, Excel) - ChatGPT based data analysis (4)\\", \\"raw\\": \\"🔥 Notifications🔥\\\\n① Teddy Note YouTube -\\\\ \ Go to\\\\n② LangChain Korean Tutorial \\\n Shortcut 👀\\\n③ Langchain Notes Free e-books (wikidocs)\\\\n Shortcut 🙌\\\\\n④ RAG Unknown LangChain Lecture Opens\\\\ Langchain Korean Tutorial Lecture Fastcampus-RAG Unlawful Note. Langchain\\", \\"raw\": null}"}]' 
```

The result is a summary of the pages that chatbots can use to answer questions.

This time on LLM `bind_tools` By adding **LLM + tool** Configure.

```python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages


# State definition
class State(TypedDict):
    # Apply add_messages to list type (add message to list)
    messages: Annotated[list, add_messages]
```

Define LLM and bind tools.

```python
from langchain_openai import ChatOpenAI

# LLM Initialization
llm = ChatOpenAI(model="gpt-4o-mini")

# Binding tools to LLM
llm_with_tools = llm.bind_tools(tools)
```

Define nodes.

```python
# Node function definition
def chatbot(state: State):
    answer = llm_with_tools.invoke(state["messages"])
    # Return a list of messages
    return {"messages": [answer]}  # 자동으로 add_messages 적용
```

Add graph creation and nodes.

```python
from langgraph.graph import StateGraph

# Initialize state graph
graph_builder = StateGraph(State)

# add note
graph_builder.add_node("chatbot", chatbot)
```

### Tool Node <a href="#tool-node" id="tool-node"></a>

Next, if the tool is called, you need to create a function that can actually be run. To do this, add tools to the new node.

Check the most recent message and `tool_calls` Call the tool if it is included `BasicToolNode` Implement.

Now implemented directly, but later on LangGraph's pre-built [ToolNode ](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.tool_node.ToolNode)Can be replaced with

```python
import json
from langchain_core.messages import ToolMessage


class BasicToolNode:
    """Run tools requested in the last AIMessage node"""

    def __init__(self, tools: list) -> None:
        # tool list
        self.tools_list = {tool.name: tool for tool in tools}

    def __call__(self, inputs: dict):
        # Extract the most recent message if it exists
        if messages := inputs.get("messages", []):
            message = messages[-1]
        else:
            raise ValueError("No message found in input")

        # Tool call result
        outputs = []
        for tool_call in message.tool_calls:
            # Save results after calling the tool
            tool_result = self.tools_list[tool_call["name"]].invoke(tool_call["args"])
            outputs.append(
                # Save tool call results as a message
                ToolMessage(
                    content=json.dumps(
                        tool_result, ensure_ascii=False
                    ),  # Convert tool call result to string
                    name=tool_call["name"],
                    tool_call_id=tool_call["id"],
                )
            )

        return {"messages": outputs}


# Create a tool node
tool_node = BasicToolNode(tools=[tool])

# Add a tool node to the graph
graph_builder.add_node("tools", tool_node)
```

### Conditional Edge <a href="#conditional-edge" id="conditional-edge"></a>

When tool nodes are added `conditional_edges` You can define.

**Edges** Routes the control flow from one node to the next.

**Conditional edges** Routes to other nodes depending on the current graph status, including the "if" statement. These functions are currently graphs `state` Take and indicate Node to call next **String or string list** Returns.

Below `route_tools` La defines a router function at the output of the chatbot `tool_calls` Check.

This function `add_conditional_edges` If you call and give it to the graph, `chatbot` Whenever the node is complete, check this function to decide where to go next.

If the condition is a tool call `tools` Without `END` Routed as.

**Reference**

* pre-built on langgraph [tools\_condition ](https://langchain-ai.github.io/langgraph/reference/prebuilt/#tools_condition)Can be replaced with

#### `add_conditional_edges` <a href="#add_conditional_edges" id="add_conditional_edges"></a>

![](https://wikidocs.net/images/page/264624/langgraph-02.png)

`add_conditional_edges` The method adds a conditional edge from the start node to multiple target nodes.

**parameter**

* `source` (str): Start node. The conditional edge is executed when leaving this node.
* `path` (Union\[Callable, Runnable]): A callable object or Runnable that determines the next node. `path_map` If not specified, one or more nodes must be returned. `END` When returned, graph execution stops.
* `path_map` (Optional\[Union\[dict\[Hashable, str], list\[str]]]): Mapping between path and node name. If omitted `path` The value that returns must be the node name.
* `then` (Optional\[str]): `path` The name of the node to run after the selected node is executed.

**return value**

* Self: Returns yourself for method chaining.

**Main features**

1. Add conditional edges to the graph.
2. `path_map` Convert to Dicker.
3. `path` Automatically analyze the function's return type `path_map` Can be generated.
4. Save the conditional branch to the graph.

**Reference**

* Adding an edge to an already compiled graph will result in a warning message.
* `path` No type hint for function's return value `path_map` If this is not provided, it is assumed that the edge at graph visualization can be converted to any node in the graph.
* If a branch of the same name already exists `ValueError` Occurs.

```python
from langgraph.graph import START, END


def route_tools(
    state: State,
):
    if messages := state.get("messages", []):
        # Extract the most recent AI messages
        ai_message = messages[-1]
    else:
        # Raises an exception if there is no message in the input state.
        raise ValueError(f"No messages found in input state to tool_edge: {state}")

    # Returns "tools" if the AI ​​message contains a tool call.
    if hasattr(ai_message, "tool_calls") and len(ai_message.tool_calls) > 0:
        # Returns "tools" if there is a tool call.
        return "tools"
    # Returns "END" if no tool call is made.
    return END


# `tools_condition` The function returns "tools" if the chatbot requests the use of tools, or "END" if a direct response is possible.
graph_builder.add_conditional_edges(
    source="chatbot",
    path=route_tools,
    # If the return value of route_tools is "tools", route to the "tools" node, otherwise route to the END node.
    path_map={"tools": "tools", END: END},
)

# tools > chatbot
graph_builder.add_edge("tools", "chatbot")

# START > chatbot
graph_builder.add_edge(START, "chatbot")

# Compile the graph
graph = graph_builder.compile()
```

**Conditional edge** It should start at a single node.

This is on the graph" `chatbot` "Whenever the node runs, calling the tool means moving to'tools', and responding directly means ending the loop.

Pre-built `tools_condition` Like, functions do not have tool calls `END` Returns the string (end graph). graph `END` When switched to, there is no more work to complete and it stops running.

```python
from langchain_teddynote.graphs import visualize_graph

# Graph visualization
visualize_graph(graph)
```

Now you can ask the bot questions other than training data.

```python
from langchain_teddynote.messages import display_message_tree

question = "teddy note YouTube"

for event in graph.stream({"messages": [("user", question)]}):
    for key, value in event.items():
        print(f"\n==============\nSTEP: {key}\n==============\n")
        display_message_tree(value["messages"][-1])
```

```
 ============== 
STEP: chatbot 
============== 

'4fens':'G'tool_calls':'call_yFcDDsMxrcuqBkvkOM01i9Eg','=tool_calls','logprobs': None} id='  run-6d5911f7-452b-4f65-8535-f08c198521fd-0' tool_calls=[TAG1>','args':'tavily_web_search':'TAG1>'query'  cache_read': 0},'output_token_details': {'reasoning': 0}}  cache_read': 0},'output_token_details': {'reasoning': 0}} 

============== 
STEP: tools 
============== 

content='" [{\"url\\": \\"https://teddynote.com/\\", \\"content\\": \\"{\\\\\"title\\\\\\": \\\\\\" TeddyNote\\\\\\\" YouTube teddy notes; Blog teddy notes; LinkedIn; LangChain. LangChain Korean Tutorial Github; LangChain Korean Tutorial Wikipedia ebook\\\\", \\\\\"raw\\\\"}\\", }\\" ② LangChain Korean Tutorial Shortcuts 👀 ③ Langchain Notes Free Electronic Books (wikidocs) Shortcuts 🙌 ④ RAG Unlawful Notes LangChain Lecture Opens Shortcuts 🙌 ⑤ Seoul PyTorch Deep-Running Lecture Shortcut < Table of Contents for Teddy Lee. Contact; Career; Lecture\\\\",  \\\\\\"raw\\\\": \\\\\"🔥 notification 🔥\\\\\\\\n① Teddy Note YouTube -\\\\\\\\\\\ Go to play!Well done,>>\\\\n② LangChain Korean tutorials\\\\\\\n There are many 👀\\\\\\n③ Langchain notes free e-books (wikidocs)\\\\\\\\\n This is what I've put down and blog addresses. name='tavily_web_search' id='7aa1ee68-66a2-473a-bd4c-d9d45766a7e9' tool_call_id='call_yFcDDsMxrcuqBkvkOM01i9Eg'  Excerpt from what you don't know.\\\\\\n📌 Hollow Hagi\\\\"}\"}]" name='tavily_web_search' id='7aa1ee68-66a2-473a-bd4  Excerpt from what you don't know.\\\\\\n📌 Hollow Hagi\\\\"}\"}]" name='tavily_web_search' id='7aa1ee68-66a2-473a-bd4 

============== 
STEP: chatbot 
============== 

content=' TeddyNote is a YouTube channel run by a character named Teddy Lee. This channel provides lecture footage on a variety of topics, including machine learning, data analysis, and deep learning. \n\n** Teddy note-related links:**\n- [Tedinot's official web site] (https://teddynote.com/)\n- [Lecture information in the book] (https://teddylee777.github.io/lectures/)\n- [YouTube channel shortcut] (https://teddylee777.github.io/about/) -Loop YouTube channel to program '1okens':'3ens':None={<_metadata}'token_usage': ={'completion_tokens': 201,'prompt_tokens': 3085  gpt-4o-mini-2024-07-18','system_fingerprint':'fp_7693ae462b','finish_reason':'stop','logprobs': None} id='run-6a51c788-603d 
```

\============== STEP: chatbot ==============

Image of structure after tool call

![](https://wikidocs.net/images/page/264624/tool-message-01.png)

* [LangSmith tracking for previous runs](https://smith.langchain.com/public/4f82ddfa-a452-40f3-ab09-4eb088b812a4/r)

<br>
