Portfolio/Learn/LLM Agents: Tool Use, Planning & Autonomous Reasoning
Machine Learning & AIAdvanced

LLM Agents: Tool Use, Planning & Autonomous Reasoning

Build AI agents that can use tools, plan multi-step actions, and solve complex tasks autonomously. From ReAct to function calling to multi-agent systems.

22 min read
March 11, 2026
AgentsTool UseReActFunction CallingLangChainPython

What Is an LLM Agent?

An LLM agent is a system where the model can observe, reason, act, and iterate. Instead of generating a single response, it decides which tools to use, executes them, observes results, and continues reasoning until the task is complete. Agents transform LLMs from text generators into autonomous problem solvers.

The agent loop: Observe (see current state) → Think (reason about what to do) → Act (call a tool or respond) → Repeat. This is the ReAct (Reasoning + Acting) pattern, and it's the foundation of modern AI agents.

Function Calling: The Foundation

Modern LLMs natively support function calling — you define tools with typed schemas, and the model decides when and how to call them. The model outputs structured function calls instead of plain text. The runtime executes the function and feeds results back. This is more reliable than parsing text output.

python
from openai import OpenAI
import json

client = OpenAI()

# Define tools the agent can use
tools = [
    {
        "type": "function",
        "function": {
            "name": "search_database",
            "description": "Search the product database by query",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "Search query"},
                    "max_results": {"type": "integer", "default": 5},
                },
                "required": ["query"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "calculate_price",
            "description": "Calculate total price with tax and discount",
            "parameters": {
                "type": "object",
                "properties": {
                    "base_price": {"type": "number"},
                    "tax_rate": {"type": "number"},
                    "discount_pct": {"type": "number", "default": 0},
                },
                "required": ["base_price", "tax_rate"],
            },
        },
    },
]

# Agent loop
messages = [
    {"role": "system", "content": "You are a shopping assistant. Use tools to help."},
    {"role": "user", "content": "Find me a laptop under $1000 and calculate the total with 8% tax."},
]

while True:
    response = client.chat.completions.create(
        model="gpt-4o", messages=messages, tools=tools,
    )
    msg = response.choices[0].message
    messages.append(msg)

    if msg.tool_calls:
        for call in msg.tool_calls:
            # Execute the function (in production, validate and sandbox this)
            result = execute_function(call.function.name, json.loads(call.function.arguments))
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": json.dumps(result),
            })
    else:
        print(msg.content)  # Final answer
        break

The ReAct Pattern

ReAct interleaves reasoning traces with actions. The model explicitly states its thinking before each action, making the agent's decision process transparent and debuggable. This is more reliable than letting the model silently choose tools.

python
# ReAct prompt structure
react_prompt = """You are a research assistant. Answer questions using the available tools.

Available tools:
- search(query): Search the web for information
- calculator(expression): Evaluate a math expression
- lookup(term): Look up a term in the knowledge base

Use this format:
Thought: <your reasoning about what to do next>
Action: <tool_name>(arguments)
Observation: <result from tool>
... (repeat as needed)
Thought: I now have enough information to answer.
Answer: <final answer>

Question: What is the population density of France?

Thought: I need to find France's population and area, then divide.
Action: search("France population 2024")
Observation: France's population is approximately 68.2 million.
Thought: Now I need the area.
Action: search("France total area km2")
Observation: France has a total area of 643,801 km².
Thought: I can now calculate population density.
Action: calculator(68200000 / 643801)
Observation: 105.93
Thought: I have all the information needed.
Answer: France's population density is approximately 106 people per km²."""

Building a Multi-Tool Agent

python
class Agent:
    """A simple ReAct-style agent with tool use."""

    def __init__(self, model: str = "gpt-4o", tools: dict = None):
        self.client = OpenAI()
        self.model = model
        self.tools = tools or {}
        self.max_steps = 10

    def register_tool(self, name: str, func, description: str, schema: dict):
        self.tools[name] = {
            "function": func,
            "spec": {
                "type": "function",
                "function": {"name": name, "description": description, "parameters": schema},
            },
        }

    def run(self, user_message: str) -> str:
        messages = [
            {"role": "system", "content": "You are a helpful assistant. Use tools when needed."},
            {"role": "user", "content": user_message},
        ]
        tool_specs = [t["spec"] for t in self.tools.values()]

        for step in range(self.max_steps):
            response = self.client.chat.completions.create(
                model=self.model, messages=messages, tools=tool_specs,
            )
            msg = response.choices[0].message
            messages.append(msg)

            if not msg.tool_calls:
                return msg.content  # Agent is done

            # Execute each tool call
            for call in msg.tool_calls:
                tool = self.tools[call.function.name]
                args = json.loads(call.function.arguments)
                result = tool["function"](**args)
                messages.append({
                    "role": "tool",
                    "tool_call_id": call.id,
                    "content": str(result),
                })

        return "Max steps reached without a final answer."

Agent reliability tips: (1) Keep tool descriptions precise and unambiguous. (2) Add input validation to every tool. (3) Set a max_steps limit to prevent infinite loops. (4) Log every reasoning step for debugging. (5) Use structured output for tool calls, not text parsing.

Multi-Agent Systems

Complex tasks benefit from multiple specialized agents collaborating. A planner agent breaks down the task, a researcher gathers information, a coder writes code, and a reviewer validates results. Each agent has focused system prompts and limited tools, making them more reliable than one agent doing everything. Frameworks like AutoGen, CrewAI, and LangGraph orchestrate multi-agent workflows.