Most of the AI requests we get now are not "add a chatbot". They sound like this: "Can the assistant look up an order, change a delivery date and email the customer?" That is an agent that uses tools, and it is a different kind of project from answering questions over documents. The model is not the hard part. The hard part is deciding what it is allowed to touch in your system.
This is how we would build it for a client running Laravel, WordPress or a custom API.
Start with the verbs, not the model
Before anyone picks a model, we write down the actions the assistant should be able to take, as plain verbs against your real data:
- look up an order by number
- list open invoices for a customer
- create a support ticket
- reschedule a booking
- refund an order
Then we sort them into three groups. Read-only actions are cheap to allow. Actions that change data but can be undone (a draft reply, a rescheduled slot) need a record of what changed. Actions that cannot be undone, such as refunds, deleting records and sending money, do not go to the model at all in the first release. The model proposes, a person approves.
Most clients are relieved by this list. It turns "AI for our business" into five or six things that a developer can estimate.
Tools are ordinary API endpoints with strict inputs
In a Laravel project, each tool is a normal controller action or a small service class behind an authenticated route. We do not give the model a database connection or a general "run this query" tool. We give it narrow functions:
get_order(order_number)
list_invoices(customer_id, status)
reschedule_booking(booking_id, new_slot)
Each one validates its input with the same form requests the rest of the application uses. If the model sends a booking id that does not exist, or a slot in the past, the validation error goes back to the model as the tool result and it can try again. We keep the tool descriptions short and literal, because the model chooses tools based on those descriptions and vague ones lead to wrong calls.
For WordPress sites the pattern is the same, using custom REST routes with capability checks instead of calling internal functions directly. For a Python service sitting beside either, a small FastAPI layer holds the model calls and talks to the main site only through these same endpoints.
Act as the user, never as an admin
The most common mistake we see is giving the assistant one powerful service account. It works in the demo and it is a risk in production.
Instead, the assistant should call tools with the logged-in user's own permissions. If a customer asks about "my orders", the tool receives the customer id from the session, not from the model's text. The model cannot ask for someone else's invoices because the customer id is never a parameter it controls. Staff-facing assistants get staff permissions, and nothing more.
This also keeps your existing authorization rules in one place. We do not want a second permission system living inside a prompt.
Keep a log you can read
Every tool call gets one row: who, which tool, what arguments, what came back, how long it took, and whether a person approved it. For anything that changes data we also store the before and after values.
This is not only for debugging. When a client asks "why did the assistant move this booking?", a support person should be able to open a page and see the conversation and the exact call. Without that, the first odd incident ends the project's credibility.
Approval steps belong in the interface
For actions that need sign-off, the screen matters as much as the backend. The assistant should show a plain summary ("Refund NPR 4,500 to order 10482, reason: damaged item") with Approve and Cancel buttons, not a paragraph the user has to interpret. The button calls the same endpoint, and the server checks that the pending action still matches what was shown.
On mobile apps we handle this the same way, with a confirmation sheet driven by the server's description of the pending action. That way the Android and iOS versions cannot drift from the web version, and a wording change does not need an app store release.
Limits that stop expensive loops
Agents can loop: call a tool, misread the result, call it again. We put hard limits in the application code, not in the prompt:
- a maximum number of tool calls per request
- a maximum cost per conversation
- a timeout for each tool
- a safe fallback message when a limit is hit, with a way to reach a human
Tool calls that fail should fail quietly for the customer and loudly in our logs. We also keep an evaluation set of real questions with the expected tool calls, and run it before every change to the prompt or model. If a new model version calls the wrong tool on one of those questions, we see it before the client does.
What we usually ship first
For a first release we would typically build:
- Three to six read-only tools and one reversible write tool.
- User-scoped permissions on every call.
- The audit log and a simple admin page to browse it.
- An approval step for anything that changes money or customer data.
- Cost and loop limits, plus a human handoff.
That is a few weeks of work for a small team, and it gives you something safe to put in front of real users. Once the logs show which requests people actually make, adding the next tool is routine.
When we would say wait
If your underlying data is inconsistent, or your business rules only live in someone's head, an agent will expose that quickly. In that case the better first project is cleaning up the API or the admin screens, and adding the assistant afterwards. We would rather tell you that in the first meeting.
If you are thinking about an assistant that works on your own system, send us the list of actions you would want it to perform. We will tell you which are safe to automate now and which should stay with a person.