# 03 — Multi-user chat с mention-detection Точка 1 из планов. Один `BaseAgent` обслуживает N пользователей. Отвечает только когда addressed (mention или admin-команда). ![Multi-user chat](./03-multi-user-chat.svg) PlantUML source (для редактирования; требует Graphviz `dot` для рендеринга): ```plantuml @startuml multi-user-chat skinparam componentStyle rectangle skinparam participantPadding 15 skinparam boxPadding 10 title Multi-user chat с mention-detection (точка 1 из планов) ' --- Участники --- actor "User A" as UA actor "User B" as UB actor "User C\n(админ)" as UC participant "Telegram /\nSlack /\nMatrix" as Channel participant "AgentRuntime\n(BaseAgent)" as Runtime participant "MentionDetector" as Detector participant "SoulProvider" as Soul participant "MemorySystem\n(MdMemory)" as Memory participant "LlmBackend\n(LiteLlm)" as Llm ' --- Сценарий --- UA -> Channel : "@bot, что нового?" UB -> Channel : "люблю котов" UC -> Channel : "/bot status" Channel -> Runtime : событие чата ' --- Внутри Runtime --- Runtime -> Detector : isMentioned(message, botName) note right of Detector variants: - SimpleMentionDetector (regex: @bot) - LlmMentionDetector (mini-classifier) - AdminCommandDetector (/command) end note Detector --> Runtime : MatchResult{isMentioned, isCommand} alt isMentioned или isCommand Runtime -> Soul : read() Runtime -> Memory : prefetch(query, topK) Runtime -> Llm : send(system + history + memory + user) Llm --> Runtime : response + tool_calls Runtime -> Memory : save(decision) Runtime --> Channel : ответ в нужный канал/thread else NOT mentioned и NOT command Runtime -> Runtime : drop (если не админ) note right Не отвечаем, но возможно: - запоминаем факт (memory-only update) - summary на long conversation end note end @enduml ``` ## Ключевые модули (что нужно будет добавить) ### `MentionDetector` interface ```kotlin interface MentionDetector { data class Result( val isMentioned: Boolean, val isAdminCommand: Boolean, val isPrivateMessage: Boolean, // DM — всегда отвечаем ) fun detect(message: ChatMessage, botName: String): Result } ``` Имплементации: - `SimpleMentionDetector` — regex `@bot`, `/command` (дешёво, latency ~0) - `LlmMentionDetector` — маленькая классификация через тот же LLM (точнее, но +1 LLM-вызов на каждое сообщение) - `HybridMentionDetector` — fast regex → fallback на LLM только если ambiguous ### `ChatAdapter` interface ```kotlin interface ChatAdapter { val channel: String // "telegram" / "slack" / "matrix" suspend fun listen(onMessage: (ChatMessage) -> Unit): Job suspend fun reply(messageId: String, text: String, threadId: String? = null) suspend fun isAdmin(userId: String): Boolean } ``` Имплементации per platform. Каждая адаптирует формат platform → `ChatMessage`. ### Конфигурация builder'а ```kotlin agentBuilder { llm(...) storage(...) memory(...) soul(...) background { ... } chat { mentionDetector = HybridMentionDetector(regex = "@bot|@Agent", llmClassifier = false) chatAdapter = TelegramChatAdapter(token = "...") // На каждое сообщение: // 1. mentionDetector.detect() // 2. если isMentioned || isAdminCommand || isPrivate → process // 3. иначе — опционально memory-only save (тихий режим) } } ``` ## Что это даёт - Один `BaseAgent` обслуживает чат целиком (один LLM, одна память — общий контекст команды) - `@bot` — explicit invocation, не «agent отвечает на всё подряд» - `/bot status` / `/bot clear-memory` — admin-команды (отдельный канал, без LLM) - DM — всегда отвечает (это личное обращение) - В групповом чате без mention — agent может **молча учить** (memory update без ответа). Полезно для «запомнил что Вася любит котов». ## Текущее состояние vs целевое ⏳ Ничего из этого нет. Сейчас `:standalone` — это HTTP API, к которому подключаются clients. Для multi-user chat нужен новый `:chat-adapter-telegram` (или -slack / -matrix) модуль + `MentionDetector` interface.