Перейти до основного вмісту
megapromotingПоговорімо

Експертиза · Автоматичний контент

Потік, який сам шукає теми дня, пише статтю, перекладає її дев'ятнадцятьма мовами й публікує — але лише після того, як проходить через ворота перевірки, які, коли не можуть перевірити, нічого не публікують.

Двигун написаний повністю і лежить саме в репозиторії цього сайту: вісім етапів, 1.706 рядків, системний таймер, що щодня запускається о 00:30 UTC. Важлива не генерація, а ворота: п’ять локальних перевірок, десять заборонених виразів і додаткова перевірка, яка шукає імена клієнтів без згоди, вигадані цифри та нагороди, яких у нас немає. Якщо ворота не можуть запуститися, публікації не буде.

Вже збудовано«delivered», тому що робота існує як файли, які ви можете відкрити, а не як опис: `scripts/content-engine/` у репозиторії megapromoting.com, 17 файлів, 1.706 рядків TypeScript, systemd unit із таймером, вісім етапів, пов’язаних між собою, і журнали запусків. Із застереженням, яке говоримо ми, а не дізнаєтеся ви: **єдиний повний запуск у журналах не опублікував жодної статті.** Gate зупинив усі три — два, бо перевірка взагалі не змогла запуститися, третій — через відсутнє поле в заголовку. `publish` залишився порожнім, IndexNow отримав нуль адрес. Запуск був локальним, на робочій станції, і тривав дві з половиною години, тобто понад ліміт у 60 хвилин, заданий у systemd unit — отже, на сервері його було б зупинено. Ми не можемо підтвердити ззовні, що двигун сьогодні працює на сервері, і не стверджуємо цього.

Автоматизація, яка сама пише й публікує, — це місце, де найпростіше пообіцяти те, чого не можна. Модель пише тисячу переконливих слів про будь-що, зокрема про речі, які не сталися, і робить це так само плавно. Якщо між моделлю та кнопкою публікації нічого не стоїть, ви побудували машину для викладання помилок в інтернет, двадцятьма мовами, щоночі. Уся робота в тому, що стоїть між ними.

Наш потік має вісім етапів. Збирає теми з відкритих джерел, дає їм оцінку від 0 до 100 за доречністю, відкидає все, що нижче 50, обирає перші N за правилом різноманітності, щоб не вийшло десять статей про одне й те саме, пише статтю румунською, перекладає її дев'ятнадцятьма мовами, пропускає через ворота і лише потім публікує. Кожен етап записує свою вартість і результат у журнал, щоб запуск можна було відновити після.

Ворота мають два шари. Перший — локальний і нічого не коштує: заголовок має існувати й містити title, description і keywords; текст має перевищувати 1.200 слів; має бути щонайменше три підзаголовки, щонайменше три внутрішні посилання і розділ із запитаннями щонайменше з трьома парами; і він не має права містити жодного з десяти маркетингових виразів, які ми внесли до чорного списку. Другий шар — це модель, яка шукає інше: назву клієнта без доказу згоди, вигадані статистики, хибні твердження про те, що можемо ми, і нагороди, яких у нас немає — перелік реальних записано у перевірці, а все інше позначається.

У ланцюгу є людське схвалення, і ми кажемо це як аргумент, а не як виправдання. Статті, що не проходять жорсткі перевірки, відкидаються, а не публікуються. Ті, що проходять жорстко, але отримують позначки від другої перевірки, зберігаються з відмітками, щоб їх переглянув людина. А якщо перевірка не може запуститися — наприклад, тому що сервіс моделей не відповідає — стаття вважається такою, що не пройшла. Fail-closed, не fail-open. Саме так і сталося під час єдиного повного запуску в журналах, і тому нічого не було опубліковано.

Що входить

Робота, за складовими

Збирає теми з відкритих джерел, паралельно, без падіння, якщо одна мовчить

Вісім збирачів запускаються одночасно: Hacker News, Reddit, Product Hunt, GitHub trending у трьох зрізах, набір із чотирнадцяти RSS-стрічок і Indie Hackers. У кожного — власна обробка помилок, тож упале джерело повертає порожній список і не зупиняє запуск. Результати об'єднуються, дублікати прибираються за ідентифікатором, потім застосовується ліміт у вісім тем на джерело, відсортованих за швидкістю поширення — щоб шумне джерело не затопило наступний етап.

Записує і відкидає, перш ніж витрачати на написання

Кожна тема отримує оцінку від 0 до 100 за те, наскільки вона відповідає тому, що ми робимо, плюс запропонований кут, цільова галузь і цільова послуга. Усе, що нижче 50, відкидається. Етап працює на дешевій моделі, з чотирма потоками паралельно, саме тому, що це етап, який вирішує, на що далі витрачаються кошти. Із фінального вибору ще знімається один шар: перший прохід бере по одній темі на галузь, щоб не вийшло десять варіацій на один і той самий напрям.

Напишіть статтю із заданою структурою, а не вільно

Генератор отримує жорстку структуру: 1.500–2.200 слів румунською, YAML-антет із заголовком до 60 символів і описом на 155, один H1 і від чотирьох до шести H2, від трьох до п’яти внутрішніх посилань, обраних із списку адрес, що існують на сайті, виділену цитату, секцію запитань, сумісну зі schema FAQPage, і фінальний заклик. Працює з трьома потоками паралельно, із бюджетом у п’ять хвилин на статтю та лімітом у 5.000 токенів на виході.

Перекладає на дев’ятнадцять мов, із глосарієм, що не перекладається

Джерело — румунська; цілі — дев’ятнадцять, від англійської, російської та української до арабської, івриту, гінді, японської та спрощеної китайської — зокрема дві системи письма справа наліво. Правила прямо задані в промпті: антет зберігає свою структуру, адреси внутрішніх посилань не переписуються і не локалізуються, числа, одиниці та дати зберігаються, а брендові терміни з глосарію залишаються недоторканими. Переклад іде лише з двома потоками паралельно, навмисно — модельний шлюз обмежує пропускну здатність під більшим навантаженням.

Жорсткий шлюз: п’ять умов і десять заборонених виразів

Локально, без жодного платного виклику: антет має існувати й містити заголовок, опис і ключові слова; текст має перевищувати 1.200 слів, порахованих після вилучення кодових блоків і тегів; щонайменше три підзаголовки другого рівня; щонайменше три внутрішні посилання до секцій, які існують на сайті; секція запитань із принаймні трьома парами. Плюс чорний список: „game-changing”, „cutting-edge”, „world-class”, „revolutionize”, „synergy” і ще п’ять. Одна поява — і стаття не проходить.

Другий шлюз шукає саме те, чого не може побачити лічильник

Друга модель, з низькою температурою та відповіддю у JSON, отримує статтю і шукає п’ять речей: ім’я клієнта без доказу, що він дав згоду, галюциновані статистики на кшталт відсоткової доступності без вимірювання, неточні твердження про те, що можемо ми, рекламний стиль, і твердження про сторонні інструменти, які створювали б враження, ніби ми ними володіємо, хоча ми лише інтегруємо їх. Є також список нагород, які ми маємо насправді — будь-яка нагорода поза списком позначається.

Коли перевірку не можна запустити, стаття вважається такою, що не пройшла

Друга перевірка має бюджет у 90 секунд, явно для того, щоб не блокувати виконання. Якщо час спливає або сервіс моделей не відповідає, помилка ловиться і перетворюється на негативний вердикт, а не ігнорується. Наслідок важливий і саме той, який нам найбільше подобається: стаття не потрапляє в публікацію, тому що наглядача не було. Це вже траплялося, під час єдиного повного запуску в журналах — дві повністю написані статті не були опубліковані, тому що перевірку не вдалося виконати.

Публікація має одну умову і три кроки

Публікацію навіть не викликають, якщо жодна стаття не пройшла шлюз. Коли її викликають: додає лише директорію з контентом, зупиняється, якщо немає що комітити, робить коміт із датою, кількістю статей і кількістю мов, і намагається виконати push. Якщо push не вдається, коміт залишається локально, а виконання триває — робота не втрачається і не стверджується, що вона опублікована. Сповіщення пошукових систем надсилається лише якщо push вдався.

Кожен запуск залишає свій підсумок

Наприкінці пишеться щоденний звіт: скільки тем зібрано, скільки пройшло за оцінкою, скільки було обрано, скільки написано, скільки перекладів вийшло, скільки пройшло шлюз, що було закомічено і що було pushed, що відповіли пошукові системи, скільки тривало, і вартість, розбита по моделях, із кількістю викликів і токенів входу та виходу. Звіт — це файл, а не панель — його можна читати, архівувати й порівнювати.

Як це виглядає

Шлях, крок за кроком.

01

Встановлюємо, що дозволено писати, а що ні

Перед будь-яким кодом: список тем, які ми покриваємо, список тих, яких не торкаємося, тон, і чорний список виразів. Тут також пишеться список тверджень, які движок не має права робити про вас — клієнтів, цифри, нагороди, порівняння. Ми доставляємо: правила, записані в тій формі, у якій вони одразу входять у перевірку, а не як окремий документ, який загубиться.

02

Запускаємо лише збирання й оцінювання, без написання чогось

Працює в сухому режимі: збираються теми, вони записуються, обираються перші, пишеться звіт, і вихід відбувається до генерації. Це дешево — у такому запуску з наших журналів було 24 виклики, усі на малій моделі, з виміряною загальною вартістю приблизно в один цент. Видно точно, що обрав би двигун, день за днем, без жодного тексту. Ми здаємо: звіти сухого запуску та узгодження щодо того, що він обирає.

03

Ми пишемо й перевіряємо, але не публікуємо

Запускаються генерація і ворота, з вимкненою публікацією. Тут калібруються пороги: якщо всі статті провалюються за довжиною, це інше, ніж якщо вони провалюються за внутрішніми посиланнями. Тут також видно, що сигналізує друга перевірка, і це найповчальніша частина. Ми здаємо: згенеровані статті, повні вердикти з причиною кожного відхилення, і скориговані пороги.

04

Відкриваємо публікацію, з воротами на місці

Запускаються системний таймер і публікація. Час обирається поза піком, з випадковою затримкою, щоб запуски не накладалися; у нас — 00:30 UTC із затримкою до 30 хвилин, і з відновленням, якщо сервер був вимкнений у той момент. Встановлюється ліміт часу на один запуск. Ми здаємо: службову одиницю і таймер, журнали та процедуру зупинки.

05

Додаємо мови, одну за одною

Переклад — це етап із найбільшим споживанням і найлегший для виходу з-під контролю. Мови додаються по смугах пріоритету, не всі відразу, і перевіряється, що виходить у кожній — особливо в нелатинських писемностях, де правило таке: брендові терміни мають лишатися латинським алфавітом. Ми здаємо: глосарій, запущені мови та виміряну вартість на мову з журналу запуску.

Opt etape1adunare din optculegători paraleli,fiecare cu prindereproprie de eroare2notare 0–100, aruncăsub 503alegere cudiversitate peindustrie4scriere în română,1.500–2.200 decuvinte5traducere în 19 limbicu glosar neatins6poarta dură, cincicondiții și zeceexpresii interzise7poarta a doua, carecaută clienți, cifreși premii inventate8publicare, doar dacăa trecut măcar unulCând poarta nu poate rula, nu se publică.
Opt etape

Дані

Чого торкаємося, де воно лежить і скільки лишається

Запитання, які ставить кожен, у кого є відповідальний за захист даних, — поставлені тут раніше, ніж їх поставить він.

Що входить
Заголовки, адреси та короткі фрагменти з публічних джерел — відкриті API та RSS-стрічки. Не збираються персональні дані, не читаються приватні облікові записи і не заходиться після автентифікації. Кожна тема зберігає джерело, канал і момент виявлення, щоб можна було відстежити, звідки пішла стаття.
Де зберігаються тексти
Як файли MDX у репозиторії коду сайту, у `content/daily/{мова}/{дата}/{slug}.mdx`. Не в окремій базі даних. Наслідок — повна історія є історією git: видно, хто що написав, коли, і можна повернутися командою. Кожна стаття має власний заголовок, з якого публічна сторінка формує структуровані дані.
Де зберігаються ключі
У файлі середовища, який читається службою systemd під час запуску, поза репозиторієм коду. Завантажувач середовища не перезаписує змінну, вже визначену в процесі, тож конфігурацію запуску можна замінити, не чіпаючи файл. Виклики до моделей ідуть через наш внутрішній шлюз, адресований локально на сервері.
Журнали і як довго зберігаються
Один JSON-файл на день у каталозі журналів двигуна, плюс стандартний вихід і вихід помилок, записані окремо в `/var/log` через службу systemd. Щоденний журнал містить вердикти воріт по кожній статті, з точною причиною відхилення та кількістю підрахованих слів. Він не містить тексту статей — вони в репозиторії.
Що не зачіпає
Не зачіпає ваші акаунти в соціальних мережах, не надсилає електронні листи, не пише в CRM. Публікація означає коміт у репозиторії сайту та сповіщення до двох пошукових систем. Якщо ви хочете публікацію в інших каналах, це обговорюється окремо, з тим самим правилом: спочатку ворота, не потім.

Випадок

Ніч, коли двигун написав три статті й не опублікував жодної

Ситуація

Повний запуск, з усіма ввімкненими етапами: збирання з відкритих джерел, оцінювання, вибір, написання румунською, переклад, ворота, публікація. Ціль була навмисно малою, три статті, щоб було видно весь ланцюжок без великих витрат.

Що ми збудували

Збирання принесло 24 унікальні теми після видалення дублікатів і обмеження на джерело. Оцінювання залишило 18 і відкинуло 6 нижче порога 50. Правило різноманітності вибрало 3, з різних індустрій. Генератор написав 3 статті румунською. Перекладач створив 57 файлів. Потім увійшли ворота.

Що вийшло

Жодна стаття не пройшла. Дві провалилися, бо другу перевірку взагалі не вдалося виконати — сервіс моделей не відповів у бюджеті 90 секунд, а помилку було трактовано як негативний вердикт, не проігноровано. Третя провалилася на одній умові: їй бракувало поля ключових слів у заголовку, хоча вона мала 2.478 слів, тобто вдвічі більше за мінімальний поріг. Оскільки жодна стаття не пройшла, публікацію навіть не було викликано: commit залишився порожнім, push не виконано, пошукові системи отримали нуль адрес. 57 перекладів залишилися на диску, невикористаними.

Чого цей випадок не каже

Запуск був на робочій станції, не на сервері — це видно зі шляхів у журналі — і тривав дві з половиною години, понад ліміт 60 хвилин, встановлений у systemd unit. Отже, він демонструє дві речі одночасно: що ворота тримають, і що щоденний пакет надто великий для налаштованого ліміту часу. Обидва факти реальні, і про обидва варто сказати.

Питання

Про що нас питають, перш ніж зателефонувати

Чи публікується без того, щоб людина побачила текст?

Так, якщо ви так оберете — і ні, якщо ні. Двигун може працювати повністю автоматично, і в цьому режимі стаття, яка проходить обидва шари воріт, стає публічною без того, щоб хтось її прочитав. Але ворота не декоративні: стаття провалюється через відсутність поля заголовка, через 1.199 слів замість 1.200 або через один-єдиний вираз із чорного списку. А якщо ви віддаєте перевагу тому, щоб кожна стаття проходила через пару очей, автоматична публікація зупиняється, і все залишається до етапу перевірки, зі сповіщеннями на виду. Наша рекомендація на початку — другий варіант, доки сповіщення не стануть нудними.

Що відбувається, коли модель помиляється у факті?

Це залежить від того, який саме факт, і варто чесно сказати, що не все вловлюється. Вловлюється: ім’я клієнта, поставлене без підтвердженої згоди; вигадана статистика на кшталт відсотка доступності без вимірювання; твердження, яке створює враження, що ми володіємо інструментом, який лише інтегруємо; нагорода, якої немає в списку реальних. Не вловлюється: хибна календарна дата в зовнішній новині або технічне твердження про сторонній продукт, яке звучить правдоподібно. Для таких речей немає іншого охоронця, крім людини, і тому кожна стаття зберігає в заголовку джерела, з яких вона почалася — перевірка залишається можливою після публікації, а виправлення — це commit.

Як зупиняється потік?

Три способи, у порядку швидкості. Зупинка системного таймера — завтра більше нічого не запуститься. Очищення змінної, що тримає ключ публікації — двигун пише й перевіряє, але сповіщення пошукових систем пропускається з попередженням у журналі. І запуск у сухому режимі, який зупиняється після вибору тем і нічого не генерує. Крім того, окремий запуск має ліміт часу в service unit, спочатку з м’яким сигналом і примусовою зупинкою після цього.

Двигун зараз працює на сервері?

Ми не можемо підтвердити це ззовні і не будемо цього стверджувати. Те, що ми можемо показати, — це код, unit служби, таймер, процедуру встановлення і журнали запусків, які в нас є. Єдиний повний запуск у журналах був на робочій станції, не на сервері — це видно з шляхів файлів — і тривав дві з половиною години, тобто понад 60 хвилин, визначені в unit systemd. Якщо вас цікавить сьогоднішній стан нашого встановлення, запитайте, і ми перевіримо разом. Ми краще відповімо на таке запитання, ніж скажемо «так», яке виявиться хибним.

Скільки статей він уже опублікував?

Із журналів, які в нас є: нуль. Повний запуск зібрав 24 теми, залишив 18 після оцінювання, обрав 3, написав 3 статті й 57 перекладів — і поріг зупинив усі три. Дві — тому що другу перевірку не вдалося запустити взагалі, одна — через відсутнє поле в заголовку, хоча в ній було 2.478 слів, тобто вона значно перевищила поріг довжини. Публікацію не викликали, пошукові системи не отримали жодної адреси. Ми могли б цього вам не казати. Кажемо, бо це саме та демонстрація, яка потрібна сервісу: поріг справді зупиняє.

Хіба це не «контент від AI», який псує позиції в пошуку?

Правильне питання не в тому, хто написав, а в тому, чи текст відповідає на щось, що хтось справді шукає, і чи його можна перевірити. Саме тому пороги у воротах поставлені на речі, які корелюють із корисністю, а не зі стилем: мінімальна довжина, структура за підзаголовками, внутрішні посилання на сторінки, які існують, секція запитань. І саме тому чорний список містить саме ті вирази, які сигналізують про текст, написаний для заповнення простору. Чого ми вам не обіцяємо — це конкретної позиції в результатах; вона залежить від безлічі речей, які не в нас.

Скільки коштує один запуск?

Вартість вимірюється, а не оцінюється: кожен виклик до моделі окремо рахує її вхідні й вихідні токени, а щоденний звіт дає підсумок, розбитий за моделями, з кількістю викликів. На сухому запуску з наших журналів: 24 виклики на дешевій моделі. На повному запуску з трьома статтями і 57 перекладами: 32 виклики, з них 3 на дорогій моделі — саме вони дали найбільшу частину вартості. Модель, яку використовують на кожному етапі, є змінною середовища, тож можна опустити один етап на дешевшу модель, не чіпаючи код. Сума в грошах залежить від обраних моделей і їхніх тарифів на момент запуску.

На яких мовах пише і що відбувається з термінами брендів?

Джерело — румунська, цільових мов — дев’ятнадцять: англійська, російська, українська, польська, німецька, італійська, французька, іспанська, нідерландська, арабська, турецька, іврит, хінді, японська, корейська, індонезійська, спрощена китайська, бразильська португальська та чеська. Для кожної мови в конфігурації записано причину, чому вона там є. Терміни брендів і технічні терміни зберігаються в глосарії й лишаються неперекладеними, зокрема в арабській, івриті та азійських письмах, де правило прямо зазначене: зберігати латинським алфавітом. Адреси внутрішніх посилань ніколи не локалізуються.

Що не готове з того, що ви описали?

Три речі, сказані зараз, щоб ви не відкривали їх самі. Власна документація місцями випередила код — коментар на початку етапу нотування згадує модель, якої вже не викликають, а реєстр джерел описує більший набір RSS-потоків, ніж у списку. Друга: два з джерел, внесених до реєстру, позначені як активні, але їхній збирач не викликається у функції, що все підсумовує, тож сьогодні вони не дають нічого. Третя: повний запуск із журналів перевищив ліміт часу в одиниці служби, а це означає, що на сервері з поточними налаштуваннями його було б зупинено на півдорозі — ліміт треба підняти або денний пакет зменшити перед автоматичним запуском.

На чому ґрунтуються твердження вище (26 джерел)

26 з них — це код і файли з наших репозиторіїв. Ми не публікуємо їхні назви чи номери рядків: разом, на одній сторінці, це надто точно описало б, як побудовані системи, які належать не лише нам. Ми проходимо їх разом із вами, у репозиторії, за запитом — перевірка залишається можливою, просто вона відбувається в розмові.

Що вам хотілося б налагодити?

Розкажіть про свій процес. Разом визначимо, що варто збудувати, що можна підключити і як перевіримо результат.

Поговорімо