다중 턴 평가
다중 턴 평가는 대화 전반에 걸쳐 Agent 동작을 테스트합니다. 싱글 대신input, 당신은inputs정렬. 각 항목은 동일한 스레드의 Agent에 순차적으로 전송되며 득점자는 모든 턴에서 누적된 출력을 확인합니다.
다중 턴 평가를 사용하는 경우다중 턴 평가를 사용하는 경우에 대한 직접 링크
- Agent는 Memory를 사용하고 이전 턴의 컨텍스트를 기억해야 합니다.
- 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')],
})
각 턴은 같은 스레드 ID로 agent.generate()를 실행하므로 Agent는 전체 대화 기록을 볼 수 있습니다. 채점자는 모든 턴에서 누적된 출력 메시지를 받습니다.
교차 호출을 위해서는 Memory가 필요합니다.교차 호출을 위해서는 Memory가 필요합니다.에 대한 직접 링크
멀티 턴 회상은 Agent에 Memory 스토어가 구성되어 있는지에 따라 달라집니다. 공유 스레드 ID 덕분에 각 턴에서 이전 턴을 볼 수 있지만, Agent에 Memory가 있어야 스레드에 기록이 유지됩니다. Agent에 Memory가 구성되어 있지 않아도 턴은 순서대로 실행되고 출력은 채점을 위해 계속 누적되지만, Agent는 이전 턴을 기억하지 못합니다(각 입력이 독립적으로 실행됨). Memory가 없는 Agent에서 inputs를 사용하면 runEvals가 경고를 기록합니다.
runEvals는 대화 ID를 관리합니다. 공유 threadId를 생성하고 resourceId를 주입합니다(Mastra Memory는 resource + thread를 기준으로 메시지 범위를 지정하므로 회상이 작동하려면 둘 다 필요함). 기본적으로 리소스는 생성된 스레드에서 파생되므로 각 대화가 격리됩니다. 예를 들어 기존 사용자의 Memory를 재사용하기 위해 특정 리소스를 고정하려면 targetOptions.memory.resource를 전달하세요. 스레드는 여전히 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' } },
})
Memory 스토어 구성 방법은 Memory를 참조하세요.
작동 원리작동 원리에 대한 직접 링크
데이터 항목에inputs array, runEvals:
- 대화를 위한 새 스레드(고유한
threadId)와 리소스를 생성합니다(호출자가 제공한targetOptions.memory.resource는 유지됨). - 해당 스레드에서
agent.generate()를 통해 각 입력을 순차적으로 전송합니다. - 모든 턴의 출력 메시지를 누적합니다.
- 평가를 위해 누적된 전체 출력을 채점자에게 전달합니다. 득점자는 모든 턴을 포함하여 전체 대화 결과를 봅니다.
점수 의미론점수 의미론에 대한 직접 링크
다중 회전 항목에 대한 채점자를 작성할 때 이러한 세부 사항이 중요합니다.
run.output은 각 턴에서 누적된 출력입니다. 출력 기반 채점자인checks.includes,checks.calledTool,checks.similarity등은 전체 대화를 평가합니다. 예를 들어checks.calledTool('get_weather', { times: 2 })는 모든 턴의 호출 횟수를 계산합니다.run.input은 첫 번째 턴의 입력일 뿐입니다. 입력과 출력을 비교하는 채점자(충실도, 답변 관련성 및 기타 입력 관련 LLM 채점자)는 전체 대화가 아니라 첫 번째 사용자 메시지만 볼 수 있습니다. 멀티 턴에서는 출력 기반 검사를 사용하거나 누적된run.output을 직접 읽는 채점자를 만드세요.
턴별 어설션turnsper-turn-assertions-with-turns에 대한 직접 링크
inputs 형식은 누적된 출력 전체, 즉 모든 턴의 출력에 단일 점수를 매깁니다. 이 방식은 턴별 실패를 숨길 수 있습니다. checks.includes('Brooklyn') 같은 출력 기반 검사는 후속 턴이 잘못되었더라도 어느 턴에서든 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을 확인하며 누적된 대화는 확인하지 않습니다. 이 방식은 잘못된 턴이 검사를 충족할 수 없고 각 턴에 올바른run.input이 사용되므로inputs의 두 가지 사각지대를 모두 해결합니다. - 턴별 결과는 판정에 반영됩니다. 턴의 게이트가 실패하면 판정은
failed가 됩니다. 게이트는 통과했지만 턴의 임계값을 충족하지 못하면scored가 됩니다. result.turnResults[i]는 각 턴의gateResults,thresholdResults,scores를 보고하므로 정확히 어느 턴에서 실패했는지 알 수 있습니다. 대화가 여러 개라면 턴 인덱스별로 결과의 평균을 계산합니다.gates또는scorers가 없는 턴도 대화를 진행합니다.- 최상위
scorers/gates는 여전히 누적된 출력 전체에 대해 실행되므로 "이 턴에서는 반드시 Tool을 호출해야 함"과 "답변에서 Brooklyn을 언급해야 함"을 함께 사용할 수 있습니다. - Agent에 스토리지가 구성되어 있다면 턴별 채점자/게이트 결과도 최상위 점수와 마찬가지로 유지되므로 점수 스토어에 나타납니다. 저장된 각 턴별 점수에는 해당 턴 인덱스(
metadata.turnIndex)가 포함되고, 대화의threadId를 공유하며, 해당 턴의 자체 Trace 범위에 연결됩니다. 전체 대화에 대한 단일 점수면 충분할 때는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매개변수 및 반환 값- 게이트 및 판정: 엄격한 요구 사항 및 품질 임계값 적용
- 빠른 검사: 구성 가능한 Zero-LLM 마이크로 채점자