Пошаговая инструкция по созданию кадрового AI-ассистента, получающего данные из 1С ЗУП через Apache Kafka и предоставляющего информацию через чат-интерфейс n8n.
Создайте файл docker-compose.yml:
version: '3.8'
services:
postgres:
image: postgres:16
container_name: n8n_postgres
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n
POSTGRES_PASSWORD: n8n_secure_password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
networks:
- app_network
kafka:
image: confluentinc/cp-kafka:7.7.0
container_name: n8n_kafka
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:29093
KAFKA_LISTENERS: PLAINTEXT://kafka:29092,CONTROLLER://kafka:29093,EXTERNAL://0.0.0.0:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,EXTERNAL://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_LOG_DIRS: /tmp/kraft-combined-logs
CLUSTER_ID: MkQkQm1sQWVFN2VkOGp5bGwzbQ
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
ports:
- "9092:9092"
volumes:
- kafka_data:/tmp/kraft-combined-logs
restart: unless-stopped
networks:
- app_network
ollama:
image: ollama/ollama:latest
container_name: n8n_ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
restart: unless-stopped
networks:
- app_network
n8n:
image: n8nio/n8n:latest
container_name: n8n_app
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: n8n_secure_password
N8N_HOST: localhost
N8N_PORT: 5678
N8N_PROTOCOL: http
WEBHOOK_URL: http://localhost:5678/
ports:
- "5678:5678"
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
- kafka
- ollama
restart: unless-stopped
networks:
- app_network
volumes:
postgres_data:
kafka_data:
ollama_data:
n8n_data:
networks:
app_network:
driver: bridgedocker compose up -d
docker compose ps # проверяем что все сервисы запущеныПосле запуска:
- n8n доступен по адресу:
http://localhost:5678 - Kafka слушает на порту:
9092 - PostgreSQL слушает на порту:
5432 - Ollama API доступен по адресу:
http://localhost:11434
После запуска контейнеров загружаем необходимые модели:
# Модель для создания эмбеддингов (векторная БД)
docker exec -it n8n_ollama ollama pull qwen3-embedding:8b
# Модель для генерации ответов агента
docker exec -it n8n_ollama ollama pull gpt-oss:120b-cloud
# Проверяем список загруженных моделей
docker exec -it n8n_ollama ollama list- Перейдите в репозиторий: https://github.com/NuclearAPK/Simple-Kafka_Adapter
- Откройте раздел Releases на странице репозитория
- Скачайте готовую сборку для вашей платформы (Windows x64 / Linux x64)
- Файл компоненты (
.dll/.so) необходимо поместить в общий макет конфигурации 1С
На сервере (рекомендуется для продуктивной работы):
// Настоятельно рекомендуется подключать компоненту в изолированном режиме,
// чтобы падение компоненты не приводило к падению процесса rphost
ПодключитьВнешнююКомпоненту("ОбщийМакет.SimpleKafka1C", "Integration",
ТипВнешнейКомпоненты.Native,
ТипПодключенияВнешнейКомпоненты.Изолированно);
Компонента = Новый("AddIn.Integration.simpleKafka1C");
На клиенте (асинхронный вызов):
Ожидать ПодключитьВнешнююКомпонентуАсинх("ОбщийМакет.SimpleKafka1C", "Integration",,
ТипПодключенияВнешнейКомпоненты.Изолированно);
Компонента = Новый("AddIn.Integration.simpleKafka1C");
- Перейдите в репозиторий: https://github.com/NuclearAPK/Kafka1CExtension
- Скачайте расширение (файл
.cfe) из раздела Releases, либо соберите из исходников (папкаsrc) - В 1С откройте: Администрирование → Печатные формы, отчеты и обработки → Расширения
- Нажмите Добавить из файла и выберите скачанный файл
.cfe - Установите флаг Активно для подключенного расширения
- Перезапустите 1С
Расширение содержит внешнюю компоненту, примеры работы с ней, а также базовые объекты для встраивания в конфигурации.
Механизм ИсторияВерсийДанных позволяет отслеживать изменения объектов и реагировать на них (например, отправлять событие в Kafka при изменении кадровых данных).
В подключенном расширении Kafka1CExtension необходимо добавить объекты, для которых требуется отслеживание изменений (например, документ КадровыйПеревод, справочник Сотрудники и т.д.).
В модуле объекта (в расширении) добавьте обработчик ПередЗаписью:
Процедура ПередЗаписью(Отказ)
ЭтотОбъект.ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписиВерсии = Истина;
ЭтотОбъект.ЗаписьИсторииДанных.ОбновлятьИсториюСразуПослеЗаписи = Истина;
КонецПроцедуры
ВыполнятьОбработкуПослеЗаписиВерсии— включает вызов обработчикаОбработкаПослеЗаписиВерсийИсторииДанныхпосле записи версииОбновлятьИсториюСразуПослеЗаписи— записывает историю синхронно, а не в фоновом задании
Если долгосрочное хранение истории версий не требуется (данные нужны только для триггера отправки в Kafka), рекомендуется удалять историю после обработки.
Важно: Если хранение истории версий не предполагается в принципе, следует периодически очищать накопленные записи, чтобы не допустить разрастания базы данных.
В модуле менеджера объекта добавьте функцию, описывающую формат сообщения:
Функция СтруктураСообщенияОбмена()
Результат = Новый Структура;
Результат.Вставить("uid", "");
Результат.Вставить("fio", "");
Результат.Вставить("func", "");
Результат.Вставить("unit", "");
Результат.Вставить("status", "");
Результат.Вставить("llc", "");
Результат.Вставить("description", "");
Возврат Результат;
КонецФункции
В модуле объекта (в расширении) добавьте процедуру, которая вызывается после записи версии истории данных. Эта процедура собирает кадровые данные и отправляет их в Kafka:
Процедура ОбработкаПослеЗаписиВерсийИсторииДанных(ИнформацияОЗаписиВерсий)
Если ИнформацияОЗаписиВерсий.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ первые 1
| КИС.Сотрудник,
| ПРЕДСТАВЛЕНИЕ(КИС.Сотрудник) КАК employee,
| ПРЕДСТАВЛЕНИЕ(КИС.Сотрудник.ФизическоеЛицо) КАК fio,
| ПРЕДСТАВЛЕНИЕ(КИС.Должность) КАК func,
| ПРЕДСТАВЛЕНИЕ(КИС.Подразделение) КАК unit,
| ПРЕДСТАВЛЕНИЕ(ЕСТЬNULL(ДанныеСостоянийСотрудников.Состояние, """")) КАК status,
| ПРЕДСТАВЛЕНИЕ(КИС.Организация) КАК llc,
| ПРЕДСТАВЛЕНИЕ(КИС.Сотрудник.кОписаниеОбязаностей) КАК description
|ИЗ
| РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&ДатаСреза,
| Сотрудник = &Сотрудник) КАК КИС
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДанныеСостоянийСотрудников
| КАК ДанныеСостоянийСотрудников
| ПО (КИС.Сотрудник = ДанныеСостоянийСотрудников.Сотрудник)";
// Установка значений параметров
Запрос.УстановитьПараметр("Сотрудник",
ИнформацияОЗаписиВерсий[0].Данные.Сотрудник);
Запрос.УстановитьПараметр("ДатаСреза", Неопределено);
РезультатЗапроса = Запрос.Выполнить();
СообщениеОбмена = СтруктураСообщенияОбмена();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
ЗаполнитьЗначенияСвойств(СообщениеОбмена, Выборка);
СообщениеОбмена.uid = XMLСтрока(Выборка.Сотрудник);
КонецЦикла;
ОбъектJSON = Новый ЗаписьJSON;
ОбъектJSON.УстановитьСтроку();
ЗаписатьJSON(ОбъектJSON, СообщениеОбмена);
СтрокаJSON = ОбъектJSON.Закрыть();
Ключ = Строка(Новый УникальныйИдентификатор);
Продюсер = Константы.KFK_ОсновнойПродюсер.Получить();
ТаблицаТопики = Продюсер.Топики;
Если ТаблицаТопики.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Топик = ТаблицаТопики[0].Топик;
АдресБрокера = ТаблицаТопики[0].Брокер.АдресБрокера;
ИдентификаторВК = KFK_Интеграция.ИдентификаторВнешнейКомпоненты();
Компонента = KFK_Интеграция.ПодключитьКомпонентуКафка(ИдентификаторВК);
// Рекомендуемые параметры для синхронной отправки
Компонента.УстановитьПараметр("queue.buffering.max.ms", "1");
Компонента.УстановитьПараметр("message.timeout.ms", "5000");
Для каждого СтрокаПараметров Из ТаблицаТопики[0].Брокер.ПараметрыПодключения Цикл
Компонента.УстановитьПараметр(
СокрЛП(СтрокаПараметров.Ключ),
СокрЛП(СтрокаПараметров.Значение));
КонецЦикла;
РезультатИнициализации = Компонента.ИнициализироватьПродюсера(АдресБрокера);
Если РезультатИнициализации Тогда
// Синхронная отправка с ожиданием подтверждения от брокера
РезультатОтправки = Компонента.ОтправитьСообщениеСОжиданиемРезультата(
СтрокаJSON, Топик,, Ключ);
Если РезультатОтправки = 2 Тогда // RD_KAFKA_MSG_STATUS_PERSISTED
// Сообщение успешно доставлено и подтверждено брокером
ИначеЕсли РезультатОтправки = 1 Тогда // RD_KAFKA_MSG_STATUS_POSSIBLY_PERSISTED
KFK_ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
"Сообщение возможно доставлено: "
+ Компонента.ПолучитьСообщениеОбОшибке());
Иначе // 0 или -1
ВызватьИсключение "Ошибка доставки: "
+ Компонента.ПолучитьСообщениеОбОшибке();
КонецЕсли;
Компонента.ОстановитьПродюсера();
Иначе
ВызватьИсключение Компонента.ПолучитьСообщениеОбОшибке();
КонецЕсли;
КонецПроцедуры
Примечание: Если вы записываете историю версии справочника Сотрудники (а не документа), замените
ИнформацияОЗаписиВерсий[0].Данные.СотрудникнаИнформацияОЗаписиВерсий[0].Данные— так как сам объект и является сотрудником.
Перед отправкой сообщений убедитесь, что целевой топик существует. Если топика нет, создайте его через метод компоненты Simple Kafka:
ИдентификаторВК = KFK_Интеграция.ИдентификаторВнешнейКомпоненты();
Компонента = KFK_Интеграция.ПодключитьКомпонентуКафка(ИдентификаторВК);
// СоздатьТопик(АдресБрокера, ИмяТопика, КоличествоПартиций, ФакторРепликации)
Результат = Компонента.СоздатьТопик("localhost:9092",
"zup.employees.updates.ok", 1, 1);
Если Результат Тогда
Сообщить("Топик успешно создан");
Иначе
Сообщить("Ошибка: " + Компонента.ПолучитьСообщениеОбОшибке());
КонецЕсли;
Параметры метода СоздатьТопик:
| Параметр | Тип | Описание |
|---|---|---|
Брокер |
Строка | Адрес брокера Kafka (например, localhost:9092) |
ИмяТопика |
Строка | Имя создаваемого топика |
КоличествоПартиций |
Число | Количество партиций |
ФакторРепликации |
Число | Фактор репликации (для одного брокера — 1) |
Для проверки отправленных сообщений можно использовать следующие инструменты:
Легковесный CLI-инструмент для работы с Kafka.
# Установка
# macOS: brew install kcat
# Ubuntu: sudo apt install kcat
# Docker: см. ниже
# Чтение всех сообщений из топика с начала
kcat -b localhost:9092 -t zup.employees.updates.ok -C -o beginning
# Чтение последних 5 сообщений
kcat -b localhost:9092 -t zup.employees.updates.ok -C -o end -c 5
# Форматированный вывод с ключом и временем
kcat -b localhost:9092 -t zup.employees.updates.ok -C \
-f 'Key: %k\nValue: %s\nTimestamp: %T\n---\n'
# Список всех топиков
kcat -b localhost:9092 -L
# Через Docker (без локальной установки)
docker run --network=host edenhill/kcat:1.7.1 \
-b localhost:9092 -t zup.employees.updates.ok -C -o beginning# Через docker exec в контейнер Kafka
docker exec -it n8n_kafka kafka-console-consumer \
--bootstrap-server localhost:29092 \
--topic zup.employees.updates.ok \
--from-beginning
# С выводом ключей
docker exec -it n8n_kafka kafka-console-consumer \
--bootstrap-server localhost:29092 \
--topic zup.employees.updates.ok \
--from-beginning \
--property print.key=true \
--property key.separator=" | "Графическое приложение для работы с Kafka:
- Скачайте с offsetexplorer.com
- Установите и запустите
- Создайте подключение:
localhost:9092 - В дереве найдите топик
zup.employees.updates.ok→ вкладка Data → просмотр сообщений
- Откройте n8n в браузере:
http://localhost:5678 - Перейдите в Settings (иконка шестеренки в левом нижнем углу)
- Выберите раздел API
- Нажмите Create an API key (Создать API Ключ)
- Скопируйте сгенерированный ключ — он понадобится для подключения MCP-сервера
Путь в интерфейсе: Settings → API → Create an API key
- Откройте VS Code
- Перейдите в Extensions (Ctrl+Shift+X)
- В поиске введите:
Claude Code - Найдите расширение Claude Code от Anthropic
- Нажмите Install
Также можно установить через терминал:
code --install-extension anthropic.claude-codeBypass-режим позволяет Claude Code автоматически выполнять операции без подтверждения каждого действия.
- Откройте Settings VS Code (Ctrl+,)
- В поиске настроек введите:
claude code - Найдите параметр Claude Code: Auto Approve
- Либо в терминале Claude Code используйте команду:
# Включить режим автоматического одобрения всех действий
claude config set autoApprove true
# Или для конкретных категорий
claude config set autoApprove.read true
claude config set autoApprove.edit true
claude config set autoApprove.bash trueТакже можно переключать режим прямо в чате Claude Code:
- Нажмите на индикатор режима в нижней панели
- Или используйте команду
/permissionsв чате
- Откройте VS Code
- File → Open Folder (Ctrl+K, Ctrl+O)
- Создайте новую папку для проекта (например,
n8n-hr-assistant) - Выберите созданную папку
- Откройте терминал (Ctrl+`)
- Инициализируйте проект:
git initCLAUDE.md — это файл конфигурации проекта для Claude Code. Он задает контекст, правила и инструменты, которые Claude будет использовать при работе с проектом. Это аналог системного промта, но привязанный к конкретному проекту. При каждом запуске сессии Claude Code автоматически считывает этот файл и следует указанным в нем инструкциям.
В терминале Claude Code введите следующий промт:
Помоги мне заполнить Claude.md для данного проекта. Этот проект создан для того
чтобы с твоей помощью автоматизировать процессы n8n. Для этого я предоставлю
доступ к инструментам:
n8n skills: https://github.com/czlonkowski/n8n-skills
n8n mcp: https://github.com/czlonkowski/n8n-mcp
Используй данные инструменты в сочетании с моими запросами для создания
качественных workflow n8n.
Все описание должно быть на русском языке и фокус workflow на AI-агентах.
Claude Code проанализирует указанные репозитории и создаст CLAUDE.md с описанием проекта, доступных инструментов и правил работы.
После успешного составления CLAUDE.md введите:
Установи mcp сервер n8n: https://github.com/czlonkowski/n8n-mcp
Установи skills: https://github.com/czlonkowski/n8n-skills
MCP-сервер — это локальный сервер, который предоставляет Claude Code набор инструментов (tools) для прямого взаимодействия с внешними системами. В данном случае MCP-сервер n8n позволяет Claude:
- Создавать, читать, обновлять и удалять workflow в n8n через API
- Управлять нодами и соединениями
- Активировать/деактивировать рабочие процессы
- Получать информацию о существующих workflow
Skills — это набор промтов и шаблонов, которые обучают Claude Code правильно работать с n8n. Они содержат:
- Шаблоны для типовых workflow (вебхуки, AI-агенты, обработка данных)
- Правила валидации конфигурации нод
- Паттерны построения рабочих процессов
- Знания о синтаксисе выражений n8n
Skills работают как «память» Claude о best practices n8n, а MCP — как «руки» для непосредственного управления инстансом.
После настройки MCP и Skills введите следующий промт:
Необходимо создать кадрового ассистента, который владеет кадровой информацией
по сотрудникам и предоставляет запрашиваемую информацию из кадровой базы знаний.
Интерфейс взаимодействия с ассистентом - публичный опубликованный чат.
Информация в кадровую базу знаний будет попадать посредством kafka.
Всю необходимую информацию по формату сообщения, моделям и топикам можешь
запросить у меня.
Необходимо создать 2 рабочих процесса в инстансе n8n.
Первый workflow: будет считывать из Kafka топика zup.employees.updates.ok
сообщения, в которых содержится кадровая информация о сотруднике - формат json
с полями:
- llc - организация в которой числится сотрудник
- unit - подразделение в котором числится сотрудник
- description - описание чем занимается сотрудник, особенности
- status - состояние (работает, уволен)
- func - должность сотрудника
- fio - фамилия имя отчество
- uid - уникальный идентификатор сотрудника.
Информацию из сообщения необходимо сохранять в векторную базу данных.
В качестве векторной базы используй Postgres. Модель для embedding -
qwen3-embedding:8b через Ollama. Ключом будет выступать uid. При записи данных,
которые содержат uid, который уже есть в базе - должно происходить обновление
записи. В ноде, которая получает сообщения из kafka должны быть заданы опции
JSON Parse Message = true и Only message.
Второй рабочий процесс - это интерфейс агента через чат (который должен быть
публичным, опубликованным). Агент должен выступать в роли специалиста, который
владеет кадровой информацией по сотрудникам и предоставляет запрашиваемую
информацию из кадровой базы знаний, которая реализована в первом рабочем
процессе. Отрази это в системном промте агента. Если в информации по сотруднику
указано что сотрудник не работает, например - уволен - обязательно укажи это
в ответе. Ответы должны быть лаконичными. В качестве модели используй gpt-oss:20b
в ollama chat model.
Claude Code создаст два workflow в n8n:
Workflow 1: Kafka → Векторная БД
[Kafka Trigger] → [JSON Parse] → [Upsert в PGVector по uid]
↓
[Ollama Embeddings: qwen3-embedding:8b]
- Триггер Kafka подписан на топик
zup.employees.updates.ok - Опции:
JSON Parse Message = true,Only message = true - Данные сохраняются в PostgreSQL с pgvector
- При совпадении
uid— обновление существующей записи
Workflow 2: Чат-агент
[Chat Trigger (публичный)] → [AI Agent] → [Ответ в чат]
↓
[PGVector Tool: поиск по базе]
[Ollama Chat: gpt-oss:20b]
- Публичный чат-интерфейс (опубликованный webhook)
- AI-агент с системным промтом кадрового специалиста
- Поиск по векторной базе знаний сотрудников
- Обязательное указание статуса сотрудника (уволен/работает)
- Лаконичные ответы
┌─────────────┐ Kafka ┌─────────────┐ PGVector ┌──────────┐
│ 1С ЗУП │───────────────→│ n8n WF #1 │───────────────→│ Postgres │
│ (Simple │ zup.employees │ (Kafka → │ Embeddings │ (pgvector│
│ Kafka) │ .updates.ok │ Vector) │←──────────────→│ DB) │
└─────────────┘ └─────────────┘ Ollama └────┬─────┘
qwen3-embedding │
:8b │
│ Поиск
┌─────────────┐ ┌─────────────┐ │
│ Пользователь│←──────────────→│ n8n WF #2 │─────────────────────┘
│ (Браузер) │ Публичный │ (AI Agent │
│ │ чат │ + Chat) │←── Ollama gpt-oss:20b
└─────────────┘ └─────────────┘