All articles
Article
June 30, 20268 min

Agents and tool calling: how to build function calling

The model only asks to call your function; you are the one who runs it. The tools schema, the execution loop, and the usual pitfalls with parallel calls.

Yenisei team


Agents and tool calling: how to build function calling

An agent is a model that has been given tools and a loop. As the conversation goes, it decides what to call: check the weather, query the database, send an email. You describe the functions, the model returns its intent to call them, you execute them and hand the result back.

Describing tools

A tool is a JSON Schema. The more precise the description, the less often the model gets the arguments wrong.

tools = [{
"type": "function",
"function": {
"name": "get_order_status",
"description": "Returns the status of an order by its number",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "Order number, for example A-1024"}
},
"required": ["order_id"],
},
},
}]

The execution loop

A point often missed: one request to the model is not the whole conversation. The model may ask for a tool, get the answer, and ask for the next one. You need a loop.

messages = [{"role": "user", "content": "Where is my order A-1024?"}]
while True:
resp = client.chat.completions.create(
model="qwen3.8-27b", messages=messages, tools=tools,
)
msg = resp.choices[0].message
messages.append(msg)
if not msg.tool_calls:
print(msg.content)
break
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = run_tool(call.function.name, args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})

Choosing a model

Models handle function calling differently. You need one that was trained for tools and keeps the structure:

  • deepseek-pro, qwen3.8-27b, kimi-k2-thinking pick tools confidently and do not invent ones that don't exist.
  • Flash models can call functions too, but on long chains they more often lose track of the order of steps. For a simple "one call, one answer" they are enough.
  • A model with a 128K context matters when the dialog history with tool results grows.

Pitfalls

  • Parallel calls. The model may return several tool_calls at once. Execute all of them and add one tool message per tool_call_id. Skip one, and the next request fails with a mismatch error.
  • Infinite loops. Set an iteration limit (8 to 10). Sometimes the model gets stuck calling the same tool again and again.
  • Garbage in the arguments. Validate arguments before executing. The model sometimes sends order_id: "my order" instead of a number. Return a meaningful error in the tool message and it will correct itself.
  • Side effects. Wrap tools that change something (send a payment, delete a record) in a confirmation. Do not give the agent the right to do anything irreversible without a human in the loop.

A design tip

Make tools large and meaningful rather than "one function, one SQL query". Instead of select_users, join_orders, filter_by_date, give it a single find_customer_orders(customer, period). A model is worse at assembling complex chains out of small primitives than at calling one clear tool. And write the descriptions in the language of your domain, not the language of your database.