Ewaluacja agentów AI w Pythonie – DeepEval, Langfuse i testy jakości krok po kroku

Praktyczny poradnik ewaluacji agentów AI w Pythonie z DeepEval i Langfuse. Metryki TaskCompletion, ToolCorrectness, tracing @observe, testy komponentów i integracja z CI/CD — od pierwszego testu po monitoring produkcyjny.

Dlaczego testowanie agentów AI to nie to samo co testowanie LLM-a

Zbudowałeś agenta AI, który planuje podróże, wyszukuje dokumenty albo automatyzuje obsługę klienta. Działa — przynajmniej na tych pięciu przykładach, które sprawdziłeś ręcznie. Puszczasz go na produkcję i… zaczyna się horror. Agent wybiera złe narzędzia, halucynuje numery telefonów, zapętla się w nieskończonym cyklu rozumowania i przepala budżet API.

Brzmi znajomo? No właśnie.

Problem polega na tym, że agenci AI zawodzą w sposób fundamentalnie inny niż klasyczne aplikacje LLM. Prosty chatbot dostaje pytanie i zwraca odpowiedź — wystarczy sprawdzić, czy ta odpowiedź jest trafna. Agent natomiast rozumuje, planuje, wybiera narzędzia, przekazuje im parametry, analizuje wyniki i podejmuje kolejne decyzje. Na każdym z tych kroków może się pomylić.

Typowe tryby awarii? Jest ich sporo: wybór poprawnego narzędzia z błędnymi parametrami, stworzenie dobrego planu, którego agent potem nie realizuje, ukończenie zadania kosztem zbędnych kroków pochłaniających tokeny, albo „poprawna" odpowiedź końcowa uzyskana w wyniku totalnie wadliwego rozumowania. Pojedynczy test wejście-wyjście nie wykryje żadnego z tych problemów.

Dobra wiadomość — w 2026 roku ekosystem narzędzi do ewaluacji agentów dojrzał na tyle, że możesz zbudować pełny pipeline testowy w kilka godzin. W tym artykule pokażę Ci jak to zrobić, od pierwszej metryki po integrację z CI/CD.

Trzy poziomy ewaluacji agenta AI

Zanim napiszesz pierwszą asercję, warto się zastanowić, co właściwie testujesz. Agent AI składa się z dwóch warstw: warstwy rozumowania (LLM decyduje co robić) i warstwy akcji (narzędzia wykonują zadania). Obie współpracują iteracyjnie — i obie wymagają osobnej oceny.

Poziom 1 — Ewaluacja end-to-end (black-box)

Sprawdzasz tylko wynik końcowy: czy agent wykonał zadanie? To najprostszy poziom, ale też najgrubszy — wiesz że coś poszło nie tak, ale nie masz pojęcia gdzie.

Poziom 2 — Ewaluacja trajektorii (glass-box)

Tutaj analizujesz pełną sekwencję kroków: jakie narzędzia agent wybrał, w jakiej kolejności, jakie parametry przekazał. Dwa agenci mogą dać identyczną odpowiedź końcową, ale jeden zrobił to w trzech krokach, a drugi w dwunastu — trajektoria ujawnia tę różnicę. I szczerze mówiąc, w praktyce ten poziom daje najwięcej insightów.

Poziom 3 — Ewaluacja komponentów (white-box)

Testujesz każdy komponent z osobna: retriever, generator, router, poszczególne wywołania LLM. Najbardziej granularne podejście — pozwala zlokalizować problem z dokładnością do jednej funkcji.

Najlepsza strategia? Łącz wszystkie trzy poziomy. End-to-end daje ogólny obraz, trajektoria pokazuje „jak", a ewaluacja komponentów mówi „gdzie" tkwi błąd. Z mojego doświadczenia — zaczynanie wyłącznie od testów end-to-end to recepta na frustrację, bo wiesz że jest źle, ale nie masz pojęcia dlaczego.

DeepEval — framework do ewaluacji agentów w Pythonie

DeepEval to open-source'owy framework, który działa jak Pytest — tyle że zamiast unit testów pisze się testy jakości dla aplikacji LLM. W wersji 3.x wprowadza dekorator @observe do śledzenia trajektorii agentów oraz dedykowane metryki agentowe: TaskCompletionMetric, ToolCorrectnessMetric i ArgumentCorrectnessMetric.

Co wyróżnia DeepEval spośród alternatyw? Kilka rzeczy. Po pierwsze — ponad 50 wbudowanych metryk (od G-Eval przez faithfulness po wykrywanie halucynacji). Po drugie — zerowy narzut latencji dzięki dekoratorowi @observe. No i po trzecie — natywna integracja z CI/CD i platformą Confident AI do monitoringu produkcyjnego.

Instalacja i konfiguracja

# Instalacja DeepEval (v3.x, kwiecien 2026)
pip install deepeval

# Opcjonalnie: Langfuse do tracingu produkcyjnego
pip install langfuse

# Ustaw klucz API — DeepEval domyslnie uzywa OpenAI jako judge
export OPENAI_API_KEY="sk-proj-..."

DeepEval wymaga modelu LLM jako „sędziego" do oceny wyników. Domyślnie używa GPT-4o z OpenAI, ale możesz podłączyć dowolny model — Claude, Gemini, a nawet lokalne modele przez Ollama. Wystarczy zaimplementować klasę DeepEvalBaseLLM (to naprawdę kilkanaście linii kodu).

Śledzenie trajektorii agenta dekoratorem @observe

Dekorator @observe to absolutny fundament ewaluacji agentów w DeepEval. Dodajesz go do funkcji agenta i jego komponentów — framework automatycznie mapuje pełne drzewo wywołań (trace), bez żadnego wpływu na wydajność.

Zobaczmy to na przykładzie agenta obsługi klienta:

from deepeval.tracing import observe, update_current_span
from deepeval.test_case import LLMTestCase

@observe()
def support_agent(user_query: str) -> str:
    """Glowna funkcja agenta obslugi klienta."""

    # Komponent 1: Wyszukiwanie w bazie wiedzy
    @observe()
    def search_knowledge_base(query: str) -> list[str]:
        # Tutaj logika retrievera (np. ChromaDB, Pinecone)
        results = vector_store.similarity_search(query, k=3)
        return [doc.page_content for doc in results]

    # Komponent 2: Klasyfikacja intencji
    @observe()
    def classify_intent(query: str) -> str:
        response = openai_client.chat.completions.create(
            model="gpt-4.1-nano",
            messages=[
                {"role": "system", "content": "Sklasyfikuj intencje uzytkownika: "
                 "zwrot, reklamacja, pytanie_o_produkt, inne"},
                {"role": "user", "content": query}
            ]
        )
        intent = response.choices[0].message.content.strip()

        # Przypisz test case do tego komponentu
        update_current_span(
            test_case=LLMTestCase(
                input=query,
                actual_output=intent,
                expected_output="reklamacja"  # oczekiwana intencja
            )
        )
        return intent

    # Komponent 3: Generowanie odpowiedzi
    @observe()
    def generate_response(query: str, context: list[str], intent: str) -> str:
        context_text = "\n".join(context)
        response = openai_client.chat.completions.create(
            model="gpt-4.1-nano",
            messages=[
                {"role": "system", "content": f"Jestes agentem obslugi klienta. "
                 f"Intencja uzytkownika: {intent}\n"
                 f"Kontekst z bazy wiedzy:\n{context_text}"},
                {"role": "user", "content": query}
            ]
        )
        return response.choices[0].message.content

    # Orkiestracja agenta
    context = search_knowledge_base(user_query)
    intent = classify_intent(user_query)
    answer = generate_response(user_query, context, intent)

    return answer

Każda funkcja oznaczona @observe() staje się osobnym spanem w trace. DeepEval buduje z nich drzewo — widzisz dokładnie, jakie komponenty zostały wywołane, w jakiej kolejności i co zwróciły.

Co mi się tu podoba? Dodanie @observe to dosłownie jedna linia kodu na komponent, zero zmian w logice biznesowej. Nie musisz przepisywać agenta ani zmieniać architektury.

Metryka TaskCompletionMetric — czy agent wykonał zadanie?

To najważniejsza metryka end-to-end. Prosto: czy agent zrealizował to, o co prosił użytkownik? TaskCompletionMetric analizuje cały trace agenta — nie tylko odpowiedź końcową, ale też kroki pośrednie — i ocenia stopień ukończenia zadania.

from deepeval.tracing import observe
from deepeval.dataset import Golden, EvaluationDataset
from deepeval.metrics import TaskCompletionMetric

# Zdefiniuj agenta z pelnym tracingiem
@observe()
def travel_planner_agent(user_input: str) -> str:
    """Agent planujacy podroze — przykladowa implementacja."""

    @observe()
    def find_flights(destination: str, date: str) -> list[dict]:
        # Symulacja wyszukiwania lotow
        return [
            {"airline": "LOT", "price": 450, "departure": "08:00"},
            {"airline": "Ryanair", "price": 180, "departure": "14:30"}
        ]

    @observe()
    def find_hotels(destination: str, checkin: str, nights: int) -> list[dict]:
        return [
            {"name": "Hotel Marais", "price_per_night": 120, "rating": 4.5},
            {"name": "Le Petit Paris", "price_per_night": 85, "rating": 4.2}
        ]

    @observe()
    def create_itinerary(destination: str, days: int) -> list[str]:
        return [
            "Dzien 1: Wieza Eiffla, rejs po Sekwanie",
            "Dzien 2: Luwr, dzielnica Marais, Montmartre"
        ]

    flights = find_flights("Paryz", "2026-05-15")
    hotels = find_hotels("Paryz", "2026-05-15", 2)
    itinerary = create_itinerary("Paryz", 2)

    return (
        f"Znalazlem loty do Paryza (najtanszy: {flights[1]['price']} PLN), "
        f"hotele (najlepszy: {hotels[0]['name']}), "
        f"i plan na {len(itinerary)} dni."
    )


# === Uruchomienie ewaluacji ===

# Krok 1: Przygotuj dataset z przypadkami testowymi
dataset = EvaluationDataset(goldens=[
    Golden(input="Zaplanuj 2-dniowa podroz do Paryza na 15 maja"),
    Golden(input="Znajdz najtanszy lot do Barcelony na weekend"),
    Golden(input="Zarezerwuj hotel w Krakowie na 3 noce blisko Rynku"),
])

# Krok 2: Zdefiniuj metryke
task_completion = TaskCompletionMetric(
    threshold=0.7,   # Minimalna ocena: 0.7 z 1.0
    model="gpt-4o"   # Model uzyty jako sedzia
)

# Krok 3: Iteruj po datasecie i ewaluuj
for golden in dataset.evals_iterator(metrics=[task_completion]):
    result = travel_planner_agent(golden.input)
    print(f"Input: {golden.input}")
    print(f"Score: {task_completion.score}")
    print(f"Reason: {task_completion.reason}")
    print("---")

TaskCompletionMetric automatycznie wyciąga z trace cel zadania (co użytkownik chciał osiągnąć) i wynik (co agent faktycznie zrobił), a następnie ocenia stopień dopasowania w skali 0–1. Próg 0.7 oznacza, że agent musi ukończyć zadanie w co najmniej 70%, żeby test przeszedł. W praktyce dla systemów produkcyjnych ustawiam ten próg na 0.8 albo wyżej.

Metryka ToolCorrectnessMetric — czy agent wybrał właściwe narzędzia?

Agent może dać „poprawną" odpowiedź, ale wybrać kompletnie złe narzędzia — na przykład szukać hoteli zamiast lotów. ToolCorrectnessMetric porównuje wywołania narzędzi agenta z oczekiwaną listą i ocenia trafność wyboru.

from deepeval.metrics import ToolCorrectnessMetric
from deepeval.test_case import LLMTestCase, ToolCall, ToolCallParams

# Zdefiniuj oczekiwane wywolania narzedzi
expected_tools = [
    ToolCall(name="find_flights", input_parameters={"destination": "Paryz", "date": "2026-05-15"}),
    ToolCall(name="find_hotels", input_parameters={"destination": "Paryz", "checkin": "2026-05-15", "nights": 2}),
    ToolCall(name="create_itinerary", input_parameters={"destination": "Paryz", "days": 2}),
]

# Konfiguracja metryki
tool_correctness = ToolCorrectnessMetric(
    threshold=0.5,
    evaluation_params=[
        ToolCallParams.INPUT_PARAMETERS  # Sprawdz tez parametry, nie tylko nazwy
    ],
    should_consider_ordering=True,  # Kolejnosc wywolan ma znaczenie
)

# Utworz test case
test_case = LLMTestCase(
    input="Zaplanuj 2-dniowa podroz do Paryza na 15 maja",
    actual_output="Plan podrozy do Paryza...",
    expected_tools=expected_tools,
    tools_called=[
        ToolCall(name="find_flights", input_parameters={"destination": "Paryz", "date": "2026-05-15"}),
        ToolCall(name="find_hotels", input_parameters={"destination": "Paryz", "checkin": "2026-05-15", "nights": 2}),
        ToolCall(name="create_itinerary", input_parameters={"destination": "Paryz", "days": 2}),
    ]
)

# Zmierz
tool_correctness.measure(test_case)
print(f"Tool Correctness Score: {tool_correctness.score}")
print(f"Reason: {tool_correctness.reason}")

Parametr should_consider_ordering=True sprawia, że kolejność wywołań narzędzi ma znaczenie — agent musi najpierw znaleźć loty, potem hotele, a na końcu stworzyć plan. Jeśli Twój agent może wywoływać narzędzia w dowolnej kolejności (co zdarza się częściej niż myślisz), ustaw tę flagę na False.

Własne metryki z G-Eval — testuj to, co naprawdę ważne

Wbudowane metryki pokrywają typowe scenariusze, ale Twój agent pewnie ma unikalne wymagania. Tu z pomocą przychodzi G-Eval — pozwala stworzyć dowolną metrykę opisując ją po prostu w języku naturalnym. DeepEval automatycznie generuje kryteria oceny przy użyciu chain-of-thought.

Szczerze? To jedna z moich ulubionych funkcji w DeepEval.

from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCase, LLMTestCaseParams

# Metryka: czy odpowiedz jest po polsku i profesjonalna?
polish_quality = GEval(
    name="PolishProfessionalQuality",
    criteria=(
        "Sprawdz, czy odpowiedz jest napisana poprawna polszczyzna, "
        "czy ton jest profesjonalny ale przyjazny, "
        "i czy nie zawiera anglicyzmow tam, gdzie istnieje polski odpowiednik."
    ),
    evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT],
    threshold=0.7,
    model="gpt-4o"
)

# Metryka: czy agent podal konkretne dane liczbowe?
specificity = GEval(
    name="ResponseSpecificity",
    evaluation_steps=[
        "Sprawdz, czy odpowiedz zawiera konkretne liczby (ceny, daty, oceny)",
        "Zweryfikuj, czy podane dane sa sprecyzowane, a nie ogolnikowe",
        "Odejmij punkty za zwroty typu 'okolo', 'mniej wiecej', 'kilka'"
    ],
    evaluation_params=[
        LLMTestCaseParams.INPUT,
        LLMTestCaseParams.ACTUAL_OUTPUT
    ],
    threshold=0.6
)

# Uzycie
test_case = LLMTestCase(
    input="Ile kosztuje lot do Paryza na 15 maja?",
    actual_output="Najtanszy lot LOT-em kosztuje 450 PLN z odlotem o 8:00, "
                  "a Ryanair oferuje polaczenie za 180 PLN z odlotem o 14:30."
)

polish_quality.measure(test_case)
specificity.measure(test_case)

print(f"Polish Quality: {polish_quality.score} — {polish_quality.reason}")
print(f"Specificity: {specificity.score} — {specificity.reason}")

G-Eval jest szczególnie przydatny do metryk subiektywnych — jakość języka, ton odpowiedzi, poziom szczegółowości — gdzie deterministyczne reguły po prostu nie wystarczą. Dla metryk obiektywnych (format JSON, obecność wymaganych pól) lepiej użyć klasycznych asercji w kodzie. Nie wszystko musi być LLM-as-a-judge.

Ewaluacja komponentów — testuj retriever, router i generator z osobna

Ewaluacja end-to-end mówi Ci „coś nie działa". To za mało.

Ewaluacja komponentów mówi „retriever zwraca nieistotne dokumenty" albo „router źle klasyfikuje 30% zapytań o reklamacje". Taka precyzja jest bezcenna przy debugowaniu — zamiast grzebać w całym agencie, od razu wiesz gdzie szukać.

from deepeval.tracing import observe, update_current_span
from deepeval.test_case import LLMTestCase
from deepeval.dataset import Golden, EvaluationDataset
from deepeval.metrics import (
    AnswerRelevancyMetric,
    FaithfulnessMetric,
    GEval,
)
from deepeval.test_case import LLMTestCaseParams

# Metryka dla retrievera: czy pobrane dokumenty sa istotne?
retrieval_relevancy = AnswerRelevancyMetric(threshold=0.7)

# Metryka dla generatora: czy odpowiedz jest wierna kontekstowi?
faithfulness = FaithfulnessMetric(threshold=0.8)

# Metryka dla routera: czy intencja jest poprawna?
intent_accuracy = GEval(
    name="IntentClassificationAccuracy",
    criteria="Sprawdz, czy sklasyfikowana intencja dokladnie odpowiada "
             "tresci zapytania uzytkownika.",
    evaluation_params=[
        LLMTestCaseParams.INPUT,
        LLMTestCaseParams.ACTUAL_OUTPUT,
        LLMTestCaseParams.EXPECTED_OUTPUT,
    ],
    threshold=0.8
)


@observe()
def customer_support_agent(query: str) -> str:
    """Agent z ewaluacja na poziomie komponentow."""

    @observe(metrics=[retrieval_relevancy])
    def retrieve_docs(q: str) -> list[str]:
        docs = vector_store.similarity_search(q, k=3)
        contents = [d.page_content for d in docs]

        update_current_span(test_case=LLMTestCase(
            input=q,
            actual_output="\n".join(contents),
            retrieval_context=contents
        ))
        return contents

    @observe(metrics=[intent_accuracy])
    def classify_intent(q: str) -> str:
        # ... wywolanie LLM ...
        intent = "reklamacja"

        update_current_span(test_case=LLMTestCase(
            input=q,
            actual_output=intent,
            expected_output="reklamacja"
        ))
        return intent

    @observe(metrics=[faithfulness])
    def generate_answer(q: str, ctx: list[str]) -> str:
        answer = "Przepraszamy za niedogodnosci..."

        update_current_span(test_case=LLMTestCase(
            input=q,
            actual_output=answer,
            retrieval_context=ctx
        ))
        return answer

    docs = retrieve_docs(query)
    intent = classify_intent(query)
    return generate_answer(query, docs)


# Uruchom ewaluacje
dataset = EvaluationDataset(goldens=[
    Golden(input="Moj produkt przyszedl uszkodzony, chce zlozyc reklamacje")
])

for golden in dataset.evals_iterator():
    customer_support_agent(golden.input)

Zwróć uwagę na wzorzec: każdy komponent ma swoją metrykę i swój LLMTestCase. Retriever jest oceniany pod kątem trafności pobranych dokumentów, router — poprawności klasyfikacji intencji, a generator — wierności wobec kontekstu. Jeden przebieg agenta generuje trzy osobne oceny.

Dokładnie wiesz, który komponent wymaga poprawy. Koniec z zgadywaniem.

Langfuse — obserwowalność i monitoring produkcyjny

DeepEval świetnie sprawdza się w fazie developmentu i CI/CD. Ale na produkcji potrzebujesz czegoś więcej — ciągłego monitoringu każdego wywołania agenta, alertów o spadku jakości i dashboardów z trendami. Do tego służy Langfuse — open-source'owa platforma obserwowalności dla aplikacji LLM.

Można to porównać do relacji między unit testami a Grafaną — jedno testuje, drugie monitoruje. Potrzebujesz obu.

Konfiguracja Langfuse z OpenTelemetry

import os
import base64

# Konfiguracja Langfuse (self-hosted lub cloud)
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com"

# Konfiguracja OpenTelemetry (Langfuse jako backend)
LANGFUSE_AUTH = base64.b64encode(
    f"{os.environ['LANGFUSE_PUBLIC_KEY']}:{os.environ['LANGFUSE_SECRET_KEY']}".encode()
).decode()

os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = (
    os.environ["LANGFUSE_HOST"] + "/api/public/otel"
)
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {LANGFUSE_AUTH}"

Tworzenie datasetu ewaluacyjnego w Langfuse

from langfuse import get_client

langfuse = get_client()

# Sprawdz polaczenie
if langfuse.auth_check():
    print("Langfuse polaczony!")

# Utworz dataset do ewaluacji agenta
langfuse.create_dataset(
    name="support-agent-eval-v1",
    description="Przypadki testowe dla agenta obslugi klienta",
    metadata={"version": "1.0", "date": "2026-04-06"}
)

# Dodaj przypadki testowe
test_cases = [
    {
        "input": {"question": "Chce zwrocic buty kupione tydzien temu"},
        "expected": {
            "intent": "zwrot",
            "response_facts": ["14 dni na zwrot", "formularz zwrotu", "darmowa przesylka"]
        }
    },
    {
        "input": {"question": "Paczka nie dotarla, numer sledzenia XYZ123"},
        "expected": {
            "intent": "reklamacja",
            "response_facts": ["sprawdzenie statusu", "numer sledzenia", "kontakt z kurierem"]
        }
    },
]

for i, tc in enumerate(test_cases):
    langfuse.create_dataset_item(
        dataset_name="support-agent-eval-v1",
        input=tc["input"],
        expected_output=tc["expected"]
    )

Łączenie DeepEval z Langfuse

Prawdziwa siła pojawia się, gdy połączysz oba narzędzia. DeepEval ocenia jakość odpowiedzi, Langfuse przechowuje trace i wyniki ewaluacji — dzięki temu masz pełną historię każdego wywołania agenta wraz z oceną jakości. Takie combo to chyba najlepsze, co możesz mieć w stacku ewaluacyjnym na dzień dzisiejszy.

from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCase, LLMTestCaseParams

# Zdefiniuj metryki DeepEval
correctness = GEval(
    name="Correctness",
    evaluation_steps=[
        "Sprawdz, czy odpowiedz zawiera wymagane fakty",
        "Odejmij punkty za brakujace kluczowe informacje",
        "Zaakceptuj parafrazy i roznice stylistyczne"
    ],
    evaluation_params=[
        LLMTestCaseParams.ACTUAL_OUTPUT,
        LLMTestCaseParams.EXPECTED_OUTPUT
    ],
    threshold=0.7
)


def evaluate_trace_with_deepeval(trace) -> dict:
    """Funkcja ewaluacyjna: pobiera trace z Langfuse, ocenia DeepEval-em."""

    test_case = LLMTestCase(
        input=trace.input["question"],
        actual_output=trace.output,
        expected_output=str(trace.expected_output.get("response_facts", []))
    )

    correctness.measure(test_case)

    return {
        "score": correctness.score,
        "reason": correctness.reason
    }


# W Langfuse mozesz zarejestrowac te funkcje jako evaluatory
# i uruchamiac je automatycznie na kazdym nowym uzyciu agenta

Integracja z CI/CD — testy jakości na każdy commit

Ewaluacja ręczna to dobry start, ale prawdziwe bezpieczeństwo daje automatyzacja. Na szczęście DeepEval integruje się z pytest, więc możesz uruchamiać testy jakości agenta jako część pipeline'u CI/CD — na każdy push i przed każdym deploymentem.

Plik testowy: test_agent_quality.py

# test_agent_quality.py
import pytest
from deepeval import assert_test
from deepeval.test_case import LLMTestCase, ToolCall
from deepeval.metrics import (
    TaskCompletionMetric,
    ToolCorrectnessMetric,
    GEval,
)
from deepeval.test_case import LLMTestCaseParams
from deepeval.tracing import observe
from deepeval.dataset import Golden, EvaluationDataset

# Import Twojego agenta
from my_agent import support_agent


# --- Metryki ---
task_completion = TaskCompletionMetric(threshold=0.7, model="gpt-4o")

polish_tone = GEval(
    name="PolishTone",
    criteria="Odpowiedz musi byc w jezyku polskim, profesjonalna i empatyczna.",
    evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT],
    threshold=0.7
)


# --- Testy end-to-end ---
class TestSupportAgentEndToEnd:
    """Testy end-to-end: czy agent realizuje zadania?"""

    @observe()
    def test_return_request(self):
        dataset = EvaluationDataset(goldens=[
            Golden(input="Chce zwrocic buty kupione 3 dni temu")
        ])
        for golden in dataset.evals_iterator(metrics=[task_completion]):
            support_agent(golden.input)

    @observe()
    def test_complaint_handling(self):
        dataset = EvaluationDataset(goldens=[
            Golden(input="Produkt przyszedl uszkodzony, zadaje reklamacje")
        ])
        for golden in dataset.evals_iterator(metrics=[task_completion]):
            support_agent(golden.input)


# --- Testy jakosci odpowiedzi ---
class TestResponseQuality:
    """Testy jakosci: ton, jezyk, kompletnosc."""

    def test_polish_professional_tone(self):
        result = support_agent("Gdzie jest moja paczka?")
        test_case = LLMTestCase(
            input="Gdzie jest moja paczka?",
            actual_output=result
        )
        assert_test(test_case, [polish_tone])

Konfiguracja GitHub Actions

# .github/workflows/agent-evals.yml
name: Agent Quality Evals

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install deepeval

      - name: Run agent evaluation suite
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: deepeval test run tests/test_agent_quality.py

      - name: Upload results to Confident AI
        if: always()
        env:
          CONFIDENT_API_KEY: ${{ secrets.CONFIDENT_API_KEY }}
        run: deepeval login --confident-api-key $CONFIDENT_API_KEY

Polecenie deepeval test run działa jak pytest — uruchamia wszystkie testy, zbiera wyniki metryk i zwraca kod wyjścia 0 (sukces) lub 1 (porażka). Jeśli jakikolwiek test nie osiągnie zdefiniowanego progu, pipeline CI się zatrzyma. Dokładnie tak, jak przy padającym unit teście — i dokładnie o to chodzi.

Praktyczna checklista — od zera do produkcyjnej ewaluacji

Dobra, podsumujmy to w konkretne kroki do wdrożenia:

  1. Dodaj @observe do agenta i jego komponentów — zero zmian w logice, pełna widoczność trajektorii. Zacznij od tego, bo to trwa 5 minut.
  2. Zdefiniuj 3–5 metryk — minimum to TaskCompletionMetric (end-to-end) plus jedna metryka komponentowa (np. ToolCorrectnessMetric lub FaithfulnessMetric). Nie przesadzaj na starcie — lepiej mieć trzy dobre metryki niż piętnaście byle jakich.
  3. Zbuduj dataset testowy — zacznij od 10–20 przypadków pokrywających najczęstsze i najbardziej krytyczne scenariusze. Dodawaj przypadki z błędów produkcyjnych w miarę ich odkrywania.
  4. Uruchamiaj testy w CI/CDdeepeval test run na każdy push. Blokuj deploy, jeśli metryki spadną poniżej progu.
  5. Skonfiguruj Langfuse na produkcji — trace każdego wywołania, alerty o spadku jakości, dashboardy z trendami.
  6. Iteruj — analizuj przypadki, w których agent zawodzi, dodawaj nowe test case'y, dostrajaj progi metryk. To nie jest projekt „ustaw i zapomnij".

Często zadawane pytania

Czym różni się ewaluacja agenta AI od testowania zwykłego LLM-a?

Testowanie LLM-a sprawdza jedną parę wejście-wyjście — czy odpowiedź jest trafna, wierna kontekstowi i bezpieczna. Ewaluacja agenta wymaga analizy całej trajektorii: sekwencji rozumowania, wyboru narzędzi, parametrów wywołań i kroków pośrednich. Agent może dać poprawną odpowiedź końcową mimo wadliwego rozumowania — i odwrotnie. Dlatego potrzebujesz metryk na trzech poziomach: end-to-end, trajektorii i komponentów.

Czy DeepEval jest darmowy i open-source?

Tak — DeepEval jest w pełni open-source (licencja Apache 2.0) i darmowy do użytku lokalnego. Wszystkie metryki, tracing i integracja z CI/CD działają bez płatnego konta. Opcjonalna platforma Confident AI dodaje dashboardy, monitoring produkcyjny i współpracę zespołową — ale sam framework ewaluacyjny jest bezpłatny. Warto to podkreślić, bo w tym segmencie rynku sporo narzędzi próbuje wciągnąć Cię w płatny plan od pierwszego dnia.

Jakie metryki wybrać na start ewaluacji agenta?

Na początek wystarczą trzy metryki: TaskCompletionMetric (czy agent wykonuje zadanie), ToolCorrectnessMetric (czy wybiera właściwe narzędzia) i jedna metryka jakości odpowiedzi — np. FaithfulnessMetric dla agentów RAG albo niestandardowa metryka G-Eval dla specyficznych wymagań. Rozbudowuj zestaw stopniowo, w miarę odkrywania nowych trybów awarii.

Jak zintegrować DeepEval z Langfuse do monitoringu produkcyjnego?

Langfuse zbiera trace z produkcji przez OpenTelemetry, a DeepEval ocenia je asynchronicznie. Skonfiguruj Langfuse jako backend OTEL, a następnie uruchamiaj metryki DeepEval na pobranych trace'ach — ręcznie, cyklicznie (np. co godzinę) albo jako webhook wywoływany przy każdym nowym trace. Wyniki ewaluacji zapisujesz z powrotem w Langfuse jako scores, co daje pełny obraz jakości agenta w czasie.

Ile przypadków testowych potrzebuję, żeby ewaluacja była wiarygodna?

Zacznij od 10–20 przypadków pokrywających najczęstsze scenariusze i najważniejsze edge case'y. To wystarczy do złapania oczywistych regresji. Z czasem rozbuduj dataset do 50–100+ przypadków, dodając realne przykłady z produkcyjnych awarii. Kluczowe jest pokrycie różnorodności — nie 50 wariantów tego samego pytania, ale reprezentatywna próbka wszystkich typów zapytań, z jakimi agent się spotyka.

O Autorze Editorial Team

Our team of expert writers and editors.