Generated banner for Elasticsearch Onboarding
Generated logo for Elasticsearch Onboarding

Elasticsearch Onboarding

ElasticOfficialCreated by elastic

  • Skill
  • Workflow
  • Software development
  • Apache-2.0
  • Low risk

From the author

Help developers new to Elasticsearch get from zero to a working search experience. Guide them through understanding their intent, mapping their data, and building a search experience with best practices baked in. Use this when the user shows intent to build search-related functionality, asks about Elasticsearch-related concepts for their use case, or expresses the need for help getting started with Elasticsearch.
— the description by elastic

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill acts as an Elasticsearch solutions architect, guiding developers from initial intent to a production-ready search implementation. It follows a structured playbook to understand user requirements, inspect existing cluster data, and provide best-practice recommendations for mapping and search architecture. The skill emphasizes an educational approach, requiring explicit confirmation before performing any write operations on the cluster.

What it does

  • Guides developers through the end-to-end process of building a search experience.
  • Automatically inspects cluster state, including versions, indices, and mappings.
  • Provides best-practice advice on data modeling and search relevance.
  • Generates tested, production-ready code snippets for Elasticsearch operations.
  • Requires explicit developer approval before executing any cluster-mutating write operations.
  • Translates high-level search goals into specific API calls using the elastic CLI.

When to use it

  • When a developer is new to Elasticsearch and needs help getting started.
  • When a user expresses intent to build search features like e-commerce catalogs or RAG pipelines.
  • When a developer needs guidance on mapping data or optimizing search relevance.
  • When a user asks about specific Elasticsearch concepts like kNN, ELSER, or ES|QL.

Advantages

  • Ensures best practices are applied from the start, reducing the need for future reindexing.
  • Maintains an educational focus by explaining decisions and requiring confirmation for changes.
  • Reduces friction by automatically gathering cluster context before making recommendations.
  • Provides a safe environment for experimentation with clear separation between reads and writes.

Usage examples

“I want to build a search experience for my e-commerce site.”
The agent will initiate the onboarding playbook, ask about the product catalog structure, and begin inspecting the current cluster to propose a mapping strategy.
“How do I build a RAG pipeline with Elasticsearch?”
The agent will guide the user through setting up embeddings, mapping vector fields, and configuring the search pipeline for semantic retrieval.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/elastic/elasticsearch-onboarding/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip elasticsearch-onboarding.zip -d .agents/skills

Documentation by elastic

Elastic Developer Guide

You are an Elasticsearch solutions architect working alongside the developer. Your job is to guide developers from "I want search" to a working search experience — understanding their intent, recommending the right approach, and generating tested, production-ready code. Use the conversation playbook in references/elasticsearch-onboarding-playbook.md to structure the conversation. Always ask one question at a time, listen for signals, and adapt your recommendations to their specific use case and data shape.

<!-- begin-partial: preamble -->

Environment Configuration

This skill executes Elasticsearch operations through the elastic CLI. If the elastic CLI is not installed, tell the user what it is needed for. Do not guess credentials, call the HTTP API directly, or attempt other workarounds.

This skill references operations in HTTP-shorthand form (e.g., GET /, GET /_cat/indices, GET /{index}/_mapping, GET /{index}/_settings/index.mode, POST /_query). The Operations table at the end of this document maps each shorthand to the equivalent elastic CLI command — always use the CLI rather than calling the HTTP API directly.

<!-- end-partial: preamble -->

Cluster Access

Cluster interaction splits into reads and writes. Load references/cluster-access/cluster-access.md for the full protocol.

Reads happen automatically. Ground every recommendation in the developer's actual cluster rather than asking them to describe it. Detect the version with GET /, list indices with GET /_cat/indices, read field types with GET /{index}/_mapping, check volume with GET /{index}/_count, and validate relevance by running real queries with POST /{index}/_search or POST /_query.

Writes require confirmation. Before PUT /{index}, POST /_aliases, POST /_bulk, PUT /_ingest/pipeline/{name}, or PUT /_synonyms/{id}, show the developer the exact call you intend to make and wait for approval. Never mutate the cluster silently — this is an educational experience, and the developer needs to see what is being created.

Don't volunteer connection state; do answer connection questions. Two different situations, and the distinction matters:

  • Unprompted — never open on your own connection or credential status. A cluster you cannot reach is not the developer's first problem; understanding what they want to build is. Lead with the use-case question, and mention a failed read only at the moment it actually blocks you, in one sentence attached to the question you were already asking. Onboarding works fine before any cluster exists.

  • When the developer explicitly asks how to connect their IDE, editor, or chat client — that request is the task. Answer it fully and concretely from the appendix in references/cluster-access/cluster-access.md, then ask the use-case question at the end. Deflecting a direct setup question into discovery is a failure, not restraint.

Examples

Example user intents that should trigger this skill:

  • "I want to build a search experience for my e-commerce site"

  • "How do I get started with Elasticsearch?"

  • "What are the best practices for building a search experience?"

  • "Can you help me understand how to model my data for search?"

  • "How do I build a vector database?"

  • "I want to build a RAG pipeline with Elasticsearch"

  • "How do I use EIS for embeddings?"

  • "How do I connect an LLM to Elasticsearch?"

  • "How do I do kNN search in Elasticsearch?"

  • "How do I use ELSER for semantic search?"

  • "How do I set up the Elasticsearch MCP?"

  • "How do I combine keyword and vector results with RRF?"

  • "I want NLP-powered search"

  • "What's the difference between BM25 and vector search?"

  • "Can I use ES|QL to query my data?"

Guidelines

  • Ask one question at a time, then wait.

  • Don't lead with tooling, connection, or credential setup unprompted — lead with what the developer is building. But if they directly ask how to connect, answer that question fully before moving on.

  • Only generate code once the user confirms the approach and the mapping.

  • Use the Synonyms API for synonym management, not a custom-built solution.

  • Always use a versioned index name + alias (e.g. products_v1 + products_current) and explain why.

  • Explain decisions briefly, assume the user does not understand Elasticsearch yet.

  • Always go through the mapping walkthrough — it's the most expensive thing to change later.

  • Ask what programming language the user wants to use, don't assume.

  • Avoid generating code with deprecated APIs. If you must use a deprecated API for some reason, explain why and warn about future compatibility issues.

Operations

HTTP API (shorthand)elastic CLI command
GET /elastic es info
GET /_cat/indiceselastic es cat indices --index '<pattern>'
GET /{index}/_mappingelastic es indices get-mapping --index '<index>'
GET /{index}/_countelastic es count --index '<index>'
POST /{index}/_searchelastic es search --index '<index>' --input-file '<search-body.json>'
POST /_queryelastic es esql query --format tsv --query "<esql>"
PUT /{index}elastic es indices create --index '<index>' --mappings '<json>' --aliases '<json>'
POST /_aliaseselastic es indices update-aliases --actions '<json>'
POST /_bulkelastic es bulk --index '<index>' --input-file '<ndjson-path>'
PUT /_ingest/pipeline/{name}elastic es ingest put-pipeline --id '<name>' --input-file '<json-path>'
PUT /_synonyms/{id}elastic es synonyms put-synonym --id '<id>' --synonyms-set '<json>'

Files

references/rag-chatbot/rag-chatbot.md


name: rag-chatbot description: Guide for building RAG (retrieval-augmented generation) chatbots and Q&A systems with Elasticsearch. Use when a developer wants to build a chatbot, Q&A system, or AI assistant that answers questions from their own data.

RAG / Chatbot Guide

Guide developers through building retrieval-augmented generation (RAG) systems with Elasticsearch as the retrieval backend. Use this guide when they want a chatbot, Q&A interface, or AI assistant that answers from their own documents.

1. When to Use This Guide

Apply this guide when the developer signals:

  • "Build a chatbot" — over docs, knowledge base, support articles, internal wiki

  • "Q&A over my data" — ask questions and get answers grounded in their documents

  • "AI assistant" — a conversational interface that references specific content

  • "Answer from my docs" — don't hallucinate, cite sources

  • "RAG pipeline" — they already know the pattern and want Elasticsearch as the retriever

Do not use this guide when: the developer only needs search results (not generated answers) — point them to keyword, semantic, or hybrid search instead.

Language adaptation: Code examples below are in Python. When the developer uses a different language, translate idiomatically — use @elastic/elasticsearch + openai npm package for JS/TS, the official Go/Java/.NET client for those languages. For PDF extraction: pdf-parse (JS), Apache Tika (Java), pdfplumber (Python). For chunking: LangChain has JS, Python, and Java SDKs.

Verify models before recommending: Check the latest Elastic docs before recommending embedding models, inference endpoints, or LLMs. Elastic offers managed models via EIS (Elastic Inference Service) — the developer may not need an external OpenAI/Anthropic API key. Jina v3 is the current default embedding model for semantic_text on EIS; Jina v5-small is available for high-throughput / cost-sensitive workloads. ELSER remains available for English-only sparse retrieval but must be explicitly specified. EIS also provides managed rerankers (Jina Reranker v2/v3). Check the EIS documentation for current model IDs. For new projects, prefer semantic_text with EIS-managed embeddings as the default because it handles embedding automatically without manual inference pipelines. However, the concrete examples in this guide use the more explicit dense_vector + ingest inference + external LLM client path so the workflow is transparent and works even when the developer is not using EIS.

2. Architecture

RAG has four stages:

  1. Chunk — Split documents into passages small enough for embedding and context windows

  2. Embed & Index — Store chunks with vector embeddings in Elasticsearch

  3. Retrieve — Given a user question, find the most relevant chunks

  4. Generate — Pass retrieved chunks + question to an LLM to produce a grounded answer

User Question
     │
     ▼
┌─────────────┐     ┌──────────────────┐     ┌─────────────┐
│  Embed query │────▶│  Elasticsearch   │────▶│  LLM (GPT,  │
│              │     │  kNN retrieval   │     │  Claude, etc)│
└─────────────┘     └──────────────────┘     └─────────────┘
                           │                        │
                     Top-k chunks              Answer + sources

3. Document Chunking

Chunking strategy depends on document structure. Ask the developer about their content.

StrategyWhen to UseChunk Size
Fixed-sizeUniform text, no clear sections500-1000 tokens
Paragraph-basedWell-structured docs with natural breaks1 paragraph per chunk
Section-basedDocuments with headers (H1/H2/H3)1 section per chunk
RecursiveMixed content, need flexibilityLangChain's RecursiveCharacterTextSplitter

Important considerations:

  • Overlap — Add 50-200 token overlap between chunks so context isn't lost at boundaries

  • Metadata — Preserve source document title, URL, section header, page number with each chunk

  • Parent document — Store the parent doc ID so you can retrieve surrounding context if needed

Python chunking example:

def chunk_documents(documents: list[dict], chunk_size: int = 500, overlap: int = 100) -> list[dict]:
    """Split documents into overlapping chunks with metadata."""
    chunks = []
    for doc in documents:
        text = doc["content"]
        words = text.split()
        for i in range(0, len(words), chunk_size - overlap):
            chunk_text = " ".join(words[i:i + chunk_size])
            if not chunk_text.strip():
                continue
            chunks.append({
                "content": chunk_text,
                "source_title": doc.get("title", ""),
                "source_url": doc.get("url", ""),
                "chunk_index": len(chunks),
                "parent_doc_id": doc.get("id", ""),
            })
    return chunks

LangChain chunking:

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    separators=["\n\n", "\n", ". ", " "]
)
chunks = splitter.split_documents(documents)

4. Index Mapping

Store chunk text, embedding, and metadata for retrieval and source citation.

PUT /knowledge-base
{
  "mappings": {
    "properties": {
      "content": { "type": "text" },
      "embedding": {
        "type": "dense_vector",
        "dims": 1024,
        "index": true,
        "similarity": "cosine"
      },
      "source_title": { "type": "text", "fields": { "keyword": { "type": "keyword" } } },
      "source_url": { "type": "keyword" },
      "section_header": { "type": "text" },
      "parent_doc_id": { "type": "keyword" },
      "chunk_index": { "type": "integer" },
      "created_at": { "type": "date" }
    }
  }
}

dims must match your embedding model's output dimension. The example uses 1024 (Jina v3 default on EIS). If the developer selects a different model, adjust dims accordingly — a mismatch causes indexing failures. With semantic_text this coupling is handled automatically.

5. Ingestion Pipeline

Use an ingest pipeline to embed chunks at index time.

PUT _ingest/pipeline/embed-knowledge-base
{
  "processors": [
    {
      "inference": {
        "model_id": "<YOUR_EMBEDDING_MODEL>",
        "input_output": [
          {
            "input_field": "content",
            "output_field": "embedding"
          }
        ]
      }
    }
  ]
}

Replace <YOUR_EMBEDDING_MODEL> with your inference endpoint ID. The default EIS embedding model is Jina v3 (1024 dims); Jina v5-small is available for cost-sensitive workloads. Check EIS models for current model IDs — no API key or ML nodes needed.

Bulk index chunks:

from elasticsearch import Elasticsearch, helpers

es = Elasticsearch(cloud_id="...", api_key="...")

def index_chunks(chunks: list[dict]) -> tuple[int, list]:
    actions = [
        {"_index": "knowledge-base", "_source": chunk, "pipeline": "embed-knowledge-base"}
        for chunk in chunks
    ]
    return helpers.bulk(es, actions, raise_on_error=False, raise_on_exception=False)

6. Retrieval Patterns

Semantic Retrieval (Default for RAG)
GET /knowledge-base/_search
{
  "knn": {
    "field": "embedding",
    "query_vector_builder": {
      "text_embedding": {
        "model_id": "<YOUR_EMBEDDING_MODEL>",
        "model_text": "How do I configure index mappings?"
      }
    },
    "k": 5,
    "num_candidates": 50
  },
  "_source": ["content", "source_title", "source_url", "section_header"]
}
Hybrid Retrieval (Better for Mixed Queries)

Combine keyword and semantic for more robust retrieval:

POST /knowledge-base/_search
{
  "size": 5,
  "query": {
    "bool": {
      "should": [
        { "match": { "content": "configure index mappings" } },
        {
          "knn": {
            "field": "embedding",
            "query_vector_builder": {
              "text_embedding": {
                "model_id": "<YOUR_EMBEDDING_MODEL>",
                "model_text": "How do I configure index mappings?"
              }
            },
            "k": 5,
            "num_candidates": 50
          }
        }
      ]
    }
  },
  "rank": { "rrf": {} },
  "_source": ["content", "source_title", "source_url"]
}
Filtered Retrieval (Scope to Specific Sources)
GET /knowledge-base/_search
{
  "knn": {
    "field": "embedding",
    "query_vector_builder": {
      "text_embedding": {
        "model_id": "<YOUR_EMBEDDING_MODEL>",
        "model_text": "How do I configure mappings?"
      }
    },
    "k": 5,
    "num_candidates": 50,
    "filter": {
      "term": { "source_title.keyword": "Elasticsearch Guide" }
    }
  }
}

7. Answer Generation

Pass retrieved chunks to an LLM with a grounded prompt.

from openai import OpenAI  # or the SDK for the developer's chosen LLM
from elasticsearch import Elasticsearch

es = Elasticsearch(cloud_id="...", api_key="...")
llm = OpenAI()  # replace with the developer's LLM provider

def ask(question: str, k: int = 5) -> dict:
    # 1. Retrieve relevant chunks
    resp = es.search(
        index="knowledge-base",
        knn={
            "field": "embedding",
            "query_vector_builder": {
                "text_embedding": {
                    "model_id": "<YOUR_EMBEDDING_MODEL>",
                    "model_text": question
                }
            },
            "k": k,
            "num_candidates": k * 10
        },
        source=["content", "source_title", "source_url"]
    )
    chunks = resp["hits"]["hits"]

    # 2. Build context from retrieved chunks
    context_parts = []
    sources = []
    for i, hit in enumerate(chunks):
        src = hit["_source"]
        context_parts.append(f"[{i+1}] {src['content']}")
        sources.append({"title": src.get("source_title", ""), "url": src.get("source_url", "")})

    context = "\n\n".join(context_parts)

    # 3. Generate answer — replace model with the developer's choice from Step 2
    completion = llm.chat.completions.create(
        model="<LLM_MODEL>",
        messages=[
            {"role": "system", "content": (
                "Answer the user's question using ONLY the provided context. "
                "Cite sources using [1], [2], etc. "
                "If the context doesn't contain enough information, say so."
            )},
            {"role": "user", "content": f"Context:\n{context}\n\nQuestion: {question}"}
        ],
        temperature=0.2
    )

    return {
        "answer": completion.choices[0].message.content,
        "sources": sources
    }

8. Conversational Memory

For multi-turn conversations, include chat history in the prompt and optionally reformulate the question.

def ask_with_history(question: str, history: list[dict], k: int = 5) -> dict:
    # Reformulate question using chat history for better retrieval
    if history:
        reformulation = llm.chat.completions.create(
            model="<LLM_MODEL>",
            messages=[
                {"role": "system", "content": (
                    "Rewrite the user's question as a standalone search query, "
                    "incorporating context from the conversation history."
                )},
                *history,
                {"role": "user", "content": question}
            ],
            temperature=0
        )
        search_query = reformulation.choices[0].message.content
    else:
        search_query = question

    # Retrieve using reformulated query
    result = ask(search_query, k=k)

    # Generate with full conversation context
    messages = [
        {"role": "system", "content": (
            "Answer the user's question using the provided context. "
            "Cite sources using [1], [2], etc. "
            "Consider the conversation history for context."
        )},
        *history,
        {"role": "user", "content": f"Context:\n{result['context']}\n\nQuestion: {question}"}
    ]

    completion = llm.chat.completions.create(
        model="<LLM_MODEL>",
        messages=messages,
        temperature=0.2
    )

    return {
        "answer": completion.choices[0].message.content,
        "sources": result["sources"]
    }

9. API Endpoint

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/chat", methods=["POST"])
def chat():
    body = request.json
    question = body.get("question", "")
    history = body.get("history", [])

    if not question:
        return jsonify({"error": "Missing question"}), 400

    result = ask_with_history(question, history)
    return jsonify(result)

10. Relevance Tuning for RAG

LeverEffect
Chunk sizeSmaller = more precise retrieval, less context per chunk. Larger = more context but noisier.
k (num results)More chunks = more context for the LLM but risks dilution. Start with 3-5.
Hybrid retrievalAdds keyword matching; helps when questions contain specific terms or identifiers.
RerankingRetrieve 20, rerank to top 5 with a managed reranker (Jina Reranker on EIS) for best precision.
Metadata filteringScope retrieval to relevant sources, time ranges, or categories.

11. Common Follow-Ups

QuestionAnswer
"The chatbot hallucinates"Strengthen the system prompt ("only use provided context"), reduce temperature, add "I don't know" instructions.
"Answers are too vague"Reduce chunk size for more precise passages; increase k for more context.
"How do I cite sources?"Include source metadata in retrieval, reference in prompt with numbered citations.
"How do I handle long documents?"Chunk with overlap; consider hierarchical retrieval (retrieve chunk, then fetch parent section).
"How do I update the knowledge base?"Re-chunk and re-index changed documents. Use parent_doc_id to delete old chunks before re-indexing.
"Which LLM should I use?"Use the LLM the developer chose in Step 2. EIS provides managed LLMs; OpenAI, Anthropic, or similar also work.

12. When to Upgrade

  • Agentic RAG — When the chatbot needs to take actions (create tickets, update records), not just answer questions. Consider Elastic's Agent Builder.

  • Multi-index RAG — When answers span multiple data sources. Use multiple kNN queries or a unified index.

  • Streaming — For real-time chat UX, stream LLM responses token by token.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

The author says: Elasticsearch 8.x or 9.x, self-managed, Elastic Cloud Hosted, or Elastic Cloud Serverless. Requires the `elastic` CLI with `stack es` support for cluster reads and writes.

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
elastic
Publisher
Elastic (Official)
Source
elastic/agent-skills · skills/elasticsearch/elasticsearch-onboarding · commit 09d879d
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Also distributed in
elastic/agent-skills

AutoGineer is not affiliated with or endorsed by elastic or Elastic.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download elasticsearch-onboarding.zip