跳到主要内容

搜索和索引

加入版本: @mastra/core@1.1.0

搜索功能让 Agent 能在已建立索引的 Workspace 文件中查找相关内容。当 Agent 需要回答问题或查找信息时,可以搜索索引内容,而不必读取每个文件。

工作原理
工作原理的直接链接

Workspace 搜索分为索引和查询两个阶段。

索引
索引的直接链接

内容必须先建立索引,之后才能搜索。为文档建立索引时:

  • 内容会被 token 化(拆分为可搜索的词项)
  • 对于 BM25:计算词频和文档统计信息
  • 对于向量:使用 embedder 函数将内容转换为 embedding,并存储在 Vector Store 中

每个已建立索引的文档都有:

  • id - 唯一标识符(通常为文件路径)
  • content - 文本内容
  • metadata - 随文档存储的可选键值数据

查询
查询的直接链接

搜索时:

  1. 使用与索引时相同的 token 化/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 查找语义相似的内容,需要 Vector Store 和 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。Embedder 函数接受数组,并按相同顺序返回 embedding 数组,同时包含两个额外属性:

  • batch: true:标记函数支持批处理。如果没有此属性,Mastra 会每次向其传入一段文本。
  • maxBatchSize:Provider 单次调用接受的最大数组长度。Mastra 会将更大的请求拆分为该大小的 chunk,并并行发送。请将其设为 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 将其作为 metadata 读取,绝不会传给 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。还可以为每个文档传入 metadata。

// 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 响应)
  • 希望在建立索引前预处理内容或将内容拆分为 chunk
  • 需要向文档添加自定义 metadata

自动建立索引
自动建立索引的直接链接

配置 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 error”、“config.yaml”
vector概念性查询、自然语言“如何处理用户身份验证”、“错误处理的最佳实践”
hybrid通用搜索、未知查询类型大多数 Agent 使用场景

Agent Tool
Agent Tool的直接链接

在 Workspace 上配置搜索后,Agent 会获得用于搜索内容和建立索引的 Tool。有关详情,请参阅 Workspace 类 Reference