Traditional customer segmentation buckets accounts into a handful of fixed tiers — small/mid/enterprise, or RFM quartiles — computed by a scheduled batch job. An LLM agent's contribution isn't a better clustering algorithm; it's the ability to segment dynamically, in natural language, against whatever question a marketing or sales team actually asks, without a data team rebuilding a query for every new criterion.
Treat it as translating intent into a query, not classification
When someone asks "show me accounts that upgraded in the last quarter but haven't used the new feature," that's a database query with a natural-language wrapper. The agent's real job is translating that request into a structured filter against your actual account and usage data — via a tool call that runs a parameterized query — not attempting to reason its way to a list of accounts from memory or general knowledge about what such customers might look like.
def query_segment(
plan_tier: str | None = None,
upgraded_within_days: int | None = None,
feature_usage: dict | None = None, # {"feature": "x", "used": False}
min_account_value: float | None = None,
) -> list[dict]:
"""Runs a parameterized query against the account/usage
tables. The agent fills these fields from the natural-
language request; it never generates account lists itself."""
...
Fixed fields cover most requests; free text covers the rest
Most segmentation requests map cleanly onto a handful of dimensions — plan tier, industry, account age, usage trend, contract value. Define those as an explicit, closed schema the agent fills in via function calling, which keeps segmentation fast, cheap, and fully deterministic once the parameters are set. For the long tail of open-ended requests that don't fit the schema ("accounts that seem confused about billing based on their support tickets"), fall back to a slower retrieval-based approach over ticket text, and be explicit with the user about which path was used — the confidence and cost profile of the two are very different.
Once the agent has extracted plan_tier="enterprise" and upgraded_within_days=90 from the request, that filter runs as a normal SQL or API query. Don't have the model re-read every account record and judge membership itself — it's slower, more expensive, and introduces classification inconsistency a database WHERE clause doesn't have.
Explaining why an account landed in a segment
Segments used for outreach campaigns need to be defensible — a sales rep or compliance reviewer may ask why a specific account is in a "high churn risk" or "upsell candidate" list. Since membership comes from the structured query, this is straightforward: surface the exact filter values that matched, not a generated explanation the model invents after the fact. This also makes segment definitions reusable and auditable rather than a one-off natural-language answer that can't be regenerated identically later.
Balancing freshness against the cost of re-querying
Segmentation against live transactional data can be expensive to run on every request if the underlying tables are large. For segments used repeatedly (e.g. a weekly campaign list), materialize the result and cache it with a clear "as of" timestamp rather than re-running the full query on every view. For one-off exploratory requests, query live — the cost tradeoff is different for a marketing team iterating on campaign criteria versus a dashboard refreshed hourly.
Evaluating whether segments are actually useful, not just correctly filtered
A segment can be technically correct (every account in it matches the stated filter) and still useless if the filter doesn't capture what the requester actually meant. Track how often generated segments get manually adjusted before a campaign goes out — a consistently high edit rate on a particular type of request signals the natural-language-to-filter translation is missing nuance, not that the underlying data is wrong.
| Request type | Handling |
|---|---|
| Fits fixed dimensions (tier, usage, value) | Structured query, deterministic and fast |
| Open-ended / text-based criteria | Retrieval over relevant text, slower, flagged as such |
| Repeated campaign segments | Materialized and cached with a timestamp |
| One-off exploratory requests | Live query, no caching needed |
Wrapping up
The agent's value in segmentation is in the translation layer — turning a natural-language request into a precise, reusable, auditable query — not in the LLM directly deciding segment membership. Keep the filter logic structured and deterministic, and reserve the model's judgment for the harder cases that genuinely need it.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.