싱글턴 종단 간 평가

싱글턴 종단 간 평가 (Single-Turn, E2E Evals)

싱글턴 유스케이스에 대해 종단 간(end-to-end) 테스트를 실행하는 방법을 배워볼게요. 종단 간 테스트는 LLM 애플리케이션을 블랙박스로 취급해 — 인풋을 넣고 최종 아웃풋을 평가해요. 단순한 파이프라인, RAG 시스템, 또는 중간 단계보다 최종 결과만 신경 쓸 때 이상적이에요.

출처: 문서

본문

개요

종단 간 테스트는 LLM 애플리케이션을 블랙박스로 취급해요 — 인풋을 제공하고 최종 아웃풋을 평가하죠. 이는 단순한 파이프라인, RAG 시스템, 또는 중간 단계보다 최종 결과만 신경 쓸 때 이상적이에요.

요구 사항 (Requirements):

종단 간 테스트는 기술적으로 컴포넌트 레벨 테스트의 부분집합이에요 — 전체 시스템을 단일 컴포넌트로 생각할 수 있거든요. 핵심 차이는 컴포넌트 레벨 테스트는 개별 부분(리트리버, 생성기, 툴)에 메트릭을 정의할 수 있게 하는 반면, 종단 간은 최종 아웃풋에만 집중한다는 점이에요.

동작 방식

  1. Confident AI에서 데이터셋 가져오기
  2. 데이터셋의 골든스를 순회하며, 각 골든스에 대해:
    • 골든스 인풋으로 LLM 앱을 호출해 actual output, tools called 같은 테스트 케이스 파라미터 생성
    • 골든스 필드를 테스트 케이스 파라미터로 매핑
    • 테스트 케이스를 데이터셋에 다시 추가
  3. 테스트 케이스에 대한 평가 실행 (로컬 또는 Confident AI에서)

Confident AI에서 평가를 실행하는 방법은 다양해요

3단계를 실행하는 방법은 많아요. 로컬 평가의 경우:

  • evaluate() 함수 사용
  • LLM 트레이싱으로 .evals_iterator() 사용
  • CI/CD에서 deepeval test run 사용

원격 평가의 경우:

  • Confident API 사용
  • evaluate() 함수 사용

프로세스 전체에서 데이터가 어디로 흐르는지 시각적으로 보면:

로컬 메트릭 (Local Metrics)

sequenceDiagram
    participant Your Code
    participant Confident AI
    participant LLM App
    participant Metrics

    Your Code->>Confident AI: Pull dataset
    Confident AI-->>Your Code: List of goldens

    loop For each golden
        Your Code->>LLM App: Invoke with golden.input
        LLM App-->>Your Code: actual_output
        Your Code->>Your Code: Create test case (input + actual_output)
    end

    Your Code->>Metrics: Run evaluation locally
    Metrics-->>Confident AI: Upload results & generate testing report

원격 메트릭 (Remote Metrics)

sequenceDiagram
    participant Your Code
    participant Confident AI
    participant LLM App

    Your Code->>Confident AI: Pull dataset
    Confident AI-->>Your Code: List of goldens

    loop For each golden
        Your Code->>LLM App: Invoke with golden.input
        LLM App-->>Your Code: actual_output
        Your Code->>Your Code: Create test case (input + actual_output)
    end

    Your Code->>Confident AI: Send test cases to the Confident API
    Confident AI->>Confident AI: Run metrics remotely
    Confident AI-->>Your Code: Return testing report link

로컬에서 E2E 테스트 실행 (Run E2E Tests Locally)

로컬 평가는 Python deepeval 라이브러리를 사용할 때만 가능해요. TypeScript나 다른 언어를 사용한다면 원격 종단 간 평가 섹션으로 건너뛰세요.

이 섹션에서는 단순한 RAG 파이프라인인 이 mock LLM 앱을 사용할게요:

Mock LLM 앱 보기

from openai import OpenAI

def llm_app(query: str) -> str:
    # Retriever for your vector db
    def retriever(query: str) -> list[str]:
        return ["List", "of", "text", "chunks"]
    # Generator that combines retrieved context with user query
    def generator(query: str, text_chunks: list[str]) -> str:
        return OpenAI().chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "user", "content": query}
            ]
        ).choices[0].message.content
    # Calls retriever then generator
    return generator(query, retriever(query))

데이터셋 가져오기

데이터셋을 가져와요(아직 없다면 만들기):

from deepeval.dataset import EvaluationDataset

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

골든스를 순회하며 테스트 케이스 생성

이 단계에서는 LLM 앱을 호출하는 네이티브 for-루프로 충분해요:

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

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

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

다른 테스트 케이스 파라미터(예: retrieval_context)도 반환하고 싶다면 LLM 앱을 다시 작성해야 할 거예요. 이 문제는 다음 섹션에서 다룰게요.

evaluate()로 평가 실행

evaluate() 함수는 테스트 런을 만들고, 평가가 로컬에서 완료되면 데이터를 Confident AI에 업로드해요.

from deepeval.metrics import AnswerRelevancyMetric
from deepeval import evaluate

# Replace with your metrics
evaluate(test_cases=dataset.test_cases, metrics=[AnswerRelevancyMetric()])

완료 ✅. 새로 만들어진 공유 가능한 테스팅 리포트 링크가 보일 거예요.

  • evaluate() 함수는 모든 테스트 케이스와 메트릭에 걸쳐 테스트 스위트를 실행해요
  • 각 메트릭은 모든 테스트 케이스에 적용돼요(예: 테스트 케이스 10개 × 메트릭 2개 = 평가 20개)
  • 모든 메트릭이 통과해야 테스트 케이스가 통과해요
  • 테스트 런의 통과율은 통과한 테스트 케이스의 비율이에요

deepeval은 기본적으로 브라우저를 자동으로 열어요. 이 동작을 끄려면 CONFIDENT_BROWSER_OPEN=NO를 설정하세요.

Video

싱글턴 테스팅 리포트

evaluate() 함수는 매우 비개입적(unopinionated)이고 비침습적(non-intrusive)이어서, 가벼운 LLM 평가 접근을 찾는 팀에게 좋아요. 하지만 다음을 의미하기도 해요:

  • 테스트 케이스 필드를 매핑하기 위한 많은 ETL을 직접 처리해야 해요. 때로는 올바른 데이터를 반환하도록 LLM 앱을 다시 써야 하기도 하죠
  • 가시성 없음 — 종단 간 평가라도 LLM 앱을 디버깅할 수 있으면 좋을 텐데요

다음 섹션에서는 이 ETL 지옥을 피하고 LLM 트레이스를 종단 간 테스트에 가져오는 방법을 보여드릴게요.

E2E 평가를 위한 LLM 트레이싱 (LLM Tracing for E2E Evals)

LLM 트레이싱은 테스트 케이스 구성과 관련된 모든 문제를 해결해줘요.

이 섹션의 @observe 데코레이터는 DeepEval의 것이며, 로컬이나 CI에서 실행하는 코드 기반 평가를 위해 설계됐어요. 프로덕션에서 앱을 트레이싱하려면 confident-trace를 대신 사용하세요 — LLM 트레이싱 퀵스타트를 참고하세요. 둘 다 같은 프로젝트로 데이터를 보내므로, 개발 시 트레이스와 프로덕션 트레이스가 나란히 존재해요.

LLM 트레이싱 설정

기존 LLM 앱에 몇 줄만 추가하면 돼요(위의 예시를 사용할게요):

from openai import OpenAI
from deepeval.tracing import observe, update_current_trace

@observe()
def llm_app(query: str) -> str:

    @observe()
    def retriever(query: str) -> list[str]:
        chunks = ["List", "of", "text", "chunks"]
        update_current_trace(retrieval_context=chunks)
        return chunks

    @observe()
    def generator(query: str, text_chunks: list[str]) -> str:
        res = OpenAI().chat.completions.create(model="gpt-4o", messages=[{"role": "user", "content": query}]
        ).choices[0].message.content
        update_current_trace(input=query, output=res)
        return res

    return generator(query, retriever(query))

위 예시는 몇 개의 @observe 데코레이터만 추가해 LLM 앱을 트레이싱하는 방법을 보여줘요:

  • 각 @observe 데코레이터는 **스팬(span)**을 만들며, 이는 컴포넌트를 나타내요
  • 반면 **트레이스(trace)**는 최상위 @observe 데코레이터가 만들며, 많은 스팬/컴포넌트로 구성돼요
  • 종단 간 테스트를 실행할 때 트레이싱된 앱 어디서든 update_current_trace 함수를 호출해 테스트 케이스 파라미터를 설정할 수 있어요

지금은 LLM 트레이싱에 대해 모든 것을 배우려 애쓰지 않아도 돼요. 전용 LLM 트레이싱 섹션에서 자세히 다룰게요.

컴포넌트 레벨 테스트에 대한 다음 섹션에서는 update_current_trace 함수를 update_current_span으로 바꿔 컴포넌트 레벨에서 테스트 케이스를 구성할 거예요.

데이터셋 가져오기 및 골든스 순회

이전과 같은 방식으로 데이터셋을 가져오고 .evals_iterator()로 골든스를 순회해요. 골든스의 데이터(대부분 input)를 사용해 LLM 앱을 호출할 거예요:

from deepeval.metrics import AnswerRelevancyMetric
from deepeval.dataset import EvaluationDataset

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

for golden in dataset.evals_iterator(metrics=[AnswerRelevancyMetric()]):
    llm_app(golden.input) # Replace with your LLM app

완료 ✅. 새로 만들어진 공유 가능한 테스팅 리포트 링크가 보일 거예요. 이것이 말 그대로 종단 간 평가를 실행하는 전부예요, 게다가 Confident AI에 트레이싱이 포함된 전체 테스팅 리포트라는 추가 이점까지 있어요.

Video

싱글턴 테스팅 리포트 (with Tracing)

for 루프를 비동기로 실행할 수도 있어요:

import asyncio
from deepeval.metrics import AnswerRelevancyMetric
from deepeval.dataset import EvaluationDataset

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

for golden in dataset.evals_iterator(metrics=[AnswerRelevancyMetric()]):
    task = asyncio.create_task(a_llm_app(golden.input))
    dataset.evaluate(task)

원격에서 E2E 테스트 실행 (Run E2E Tests Remotely)

원격 종단 간 평가는 디버깅을 위한 추적성은 없지만 다음이 좋아요:

  • 팀원이 코드를 거치지 않고 메트릭을 만들 수 있어요
  • 어떤 언어든 Confident API를 통해 지원해요

이는 Confident API를 통해 가능해요.

메트릭 컬렉션 만들기

Project > Metric > Collections로 이동해요:

Video

원격 평가용 메트릭 컬렉션

데이터셋 가져오고 테스트 케이스 구성

선택한 언어로 LLM 앱을 호출해 유효한 LLMTestCase 데이터 모델 목록을 구성해요.

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:
    test_case = LLMTestCase(input=golden.input, actual_output=llm_app(golden.input))
    dataset.add_test_case(test_case)

TypeScript

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

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

  dataset.addTestCase(testCase);
}

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(())
}

/v1/evaluate 엔드포인트 호출

Python

from deepeval import evaluate

evaluate(test_case=dataset.test_cases, metric_collection="YOUR-COLLECTION-NAME")

TypeScript

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

const dataset = new EvaluationDataset();

evaluate({
  llmTestCases: dataset.testCases as LLMTestCase[],
  metricCollection: "YOUR-COLLECTION-NAME",
});

curl

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(())
}

고급 사용법 (Advanced Usage)

이제 싱글턴 종단 간 평가를 실행하는 법을 배웠으니, 함께 해야 할 몇 가지가 있어요.

프롬프트와 모델 기록 (Log prompts and models)

평가 중 LLM 앱에서 사용한 구성을 Confident AI에 알려주세요.

이는 Confident AI가 어떤 하이퍼파라미터가 사후적으로 더 잘 수행했는지 알려주는 데 도움이 돼요.

Python

evaluate() 함수의 hyperparameters 인자에 자유 형식 키-값 쌍을 추가하면 돼요:

from deepeval.prompt import Prompt

prompt = Prompt(alias="YOUR-PROMPT-ALIAS")
prompt.pull()

evaluate(
    hyperparameters={
        "Model": "YOUR-MODEL",
        "Prompt Version": prompt # An instance of your Prompt
    },
    test_cases=[...],
    metrics=[...]
)

Prompt 인스턴스를 제공하는 것은 Confident AI에서 프롬프트 버전을 가져온 경우에만 동작해요.

TypeScript

evaluate() 함수의 hyperparameters 인자에 자유 형식 키-값 쌍을 추가하면 돼요:

evaluate({
  hyperparameters: {
    Model: "YOUR-MODEL",
    "Prompt": prompt,
  },
  llmTestCases: [...],
  metricCollection: "YOUR-COLLECTION-NAME",
});

curl

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?"
  },
  "hyperparameters": {
    "model": "gpt-4o-mini",
    "prompt-version": "ai_generation_v2"
  }
}'
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?"
        },
        "hyperparameters": {
            "model": "gpt-4o-mini",
            "prompt-version": "ai_generation_v2"
        }
    },
)

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?"
    },
    "hyperparameters": {
      "model": "gpt-4o-mini",
      "prompt-version": "ai_generation_v2"
    }
  }),
});

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?"
  },
  "hyperparameters": {
    "model": "gpt-4o-mini",
    "prompt-version": "ai_generation_v2"
  }
}`

	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?"
              },
              "hyperparameters": {
                "model": "gpt-4o-mini",
                "prompt-version": "ai_generation_v2"
              }
            }""";

        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?"
          },
          "hyperparameters": {
            "model": "gpt-4o-mini",
            "prompt-version": "ai_generation_v2"
          }
        }))
        .send()
        .await?;

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

    Ok(())
}

테스트 런에 식별자 추가 (Add identifer to test runs)

identifer 인자는 테스트 런에 이름을 붙이게 해주는데, 플랫폼에서 회귀 테스트를 실행할 때 매우 유용해요.

Python

evaluate(
    identifer="Any custom string",
    test_cases=[...],
    metrics=[...]
)

TypeScript

evaluate({
  identifer: "Any custom string",
  llmTestCases: [...],
  metricCollection: "YOUR-COLLECTION-NAME",
});

curl

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?",
    "name": "Your Test Case Name"
  },
  "identifier": "run-399-102"
}'
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?",
            "name": "Your Test Case Name"
        },
        "identifier": "run-399-102"
    },
)

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?",
      "name": "Your Test Case Name"
    },
    "identifier": "run-399-102"
  }),
});

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?",
    "name": "Your Test Case Name"
  },
  "identifier": "run-399-102"
}`

	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?",
                "name": "Your Test Case Name"
              },
              "identifier": "run-399-102"
            }""";

        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?",
            "name": "Your Test Case Name"
          },
          "identifier": "run-399-102"
        }))
        .send()
        .await?;

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

    Ok(())
}

테스트 케이스 이름 짓기 (Name test cases)

식별자와 비슷하게, 테스트 케이스에 이름을 붙이면 회귀 테스트 중 서로 다른 테스트 런에서 테스트 케이스를 검색하고 매칭할 수 있어요.

Python

evaluate(
    test_cases=[LLMTestCase(name="Any custom string", ...)],
    metric_collection="..."
)

TypeScript

evaluate({
  llmTestCases: [new LLMTestCase({ name: "Any custom string", ... })],
  metricCollection: "..."
});

curl

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?",
    "name": "Your Test Case Name"
  },
  "identifier": "run-399-102"
}'
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?",
            "name": "Your Test Case Name"
        },
        "identifier": "run-399-102"
    },
)

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?",
      "name": "Your Test Case Name"
    },
    "identifier": "run-399-102"
  }),
});

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?",
    "name": "Your Test Case Name"
  },
  "identifier": "run-399-102"
}`

	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?",
                "name": "Your Test Case Name"
              },
              "identifier": "run-399-102"
            }""";

        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?",
            "name": "Your Test Case Name"
          },
          "identifier": "run-399-102"
        }))
        .send()
        .await?;

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

    Ok(())
}

기본적으로 Confident AI는 인풋 매칭을 기반으로 테스트 케이스를 매칭하므로, 회귀 테스트를 위해 테스트 케이스에 이름을 붙이는 것은 엄격히 필수는 아니에요.

더 알아보기