Hindsight должен был обработать большой архив из Cursor: рабочие чаты, планы и принятые решения. Для извлечения фактов ему нужны запросы к языковой модели.

До этого запросы уходили через внешний роутер. Работало, но добавляло отдельную оплату и ещё одну зависимость. При этом доступ к Codex у меня уже был через OAuth и подписку — отдельного ключа официального OpenAI API не было.

Поэтому задача выглядела так: дать Hindsight привычный OpenAI API, а авторизацию через Codex оставить внутри собственного контура.

Сразу обозначу границу результата. Собственная точка входа заработала, Hindsight умеет к ней подключаться, а доступ без учётных данных закрывается. Но чистого сравнения скорости и качества с внешним роутером пока нет. Переводить на неё весь импорт без такого сравнения я не стал.

Что требовалось от API#

Hindsight не нужен полный набор OpenAI API. Для текущего контура хватало трёх маршрутов:

GET  /health
GET  /v1/models
POST /v1/chat/completions

Требования были конкретными:

  • доступ по Bearer-токену;
  • ответ /v1/models в привычном формате;
  • выбор модели по умолчанию;
  • обычная и потоковая выдача ответа;
  • отсутствие передачи клиентских учётных данных дальше, в Codex;
  • запуск как отдельного сервиса и понятное поведение после перезапуска.

Последний пункт оказался не менее важен, чем сам HTTP-ответ. Рабочий слой должен переживать отказ, повторный запуск и ошибки соседних компонентов.

Схема исходного контура была простой:

Задача: Hindsight, собственный API и Codex
клиент
Hindsight
Retain и извлечение фактов
текущий маршрут
Внешний роутер
Совместимый API и отдельная оплата
новый маршрут
hermes proxy
Локальная HTTP-точка входа
доступ
Codex
OAuth и оплаченная подписка

Первый вариант: отдельный прокси#

Сначала мы собрали простой отдельный прокси. Он должен был взять доступный в Hermes Codex OAuth, спрятать его за HTTP-слоем и отдать Hindsight обычный base_url с Bearer-авторизацией.

Первый вариант: отдельный прокси
клиент
Hindsight
Отправляет запросы через base_url
отдельный процесс
Прокси
HTTP, авторизация клиента и перевод форматов
верхний сервис
Codex
OAuth и подписка

Для проверки гипотезы этого хватало. Для постоянного контура появлялись дополнительные обязанности: отдельно запускать и обновлять процесс, хранить учётные данные, ограничивать доступ, обрабатывать ошибки и следить за совместимостью с разными форматами запросов.

Если тот же маршрут можно было встроить в Hermes, отдельный процесс становился лишним звеном.

Что нашли в Hermes и вокруг него#

В документации Hermes были описаны две нужные части:

  • локальный HTTP-сервер для внешних клиентов, использующий подписку провайдера;
  • провайдер openai-codex, который получает доступ через ChatGPT OAuth или код устройства и хранит учётные данные в хранилище Hermes.

Но из этого ещё не следовало, что openai-codex можно без доработок использовать внутри hermes proxy. Это пришлось проверять по исходникам, PR, командной строке и локальным тестам.

Полезными оказались три PR в репозитории Hermes:

  • PR #54877 переиспользовал пул Codex-учётных данных и переводил Chat Completions в Responses. В ходе ревью нашли потерю tools и tool_choice, проблемы с текстовыми ответами без потоковой выдачи и расхождения между документацией и --help.
  • PR #62297 добавлял пул Codex OAuth. Отдельно проверялись логирование источника клиента, права на файл учётных данных и документация. В тестах было 49 passed.
  • PR #62510 добавлял аутентифицированный верхний сервис Codex. В нём проверялись обновление через пул, права 0600, тип файла и границы передачи заголовков.

Из этих изменений следовало несколько ограничений:

  • OAuth-токен нельзя просто передать наружу;
  • Bearer-токен клиента нельзя отправлять в Codex;
  • совместимость с Responses API и вызовами инструментов требует отдельных тестов;
  • обновление учётных данных должно проходить через их пул, а не через случайное чтение файла.

Параллельно мы посмотрели несколько самостоятельных реализаций: mehdic/codex-proxy, dvcrn/codex-oauth-proxy, Securiteru/codex-openai-proxy, EvanZhouDev/openai-oauth, icebear0828/codex-proxy и David-Factor/codex-responses-proxy.

Они подтвердили, что задача повторяется в разных проектах, но готовый прокси нельзя выбирать только по наличию HTTP-маршрута. Нужно отдельно проверять схему авторизации, потоковую выдачу, Responses API, вызовы инструментов, повторные попытки и хранение учётных данных.

В итоге мы выбрали нативный путь: провайдер openai-codex внутри hermes proxy, а внешний интерфейс для Hindsight оставили совместимым с API OpenAI. Локальная реализация опиралась на ветку native-codex-single, основанную на PR #54877 и изменённую после ревью.

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

Проверка HTTP-слоя#

Сначала проверили, что сервис закрывает доступ без учётных данных:

/health     → 401
/v1/models  → 401

С авторизацией из защищённого файла получили:

/health     → 200
/v1/models  → 200

/v1/models возвращал JSON в формате OpenAI. Моделью по умолчанию была gpt-5.5.

Затем отправили настоящий запрос:

POST /v1/chat/completions

Запрос прошёл. После этого отдельно проверили stream=true. Обычный JSON-ответ ещё не подтверждает совместимость с клиентом: агентские инструменты часто ждут потоковую выдачу и обрабатывают её по частям.

К моменту перехода на нативный путь проверили:

  • /v1/models возвращает ожидаемый формат;
  • /v1/chat/completions отвечает 200;
  • потоковая выдача работает;
  • Bearer-токен принимается на внешней точке входа;
  • клиентский заголовок авторизации не передаётся в Codex;
  • проверки Hermes завершились результатом 49 passed.

Это уже был сервис, к которому можно подключать другой компонент, а не скрипт, отвечающий только на /health.

Зачем здесь Hindsight#

Hindsight должен был разобрать экспорт из Cursor. Сырой архив для импорта не годился: в нём были рабочие разговоры, планы, служебные файлы, MCP, инструменты, навыки, изображения и кэши.

Перед импортом мы отфильтровали корпус, оставили полезные чаты и планы, добавили очистку чувствительных данных и проверили шаблоны секретов. После этого собрали пакет JSONL:

полезных документов: 4 594
фрагментов Hindsight: 7 271
размер пакета:         около 192 МБ

Импорт сделали возобновляемым. Отдельный файл состояния хранит последний индекс, поэтому после остановки процесс продолжает работу с сохранённой позиции.

Секреты не передавались через argv:

argv secret leaks = 0

Внешняя языковая модель нужна Hindsight только для retain и извлечения фактов. Векторные представления и повторная сортировка результатов работают локально:

embeddings: BAAI/bge-m3
reranker:   BAAI/bge-reranker-v2-m3

Поэтому прокси не обязан реализовывать /v1/embeddings. Для этого контура достаточно chat completions.

Почему внешний роутер пока не списан#

До эксперимента Hindsight ходил через внешний совместимый с API OpenAI роутер с моделью openai/gpt-5.6-luna.

Для retain важны не только цена и скорость. Модель должна извлекать факты, сохранять предсказуемую структуру ответа и не наполнять память лишними записями. На выбранном роутере openai/gpt-5.6-luna соответствовала этим требованиям. Опубликованный тариф составлял 101 ₽ за 1 млн входных токенов и 608 ₽ за 1 млн выходных.

Зафиксированный импорт GBrain израсходовал:

входные токены:  1 644 544
выходные токены:   460 421
стоимость:              около 446 ₽

Полный импорт из Cursor тогда не обсчитывали: в журналах сохранилось число фрагментов, но не суммарный расход токенов.

Сначала retain выполнялся с одним параллельным запросом. Затем проверили два:

HINDSIGHT_API_LLM_MAX_CONCURRENT=4
HINDSIGHT_API_RETAIN_LLM_MAX_CONCURRENT=2
HINDSIGHT_API_CONSOLIDATION_LLM_MAX_CONCURRENT=1
BATCH_SIZE=2

На участке 154 → 168 получили:

14 фрагментов / 423,7 секунды ≈ 1,98 фрагмента/мин

До этого скорость составляла примерно 1,0–1,1 фрагмента/мин. В журналах не было 429, 5xx или тайм-аутов, Hindsight оставался в состоянии healthy, а пакеты возвращали status=200.

Повышать параллельность до 3 или 4 сразу не стали. Для памяти важнее качество извлечения, стабильность схемы и отсутствие тихих потерь, чем одна удачная цифра пропускной способности.

Пробный запуск через собственную точку входа#

На основной импорт новый маршрут не включали. Для проверки создали отдельный canary-bank, чтобы не смешивать эксперимент с основной памятью и не ломать возобновляемый импорт.

Временно установили такую конфигурацию:

HINDSIGHT_API_LLM_BASE_URL=https://your-openai-compatible-endpoint.example/v1
HINDSIGHT_API_LLM_MODEL=gpt-5.5
HINDSIGHT_API_RETAIN_LLM_MAX_CONCURRENT=1

Первая ошибка оказалась не в API. PostgreSQL внутри Docker упёрся в стандартный размер /dev/shm64M:

could not resize shared memory segment
No space left on device

HTTP-слой был доступен, Hindsight до него доходил, но запись в память падала на уровне базы. Для PostgreSQL добавили:

shm_size: 1g

Пересоздали контейнеры и повторили проверку:

  • Hindsight поднялся со статусом healthy;
  • соединение с моделью прошло;
  • запись в bank вернула 200;
  • пакет retain на участке 240 → 242 вернул 200.

Функциональная проверка прошла. Но короткий замер скорости показал:

2 фрагмента / 262,2 секунды ≈ 0,46 фрагмента/мин

Это хуже предыдущего участка через внешний роутер. Сравнение нельзя считать честным: параллельно выполнялась задача consolidation, которая влияла на время.

Поэтому эксперимент остановили. Основной конфиг Hindsight вернули на стабильный роутер, а импорт Cursor оставили на паузе с сохранённым состоянием.

Что доказано, а что нет#

Доказано#

  • собственная точка входа, совместимая с API OpenAI, поверх Codex OAuth;
  • ответ 401 без учётных данных;
  • рабочие /health и /v1/models;
  • рабочий chat/completions;
  • проверенная потоковая выдача;
  • нативный openai-codex в Hermes;
  • подключение Hindsight к собственной точке входа;
  • изоляция эксперимента через canary-bank;
  • исправление ошибки Docker/PostgreSQL с общей памятью;
  • возобновляемый импорт после остановки;
  • отсутствие передачи клиентского токена в Codex.

Пока не доказано#

  • что собственная точка входа быстрее внешнего роутера на одинаковом чистом тесте;
  • что качество фактов совпадает на длинной выборке;
  • что новый маршрут нужно оставить постоянным именно для Hindsight;
  • что chat completions хватит будущим клиентам;
  • что сейчас нужны /v1/embeddings, Responses API и полный набор вызовов инструментов.

Рабочий 200 доказывает только успешный запрос. Он не доказывает скорость, качество памяти и готовность к длительной эксплуатации.

Следующий эксперимент#

Чтобы выбрать маршрут, нужен воспроизводимый тест:

  1. Продолжить импорт Cursor по сохранённым контрольным точкам.
  2. Взять одинаковые 10–20 фрагментов и одну модельную задачу.
  3. Убрать параллельный consolidation и прочие факторы, влияющие на замер.
  4. Сравнить внешний роутер и собственную точку входа по скорости, ответам 401/403/429/5xx, тайм-аутам и качеству извлечённых фактов.
  5. После этого решить, какой маршрут оставить для Hindsight.
  6. Отдельно определить необходимый набор совместимости для будущих клиентов.

До такого сравнения внешний интерфейс можно считать рабочим, но постоянный маршрут — ещё нет.

Где проходит инженерная граница#

Свой API поверх Codex — это не отдельный продукт и не красивый адрес. Это слой между клиентом и авторизацией, который приходится сопровождать.

В нём есть:

  • проверка доступа;
  • потоковая выдача;
  • выбор модели;
  • запуск через systemd;
  • тайм-ауты и ограничения скорости;
  • хранение и обновление учётных данных;
  • поведение Docker и PostgreSQL;
  • состояние длительного импорта;
  • восстановление после остановки.

Если слой должен работать постоянно, нужно заранее знать, что произойдёт при 401, 429, падении базы, остановке процесса и повторном запуске через несколько часов.

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

Если вы строите совместимый с API OpenAI слой поверх Codex OAuth, подключаете Hermes Agent или Hindsight либо разбираете собственную ИИ-инфраструктуру, напишите мне в Telegram или на почту. Помогу проверить границы совместимости, хранение учётных данных и поведение системы при сбоях.