데이터셋 가져오기

데이터셋 가져오기 (Pull Datasets)

데이터셋을 로컬로 가져와 평가에 사용해볼게요. 이전 섹션에서 Confident API로 골든스를 푸시하고 큐에 넣는 방법을 배웠다면, 이제 싱글턴·멀티턴 데이터셋을 평가용으로 가져오고, 커스텀 열과 멀티모달 골든스(이미지)를 활용하는 방법까지 익힐 거예요. 평가 루프를 직접 제어한다는 점이 노코드 평가와의 핵심 차이예요.

출처: 문서

본문

개요

이전 섹션에서 Confident API를 통해 골든스를 푸시하고 큐에 넣는 방법을 배웠어요. 이 섹션에서는 다음을 배울 거예요:

  • 평가를 위해 싱글턴·멀티턴 데이터셋 가져오기
  • 골든스에서 커스텀 열 값 접근하기
  • 멀티모달 골든스(이미지)를 평가 가능한 형식으로 파싱하기
  • 싱글턴 데이터셋에서 평가를 실행하기 위해 evals_iterator 사용하기(Python 전용)

동작 방식

코드 기반 평가는 노코드 평가와 비슷한 과정을 따르지만, 평가 루프를 직접 제어해요:

  1. 데이터셋 가져오기 — Confident API를 사용해 Confident AI에서 골든스 가져오기
  2. AI 앱 호출 — 각 골든스의 인풋으로 AI 앱 호출
  3. 테스트 케이스 생성 — 골든스 필드와 AI 아웃풋을 테스트 케이스로 매핑
  4. 평가 실행 — 테스트 케이스에 메트릭 실행 및 결과 푸시

데이터 흐름을 시각적으로 보면:

sequenceDiagram
    participant You as Your Code
    participant Platform as Confident AI
    participant AI as Your AI App
    participant Metrics as Local/Remote Metrics

    You->>Platform: Pull dataset (goldens)
    Platform-->>You: Return goldens

    loop For each golden in dataset
        You->>AI: Invoke with golden.input
        AI-->>You: Generate output
        You->>You: Create test case from golden + output
    end

    You->>Metrics: Run evaluation on test cases
    Metrics-->>You: Metric scores
    You->>Platform: Push test run results
    Platform-->>You: Test run created

노코드 평가와의 핵심 차이는 평가 루프를 직접 제어한다는 점이에요 — 골든스 가져오기, AI 앱 호출, 테스트 케이스 구성이 모두 여러분의 코드에서 일어나요.

어떤 프로젝트에서든 CONFIDENT_API_KEY를 구성하면 데이터셋을 관리할 수 있어요.

  • 기본 사용 시 CONFIDENT_API_KEY를 환경 변수로 설정하세요.
  • 특정 프로젝트를 대상으로 하려면 EvaluationDataset을 만들 때 confident_api_key를 직접 전달하세요.
from deepeval.dataset import EvaluationDataset

dataset = EvaluationDataset(confident_api_key="confident_us...")
dataset.delete(alias="YOUR-DATASET-ALIAS")

둘 다 제공되면 EvaluationDataset에 전달한 confident_api_key가 환경 변수보다 항상 우선해요.

Confident API로 골든스 가져오기

데이터셋은 싱글턴 또는 멀티턴이며, 싱글턴 데이터셋을 가져오면 싱글턴 골든스가 주어지고 그 반대도 마찬가지라는 점을 알아두세요.

싱글턴 골든스를 싱글턴 테스트 케이스로 매핑하고, 그 반대도 매핑하는 것은 여러분의 책임이에요.

Confident API로 골든스를 가져오면 기본적으로 **확정(finalized)**된 골든스만 가져와요. 아래는 싱글턴 데이터셋 예시예요(멀티턴 데이터셋 사용은 여기 클릭):

재현 가능한 평가 실행을 위해 pull(...)에 version="00.00.01"(Python) 또는 { version: "00.00.01" }(TypeScript)을 전달해 특정 데이터셋 버전에 고정하세요. version을 생략하면 최신 버전을 가져오거나, 데이터셋에 버전이 없으면 비버전 골든스를 가져와요. 자세한 내용은 데이터셋 버전 관리를 참고하세요.

Python

골든스 가져오기

먼저 .pull() 메서드를 사용해요:

from deepeval.dataset import EvaluationDataset

dataset = EvaluationDataset()
dataset.pull(alias="YOUR-DATASET-ALIAS")

print(dataset.goldens) # Check it's pulled correctly

테스트 케이스 구성

그런 다음 골든스 데이터셋을 순회하며 테스트 케이스 목록을 만들어요:

from deepeval.dataset import EvaluationDataset
from deepeval.test_case import LLMTestCase

dataset = EvaluationDataset()
dataset.pull(alias="YOUR-DATASET-ALIAS")

for golden in dataset.goldens:
    test_case = LLMTestCase(
        input=golden.input,
        actual_output=llm_app(golden.input),
        # map any additional fields here
    )
    dataset.add_test_case(test_case)

멀티턴 데이터셋의 경우 ConversationalTestCase를 대신 만들게 돼요:

from deepeval.test_case import ConversationalTestCase

for golden in dataset.goldens:
  test_case = simulate(golden) # simulate conversation
  dataset.add_test_case(test_case)

평가 실행

이전 단계에서 .add_test_case()를 호출했기 때문에, evaluate를 실행할 때마다 Confident AI가 생성된 테스트 런을 데이터셋에 자동으로 연결해요:

from deepeval import evaluate

evaluate(test_cases=dataset.test_cases, metrics=[...])

TypeScript

골든스 가져오기

먼저 .pull() 메서드를 사용해요:

import { EvaluationDataset } from "deepeval";

const dataset = new EvaluationDataset();
dataset.pull({ alias: "YOUR-DATASET-ALIAS" });

console.log(dataset.goldens);

테스트 케이스 구성

그런 다음 골든스 데이터셋을 순회하며 테스트 케이스 목록을 만들어요:

import { EvaluationDataset, Golden, LLMTestCase } from "deepeval";

const dataset = new EvaluationDataset();
dataset.pull({ alias: "YOUR-DATASET-ALIAS" });

for (const golden of dataset.goldens as Golden[]) {
  const testCase = new LLMTestCase({
    input: golden.input,
    actualOutput: llmApp(golden.input),
    // map any additional fields here
  });

  dataset.addTestCase(testCase);
}

멀티턴 데이터셋의 경우 ConversationalTestCase를 대신 만들게 돼요:

import {
  ConversationalGolden,
  ConversationSimulator,
  EvaluationDataset,
} from "deepeval";

const dataset = new EvaluationDataset();
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);
}

평가 실행

이전 단계에서 .addTestCase()를 호출했기 때문에, evaluate를 실행할 때마다 Confident AI가 생성된 테스트 런을 데이터셋에 자동으로 연결해요:

import { ConversationalTestCase, EvaluationDataset, evaluate } from "deepeval";

const dataset = new EvaluationDataset();
dataset.pull({ alias: "YOUR-DATASET-ALIAS" });

evaluate({
  conversationalTestCases: dataset.testCases as ConversationalTestCase[],
  metrics: [...],
});

curl

골든스 가져오기

먼저 /v1/datasets 엔드포인트로 골든스를 가져와요.

Request (GET /v1/datasets/{alias}) — API reference

curl -X GET "https://api.confident-ai.com/v1/datasets/{alias}" \
  -H "CONFIDENT_API_KEY: <PROJECT-API-KEY>"
import requests

response = requests.get(
    "https://api.confident-ai.com/v1/datasets/{alias}",
    headers={
        "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
    },
)

print(response.json())
const response = await fetch("https://api.confident-ai.com/v1/datasets/{alias}", {
  method: "GET",
  headers: {
    "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
  },
});

const data = await response.json();
console.log(data);
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.confident-ai.com/v1/datasets/{alias}", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.confident-ai.com/v1/datasets/{alias}"))
            .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
            .GET()
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.body());
    }
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::Client::new()
        .get("https://api.confident-ai.com/v1/datasets/{alias}")
        .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
        .send()
        .await?;

    println!("{}", response.text().await?);

    Ok(())
}

테스트 케이스 구성

가져온 골든스에서 골든스 필드를 보존하며 테스트 케이스의 JSON 배열을 구성해요.

싱글턴 (Single-Turn)

[
  {
    "input": "How tall is Mount Everest?",
    // Replace with your LLM app output
    "actualOutput": "Mount Everest is 9K meters tall."
  }
]

싱글턴 테스트 케이스 생성 파라미터 보려면 여기를 클릭

LLMTestCase의 파라미터

  • input: string
  • actualOutput: string
  • name: string
  • expectedOutput: string
  • retrievalContext: list of strings
  • context: list of strings
  • toolsCalled: list of ToolCall
  • expectedTools: list of ToolCall

멀티턴 (Multi-Turn)

[
  {
    "scenario": "User asking about Mount Everest height.",
    "turns": [
      { "role": "user", "content": "How tall is Mount Everest?" },
      { "role": "assistant", "content": "Mount Everest is 9K meters tall." }
      // Replace with your LLM app outputs
    ],
  }
]

멀티턴 테스트 케이스 생성 파라미터 보려면 여기를 클릭

ConversationalTestCase의 파라미터

  • turns: list of Turn
  • scenario: string
  • name: string
  • expectedOutput: string
  • userDescription: string
  • chatbotRole: string

메트릭 컬렉션 만들기

v1/metric-collections를 통해 메트릭 컬렉션을 만들어요.

Request (POST /v1/metric-collections) — API reference

curl -X POST "https://api.confident-ai.com/v1/metric-collections" \
  -H "CONFIDENT_API_KEY: <PROJECT-API-KEY>" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Collection Name",
  "multiTurn": false,
  "metricSettings": [
    {
      "metric": {
        "name": "Answer Relevancy"
      },
      "threshold": 0.8
    }
  ]
}'
import requests

response = requests.post(
    "https://api.confident-ai.com/v1/metric-collections",
    headers={
        "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
    },
    json={
        "name": "Collection Name",
        "multiTurn": False,
        "metricSettings": [
            {
                "metric": {
                    "name": "Answer Relevancy"
                },
                "threshold": 0.8
            }
        ]
    },
)

print(response.json())
const response = await fetch("https://api.confident-ai.com/v1/metric-collections", {
  method: "POST",
  headers: {
    "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "Collection Name",
    "multiTurn": false,
    "metricSettings": [
      {
        "metric": {
          "name": "Answer Relevancy"
        },
        "threshold": 0.8
      }
    ]
  }),
});

const data = await response.json();
console.log(data);
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	body := `{
  "name": "Collection Name",
  "multiTurn": false,
  "metricSettings": [
    {
      "metric": {
        "name": "Answer Relevancy"
      },
      "threshold": 0.8
    }
  ]
}`

	req, err := http.NewRequest("POST", "https://api.confident-ai.com/v1/metric-collections", strings.NewReader(body))
	if err != nil {
		panic(err)
	}
	req.Header.Set("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "name": "Collection Name",
              "multiTurn": false,
              "metricSettings": [
                {
                  "metric": {
                    "name": "Answer Relevancy"
                  },
                  "threshold": 0.8
                }
              ]
            }""";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.confident-ai.com/v1/metric-collections"))
            .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.body());
    }
}
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::Client::new()
        .post("https://api.confident-ai.com/v1/metric-collections")
        .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
        .json(&json!({
          "name": "Collection Name",
          "multiTurn": false,
          "metricSettings": [
            {
              "metric": {
                "name": "Answer Relevancy"
              },
              "threshold": 0.8
            }
          ]
        }))
        .send()
        .await?;

    println!("{}", response.text().await?);

    Ok(())
}

평가 실행

구성한 테스트 케이스와 만든 메트릭 컬렉션을 사용해 /v1/evaluate로 평가를 실행해요.

Request (POST /v1/evaluate) — API reference

curl -X POST "https://api.confident-ai.com/v1/evaluate" \
  -H "CONFIDENT_API_KEY: <PROJECT-API-KEY>" \
  -H "Content-Type: application/json" \
  -d '{
  "metricCollection": "Collection Name",
  "llmTestCases": [
    {
      "input": "How tall is mount everest?",
      "actualOutput": "No clue, pretty tall I guess?"
    }
  ]
}'
import requests

response = requests.post(
    "https://api.confident-ai.com/v1/evaluate",
    headers={
        "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
    },
    json={
        "metricCollection": "Collection Name",
        "llmTestCases": [
            {
                "input": "How tall is mount everest?",
                "actualOutput": "No clue, pretty tall I guess?"
            }
        ]
    },
)

print(response.json())
const response = await fetch("https://api.confident-ai.com/v1/evaluate", {
  method: "POST",
  headers: {
    "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "metricCollection": "Collection Name",
    "llmTestCases": [
      {
        "input": "How tall is mount everest?",
        "actualOutput": "No clue, pretty tall I guess?"
      }
    ]
  }),
});

const data = await response.json();
console.log(data);
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	body := `{
  "metricCollection": "Collection Name",
  "llmTestCases": [
    {
      "input": "How tall is mount everest?",
      "actualOutput": "No clue, pretty tall I guess?"
    }
  ]
}`

	req, err := http.NewRequest("POST", "https://api.confident-ai.com/v1/evaluate", strings.NewReader(body))
	if err != nil {
		panic(err)
	}
	req.Header.Set("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        String body = """
            {
              "metricCollection": "Collection Name",
              "llmTestCases": [
                {
                  "input": "How tall is mount everest?",
                  "actualOutput": "No clue, pretty tall I guess?"
                }
              ]
            }""";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.confident-ai.com/v1/evaluate"))
            .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.body());
    }
}
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::Client::new()
        .post("https://api.confident-ai.com/v1/evaluate")
        .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
        .json(&json!({
          "metricCollection": "Collection Name",
          "llmTestCases": [
            {
              "input": "How tall is mount everest?",
              "actualOutput": "No clue, pretty tall I guess?"
            }
          ]
        }))
        .send()
        .await?;

    println!("{}", response.text().await?);

    Ok(())
}

커스텀 열 사용하기 (Using Custom Columns)

데이터셋에 커스텀 열이 있다면 각 골든스의 custom_column_key_values 필드를 통해 접근할 수 있어요:

Python

from deepeval.dataset import EvaluationDataset
from deepeval.test_case import LLMTestCase

dataset = EvaluationDataset()
dataset.pull(alias="YOUR-DATASET-ALIAS")

for golden in dataset.goldens:
    # Access custom column values
    difficulty = golden.custom_column_key_values.get("difficulty")
    category = golden.custom_column_key_values.get("category")

    # Use them in your test case or LLM app invocation
    test_case = LLMTestCase(
        input=golden.input,
        actual_output=llm_app(golden.input, difficulty=difficulty),
    )
    dataset.add_test_case(test_case)

TypeScript

import { EvaluationDataset, Golden, LLMTestCase } from "deepeval";

const dataset = new EvaluationDataset();
await dataset.pull({ alias: "YOUR-DATASET-ALIAS" });

for (const golden of dataset.goldens as Golden[]) {
  // Access custom column values
  const difficulty = golden.customColumnKeyValues?.difficulty;
  const category = golden.customColumnKeyValues?.category;

  // Use them in your test case or LLM app invocation
  const testCase = new LLMTestCase({
    input: golden.input,
    actualOutput: await llmApp(golden.input, { difficulty }),
  });
  dataset.addTestCase(testCase);
}

이미지 사용하기 (Using Images)

이미지를 포함하는 모든 (목록형) 골든스 텍스트 필드(예: input, scenario 등)는 [DEEPEVAL:IMAGE:url] 형식이에요. [DEEPEVAL:IMAGE:url] 형식 안의 url은 누구나 접근 가능한 공개 url이에요.

이미지를 포함하는 골든스의 경우, 다음과 같이 파싱해 사용할 수 있어요:

Python

deepeval 파이썬 SDK는 convert_to_multi_modal_array라는 유틸리티 메서드를 제공해요. 이 메서드는 [DEEPEVAL:IMAGE:url] 형식의 이미지가 포함된 문자열을 문자열과 MLLMImage 항목의 목록으로 변환하는 데 유용해요.

from deepeval.dataset import EvaluationDataset
from deepeval.utils import convert_to_multi_modal_array

dataset = EvaluationDataset()
dataset.pull(alias="My Evals Dataset")

for golden in dataset.goldens:
    multimodal_array = convert_to_multi_modal_array(golden.input)

여기서 multimodal_array는 문자열과 MLLMImage를 담은 목록이에요. 이 배열을 순회해 이미지가 포함된 messages 배열을 만들어 MLLM에 전달할 수 있어요. openai용 messages 배열을 만드는 예시는 다음과 같아요:

messages = []
for element in multimodal_array:
    if isinstance(element, str):
        messages.append({"type": "text", "text": element})
    elif isinstance(element, MLLMImage):
      if element.url:
          messages.append(
              {
                  "type": "image_url",
                  "image_url": {"url": element.url},
              }
          )

TypeScript

[DEEPEVAL:IMAGE:url] 형식의 이미지가 포함된 문자열을 문자열과 URL 배열로 변환하는 커스텀 메서드를 사용할 수 있어요.

const parseMultimodalString = (s: string) => {
  const PATTERN = /\[DEEPEVAL:IMAGE:(.*?)\]/g;

  const result = [];
  let lastEnd = 0;
  let match;

  while ((match = PATTERN.exec(s)) !== null) {
    const start = match.index;
    const end = PATTERN.lastIndex;

    if (start > lastEnd) {
      result.push(s.slice(lastEnd, start));
    }

    const imageUrl = match[1];
    result.push({ url: imageUrl });

    lastEnd = end;
  }

  if (lastEnd < s.length) {
    result.push(s.slice(lastEnd));
  }

  return result;
}

이제 이 메서드로 multimodalArray를 얻고 messages 배열을 구성해 MLLM에 전달할 수 있어요. openai 형식 메시지를 만드는 데 사용하는 예시는 다음과 같아요:

const multimodalArray = parseMultimodalString(golden.input);

const messages = [];

for (const element of multimodalArray) {
  if (typeof element === "string") {
    messages.push({
      type: "text",
      text: element,
    });
  } else if (element.url) {
    messages.push({
      type: "image_url",
      image_url: { url: element.url },
    });
  }
}

curl

Request (GET /v1/datasets/{alias}) — API reference

curl -X GET "https://api.confident-ai.com/v1/datasets/{alias}" \
  -H "CONFIDENT_API_KEY: <PROJECT-API-KEY>"
import requests

response = requests.get(
    "https://api.confident-ai.com/v1/datasets/{alias}",
    headers={
        "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
    },
)

print(response.json())
const response = await fetch("https://api.confident-ai.com/v1/datasets/{alias}", {
  method: "GET",
  headers: {
    "CONFIDENT_API_KEY": "<PROJECT-API-KEY>",
  },
});

const data = await response.json();
console.log(data);
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, err := http.NewRequest("GET", "https://api.confident-ai.com/v1/datasets/{alias}", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.confident-ai.com/v1/datasets/{alias}"))
            .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
            .GET()
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.body());
    }
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::Client::new()
        .get("https://api.confident-ai.com/v1/datasets/{alias}")
        .header("CONFIDENT_API_KEY", "<PROJECT-API-KEY>")
        .send()
        .await?;

    println!("{}", response.text().await?);

    Ok(())
}

여기서 가져온 데이터셋은 [DEEPEVAL:IMAGE:url] 패턴으로 골든스 필드 안에 이미지가 있어요. 필드를 파싱해 공개 url을 가져와 필요에 따라 사용하세요.

커스텀 (Custom)

[DEEPEVAL:IMAGE:url] 형식의 이미지가 포함된 문자열을 문자열과 URL 배열로 변환하는 커스텀 메서드를 사용할 수 있어요.

def parse_multimodal_string(s: str):
    PATTERN = r"\[DEEPEVAL:IMAGE:(.*?)\]"
    matches = list(re.finditer(pattern, s))

    result = []
    last_end = 0
    for m in matches:
        start, end = m.span()
        if start > last_end:
          result.append(s[last_end:start])

        image_url = m.group(1)

        result.append({"url": image_url})
        last_end = end

    if last_end < len(s):
        result.append(s[last_end:])
        return result

이 메서드로 multimodal_array를 얻고 messages 배열을 구성해 MLLM에 전달할 수 있어요. 사용 예시는 다음과 같아요:

multimodal_array = parse_multimodal_string(golden.input)

messages = []
for element in multimodal_array:
    if isinstance(element, str):
        messages.append({"type": "text", "text": element})
    else:
        if element.get("url") is not None:
          messages.append(
              {
                  "type": "image_url",
                  "image_url": {"url": element.url},
              }
          )

이것은 코드에서 데이터셋을 사용할 때만 필요해요 — Confident AI는 플랫폼에서 이미지 파싱과 변환을 자동으로 처리해요.

deepeval의 GPTModel, GeminiModel 같은 네이티브 모델은 [DEEPEVAL:IMAGE:url] 형식 안의 이미지를 자동으로 파싱해줘요. 이미지가 있는 골든스 필드를 .generate() 또는 .a_generate() 메서드에 그냥 전달하면 deepeval이 내부적으로 이미지를 자동 처리해요!

from deepeval.models import GPTModel
from deepeval.dataset import EvaluationDataset

dataset = EvaluationDataset()
dataset.pull(alias="My Evals Dataset")

model = GPTModel(model="gpt-5.2")

for golden in dataset.goldens:
  print(model.generate(golden.input)) # Images are automatically handled by deepeval

Evals Iterator 사용하기 (Using Evals Iterator)

일반적으로 데이터셋을 테스트 케이스 목록으로 평가에 제공하면 돼요. 하지만 싱글턴, 종단 간 OR 컴포넌트 레벨 평가를 실행하면서 Python에서 deepeval을 쓰는 경우, evals_iterator()를 사용할 수 있어요:

from deepeval.dataset import EvaluationDataset

dataset = EvaluationDataset()
dataset.pull(alias="YOUR-DATASET-ALIAS")

for golden in dataset.evals_iterator():
    llm_app(golden.input) # Replace with your LLM app

# Async version
# import asyncio
#
# for golden in dataset.evals_iterator():
#    task = asyncio.create_task(a_llm_app(golden.input))
#    dataset.evaluate(task)

이 기능이 동작하려면 LLM 앱을 트레이싱해야 해요. 자세한 내용은 트레이싱으로 싱글턴 종단 간 평가 실행 섹션을 읽어보세요.

CI/CD에서의 데이터셋

CI/CD에서 데이터셋을 사용하는 것은 로컬 평가와 같은 패턴을 따라요 — 데이터셋 가져오기 → 테스트 케이스 생성 → 평가 실행. 유일한 차이는 evaluate() 대신 assert_test()를 사용해 pytest와 통합한다는 점이에요:

import pytest
from deepeval.test_case import LLMTestCase
from deepeval.dataset import EvaluationDataset
from deepeval.metrics import AnswerRelevancyMetric
from deepeval import assert_test

dataset = EvaluationDataset()
dataset.pull(alias="YOUR-DATASET-ALIAS")

for golden in dataset.goldens:
    test_case = LLMTestCase(input=golden.input, actual_output=llm_app(golden.input))
    dataset.add_test_case(test_case)

@pytest.mark.parametrize("test_case", dataset.test_cases)
def test_llm_app(test_case: LLMTestCase):
    assert_test(test_case, metrics=[AnswerRelevancyMetric()])

그런 다음 deepeval test run test_llm_app.py로 실행해 테스트를 수행해요. 자동화된 테스트 설정에 대해 더 자세히 알아보려면 CI/CD 단위 테스트 섹션을 참고하세요.

다음 단계

이제 데이터셋 수명 주기에 익숙해졌으니, 평가를 종단 간 실행하는 데 뛰어들 시간이에요.

싱글턴 평가 (Single-Turn Evals)

싱글턴 상호작용에 종단 간 또는 컴포넌트 레벨 평가를 실행해요.

멀티턴 평가 (Multi-Turn Evals)

멀티턴 테스트 케이스로 대화형 AI를 평가해요.

더 알아보기