Building a Production-Grade GenAI Customer Support Assistant with Comprehensive Observability

elastic · Tech · 2024 · source Agentic: no · promotional bias: med · source-backed · extracted with prompt 0.5.4 (schema v2)

Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.

Problem

Domain: Observability for a production GenAI customer support assistant

Elastic needed to launch and operate a RAG-powered Support Assistant with enough observability to prove adoption, detect regressions, debug LLM and RAG failures, manage abuse, and respond quickly when customer-facing chatbot behavior degraded.

Constraints:

Architecture

Elastic built an observability stack around the Support Assistant using a dedicated Elastic Cloud monitoring cluster, Elastic Node APM in the Node API, Filebeat logs, a console wrapper that appends APM trace IDs, Elastic Synthetics monitors for the app and upstream dependencies, Stack Monitoring from data clusters, and Azure OpenAI integration metrics shipped through Elastic Agent on a GCP VM. Kibana dashboards track status and milestones, APM custom spans measure first-token and completion durations, alerting thresholds are derived from dashboard metrics and baselines, prompt-level decline responses are monitored, and abuse controls use endpoint-specific rate limits plus feature flags.

Diagram confidence: reconstructed (reconstructed from prose).

flowchart LR
  support-assistant[Support Assistant] --> support-portal-frontend[Support Portal frontend]
  support-portal-frontend --> node-api-service[Node API service]
  node-api-service --> rag-search[RAG search]
  node-api-service --> azure-openai-service[Azure OpenAI service]
  node-api-service --> elastic-node-apm-client[Elastic Node APM client]
  node-api-service --> filebeat-and-log-wrapper[Filebeat and log wrapper]
  node-api-service --> feature-flag-system[Feature flag system]
  rag-search --> application-data-clusters[Application data clusters]
  elastic-node-apm-client --> monitoring-elastic-cloud-cluster[Monitoring Elastic Cloud cluster]
  filebeat-and-log-wrapper --> monitoring-elastic-cloud-cluster
  elastic-synthetics-monitors[Elastic Synthetics monitors] --> monitoring-elastic-cloud-cluster
  application-data-clusters --> stack-monitoring[Stack Monitoring]
  stack-monitoring --> monitoring-elastic-cloud-cluster
  azure-openai-service --> azure-openai-integration[Azure OpenAI integration]
  azure-openai-integration --> monitoring-elastic-cloud-cluster
  monitoring-elastic-cloud-cluster --> kibana-dashboards[Kibana dashboards]
  kibana-dashboards --> alerting-rules[Alerting rules]
Drag to pan · scroll or use +/− to zoom · reconstructed from the source's described component topology

Components

Component Role Interacts with
Support Assistant RAG-powered customer support chatbot for Elastic product technical support. Support Portal frontend, Node API service, Azure OpenAI service, RAG search
Support Portal frontend Browser-facing UI that streams chat events and displays assistant responses/errors. Support Assistant, Node API service
Node API service Server-side API layer that handles chat completion requests, timeouts, prompt rules, rate limits, and payload sizing. Support Portal frontend, Elastic Node APM client, Azure OpenAI service, RAG search, Feature flag system
RAG search Retrieval path that supplies support context for chat completions. Node API service, Application data clusters
Azure OpenAI service LLM backend for streamed support-assistant completions. Node API service, Azure OpenAI integration
Elastic Node APM client APM instrumentation in the API service for transactions, spans, errors, and environment-tagged telemetry. Node API service, Monitoring Elastic Cloud cluster
Filebeat and log wrapper Log shipping and trace-ID enrichment for correlating logs with APM transactions. Node API service, Monitoring Elastic Cloud cluster
Elastic Synthetics monitors HTTP monitors checking the application and upstream dependencies from relevant geographic locations. Support Portal frontend, Application data clusters, Monitoring Elastic Cloud cluster
Application data clusters Production and staging Elastic clusters for support application data such as knowledge articles and crawled documentation. RAG search, Stack Monitoring, Elastic Synthetics monitors
Stack Monitoring Infrastructure monitoring data shipped from application data clusters for cluster and service visibility. Application data clusters, Monitoring Elastic Cloud cluster
Monitoring Elastic Cloud cluster Dedicated cluster for observability data storage, analysis, dashboards, and alerting. Elastic Node APM client, Filebeat and log wrapper, Elastic Synthetics monitors, Stack Monitoring, Azure OpenAI integration, Kibana dashboards
Kibana dashboards Status and milestones dashboards for usage, latency, errors, capacity, unique users, chat completions, and operational links. Monitoring Elastic Cloud cluster, Alerting rules
Alerting rules Threshold-based notifications built from dashboard metrics and baselines. Kibana dashboards, Monitoring Elastic Cloud cluster
Feature flag system Launch, allow, and block controls for enabling or disabling Support Assistant access by feature, user, or organization. Node API service
Azure OpenAI integration Elastic Agent integration that ships Azure OpenAI logs and metrics into observability. Azure OpenAI service, Monitoring Elastic Cloud cluster

Data flow

Support Assistant traffic flows through the Support Portal frontend and Node API to RAG search and Azure OpenAI chat completion. The Node APM client captures transactions and custom spans, Filebeat ships correlated logs, synthetics monitors check the app and upstream services, Stack Monitoring observes data clusters, and Azure OpenAI integration sends model-service telemetry. Kibana dashboards and alerts then expose usage, latency, errors, capacity, milestones, declined requests, rate-limit hits, and payload-size failures.

4 supporting quotes
“We run Elastic’s Node APM client within our Node application that serves the API, and have Filebeat running to capture logs.”article, stated
“a wrapper function for our console.log and console.error calls that appends APM trace information at the end of each message”article, stated
“We use the Elastic Synthetics Monitoring feature to check on the liveliness of our application and critical upstream services”article, stated
“have the Azure OpenAI integration shipping logs and metrics from that service via an Elastic Agent running on a GCP Virtual Machine.”article, stated

Why this architecture

Elastic treated GenAI observability as a launch prerequisite rather than an afterthought. A separate monitoring cluster protected production data workloads, trace/log correlation made debugging feasible, custom spans exposed streaming-specific latency, dashboards showed usage and value, alerts converted dashboard metrics into action, and prompt/endpoint instrumentation captured LLM-specific risks such as declined requests, timeouts, abuse, and RAG payload expansion.

4 supporting quotes
“You're going to want three main pieces in place. A status dashboard, alerting, and a milestones dashboard, in that order.”article, stated
“We have an Elastic Cloud cluster dedicated to monitoring purposes. This is where all of the observability data I am going to discuss gets stored and analyzed.”article, stated
“Custom spans are a great way to monitor performance of specific tasks. In this case where we are streaming chat completions, I want to know how long until the first tokens of generation arrive, and also how long the entire completion process takes.”article, stated
“Ideally, observability is more than one thing. It's multi-faceted to provide multiple angles and viewpoints for creating a more complete understanding.”article, stated

Models used: GPT-4 · GPT-4o · Azure OpenAI LLM service

Scale

Alternatives considered (roads not taken)

Wait until late development to add APM

Rejected because: Early APM captures development errors and sample data needed for dashboard validation and alert baselines.

“you might be planning to hold off integrating APM until the end of the development process, but I would encourage you to do it sooner.”article, stated
“development by capturing details you can investigate during an expected error, and then it’s also capturing sample data. That can be useful for both verifying your visualizations (for metrics involving counts), and then also for establishing baseline values for metric categories like”article, stated

Keep first-generation timeout detection in browser code

Rejected because: The server could not reliably observe client aborts, so timeout/error capture needed to move into the API layer.

“APM agent runs on our server, and the timeout for first generation was configured in the client code that runs in the user’s browser, so I started experimenting with listening for events on the server to detect when the client had sent the abort signal so that I could send an error to APM with”article, stated
“I listened on the request, I listened on the socket, and then I did some Internet searches, and reached the conclusion that at least for our application stack there was no practical or built-in way for our server to recognize the client had timed out.”article, stated

Use a separate third-party service to decide whether to respond

Rejected because: Elastic initially chose prompt instructions plus a standardized decline response, while leaving a separate LLM/service as a future hardening option.

“Our approach was to add instructions to the prompt before the user's input as opposed to using a separate call to a 3rd party service.”article, stated
“As our hardening needs evolve we may consider adding a service, or at least splitting the task of deciding whether or not to attempt to respond into a separate LLM request dedicated to making that determination.”article, stated

Rely on the general application-wide rate limit

Rejected because: The chat completion endpoint needed a much lower limit tuned to LLM cost, latency, customer behavior, and abuse risk.

“Our application already had a general rate limit across all of our endpoints, however that rate limit is meant to be a very generous rate that should really only get triggered if something was actually going wrong and causing a significant amount of spam traffic.”article, stated
“For a rate limit to be meaningful as applied to the Support Assistant chat completion endpoint, it was going to have to be a much lower limit.”article, stated

Keep sending full RAG result content through the browser payload

Rejected because: Large RAG context caused HTTP 413 errors; the long-term plan was to send limited metadata and fetch content server-side by ID.

“the amount of context generated from our RAG search, combined with the user’s input was exceeding the default limits.”article, stated
“instead of sending the full content of the RAG results back to the client to include in the completion payload”article, stated

Patterns

observability-tracing (llmops-operations)

Elastic correlated APM transactions, custom spans, logs, synthetics, stack monitoring, Azure OpenAI metrics, dashboards, and alerts to operate the Support Assistant.

“APM understands that this trace traveled through our frontend server (green spans), and our API service (blue spans).”article, stated
“I want to know how long until the first tokens of generation arrive, and also how long the entire completion process takes.”article, stated

rag-retrieval (support-assistant)

The Support Assistant uses RAG search context in chat completion payloads for Elastic product support.

“latency of our RAG searches and our chat completions.”article, stated
“the amount of context generated from our RAG search, combined with the user’s input was exceeding the default limits.”article, stated

guardrails-io-filtering (support-scope)

The prompt declines unrelated or unsupported topics such as non-support requests and billing, and standardized declines are monitored as observable events.

“questions unrelated to getting technical support for Elastic products”article, stated
“With a predefined response in hand, the chatbot reliably used the standard response when declining a request.”article, stated

latency-optimization (streaming-ux)

Elastic uses streaming responses, first-token custom spans, a 10-second first-generation timeout, and server-side abort handling to preserve perceived responsiveness.

“we chose to use streaming responses from the LLM in order to avoid having to wait for the LLM generation to finish before being able to show anything to the user.”article, stated
“enforce a 10 second timeout on getting the first chunk of generated text back.”article, stated

safety-policy-enforcement (abuse-control)

The assistant is gated to logged-in support subscribers, uses a lower endpoint-specific rate limit, tracks HTTP 429s, and can block access through feature flags.

“Our two prongs of abuse mitigation are a reduced rate limit for the chat completion endpoint, and a feature flag system”article, stated
“the second was to allow flags to be configured such that they blocked access to a feature.”article, stated

Tradeoffs

Use a dedicated monitoring cluster

Chose separate Elastic Cloud cluster for observability data over mixing monitoring data with production and staging data clusters.

Rationale: Separating monitoring from application data reduces interference and keeps observability data centralized for analysis.

“We have an Elastic Cloud cluster dedicated to monitoring purposes.”article, stated
“It is separate from our production and staging data Elastic clusters”article, stated

Add prompt instructions before user input for declines

Chose prompt-level hardening with standardized decline response over separate third-party or dedicated LLM moderation call in the first implementation.

Rationale: Prompt-level scope control was simpler to launch, while the team kept service-based classification as a future hardening option.

“Our approach was to add instructions to the prompt before the user's input as opposed to using a separate call to a 3rd party service.”article, stated
“we may consider adding a service, or at least splitting the task of deciding whether or not to attempt to respond into a separate LLM request”article, stated

Set a lower assistant-specific rate limit

Chose 20 chat completions per one-hour window over the generous general application rate limit.

Rationale: The assistant endpoint had higher cost and abuse risk, but the limit still needed to avoid penalizing enthusiastic legitimate users.

“it was going to have to be a much lower limit. It was also important to leave the limit generous enough”article, stated
“20 chat completions in a one hour window”article, stated

Use boosted payload size as a short-term RAG fix

Chose increase accepted payload size over blocking launches on a deeper orchestration refactor.

Rationale: Increasing the limit eliminated HTTP 413s quickly, but the team recognized server-side fetch-by-ID as the long-term design.

“We increased the size of the payloads accepted by the chat completion endpoint”article, stated
“our fix to expand the accepted payload size is really more of a short-term bandage than a long-term solution.”article, stated

Page only on actionable alerts

Chose different notification thresholds and channels based on urgency/actionability over paging for every dashboard anomaly.

Rationale: Unactionable alerts train teams to ignore alerting noise.

“there are some alerts where you may want to configure multiple alerts at different thresholds.”article, stated
“What you don't want, is to accidentally train your team to ignore alerts because they are so often inactionable.”article, stated

Gotchas (what bit them)

Client-side first-token timeouts were invisible to server APM.

Root cause: The timeout ran in browser code, and the API server had no practical built-in way to know the client aborted.

Resolution: Elastic moved the timeout and AbortController into the API layer, captured the error with APM, and sent an Error event before closing the stream.

“there was no practical or built-in way for our server to recognize the client had timed out.”article, stated
“I moved the timeout and AbortController from the client code to be in our API layer”article, stated

A generic error stream close left the client stuck in loading state.

Root cause: Closing the server connection without sending a proper generated event gave the browser no signal to update UI state.

Resolution: The server sends an Error event before closing the streaming response.

“Without sending back a proper error signal or any generated response text, the client code was not running the parts of the code where we exited the loading state.”article, stated
“By adding an extra step to send an Error event before closing the connection, the client code was able to update the UI state”article, stated

Declined responses were hard to monitor until standardized.

Root cause: The LLM declined inconsistently, making it difficult to count and investigate rejections.

Resolution: Elastic added a standard decline response and logged matches with captureError.

“it wasn’t very consistent with how it replied. And the quality of the response varied.”article, stated
“When I see a match, I use captureError to keep a record of it.”article, stated

RAG context exceeded default payload limits.

Root cause: The browser sent full RAG result content plus the user's input to the chat completion endpoint.

Resolution: Elastic increased accepted payload size as a short-term fix and planned a server-side fetch-by-ID refactor.

“we started seeing HTTP 413 status codes coming back for a small, but non-negligible amount of traffic.”article, stated
“the issue was the amount of context generated from our RAG search, combined with the user’s input was exceeding the default limits.”article, stated

A data-loading bug looked like unexpectedly high throughput.

Root cause: The system loaded the same data repeatedly from the server.

Resolution: APM endpoint throughput made the anomaly visible and confirmed the fix when TPM dropped.

“a bug causing us to load the same data over and over again from our server.”article, stated
“We saw in the APM data that the throughput for one of our endpoints was well over 100 transactions per minute”article, stated

Evaluation

Elastic evaluated the Support Assistant operationally through status dashboards, APM traces, custom spans, alert thresholds, milestone dashboards, synthetic checks, declined-request tracking, HTTP status monitoring, rate-limit metrics, and production bug/incident signals.

Metrics: chat completions · unique users · returning users · errors and HTTP status codes · RAG search latency · chat completion latency · time to first generated token · total completion duration · HTTP 429 rate-limit events · HTTP 413 payload-size errors · declined-request matches

4 supporting quotes
“summary stats like the total number of chat completions, unique users, and errors for the time range of the dashboard.”article, stated
“The next set of visualizations are time series charts to examine latency, and usage over time.”article, stated
“I then configured an alert looking for HTTP 429 responses on the chat completion endpoint”article, stated
“The two metrics I was most interested in highlighting with milestones were unique users, and chat completions.”article, stated

Outcomes

The observability setup tracks launch-to-now usage, all-time/7-day/30-day milestone windows, RAG and completion latency, 10-second first-token timeouts, 20-completion hourly rate limits, HTTP status codes, users with most errors, and Azure OpenAI service logs/metrics.

4 supporting quotes
“We could confirm the fix when we saw the throughput reduce to a much more reasonable 1 TPM.”article, stated
“we served up our 100th chat completion 21 hours post launch”article, stated
“I am very happy to report that we have not had anyone hit the limit in these first couple of weeks since launch.”article, stated
“ever since we released the fix, we haven’t seen any more transactions with 413 response status.”article, stated