Welcome to Agents Academy
Module 05 · Agent basics · ~10 min
The agent loop.
By the end of this module, your agent can keep working without you in the loop. Observe, think, act, repeat, the basic shape of every autonomous worker, with stop conditions that prevent it from running away with itself.
To get there, you’ll wire observation → reasoning → action → repeat. The same loop shape every later module, memory, monitoring, kill switches, bolts on to.
Agent basics tier · Reference cardWhat is an agent loop?
An agent loop is the observe → think → act → observe cycle an autonomous agent repeats until a stop condition ends the run. The agent observes the current state, thinks (an LLM call decides what, if anything, to do next), acts by executing the chosen tool, such as placing an order or fetching detail, then feeds the result back in as the next observation. Everything else, memory, monitoring, kill switches, is plumbing around that cycle. The loop ends when the model returns plain text with no tool call (it is done) or a stop condition trips. A step cap is essential: a loop without one is a credit card with no limit, because every iteration burns inference cost and trade fees. Cap typical sessions around 10 to 20 steps and alert when the agent hits the cap.
No Limitless API claims here; this is agent-runtime teaching. Verified 2026-06-09.
Section 01
The loop in plain English.
An agent doesn’t make one decision and stop. It makes a decision, executes it, observes the result, and uses what it learned to make the next decision. That cycle, observe, think, act, observe, is the agent loop. Everything else (memory, monitoring, kill switches) is plumbing around the loop.
Observe
Read state: positions, market prices, pending orders.
Think
LLM call. Model decides what (if anything) to do next.
Act
Execute the chosen tool. Place order, fetch detail, etc.
Observe
Feed the tool result back. Loop until terminal or max iters.
Section 02
A minimal loop in code.
A working agent loop in 30 lines. The model gets one tool (browse_markets from Module 04) and a simple system prompt. The loop runs until the model returns plain text (no tool call) or hits the iteration cap.
How to run this
- Set three env vars: LIMITLESS_API_KEY (the tool hits Limitless), plus ANTHROPIC_API_KEY (TypeScript) or OPENAI_API_KEY (Python). Keep Module 04’s tool file next to this one so the ./tools import resolves.
- Save as agent-loop.ts next to tools.ts from Module 04, then run npx tsx agent-loop.ts.
- Save as agent_loop.py next to tools.py from Module 04, then run python agent_loop.py.
- You see the agent call browse_markets once, the loop feeds the market list back to the model, and the model prints a final summary of 5 markets prefixed with Final:. If it hits Max iterations reached, your tool string is likely too large, trim it.
// Module 05: Minimal agent loop
import Anthropic from '@anthropic-ai/sdk';
import { browseMarkets, browseMarketsTool } from './tools.js';
const client = new Anthropic();
const MAX_ITERS = 10;
async function runAgent(userMessage: string) {
const messages: Anthropic.MessageParam[] = [{ role: 'user', content: userMessage }];
for (let i = 0; i < MAX_ITERS; i++) {
const resp = await client.messages.create({
model: 'claude-opus-4-8',
max_tokens: 1024,
system: 'You are a Limitless trading research agent. Use tools to investigate markets.',
tools: [browseMarketsTool],
messages,
});
// Terminal: model returned plain text, no more tool calls.
if (resp.stop_reason === 'end_turn') {
const text = resp.content.find(c => c.type === 'text');
console.log('Final:', text?.text);
return;
}
// Execute any tool calls and feed results back.
const toolUses = resp.content.filter(c => c.type === 'tool_use');
messages.push({ role: 'assistant', content: resp.content });
const toolResults = await Promise.all(toolUses.map(async (tu: any) => ({
type: 'tool_result' as const,
tool_use_id: tu.id,
content: await browseMarkets(tu.input),
})));
messages.push({ role: 'user', content: toolResults });
}
console.warn('Max iterations reached');
}
runAgent('Find me 5 active markets.').catch(console.error);
# Module 05: Minimal agent loop
import asyncio
from openai import AsyncOpenAI
from tools import browse_markets, browse_markets_tool
client = AsyncOpenAI()
MAX_ITERS = 10
async def run_agent(user_message: str) -> None:
messages = [
{"role": "system", "content": "You are a Limitless trading research agent. Use tools to investigate markets."},
{"role": "user", "content": user_message},
]
for _ in range(MAX_ITERS):
resp = await client.chat.completions.create(
model = "gpt-4o",
messages = messages,
tools = [browse_markets_tool],
)
msg = resp.choices[0].message
messages.append(msg.model_dump(exclude_none=True))
# Terminal: no tool calls means model is done.
if not msg.tool_calls:
print("Final:", msg.content)
return
for call in msg.tool_calls:
args = __import__("json").loads(call.function.arguments)
result = await browse_markets(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": result,
})
print("Max iterations reached")
if __name__ == "__main__":
asyncio.run(run_agent("Find me 5 active markets."))
Section 03
Stop conditions.
A loop without termination is a fire hazard. Every agent loop needs at least four stop conditions wired in from day one:
Model says stop
No tool calls in the response, model is done reasoning. Cleanest exit.
Max iterations
Hard cap (10–20 typical). If the loop hits it, log a warning and bail.
Wall-clock budget
Total time per run. Prevents a hung tool from blocking the agent forever.
Kill switch tripped
External flag the operator can set. Module 14 covers the file-flag pattern.
Section 04
Recovery from tool errors.
Tool calls fail. Network blips, rate limits, schema mismatches. The right pattern is to surface the error to the model as a tool result with an error string, let the model decide whether to retry, try a different tool, or give up. Don’t catch the error and silently swallow it; the model needs context to make a good decision.
What you should NOT do: throw an exception out of the loop. That kills the run and loses all the reasoning so far. Capture every tool error, format it as a string the model can read (e.g. “Error: rate limit exceeded, retry in 60s”), and let the model react.
The exception to the rule
If the error indicates a SECURITY problem (auth failure, signature invalid, IP blocked), do NOT feed it to the model. Stop the loop immediately and alert a human. The model is not equipped to recover from auth issues, and it might try increasingly desperate things.
Wire this loop into your dashboard.
The seed NDJSON you dropped in Module 02 was eight lines of mock data. As soon as your agent loop is producing real iterations, append each one to $ACADEMY_DATA_DIR/agent.log.ndjson with the same field shape (ts, iter, event, tool, tokens_in, tokens_out, cost_usd). The panel and the Telegram bot will pick up the new lines automatically, that’s the moment the dashboard stops being a mockup and becomes your live view.
The agent loop: what people ask
Each answer also ships invisibly as schema.org FAQ data for search engines and AI assistants. Tap a question to expand.
-
What is the agent loop (observe, think, act, observe)?
It is the cycle an agent repeats instead of deciding once and stopping: observe the current state, think (an LLM call decides the next move), act (run the chosen tool), then observe the result and feed it back. The model loops until it returns plain text with no tool call, or a stop condition trips. Memory, monitoring, and kill switches are plumbing around it. -
How is an agent different from a single LLM call?
A single call makes one decision and stops. An agent loops: it executes its decision, observes the result, and uses what it learned to make the next decision. That repeated observe-think-act cycle is what lets an agent investigate a market over several tool calls instead of guessing in one shot. -
How does an agent loop terminate safely?
Wire at least four stop conditions from day one: the model returns no tool call (the cleanest exit), a hard iteration cap (maxStepsof 10 to 20 is typical), a wall-clock budget so a hung tool cannot block forever, and an operator kill switch. Hitting the cap should log a warning and bail; a loop without a step cap is a credit card with no limit. -
How should an agent recover from a tool error?
Surface the error to the model as a tool result string (for exampleError: rate limit exceeded, retry in 60s) and let it decide whether to retry, switch tools, or give up. Never throw out of the loop, that kills the run and loses all reasoning so far. The exception is a security error (auth failure, invalid signature, IP block): stop the loop immediately and alert a human. -
What do you need to run a minimal agent loop?
Three env vars:LIMITLESS_API_KEY(the tool calls Limitless) plusANTHROPIC_API_KEY(TypeScript) orOPENAI_API_KEY(Python). Give the model one tool and a short system prompt; the whole loop is about 30 lines and runs until the model prints a final summary or hitsMAX_ITERS.
Module checklist
Five quick confirmations.
Tick each item once you’ve actually done it. The Continue button unlocks at 5/5.
I can describe the four steps of the agent loop without looking
I built a working loop in TypeScript or Python that calls one tool
My loop has all four stop conditions wired in (model done, max iters, wall-clock, kill switch placeholder)
Tool errors are caught and surfaced to the model as readable strings
I know which errors should NOT be fed back to the model (security, auth)
Module 05 complete
Loop spinning.
Your agent runs on its own now. It looks at the market, decides what to do, calls a tool, and reads the result, over and over, until a goal is met or a guard rail trips. That’s the difference between a chat session and an actual worker that can move money.
Concretely, you have a working agent loop with safe termination. A complete observe → think → act cycle, four stop conditions wired in from day one, and a recovery pattern for tool errors that keeps the model in the driver’s seat.
A minimal but complete runAgent/run_agent function, observe, think, act, observe, that drives Module 04’s browse_markets tool end-to-end through Claude or GPT-4o.
The four stop conditions every loop needs wired in: model says end_turn, MAX_ITERS cap, wall-clock budget, kill-switch placeholder.
A recovery pattern for tool errors, surface them to the model as readable strings, and the one exception to that rule: auth/security failures stop the loop and alert a human.
Next up: giving the loop a memory, persisting position state, appending reasoning traces, and knowing when a vector store actually pays off.
Complete the checklist above to unlock