Unknown - это ошибка или результат?
Когда вы начинаете классифицировать interactions как Human, Human + AI, Agent или Unknown, bucket Unknown часто оказывается больше, чем ожидают люди. Возникает соблазн воспринимать это как пробел, который нужно скрыть, либо путем включения Unknown в Human, либо путем отношения к нему как к подозрительному. Оба варианта создают проблемы.
Unknown означает, что доступные evidence не позволяют сделать надежное определение. Request от client, который не запускает JavaScript, вызов endpoint, где вы не собираете signals, session, которая завершилась после одной страницы: все это может быть вполне законным, и ни один из этих случаев не дает вам достаточно данных, чтобы сказать, кто или что действовало.
Честно отображать Unknown - это то, что делает остальные classes заслуживающими доверия. Если ваша система никогда не говорит Unknown, каждый результат Human частично является догадкой, и вы не можете понять, какая именно его часть. В нашей статье почему Unknown является допустимым результатом при классификации AI traffic этот вопрос разбирается подробнее.
Это руководство описывает, как практически работать с Unknown: явное резервное поведение для каждого route, правила, которые не дают ему просачиваться в другие classes, как его отслеживать и как снижать его там, где это важно.
Шаг 1: Никогда не подставляйте Human по умолчанию
Самая распространенная ошибка - это функция classification, которая возвращает Human всякий раз, когда не находит signal automation. Это выглядит разумно и является неверным. Отсутствие bot signal не означает присутствие human signal.
// Wrong: silence becomes Human
function classifyWrong(e: Evidence) {
if (e.signatureValid) return { actor: "agent", basis: "verified" };
if (e.declaredAgent) return { actor: "agent", basis: "declared" };
return { actor: "human", basis: "inferred" };
}
// Right: Human requires positive evidence, otherwise Unknown
function classify(e: Evidence) {
if (e.signatureValid) return { actor: "agent", basis: "verified" };
if (e.declaredAgent) return { actor: "agent", basis: "declared" };
if (e.interactiveSession) return { actor: "human", basis: "inferred" };
return { actor: "unknown", basis: "unknown" };
}Применяйте то же правило везде, где формируются results: timeouts, errors, устаревшие cache entries и отсутствующая telemetry - все это должно приводить к Unknown. Напишите test для каждого из этих путей, чтобы поведение не могло незаметно откатиться. В примере ниже используется встроенный Node.js test runner.
import { test } from "node:test";
import assert from "node:assert/strict";
test("missing evidence is unknown, not human", () => {
const r = classify({ signatureValid: false, declaredAgent: false, interactiveSession: false });
assert.equal(r.actor, "unknown");
});Шаг 2: Для каждого route задайте явное резервное поведение
Для Unknown нужно определенное поведение, и правильный вариант зависит от route. Решайте это отдельно для каждого route, фиксируйте решение письменно и храните его в configuration. О том, как такие fallback вписываются в более широкую model policy, см. как проектировать policies уровня app для людей и AI agents.
| Тип route | Пример | Разумное резервное поведение для Unknown |
|---|---|---|
| Public content | Marketing pages, docs | Разрешить, стандартный response |
| Read-only data | Catalog, search API | Разрешить со стандартным rate limit |
| Account creation | Signup | Сохранить существующие checks, optional review queue |
| Sensitive change | Password, email, billing details | Существующая re-authentication, без ослабления |
| High-value action | Bulk export, admin operations | Существующая strong authorization |
Паттерн прост. Для routes с низким риском Unknown ведет себя как обычный traffic. Для routes с высоким риском Unknown получает ту же protection, которая уже есть у route. В обоих случаях actor result не ухудшает положение неклассифицированного, но законного user.
type Fallback = "allow" | "standard_limit" | "existing_checks" | "review";
const unknownFallback: Record<string, Fallback> = {
"GET /docs/*": "allow",
"GET /api/catalog": "standard_limit",
"POST /signup": "existing_checks",
"POST /account/password": "existing_checks",
"POST /exports": "review",
};Шаг 3: Обрабатывайте timeouts и errors одинаково
Оценка actor находится на path request, поэтому для нее должен быть жесткий временной лимит. Когда лимит превышен, возвращайте Unknown и применяйте fallback route. Никогда не блокируйте request только потому, что evaluation заняла слишком много времени.
async function withBudget<T>(work: Promise<T>, ms: number, onTimeout: T): Promise<T> {
let timer: NodeJS.Timeout;
const timeout = new Promise<T>((resolve) => {
timer = setTimeout(() => resolve(onTimeout), ms);
});
try {
return await Promise.race([work, timeout]);
} finally {
clearTimeout(timer!);
}
}
const result = await withBudget(evaluateActor(req), 150, { actor: "unknown", basis: "unknown" });Фиксируйте, почему result получил статус Unknown: нет evidence, timeout, evaluation error или устаревший evidence. Эти причины подсказывают, что именно нужно исправить.
Шаг 4: Не считайте Unknown подозрительным
Unknown - это не risk score. Многие законные client всегда будут Unknown на некоторых routes: command-line tools, server-side integrations без signatures, accessibility tools, browsers с приоритетом privacy и простые feed readers. Даже крупные AI operators сегодня в основном идентифицируют своих fetchers через user agent и опубликованные диапазоны IP. Документация OpenAI о crawlers, например, по состоянию на November 2026 перечисляет строку user agent и опубликованный файл IP address для каждого из своих bots.
Если вы хотите реагировать на подозрительное поведение, опирайтесь на поведение, которое можно описать, например на rates request, patterns failed login или reports abuse, с собственным evidence. Держите эту логику отдельно от actor class, чтобы одно не искажало другое.
Шаг 5: Как отслеживать долю Unknown?
Доля Unknown - это процент interactions, классифицированных как Unknown. Отслеживайте ее по route, по integration context и во времени. Это один из самых полезных health metrics, которые у вас есть.
type Row = { route: string; actor: string };
export function unknownRate(rows: Row[]) {
const byRoute = new Map<string, { total: number; unknown: number }>();
for (const r of rows) {
const s = byRoute.get(r.route) ?? { total: 0, unknown: 0 };
s.total += 1;
if (r.actor === "unknown") s.unknown += 1;
byRoute.set(r.route, s);
}
return [...byRoute].map(([route, s]) => ({ route, rate: s.unknown / s.total, total: s.total }));
}Что говорят цифры:
- Высокая, стабильная доля на route обычно означает, что там вам не хватает signal. Страница, которая никогда не загружает browser integration, или API, который никогда не видит signed requests, в основном останется Unknown.
- Резкий рост часто означает, что что-то изменилось в вашем stack, например release, который удалил header, rule CDN, который вырезает headers signatures, или изменение frontend, после которого перестали отправляться declarations вашего app.
- Резкое падение тоже стоит проверять. Если Unknown снижается, а Human растет, убедитесь, что пришли новые evidence, а не silently изменилась fallback behavior.
Настраивайте alerts на резкие изменения, а не на абсолютные уровни. У каждого application своя норма.
Шаг 6: Как снижать Unknown там, где это важно?
Не нужно доводить Unknown до нуля. Нужно достаточно evidence на тех routes, где принимаются решения. Идите по ним в порядке влияния:
- Сначала добавляйте evidence на маршрутах с высоким влиянием. Если изменения account в основном Unknown, именно там browser integration или server-side check даст наибольший эффект.
- Попросите automated clients идентифицировать себя. Опубликуйте documentation для вашего API, где объясняется, как agents могут подписывать requests с помощью HTTP Message Signatures и конвенций IETF Web Bot Auth, и дайте verified clients лучший experience, например более высокие limits или structured responses. Clients, которые могут пройти verification, начнут это делать.
- Используйте знания своего application. Если users взаимодействуют со встроенным assistant, фиксируйте это как declaration. Ваш app часто знает больше, чем network.
- Почините plumbing. Убедитесь, что proxies передают headers, на которые вы полагаетесь, и что evaluation имеет достаточно time budget для завершения.
Практический обзор signals, которые выводят traffic из Unknown, см. в материале как понять, что website traffic приходит от AI agents.
Шаг 7: Показывайте Unknown честно в dashboards
Отображайте Unknown как отдельную series с таким же визуальным весом, как и у остальных classes. Не прячьте его в bucket "other" и не окрашивайте его как alert. Когда stakeholders видят большую долю Unknown, правильная реакция - "где нам нужно больше evidence", а не "где боты".
Шаг 8: Анализируйте Unknown по outcomes, а не по предположениям
Раз в несколько недель берите sample Unknown interactions из ваших самых важных routes и смотрите, что реально произошло. Завершилась ли session покупкой, создала ли project, прочитала ли три article или многократно провалилась на login? Outcomes показывают, какой traffic находится в bucket, не заставляя вас преждевременно навешивать на него label.
Сгруппируйте найденное в три практические категории:
- Законный и с низким риском. Feed readers, link previews, monitoring tools и people с настройками privacy. Оставьте fallback route без изменений.
- Законная automation, которая могла бы идентифицировать себя. Integrations и agents, которые с радостью использовали бы structured path или подписывали requests, если бы вы это предложили. Опубликуйте guidance и при возможности свяжитесь с ними.
- Поведение, которому нужен собственный control. Patters credential stuffing или scraping, оба описаны в проекте OWASP Automated Threats to Web Applications. Решайте это с помощью behavior-based controls, у которых есть собственный evidence, например rates failed login, и держите их отдельно от actor class.
Этот review помогает держать ваши fallbacks связанными с реальным traffic и дает четкий список того, где добавлять evidence дальше.
Частые вопросы
Должен ли Unknown учитываться в rate limits? Используйте стандартные limits. Rate limiting по classes допустим для verified agents, которым можно дать более высокие limits, но Unknown должен получать обычный limit, а не карательный.
Можно ли challenging пользователей Unknown? Только там, где route и так challenge всех в этой ситуации. Добавлять challenge только потому, что result Unknown, значит наказывать законных users, чьи clients отправляют меньше signals.
Будет ли доля Unknown снижаться по мере развития standards? По мере того как все больше automated clients подписывают requests, больше traffic agents будет переходить из Unknown или declared в verified. Ваш monitoring покажет этот сдвиг по каждому route.
Где здесь подходит Proof of Actor
Proof of Actor сообщает Unknown как результат первого класса, с причиной, и никогда не превращает его в Human. Ваш application выбирает fallback для каждого route, а analytics показывают, где заканчивается coverage, чтобы вы могли добавить evidence там, где это важно.
Чтобы настроить это в production application вместе с нашей командой, подайте заявку на ранний доступ. Чтобы точно понять, что именно может установить evidence, прочитайте Detection & Limitations.
Источники
- RFC 9421: HTTP Message Signatures: standard, который automated clients могут использовать для подписи requests
- draft-ietf-webbotauth-httpsig-protocol: черновик IETF Web Bot Auth для signed automated traffic (work in progress по состоянию на November 2026)
- OpenAI: Overview of OpenAI Crawlers: user agent strings и опубликованные files IP address для bots OpenAI
- OWASP Automated Threats to Web Applications: taxonomy automated threats, таких как credential stuffing и scraping
- Node.js test runner: module
node:test, использованный в примере