
pacod-ap хоства произволен брой самостоятелни AI агенти в едно внедряване. Всеки агент отговаря от собствената си база знания и точни справочни факти, посочва източниците си и се преглежда по собствените си разговори; всички споделят един вход чрез pacod-id, една административна конзола и един LLM шлюз, pacod-gw.
Платформа за изграждане на агенти
- Лиценз: Apache-2.0
- Версия: 0.1.0 · Предварителна
- Написан на: Python, TypeScript
- Технологии: FastAPI, LangGraph, MCP, SvelteKit, PostgreSQL, Redis, Qdrant, Caddy, Grafana
- Вижте в GitHub · Докладвайте проблем
Възможности
Агентите като профили
Всеки агент има собствена база знания, справочни факти, промптове, директиви, настройки, разговори и прегледи, а изолацията между агентите се налага в кода.
Справочни факти
Цените, ставките, лимитите и датите са типизирани редове в PostgreSQL, четени през MCP, като агентът и годината се задават от кода — моделът ги цитира, вместо да ги припомня.
Знания за всеки агент
Двуетапно търсене в собствените Qdrant колекции на агента (bge-m3, пренареждане с cross-encoder, родителски пасажи) плюс курирано ниво, одобрявано от администратори.
Визуален редактор на агенти
Включвайте и настройвайте компонентите на агента върху платно, изпробвайте черновата като dev версия и я публикувайте; всяка версия се пази, а връщането назад мести указател.
Защитен стрийминг чат
Проверките за език, квота и тема отговарят, преди потокът да се отвори; отговорът идва с източниците си, а същите агенти са достъпни и през OpenAI-съвместим API.
Цикъл на преглед
Работен процес анализира разговорите на всеки агент спрямо това, което той реално е прочел, пише поправката, групира повтарящите се проблеми и оставя всяка промяна на човек.
Една административна конзола
Агенти, потребители, знания, факти, директиви, промптове, модели, кешове, разговори, конзола за отстраняване на грешки по стъпки, анализи и бюлетин — за всички агенти или за един.
Многоезичност
Агентите отговарят на български и английски според внедряването и агента; интерфейсът е на английски, български, немски и руски.

Екранни снимки




Защо съществува
pacod-ap е създаден за агенти, чиято задача е да дават верни отговори с посочени източници в една област — там, където грешна цена, ставка или срок е по-лошо от липсата на отговор. Три правила го определят:
- Всяко число идва от структуриран източник на истина, никога от паметта на модела.
- Всяко твърдение посочва източник: пасаж от базата знания,
source_urlна факт или уеб страница от разрешения списък. - Администраторите коригират агента без ново внедряване. Моделите, промптовете, директивите, уеб търсенето и кеширането се прилагат още от следващия ход.
Платформата започва като български данъчен асистент и пази неговата структура. Данъчният асистент сега идва като разширен пример, а агентът по подразбиране е общ — направен, за да бъде копиран.
Едно внедряване, много агенти
Агентът е профил: един ред в таблицата profiles плюс всичко, което принадлежи към него. С
платформата идват два:
- starter, агентът по подразбиране: бюро за помощ на измислена услуга за бележки, „Nimbus Notes“, с 29 справочни факта, малък корпус на български и английски и златен набор от тестови въпроси. Дава на всяка функция нещо за показване.
- taxes, разширен пример за българските данъци и счетоводство, със собствени промптове, факти,
правен корпус и списък с разрешени сайтове за уеб търсене (
make seed-example PROFILE=taxes). Корпусът показва търсене в реален свод от закони. Той не се поддържа актуален и нищо, което агентът казва, не е правен или данъчен съвет.
Изолацията между агентите е граница на сигурността: всеки ред на агент носи profile_id, всеки
агент има собствени колекции в Qdrant, а ходът на един агент достига само до неговите данни.
| За всеки агент | Общо за внедряването |
|---|---|
| Справочни факти, директиви, промптове, заредени източници | Акаунти и роли (pacod-id) |
| Модел за всяка роля; настройки за уеб търсене, кеш, преглед и бюлетин | LLM шаблони и ключове за уеб търсене |
| Разговори, одитни записи, следи от търсенето, стъпки за отстраняване на грешки | Услугата за ембединги: едно пространство с 1024 измерения |
| Находки от прегледа, повтарящи се проблеми, златни тестове, версии от редактора | Доставчици, модели, цени и лимити за токени (pacod-gw) |
Хората виждат само агентите, които са им възложени, а нов човек получава всички пуснати агенти при
първата си заявка. Администраторите работят с всички агенти или с един, избран горе в конзолата.
Квотите се броят на човек, общо за всички агенти. Агентът е в едно от три състояния — prod, dev
или archived — и само агент в prod се вижда от някой, който не е администратор.
Частите
| Контейнер | Роля |
|---|---|
pacod-caddy |
Единственият публичен вход: TLS и обратно прокси, със сайтове за приложението, pacod-id и конзолата на шлюза |
ap-frontend |
SvelteKit backend-for-frontend: публичният сайт, чатът и административната конзола; токените за вход остават на сървъра |
ap-api |
API на чата и LangGraph агентът, плюс OpenAI-съвместим /v1 |
ap-admin-api |
Управляващата равнина: агенти, възлагания, настройки на агентите и фасади към pacod-id и шлюза |
ap-ingestion или ap-ingestion-cpu |
Разделя, ембедва и съхранява документи в колекциите на един агент |
ap-mcp |
Инструментите за справочни факти get_fact и list_facts |
pacod-embeddings |
Ембединги с bge-m3 и cross-encoder bge-reranker, общи за всички агенти |
ap-improvement-loop-worker, ap-newsletter-worker |
Винаги работещи процеси, които изпразват опашки в PostgreSQL, по една задача наведнъж |
ap-redis, ap-pgbouncer |
Кешове, квоти и сесиите на фронтенда; пулът от връзки пред PostgreSQL |
pacod-postgres, pacod-qdrant, pacod-id |
Споделени компоненти: PostgreSQL 18.6, векторното хранилище и доставчикът на идентичност |
Всеки контейнер има едно име в споделената Docker мрежа pacod-net: ap-* за собствените услуги
на pacod-ap, pacod-* за компонентите, които споделя с другите pacod проекти. Слушателите, до
които стигат само сървъри — API за управление на шлюза и вътрешният слушател на pacod-id — са във
втора мрежа, pacod-admin-net. Тя е вътрешна, без изход навън, и в нея влизат само онези, които
ги извикват.
Как протича един ход в чата
Проверките се изпълняват, преди нещо да тръгне по потока, така че отказът е истински HTTP статус, а не грешка по средата на потока:
- Удостоверяване. Токенът от pacod-id се проверява. Приключена сесия или деактивиран акаунт се отказва (401); ако ключовете за подпис на pacod-id не могат да се прочетат, отговорът е 503.
- Агент. Поисканият агент трябва да е възложен на човека и пуснат (иначе 403). Който не посочи агент, получава единствения си възложен — никога предположение.
- Език. Разпознава се при всеки ход и се сверява с езиците на агента (400).
- Квота. Дневният лимит за токени на човек в шлюза, по подразбиране 100 000 токена, плюс ограничение на минута и незадължителна квота за заявки в Redis (429). Ако шлюзът не отговори, проверката за токени пропуска хода и го записва в лога.
- Тема. Твърд филтър за незаконни фрази (403), после евтин класификатор: въпрос извън темата се отклонява (400), а неясен въпрос получава уточняващи въпроси.
След това се отваря потокът от server-sent events. Първи въпрос може да получи отговор от семантичния кеш на агента. Иначе се изпълнява малък LangGraph граф:
- plan: планиращият модел връща JSON с инструментите за извикване и дали въпросът е сложен.
- execute tools: всички планирани извиквания вървят едновременно:
kb_search,get_factиlist_facts, иweb_search. - synthesize: основният модел стриймва отговора от този контекст, с разширено мислене при сложни въпроси, а след него идват три предложени следващи въпроса.
Клиентът вижда хода като поредица от събития:
tool_call … citation … [thinking] … token … token … suggestions … done {turn_id, usage, quota}
По подразбиране последните четири размени се пазят дословно, а по-старите се сгъват в текущо
резюме, така че дългият разговор не изпраща целия си препис отново. OpenAI-съвместимият /v1
изпълнява същите проверки и същия агент, без кеша на отговорите: всеки агент е модел, а
<slug>@dev достига dev версията на агента за администратори. Той не минава през публичния вход;
клиентите го достигат във вътрешната мрежа с токена на човек или с pidt_ токена на служебен
акаунт в pacod-id.
Знания, факти и уебът
Планиращият модел избира източник според вида на твърдението:
- Числата, цените, ставките, лимитите и датите идват само от справочни факти: типизирани
редове в PostgreSQL, предоставяни през MCP от
ap-mcp. Кодът, а не моделът, задава агента и референтната година при всяко извикване, така че планиращият модел не може да стигне до фактите на друг агент. Число, което човекът изрично поиска, се проверява и срещу базата знания, и срещу уеба, където е разрешен: фактът печели, а разминаването се посочва. - Обясненията идват от базата знания: плътно търсене с bge-m3 в Qdrant (по подразбиране 20 кандидата), пренареждане с cross-encoder, което пази най-добрите пет, после малките части се разширяват до родителския си пасаж. Отделна, с по-малка тежест курирана колекция пази бележките, които администраторите са написали ръчно, повишили от разговор или одобрили от опашката за преглед.
- Новата информация идва от уеб търсене, винаги заедно с базата знания и ограничено до
списък с разрешени домейни за агента, който се проверява при заявката и отново при върнатите
адреси. За
starterто е изключено.
Фактът има една обща форма, така че цените на едно бюро за помощ и сроковете на един данъчен агент делят една таблица. Нова област е данни, а не схема:
{"fact_key": "plan_price_monthly", "variant": "plus", "category": "pricing",
"legal_year": 2026, "value_type": "money", "value_num": 6, "unit": "EUR",
"label_en": "Plus plan — price per user per month (billed monthly)",
"source_url": "https://nimbus-notes.example/pricing"}
Типовете стойности са number, percent, count, money, range, date, annual_date (ден
от всяка година), monthly_day, text и boolean. Администраторите редактират фактите на всеки
агент в конзолата, с износ и внос в JSON. Незадължителен проверяващ модел, изключен по
подразбиране, сверява числата и твърденията на черновата с резултатите от инструментите, преди
отговорът да бъде изпратен.
Редакторът на агенти
Суперпотребителите изграждат и променят агенти във визуален редактор. Агентът е граф от компоненти
от библиотека, подредени върху платно: входът на чата, проверките за език, квота и тема,
семантичният кеш, паметта на разговора, планиращият модел, трите инструмента, синтезиращият модел,
проверяващият модел, предложенията за следващи въпроси и потокът на чата. Засега всеки граф следва
един шаблон, разговорния RAG агент (rag_chat_v1), с по едно място за всеки компонент, така че
компонентите се включват, изключват и настройват, а не се свързват свободно.
- Наследяване или промяна. Всяка настройка на компонент или наследява текущата стойност на агента, или я заменя. Формата се генерира от схемата на компонента.
- Чернова, dev, продукция. Промяната се записва като чернова, внедрява се като dev версия, с която могат да разговарят само администратори (в тестовия панел на редактора или с превключвател PROD/DEV в чата), и се публикува, когато е готова. Версиите само се добавят, така че публикуването и връщането назад само местят указател, а и двете изчистват кешовете на отговорите на агента.
- AI асистент пише чернови на текстовете на компонент, например промптовете му, по указание на автора. Той никога не пипа модели, числа или превключватели, предложенията му се проверяват като чернова и нищо не се променя, докато авторът не ги приложи.
- Публична страница. Редакторът пази и текста за агента в публичния сайт: картата му на
началната страница и собствената му страница на
/agents/<slug>.
Настройка, която публикувана версия управлява, е заключена на по-старите страници за настройки: те показват стойността ѝ и отказват да я променят, така че двете места никога не си противоречат.
Цикълът на подобрение
Всеки агент се подобрява от собствения си трафик. Работен процес преглежда последните разговори на
агента, когато администратор натисне Run now, всеки ден, след като графикът бъде включен (по
подразбиране е изключен), или за един ход, избран на страницата Conversations. Преписите първо се
анонимизират: имейл адреси, IBAN, номера на карти и телефони, а с пакета bg и ЕГН, ЕИК и
БУЛСТАТ. След това пускането минава през пет етапа:
- Сортиране. Ходовете, които човекът е оценил, винаги се анализират; празните, извън темата, уточняващите и кешираните винаги отпадат. За останалите по подразбиране решава евтин модел; може и всички да се анализират, или всички да отпаднат.
- Доказателства. Проверяващият чете това, което асистентът реално е прочел: дословния контекст на отговора с фактите и пасажите му, действащите директиви, разговора дотук, извикванията на инструменти и пасажите, които търсенето е намерило и отхвърлило.
- Анализ. Двама независими анализатори и съдия или един анализатор и критик. Състав, който не е наистина независим, го казва преди и след пускането и никога не се брои за пълно съгласие.
- Насочване и поправка. Всяка находка отива там, където може да се поправи, с написана поправка: отговора, който е трябвало да бъде даден, и фактите, които един тест може да провери.
- Групиране. Сходните находки се групират в повтарящи се проблеми. Повтарящият се проблем е задачата, по която работи проверяващият: отворена, в работа, решена или отхвърлена.
| Грешка | Къде отива |
|---|---|
| Липса в съдържанието | Кандидат за базата знания в опашката Review, който човек редактира и одобрява |
| Пропуск в търсенето | Следата от търсенето на хода, за настройка |
| Халюцинация, тон, поведение | Поправен отговор и предложено правило, което при поискване става неактивна директива |
| Чист отговор | Златен тест с фактите, които отговорът трябва да съдържа |
Нищо не стига до курираната колекция, преди администратор да го одобри, освен ако флагът за автоматично добавяне, изключен по подразбиране, е включен и минат всички строги проверки: пълно съгласие, пълни доказателства, обосновано твърдение с нисък риск и висока увереност. Твърдение, което източниците опровергават, никога не се добавя. Проверяващите отбелязват всяка находка като полезна, отхвърлена, дубликат или поправена, а делът на отхвърлените е собствената мярка на цикъла за това колко добре работи. Два заключителни прохода проверяват поправките: мета-оценка, най-много веднъж дневно след пълно пускане, дали курираните бележки се използват и незадължително повторно задаване на въпросите зад обявените поправки през живия агент (изключено по подразбиране, защото всеки случай е цял ход).
Сигурност и изолация
- Идентичността е делегирана. Акаунтите, паролите, сесиите и ролите живеят в pacod-id. Фронтендът вписва хората като поверителен клиент и пази токените им в Redis; браузърът държи само непрозрачен идентификатор на сесията. API-то следи потока от събития на pacod-id, така че приключена сесия или деактивиран акаунт се отказват за секунди, а административните страници отказват да действат по копие на ролите, по-старо от 60 секунди. Промените по акаунти и роли се правят в pacod-id от името на администратора, който ги прави: само суперпотребител прави суперпотребител, а последният администратор не може да бъде премахнат.
- Агентите са оградени в кода. Агентът се упълномощава преди всякаква работа, аргументите на
инструментите, които докосват данни на агента, се задават от кода,
/v1/modelsпоказва само агентите на извикващия, а всеки кеш ключ на агент носи идентификатора му. - Вътрешните врати са заключени. MCP инструментите и крайната точка, която приема телеметрията на
фронтенда, искат споделен токен и отказват всяко извикване, докато той е празен. Токените за разработка
искат два отделни превключвателя. API за управление на шлюза и вътрешният слушател на pacod-id са
достъпни само в
pacod-admin-net. - Тайните стоят на мястото си. Ключовете на LLM доставчиците живеят само в шлюза. Ключовете за уеб търсене са шифровани в базата данни. Файловете с тайни се генерират, имат права 0600 и са в gitignore, а скриптовете, които ги пишат, извеждат имена, никога стойности. Логът за достъп на публичния вход пропуска кодовете за вход, токените и ключовете.
- Безопасни промптове. Отказът от незаконни искания стои най-горе в промптовете на планиращия и синтезиращия модел и е над директивите на администраторите, а твърд филтър на фрази работи преди класификатора. Текстът на администратори и потребители винаги се вмъква като стойност, никога като шаблон.
Пускане и поддръжка
Три Docker Compose стека тръгват по ред и make up-all прави точно това:
- Приложението, в два compose файла. Файлът на приложението съдържа собствените услуги на pacod-ap, Redis, PgBouncer и услугата за ембединги. Самостоятелният файл съдържа неговите копия на споделените компоненти: Caddy, PostgreSQL, Qdrant и pacod-id, а локално и mailpit.
- Шлюзът: файлът на приложението на pacod-gw от хранилище до това. Той влиза през същия pacod-id, пази базата си в същия PostgreSQL и се обслужва от същия Caddy.
- Наблюдението: Prometheus, Alertmanager и Grafana. То не е задължително и
make up-allго пуска, когато съществуваmonitoring/.env.
На лаптоп приложението е на http://localhost:8080, pacod-id на http://127.0.0.1:8070, конзолата
на шлюза на http://127.0.0.1:13000, а всички писма на http://127.0.0.1:8025. Шлюзът отговаря
със своите имитиращи доставчици: предварително зададени отговори, без разходи. Docker има нужда от
около 8 GB памет и 20 GB диск; LOCAL_EMBEDDINGS_BACKEND=hash пропуска модела за ембединги от 2 GB,
с цената на безсмислени резултати от търсенето.
Тъй като споделените компоненти са в отделен файл, pacod-demo зарежда файла на приложението на pacod-ap до другите pacod проекти и пуска по едно копие от всеки споделен компонент; резултатът работи на demo.pacod.io, с pacod-ap на app.demo.pacod.io. Демо без ключ на доставчик отговаря с имитацията на шлюза, а без незадължителния си компонент за зареждане на документи агентът starter отговаря само от справочните си факти. На един хост може да работи само по едно копие от всеки споделен компонент, така че самостоятелен pacod-ap не работи до друг pacod стек.
- Настройките се четат от кеш в Redis, после от ред в
app_settingsс ключ по агент, после от стойността по подразбиране в средата. При срив се използват стойностите по подразбиране, вместо ходът да спре. - Обръщенията към LLM се приписват на човека, сесията, хода, агента и целта, така че страниците за използване, разходи и анализи в конзолата четат отчетите на шлюза, вместо да водят собствена счетоводна книга. Следата на шлюза за един ход е самият ход. Обръщение с изтекло време никога не се повтаря, защото доставчикът го таксува така или иначе: човекът пита отново.
- Наблюдаемост. Одитен запис за всяка заявка, включвана по желание конзола за отстраняване на грешки по стъпки, структурирани логове в общия формат на pacod и Prometheus метрики във всяка услуга, с табла в Grafana за приложението, инфраструктурата, уеб трафика, обхождащите ботове и използването, плюс собствените табла на шлюза.
- Резервно копие.
make backupзаписва един архив с дъмповете на PostgreSQL, снимките на Qdrant, качените документи, настройките и тайните, които трябва да пътуват заедно с дъмповете;make restoreправи обратното. Собствените хранилища на шлюза се архивират с инструментите на pacod-gw.
Какво е тествано и какви са границите
- Unit тестовете вървят без база данни, Redis или шлюз и покриват изолацията между агентите, клиента на шлюза спрямо записани отговори, проверката на идентичността и каталога с роли. Интеграционните тестове вървят срещу работещ стек: MCP сървъра с фактите на starter, включително изолацията, и проверките на чата.
make smoke-allпроверява здравето на услугите, пуска един ход в чата през имитацията на шлюза, проверява, че следата на шлюза за този ход струва колкото отчита самият ход в събитиетоdone, и пуска собствения smoke тест на шлюза.- Преходът от LiteLLM прокси към pacod-gw е проверен срещу имитацията на шлюза: делти на разсъжденията, цитати от вградено уеб търсене, кеширани токени, отрязани отговори, параметрите за мислене и кеширане на промпта и формата на изтеклото време. Две проверки остават отворени и за двете трябват ключове на доставчици: сравнение със записани отговори на старото прокси и един жив сложен ход.
- Истински доставчици. Преминаването към истински доставчици е подготвено, но още не е изпълнено с истински ключове.
- Изключени по подразбиране: дневният график на прегледа, автоматичното добавяне в курираното ниво, повторното задаване на поправените въпроси и проверяващият модел. Дообучението на търсенето по обратната връзка се пуска ръчно; нищо не го планира.
- Граници. Агентите говорят два езика, а трети е промяна в кода, не настройка. Редакторът има един шаблон. Ход с изтекло време се губи и трябва да бъде зададен отново.
Изградете свой агент
Има два пътя:
- В конзолата. Суперпотребител създава агент на страницата Profiles или в редактора на агенти.
Новият агент започва в
devс общите промптове; изпробвате го там и го публикувате, когато е готов. - Като код.
ProfileSpecвpacod-api/scripts/profiles/<id>.py, с данните вpacod-api/data/profiles/<id>/: справочни факти, корпус и златен набор от въпроси.
from scripts.seed_profile import ProfileSpec
SPEC = ProfileSpec(
profile_id="helpdesk",
slug="helpdesk",
name="Acme Help Desk",
description="Answers questions about Acme's products and plans.",
topic_name="acme_helpdesk",
topic_description_en="Questions about Acme's products: plans, prices, limits, accounts, support.",
topic_description_bg="Въпроси за продуктите на Acme: планове, цени, лимити, акаунти, поддръжка.",
legal_year=2026, # the reference year the facts and the corpus are filed under
languages="en,bg",
settings_overrides={"websearch.enabled": "false"},
assign_to_all_users=True,
)
make seed-example PROFILE=<id> го инсталира: агента, колекциите му, заменените промптове и
настройки (никога върху това, което администратор вече е настроил), фактите и корпуса му.
Описанието на темата е важно: проверката на темата сверява всеки въпрос с него, а цикълът на
преглед го използва като обхват на агента. После разговаряйте с агента, погледнете конзолата за
отстраняване на грешки и следата на шлюза за един ход и пуснете make eval срещу златния набор.
Оттам нататък поема цикълът на преглед.
Прочетете още
- Runbook: стекове, тайни, истински доставчици, резервни копия, отстраняване на проблеми.
- LLM шлюзът: приписване, повторни опити, квоти, използване.
- Изградете свой агент: пълното ръководство.
- Архитектура на API и архитектура на UI: дизайнът в подробности.
Бърз старт
Бърз старт на лаптоп
make secrets-init
make up-all
make bootstrap-local
make smoke-all
open http://localhost:8080
Работи с
- pacod-gw: Всяко обръщение към LLM минава през шлюза и се приписва на човека, хода, агента и целта; моделите, цените, ключовете на доставчиците, лимитите за токени, използването и разходите са на шлюза.
- pacod-id: Акаунтите, паролите, сесиите и ролите живеят в pacod-id; хората влизат през него, а приложението и конзолата на шлюза споделят един акаунт.
- pacod-kb: pacod-ap носи pacod-embeddings, споделената услуга за ембединги и пренареждане с bge-m3; пуснат до нея, pacod-kb може да ембедва и пренарежда през нея, вместо сам да зарежда модела.