@infomaximum/ai-orchestrator
v7.1.0
Published
LangGraph-based orchestrator for multi-agent pipelines over Claude Code CLI
Readme
@infomaximum/ai-orchestrator
LangGraph-based движок для мультиагентных пайплайнов поверх LLM-агентов. Движок не привязан к конкретному провайдеру: в комплекте идут claudeAdapter (запуск Claude Code CLI) и codexAdapter (запуск OpenAI Codex CLI), один из них нужно явно подключить в конфиге, либо реализовать свой AgentAdapter.
Что даёт пакет:
- запуск агентов через
claude -pс verdict-протоколом (маркер<verdict>в финальном ответе агента + резервный JSON-файл); - human-in-the-loop через
interrupt()с CLI-промптом и$EDITORдля развёрнутого фидбэка; - persistent-сессии — после каждого запуска сохраняется чекпоинт, прерванное продолжается через
imo resume; - параллельный запуск агентов — статический (
addParallelAgents) и динамический (addDynamicFanOut+makeRetryRouter), с per-branch retry и общим прогресс-индикатором; - CLI:
init/run/resume/sessions/graph.
Установка
npm install @infomaximum/ai-orchestratorТребования: Node.js ≥ 18, установленный claude CLI в $PATH. Файлы агентов (.md) можно держать где удобно. Рекомендуемый вариант — рядом с файлом пайплайна, тогда путь к ним собирается через import.meta.url. Альтернатива — общая директория .claude/agents/.
Quick start
# 1. В корне своего проекта
npx imo init # создаст ai-orchestrator.config.mjs + дополнит .gitignore
# 2. Напиши свой пайплайн (см. docs/writing-pipelines.md) и зарегистрируй его в ai-orchestrator.config.mjs
# 3. Запусти
npx imo run my-pipeline "<описание задачи>"Каждый запуск создаёт сессию — директорию <sessionsDir>/<id>/ с чекпоинтами, логами и артефактами агентов. Если сессия прервалась (Ctrl-C, упал агент, остановились на human-gate (ожидание подтверждения от человека) и закрыли терминал) — её можно продолжить с последнего чекпоинта: imo resume <id>. Список сессий — imo sessions.
CLI
imo init # ai-orchestrator.config.mjs + .gitignore + .claude/agents/imo-pipeline-writer.md
imo run <pipeline> "<описание>" # свежий запуск пайплайна
imo resume <session-id> # продолжить прерванную сессию
imo sessions # список всех сессий
imo graph <pipeline> # ASCII-визуализация графа пайплайна
imo help # справкаФлаги:
--config <path>— путь к конфигу (по умолчаниюai-orchestrator.config.mjsв cwd).imo graph:--mermaid(raw Mermaid вместо ASCII),--ascii-only(только ASCII-символы вместо Unicode),--no-color,--output <path>(записать в файл).
Конфиг
// ai-orchestrator.config.mjs
import { claudeAdapter } from "@infomaximum/ai-orchestrator/runtime";
export default {
sessionsDir: ".ai-orchestrator/sessions",
pipelinesDir: "configs/ai-pipelines",
lastMigratedVersion: "6.0.0",
adapter: claudeAdapter, // обязательное поле: claudeAdapter или свой AgentAdapter
pipelines: {
"my-pipeline": () => import("./configs/ai-pipelines/my-pipeline.mjs").then((m) => m.myPipeline),
},
};adapter — обязательное поле. Без него imo run упадёт с ошибкой loadConfig. Используй claudeAdapter из @infomaximum/ai-orchestrator/runtime или реализуй свой AgentAdapter (см. секцию 8.5 в docs/writing-pipelines.md).
В пакете встроены два адаптера: claudeAdapter (запуск claude -p) и codexAdapter (запуск codex exec). Импорт обоих — import { claudeAdapter, codexAdapter } from "@infomaximum/ai-orchestrator/runtime";. Установка CLI — на стороне пользователя: claude (см. claude.com/code) или codex (npm i -g @openai/codex / brew install --cask codex). Флаги по умолчанию подобраны под non-interactive исполнение (Claude: --dangerously-skip-permissions; Codex: --dangerously-bypass-approvals-and-sandbox + --skip-git-repo-check). Поле tools во frontmatter применимо только к claudeAdapter — в codexAdapter оно логируется warning'ом и игнорируется (у codex exec нет CLI-whitelist'а инструментов).
lastMigratedVersion — опциональное поле, фиксирует версию движка, под которую актуализированы пайплайны. Используется при обновлении пакета (см. ниже). imo init подставляет туда текущую версию движка автоматически.
Миграции при обновлении
Каждая версия пакета содержит файл docs/migrations/v<X.Y.Z>.md (внутри npm-tarball'а). В нём — структурированные записи о breaking-changes, deprecations и новых возможностях с примерами before/after и инструкцией по миграции.
Чтобы обновить существующие пайплайны:
- Укажи в
ai-orchestrator.config.mjsполеlastMigratedVersionс версией, на которой ты сейчас. - Открой Claude Code в проекте и попроси: «обнови мои пайплайны под текущую версию
@infomaximum/ai-orchestrator». - Агент
pipeline-writer(его системный промпт —node_modules/@infomaximum/ai-orchestrator/agents/pipeline-writer.md) сам прочитает миграции, найдёт затронутые места и предложит правки. После твоего подтверждения он обновитlastMigratedVersion.
Если поля lastMigratedVersion нет — агент один раз спросит «обновляем под текущую или работаем как есть?». Ответ «как есть» отключает миграции до тех пор, пока поле не появится в конфиге.
Контракт агента
Каждый узел-агент в твоём пайплайне зовёт callAgent(...) — это запускает агента через адаптер провайдера (берётся из OrchestratorConfig.adapter или явно из callAgent.adapter) с system-prompt из .md-файла, путь к которому ты передаёшь в agentPath (обязательно абсолютный — собирай через import.meta.url). Дефолта нет — без adapter в конфиге imo run упадёт сразу с ошибкой loadConfig.
См. секцию 8.5 в docs/writing-pipelines.md для деталей про адаптеры провайдеров.
Агент сообщает вердикт двумя маркерами в финальном ответе:
<verdict>ok</verdict>
<summary>одна строка про результат</summary>Маркер <verdict> обязателен, <summary> — желателен. Маркер ловится из stream-json события result (это финальный текст агента, его модель выдаёт всегда). Если маркеров <verdict> несколько — побеждает последний.
Резервный канал — записать тот же вердикт в <sessionDir>/verdict-<stateName>.json:
{ "verdict": "ok", "summary": "одна строка про результат" }Файл нужен только если по какой-то причине маркер в ответе не появился. callAgent сначала ищет маркер, потом — лезет в файл. verdict обязан быть одним из validVerdicts, которые ты передал в callAgent. Несоответствие в маркере — отказ без fallback на файл (агент явно выдал «не тот» вердикт). Несоответствие в файле и нет маркера — узел упадёт с ошибкой, сессия пометится failed. Это весь интерфейс между движком и агентом. Что агент делает внутри (читает и правит файлы, запускает тесты, что-то ещё) — движку неважно.
Что лежит в директории сессии
<sessionsDir>/<id>/ после прогона:
meta.json # id, pipeline, args, status, startedAt, updatedAt
verdict-<stateName>.json # резервный канал вердикта (если агент не дал маркер в финальном ответе)
logs/<stateName>-<round>.log # события адаптера (для отладки упавшего агента)
logs/<stateName>-<round>.codex-system.md # [только codexAdapter] system-prompt, переданный в codex exec
logs/<stateName>-<round>.codex-final.txt # [только codexAdapter] финальный ответ модели (--output-last-message)
checkpoints/<id>/_default/... # JSON-чекпоинты LangGraph (для imo resume)
<артефакты от агентов> # plan.md, diagnosis.md и что ещё пишет твой агентКогда что-то упало — смотри сначала в logs/<state>-<round>.log (события адаптера — там видно, был ли маркер <verdict> в финальном ответе). При использовании codexAdapter дополнительно проверь logs/<state>-<round>.codex-final.txt — там чистый финальный ответ модели. Файл verdict-<state>.json появляется только в fallback-ситуации.
Создание своего пайплайна
Важно: пайплайны пишутся на JavaScript с расширением
.mjsи обязательной директивой// @ts-checkв первой строке + JSDoc-типизацией. TypeScript не используется — файлы загружаются напрямую черезimport()без шага сборки. Расширение.mjsгарантирует ESM-режим независимо от"type"вpackage.jsonпотребителя. См. полное руководство ниже.
Полное руководство — docs/writing-pipelines.md. Содержит:
- ментальную модель и контракты,
- 4 типа узлов с примерами (agent / human-gate / routing / shell),
- conditional edges, verdict-протокол, идемпотентность для resume,
- полный рабочий шаблон пайплайна (≈80 строк, копируется и адаптируется),
- best practices, антипаттерны, чек-лист перед пушем,
- шпаргалка по JSDoc-типизации (typedef, @type, @param, inline cast).
После npm install @infomaximum/ai-orchestrator файл доступен по пути
node_modules/@infomaximum/ai-orchestrator/docs/writing-pipelines.md —
передай его LLM-агенту, и тот сможет писать пайплайны без обращения к исходникам.
Также imo init создаёт агента imo-pipeline-writer в .claude/agents/ — Claude Code может его вызвать командой типа «напиши пайплайн который делает X», агент сам прочитает руководство и сгенерирует .mjs-файл.
Параллельные агенты
Если несколько агентов могут работать одновременно, движок предоставляет две публичные builder-функции из @infomaximum/ai-orchestrator/runtime:
addParallelAgents— статический fan-out: ветки и их агенты известны на этапеpipeline.build(). Подходит для «запусти ревьюера, тестировщика и линтера параллельно».addDynamicFanOut+makeRetryRouter— динамический fan-out: количество и состав веток выясняется в runtime по результату узла-исследователя. Подходит для «найди все библиотеки в проекте и проанализируй каждую».
Обе записывают сбой ветки в BranchResult.error — падение одной ветки не отменяет выполнение остальных. Прогресс рендерится через общий tracker, а повтор конкретной ветки делается через conditional edge от merge-узла.
Подробности, ограничения LangGraph 1.x, полные примеры и подводные камни — в docs/writing-pipelines.md секции 4.5 и 4.6.
Минимальный пример
// configs/ai-pipelines/my-pipeline.mjs
// @ts-check
import path from "node:path";
import { fileURLToPath } from "node:url";
import { Annotation, END, START, StateGraph } from "@langchain/langgraph";
import { callAgent } from "@infomaximum/ai-orchestrator/runtime";
const here = path.dirname(fileURLToPath(import.meta.url));
const State = Annotation.Root({
args: Annotation({ reducer: (_, b) => b, default: () => "" }),
sessionDir: Annotation({ reducer: (_, b) => b, default: () => "" }),
rounds: Annotation({
reducer: (a, b) => ({ ...(a ?? {}), ...(b ?? {}) }),
default: () => /** @type {Record<string, number>} */ ({}),
}),
lastFeedback: Annotation({ reducer: (_, b) => b, default: () => "" }),
});
/** @typedef {typeof State.State} StateType */
/** @type {import("@infomaximum/ai-orchestrator").PipelineDefinition<StateType>} */
export const myPipeline = {
id: "my-pipeline",
build: (deps) => {
return new StateGraph(State)
.addNode(
"step1",
/** @param {StateType} state */
async (state) => {
const round = (state.rounds.step1 ?? 0) + 1;
await callAgent({
agentPath: path.join(here, "agents/implementer.md"),
prompt: `... ${state.args} ...`,
sessionDir: state.sessionDir,
stateName: "step1",
round,
validVerdicts: ["ok"],
adapter: deps.adapter, // передавай adapter из deps для поддержки глобальной подмены
});
return { rounds: { step1: round } };
}
)
.addEdge(START, "step1")
.addEdge("step1", END)
.compile({ checkpointer: deps.checkpointer });
},
};Локаль для $EDITOR (важно для UTF-8)
Human-gate с requiresFeedback открывает $EDITOR (по умолчанию nano/vi). Если LANG/LC_ALL не UTF-8 — кириллица в редакторе будет отображаться некорректно. Поправь:
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8или в Dockerfile:
RUN apt-get install -y locales && \
sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen && \
locale-gen en_US.UTF-8 && \
update-locale LANG=en_US.UTF-8
ENV LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8Лицензия
MIT
