subochev
adcb8f54d6
skills: каталог + ленивая загрузка read_skill
...
Пользовательские инструкции («навыки») живут в указанной папке
(AGENTIK_SKILLS_DIR), рекурсивно читаются при старте и попадают в
системный промпт в сжатом виде: только имя + краткое описание.
Полный текст модель подгружает по требованию, вызывая встроенный
инструмент read_skill(name).
*:skills
- SkillCatalog + SkillPrompt (commonMain): рендер секции системного
промпта; тело навыка в промпт не течёт.
- SkillParser.parseAuto(): теперь читает и opencode-стиль SKILL.md
(YAML frontmatter + markdown тело), и голый *.yaml/*.yml
(поля name, description, опц. body). parseOrThrow для strict-путей.
- SkillParseError.render(): человекочитаемое описание ошибки для
логов и диагностики.
- SkillLoader (jvmMain): рекурсивный обход папки, детерминированный
порядок (по пути), ошибки отдельных файлов не валят загрузку;
дубликаты имён → ошибка, выигрывает первый по пути.
* :standalone
- AgentikConfig.skillsDir + env AGENTIK_SKILLS_DIR.
- ChatAgent: параметр skills (SkillCatalog); системный промпт
автоматически дополняется секцией «## Навыки» и в working memory
сидится вместе с базовым промптом.
- При непустом каталоге в tools автоматически добавляется
SkillReadTool (имя read_skill) — модель может загрузить полный
текст навыка, как обычный LiteTool.
- Main.kt: загружает навыки и шумно логирует ошибки загрузки в stderr.
* docs
- STANDALONE.md: секция «Навыки (skills)», env-переменная в таблице.
- Формат SKILL.md (opencode frontmatter) + голый *.yaml/*.yml.
Тесты: :skills jvmTest 34, :standalone jvmTest 69 (новые — состав
системного промпта, регистрация read_skill, навыки не утекают в
промпт телом).
2026-09-14 00:25:22 +03:00
subochev
4c66947c25
standalone: add MCP client + tool-call loop
...
- Add mcp/McpConfig + McpRegistry wrapping io.modelcontextprotocol:kotlin-sdk-client 0.15.0
- Supports stdio (uvx/npx/python) and streamable HTTP transports
- Claude Desktop-compatible JSON config (AGENTIK_MCP_CONFIG)
- server__tool name prefix to avoid collisions between servers
- Add agent/NamedTool (name + LiteTool pair) and tools: List<NamedTool> on ChatAgent/ChatConversation
- Implement tool-call loop in ChatConversation.runTurn:
- delta.toolCalls -> emit ToolCall event -> persist audit -> execute tool
-> emit ToolResult -> persist -> liteConv.addToolResult(callId, name, result)
- separate tc-/tr- prefixes keep SQL PRIMARY KEY unique while toolCallId FK is preserved
- 8 McpConfig + 4 McpRegistry unit tests; +1 ChatAgentTest tool-loop test (44/44 total)
- e2e verified: real MCP fetch server (mcp-server-fetch) + litellm local/codding
-> LLM calls fetch__fetch, MCP exec, result fed back, conversation continues
docs/STANDALONE.md: drop 'no tools / no MCP' from §8; replace 'Подключить тул (v2)' stub
with full in-agent + MCP recipe and tool-loop algorithm in §7
2026-09-13 15:59:58 +03:00
subochev
9e5d61707d
standalone v1: dual-backend (openai + litert-google) with SQLDelight dual-log persistence
...
Replace EchoProtoAgent / EchoAgent / EchoA2aHandler placeholders with a real
stateful agent on top of SQLite (SQLDelight 2.3.2) and litert-api v6.
persistence (commonMain):
- ConversationStore / MessageStore / WorkingMemoryStore — three narrow
interfaces, all operations suspend, AutoCloseable.
- MessageRecord sealed: UserMessage / AssistantMessage (Body subtype),
ToolCall / ToolResult (audit-only), Summary / System (working-memory-only
synthetic). Snake-case @SerialName discriminators.
- WorkingMemoryEntry sealed: System / User(sourceMessageId) /
Assistant(sourceMessageId); sourceMessageId is null for System.
- Two-table dual-log model: append-only message audit + mutable
working_memory with monotonic order_idx.
SQLite (jvmMain):
- SQLDelight schema + SqliteConversationStore / SqliteMessageStore /
SqliteWorkingMemoryStore under src/jvmMain/sqldelight/.
- SqliteStores.open(path) / inMemory(); Schema.create gated on
sqlite_master probe for idempotency.
- All payload_json is the MessageRecord encoded as JSON; subtype-specific
fields avoid migrations.
agent (jvmMain):
- ChatAgent — stateful proto.Agent with live in-memory cache, lock-protected,
AgentEvent bus (Created/Deleted).
- ChatConversation — long-lived LiteConversation handle; created lazily on
first send from working_memory (system + initial messages), reused across
all subsequent turns (REQUIRED for litert-google KV-cache).
- Per turn: append User to audit + WM → sendStreamContents (wrapped in
transformWhile for litert-google-jvm 0.16.1 isDone workaround) → emit
AppendText deltas → append Assistant to audit + WM + touch conversation.
- isClosed flag so getConversation reconstructs after close.
llm (jvmMain):
- LlmConfig data class with LlmBackend enum (OPENAI / GOOGLE); fromEnv
parses AGENTIK_LLM_BACKEND and dispatches to backend-specific config.
- OpenAI: litert-openai, OpenAI-compatible endpoint, validated
baseUrl/apiKey/model.
- Google: litert-google (reflection-resolved pw.binom.litert.google
factory) on top of litertlm-jvm 0.16.1 native engine;
visionBackend/audioBackend = null (LiteRT-LM 0.16.1 binds encoder
graph even with null backend, but a model lacking encoder crashes;
null is the correct "don't bind" signal).
- foldSystemIntoFirstUser (default true for GOOGLE) folds system prompt
into the first user message to avoid chat template alternation issues.
build:
- Add sqldelight plugin + runtime + sqlite-driver + coroutines-extensions
to gradle/libs.versions.toml.
- litert-openai: implementation; litert-google: runtimeOnly (resolved via
reflection at runtime).
- KMP jvm executable via @OptIn(ExperimentalKotlinGradlePluginApi) +
jvm { binaries { executable { mainClass.set("...MainKt") } } }.
tests (jvmTest): 30 passing
- PersistenceTest (11): conversation upsert/list/cascade-delete/rename/
touch; message audit append/list; working-memory order preservation;
image-content payload roundtrip.
- ChatAgentTest (14): system-prompt seeding; persistent vs temp
persistence across SqliteStores reopen; multi-turn audit + WM growth;
interrupt of in-flight slow send; agentEvents Created/Deleted flow;
closed-conv reconstruct via getConversation.
- LlmConfigTest (6): env happy path, defaults, missing fields throw.
smoke tested e2e:
- openai backend against real llm.binom.pw/v1 (myopenai/local/codding)
— multi-turn dialogue persisted, kill -9 + restart survives.
- google backend against gemma-4-E2B-it.litertlm — multi-turn
("Hello there!" → "2 + 2 = 4"), KV-cache survives across turns,
SSE start→append_text*→end cleanly closes.
docs/STANDALONE.md updated for v1 architecture, dual-backend env table,
long-lived LiteConversation invariant, and litert-google-jvm 0.16.1
isDone-stream workaround.
2026-09-13 12:39:36 +03:00