AI-агент, который помогает малому и среднему бизнесу в Казахстане находить государственные тендеры, оценивать шансы на участие и готовить черновики заявочных документов.
MVP: backend на FastAPI + SQLite, frontend на Next.js (App Router) + TypeScript + Tailwind, AI-слой на Google Gemini API.
Реальный открытый API госзакупок (ows.goszakup.gov.kz, REST v3 / GraphQL) требует Bearer-токен,
который выдаёт АО «Центр электронных финансов» по отдельной заявке — регистрация здесь:
https://goszakup.gov.kz/ru/developer/ows_v3. Без токена оба эндпоинта (/v3/trd-buy, /v3/graphql)
отвечают 401 Unauthorized (проверено вживую).
Пока токена нет, приложение работает в режиме TENDER_DATA_SOURCE=mock — синхронизация
(POST /api/tenders/sync) читает 8 реалистичных тендеров из backend/mock_data/tenders.json,
структурно совместимых с полями trd-buy/lots реального API (name_ru, total_sum, end_date,
customer_name_ru, ref_subject_type_id и т.д.).
Когда токен будет получен:
- Впиши его в
backend/.envкакGOSZAKUP_API_TOKEN. - Смени
TENDER_DATA_SOURCE=mockнаTENDER_DATA_SOURCE=live. - Проверь ответ реального
/v3/trd-buyи при необходимости поправь маппинг полей вbackend/app/services/goszakup_client.py::_fetch_live_tenders— он написан по документации, но не был протестирован вживую (доступ был заблокирован на момент разработки).
Модели данных, роуты и frontend менять не нужно — они не завязаны на источник тендеров.
- Backend: Python 3.11+, FastAPI (+
BackgroundTasksдля асинхронных AI-задач), SQLAlchemy, SQLite - AI: Google Gemini API (
google-genai, модельgemini-3.6-flash), structured output черезresponse_schemaдля JSON-ответов (анализ тендера) - Frontend: Next.js (App Router) + TypeScript + Tailwind CSS + shadcn/ui
Исходное ТЗ предполагало Anthropic Claude API — заменено на Google Gemini по явному запросу заказчика в ходе разработки (ключа Anthropic не было под рукой). Вся архитектура AI-слоя (промпты вынесены в
app/services/prompts.py, structured output, отдельные сервисы для анализа и генерации черновиков) сохранена, поменялся только провайдер и SDK-вызовы.
Анализ тендера и генерация черновика — не синхронные запросы, а фоновые задачи:
POST /api/analysis/{tender_id}/POST /api/drafts/{tender_id}создают запись со статусомqueued, сразу возвращают её (202 Accepted) и ставят реальную работу вBackgroundTasks(run_analysis_job/run_draft_jobвapp/services/).- Фоновая задача сама переводит запись в
processing, вызывает Gemini, затем сохраняет результат со статусомcompleted(илиfailed+error_messageпри сбое API). - Фронтенд поллит
GET /api/analysis/{id}(илиGET /api/drafts/item/{id}) каждые ~700мс (hooks/use-poll.ts), пока статус не станет финальным, и показываетJobProgress— реальный прогресс-бар + скелетоны + сменяющиеся подписи этапов. - Анализ возвращает не просто число, а
requirement_checks— Gemini сам разбирает текст требований тендера на пункты и по каждому сверяет профиль компании (✓/✗ + пояснение) — это и естьRequirementChecklistна странице тендера, видимая "логика работы Gemini".
Два bulk-действия демонстрируют, что агент способен работать по всему списку сразу:
- «Проанализировать все» на дашборде (
POST /api/analysis/bulk) — ставит в очередь анализ всех ещё не оценённых (или ранее упавших) тендеров компании; кольца Match Score в таблице заполняются по мере готовности фоновых задач. - «Сгенерировать полный пакет» на странице тендера (
POST /api/drafts/bulk) — параллельно запускает все 3 типа документов вместо одного за раз.
На бесплатном тарифе Gemini API (
gemini-3.6-flash) действует жёсткий лимит запросов в минуту и в день. При его превышении задача просто получает статусfailedс понятным сообщением и кнопкой «Повторить» — это штатная обработка ошибки, а не баг.
tenderagent/
backend/
app/
main.py # FastAPI app, роуты, CORS
config.py # переменные окружения (pydantic-settings)
database.py # SQLAlchemy engine/session
models/ # CompanyProfile, Tender, TenderAnalysis, DraftDocument
schemas/ # Pydantic-схемы запросов/ответов
services/
goszakup_client.py # синхронизация тендеров (mock/live)
ai_analysis.py # AI-анализ + run_analysis_job (фоновая задача)
draft_generator.py # генерация черновиков + run_draft_job (фоновая задача)
prompts.py # все промпты - редактируются отдельно от логики вызова
formatting.py # форматирование Tender/CompanyProfile для промптов
routers/
company.py, tenders.py, analysis.py, drafts.py # bulk-эндпоинты в analysis.py/drafts.py
tests/
test_api.py # pytest: CRUD, синхронизация/дедупликация, async job flow, 404-пути
mock_data/
tenders.json # 8 реалистичных тендеров для режима mock
requirements.txt
.env.example
frontend/
src/
app/
tenders/ # дашборд: Match Score кольца, bulk-анализ
tenders/[id]/ # детали тендера: AI-анализ, requirement checklist, пакет документов
profile/ # профиль компании
lib/
api.ts # клиент к backend API + TypeScript-типы
recommendation.ts # общие лейблы/цвета рекомендаций
hooks/use-poll.ts # generic-поллинг фоновых задач
components/
ui/ # shadcn/ui примитивы (button, card, table, select, ...)
match-score-ring.tsx # радиальный "meter" для fit_score
requirement-checklist.tsx # построчное сопоставление требований и профиля
job-progress.tsx # skeleton + progress + этапы во время фоновой задачи
README.md
cd backend
python -m venv venv
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .envЗаполни backend/.env:
GEMINI_API_KEY=твой_ключ_gemini # обязателен для AI-анализа и генерации черновиков
GOSZAKUP_API_TOKEN= # оставь пустым, пока нет токена от ЦЭФ
TENDER_DATA_SOURCE=mock # mock | live
DATABASE_URL=sqlite:///./tenderagent.db
Ключ Gemini можно получить в Google AI Studio: https://aistudio.google.com/apikey
Запуск сервера (порт 8000 указан для примера — если он занят другим процессом, используй
--port 8010 или любой свободный и поправь NEXT_PUBLIC_API_URL во frontend/.env.local):
uvicorn app.main:app --reload --port 8000Проверка: curl http://127.0.0.1:8000/api/health → {"status":"ok"}.
Интерактивная документация API: http://127.0.0.1:8000/docs
Тесты:
pytest tests/ -vcd frontend
npm installСоздай frontend/.env.local:
NEXT_PUBLIC_API_URL=http://127.0.0.1:8000
(адрес должен совпадать с портом, на котором реально поднят backend)
npm run devОткрой http://localhost:3000 — редиректит на /tenders.
- Открой
/profile, заполни профиль компании (сфера деятельности, описание, опыт, документы). - Открой
/tenders, нажми «Обновить тендеры» — подтянутся 8 мок-тендеров. - Нажми «Проанализировать все» — фоново запустится анализ сразу всех тендеров, Match Score кольца в таблице заполнятся по мере готовности (поллинг раз в ~1.5с).
- Открой любой тендер — увидишь
RequirementChecklist: построчное сопоставление требований тендера с профилем компании (✓/✗ + пояснение от Gemini), а не просто число. - В блоке «Пакет документов для подачи» нажми «Сгенерировать полный пакет» — параллельно запустятся все 3 типа документов; либо выбери один тип и «Сгенерировать черновик».
| Переменная | Где | Описание |
|---|---|---|
GEMINI_API_KEY |
backend/.env | Ключ Google Gemini API, обязателен для AI-функций |
GOSZAKUP_API_TOKEN |
backend/.env | Bearer-токен ows.goszakup.gov.kz, нужен только для TENDER_DATA_SOURCE=live |
TENDER_DATA_SOURCE |
backend/.env | mock (по умолчанию) или live |
DATABASE_URL |
backend/.env | По умолчанию sqlite:///./tenderagent.db |
NEXT_PUBLIC_API_URL |
frontend/.env.local | URL backend API, например http://127.0.0.1:8000 |
- Один активный профиль компании, без авторизации/многопользовательского режима.
- Черновики документов — только текст для копирования, без реальной подачи заявки или ЭЦП.
- Источник тендеров — мок-данные, пока не получен токен goszakup (см. раздел выше).
- Рассчитано на 10-20 тендеров для демонстрации, не на промышленный масштаб.