AI Function Calling & Tool Use
debt(d5/e5/b5/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep as the tool, with automated detection of unvalidated tool arguments, SQL/shell commands built from model-provided arguments. This is a specialist SAST tool, not a default linter — it requires custom rules to flag missing validation on LLM-provided inputs. Runtime misuse (e.g. subtly wrong arguments that pass type checks but are semantically dangerous) may still slip through, pushing toward d6, but the explicit semgrep pattern match keeps it at d5.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix says to define strict JSON schemas and validate all tool arguments — but in practice this means adding validation layers to every tool handler, implementing confirmation steps for write operations (common_mistakes), and potentially refactoring how tool results are processed. For a single tool it's e3, but real-world function calling typically involves multiple tools across the application, making it a multi-file refactor within the AI integration component.
Closest to 'persistent productivity tax' (b5). Function calling applies across web, cli, and queue-worker contexts. Once you adopt AI tool use, every new tool requires schema definitions, validation logic, and safety checks. The pattern becomes a persistent tax: each new capability added to the agent requires careful schema design, input sanitisation, and testing. It doesn't quite define the system's shape (b7-b9), but it creates an ongoing structural commitment that affects multiple work streams as the agent's capabilities grow.
Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field is telling: developers believe 'function calling gives the LLM direct database access' when in fact the LLM only returns arguments and your code controls execution. This is a serious conceptual trap because the name 'function calling' strongly implies the model calls functions, mirroring how function calls work everywhere else in programming. Developers coming from any programming background will assume the model executes code, not that it merely suggests structured arguments. Combined with common_mistakes like not treating LLM arguments as untrusted input (contradicting the trust model developers have for their own function arguments), this is a genuine t7 trap.
Also Known As
TL;DR
Explanation
Function calling flow: define tools with name/description/JSON schema, send to LLM with the user message, LLM responds with tool_use block (function name + arguments), application executes the function, return result to LLM, LLM generates final response. The LLM decides when to call tools — your application controls execution. Treat LLM-provided arguments as untrusted user input. PHP: Anthropic and OpenAI PHP SDKs both support tool definitions.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Not validating LLM-provided arguments — treat as untrusted input
- Giving LLMs write access without confirmation steps
- Overly broad tool descriptions — vague descriptions cause unnecessary calls
- Not handling malformed or missing tool arguments gracefully
Avoid When
- Do not trust the LLM-supplied arguments as safe input — always validate and sanitise before executing the function.
- Avoid exposing functions that perform irreversible operations (delete, send email, charge card) without a confirmation step.
- Do not use function calling as a shortcut for executing arbitrary code or SQL provided by the model.
When To Use
- Use function calling when you need the LLM to extract structured data (dates, filters, search parameters) from natural language.
- Apply it to replace brittle regex/JSON parsing of free-text LLM output — the model returns validated structured arguments.
- Use for tool orchestration in agents where each tool has a well-defined schema the model can select and populate.
Code Examples
// Direct SQL from LLM — injection risk:
$toolCall = $response->content[0];
$db->query($toolCall->input['query']); // Never do this!
foreach ($response->content as $block) {
if ($block->type === 'tool_use' && $block->name === 'searchGlossary') {
// Validate and sanitise — treat as untrusted:
$query = substr(strip_tags($block->input['query'] ?? ''), 0, 100);
$result = $this->glossary->search($query); // Safe parameterised search
}
}