Асинхронный сбор · событийная индексация · локальный RAG
Как устроен TenderLens¶
TenderLens получает открытые закупки из TED и Contracts Finder, безопасно скачивает документы, сохраняет данные в PostgreSQL, асинхронно строит векторный индекс и отвечает на вопросы только по найденным фрагментам.
Система в одной схеме¶
flowchart LR
TED["TED Search API"] --> CR["Crawler"]
CF["Contracts Finder"] --> CR
CR -->|"UPSERT + файлы"| PG[("PostgreSQL")]
CR -->|"tender.changed.v1"| NATS["NATS JetStream"]
NATS --> IDX["Indexer"]
IDX -->|"chunks + VECTOR(1024)"| PG
UI["Browser"] --> API["FastAPI"]
API --> PG
API --> AI["Fake AI / Ollama"]
IDX --> AI
Это не десять независимых учебных приложений. Основная работа — задание №7: асинхронный crawler. Search/RAG, rate limiter, Docker Compose и CI — бонусные расширения, которые показывают полный жизненный цикл собранных данных.
Путь одной закупки¶
1 · Получение. Адаптер забирает страницу внешнего API и превращает разные JSON-форматы в единый TenderRecordV1.
2 · Фиксация. CrawlerService.persist_record() делает идемпотентный UPSERT и регистрирует вложения.
3 · Доставка. После commit crawler публикует TenderChangedV1 в durable-очередь NATS JetStream.
4 · Индексация. IndexerService.process() извлекает текст, создаёт chunks и атомарно заменяет индекс.
5 · Ответ. SearchService ищет по cosine similarity; `/ask` вызывает Ollama только при наличии релевантного контекста.
С чего начать¶
| Если нужно… | Откройте | Результат |
|---|---|---|
| запустить проект | Первые 10 минут | работающий Compose, ключ и первый запрос |
| понять границы процессов | Обзор архитектуры | роли, хранилища и зависимости |
| проследить один сценарий | Потоки данных | sequence-схемы crawl, index и ask |
| найти реализацию | Дерево репозитория | назначение каждого tracked-файла |
| найти класс или функцию | Python API | сигнатура, диапазон строк и GitHub-ссылка |
| разобраться в термине | Глоссарий | определения с привязкой к TenderLens |
| проверить всё вручную | Запуск и реальная LLM | пошаговый acceptance-сценарий |
| понять качество | Тестирование | unit → API → integration → E2E |
Что система гарантирует¶
- одна и та же закупка не размножается при повторном crawl;
- cursor источника продвигается только после обработанной порции;
- redirect проверяется на каждом переходе, private/local IP запрещены;
- вложение пишется потоково во временный файл, ограничивается по размеру и получает SHA-256;
- старое событие не может перезаписать новую версию закупки;
- низкорелевантный вопрос не вызывает генеративную модель;
- открытый API-ключ не хранится в базе.
Ограничения перечислены рядом с решениями в компромиссах, а доказательства требований — в traceability.