Evidence-Gated AI Support Automation β Powered Entirely by Elasticsearch
AI-driven ticket triage that refuses to hallucinate. Every automated response requires β₯2 grounded citations from Elasticsearch before reaching a customer. No citations? Routed to a human.
Elasticsearch Features Used: ES|QL Β· BM25 Full-Text Β· kNN Vector Search Β· Reciprocal Rank Fusion Β· Vector Deduplication Β· Confidence Scoring Β· Audit Timeline
- Problem Statement
- Solution Overview
- Architecture
- Elasticsearch Features Used
- Workflow Pipeline
- Safety & Explainability
- Local Setup
- Demo Walkthrough
- License
AI-powered support automation sounds great β until it fails catastrophically:
| Failure Mode | What Happens | Real-World Impact |
|---|---|---|
| Hallucination | LLM fabricates solutions that don't exist | Customers follow wrong steps, escalate |
| No Grounding | Responses lack evidence from actual docs | Support team can't verify AI answers |
| No Auditability | Decisions are black-box | Compliance fails, debugging impossible |
| Duplicate Noise | Same issue creates multiple tickets | Teams waste hours on redundant work |
Traditional AI support tools generate confident-sounding answers with zero accountability. ElasticOps Copilot takes the opposite approach.
ElasticOps Copilot is an evidence-gated AI support agent where every decision is grounded in Elasticsearch data:
- π Real-time incident detection using ES|QL aggregations over application logs
- π§ Hybrid retrieval combining BM25 full-text search with kNN vector similarity via Reciprocal Rank Fusion (RRF)
- π Semantic deduplication using vector similarity to prevent duplicate tickets
- π‘οΈ Citation gating β auto-responses require β₯2 verified sources, otherwise routed to humans
- π Confidence scoring with transparent three-component breakdown (KB, resolutions, similar tickets)
- π Full audit trail β every agent step is logged to Elasticsearch with timestamps and evidence
Every feature runs on Elasticsearch. No external AI APIs required for core functionality.
graph TB
subgraph Interface["π₯οΈ Interface Layer"]
UI["Copilot UI<br/>Dashboard & Home"]
INBOX["Inbox<br/>Incidents & Tickets"]
SEARCH["Search Explorer<br/>Hybrid Search + Explain"]
end
subgraph Agent["π€ Agent Workflow Layer"]
direction LR
EMBED["1. Embed<br/>SHA-256 β 384d Vector"]
CLASSIFY["2. Classify<br/>Rule-Based Triage"]
DEDUPE["3. Dedupe<br/>kNN Similarity >.95"]
RETRIEVE_KB["4. Retrieve KB<br/>BM25 + kNN Hybrid"]
RETRIEVE_RES["5. Retrieve Resolutions<br/>kNN Filtered Search"]
DRAFT["6. Draft<br/>Citation-Gated Response"]
ACT["7. Act<br/>Update / Flag / Merge"]
end
subgraph Elastic["β‘ Elasticsearch Layer"]
LOGS["logs-app<br/>ES|QL Spike Detection"]
TICKETS["tickets<br/>BM25 + kNN"]
KB["kb-articles<br/>Hybrid Search"]
RES["resolutions<br/>kNN Retrieval"]
RRF["RRF Fusion<br/>Reciprocal Rank Fusion"]
CONF["Confidence Engine<br/>3-Component Scoring"]
AUDIT["ops-runs / ops-metrics<br/>Audit Timeline & KPIs"]
end
UI --> Agent
INBOX --> Agent
SEARCH --> RRF
EMBED --> CLASSIFY --> DEDUPE --> RETRIEVE_KB --> RETRIEVE_RES --> DRAFT --> ACT
LOGS -.->|"ES|QL: error spike β₯40/5min"| INBOX
DEDUPE -->|"kNN cosine similarity"| TICKETS
RETRIEVE_KB -->|"BM25 + kNN"| KB
RETRIEVE_RES -->|"kNN filtered"| RES
RRF -->|"1/(k+rank) fusion"| SEARCH
DRAFT -->|"β₯2 citations required"| CONF
ACT -->|"step-by-step trace"| AUDIT
style Interface fill:#1a1a2e,stroke:#16213e,color:#e2e8f0
style Agent fill:#0f3460,stroke:#16213e,color:#e2e8f0
style Elastic fill:#533483,stroke:#16213e,color:#e2e8f0
style LOGS fill:#e94560,stroke:#e94560,color:#fff
style RRF fill:#e94560,stroke:#e94560,color:#fff
style CONF fill:#e94560,stroke:#e94560,color:#fff
style AUDIT fill:#e94560,stroke:#e94560,color:#fff
Detects error spikes in application logs using Elasticsearch's native query language:
FROM logs-app
| WHERE @timestamp >= NOW() - 5 minutes
| WHERE level == "ERROR"
| STATS errors = COUNT(*) BY service, env
| WHERE errors >= 40
| SORT errors DESCπ lib/esql.ts β When a spike is detected, an incident is auto-created and linked to a new ticket.
Term-frequency scoring across KB articles and tickets:
multi_match: {
query: queryText,
fields: ['title^2', 'content'],
fuzziness: 'AUTO',
}π lib/searchTemplates.ts β Boosts title matches 2Γ for relevance.
384-dimensional vectors for semantic search, deduplication, and resolution retrieval:
knn: {
field: 'embedding',
query_vector: queryVector,
k: 10,
num_candidates: 100,
}π lib/embed.ts β Deterministic SHA-256 embeddings, no external API needed.
Combines BM25 and kNN results using rank-based fusion:
// RRF formula: score = Ξ£ 1/(k + rank)
rrfScore += 1 / (rrfK + bm25.rank);
rrfScore += 1 / (rrfK + knn.rank);π app/api/search/route.ts β Each result shows component BM25 score, kNN score, and final RRF rank.
Prevents duplicate tickets using kNN cosine similarity with a 0.95 threshold:
const isDuplicate = similarTickets.some(t => t.score > 0.95);π app/api/run/ticket/[id]/route.ts β Duplicates are tagged and surfaced for merging.
Three-component weighted scoring:
| Component | Weight | Source |
|---|---|---|
| KB Article Relevance | 40% | Top-3 avg kNN+BM25 scores |
| Resolution Match | 30% | Top-3 avg kNN scores |
| Similar Ticket Signal | 30% | Top-3 avg kNN scores |
Decision paths: β₯0.7 β Auto-triage | 0.4β0.7 β Needs Review | <0.4 β Human Only
Every workflow execution writes a complete trace to ops-runs:
{
"run_id": "run_1708...",
"workflow": "ticket_triage",
"steps": {
"embed": { "started_at": "...", "dims": 384 },
"classify": { "category": "authentication", "severity": "high" },
"dedupe": { "isDuplicate": false, "similarTickets": [...] },
"retrieve_kb": { "articles_found": 5 },
"draft": { "confidence": "high", "citations": [...] },
"act": { "action": "updated" }
},
"duration_ms": 342
}KPIs tracked in ops-metrics: duplicates prevented, time saved, tickets auto-triaged, MTTA.
The triage agent executes a deterministic 7-step pipeline for every ticket:
Embed β Classify β Dedupe β Retrieve KB β Retrieve Resolutions β Draft β Act
| Step | What It Does | Elasticsearch Feature |
|---|---|---|
| 1. Embed | Generate 384-dim vector from ticket text | SHA-256 deterministic embedding |
| 2. Classify | Assign category, severity, priority | Rule-based on ticket content |
| 3. Dedupe | Find similar open tickets (score >0.95 = duplicate) | kNN vector search with filters |
| 4. Retrieve KB | Find relevant knowledge base articles | BM25 + kNN hybrid search |
| 5. Retrieve Resolutions | Find matching resolution playbooks | kNN filtered by category + severity |
| 6. Draft | Generate response with citations | Citation gating (β₯2 sources) |
| 7. Act | Update ticket, write audit log, record metrics | ES index + ops-runs timeline |
Each step records timestamps, inputs, and outputs to the audit trail β making the entire process reproducible and debuggable.
ElasticOps Copilot is built for trust, not just speed. Three safety mechanisms prevent AI failures:
const shouldUpdate = confidence === 'high'
&& citations.length >= 2
&& !isDuplicate;If the agent can't find at least 2 relevant sources (KB articles + resolutions), it refuses to auto-respond and flags the ticket for human review.
Every triage result includes a breakdown showing exactly why the system is confident (or not):
{
"kb_score": 0.82, // How well KB articles matched
"resolution_score": 0.71, // How well resolutions matched
"similar_tickets_score": 0.45, // Historical ticket similarity
"overall": 0.68 // Weighted: 40% KB + 30% Res + 30% Tickets
}
Three decision paths keep humans in the loop:
| Confidence | Citations | Action |
|---|---|---|
| High (β₯0.7) | β₯2 | β Auto-triage + update ticket |
| Any | Any | π Duplicate β tag for merge |
| Low (<0.7) | <2 | π¨ NEEDS_HUMAN β routed to agent |
Internal note on flagged tickets: "NEEDS_HUMAN: Insufficient automated context. Manual review required."
- Node.js β₯18
- Docker (for local Elasticsearch) OR an Elastic Cloud account
# Clone and install
git clone https://github.com/your-org/elasticops-copilot.git
cd elasticops-copilot
npm install
# Start the app
npm run devOpens at: http://localhost:3000
Copy .env.example and configure:
cp .env.example .env.local# Elasticsearch β choose cloud or local
ELASTIC_MODE=cloud # 'cloud' or 'local'
ELASTIC_CLOUD_ID=your_cloud_id_here # For cloud mode
ELASTIC_API_KEY=your_api_key_here # For cloud mode
ELASTIC_URL=http://localhost:9200 # For local mode
# Application
EMBED_DIMS=384
APP_URL=http://localhost:3000# One-command bootstrap: starts ES, creates indices, generates data, runs app
./demo/bootstrap.shSee CLOUD_SETUP.md for Elastic Cloud configuration.
- Navigate to
/inboxβ Click "Detect Error Spike" - ES|QL finds error spikes β auto-creates incident + ticket
- View timeline showing each detection step
| Inbox β Incident List | Audit Timeline |
|---|---|
![]() |
![]() |
- Open any ticket β Click "Run Triage"
- Watch: Embed β Classify β Dedupe β Retrieve β Draft β Act
- Review citations, confidence score, and KB recommendations
| Ticket Detail | Triage Results |
|---|---|
![]() |
![]() |
- Navigate to
/searchβ Search: "authentication error" - Toggle KB vs Tickets mode
- Expand "Why ranked here?" to see BM25/kNN/RRF scoring
| BM25 Search | kNN Vector Search |
|---|---|
![]() |
![]() |
- View KPIs: duplicates prevented, time saved, MTTA
- Chat with the AI Copilot for guided support
| Dashboard | Copilot Chat |
|---|---|
![]() |
![]() |
elasticops-copilot/
βββ app/ # Next.js 14 App Router
β βββ api/
β β βββ run/ticket/[id]/ # 7-step triage workflow
β β βββ run/incident/detect/ # ES|QL spike detection
β β βββ search/ # RRF hybrid search
β β βββ metrics/ # KPI aggregations
β β βββ timeline/[id]/ # Audit trail viewer
β βββ inbox/ # Incidents + tickets list
β βββ search/ # Search explorer UI
β βββ dashboard/ # Metrics dashboard
βββ lib/
β βββ esql.ts # ES|QL spike detection queries
β βββ searchTemplates.ts # BM25, kNN, hybrid search builders
β βββ embed.ts # Deterministic 384-dim embeddings
β βββ elastic.ts # Elasticsearch client
βββ infra/
β βββ mappings/ # 7 index mappings (JSON)
β βββ docker-compose.yml # Local Elasticsearch
βββ agent_builder/ # Agent Builder integration artifacts
βββ demo/ # Bootstrap scripts + demo materials
MIT License β see LICENSE
Built for the Elasticsearch Hackathon. Every core feature β search, analytics, storage, scoring, and observability β runs on Elasticsearch. No external AI APIs required for the triage pipeline.
Quick Links: π Inbox Β· π Search Β· π Dashboard Β· π€ Copilot








