~/projekty / ai-semantic-db
Semantic DB
CLI do lokalnego wyszukiwania semantycznego w twoich danych strukturalnych: definiujesz schemat, dodajesz rekordy i szukasz zwykłym językiem. Bez chmury i kluczy API.
- rola
- projekt i implementacja: model schematu, CLI, embeddingi, testy
- czas
- sie – wrz 2026
- typ
- własny
- stack
- Python 3.12 · Typer · SQLAlchemy · Alembic · PostgreSQL + pgvector · Ollama (bge-m3)
01 / problem
Wyszukiwanie semantyczne bez usługi w chmurze
Istniejące narzędzia do wyszukiwania semantycznego działają w chmurze albo są związane z jednym źródłem danych. Celem było zdefiniowanie własnej struktury, trzymanie danych na własnej maszynie i szukanie po znaczeniu, a nie po słowach kluczowych.
02 / rozwiązanie
Kolekcje sterowane schematem z lokalnymi embeddingami
Kolekcję definiuje się zwięzłą gramatyką pól, np. price:float:embed:unit=PLN, obsługującą typy text, enum, int, float, bool, date i tablice stringów. Każdy rekord jest renderowany do jednej sformatowanej karty tekstowej i przy zapisie osadzany modelem bge-m3 przez Ollama, więc dodany znaczy przeszukiwalny. Wyszukiwanie to cosinusowe top-k z tabelą Rich i trybem --explain; odmawia działania, gdy kolekcję osadzono innym modelem, bo odległości między modelami nic nie znaczą.
- [screenshot 1]
[podpis]
- [screenshot 2]
[podpis]
03 / architektura
Jak to działa
- schemat
- rekordy
- embeddingi
- pgvector
- pytanie
- embeddingi
- retrieval top-k
- CLI
CLI w Pythonie 3.12 na Typer, w warstwach: domain, application (porty i przypadki użycia), infrastructure (SQLAlchemy, Alembic, klient Ollama) i cli, a kontrakty import-linter pilnują, by zależności szły do środka. Rekordy to payloady JSONB w PostgreSQL 17 z pgvector (halfvec 1024), więc schemat zmienia się bez DDL dla każdej kolekcji. Zapisy to przypadki użycia; odczyty przechodzą przez jedną fasadę zapytań.
04 / wyniki
Efekt
05 / wnioski
Czego się nauczyłem
- Payloady JSONB zamiast tabel dla każdej kolekcji kosztują trochę szybkości, ale dają dużo prostsze migracje i dowolne schematy.
- Zapisywanie dokładnie wyrenderowanego tekstu obok każdego wektora chroni przed cichym rozjazdem, gdy szablon zmieni się po utworzeniu embeddingów.
galeria
- [zdjęcie]
[podpis]
- [zdjęcie]
[podpis]
- [zdjęcie]
[podpis]