[Advanced Guide] Building an Action-Taking LLM Agent with the ReAct Pattern Combining Tool Calling and Memory
The pace of LLM progress is remarkable. LLMs have moved beyond simple text generators and are entering a stage where they reason and act like software. Yet the first LLM calls most developers make still amount to a one-way conversation: prompt in $\rightarrow$ text out.
If your product needs to “tell the user today’s weather in Seoul and, based on that, put together a personalized outing plan,” a simple chatbot will not cut it. You have to call external APIs and remember prior conversation.
This post goes beyond LLM theory. It walks through a practical, code-first methodology for building a genuinely intelligent agent by combining working complex logic—external tool use (Tool Calling) and long-term memory (Memory).
Why You Need an Action-Taking Agent, Not Just a Chatbot
The AI we actually want does not merely answer questions. It is given a goal and then plans and executes the sequence of steps needed to achieve it. That is the definition of an agent.
Three core capabilities are required to become an agent:
- Reasoning: In what order should I think to achieve the given goal? (→ ReAct pattern)
- Action: Can I interact with the outside world based on that reasoning? (→ Tool Calling)
- Memory: Can I retain and use past experience and external knowledge? (→ Memory Management)
Only when these three work together do you get complex, service-level automation.
Connecting to the Outside World: How Tool Calling Works and How to Implement It in Depth
Tool Calling (also called Function Calling) is the core mechanism that lets an LLM go beyond its own knowledge: it can invoke a specific external function or API to fetch up-to-date information or computation results.
The key idea is to tell the LLM explicitly, “you can use these capabilities,” and let it decide on its own, “I should use this one.”
Simple Prompting vs. Structured Tool Calling: A Performance Comparison
| Aspect | Simple prompt-based calling | Structured Tool Calling |
|---|---|---|
| How it works | You must describe how to use the API in the prompt as text. | The LLM decides to call based on a defined function signature (schema). |
| Stability | Low. Easy to omit or misinterpret in complex logic. | Very high. Call parameters are enforced via structured JSON/schema. |
| Reliability | Low. Heavily dependent on LLM hallucination. | High. Results are verified because real code execution happens. |
| Best for | Simple Q&A, creative writing | Data lookup, calculation, external system integration—any case that requires execution |
Hands-on: Defining and Wiring a Custom Tool
Assume the agent we are building needs an “exchange rate lookup” capability. We will define that as a tool.
import json
from typing import List, Dict, Any
# LangChain Tool .
def get_exchange_rate(base_currency: str, target_currency: str) -> str:
"""
when .
example: get_exchange_rate("USD", "KRW") call when, USD KRW .
"""
print(f"--- [SYSTEM LOG] API call when: {base_currency} -> {target_currency} ---")
# external API call .
if base_currency == "USD" and target_currency == "KRW":
return json.dumps({"rate": 1350.5, "source": "Mock_API"})
return json.dumps({"error": " ."})
# list (LLM tool )
available_tools = [
{
"name": "get_exchange_rate",
"description": " .",
"parameters": {
"type": "object",
"properties": {
"base_currency": {"type": "string", "description": " (: USD)"},
"target_currency": {"type": "string", "description": " (: KRW)"}
},
"required": ["base_currency", "target_currency"]
}
}
]An Agent That Remembers Over Time: Memory Management Strategy
The moment an agent loses conversational context, its intelligence drops sharply. Memory management determines whether the agent can persist.
Short-term vs. Long-term Memory
- Short-term memory (context window): The recent conversation still sitting in the current chat. It is strictly limited by how many tokens the LLM can process at once (the context window). (e.g., the last 10 turns)
- Long-term memory (vector DB): All past conversations, user-uploaded documents, and external knowledge bases are embedded and stored in a vector database. When needed, the agent retrieves relevant information and injects it into context. (the core of RAG)
In a real implementation you combine both. As the conversation grows, the oldest turns are summarized into the context, and important information is stored in a vector DB so it can be retrieved whenever needed. That is the standard approach.
🚀 Putting It Together: The ReAct Pattern and Error Handling
The strongest agents follow the ReAct (Reasoning + Acting) pattern. They solve complex problems through a loop of Thought $\rightarrow$ Action $\rightarrow$ Observation $\rightarrow$ next Thought.
Below is a conceptual flow that combines tool use and error handling on top of this pattern.
# --- ---
def run_agent_cycle(initial_query: str, tools: dict):
"""
ReAct run.
"""
current_state = initial_query
max_iterations = 5
print(f"--- [START] : {initial_query} ---")
for i in range(max_iterations):
print(f"\n===== [Iteration {i+1}] =====")
# 1. Thought :
thought = f" '{current_state}' , tool ."
print(f"[Thought]: {thought}")
# 2. Action : tool
# ( LLM )
action_name = "search_weather" # example
action_input = {"city": "", "date": ""}
print(f"[Action]: {action_name} run, : {action_input}")
try:
# 3. Tool Execution (tool run): call
if action_name in tools:
tool_function = tools[action_name]
observation = tool_function(action_input)
# 4. Observation : tool run Result
print(f"[Observation]: {observation}")
# 5. : Result
current_state = f"tool run Result: {observation}. ."
else:
current_state = ": tool."
break
except Exception as e:
# : tool failed when, when
error_message = f"tool run : {str(e)}"
print(f"[Observation]: {error_message}")
current_state = error_message
break
print("\n--- [END] ---")
return current_state
# --- tool (Tool Definition) ---
def search_weather(params: dict) -> str:
"""when ."""
city = params.get("city")
date = params.get("date")
if not city:
raise ValueError("when required.")
return f"'{city}' '{date}' 25. (API call )"
def get_user_profile(user_id: str) -> str:
"""User ID ."""
return f"User ID {user_id} ' '."
available_tools = {
"search_weather": search_weather,
"get_user_profile": get_user_profile
}
# --- run ---
final_result = run_agent_cycle(" .", available_tools)
print(f"\n[ Result ]: {final_result}")Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.