Field notes from working through example 12 of Ardan Labs’ Ultimate AI course by Bill Kennedy and Florin Pățan (Apache 2.0). My fork: PratikDhanave/ai-training. Thank you Bill and Florin for teaching this material — the patterns in this post are derived from the course; the production reflections at the end are mine.
What the example teaches
Tool calling is two phases:
Phase 1. Application sends tools + user message → LLM responds with a tool call (function name + JSON args) instead of a freeform answer.
Phase 2. Application executes the tool, appends the result to the conversation, calls the LLM again → LLM uses the result to produce the final answer.
The “function” the LLM “calls” is just a JSON object the application interprets. The LLM doesn’t run code; the application does.
What it looks like
// Phase 1
tools := []Tool{{Name: "get_weather", Schema: weatherSchema, Run: getWeather}}
resp := llm.Chat(ctx, []Message{userMsg}, tools)
// resp.ToolCalls = [{Tool: "get_weather", Args: {"city": "Pune"}}]
// Application runs the tool
result := getWeather(resp.ToolCalls[0].Args)
// result = "32°C, partly cloudy"
// Phase 2 — append both the call and the result
messages := []Message{
userMsg,
assistantToolCall(resp.ToolCalls[0]),
toolResult(resp.ToolCalls[0].ID, result),
}
finalResp := llm.Chat(ctx, messages, tools)
// finalResp.FinalAnswer = "It's 32°C and partly cloudy in Pune."
What I learned
The shape is the same across providers. OpenAI, Anthropic, Gemini, Bedrock — all use a variant of this two-phase shape. The exact JSON differs; the mental model is the same.
Schema strictness changes everything. With permissive schemas, the LLM passes weird args (“city”: “P-u-n-e”). With strict JSON Schema validation, the args are parseable or the tool refuses. Strict every time.
Production connection
Genie’s tool dispatcher follows this shape exactly. The schema enforcement comes from pkg/governance/schema_policy.go — every tool’s args validate against a JSON schema before the tool runs. The pattern from Ardan; the policy layer is Genie-specific.
Credit & reference. This post is field notes on example 12 from Ardan Labs’ Ultimate AI by Bill Kennedy + Florin Pățan, licensed Apache 2.0. The original example: cmd/examples/example12-tool-calling/. My fork with notes: PratikDhanave/ai-training. Highly recommend the course for anyone building AI applications in Go — the material is rigorous and the Kronk + yzma + llama.cpp pipeline gives you hardware-accelerated local inference end-to-end. Thank you, Bill and Florin.