Первый слайд «было/стало» для владельца кофейни «Зерно» собрался с аккуратной таблицей и верными цифрами. Проверка кодом ответила «ОК»: каждое число в тексте нашлось в данных. А в выводах стояло «1 216 визитов в кофейню», хотя это визиты на сайт. Заголовок обещал, что «реклама работает без сбоев», хотя таблица этого не говорит. Владелец прочёл бы и поверил. В этом уроке вы соберёте слайд, на котором цифры не может испортить модель, и научитесь ловить то, что код не поймает.
Чему научитесь в этом уроке:
• брать цифры для слайда из таблицы «Цифры недели», которую пополняет отчёт 4.4;
• разделять работу: таблицу и пометку «на прежнем уровне» рисует код, модель пишет только заголовок и три вывода;
• просить у модели ответ в JSON и проверять кодом, что каждое число из текста есть в данных;
• получать слайд как HTML-страницу 1280×720 и превращать её в картинку или PDF;
• читать слайд глазами владельца перед отправкой.
Витрина отчёта
Отчёт из урока 4.4 похож на склад: всё есть, но, чтобы найти нужное, надо походить между полками. Владельцу или клиенту нужна витрина. Одна полка, на ней шесть цифр и три подписи: что это значит и что делать дальше. Витрину смотрят полминуты, поэтому любая неточность на ней заметнее, чем в длинном отчёте.
Витрину раскладывают двое. Цифры на полку ставит код: он берёт их из данных и ничего не округляет по своему вкусу. Подписи пишет модель, потому что с человеческими словами она справляется лучше кода. Так модель не может перепутать 10 654 и 10 994: таблицу она не рисует вовсе.
Как устроен сценарий
Сценарий в прогоне курса называется «Т3 прогон 4.6: слайд было/стало». Цепочка такая: запуск вручную (в уроке 4.7 добавим расписание на понедельник, 9:30) → Data Table «Последние цифры» → Code «Цифры и план» → Basic LLM Chain «Текст слайда» → Code «Слайд». Code это узел, в котором пишется короткая программа на JavaScript. Basic LLM Chain отправляет модели системное сообщение и вашу просьбу и возвращает ответ; модель та же, что во всём модуле, через CloseRouter. В прогоне за «Слайдом» стоял ещё узел записи в таблицу «Журнал аналитики», его разберём в уроке 4.7.
Откуда слайд берёт цифры
В первой версии цифры двух недель были вписаны в код руками. Для слайда по расписанию это не годится: каждый понедельник выходил бы один и тот же слайд. Поэтому слайд берёт цифры из таблицы данных «Цифры недели». Её пополняет отчёт из урока 4.4: узел «В таблицу цифр» при каждом запуске дописывает строку с двумя неделями. Если вы собирали 4.4 без этого узла, добавьте его и запустите отчёт один раз, иначе слайду нечего будет взять.
Первым после запуска поставьте узел Data Table с действием Get, имя «Последние цифры», таблица «Цифры недели». В его настройках включите Return All (отдать все строки) и Always Output Data (отдать результат, даже если таблица пустая).
Узел «Цифры и план»
Код берёт из таблицы последнюю строку, раскладывает две недели на «было» и «стало», ставит каждой строке пометку шума по тому же порогу, что в уроке 4.5 (меньше 10% или меньше 5 штук), добавляет план проверок и подпись периода. Вставьте его целиком:
// Цифры двух недель — последняя строка таблицы «Цифры недели», её пишет отчёт 4.4
const rows = $('Последние цифры').all().map(i => i.json).filter(r => r.weeks);
if (!rows.length) throw new Error('В таблице «Цифры недели» пусто: сначала запустите отчёт 4.4 со свежими выгрузками');
const last = rows.sort((a, b) => b.id - a.id)[0];
const [p, c] = JSON.parse(last.weeks);
const pick = x => ({ расход: x.расход, клики: x.клики, ctr: Number(x.ctr), конверсии: x.конверсии, цена_конверсии: x.цена_конверсии, визиты: x.визиты });
const was = pick(p), now = pick(c);
// План проверок — гипотезы, принятые в уроке 4.5; меняйте, когда принимаете новые
const hyps = ['Серия постов «Закулисье» в канале: визиты 1216 → 1338 за 7 дней', 'Оффер «сет-дегустация» в Директе: конверсии 52 → 60 в неделю за 14 дней'];
const rowsOut = Object.keys(now).map(k => { const pc = (now[k] - was[k]) / was[k] * 100; return { k, was: was[k], now: now[k], p: +pc.toFixed(1), noise: Math.abs(pc) < 10 || Math.abs(now[k] - was[k]) < 5 }; });
return [{ json: { rows: rowsOut, hyps, period: p.неделя + ' → ' + c.неделя } }];
Менять для себя нужно только список hyps: это две гипотезы, принятые в уроке 4.5. Когда примете новые, перепишите строки. Цифры трогать не нужно, они приходят из таблицы. Если таблица пустая, узел остановится с ошибкой «сначала запустите отчёт 4.4 со свежими выгрузками». Слово «Оффер» из плана модель тоже увидит; в промпте ниже его просят заменить на «предложение в рекламе».
Узел «Текст слайда»
Модель получает таблицу и план, а отвечает только JSON (текст в строгом формате «поле: значение»): заголовок и три вывода. Системное сообщение, с которым закончился прогон курса:
Ты готовишь один слайд для владельца кофейни «Зерно». Таблица цифр уже есть на слайде — НЕ пересказывай её. Напиши JSON без пояснений и без ```:
{"заголовок":"до 60 знаков, главный вывод недели","выводы":["…","…","…"]}
Правила:
- 3 вывода, каждый до 120 знаков: что эти цифры значат для владельца и что с этим делать.
- Без терминов: не «конверсии», «CTR», «оффер», а «построили маршрут», «доля кликнувших», «предложение в рекламе».
- Строку с пометкой «шум» не называй ни ростом, ни падением.
- Числа — только из таблицы и плана, ничего не досчитывай; пиши их с пробелом между тысячами и с запятой: 10 994, 3,32.
- Последний вывод — что проверяем на следующей неделе и по какой цифре поймём результат.
Сообщение пользователя собирает строки таблицы с пометкой «шум» и план проверок:
{{ "Неделя к неделе:\n" + $json.rows.map(r => r.k + ": было " + r.was + ", стало " + r.now + " (" + r.p + "%)" + (r.noise ? " — шум" : "")).join("\n") + "\n\nПлан проверок:\n" + $json.hyps.join("\n") }}
Каждая строка здесь появилась после ошибки. О ней ниже.
Узел «Слайд»
Этот узел делает три вещи. Сначала разбирает JSON модели. Потом проверяет: каждое число из заголовка и выводов должно найтись в данных, пробел между тысячами при этом снимается, так что «10 994» и 10994 считаются одним числом. И наконец рисует HTML-страницу 1280×720: слева таблица «Показатель / Было / Стало / Итог», справа три вывода. Где у строки пометка «шум», в колонке «Итог» стоит «на прежнем уровне». Подписи строк тоже задаёт код: «Расход, ₽», «Клики», «Доля кликнувших, %», «Построили маршрут», «Цена одного маршрута, ₽», «Визиты на сайт». Готовый файл slide-zerno.html узел отдаёт как вложение (binary).
Вставьте код целиком. Узел перед ним должен называться ровно «Цифры и план»: код обращается к нему по имени.
// Слайд
const d=$('Цифры и план').first().json;
let t;try{t=JSON.parse(String($input.first().json.text).replace(/^\s*```(json)?/,'').replace(/```\s*$/,''))}catch(e){return [{json:{ok:false,ошибка:'модель вернула не JSON'}}]}
// Проверка: каждое число в тексте слайда есть в данных
const known=new Set();d.rows.forEach(r=>[r.was,r.now,Math.abs(r.p)].forEach(x=>known.add(String(x).replace('.',','))&&known.add(String(x))));d.hyps.join(' ').match(/\d+/g).forEach(x=>known.add(x));[7,14].forEach(x=>known.add(String(x)));
const text=[t.заголовок,...t.выводы].join(' ');const nums=(text.replace(/(\d)[\s\u00a0](?=\d{3}\b)/g,'$1').match(/\d+(?:[.,]\d+)?/g)||[]);const unknown=nums.filter(x=>!known.has(x)&&!known.has(x.replace(',','.')));
const names={расход:'Расход, ₽',клики:'Клики',ctr:'Доля кликнувших, %',конверсии:'Построили маршрут',цена_конверсии:'Цена одного маршрута, ₽',визиты:'Визиты на сайт'};
const esc=s=>String(s).replace(/&/g,'&').replace(/</g,'<');
const fmt=v=>String(v).replace('.',',').replace(/\B(?=(\d{3})+(?!\d))/g,' ');
const tr=d.rows.map(r=>'<tr><td>'+names[r.k]+'</td><td>'+fmt(r.was)+'</td><td>'+fmt(r.now)+'</td><td class="'+(r.noise?'n':(r.p>0?'u':'d'))+'">'+(r.noise?'на прежнем уровне':(r.p>0?'+':'')+fmt(r.p)+'%')+'</td></tr>').join('');
const html='<!doctype html><meta charset="utf-8"><title>Зерно: неделя</title><style>body{font-family:Arial,sans-serif;margin:0;background:#f6f1ea;color:#2b211a}.s{width:1280px;height:720px;box-sizing:border-box;padding:56px 72px}h1{font-size:40px;margin:0 0 8px}.p{color:#7a6a5c;font-size:20px;margin-bottom:28px}.g{display:flex;gap:48px}table{border-collapse:collapse;font-size:21px;width:640px}td{padding:10px 12px;white-space:nowrap;border-bottom:1px solid #dccfc2}tr:first-child td{font-weight:bold;color:#7a6a5c}.u{color:#2e7d32}.d{color:#c62828}.n{color:#7a6a5c}ul{font-size:22px;line-height:1.45;padding-left:22px;margin:0;width:460px}li{margin-bottom:14px}</style><div class="s"><h1>'+esc(t.заголовок)+'</h1><div class="p">Кофейня «Зерно» · '+esc(d.period)+'</div><div class="g"><table><tr><td>Показатель</td><td>Было</td><td>Стало</td><td>Итог</td></tr>'+tr+'</table><ul>'+t.выводы.map(x=>'<li>'+esc(x)+'</li>').join('')+'</ul></div></div>';
return [{json:{заголовок:t.заголовок,выводы:t.выводы,проверка_чисел:unknown.length?'чисел нет в данных: '+unknown.join(', '):'ОК',html},binary:{slide:await this.helpers.prepareBinaryData(Buffer.from(html,'utf8'),'slide-zerno.html','text/html')}}];
Две строки стоит узнать в лицо. В строке с known код собирает все числа из данных и плана, в строке с unknown ищет в тексте модели числа, которых среди них нет. Результат попадает в поле проверка_чисел: «ОК» или «чисел нет в данных» со списком. На учебных цифрах в таблице выходит шесть строк, и у всех шести итог «на прежнем уровне».
Как показать слайд
Скачайте slide-zerno.html из вывода узла и откройте в браузере. Дальше два пути: сделать снимок экрана и вставить картинку в сообщение или презентацию, либо выбрать в браузере печать и сохранить страницу в PDF. В прогоне курса слайд смотрели в браузере и снимали снимком экрана, печать в PDF не пробовали.
Что показал прогон
Прогон 1. Числа были верными, но вид и текст подвели. Числа в таблице переносились посередине: «10 / 654» на двух строках. Выводы пересказывали таблицу, то есть повторяли то, что владелец и так видит слева. Модель написала «конверсии», хотя промпт просил без терминов, и «3.32» с точкой. Исправили так: в вёрстке запретили перенос внутри числа, в промпт добавили «НЕ пересказывай её» и «что это значит и что с этим делать», задали образец «10 994, 3,32». Проверку чисел научили понимать пробел между тысячами.
Прогон 2. Вёрстка чистая, проверка чисел «ОК». Вот этот слайд:

Все числа на месте, и всё же слайд нельзя отправлять. Первый вывод говорит про «1 216 визитов в кофейню», а в таблице прямо написано «Визиты на сайт». Модель видела только слово «визиты» и додумала самое понятное владельцу. Заголовок «реклама работает без сбоев» данными не подтверждён: таблица говорит лишь, что за неделю ничего заметно не изменилось. Это разные утверждения.
Запуск с цифрами из таблицы. После того как слайд научился брать цифры из «Цифр недели», его запустили ещё раз. Период на слайде пришёл из таблицы, все шесть показателей тоже, проверка чисел «ОК». А в выводах снова натяжка: «52 гостя построили маршрут… реклама работает стабильно». Число верное, а вывод о рекламе из таблицы не следует. Это ошибка того же рода, что «визиты в кофейню».
Правило: проверка кодом ловит выдуманные числа, но не подмену смысла. Перед отправкой прочитайте слайд глазами владельца: что он поймёт из каждой фразы и правда ли это по таблице.
Чтение глазами владельца
Это последний шаг перед отправкой, и он ручной. Задайте себе три вопроса:
- Тот ли это показатель? Визиты на сайт не равны гостям в зале, построенный маршрут не равен покупке.
- Следует ли вывод из таблицы? Если все строки «на прежнем уровне», слова «растёт», «падает», «работает отлично» не подходят.
- Понятно ли, что делать? Последний вывод должен называть проверку на следующую неделю и цифру, по которой судить.
Есть и вторая мера. Модели не хватило подписей, значит, их стоит дать ей прямо в промпте. Например, добавить к правилам такие строки:
- Подписи показателей: «визиты» — это визиты на сайт, не в кофейню; «конверсии» — гости, которые построили маршрут в Картах.
- Заголовок говорит только то, что видно из таблицы. Если все строки «на прежнем уровне», так и пиши: неделя без заметных изменений.
В прогоне курса не проверено. Подписи в промпт в прогоне не добавляли: и во втором прогоне, и в запуске с цифрами из таблицы промпт был тот же, что выше, и натяжка повторилась. Мы ожидаем, что с подписями таких ошибок станет меньше, но чтение глазами всё равно остаётся.
Если не получилось
- «Цифры и план» пишет, что в таблице пусто. Отчёт 4.4 ещё ни разу не записал строку в «Цифры недели». Проверьте, что в нём есть узел «В таблицу цифр», и запустите отчёт формой.
- «Слайд» падает на разборе ответа. Откройте вывод «Текста слайда». Если ответ обёрнут в
```jsonили начинается со слов, проверьте строку промпта «JSON без пояснений и без «`» и снимите обёртку в коде так же, как в узле «Проверка гипотез» урока 4.5. - Проверка чисел ругается на верное число. Скорее всего, модель написала его по-другому: с точкой вместо запятой или с пробелом, который код не снимает. Посмотрите, как число записано в выводе, и сравните с образцом «10 994, 3,32».
- Число в таблице разрывается на две строки. В вёрстке ячеек с числами нужен запрет переноса (в CSS это
white-space: nowrap). - Выводы повторяют таблицу. Проверьте, что в промпте есть «НЕ пересказывай её» и правило «что эти цифры значат для владельца и что с этим делать».
Контрольный вопрос. Проверка кодом ответила «ОК», а в выводе стоит «1 216 визитов в кофейню». Почему проверка это пропустила?
Подсказка: Вспомните, что именно сравнивает узел «Слайд» с данными.
На курсе задания проверяются автоматически. Подобрать курс
Контрольный вопрос. Что на слайде делает код, а не модель? Выберите все верные варианты.
Выберите все верные варианты.
Подсказка: Модель отвечает JSON с двумя полями. Всё остальное делают узлы Code.
На курсе задания проверяются автоматически. Подобрать курс
Контрольный вопрос. Все шесть строк таблицы помечены «на прежнем уровне». Какой заголовок слайда честный?
Подсказка: Заголовок может говорить только то, что видно в колонке «Итог».
На курсе задания проверяются автоматически. Подобрать курс
Задание. МИССИЯ «Витрина отчёта». Запустите отчёт урока 4.4 с узлом «В таблицу цифр», чтобы в таблице «Цифры недели» была строка. Соберите в n8n сценарий слайда: запуск, Data Table «Последние цифры», Code «Цифры и план», Basic LLM Chain «Текст слайда», Code «Слайд». В «Цифрах и плане» впишите свои гипотезы из урока 4.5. Запустите, откройте slide-zerno.html в браузере и прочитайте слайд глазами владельца. Если нашли подмену смысла, добавьте в промпт подписи показателей и запустите ещё раз. Приложите: (1) системное сообщение «Текста слайда»; (2) снимок экрана слайда (или PDF); (3) вывод проверки чисел; (4) скриншот последней строки таблицы «Цифры недели»; (5) две-три фразы: что вы нашли при чтении глазами владельца и что поменяли. Принято, если: цифры слайд берёт из таблицы «Цифры недели», а не из чисел, вписанных в код; период на слайде совпадает с последней строкой таблицы; таблицу и пометку «на прежнем уровне» рисует код, а модель возвращает JSON только с заголовком и тремя выводами; проверка чисел есть и её результат приложен; на слайде шесть показателей с понятными подписями; заголовок и выводы не называют шум ростом или падением и не утверждают того, чего нет в таблице; последний вывод называет проверку на следующую неделю и цифру; персональные данные клиентов не указаны.
Подсказка: Если при чтении вы не нашли ни одной подмены смысла, это тоже результат: напишите, какие три вопроса задали себе и что ответили.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
На курсе задания проверяются автоматически. Подобрать курс
Что дальше
Слайд собирается по кнопке и берёт последние посчитанные недели. В практикуме модуля поставим его и мониторинг конкурентов на расписание по понедельникам и будем записывать итог каждого запуска в таблицу «Журнал аналитики».
Итог:
• цифры слайд берёт из последней строки таблицы «Цифры недели», её пишет отчёт 4.4;
• таблицу «было/стало/итог» и пометку «на прежнем уровне» рисует код, модель пишет только заголовок и три вывода;
• код проверяет, что каждое число из текста есть в данных;
• верные числа не гарантируют верный смысл: в прогоне визиты на сайт стали «визитами в кофейню», а ровная неделя «стабильной работой рекламы»;
• перед отправкой слайд читают глазами владельца, а подписи показателей стоит дать модели в промпте.
