Standalone refactor — modularity + correctness improvements after
STANDALONE-REVIEW findings. Touches ~30 files. Build green, 178 tests pass.
(1) Module extractions — generic components out of :standalone:
• :llm-tools (new KMP module, package pw.binom.agentik.llm.tools)
- LlmReflector, SkillMiner, LlmMemoryReviewer, LiteLlmContextCompactor
- Parsers: ReflectionParser, SkillMiningParser, ReviewDecisionParser
- Prompts: ReflectionPrompts, SkillMiningPrompts, ReviewPrompts
• :mcp-bridge (new JVM module, package pw.binom.agentik.mcp.bridge)
- McpConfig, McpRegistry, McpLiteToolAdapter
• NamedTool moved from :standalone to :agent-toolsets/commonMain
- Generic (name + LiteTool) wrapper, used by both :mcp-bridge
and :standalone's tool dispatcher
:standalone loses ~1400 lines, depends on the two new modules.
(2) Background work → event-driven (no more interval-polling):
• New :standalone/agent/BackgroundEvents.kt — internal event bus:
- ToolCallEvent.Succeeded/Failed (emitted by ToolDispatcher after invoke)
- CompactionEvent.Triggered (emitted by CompactionCoordinator pre-delete)
- ConversationLifecycleEvent.Closing (emitted by ConversationLoop.close)
• BackgroundScheduler rewritten as event subscriber:
- On Closing: final reflection + skill mining (last-chance extraction)
- On Compaction (turnsToDelete > 10): skill mining (debounced 60s)
- On ToolFailure x2 in 60s window: reflection (debounced 5min)
- Dropped: maybeScheduleReview/Reflection/SkillMining (interval-based)
- Dropped config: memoryReviewInterval, reflectionInterval, skillMiningInterval
• ToolDispatcher emits ToolCallEvent after each invoke.
• CompactionCoordinator emits CompactionEvent before workingMemory.compact().
• ConversationLoop.close() emits Closing BEFORE agentScope.cancel() so the
subscription gets to run final reflection/mining.
Net effect: typical 30-turn conversation runs ~38 LLM calls (was: 30 main +
3 review + 3 reflection + 2 mining). With event-driven, review/mining only fire
when their triggers actually make sense (compaction about to delete, or
conversation closing).
(3) AppConfig single source of truth:
• Replaces AgentikConfig + LlmConfig.fromEnv + McpConfig.fromEnv with one
AppConfig.fromEnv() that reads all ~25 env vars in a single pass.
• Sections: AgentSection, LlmSection, McpSection, MemorySection,
EmbeddingSection, ReflectionSection, SkillMiningSection, DebugSection.
• OPENAI_CONTEXT_WINDOW / AGENTIK_GOOGLE_CONTEXT_WINDOW no longer
read twice (was a bug per STANDALONE-REVIEW E3).
(4) Other fixes inherited from earlier waves:
• Hardening — size caps on user-input boundaries:
MAX_MEMORY_CONTENT_LEN=32KB, MAX_SKILL_BODY_LEN=64KB,
MAX_MCP_CONFIG_BYTES=1MB, MAX_A2A_REPLY_LEN=10MB, MAX_PORT=65535,
blank-rejection in LlmConfig.requireEnv, URL/command validation.
• Single scope — :standalone/agent/ConversationLoop has one
agentScope (was: scope + backgroundScope).
• liteConvRef race fix — capture-then-use pattern replaces !!-after-read;
close() + runTurn.finally race on LiteConv JNI handled via
AtomicReference.getAndSet.
• SkillMiner.maxTurns / LlmReflector.maxTurns exposed as public (needed
by BackgroundScheduler for prompt sizing).
• Tests: MemoryWiringTest updated for new compaction-triggered review
behavior; all parser/test imports updated for new packages.
Test results: 178/178 in :standalone, 36/36 in :agent-toolsets — all green.
agentik
Локальный stateful LLM-агент с persistent-памятью, инструментами и
несколькими transport-фасадами (AG-UI, A2A, наш :proto).
Реализован на Kotlin Multiplatform, выполняется как single JVM-jar.
Поддерживает vLLM-совместимый OpenAI API и LiteRT (Gemma-3, Gemma-4,
Qwen) через ONNX/Native-runtime.
Что внутри
agentik/
├── proto/ stateful KMP protocol: Agent / Conversation / Message / Event
├── server/ Ktor-фасад → /agentik (HTTP+JSON+SSE)
├── client/ Ktor-клиент → тот же /agentik, с KMP-native
├── skills/ парсер SKILL.md / *.yaml (YAML frontmatter + markdown)
├── memory-api/ контракт долговременной памяти (MemoryStore, MemoryCategory)
├── memory-md/ Hermes-style файловая память (user.md / world.md / ...)
├── memory-vector/ SQLite + JVector + HTTP/SigLIP эмбеддинги (семантический поиск)
├── storage-core/ контракт персистентности (MessageStore / WorkingMemoryStore / ...)
├── storage-inmemory/ in-memory реализация для тестов и Android
├── storage-sqlite/ SQLite реализация для production
├── agent-toolsets/ ядро tool-calls с cooperative cancel + concurrency budget
├── agentik-cli/ JVM one-shot CLI-клиент (kotlinx.cli) к /agentik
├── ~~agentik-tui/~~ ~~Compose-for-Mosaic TUI-клиент (desktop)~~ — исключён 2026-09-17
└── standalone/ single-jar HTTP-сервер со всеми transport'ами и движками
Каждый подмодуль имеет собственный README.md с деталями
(см. "Модули" ниже).
Quickstart
1. Скачать fatjar
CI артефакты доступны на Gitea через GitHub Actions artifacts на tag-релизах, либо соберите из исходников:
git clone https://git.binom.pw/subochev/agentik
cd agentik
./gradlew :standalone:shadowJar
Результат: standalone/build/libs/agentik-0.1.0-all.jar (~10–250 МБ,
зависит от LLM-backend'а).
2. Запустить с OpenAI-compatible backend (vLLM / Ollama / OpenAI)
AGENTIK_LLM_BACKEND=openai \
AGENTIK_LLM_API_URL=http://192.168.88.135:8001/v1 \
AGENTIK_LLM_MODEL=Qwen3.8-27B-NVFP4 \
AGENTIK_LLM_CONTEXT_TOKENS=115000 \
java --enable-native-access=ALL-UNNAMED -jar agentik-0.1.0-all.jar
3. Запустить с локальной LiteRT-моделью (Gemma-4-E2B)
AGENTIK_LLM_BACKEND=google \
AGENTIK_GOOGLE_MODEL_PATH=/root/gemma-4-E2B-it.litertlm \
java --enable-native-access=ALL-UNNAMED -jar agentik-0.1.0-all.jar pull-model # скачать
java --enable-native-access=ALL-UNNAMED -jar agentik-0.1.0-all.jar # запустить
Больше деталей по env'ам — в standalone/README.md.
Подключиться
# CLI
java --enable-native-access=ALL-UNNAMED -jar agentik-cli-0.1.0-SNAPSHOT-all.jar --help
# curl
curl http://localhost:8080/health
Модули
- Запускаемые:
:standalone— single-jar HTTP-сервер.:agentik-cli— one-shot CLI-клиент (kotlinx.cli), JVM + 4 native.
- Библиотеки (контракты и реализации):
:proto— stateful KMP-протокол.:server— HTTP/SSE фасад:proto.:client— Ktor-клиент:server.:skills— парсер SKILL.md.:memory-api— контракт памяти.:memory-md— Hermes-style файл.:memory-vector— SQLite + JVector.:storage-core— контракт storage.:storage-inmemory— RAM-реализация.:storage-sqlite— SQLite production.:agent-toolsets— тулы и диспетчер.
Где смотреть версии
Каталог gradle/libs.versions.toml. Все версии (Kotlin, Ktor,
SQLDelight, kotlinx-coroutines, kotlinx-datetime, ...) сгруппированы
в секции [versions]; все dep-aliases — в секции [libraries].
Версия самого agentik (cм. <version> в nexus.pom) — тоже в
gradle.properties (через $AgentikVersion или env AGENTIK_VERSION).
На tag-релизе (например v0.2.0) — CI подставляет версию из
тега и публикует.
Публикация
./gradlew :<module>:publish → в caffeine (Nexus).
Параметры через:
binom.repo.url(http://<your-nexus>/repository/caffeine/)binom.repo.userbinom.repo.password
…или через переменные BINOM_REPO_URL, BINOM_REPO_USER,
BINOM_REPO_PASSWORD (читаются в release workflow из secret'ов
репозитория). Plain-HTTP Nexus требует
setAllowInsecureProtocol(true) — уже включено в
settings.gradle.kts.
CI/CD
Gitea Actions (https://git.binom.pw/subochev/agentik/actions):
.gitea/workflows/ci.yml— PR-build, прогон тестов, проверка shadowjar'ов..gitea/workflows/release.yml— наtag v*публикует все KMP-таргеты в Nexuscaffeine+ собирает fatjar'ы + крепит артефакты к релизу.
Что отличает от других агентских фреймворков
- Stateful protocol — сервер сам владеет диалогом; переписка не
пересобирается клиентом на каждый
send(в отличие от AG-UI). - Все три транспорта в одном процессе — AG-UI, A2A, наш proto. Один fatjar — три API.
- Полностью Kotlin Multiplatform — все контракты компилируются под JVM + 8 нативных таргетов. Можно встроить в iOS / Android / Desktop / CLI.
- Прерывание tool-calls сохраняется в working memory — нет потери контекста, если пользователь нажал Ctrl-C во время долгого tool-вызова.
Лицензия
Apache-2.0 — смотрите LICENSE.
Участие в проекте
PR-ы приветствуются. Не забывайте синхронизировать версии в
gradle/libs.versions.toml и обновлять per-module README при
изменении API.