← Nguyễn Trung Tâm · Portfolio

Giving an LLM real hands

Function-calling that drives a real WooCommerce cart on a live store

By Nguyễn Trung Tâm AI Engineer ~8 min read Live: tanphatsolutions.vn
LLMFunction CallingStructured Output WooCommerce Store APIOpenRouterProduction

Most "AI chatbots" on shopping sites can talk but can't do anything — they answer, then leave the customer to go find and click everything themselves. On a building-materials store I built a sales chatbot with a different goal: it should recommend the right product out of thousands of SKUs and then act — add it to the cart, adjust quantities, open checkout — all inside one chat window. This is a write-up of how that works, why the interesting part is the function-calling contract and the tool layer, not the prompt, and the production traps I hit making it reliable.

01The problem: an assistant that can act

The store runs on WordPress + WooCommerce. A shopper asking "tôi cần gạch ốp lát phòng tắm" doesn't want a paragraph — they want the two or three right products and a fast path to buy. So the chatbot needs three capabilities beyond chatting: retrieve matching products from the live catalog, render them as real product cards, and mutate the cart on the customer's behalf. The last one is where an LLM stops being a text box and starts being an agent with tools.

02Architecture: keep the model on the server

The whole thing is a self-contained WordPress plugin. The browser widget is deliberately dumb: it renders messages and calls one endpoint. The LLM lives server-side, reached through OpenRouter (one endpoint, swappable models, no lock-in). The API key never touches the client.

  Browser widget (vanilla JS)
        │  POST { messages: last 10, page_url }   + X-WP-Nonce
        ▼
  Plugin REST route  (WordPress)  ──►  OpenRouter  ──►  LLM
        │                                   │
        │            structured JSON  ◄──────┘
        │      { reply, products[], add[] }
        ▼
  Widget renders reply + product cards, then EXECUTES actions
        │
        ▼
  WooCommerce Store API   (view / add / update / remove / checkout)
The model decides; the client executes against a real commerce API. The key stays server-side.

Two design decisions matter here. First, the API key is a server secret — a browser-side LLM call would leak it to anyone who opens devtools. Second, the model never touches the database directly. It emits intents; the client turns them into calls against WooCommerce's official Store API. That boundary is what keeps a chatty model from becoming a security problem.

03Context: last 10 messages + the page

Every turn sends the last 10 messages (a rolling window — enough memory to stay coherent, bounded so tokens and latency stay flat) plus the current page URL. That URL is cheap grounding: if the customer is already on a product page, the model can talk about this product without being told which one.

04The contract: structured output as the tool interface

The heart of the system is the shape of what comes back. The LLM doesn't return prose — it returns a small JSON object the client knows how to act on:

{
  "reply": "Dạ bên em gợi ý 2 mẫu gạch ốp phòng tắm chống trơn ạ:",
  "products": [
    { "id": 1841, "name": "...", "price": "...", "image": "...", "url": "...", "addable": true }
  ],
  "add": [ { "url": "https://.../san-pham/..." } ]   // an action: put this in the cart
}

Three channels in one response:

This is function-calling in spirit: the model chooses an action and its arguments; deterministic code validates and executes it. The LLM proposes; the commerce API disposes.

05The tool layer: driving a real cart

Actions run against the WooCommerce Store API — the same API the theme's own cart uses — so the chatbot's cart is the site's cart. Inside the chat the customer can view the cart, change quantities, remove lines, see the live total, and jump to checkout. Two things made this genuinely hard in production:

Add-to-cart also refreshes the theme's own cart fragments and fires WooCommerce's added_to_cart event, so the header cart count and the theme drawer stay in sync — the chatbot feels like part of the store, not a bolted-on widget. When the theme's slide-out cart opens, a MutationObserver hides the chatbot so the two never overlap.

06Safety & failure modes

07What it does, live

The result is a 24/7 assistant that advises, recommends real products, fills the cart, and walks the customer to checkout — one conversation, no page hunting. It's live in production on a real store. You can open the chat bubble on tanphatsolutions.vn and ask it for a product.

08Takeaways

Written by Nguyễn Trung Tâm — AI Engineer & AI-native builder.
More work: nguyentrungtam-portfolio.pages.dev · GitHub: nguyentrungtamwork-hue