跳至主要內容

執行實驗

新增於: @mastra/core@1.4.0

實驗會將數據集中的每個項目傳送給目標(Agent、Workflow 或評分器),然後選擇性地為輸出評分。如要評估 LLM 評審本身,請使用評分器作為目標。結果預設會保存至儲存空間,讓你比較使用不同提示、模型或程式碼變更的執行結果。

基本實驗
基本實驗 的直接連結

使用目標和評分器呼叫 startExperiment()

src/mastra/experiments/basic.ts
import { mastra } from '../index'

const dataset = await mastra.datasets.get({ id: 'translation-dataset-id' })

const summary = await dataset.startExperiment({
name: 'gpt-5.1-baseline',
targetType: 'agent',
targetId: 'translation-agent',
scorers: ['accuracy', 'fluency'],
})

console.log(summary.status) // 'completed' | 'failed'
console.log(summary.succeededCount) // number of items that ran successfully
console.log(summary.failedCount) // number of items that failed

startExperiment() 會阻塞直至所有項目完成。如要以發出後不作等待的方式執行,請參閱非同步實驗

Studio
Studio 的直接連結

你亦可在 Studio 執行實驗。新增數據集項目後,開啟該項目並選擇 Run Experiment,然後設定目標、評分器及選項。

執行實驗後,Experiments 分頁會顯示該數據集的所有執行記錄(包括狀態、數量和時間戳記)。選擇實驗即可查看各項目的結果、分數和執行 Trace。

Experiments 分頁選擇 Compare,再選取兩個或以上的實驗,即可並排比較其分數和結果。

實驗目標
實驗目標 的直接連結

你可以將實驗指向已註冊的 Agent、Workflow 或評分器。

已註冊的 Agent
已註冊的 Agent 的直接連結

指向已在 Mastra 執行個體註冊的 Agent:

src/mastra/experiments/agent-target.ts
const summary = await dataset.startExperiment({
name: 'agent-v2-eval',
targetType: 'agent',
targetId: 'translation-agent',
scorers: ['accuracy'],
})

每個項目的 input 都會直接傳入 agent.generate(),因此必須是 stringstring[]CoreMessage[]

已啟用記憶的 Agent
已啟用記憶的 Agent 的直接連結

當目標 Agent 有自己的記憶,而請求上下文帶有資源 ID(MASTRA_RESOURCE_ID_KEY,可由驗證中介軟件、實驗或項目的 requestContext,或 Studio Run Experiment 表單設定)時,實驗執行器會為每個項目注入新的記憶對話串。請求上下文中的資源 ID 表示「以此資源的身分執行」:每個項目的對話會以該資源下的對話串形式保存,而重試的項目每次嘗試都會使用新的對話串,避免較早失敗嘗試的內容滲入重試上下文。

注入的對話串會加上標籤,讓你能將其對應至執行記錄:對話串 metadata 會包含 experimentId,以及以 experimentItemId 表示的數據集項目 ID。系統不會為這些對話串產生標題。

由於對話串屬於呼叫者的資源,資源範圍的記憶功能會在執行期間讀取和寫入該資源的狀態:

  • 資源範圍的工作記憶更新會保存至資源,而同一次執行中較後的項目會看到較早項目作出的更新。
  • 資源範圍的語意回憶可向實驗呈現該資源過往的對話,而實驗的對話記錄亦可在該資源其後的對話中被回憶。

如要根據真實使用者累積的上下文評估 Agent,這項功能便很有用。如不希望實驗執行過程觸及真實使用者狀態,請改用專用的評估資源 ID 執行實驗。

以下情況會略過對話串注入:

  • 如果請求上下文亦設定了 MASTRA_THREAD_ID_KEY,執行器會直接使用該對話串,因此每個項目(包括重試)都會共用同一段對話。
  • 如果 Agent 沒有記憶,或請求上下文沒有資源 ID,執行便不會使用記憶,也不會保存任何內容。

已註冊的 Workflow
已註冊的 Workflow 的直接連結

指向已在 Mastra 執行個體註冊的 Workflow:

src/mastra/experiments/workflow-target.ts
const summary = await dataset.startExperiment({
name: 'workflow-eval',
targetType: 'workflow',
targetId: 'translation-workflow',
scorers: ['accuracy'],
})

Workflow 會接收每個項目的 input 作為觸發資料。

已註冊的評分器
已註冊的評分器 的直接連結

指向評分器,以根據基準真值評估 LLM 評審:

src/mastra/experiments/scorer-target.ts
const summary = await dataset.startExperiment({
name: 'judge-accuracy-eval',
targetType: 'scorer',
targetId: 'accuracy',
})

評分器會接收每個項目的 inputgroundTruth。隨着底層模型改變,基於 LLM 的評審可能會隨時間出現偏移,因此定期根據已知正確的標籤重新校準非常重要。數據集可提供穩定的基準,以偵測這種偏移。

評分結果
評分結果 的直接連結

每個項目的目標執行完畢後,評分器會自動執行。請傳入評分器執行個體或已註冊的評分器 ID:

// Reference scorers registered on the Mastra instance
const summary = await dataset.startExperiment({
name: 'with-registered-scorers',
targetType: 'agent',
targetId: 'translation-agent',
scorers: ['accuracy', 'fluency'],
})

每個項目的結果都包含各評分器的分數:

for (const item of summary.results) {
console.log(item.itemId, item.output)
for (const score of item.scores) {
console.log(` ${score.scorerName}: ${score.score}${score.reason}`)
}
}

請瀏覽評分器概覽,了解可用及自訂評分器的詳細資料。

控制每次執行的保存設定
控制每次執行的保存設定 的直接連結

使用 persistence 略過特定執行的儲存寫入操作。你可以分別停用實驗記錄和評分記錄:

src/mastra/experiments/no-persistence.ts
const summary = await dataset.startExperiment({
targetType: 'agent',
targetId: 'translation-agent',
scorers: ['accuracy'],
persistence: {
experiments: 'none',
scores: 'none',
},
})

目標和評分器仍會執行,而 startExperiment() 仍會在 summary 中傳回項目結果及分數。這些設定互相獨立。例如,只設定 scores: 'none',即可保存實驗及其項目結果,而不建立評分記錄。

省略的設定預設為 'default',即保留標準儲存行為。這項政策只控制該次執行所建立的實驗及評分記錄,不會停用目標所使用的儲存功能,例如 Agent 記憶、向量、可觀測性或自訂 Tool 儲存空間。

startExperimentAsync() 使用 experiments: 'none' 執行時,不會保存實驗記錄、進度更新或項目結果。評分保存仍由 persistence.scores 獨立控制。如果沒有實驗事件觀察器,該次執行會採用發出後不作等待的方式,而實驗 API 無法報告執行成功或失敗。

呼叫者需要取得傳回摘要時,請使用同步的 startExperiment()。實驗事件觀察器可以接收生命週期事件及終結摘要。

觀察實驗事件
觀察實驗事件 的直接連結

使用 onEvent,在實驗執行期間接收已版本化且可安全轉換為 JSON 的生命週期事件。這適用於 startExperiment()startExperimentAsync()runExperiment()

src/mastra/experiments/observe.ts
import type { ExperimentEvent } from '@mastra/core/datasets'

const events: ExperimentEvent[] = []

await dataset.startExperimentAsync({
task: async ({ input }) => processItem(input),
persistence: { experiments: 'none' },
onEvent: async event => {
events.push(event)
await publishEvent(event)
},
})

觀察器會接收以下事件類型:

  • experiment.run.started:識別執行記錄、目標、已解析的數據集版本及項目數量。
  • experiment.item.completed:報告評分後已提交的項目結果,包括分數、錯誤、重試次數、Tool mock 詳細資料及穩定的項目識別資料。
  • experiment.run.finished:報告最終結果及摘要計數器。

Mastra 會等待每次觀察器呼叫完成,才傳送下一個事件。這種序列化傳送會施加背壓,並確保事件的 sequence 值與傳送次序一致,而項目仍可並行執行。

如果觀察器拋出錯誤或拒絕,Mastra 會中止餘下的執行,並以 MastraError 拒絕 runExperiment(),其 idEXPERIMENT_EVENT_OBSERVER_FAILED。系統不會透過失敗的觀察器傳送終結事件。對於 startExperimentAsync(),分離式觀察器失敗時該方法已經傳回,因此如呼叫者需要直接錯誤報告,請在觀察器內處理傳送失敗。

Mastra 會等待 experiment.run.finished 事件完成,才保存實驗的最終狀態。停用實驗保存時,應將該事件視為具權威性的終結訊號,但不要將它用作儲存空間的寫入後讀取訊號。

匯出的事件類型包括 ExperimentEventExperimentRunStartedEventExperimentItemCompletedEventExperimentRunFinishedEvent。讀取事件專屬屬性前,請使用可辨識聯合的 type 欄位收窄事件類型。

Tool mock
Tool mock 的直接連結

當實驗執行會呼叫具副作用 Tool 的 Agent 時,可將靜態 Tool mock 附加至個別數據集項目,使執行結果具確定性。實驗期間,模擬的 Tool 會傳回已宣告的輸出,而不會實際執行。項目中沒有 mock 的 Tool 預設會即時執行。

Mock 儲存在數據集項目上,因此會跟隨資料列進行版本管理,並與測試案例一同移轉。每個 mock 會宣告 Tool 名稱、預期引數及要傳回的輸出:

src/mastra/experiments/tool-mocks.ts
await dataset.addItem({
input: 'What is the weather in Seattle?',
toolMocks: [
{
toolName: 'getWeather',
args: { city: 'Seattle' },
output: { temperature: 60, conditions: 'rainy' },
},
],
})

Tool mock 只支援 agent 目標。

封鎖未宣告的 Tool
封鎖未宣告的 Tool 的直接連結

在實驗上設定 unmockedToolPolicy: 'deny',即可封鎖所有沒有 mock 的 Tool 呼叫。當即時呼叫可能造成副作用時,這項設定便很有用:

const summary = await dataset.startExperiment({
targetType: 'agent',
targetId: 'weather-agent',
unmockedToolPolicy: 'deny',
})

預設政策為 'allow'。你可以在個別已儲存或內嵌項目上覆寫實驗政策:

await dataset.addItem({
input: 'What is the weather in Seattle?',
unmockedToolPolicy: 'allow',
})

項目值的優先次序高於實驗值。被拒絕的呼叫會在 Tool 執行前以 TOOL_MOCK_NOT_DECLARED 失敗。該失敗不會重試,也不會加入 liveCalls

配對與消耗
配對與消耗 的直接連結

引數會嚴格配對:物件鍵的次序會被忽略、陣列次序則會影響結果,而且不會進行類型強制轉換。只有當 Agent 呼叫 Tool 時使用的引數與 mock 的 args 深度相等,該 mock 才會被採用。

當項目為相同 Tool 和引數宣告多個 mock 時,這些 mock 會按次序消耗:第一次呼叫取得第一個 mock,下一次呼叫取得第二個,如此類推。系統會按每個 (toolName, args) 群組追蹤次序,不同引數之間則互相獨立。

配對模式
配對模式 的直接連結

每個 mock 預設會嚴格根據其 args 配對。設定 matchArgs: 'ignore' 即可只根據 Tool 名稱配對;系統不會比較 mock 的 args,並會提供該 Tool 下一個尚未消耗的 mock,而不論 Agent 如何呼叫:

const subAgentMock = {
toolName: 'agent-balanceAgent',
args: { prompt: 'look up the balance for YJ' },
output: { text: "YJ's balance is $100." },
matchArgs: 'ignore',
}

當 Tool 引數雜亂或由模型產生時,這項設定便很有用。最常見的情況是模擬 子 Agent 的回應:委派的子 Agent 會以 agent-<name> Tool 的形式提供給上層,而其引數包括由 LLM 撰寫的 prompt 及在執行階段注入的欄位。模擬 agent-<name> 會傳回預設回應,取代執行子 Agent 及其內部 Tool。從 Trace 建立 mock 時,系統會自動以 matchArgs: 'ignore' 衍生子 Agent 委派呼叫。你可以將其改為 'strict',以固定確切引數。

失敗
失敗 的直接連結

Tool 呼叫違反 mock 設定時,項目便會失敗:

  • TOOL_MOCK_MISMATCH:呼叫 Tool 時使用的引數與所有 mock 均不配對。
  • TOOL_MOCK_EXHAUSTED:所有配對的 mock 均已消耗。
  • TOOL_MOCK_NOT_DECLARED:Tool 沒有 mock,而生效的 unmockedToolPolicy'deny'

出現以上任何失敗時,Agent 執行會立即中止,因此模型無法繼續呼叫任何其他 Tool,包括原本會即時執行且未模擬的具副作用 Tool。這些失敗具確定性,因此不會重試。已宣告但從未使用的 mock 不會令項目失敗,而會報告為尚未消耗。

Mock 攔截生效期間,Agent 的 Tool 會循序執行,因此重複的 (toolName, args) mock 會按 Provider 的呼叫次序消耗。項目宣告了 mock,或其生效的 unmockedToolPolicy'deny' 時,攔截便會生效。

診斷
診斷 的直接連結

每個項目結果都帶有 toolMockReport,說明該次執行如何處理項目的 mock:

for (const item of summary.results) {
const report = item.toolMockReport
if (!report) continue

console.log(report.served) // mocks matched and returned
console.log(report.unconsumed) // mocks declared but never used
console.log(report.liveCalls) // undeclared tools allowed to run live
console.log(report.failure) // the first deterministic mock failure, if any
}

Studio 中,你可以編輯數據集項目,以 JSON 陣列編寫 Tool mock,並開啟實驗結果查看同一份報告。

限制
限制 的直接連結

  • 模擬呼叫不會產生 Tool span。 模擬呼叫會在 Tool 執行前傳回輸出,因此不會建立 Tool span。由已儲存 Trace 支援的軌跡評分器可能因此無法看到模擬的 Tool 呼叫。回退至 Agent 訊息輸出的軌跡擷取仍能看到這些呼叫,因此軌跡評分可能會因可觀測性設定而有所不同。
  • 儲存支援。 LibSQL、PostgreSQL、MongoDB 和 Spanner 適配器均可保存 Tool mock 和 Tool mock 報告。MySQL 適配器不支援這些內容,並會拒絕帶有 Tool mock 或 Tool mock 報告的寫入操作。所有數據集儲存適配器均會保存 unmockedToolPolicy

非同步實驗
非同步實驗 的直接連結

startExperiment() 會阻塞直至每個項目完成。對於需長時間執行的數據集,請使用 startExperimentAsync() 在背景啟動實驗:

src/mastra/experiments/async.ts
const { experimentId, status } = await dataset.startExperimentAsync({
name: 'large-dataset-run',
targetType: 'agent',
targetId: 'translation-agent',
scorers: ['accuracy'],
})

console.log(experimentId) // UUID
console.log(status) // 'pending'

使用 getExperiment() 輪詢完成狀態:

let experiment = await dataset.getExperiment({ experimentId })

while (experiment.status === 'pending' || experiment.status === 'running') {
await new Promise(resolve => setTimeout(resolve, 5000))
experiment = await dataset.getExperiment({ experimentId })
}

console.log(experiment.status) // 'completed' | 'failed'

設定選項
設定選項 的直接連結

並行處理
並行處理 的直接連結

控制可並行執行的項目數量(預設:5):

const summary = await dataset.startExperiment({
targetType: 'agent',
targetId: 'translation-agent',
maxConcurrency: 10,
})

逾時及重試
逾時及重試 的直接連結

設定每個項目的逾時時間(以毫秒為單位)和重試次數:

const summary = await dataset.startExperiment({
targetType: 'agent',
targetId: 'translation-agent',
itemTimeout: 30_000, // 30 seconds per item
maxRetries: 2, // retry failed items up to 2 times
})

重試會使用指數退避。中止錯誤一律不會重試。

中止實驗
中止實驗 的直接連結

傳入 AbortSignal 以取消正在執行的實驗:

const controller = new AbortController()

// Cancel after 60 seconds
setTimeout(() => controller.abort(), 60_000)

const summary = await dataset.startExperiment({
targetType: 'agent',
targetId: 'translation-agent',
signal: controller.signal,
})

餘下項目會在摘要中標示為已略過。

固定數據集版本
固定數據集版本 的直接連結

針對數據集的特定快照執行:

const summary = await dataset.startExperiment({
targetType: 'agent',
targetId: 'translation-agent',
version: 3, // use items from dataset version 3
})

查看結果
查看結果 的直接連結

列出實驗
列出實驗 的直接連結

const { experiments, pagination } = await dataset.listExperiments({
page: 0,
perPage: 10,
})

for (const exp of experiments) {
console.log(`${exp.name}${exp.status} (${exp.succeededCount}/${exp.totalItems})`)
}

實驗詳細資料
實驗詳細資料 的直接連結

const experiment = await dataset.getExperiment({
experimentId: 'exp-abc-123',
})

console.log(experiment.status)
console.log(experiment.startedAt)
console.log(experiment.completedAt)
📹 觀看

觀看 Mastra 數據集和實驗 Workflow,了解數據集和實驗如何協助提高可靠性。

項目層級結果
項目層級結果 的直接連結

const { results, pagination } = await dataset.listExperimentResults({
experimentId: 'exp-abc-123',
page: 0,
perPage: 50,
})

for (const result of results) {
console.log(result.itemId, result.output, result.error)
}

了解摘要
了解摘要 的直接連結

startExperiment() 會傳回 ExperimentSummary,當中包含計數和各項目結果:

  • 實驗完成但部分項目失敗時,completedWithErrorstrue
  • 透過 signal 取消的項目會計入 skippedCount

請瀏覽 startExperiment 參考資料,查看完整的參數及傳回類型文件。