Engrain by ByteRush

AGT.06.01 Mid-level· 10 minCore — asked often

Tool calling: how a model asks your code to act

The model never runs your functions. It asks; your code runs the tool and sends the result back. Learn the tool-use loop, schemas, parallel calls and errors as results.

Comes up in: AI engineering, Coding

Learn#

Karan wires a support bot to his shop’s order API. A customer asks “Where is order 1042?” and the bot confidently makes up a date. The model has no access to his database — it can only produce text.

Tool calling (also called function calling) fixes this. You describe tools — a name, a plain description, and a tool schema for the input — and send them with the conversation Source: Claude docs: Tool use.

The model never runs your code. When it needs a tool, it stops with stop_reason: "tool_use" and returns the tool’s name and input. Your code runs the function and sends back a tool result with the same ID. Repeat until the model answers in text.

Here a fake model (free, same output every run) asks for two calls at once; one fails:

function fakeModel(messages) {
  const last = messages.at(-1);
  if (typeof last.content === "string") return { stop_reason: "tool_use", content: [
    { type: "tool_use", id: "t1", name: "get_weather", input: { city: "Pune" } },
    { type: "tool_use", id: "t2", name: "get_weather", input: { city: "Atlantis" } } ] };
  return { stop_reason: "end_turn", content: [{ type: "text", text: "Pune is 31°C. I couldn't find Atlantis." }] };
}
const tools = { get_weather: ({ city }) => {
  const data = { Pune: 31 };
  if (!(city in data)) throw new Error(`unknown city: ${city}`);
  return `${data[city]}°C`;
} };

const messages = [{ role: "user", content: "Weather in Pune and Atlantis?" }];
for (;;) {
  const reply = fakeModel(messages);
  messages.push({ role: "assistant", content: reply.content });
  if (reply.stop_reason !== "tool_use") { console.log("final:", reply.content[0].text); break; }
  const results = reply.content.filter((b) => b.type === "tool_use").map((call) => {
    try { return { type: "tool_result", tool_use_id: call.id, content: tools[call.name](call.input) }; }
    catch (err) { return { type: "tool_result", tool_use_id: call.id, content: err.message, is_error: true }; }
  });
  for (const r of results) console.log(r.tool_use_id, r.is_error ? "ERROR" : "ok", r.content);
  messages.push({ role: "user", content: results }); // all results in ONE message
}

Output (verified)

t1 ok 31°C
t2 ERROR unknown city: Atlantis
final: Pune is 31°C. I couldn't find Atlantis.
The tool-use loop
Your codeModel APIYour tool (function)1messages + tool definitions2stop: tool_use (name + input)3run it4result or error5tool_result (same id)6final answer (or more tool calls)
Describe this diagram

Your code, highlighted, sends the conversation and the tool definitions to the model API. The model replies that it wants a tool used, giving the tool name and input. Your code runs that tool, which returns a result or an error. Your code sends the result back to the model, linked by the same ID. The model then gives a final answer, or asks for more tools, and the loop repeats.

Predict#

Predict first

A tool throws an exception inside your loop. What should your code send to the model?

Interview angle#

Say it like this — about 30 seconds

“I send the conversation plus tool definitions — name, description, JSON schema. If the model needs a tool it stops with tool_use and gives me the name and input. My code validates the input, runs the function and sends a tool_result with the same ID; errors go back as results too. If it asked for several tools, I run them and return all results in one message. I loop until it answers in text, with a turn limit.”

Stays on your device. Nothing is uploaded.
Follow-up How do you make tools reliable for the model?
Few, clearly named tools with plain descriptions; strict schemas so inputs are valid; small, useful outputs; error messages that say how to fix the call.
Follow-up What stops an infinite loop?
A maximum number of turns, a time or token budget, and stopping on repeated identical calls.
Follow-up Is it safe to let the model call any tool?
No. Give least privilege, validate every input, and require human approval for risky actions such as payments or emails (AGT.08.02, AGT.10).

Practice#

On your own

Implement runAgent(model, tools, question) against the provided fake model (no paid API needed).

Done when

  • Parallel tool calls from one turn are answered in a single user message, matched by ID.
  • A thrown error or unknown tool becomes a tool_result with is_error: true, and the loop continues.
  • The loop stops on a text answer or after maxTurns.
Your solution
Ctrl/⌘ + Enter

Remember#

Try to answer each card before you open it. Then add them to your reviews.

In tool calling, who executes the tool?

Your code — the model only asks for it.

How does a tool result find the call it answers?

It carries the same tool-use ID.

The model asks for two tools in one turn. How do you send the results?

Both tool results together in one user message.

What do you send back when a tool fails?

A tool result marked as an error, with a helpful message.

Go deeper

Four layers. Stop at the one your interview needs; each ends with a quick check.

The core ideaEvery round

A tool is a function you describe to the model: name, description, input schema. The loop is ask → run → return → repeat, and it always ends with a text answer or a limit you set.

Check yourself: What three things define a tool?

A name, a description and an input schema.

How it works underneathSenior rounds

The model writes a structured tool_use block instead of text; the API returns it with stop_reason: "tool_use". You append the assistant turn unchanged, then a user turn of tool_result blocks.

Marking a tool strict makes the API guarantee its input matches the schema
; still validate, because your code is the last line of defence.

Check yourself: Which stop reason means 'run a tool now'?

tool_use.

Edge cases and trapsWhere people slip

Unknown tool names, invalid JSON, timeouts and huge outputs all happen. Return short, specific errors (“city must be a known city name”) rather than stack traces. Truncate large results. Never put secrets in tool descriptions — they are part of the prompt.

Check yourself: Why not send each parallel result in its own message?

Results from one turn belong in one message; splitting them breaks the pairing and can teach the model to stop calling tools in parallel.

In productionStaff-level answers

Design fewer, higher-level tools (“find_order” beats five raw SQL tools), log every call for evals (AGT.09), and gate risky actions behind approval.
Providers also offer server-side tools such as web search or code execution, which run on their side — check each provider’s current list and pricing Source: Claude docs: Tool use (client vs server tools).
Engrain itself uses no paid AI; you practise here with a fake model, and later with your own key or a local model.

Check yourself: Name one tool-design rule that cuts errors.

Few, clearly described tools with strict schemas and helpful error messages.

Finished the five steps? Mark it, and its cards join your reviews.

Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

Interview questions this answers

  • Explain how tool calling works end to end.
  • How do you design good tools for an LLM?