mirror of
https://gitverse.ru/kpa39l/project-doc-processing.git
synced 2026-09-29 09:15:04 +00:00
Initial commit: Hermes skill project-doc-processing
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# Легенда обозначений розеток — комплект СКС «Винный город» (блок A)
|
||||
|
||||
Источник: свежий лист шкафа /mnt/vinogorod/ИТ/trash/2026-09-05_08-44-17.png (нижняя часть листа, таблица «Условные графические обозначения»). Прочитано vision-моделью (polza deepseek-vision) по кропу нижней полосы.
|
||||
|
||||
ВАЖНО: легенда может отличаться между листами комплекта — перед импортом сверять с каждым листом, не копировать вслепую.
|
||||
|
||||
## Маркировка розеток/назначений (таблица «Маркировка патч-панелей»)
|
||||
| Код | Назначение |
|
||||
|---|---|
|
||||
| (A) | Гнездо доступа Wi-Fi |
|
||||
| (V) | IP-камера видеонаблюдения |
|
||||
| (S) | IP-контроллер (КДУ/СОТС) |
|
||||
| (I) | Гнездо доступа ИБП |
|
||||
| (D) | Гнездо IP-телефонии |
|
||||
| (T) | Телефон |
|
||||
| (Д) | Инфракрасный (сетевой) датчик периметра |
|
||||
| (СР) | Вызывная панель |
|
||||
|
||||
## Линии (условные графические обозначения)
|
||||
- сплошная линия — кабель электрический (СВ)
|
||||
- двойная сплошная — кабель электрический коммутационный с оборудованием
|
||||
- пунктирная — Ethernet UTP cat 6a
|
||||
- волнистая — Ethernet UTP cat 6a внешней прокладки
|
||||
- тройная вертикальная — патч-корд
|
||||
- двойная вертикальная — патч-корд оптический
|
||||
- длинный штрих — DAC-кабель
|
||||
|
||||
## Маркировка оборудования в шкафу
|
||||
- SW# — коммутатор доступа; SWA — коммутатор агрегации
|
||||
- PR / PR1 / PR2 — устройство распределения / управления
|
||||
- патч-панель, розетка RJ-45, сетевой шкаф
|
||||
|
||||
## Замечание по вчерашнему разбору (2026-09-04)
|
||||
На первом скриншоте (PP.E.1/А.1) модель дала коды (v), (d), (a), (w). По этой легенде:
|
||||
- (v) в легенде нет — есть (V) = IP-камера
|
||||
- (a) == (A) Wi-Fi, (d) == (D) IP-телефония вероятно
|
||||
- (w) в легенде отсутствует → перепроверить на исходном листе
|
||||
Не утверждать расшифровку, пока не сверено с листом.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Spec/структура parser — engineering PDF text layer
|
||||
|
||||
Pattern that worked on the wine-city СКС project (Спецификация оборудования ТШ шкафов из 90-page DWG-render PDF, 384KB extracted text). Reusable for any "оборудование по шкафам/узлам" spec table whose text layer is radially extracted.
|
||||
|
||||
## Goal
|
||||
Turn the extracted .txt (with `===== СТРАНИЦА N =====` page markers) into structured items WITHOUT reading 2000 lines/spec into the LLM.
|
||||
|
||||
## Heuristic-обозначение parser
|
||||
```python
|
||||
import re
|
||||
def parse_spec(lines, start, end):
|
||||
"""items = [ {oboz, name, qty}, ... ]"""
|
||||
items, i = [], start
|
||||
while i < end:
|
||||
s = lines[i-1].strip()
|
||||
if not s:
|
||||
i += 1; continue
|
||||
# обозначение: one token, upper/alnum/dash/dot, >3 chars, not starting with digit
|
||||
if re.fullmatch(r'[A-ZА-Я0-9][A-ZА-Я0-9\-.]+', s) and len(s) > 3 and not s[0].isdigit():
|
||||
cur = {"oboz": s, "name": [], "qty": None}
|
||||
j = i + 1; name_lines = []
|
||||
while j < end and len(name_lines) < 8: # cap: наименование ≤8 строк
|
||||
t = lines[j-1].strip()
|
||||
if re.fullmatch(r'[A-ZА-Я0-9][A-ZА-Я0-9\-.]+', t) and len(t) > 3 and not t[0].isdigit():
|
||||
break # next обозначение
|
||||
if re.fullmatch(r'\d+', t):
|
||||
cur["qty"] = int(t); j += 1; break # qty = bare int
|
||||
if t and not re.fullmatch(r'\d+', t):
|
||||
name_lines.append(t)
|
||||
j += 1
|
||||
cur["name"] = " ".join(name_lines)[:140]
|
||||
items.append(cur); i = j
|
||||
else:
|
||||
i += 1
|
||||
return items
|
||||
```
|
||||
|
||||
## Known noise to filter (DWG-render text layer)
|
||||
- Rack U-number grids: long runs of bare ints (1..47) — compiler may capture as qty=None items; ignore `qty is None` rows unless they carry a real name (they won't: name stays empty).
|
||||
- SFP1..4 / QSFP1..6 / "резерв" / "220V Port 1..." label rows leak in from switch faceplate text. Drop tokens in {SFP*, QSFP*} and empty-name items.
|
||||
- Real spec items are the ones with BOTH a multi-char обозначение AND a non-empty name AND a qty — keep those as the equipment table.
|
||||
|
||||
## Diff across nearly-identical sections (e.g. ТШ-A.1..A.4)
|
||||
Since соседние spec-блоки почти одинаковы, don't re-read each: parse all to JSON, then compare counts per (oboz, name) to spot the deltas (A.1 vs A.2 vs A.3 vs A.4) and only narrate what differs.
|
||||
|
||||
## Finding section boundaries without full reads
|
||||
Scan the .txt lines for header regexes once (cheap): `СПЕЦИФИКАЦИЯ`, `ВЕДОМОСТЬ`, `СХЕМА`, `ОБЩИЕ ДАННЫЕ`, `СОДЕРЖАНИЕ`; record their line numbers, then `read_file(offset=…)` ONLY the interesting ranges.
|
||||
|
||||
## Verification
|
||||
Before trusting counts, `read_file` 1-2 rows at their known line and match против исходной таблицы on the actual PDF page (`page.get_text()` for the page). Spec numbers are consumable-accurate (закупочные количества) — worth the check.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Vision-reading CAD screenshots — crop & zoom technique
|
||||
|
||||
When the source is a **PNG screenshot of a drawing sheet** (rack/ТШ schematic, port→socket table) instead of a PDF, the vision model reads it directly — but dense sheets need the crop-zoom technique. This reference captures the working pattern from the vinogorod СКС screenshot sessions.
|
||||
|
||||
## Workflow
|
||||
1. Probe the sheet size first (`PIL.Image.open().size`). Large sheets (~2300px) read well whole; small ones (~500px) need crops.
|
||||
2. Cut the sheet into bands (top/mid/bottom strips, left/right halves, quarters) with `img.crop()`, upscale ×2-4 with `Image.LANCZOS`, save to /tmp.
|
||||
3. Send each crop with a **narrow, fact-only question**: "перечисли все подписи по одной на строку, ничего не пропускай". Explicit formatted-single-line output beats "опиши лист".
|
||||
4. **Timeout retry pattern**: cloud vision (polza deepseek-vision) times out on dense crops — retry the SAME region as a smaller/narrower crop, or split the question. A timeout is not a dead end; the region is readable in pieces.
|
||||
5. Cross-check across crops of the same sheet (top = device labels, bottom = legend/tables); merge answers. Vision text is imperfect — treat "(v) vs (V)", "Swd vs SWd" as the same until proven otherwise.
|
||||
6. On engineer sheet sets, the **legend/«Условные графические обозначения» sits at the BOTTOM of each rack sheet** — crop the bottom strip for socket-code decoding. Legends can differ BETWEEN sheets of one set — verify per sheet, never copy from a previous one.
|
||||
|
||||
## Proven example (2026-09-05)
|
||||
Sheet `/mnt/vinogorod/ИТ/trash/2026-09-05_08-44-17.png` (2299×1789): bottom strip crop (55-100% height, ×1.6 LANCZOS) exposed the legend tables «Условные графические обозначения», «Маркировка патч-панелей», «Маркировка оборудования в шкафу». Full decode in `sks-rack-legend.md`.
|
||||
|
||||
## Model quirks
|
||||
- polza deepseek-vision (~0.1₽/request): fine on large sheets; times out on dense crops → narrow the crop, not the timeout.
|
||||
- Small sheets (~500px wide) read worse than large ones; always upscale ×3+ before sending.
|
||||
- Split one big "read everything" question into several per-region questions — the model returns cleaner single-line lists.
|
||||
|
||||
## Cross-links
|
||||
- Legend decode: `sks-rack-legend.md`
|
||||
- Spec/table parsing (PDFs): `spec-parser-pattern.md`
|
||||
Reference in New Issue
Block a user