StackTraces
Meilisearch從零跑通

從零跑通 Meilisearch 內置 embedder 自動嵌入:8 步,外加 3 個容易卡住的地方

@debug_master2026-10-11

跟著這篇走完,你會把 Meilisearch 的語義搜索從“手動轉向量”換成“內置 embedder 自動嵌入”:在索引設置裡配一次 huggingFace 類型的 embedder,文檔入庫和查詢都不用你再碰向量——傳進去的是純文本,Meilisearch 自己把文本轉成 512 維向量存起來,搜索時它又自己把你的查詢詞轉成向量去比對。整個過程 8 步,每步都有一條命令能驗證自己做對了沒有。

動手前先交代環境。localhost:7700 是 Meilisearch 的預設監聽地址,master key 固定用 aSampleMasterKey(與系列一致),索引沿用上篇的 books_vec(還是那 6 條虛構書籍),你要是換了端口、master key、索引名或嵌入模型,把示例值替換掉就行。這篇預設你已經裝好並跑通了從零跑通 Meilisearch 本地搜索服務,安裝、啟動、異步任務輪詢的套路全部複用第一篇。跟上篇最大的區別說在前面:上一篇(userProvided)要自己裝 Python、跑模型、把文檔和查詢都算成 512 維向量再交給 Meilisearch;這篇一行 Python 都不用寫,向量這回事整個交給服務端。

Meilisearch:從零跑通內置 embedder 自動嵌入的 8 個步驟

前置條件

項 要求 說明
Meilisearch v1.54.3 或更高 內置 embedder(嵌入器)v1.6 起才有,低於這個版本設定接口會不認
前置文章 已跑通 meilisearch-local-search 複用其安裝/啟動/master key/異步任務輪詢套路;沒跑通先回去補
網絡 首次需能訪問 HuggingFace 模型首次使用時自動下載;完全離線或內網環境會卡在下載這一步
master key aSampleMasterKey 與系列一致
curl 任意版本 全文用 curl 調 REST API

先跑這條,確認第一篇的服務還活著。

curl http://localhost:7700/health
{
  "status": "available"
}

返回 {"status":"available"} 就滿足條件。要是返回 curl: (7) Failed to connect to localhost port 7700,說明服務沒起——回第一篇第一步用 meilisearch --master-key="aSampleMasterKey" 重新啟動,再回來。

第一步:建 books_vec 索引,直接傳純文本文檔(不帶向量)

上一篇到這一步之前,得先裝 Python、跑 bge 模型把 6 本書轉成帶 _vectors 的 JSON,然後才能傳文檔。內置 embedder 模式不用走那套——文檔就是普通 JSON,向量由服務端在後臺自動生成,所以這一步先建索引、把純文本傳進去,驗證“不帶向量也能入庫”。

索引走顯式創建,指定主鍵 id,沿用系列慣例:

curl -X POST http://localhost:7700/indexes \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"uid":"books_vec","primaryKey":"id"}'
{
  "taskUid": 20,
  "indexUid": "books_vec",
  "status": "enqueued",
  "type": "indexCreation",
  "enqueuedAt": "2026-10-11T10:00:00.000Z"
}

接著傳文檔。注意看,字段裡有 id、title、author 和 summary(書的簡介,後面模板要把它拼進去),沒有 _vectors:

curl -X POST http://localhost:7700/indexes/books_vec/documents \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  --data-binary '[
    {"id":1,"title":"三体","author":"刘慈欣","summary":"仰望星空,写人类文明与三体文明星际博弈的科幻巨作"},
    {"id":2,"title":"球状闪电","author":"刘慈欣","summary":"以球状闪电为引的科幻小说,物理奇观与人生选择"},
    {"id":3,"title":"活着","author":"余华","summary":"写普通人在苦难中活下去的故事,农村家庭的伦理与温情"},
    {"id":4,"title":"许三观卖血记","author":"余华","summary":"写小人物靠卖血养家的故事,底层生活的坚韧与荒诞"},
    {"id":5,"title":"明朝那些事儿","author":"当年明月","summary":"用通俗笔法写明朝三百年历史,官场斗争与王朝兴衰"},
    {"id":6,"title":"万历十五年","author":"黄仁宇","summary":"从万历十五年切入,剖析明朝制度的运转与深层困境"}
  ]'
{
  "taskUid": 21,
  "indexUid": "books_vec",
  "status": "enqueued",
  "type": "documentAdditionOrUpdate",
  "enqueuedAt": "2026-10-11T10:01:00.000Z"
}

上傳是異步的,等任務 succeeded 後驗證文檔確實進去了:

curl http://localhost:7700/tasks/21 -H "Authorization: Bearer aSampleMasterKey"
{
  "uid": 21,
  "indexUid": "books_vec",
  "status": "succeeded",
  "type": "documentAdditionOrUpdate",
  "details": {
    "receivedDocuments": 6,
    "indexedDocuments": 6
  }
}

看到 indexedDocuments: 6 就說明 6 條純文本都落庫了。注意:現在這個索引還沒有任何向量,也做不了語義搜索——向量是下一步配了 embedder 之後才自動生成的。

第二步:設定 huggingFace embedder(model + documentTemplate)

Meilisearch 預設只會做關鍵詞檢索,讓它自己轉向量,得先在索引設置裡聲明一個 embedder。embedder(嵌入器)就是一條“向量由誰生成、用什麼模型、拿文檔的哪些字段去嵌入”的設定。這裡用 huggingFace 源——意思是模型跑在你本機,不需要任何 API key,Meilisearch 首次使用時會自動從 HuggingFace 下載模型。這一步不配,後面文檔永遠只有純文本,搜了也白搜。

documentTemplate(文檔模板)用 Liquid 模板語法把每條文檔拼成一段要送去嵌入的文本:{{doc.title}} 取文檔的 title 字段,{{doc.summary}} 取簡介。這裡的 doc 是模板引擎的固定變數名,指“當前正在嵌入的這條文檔”——建索引、傳文檔時都不需要聲明它,Meilisearch 渲染模板時自動把每條文檔注入成 doc;所以 {{doc.xxx}} 裡寫的字段名,跟第一步文檔 JSON 的鍵一一對應(title、author、summary),寫成文檔裡不存在的鍵,整批嵌入就會報錯(第七步的 {{doc.genre}} 就是這麼翻車的)。語義搜索比對的就是這段渲染文本,所以模板裡放哪些字段,決定了能被什麼樣的查詢搜到——只有書名和作者時,模型只能靠對名作的認識來匹配;把簡介拼進去,“按內容搜”才有真實依據。它決定了“模型看到的這段文字是什麼”,直接影響檢索質量。

curl -X PATCH http://localhost:7700/indexes/books_vec/settings/embedders \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"default":{"source":"huggingFace","model":"BAAI/bge-small-zh-v1.5","documentTemplate":"中文书籍,书名《{{doc.title}}》,作者 {{doc.author}},简介 {{doc.summary}}"}}'
{
  "taskUid": 23,
  "indexUid": "books_vec",
  "status": "enqueued",
  "type": "settingsUpdate",
  "enqueuedAt": "2026-10-11T10:05:00.000Z"
}

model 用的還是系列前兩篇那個 BAAI/bge-small-zh-v1.5(中文、輸出 512 維),跟前幾篇的向量完全同一路數,只是生成方從你本地的 Python 換成了 Meilisearch 服務端。bge 系模型架構一致,官方推薦列表裡是 BAAI/bge-base-en-v1.5 這些英文模型,中文模型沒單列,Meilisearch 側實際加載行為以官方文檔和你的運行結果為準。注意這裡不用像 userProvided 那樣聲明 dimensions——huggingFace 源的維度由模型自己決定,配了反而多餘。

驗證,把 embedder 設置讀回來看看。

curl http://localhost:7700/indexes/books_vec/settings/embedders -H "Authorization: Bearer aSampleMasterKey"
{
  "default": {
    "source": "huggingFace",
    "model": "BAAI/bge-small-zh-v1.5",
    "documentTemplate": "中文书籍,书名《{{doc.title}}》,作者 {{doc.author}},简介 {{doc.summary}}"
  }
}

讀到 {"default":{"source":"huggingFace",...}} 說明設定落進去了。embedder 名字是 default,後面搜索時 hybrid.embedder 要跟它一致。如果讀出來是 {},多半是設置任務還沒跑完就查了,把 taskUid 輪詢到 succeeded 再看。這一步提交後 Meilisearch 會在後臺幹活:下載模型、給 6 條文檔逐個嵌入——這正是下一步要盯的事。

順帶說清一個容易想歪的點:語義搜索比對的是 documentTemplate 渲染出來的文本向量,不是“讀了書的內容”。後文查詢“仰望星空,講人類文明命運的科幻鉅作”能命中《三體》,靠的正是模板裡拼進去的 summary——簡介寫了“科幻、人類文明”,模型把查詢向量跟這段簡介對齊。如果模板只放書名和作者,也能靠模型對《三體》這類名作的先驗勉強命中,但那層匹配不穩定,換個冷門書就不靈了。

第三步:盯任務隊列,等模型下載 + 全量自動嵌入完成

設定提交了,但嵌入是異步的。Meilisearch 拿到 embedder 設定後會先下載模型(bge-small 系列權重約 90 多 MB,看網速,可能要幾分鐘),然後批量處理索引裡所有文檔、給每條生成 512 維向量,最後把結果緩存下來——文檔內容不變就不會重算。任務沒跑完就去搜,會搜到“沒有向量的索引”,結果跟普通關鍵詞沒區別。 所以這一步就是盯任務隊列。

輪詢剛才的 taskUid:

curl http://localhost:7700/tasks/23 -H "Authorization: Bearer aSampleMasterKey"
{
  "uid": 23,
  "indexUid": "books_vec",
  "status": "succeeded",
  "type": "settingsUpdate",
  "enqueuedAt": "2026-10-11T10:05:00.000Z",
  "startedAt": "2026-10-11T10:05:01.000Z",
  "finishedAt": "2026-10-11T10:08:32.000Z"
}

驗證就一條:status: "succeeded"。任務從 enqueued → processing → succeeded,中間卡幾分鐘是正常的(首次在下載模型)。要是變成 failed,八成是模板渲染問題——documentTemplate 裡引用了文檔裡不存在的字段,整批嵌入會失敗,報 template_rendering_error,排查入口見Meilisearch documentTemplate 引用缺失字段報 template_rendering_error。想確認向量真生成了,等第四步用搜索來驗證最直觀。

第四步:純文本查詢做語義搜索(不帶 vector,查詢自動嵌入)

這一步是整篇的答案。上一篇的 userProvided 模式,檢索前要先跑 Python 給查詢詞算一個 512 維向量,再塞進 vector 參數發過去;內置 embedder 模式不用了——q 直接傳中文文本,Meilisearch 用同一個模型自動把查詢詞轉成向量,然後跟庫裡每條文檔的向量比對相似度。

curl -X POST http://localhost:7700/indexes/books_vec/search \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"q":"仰望星空,讲人类文明命运的科幻巨作","hybrid":{"embedder":"default","semanticRatio":1.0},"showRankingScore":true}'

hybrid(混合檢索)物件裡 embedder 指名用哪個嵌入器(這裡就是第二步配的 default),semanticRatio(語義佔比)控制關鍵詞與語義結果的比例,1.0 就是隻看語義。showRankingScore: true 讓返回帶上 _rankingScore(相關性分數,v1.8 起語義分併入這個字段,之前的 _semanticScore 已移除)。返回體按官方文檔結構大致長這樣(具體分值以你本地實測為準):

{
  "hits": [
    {
      "id": 1,
      "title": "三体",
      "author": "刘慈欣",
      "_rankingScore": 0.82
    },
    {
      "id": 2,
      "title": "球状闪电",
      "author": "刘慈欣",
      "_rankingScore": 0.71
    }
  ],
  "query": "仰望星空,讲人类文明命运的科幻巨作",
  "processingTimeMs": 3,
  "estimatedTotalHits": 2
}

驗證方式是做一次對照實驗:同一個查詢詞去掉 hybrid 跑普通關鍵詞搜索。

curl -X POST http://localhost:7700/indexes/books_vec/search \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"q":"仰望星空,讲人类文明命运的科幻巨作"}'
{
  "hits": [],
  "query": "仰望星空,讲人类文明命运的科幻巨作",
  "processingTimeMs": 1,
  "estimatedTotalHits": 0
}

關鍵詞搜索一個詞都對不上書名,返回空;語義搜索靠自動嵌入的向量把“講人類文明命運的科幻鉅作”映射到三體——具體來說,是模板裡拼進去的 summary(三體的簡介寫了“科幻、人類文明”)跟查詢描述的內容對上了,模型拿查詢向量跟簡介向量比相似度。這正好回答開頭那個問題:查詢轉向量這步,真不用自己做了。要是返回 400,先看錯誤碼——hybrid.embedder 寫錯名字會報 invalid_search_embedder,hybrid 結構寫錯會報 invalid_search_hybrid_query,排查入口都是這篇(見Meilisearch semanticRatio 越界或 embedder 名寫錯報錯)。

第五步:寫入一條新文檔,等自動嵌入完成後立即再查

第二步配好 embedder 後,自動嵌入不只發生在初始那 6 條文檔上——之後每次新增文檔,Meilisearch 都會在入庫時自動轉向量。這一步驗證寫入路徑:傳一條新書,任務跑完直接搜,新書應該能命中語義查詢。注意看,傳的還是純文本,沒有 _vectors 字段:

curl -X POST http://localhost:7700/indexes/books_vec/documents \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"id":7,"title":"围城","author":"钱钟书","summary":"写婚姻与人生的围城困局,知识分子的困惑与讽刺"}'
{
  "taskUid": 24,
  "indexUid": "books_vec",
  "status": "enqueued",
  "type": "documentAdditionOrUpdate",
  "enqueuedAt": "2026-10-11T10:10:00.000Z"
}

新文檔也要先過一遍嵌入,照舊輪詢任務號:

curl http://localhost:7700/tasks/24 -H "Authorization: Bearer aSampleMasterKey"
{
  "uid": 24,
  "indexUid": "books_vec",
  "status": "succeeded",
  "type": "documentAdditionOrUpdate",
  "details": {
    "receivedDocuments": 1,
    "indexedDocuments": 1
  }
}

驗證:搜一個能映射到《圍城》的語義查詢,它應該進結果並且排前面:

curl -X POST http://localhost:7700/indexes/books_vec/search \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"q":"婚姻是围城,困住人的故事","hybrid":{"embedder":"default","semanticRatio":1.0},"showRankingScore":true}'
{
  "hits": [
    {
      "id": 7,
      "title": "围城",
      "author": "钱钟书",
      "_rankingScore": 0.79
    }
  ],
  "query": "婚姻是围城,困住人的故事",
  "processingTimeMs": 3,
  "estimatedTotalHits": 1
}

hits 裡出現 id: 7 的《圍城》就對了——圍城的 summary 寫的是“寫婚姻與人生的圍城困局”,跟查詢“婚姻是圍城,困住人的故事”說的是同一件事,所以直接命中,這就是把內容寫進模板的效果。這一步跟第三步傳初始 6 條文檔沒有任何區別——都是純文本進去、自動嵌入、立刻可查。寫入路徑(入庫自動轉向量)驗證通過。要是任務沒跑完就去搜,新文檔會搜不到,排查見Meilisearch 添加文檔後立即搜索返回空結果。

第六步:更新一條文檔,等自動重嵌入完成後再次查詢

更新路徑同理:改文檔內容 → Meilisearch 重新給這條文檔生成向量、替換舊向量,內容沒變的文檔不會重算(文檔不變不重算,是自動嵌入的緩存策略)。這一步把第五步新加的 id: 7 從《圍城》改成《邊城》,然後查兩次驗證。

更新就是“同一條 id 再傳一次”,POST /documents 對已存在的 id 是覆蓋更新:

curl -X POST http://localhost:7700/indexes/books_vec/documents \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"id":7,"title":"边城","author":"沈从文","summary":"写湘西边陲小镇的乡土爱情,纯净而忧伤的故事"}'
{
  "taskUid": 25,
  "indexUid": "books_vec",
  "status": "enqueued",
  "type": "documentAdditionOrUpdate",
  "enqueuedAt": "2026-10-11T10:12:00.000Z"
}
curl http://localhost:7700/tasks/25 -H "Authorization: Bearer aSampleMasterKey"
{
  "uid": 25,
  "indexUid": "books_vec",
  "status": "succeeded",
  "type": "documentAdditionOrUpdate",
  "details": {
    "receivedDocuments": 1,
    "indexedDocuments": 1
  }
}

驗證一:搜《邊城》的語義查詢,id: 7 以新書名命中:

curl -X POST http://localhost:7700/indexes/books_vec/search \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"q":"湘西边陲小镇的乡土爱情故事","hybrid":{"embedder":"default","semanticRatio":1.0},"showRankingScore":true}'
{
  "hits": [
    {
      "id": 7,
      "title": "边城",
      "author": "沈从文",
      "_rankingScore": 0.77
    }
  ],
  "query": "湘西边陲小镇的乡土爱情故事",
  "processingTimeMs": 3,
  "estimatedTotalHits": 1
}

驗證二:回搜第五步那個“婚姻是圍城”的查詢,id: 7 不再出現——舊向量已被替換:

curl -X POST http://localhost:7700/indexes/books_vec/search \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"q":"婚姻是围城,困住人的故事","hybrid":{"embedder":"default","semanticRatio":1.0},"showRankingScore":true}'
{
  "hits": [],
  "query": "婚姻是围城,困住人的故事",
  "processingTimeMs": 2,
  "estimatedTotalHits": 0
}

id: 7 從“圍城命中”變成“邊城命中”,就是更新觸發自動重嵌入的直接證據:全程只改了文檔文本,向量一次都沒手動碰過。到這裡,寫入、更新、查詢三條路徑的自動嵌入全部驗證完。

第七步:改 documentTemplate 觸發自動重嵌入,用 render-template 先預演

模板決定模型看到什麼文本,模板寫得差,語義就偏。但改模板有一個好處:不用重新傳文檔。改一下 embedder 設定,Meilisearch 檢測到 documentTemplate 變了,會自動把全部文檔重新嵌入一遍。這一步先介紹一個預演工具,再演示改模板重嵌入。

render-template 接口可以在不改任何設定的情況下,把某條文檔套上模板看渲染結果——模板裡有拼寫錯誤、引用了不存在的字段,在這裡就能提前發現:

curl -X POST http://localhost:7700/render-template \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"template":{"kind":"inlineDocumentTemplate","inline":"中文书籍,书名《{{doc.title}}》,作者 {{doc.author}},简介 {{doc.summary}}"},"input":{"kind":"inlineDocument","inline":{"id":1,"title":"三体","author":"刘慈欣","summary":"仰望星空,写人类文明与三体文明星际博弈的科幻巨作"}}}'
{
  "template": "中文书籍,书名《{{doc.title}}》,作者 {{doc.author}},简介 {{doc.summary}}",
  "rendered": "中文书籍,书名《三体》,作者 刘慈欣,简介 仰望星空,写人类文明与三体文明星际博弈的科幻巨作"
}

rendered 就是模型實際會看到的那段文本。想給模板加點上下文(比如分類信息),改完先預演一遍,確認沒問題再提交設置:

curl -X PATCH http://localhost:7700/indexes/books_vec/settings/embedders \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"default":{"source":"huggingFace","model":"BAAI/bge-small-zh-v1.5","documentTemplate":"中文书籍,书名《{{doc.title}}》,作者 {{doc.author}},类型是 {{doc.genre}}"}}'
{
  "taskUid": 27,
  "indexUid": "books_vec",
  "status": "enqueued",
  "type": "settingsUpdate",
  "enqueuedAt": "2026-10-11T10:15:00.000Z"
}

等等,這裡有個坑:{{doc.genre}} 引用了 genre 字段,但 6 條書里根本沒有這個字段——現在輪詢任務,你會看到它 failed:

curl http://localhost:7700/tasks/27 -H "Authorization: Bearer aSampleMasterKey"
{
  "uid": 27,
  "indexUid": "books_vec",
  "status": "failed",
  "type": "settingsUpdate",
  "error": {
    "message": "error while rendering template: user error: missing field in document: liquid: Unknown index",
    "code": "template_rendering_error",
    "type": "invalid_request",
    "link": "https://docs.meilisearch.com/errors#template_rendering_error"
  }
}

模板引用不存在的字段,整批嵌入直接失敗——這就是第七步為什麼先讓你用 render-template 預演。修法有兩個:把模板改回只引用存在的字段(title、author、summary),或者給文檔補上 genre 字段再重新提交設置。改回去、任務 succeeded 後,重新搜第四步那個查詢,Meilisearch 會用新模板生成的向量重新排序,分值會跟改模板前不一樣——這就是“自動重嵌入”生效的證據。

第八步(進階):加第二個 embedder,一個索引兩套向量

一個索引可以配多個 embedder,各自用不同模型生成一套獨立向量,搜索時用 hybrid.embedder 指定用哪套。這一步加一個官方推薦的多語言模型 sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2(384 維,支持中文),跟 default 並存,然後對比同一查詢在兩家模型下的結果差異。

curl -X PATCH http://localhost:7700/indexes/books_vec/settings/embedders \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"default":{"source":"huggingFace","model":"BAAI/bge-small-zh-v1.5","documentTemplate":"中文书籍,书名《{{doc.title}}》,作者 {{doc.author}},简介 {{doc.summary}}"},"multilingual":{"source":"huggingFace","model":"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2","documentTemplate":"中文书籍,书名《{{doc.title}}》,作者 {{doc.author}},简介 {{doc.summary}}"}}'
{
  "taskUid": 29,
  "indexUid": "books_vec",
  "status": "enqueued",
  "type": "settingsUpdate",
  "enqueuedAt": "2026-10-11T10:20:00.000Z"
}

等任務 succeeded(第二個模型也要下載一次)。注意這個 PATCH 是整份替換 embedder 設定,提交時把兩個 embedder 都寫上,只寫 multilingual 會把 default 頂掉。接著用同一個查詢詞,hybrid.embedder 切到 multilingual 再搜一次:

curl -X POST http://localhost:7700/indexes/books_vec/search \
  -H "Authorization: Bearer aSampleMasterKey" \
  -H "Content-Type: application/json" \
  -d '{"q":"仰望星空,讲人类文明命运的科幻巨作","hybrid":{"embedder":"multilingual","semanticRatio":1.0},"showRankingScore":true}'
{
  "hits": [
    {
      "id": 1,
      "title": "三体",
      "author": "刘慈欣",
      "_rankingScore": 0.78
    },
    {
      "id": 2,
      "title": "球状闪电",
      "author": "刘慈欣",
      "_rankingScore": 0.69
    }
  ],
  "query": "仰望星空,讲人类文明命运的科幻巨作",
  "processingTimeMs": 4,
  "estimatedTotalHits": 2
}

驗證:同一個查詢詞,兩個 embedder 都命中三體、球狀閃電,但 _rankingScore 不同——說明兩套向量都在工作、各算各的。多 embedder 的實際價值在混合場景:比如一個 embedder 管中文文本、另一個管圖片向量(圖片走 userProvided),檢索時按需切換。到這一步,內置 embedder 的自動嵌入鏈路就完整跑通了。

核心概念

下面這張圖是整條調用鏈路的走向:純文本文檔和純文本查詢在兩端進,向量在 Meilisearch 內部自動生成,兩邊都不用你碰向量。

Meilisearch 內置 embedder 自動嵌入調用鏈路架構圖

概念 是什麼 類比
embedder 索引設置裡聲明“向量由誰生成、用什麼模型、嵌入哪些字段”的設定項 快遞櫃上開好的儲物格,聲明瞭誰送貨、貨多長
自動嵌入(auto-embedding) 文檔入庫和查詢檢索時由服務端自動轉向量,客戶端只傳文本 自助餐廳配餐,菜和湯都給你盛好,不用自己下廚
寫入 / 更新自動嵌入 配好 embedder 後,新增或更新的文檔入庫時同樣自動轉向量;文檔內容不變不重算(緩存生效) 補新菜、換舊菜都是後廚自動做,不用你親自下廚
huggingFace(source) 嵌入源的一種:模型跑在本機,無需 API key,首次使用自動下載 家裡自帶的廚房,做飯不用叫外賣
documentTemplate 用 Liquid 語法把文檔拼成嵌入文本的模板,決定模型看到什麼 給模型看的“文檔摘要卡”,只寫重點
render-template 不改設定、先用樣例文檔預演模板渲染結果的接口 下館子前先看選單配圖,避免點錯菜
hybrid / semanticRatio 混合檢索模式:關鍵詞與語義並行檢索後合併排序;semanticRatio 控制兩者比例 查重名再查意思,旋鈕擰到哪頭就聽哪頭

常見問題

template_rendering_error

現象:提交 embedder 設定或改 documentTemplate 後任務變成 failed,error.code 是 template_rendering_error,message 含 “missing field in document: liquid: Unknown index”。

原因:documentTemplate 裡用 {{doc.xxx}} 引用了文檔中不存在的字段,整批嵌入失敗。修法:把模板改回只引用真實存在的字段,或用 {% if doc.xxx %} 包一層守衛。

相關文章:Meilisearch documentTemplate 引用缺失字段報 template_rendering_error

invalid_search_embedder

現象:搜索時返回 400,錯誤碼 invalid_search_embedder。

原因:hybrid.embedder 寫的名字跟索引裡設定的 embedder 對不上——比如設定的是 default 卻寫了 "Default",或者索引里根本沒配過這個 embedder。大小寫和名字都要嚴格一致。

相關文章:Meilisearch semanticRatio 越界或 embedder 名寫錯報錯

配完 embedder 立刻搜索,結果跟普通關鍵詞一樣

現象:第二步配完 embedder 馬上搜,hits 是純關鍵詞結果或空,語義完全沒生效。

原因:嵌入是異步任務,模型下載加全量嵌入還沒跑完你就去搜了。settingsUpdate / documentAddition 任務隊列裡的活沒幹完,索引還沒有向量。輪詢 taskUid 到 succeeded 再搜。

相關文章:Meilisearch 添加文檔後立即搜索返回空結果

下一步

跟手動轉向量的版本對比

這篇的自動嵌入版,跟之前 userProvided 手動轉向量的版本是同一套搜索能力的兩條實現路線。手動版要自己裝 Python、生成向量、處理維度,自動版一行 Python 不用寫——但手動版適合圖片等非文本向量的場景。

從零跑通 Meilisearch 純向量語義搜索

換一套純向量數據庫試試

Meilisearch 是“關鍵詞 + 向量”雙引擎,語義搜索是其中一半。如果你只要向量、不要關鍵詞,可以對比純向量數據庫 Chroma 的玩法——那邊從頭到尾都是向量在說話。

從零跑通 Chroma 向量語義搜索

用 Ollama 當嵌入源

內置 embedder 除了本地 huggingFace 源,還支持 ollama 源(把 url 指向 http://localhost:11434/api/embeddings,模型用 nomic-embed-text 這類)和 openAi / rest 外部源。如果你的機器上已經跑著 Ollama,本地模型這一路可以先接上。

從零跑通 Ollama 本地對話模型

卡在哪一步了?

從零跑通的過程中撞到報錯?把命令和輸出記下來,幫助下一個剛起步的人。

貢獻一條解法