Перейти к содержанию

Асинхронный сбор · событийная индексация · локальный RAG

Как устроен TenderLens

TenderLens получает открытые закупки из TED и Contracts Finder, безопасно скачивает документы, сохраняет данные в PostgreSQL, асинхронно строит векторный индекс и отвечает на вопросы только по найденным фрагментам.

3 ролиcrawler · indexer · API
2 источникаTED · Contracts Finder
1 событиеtender.changed.v1
1024размерность embedding

Система в одной схеме

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.

Основная ветка: main · публичный репозиторий: komaroffsergei/tender-lens