블로그

TypeScript로 Elasticsearch MCP 서버 생성

TypeScript와 Claude Desktop을 사용하여 Elasticsearch MCP 서버를 생성하는 방법을 알아보세요.

Elasticsearch에서 대규모 지식 기반을 다룰 때, 정보를 찾아내는 것은 첫 관문을 넘긴 것에 불과합니다. 엔지니어는 종종 여러 문서에서 결과를 종합하고, 요약을 작성하며, 답변을 출처까지 추적해야 합니다. 모델 컨텍스트 프로토콜(MCP)은 이를 달성하기 위해 Elasticsearch를 거대 언어 모델(LLM) 기반 애플리케이션과 연결하는 표준화된 방법을 제공합니다. Elastic은 Elastic Agent Builder(기능 중 MCP 엔드포인트 포함)와 같은 공식 솔루션을 제공하지만, 사용자 지정 MCP 서버를 구축하면 검색 논리, 결과 형식, 검색된 콘텐츠가 종합, 요약, 인용을 위해 LLM에 전달되는 방식을 완전히 제어할 수 있습니다.

이 글에서는 사용자 지정 Elasticsearch MCP 서버 구축의 장점을 살펴보고, Elasticsearch를 LLM 기반 애플리케이션에 연결하는 TypeScript로 서버를 생성하는 방법을 보여드리겠습니다.

사용자 지정 Elasticsearch MCP 서버를 구축해야 하는 이유는 무엇입니까?

Elastic은 MCP 서버에 대한 몇 가지 대안을 제공합니다.

MCP 서버가 Elasticsearch와 상호 작용하는 방식을 더 세밀하게 제어하고 싶다면, 직접 사용자 지정 서버를 구축하여 요구 사항에 딱 맞게 최적화할 수 있는 유연성을 확보할 수 있습니다. 예를 들어, Agent Builder의 MCP 엔드포인트는 Elasticsearch 쿼리 언어(ES|QL) 쿼리로 제한되지만, 사용자 지정 서버를 사용하면 전체 쿼리 DSL을 사용할 수 있습니다. 또한 결과를 LLM으로 전달되기 전에 결과의 서식을 지정하는 방법을 제어할 수 있으며, 이번 튜토리얼에서 다룰 OpenAI 기반 요약 기능과 같은 추가적인 처리 단계를 통합할 수도 있습니다.

이 글을 마칠 때쯤이면, Elasticsearch 인덱스에 저장된 정보를 검색하고, 요약하며, 인용을 제공하는 TypeScript로 된 MCP 서버를 갖게 됩니다. 검색에는 Elasticsearch를, 요약 및 인용 생성에는 OpenAI gpt-4o-mini 모델을 사용하며, 사용자 쿼리를 받고 응답을 제공하는 MCP 클라이언트와 UI로는 Claude Desktop을 사용할 것입니다. 최종적으로 엔지니어가 조직 내 기술 문서 전반에서 모범 사례를 발견하고 종합할 수 있도록 돕는 내부 지식 어시스턴트를 구축하게 됩니다.

TypeScript와 Claude Desktop을 사용하여 Elastic MCP 서버를 생성합니다.

필수 구성 요소:

  • Node.js 20+

  • Elasticsearch

  • OpenAI API 키

  • Claude Desktop

MCP란 무엇입니까?

MCPAnthropic에서 만든 오픈 표준으로, LLM과 Elasticsearch와 같은 외부 시스템 간에 안전한 양방향 연결을 제공합니다. MCP의 현황에 대한 자세한 내용은 이 글에서 확인할 수 있습니다.

MCP 환경은 광범위한 사용 사례를 지원하는 서버들이 등장하며 매일 진화하고 있습니다. 게다가, 이 글에서 보여 드릴 것처럼 자신만의 맞춤형 MCP 서버를 구축하는 것도 매우 쉽습니다.

MCP 클라이언트

사용 가능한 MCP 클라이언트 목록은 매우 방대하며, 각 클라이언트에는 저마다의 특징과 제한 사항이 있습니다. 단순함과 대중성을 고려하여 Claude Desktop을 MCP 클라이언트로 사용하겠습니다. Claude Desktop은 사용자가 자연어로 질문을 던지는 채팅 인터페이스 역할을 하며, MCP 서버에 노출된 도구를 자동으로 호출하여 문서를 검색하고 요약을 생성합니다.

‘커피 한 잔과 함께하는 Claude 타임인가요? 오늘은 어떻게 도와드릴까요?' 라는 문구가 적힌 Claude 4.5 Sonnet 페이지입니다.

Elasticsearch MCP 서버 생성하기

TypeScript SDK를 사용하면, 사용자 쿼리 입력을 기반으로 Elasticsearch 데이터를 쿼리하는 방법을 이해하는 서버를 쉽게 만들 수 있습니다.

이 글에서는 Elasticsearch MCP 서버를 Claude Desktop 클라이언트와 통합하는 단계를 설명합니다.

  1. Elasticsearch용 MCP 서버를 구성합니다.

  2. MCP 서버를 Claude Desktop에 로드합니다.

  3. 테스트해 보세요.

Elasticsearch용 MCP 서버 구성

시작하려면 Node 애플리케이션을 초기화하십시오:

npm init -y

이렇게 하면 package.json 파일이 생성되며, 이를 통해 이 애플리케이션에 필요한 의존성을 설치하기 시작할 수 있습니다.

npm install @elastic/elasticsearch @modelcontextprotocol/sdk openai zod && npm install --save-dev ts-node @types/node typescript
  • @elastic/elasticsearch 패키지를 통해 Elasticsearch Node.js 라이브러리에 액세스할 수 있습니다.

  • @modelcontextprotocol/sdk는 MCP 서버 생성 및 관리, 도구 등록, MCP 클라이언트와의 통신 처리를 위한 핵심 도구를 제공합니다.

  • openai를 사용하면 OpenAI 모델과 상호 작용하여 요약이나 자연어 응답을 생성할 수 있습니다.

  • zod 각 도구의 입력 및 출력 데이터에 대해 구조화된 스키마를 정의하고 검증하는 것을 돕습니다.

ts-node, @types/node, typescript 는 개발 중에 코드의 타입을 지정하고 스크립트를 컴파일하는 데 사용됩니다.

데이터셋 설정

Claude Desktop이 MCP 서버를 통해 쿼리할 수 있는 데이터를 제공하기 위해, 가상의 내부 지식 기반 데이터 세트를 사용하겠습니다. 이 데이터 세트의 문서는 다음과 같습니다.

{
    "id": 5,
    "title": "Logging Standards for Microservices",
    "content": "Consistent logging across microservices helps with debugging and tracing. Use structured JSON logs and include request IDs and timestamps. Avoid logging sensitive information. Centralize logs in Elasticsearch or a similar system. Configure log rotation to prevent storage issues and ensure logs are searchable for at least 30 days.",
    "tags": ["logging", "microservices", "standards"]
}

데이터를 수집하기 위해, Elasticsearch에 인덱스를 생성하고 데이터 세트를 로드하는 스크립트를 준비했습니다. 여기서 확인하실 수 있습니다.

MCP 서버

index.ts(이)라는 이름의 파일을 생성하고, 의존성을 가져오고 환경 변수를 처리하기 위해 다음 코드를 추가하세요.

// index.ts
import { z } from "zod";
import { Client } from "@elastic/elasticsearch";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import OpenAI from "openai";

const ELASTICSEARCH_ENDPOINT =
  process.env.ELASTICSEARCH_ENDPOINT ?? "http://localhost:9200";
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY ?? "";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "";
const INDEX = "documents";

또한, Elasticsearch와 OpenAI 호출을 처리할 클라이언트들을 초기화해 보겠습니다.

const openai = new OpenAI({
  apiKey: OPENAI_API_KEY,
});

const _client = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
});

구현을 더 견고하게 만들고 입력 및 출력 데이터의 구조를 보장하기 위해, zod(을)를 사용하여 스키마를 정의하겠습니다. 이를 통해 런타임에 데이터를 검증하고, 오류를 조기에 발견하며, 도구의 응답을 프로그램 방식으로 더 쉽게 처리할 수 있습니다.

const DocumentSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
});

const SearchResultSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
  score: z.number(),
});

type Document = z.infer<typeof DocumentSchema>;
type SearchResult = z.infer<typeof SearchResultSchema>;

구조화된 출력을 자세히 알아보려면 여기를 참조하세요.

이제 MCP 서버를 초기화해 보겠습니다.

const server = new McpServer({
  name: "Elasticsearch RAG MCP",
  description:
    "A RAG server using Elasticsearch. Provides tools for document search, result summarization, and source citation.",
  version: "1.0.0",
});

MCP 도구 정의

모든 구성이 완료되면, MCP 서버가 외부에 제공할 도구를 작성하기 시작할 수 있습니다. 이 서버는 두 가지 도구를 외부에 제공합니다.

  • search_docs: 전체 텍스트 검색을 사용하여 Elasticsearch에서 문서를 검색합니다.

  • summarize_and_cite: 사용자의 질문에 답하기 위해, 이전에 검색된 문서들로부터 정보를 요약하고 종합합니다. 이 도구는 또한 출처 문서를 참조하는 인용 정보를 추가합니다.

이 도구들은 함께 작동하여 간단한 '검색 후 요약' 워크플로우를 형성합니다. 하나의 도구가 관련 문서를 가져오면, 다른 도구가 해당 문서들을 바탕으로 인용구가 포함된 요약 응답을 생성하는 방식입니다.

도구 응답 형식

각 도구는 임의의 입력 매개변수를 허용할 수 있지만, 다음과 같은 구조로 응답해야 합니다.

  • 내용: 비정형 형식으로 된 도구의 응답입니다. 이 필드는 일반적으로 텍스트, 이미지, 오디오, 링크 또는 임베딩을 반환하는 데 사용됩니다. 이 애플리케이션의 경우 도구가 생성한 정보를 포함한 서식 있는 텍스트를 반환하는 데 사용됩니다.

  • structuredContent: 각 도구의 결과를 구조화된 형식으로 제공하기 위해 사용되는 선택적 반환 값입니다. 이는 프로그램 방식의 처리에 유용합니다. 비록 이 MCP 서버에서는 사용되지 않지만, 다른 도구를 개발하거나 결과를 프로그램 방식으로 처리하고자 할 때 유용하게 활용될 수 있습니다.

그 구조를 염두에 두고, 각 도구에 대해 자세히 살펴보겠습니다.

Search_docs 도구

이 도구는 사용자의 쿼리를 기반으로 가장 관련성 높은 문서들을 검색하기 위해 Elasticsearch 인덱스에서 전체 텍스트 검색을 수행합니다. 또한 주요 일치 항목을 강조하고, 연관성 점수와 함께 빠른 개요를 제공합니다.

server.registerTool(
  "search_docs",
  {
    title: "Search Documents",
    description:
      "Search for documents in Elasticsearch using full-text search. Returns the most relevant documents with their content, title, tags, and relevance score.",
    inputSchema: {
      query: z
        .string()
        .describe("The search query terms to find relevant documents"),
      max_results: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of results to return"),
    },
    outputSchema: {
      results: z.array(SearchResultSchema),
      total: z.number(),
    },
  },
  async ({ query, max_results }) => {
    if (!query) {
      return {
        content: [
          {
            type: "text",
            text: "Query parameter is required",
          },
        ],
        isError: true,
      };
    }

    try {
      const response = await _client.search({
        index: INDEX,
        size: max_results,
        query: {
          bool: {
            must: [
              {
                multi_match: {
                  query: query,
                  fields: ["title^2", "content", "tags"],
                  fuzziness: "AUTO",
                },
              },
            ],
            should: [
              {
                match_phrase: {
                  title: {
                    query: query,
                    boost: 2,
                  },
                },
              },
            ],
          },
        },
        highlight: {
          fields: {
            title: {},
            content: {},
          },
        },
      });

      const results: SearchResult[] = response.hits.hits.map((hit: any) => {
        const source = hit._source as Document;

        return {
          id: source.id,
          title: source.title,
          content: source.content,
          tags: source.tags,
          score: hit._score ?? 0,
        };
      });

      const contentText = results
        .map(
          (r, i) =>
            `[${i + 1}] ${r.title} (score: ${r.score.toFixed(
              2,
            )})\n${r.content.substring(0, 200)}...`,
        )
        .join("\n\n");

      const totalHits =
        typeof response.hits.total === "number"
          ? response.hits.total
          : (response.hits.total?.value ?? 0);

      return {
        content: [
          {
            type: "text",
            text: `Found ${results.length} relevant documents:\n\n${contentText}`,
          },
        ],
        structuredContent: {
          results: results,
          total: totalHits,
        },
      };
    } catch (error: any) {
      console.log("Error during search:", error);

      return {
        content: [
          {
            type: "text",
            text: `Error searching documents: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);

We configure fuzziness: “AUTO” to have a variable typo tolerance based on the length of the token that’s being analyzed. We also set title^2 to increase the score of the documents where the match happens on the title 필드.

summarize_and_cite 도구

이 도구는 이전 검색에서 가져온 문서들을 바탕으로 요약을 생성합니다. 사용자의 질문에 답하기 위해 OpenAI의 gpt-4o-mini 모델을 사용하여 가장 관련성 높은 정보를 종합하며, 검색 결과에서 직접 도출된 응답을 제공합니다. 요약과 더불어, 사용된 출처 문서들에 대한 인용 메타데이터도 함께 반환합니다.

server.registerTool(
  "summarize_and_cite",
  {
    title: "Summarize and Cite",
    description:
      "Summarize the provided search results to answer a question and return citation metadata for the sources used.",
    inputSchema: {
      results: z
        .array(SearchResultSchema)
        .describe("Array of search results from search_docs"),
      question: z.string().describe("The question to answer"),
      max_length: z
        .number()
        .optional()
        .default(500)
        .describe("Maximum length of the summary in characters"),
      max_docs: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of documents to include in the context"),
    },
    outputSchema: {
      summary: z.string(),
      sources_used: z.number(),
      citations: z.array(
        z.object({
          id: z.number(),
          title: z.string(),
          tags: z.array(z.string()),
          relevance_score: z.number(),
        })
      ),
    },
  },
  async ({ results, question, max_length, max_docs }) => {
    if (!results || results.length === 0 || !question) {
      return {
        content: [
          {
            type: "text",
            text: "Both results and question parameters are required, and results must not be empty",
          },
        ],
        isError: true,
      };
    }

    try {
      const used = results.slice(0, max_docs);

      const context = used
        .map(
          (r: SearchResult, i: number) =>
            `[Document ${i + 1}: ${r.title}]\\n${r.content}`
        )
        .join("\n\n---\n\n");

      // Generate summary with OpenAI
      const completion = await openai.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [
          {
            role: "system",
            content:
              "You are a helpful assistant that answers questions based on provided documents. Synthesize information from the documents to answer the user's question accurately and concisely. If the documents don't contain relevant information, say so.",
          },
          {
            role: "user",
            content: `Question: ${question}\\n\\nRelevant Documents:\\n${context}`,
          },
        ],
        max_tokens: Math.min(Math.ceil(max_length / 4), 1000),
        temperature: 0.3,
      });

      const summaryText =
        completion.choices[0]?.message?.content ?? "No summary generated.";

      const citations = used.map((r: SearchResult) => ({
        id: r.id,
        title: r.title,
        tags: r.tags,
        relevance_score: r.score,
      }));

      const citationText = citations
        .map(
          (c: any, i: number) =>
            `[${i + 1}] ID: ${c.id}, Title: "${c.title}", Tags: ${c.tags.join(
              ", ",
            )}, Score: ${c.relevance_score.toFixed(2)}`,
        )
        .join("\n");

      const combinedText = `Summary:\\n\\n${summaryText}\\n\\nSources used (${citations.length}):\\n\\n${citationText}`;

      return {
        content: [
          {
            type: "text",
            text: combinedText,
          },
        ],
        structuredContent: {
          summary: summaryText,
          sources_used: citations.length,
          citations: citations,
        },
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error generating summary and citations: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);

마지막으로, stdio를 사용하여 서버를 시작해야 합니다. 이는 MCP 클라이언트가 서버의 표준 입력과 표준 출력 스트림을 읽고 씀으로써 통신하게 된다는 것을 의미합니다. stdio는 가장 단순한 전송 옵션이며, 클라이언트를 통해 하위 프로세스로 실행되는 로컬 MCP 서버에 적합합니다. 파일 끝에 다음 코드를 추가합니다.

const transport = new StdioServerTransport();
server.connect(transport);

이제 다음 명령어를 사용하여 프로젝트를 컴파일하십시오:

npx tsc index.ts --target ES2022 --module node16 --moduleResolution node16 --outDir ./dist --strict --esModuleInterop

이렇게 하면 dist 폴더가 생성되고, 그 안에 index.js 파일이 생성됩니다.

MCP 서버를 Claude Desktop에 로드

Claude Desktop에서 MCP 서버를 구성하려면 이 가이드를 따르세요. Claude 구성 파일에서 다음 값들을 설정해야 합니다.

{
  "mcpServers": {
    "elasticsearch-rag-mcp": {
      "command": "node",
      "args": [   "/Users/user-name/app-dir/dist/index.js"
      ],
      "env": {
        "ELASTICSEARCH_ENDPOINT": "your-endpoint-here",
        "ELASTICSEARCH_API_KEY": "your-api-key-here",
        "OPENAI_API_KEY": "your-openai-key-here"
      }
    }
  }
}

args 값은 dist 폴더 안에 있는 컴파일된 파일을 가리켜야 합니다. 또한 코드에 정의된 것과 똑같은 이름으로 구성 파일 내에 환경 변수를 설정해야 합니다.

테스트해 보기

각 도구를 실행하기 전에, 검색 및 도구를 클릭하여 도구들이 활성화되어 있는지 확인하세요. 여기에서 각 도구를 개별적으로 활성화하거나 비활성화할 수도 있습니다.

‘좋은 오후입니다, 제프. 오늘은 어떻게 도와드릴까요?’ 라는 문구가 적힌 Claude 4.5 Sonnet 페이지입니다.

마지막으로 Claude Desktop 채팅에서 MCP 서버를 테스트하고 질문을 시작하십시오:

인증 방식 및 역할 기반 액세스 제어에 관한 문서를 찾는 Claude Desktop 채팅창의 사용자 검색 요청과 그에 대한 Claude의 응답입니다.

'인증 방법 및 역할 기반 액세스 제어에 관한 문서를 검색해 줘'라는 질문에 대해, search_docs 도구가 실행되어 다음과 같은 결과를 반환합니다.

Most Relevant Documents:
Access Control and Role Management (highest relevance) - This document covers role-based access control (RBAC) principles, including ensuring users only have necessary permissions, regular auditing of user roles, revoking inactive accounts, and implementing just-in-time access for sensitive operations.
User Authentication with OAuth 2.0 - This document explains OAuth 2.0 authentication, which enables secure delegated access without credential sharing. It covers configuring identity providers, token management with limited scope and lifetime, and secure storage of refresh tokens.
Container Security Guidelines - While primarily about container security, this document touches on access control aspects like running containers as non-root users and avoiding embedded credentials.
Incident Response Playbook - This mentions role assignment during incidents (incident commander, communications lead, etc.), which relates to access control in emergency scenarios.
Logging Standards for Microservices - This document includes guidance on avoiding logging sensitive information, which is relevant to authentication security.

응답 내용은 다음과 같습니다. '좋습니다! 인증 방식 및 역할 기반 액세스 제어에 관한 관련 문서 5개를 찾았습니다. 검색된 내용은 다음과 같습니다.'

도구 호출은 응답 페이로드의 일부로 소스 문서들을 반환하며, 이 문서들은 나중에 인용구를 생성하는 데 사용됩니다.

인증 방식 및 역할 기반 액세스 제어에 관한 5개의 관련 문서를 포함하는 스크롤형 응답의 Claude 4.5 Sonnet 페이지입니다.

한 번의 상호 작용 내에서 여러 도구를 연결하여 사용할 수도 있습니다. 이 경우, Claude Desktop은 사용자의 질문을 분석한 뒤, 관련 문서를 가져오기 위해 먼저 search_docs(을)를 호출하고, 그 결과를 summarize_and_cite에 전달하여 최종 답변을 생성해야 한다고 판단합니다. 이 모든 과정은 별도의 사용자 프롬프트 없이 이루어집니다.

'제프가 돌아왔네요.'라는 문구와 함께, '우리 시스템 전반의 인증 및 액세스 제어를 개선하기 위한 핵심 권장 사항이 뭐야? 참고 문헌 포함해 줘.'라는 사용자의 새로운 질문이 표시된 Claude Desktop 채팅 화면입니다.

이 경우, '우리 시스템 전반의 인증 및 액세스 제어를 개선하기 위한 핵심 권장 사항이 뭐야? 참고 문헌 포함해 줘.'라는 쿼리에 대한 답변입니다. 다음과 같은 결과를 얻었습니다.

Based on the documentation, here are the main recommendations to improve authentication and access control across your systems:

Key Recommendations

1. Implement Role-Based Access Control (RBAC)
Ensure users have only the permissions necessary for their job functions. This principle of least privilege minimizes security risks by limiting access to what's truly needed. [1]

2. Regular Access Audits
Conduct regular audits of user roles and promptly revoke access for inactive accounts. This helps prevent unauthorized access from dormant or compromised accounts. [1]

3. Just-in-Time (JIT) Access
Implement just-in-time access for sensitive operations, where permissions are granted only when needed and for a limited duration. This reduces the attack surface by minimizing standing privileges. [1]

4. OAuth 2.0 for Secure Authentication
Use OAuth 2.0 to enable secure delegated access without sharing user credentials. This protocol allows third-party applications to access resources on behalf of users without exposing passwords. [2]

5. Token Security and Management
Configure your identity provider to issue tokens with limited scope and lifetime

Store refresh tokens securely
Validate access tokens consistently to maintain security [2]
References

Access Control and Role Management (Tags: security, access-control)
User Authentication with OAuth 2.0 (Tags: authentication, oauth)
These recommendations work together to create a defense-in-depth approach, where multiple security layers protect your systems from unauthorized access.

이전 단계와 마찬가지로, 이 질문에 대한 각 도구의 응답을 확인할 수 있습니다.

'우리 시스템 전반의 인증 및 액세스 제어를 개선하기 위한 핵심 권장 사항이 뭐야? 참고 문헌 포함해 줘'라는 질문에 대한 각 도구의 응답을 포함하는 스크롤형 텍스트의 Claude Desktop 채팅 페이지입니다.

참고: 각 도구 사용 승인 여부를 묻는 하위 메뉴가 나타나면 항상 허용 또는 한 번 허용을 선택하십시오.

사용자가 선택할 수 있는 '항상 허용' 및 '한 번만 허용' 옵션이 표시된 Claude Desktop 화면입니다.

결론

MCP 서버는 로컬 및 원격 애플리케이션 모두를 위한 LLM 도구 표준화를 향한 중요한 진전을 의미합니다. 완전한 호환성을 구현하기 위해 아직 작업 중이지만, 이를 향해 빠르게 나아가고 있습니다.

이 글에서 Elasticsearch를 LLM 기반 애플리케이션에 연결하는 사용자 지정 MCP 서버를 TypeScript로 구축하는 방법을 배웠습니다. 서버는 두 가지 도구를 제공합니다. Query DSL을 사용하여 관련 문서를 가져오는 search_docs(와)과, OpenAI 모델을 통해 인용구가 포함된 요약을 생성하고 Claude Desktop을 클라이언트 UI로 사용하는 summarize_and_cite입니다.

다양한 클라이언트와 서버 제공 업체 간의 호환성 미래는 매우 유망해 보입니다. 다음 단계로는 에이전트에 더 많은 기능과 유연성을 추가하는 과정이 포함됩니다. 검색 템플릿을 사용하여 쿼리를 매개변수화함으로써 정확도와 유연성을 얻는 방법에 대한 실용적인 을 읽어 보실 수 있습니다.

관련 콘텐츠

채팅창을 넘어선 Agent Builder: Augmented Infrastructure 소개

Alexander Wert

Elasticsearch로 에이전틱 메모리 관리하기

Someshwaran Mohankumar

Elasticsearch 추론 API와 Hugging Face 모델 함께 사용하기

Jeffrey Rengifo

Elastic 에이전트 빌더와 GPT-OSS로 HR용 AI 에이전트 구축하기

Tomás Murúa

셸 도구는 컨텍스트 엔지니어링을 위한 만능 해결책이 아닙니다

Leonie Monigatti

최첨단 검색 환경을 구축할 준비가 되셨나요?

충분히 고급화된 검색은 한 사람의 노력만으로는 달성할 수 없습니다. Elasticsearch는 여러분과 마찬가지로 검색에 대한 열정을 가진 데이터 과학자, ML 운영팀, 엔지니어 등 많은 사람들이 지원합니다. 서로 연결하고 협력하여 원하는 결과를 얻을 수 있는 마법 같은 검색 환경을 구축해 보세요.

직접 사용해 보세요