多輪 Evals
多輪 Evals 會測試 Agent 在一段對話中的行為。你不必提供單一 input,而是提供 inputs 陣列。每個項目都會在同一個 thread 上依序傳送給 Agent,評分器則會看到所有對話輪次累積的輸出。
多輪 Evals 的適用時機「多輪 Evals 的適用時機」的直接連結
- Agent 使用記憶體,而且必須回想先前對話輪次的內容
- Agent 會處理依賴先前回應的後續問題
- 你需要驗證對話中的 Tool 呼叫順序
- Agent 會執行多步驟 Workflow(搜尋、確認、執行)
快速入門「快速入門」的直接連結
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'
import { weatherAgent } from '../agents'
const result = await runEvals({
data: [
{
inputs: [
'What is the weather in Brooklyn?',
'What about tomorrow?',
'Compare the two forecasts.',
],
},
],
target: weatherAgent,
scorers: [checks.calledTool('get_weather', { times: 2 }), checks.includes('Brooklyn')],
})
每個對話輪次都會使用相同的 thread ID 執行 agent.generate(),因此 Agent 能看到完整對話記錄。評分器會收到所有對話輪次累積的輸出訊息。
跨輪回想需要記憶體「跨輪回想需要記憶體」的直接連結
多輪回想需要 Agent 已設定記憶體儲存區。共用 thread ID 可讓每個對話輪次看到先前輪次,但只有 Agent 設有記憶體時,thread 才會保存記錄。若 Agent 未設定記憶體,對話輪次仍會依序執行,輸出也仍會累積供評分使用,但 Agent 不會記得先前輪次(每筆輸入都會獨立執行)。當你對未設定記憶體的 Agent 使用 inputs 時,runEvals 會記錄警告。
runEvals 會替你管理對話識別資訊:它會產生共用 threadId 並注入 resourceId(Mastra 記憶體會依資源與 thread 界定訊息範圍,因此兩者都是回想功能的必要條件)。資源預設衍生自產生的 thread,讓每段對話彼此隔離。若要固定特定資源,例如重複使用現有使用者的記憶體,請傳入 targetOptions.memory.resource;thread 仍由 runEvals 管理,因此不必提供:
await runEvals({
target: weatherAgent,
data: [{ inputs: ['What is the weather in Brooklyn?', 'What about tomorrow?'] }],
scorers: [checks.similarity('weather forecast')],
targetOptions: { memory: { resource: 'user-42' } },
})
如需記憶體儲存區的設定方式,請參閱記憶體。
運作方式「運作方式」的直接連結
當資料項目含有 inputs 陣列時,runEvals 會:
- 為對話建立新的 thread(不重複的
threadId)與資源(會保留呼叫端提供的targetOptions.memory.resource) - 在該 thread 上透過
agent.generate()依序傳送每筆輸入 - 累積所有對話輪次的輸出訊息
- 將完整的累積輸出傳給評分器進行評估
評分器會看到完整對話輸出,包括每個對話輪次。
評分語意「評分語意」的直接連結
為多輪項目撰寫評分器時,請注意以下細節:
run.output是每個對話輪次的累積輸出。 輸出式評分器(如checks.includes、checks.calledTool、checks.similarity等)會評估整段對話。例如,checks.calledTool('get_weather', { times: 2 })會計算所有對話輪次中的呼叫次數。run.input只包含第一輪輸入。 比較輸入與輸出的評分器(忠實度、答案相關性與其他輸入相對式 LLM 評分器)只會看到第一則使用者訊息,而非完整對話。對多輪評估而言,請優先使用輸出式檢查,或建立直接讀取累積run.output的評分器。
使用 turns 進行逐輪斷言「per-turn-assertions-with-turns」的直接連結
inputs 形式會將累積輸出視為整體評分,也就是針對所有對話輪次的輸出給出單一分數。這可能掩蓋個別輪次的失敗:只要_任一_輪提到 Brooklyn,checks.includes('Brooklyn') 之類的輸出式檢查就會通過,即使後續輪次已出錯也是如此。
若要斷言特定對話輪次的行為正確,請改用 turns。每個對話輪次都是一個物件,包含自己的 input,並可選擇提供只評估該輪輸入與輸出的 gates/scorers:
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'
import { weatherAgent } from '../agents'
const result = await runEvals({
data: [
{
turns: [
{
input: 'What is the weather in Brooklyn?',
gates: [checks.calledTool('get_weather')],
},
{
// The follow-up must call the tool again — it can't be satisfied
// by the first turn's tool call.
input: 'What about tomorrow?',
gates: [checks.calledTool('get_weather')],
scorers: [{ scorer: checks.similarity('tomorrow forecast'), threshold: 0.5 }],
},
],
},
],
target: weatherAgent,
})
result.verdict // 'passed' | 'scored' | 'failed'
result.turnResults // per-turn gate/threshold/scorer outcomes
語意如下:
- 逐輪閘門或評分器只會看到該輪的
run.input與run.output,絕不會看到累積對話。這解決了inputs的兩個盲點:錯誤的對話輪次無法滿足檢查,而且每一輪的run.input都正確無誤。 - 逐輪結果會納入判定結果:某一輪的閘門失敗會使判定結果成為
failed;若閘門皆通過,但某輪未達臨界值,則結果為scored。 result.turnResults[i]會回報各輪的gateResults、thresholdResults與scores,因此能精確指出失敗的對話輪次。若有多段對話,則依對話輪次索引計算平均結果。- 未設定
gates或scorers的對話輪次仍會推進對話。 - 頂層
scorers/gates仍會針對累積輸出整體執行,因此你可以結合「這一輪必須呼叫 Tool」與「答案提到 Brooklyn」等條件。 - Agent 設有儲存區時,每個逐輪評分器/閘門結果都會像頂層分數一樣保存,因此逐輪結果會出現在分數儲存區中。每個已儲存的逐輪分數都會標示其對話輪次索引(
metadata.turnIndex)、共用該段對話的threadId,並連結至該輪自己的 Trace span。
若整段對話只需一個整體分數,請使用 inputs;若正確性取決於個別對話輪次,請使用 turns。同一資料項目中的 turns 不能與 input 或 inputs 結合使用。
結合閘門與臨界值「結合閘門與臨界值」的直接連結
多輪資料項目可搭配閘門與判定結果使用。請使用輸出式評分器,讓閘門判斷反映完整對話:
import { runEvals } from '@mastra/core/evals'
import { checks } from '@mastra/evals/checks'
const result = await runEvals({
data: [
{
inputs: ['My favorite city is Brooklyn.', 'What is the weather in my favorite city?'],
},
],
target: weatherAgent,
gates: [checks.calledTool('get_weather')],
scorers: [{ scorer: checks.similarity('Brooklyn weather forecast'), threshold: 0.5 }],
})
result.verdict // 'passed' | 'scored' | 'failed'
混用單輪與多輪項目「混用單輪與多輪項目」的直接連結
單次 runEvals 呼叫可以同時包含單輪與多輪資料項目:
const result = await runEvals({
data: [
{ input: 'What is the weather in Brooklyn?' },
{
inputs: ['My favorite city is Brooklyn.', 'What is the weather in my favorite city?'],
},
],
target: weatherAgent,
scorers: [checks.includes('Brooklyn')],
})
單輪項目照常使用 input。多輪項目使用 inputs,可完全省略 input。
驗證「驗證」的直接連結
若 inputs 存在但為空,runEvals 會擲回 MastraError:
// Throws: 'inputs' must be a non-empty array
await runEvals({
data: [{ inputs: [] }],
target: myAgent,
scorers: [myScorer],
})
相關資源「相關資源」的直接連結
runEvals()參考:runEvals參數與傳回值的完整 API- 閘門與判定結果:強制執行必要要求與品質臨界值
- Quick Checks:不使用 LLM 的可組合微型評分器