feat: SVG pipeline block diagram
Deploy contracts-flask / validate (push) Successful in 0s

This commit is contained in:
2026-06-29 12:25:42 +04:00
parent dab66a2a7d
commit 39e370f393
+62 -4
View File
@@ -39,11 +39,69 @@
<div class="nav"><a href="/">← Вернуться к загрузке</a></div>
<h1>🏗️ Архитектура — сверка договоров</h1>
<p style="color:var(--muted)">Пайплайн: загрузка → парсинг → классификация → группировка → сравнение → аудит</p>
<!--
Все комментарии ниже — прямые цитаты или пересказ пожеланий Заказчика (Сергей Мищук) из переписки 2025–2026.
-->
<svg viewBox="0 0 860 280" style="width:100%;max-width:860px;margin:16px 0;" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#58a6ff"/></marker>
<linearGradient id="g1" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#1a3a2a"/><stop offset="100%" stop-color="#0d2818"/></linearGradient>
<linearGradient id="g2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#1a2a3a"/><stop offset="100%" stop-color="#0d1a28"/></linearGradient>
<linearGradient id="g3" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#2a1a3a"/><stop offset="100%" stop-color="#1a0d28"/></linearGradient>
<linearGradient id="g4" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#3a2a1a"/><stop offset="100%" stop-color="#281a0d"/></linearGradient>
<filter id="shadow"><feDropShadow dx="0" dy="1" stdDeviation="2" flood-opacity="0.3"/></filter>
</defs>
<!-- Row 1: Pipeline -->
<!-- 1. Upload -->
<rect x="10" y="30" width="120" height="80" rx="8" fill="url(#g1)" stroke="#3fb950" stroke-width="1.5" filter="url(#shadow)"/>
<text x="70" y="60" text-anchor="middle" fill="#3fb950" font-size="13" font-weight="bold">📤 Загрузка</text>
<text x="70" y="80" text-anchor="middle" fill="#8b949e" font-size="10">.docx .pdf .doc .zip</text>
<text x="70" y="96" text-anchor="middle" fill="#8b949e" font-size="9">Whitelist форматов</text>
<line x1="132" y1="70" x2="160" y2="70" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#arrow)"/>
<!-- 2. Parse -->
<rect x="165" y="30" width="120" height="80" rx="8" fill="url(#g2)" stroke="#58a6ff" stroke-width="1.5" filter="url(#shadow)"/>
<text x="225" y="60" text-anchor="middle" fill="#58a6ff" font-size="13" font-weight="bold">🔍 Парсинг</text>
<text x="225" y="80" text-anchor="middle" fill="#8b949e" font-size="10">pdfplumber</text>
<text x="225" y="96" text-anchor="middle" fill="#8b949e" font-size="10">python-docx</text>
<line x1="287" y1="70" x2="315" y2="70" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#arrow)"/>
<!-- 3. Classify -->
<rect x="320" y="30" width="130" height="80" rx="8" fill="url(#g3)" stroke="#bc8cff" stroke-width="1.5" filter="url(#shadow)"/>
<text x="385" y="55" text-anchor="middle" fill="#bc8cff" font-size="13" font-weight="bold">🏷️ Классификация</text>
<text x="385" y="73" text-anchor="middle" fill="#8b949e" font-size="9">3-этапный фильтр</text>
<text x="385" y="88" text-anchor="middle" fill="#8b949e" font-size="9">имя → заголовки → LLM</text>
<text x="385" y="103" text-anchor="middle" fill="#f85149" font-size="8">мусор отсеивается</text>
<line x1="452" y1="70" x2="480" y2="70" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#arrow)"/>
<!-- 4. Group -->
<rect x="485" y="30" width="120" height="80" rx="8" fill="url(#g4)" stroke="#d2991d" stroke-width="1.5" filter="url(#shadow)"/>
<text x="545" y="60" text-anchor="middle" fill="#d2991d" font-size="13" font-weight="bold">📋 Группировка</text>
<text x="545" y="80" text-anchor="middle" fill="#8b949e" font-size="10">по номерам</text>
<text x="545" y="96" text-anchor="middle" fill="#8b949e" font-size="10">договоров</text>
<line x1="607" y1="70" x2="635" y2="70" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#arrow)"/>
<!-- 5. Compare -->
<rect x="640" y="30" width="130" height="80" rx="8" fill="url(#g2)" stroke="#58a6ff" stroke-width="1.5" filter="url(#shadow)"/>
<text x="705" y="55" text-anchor="middle" fill="#58a6ff" font-size="13" font-weight="bold">🤖 LLM-сравнение</text>
<text x="705" y="73" text-anchor="middle" fill="#8b949e" font-size="9">extract + diff</text>
<text x="705" y="88" text-anchor="middle" fill="#8b949e" font-size="9">gpt-oss-120b</text>
<text x="705" y="103" text-anchor="middle" fill="#f85149" font-size="8">ADD UPDATE DELETE</text>
<!-- Row 2: Protection layer -->
<rect x="10" y="140" width="860" height="55" rx="8" fill="#1a0a0a" stroke="#30363d" stroke-width="1" stroke-dasharray="4,3"/>
<text x="440" y="162" text-anchor="middle" fill="#f85149" font-size="12" font-weight="bold">🛡️ Защита на каждом этапе</text>
<text x="440" y="182" text-anchor="middle" fill="#8b949e" font-size="10">ZIP-бомба (500 файлов, 500MB, ratio 100×) | Path traversal | Whitelist форматов | content_hash дедупликация | UNRESOLVED для битых данных | авто-нормализация чисел/дат | арифметическая проверка</text>
<line x1="440" y1="128" x2="440" y2="138" stroke="#f85149" stroke-width="1" stroke-dasharray="2,2"/>
<!-- Row 3: Event Sourcing output -->
<rect x="10" y="220" width="860" height="40" rx="8" fill="#161b22" stroke="#3fb950" stroke-width="1.5" filter="url(#shadow)"/>
<text x="440" y="245" text-anchor="middle" fill="#3fb950" font-size="12" font-weight="bold">📊 Результат: spec_events (аудит) → spec_current (текущая спецификация) | Промпты с версионированием и редактором | Промежуточные результаты по каждому файлу</text>
</svg>
<hr>