Консьерж, прочитавший всё, что мы когда-либо публиковали
Чат-пузырь есть на сайте каждого агентства. Чаще всего это форма в маскарадном костюме. Наш обязан знать работы, местонахождение цен, границы основателя и все двадцать одно эссе этого Журнала — поэтому мы собрали его так же, как собираем всё остальное, и записали, во что он обошёлся.
На этой странице
В углу этой страницы есть небольшая таблетка с надписью спросить студию. За ней — то, что каждый сайт консалтинга обещает с 2016 года и почти ни один не выполнил: чат, который действительно знает компанию, от лица которой говорит. Спросите, что входит в проект веб-платформы, — он перечислит настоящие результаты. Спросите, что мы думаем о сгенерированном коде, — он процитирует эссе. Спросите, где живёт Эдвард, — он вежливо откажется, потому что вместе с фактами мы преподали ему манеры.
Можно было вставить сторонний виджет над футером и пойти дальше. Но мы напечатали под собственным именем мы не просто разрабатываем — мы создаём каждую строку кода и пиксель, и арендованный чат-пузырь, транслирующий чужой брендинг из нашего угла, спорил бы с каждой страницей, над которой всплывает. Поэтому консьерж наш от начала до конца: Supabase Edge Function, модель OpenAI, клиент размером с визитную карточку и слой знаний с историей, которую мы не собирались рассказывать.
Личность без аккаунта
Первое проектное решение — кто такой посетитель. История чата должна переживать перезагрузку страницы — потерять разговор из-за того, что вы заглянули на страницу цен, это самодеятельность, — но никто не хочет заводить аккаунт, чтобы задать вопрос, а мы не хотели поднимать анонимную аутентификацию ради того, что по устройству — гостевая книга с мнениями.
Поэтому личность — это токен: виджет чеканит случайный идентификатор треда и секрет в хранилище вашего браузера, а сервер держит только хеш секрета. Предъявите пару — разговор продолжится; потеряете — просто начнёте заново. Таблицы тредов закрыты на сервисную роль: публичной политики чтения не существует вовсе, так что неправильно настраивать нечего. Это та же форма, что и у токенов доступа, которыми наши страницы со сметами пользуются с первого дня, — ровно поэтому мы ей и доверились.
Всегда 200
Ответы льются токен за токеном по server-sent events, и у транспорта одно правило, унаследованное от генератора /blueprint: ответ — всегда поток событий с HTTP 200, даже когда это дурные вести. Упёрлись в лимит? Это событие error. Токен треда недействителен? Событие error. Альтернатива — голый 4xx после того, как CORS-preflight уже прошёл успешно, — это отказ, который браузер видит, а ваш JavaScript прочитать не может: худшее из обоих миров.
Поток несёт не только текст. Когда модель заканчивает, событие done доставляет идентификатор сохранённого ответа — на нём и держатся маленькие «пальцы» под ответом. Когда посреди разговора пойман лид, событие lead сообщает об этом странице, чтобы наша аналитика его посчитала. Протокол шириной в три глагола, и таким мы намерены его держать.
Злоупотребления ограничены арифметикой, а не оптимизмом: десять запросов в минуту на адрес через тот же долговечный ограничитель, что и у контактных форм, две тысячи символов на сообщение, окно контекста в двадцать сообщений, тысяча токенов на ответ. Боту, желающему потратить наш бюджет на инференс, придётся встать в очередь за чужими curl.
Файл, который мы написали для чужих роботов
Интересной задачей было знание. Консьерж, отвечающий из общей выучки модели, — это неприятность в хорошем костюме: он выдумает услуги, которых мы не продаём, и цены, которых мы не называли. Всё, что он говорит о студии, должно исходить от студии.
А вот часть, которую мы не планировали. Несколько месяцев назад мы опубликовали /llms-full.txt — машиночитаемый дайджест всего сайта, поддерживаемый автоматически из того же контента, что рендерят страницы, — чтобы чужие AI-ассистенты описывали нас точно. Когда мы отправились искать корпус для заземления собственного консьержа, он уже лежал на месте: актуальный, полный и полгода как испытанный чужими роботами. Функция забирает его, ненадолго кеширует и вручает модели с каждым вопросом. Файл, написанный для чужих роботов, оказался мозгом нашего.
Поверх дайджеста — ещё два яруса. Короткий, написанный от руки канон несёт правила, которым нельзя дрейфовать: запрещённые слова бренда, отказ выдумывать числа и профиль основателя с явной границей — всё, что консьержу позволено говорить об Эдварде лично, умещается в шесть пунктов, и седьмой ему предписано отклонять. Не потому, что иначе модель принялась бы сплетничать, а потому, что пределы публичного бота должны быть проектным решением, записанным на бумаге, а не эмерджентным свойством.
Учим его Журналу
Третий ярус — этот Журнал. Двадцать одно эссе о том, как мы строим вещи, — ровно тот материал, которого требуют самые трудные вопросы посетителя: как вы сделали Safari быстрым, что сломалось в музее, зачем вообще публиковать цены, — но при своих примерно шестидесяти тысячах слов он не может ездить в каждом запросе.
Поэтому эссе нарезаются на пассажи и превращаются в векторы, и каждый входящий вопрос эмбеддится тем же способом и сверяется с ними. Четыре ближайших пассажа выше порога схожести едут вместе с промптом; модели велено свободно цитировать и ссылаться на источник. Спросите, как мы ускорили мобильный Safari, — и консьерж ответит настоящими числами из /articles/making-mobile-safari-fast: TTFB от 250 до 950 миллисекунд, ставший однозначным, 91 килобайт JavaScript, покинувший начальный бандл, — потому что он читает те же абзацы, что читали бы вы.
Индекс пересобирается автоматически при каждом деплое. Что порождает фразу, которую приятно набирать: к моменту, когда вы это читаете, консьерж это уже прочитал. Статьи о машине становятся частью машины — спросите его, как он устроен, и он процитирует вам эту самую страницу.
Когда нас хотят нанять
У чата на сайте студии одна коммерческая задача: заметить момент, когда разговор перестаёт быть любопытством и становится проектом. Для этого у модели один-единственный инструмент — она может завести лид с именем, почтой и брифом в одну строку, и ей предписано использовать его не более одного раза, только с данными, которые посетитель действительно напечатал, и никогда не спрашивать дважды. Лид ложится в ту же таблицу, что и у контактной формы, студии приходит уведомление, а посетитель получает одно предложение подтверждения вместо серии писем-догонялок.
Механически это наименее эффектная часть сборки и самая поучительная: посреди потока модель приостанавливает собственный ответ, вызывает инструмент, получает результат и продолжает говорить — два запроса вверх по течению, сшитые поверх одного соединения с браузером, незаметные, если не смотреть на провод. Посетитель переживает одно предложение. Эта асимметрия — и есть почти вся инженерия.
Числа, которые можно напечатать
- ~5,800
- промпт-токенов едет с каждым вопросом
- 130
- пассажей Журнала в векторном индексе
- 6
- локалей, на которых консьерж здоровается
- 10/min
- запросов на посетителя, прежде чем возразит ограничитель
Измерено в день релиза, а не оценено — ответ, породивший счётчик токенов, целиком гласил: «OK».
Каждый ответ записывает собственные токен-квитанции в базу, так что месячная стоимость консьержа — это запрос к базе, а не догадка. Мы видим и то, какие ответы зарабатывают палец вверх, а какие нет: оценки ложатся рядом с квитанциями. Функция, которую нельзя измерить, — это функция, которую нельзя защитить; эта выходит с собственной бухгалтерией.
Пределы, названные вслух
- Индекс Журнала пока только английский. Межъязыковое сопоставление срабатывает чаще, чем имеет право, но русский вопрос встречает английские пассажи; честное решение — заэмбеддить переведённые эссе, и оно в списке.
- Модель видит последние двадцать сообщений треда. Начало длинного разговора со временем выпадает из виду; скользящее резюме спроектировано, но не построено.
- Ограничение частоты — на адрес, так что офис за одним роутером делит одну квоту. Пока в неё никто не упёрся; в день, когда это случится, ключ поумнеет.
- Консьерж говорит от имени студии, но остаётся языковой моделью. Он заземлён, ограничен и измерен — и если он когда-нибудь скажет то, чего сайт не говорит, прав сайт.
Уместен ли чат на сайте студии вообще — вопрос справедливый: мы написали целое эссе об инструментах, которые вас уважают, а плохой консьерж — их противоположность. Наш ответ — держать его по той же планке, что и всё остальное здесь: он стримит, потому что заставлять ждать невежливо; отказывает, потому что границы — тоже контент; цитирует, потому что читал источники; и уходит, когда вы на странице, у которой своя мебель. Музей держит собственный чат — наш остаётся за кадром.