Pi Coding Agent: свой провайдер, стоимость $0 и ошибка 500
Pi показывает $0 за каждую сессию на своём провайдере, а модели Anthropic отдают 500 при рассуждении. Оба случая лечатся конфигом. Проверено на pi 0.84.1.
В Pi уже подключены 36 провайдеров по API-ключу и шесть подписочных логинов, и всё равно велика вероятность, что вашего среди них нет. Направить его в другое место — это один JSON-файл и минут пять, и это лёгкая часть. Интересное начинается после: три вещи ломаются, и ни одна из них не сообщает о себе как о проблеме конфигурации.
Всё описанное ниже прогнано 2026-08-18 на pi 0.84.1 под macOS против шлюза, который отдаёт 131 модель через OpenAI-совместимый эндпоинт.
Файл конфигурации: ~/.pi/agent/models.json
Поля провайдера: baseUrl, api, apiKey, models[]
Протоколы: openai-completions, openai-responses,
anthropic-messages, google-generative-ai
Ключ из окружения: "$OFOX_API_KEY"
Ключ из связки: "!security find-generic-password -ws ofox"
Модель без записи: работает, с предупреждением, контекст 128K
Учёт стоимости: $0.00, пока не добавите блок cost
Рассуждение + Claude: 500, пока не добавите supportsDeveloperRole: false
Переопределение: только baseUrl, остановиться до /v1
Перечитывание: автоматически, при каждом открытии /model
Что вы получите после этой настройки, а что нет?
Вы получите каждую модель, которую продаёт ваш шлюз, внутри собственного цикла Pi и с оплатой по одному ключу. Вы не получите список моделей, подтянутый со шлюза, работающие цифры стоимости и хоть какое-то предупреждение, когда модель тихо работает на восьмой части своего реального окна контекста.
Работает сразу:
- Любой эндпоинт, который говорит на OpenAI chat completions, OpenAI Responses, Anthropic Messages или Google Generative AI.
- Переключение моделей посреди сессии через
/model, в том числе между провайдерами, потому что файл перечитывается при каждом открытии списка. - Ключи из переменной окружения или из команды оболочки, так что ничего секретного в JSON лежать не обязано.
- Уровни размышления, ввод изображений и вызов инструментов, если объявить их для каждой модели.
Сразу не работает:
- Никакого обнаружения каталога. Направленный на локальный сервер, который логирует каждый запрос, Pi отправил четыре POST на
/v1/chat/completionsза прогон и ни одного GET на/v1/models. Он знает ровно то, что вы напечатали. - Никаких цифр стоимости. Счётчики токенов точны, деньги равны нулю, пока вы сами не пропишете ставки.
- Никакого распознавания протокола. Переопределите встроенного провайдера неверным базовым путём, и вернувшаяся ошибка расскажет про авторизацию, а не про протокол.
Направлять Pi на шлюз или пользоваться встроенными провайдерами?
Если вы уже платите Anthropic, OpenAI или Google напрямую, берите встроенные. Свой провайдер нужен, когда нужной модели в этом наборе нет или когда один ключ на все инструменты важнее, чем отдельные панели у каждого вендора.
Когда свой провайдер оправдывает себя:
- Вы работаете с моделями, которых нет ни у одного провайдера первой руки. На практике это большинство китайских флагманов с открытыми весами и всё, что доступно через хостинг, а не официально.
- Вы уже пускаете Claude Code или Codex CLI через шлюз и хотите один ключ и один счёт вместо четырёх.
- Вы хотите сравнить дешёвую модель по умолчанию с дорогой моделью для эскалации, не заводя второй аккаунт ради второй модели.
Когда файл того не стоит:
- У вас один вендор и один тариф.
/loginнапрямую закрывает шесть подписок, включая ChatGPT Plus и Pro, Claude Pro и Max, GitHub Copilot, xAI и OpenRouter, и ничего из описанного здесь не понадобится. - У вас локальная среда исполнения. Ollama, vLLM и llama.cpp описаны в документации и требуют только
baseUrlи идентификатор модели, остальное из этой статьи не нужно. - Вы хотели лишь сменить ключ у встроенного провайдера. Это переопределение в одну строку, о нём ближе к концу.
Правило остановки: если pi --list-models уже показывает модель, которую вы собираетесь запускать, закрывайте вкладку. Всё здесь написано ради моделей, о которых Pi не знает.
Что нужно подготовить заранее?
Node 22 или новее, ключ и базовый URL, который вы хотя бы раз дёрнули через curl.
| Что нужно | Что использовали мы | Примечания |
|---|---|---|
| Node.js | 24.14.1 | Пакет объявляет engines: node >=22.19.0 |
| Pi | 0.84.1 (последняя 0.84.2) | @earendil-works/pi-coding-agent, MIT |
| Эндпоинт | https://api.ofox.ai/v1 | Должен отвечать на /chat/completions, а не только на /models |
| Ключ | один ключ шлюза | Хранится в $OFOX_API_KEY, никогда не в тексте конфига |
| Идентификаторы моделей | точные строки | Идентификаторы шлюза, а не вендора |
Если ещё не установлено:
npm install -g @earendil-works/pi-coding-agent
pi --version
Одну вещь стоит решить до того, как писать файл: выбранное имя провайдера попадёт в каждый флаг --provider и в каждую запись сессии. Переименуете позже — старые сессии будут ссылаться на провайдера, которого больше нет.
Как добавить своего провайдера в Pi?
Четыре поля в одном файле и одна команда, которая докажет, что всё работает.
Шаг 1: напишите блок провайдера
Всё лежит в ~/.pi/agent/models.json. Минимально жизнеспособная запись:
{
"providers": {
"ofox": {
"baseUrl": "https://api.ofox.ai/v1",
"api": "openai-completions",
"apiKey": "$OFOX_API_KEY",
"models": [
{ "id": "deepseek/deepseek-v4-flash", "contextWindow": 1000000, "maxTokens": 384000 }
]
}
}
}
Начинать стоит с openai-completions. Это самая широко реализованная форма, и на нашем шлюзе для той же модели заработал и openai-responses, но рассчитывать на это в другом месте нельзя.
Шаг 2: держите ключ в окружении, а не в файле
export OFOX_API_KEY=sk-...
apiKey разрешается тремя способами: литеральная строка, подстановка $VAR или ${VAR} и !command, которая выполняет команду оболочки и берёт её stdout. На общей машине нужен третий вариант:
"apiKey": "!security find-generic-password -ws ofox"
Шаг 3: убедитесь, что Pi видит модели
pi --list-models ofox
provider model context max-out thinking images
ofox deepseek/deepseek-v4-flash 1M 384K no no
ofox moonshotai/kimi-k3 1M 1M no no
ofox z-ai/glm-5.2 1M 128K yes no
Эти колонки берутся из вашего файла, а не со шлюза. Удалите из записи contextWindow и maxTokens, и та же команда напечатает для неё 128K и 16.4K, то есть задокументированные значения Pi по умолчанию. Если у рассуждающей модели в колонке thinking стоит no, значит не хватает вашего объявления, а не эндпоинт отказывает.
Шаг 4: запустите что-нибудь, что трогает диск
Режим print проверяет быстрее всего, потому что он задействует цикл инструментов, а не только эндпоинт дополнения:
pi --provider ofox --model deepseek/deepseek-v4-flash -p \
"Read buggy.py, run it, and state the one-line bug. Do not edit files."
Во временном каталоге с файлом из двух строк, где в функции add написано return a - b, DeepSeek V4 Flash прочитал файл, запустил интерпретатор через инструмент bash и ответил верно с первой попытки. Вот и весь интеграционный тест: чтение файла, выполнение команды, ответ.
Работает ли openai-responses?
На нашем шлюзе да, для той же модели, и единственное изменение — название протокола. Заменяем "api": "openai-completions" на "api": "openai-responses", запускаем тот же промпт и получаем тот же ответ.
Обобщать это не стоит. Поддержку Responses решает тот, кто хостит модель, и решает для каждой модели отдельно, а не для шлюза целиком, поэтому эндпоинт может отвечать на /v1/responses для одной модели и вообще не иметь такого маршрута для следующей. openai-completions покрыт шире всех, и выбирать другое без прямой необходимости смысла нет. Заставляет задуматься об этом только Codex CLI, потому что он говорит исключительно на Responses.
Почему pi auth check пишет ready с нерабочим ключом?
Потому что он проверяет наличие ключа, а не его действительность. Мы намеренно указали провайдеру неверный ключ и спросили:
pi auth check --provider ofox
# ready
Та же конфигурация, следующий запрос:
401: {"message":"Invalid or expired API key","type":"invalid_api_key","code":401}
ready означает, что Pi во что-то разрешил слот apiKey. Относитесь к этому как к проверке орфографии в имени переменной окружения, не более. Настоящая проверка готовности — это шаг 4.
Почему Pi показывает $0 за каждую сессию?
Потому что у пользовательского провайдера нет прайс-листа, а придумывать его Pi не станет. Учёт токенов при этом точен. Вот запись об использовании, которую Pi сохранил для двух ходов того первого прогона, прямо из файла сессии в ~/.pi/agent/sessions/:
| Ход | input | output | cacheRead | reasoning | total | cost |
|---|---|---|---|---|---|---|
| 1 | 2,840 | 122 | 0 | 12 | 2,962 | $0.00 |
| 2 | 52 | 56 | 2,944 | 0 | 3,052 | $0.00 |
В этой таблице стоит разделить две вещи. Значение cacheRead настоящее: на втором вызове шлюз вернул prompt_tokens_details.cached_tokens, и Pi это записал. Колонка cost не то чтобы неверна, её попросту нет. Все поля внутри cost стоят на нуле, потому что запись модели никогда не объявляла ставок.
Добавьте их, и арифметика заработает:
{
"id": "anthropic/claude-sonnet-5",
"contextWindow": 1000000,
"maxTokens": 128000,
"cost": { "input": 2, "output": 10, "cacheRead": 0.2, "cacheWrite": 2.5 }
}
Ставки указываются за миллион токенов и берутся со страницы цен того, через кого вы реально ходите, а не со страницы вендора. Следующий прогон на Claude Sonnet 5 записал 4 111 входных и 4 выходных токена и оценил их в $0.008222 плюс $0.00004, итого $0.008262, то есть ровно эти счётчики, умноженные на $2 и $10 за миллион. Эту арифметику Pi выполняет локально, поэтому цифра честна ровно настолько, насколько честны введённые вами ставки. Вводите ставки шлюза, а не производителя модели, и перепроверяйте их, когда страница меняется.
То же касается contextWindow. Sonnet 5 на этом шлюзе — модель с окном 1M, и в записи это должно быть указано, иначе тихо возьмёт верх значение по умолчанию в 128K.
Почему модель падает с «unsupported message role: developer»?
Потому что reasoning: true заставляет Pi отправлять системный промпт сообщением с ролью developer, а принимает эту роль не каждый вышестоящий сервис. Сбой громкий и выглядит как отказ сервера:
500: {"code":null,"message":"Request error: failed to convert messages:
unsupported message role: developer","param":null,"type":"api_error"}
Ничто в этой строке не указывает на вашу конфигурацию, поэтому её стоит изолировать как следует. Три прогона, один шлюз, одна модель, по одному изменённому полю за раз:
| Запись модели | Результат |
|---|---|
reasoning: true | 500, unsupported message role: developer |
reasoning: true плюс compat: { supportsDeveloperRole: false } | Работает |
reasoning: false | Работает |
Значит, спусковой крючок — роль developer, и у двух способов починки разная цена. Прогон тех же трёх конфигураций против локального сервера, который логирует тела запросов, показывает, что именно меняется:
| Запись модели | messages[].role | Отправленный reasoning_effort |
|---|---|---|
reasoning: true | ["developer", "user"] | "medium" |
плюс supportsDeveloperRole: false | ["system", "user"] | "medium" |
reasoning: false | ["system", "user"] | отсутствует |
Переключатель compat переносит системный промпт в сообщение system и оставляет reasoning_effort на месте, поэтому размышление выживает. reasoning: false тоже убирает ошибку, но за счёт полного удаления reasoning_effort из запроса, и это обычно плохой обмен.
Тот же перехват отвечает на частый вопрос про maxTokensField: на openai-completions Pi отправляет max_completion_tokens, а не max_tokens. Если ваш эндпоинт понимает только старое поле, переключать нужно именно это.
Роль отвергается лишь на части каталога. Один шлюз, один и тот же reasoning: true, три семейства моделей:
| Модель | Результат при reasoning: true |
|---|---|
| DeepSeek V4 Flash | Работает |
| GLM 5.2 | Работает |
| Claude Sonnet 5 | 500, пока не добавлен supportsDeveloperRole: false |
Закономерность определяется формой вышестоящего API, а не политикой шлюза. В API Anthropic нет роли developer, поэтому шлюзу, переводящему запросы формы OpenAI в Messages, просто не во что её отобразить. Вышестоящие сервисы формы OpenAI принимают её и идут дальше. Это тот же класс проблем, что и пустое описание инструмента у Codex CLI, которое одни сервисы валидируют, а другие игнорируют. Мы столкнулись с ним, когда прогоняли девять harness через один шлюз. Вывод повторяется: когда клиент и эндпоинт спорят, сначала читайте тело запроса, а потом меняйте настройки.
В документации Pi есть ещё два переключателя того же семейства: supportsReasoningEffort для серверов, отвергающих параметры рассуждения, и maxTokensField для серверов, которым нужен max_completion_tokens вместо max_tokens. Если модель отдаёт 400 сразу после включения размышления, пробовать надо их.
Обязательно ли перечислять каждую модель?
Нет, и на шлюзе со 131 моделью пробовать не стоит. Идентификатор, которого Pi никогда не видел, всё равно запускается:
pi --provider ofox --model z-ai/glm-5.2 -p "say ok"
# Warning: Model "z-ai/glm-5.2" not found for provider "ofox". Using custom model id.
# ok
Именно этот запасной путь отличает конфиг на пять строк от конфига на пятьсот. У него есть и скрытая цена. Незаявленная модель наследует значения Pi по умолчанию, задокументированные как 128 000 контекста и 16 384 максимума вывода, и pi --list-models печатает ровно эти две цифры для любой записи, где они опущены. Автоматическое сжатие срабатывает при contextTokens > contextWindow - reserveTokens, где reserveTokens по умолчанию равен 16 384, поэтому модель с окном 1M начнёт пересказывать себя где-то около 111 600 токенов, а не ближе к миллиону. Ничего из этого в выводе не объясняется. В сообществе проекта действительно жалуются, что сжатие приходит раньше ожидаемого; это как минимум один механизм, который даёт такой эффект, и исключить его дешевле, чем винить модель.
Практическое разделение выглядит так: пусть незаявленные идентификаторы покрывают этап разведки, а для двух-трёх моделей, которые вы гоняете ежедневно, напишите полноценные записи с contextWindow, maxTokens, reasoning, input и cost. Всё, что Pi показывает о модели, включая приём изображений, берётся из этой записи, а не с эндпоинта.
Можно ли направить встроенный провайдер anthropic на шлюз?
Можно, и для моделей Claude это лучший маршрут, если дать ему базовый путь Anthropic, а не OpenAI. Переопределение занимает одну строку и не требует собственного списка моделей:
{ "providers": { "anthropic": { "baseUrl": "https://api.ofox.ai/anthropic", "apiKey": "$OFOX_API_KEY" } } }
pi --provider anthropic --model claude-sonnet-5 -p "Reply with exactly: ok"
# ok
Pi сохраняет весь свой встроенный каталог Claude с уже правильными окнами: Claude Fable 5 на 1M, записи Opus и Haiku на 200K. Ничего не объявлять, ничего не синхронизировать, и никакой роли developer поблизости, потому что здесь Messages — родная форма. Идентификатор вендора работает как есть, без приставки шлюза.
Ошибка в базовом URL порождает две ошибки, и обе описывают не ту проблему. Направляем на путь OpenAI:
401 {"error":{"message":"You didn't provide an API key. You need to provide your API key
in an Authorization header using Bearer auth ...","type":"invalid_request_error","code":401}}
Никакого сбоя авторизации здесь нет. Pi говорит на Messages, поэтому отправляет x-api-key, а путь OpenAI принимает только Authorization: Bearer. Добавьте заголовок Bearer через поле headers у провайдера, и появится честный ответ: 404 Unsupported OpenAI API endpoint. Тот 401 был несовпадением протоколов в костюме авторизации.
Второй способ ошибиться — задвоить сегмент версии:
404 {"error":{"message":"Unsupported Anthropic API endpoint. ...","code":404}}
Это .../anthropic/v1 в конфигурации. Pi сам дописывает /v1/messages, поэтому базовый URL заканчивается на /anthropic.
baseUrl | Результат |
|---|---|
https://api.ofox.ai/anthropic | Работает, весь встроенный каталог Claude на месте |
https://api.ofox.ai/anthropic/v1 | 404, Unsupported Anthropic API endpoint |
https://api.ofox.ai/v1 | 401, который на самом деле 404 не на том протоколе |
То есть у Claude есть два маршрута по одному ключу: переопределение встроенного провайдера выше или пользовательская запись openai-completions с идентификаторами самого шлюза, как в примере со стоимостью. Переопределение короче и полностью обходит роль developer. Пользовательская запись пригодится, когда нужны свои значения cost и contextWindow, которых Pi ещё не знает.
Стоит ли вообще запускать модели Claude в Pi?
Они работают, и при этом автор Pi сам задокументировал проблему со схемой на самых новых из них. 2026-07-04 Armin Ronacher написал, что «новые модели Claude иногда вызывают инструмент edit в Pi с лишними, выдуманными полями во вложенном массиве edits[]», из-за чего «модель придумывает несуществующие ключи, и Pi отклоняет вызов инструмента и просит повторить». Неприятнее всего его вывод о тенденции: «с новыми моделями Anthropic это становится хуже: и Opus 4.8, и Sonnet 5 такое показывают, а более старые модели ни одной».
Это рассогласование обучения и инструментов, базовым URL его не починить, и каждое срабатывание стоит одной повторной попытки. Claude от этого не становится непригодным в Pi. Но это значит, что если вы выбираете модель по умолчанию для harness с собственным инструментом редактирования, то самая новая Claude не становится автоматически самым безопасным выбором, и стоит поглядывать в лог сессии на повторные вызовы инструмента для одной и той же правки.
Как посмотреть, что Pi отправляет на самом деле?
Воспроизведите вызов через curl и сравните с тем, что записал Pi. Двух файлов и одной команды хватает почти для всего, и прокси при этом не нужен.
Первая остановка — лог сессии. Каждый прогон пишет файл в формате JSON Lines в ~/.pi/agent/sessions/<project>/, по одной записи на событие, включая строку model_change с именем провайдера и идентификатором модели, которые разрешил Pi, и сообщение ассистента с блоком usage. Если идентификатор модели в этом файле не тот, который вы собирались запускать, дело во флаге или в запасном варианте, и никакая настройка провайдера этого не исправит.
Вторая — сам эндпоинт. Отправьте такой же запрос сами:
curl -s https://api.ofox.ai/v1/chat/completions \
-H "Authorization: Bearer $OFOX_API_KEY" -H 'Content-Type: application/json' \
-d '{"model":"deepseek/deepseek-v4-flash","messages":[{"role":"user","content":"hi"}],"max_tokens":8}' \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["usage"])'
В usage внимательно читать стоит два места. prompt_tokens_details.cached_tokens — это то, что Pi отображает в колонку cacheRead, поэтому если такой ключ не приходит, цифры кеша в логе сессии останутся нулевыми, каким бы стабильным ни был ваш префикс. А completion_tokens_details.reasoning_tokens показывает, действительно ли работает размышление, и это быстрее, чем читать ответ и гадать.
Сделать это до правки конфигурации — главный вывод из всех наших материалов про подключение harness. Строку ошибки пишет тот, кто её выбросил, и это далеко не всегда тот компонент, который неисправен.
Что ломается при настройке и как это чинить?
Шесть сбоев: пять воспроизведены на живом эндпоинте с 0.84.1, ещё один показан на собственной таблице моделей Pi.
| Симптом | Причина | Как чинить |
|---|---|---|
401: {"message":"Invalid or expired API key"...} | Ключ разрешился, но неверный, либо переменная пуста в этой оболочке | Прежде чем винить файл, выполните echo $OFOX_API_KEY; pi auth check такое не ловит |
404 404 page not found | В baseUrl нет сегмента версии | Используйте https://host/v1, а не https://host |
500 ... unsupported message role: developer | reasoning: true на модели, у чьего вышестоящего API нет роли developer | Добавьте compat: { supportsDeveloperRole: false } |
401 ... provide your API key ... using Bearer auth на провайдере anthropic | Переопределение указывает на путь OpenAI, поэтому Pi шлёт x-api-key туда, где принимают только Bearer | Возьмите базовый путь Anthropic у шлюза, заканчивающийся на /anthropic |
404: {"message":"Model 'openai/gpt-5.6' not found","type":"model_not_found"} | Идентификатор корректен по форме, но его нет в каталоге этого шлюза | Считайте идентификатор со страницы моделей самого шлюза: анонс модели вендором не помещает её в каждый каталог |
| Сессия сжимается намного раньше реального окна модели | Незаявленная модель откатилась к значению по умолчанию 128K | Объявите для неё contextWindow и maxTokens |
Четвёртая и пятая строки отнимают больше всего времени, потому что обе ошибки описывают не то, что происходит на самом деле.
Как командам делиться конфигурацией провайдера для Pi?
Делитесь файлом, никогда ключом. Пока каждый apiKey — это $VAR или !command, в models.json нет ни одного секрета, и его спокойно можно закоммитить в репозиторий с dotfiles или в скрипт первичной настройки.
Разделение, которое переживает контакт более чем с одним разработчиком:
- Коммитить блок провайдера: базовый URL, протокол и полные записи моделей с
contextWindow,maxTokens,reasoningиcost. Это факты об эндпоинте, одинаковые для всех, и именно ошибки в них дают тихое раннее сжатие и фальшивые счета на $0. - Никогда не коммитить ключ. В общем файле
"apiKey": "$OFOX_API_KEY", реальное значение — в профиле оболочки или связке ключей каждого разработчика. - Зафиксируйте проверенную версию. Pi выходит примерно раз в неделю: с 0.82.1 до 0.84.2 меньше чем за месяц. Записывайте, на какой версии проверена ваша конфигурация.
- Дайте всем один и тот же базовый URL. Один эндпоинт означает один каталог моделей, один пул лимитов и одно место, где видно расходы, вместо догадок у каждого разработчика.
Именно последний пункт команды и пропускают, и именно он превращает вопрос «на какой ты сейчас модели?» в обычный просмотр.
Как направить все harness на один ключ?
Каждый harness хранит доступ к моделям на своём диалекте. Claude Code читает ANTHROPIC_BASE_URL и ANTHROPIC_AUTH_TOKEN. Codex CLI требует блок model_providers в config.toml и не принимает ничего, кроме Responses API. У Cline есть панель настроек. DeepSeek Harness хочет форму своего провайдера или DEEPSEEK_BASE_URL. Pi хочет описанный выше JSON-файл. Пять инструментов, пять мест для ротации ключа, пять расходящихся списков моделей.
Все они говорят по HTTP с OpenAI-совместимым или Anthropic-совместимым эндпоинтом, поэтому решение везде одно: один базовый URL, один ключ, и меняется только строка с моделью. Ровно поэтому форма своего провайдера во всех этих инструментах состоит из одних и тех же четырёх полей.
В ofox этот эндпоинт — https://api.ofox.ai/v1 с openai-completions, и 2026-08-18 один ключ дотянулся до 131 модели, включая Kimi K3 и MiniMax M3 рядом с записями DeepSeek, GLM и Claude из примеров выше. Аналогичная настройка для остальных инструментов описана в нашем руководстве по своему провайдеру в Codex CLI, в разборе конфигурации OpenCode и в настройке Cursor, Claude Code и Cline.
Как Pi смотрится рядом с harness, которым вы уже пользуетесь?
Работа та же, площадь поверхности заметно меньше, а конфигурационный файл честен насчёт того, как мало он предполагает. Pi даёт модели четыре инструмента и API расширений, тогда как Claude Code сразу даёт хуки, субагентов, скиллы и MCP-серверы. В отрыве от задачи ни один не лучше. Вопрос в том, хотите вы собранное или собираемое.
Путь со своим провайдером показывает, где у этого минимализма есть цена. Ни загрузки каталога, ни таблицы цен, ни распознавания протокола. Каждая из трёх проблем в этой статье — это отказ Pi угадывать за вас, и каждое исправление — это вы, один раз записавший факт.
О месте Pi среди остальных, включая данные OpenRouter о том, какие модели люди реально запускают внутри каждого harness, читайте в обзоре девяти harness. Отдельно по терминальным агентам подробнее разобрано в сравнении Claude Code, Codex CLI и Cursor.
References
Часто задаваемые вопросы
- Что такое Pi coding agent?
- Терминальный coding agent от Armin Ronacher и Mario Zechner под лицензией MIT, который теперь развивается под маркой Earendil на github.com/earendil-works/pi. Модели он даёт четыре встроенных инструмента (read, write, edit, bash) и почти ничего сверх этого: вместо хуков, субагентов и скиллов здесь открытый API расширений. На 2026-08-18 у репозитория 92 619 звёзд, а npm-пакет собирает 1,37 млн загрузок в неделю.
- Каким npm-пакетом ставится Pi?
- @earendil-works/pi-coding-agent, сейчас версия 0.84.2, engines требует node >=22.19.0. Старый пакет @mariozechner/pi остановился на 0.70.6 и собирает несколько сотен загрузок в неделю: это канал до перехода под Earendil, и установка оттуда даст вам версию из прошлой эпохи проекта.
- Поддерживает ли Pi сторонние API-эндпоинты?
- Да, через ~/.pi/agent/models.json. Запись провайдера состоит из baseUrl, api, apiKey и массива models, где api принимает одно из значений openai-completions, openai-responses, anthropic-messages или google-generative-ai. Ни правок кода, ни форка не требуется, а сам файл перечитывается каждый раз при открытии выбора /model.
- Может ли Pi читать ключ из переменной окружения?
- Да. apiKey понимает подстановку $VAR и ${VAR}, а также форму !command, которая выполняет команду оболочки и берёт её stdout как ключ. Вторая форма нужна, чтобы читать секрет из системной связки ключей, а не хранить его в JSON. Для литерального знака доллара используйте $$.
- Нужно ли перечислять в models.json каждую модель?
- Нет. Если передать идентификатор, которого нет в списке, Pi напечатает Warning: Model not found for provider и запустит его как пользовательский идентификатор модели. Подвох в том, что незаявленная модель наследует значения по умолчанию, то есть 128 000 контекста и 16 384 максимума вывода, поэтому модель с окном 1M начнёт сжимать историю гораздо раньше, чем нужно.
- Почему Pi показывает $0 за каждый запрос?
- Потому что у пользовательского провайдера нет метаданных о ценах. Токены записываются в файл сессии корректно, но все поля внутри cost остаются нулевыми, пока вы не добавите в запись модели блок cost со ставками input, output, cacheRead и cacheWrite за миллион токенов.
- Можно ли направить встроенный провайдер anthropic в Pi на шлюз?
- Можно, если дать ему базовый путь Anthropic Messages, а не путь OpenAI. Переопределение baseUrl у встроенного провайдера anthropic сохраняет весь каталог Claude в Pi с правильными окнами контекста и не требует массива models. Останавливайте URL до сегмента версии, потому что Pi сам дописывает /v1/messages, а путь OpenAI вернёт 401, который на деле означает несовпадение протоколов.
- Что означает unsupported message role: developer в Pi?
- Это значит, что в записи модели стоит reasoning: true, поэтому Pi отправляет системный промпт сообщением с ролью developer, а вышестоящий сервис такую роль не принимает. Добавьте compat с supportsDeveloperRole: false, чтобы сохранить рассуждение, либо поставьте reasoning: false и потеряете его. На шлюзе с формой OpenAI это проявляется только на моделях семейства Anthropic.


