Как запустить приложение с Claude API на VPS
Узнайте, как настроить Python на Ubuntu 24.04 для анализа логов через Claude API. Реализуйте streaming, обработку ошибок и контроль затрат на токены.
Что вы создаете
Консольный инструмент на чистом Ubuntu 24.04 VPS. Вы передаете ему сообщение об ошибке или фрагмент лога, а он возвращает диагностику на обычном английском языке: journalctl -u nginx -n 50 | explain. Программа состоит примерно из шестидесяти строк кода на Python. Она охватывает все необходимые компоненты для работы с Claude API: правильное хранение ключа, использование virtualenv, обработку структур ответа SDK, потоковую передачу (streaming), типизированные цепочки исключений и unit-файл systemd для фонового выполнения.
Я выбрал этот проект намеренно. Большинство руководств по созданию «первого приложения с API» предлагают написать чат-бота, который вы больше никогда не откроете. Анализатор логов полезен на сервере с первого дня. Он заставляет вас проработать две вещи, в которых новички чаще всего допускают ошибки: корректное чтение объекта ответа и контроль расходов. API тарифицируется за каждый токен, и лимиты ограничены только теми, которые вы установите сами. Контроль затрат здесь является важным этапом проектирования, а не второстепенной задачей — этот же принцип важен, когда вы перейдете к запуску Claude Code на этом же VPS в tmux.
Получите API key в Console
Доступ к API управляется в Anthropic Console по адресу platform.claude.com. Сначала зарегистрируйтесь, затем создайте ключ в разделе Settings → API Keys (ссылка в документации ведет сразу на platform.claude.com/settings/keys). Ключ отображается только один раз, начинается с sk-ant-, и его нельзя получить повторно. Скопируйте его немедленно или удалите и выпустите заново.
Оплата: по состоянию на июль 2026 года бесплатный уровень использования API отсутствует. Согласно документации Anthropic, новым пользователям предоставляется небольшое количество бесплатных кредитов для тестирования. Точная сумма зависит от того, что указано в Console при регистрации. После исчерпания кредитов необходимо пополнить баланс аккаунта, чтобы запросы выполнялись успешно. Это не связано с подпиской на claude.ai: планы Pro или Max не включают кредиты для API, а наличие API key не дает доступа к чат-приложению. Если вы выбираете между подпиской и API, изучите этот вопрос отдельно: какой план Claude вам действительно нужен.
Создавайте ключ с ограничениями (scope) для одного проекта или сервера. Если ключ будет скомпрометирован — а при длительном использовании это неизбежно — вы сможете отозвать его, не нарушая работу остальных ваших сервисов.
Не храните ключ в .bashrc
Использование export ANTHROPIC_API_KEY=sk-ant-... в ~/.bashrc является плохой практикой. Это создает три отдельные проблемы:
- Переменную наследует каждый процесс. Переменная окружения, экспортированная в вашей оболочке при входе, передается всему, что вы запускаете: веб-приложению, отчету об ошибках, который включает окружение в отчет, или открытой странице
phpinfo(). Поверхность атаки расширяется до «всего, что запускает данный пользователь». - Ввод ключа сохраняется в
~/.bash_history. Если вы один раз введете команду export вручную, ключ останется в текстовом файле навсегда и попадет во все резервные копии вашей домашней директории. - Ключ недоступен для systemd. Сервисы не читают ваш
.bashrc. Этот метод перестает работать, когда вы превращаете скрипт в unit — обычно это проявляется как ошибка 401 в 6 утра.
Правильный подход на сервере — использование отдельного файла окружения с правами 600, который загружается только необходимым процессом:
sudo mkdir -p /opt/explain
sudo install -m 600 -o root -g root /dev/null /etc/claude-explain.env
printf 'ANTHROPIC_API_KEY=sk-ant-YOUR-KEY-HERE\n' | sudo tee /etc/claude-explain.env >/dev/nullЧтобы ключ не попал в swap-файлы редактора, используйте tee через printf вместо ручного ввода. В любом случае проверьте с помощью ls -l /etc/claude-explain.env, что файл читается как -rw------- и принадлежит root. Интерактивные оболочки получают ключ при каждом запуске через wrapper (см. ниже), а systemd получает его через EnvironmentFile= — root считывает файл перед снижением привилегий, поэтому пользователю сервиса не требуется доступ на чтение. Ключ никогда не появится в коде, в git, в выводе ps или в истории команд shell.
Установка SDK в venv
В Ubuntu 24.04 предустановлен Python 3.12 с включенным механизмом PEP 668. Прямая установка pip install anthropic в системный интерпретатор приведет к ошибке error: externally-managed-environment. Это ожидаемое поведение ОС — используйте virtualenv:
sudo apt update && sudo apt install -y python3-venv
sudo python3 -m venv /opt/explain/venv
sudo /opt/explain/venv/bin/pip install anthropicНа сервере не требуется активация окружения: прямой вызов /opt/explain/venv/bin/python всегда использует пакеты из venv.
Первый вызов и корректное чтение ответа
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
messages=[{"role": "user", "content": "Explain what a systemd unit file is in three sentences."}],
)
for block in response.content:
if block.type == "text":
print(block.text)Две вещи в этих двенадцати строках определяют логику работы API. Во-первых, вызов anthropic.Anthropic() без аргументов считывает ключ из переменных окружения — никогда не передавайте его в виде строкового литерала. Во-вторых, response.content является списком блоков контента, а не строкой. При прямой печати вы получите типичный для новичков результат:
[TextBlock(citations=None, text='A systemd unit file is...', type='text')]Это не ошибка, а объектное представление (repr). Ответы могут содержать несколько типов блоков (текст, вызовы инструментов, процесс размышления), поэтому необходимо итерироваться по списку и проверять block.type == "text" перед обращением к .text. Реализуйте этот цикл сразу; это избавит вас от целого класса ошибок типа «выводится мусор».
Используйте точный ID модели claude-opus-4-8. ID текущего поколения не содержат даты — не следуйте привычке (или старым статьям), предлагающей добавлять суффикс с датой; это приведет к ошибке 404, которая описана ниже.
Описание инструмента
Ниже представлен полный код программы: чтение из stdin, потоковый вывод диагностики и обработка ошибок:
#!/usr/bin/env python3
"""explain: pipe an error or log excerpt in, get a diagnosis out."""
import sys
import anthropic
MODEL = "claude-opus-4-8"
def main() -> int:
text = sys.stdin.read().strip()
if not text:
print("usage: journalctl -u nginx -n 50 | explain", file=sys.stderr)
return 1
client = anthropic.Anthropic()
try:
with client.messages.stream(
model=MODEL,
max_tokens=1500,
system=(
"You are a senior Linux sysadmin. The user pipes you server "
"logs or error output. Name the most likely cause outright, "
"then give the commands to confirm and fix it. Be terse."
),
messages=[{"role": "user", "content": text}],
) as stream:
for chunk in stream.text_stream:
print(chunk, end="", flush=True)
print()
except anthropic.RateLimitError as e:
retry_after = e.response.headers.get("retry-after", "60")
print(f"rate limited; retry in {retry_after}s", file=sys.stderr)
return 2
except anthropic.APIStatusError as e:
print(f"API error {e.status_code}: {e.message}", file=sys.stderr)
return 2
except anthropic.APIConnectionError:
print("network error reaching the API", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
sys.exit(main())Сохраните этот код в файл /opt/explain/explain.py, затем добавьте обертку для загрузки ключа при интерактивном использовании:
sudo tee /usr/local/bin/explain >/dev/null <<'EOF'
#!/bin/sh
set -a; . /etc/claude-explain.env; set +a
exec /opt/explain/venv/bin/python /opt/explain/explain.py "$@"
EOF
sudo chmod 755 /usr/local/bin/explain(Обертку необходимо запускать через sudo, либо файл окружения должен принадлежать группе, в которую входит ваш пользователь — выберите один из этих вариантов вместо изменения прав доступа файла на 644.)
Преимущества потоковой передачи. client.messages.stream выводит токены по мере их поступления, а не ждет завершения всей генерации. Это позволяет избежать таймаутов HTTP при длительном выводе — именно поэтому SDK отклоняет слишком большие значения max_tokens при не-потоковых вызовах. Если вам требуется получить собранный объект, вызовите stream.get_final_message() внутри блока with.
Порядок обработки исключений. SDK генерирует типизированные исключения, начиная с наиболее специфичных: RateLimitError соответствует ошибке 429 и содержит заголовок retry-after с указанием времени ожидания; APIStatusError обрабатывает другие ответы, отличные от 2xx (проверьте e.status_code >= 500 на наличие проблем на стороне сервера); APIConnectionError означает отсутствие ответа на запрос. Прежде чем создавать цикл повторных попыток, учтите: SDK уже самостоятельно выполняет повторные попытки при ошибках 429 и 5xx — по умолчанию дважды с экспоненциальной задержкой (max_retries на стороне клиента). К моменту выполнения вашего except все попытки будут исчерпаны, поэтому в CLI правильным решением будет вывод ошибки и завершение работы, а не ожидание и повторные запросы.
Контроль затрат
Этот раздел выделен отдельно, так как API не имеет встроенного ежемесячного лимита, помимо настроенных вами параметров. Любая ошибка здесь приводит к скрытому росту расходов.
max_tokens — это ваш лимит затрат на один вызов. Генерация выходных токенов обходится дороже — на модели Opus 4.8 цена за выход в пять раз выше цены за вход. Параметр max_tokens устанавливает жесткое ограничение на количество генерируемых токенов. Ошибочный промпт не может стоить дороже, чем вы разрешили. Подбирайте размер под задачу: 1,500 токенов достаточно для анализа логов; для задач классификации требуется 100. Если ответы обрываются на полуслове с ошибкой stop_reason: "max_tokens", вы установили слишком низкий лимит — увеличивайте его осознанно, а не используйте огромные значения по умолчанию.
Считайте объем перед отправкой. Входные данные также стоят денег, а логи занимают много места. API предоставляет бесплатный эндпоинт для подсчета (у него свои лимиты запросов, отличные от лимитов на создание сообщений):
count = client.messages.count_tokens(
model="claude-opus-4-8",
messages=[{"role": "user", "content": big_log_text}],
)
print(count.input_tokens)Используйте его, чтобы избежать случайной отправки лога объемом 2 GB через инструмент. Не используйте tiktoken для этих целей — это токенизатор OpenAI, он занижает количество токенов Claude примерно на 15–20% на обычном тексте и еще больше на программном коде.
Выбирайте модель под задачу, а не по принципу лояльности. По состоянию на июль 2026 года: Opus 4.8 (claude-opus-4-8) стоит $5 за миллион входных токенов и $25 за миллион выходных; Haiku 4.5 (claude-haiku-4-5) стоит $1/$5 при контексте 200K; Sonnet 5 (claude-sonnet-5) занимает промежуточное положение со стоимостью $3/$15 (действует ознакомительная цена $2/$10 до 31 августа 2026 года). Для примера: фрагмент лога в 2,000 токенов с ответом в 500 токенов будет стоить около $0.0225 на Opus и $0.0045 на Haiku. Начинайте работу с Opus, пока оцениваете качество ответов, затем попробуйте те же промпты на Haiku — при больших объемах простых преобразований результат часто неотличим, а цена в пять раз ниже. Перед внесением этих данных в бюджет проверьте актуальные значения на странице с ценами.
Используйте Batches для задач, не требующих мгновенного ответа. Batches API обрабатывает запросы асинхронно по цене 50% от стандартной. Большинство пакетов обрабатывается в течение часа. Ночные сводки, заполнение исторических данных, массовая классификация — все, что не требует немедленного участия человека, следует отправлять через Batches.
Используйте кэширование промптов для повторяющегося контекста. Если каждый вызов заново отправляет один и тот же большой системный промпт или инструкцию, пометьте их как кэшируемые:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
system=[{
"type": "text",
"text": RUNBOOK_TEXT, # the same 30K tokens on every call
"cache_control": {"type": "ephemeral"},
}],
messages=[{"role": "user", "content": question}],
)
print(response.usage.cache_read_input_tokens) # non-zero from the second call onЗапись в кэш стоит примерно 1.25x от цены входных данных, чтение из кэша — около 0.1x при TTL 5 минут. Таким образом, второй вызов в течение этого окна уже окупает первый. Есть два нюанса. Кэшируемый префикс должен превышать минимальный порог для конкретной модели (несколько тысяч токенов на Opus), иначе короткий системный промпт не будет кэшироваться. И если cache_read_input_tokens остается равным нулю при идентичных вызовах, значит, в вашем префиксе что-то меняется при каждом запросе (обычно это метка времени).
Учитывайте, что именно считается входными данными. Системные промпты, определения инструментов и — в многошаговых диалогах — вся история, которую вы пересылаете на каждом шаге, тарифицируются как входные токены. Стоимость чат-цикла, в котором история не обрезается, растет квадратично. Необходимо понимать принцип расчета, прежде чем создавать диалоговые системы: как на самом деле суммируется использование токенов и оплата Claude.
Запуск через systemd
Результат использования environment-file: таймер, который каждое утро формирует отчет об ошибках за вчерашний день.
# /etc/systemd/system/log-digest.service
[Unit]
Description=Daily error-log digest via the Claude API
[Service]
Type=oneshot
User=explain
Group=systemd-journal
EnvironmentFile=/etc/claude-explain.env
ExecStart=/bin/sh -c 'journalctl -p err --since yesterday | /opt/explain/venv/bin/python /opt/explain/explain.py >> /var/log/log-digest.txt'# /etc/systemd/system/log-digest.timer
[Unit]
Description=Run the log digest every morning
[Timer]
OnCalendar=06:15
Persistent=true
[Install]
WantedBy=timers.targetsudo useradd -r -s /usr/sbin/nologin explain
sudo touch /var/log/log-digest.txt && sudo chown explain /var/log/log-digest.txt
sudo systemctl daemon-reload
sudo systemctl enable --now log-digest.timer
sudo systemctl start log-digest.service # test it once, right nowОбратите внимание на преимущества EnvironmentFile=: systemd считывает файл с правами root и режимом 600 перед переключением на непривилегированного пользователя explain. Таким образом, процесс получает переменную, а пользователь не может прочитать файл с ключом. Группа systemd-journal предоставляет доступ к логам. Проверьте работу с помощью ручного systemctl start и прочитайте journalctl -u log-digest.service — не ждите 06:15, чтобы обнаружить опечатку. Если этот паттерн станет сложнее конвейера shell, тот же метод использования environment-file можно применить в рабочих процессах n8n на базе Claude на этом же сервере.
Режимы сбоев и соответствующие сообщения об ошибках
401 при рабочем ключе. Текст исключения:
anthropic.AuthenticationError: Error code: 401 - {'type': 'error', 'error': {'type': 'authentication_error', 'message': 'invalid x-api-key'}, 'request_id': 'req_011CSHoEeqs5C35K2UUqR7Fy'}Если ключ работает в вашей оболочке (shell), но сервис возвращает 401, значит, сервис не получил ключ. Помните, что systemd не считывает .bashrc; убедитесь, что EnvironmentFile= указывает на верный путь. Другие причины: кавычки в env-файле (ANTHROPIC_API_KEY="sk-ant-..." — systemd удаляет кавычки, но . file вашего shell-wrapper сохраняет их в значении, если кавычки были расставлены некорректно), лишние пробелы в конце строки или ключ, который вы отозвали в Console на прошлой неделе.
404 из-за опечатки в названии модели. Самая частая причина — добавление суффикса даты к текущему ID модели:
anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-opus-4-8-20260115'}, 'request_id': 'req_011CSJqymAvNw4bT3qmDdMbA'}ID текущего поколения должны быть записаны точно — claude-opus-4-8, claude-haiku-4-5, claude-sonnet-5. Копируйте их из документации к моделям, никогда не используйте память или старые руководства.
429 rate_limit_error. Строка типа ошибки — rate_limit_error, в ответе содержится заголовок retry-after с указанием секунд ожидания. SDK уже выполнил две попытки повторного запроса с задержкой (backoff) до того, как вы увидите исключение. Постоянные ошибки 429 означают, что ваша текущая нагрузка действительно превышает лимит вашего тарифного плана. Группируйте задачи (batch) или распределяйте их во времени; не увеличивайте частоту повторных запросов в цикле.
Выводится объект, а не текст. Вывод выглядит как [TextBlock(citations=None, text='...', type='text')]. Вы вывели response.content вместо того, чтобы итерировать блоки и считывать .text из тех блоков, где block.type == "text". Во всех примерах SDK выше это реализовано верно; скопируйте цикл.
error: externally-managed-environment. Вы запустили pip install в системном Python в Ubuntu 24.04. Используйте venv — никогда не используйте --break-system-packages на сервере, для которого важна стабильность.
Обрезанные ответы. response.stop_reason == "max_tokens" означает, что модель достигла лимита выходных токенов (output cap) в процессе генерации. Это штатное поведение; увеличьте лимит намеренно.
Когда ваше первое приложение заработает, создание AI-агента с помощью Claude превратит те же самые вызовы API в агента, использующего инструменты (tools).
FAQ
Какова стоимость тестирования Claude API?
Для такого инструмента стоимость крайне мала. По состоянию на июль 2026 года, Opus 4.8 стоит $5 за миллион входных токенов и $25 за миллион выходных токенов. Типичная диагностика логов (несколько тысяч входных токенов и несколько сотен выходных) обойдется примерно в два цента. При использовании Haiku 4.5 ($1/$5) стоимость составит менее полуцента. Месяц ежедневных сводок стоит меньше чашки кофе. Основной риск заключается не в цене за один вызов, а в бесконечных циклах и неограниченном max_tokens, поэтому в данном руководстве параметры обоих ограничены явно.
Есть ли бесплатный уровень для Claude API?
По состоянию на июль 2026 года постоянного бесплатного уровня нет. Согласно документации Anthropic, новым пользователям предоставляется небольшое количество бесплатных кредитов для тестирования API — это разовый пробный период, точный размер которого указан в Console при регистрации. После этого необходимо пополнить баланс аккаунта. Если ваша цель — нулевая предельная стоимость запроса, а не качество передовых моделей, альтернативой является self-host an open-weight model with Ollama, где оплата производится ресурсами RAM вместо токенов.
Как обеспечить безопасность API-ключа на сервере?
Никогда не храните ключ в коде, в git, не экспортируйте его через .bashrc и не вводите в shell, если история команд сохраняется. Поместите ключ в файл, принадлежащий root, с правами доступа 600. Загружайте ключ для каждого отдельного процесса: используйте скрипт-обертку для интерактивного использования и EnvironmentFile= для systemd. Используйте один ключ на один сервер или проект, чтобы отзыв скомпрометированного ключа был точечным действием, а не ампутацией всей системы. Если ключ попал на paste-сайт или в git commit, немедленно отзовите его в Console; удаление коммита не устраняет факт утечки.
С какой модели Claude стоит начать?
Начните с claude-opus-4-8, пока вы оцениваете, достаточно ли качественны ответы для разработки. Вам нужно оценить идею при максимальном качестве, а при небольших объемах разница в цене составит центы. Когда промпт будет отлажен, повторно запустите реальные входные данные на claude-haiku-4-5; для задач суммаризации, классификации и сортировки логов эта модель часто работает так же хорошо, но в пять раз дешевле. Переходите на Haiku или Sonnet на основе результатов измерений, а не по умолчанию.