Welcome to Agents Academy
Module 04 · Agent basics · ~8 min
Tool use 101.
By the end of this module, your agent can actually do things, not just describe them. The moment an LLM stops being a chatbot and becomes a worker that picks up tools, runs them, and uses the answers to decide what to do next.
To get there, you’ll learn how LLMs call functions, and how we’ll wire Limitless to them. Module 03’s raw SDK reads become LLM-callable skills, the exact schema + implementation pair the agent loop in Module 05 will call repeatedly.
Agent basics tier · Reference cardHow does LLM tool use work?
You hand the model a list of functions (“tools”), each with a name, a description, and a parameter schema; the model picks a tool and fills in the arguments, your runtime executes the call and feeds the result back, and the model keeps reasoning. That loop is the substrate of every modern AI agent. The schema shape is mostly the same across providers (Claude tools, OpenAI function calling), and the description is the part that does the work: the model decides when to call a tool from its description alone, so write it like a unit-test docstring. For a Limitless agent, the module turns Module 03’s SDK reads into a browse_markets tool and lays out the six-tool starter surface the Module 05 agent loop will call repeatedly.
Limitless facts verified 2026-06-09; the agent tools are illustrative by design.
Section 01
What tool use is.
Modern LLMs don’t just generate text. You hand them a list of functions (“tools”) with names, descriptions, and parameter schemas. The model decides, based on its understanding of the user’s request, which tool to call, with what arguments. Your runtime executes the call, captures the result, and feeds it back to the model. The model then generates more text or calls more tools. That loop is the entire substrate of every modern AI agent.
Step 1
You define
A JSON schema describing the tool: name, what it does, what arguments it takes.
Step 2
Model decides
Reads the description, picks a tool, fills in the arguments, returns a structured call.
Step 3
You execute
Run the function, send the result back to the model. The model continues reasoning.
Section 02
Anatomy of a tool definition.
The schema for a tool is mostly the same shape across providers. A minimal browse_markets tool for Claude (TypeScript) and OpenAI (Python). Notice how the description matters more than you’d think, the model reads it to decide when to call the tool.
How to run this
- Install the LLM SDK you picked: npm install @anthropic-ai/sdk (TypeScript) or pip install openai (Python). No API key needed yet, this snippet only declares the schema.
- Save the snippet above as browse-markets-tool.ts, then type-check it with npx tsc --noEmit browse-markets-tool.ts.
- Save the snippet above as browse_markets_tool.py, then run python -c "import browse_markets_tool; print(browse_markets_tool.tools)".
- No errors and the tool dict prints, your schema is valid and ready for the agent loop in Module 05. If you see a schema validation error, the model would reject the tool at runtime; fix it now.
// Module 04: Tool definition (Claude format)
// A minimal tool the agent can call to browse Limitless markets.
import Anthropic from '@anthropic-ai/sdk';
const browseMarketsTool: Anthropic.Tool = {
name: 'browse_markets',
description:
'List currently active prediction markets on Limitless. ' +
'Use this when you need to discover what markets exist before deciding what to trade. ' +
'Returns up to 10 markets sorted by newest first.',
input_schema: {
type: 'object',
properties: {
limit: {
type: 'integer',
description: 'Maximum markets to return (1-50). Default 10.',
minimum: 1,
maximum: 50,
},
category: {
type: 'string',
description: 'Optional category filter, e.g. "crypto", "politics", "sports".',
},
},
required: [],
},
};
const tools: Anthropic.Tool[] = [browseMarketsTool];
# Module 04: Tool definition (OpenAI function-calling format)
# A minimal tool the agent can call to browse Limitless markets.
from openai import OpenAI
browse_markets_tool = {
"type": "function",
"function": {
"name": "browse_markets",
"description": (
"List currently active prediction markets on Limitless. "
"Use this when you need to discover what markets exist before "
"deciding what to trade. Returns up to 10 markets sorted by newest first."
),
"parameters": {
"type": "object",
"properties": {
"limit": {
"type": "integer",
"description": "Maximum markets to return (1-50). Default 10.",
"minimum": 1,
"maximum": 50,
},
"category": {
"type": "string",
"description": "Optional category filter, e.g. 'crypto', 'politics', 'sports'.",
},
},
"required": [],
},
},
}
tools = [browse_markets_tool]
Declare side effects in the description
If a tool changes state, places an order, cancels a position, moves money, say so in plain English in the description. The model cannot see your implementation; the description is all it has to judge consequences. A read-only tool like browse_markets is safe to call speculatively. A write tool like place_limit_order should say: “Side effect: places a real order that spends real USDC. Not reversible.”
Section 03
Wiring a Limitless function.
The schema is just a description. The implementation calls the SDK and returns a string the model can read. Keep responses small and focused, the model pays in tokens for every byte of result you stream back.
// Module 04: Tool implementation
// Calls the Limitless SDK and returns a model-readable string.
import { HttpClient, MarketFetcher } from '@limitless-exchange/sdk';
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
apiKey: process.env.LIMITLESS_API_KEY,
});
const marketFetcher = new MarketFetcher(httpClient);
export async function browseMarkets(args: { limit?: number; category?: string }) {
const markets = await marketFetcher.getActiveMarkets({
limit: args.limit ?? 10,
sortBy: 'newest',
});
// Return a compact summary the model can reason about.
// Avoid dumping the whole API response: too many tokens.
return markets
.slice(0, args.limit ?? 10)
.map((m, i) => `${i + 1}. ${m.title} (slug: ${m.slug})`)
.join('\n');
}
# Module 04: Tool implementation
# Calls the Limitless SDK and returns a model-readable string.
from limitless_sdk import HttpClient
from limitless_sdk.markets import MarketFetcher
http_client = HttpClient() # auto-loads LIMITLESS_API_KEY
market_fetcher = MarketFetcher(http_client)
async def browse_markets(limit: int = 10, category: str | None = None) -> str:
markets = await market_fetcher.get_active_markets()
rows = markets["data"][:limit]
# Return a compact summary the model can reason about.
# Avoid dumping the whole API response: too many tokens.
return "\n".join(
f"{i + 1}. {m['title']} (slug: {m['slug']})"
for i, m in enumerate(rows)
)
How to run this
- Keep LIMITLESS_API_KEY set from Module 03. This file exports a function; it needs to be called to see output, so append a quick smoke test to the bottom.
- Save as browse-markets-impl.ts, append browseMarkets({ limit: 3 }).then(console.log), then run npx tsx browse-markets-impl.ts.
- Save as browse_markets_impl.py, append import asyncio; print(asyncio.run(browse_markets(limit=3))), then run python browse_markets_impl.py.
- Three numbered lines like 1. Will ETH close above $3k? (slug: will-eth-close-above-3k) print, that string is exactly what the model will see when it calls your tool.
Section 04
Choosing what to expose.
The temptation is to expose everything. Resist it. The fewer tools your agent has, the better its choices, and the cheaper its mistakes. Start with the six tools below; add more only when you can articulate exactly when the model should call them.
browse_markets
List active markets, optionally filtered.
get_market_details
Fetch a single market by slug.
get_my_positions
What the agent currently owns.
get_orderbook
Depth + best bid/ask for a market.
place_limit_order
Place a CLOB limit order. Always limit, never market, model can’t reason about slippage.
cancel_order
Cancel by order id. Idempotent.
Do this
Tools should refuse, not ask
If place_limit_order receives a price outside 0.01–0.99, it should return an error string: “Error: price must be between 0.01 and 0.99.” The model will read the error and adjust. Never prompt the model for clarification from inside a tool.
Anti-pattern
The do_stuff tool
A single tool named execute_action that takes action_type as a string parameter. The model has to guess valid action types. This makes every call fragile and every failure opaque. Prefer one tool per verb.
Prerequisite: API Academy Module 05, Orders
The place_limit_order tool wraps the SDK’s order placement call. If you’ve never placed an order from raw SDK code, API Academy Module 05 walks through it in ~10 minutes. Optional but recommended.
LLM tool use: what people ask
Each answer also ships invisibly as schema.org FAQ data for search engines and AI assistants. Tap a question to expand.
-
Why do tool descriptions matter more than implementations?
Because the model decides when to call a tool from the description alone; it never sees your code. A vague description (“Get information”) loses to a sharp one (“Fetch the current bid/ask for a specific market by slug”) every time, silently deprioritising the tool you wanted called. Write descriptions like unit-test docstrings: inputs, output shape, when to call, when not to call, in 1–3 sentences ending with a concrete example. -
Which tools should a Limitless trading agent expose?
Start with six:browse_markets,get_market_details,get_my_positions,get_orderbook,place_limit_order, andcancel_order. The fewer tools your agent has, the better its choices and the cheaper its mistakes; add more only when you can articulate exactly when the model should call them. The anti-pattern is oneexecute_actiontool taking anaction_typestring the model has to guess. -
How should a tool handle invalid input from the model?
Refuse, never ask. Ifplace_limit_orderreceives a price outside 0.01–0.99, return an error string such as “Error: price must be between 0.01 and 0.99”; the model reads the error and adjusts on the next call. Never prompt the model for clarification from inside a tool, the conversation belongs to the loop, not to the implementation. -
How should write tools declare side effects?
In plain English, inside the description, because the model cannot see your implementation; the description is all it has to judge consequences. A read-only tool likebrowse_marketsis safe to call speculatively, while a write tool likeplace_limit_ordershould say: “Side effect: places a real order that spends real USDC. Not reversible.” The module also keeps agents on limit orders, never market orders, because the model cannot reason about slippage. -
How big should a tool’s response be?
Compact. The model pays in tokens for every byte of result you stream back, so return a focused summary instead of dumping the whole API response: the module’sbrowse_marketsimplementation reduces each market to a numbered line of title plus slug. That string is exactly what the model sees when it calls your tool, and it is all it needs to pick the next action.
Module checklist
Five quick confirmations.
Tick each item once you’ve actually done it. The Continue button unlocks at 5/5.
I understand the observe → decide → execute → respond loop of tool use
I can write a tool schema (Claude or OpenAI format) with name, description, and parameters
I wired the browse_markets tool to the Limitless SDK in my preferred language
I know the six tools every Limitless agent should expose, and why I’d resist adding more
I keep tool responses compact, small summaries, not raw API dumps
Module 04 complete
Tools defined.
Your agent has hands now. When it decides to look at a market, place an order, or pull a position, it doesn’t describe the API to itself, it just calls the tool and uses the answer to make the next decision.
Concretely, you can describe a tool to an LLM and implement it against the Limitless SDK. A working schema, a token-efficient implementation, and a starter surface of six tools every Limitless agent should expose.
A working browse_markets tool schema in Claude (TypeScript) or OpenAI (Python) format, name, description, and JSON-schema parameters the model can actually reason about.
A tool implementation that calls the Limitless SDK and returns a compact, token-efficient string, not a raw JSON dump, for the model to read.
The six-tool starter surface every Limitless agent should expose, browse_markets, get_market_details, get_my_positions, get_orderbook, place_limit_order, cancel_order, plus the rule of thumb for when to add more.
Next up: the loop that ties it together, observe, decide, execute, respond, and the stop conditions that keep it from spiralling.
Complete the checklist above to unlock