Files
agentik/docs/diagrams/02-agent-composition.md
T
subochev 25771a0c33
ci / JVM build + tests (push) Failing after 1m57s
docs(diagrams): agent architecture overview with pre-rendered SVG
PlantUML diagrams for future agent architecture (Android, multi-user
chat, sub-agents, A2A):
- 01-module-layers.md — целевая модульная структура
- 02-agent-composition.md — AgentBuilder DSL + MemoryBackend.exposesTools()
- 03-multi-user-chat.md — mention-detection sequence
- 04-sub-agents.md — spawnChild + Flow<SubAgentEvent> + A2A
- 05-android-stack.md — что меняется на Android vs Standalone

Каждый .md включает пред-рендеренный SVG (показывается во всех markdown
viewers без PlantUML plugin) + PlantUML source в code block (для
редактирования). SVG нужен потому что PlantUML требует Graphviz dot
для рендеринга — без него IntelliJ/VSCode выдают ошибку.

Регенерация SVG после правки PlantUML-source:
  docker run --rm -v "$PWD:/work" plantuml/plantuml -tsvg /work/docs/diagrams/*.md
2026-09-18 20:02:41 +03:00

4.7 KiB

02 — Agent Builder: композиция (целевое API)

Как AgentBuilder собирает BaseAgent из компонентов. Memory backend сам объявляет свои tools — builder их авто-мержит. BackgroundScheduler подписан на события, не interval-poll.

Agent Composition

PlantUML source (для редактирования; требует Graphviz dot для рендеринга):

@startuml agent-composition
skinparam componentStyle rectangle

title Agent Builder — композиция (целевое API)

' --- Builder ---
rectangle "AgentBuilder" as Builder {
    rectangle "llm: LiteLlm (обязательно)" as Llm
    rectangle "storage: StorageBundle (обязательно)" as Storage
    rectangle "memory: MemorySystem (обязательно)" as Mem
    rectangle "soul: SoulProvider (default NoopSoul)" as Soul
    rectangle "tools: List<NamedTool> (авто-сборка из backends)" as Tools
    rectangle "background: BackgroundConfig (default EmptyBg)" as Bg
    rectangle "toolset: List<ToolsetContribution> (default empty)" as Ts
}

' --- Backends with their tool side-effects ---
rectangle "MemoryMd" as MdMem {
    interface "MemorySystem" as MemSys
    interface "List<NamedTool>" as MdTools
    note right
        MemoryMd.exposesTools() →
        memory_save / memory_read /
        memory_list / memory_delete
    end note
}

rectangle "McpRegistry" as McpReg {
    interface "List<NamedTool>" as McpTools
    note right
        McpRegistry.namedTools →
        server__tool1, server__tool2,
        ...
    end note
}

rectangle "BackgroundEvents" as Events {
    interface "MutableSharedFlow<CompactionEvent|ToolCallEvent|LifecycleEvent>" as Flow
    note right
        Эмитится из:
        - CompactionCoordinator
        - ToolDispatcher
        - ConversationLoop.close()
    end note
}

rectangle "BackgroundScheduler" as Sched {
    interface "policy: trigger + debounce" as Policy
    note right
        Подписан на Events.
        НИКАКОГО interval-polling.
    end note
}

' --- Получаемый Agent ---
rectangle "BaseAgent\n(impl: ConversationLoop)" as Agent {
    rectangle "send / interrupt / events" as API
    rectangle "BackgroundScheduler\nподписка" as Sub
}

' --- Стрелки зависимостей ---
Builder --> Llm
Builder --> Storage
Builder --> Mem
Builder --> Soul
Builder --> Tools
Builder --> Bg
Builder --> Ts

MdMem --> Mem : implements
MdMem --> MdTools : exposes

McpReg --> McpTools : exposes

Bg --> Events : subscribes-to
Bg --> Sched : holds

Tools <-- MdTools : auto-merge
Tools <-- McpTools : auto-merge

Builder --> Agent : build()
Agent --> API
Agent --> Sub

@enduml

Целевой Kotlin DSL

val agent = agentBuilder {
    // Обязательные
    llm(OpenAiLlm.fromEnv())           // или LitertAndroid.onDevice(context)
    storage(SqliteStorage(path))         // или SqliteStorage.android(context)
    memory(MemoryMd(root = "~/memory")) // или MemoryVector(embedding = HttpEmbedding(...))

    // Опциональные
    soul(FileSoul("~/SOUL.md"))          // или HttpSoul(url), NoopSoul()
    background {
        // triggers: OnClosing (reflection+mining), OnCompaction(minTurns=10, mining=true)
        // event-driven, не interval
    }
    tools {
        // memoryMd.exposesTools() + mcpRegistry.namedTools авто-подцепляются
        +FileReadTool(root = "/data")
    }
    toolset {
        +MemoryToolsToolset(memoryMd)
    }
}.build()

Ключевые решения

  • MemoryBackend.exposesTools() — backend декларирует свои tools. Не «подставить любой backend», а «backend сообщает что он умеет». Это убирает coupling «какие tools совместимы с какими backends».
  • Builder требует только llm + storage + memory как обязательные. Всё остальное — опционально с разумными default'ами (NoopSoul, EmptyBackground, empty toolset).
  • BaseAgent — реализация ConversationLoop через builder. Конструктор принимает все нужные компоненты. Один и тот же BaseAgent в :standalone, :agentik-cli, :agentik-android — отличается только wiring через builder.
  • BackgroundScheduler подписан на BackgroundEvents — это даёт event-driven по умолчанию. OnEvery(n) interval-режим — опциональный fallback (не default).