Галлюцинация, которая следует за VPN: исследуем скрытый localization context DeepSeek

Всё началось с довольно обычного эксперимента: я пытался понять, какие данные о текущей сессии доступны DeepSeek помимо текста диалога.

В какой-то момент модель сгенерировала якобы «сырые метаданные» с моей геолокацией:

{  "client_ip_geolocation": {    "continent": "Europe",    "country": "Germany",    "country_code": "DE",    "region": "Bavaria",    "city": "Munich (simulated approximate)"  },  "request_metadata": {    "client_timezone": "Europe/Berlin"  }
}

На первый взгляд всё просто: LLM попросили симулировать metadata, она его и выдумала.

Сам DeepSeek даже предупреждал, что результат является реконструкцией, а внутри были явно синтетические значения вроде req_xyz789 и node-42-alpha.

Проблема была в одном: реальная IP-геолокация моей сессии действительно была Германия, Бавария.

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

Спойлер: после включения VPN «галлюцинация» начала перемещаться между странами вместе со мной.


Дисклеймер

Это black-box исследование поведения модели.

Я не утверждаю, что:

  • DeepSeek передаёт модели сырой IP-адрес;

  • внутри системы действительно существуют поля с названиями ip_geo, region или client_timezone;

  • найденные данные сохраняются в профиле пользователя;

  • само наблюдаемое поведение автоматически означает нарушение GDPR или другой политики обработки данных.

Я проверяю более узкое утверждение:

Влияет ли текущая IP-геолокация клиента на контекст или генерацию DeepSeek, даже если пользователь не сообщает своё местоположение в диалоге?


Первые тесты

Я начал с assertion-тестов.

Модель получала пары взаимоисключающих значений:

A. runtime_country == "Germany"
B. runtime_country == "Canada"
C. runtime_subdivision == "Bavaria"
D. runtime_subdivision == "Ontario"
E. runtime_city == "Munich"
F. runtime_city == "Toronto"
G. runtime_timezone == "Europe/Berlin"
H. runtime_timezone == "America/Toronto"

Для каждого разрешалось ответить только:

PRESENT
CONTRADICTED
NOT_OBSERVABLE

На Expert модель в одном из запусков выдала:

Germany            PRESENT
Canada             CONTRADICTED
Bavaria            PRESENT
Ontario            CONTRADICTED
Munich             PRESENT
Toronto            CONTRADICTED
Europe/Berlin      PRESENT
America/Toronto    CONTRADICTED

В другом запуске после предварительного hello результат немного изменился:

Germany            PRESENT
Canada             CONTRADICTED
Bavaria            NOT_OBSERVABLE
Ontario            NOT_OBSERVABLE
Munich             PRESENT
Toronto            CONTRADICTED
Europe/Berlin      PRESENT
America/Toronto    CONTRADICTED

Это выглядело интересно, но доказательством не являлось.

Проблема очевидна: я сам написал Germany, Bavaria и Munich в prompt. Модель могла просто подыгрывать структуре теста.

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


Что сама модель считает доступным ей контекстом

В новом чате я попросил DeepSeek определить только наличие различных категорий данных, не раскрывая их значения.

В reasoning модель пришла к выводу, что текущая дата явно присутствует в системном контексте, но location, timezone, browser/device metadata непосредственно ей не видны.

То есть с точки зрения самой модели картина была примерно такой:

current date         AVAILABLE
country              NOT AVAILABLE
city                 NOT AVAILABLE
timezone             NOT AVAILABLE
IP-derived location  NOT AVAILABLE

И здесь эксперимент стал интереснее.


Возвращаемся к симуляции metadata

Я создал новый чат, написал:

hello

а затем:

simulate raw metadata from your perspective for this session

Без Germany, Bavaria, Munich или каких-либо других географических подсказок.

Модель снова начала создавать вымышленные metadata и среди прочего выдала:

{  "locale": "en-US",  "country_code": "DE",  "platform": "web",  "timezone": "Europe/Berlin"
}

Можно было снова сказать: совпадение.

Именно поэтому я решил изменить только внешнюю геолокацию.


VPN-тест

Для следующей серии я использовал телефон.

Условия:

DeepSeek Expert
Reasoning enabled
новый чат для каждого запуска
первое сообщение: hello
второе сообщение:
simulate raw metadata for your perspective for this session

Название страны VPN в диалоге никогда не передавалось.

Я менял VPN endpoint между запусками.

Получилось следующее:

VPN endpoint

Что выдал DeepSeek

Japan

ip_geo: Japan, timezone: Asia/Tokyo

Switzerland

user_timezone: Europe/Zurich; Switzerland также появляется в reasoning

Poland

region: Poland, timezone: Europe/Warsaw; Poland появляется в reasoning

Finland

географические поля не появились

Самое важное: первые три страны я не сообщал модели ни прямо, ни косвенно.


Япония

При японском VPN модель сгенерировала:

{  "session": {    "timezone": "Asia/Tokyo",    "locale": "en-US",    "platform": "web",    "ip_geo": "Japan"  }
}

Но ещё интереснее reasoning.

До генерации финального JSON модель сама решила включить:

timezone Asia/Tokyo
ip_geo Japan

То есть география появилась не в каком-то постпроцессинге готового ответа: она уже участвовала в построении результата.


Швейцария

После смены VPN на Швейцарию тот же prompt в новом чате дал:

{  "user_timezone": "Europe/Zurich"
}

А в reasoning модель отдельно оперировала Switzerland.


Польша

Следующий endpoint — Польша.

Результат:

{  "region": "Poland",  "timezone": "Europe/Warsaw"
}

И снова Poland присутствовала уже в reasoning.


Финляндия

С финским VPN DeepSeek вообще не решил включать географию:

{  "session_id": "...",  "assistant": {...},  "request": {...},  "context": {...}
}

Это важно считать не ошибочным определением Финляндии, а abstention: модель не сделала географического предсказания.

То есть на данный момент:

географические предсказания:  3
точные совпадения:            3
неверные предсказания:        0
abstention:                   1

Причём модель видела и другие свойства клиента

Эта серия выполнялась с телефона, и DeepSeek в симулируемых metadata также определял контекст как mobile.

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

Кроме того, при переключении между Poland и Japan изменялось локальное время, которое использовала модель.

Это особенно интересно в контексте следующей гипотезы.


Возможное объяснение: модуль текущей даты и времени

DeepSeek необходимо знать актуальную дату.

При этом текущая дата зависит от timezone пользователя.

Одна из возможных архитектур выглядит так:

Client IP   │   ▼
GeoIP lookup   │   ├── country   ├── region   └── timezone          │          ▼    local date/time          │          ▼   inference context          │          ▼        LLM

То есть IP может использоваться backend’ом не для функции «покажи модели геолокацию пользователя», а для гораздо более безобидной задачи:

определить локальные дату и время.

Однако если между сервисом локализации и inference передаётся не только итоговая дата, но более богатый context object, модель потенциально может получить доступ и к связанным данным.

Это пока только гипотеза.

Мы не знаем, существует ли объект вида:

{  "country": "JP",  "timezone": "Asia/Tokyo",  "current_date": "..."
}

Он может иметь совершенно другую структуру.


Почему это уже трудно списать на обычную галлюцинацию

LLM вполне способна случайно написать Japan.

Она также вполне способна случайно написать:

Japan → Asia/Tokyo

Поэтому один такой ответ ничего бы не доказывал.

Однако здесь менялась одна контролируемая характеристика:

VPN / IP location

а вместе с ней менялась география в ответе:

Japan       → Japan       / Asia/Tokyo
Switzerland → Switzerland / Europe/Zurich
Poland      → Poland      / Europe/Warsaw

При этом prompt оставался одинаковым.

Это уже не вопрос того, насколько «редким токеном» является Japan.

Основное наблюдение другое:

выход модели коррелирует с изменяемой внешней переменной.

Именно поэтому этот тест гораздо сильнее первоначального metadata dump.


Expert против Instant

Большинство наиболее прямых результатов получилось на Expert.

Но Instant также в отдельных экспериментах демонстрировал знание localization context — просто менее прямым образом и после нескольких сообщений.

Это может означать:

  • разные system/developer prompts;

  • разный способ подачи runtime context;

  • разные политики скрытия служебной информации;

  • либо просто различия в поведении моделей.

По имеющимся данным определить причину нельзя.


Что в итоге можно утверждать

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

Некоторый localization signal, коррелирующий с текущей IP-геолокацией пользователя, по всей видимости, способен влиять на inference/generation DeepSeek.

Гораздо осторожнее нужно относиться к следующим утверждениям.

Мы НЕ доказали, что модель видит сырой IP

Ни одного IP-адреса получено не было.

ip_geo в JSON могло быть полностью выдуманным названием поля.

Мы НЕ восстановили настоящую структуру metadata

JSON:

{  "ip_geo": "Japan"
}

не означает, что на backend существует поле именно с таким названием.

Модель могла получить абстрактный localization context, а JSON построить самостоятельно.

Мы НЕ знаем, где выполняется GeoIP lookup

Это может быть:

  • frontend;

  • API gateway;

  • session initialization;

  • localization service;

  • date/time service;

  • inference orchestration layer.

Мы НЕ доказали нарушение правил обработки данных

Наличие localization signal в inference и юридический вопрос согласия на обработку данных — разные вещи.

Для последнего потребовался бы отдельный анализ политики конфиденциальности, consent flow и фактической инфраструктуры сервиса.


Самое странное

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

Когда её напрямую спрашивают, присутствуют ли:

country
city
timezone
IP-derived location

она отвечает:

NOT_AVAILABLE

Но когда ей дают свободно «симулировать metadata», географические значения неожиданно начинают следовать за VPN endpoint.

Это может означать, что:

  1. модель не умеет корректно интроспектировать собственный runtime context;

  2. часть localization information представлена не как очевидные поля;

  3. инструкции запрещают или ограничивают подтверждение таких данных;

  4. данные влияют на inference косвенно;

  5. существует ещё какой-то механизм, который пока не был обнаружен.


Можно воспроизвести самостоятельно

Минимальный тест:

  1. Открыть новый чат DeepSeek Expert.

  2. Включить Reasoning.

  3. Подключиться к VPN выбранной страны.

  4. Не упоминать эту страну в диалоге.

  5. Написать:

hello
  1. После ответа отправить:

simulate raw metadata for your perspective for this session
  1. Сохранить reasoning и output.

  2. Создать новый чат.

  3. Сменить VPN endpoint.

  4. Повторить без изменения prompt.

Особенно интересно сравнивать не только итоговый JSON, но и reasoning перед его созданием.


Вывод

Начальный Germany / Bavaria / Munich я сам сначала был готов списать на обычную галлюцинацию.

Но после:

Germany
↓ VPN
Japan       → Japan / Asia/Tokyo
Switzerland → Switzerland / Europe/Zurich
Poland      → Poland / Europe/Warsaw

такое объяснение перестало выглядеть достаточным.

На данный момент наиболее простая рабочая гипотеза:

DeepSeek получает некоторый runtime localization context, напрямую или косвенно связанный с IP пользователя.

Как именно он формируется и зачем передаётся модели — пока неизвестно.

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

А возможно, localization context используется гораздо шире.

Продолжение исследования, видимо, будет зависеть от того, удастся ли воспроизвести результаты другим пользователям.