Ідея за 30 секунд
Unit-тести для AI-агентів перевіряють локальну логіку: вибір інструмента, обробку відповіді, stop reason і формат виходу.
Їхня головна цінність у тому, що вони швидкі, детерміновані й ізольовані, тому допомагають одразу побачити, де зламалася конкретна частина системи.
Проблема
Без unit-тестів команди часто перевіряють агентів тільки через ручні запуски або важкі end-to-end тести.
Це створює типові проблеми:
- помилка в локальній логіці знаходиться занадто пізно;
- важко зрозуміти, чи зламався саме код, чи зовнішня залежність;
- дрібні регресії накопичуються і потрапляють у реліз.
У підсумку навіть проста зміна може викликати ланцюг непрозорих збоїв у production.
Коли використовувати
Unit-тести варто писати щоразу, коли у вас є локальна і перевірювана логіка:
- вибір інструмента за типом запиту;
- валідація структури виходу;
- обробка помилок інструментів;
- умови завершення run (
stop_reason); - правила безпеки на рівні кроку або функції.
Якщо поведінку можна перевірити без мережі і без повного середовища агента, це хороший кандидат для unit-тесту.
Реалізація
На практиці unit-тестування агентів тримається на простому правилі: одна поведінка — один тест — контрольовані умови. Приклади нижче схематичні і не прив'язані до конкретного фреймворку.
Unit-рівень не підходить для оцінки загальної якості відповіді, корисності результату або "розумності" агента. Для цього краще використовувати eval harness і golden datasets.
Як це працює в одному тесті
Короткий цикл unit-тесту
- Test case — одна поведінка для перевірки.
- Setup — fakes, mocks і фіксовані умови.
- Run — запуск конкретної функції або кроку.
- Assertions — перевірка
tool choice,schema,stop reason.
1. Ізолюйте логіку рішень агента
def choose_tool(intent: str, tools_allowed: list[str]) -> str:
if intent == "price_lookup" and "crypto_price_api" in tools_allowed:
return "crypto_price_api"
return "web_search"
Чим менше побічних залежностей у функції, тим стабільніший тест.
2. Підміняйте зовнішні інструменти
class FakeTools:
def crypto_price_api(self, symbol: str):
return {"symbol": symbol, "price": 65000}
Unit-тест має перевіряти логіку агента, а не доступність зовнішніх API.
3. Перевіряйте не тільки текст відповіді
def test_tool_selection_and_schema():
tools = FakeTools()
agent = Agent(tools=tools)
result = agent.run("What is the price of BTC?")
assert result.selected_tool == "crypto_price_api"
assert isinstance(result.output, dict)
assert result.output["symbol"] == "BTC"
Краще фіксувати структурні інваріанти (selected_tool, schema, stop reason), а не лише фінальний текст.
4. Перевіряйте негативні сценарії
def test_tool_error_is_handled():
tools = FailingTools()
agent = Agent(tools=tools)
result = agent.run("Find BTC price")
assert result.stop_reason == "tool_error_handled"
assert result.error is not None
Помилки інструментів мають мати передбачувану і перевірювану поведінку.
5. Інтегруйте unit-тести в CI
name: unit-tests
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install -r requirements.txt
- run: pytest tests/unit -q
Якщо тест повільний або нестабільний, переносьте його в eval harness або інтеграційний шар.
Типові помилки
Залежність від реальних API
Тест падає не через логіку агента, а через мережу або зовнішній сервіс.
Типова причина: відсутні fakes або mocks для інструментів.
Тестування тільки фінального тексту
Тест "зелений", але не гарантує правильний вибір інструмента чи формат відповіді.
Типова причина: немає перевірок selected_tool, schema і stop reason.
Забагато логіки в одному тесті
Один тест перевіряє одразу кілька сценаріїв, і при падінні незрозуміло, що саме зламалося.
Типова причина: немає правила "один тест — одна поведінка".
Нестабільне тестове середовище
Навіть коректні unit-тести стають шумними, якщо між прогонами плавають залежності, конфігурація або підміни інструментів.
Типова причина: unit-тести частково залежать від реального runtime або зовнішніх викликів.
Спроба покрити все через e2e-прогони
Команда пише тільки великі сценарії і пропускає базові локальні перевірки.
Типова причина: немає чіткого розділення між unit, eval і regression рівнями.
Коротко
- Unit-тести для агентів перевіряють локальну і детерміновану логіку.
- Підміняйте інструменти через fakes або mocks, щоб прибрати мережевий шум.
- Фіксуйте структурні перевірки: tool choice, schema, stop reason.
- Швидкі unit-тести мають запускатись у кожному PR.
FAQ
Q: Unit-тести можуть замінити eval harness?
A: Ні. Unit-тести ловлять локальні збої, а eval harness перевіряє поведінку агента на завершених сценаріях.
Q: Чи варто підключати реальну LLM у unit-тестах?
A: Бажано мінімально. Для unit-рівня краще перевіряти детерміновану логіку через fakes або mocks і контрольовані умови.
Q: Що обов'язково перевіряти в unit-тесті агента?
A: Вибір інструмента, структуру виходу, обробку помилок і stop reason у негативних сценаріях.
Q: Коли переносити тест з unit-рівня в eval-рівень?
A: Коли тест залежить від повної поведінки агента на сценарії, метрик якості відповіді або baseline-порівнянь.
Що далі
Після unit-рівня додавайте сценарні перевірки через Eval Harness, а стабільний набір кейсів ведіть через Golden Datasets.
Для контролю змін між версіями додайте Regression Testing, а для розбору production-інцидентів використовуйте Replay and Debugging. Загальну картину тримайте у Стратегії тестування.