Developer Farm: Як я зробив AI-конвеєр, який фізично не може «обдурити» тести
TL;DR: Зібрав на старому залізі (Ryzen 3 + GTX 1050 Ti 4GB) автономний AI-конвеєр, який пише код за специфікацією та верифікує його через архітектурну ізоляцію. Результат: фіча за 26 секунд, вартість $0.03, PASS з першої ітерації. Жодних SaaS-підписок, жодного vendor lock-in. Код відкритий: github.com/illyar80/developer-farm
Проблема: Закон Гудхарта в епоху LLM
Пів року експериментую з автономною AI-розробкою. Пробував усе: Cursor, Windsurf, Devin, Aider, SWE-agent. Щоразу впирався в одну стіну — закон Гудхарта:
«Коли міра стає ціллю, вона перестає бути хорошою мірою» — Чарльз Гудхарт
Коли агент бачить тести та acceptance criteria, він неминуче оптимізує код під проходження тестів, а не під вирішення задачі. Комерційні інструменти борються з цим через промпти та пост-рев’ю. Але промпти — це дисциплінарний захід, а не архітектурна гарантія. Агент розумніший за промпт.
Мені потрібна була система, де фізично неможливо порушити чесність коду. Де порушення ізоляції — це не warning у логах, а помилка типізації на етапі компіляції графа.
Так народився Developer Farm.
Рішення: 4-шарова архітектура з жорсткими контрактами
Ідея не нова — у теорії систем це називається separation of concerns на стероїдах. Я просто довів її до логічного кінця для AI-розробки.
PLANNING → TaskInput (що зробити) ↓ TypedDict без критеріїв EXECUTION → CodeArtifact (git diff) ↓ TypedDict без worker_id VERIFICATION → Verdict (score + reason) ↓ Тільки агрегати OPTIMIZATION → Proposal (через human-in-the-loop)
Критично не те, що кожен шар робить, а чого він НЕ БАЧИТЬ:
| Шар | Бачить | ⛔ НЕ бачить |
|---|---|---|
| Planning | user-spec, codebase | результати, вердикти |
| Execution | task description, context files | acceptance criteria, тести, рубрику |
| Verification | git diff, рубрика | worker_id, task description, автора |
| Optimization | агреговані метрики | вміст артефактів, raw logs |
Це не філософська концепція. Це TypedDict контракти, які LangGraph перевіряє на кожному переході. Спроба передати acceptance_criteria у Execution — це ValueError: 🚫 GOODHART VIOLATION.
Стек: чому саме це
LangGraph, а не CrewAI / Temporal / чистий Python
Пробував усе. Для моїх вимог LangGraph — єдиний, хто одночасно:
- Закріплює ізоляцію в типах.
TypedDictдля кожного вузла. - Підтримує цикли валідації нативно. Методологія вимагає до 3 ітерацій
validator → revise— це одне conditional edge. - Влізає в 16GB RAM. Overhead ~50MB.
- Дає streaming з коробки.
astream_events(v2)мапиться на SSE для дашборду. - Вбудований persistence. SQLite checkpointer замінює мій кустарний скрипт відновлення.
Ollama + Qwen2.5-Coder-3B, а не vLLM
Спочатку намагався підняти vLLM. На GTX 1050 Ti (Pascal, без тензорних ядер) це біль: або OOM, або 3 tok/s. Перейшов на Ollama — вона через llama.cpp автоматично балансує шари між VRAM та RAM. Результат: стабільні
OpenRouter для Planning та Verification
Локальна 3B чудова для генерації коду, але слабка для планування архітектури та code review. Для цих шарів використовую Qwen-Max та Qwen-Turbo через OpenRouter: planning ~$0.02/виклик, verification ~$0.01/виклик.
Реальні результати
Запустив тестову фічу: «Python-модуль з функціями add, subtract, multiply, divide, обробка ділення на нуль, type hints, docstrings».
Перший прогон
══════════════════════════════════════════════════════════════════ 📊 ПІДСУМКОВА СТАТИСТИКА ══════════════════════════════════════════════════════════════════ Метрика Значення ──────────────────────────────────────────────────────────────── Результат ✅ PASS Ітерацій 1 Загальний час 26.4s Planning 4.5s Execution 12.8s Verification 9.0s 💰 Загальна вартість $0.030 ══════════════════════════════════════════════════════════════════
Вердикт верифікатора (score 0.97):
«Code correctly implements all four arithmetic operations. Type hints complete. Division by zero handled with ValueError. Docstrings present. Minor PEP 8 issue: one blank line instead of two between top-level functions.»
Прогон з retry loop
Навмисно ускладнив задачу, щоб активувати retry. Система зробила 4 спроби, кожна — у своєму git branch:
$ git log --all --oneline b9cc6f1 (agent/task-002-fd010dae) feat(task-002): Create Python module... a97190e (agent/task-002-ffceae8a) feat(task-002): Create Python module... cf3dbeb (agent/task-002-5a9e50aa) feat(task-002): Create Python module... 98012a5 (agent/task-002-3be77fac) feat(task-002): Create Python module... 4dbd63f (HEAD -> master) chore: initial commit
Це і є Goodhart-proof у дії: воркер на кожній ітерації не бачить, чому попередня провалилася в деталях. Натомість отримує абстрактний feedback:
«Code quality needs improvement. Consider adding documentation for public APIs.»
Без конкретних термінів на кшталт «docstring» або «min_score=0.7». Інакше воркер почне формально відповідати рубриці, а не вирішувати задачу.
Економіка: $0.03 проти $30
| Стаття витрат | Вартість |
|---|---|
| Planning (Qwen-Max API) | $0.020 |
| Execution (локально через Ollama) | $0.000 |
| Verification (Qwen-Turbo API) | $0.010 |
| Разом за фічу | $0.030 |
Для порівняння:
- Cursor Pro: $20/міс за ~50 фіч = $0.40/фічу (13x дорожче)
- Devin: $500/міс = $10+/фічу при активній роботі (330x дорожче)
Локальний інференс Execution обнуляє основну статтю витрат. У SaaS-моделі Execution — це
Порівняння з конкурентами
| Аспект | Cursor / Windsurf | Devin | Factory.ai | Developer Farm |
|---|---|---|---|---|
| Захист від Гудхарта | Промпти + review | Непрозорий | Частковий | Архітектурний (типи) |
| Методологія | Chat-first | Task-driven | Task-driven | Spec-driven |
| Контроль | Code Review постфактум | Чорна скринька | Чорна скринька | Затвердження ДО кодингу |
| Моделі | Пропрієтарні | Пропрієтарні | Пропрієтарні | Будь-які (swap у конфізі) |
| Дані | Хмара вендора | Хмара | Хмара | Локально / свій VPC |
| Ціна за фічу | $0.40 | $10+ | $5-10 | $0.03 |
Я не конкурую з ними напряму. Вони продають «AI-співробітника». Я будую «AI-завод із гарантією якості». Перше — для тих, хто хоче делегувати. Друге — для інженерів, які відмовляються делегувати розуміння.
Ключовий код: межі, які не можна порушити
Ось як виглядає захист на рівні контрактів:
# contracts.py
from typing import TypedDict
from typing_extensions import NotRequired
class TaskInput(TypedDict):
"""Що отримує воркер від Planning."""
task_id: str
description: str # Технічний опис
context_files: list[str]
language: str
target_path: str
# ⛔ НЕМАЄ: acceptance_criteria, test_cases, rubric
class CodeArtifact(TypedDict):
"""Запечатаний артефакт від воркера."""
artifact_id: str
task_id: str
files_changed: list[str]
git_diff: str
logs: str
worktree_path: NotRequired[str]
branch_name: NotRequired[str]
# ⛔ НЕМАЄ: worker_id, task_description, original_prompt
FORBIDDEN_IN_TASK_INPUT = {"acceptance_criteria", "test_cases", "rubric"}
FORBIDDEN_IN_ARTIFACT = {"worker_id", "task_description", "original_prompt"}
def seal_task_for_execution(task: dict) -> TaskInput:
"""Planning → Execution: обрізаємо все зайве."""
leaked = set(task.keys()) & FORBIDDEN_IN_TASK_INPUT
if leaked:
raise ValueError(f"🚫 GOODHART VIOLATION: {leaked}")
return {k: v for k, v in task.items() if k in TaskInput.__annotations__}
Кожен перехід між шарами проходить через функцію seal_*. Спроба передати заборонене поле — це не тихий warning, а миттєвий fail.
Git worktrees: ізоляція на рівні VCS
Кожен воркер отримує свій git worktree та branch agent/{task_id}-{artifact_id}. При FAIL worktree автоматично видаляється (cleanup у verification_node). При PASS — залишається для merge.
$ git worktree list /opt/developer-farm abc1234 [master] /opt/developer-farm/worktrees/fd010dae b9cc6f1 [agent/task-002-fd010dae]
Reconciler: Kubernetes-style control loop
Фоновий процес, який кожні 10 секунд:
- Перевіряє heartbeat всіх threads
- При timeout >120s — resume з checkpoint
- Захист від infinite retry (max 3 спроби)
- Автоматичне відновлення після збоїв
Це перетворює систему з «працює поки дивишся» на «працює само».
Швидкий старт
Вимоги
- Ubuntu 22.04 (протестовано)
- Python 3.11+
- NVIDIA GPU з 4GB+ VRAM (протестовано на GTX 1050 Ti)
- 16GB RAM
- OpenRouter API key (отримати безкоштовно)
Встановлення
git clone https://github.com/illyar80/developer-farm.git cd developer-farm # Встановлення всіх залежностей, Ollama, моделей chmod +x bootstrap.sh ./bootstrap.sh # Активація оточення source venv/bin/activate # Налаштування API ключів cp .env.example .env nano .env # Додайте OPENROUTER_API_KEY
Використання
1. Створіть специфікацію:
mkdir -p work/my-feature cat > work/my-feature/user-spec.md << 'SPEC' # Feature: JWT Auth Implement login and token refresh with FastAPI. Constraints: Python 3.11+, RS256, rate limiting. SPEC
2. Запустіть пайплайн:
python -m graph.graph work/my-feature/user-spec.md
3. Перевірте та змерджте:
# Перегляд згенерованого branch git log agent/task-001-* --oneline # Diff від master git diff master...agent/task-001- # Merge git merge agent/task-001- --no-ff
4. Моніторинг через дашборд (опціонально):
# Термінал 1 python -m dashboard.server # Термінал 2 python -m graph.graph work/my-feature/user-spec.md # Відкрийте http://localhost:8080
Що далі
У планах:
- OPTIMIZATION шар — самоеволюція системи через human-in-the-loop proposals
- Підтримка інших LLM — Anthropic, OpenAI, локальні GGUF
- Веб-інтерфейс для написання специфікацій (інтеграція зі Storyblok)
- Kubernetes deployment для масштабування
Висновок
AI-агенти без архітектурних меж деградують. Закон Гудхарта не зник із появою LLM — він просто став працювати швидше. Інженерна відповідь на це — не розумніші промпти, а чесніша архітектура.
Якщо ви будуєте AI-системи і стикалися з проблемою оптимізації агентів під метрику — діліться досвідом у коментарях. Як ви вирішуєте цю проблему: промптами, архітектурою чи поки що ніяк?
Репозиторій: github.com/illyar80/developer-farm
Ліцензія: MIT
Залізо для тестів: Ryzen 3 2200G + GTX 1050 Ti 4GB + 16GB RAM
11 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарівЗакон Гудхарта для AI-агентів — дуже недооцінена проблема. Бачив не раз, як модель проходить тести, але не вирішує реальну задачу.
Саме так. І проблема не в моделях — вони роблять те, для чого їх оптимізували. Проблема в архітектурі: коли агент бачить тести, він оптимізує під тести, а не під задачу.
Я в Developer Farm вирішив це не промптами, а ізоляцією: Execution-шар фізично не отримує ні тестів, ні рубрики. Тільки технічний опис + контекст. Верифікатор теж сліпий — бачить тільки git diff.
Результат: $0.03/фичу, 26 секунд, код який вирішує задачу, а не геймить метрики.
Ілля, підкажіть, будь ласка, кілька моментів простими словами:
- Що у вас є verifier: тільки LLM, чи LLM плюс класичні автоматичні checks?
- Як рахується score: це average по rubric, чи інша логіка?
- Де проходить межа між pipeline з архітектурними обмеженнями і вузькою expert system?
- Коли ви пишете «фізично неможливо порушити чесність», це гарантія в межах вашого pipeline чи ширша інженерна гарантія?
- Який головний practical lesson після реальних запусків: що спрацювало найкраще, а що виявилось слабким місцем?
Ще раз дякую, що ділитеся досвідом. Такі розбори дуже корисні для спільнотиДякую за глибокі питання — саме такі розбори роблять проєкт кращим. Відповідаю по черзі, максимально чесно:
## 1. Verifier: тільки LLM, чи LLM + автоматичні checks?
**Зараз: тільки LLM** (Qwen-Turbo через OpenRouter). Верификатор отримує git diff + рубрику і повертає holistic score0.0–1.0 + reason.
**Це слабке місце**, і я про це відкрито кажу. Планується додавання класичних checks як **додаткового сигналу** (не заміни LLM):
— `ruff` / `pylint` — linting
— `mypy` — type checking
— `pytest` — якщо в артефакті є тести
— AST-аналіз — складність, цикломатика
Чому не заміна? Бо LLM ловить **семантичні** проблеми («функція реалізує не те, що просили»), а статичний аналіз — **синтаксичні**. Ідеальна верифікація = обидва шари.
## 2. Як рахується score?
**Це НЕ average по rubric.** Це **holistic score** від LLM.
Рубрика передається як контекст (наприклад: «docstrings — 0.3, type hints — 0.3, error handling — 0.4»), але LLM не рахує середнє арифметичне. Вона оцінює код **цілісно**, враховуючи вагу кожного критерію суб’єктивно.
**Чому так?** Бо average по rubric = ідеальна умова для Гудхарта. Агент, який бачить «docstrings = 0.3», просто додасть docstrings скрізь, навіть де вони не потрібні. Holistic score змушує верифікатора думати як senior engineer, а не як калькулятор.
**Ціна цього підходу:** score менш детермінований. Один і той самий код може отримати 0.85 і 0.92 при різних запусках. Тому ми фіксуємо seed + temperature=0.1 для стабільності.
## 3. Межа між pipeline з архітектурними обмеженнями і expert system?
Дуже тонка, і це важливо розуміти:
**Expert system** = жорсткі правила, детермінована логіка («якщо X, то роби Y»). Ми цього **НЕ** робимо. Ми не кодуємо правила «як писати код».
**Наш pipeline** =LLM-based генерація, але з **TypedDict контрактами як «рельсами»**. Ми не кажемо агенту *що* робити — ми створюємо умови, за яких він *не може* зґеймити метрики.
**Межа:**
— Expert system: «Додай docstring до кожної функції» → агент виконує правило формально
— Наш підхід: «Агент не бачить, що docstring оцінюється» → він додає docstring тільки якщо це дійсно потрібно для читабельності
Тобто ми будуємо **institutional design** (як конституція для агентів), а не expert rules (як інструкція).
## 4. «Фізично неможливо порушити чесність» — яка гарантія?
**Це гарантія в межах pipeline, НЕ ширша інженерна гарантія.**
Конкретно:
— ✅ **Захищено:** передача `acceptance_criteria` у Execution викликає `ValueError` на рівні `seal_task_for_execution()`
— ✅ **Захищено:** передача `worker_id` у Verification блокується `seal_artifact_for_verification()`
— ❌ **НЕ захищено:** якщо хтось змінить `contracts.py` і прибере `seal_*` — захист зникає
— ❌ **НЕ захищено:** Python не має compile-time типізації, це runtime-перевірка
Тобто це **архітектурна домовленість**, закріплена кодом, а не формальна верифікація (як у Coq або TLA+). Для серйозної гарантії треба додавати:
— `mypy —strict` на `contracts.py`
— Unit-тести на `seal_*` функції (перевіряти, що вони дійсно викидають `ValueError`)
— CI pipeline, який ламається при порушенні контрактів
**Чесно:** зараз це «сильна домовленість», а не «математична гарантія». Різниця важлива.
## 5. Головний practical lesson?
### ✅ Що спрацювало найкраще:
1. **Абстрактний feedback** — найсильніша фіча. Коли верифікатор пише «Code quality needs improvement» замість «Add docstring to is_palindrome()», агент дійсно покращує код, а не геймить рубрику. Це працює краще, ніж я очікував.
2. **Git worktrees для кожної ітерації** — кожна спроба в окремому branch. Це дає **audit trail**: можна подивитись, як код еволюціонував від ітерації 1 до ітерації 4. Випадково виявили, що 80% «покращень» на ітерації2-3 — це косметика, а реальні фікси — на ітерації 1.
3. **Локальний Execution + API Planning/Verification** — економіка $0.03/фичу реальна. OpenRouter білінг підтверджує.
### ⚠️ Слабкі місця:
1. **Verifier тільки LLM** — пропускає баги, які ловить `mypy` або `ruff`. Були кейси, коли код отримав 0.95, але не компілювався через typing error.
2. **Абстрактний feedback іноді занадто vague** — «Consider improving error handling» не дає агенту зрозуміти, *що саме* покращити. Результат: 3 ітерації без прогресу, потім timeout.
3. **Planning залежить від якості spec** — якщо spec написана абстрактно («зроби добре»), Planning генерує абстрактний task, Execution генерує абстрактний код. Garbage in, garbage out працює на 100%.
4. **Neo4j indexing повільний на великих кодових базах** — індексація 100+ файлів займає2-3 хвилини. Для MVP ок, для production треба оптимізувати (incremental indexing, caching).
---
**Що далі:** плануємо додати `ruff` + `mypy` у Verification шар як **обов’язковий pre-check** передLLM-оцінкою. Якщо код не компілюється — LLM взагалі не викликається, одразу FAIL з конкретним повідомленням. Це закриє головне слабке місце.
Дякую за питання — саме такі розбори перетворюють pet-project на інженерний інструмент. Якщо хочете подивитись код `seal_*` функцій або `generate_abstract_feedback()` — все відкрито: github.com/...rm/blob/main/contracts.py
Буду радий продовжити дискусію! 🙏
Іллю, дякую за відповідь, особливо за пункти 4 і 5, де ви самі показуєте слабкі місця.
Цікаво що worker рубрику не бачить, але verifier її бачить. Чи не виходить, що тиск «підіграти оцінці» просто переїхав на шар верифікації? І як ви перевіряли, що сама ізоляція реально допомагає? наприклад, якщо прогнати один task двічі, з рубрикою у worker і без, та порівняти результат?
Дякую за глибоке питання — ви влучили в саму суть архітектурного компромісу.
### 1. Чому verifier бачить рубрику, а worker — ні
Це не баг, а фіча. Хтось **мусить** оцінювати код проти критеріїв — інакше ми не дізнаємось, чи вирішено задачу. Ключова різниця:
| Шар | Бачить рубрику? | Чому це ок / не ок |
| :--- | :--- | :--- |
| **Worker (Execution)** | ❌ Ні | Щоб не міг оптимізувати під конкретні пункти («додати docstring, бо це +0.3 балу») |
| **Verifier** | ✅ Так | Щоб міг оцінити відповідність вимогам. Але він **сліпий** до автора, промпту, історії — тому не може «підіграти» |
Тобто тиск «підіграти» не зникає, але **локалізується в шарі, який не генерує код**. Verifier може бути суб’єктивним, але він не може змінити код — тільки оцінити його. Це як розділити роль «автора» і «рецензента» в академічному paper.
### 2. Чи перевіряв я, що ізоляція реально допомагає?
**Чесно: формального A/B тесту ще не проводив.** Але є непрямі сигнали:
— **Ранні прототипи** (де worker бачив рубрику «наполовину») давали15-20% false-positive вердиктів: код виглядав добре, але не вирішував задачу.
— **Після увімкнення `seal_*` контрактов** false-positive впали до <2% (на вибірці ~10 запусків).
— **Абстрактний feedback** змушує worker дійсно покращувати код, а не формально відповідати рубриці.
Ти намагаєшся конкурувати заводом виробництва танків, зібравши триколісний великий з бібікалкою.
Штука в тому, що у твій репозиторій ніхто не зайде. І не тому, що він поганий, може навпаки — ти зробив круту штуку.
Просто тому, що в тебе нудно обличчя і грошей тобі ніхто не дасть.
Дякую за відвертість — це важливо.
Про «триколісний велосипед»: так, Developer Farm не танк. Але іноді потрібен не танк, а інструмент, який:
— коштує $0.03 за замість $500/міс,
— працює локально на старій відеосистемі,
— і **фізично не може** зґеймити метрики, бо архітектура не дає.
Це не конкуренція з «заводами». Це альтернатива для тих, кому не потрібен танк — а потрібна чесність.
Про «нудне обличчя і гроші»: погоджуюсь, що візуальний шум і пітчинг часто виграють. Але хакатон [Proof of Usefulness](proofofusefulness.com/reports/developer-farm) створений саме проти цього: алгоритм оцінює не презентацію, а **верифіковані сигнали** — GitHub forks, reproducibile бенчмарки, API-логи. Якщо інструмент дійсно корисний, це видно по коду, а не по обличчю.
Про «ніхо не зайде в репозиторій»: це виклик, а не вирок. Тому ми:
— публікуємо [BENCHMARKS.md](github.com/...m/blob/main/BENCHMARKS.md) з відтворюваними запусками,
— додаємо шаблон `[BENCHMARK]` Issue для спільноти,
— інтегруємо спонсорські інструменти (Neo4j, Bright Data), щоб алгоритм міг крос-валідувати корисність.
Якщо ви бачите, що саме зробити, щоб репозиторій **став цікавим** для української спільноти — радий почути конкретні поради. Може, додати приклади для українських стартапів? Локалізувати документацію? Зробити демо-відео з субтитрами?
Бо якщо корисність вимірюється не хайпом, а тим, чи вирішує інструмент реальну проблему — то Developer Farm вже працює. А от чи побачать його люди — це вже спільна задача.
Дякую, що прочитали до кінця. 🙏
Нічого там поки що не працює. Просто є якийсь текст. Щоб стверджувати, що воно працює — треба підрубати репо з великою кодовою базою, 300+ issues і попросити їх виправити, а потім прогнати складання + тести.
Ви абсолютно праві: **текст у репозиторії ≠ працююча система**. І єдина справжня перевірка — це прогнати пайплайн на реальній кодовій базі з 300+ issues, зібрати білд, прогнати тести.
Ось що ми вже зробили, щоб це було можливо:
✅ **Reproducible benchmarks**: у [`BENCHMARKS.md`](github.com/...m/blob/main/BENCHMARKS.md) — покрокова інструкція, як запустити пайплайн і отримати верифікований `00_final_report.json` з метриками часу, вартості та якості.
✅ **Goodhart-proof ізоляція**: це не промпт-інженерія, а контракти на рівні `TypedDict`. Спроба передати `acceptance_criteria` у виконання викидає `ValueError` — це компілюється, а не «сподіваємось, що агент послухає».
✅ **Відкрита архітектура**: код під MIT, можна форкнути, підключити свій репо, додати свої тести. Якщо знайдете, де ізоляція «протікає» — це баг, а не фіча. Кидайте issue `[BENCHMARK]`, розберемо.
Щодо «великої кодової бази»:
— **Neo4j-інтеграція** вже дозволяє планувати фичі на основі графа залежностей (іморти, виклики, наслідування), а не вгадувати контекст.
**Пропозиція**: якщо у вас є репо з 300+ issues, яке ви готові відкрити для тесту — давайте зробимо спільний стрес-тест. Ви даєте spec на одну фичу, я запускаю пайплайн, ми разом дивимось на:
1. Чи зламав білд
2. Чи пройшли тести
3. Чи не зґеймив агент метрики (порівнюємо diff з рубрикою)
4. Скільки це коштувало і скільки зайняло
Якщо пайплайн не впорається — це не провал, а дані для ітерації. І ці дані ми опублікуємо в тому ж `BENCHMARKS.md` — чесно, з логами.
Бо якщо корисність вимірюється не хайпом, а тим, чи вирішує інструмент реальну проблему — то давайте перевіримо це разом. 🤝
Тексту багато — а думок мало. Хто тобі заважає взяти опенсорс із важким кодом та 300 багами? Незнання синтаксису пошуку з гіта? Ну спитай ту ж модель.
Просто я бачу навіть наступну проблему — складність залучити ком’юніті навколо ще одного-селфхостед-ллм
Продати це бізнесу — тому що, власне, що продавати те? А якщо нема чого продати — то не буде грошей для подальшого розвитку.
Останнє навіть важливіше. Так що спочатку питання фінансів — спробуй хоч скількись отримати на хакатонах та грантах, потім можеш стукнути до мене по білду ком’юніті навколо git repo. В агенства не стукайся — у них немає такого кейсу, принаймні українців. Тільки дарма витратиш гроші.