跳至主要內容

搜尋與索引

新增於: @mastra/core@1.1.0

搜尋功能可讓 Agent 在已建立索引的 Workspace 檔案中找出相關內容。Agent 需要回答問題或尋找資訊時,可以搜尋索引內容,不必讀取每個檔案。

運作方式
「運作方式」的直接連結

Workspace 搜尋分成兩個階段:建立索引與查詢。

建立索引
「建立索引」的直接連結

內容必須先建立索引才能搜尋。為文件建立索引時:

  • 內容會被 Tokenize(拆分成可搜尋的詞彙)
  • BM25:計算詞頻與文件統計資料
  • 向量:使用 Embedder 函式將內容嵌入,並儲存在向量儲存區

每份已建立索引的文件包含:

  • id - 唯一識別碼(通常是檔案路徑)
  • content - 文字內容
  • metadata - 與文件一同儲存的選用鍵值資料

查詢
「查詢」的直接連結

搜尋時:

  1. 查詢會使用與建立索引時相同的 Tokenization/Embedding 方式處理
  2. 依文件與查詢的相關性評分
  3. 依分數排序結果,並連同相符內容傳回

Workspace 支援三種搜尋模式:BM25 關鍵字搜尋、向量語意搜尋,以及結合兩者的混合搜尋。

BM25 會依詞頻與文件長度為文件評分,適合精確比對與特定術語。

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
})

若要自訂 BM25 參數(k1 是詞頻飽和度,b 是文件長度正規化):

src/mastra/workspaces.ts
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: {
k1: 1.5,
b: 0.75,
},
})

向量搜尋使用 Embedding 尋找語意相似的內容,需要向量儲存區與 Embedder 函式。

src/mastra/workspaces.ts
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'
import { PineconeVector } from '@mastra/pinecone'
import { embed } from 'ai'
import { openai } from '@ai-sdk/openai'

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
vectorStore: new PineconeVector({
apiKey: process.env.PINECONE_API_KEY,
index: 'workspace-index',
}),
embedder: async (text: string) => {
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: text,
})
return embedding
},
})

批次 Embedding
「批次 Embedding」的直接連結

上述 Embedder 每次接收一段文字。為含數百個檔案的 Workspace 建立索引時,會呼叫 Provider 數百次,既緩慢又昂貴。

當 Provider 支援批次處理時(例如 OpenAI 的 embedMany),請傳入接收文字陣列並能在單次呼叫中取回多個 Embedding 的 Embedder。若要選用此功能,請在函式上設定 batch: true 屬性。Mastra 會在執行階段檢查該屬性,並切換至批次路徑。

以下範例將單一文字 Embedder 改為批次 Embedder。此函式接收陣列,並以相同順序傳回 Embedding 陣列,另帶有兩個額外屬性:

  • batch: true:標示函式支援批次處理。若缺少此屬性,Mastra 會逐段文字呼叫函式。
  • maxBatchSize:Provider 單次呼叫可接受的最大陣列。Mastra 會依此大小分割較大的請求並平行傳送。請設為 Provider 文件中的限制(例如 OpenAI 為 2048、Cohere 為 96、Voyage 為 128)。省略時,所有待處理文字會在單一請求中送出。
src/mastra/workspaces.ts
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'
import { PineconeVector } from '@mastra/pinecone'
import { embedMany } from 'ai'
import { openai } from '@ai-sdk/openai'

const model = openai.embedding('text-embedding-3-small')

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
vectorStore: new PineconeVector({
apiKey: process.env.PINECONE_API_KEY,
index: 'workspace-index',
}),
embedder: Object.assign(
async (texts: string[]) => {
const { embeddings } = await embedMany({ model, values: texts })
return embeddings
},
{ batch: true as const, maxBatchSize: 2048 },
),
})

Object.assign 會將 batchmaxBatchSize 屬性加入 Embedder 函式。Mastra 將它們讀作中繼資料,絕不會傳給 Provider。

單一文字 Embedder 仍可使用。函式簽章 (text: string) => Promise<number[]> 並未變更,因此現有程式碼不必修改即可繼續執行。

同時設定 BM25 與向量搜尋,即可啟用結合關鍵字比對與語意理解的混合模式。

src/mastra/workspaces.ts
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
vectorStore: pineconeVector,
embedder: embedderFn,
})

自訂索引名稱
「自訂索引名稱」的直接連結

搜尋索引名稱預設衍生自 Workspace ID。若要設定自訂名稱,請使用 searchIndexName

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
searchIndexName: 'my_workspace_vectors',
})

索引名稱必須是有效的 SQL 識別碼:以字母或底線開頭,只能包含字母、數字或底線,且最多 63 個字元。

建立內容索引
「建立內容索引」的直接連結

手動建立索引
「手動建立索引」的直接連結

使用 workspace.index(),以程式方式將內容加入搜尋索引。檔案路徑會成為文件 ID,也可以為每份文件傳入中繼資料。

// Basic indexing
await workspace.index('/docs/guide.md', 'Content of the guide...')

// Index with metadata for filtering or context
await workspace.index('/docs/api.md', apiDocContent, {
metadata: {
category: 'api',
version: '2.0',
},
})

手動建立索引適用於:

  • 要建立索引的內容並非來自檔案(例如資料庫記錄、API 回應)
  • 想在建立索引前預先處理或分割內容
  • 需要為文件加入自訂中繼資料

自動建立索引
「自動建立索引」的直接連結

設定 autoIndexPaths,即可在 Workspace 初始化時自動為檔案建立索引。每個項目可以是目錄路徑(遞迴建立索引),或用於選擇性建立索引的 glob 模式。

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
autoIndexPaths: ['docs', 'support/faq'],
})

await workspace.init()

呼叫 init() 時,系統會讀取所有相符檔案並建立搜尋索引。檔案路徑會成為文件 ID。

Glob 模式可讓你為特定檔案類型建立索引:

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
autoIndexPaths: ['docs/**/*.md', 'support/**/*.txt'],
})

搜尋
「搜尋」的直接連結

使用 workspace.search() 尋找相關內容。結果會依相關性分數排序。

const results = await workspace.search('password reset')

for (const result of results) {
console.log(`${result.id}: ${result.score}`)
console.log(result.content)
}

搜尋選項
「搜尋選項」的直接連結

你可以使用選項自訂搜尋行為:

const results = await workspace.search('authentication flow', {
topK: 10,
mode: 'hybrid',
minScore: 0.5,
vectorWeight: 0.5,
})
選項說明
topK要傳回的結果數量上限。預設:5
mode搜尋模式:'bm25''vector''hybrid'。預設會依設定選用最佳的可用模式。
minScore篩除低於此分數門檻(0–1)的結果。
vectorWeight在混合模式中,向量分數相對於 BM25 的權重。0 = 全部 BM25,1 = 全部向量,0.5 = 兩者相等。

搜尋結果
「搜尋結果」的直接連結

每筆結果包含:

interface SearchResult {
id: string // Document ID (typically file path)
content: string // The matching content
score: number // Relevance score (0-1)
lineRange?: {
// Lines where the match was found
start: number
end: number
}
metadata?: Record<string, unknown> // Metadata stored with the document
scoreDetails?: {
// Score breakdown (hybrid mode only)
vector?: number
bm25?: number
}
}

理解分數:

  • 分數範圍為 0 到 1,其中 1 代表完全相符
  • BM25 分數會依結果集中的最佳相符項目正規化
  • 向量分數代表查詢與文件 Embedding 之間的餘弦相似度
  • 在混合模式中,系統會使用 vectorWeight 參數合併分數

各模式的適用時機
「各模式的適用時機」的直接連結

模式最適合查詢範例
bm25精確詞彙、技術查詢、程式碼"useState Hook"、"404 錯誤"、"config.yaml"
vector概念式查詢、自然語言"如何處理使用者驗證"、"錯誤處理的最佳實務"
hybrid一般搜尋、未知查詢類型多數 Agent 使用情境

Agent Tool
「Agent Tool」的直接連結

在 Workspace 上設定搜尋後,Agent 會取得搜尋內容及建立內容索引的 Tool。詳情請參閱 Workspace 類別參考