RAG база знания
pacod-kb
Превърнете файлове и уеб страници във версионирано знание с възможност за търсене — със стабилен договор за моделите, които го четат.
pacod-kb зарежда файлове и уеб страници, преобразува ги в каноничен Markdown, индексира ги за хибридно семантично търсене и търсене по ключови думи, пази всяка версия и подава на моделите надолу по веригата фрагменти със стабилен договор за метаданните. Работи само на CPU.
- Apache-2.0
- Версия 1.0.0
- Стабилна
Възможности
-
Зарежда всичко
PDF, DOCX, HTML, Markdown, текст и JSON — качени, пуснати в папка, изтеглени от URL или наблюдавани за промени.
-
Каноничен Markdown
Заглавия, съобразени със структурата, за правни текстове на осем езика, научни статии и публикации; шаблонният текст се филтрира.
-
Сигурност на съдържанието
Многостепенна проверка за prompt injection и спам блокира документите или ги поставя под карантина, преди да бъдат индексирани; резултатите се ограждат като недоверени данни.
-
Хибридно търсене
Плътни вектори от BGE-M3 в Qdrant плюс ключови думи с BM25, обединени чрез reciprocal rank fusion.
-
Всяка версия се пази
Заменените версии остават достъпни за търсене чрез заявки към момент във времето; git архив записва всяка промяна.
-
Калибрирана релевантност
Незадължителен cross-encoder reranker дава калибрирана оценка на релевантността от 0 до 1.
-
Само CPU
Ембедингите, OCR и търсенето остават отзивчиви само на CPU, дори докато се зареждат документи.
-
UI и CLI
Уеб интерфейс и CLI за документи, проекти, задачи, използване и разходи за AI, и членове с роли viewer, editor и admin.
Мястото в системата
Защо съществува
По-голямата част от работата в retrieval-augmented generation се случва, преди моделът да прочете и дума: да вкараш документите, да ги поддържаш чисти, да намериш правилния пасаж и да кажеш на модела откъде идва. pacod-kb е точно тази част, изградена като самостоятелна услуга.
Три изисквания са я оформили. Отговорите трябва да могат да се цитират, затова всеки фрагмент носи пътя си от заглавия и байтови отмествания в стабилен Markdown текст. Източниците се променят, затова нищо не се презаписва: новата версия заменя старата, която остава достъпна за търсене по дата. А зареденият текст е недоверен вход за модела, затова се проверява за prompt injection преди индексиране и се огражда като данни на изхода.
От документи до отговори
Документът минава по един и същ път, независимо дали е качен, пуснат в папката dropzone, изтеглен от URL или изтеглен отново от наблюдаван уеб източник:
- Приемане. SHA-256 на байтовете на източника е идентификаторът на документа, така че един и същ файл никога не се зарежда два пъти.
- Парсинг. PDF (docling, с незадължително OCR за сканирани страници), DOCX, HTML, Markdown, текст и JSON се превръщат в Markdown с YAML front matter.
- Проверка на езика. Приемат се английски, немски, български, руски, сръбски, македонски, хърватски и словенски; останалите се отказват.
- Проверка на детерминистичния парсинг за prompt injection и спам, преди какъвто и да е LLM да види текста.
- Преобразуване (незадължително). LLM обработка през pacod-gw подрежда Markdown-а. Прекъснат, отказан или празен отговор запазва детерминистичния парсинг.
- Почистване и структуриране. Филтър за шум премахва номера на страници, колонтитули и шаблонен текст; повишаването на заглавия разпознава структурата на правни текстове, научни публикации и статии.
- Съхраняване на
document.md, манифест и моментно копие на източника. - Индексиране. Фрагменти до 500 токена, съобразени със заглавията, се вграждат с BGE-M3 в Qdrant и се записват в индекс по ключови думи, в собствената колекция на проекта.
Архитектура
pacod-kb се състои от две услуги, kb-pipeline и kb-ui, както и от модела и хранилището, които те ползват:
| Част | Роля |
|---|---|
Pipeline API (kb-pipeline, :8000) |
Зареждане, индексиране, търсене, задачи, настройки; единствената част, която вика AI шлюза |
Уеб интерфейс (kb-ui, :8080) |
Вход през pacod-id, сесии, членове, страниците; вика конвейера с API ключ |
| BGE-M3 | Ембедингите, в собствения процес на конвейера или през обща услуга за ембединги |
| Qdrant | Плътни вектори, вградени в конвейера или като сървър, който могат да ползват и други продукти |
| SQLite FTS5 или PostgreSQL | Индексът по ключови думи с BM25 |
| Файлова система или PostgreSQL | Документи, манифести и регистърът на използването и разходите за AI |
Разделението е умишлено. Конвейерът парсва недоверени документи и изтегля произволни URL адреси,
затова не получава нито една от тайните на pacod-id; интерфейсът пък никога не вижда ключа за
шлюза. Всички контейнери са в една обща Docker мрежа, pacod-net, така че разделението се крепи
на тайните, а не на мрежите: всеки контейнер получава изричен списък от променливи на средата, а
един тест го проверява.
Всяко извикване от интерфейса носи X-KB-Actor — идентификатора на влезлия човек. Конвейерът му
се доверява само заедно с API ключа, записва го към задачите, промените в настройките и
използването и го предава на шлюза. Когато pacod-id изтрие акаунт, kb заменя този идентификатор с
deleted-user навсякъде, преди да потвърди изтриването.
Модел на сигурност
Сигурност на съдържанието. Проверката нормализира текста, за да неутрализира замаскирането, сравнява го с многоезичен набор от правила и връща една от три присъди:
- block: една находка с висока тежест за injection или замаскиране или висока оценка на заплахата. Зареждането се проваля и нищо не се съхранява.
- quarantine: документът се пази със статус
reviewи никога не се индексира. Само спам никога не води до блокиране. - clean: документът се индексира.
Незадължително второ мнение от LLM през шлюза се иска само когато правилата не са блокирали; то
има 15 секунди и при грешка не блокира документа. Материал, за който се знае, че е безопасен, може
да прескочи проверката с kb ingest --trusted. На изхода /query огражда всеки фрагмент като
недоверени данни.
Изтегляне. WEB_FETCH_ALLOW_PRIVATE=false, препоръчително в продукция, отказва зареждане от
URL, който води до частни адреси — защита срещу server-side request forgery.
Хора. Интерфейсът удостоверява хората чрез pacod-id с OIDC и PKCE и пази токените в криптирана сесия на сървъра; бисквитката носи само случайна тайна. Ролите идват от потока за оторизация на pacod-id. Ако копието на интерфейса е по-старо от 60 секунди, страниците отговарят с 503, вместо да гадаят кой какво може.
| Роля | Може |
|---|---|
| viewer | Четене и търсене |
| editor | Зареждане, повторно индексиране и изтриване на документи; управление на проекти |
| admin | AI Management, промптове, превключватели по време на работа, членове |
Търсене, подкрепено с измервания
Заявката минава през Qdrant и през индекса по ключови думи, а двете класирания се обединяват чрез
reciprocal rank fusion (k = 60). Филтрите по дата и история се прилагат вътре в двете търсения.
Незадължителен cross-encoder, BAAI/bge-reranker-v2-m3, пренарежда най-добрите кандидати и дава
оценка на релевантността от 0 до 1, сравнима между заявките, така че min_score може да отсее
липсващите отговори.
Стойностите по подразбиране идват от инструмент за оценка на извличането: 14 документа на шест езика (GDPR, Директивата за защита на данните в правоприлагането, четири конституции, статии от Wikipedia) и 92 ръчно написани заявки — перифрази, точни термини, междуезикови, на роден език и такива без отговор. Измерено на процесора на машина за разработка — документацията подчертава, че това не са продукционни числа:
- Без reranker правилният раздел е сред първите пет при 98,7 % от заявките с отговор; R@1 е 0,83, при около 50 ms.
- С reranker R@1 се покачва до 0,91, при около 2 s на заявка.
- При голямо натоварване от зареждане търсенето чакаше около 24 s за цял пакет за вграждане. Сега заявките минават пред работата по зареждането и чакат най-много един малък микропакет: около 0,4 s (p50), когато задачите за зареждане се изпълняват една след друга, както ги изпълнява конвейерът.
- Пакетите, сортирани по дължина, правят вграждането при зареждане около 2,2× по-бързо, при непроменени вектори.
Reranker-ът е изключен по подразбиране: подобрението му остана малко под прага при отделените за проверка заявки, а латентността му тепърва трябва да се потвърди на продукционния процесор.
Какво получава моделът
POST /query връща фрагменти по стабилен, документиран договор за метаданните:
{"query": "maximum fine for a data breach", "top_k": 5, "as_of": "2025-12-30"}
Отговорът е {"results": [...], "reranked": false, "score_kind": "rrf"}. Всеки резултат съдържа:
| Поле | За какво служи |
|---|---|
chunk_id |
Стабилен идентификатор, който кодира структурата от заглавия |
source_id |
Идентификаторът на документа: SHA-256 на байтовете на източника |
heading_path |
Път от заглавия като ["Chapter 1", "§ 5"], за цитиране и филтри по раздел |
byte_start, byte_end |
UTF-8 отмествания в съхранения Markdown текст |
valid_until |
Кога тази версия е заменена, или null, докато е актуална |
text |
Вграденият текст, ограден като недоверени данни |
score, rrf_score, rerank_score |
Оценката за подреждане и нейните съставки |
Всеки документ има и манифест: заглавие, източник, тип документ, статус, проект, парсерът или
моделът, създал текста, и дневник на жизнения цикъл (ingested, superseded, security_flagged,
llm_usage и други).
Версии, а не презаписване
Нова версия, заредена с --supersedes, или променен наблюдаван източник маркира старата с
valid_until — едновременно във front matter, в манифеста, в индекса по ключови думи и във
векторните данни. Търсенията връщат актуалните фрагменти по подразбиране; as_of връща валидното
на дадена дата; include_historical връща всичко. Ако наблюдавана страница се върне към предишно
състояние, по-ранната версия се възстановява. Замените и повторните изграждания преизползват
векторите на непроменените фрагменти.
Експлоатация
kb следва общата мрежова конвенция на pacod: всеки контейнер е в една Docker мрежа, pacod-net.
docker-compose.yml съдържа собствените услуги на kb, kb-pipeline и kb-ui.
docker-compose.standalone.yml съдържа копията на kb от общите компоненти, които се включват с
профилите на Docker Compose: Qdrant, PostgreSQL, pacod-id с база данни до тази на kb, прихващач на
поща и шлюз за разработка, чиито фиктивни доставчици държат всичко на машината. make up зарежда
и двата файла. На една машина може да работи само по едно копие от всеки общ компонент. В
продукция насочвате kb към истински pacod-gw, поставяте pacod-id зад TLS и премахвате профилите
за разработка.
pacod-demo, достъпен на demo.pacod.io, пуска kb заедно с останалите проекти на pacod. Той зарежда само файла на приложението на kb: kb държи базата си данни в общия PostgreSQL и колекциите си в общия Qdrant, вгражда през общата услуга за ембединги, а интерфейсът му се обслужва зад общия входен Caddy.
- Хранилище.
KB_STORAGE_BACKENDеfilesystemилиdatabase; смяната не мигрира документите. - Данни. Директориите с данни са променливи (
KB_ROOT,KB_MODEL_CACHE,KB_QDRANT_DATA,KB_POSTGRES_DATA). Име без наклонена черта е Docker том; под macOSmake setupизползва томове. - Процесор.
TORCH_NUM_THREADS=0следва квотата на контейнера за процесор.EMBED_BATCH_TOKENS(1024) ограничава колко дълго една заявка може да чака зад зареждане. - Модели офлайн. Пуснете веднъж с достъп до мрежата, за да се напълни кешът на моделите, след
това задайте
HF_HUB_OFFLINE=1. - Общи модели. С
EMBEDDINGS_URLконвейерът вгражда през общата услуга за ембединги, вместо сам да зарежда BGE-M3. Векторите са същите, така че нищо не се вгражда наново, а конвейерът има нужда от около 2 GB по-малко памет.RERANKER_URLправи същото за reranker-а. - Общ Qdrant. На Qdrant, който ползват и други продукти, страниците за Qdrant в интерфейса
показват и засягат само колекциите на kb (
KB_QDRANT_COLLECTION_PREFIX, по подразбиранеkb_). - Превключватели по време на работа. Таблото включва и изключва проверката на съдържанието, AI преобразуването и проверката на езика без рестарт; при следващото стартиране отново важат променливите на средата.
- AI. Администраторите избират модели и редактират промптове в AI Management. Всяко извикване носи непрозрачна атрибуция (функция, задача, проект, човек), а kb записва токените с разхода, изчислен от шлюза. Ако няма конфигуриран шлюз, AI е изключен и нищо друго не се чупи.
Резервните копия обхващат KB_ROOT или базата данни на kb в PostgreSQL, хранилището на Qdrant
(или го изградете наново с kb rebuild --all-projects), базата данни на pacod-id в същия
PostgreSQL заедно с ключа ѝ за криптиране на ключове, и .env.
Ограничения и още за четене
- Конвейерът и интерфейсът работят с по един worker: вграденият Qdrant държи заключване на файл, а управлението на сесиите в интерфейса предполага един процес.
- Reranker в собствения процес на конвейера приблизително удвоява паметта му; с
RERANKER_URLконвейерът не зарежда такъв. - Сръбският се приема, но детекторът на езици няма профил за сръбски, затова сръбски текст може да бъде разпознат грешно или отказан.
- Документирани за следващото повторно индексиране, но не включени: разредени вектори от BGE-M3 за половината с ключови думи, почистване на разделянето на фрагменти и изграждане в колекция-сянка преди превключване.
Повече подробности:
- Архитектура: контейнери, вход, шлюзът, кой какво е направил.
- Договор за метаданните: всяко поле на фрагмент и манифест.
- Оценка на извличането: пълните числа и решенията зад стойностите по подразбиране.
- Ръководство за експлоатация: разработка, продукция, експлоатация и отстраняване на проблеми.
- Миграции: надграждане на
съществуваща инсталация, включително преминаването към
pacod-net.
Технологии
- Написан на
- Python
- Технологии
- FastAPI
- Qdrant
- BGE-M3
- BM25
- SQLite
- PostgreSQL
- HTMX
- Docker
Бърз старт
make setupmake upopen http://localhost:8080/kbuv syncmake checkmake evalРаботи с
-
pacod-id
Хората влизат през pacod-id; ролите viewer, editor и admin се предоставят там.
-
pacod-gw
Незадължителното LLM почистване минава през шлюза, който остойностява и отчита всяко извикване.
-
pacod-ap
Когато работи заедно с pacod-ap, kb може да вгражда и пренарежда през неговата обща услуга за ембединги, със същите вектори от BGE-M3.
Лиценз
Apache-2.0