추론 API와 함께 올라마 사용
추론 API를 사용해 Ollama를 Elasticsearch와 통합하는 방법을 알아보세요.
이 문서에서는 Ollama를 사용해 로컬 모델을 Elasticsearch 추론 모델에 연결한 다음 Playground를 사용해 문서에 질문하는 방법을 알아보세요.
Elasticsearch는 사용자가 Open Inference API를 사용하여 LLM에 연결할 수 있도록 지원하며, Amazon Bedrock, Cohere, Google AI, Azure AI Studio, HuggingFace 등 서비스형 제공업체를 지원합니다.
Ollama는 자체 인프라(로컬 머신/서버)를 사용하여 LLM 모델을 다운로드하고 실행할 수 있는 도구입니다. 여기에서 Ollama와 호환되는 사용 가능한 모델 목록을 확인할 수 있습니다.
각 모델을 설정해야 하는 다양한 방법이나 모델 기능에 액세스하기 위한 API를 만드는 방법에 대해 걱정할 필요 없이 다양한 오픈 소스 모델을 호스팅하고 테스트하려는 경우 Ollama가 모든 것을 처리하므로 훌륭한 옵션이 될 수 있습니다.
Ollama API는 OpenAI API와 호환되므로 추론 모델을 쉽게 통합하고 Playground를 사용하여 RAG 애플리케이션을 만들 수 있습니다.
필수 구성 요소
Elasticsearch 8.17
Kibana 8.17
Python
단계
올라마 LLM 서버 설정
LLM 서버를 설정하여 Ollama를 사용하여 Playground 인스턴스에 연결하겠습니다. 그래야 합니다:
올라마를 다운로드하여 실행합니다.
ngrok을 사용하여 인터넷을 통해 Ollama를 호스팅하는 로컬 웹 서버에 액세스합니다.
Ollama 다운로드 및 실행
Ollama를 사용하려면 먼저 다운로드해야 합니다. Ollama는 Linux, Windows, macOS를 지원하므로 여기에서 사용 중인 OS와 호환되는 Ollama 버전을 다운로드하세요. Ollama가 설치되면 지원되는 LLM 목록에서 모델을 선택할 수 있습니다. 이 예에서는 일반적인 다국어 모델인 llama3.2 모델을 사용하겠습니다. 설정 과정에서 Ollama용 명령줄 도구를 활성화합니다. 다운로드가 완료되면 다음 줄을 실행하면 됩니다:
ollama pull llama3.2출력됩니다:
pulling manifest
pulling dde5aa3fc5ff... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 2.0 GB
pulling 966de95ca8a6... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 1.4 KB
pulling fcc5a6bec9da... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 7.7 KB
pulling a70ff7e570d9... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 6.0 KB
pulling 56bb8bd477a5... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 96 B
pulling 34bb5ab01051... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 561 B
verifying sha256 digest
writing manifest
success설치가 완료되면 다음 명령으로 테스트할 수 있습니다:
ollama run llama3.2질문해 보겠습니다:

모델이 실행되면 Ollama는 포트 "11434" 에서 기본적으로 실행되는 API를 활성화합니다. 공식 문서에 따라 해당 API를 요청해 보겠습니다:
curl http://localhost:11434/api/generate -d '{
"model": "llama3.2",
"prompt": "What is the capital of France?"
}'이것이 저희가 받은 답변입니다:
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.152817532Z","response":"The","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.251884485Z","response":" capital","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.347365913Z","response":" of","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.446837322Z","response":" France","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.542367394Z","response":" is","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.644580384Z","response":" Paris","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.739865362Z","response":".","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.834347518Z","response":"","done":true,"done_reason":"stop","context":[128006,9125,128007,271,38766,1303,33025,2696,25,6790,220,2366,18,271,128009,128006,882,128007,271,3923,374,279,6864,315,9822,30,128009,128006,78191,128007,271,791,6864,315,9822,374,12366,13],"total_duration":6948567145,"load_duration":4386106503,"prompt_eval_count":32,"prompt_eval_duration":1872000000,"eval_count":8,"eval_duration":684000000}이 엔드포인트의 구체적인 응답은 스트리밍이라는 점에 유의하세요.
ngrok을 사용하여 엔드포인트를 인터넷에 노출하기
엔드포인트는 로컬 환경에서 작동하기 때문에 인터넷을 통해 다른 지점(예: Elastic Cloud 인스턴스)에서 액세스할 수 없습니다. ngrok을 사용하면 공인 IP를 제공하는 포트를 노출할 수 있습니다. ngrok에서 계정을 만들고 공식 설정 가이드를 따르세요.
ngrok 에이전트가 설치 및 구성되면 Ollama가 사용 중인 포트를 노출할 수 있습니다:
ngrok http 11434 --host-header="localhost:11434"참고: --host-header="localhost:11434"헤더는 요청의 "호스트" 헤더가 "localhost:11434와 일치하는지 확인합니다."
이 명령을 실행하면 ngrok과 Ollama 서버가 로컬에서 실행되는 한 작동하는 공개 링크가 반환됩니다.
Session Status online
Account xxxx@yourEmailProvider.com (Plan: Free)
Version 3.18.4
Region United States (us)
Latency 561ms
Web Interface http://127.0.0.1:4040
Forwarding https://your-ngrok-url.ngrok-free.app -> http://localhost:11434
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00 ```"포워딩" 에서 ngrok이 URL을 생성한 것을 볼 수 있습니다. 나중에 사용할 수 있도록 저장하세요.
이제 ngrok에서 생성한 URL을 사용하여 엔드포인트에 HTTP 요청을 다시 해보겠습니다:
curl https://your-ngrok-endpoint.ngrok-free.app/api/generate -d '{
"model": "llama3.2",
"prompt": "What is the capital of France?"
}'응답은 이전 응답과 비슷해야 합니다.
매핑 만들기
ELSER 엔드포인트
이 예제에서는 Elasticsearch 추론 API를 사용하여 추론 엔드포인트를 생성하겠습니다. 또한 임베딩을 생성하는 데 ELSER를 사용합니다.
PUT _inference/sparse_embedding/medicines-inference
{
"service": "elasticsearch",
"service_settings": {
"num_allocations": 1,
"num_threads": 1,
"model_id": ".elser_model_2_linux-x86_64"
}
}이 예제에서는 두 가지 종류의 약을 판매하는 약국이 있다고 가정해 보겠습니다:
처방전이 필요한 의약품.
처방전이 필요하지 않은 의약품.
이 정보는 각 약품의 설명 필드에 포함됩니다.
LLM은 이 필드를 해석해야 하므로 이것이 우리가 사용할 데이터 매핑입니다:
PUT medicines
{
"mappings": {
"properties": {
"name": {
"type": "text",
"copy_to": "semantic_field"
},
"semantic_field": {
"type": "semantic_text",
"inference_id": "medicines-inference"
},
"text_description": {
"type": "text",
"copy_to": "semantic_field"
}
}
}
}text_description 필드에는 설명의 일반 텍스트가 저장되고 semantic_text 필드 유형인 semantic_field 필드에는 ELSER에서 생성한 임베딩이 저장됩니다.
copy_to 속성은 필드 이름 및 text_description 의 콘텐츠를 시맨틱 필드에 복사하여 해당 필드에 대한 임베딩을 생성합니다.
데이터 인덱싱
이제 _bulk API를 사용하여 데이터를 색인해 보겠습니다.
POST _bulk
{"index":{"_index":"medicines"}}
{"id":1,"name":"Paracetamol","text_description":"An analgesic and antipyretic that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":2,"name":"Ibuprofen","text_description":"A nonsteroidal anti-inflammatory drug (NSAID) available WITHOUT a prescription."}
{"index":{"_index":"medicines"}}
{"id":3,"name":"Amoxicillin","text_description":"An antibiotic that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":4,"name":"Lorazepam","text_description":"An anxiolytic medication that strictly requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":5,"name":"Omeprazole","text_description":"A medication for stomach acidity that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":6,"name":"Insulin","text_description":"A hormone used in diabetes treatment that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":7,"name":"Cold Medicine","text_description":"A compound formula to relieve flu symptoms available WITHOUT a prescription."}
{"index":{"_index":"medicines"}}
{"id":8,"name":"Clonazepam","text_description":"An antiepileptic medication that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":9,"name":"Vitamin C","text_description":"A dietary supplement that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":10,"name":"Metformin","text_description":"A medication used for type 2 diabetes that requires a prescription."}대응:
{
"errors": false,
"took": 34732020848,
"items": [
{
"index": {
"_index": "medicines",
"_id": "mYoeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "mooeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "m4oeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "nIoeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "nYoeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 4,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "nooeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 5,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "n4oeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 6,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "oIoeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 7,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "oYoeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 8,
"_primary_term": 1,
"status": 201
}
},
{
"index": {
"_index": "medicines",
"_id": "oooeMpQBF7lnCNFTfdn2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 9,
"_primary_term": 1,
"status": 201
}
}
]
}Playground를 사용하여 질문하기
Playground는 Elasticsearch 인덱스와 LLM 공급자를 사용해 RAG 시스템을 빠르게 생성할 수 있는 Kibana 도구입니다. 자세한 내용은 이 도움말을 참조하세요.
로컬 LLM을 플레이그라운드에 연결
먼저 방금 만든 공개 URL을 사용하는 커넥터를 만들어야 합니다. Kibana에서 >플레이그라운드를 검색한 다음 "LLM에 연결" 을 클릭합니다.

이 작업을 수행하면 Kibana 인터페이스의 왼쪽에 메뉴가 표시됩니다. 거기에서 "OpenAI" 을 클릭합니다.

이제 OpenAI 커넥터 구성을 시작할 수 있습니다.
"커넥터 설정" 으로 이동하여 OpenAI 제공업체의 경우 "기타(OpenAI 호환 서비스)" 를 선택합니다:

이제 다른 필드를 구성해 보겠습니다. 이 예에서는 모델 이름을 "medicines-llm" 으로 지정합니다. URL 필드에는 ngrok에서 생성한 URL(/v1/chat/completions)을 사용합니다. "기본 모델" 필드에서 "llama3.2" 을 선택합니다. API 키를 사용하지 않으므로 임의의 텍스트를 입력하면 계속 진행할 수 있습니다:

"저장" 을 클릭하고 "데이터 소스 추가" 를 클릭하여 인덱스 의약품을 추가합니다:


좋아요! 이제 로컬에서 실행 중인 LLM을 RAG 엔진으로 사용하여 Playground에 액세스할 수 있습니다.

테스트하기 전에 상담원에게 더 구체적인 지침을 추가하고 모델에 전송되는 문서 수를 10개로 늘려서 답변에 사용 가능한 문서가 가장 많이 포함되도록 해 보겠습니다. 컨텍스트 필드는 semantic_field 이며, 여기에는 copy_to 속성 덕분에 약품의 이름과 설명이 포함됩니다.

이제 질문을 해보겠습니다: 처방전 없이 클로나제팜을 구입할 수 있나요? 를 클릭하고 어떤 일이 일어나는지 확인하세요:

예상대로 정답을 맞혔습니다.
다음 단계
다음 단계는 나만의 애플리케이션을 만드는 것입니다! Playground는 컴퓨터에서 실행하고 필요에 맞게 사용자 지정할 수 있는 Python 코드 스크립트를 제공합니다. 예를 들어, FastAPI 서버 뒤에 배치하여 UI에서 소비되는 QA 의약품 챗봇을 만들 수 있습니다.
이 코드는 Playground의 오른쪽 상단에 있는 코드 보기 버튼을 클릭하면 찾을 수 있습니다:

그리고 엔드포인트 & API 키를 사용하여 코드에 필요한 ES_API_KEY 환경 변수를 생성합니다.
이 특정 예제의 코드는 다음과 같습니다:
## Install the required packages
## pip install -qU elasticsearch openai
import os
from elasticsearch import Elasticsearch
from openai import OpenAI
es_client = Elasticsearch(
"https://your-deployment.us-central1.gcp.cloud.es.io:443",
api_key=os.environ["ES_API_KEY"]
)
openai_client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
)
index_source_fields = {
"medicines": [
"semantic_field"
]
}
def get_elasticsearch_results():
es_query = {
"retriever": {
"standard": {
"query": {
"nested": {
"path": "semantic_field.inference.chunks",
"query": {
"sparse_vector": {
"inference_id": "medicines-inference",
"field": "semantic_field.inference.chunks.embeddings",
"query": query
}
},
"inner_hits": {
"size": 2,
"name": "medicines.semantic_field",
"_source": [
"semantic_field.inference.chunks.text"
]
}
}
}
}
},
"size": 3
}
result = es_client.search(index="medicines", body=es_query)
return result["hits"]["hits"]
def create_openai_prompt(results):
context = ""
for hit in results:
inner_hit_path = f"{hit['_index']}.{index_source_fields.get(hit['_index'])[0]}"
## For semantic_text matches, we need to extract the text from the inner_hits
if 'inner_hits' in hit and inner_hit_path in hit['inner_hits']:
context += '\n --- \n'.join(inner_hit['_source']['text'] for inner_hit in hit['inner_hits'][inner_hit_path]['hits']['hits'])
else:
source_field = index_source_fields.get(hit["_index"])[0]
hit_context = hit["_source"][source_field]
context += f"{hit_context}\n"
prompt = f"""
Instructions:
- You are an assistant specializing in answering questions about the sale of medicines.
- Answer questions truthfully and factually using only the context presented.
- If you don't know the answer, just say that you don't know, don't make up an answer.
- You must always cite the document where the answer was extracted using inline academic citation style [], using the position.
- Use markdown format for code examples.
- You are correct, factual, precise, and reliable.
Context:
{context}
"""
return prompt
def generate_openai_completion(user_prompt, question):
response = openai_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": user_prompt},
{"role": "user", "content": question},
]
)
return response.choices[0].message.content
if __name__ == "__main__":
question = "my question"
elasticsearch_results = get_elasticsearch_results()
context_prompt = create_openai_prompt(elasticsearch_results)
openai_completion = generate_openai_completion(context_prompt, question)
print(openai_completion)Ollama에서 작동하게 하려면 OpenAI 서버 대신 Ollama 서버에 연결하도록 OpenAI 클라이언트를 변경해야 합니다. 여기에서 OpenAI 예제 및 호환되는 엔드포인트의 전체 목록을 확인할 수 있습니다.
openai_client = OpenAI(
# you can use http://localhost:11434/v1/ if running this code locally.
base_url='https://your-ngrok-url.ngrok-free.app/v1/',
# required but ignored
api_key='ollama',
)또한 완성 메서드를 호출할 때 모델을 llama3.2로 변경합니다:
def generate_openai_completion(user_prompt, question):
response = openai_client.chat.completions.create(
model="llama3.2",
messages=[
{"role": "system", "content": user_prompt},
{"role": "user", "content": question},
]
)
return response.choices[0].message.content질문을 추가해 보겠습니다: 처방전 없이 클로나제팜을 구입할 수 있나요? Elasticsearch 쿼리로 이동합니다:
def get_elasticsearch_results():
es_query = {
"retriever": {
"standard": {
"query": {
"nested": {
"path": "semantic_field.inference.chunks",
"query": {
"sparse_vector": {
"inference_id": "medicines-inference",
"field": "semantic_field.inference.chunks.embeddings",
"query": "Can I buy Clonazepam without a prescription?"
}
},
"inner_hits": {
"size": 2,
"name": "medicines.semantic_field",
"_source": [
"semantic_field.inference.chunks.text"
]
}
}
}
}
},
"size": 3
}
result = es_client.search(index="medicines", body=es_query)
return result["hits"]["hits"]또한 몇 번의 인쇄를 통해 완료 호출에도 Elasticsearch 결과를 질문 컨텍스트의 일부로 전송하고 있음을 확인할 수 있습니다:
if __name__ == "__main__":
question = "Can I buy Clonazepam without a prescription?"
elasticsearch_results = get_elasticsearch_results()
context_prompt = create_openai_prompt(elasticsearch_results)
print("========== Context Prompt START ==========")
print(context_prompt)
print("========== Context Prompt END ==========")
print("========== Ollama Completion START ==========")
openai_completion = generate_openai_completion(context_prompt, question)
print(openai_completion)
print("========== Ollama Completion END ==========")이제 다음 명령을 실행해 보겠습니다.
pip install -qU elasticsearch openai
python main.py
다음과 같은 내용이 표시될 것입니다:
========== Context Prompt START ==========
Instructions:
- You are an assistant specializing in answering questions about the sale of medicines.
- Answer questions truthfully and factually using only the context presented.
- If you don't know the answer, just say that you don't know, don't make up an answer.
- You must always cite the document where the answer was extracted using inline academic citation style [], using the position.
- Use markdown format for code examples.
- You are correct, factual, precise, and reliable.
Context:
Clonazepam
---
An antiepileptic medication that requires a prescription.A nonsteroidal anti-inflammatory drug (NSAID) available WITHOUT a prescription.
---
IbuprofenAn anxiolytic medication that strictly requires a prescription.
---
Lorazepam
========== Context Prompt END ==========
========== Ollama Completion START ==========
No, you cannot buy Clonazepam over-the-counter (OTC) without a prescription [1]. It is classified as a controlled substance in the United States due to its potential for dependence and abuse. Therefore, it can only be obtained from a licensed healthcare provider who will issue a prescription for this medication.
========== Ollama Completion END ==========결론
이 글에서는 Ollama와 같은 도구를 Elasticsearch 추론 API 및 Playground와 함께 사용할 때 그 힘과 다양성을 확인할 수 있습니다.
몇 가지 간단한 단계를 거친 후, 자체 인프라에서 실행되는 LLM을 사용하는 채팅이 포함된 RAG 애플리케이션을 무료로 사용할 수 있게 되었습니다. 이를 통해 리소스와 민감한 정보를 더 잘 제어할 수 있을 뿐만 아니라 다양한 작업에 대한 다양한 모델에 액세스할 수 있습니다.
Elastic 추론 API와 함께 Ollama를 사용하는 방법
Ollama와 Elastic 추론 API를 사용해 Playground로 RAG 앱 생성하기
1.
Ollama LLM 서버 설정
2.
매핑 만들기
3.
인덱스 데이터
4.
Playground를 사용하여 질문하기
자주 묻는 질문
올라마란 무엇인가요?
Ollama는 자체 인프라(로컬 머신/서버)를 사용하여 LLM 모델을 다운로드하고 실행할 수 있는 도구입니다.
