Skip to content
All guides

Build an agent with tool calling

An agent is a loop: the model reads the task, asks for one of your tools, your code runs it and sends back the result, and the model continues until it can answer. The model decides what to do; your code decides what is allowed.

Recommended models

Chosen from models whose abilities we have verified for this job. Cost is for one agent step (about 3K tokens in, 300 out), at current prices.

ModelInput / 1MOutput / 1MContextPer request
Our pickDeepSeek V4.1 Flash 1017
deepseek/deepseek-v4.1-flash-1017
$0.375$1.501.05M$0.001388
Also strongGLM 5.3
z-ai/glm-5.3
$1.75$5.501.05M$0.006025
Lowest costgpt-oss-20b
openai/gpt-oss-20b
$0.0625$0.5625128K$0.000325

How it works

  1. 1Write ordinary functions for what the agent may do, and describe each with a name, a description and JSON Schema parameters.
  2. 2Call the model with tools. If the reply contains tool_calls, run each one and append a tool message with its result.
  3. 3Repeat until the model answers without tool calls. Always cap the number of steps.

Copy, paste, run

Set AMR_API_KEY to a key from your dashboard, install the OpenAI SDK (pip install openai or npm install openai) and run it. Swap the model for any other in the table.

import json
import os

from openai import OpenAI

client = OpenAI(base_url="https://api.aimodelsrouter.ai/v1", api_key=os.environ["AMR_API_KEY"])
MODEL = "deepseek/deepseek-v4.1-flash-1017"


# 1. Your real functions. The model decides when to call them; your code runs them.
def get_order_status(order_id: str) -> dict:
    return {"order_id": order_id, "status": "processing", "carrier": None}  # replace with a real lookup


def refund_order(order_id: str, reason: str) -> dict:
    return {"order_id": order_id, "refund": "approved", "amount_usd": 49.0}  # replace with a real action


FUNCTIONS = {"get_order_status": get_order_status, "refund_order": refund_order}

# 2. Describe them to the model.
tools = [
    {"type": "function", "function": {
        "name": "get_order_status", "description": "Look up the shipping status of an order.",
        "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}}},
    {"type": "function", "function": {
        "name": "refund_order", "description": "Refund an order. Only when the customer asks for a refund.",
        "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}, "reason": {"type": "string"}},
                       "required": ["order_id", "reason"]}}},
]

# 3. The agent loop: call the model, run the tools it asks for, send back the results, repeat.
messages = [{"role": "user", "content": "Where is my order 1042? If it hasn't shipped yet, refund it."}]
for _ in range(10):  # safety limit on steps
    response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
    message = response.choices[0].message
    if not message.tool_calls:
        print(message.content)
        break
    messages.append(message.model_dump(exclude_none=True))
    for call in message.tool_calls:
        result = FUNCTIONS[call.function.name](**json.loads(call.function.arguments))
        print(f"-> {call.function.name}({call.function.arguments}) = {result}")
        messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})

Tested 2026-10-28: both versions were run against the live API with each model recommended at the time, and their answers checked.

Tips

  • Write tool descriptions as instructions: when to use the tool and when not to. Vague descriptions cause wrong calls.
  • Validate arguments and permissions in your code before running an action such as a refund; never trust the model's arguments blindly.
  • Return real errors from tools (for example "order not found") so the model can recover instead of guessing.
All 31 models that fit this job