An AI agent is a system built around a large language model that can autonomously plan and take a sequence of actions - such as calling external tools, running code, or querying a database - to accomplish a goal, rather than simply producing a single text response to a single prompt. Where a plain LLM call takes a prompt and returns text, an agent can decide what to do next based on the results of its own previous actions, looping until the goal is achieved or a limit is reached.
A basic LLM application sends one prompt and returns one response - useful for tasks like summarization or classification, but limited when a task requires up-to-date information the model doesn't have, precise calculation the model is unreliable at, or multiple dependent steps where later steps depend on the results of earlier ones. An agent addresses this by giving the model access to external tools and letting it decide, based on the current state of the task, what action to take next, observe the result, and decide again - repeating until the task is complete.
Most modern LLM APIs support "tool calling" (also called function calling): the application describes a set of available tools - each with a name, description, and expected parameters - and the model can respond by requesting that a specific tool be called with specific arguments, instead of, or in addition to, generating text.
{
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}
The application, not the model itself, is responsible for actually executing the requested tool (calling the real weather API, in this example) and returning the result back to the model as part of the ongoing conversation.
An agent typically runs in a loop: send the current conversation (including the original goal and any prior tool results) to the model; if the model requests a tool call, execute it and append the result to the conversation; if the model instead returns a final answer, stop and return it to the user.
messages = [{"role": "user", "content": goal}]
for _ in range(max_steps):
response = llm.call(messages, tools=available_tools)
if response.tool_call:
result = execute_tool(response.tool_call)
messages.append(tool_result_message(result))
else:
return response.content
For complex goals, an agent often needs to break a task into smaller sub-steps before executing any of them - for example, "research a topic, then draft an outline, then write each section" rather than attempting everything in one pass. Some agent designs have the model explicitly produce a plan first and then work through it step by step, which tends to produce more reliable results than letting the model improvise the entire task in a single continuous stream of tool calls.
Because an LLM has no memory beyond what's included in its current context window, an agent working on a long-running task needs an explicit strategy for retaining important information as the task progresses - such as summarizing earlier steps to keep the conversation within the context window, or storing intermediate results in an external store (including a vector database) that can be retrieved again later, rather than keeping everything in the raw conversation history indefinitely.
Giving a model the ability to take real-world actions - sending emails, running code, spending money, modifying files - raises the stakes of any mistake or hallucination well beyond a plain chat response. Well-designed agent systems typically include guardrails such as: restricting which tools are available for a given task, requiring human approval before high-impact actions, sandboxing code execution, setting a maximum number of steps to prevent runaway loops, and logging every action taken so behavior can be audited after the fact.
By: Tomas Silny
Edited: 2026-08-13 06:50:00