# HWDC 2024 - 當 Elasticsearch 遇上 AI
:::info
## 本次工作坊參考資訊
- 📊 [工作坊投影片](https://hwdc2024-ifdppud.gamma.site/)
- 📄 [工作坊 HackMD 連結](https://hackmd.io/@estraining/hwdc2024)
- 🔧 [Google Colab 筆記本](https://colab.research.google.com/drive/1vYvop3r9IjOpSwYJguVzBjEbqeTyRSRn)
- 📢 [喬叔 FB 粉絲頁連結](https://www.facebook.com/Joe.ElasticStack)
- 🎓 [喬叔教育訓練網站](https://training.onedoggo.com)
- 🌐 [網頁版](https://training.onedoggo.com/tech-sharing/workshop/hwdc2024)
## 工作坊環境準備
- Elasticsearch 8.15 以上版本
- 推薦:在 [Elastic Cloud](https://cloud.elastic.co) 使用 Email 註冊即可取得 14 天試用的免費環境,不用綁信用卡!
- 自行架設:確保版本一定要在 8.15.0 以上,並且是乾淨的環境,單純參與工作坊使用。
> 工作坊進行中,無法替大家解決各種自行架設造成的環境問題,因此不建議使用自行架設,若是要使用自行架設的環境,請確認自己對於 Elasticsearch 足夠熟悉能排除障礙。
- 這次的操作會使用到 [Google Colab](https://colab.research.google.com/drive/1vYvop3r9IjOpSwYJguVzBjEbqeTyRSRn?usp=sharing)
- 需要登入你的 Google 帳號,並且依以下教學複製工作坊要使用的 Python Notebook 到你的 Google Drive 當中。
:::
## 1. 準備 Elasticsearch 環境
依照不同的環境,你需要完成以下的任務
使用 Elastic Cloud:
- :white_large_square: 從 Elastic Cloud 取得 `Cloud ID`
- :white_large_square: 安裝 Elasticsearch Analysis ICU Plugin 以及增加 Machine Learning Instance (重新啟動集群需要 5 分鐘)
- :white_large_square: 在 Kibana 建立 API Key
自行架設的 Elasticsearch:
- :white_large_square: 安裝 Elasticsearch Analysis ICU Plugin
- :white_large_square: 若有啟用 Security,準備好能存取 Elasticsearch 的使用者帳號密碼
### 1.1 Elastic Cloud 版本的環境準備
#### a. 註冊 Elastic Cloud



收 Email 確認信。

驗證並重新登入後,進入設定流程。

可以選 Search (非必要)

可以考慮選 Google Cloud Taiwan 的 Region,或許會比較快一點 (非必要)

:::info
點選 Launch 之後,大約要等 3~5 分鐘,等待 Elasticsearch Cluster 建立。
:::
最後完成時,你會看到下面這樣的畫面,這已經是進入 Kibana 的畫面了。

若是要進入

#### b. 取得 Elastic Cloud ID

#### c. 安裝 ICU Analysis Plugin


:::warning
第 5 步到第 6 步之中,多增加新增 Machine Learning Instance
* 為了之後試用 ESRE (Elasticsearch Relevance Engine) 功能
:::

:::info
試用版的 Machine Learning Instance 的配置,可以增加到 4 GB RAM (非必要,只是可以跑快一些)
:::



:::warning
Confirm 之後,大約要等 5 分鐘的時間,讓 Elasticsearch Cluster Rolling update.
:::
#### d. 建立 API Key
1. 在 Kibana 網頁上方,搜尋 `API Keys`,進入 **Security / API Keys** 頁面。
:::info
Kibana 網址在哪? 從 [1.1](#a-%E5%8F%96%E5%BE%97-Elastic-Cloud-ID) 步驟的畫面上,就可以看到 Kibana 的 Endpoint,可直接點 Open。
:::
2. 點選右上 `Create API Key`。

3.隨便取一個名字,例如 `HWDC2024-demo`,按下 `Create API Key`。

4. 畫面上會出現 Encoded 的 API Key,將其複製起來保存。

### 1.2 自行安裝 Elasticsearch 版本的環境準備 (Elastic Cloud 版請略過)
#### a. 安裝 ICU Analysis Plugin
在**每個** Elasticsearch 節點執行以下執行,安裝 plugin,安裝完成後重新啟動節點。
```
./bin/elasticsearch-plugin install analysis-icu
```
## 2. 準備 Google Colab 的環境
### 2.1 複製 Colab Notebook 並且設定 Secret
#### a. 先將這次工作坊的 Python Notebook 複製到你的 Google Drive
請點選這份 [Colab Notebook](https://colab.research.google.com/drive/1vYvop3r9IjOpSwYJguVzBjEbqeTyRSRn?usp=sharing) 並且複製到自己的 Google Drive。
:::info
請先複製到自己的 Google Drive,才能保存後續你所有的修改內容。
:::

#### b. 設定 `ELASTIC_CLOUD_ID` 與 `ELASTIC_API_KEY` Secret
:::info
後續在 Colab 中進行 **1. 準備環境** 時,會需要透過這些設定,存取你的 Elasticsearch。
:::

### 2.2 執行 Colab Notebook 中的 `1. 準備環境`
將 `1. 準備環境` 裡面的每個步驟都先執行過,準備機器與安裝相依性套件。
---
## 6. ElasticSearch Relevance Engine (ESRE)
:::info
因為 ESRE 的出現,許多功能在 Elastic Stack 當中即可獨立完成,我們暫時不再需要 Python Client 的操作,接下來的操作步驟將透過 Kibana 進行。
:::
### 6.1 使用 Kibana > Search > Machine Learning > Model Management > Trained Models 下載與佈署模型
1. 打開 kibana
2. 在上方搜尋框,搜尋 `Machine Learning`,並且點選第一個推薦的結果

3. 進入 Machine Learning 頁面後,在左方選單中,點選 **Model Management** 底下的 **Trained Models**

4. 點選 **Add trained model**
5. 選擇 E5 模型,並點選下載。

6. 等待下載完成。

7. 點選 Deploy 的按鈕。

8. Start 完成後,會出現 Deployed 的狀態。

### 6.2 建立 Ingest Pipeline 配合 Inference Process 使用模型進行 Embedding 處理
```
# 模擬執行的效果
POST _ingest/pipeline/_simulate
{
"pipeline": {
"processors": [
{
"inference": {
"model_id": ".multilingual-e5-small_linux-x86_64",
"input_output": [
{
"input_field": "overview",
"output_field": "overview_embedding"
}
]
}
}
]
},
"docs": [
{
"_source": {
"overview": "在《閃電俠》中,貝瑞使用超能力回到過去,想要改變已發生的事件,卻衝擊了時空秩序。當貝瑞試圖拯救家人時,不小心改變了未來,使他受困於另一個時空中;在這個時空中,回歸的薩德將軍正威脅著毀滅世界,但卻沒有超級英雄可以求助,除非貝瑞能說服一位退休的蝙蝠俠重現江湖,解救一位被監禁的氪星人……儘管他可能找錯了人。到頭來,貝瑞為了拯救他所在的世界,返回他所知的未來,唯一的希望就是用盡生命賽跑,但他最後做出的犧牲足以拯救整個宇宙嗎?"
}
}
]
}
# 先查看 Movies 的 Mapping 現況
GET movies/_mapping?filter_path=**.overview*
# 正式建立 Ingest Pipeline
PUT _ingest/pipeline/movies_embedding
{
"description": "HWDC 2024 Joe's Demo",
"processors": [
{
"inference": {
"model_id": ".multilingual-e5-small_linux-x86_64",
"input_output": [
{
"input_field": "overview",
"output_field": "overview_embedding_v2"
}
]
}
}
]
}
# 使用 Update By Query 將所有文件重新 Reindex 一遍,並套用 Ingest Pipeline 進行 Embedding 處理
POST movies/_update_by_query?pipeline=movies_embedding
{
"query": {
"match_all": {}
}
}
# 先查看 Movies 的 Mapping 更新後的結果
GET movies/_mapping?filter_path=**.overview*
# 修改 movies 的 Index Settings,讓未來的資料寫入時,都會套用此 Ingest Pipeline
PUT movies/_settings
{
"index.default_pipeline": "movies_embedding"
}
# 查看修改後的 Index Settings
GET movies/_settings
```
### 6.3 搜尋時,也能直接使用 Elasticsearch 內部的 Model,不需再 Python Client 先進行 Embedding 的處理。
```
# 當 Elasticsearch 裡面有這個 Embedding Model,我們 Search 時也能直接使用
GET movies/_search
{
"_source": {
"excludes": "overview_*"
},
"knn": {
"field": "overview_embedding_v2",
"k": 10,
"num_candidates": 100,
"query_vector_builder": {
"text_embedding": {
"model_id": ".multilingual-e5-small_linux-x86_64",
"model_text": "低級的超級英雄"
}
}
}
}
```
### 6.4 使用外部 OpenAI 的 Text Embedding 模型
:::info
這把 API Key 供大家試用,僅提供 gpt-4o-mini & text-embedding-3-small,並且在研討會結束後就會移除。
`sk-proj-(已失效,請改用自己的 API Key)`
可在 Kibana Dev Tools 的 Variables 設定中,加入 `openai_api_key` 名稱的變數,以方便直接執行以下的範例。
:::
```
# 建立 Text Embedding 模型設定
PUT _inference/text_embedding/openai-embeddings-3-small
{
"service": "openai",
"service_settings": {
"api_key": "${openai_api_key}",
"model_id": "text-embedding-3-small"
}
}
# 測試 Embedding
POST _inference/text_embedding/openai-embeddings-3-small
{
"input": "一隻狗狗"
}
```
:::success
這個透過 Inference API 建立好的 text_embedding 的 model id: `openai-embeddings-3-small` 就如同前面 Elasticsearch 載入的模型,可直接在 **Inference Processor** 或是 knn 的 **query_vector_build** 直接使用
:::
### 6.5 使用外部 OpenAI 的 Completion 推論模型
```
# 建立 OpenAI Completion 推論模型設定 (喬叔提供的 ApiKey 只能使用 gpt-4o-mini)
PUT _inference/completion/openai-completion-gpt-4o-mini
{
"service": "openai",
"service_settings": {
"api_key": "${openai_api_key}",
"model_id": "gpt-4o-mini"
}
}
# 測試使用 OpenAI Completion API 進行推論
POST _inference/completion/openai-completion-gpt-4o-mini
{
"input": "OpenAI 是什麼?"
}
```
### 6.6 Ingest Pipeline 配合 OpenAI Completion 推論模型的使用
#### 新聞摘要
```
POST _ingest/pipeline/_simulate
{
"pipeline": {
"processors": [
{
"script": {
"source": """ctx._prompt = '你是一位專業的台灣新聞編輯,你的任務是將新聞內容產生50字重點摘要。新聞內容:' + ctx.content
"""
}
},
{
"inference": {
"model_id": "openai-completion-gpt-4o-mini",
"input_output": {
"input_field": "_prompt",
"output_field": "summary"
}
}
},
{
"remove": {
"field": "_prompt"
}
}
]
},
"docs": [
{
"_source": {
"content": """〔記者吳昇儒、俞肇福/基隆報導〕基隆一名5個月大倪姓女嬰8月1日委由周姓保母照顧後,卻於15日發現有全身癱軟情形,送醫急救後,竟發現腦部出血,醫生研判是遭到外力劇烈搖晃導致。家屬質疑,是透過基隆市保母系統找來的周姓保母對女嬰不當對待,才會導致女嬰腦部嚴重受創,醫院目前已通報發生疑似「兒虐事件」,由檢警偵辦釐清中。
基隆市政府兒童及少年事務處指出,目前已全力協助救治女嬰,並協助其家庭度過難關,將會儘快查明事件真相,該案也依規定通報衛福部社會及家庭署備案,兒少保護網絡群組也已通報基隆地檢署,由檢察官指揮基隆警二分局調查中,釐清女嬰傷勢成因。"""
}
},
{
"_source": {
"content": """〔記者吳昇儒/新北報導〕62歲郭姓男子今日上午與友人前往新北市南子吝步道健行時,不慎在路途中摔倒導致左腳骨折,無法行走。消防局獲報後,立刻派員前往救援,約於1小時後抵達郭男所在區域,利用捲式擔架將其固定後,花2小時將他搬運下山,送醫救治後幸無生命危險。
消防隊員們詢問後得知,郭男和另2名友人共同前往南子吝步道健行,走到一半突然跌倒,導致左腳骨折,已無法自行山下,才會請消防隊員前往搶救。
隊員於12時27分接觸到受傷的郭男,見他左腳腫脹有骨折情形,所幸意識清楚,暫無生命危險,隨即利用捲式擔架將其固定,並將患者搬運到登山口,送往醫院急救。
新北市政府消防局第六大隊大隊長羅億田表示,提醒所有喜愛登山的朋友,從事登山戶外運動應結伴同行,假設遇到危險或身體不適才能有同伴在第一時間電話求救或作緊急處置。如果不幸發生意外情況,應立即撥打119或110報案,提供正確的人事時地物等情資,才能讓救援迅速到位。"""
}
},
{
"_source": {
"content": """〔記者林宜樟/嘉義報導〕嘉義縣警局水上分局8月13日發生曾姓通緝犯脫逃事件,闖進民宅偷走神明金牌及車輛,開往中埔鄉方向後棄車逃逸;水上分局偵查隊今天上午在彰化縣芬園鄉一處日租套房,將曾姓通緝犯逮捕,並查獲接應曾嫌的劉姓嫌犯,將2人帶回水上分局偵辦。
嘉義縣水上警分局員警8月13日逮捕43歲曾姓毒品通緝犯,曾男在分局偵查隊接受筆錄時,竟趁中午吃便當解開手銬之際,從後門逃跑,並闖民宅偷走,竊取價值10萬元金飾財物,並偷走賓士車代步逃逸,之後將車子棄置在中埔鄉公館偏僻草叢逃逸無蹤,警方漏夜展開地毯式搜捕。"""
}
}
]
}
```
#### 擷取電影重要關鍵字
```
POST _ingest/pipeline/_simulate
{
"pipeline": {
"processors": [
{
"inference": {
"model_id": ".multilingual-e5-small_linux-x86_64",
"input_output": [
{
"input_field": "overview",
"output_field": "overview_embedding"
}
]
}
},
{
"script": {
"source": """
ctx._prompt='針對以下電影標題與內容描述,產生5~10個最能代表這部電影的關鍵字,關鍵字以|隔開,直接回覆關鍵字結果,不需要任何解釋。
電影標題: ' + ctx.title + '
電影內容:
' + ctx.overview + '
關鍵字分別為: '"""
}
},
{
"inference": {
"model_id": "openai-completion-gpt-4o-mini",
"input_output": [
{
"input_field": "_prompt",
"output_field": "_keywords"
}
]
}
},
{
"split": {
"field": "_keywords",
"separator": "\\|",
"target_field": "keywords"
}
},
{
"remove": {
"field": "_prompt"
}
},
{
"remove": {
"field": "_keywords"
}
}
]
},
"docs": [
{
"_source": {
"title": "殺戮列車",
"overview": "當一名陸軍突擊隊員發現他的真愛違背自身意願訂婚時,他登上了開往新德里的火車,大膽地試圖破壞包辦婚姻。但當一群持刀的盜賊開始恐嚇他所乘坐火車上的無辜乘客時,這名突擊隊員親自與他們對抗,展開了一場不顧生死的殺戮狂歡,以拯救周圍的人——將原本平常的通勤變成了一場充滿腎上腺素的刺激之旅。"
}
},
{
"_source": {
"title": "陰間大法師",
"overview": "一對溺死的年輕夫妻化為厲鬼返家,卻必須尋求一個滑稽鬼怪的幫助,嚇嚇這些魯莽無禮的新屋主。"
}
}
]
}
```
### 6.7 Semantic Type & Semantic Query
```
PUT _inference/text_embedding/e5-small
{
"service": "elasticsearch",
"service_settings": {
"num_allocations": 1,
"num_threads": 1,
"model_id": ".multilingual-e5-small_linux-x86_64"
}
}
PUT movies/_mapping
{
"properties": {
"overview_embedding_v3": {
"type": "semantic_text",
"inference_id": "e5-small"
}
}
}
POST movies/_update_by_query
{
"query": {
"match_all": {}
},
"script": {
"source": "ctx._source.overview_embedding_v3 = ctx._source.overview",
"lang": "painless"
}
}
GET movies/_search
{
"query": {
"semantic": {
"field": "overview_embedding_v3",
"query": "主角是狗的電影"
}
}
}
```