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
5.3 KiB
04 — Sub-agents + A2A между агентами
Точки 2 и 3 из планов. Orchestrator-агент spawn'ит sub-агентов с изолированным контекстом. Независимые агенты общаются через A2A.
PlantUML source (для редактирования; требует Graphviz dot для рендеринга):
@startuml sub-agents-and-a2a
skinparam componentStyle rectangle
title Sub-agents + A2A между агентами (точка 2+3 из планов)
' --- Orchestrator ---
rectangle "OrchestratorAgent\n(BaseAgent + tools)" as Orch {
rectangle "ConversationLoop\n(main user)" as MainConv
}
' --- Sub-agent spawn ---
rectangle "subAgent(\n task: String,\n config: AgentConfig\n): Flow<SubAgentEvent>" as SpawnAPI
note right of SpawnAPI
Spawn API — НЕ отдельный модуль,
а convenience поверх BaseAgent:
val sub = agent.spawnChild(config) {
systemPrompt = "..."
tools = [ReadTool, WriteTool]
memory = EmptyMemory // изолированно
}
sub.events.collect { ... }
end note
' --- Дочерний агент (изолированный контекст) ---
rectangle "SubAgent\n(изолированный scope)" as Sub {
rectangle "ConversationLoop\n(child)" as SubConv
rectangle "backgroundScope\n(lifecycle scoped)" as SubBg
}
' --- A2A между независимыми агентами ---
rectangle "Agent A\n(BaseAgent)" as AgentA
rectangle "Agent B\n(BaseAgent)" as AgentB
rectangle "A2A Server\n(:a2a-server)" as A2ASrv
AgentA -> A2ASrv : POST /\n(application/json)
A2ASrv -> AgentB : dispatch(message)
AgentB --> A2ASrv : response
A2ASrv --> AgentA : SSE / JSON-RPC
' --- Стрелки ---
Orch -> SpawnAPI : calls
SpawnAPI -> Sub : creates with custom config
Sub -> SubBg : has its own
Orch -> Orch : main flow continues
Sub --> Orch : Flow<SubAgentEvent> emits\n(Started / ToolCalled / ToolResult /\nAssistantMessage / Done / Failed)
Orch -> A2ASrv : can also delegate to remote agent
@enduml
Sub-agents API
sealed interface SubAgentEvent {
data class Started(val taskId: String) : SubAgentEvent
data class AssistantMessage(val text: String) : SubAgentEvent
data class ToolCalled(val toolName: String, val args: JsonObject) : SubAgentEvent
data class ToolResult(val toolName: String, val result: String) : SubAgentEvent
data class Done(val taskId: String, val finalResult: String) : SubAgentEvent
data class Failed(val taskId: String, val error: String) : SubAgentEvent
}
interface BaseAgent {
// ... existing methods ...
/**
* Spawn дочерний агент с изолированным контекстом (memory, system prompt,
* tools). Возвращает Flow событий жизненного цикла + результата.
* Cancellation родителя НЕ отменяет sub-agent — sub-agent живёт до Done/Failed.
*/
fun spawnChild(config: SubAgentConfig): Flow<SubAgentEvent>
}
data class SubAgentConfig(
val systemPrompt: String,
val tools: List<NamedTool> = emptyList(),
val memory: MemorySystem = EmptyMemory(),
val model: LiteLlm? = null, // если null — делит LLM родителя
val maxTurns: Int = 10,
val timeoutMs: Long = 60_000,
)
Зачем изолированный scope
Sub-agent получает свою копию контекста, не делит memory с родителем. Это критично:
research_subagent— должен исследовать тему, не отвечать на основные сообщения пользователяsummarize_subagent— суммаризировать документ, не трогать основной диалогcode_review_subagent— ревьюить PR, не видеть разговор
Если нужно расшарить контекст — это explicit через sharedMemory: SharedMemoryHandle параметр, не default.
A2A между независимыми агентами
Уже есть :a2a-server модуль (см. standalone/build.gradle.kts — implementation(libs.a2a.server)). Использовался для AG-UI/A2A протокола в :standalone. Можно переиспользовать для межагентного общения.
Сценарий: orchestrator-agent не может сам решить задачу → делегирует remote-агенту через A2A → получает response → продолжает. Это уже работающая инфраструктура.
Текущее состояние vs целевое
✅ Уже есть:
:a2a-serverподключёнBaseAgent.spawnChild— не существует, ноConversationLoopуже умеет создавать изолированный scope через свойagentScope— нужна только обёртка
⏳ Не сделано:
SubAgentConfig+Flow<SubAgentEvent>APIEmptyMemory(null-object для изолированного scope)- Lifecycle management (parent dies → child должен complete or be cancelled?)
- Сериализация sub-agent state для отладки (event log)