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-thinkingpick 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_callsat once. Execute all of them and add onetoolmessage pertool_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
argumentsbefore executing. The model sometimes sendsorder_id: "my order"instead of a number. Return a meaningful error in thetoolmessage 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.