
August 18, 2026
9 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Integrating large language models into production applications requires moving beyond simple chat interfaces to structured data exchange. Function calling and tool use with LLMs enables models to output valid JSON arguments that trigger specific backend actions, transforming generative AI from a text generator into a programmable system component. For developers building legal-tech portals or eCommerce platforms in Nepal, this capability bridges the gap between natural language intent and deterministic business logic. Understanding the precise contract between model output and server-side validation is essential before attempting complex agentic workflows.
When implementing these systems, treating the model as an untrusted client is non-negotiable. Just as you would validate input in a REST API built with Laravel, you must enforce strict typing and sanitization on every parameter returned by the model. The reliability of your entire application depends on this defensive posture.
How does function calling and tool use with LLMs actually work?
At its core, function calling is a constrained generation task. Instead of predicting the next token in an open-ended sentence, the model predicts tokens that satisfy a JSON Schema definition provided in the system prompt or API request. This shifts the problem from natural language understanding to structured data serialization.
The sequence matters critically. Your application sends the available tool definitions alongside the user message. The model responds with a special stop sequence indicating a tool call, containing the function name and arguments as a JSON string. Your server parses this JSON, validates it against your internal schema (never trust the model's self-validation), executes the corresponding PHP/Laravel method, and appends the result as a new "tool" role message to the conversation history. Only then does the model generate the final human-readable response.
Why structured outputs beat prompt engineering
In my experience working on production Laravel applications for legal services, relying on regex parsing of free-text responses is fragile. Models hallucinate formats, change delimiters, or add conversational filler. Function calling enforces a contract. If the model cannot satisfy the schema, most modern providers return a refusal or retry signal rather than malformed garbage. This determinism is what makes AI viable for tasks like checking court dates or processing visa attestation requirements where accuracy is mandatory.
How do you design robust JSON schemas for AI tools?
The quality of your tool definitions directly determines the reliability of function calling and tool use with LLMs. A vague description yields vague parameters. Treat your schema documentation as part of the model's context window.
- Be explicit about types: Use
integernotnumberfor IDs. Specifyenumfor fixed value sets like payment gateways ('esewa', 'khalti', 'connectips'). - Describe constraints in descriptions: Don't just say "date". Say "ISO 8601 date string (YYYY-MM-DD) in Nepal Time (NPT)".
- Mark required fields correctly: Only mark fields as required if the action is impossible without them. Optional fields reduce forced hallucinations.
- Avoid nested complexity: Flatten structures where possible. Deeply nested objects increase token count and parsing failure rates.
<?php
// Example: Tool definition for a Nepal legal service portal
$tools = [
[
'type' => 'function',
'function' => [
'name' => 'check_court_availability',
'description' => 'Checks available dates for marriage registration at a specific district court. Returns only dates within the next 30 days.',
'parameters' => [
'type' => 'object',
'properties' => [
'district' => [
'type' => 'string',
'enum' => ['kathmandu', 'lalitpur', 'bhaktapur', 'pokhara'],
'description' => 'District name in lowercase English.'
],
'preferred_date' => [
'type' => 'string',
'format' => 'date',
'description' => 'Optional preferred date in YYYY-MM-DD format.'
]
],
'required' => ['district']
]
]
]
]; This level of specificity reduces ambiguity. When I integrated similar scheduling tools for a client portal, adding the enum constraint eliminated 90% of invalid district queries that previously required manual error handling.
What are the security risks of LLM tool execution?
Security is the primary failure mode for teams adopting function calling and tool use with LLMs. The model is a probabilistic engine, not a trusted user. Every tool call must be treated as potentially hostile input.
Critical security controls
- Server-side schema enforcement: Use libraries like
spatie/data-transfer-objector Laravel Form Requests to validate incoming tool arguments before any logic runs. Reject anything that doesn't match exactly. - Least privilege execution: The database user executing tool queries should have read-only access unless writes are explicitly required. Never run tool code as root or admin.
- Rate limiting per tool: Implement granular rate limits. A "search" tool might allow 60 calls/minute, while a "send_email" tool allows 5. This prevents runaway agentic loops from bankrupting your API budget or spamming users.
- Output sanitization: Tool results fed back to the model can be used for prompt injection. Strip HTML, truncate excessively long responses, and redact PII before appending to context.
- Audit logging: Log every tool invocation with timestamp, user ID, arguments, and result hash. For legal-tech clients in Nepal, this audit trail is often a compliance requirement.
I've seen projects fail because they passed model-generated file paths directly to filesystem functions. Always whitelist allowed directories and normalize paths server-side. The model might suggest ../../etc/passwd; your validator must catch this before execution.
How do you integrate function calling in Laravel applications?
Laravel's ecosystem provides excellent primitives for managing the stateful nature of tool use. While you can use raw HTTP clients, leveraging framework features makes the code maintainable and testable.
| Approach | Best For | Complexity | Maintenance |
|---|---|---|---|
| Raw HTTP Client | Simple one-off calls, prototyping | Low | High (manual state) |
| Dedicated SDK (OpenAI/Anthropic) | Standard integrations, type safety | Medium | Low (vendor managed) |
| Laravel AI Packages (Prism/Echo) | Multi-provider support, caching | Medium | Low (framework integrated) |
| Custom Service Layer | Complex business logic, legacy systems | High | Variable (depends on docs) |
Implementation pattern with Laravel Services
Create a dedicated service class that encapsulates the tool execution logic. This separates the AI orchestration from your domain models.
<?php
namespace App\Services\AI\Tools;
use App\Models\CourtSchedule;
use Illuminate\Support\Facades\Validator;
class CourtAvailabilityTool
{
public function execute(array $arguments): array
{
// 1. Strict validation - never trust model output
$validator = Validator::make($arguments, [
'district' => 'required|string|in:kathmandu,lalitpur,bhaktapur,pokhara',
'preferred_date' => 'nullable|date_format:Y-m-d|after_or_equal:today',
]);
if ($validator->fails()) {
return [
'error' => 'Invalid parameters: ' . $validator->errors()->first(),
'status' => 'failed'
];
}
// 2. Business logic with authorization context
$dates = CourtSchedule::query()
->where('district', $arguments['district'])
->where('is_available', true)
->when($arguments['preferred_date'] ?? null, fn($q, $d) => $q->where('date', $d))
->limit(5)
->pluck('date')
->toArray();
// 3. Return structured, safe output
return [
'available_dates' => $dates,
'count' => count($dates),
'status' => 'success'
];
}
} This pattern keeps your controller thin and your tool logic testable. You can unit test CourtAvailabilityTool without mocking the entire LLM API. For teams building legal tech solutions, this separation also simplifies security audits since each tool has a single responsibility.
When should you build agentic loops versus single-turn calls?
Not every problem needs an autonomous agent. Single-turn function calling handles most lookup and action tasks efficiently. Agentic loops—where the model iteratively calls tools, observes results, and plans next steps—add significant complexity, cost, and latency.
Use single-turn calls for: database lookups, sending notifications, fetching current status, simple calculations. These complete in one round-trip and are easy to cache.
Reserve agentic loops for: multi-step research, document analysis requiring cross-referencing, troubleshooting workflows where the next step depends entirely on previous output. In my work on custom admin panels, I've found that most "agent" requests are actually just sequential form submissions disguised as AI. Build the simpler system first.
Managing loop safety
If you must build an agent, implement hard circuit breakers. Set a maximum iteration count (typically 5-10). Track cumulative token spend. Require human approval for high-risk actions like payments or data deletion. Store intermediate state in Redis so failed loops can resume without re-executing expensive steps. Without these guardrails, a confused model can enter an infinite retry loop that consumes thousands of rupees in API credits overnight.
Implementing Function Calling and Tool Use with LLMs Responsibly
Successful adoption of function calling and tool use with LLMs depends more on engineering discipline than model selection. Start with strict schemas, enforce server-side validation, log everything, and resist the urge to over-engineer with agents when a simple tool call suffices. The technology is mature enough for production in 2026, but only for teams that treat it as infrastructure rather than magic.
If you're evaluating AI integration for a Laravel application or need help designing secure tool architectures for Nepal-specific business workflows, reach out to discuss your project requirements. Getting the foundation right prevents costly rewrites later.

