跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

Search 搜索和索引

搜索为 agents 提供一种快速查找相关内容的方式,而无需读取每个文件。它与直接文件系统访问和 mounts配合使用,但会将可搜索内容存储在单独的索引中。

查询读取该索引,而不是实时文件系统。这种分离也让你可以索引来自数据库、API 或其他应用来源的内容。

快速开始
快速开始的直接链接

启用 BM25 关键词搜索,向索引添加一个文档,并搜索它:

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

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

await workspace.index('/docs/guide.md', 'Reset passwords from the account settings page.')

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

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

workspace.index() 的第一个参数是搜索结果中返回的文档 ID。在此示例中它看起来像文件路径,但 index() 不会读取或创建该文件。

配置搜索还会为 agents 提供搜索和索引工具。参见 Agent 工具以控制它们。

选择搜索模式
选择搜索模式的直接链接

Mastra 支持三种搜索模式:

模式最适合示例查询
bm25精确术语、技术查询和代码"useState hook"、"404 error"、"config.yaml"
vector概念和自然语言问题"how to handle user authentication"
hybrid精确查询和概念查询的混合大多数 agent 搜索用例

如果不向 workspace.search() 传入模式,当同时配置了 BM25 和向量搜索时,Mastra 会使用混合搜索。否则,它会使用已配置的模式。

BM25 根据词频和文档长度对文档评分。它不需要外部服务或嵌入模型。传入 bm25: true 以使用快速开始中显示的默认值。

要调整词频饱和度和文档长度归一化,传入 k1 和 b:

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

向量搜索使用嵌入来查找语义相似的内容。配置一个向量存储,以及一个每次嵌入一个字符串的函数:

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

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

批量嵌入
批量嵌入的直接链接

单文本嵌入器会为每个文档单独调用一次提供商。对于大型索引,使用提供商的批量 API 在一次调用中嵌入多个文档。

批量嵌入器必须:

  • 接受字符串数组。
  • 按相同顺序为每个字符串返回一个嵌入。
  • 具有 batch: true 属性,以便 Mastra 在 runtime 检测到它。
  • 可选地设置 maxBatchSize 为提供商接受的最大数组长度。

Mastra 会根据 maxBatchSize 拆分更大的索引集,并可以并发处理结果分组。将此值设置为提供商文档中给出的限制:

提供商最大输入数
OpenAI2048
Cohere96
Voyage128

如果省略 maxBatchSize,Mastra 会使用其内部批量大小。

用批量嵌入器替换单文本嵌入器:

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

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

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

Object.assign() 会将 batch 和 maxBatchSize 作为属性添加到函数上。Mastra 将它们作为元数据读取,不会传递给提供商。具有 (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 后跟 _search。当需要稳定或共享的名称时,设置 searchIndexName:

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

名称必须以字母或 _ 开头,只能包含字母、数字或 _,并且最多包含 63 个字符。

索引内容
索引内容的直接链接

使用 workspace.index() 将提供的文本添加到搜索索引。Mastra 会为 BM25 搜索对内容进行分词,并在配置向量搜索时生成嵌入。

文档 ID 无需在文件系统中存在。当结果需要过滤或更多上下文时,添加元数据:

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

await workspace.index('/docs/api.md', apiDocContent, {
metadata: {
category: 'api',
version: '2.0',
},
})

手动索引非常适合数据库记录和 API 响应,或者当你需要自行预处理、分块或标注内容时。

保持文件和索引同步
保持文件和索引同步的直接链接

文件系统和索引的变更相互独立。写入、编辑或删除文件不会更新其已索引内容,workspace.index() 也不会读取或更改文件。

要写入文件并使其可搜索,请执行这两个操作:

const path = 'notes/launch.md'
const content = '# Launch notes\n\nShip the new dashboard on Friday.'
const filesystem = workspace.filesystem

if (!filesystem) {
throw new Error('This operation requires a static filesystem')
}

await filesystem.writeFile(path, content)
await workspace.index(path, content)

编辑文件后,请再次索引其完整的更新内容。删除文件会保留其已索引文档,Mastra 目前不提供用于移除单个已索引文档的公共方法。当 agent 必须在无需维护单独索引的情况下搜索当前文件系统状态时,请改用 grep 文件工具。

搜索内容
搜索内容的直接链接

使用 workspace.search() 返回按相关性排序的文档:

const results = await workspace.search('authentication flow', {
topK: 10,
mode: 'hybrid',
vectorWeight: 0.5,
})
选项描述
topK最大结果数。直接 API 调用默认 10。Agent 搜索工具默认 5。
mode搜索模式:'bm25'、'vector' 或 'hybrid'。默认为 workspace 配置的最佳可用模式。
minScore移除低于此分数的结果。分数范围取决于模式、向量提供商和已索引内容。
vectorWeight在混合模式下,赋予向量分数的权重。0 表示向量分数权重为零,1 表示 BM25 分数权重为零,0.5 表示两者权重相等。
filter向量检索使用的向量存储元数据过滤器。在混合模式下,它不会过滤仅由 BM25 返回的结果。语法取决于配置的向量存储。

每个结果包含匹配内容及其分数:

interface SearchResult {
id: string
content: string
score: number
lineRange?: {
start: number
end: number
}
metadata?: Record<string, unknown>
scoreDetails?: {
vector?: number
bm25?: number
}
}

分数范围不可互换:

  • 独立 BM25 搜索返回原始 BM25 分数,不限于 0 到 1。
  • 向量分数的含义取决于配置的向量存储。
  • 混合搜索会为加权计算对 BM25 进行 min-max 归一化,但使用向量提供商的原始分数。组合分数不会被钳制到 0 到 1。
  • scoreDetails 保留原始组件分数,包括未归一化的 BM25 分数。
  • 针对活动模式、向量提供商和已索引内容调整 minScore,而不是在不同索引之间复用同一阈值。

Agent 工具
Agent 工具的直接链接

配置搜索后,agents 会收到 mastra_workspace_search 和 mastra_workspace_index。使用 WORKSPACE_TOOLS.SEARCH 分别配置它们。

当 agent 应搜索现有内容而不更改索引时,禁用索引:

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

const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
tools: {
[WORKSPACE_TOOLS.SEARCH.INDEX]: {
enabled: false,
},
},
})

索引工具接受与 workspace.index() 相同的提供的 path、content 和可选元数据。由于它可能触发嵌入调用和向量存储写入,当 agents 不应在未经审查的情况下更改索引时,要求审批:

src/mastra/workspaces.ts
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
tools: {
[WORKSPACE_TOOLS.SEARCH.INDEX]: {
requireApproval: true,
},
},
})

requireReadBeforeWrite 适用于文件系统写工具,而不是索引变更。当静态文件系统为只读时,Mastra 也会排除索引工具,即使索引不会写入该文件系统。

关于完整工具列表,请参阅搜索工具参考;关于共享设置,请参阅工具配置参考。

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

使用 autoIndexPaths 在应用启动期间从静态文件系统内容构建索引。

注意

autoIndexPaths 作为 workspace.init() 的一部分运行。将 workspace 传给 new Mastra({ workspace }) 只会注册它,不会初始化它或构建索引。除非另一个 runtime(例如 AgentController)负责 workspace 初始化,否则请调用并 await workspace.init()。

await workspace.init()

当你的应用负责初始化时,请在启动期间、开始服务请求之前调用一次。

在正常的 Mastra 应用中,先构造 Mastra,以便其 logger 可供 workspace 使用。然后在模块启动时初始化 workspace:

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

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

export const mastra = new Mastra({ workspace })

await workspace.init()

顶层 await 会让模块启动保持挂起,直到初始化完成。当 init() resolve 时,Mastra 已尝试读取并索引每个匹配的文件。

每个 autoIndexPaths 条目可以是文件、目录或 glob 模式。递归目录和 glob 遍历限制为十层:

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

export const mastra = new Mastra({ workspace })

await workspace.init()

自动索引支持静态 filesystem 或 mounts。请在路径中包含挂载前缀,例如 /docs/**/*.md。它不支持文件系统解析器,因为 workspace 初始化时尚未选择任何提供商。请改为手动索引解析器支持的内容。

初始化会执行一次扫描,不会监视后续更改。它会在索引前将大型文件拆分为块,并在向量模式下生成嵌入。

保持配置的路径尽量窄,并避免依赖或输出目录。不要包含二进制文件树。对于大型向量索引,请使用批量嵌入器。

再次调用 workspace.init() 会再次扫描路径,并在重建之前清除进程本地的 BM25 索引。不要从 agent 调用、工具、workflow 步骤或请求处理器中调用它。

持久化向量索引的行为不同。自动索引会替换它读取的文件,但可能保留在后续扫描之前已删除文件的文档。当向量需要在进程重启后继续存在时,请使用稳定的 workspace id 或 searchIndexName,并围绕向量存储的持久化行为规划清理。