Если вы в курсе новостей последних трёх лет, то наверняка слышали о таком термине, как ИИ-агент. Даже больше — вы могли использовать или хотя бы знать о таких ИИ-агентных системах, как Claude Code или Codex.
Первое время они кажутся просто маркетингом и попыткой оправдать ИИ-пузырь — зачем строить какую-то абстрактную структуру, платить за токены, настраивать агентов и субагентов, если можно идти по стандартному сценарию использования ИИ-чата: открыть сам чат, вставить текст, сформулировать промпт и получить выхлоп. Упростило нам жизнь? Безусловно! Но это быстро упирается в некий потолок. Модель — это просто модель, она не знает ни контекст, ни работает с вашим проектом напрямую. А вот ИИ-агент — уже целая система, ведь это не просто LLM, а LLM + harness.
И вот беда — Claude Code и Codex закрытые, проприетарные и только для их моделей. Как раз тут и приходит OpenCode — open-source harness-система, где модель-агент может читать файлы, запускать тесты, вызывать другие модели, передавать работу субагентам, подключать MCP, скиллы и плагины. А главное — он не привязан к одному вендору. Можно использовать хоть официальные, хоть сторонние провайдеры и вызывать какие угодно доступные модели, хоть GLM 5.3, хоть Claude Opus 5.5.
В этой статье я пройдусь по основам создания ИИ-агентов в OpenCode с подключением OpenAI-совместимого шлюза.
Как я уже говорил, формула «Агент = LLM + Harness» звучит просто, но на самом деле достаточно точна. Под Harness подразумевают всё, что связано с тем, чтобы заставить ИИ работать и прямо решать задачи, то есть туда входит вся наша мишура со скиллами, контекстом, интеграциями, мультиагентной логикой. Так что в современном мире, если уж использовать ИИ, то нужно по-умному — и в этом OpenCode поможет.
Claude Code и Codex задали планку, но у них разная цена свободы. Claude Code — проприетарный harness, привязанный к моделям Anthropic; другие модели подключаются через gateway на ANTHROPIC_BASE_URL. Codex CLI LLM-агностичен через Open Responses API, но остаётся продуктом OpenAI. OpenCode занимает третью позицию — MIT-лицензия, 75+ провайдеров, локальные модели через Ollama или vLLM и полный контроль над тем, какая модель выполняет роль оркестратора, кодера, ревьюера или тестировщика.
Следующий вопрос — провайдер. OpenCode позволяет использовать OpenAI-совместимый шлюз, и вы можете прогнать GPT-6.1 Sol, Claude Opus 5.5, GLM-5.3 или Qwen через наш сервис Bothub. Он даёт доступ к сотням моделей через один OpenAI-совместимый эндпоинт https://openai.bothub.chat/v1, оплату с российской карты без VPN и внутреннюю валюту Caps, которая не сгорает пакетами. По реферальной ссылке начисляется 300 000 CAPS — этого достаточно, чтобы поднять конфиг из этой статьи и сравнить модели на своих задачах. Актуальный каталог доступен на https://bothub.chat/models.
Как поставить, войти и настроить
А теперь перейдём к установке и настройке, тут есть пара интересных моментов, с которыми я сам сталкивался.
Ставится OpenCode глобально через npm (или через расширение в VSCode, который автоматически ставит OpenCode). Тут есть нюанс — актуальная ветка v2 живёт под пакетом @opencode/cli, а старый opencode-ai — это уже легенда, линия 1.x.
npm i -g @opencode/cli opencode --version
Есть и альтернативы — brew install anomalyco/tap/opencode-v2, bun install -g --trust @opencode/cli, установка через официальный скрипт curl -fsSL https://opencode.ai/v2/install | bash.
А теперь поговорим об интеграции кастомного провайдера, в нашем случае — openai-совместимого шлюза BotHub: https://openai.bothub.chat/v1. Создать API-ключ вы можете по этой ссылке.
В конфиге используется пакет @ai-sdk/openai-compatible и baseURL ровно тот, что указан в документации сервиса. Вот как может выглядеть минимальный пример:
{ "$schema": "https://opencode.ai/config.json", "provider": { "bothub": { "npm": "@ai-sdk/openai-compatible", "name": "Bothub Gateway", "options": { "baseURL": "https://openai.bothub.chat/v1" }, "models": { "gpt-6.1-sol": { "name": "GPT-6.1 Sol" }, "claude-sonnet-5.5": { "name": "Claude Sonnet 5.5" }, "claude-opus-5.5": { "name": "Claude Opus 5.5" }, "glm-5.3": { "name": "GLM-5.3" }, "glm-5.3-flash": { "name": "GLM-5.3 Flash" } } } } }
Как ключи идут ID моделей для API (а сам список моделей доступен по этой ссылке).
Но куда же вставить API-ключ? Можно, конечно, и в конфиг или через env, но гораздо безопаснее будет воспользоваться командой opencode auth login.
В списке провайдеров ищем нашего провайдера (в данном случае — Bothub Gateway). Таким образом, креды не сохраняются в файл или в env.
А теперь о том, какая беда была у меня. OpenCode работает на Bun. А Bun’s native fetch, мягко говоря, странно относится к HTTP_PROXY и HTTPS_PROXY: в одних сценариях он их читает, в других игнорирует, а при установке плагинов через npm-цепочку может упасть с ошибкой fetch() proxy.url must be a non-empty string. Причина — несовместимость типов между @npmcli/agent и полифиллом node:http в Bun: агент возвращает объект URL, а fetch ждёт строку. Выглядит как баг, и это он и есть.
Отдельная история — socks5://. Bun такое не переваривает, ему нужен http://-формат. И даже если вы всё сделали правильно, NO_PROXY с localhost и 127.0.0.1 обязателен, иначе TUI может уйти в прокси и зациклиться на собственных запросах.
Так что лучше убрать все прокси или хотя бы перевести их в HTTP/HTTPS-вид. Лично я на это убил времени знатно, ибо с прокси он плохо работает.
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY opencode service restart opencode
Прежде чем собирать оркестратора и субагентов, убедитесь, что всё работает и запросы отправляются.

Агенты и субагенты
Вот мы и добрались наконец до самого интересного. Провайдер подключён, ключ на месте, модели видны в /models — самое время строить агентскую систему. Нам нужно не просто раскидать модели и дать им имена, а настроить промпты и разрешения.
Перед этим хочу привести свой личный конфиг:
{ "$schema": "https://opencode.ai/config.json", "provider": { "bothub": { "npm": "@ai-sdk/openai-compatible", "name": "Bothub Gateway", "options": { "baseURL": "https://openai.bothub.chat/v1" }, "models": { "gpt-6.1-sol": { "name": "GPT-6.1 Sol" }, "claude-sonnet-5.5": { "name": "Claude Sonnet 5.5" }, "glm-5.3": { "name": "GLM-5.3" }, "glm-5.3-flash": { "name": "GLM-5.3 Flash" }, "gemini-4-argon": { "name": "Gemini 4 Argon" }, "claude-opus-5.5": { "name": "Claude Opus 5.5" }, "gpt-6-luna": { "name": "GPT-6 Luna" }, "gpt-6-astra": { "name": "GPT-6 Astra" } } } }, "model": "bothub/gpt-6.1-sol", "small_model": "bothub/glm-5.3-flash", "plugin": [ "opencode-wakatime" ], "mcp": { "context7": { "type": "remote", "url": "https://mcp.context7.com/mcp", "enabled": true } }, "agents": { "orchestrator": { "model": "bothub/gpt-6.1-sol", "description": "Primary orchestrator. Decomposes tasks, routes work to subagents, and synthesizes the final answer.", "mode": "primary", "temperature": 0.1, "prompt": "You are a senior engineering lead acting as an orchestrator. Your job is to break down the user's request into clear, parallelizable workstreams, delegate each workstream to the most appropriate subagent (architect, coder, reviewer, security, docs, tests), and then merge their outputs into a coherent final response. Do not write production code yourself unless explicitly asked. Always prefer delegation over doing everything alone.", "permission": { "edit": "deny", "bash": "ask", "webfetch": "allow" } }, "architect": { "model": "bothub/gemini-4-argon", "description": "System architect. Designs high-level solutions, module boundaries, data flows, and integration strategies.", "mode": "subagent", "temperature": 0.1, "prompt": "You are a principal software architect. Given a problem statement or an existing codebase, produce a high-level design: component boundaries, responsibilities, data flow, key interfaces, and trade-offs. Consider scalability, maintainability, and operational concerns. Do not write full implementations unless asked for a small illustrative snippet. Your output should be a structured design document or a concise architectural decision record.", "permission": { "edit": "deny", "bash": "ask", "webfetch": "allow" } }, "coder": { "model": "bothub/claude-sonnet-5.5", "description": "Primary coding agent. Implements features, fixes bugs, refactors, and writes production code.", "mode": "primary", "temperature": 0.0, "prompt": "You are an expert software engineer. Implement the requested change with clean, idiomatic, well-tested code. Follow the project's existing conventions. When you edit files, prefer minimal, focused diffs. Run the relevant tests or linters if available. If something is ambiguous, ask a clarifying question instead of guessing. Do not add unnecessary comments or over-engineer.", "permission": { "edit": "allow", "bash": "ask", "webfetch": "allow" } }, "reviewer": { "model": "bothub/claude-opus-5.5", "description": "Code reviewer. Finds bugs, logic errors, style violations, and architectural inconsistencies.", "mode": "subagent", "temperature": 0.2, "prompt": "You are a meticulous code reviewer. Review the given diff or file for correctness, edge cases, performance, readability, and adherence to project conventions. Categorize findings by severity (blocker, major, minor, nit). Explain why something is a problem, not just what to change. Do not rewrite the code yourself; your output is a review, not a patch.", "permission": { "edit": "deny", "bash": "allow", "webfetch": "deny" } }, "security": { "model": "bothub/glm-5.3", "description": "Security engineer. Audits code for vulnerabilities, injection flaws, insecure defaults, and supply-chain risks.", "mode": "subagent", "temperature": 0.1, "prompt": "You are a paranoid application security engineer. Review the given code for OWASP Top 10 issues, injection vectors, authentication/authorization flaws, insecure deserialization, secret leakage, and dependency risks. For each finding, describe the attack scenario and a concrete remediation. Prioritize exploitable issues over theoretical ones. Assume the attacker has full knowledge of the codebase.", "permission": { "edit": "deny", "bash": "allow", "webfetch": "allow" } }, "docs": { "model": "bothub/gpt-6-luna", "description": "Technical writer. Produces README files, API references, and user-facing documentation.", "mode": "subagent", "temperature": 0.4, "prompt": "You are a senior technical writer. Turn code, diffs, or rough notes into clear, accurate documentation. Match the project's existing tone and structure. Prefer examples over abstract descriptions. For API docs, include request/response schemas and edge cases. Do not invent behavior that is not present in the code; if something is unclear, state the assumption explicitly.", "permission": { "edit": "deny", "bash": "deny", "webfetch": "allow" } }, "tests": { "model": "bothub/glm-5.3-flash", "description": "Test engineer. Writes unit, integration, and end-to-end tests with good coverage.", "mode": "subagent", "temperature": 0.1, "prompt": "You are a test engineer. Given a function, module, or feature, write tests that cover the happy path, edge cases, error conditions, and boundary values. Use the project's existing test framework and conventions. Prefer table-driven or parameterized tests where appropriate. Do not modify production code; your output is test code only.", "permission": { "edit": "deny", "bash": "allow", "webfetch": "deny" } } } }
Primary и subagent — в чём разница на практике
В OpenCode агенты делятся на два типа: Primary — это тот, с кем вы общаетесь напрямую. Открыли TUI, выбрали агента (или он выбран по умолчанию), и вот он перед вами. Subagent — это исполнитель, которого primary-агент вызывает сам, через делегирование или через @mention. Работает он в дочерней сессии, со своим контекстом, и в основной чат возвращает только результат.
Кстати, OpenCode уже приходит с четырьмя встроенными агентами: build и plan — это primary, а general и explore — субагенты. Их можно переопределять, но в нашем конфиге мы просто добавляем своих, не трогая встроенные. Почему бы и нет, иногда стандартный build удобнее кастомного оркестратора.
Кстати, вот сами агенты в виде таблицы:
Агент |
Mode |
Модель |
Роль |
edit |
bash |
Что важно |
|---|---|---|---|---|---|---|
|
primary |
GPT-6.1 Sol |
Декомпозиция, делегирование |
deny |
ask |
Сам код не пишет |
|
subagent |
Gemini 4 Argon |
Высокоуровневый дизайн |
deny |
ask |
ADR / design doc |
|
primary |
Claude Sonnet 5.5 |
Реализация |
allow |
ask |
Минимальные диффы |
|
subagent |
Claude Opus 5.5 |
Ревью |
deny |
allow |
Только review, без патчей |
|
subagent |
GLM-5.3 |
Аудит уязвимостей |
deny |
allow |
OWASP, injection |
|
subagent |
GPT-6 Luna |
Документация |
deny |
deny |
Не выдумывать поведение |
|
subagent |
GLM-5.3 Flash |
Тесты |
deny |
allow |
Prod-код не трогает |
Я руководствовался в основном новостями. GPT 6.1-Sol неплохой оркестратор (не слишком дорогой и достаточно умный), Gemini 4 Argon как архитектор стоит из-за желания потестировать, а ещё он хорош для ресерча и планирования архитектуры. Как кодер — старый добрый клод, Claude Sonnet 5.5, последняя на момент написания статьи. Как ревьювер — уже сам Opus, так как он дорогой, но умный. GLM-5.3 я решил для аудита уязвимости протестировать (я вообще постарался не использовать 1-2 флагманские модели, а разные, в том числе и открытые). Как документация — дешёвый GPT-6 Luna. А для тестов GLM-5.3 Flash, хоть и flash-модель обучена на ответах основной модели, но мне для тестов больше и не надо.
Самое важное — что только Coder имеет право на редактуру. Если несколько агентов или субагентов могут править файлы, начинается хаос: то оркестратор что-то поправит, то ревьюер что-то перепишет, и в итоге diff разрастается до неприличия, а кто именно внёс изменение — непонятно. Поэтому в моей схеме edit: allow есть только у coder. Он — исполнитель, его дело — писать код. Остальные либо советуют, либо проверяют.
У reviewer и security при этом bash: allow. Звучит немного странно: править файлы нельзя, а команды запускать можно? Но в этом и смысл. Ревьюеру нужно уметь прогнать линтер, посмотреть git diff, проверить, что тесты проходят.
Разрешения как технический запрет
Инструкцию «не делай git push» можно написать в промпте. Модель её, скорее всего, послушается. Но может и не послушаться — особенно если задача сформулирована двусмысленно или контекст подтолкнул к «завершению работы». Технический deny обойти нельзя, и это его главное преимущество. Вот как это выглядит в конфиге:
"permission": { "bash": { "*": "ask", "git status": "allow", "git diff *": "allow", "git commit *": "deny", "git push *": "deny" } }
Всё, что не разрешено явно, спрашивает подтверждения. Всё, что запрещено, — запрещено на уровне harness. Удобно, что правила можно прописывать точечно, допустим git diff * разрешён, а git commit * уже нет.
Markdown-агенты — альтернатива JSON
Кстати, держать всех агентов в одном opencode.json необязательно. Их можно выносить в отдельные markdown-файлы: .opencode/agents/<name>.md. Имя файла становится именем агента, YAML frontmatter — конфигом (модель, режим, permissions), а тело файла — системным промптом. Удобно, когда промпты длинные и их хочется версионировать отдельно от общих настроек. Плюс, если вы работаете в команде, такие файлы можно коммитить в репозиторий, и у всех будут одинаковые агенты.
--- description: Reviews code for quality and best practices mode: subagent model: anthropic/claude-sonnet-5-5 temperature: 0.1 tools: write: false edit: false bash: false --- You are in code review mode. Focus on: - Code quality and best practices - Potential bugs and edge cases - Performance implications - Security considerations Provide constructive feedback without making direct changes.
Вызов субагентов
В моей системе оркестратор получает задачу, разбивает её на части и вызывает нужного субагента через @mention или через внутренний механизм делегирования, или на крайний случай вы сами можете оперировать субагентами. В TUI это выглядит как обычный диалог, но под капотом запускается дочерняя сессия с чистым контекстом. Родительский агент видит только результат, что экономит токены и не засоряет основной контекст.
Но увы, не все субагенты доступны для вызова из любого primary-агента. В конфиге это регулируется через permissions на уровне агента, но в моей текущей схеме я не стал это ужесточать — оркестратор и так знает, кого звать. Если понадобится, можно запретить вызов конкретных субагентов, но это уже тонкая настройка под конкретный воркфлоу.
Дальше логично было бы поговорить о том, как добавить skills и MCP. А пока — проверьте, что ваши агенты видны в /agents и что каждый из них отвечает на простой запрос. Если что-то не так, скорее всего, дело в ID модели или в правах.
Skills, Commands, MCP и плагины
Агенты настроены, права розданы, можно запускать. Но агентная система — не агентная, если нет MCP и скиллов. Без них конструкция прямолинейная. Но именно они превращают базовый harness в полноценную систему.
Skills: инструкции, которые подгружаются по требованию
Скиллы в OpenCode (и в принципе в ИИ-агентах) — это способ упаковать повторяющийся алгоритм или инструкции в отдельный файл и не тащить его в системный промпт каждый раз. Работает просто: вы кладёте SKILL.md в .opencode/skills/<id>/, агент видит имя и описание скилла, а полный текст подтягивает только когда задача действительно требует. Что немаловажно, это экономит контекст — а он в агентной работе не резиновый.
Выглядит это примерно так:
--- name: code-review description: Последовательность проверки диффа перед мержем --- 1. Изучи изменённые файлы и связанные с ними вызовы 2. Проверь обработку ошибок и граничные случаи 3. Найди потенциальные регрессии в зависимом коде 4. Сверься с конвенциями проекта 5. Выдай замечания по степени критичности
Формат, на первый взгляд, простой, опять markdown с промптом. Но за этой простотой — важное отличие: скилл не имеет своих инструментов и разрешений, он наследует всё от агента, который его вызвал. То есть если ревьюеру запрещено редактировать файлы, то и скилл code-review внутри него файлы редактировать не сможет. Кроме того, позволяет выводить инструкции, кодстайл отдельно, и ИИ-агент будет это учитывать.
Commands: заранее заготовленные последовательности
Команды — это шаги, которые вы уже прошли руками и не хотите проходить снова. Живут в .opencode/commands/, вызываются через /имя. Классический пример — /fix-bug: изучи баг → найди код → причина → решение → подтверждение → изменения → тесты → diff. Всё это прописано в одном файле, и агенту не нужно каждый раз объяснять, в каком порядке что делать.
Разница между командой и скиллом в том, что команда запускается явно, а скилл подтягивается автоматически по описанию. Ну и команда обычно задаёт жёсткий порядок шагов, а скилл — скорее набор правил, которые применяются по ситуации. Кстати, и то и другое можно версионировать вместе с проектом, что для командной работы звучит разумно.
Создавать можно также через файлы в .opencode/commands. Например, можно создать файл .opencode/commands/review.md со следующим содержанием:
--- description: Review code for correctness agent: plan model: anthropic/claude-sonnet-5-5#high --- Review $ARGUMENTS. Report bugs first.
Команды поддерживают позиционирование аргументов:
Compare $1 with $2.
Или вызов shell-команд:
Summarize these commits: !`git log --oneline -$1`
Есть ещё много функционала, более подробно вы можете прочитать в документации.
MCP: внешние инструменты
MCP-серверы — это отдельная история. В моём конфиге подключён ровно один, context7, и он remote:
"mcp": { "context7": { "type": "remote", "url": "https://mcp.context7.com/mcp", "enabled": true } }
Зачем он нужен? Context7 подтягивает актуальную документацию библиотек прямо в контекст агента. Полезно, когда работаешь с фреймворком, который меняется быстрее, чем обновляются твои знания о нём. Локальные MCP-серверы (type: "local" с command) подключаются так же, только вместо URL указывается команда запуска — часто это нужно для GitLab, Jira или внутренних API.
Тут стоит помнить, что MCP — не всегда лучший путь. Если задача решается скриптом или обычным HTTP-запросом, тащить MCP ради модного слова не стоит. Он оправдан, когда нужен именно структурированный интерфейс к внешней системе.
Плагины: расширение самого harness
Плагины — это уже про сам OpenCode. В моём конфиге прописан opencode-wakatime, который трекает время работы прямо из TUI. Устанавливается автоматически через Bun при старте, если прописан в "plugin": [...].
Примечательно, что плагины — одна из тех областей, где версии болезненно расходятся. Плагины, написанные под v1, на v2 работать не будут, их нужно либо адаптировать, либо искать v2-совместимый аналог.
Заключение
Собрать агентскую систему в OpenCode — это такая же архитектурная задача, а не просто выбор моделей, ведь, как я уже говорил, ИИ — это LLM + Harness. Хотя, признаюсь, выбор моделей — самая приятная часть процесса. Кроме того, стоит помнить, что агентская система — вещь молодая и постоянно развивается.
Я начал пользоваться ИИ-агентами недавно и понял, что они достаточно удобные, а при качественной настройке можно оптимизировать свою работу и ускорить её, а не просто тратить тысячи рублей на токены ради простейшего вайбкодинга.
И не забывайте ревьювить и читать выхлоп модели, ибо ИИ-агенты постепенно забирают часть работы — написание кода — но работа программного инженера заключается не только в написании кода. А ещё то, что сложные вещи может за вас написать ИИ-агент, — не причина не изучать сложные вещи и не развиваться. Даже иронично, чтобы ИИ забрал работу, приходится работать…