Tool calling
A model cannot run anything itself. With tool calling, it replies with a structured request, your code performs the call, and you send the result back. The loop repeats until the model answers in plain text.
A request is just data:
call = {
"name": "add",
"arguments": {"a": 2, "b": 3},
}The safe way to run it is a dispatch table: a dict of the functions you allow.
TOOLS = {"add": lambda a, b: a + b}
fn = TOOLS[call["name"]]
print(fn(**call["arguments"])) # 5Rules
- The name is untrusted. Look it up in the table, and never pass it to
evalorgetattr. - Arguments may be wrong or missing.
- A failing tool must not crash the loop. Return the error as data, so the model can read it and try again.
Your task
Write TOOLS with add(a, b) and word_count(text). Write run_tool_call(call, tools=TOOLS) returning {"ok": True, "result": value} on success and {"ok": False, "error": message} for a call that is not a dict, an unknown or missing name, non-dict arguments, wrong arguments, or a tool that raises. Missing arguments means {}.
The explanation opens here once every check passes.