
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 · Создать issue
Возможности
Агенты как профили
У каждого агента своя база знаний, справочные факты, промпты, директивы, настройки, разговоры и разборы, а изоляцию между агентами обеспечивает код.
Справочные факты
Цены, ставки, лимиты и сроки — это типизированные строки в 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 в чате), и публикуется, когда готово. Версии только добавляются, поэтому публикация и откат лишь переставляют указатель, и оба сбрасывают кэши ответов агента.
- ИИ-ассистент набрасывает тексты компонента, например его промпты, по указанию автора. Он никогда не трогает модели, числа и переключатели, его предложения проверяются как черновик, и ничего не меняется, пока автор их не применит.
- Публичная страница. Редактор хранит и текст агента для публичного сайта: его карточку на
главной странице и собственную страницу по адресу
/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.
Что проверено и где пределы
- Модульные тесты работают без базы данных, 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 может пользоваться им, а не загружать модель сам.