Перейти к содержимому
OmniRoute source

ACP (Agent Client Protocol) (Русский)

ACP (Agent Client Protocol) — это транспорт типа «CLI как бэкенд» для OmniRoute. Вместо перехвата вызовов HTTP API к поставщикам ИИ ACP запускает CLI-агенты как дочерние процессы и передаёт запросы через их нативный интерфейс.

Преимущество Описание
API-ключи не требуются Использует существующую аутентификацию вашего CLI
Нативный протокол Использует нативный формат ввода-вывода каждого CLI
Автообнаружение Обнаруживает установленные в системе CLI
15 встроенных агентов Предварительно настроен для популярных CLI-инструментов
Пользовательские агенты Позволяет добавлять собственные CLI-инструменты через настройки
Управление процессами Управляет жизненным циклом (запуск, отправка, завершение)

ACP изначально поддерживает 15 встроенных CLI-агентов:

ID агента Отображаемое имя Исполняемый файл Протокол
codex OpenAI Codex CLI codex stdio
claude Claude Code CLI claude stdio
goose Goose CLI goose stdio
openclaw OpenClaw openclaw stdio
aider Aider aider stdio
opencode OpenCode opencode stdio
cline Cline cline stdio
qwen Qwen Code qwen --acp stdio
forge ForgeCode forge stdio
amazon-q Amazon Q Developer q stdio
interpreter Open Interpreter interpreter stdio
cursor-cli Cursor CLI cursor stdio
warp Warp AI warp stdio
gemini Gemini CLI gemini stdio
zcode ZCode zcode stdio

Вы можете добавлять собственные CLI-агенты через настройки. Пользовательские агенты поддерживают те же возможности, что и встроенные.


Окно терминала
# Пример: установите Claude Code CLI
npm install -g @anthropic-ai/claude-code
# Проверьте установку
claude --version

ACP автоматически обнаруживает установленные в вашей системе CLI-агенты. Настройка не требуется!

После обнаружения ACP можно использовать как транспорт для любого поддерживаемого поставщика. OmniRoute автоматически использует ACP, когда CLI доступен.


┌─────────────────┐
│ OmniRoute │
│ (HTTP-прокси) │
└────────┬────────┘
│
│ spawn()
▼
┌─────────────────┐
│ Дочерний процесс│
│ (CLI-агент) │
│ │
│ stdin ◄──────┤ Отправка запроса
│ stdout ──────►│ Получение ответа
│ stderr ──────►│ Получение ошибок
└─────────────────┘
  1. Запуск — ACP создаёт дочерний процесс для CLI-агента
  2. Отправка — ACP записывает запросы в stdin процесса
  3. Получение — ACP считывает ответы из stdout/stderr
  4. Обнаружение простоя — ACP ожидает 2 секунды бездействия, прежде чем считать ответ завершённым
  5. Завершение — ACP завершает процесс (SIGTERM, затем SIGKILL через 5 секунд)

Для взаимодействия с CLI-агентами ACP использует stdio (стандартный ввод-вывод). Протокол выглядит следующим образом:

  1. Отправка запроса — запись в stdin с символом новой строки
  2. Ожидание ответа — чтение из stdout до наступления простоя (отсутствие вывода в течение 2 секунд)
  3. Тайм-аут — по умолчанию 120 секунд (настраивается)

Обнаруживает все установленные в системе CLI-агенты. Результаты кэшируются на 60 секунд.

import { detectInstalledAgents } from "@/lib/acp";
const agents = detectInstalledAgents();
// Возвращает: CliAgentInfo[]
interface CliAgentInfo {
id: string; // например, "codex", "claude"
name: string; // Отображаемое имя
binary: string; // Имя запускаемого бинарного файла
versionCommand: string; // Команда определения версии
version: string | null; // Обнаруженная версия (null, если не установлен)
installed: boolean; // Установлен ли агент
providerAlias: string; // ID провайдера в OmniRoute
spawnArgs: string[]; // Аргументы, передаваемые при запуске
protocol: "stdio" | "http"; // Протокол связи
isCustom?: boolean; // Является ли агент пользовательским
}

Получает только агенты, которые установлены и доступны для ACP.

import { getAvailableAgents } from "@/lib/acp";
const available = getAvailableAgents();
// Возвращает: CliAgentInfo[] (только установленные агенты)

Получает конкретного агента по ID.

import { getAgentById } from "@/lib/acp";
const agent = getAgentById("claude");
// Возвращает: CliAgentInfo | undefined

Устанавливает определения пользовательских агентов из настроек.

import { setCustomAgents } from "@/lib/acp";
setCustomAgents([
{
id: "my-custom-cli",
name: "My Custom CLI",
binary: "mycli",
versionCommand: "mycli --version",
providerAlias: "my-provider",
spawnArgs: [],
protocol: "stdio",
},
]);

Запускает новый процесс CLI-агента.

import { acpManager } from "@/lib/acp";
const session = acpManager.spawn("claude", "claude", ["--print", "--output-format", "json"], {
/* пользовательские переменные окружения */
});
// Возвращает: AcpSession

Допустимые ID агентов: ["claude", "codex", "gemini", "qwen"]

Отправляет запрос CLI-агенту и получает ответ.

import { acpManager } from "@/lib/acp";
const response = await acpManager.sendPrompt(
"acp-claude-1234567890-abc123",
"What is 2+2?",
120000 // тайм-аут 2 минуты
);
// Возвращает: Promise<string>

Завершает сеанс и освобождает ресурсы.

import { acpManager } from "@/lib/acp";
const killed = acpManager.kill("acp-claude-1234567890-abc123");
// Возвращает: boolean

Получает все активные сеансы.

import { acpManager } from "@/lib/acp";
const sessions = acpManager.getActiveSessions();
// Возвращает: AcpSession[]

Завершает все сеансы.

import { acpManager } from "@/lib/acp";
acpManager.killAll();
interface AcpSession {
id: string; // Уникальный ID сеанса
agentId: string; // ID агента (например, "claude")
process: ChildProcess; // Дескриптор дочернего процесса
alive: boolean; // Активен ли процесс
stdoutBuffer: string; // Накопленный буфер stdout
stderrBuffer: string; // Накопленный буфер stderr
createdAt: Date; // Временная метка создания
}

AcpManager расширяет EventEmitter и генерирует следующие события:

Генерируется, когда CLI-агент записывает данные в stdout.

acpManager.on("stdout", ({ sessionId, data }) => {
console.log(`[${sessionId}] stdout: ${data}`);
});

Генерируется, когда CLI-агент записывает данные в stderr.

acpManager.on("stderr", ({ sessionId, data }) => {
console.error(`[${sessionId}] stderr: ${data}`);
});

Генерируется при завершении процесса CLI-агента.

acpManager.on("exit", ({ sessionId, code, signal }) => {
console.log(`[${sessionId}] exited with code ${code}, signal ${signal}`);
});

Генерируется при возникновении ошибки в процессе CLI-агента.

acpManager.on("error", ({ sessionId, error }) => {
console.error(`[${sessionId}] error: ${error}`);
});

ACP наследует все переменные окружения родительского процесса и может быть дополнен пользовательскими переменными окружения:

acpManager.spawn("claude", "claude", [], {
ANTHROPIC_API_KEY: "sk-...",
DEBUG: "true",
});

Каждый агент имеет аргументы запуска по умолчанию, определённые в реестре. Их можно переопределить:

acpManager.spawn("claude", "claude", ["--print", "--verbose"], {});

Тайм-аут запроса по умолчанию составляет 120 секунд (2 минуты). Его можно переопределить:

await acpManager.sendPrompt(sessionId, prompt, 300000); // 5 минут

Результаты обнаружения агентов кешируются на 60 секунд, чтобы избежать ресурсоёмкого сканирования файловой системы. Чтобы принудительно обновить кеш:

import { refreshAgentCache } from "@/lib/acp";
refreshAgentCache();

ACP проверяет команды получения версии, чтобы предотвратить атаки с внедрением команд:

const DISALLOWED_VERSION_COMMAND_CHARS = /[;&|<>`$\r\n]/;

Команды получения версии, содержащие следующие символы, отклоняются:

  • ; — Разделитель команд
  • & — Фоновый процесс
  • | — Конвейер
  • <, > — Перенаправление
  • ` — Подстановка команды
  • $ — Подстановка переменной
  • \r, \n — Переносы строк

ACP проверяет, совпадает ли исполняемый файл в команде получения версии с ожидаемым именем исполняемого файла (кроме случаев, когда используется пользовательский агент).

Каждый сеанс ACP выполняется в отдельном дочернем процессе. Процесс завершается при окончании сеанса или по истечении тайм-аута.


  • Первый вызов: ~50-200 мс (выполняет команду version для каждого агента)
  • Кешированные вызовы: <1 мс (возвращает результат из кеша)
  • TTL кеша: 60 секунд
  • Запуск: ~50-100 мс
  • Отправка запроса: ~10-50 мс
  • Ожидание ответа: Зависит от CLI-агента (обычно 1-30 секунд)
  • Завершение: ~5 секунд (SIGTERM) + немедленно (SIGKILL)
  • Память на сеанс: ~10-50 МБ (зависит от CLI-агента)
  • ЦП: Минимальное использование (ограничено операциями ввода-вывода)
  • Диск: Не используется

Проблема: acpManager.spawn() выдаёт ошибку Unknown agent: &lt;id&gt;

Решение: В spawn() разрешены только следующие агенты:

  • claude
  • codex
  • gemini
  • qwen

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

Проблема: acpManager.sendPrompt() выдаёт ошибку Session ${sessionId} is not alive

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

const session = acpManager.getSession(sessionId);
if (!session?.alive) {
// Повторно запустите сеанс
acpManager.spawn("claude", "claude", [], {});
}

Проблема: acpManager.sendPrompt() выдаёт ошибку ACP timeout after 120000ms

Решение: Увеличьте тайм-аут:

await acpManager.sendPrompt(sessionId, prompt, 300000); // 5 минут

Проблема: detectInstalledAgents() не находит ваш CLI

Решения:

  1. Проверьте PATH: Убедитесь, что CLI находится в системной переменной PATH
  2. Проверьте команду получения версии: Выполните claude --version вручную
  3. Проверьте разрешения: Убедитесь, что CLI является исполняемым
  4. Пользовательский агент: Добавьте пользовательское определение агента для нестандартных CLI

Проблема: ACP не может выполнить CLI

Решения:

  1. Проверьте права доступа к файлу: chmod +x /usr/local/bin/claude
  2. Проверьте владельца: Убедитесь, что OmniRoute имеет права на чтение и выполнение
  3. Проверьте SELinux/AppArmor: Эти системы могут блокировать запуск процессов

import { acpManager, detectInstalledAgents } from "@/lib/acp";
// Обнаружение установленных агентов
const agents = detectInstalledAgents();
const claude = agents.find((a) => a.id === "claude");
if (claude?.installed) {
// Запуск нового сеанса
const session = acpManager.spawn("claude", claude.binary, ["--print", "--output-format", "json"]);
// Отправка запроса
const response = await acpManager.sendPrompt(
session.id,
"Объясни квантовые вычисления в 100 словах"
);
console.log("Ответ Claude:", response);
// Очистка ресурсов
acpManager.kill(session.id);
}

Пример 2: Автоматическое обнаружение с резервным вариантом

Заголовок раздела «Пример 2: Автоматическое обнаружение с резервным вариантом»
import { acpManager, getAvailableAgents } from "@/lib/acp";
const available = getAvailableAgents();
// Сначала пробуем Claude, затем используем Codex как резервный вариант
let agentId = "claude";
if (!available.find((a) => a.id === "claude")) {
if (available.find((a) => a.id === "codex")) {
agentId = "codex";
} else {
throw new Error("ACP-совместимый CLI-агент не найден");
}
}
const agent = available.find((a) => a.id === agentId)!;
const session = acpManager.spawn(agentId, agent.binary, agent.spawnArgs);
const response = await acpManager.sendPrompt(session.id, "Привет!");
acpManager.kill(session.id);
import { setCustomAgents, detectInstalledAgents } from "@/lib/acp";
// Регистрация пользовательского CLI-агента
setCustomAgents([
{
id: "my-llm-cli",
name: "My LLM CLI",
binary: "myllm",
versionCommand: "myllm --version",
providerAlias: "my-llm-provider",
spawnArgs: ["--format", "json"],
protocol: "stdio",
},
]);
// Теперь detectInstalledAgents() будет включать "my-llm-cli"
const agents = detectInstalledAgents();


  • Проект AionUi — Источник вдохновения для автоматического обнаружения ACP
  • Исходный код ACP — Подробности реализации
    • manager.ts — Управление жизненным циклом процессов
    • registry.ts — Обнаружение и регистрация агентов
    • index.ts — Экспорт публичного API

Исходный код OmniRoute (a58000c7685f)

HagiCode

HagiCode — агентная среда разработки со структурированными процессами, параллельным выполнением несколькими агентами и интерфейсами Hero Dungeon.

Превращайте идеи в полезное ПО с более умным, быстрым и увлекательным агентным рабочим процессом.

Главный экран HagiCode в светлой теме
  • SmartСтруктурированные процессы превращают намерение в исполнимый путь от идеи до готового изменения.
  • EfficientМультиагентные процессы параллельно продвигают исследование, реализацию и проверку.
  • FunHero Dungeon делает длительную совместную разработку наглядной и увлекательной.
Перейти на HagiCode