惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
宝玉的分享
宝玉的分享
GbyAI
GbyAI
H
Hacker News: Front Page
WordPress大学
WordPress大学
博客园 - 【当耐特】
I
InfoQ
T
The Blog of Author Tim Ferriss
雷峰网
雷峰网
S
Secure Thoughts
S
Security @ Cisco Blogs
Engineering at Meta
Engineering at Meta
L
Lohrmann on Cybersecurity
博客园 - 叶小钗
K
Kaspersky official blog
G
Google Developers Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
AWS News Blog
AWS News Blog
T
Tailwind CSS Blog
D
DataBreaches.Net
Attack and Defense Labs
Attack and Defense Labs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
About on SuperTechFans
N
News and Events Feed by Topic
F
Full Disclosure
C
CERT Recently Published Vulnerability Notes
H
Help Net Security
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Check Point Blog
Latest news
Latest news
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Recorded Future
Recorded Future
Know Your Adversary
Know Your Adversary
Y
Y Combinator Blog
P
Palo Alto Networks Blog
Help Net Security
Help Net Security
O
OpenAI News
V
V2EX
有赞技术团队
有赞技术团队
爱范儿
爱范儿
罗磊的独立博客
Blog — PlanetScale
Blog — PlanetScale
C
CXSECURITY Database RSS Feed - CXSecurity.com
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
LINUX DO - 最新话题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cloudbric
Cloudbric

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Технический и продуктовый мониторинг за кастомизациями Битрикс24: как настроить и на что смотреть
Игорь Росляков · 2026-06-17 · via Все публикации подряд на Хабре

Средний

16 мин

29

Привет! Меня зовут Игорь Росляков, я технический писатель. По приглашению руководителя направления «Маркет и интеграции» Сергея Вострикова готовлю цикл статей на тему ИИ-ассистированной разработки решений для Битрикс24.

Сегодня посмотрим на практике, как работать с техническими метриками и продуктовыми сигналами, если у вас есть приложение для портала Битрикс24. Из чего состоит работа и что будем делать:

  • Первая часть проекта — готовое приложение чат-бота, которое подключено к моему порталу. Его можно посмотреть здесь: github.com/igorrosliakov-bitrix24/Bitrix24-ChatBot

  • Вторая часть — проект телеметрии, который специально создан для подключения Observability к кастомизациям Битрикс24: github.com/bitrix-tools/b24-ai-starter-otel

  • Мы уже подключили репозиторий телеметрии к репозиторию приложения. Телеметрия собирает метрики и показывает дашборды через Grafana. Теперь нам нужно понять, как получать из метрик полезную для бизнеса информацию: как пользователи работают с приложением, какими функциями пользуются и где сталкиваются с ошибками.

Проект приложения чат-бота основан на готовом шаблоне — AI-стартере для разработки с ИИ-агентами github.com/bitrix-tools/b24-ai-starter. И в стартер, и в проект телеметрии уже вшиты инструкции для работы с ИИ. Поэтому всю работу будем делать через агента — я использовал Codex.

Что мы уже делали с проектом ИИ-стартера

Что будет в этой статье:

  • Что нужно для повторения нашего проекта

  • Что уже есть и что сделаем сегодня

  • Что нужно для составления полезных отчётов с продуктовыми сигналами

  • Составляем план

  • Этап 1: добавляем записи о важных событиях

  • Этап 2: определяем атрибуты, которые будем фиксировать

  • Этап 3: расширяем технический мониторинг

  • Этап 4: добавляем новый дашборд

  • Этап 5: проверяем работу продуктового мониторинга

  • Что будем делать дальше

  • Содержание цикла статей про создание приложений с AI-агентами

Что нужно для повторения нашего проекта

Понадобятся работающее, подключенное к вашему порталу приложение, и подключенный репозиторий телеметрии. 

Можно скопировать готовый проект чат-бота и добавить его в ваш аккаунт. Все зависимости и алгоритм действий для регистрации в портале подробно описаны в предыдущей статье в разделе «Подготовка»:

Самый простой способ подключить телеметрию — клонировать репозиторий b24-ai-starter-otel и поместить его рядом с проектом приложения локально в одно рабочее пространство:

После этого чаще всего достаточно попросить агента подсоединить телеметрию к приложению. Если вам интересно подробнее, как это будет работать, можно почитать нашу предыдущую статью про подключение Observability.

Что уже есть и что сделаем сегодня

Сначала разберёмся, что есть в проекте сейчас, и как будем двигаться.

У нас 2 проекта, которые работают в связке.

Проект чат-бота подключен к порталу. Бот умеет выдавать котировки акций, курсы валют и список задач. Писать ему нужно в мессенджере:

Проект телеметрии подключен к боту и умеет собирать некоторые показатели. Набор дашбордов в Grafana выглядит так:

Телеметрия не связана с порталом Битрикс24 и в нашем случае работает локально на нашей машине через Docker. Другой вариант запуска — в вашем приложении при подключении внешнего сервиса телеметрии. Приложение общается с порталом и отдаёт метрики в телеметрию, а телеметрия показывает результаты в Grafana.

В статье мы добавим несколько оптимизаций, построим новый дашборд и покажем, как можно собирать информацию о работе сервисов для бизнес-анализа.

Что нужно для составления полезных отчётов с продуктовыми сигналами

В процессе мониторинга мы собираем технические метрики и продуктовые события-сигналы.

Технические метрики отвечают на вопрос о том, как работает приложение: сколько произошло ошибок, дошёл ли вебхук, сколько запросов вернули ошибку 500. По техническим метрикам мы понимаем, работает ли приложение стабильно.

Продуктовые события-сигналы показывают, дошёл ли пользователь до ценности продукта. Это больше полезно маркетингу, аналитикам, менеджерам, владельцу сервиса. Продуктовые события могут показать, что пользователь установил бота, вызвал определённую команду, начал работать с ботом регулярно. Эти вещи показывают вовлечённость.

Продуктовых сигналов может быть любое количество. Это зависит от целей владельца приложения. Например, в мониторинг можно добавить такое:

  • chatbot_joined_chat — пользователь открыл/добавил бота в чат.

  • chatbot_message_received — пользователь написал сообщение боту.

  • chatbot_command_used — бот понял команду: /stock, /fx, /market, /morning, help.

  • chatbot_activation_completed — пользователь получил первый успешный полезный ответ.

  • chatbot_repeat_usage — пользователь вернулся/написал не первый раз.

  • chatbot_regular_usage — пользователь стал пользоваться регулярно.

Полезные бизнес-гипотезы чаще строятся не по одному событию, а по связкам сигналов. Чаще это связки разных сигналов, по которым можно сделать какие-то предположения. 

Например, мы видим, что пользователь установил приложение, но не написал боту. Можно выдвинуть гипотезу: людям не понятно, где найти бота, поэтому нужен onboarding или понятная кнопка «Открыть чат с ботом».

Или такой пример: пользователь вызывает только команду курса акций и валют /stock, но не пользуется списком задач /morning. Гипотеза такая: ценность финансовых котировок понятна, а дайджест задач не очевиден.

Составляем план

Сначала мы с агентом разобрались в режиме планирования, что будем понимать под техническими метриками и продуктовыми событиями и что стоит добавить в нашего бота для демонстрационных целей.

После этого я попросил ИИ составить план и сохранить его в отдельный .md-файл, чтобы потом сверяться с ним перед каждым шагом. Спойлер — получалось в целом успешно, но не 100%, потому что иногда агент забегал вперёд и брал что-то из следующего этапа. Но это было не слишком критично, и в целом агент всё же следовал намеченной траектории развития проекта.

Вот несколько примеров событий, которые предложил добавить агент:

  • chatbot_joined_chat — пользователь открыл или добавил бота в чат.

  • chatbot_message_received — бот получил пользовательское сообщение.

  • chatbot_command_used — бот распознал команду.

  • chatbot_unknown_command — пользователь написал то, что бот не понял.

Кроме этих сигналов, по истории событий в Grafana будут считаться показатели регулярности работы пользователя с ботом. Формулы будут упрощённые, но идея реальная: видеть, как часто пользуются приложением.

Этап 1: добавляем записи о важных событиях

Сначала мы сделали так, чтобы бот начал оставлять дополнительные записи в телеметрии. Приложение уже умело фиксировать установку приложения и регистрацию бота. То есть мы видели начало пути:

app_installed -> chatbot_registered

Но дальше была слепая зона: мы не видели, открыл ли пользователь чат, написал ли боту, получил полезный ответ или столкнулся с ошибкой. Поэтому агент добавил события, которые описывают поведение пользователя после регистрации бота. 

Главный файл этапа — контроллер в боте, который находится в /backends/php/src/Controller/ChatbotEventsController.php. Он принимает вебхук от Битрикс24, определяет тип события или команду пользователя, вызывает нужную бизнес-логику и отправляет ответ в чат. После этого этапа он начал описывать каждый важный переход с помощью телеметрии.

Верхнеуровнево скрипт состоит из четырех частей:

1. process() управляет полным сценарием обработки вебхука.

2. classifyMessageForTelemetry() превращает сообщение в безопасные аналитические признаки.

3. Методы build*Reply() формируют ответы для команд help, stock, fx, market и morning.

4. sendReply() отправляет ответ в Битрикс24 и записывает результат отправки.

Вызовы телеметрии встроены в уже существующий сценарий обработки сообщения. Поэтому событие появляется только тогда, когда соответствующее действие действительно произошло. Упрощённо главная часть выглядит так:

// фиксируем технический факт: Битрикс24 доставил webhook приложению
$this->telemetry->trackEvent(
   'chatbot_webhook_received',
   $baseTelemetryAttributes,
);


// извлекаем сообщение для бизнес-логики, но не отправляем его текст в телеметрию
$message = $this->extractUserMessage($payload);


// заменяем исходный текст безопасным набором признаков
$commandAttributes = array_merge(
   $baseTelemetryAttributes,
   $this->classifyMessageForTelemetry($message),
);


// фиксируем продуктовое действие: пользователь написал боту
$this->telemetry->trackEvent(
   'chatbot_message_received',
   $commandAttributes,
);


// разделяем распознанные и неизвестные команды
$this->telemetry->trackEvent(
   'unknown' === $commandAttributes['chatbot.command']
       ? 'chatbot_unknown_command'
       : 'chatbot_command_used',
   $commandAttributes,
);

TelemetryInterface здесь является общим входом в уже подключенную систему OpenTelemetry. Контроллер сообщает ей название события и набор атрибутов, а дальнейшая отправка в OTel Collector остается задачей инфраструктурного слоя приложения.

Теперь общая схема использования выглядела так. Когда Битрикс24 присылает вебхук боту, приложение фиксирует:

chatbot_webhook_received

Это значит, что Битрикс24 действительно постучался к нашему приложению. Когда пользователь открывает или добавляет бота в чат, появляется:

chatbot_joined_chat

Когда пользователь пишет сообщение, поступает другой сигнал:

chatbot_message_received

Дальше приложение пытается понять, какую команду ввёл пользователь. Например, /help или /market AAPL MSFT USD EUR. Если команда понятна, фиксируется chatbot_command_used. Если нет, фиксируется chatbot_unknown_command.

Это  важно для продуктовой аналитики. Если записей unknown_command слишком много, то пользователи ожидают от бота чего-то другого, не того, что мы закладывали. Например, люди пишут свободным текстом, а бот рассчитан на команды. Это уже маркетинговая или продуктовая гипотеза.

Когда бот успешно отправил в Битрикс24 ответ, фиксируется chatbot_reply_sent. Если не смог отправить ответ, то chatbot_reply_failed.

А когда пользователь получил первый по-настоящему полезный ответ, например на /market, /stock, /fx или /morning, фиксируется chatbot_activation_completed. То есть мы начинаем видеть ту точку, где клиенты или сотрудники начали получать какой-то реально полезный эффект.

Ещё мы специально не сохраняем сырой текст сообщения. Это важно, потому что в телеметрию не нужно отправлять пользовательские фразы целиком. Например, пользователь пишет: /market AAPL MSFT USD EUR. Мы не сохраняем эту строку целиком как текст сообщения. Вместо этого сохраняем признаки, которые могут выглядеть так:

  • chatbot.command = market

  • chatbot.has_arguments = true

  • chatbot.message_length_bucket = 21-100

В коде это выглядит так:

return [
   // единое имя команды позволяет группировать разные варианты написания
   'chatbot.command' => $canonicalCommand,


   // тип показывает, использует человек команды или пишет свободным текстом
   'chatbot.message_type' => $messageType,


   // сохраняем факт наличия аргументов, но не сами аргументы
   'chatbot.has_arguments' => [] !== $arguments ? 'true' : 'false',


   // количество аргументов полезно для анализа сложности запросов
   'chatbot.arguments_count' => (string) count($arguments),


   // точную длину заменяем диапазоном
   'chatbot.message_length_bucket' => $this->messageLengthBucket($message),
];

Теперь можно построить нормальный отчёт. Мы будем знать, сколько людей вызвали market, сколько вызвали help, сколько писали непонятные команды и другие понятные бизнесу результаты. Так мы можем строить аналитику, но одновременно не складываем в ClickHouse лишние пользовательские данные. В результате у нас появляется связная история, которая будет выглядеть примерно так:

  • Портал установил приложение →

  • Бот зарегистрировался →

  • Пользователь открыл чат →

  • Пользователь написал help →

  • Бот успешно ответил.

Этап 2: определяем атрибуты, которые будем фиксировать

Это был как раз тот случай, когда агент уже реализовал всё запланированное в предыдущем шаге. Чтобы не бездельничать, ИИ немного оптимизировал наше приложение тестами и добавил несколько проверок:

  • Содержит ли UIProfile нужные chatbot.* атрибуты.

  • Пропускает ли simple-ui безопасные признаки бота.

  • Отфильтровывается ли сырой текст сообщения, например chatbot.message_text.

Технически это реализовано так. Агент изменил 3 файла в проекте бота:

  • /backends/php/src/Service/Telemetry/Profiles/UIProfile.php

  • /backends/php/tests/Telemetry/Profiles/UIProfileTest.php

  • /backends/php/tests/Telemetry/FilteringTest.php

UIProfile.php содержит разрешенный список атрибутов для профиля simple-ui. Это фильтр перед отправкой: если ключ не разрешен профилем, телеметрический сервис удалит его из события. Тесты отвечают за две разные проверки: UIProfileTest проверяет состав самого разрешенного списка, а FilteringTest пропускает через реальный фильтр смешанный набор данных и проверяет итог.

В профиль добавлена отдельная группа:

return [
   // другие группы атрибутов профиля


   // идентифицирует бота и исходный тип события Битрикс24
   'chatbot.code',
   'chatbot.event',


   // описывает использование без сохранения текста сообщения
   'chatbot.command',
   'chatbot.message_type',
   'chatbot.has_arguments',
   'chatbot.arguments_count',
   'chatbot.message_length_bucket',


   // описывает результат и скорость ответа
   'chatbot.reply_status',
   'chatbot.reply_duration_ms',
];

Если ChatbotEventsController добавит разрешённый ключ chatbot.command, он пройдет дальше. Если кто-то позже попробует добавить chatbot.message_text, фильтр его отбросит, потому что такого ключа в списке нет.

Дополнительно тестируем не только наличие полезных полей, но и отсутствие опасных:

$attributes = [
   // безопасный аналитический признак
   'chatbot.command' => 'market',


   // исходные пользовательские данные, которые не должны пройти
   'chatbot.message_text' => '/market AAPL MSFT USD EUR',
   'chatbot.raw_payload' => '{"MESSAGE":"/market AAPL MSFT USD EUR"}',
];


// моделируем фильтрацию перед отправкой события в OTel Collector
$filtered = $manager->filterAttributes($attributes);


// команда остается доступной для отчета
$this->assertArrayHasKey('chatbot.command', $filtered);


// сырой текст и исходный payload удаляются
$this->assertArrayNotHasKey('chatbot.message_text', $filtered);
$this->assertArrayNotHasKey('chatbot.raw_payload', $filtered);

Так второй этап закрепляет правило из первого этапа технически: полезные признаки должны пройти, а пользовательский текст — нет.

Этап 3: расширяем технический мониторинг

Здесь мы стали точнее понимать, почему бот может не работать. На первых шагах мы начали видеть пользовательские действия: пользователь написал боту, бот понял команду и отправил ответ.

Но для технического мониторинга этого мало. Пользователь мог написать /market AAPL, а ответ не пришёл или пришёл медленно. Нужно понять, где проблема: это вебхук не дошёл или бот не понял команду? Или тормозит внешний API котировок? Например, предыдущая версия бота использовала сервис stooq.com, и в какой-то момент API перестал отвечать.

Теперь каждая важная внешняя операция измеряется отдельно. Например, команда /market AAPL MSFT USD EUR вызывает несколько зависимостей:

  • Получить котировку AAPL.

  • Получить котировку MSFT.

  • Получить курс USD/EUR.

  • Отправить ответ в чат Битрикс24.

Теперь по каждой такой операции можно увидеть, какая система и какой метод вызывались, прошло это успешно или с ошибкой и сколько времени заняло. 

То есть до этого мы могли узнать только то, что бот не ответил. А теперь стало намного подробнее. Мы видим, например, что бот получил сообщение, понял команду и получил котировки, но API Битрикс24 imbot.message.add вернул ошибку.

На практике это может применяться так. В продуктовой аналитике видно, что пользователь начал работать с ботом, но не продолжил: first_message есть, а repeat_message нет. Тогда мы идём в технический мониторинг и смотрим, был ли ответ успешным, быстрым, не падал ли внешний API и не было ли ошибок Битрикс24. Далеко не факт, что причина в технике, но так у нас будет полная картина для понимания пользовательского опыта.

Код менялся в том же файле-контроллере, который агент менял на 1-м этапе: ChatbotEventsController.php. Чтобы измерять каждый внешний вызов отдельно, мы добавили новый главный метод — trackMeasuredDependencyCall(). Он используется для получения курсов валют, котировок и задач через API Битрикс24. Отправка готового ответа через imbot.message.add измеряется отдельно в sendReply().

Добавился единый измеритель внешних зависимостей:

private function trackMeasuredDependencyCall(
   string $systemName,
   string $operationType,
   string $apiMethod,
   array $telemetryAttributes,
   callable $operation,
): mixed {
   // запускаем отдельный таймер именно для этой зависимости
   $startedAt = hrtime(true);


   // эти поля объясняют, какую систему и какую операцию мы измеряли
   $attributes = array_merge($telemetryAttributes, [
       'external.system_name' => $systemName,
       'external.operation_type' => $operationType,
       'api.method' => $apiMethod,
   ]);


   try {
       // выполняем исходный вызов внутри trace-span
       $result = $this->telemetry->trackOperation(
           'dependency.' . $apiMethod,
           $operation,
           $attributes,
       );


       // записываем успешный вызов и его длительность для Grafana
       $this->telemetry->trackEvent('external_api_call', array_merge(
           $attributes,
           [
               'api.status' => 'success',
               'api.duration_ms' => (string) $this->elapsedMs($startedAt),
           ],
       ));


       return $result;
   } catch (\Throwable $throwable) {
       // при ошибке сохраняем тот же контекст и длительность
       $this->telemetry->trackEvent('external_api_call', array_merge(
           $attributes,
           [
               'api.status' => 'error',
               'api.duration_ms' => (string) $this->elapsedMs($startedAt),
           ],
       ));


       // исключение пробрасывается дальше, поэтому поведение приложения не маскируется
       throw $throwable;
   }
}

Здесь callable $operation — переданная функция с реальным API-вызовом. Благодаря этому один helper измеряет разные зависимости одинаково, а код построения ответа не дублирует обработку времени и ошибок.

Этап 4: добавляем новый дашборд

Первоначально шаг назывался «Что изменить в коде», но к этому моменту агент уже добавил все нужные изменения на предыдущих шагах. Поэтому вместо кода агент добавил новый дашборд в Grafana: Chatbot Product Funnel & Technical Health. Это пример того, как может быть составлен полезный сценарий. 

Дашборды лежат grafana/provisioning/dashboards в виде JSON-файлов: product-analytics.json, conversion-funnel.json, errors-overview.json. Grafana настроена так, что читает эту папку с помощью манифеста dashboards.yaml.

Настройка панели внутри dashboard — это SQL-запрос к ClickHouse. Например:

SELECT count()
FROM telemetry.otel_logs
WHERE LogAttributes['event.name'] = 'chatbot_message_received'

То есть Grafana здесь — визуальный слой поверх ClickHouse. Сначала всё происходящее сохраняется как строки данных. А для удобного быстрого анализа нужна визуализация.

На новом дашборде есть несколько полезных частей.

Путь пользователя. Это запись успешных последовательных действий:

установка -> 
бот зарегистрирован -> 
первое сообщение -> 
повторное сообщение -> 
регулярное использование

Обратите внимание, что мы для статьи использовали упрощённые показатели «повторное сообщение» (пользователь написал боту дважды) и «регулярное использование» (три раза и более). В реальных сценариях можно задать более сложные формулы, лучше отвечающие задачам бизнеса. Наши последние 3 шага считаются так:

first_message = есть 1+ chatbot_message_received

repeat_message = есть 2+ chatbot_message_received

regular_usage = есть 3+ chatbot_message_received

Timeline показывает события с временными метками, что может быть особенно удобно. Например, 10:01 app_installed или 10:05 chatbot_command_used / market.

Техническое здоровье. Это ответы на вопросы технических метрик: пришёл ли вебхук вообще, смог ли бот отправить ответ, падала ли обработка запроса. Техническое здоровье помогает объяснить проблемы продуктовой воронки, когда пользователи не пользуются продуктом или отдельными функциями.


Если новый дашборд показывает, как пользователь проходит путь работы с ботом, то остальные дашборды отвечают на более широкие вопросы. Например, Errors Overview показывает, что сломалось и на каком портале, а API Performance разбирает производительность вызовов API Битрикс24.

Этап 5: проверяем работу продуктового мониторинга

Агент сделал нам простой дашборд для демонстрации продуктовых событий-сигналов. Это воронка, которая может показать вещи вроде такой: пользователь написал первое сообщение, но не вернулся. Тогда мы открываем другие дашборды и проводим исследование, почему так получилось.

Для проверки работоспособности бота я переустановил приложение в портале и заново проставил секреты в .env-файле. Если у вас приложение тоже установлено, а вы хотите проверить все шаги, начиная с установки, нужно сделать так же.

Теперь открываем дашборд, начинаем проверку и параллельно смотрим результаты.  Начинаем с установки. Сохраняем приложение в портале и смотрим результат в Grafana. На первоначально чистом дашборде появляется первый сигнал:

Первый шаг пройден: app_installed = 1. Это значит, что приложение открылось в режиме установки, получило OAuth/auth payload от Битрикс24 и записало факт установки. 

Но видно, что установка не завершилась. Нам нужен следующий сигнал, app_install_finalized. Он появится, когда Битрикс24 пришлёт lifecycle-событие ONAPPINSTALL нашему приложению. Событие должно прийти на эндпойнт /api/app-events/. Тогда бэкенд обрабатывает событие и присылает в телеметрию app_install_finalized.

Чтобы узнать, почему этого сигнала не было в конце установки, запускаем терминал для проекта приложения и посылаем в терминал команду:

docker compose --env-file .env logs api-php --tail=100. 

После этого читаем полученные логи или отправляем их агенту и запрашиваем объяснение. В моём случае агент сам посмотрел логи ClicksHouse и нашёл такие ключевые ошибки:

duplicate key value violates unique constraint "unique_b24_user_domain"
Key (b24_user_id, domain_url)=(1, b24-w9apyi.bitrix24.ru) already exists.

Это значит, что приложение не смогло сохранить портал и пользователя в локальную базу данных из-за того, что там остались записи от прошлой установки. Это потому, что я уже запускал этот же проект на своём портале.

Решить это можно разными способами, но я выбрал самый простой и попросил агента сбросить данные локальной БД. Так можно делать, если записями в базе можно пожертвовать. Тогда записи обнулятся:

application_installation = 0
bitrix24account = 0

После этого повторная установка в портале прошла полностью успешно:

Чтобы найти бота, нужно открыть мессенджер и начать вводить его имя — оно задано константой в AppLifecycleController.php:

private const CHATBOT_NAME = 'Task Radar';

Вводим имя на портале:

Начнём с ознакомительной команды, которая объяснит возможности бота:

Бот успешно ответил согласно сценарию, значит, на дашборде это должно отобразиться:

Попробуем отправить следующее сообщение и запросить курсы акций и валют. Бот возвращает ответ, но пишет, что курсы акций сейчас недоступны:

Сообщение возвращено успешно, поэтому в метриках появился следующий законченный этап:

Перед последним шагом я попросил агента поменять API для получения курса акций. Теперь сначала мы отправляем запрос на Alpha Vantage и Yahoo Finance, а если оба сервиса не отвечают, в последнюю очередь пробуем Stooq, который в моём случае перестал отвечать.

Отправляем третье сообщение боту и получаем полный ответ:

Но при этом у нас сломался таймлайн в Grafana:

Если нажать на таймлайн, можно получить описание ошибки:

A Status: 500. Message: error querying the database: sendQuery: [HTTP 500] response body: "Code: 10. DB::Exception: Not found column LogAttributes in block. There are only columns: and(greaterOrEquals(Timestamp, toDateTime(1781252755)), lessOrEquals(Timestamp, toDateTime(1781256355)), in(arrayElement(LogAttributes, 'event.name'), ('app_installed', 'app_install_finalized', 'event_subscription_registered', 'chatbot_registered', 'chatbot_joined_chat', 'chatbot_message_received', 'chatbot_command_used', 'chatbot_unknown_command', 'chatbot_activation_completed', 'chatbot_reply_sent', 'chatbot_reply_failed', 'chatbot_processing_completed', 'chatbot_processing_failed')), or(equals('__all__', '__all__'), equals(arrayElement(LogAttributes, 'portal.domain'), '__all__'))), arrayElement(LogAttributes, 'portal.domain'), arrayElement(LogAttributes, 'event.name'), Timestamp: While executing MergeTreeSelect(pool: ReadPool, algorithm: Thread). (NOT_FOUND_COLUMN_IN_BLOCK) (version 23.12.6.19 (official build)) "

Суть в том, что ClickHouse споткнулся на запросе, где агент одновременно выбирал поля из LogAttributes[...], фильтровал по ним и сортировал по Timestamp. Когда агент поправил запрос, Grafana снова начала работать нормально:

Смотреть события портала можно не только в Grafana, но и в командной строке. Например, таким точечным запросом:

docker compose --env-file .env exec -T clickhouse clickhouse-client --query "
SELECT
  Timestamp,
  LogAttributes['event.name'] AS event,
  LogAttributes['chatbot.command'] AS command,
  LogAttributes['chatbot.reply_status'] AS reply_status,
  LogAttributes['portal.domain'] AS portal
FROM telemetry.otel_logs
WHERE LogAttributes['portal.domain'] = 'b24-w9apyi.bitrix24.ru'
ORDER BY Timestamp DESC
LIMIT 30
" --format=PrettyCompact

Результат будет выглядеть примерно так:

Что дальше

На проекте стартера можно собрать почти любое приложение-кастомизацию для своего портала, поэтому в следующий раз мы попробуем сделать и подключить ещё что-то интересное и полезное, или продолжим тему мониторинга, продуктовых событий и алертов.

Если у вас есть вопросы или запросы на освещение других тем — пишите в комментариях, а мы постараемся рассказать про них в следующих материалах. 

Содержание цикла статей про создание приложений с AI-агентами

  1. Пишем первое приложение с AI-стартером, чтобы видеть прибыли и убытки

  2. Добавляем в бизнес-портал Битрикс24 роботов для автоматизации

  3. Что даёт воспроизводимая среда разработки и как развернуть контейнеры на VPS

  4. Анализ и модернизация коннектора баз данных с помощью AI-агентов

  5. Создание чат-бота в портале Битрикс24 с помощью AI-агентов

  6. Как стартер-кит может стать стандартом разработки

  7. OTel Collector вкастомизации Битрикс24: подключаем Observability

  8. Технический и продуктовый мониторинг за кастомизациями Битрикс24: как настроить и на что смотреть