跳至主要內容

執行實驗

新增於: @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 擁有自己的記憶體,且 request context 帶有資源 ID(MASTRA_RESOURCE_ID_KEY,由驗證中介軟體、實驗或項目的 requestContext,或 Studio Run Experiment 表單設定)時,實驗執行器會為每個項目注入新的記憶體討論串。request context 中的資源 ID 表示「以此資源執行」:各項目的對話會保存為該資源下的討論串,而重試項目每次嘗試都會取得新討論串,避免先前失敗嘗試的內容洩漏至重試 context。

注入的討論串會加上標記,以便對應回該次執行:討論串中繼資料包含 experimentId,並以 experimentItemId 保存資料集項目 ID。系統不會為這些討論串產生標題。

由於討論串屬於呼叫者的資源,資源範圍記憶體功能在執行期間會讀寫該資源的狀態:

  • 資源範圍工作記憶體的更新會保存至資源,同次執行中的後續項目可看到較早項目所做的更新。
  • 資源範圍語意回憶可向實驗提供該資源先前的對話;實驗逐字稿也能在該資源後續的對話中被回憶。

若要使用真實使用者累積的 context 評估 Agent,此功能很實用。若不希望實驗執行接觸真實使用者狀態,請改用專用的評估資源 ID 執行實驗。

下列情況會略過討論串注入:

  • 若 request context 也設定 MASTRA_THREAD_ID_KEY,執行器會直接使用該討論串,因此所有項目(及重試)都共用同一段對話。
  • 若 Agent 沒有記憶體,或 request context 沒有資源 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 儲存區。

experiments: 'none' 執行 startExperimentAsync() 時,不會保存實驗記錄、進度更新或項目結果。分數保存仍由 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 會中止剩餘執行,並以 idEXPERIMENT_EVENT_OBSERVER_FAILEDMastraError 拒絕 runExperiment()。系統不會透過失敗的觀察器傳送最終事件。對 startExperimentAsync() 而言,分離的觀察器失敗時方法已傳回,因此呼叫者需要直接錯誤回報時,請在觀察器內處理傳遞失敗。

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

匯出的事件類型包括 ExperimentEventExperimentRunStartedEventExperimentItemCompletedEventExperimentRunFinishedEvent。讀取事件特有屬性前,請使用可辨識的 type 欄位縮小事件類型。

Tool mock
「Tool mock」的直接連結

當實驗執行會呼叫具副作用 Tool 的 Agent 時,請將靜態 Tool mock 附加至個別資料集項目,使執行結果具確定性。實驗期間,被 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,下一次取得第二個,依此類推。順序會按每個 (toolName, args) 群組追蹤,不同引數之間彼此獨立。

比對模式
「比對模式」的直接連結

每個 mock 預設會嚴格比對其 args。設定 matchArgs: 'ignore' 可只比對 Tool 名稱;系統不會比較 mock 的 args,無論 Agent 如何呼叫,都會提供該 Tool 下一個尚未取用的 mock:

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 的形式提供給父 Agent,其引數包含 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,包括原本會實際執行且具副作用的未 mock 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;開啟實驗結果可查看相同報告。

限制
「限制」的直接連結

  • Mock 呼叫沒有 Tool span。 Mock 呼叫會在 Tool 執行前傳回輸出,因此不會建立 Tool span。由已儲存 Trace 支援的軌跡評分器可能看不到 mock 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 資料集與實驗工作流程,瞭解資料集與實驗如何協助提升可靠性。

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

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 參考文件