Новый модуль :integrations-telegram, опциональный «плагин» для standalone:
* TelegramBridgeComponent — Component-имплементация, устанавливается
в agent через agent.install(...) только при заданном
AGENTIK_TELEGRAM_TOKEN. Нет токена — нет polling'а, classpath не
содержит pw.binom.telegram.* (factory возвращает AutoCloseable,
а Component-объект создаётся внутри factory и утекает в
standalone только через agent-api).
* Long-polling getUpdates?timeout=N — не webhook, не нужен публичный URL.
* chatId ↔ ConversationId маппинг в общей SQLite-БД standalone'а
(таблица tg_chat_map); persistent=true переиспользует conv_id,
persistent=false — каждый Telegram-message = свежий temp-диалог.
* Group-чаты и /new стартуют временный диалог, не пишут в мапу.
* Tool-call показывает «⚙️ обрабатываю…» через sendChatAction(typing)
по onlineOutbox.onlineEvents.
* Outbound streaming через editMessageText с guard'ом «message is not
modified»; End с пустым телом → «(empty response)».
* Ошибки conv.send — одной строкой в чат, polling не падает.
* uninstall отменяет scope, polling и stream-джобы корректно
останавливаются.
Конфиг — через env (AppConfig.fromEnv):
* AGENTIK_TELEGRAM_TOKEN (default пусто → модуль не активен)
* AGENTIK_TELEGRAM_POLLING_TIMEOUT (default 30s, ок 25–35)
* AGENTIK_TELEGRAM_PERSISTENT (default true)
Зависимость: наша in-house либа caffeine-mgn/telegramClient
(pw.binom.telegram:telegramClient:1.0.0-SNAPSHOT из mavenLocal).
standalone/Main.kt:
* integrationsScope (SupervisorJob + Dispatchers.Default) создаётся
до server'а; tgClient закрывается shutdown-hook'ом ДО agent.close()
и integrationsScope.cancel() — порядок важен, иначе polling
long-poll не прерывается и JVM висит на выходе.
Тесты (8/8 ✅):
* первый входящий текст → persistent conv + conv.send
* повтор из того же chat → тот же conv
* non-persistent → каждый message = новый temp-conv
* typing-индикатор на каждом сообщении
* outbound streaming → 3 editMessage подряд
* End с пустым телом → draft заменяется на «(empty response)»
* ошибка conv.send → одна строка в чат, polling не падает
* uninstall → polling остановлен, новые push'и не обрабатываются
standalone/README.md — добавлен раздел «Telegram-интеграция» с env-таблицей,
описанием поведения и инструкцией по включению.
12 KiB
:standalone — single-jar HTTP-сервер со всеми транспортами
Что это
Главный исполняемый модуль проекта — single-jar HTTP-сервер с:
- A2A transport на
POST /(JSON-RPC) +GET /.well-known/agent-card.json. :prototransport наPOST /agentik/*(HTTP+JSON+SSE) — наш stateful.- Embedded LLM backend:
GOOGLE(LiteRT) илиOPENAI-совместимый (vLLM, LiteLLM, OpenAI API). - SQLite persistence через
:storage-sqlite. - Memory backend:
md(файловый) илиvector(SQLite+JVector+ HTTP/SIGLIP-embeddings). - Skills из
~/.agentik/skills/*.md. - SOUL из
~/.agentik/SOUL.md. - Background подпроцессы: рефлексия, skill-mining, memory-reviewer.
Решает: даёт пользователю один JAR (10–250 МБ), который запускается
через java -jar agentik-0.1.0-all.jar, и поднимает сразу все
транспорты, которые другие системы могут хавать.
Как запустить
Требования
- JVM 21+.
- (Опционально) CUDA-устройство для
:backend=google(LiteRT). - (Опционально) LM через OpenAI-совместимый endpoint (vLLM / Ollama
/ OpenAI) для
:backend=openai.
Запуск из готового fatjar
java --enable-native-access=ALL-UNNAMED \
-jar agentik-0.1.0-all.jar
С дефолтами — встроенный SQLite, OpenAI-compatible backend на
http://localhost:8001/v1, порт 8080.
Запуск через Gradle (dev)
./gradlew :standalone:run
pull-model subcommand (для LiteRT)
# Сначала скачать модель под 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
Скачивает https://static.binom.pw/models/gemma-4-E2B-it.litertlm
(2.5 ГБ, с Range-resume). Поддерживает override через
AGENTIK_GOOGLE_MODEL_URL и verify через
AGENTIK_GOOGLE_MODEL_SHA256_URL.
Переменные среды
Полный список — общий для всего :standalone-процесса:
| Env | Default | Что делает |
|---|---|---|
AGENTIK_PORT |
8080 |
Порт HTTP-сервера |
AGENTIK_DB_PATH |
./agentik.db |
Путь к SQLite |
AGENTIK_TOKEN |
(пусто) | Bearer-токен для HTTP-фасада /agentik. Пусто — авторизация выключена |
AGENTIK_A2A_TOKEN |
(пусто) | Bearer-токен для A2A-фасада /a2a. Пусто — авторизация выключена (независим от AGENTIK_TOKEN) |
AGENTIK_AGENT_ID |
agentik |
ID агента (для multi-instance) |
AGENTIK_LLM_BACKEND |
openai |
openai или google |
AGENTIK_LLM_MODEL |
(выбирается по backend) | Имя модели |
AGENTIK_LLM_API_URL |
http://localhost:8001/v1 |
Endpoint для OpenAI-compatible |
AGENTIK_LLM_API_KEY |
no-key-needed |
Auth header |
AGENTIK_LLM_CONTEXT_TOKENS |
115000 |
Сколько токенов остаётся модели |
AGENTIK_GOOGLE_MODEL_PATH |
/root/gemma-4-E2B-it.litertlm |
Путь к .litertlm файлу |
AGENTIK_GOOGLE_MODEL_URL |
https://static.binom.pw/models/gemma-4-E2B-it.litertlm |
Откуда скачивать |
AGENTIK_GOOGLE_MODEL_SHA256_URL |
— | Если задан — verify по SHA-256 |
AGENTIK_AUTO_DOWNLOAD_MODEL |
0 |
1 = скачать модель если её нет |
AGENTIK_MEMORY_BACKEND |
vector |
md, vector или off |
AGENTIK_EMBEDDING_BACKEND |
http |
http или siglip (только для vector) |
AGENTIK_EMBEDDING_API_URL |
http://localhost:8001/v1 |
Endpoint для эмбеддингов |
AGENTIK_EMBEDDING_MODEL |
text-embedding-3-small |
Имя embedding-модели |
AGENTIK_SOUL_PATH |
~/.agentik/SOUL.md |
Путь к SOUL.md |
AGENTIK_SKILLS_DIR |
~/.agentik/skills/ |
Каталог SKILL.md |
AGENTIK_MEMORY_DIR |
~/.agentik/memory/ |
Каталог для md-памяти |
AGENTIK_TOOLSETS_DEFAULT |
memory,skills,files,web |
Включённые тулы |
AGENTIK_DEBUG |
0 |
1 = verbose logging |
AGENTIK_TELEGRAM_TOKEN |
(пусто) | Bot token от @BotFather. Пусто — Telegram-интеграция выключена, standalone работает ровно как без неё |
AGENTIK_TELEGRAM_POLLING_TIMEOUT |
30 |
Long-poll timeout в секундах. Bot API держит HTTP-соединение открытым до N секунд, возвращает 200 OK с пустым массивом если апдейтов нет. Меньше — больше polling-нагрузки, больше — дольше «висит» соединение. 25–35 обычно ок |
AGENTIK_TELEGRAM_PERSISTENT |
true |
true → диалог из чата сохраняется в SQLite как обычный ConversationRecord и переживает рестарт. false → каждый Telegram-message стартует свежий temp=true диалог (как «temporary chat» в Android-клиенте) |
Значения читаются через AgentikConfig.fromEnv() в :standalone/.../Main.kt.
Telegram-интеграция
Опциональный «мост» между Telegram-чатом и standalone-агентом.
Реализован отдельным модулем :integrations-telegram и подключается
ТОЛЬКО при заданном AGENTIK_TELEGRAM_TOKEN (нет токена — нет polling'а,
нет зависимости в classpath при выключенном флаге, см. ниже).
Что умеет
- Long-polling через Bot API (
getUpdates?timeout=N). Не webhook — не нужен публичный URL, всё работает за NAT. - Маппинг
chatId ↔ ConversationIdв общей SQLite-БД standalone'а (таблицаtg_chat_map). Один Telegram-чат = один диалог с агентом; сообщения из чата стримят ответы обратно черезeditMessageText. - Group-чаты (
group,supergroup) и команда/newстартуют новый временный диалог (ConversationRecord.isTemporal = true) — не пишут в мапу, удаляются при следующем/new. - Tool-call показывает «⚙️ обрабатываю…» через
sendChatAction(typing)— включается наonlineOutbox.onlineEventsпока обрабатывается ход. - Ошибки агента стримятся в чат одной строкой, polling не падает.
Как включить
-
Создать бота через
@BotFather, скопировать токен. -
Запустить standalone с токеном:
AGENTIK_TELEGRAM_TOKEN=123456:ABC-DEF... \ java --enable-native-access=ALL-UNNAMED \ -jar agentik-0.1.0-all.jar -
Написать боту в личку (или добавить в группу). В логе standalone'а появится:
telegram: enabled (polling timeout=30s, persistent=true) -
Ответы приходят стримингом —
editMessageTextобновляет одно и то же сообщение каждые ~1с пока агент генерирует.
Persistent vs temporary
| Сценарий | AGENTIK_TELEGRAM_PERSISTENT |
Поведение |
|---|---|---|
| Личка с ботом | true (default) |
Один диалог на пользователя, переживает рестарт, /new стартует новый |
| Личка с ботом | false |
Каждое сообщение — свежий temp диалог |
| Group-чат | (любое) | Всегда temp — каждый message может быть от другого юзера, диалоги не персистятся |
/new |
(любое) | Стартует новый temp диалог (для persistent=true это закрывает старый ConversationRecord) |
Технические заметки
- Либа — своя,
caffeine-mgn/telegramClient(pw.binom.telegram:telegramClient), опубликованная черезpublishToMavenLocal. Артефактpw.binom.telegram.*НЕ утекает в classpath standalone'а — host-сайд видит только фабрикуcreateTelegramBridgeComponent, которая возвращаетAutoCloseable. - Polling/stream-джобы живут на отдельном
integrationsScope. В shutdown-hook'е он отменяется доagent.close()— иначе long-poll держит соединение доAGENTIK_TELEGRAM_POLLING_TIMEOUTи блокирует JVM-выход. - Один бот на standalone (таблица
tg_chat_mapглобальная). Если когда-нибудь захочется multi-agent + multi-bot — мапу надо будет разводить по агентам.
Эндпоинты
| Метод | Путь | Transport | Описание |
|---|---|---|---|
GET |
/health |
любой | health-check ({"ok":true}) |
POST |
/ |
A2A | JSON-RPC message/send, tasks/get, tasks/cancel |
GET |
/.well-known/agent-card.json |
A2A | Discovery |
POST |
/agentik/conversations |
:proto | Создать диалог |
GET |
/agentik/conversations |
:proto | Список диалогов |
GET |
/agentik/conversations/:id |
:proto | Snapshot |
GET |
/agentik/conversations/:id/messages |
:proto | История |
POST |
/agentik/conversations/:id/send |
:proto | Send (SSE) |
GET |
/agentik/conversations/:id/events |
:proto | Live-events (SSE) |
POST |
/agentik/conversations/:id/interrupt |
:proto | Прервать |
POST |
/agentik/conversations/:id/rename |
:proto | Переименовать |
DELETE |
/agentik/conversations/:id |
:proto | Удалить |
Тесты
./gradlew :standalone:jvmTest # unit-тесты
./gradlew :standalone:integrationTest # integration (Testcontainers)
./gradlew :standalone:shadowJar # → build/libs/agentik-0.1.0-all.jar
Известные ограничения
- vLLM не поддерживает cancel-inference (
interrupt()только закрывает client SSE-socket; бэкенд всё равно генерирует до конца). - A2A JSON discriminator —
"kind"(text/file/data), а не"type". См.A2aJsonв:standalone. - SSE в не-TTY ssh закрывается на default Ktor timeout.
Текущий статус
Production-ready. Все KMP-модули проекта интегрированы. Полный
manual-test checklist смотрите в MANUAL-TESTS.md
или MANUAL-TESTS.md в корне.
Где скачать
- Source:
git clone https://git.binom.pw/subochev/agentik - Fatjar: Gitea CI artifacts (через
.gitea/workflows/release.ymlна tagv*) или собирается через./gradlew :standalone:shadowJar.
Версии
Все gradle/libs.versions.toml. Поднять версию → release через
git tag v0.2.0 && git push --tags → CI собирает все KMP-таргеты
публикует артефакты.