Search 搜索和索引
搜索为 agents 提供一种快速查找相关内容的方式,而无需读取每个文件。它与直接文件系统访问和 mounts配合使用,但会将可搜索内容存储在单独的索引中。
查询读取该索引,而不是实时文件系统。这种分离也让你可以索引来自数据库、API 或其他应用来源的内容。
快速开始快速开始的直接链接
启用 BM25 关键词搜索,向索引添加一个文档,并搜索它:
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 关键词搜索的直接链接
BM25 根据词频和文档长度对文档评分。它不需要外部服务或嵌入模型。传入 bm25: true 以使用快速开始中显示的默认值。
要调整词频饱和度和文档长度归一化,传入 k1 和 b:
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: {
k1: 1.5,
b: 0.75,
},
})
向量搜索向量搜索的直接链接
向量搜索使用嵌入来查找语义相似的内容。配置一个向量存储,以及一个每次嵌入一个字符串的函数:
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 拆分更大的索引集,并可以并发处理结果分组。将此值设置为提供商文档中给出的限制:
| 提供商 | 最大输入数 |
|---|---|
| OpenAI | 2048 |
| Cohere | 96 |
| Voyage | 128 |
如果省略 maxBatchSize,Mastra 会使用其内部批量大小。
用批量嵌入器替换单文本嵌入器:
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 和向量搜索以组合关键词和语义结果:
const workspace = new Workspace({
filesystem: new LocalFilesystem({ basePath: './workspace' }),
bm25: true,
vectorStore: pineconeVector,
embedder: embedderFn,
})
自定义索引名称自定义索引名称的直接链接
向量索引名称默认是经清理的 workspace ID 后跟 _search。当需要稳定或共享的名称时,设置 searchIndexName:
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 应搜索现有内容而不更改索引时,禁用索引:
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 不应在未经审查的情况下更改索引时,要求审批:
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:
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 遍历限制为十层:
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,并围绕向量存储的持久化行为规划清理。