Laf 已成功上架 Sealos 模板市場,可通過 Laf 應用模板來一鍵部署!
這意味着 Laf 在私有化部署上的擴展性得到了極大的提升。
Sealos 作爲一個功能強大的雲操作系統,能夠秒級創建多種高可用數據庫,如 MySQL、PostgreSQL、MongoDB 和 Redis 等,也可以一鍵運行各種消息隊列和微服務,甚至 GPU 集羣上線後還可以跑各種 AI 大模型。
將 Laf 一鍵部署到 Sealos 中,我們就可以在 Laf 中直接通過內網調用 Sealos 提供的所有這些能力。無論用戶需要什麼樣的後端支持,只需在 Sealos 上運行相應的服務即可。這種集成模式不僅提高了資源的利用效率,而且還提供了無縫的技術集成,使得 Laf 成爲一個更加強大和多功能的 Serverless 平臺,彌補了傳統 Serverless 平臺在後端能力方面的不足。
Sealos 強大的模板市場提供了豐富的應用生態,用戶可以在模板市場中一鍵部署各種應用。本文以 Elasticsearch 爲例,展示如何在 Laf 中調用 Sealos 模板市場中部署的 Elasticsearch 來搭建一個向量數據庫,提供定製化知識庫搜索能力。
背景知識
如果我們想往大模型裏邊注入知識,最先能想到的就是對大模型進行微調,大模型有很好的根據上文來回答問題的能力。
假設一個場景,我有個問題是:“請給我介紹一下萬能青年旅店這支樂隊 “(假設模型內部並沒有存儲萬青的相關信息),然後我有個 100w 字的文檔,裏邊包含了世界上所有樂隊的介紹。如果模型對無限長的輸入都有很好的理解能力,那麼我可以設計這樣一個輸入 “以下是世界上所有樂隊的介紹:[插入 100w 字的樂隊簡介文檔],請根據上文給我介紹一下萬青這支樂隊”,讓模型來回答我的問題。但模型支持的輸入長度是很有限的,比如 ChatGPT 只支持 32K Token 長度的輸入 (大約 50 頁文本)。
實際上,如果想讓大模型根據文檔來回答問題,必須要精簡在輸入中文檔內容的長度。一種做法是,我們可以把文檔切成若干段,只將少量的和問題有關的文檔片段拿出來,放到大模型的輸入裏。至此,”大模型外掛數據庫 “的問題轉換成了 “文本檢索的問題” 了,目標是根據問題找出文檔中和問題最相關的片段,這已經和大模型本身完全無關了。
文本檢索裏邊比較常用的是利用向量進行檢索,我們可以把文檔片段全部向量化 (通過語言模型,如 bert 等),然後存到向量數據庫 (如 Annoy、FAISS、hnswlib 等) 裏邊,來了一個問題之後,也對問題語句進行向量話,以餘弦相似度或點積等指標,計算在向量數據庫中和問題向量最相似的 top k 個文檔片段,作爲上文輸入到大模型中。
向量數據庫都支持近似搜索功能,在犧牲向量檢索準確度的情況下,提高檢索速度。完整流程圖如下所示:
按照這個思路我們需要做的事情有兩個,一個是把文檔向量化,另一個是搭建一個向量數據庫。文檔向量化最簡單的方法可以使用 openai
提供的轉化接口將文檔轉化成向量數組,除此之外還可以通過 bert 模型。OpenAI 還給出了向量數據庫參考選項,建議我們使用 cosin 相似度公式來求向量相似度:
如何在 Sealos 上快速部署向量數據庫呢?從 OpenAI 的推薦上我們看到了裏面有個 Elasticsearch 選項,那我們就用它了。
部署 Laf 與 Elasticsearch
首先我們需要打開 Sealos 公有云桌面:https://cloud.sealos.top
Sealos 是完全開源的,您也可以通過 Sealos 構建自己的私有云:https://sealos.run/self-hosting
然後進入 “模板市場”,通過 Laf 模板與 Elasticsearch 模板分別部署 Laf 和 Elasticsearch。
然後在 Laf 中新建一個應用,安裝依賴 elastic/elasticsearch
:
一旦應用創建完畢,您可以使用雲函數代碼來連接 Elasticsearch。在此示例中,我們直接插入了 10 條測試數據 (爲了簡化演示過程,我們直接使用了測試數據,並沒有用 OpenAI 的接口去生成文檔的向量數據)。
import cloud from '@lafjs/cloud'
const { Client } = require('@elastic/elasticsearch')
const ca = `-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIQQKs5V2terYVNUrHt9K0CzTANBgkqhkiG9w0BAQsFADAb
MRkwFwYDVQQDExBlbGFzdGljc2VhcmNoLWNhMB4XDTIzMTEyMjA3MDcxOFoXDTI0
MTEyMTA3MDcxOFowGzEZMBcGA1UEAxMQZWxhc3RpY3NlYXJjaC1jYTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAPYyHrFgyoD3Pkkc/ekXhHGKi+qKPBbp
afPuGImQfTtkGlzhaHJ7Iy3MZojP/iyt3FTY+LvxODsbkgIrQJWwiG2s26rw03Zd
lphf7RULRa9Z/TKt0jxHV9M419ge2zRij6Al3uUHCP2FxjVMgYjuFisKwNalQfUE
spCTq9lWNp4bKP32GieEBQKeNRD8ElNBJkInIA2aTyH2TIhyICK0f5GjH52rxKeV
wrE/BHq8zomHRVtTM67KHoXc9RJgYNICfooeDHvi/f9f+pWrX881rmbNWXGcxu2u
GQLqCAkqpIpUwn5HAoSvUYHmxwgaDC866fjsgxv/6DMDJuGPmfsBqQMCAwEAAaNh
MF8wDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
AjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQaGk9O4hQFjJPU6ay8qqU8CNug
uzANBgkqhkiG9w0BAQsFAAOCAQEAfZUesinfp1jeSqfHBSPHOgZ1q/v8xoClEPRl
wzh8sbL14iuuSb190J8zQefvzxC7ip4kVCVTW52fBZNyoMpvj0cXKWRGFmz3yHIs
TNdwOy15mQRQGbOTDBkQ528SbrmrWF4W7kDMoWs0t02UIlSfBWDjJrVharRR9QuF
cGjoS59TCAFcHHUsPO3lcUT1TCq/W4xnds3zBxJiGeIdmDqE6DbS78YfwP9rhTx0
oxcQwpKaOj8vxQNQxNbJRmWgffx0PgUzFPni/N5FgFQQXDPG4i0gMciekHWz8VRM
pp2z1uD1lVdDa/83w/IZCQOqDU7cRjDosg+gaAefFGNMHVbPBw==
-----END CERTIFICATE-----
`
export default async function (ctx: FunctionContext) {
const client = new Client({
node: 'https://elasticsearch-master.ns-wz9g09tc.svc.cluster.local:9200',
auth: {
username: 'elastic',
password: 'zhtvadgdinhkyirozeznxlxd'
},
tls: {
ca: ca,
rejectUnauthorized: false
}
})
const health = await client.cluster.health()
console.log(health)
// 刪除已存在的索引(如果有)
await client.indices.delete({
index: 'vectors',
ignore_unavailable: true
})
// 創建一個新的向量索引
await client.indices.create({
index: 'vectors',
body: {
mappings: {
properties: {
embedding: {
type: 'dense_vector',
// 向量列表的長度
dims: 3,
index:true,
// 字段索引,consin函數求相似度
similarity:'cosine'
},
text: {
type: 'text'
}
}
}
}
})
// 測試數據
const documents = [
{ embedding: [0.5, 10, 6], text: 'text1' },
{ embedding: [-0.5, 10, 10], text: 'text2' },
{ embedding: [1.0, 5, 8], text: 'text3' },
{ embedding: [-0.2, 8, 12], text: 'text4' },
{ embedding: [0.8, 12, 4], text: 'text5' },
{ embedding: [-0.7, 6, 14], text: 'text6' },
{ embedding: [0.3, 14, 2], text: 'text7' },
{ embedding: [-0.4, 16, 8], text: 'text8' },
{ embedding: [0.6, 8, 10], text: 'text9' },
{ embedding: [-0.6, 12, 6], text: 'text10' }
];
// 插入測試數據
for (const doc of documents) {
await client.index({
index: 'vectors',
document: doc,
refresh: true
});
}
// Define the vector to search for
const query_vector = [0.2, 12, 5]
const body = await client.knnSearch({
index: 'vectors',
knn: {
field: 'embedding',
query_vector: query_vector,
k: 3,
num_candidates: 5
},
_source: ["text"]
});
// 輸出搜索結果
console.log(JSON.stringify(body, null, 2))
return { data: 'hi, laf' }
}
通過 cosin 相似度搜索,我們找到了與向量 [0.2, 12, 5]
最相似的三條向量數據。這些數據的文本分別是 text8
、text5
和 text10
。
Elasticsearch 內網調用地址如下:
ca 的值就是 Elasticsearch 的證書,Elasticsearch 的證書可以通過命令行來獲取,先在 Sealos 桌面中打開 “終端” App,然後執行以下命令獲取證書:
kubectl get secret elasticsearch-master-certs -o jsonpath="{.data.ca\.crt}"|base64 -d
Elasticsearch 的用戶名密碼可以通過以下命令獲取:
$ kubectl get secret elasticsearch-master-credentials -o jsonpath="{.data.username}"|base64 -d && echo
elastic
$ kubectl get secret elasticsearch-master-credentials -o jsonpath="{.data.password}"|base64 -d && echo
xurcwgjxpfztmgjquufyyiml
至此簡單的 Demo 已經完成了,後續我們需要做的就是持續地向我們的向量數據庫中添加更多文檔的向量化數據,通過這種方式,我們可以構建起一個功能強大的知識庫。當用戶提問時,先將用戶問題轉換成向量數據,然後在向量數據庫中找到最相似的文檔,將文檔作爲上文輸入到大模型中,最後大模型輸出答案。我們的明星項目 FastGPT 就是這樣做的哦。另外不難看出 Bing Chat 也是異曲同工。
總結
通過將 Laf 集成到 Sealos 雲操作系統中,可以更高效地利用雲操作系統的資源。用戶可以直接在 Laf 中調用 Sealos 提供的各種數據庫和服務,如 MySQL、PostgreSQL、MongoDB 和 Redis 等,以及消息隊列和微服務,實現資源的最大化利用。這種集成方式使得 Laf 成爲了一個功能更加全面的 Serverless 平臺。尤其是在後端能力方面,這種集成提供了一個無縫的解決方案,彌補了傳統 Serverless 平臺的不足。