Bernardi Volvo case study

Inside the build: an AI sales assistant for automotive dealerships

A technical deep dive into the architecture powering our production dealership chatbot, live at Bernardi Volvo Cars, Natick, MA.

The short version

This is a conversational AI sales assistant built for automotive dealerships, currently live in production on bernardivolvocars.com. It handles sales conversations, service and leasing enquiries, test drive booking, and human handoff, all while staying grounded in real dealership data instead of guessing. It’s built on two platforms working together: Botpress, an autonomous LLM driven conversation engine that handles the actual conversation, reasoning, and decision making, and Make.com, an automation layer that handles everything that happens after the conversation: emails, spreadsheets, bookings, digests.

Botpress

Autonomous LLM driven conversation engine. Handles the actual conversation, reasoning, and decision making.

Make.com

Automation layer. Handles everything after the conversation: emails, spreadsheets, bookings, digests.

Architecture at a glance

The widget itself is injected via a lightweight Cloudflare Worker, which defers loading the full chat bundle until the visitor actually clicks the launcher, keeping the dealership’s page load fast.

Website visitor

A dealership site visitor opens the chat widget.

Cloudflare Worker

Loads the widget on demand, keeping the dealership’s page fast.

Botpress conversation engine

Sales_Agent autonomous node, Personality and Policy agents, knowledge base retrieval, and sub-flows for Service, HITL, and Test Drive Booking.

Make.com automation

Service, Leasing, and HITL handoffs, test drive confirmation, conversation logging, and the weekly digest.

Sheets, Gmail and Calendly

Google Sheets for logging and analytics, Gmail for transactional email, Calendly for booking.

The conversation layer: Botpress

Autonomous nodes, not decision trees

Older generation chatbots are built as rigid decision trees: if the user says X, show button Y. This system is built differently. The core of the conversation is an autonomous node, an LLM driven reasoning loop (Botpress calls this the LLMz architecture) that decides, turn by turn, what to do based on a set of guidelines rather than a fixed script. The main autonomous node, Sales_Agent, is wired up with callable skills it can hand off into: Service for appointment enquiries and trade in details, HITL for escalation to a human sales rep, and Test Drive Booking, which routes into the Calendly scheduling flow. Each skill is a self contained flow with its own entry and exit nodes, so the autonomous agent can drop into a structured process when precision matters and hand control back once it’s done.

Personality and Policy agents

Behaviour and tone are not baked into a single giant prompt. They are split across dedicated agents: a Personality Agent that governs tone, voice, and formatting conventions, a Policy Agent that enforces hard constraints on what the bot will and won’t do, a Knowledge Agent that governs how and when the bot pulls from the knowledge base, a Vision Agent that handles image related responses, and Summary and Translator agents that support conversation summarisation and multilingual handling. This separation matters for maintenance: a tone tweak doesn’t risk touching a safety rule, and a knowledge retrieval fix doesn’t risk breaking the personality layer.

Guidelines over hardcoded scripts

The Sales_Agent’s behaviour is defined through a large, versioned guidelines document (currently v2.3) rather than scattered logic across nodes. Guidelines are auditable, diffable between versions, and testable as a single artefact, rather than logic buried across dozens of node configurations. One hard lesson baked into this approach: autonomous nodes are reliable at generating conversation but not at reliably executing state changes from prose instructions alone. Anything that must happen, like writing a variable or confirming a booking flag, is implemented as an explicit code action in a standard node, not left to the LLM to remember from a guideline.

Guardrails in practice

Colour names are sourced strictly from a maintained knowledge base document, never from the model’s own training data, since model year colour palettes change and training data doesn’t

Image and visual references are only triggered by explicit visual language from the user, not inferred

A node repetition safeguard nudges the conversation toward a concrete next step if it’s gone several turns without resolution, preventing the bot from looping indefinitely

The knowledge layer

Retrieval augmented generation is only as good as its source separation. This build uses two isolated knowledge bases, deliberately kept apart.

Website knowledge base

A full crawl of the dealership’s website: inventory pages, model pages, specials, service info. Refreshed weekly, and currently the broadest source of model coverage.

Hours and contact knowledge base

A small, hand curated set of verified operating hours and department phone numbers, deliberately kept isolated from the website crawl.

Why isolate them? A general website crawl can pull in third party call tracking numbers embedded in listing pages, numbers that look plausible but aren’t the dealership’s actual lines. Mixing that into the same retrieval pool as verified contact information would let the bot occasionally surface the wrong phone number with full confidence. Keeping hours and contact details in their own curated source eliminates that failure mode entirely. A dedicated model colour reference document is maintained separately for the same reason: LLMs are fluent enough to invent a plausible sounding trim colour if not explicitly constrained to a source of truth.

Honest state of the knowledge layer

This build is not fully complete. Manuals, brochures, and technical spec documents exist as knowledge sources but remain a work in progress. The website crawl currently carries most of the retrieval weight. This is called out deliberately rather than glossed over: a chatbot’s usefulness is bounded by what it actually has access to, and pretending otherwise creates trust problems down the line.

The automation layer: Make.com

Once the conversation reaches a decision point, a service request, a handoff, a booking, Botpress fires a webhook into Make.com, which handles everything downstream. Splitting it this way keeps the conversation engine focused purely on conversation, while operational logic lives in a system built for exactly that.

Conversation Log

Logs the full transcript plus an AI generated summary to Google Sheets after every session.

HITL Handoff

Fires when a conversation needs a human. Sends a notification email to the sales team and logs the request.

Service Request Handoff

Captures service appointment requests, including trade in details, and routes them to the service team.

Leasing Request Handoff

Mirrors the service flow structure for lease specific enquiries.

Test Drive Booking

Confirms bookings made through Calendly, pulls recent conversation context for an accurate in chat confirmation, and prevents duplicate confirmations on reschedules.

Weekly Digest

Compiles a Monday morning summary of the week’s conversations, handoffs, and bookings for the dealership team.

All of this writes into a shared Google Sheets analytics workspace, with dedicated tabs for conversation logs, handoff requests, test drive leads, and leasing requests. Calendly handles the actual test drive scheduling, with a registered webhook subscription pushing booking events back into Make.com in real time. Gmail handles every transactional email in the system.

Engineering lessons worth sharing

A few of the more useful, hard won details from building this in production.

LLM driven state writes are unreliable. If a value absolutely must be captured correctly, like a phone number or a confirmation flag, it belongs in an explicit code action, not a natural language instruction to an autonomous agent.

Field indexing conventions differ across a pipeline. Spreadsheet filter conditions and downstream field mappers can use different indexing schemes for the same columns. Mixing them up causes silent wrong column reads that don’t throw errors, they just quietly corrupt data.

Automation platform imports can silently break references. Re importing a scenario blueprint can reshuffle internal module IDs, breaking hardcoded field references in email templates that look fine until they’re tested.

Prompt permissions need to be resolved at the source, not patched over. If a lower level instruction technically permits something a higher level rule tries to forbid, the conflict will eventually get exploited by the model. The fix is to resolve the contradiction where it originates, not add another rule on top.

Reasoning models aren’t a drop in replacement everywhere. Certain automation modules expect plain text completions; reasoning style models can return empty output through those same integration points if swapped in without adjustment.

Where this is heading

This build is designed from the ground up to be productisable, not a one off for a single dealership, but a template that can be reconfigured for other automotive clients. A sanitised demo clone already exists, proving the architecture generalises beyond the flagship deployment. Planned next steps include an after sales and owner support capability, scoped to a separate, curated knowledge source, with proactive service status and reminder logic kept as human handled rather than automated, since some things genuinely shouldn’t be fully automated yet, and continued expansion of the manuals and technical document knowledge base.

Built and maintained by LK Motion.

Is this relevant to your business?

A 20-minute call is all it takes to find out.