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:
- Dodaj
@observedo agenta i jego komponentów — zero zmian w logice, pełna widoczność trajektorii. Zacznij od tego, bo to trwa 5 minut. - Zdefiniuj 3–5 metryk — minimum to
TaskCompletionMetric(end-to-end) plus jedna metryka komponentowa (np.ToolCorrectnessMetriclubFaithfulnessMetric). Nie przesadzaj na starcie — lepiej mieć trzy dobre metryki niż piętnaście byle jakich. - 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.
- Uruchamiaj testy w CI/CD —
deepeval test runna każdy push. Blokuj deploy, jeśli metryki spadną poniżej progu. - Skonfiguruj Langfuse na produkcji — trace każdego wywołania, alerty o spadku jakości, dashboardy z trendami.
- 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.