Multi-Turn Evals
Simulate conversations and run end-to-end testing for multi-turn use cases
Overview
Multi-turn evaluation requires:
- A multi-turn dataset of conversational goldens
- A callback function that wraps around your chatbot to generate conversation turns
- A list of multi-turn metrics you wish to evaluate with
How It Works
- Pull your multi-turn dataset from Confident AI
- Define a callback that invokes your chatbot to generate conversation turns
- Simulate conversations for each golden in your dataset
- Run evaluation on the resulting test cases
Define Your Callback
Define a callback that wraps around your chatbot and generates the next conversation turn:
from deepeval.test_case import Turn
from typing import List
def chatbot_callback(input: str, turns: List[Turn], thread_id: str) -> Turn:
messages = [{"role": turn.role, "content": turn.content} for turn in turns]
messages.append({"role": "user", "content": input})
response = your_chatbot(messages) # Replace with your chatbot
return Turn(role="assistant", content=response)Run Evals Locally
Running evals locally is only possible with the Python deepeval library. For Typescript or other languages, skip to remote evals.
Pull dataset
Pull your multi-turn dataset (and create one if you haven't already):
main.py from deepeval.dataset import EvaluationDataset dataset = EvaluationDataset() dataset.pull(alias="YOUR-DATASET-ALIAS")Simulate conversations
Create a simulator with your callback and generate test cases from your goldens:
main.py from deepeval.simulator import ConversationSimulator simulator = ConversationSimulator(model_callback=chatbot_callback) for golden in dataset.goldens: test_case = simulator.simulator(golden) dataset.add_test_case(test_case)Run evaluation
The
evaluate()function runs your test suite and uploads results to Confident AI:main.py from deepeval.metrics import TurnRelevancyMetric from deepeval import evaluate # Replace with your metrics evaluate(test_cases=dataset.test_cases, metrics=[TurnRelevancyMetric()])Done! You should see a link to your newly created sharable testing report.
- Each metric is applied to every test case (e.g., 10 test cases × 2 metrics = 20 evaluations)
- A test case passes only if all metrics for it pass
- The test run's pass rate is the proportion of test cases that pass
Multi-Turn Testing Reports
Run Evals Remotely
Create metric collection
Go to Project > Metric > Collections:
Metric Collection for Remote Evals Pull dataset and simulate conversations
Set
run_remoteto true to run simulations remotely:main.py from deepeval.simulator import ConversationSimulator from deepeval.dataset import EvaluationDataset dataset = EvaluationDataset() dataset.pull(alias="YOUR-DATASET-ALIAS") simulator = ConversationSimulator(model_callback=chatbot_callback, run_remote=True) for golden in dataset.goldens: test_case = simulator.simulator(golden) dataset.add_test_case(test_case)index.ts import { ConversationalGolden, ConversationSimulator, EvaluationDataset, } from "deepeval"; const dataset = new EvaluationDataset(); await dataset.pull({ alias: "YOUR-DATASET-ALIAS" }); const simulator = new ConversationSimulator({ modelCallback: chatbotCallback }); const testCases = await simulator.simulate({ conversationalGoldens: dataset.goldens as ConversationalGolden[], }); for (const testCase of testCases) { dataset.addTestCase(testCase); }Click to see example callback in Typescript
const chatbotCallback = async (args: { input: string; turns: Turn[]; threadId: string; }): Promise<Turn> => { return new Turn({ role: "assistant", content: your_chatbot(args.input), }); };Use
/v1/simulateto generate the first user turn for each golden:curl -X POST https://api.confident-ai.com/v1/simulate \ -H "CONFIDENT_API_KEY: <PROJECT-API-KEY>" \ -H "Content-Type: application/json" \ -d '{ "golden": [{ "scenario": "A frustrated user asking for a refund.", "userDescription": "A white male who is a customer for over 2 years." }] }'Continue calling
/v1/simulateuntil you've simulated the desired number of turns.Run evaluation
main.py from deepeval import evaluate evaluate(test_case=dataset.test_cases, metric_collection="YOUR-COLLECTION-NAME")index.ts import { ConversationalTestCase, evaluate, EvaluationDataset, } from "deepeval"; const dataset = new EvaluationDataset(); dataset.pull({ alias: "YOUR-DATASET-ALIAS" }); evaluate({ conversationalTestCases: dataset.testCases as ConversationalTestCase[], metricCollection: "YOUR-COLLECTION-NAME", });POST/v1/evaluate curl -X POST "https://api.confident-ai.com/v1/evaluate" \ -H "CONFIDENT_API_KEY: <PROJECT-API-KEY>" \ -H "Content-Type: application/json" \ -d '{ "metricCollection": "string", "llmTestCases": [ { "input": "string", "actualOutput": "string", "name": "string", "expectedOutput": "string", "retrievalContext": [ "string" ], "context": [ "string" ], "toolsCalled": [ { "name": "string", "description": "string", "inputParameters": {}, "output": "string", "reasoning": "string" } ], "expectedTools": [ { "name": "string", "description": "string", "inputParameters": {}, "output": "string", "reasoning": "string" } ] } ], "conversationalTestCases": [ { "turns": [ { "role": "user", "content": "string", "userId": "string", "retrievalContext": [ "string" ], "toolsCalled": [ { "name": null, "description": null, "inputParameters": null, "output": null, "reasoning": null } ] } ], "scenario": "string", "name": "string", "expectedOutcome": "string", "userDescription": "string", "chatbotRole": "string" } ], "hyperparameters": {}, "identifier": "string" }'
Advanced Usage
Early Stopping
To stop a simulation naturally before it reaches the maximum number of turns, provide an expected_outcome for each golden. The conversation will end automatically after the expected outcome has been reached.
from deepeval.dataset import ConversationalGolden
conversation_golden = ConversationalGolden(
scenario="Andy Byron wants to purchase a VIP ticket to a cold play concert.",
expected_outcome="Successful purchase of a ticket.",
user_description="Andy Byron is the CEO of Astronomer.",
)import { ConversationalGolden } from "deepeval";
const conversationGolden = new ConversationalGolden({
scenario: "Andy Byron wants to purchase a VIP ticket to a cold play concert.",
expectedOutcome: "Successful purchase of a ticket.",
userDescription: "Andy Byron is the CEO of Astronomer.",
});If the expected outcome is provided and reached during simulation, the
conversation will be marked as complete in the response to /v1/simulate.
curl -X POST https://api.confident-ai.com/v1/simulate \
-H "CONFIDENT_API_KEY: <PROJECT-API-KEY>" \
-H "Content-Type: application/json" \
-d '{
"golden": [{
"scenario": "Andy Byron wants to purchase a VIP ticket to a cold play concert.",
"userDescription": "Andy Byron is the CEO of Astronomer.",
"expectedOutcome": "Successful purchase of a ticket."
}],
"callback": "https://your-callback-endpoint.com/simulate"
}'Extend Existing Turns
You can extend existing conversations by providing existing Turns to each golden. The simulator will automatically detect and continue simulating from the existing turns.
from deepeval.dataset import ConversationalGolden
from deepeval.test_case import Turn
conversation_golden = ConversationalGolden(
scenario="Andy Byron wants to purchase a VIP ticket to a cold play concert.",
user_description="Andy Byron is the CEO of Astronomer.",
turns=[
Turn(role="user", content="Hi"),
Turn(role="assistant", content="Hello! How can I help you today?"),
Turn(role="user", content="I want to purchase a VIP ticket to a cold play concert."),
]
)import { ConversationalGolden, Turn } from "deepeval";
const firstTurn = new Turn({
role: "user",
content: "Hi",
});
const secondTurn = new Turn({
role: "assistant",
content: "Hello! How can I help you today?",
});
const thirdTurn = new Turn({
role: "user",
content: "I want to purchase a VIP ticket to a cold play concert.",
});
const conversationGolden = new ConversationalGolden({
scenario: "Andy Byron wants to purchase a VIP ticket to a cold play concert.",
userDescription: "Andy Byron is the CEO of Astronomer.",
turns: [firstTurn, secondTurn, thirdTurn],
});curl -X POST https://api.confident-ai.com/v1/simulate \
-H "CONFIDENT_API_KEY: <PROJECT-API-KEY>" \
-H "Content-Type: application/json" \
-d '{
"golden": [{
"scenario": "Andy Byron wants to purchase a VIP ticket to a cold play concert.",
"userDescription": "Andy Byron is the CEO of Astronomer.",
"turns": [
{"role": "user", "content": "Hi"},
{"role": "assistant", "content": "Hello! How can I help you today?"}
]
}]
}'