-
Notifications
You must be signed in to change notification settings - Fork 5
EN Course 07 Tool Calling
Tool calling has three phases:
- parse the model tool request
- dispatch to a runtime implementation
- write the tool result back into the conversation
The dispatch table is intentionally direct:
tool_dispatch() {
local name="$1"; shift
case "$name" in
Read) tool_read "$@" ;;
Write) tool_write "$1" "$2" ;;
Edit) tool_edit "$@" ;;
Bash) tool_bash "$1" "$2" ;;
Glob) tool_glob "$1" "$2" ;;
Grep) tool_grep "$1" "$2" "$3" "$4" ;;
TodoWrite) printf '%s' "$1" ;;
PlanConfirm) tool_plan_confirm ;;
PlanClear) tool_plan_clear ;;
Skill) tool_skill "$1" ;;
WebSearch) tool_web_search "$1" ;;
WebFetch) tool_web_fetch "$1" ;;
SubAgent) tool_sub_agent "$1" "$2" "$3" ;;
esac
}The model sends JSON. The runtime converts each known tool into positional arguments using a tool-specific parameter list:
tool_param_keys() {
case "$1" in
Read) printf 'path offset limit' ;;
Write) printf 'path content' ;;
Edit) printf 'path old_string new_string' ;;
Bash) printf 'command timeout' ;;
Glob) printf 'pattern path' ;;
Grep) printf 'pattern path glob context' ;;
esac
}This keeps each tool implementation small and avoids re-parsing schema details inside every tool.
tools.json is not just documentation. It is a provider-facing protocol surface shared by all runtimes.
util_load_tool_defs() {
local tools_file script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
tools_file="$script_dir/tools.json"
[[ -f "$tools_file" ]] || util_die "Cannot find tools.json: $tools_file"
TOOL_DEF_JSON=$(<"$tools_file")
}Claude requests can send this schema directly. OpenAI requests convert the same tool definitions into type=function tools at the transport boundary.
The agent loop should not know which provider shape was used. It only receives normalized TOOL_CALL messages and dispatches by tool name.
Tool output can be large. The runtime truncates long results before sending them back to the model:
tool_format_result() {
local output="$1" size marker marker_len tail_lines=5 tail_text tail_len head_len
if (( ${#output} <= TOOL_RESULT_MAX_BYTES )); then
printf '%s' "$output"
return 0
fi
# keep the beginning, a truncation marker, and the final lines
}The key rule is that the assistant tool_use and the corresponding user tool_result must remain paired in conversation history.
Bash Tool Permission Mode explains why the shell tool needs a separate policy layer.