- matrix-gateway: POST /internal/matrix/presence/online endpoint - usePresenceHeartbeat hook with activity tracking - Auto away after 5 min inactivity - Offline on page close/visibility change - Integrated in MatrixChatRoom component
573 lines
25 KiB
Markdown
573 lines
25 KiB
Markdown
# 📊 Архітектура потоків даних Monitor Agent
|
||
|
||
**Дата:** 2025-11-23
|
||
**Статус:** ✅ Всі потоки даних працюють
|
||
|
||
---
|
||
|
||
## 🎯 Загальна архітектура
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ ДЖЕРЕЛА ДАНИХ │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ 1. WebSocket події (ws://localhost:8899/ws/events) │
|
||
│ - node_event, agent_event, system_event, project_event │
|
||
│ │
|
||
│ 2. Project Changes (projectChangeTracker) │
|
||
│ - file, config, service, agent, deployment, git changes │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ ОБРОБКА ТА ЗБЕРЕЖЕННЯ │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ 1. Загальний Monitor Agent (DAARION) │ │
|
||
│ │ - Отримує ВСІ події з усіх джерел │ │
|
||
│ │ - Генерує повідомлення через mistral-nemo:12b │ │
|
||
│ │ - Зберігає в загальну пам'ять: monitor │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ 2. Memory Service (PostgreSQL) │ │
|
||
│ │ │ │
|
||
│ │ Подвійне збереження: │ │
|
||
│ │ ├── Специфічна пам'ять: │ │
|
||
│ │ │ - monitor-node-{node_id} │ │
|
||
│ │ │ - monitor-microdao-{microdao_id} │ │
|
||
│ │ │ │ │
|
||
│ │ └── Загальна пам'ять: │ │
|
||
│ │ - monitor (ВСІ події з усіх нод та мікроДАО) │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ ВІДОБРАЖЕННЯ В ЧАТАХ │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ MonitorChat (Глобальний) │ │
|
||
│ │ - Показує ВСІ події (не фільтрує) │ │
|
||
│ │ - Розташований на всіх сторінках │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ NodeMonitorChat (Кабінети НОД) │ │
|
||
│ │ - Фільтрує події по node_id │ │
|
||
│ │ - Показує тільки події для конкретної НОДИ │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ MicroDaoMonitorChat (Кабінети мікроДАО) │ │
|
||
│ │ - Фільтрує події по microdao_id │ │
|
||
│ │ - Показує тільки події для конкретного мікроДАО │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ DaarionMonitorChat (Кабінет DAARION) │ │
|
||
│ │ - Показує ВСІ події (агрегація) │ │
|
||
│ │ - Аналогічно MonitorChat │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 📥 1. Надходження даних до загального Monitor Agent (DAARION)
|
||
|
||
### Джерела даних:
|
||
|
||
#### A. WebSocket події (`useMonitorEvents`)
|
||
|
||
**Файл:** `src/hooks/useMonitorEvents.ts`
|
||
|
||
```typescript
|
||
// WebSocket підключення
|
||
const ws = new WebSocket('ws://localhost:8899/ws/events');
|
||
|
||
// Отримання події
|
||
ws.onmessage = async (event) => {
|
||
const monitorEvent: MonitorEvent = {
|
||
timestamp: data.timestamp,
|
||
type: data.type, // 'agent' | 'node' | 'system' | 'project'
|
||
action: data.action,
|
||
message: data.message,
|
||
details: data.details,
|
||
node_id: data.node_id,
|
||
};
|
||
|
||
// Збереження в пам'ять (батчинг)
|
||
await addMonitorEventToBatch(nodeId, {
|
||
kind: eventKind,
|
||
body_text: monitorEvent.message,
|
||
body_json: { ...monitorEvent.details }
|
||
});
|
||
};
|
||
```
|
||
|
||
**Що надходить:**
|
||
- Події з нод (node_event)
|
||
- Події з агентів (agent_event)
|
||
- Системні події (system_event)
|
||
- Події проєкту (project_event)
|
||
|
||
#### B. Project Changes (`projectChangeTracker`)
|
||
|
||
**Файл:** `src/services/projectChangeTracker.ts`
|
||
|
||
```typescript
|
||
// Відстеження змін проєкту
|
||
private async generateMonitorMessage(change: ProjectChange) {
|
||
const baseMessage = this.formatChangeMessage(change);
|
||
const monitorMessage = `🤖 **Monitor Agent повідомляє:**\n\n${baseMessage}`;
|
||
|
||
// Збереження в пам'ять Monitor Agent
|
||
await this.saveToMonitorMemory(change, monitorMessage);
|
||
|
||
// Відправка події для відображення в чаті
|
||
this.emitChangeEvent(monitorMessage, change);
|
||
|
||
// Генерація через API (асинхронно)
|
||
await this.tryGenerateViaAPI(change, baseMessage);
|
||
}
|
||
```
|
||
|
||
**Що надходить:**
|
||
- Зміни файлів (file)
|
||
- Зміни конфігурацій (config)
|
||
- Зміни сервісів (service)
|
||
- Зміни агентів (agent)
|
||
- Деплойменти (deployment)
|
||
- Git коміти (git)
|
||
|
||
### Обробка загальним Monitor Agent:
|
||
|
||
**Файл:** `services/monitor-agent-service/app/main.py`
|
||
|
||
```python
|
||
# Загальний Monitor Agent отримує контекст з загальної пам'яті
|
||
async def get_monitor_memory_context(
|
||
node_id: Optional[str] = None,
|
||
microdao_id: Optional[str] = None,
|
||
limit: int = 10
|
||
) -> str:
|
||
# 1. Загальна пам'ять (для всіх Monitor Agent)
|
||
response = await client.get(
|
||
f"{MEMORY_SERVICE_URL}/agents/monitor/memory",
|
||
params={"limit": limit // 2}
|
||
)
|
||
|
||
# 2. Специфічна пам'ять (якщо вказана)
|
||
if node_id:
|
||
agent_id = f"monitor-node-{node_id}"
|
||
elif microdao_id:
|
||
agent_id = f"monitor-microdao-{microdao_id}"
|
||
|
||
# Генерація відповіді через mistral-nemo:12b
|
||
response = await client.post(
|
||
f"{OLLAMA_BASE_URL}/api/generate",
|
||
json={"model": "mistral-nemo:12b", "prompt": full_prompt}
|
||
)
|
||
```
|
||
|
||
**Результат:**
|
||
- ✅ Загальний Monitor Agent отримує ВСІ події
|
||
- ✅ Генерує повідомлення через LLM
|
||
- ✅ Зберігає в загальну пам'ять `monitor`
|
||
|
||
---
|
||
|
||
## 💾 2. Збереження в загальну пам'ять агентів Monitor сервісу
|
||
|
||
### Архітектура збереження:
|
||
|
||
**Файл:** `services/memory-service/app/monitor_events.py`
|
||
|
||
```python
|
||
async def save_monitor_events_batch(
|
||
batch: MonitorEventBatch,
|
||
db: Session
|
||
) -> MonitorEventResponse:
|
||
# Визначаємо специфічний agent_id
|
||
specific_agent_id = f"monitor-node-{batch.node_id}"
|
||
|
||
# Загальний agent_id для всіх Monitor Agent
|
||
global_agent_id = "monitor"
|
||
|
||
for event_data in batch.events:
|
||
# 1. Зберегти в специфічну пам'ять
|
||
specific_event = AgentMemoryEventCreate(
|
||
agent_id=specific_agent_id, # monitor-node-{node_id}
|
||
body_text=event_data.get("body_text", ""),
|
||
body_json=event_data.get("body_json", {})
|
||
)
|
||
create_agent_memory_event(db, specific_event)
|
||
|
||
# 2. Зберегти в загальну пам'ять
|
||
global_event = AgentMemoryEventCreate(
|
||
agent_id=global_agent_id, # monitor
|
||
body_text=event_data.get("body_text", ""),
|
||
body_json={
|
||
**event_data.get("body_json", {}),
|
||
"source_node": batch.node_id, # Додаємо джерело
|
||
"specific_agent_id": specific_agent_id
|
||
}
|
||
)
|
||
create_agent_memory_event(db, global_event)
|
||
```
|
||
|
||
### Структура пам'яті:
|
||
|
||
#### A. Специфічна пам'ять (для кожної НОДИ/мікроДАО):
|
||
|
||
```
|
||
monitor-node-node-1
|
||
├── Події з НОДА1
|
||
└── Зберігається в таблиці agent_memory_events
|
||
|
||
monitor-node-node-2
|
||
├── Події з НОДА2
|
||
└── Зберігається в таблиці agent_memory_events
|
||
|
||
monitor-microdao-daarion-dao
|
||
├── Події з DAARION MicroDAO
|
||
└── Зберігається в таблиці agent_memory_events
|
||
```
|
||
|
||
#### B. Загальна пам'ять (для всіх Monitor Agent):
|
||
|
||
```
|
||
monitor
|
||
├── ВСІ події з усіх НОД та мікроДАО
|
||
├── body_json.source_node - джерело події
|
||
├── body_json.specific_agent_id - специфічний agent_id
|
||
└── Зберігається в таблиці agent_memory_events
|
||
```
|
||
|
||
### Батчинг для оптимізації:
|
||
|
||
**Файл:** `src/api/monitorMemory.ts`
|
||
|
||
```typescript
|
||
// Батч для збереження подій
|
||
const BATCH_SIZE = 10; // Зберігати батч кожні 10 подій
|
||
const BATCH_TIMEOUT = 5000; // Або кожні 5 секунд
|
||
|
||
export async function addMonitorEventToBatch(
|
||
nodeId: string,
|
||
event: Omit<MonitorEvent, 'node_id'>
|
||
): Promise<void> {
|
||
eventBatch.push(fullEvent);
|
||
|
||
// Якщо батч досяг розміру, зберігаємо
|
||
if (eventBatch.length >= BATCH_SIZE) {
|
||
await flushMonitorEventBatch();
|
||
}
|
||
}
|
||
```
|
||
|
||
**Результат:**
|
||
- ✅ Подвійне збереження: специфічна + загальна пам'ять
|
||
- ✅ Батчинг для оптимізації (10 подій або 5 секунд)
|
||
- ✅ Всі події доступні в загальній пам'яті `monitor`
|
||
|
||
---
|
||
|
||
## 💬 3. Відображення повідомлень у власних чатах кожного агента
|
||
|
||
### Архітектура відображення:
|
||
|
||
#### A. MonitorChat (Глобальний - ВСІ події)
|
||
|
||
**Файл:** `src/components/monitor/MonitorChat.tsx`
|
||
|
||
```typescript
|
||
export function MonitorChat() {
|
||
const { events, isConnected } = useMonitorEvents();
|
||
|
||
// Додаємо події від Monitor Agent як повідомлення
|
||
// Головний MonitorChat показує ВСІ події (не фільтрує)
|
||
useEffect(() => {
|
||
if (events.length > 0 && isOpen) {
|
||
events.forEach((event) => {
|
||
const eventMessage: ChatMessage = {
|
||
id: `event-${event.timestamp}-${event.action}`,
|
||
role: 'assistant',
|
||
content: `📊 ${getEventIcon(event.type)} ${event.message}`,
|
||
timestamp: event.timestamp,
|
||
};
|
||
|
||
setMessages((prev) => {
|
||
const newMessages = [...prev, eventMessage];
|
||
return newMessages.slice(-100); // Максимум 100 повідомлень
|
||
});
|
||
});
|
||
}
|
||
}, [events, isOpen]);
|
||
}
|
||
```
|
||
|
||
**Де відображається:**
|
||
- ✅ Всі сторінки (через `App.tsx`)
|
||
- ✅ Показує ВСІ події з усіх НОД та мікроДАО
|
||
- ✅ Не фільтрує події
|
||
|
||
#### B. NodeMonitorChat (Кабінети НОД - фільтр по node_id)
|
||
|
||
**Файл:** `src/components/monitor/NodeMonitorChat.tsx`
|
||
|
||
```typescript
|
||
export function NodeMonitorChat({ nodeId, nodeName }: NodeMonitorChatProps) {
|
||
const { events, isConnected } = useMonitorEvents();
|
||
|
||
// Фільтруємо події тільки для цієї ноди
|
||
const nodeEvents = events.filter(event => event.node_id === nodeId);
|
||
|
||
// Додаємо події від Monitor Agent як повідомлення
|
||
useEffect(() => {
|
||
if (nodeEvents.length > 0 && isOpen) {
|
||
const latestEvent = nodeEvents[0];
|
||
const eventMessage: ChatMessage = {
|
||
id: `event-${latestEvent.timestamp}`,
|
||
role: 'assistant',
|
||
content: `📊 ${getEventIcon(latestEvent.type)} ${latestEvent.message}`,
|
||
timestamp: latestEvent.timestamp,
|
||
};
|
||
|
||
setMessages((prev) => {
|
||
const newMessages = [...prev, eventMessage];
|
||
return newMessages.slice(-50); // Максимум 50 повідомлень
|
||
});
|
||
}
|
||
}, [nodeEvents, isOpen]);
|
||
}
|
||
```
|
||
|
||
**Де відображається:**
|
||
- ✅ Кабінет НОДА1 (`/nodes/node-1-hetzner-gex44`)
|
||
- ✅ Кабінет НОДА2 (`/nodes/node-2-macbook-m4max`)
|
||
- ✅ Показує тільки події для конкретної НОДИ
|
||
- ✅ Фільтрує по `node_id`
|
||
|
||
#### C. MicroDaoMonitorChat (Кабінети мікроДАО - фільтр по microdao_id)
|
||
|
||
**Файл:** `src/components/monitor/MicroDaoMonitorChat.tsx`
|
||
|
||
```typescript
|
||
export function MicroDaoMonitorChat({ microDaoId, microDaoName }: MicroDaoMonitorChatProps) {
|
||
const { events, isConnected } = useMonitorEvents();
|
||
|
||
// Фільтруємо події тільки для цього мікроДАО
|
||
const microDaoEvents = events.filter(event =>
|
||
event.details?.team_id === microDaoId ||
|
||
event.details?.microdao_id === microDaoId
|
||
);
|
||
|
||
// Додаємо події від Monitor Agent як повідомлення
|
||
useEffect(() => {
|
||
if (microDaoEvents.length > 0 && isOpen) {
|
||
const latestEvent = microDaoEvents[0];
|
||
const eventMessage: ChatMessage = {
|
||
id: `event-${latestEvent.timestamp}`,
|
||
role: 'assistant',
|
||
content: `📊 ${getEventIcon(latestEvent.type)} ${latestEvent.message}`,
|
||
timestamp: latestEvent.timestamp,
|
||
};
|
||
|
||
setMessages((prev) => {
|
||
const newMessages = [...prev, eventMessage];
|
||
return newMessages.slice(-50); // Максимум 50 повідомлень
|
||
});
|
||
}
|
||
}, [microDaoEvents, isOpen]);
|
||
}
|
||
```
|
||
|
||
**Де відображається:**
|
||
- ✅ Кабінет DAARION MicroDAO (`/microdao/daarion`)
|
||
- ✅ Кабінет GREENFOOD MicroDAO (`/microdao/greenfood`)
|
||
- ✅ Кабінет ENERGY UNION MicroDAO (`/microdao/energy-union`)
|
||
- ✅ Показує тільки події для конкретного мікроДАО
|
||
- ✅ Фільтрує по `microdao_id` або `team_id`
|
||
|
||
#### D. DaarionMonitorChat (Кабінет DAARION - ВСІ події)
|
||
|
||
**Файл:** `src/components/monitor/DaarionMonitorChat.tsx`
|
||
|
||
```typescript
|
||
export function DaarionMonitorChat() {
|
||
const { events, isConnected } = useMonitorEvents();
|
||
|
||
// DaarionMonitorChat показує ВСІ події (агрегація з усіх НОД та мікроДАО)
|
||
// Аналогічно MonitorChat, але в кабінеті DAARION
|
||
}
|
||
```
|
||
|
||
**Де відображається:**
|
||
- ✅ Кабінет DAARION (`/microdao/daarion`)
|
||
- ✅ Показує ВСІ події (агрегація)
|
||
- ✅ Аналогічно MonitorChat
|
||
|
||
#### E. DagiMonitorPage (Головна сторінка моніторингу)
|
||
|
||
**Файл:** `src/pages/DagiMonitorPage.tsx`
|
||
|
||
```typescript
|
||
export function DagiMonitorPage() {
|
||
const { events, isConnected } = useMonitorEvents();
|
||
|
||
// Конвертуємо MonitorEvent в ProjectChange для генерації повідомлень
|
||
useEffect(() => {
|
||
if (events.length > 0) {
|
||
const latestEvent = events[0];
|
||
|
||
// Конвертуємо подію в ProjectChange
|
||
const projectChange: ProjectChange = {
|
||
id: `change-${latestEvent.timestamp}`,
|
||
type: latestEvent.type,
|
||
action: latestEvent.action,
|
||
path: latestEvent.details?.path || '',
|
||
description: latestEvent.message,
|
||
timestamp: latestEvent.timestamp,
|
||
details: {
|
||
node_id: latestEvent.node_id,
|
||
...latestEvent.details,
|
||
},
|
||
};
|
||
|
||
// Додаємо до projectChangeTracker для генерації повідомлення
|
||
projectChangeTracker.addChange(projectChange);
|
||
}
|
||
}, [events]);
|
||
|
||
// Обробник подій про зміни проєкту (повідомлення від Monitor Agent)
|
||
useEffect(() => {
|
||
const handleProjectChange = (customEvent: CustomEvent) => {
|
||
const changeMessage = customEvent.detail as ProjectChange;
|
||
const message = changeMessage.description || '';
|
||
|
||
// Додаємо повідомлення в чат
|
||
const monitorMessage = `🤖 **Monitor Agent повідомляє:**\n\n${message}`;
|
||
|
||
setMessages((prev) => {
|
||
const newMessages = [{
|
||
id: changeMessage.id,
|
||
role: 'assistant',
|
||
content: monitorMessage,
|
||
timestamp: changeMessage.timestamp,
|
||
}, ...prev]; // Нові повідомлення зверху
|
||
|
||
return newMessages.slice(0, 100);
|
||
});
|
||
};
|
||
|
||
window.addEventListener('project-change', handleProjectChange);
|
||
return () => window.removeEventListener('project-change', handleProjectChange);
|
||
}, []);
|
||
}
|
||
```
|
||
|
||
**Де відображається:**
|
||
- ✅ Головна сторінка моніторингу (`/dagi-monitor`)
|
||
- ✅ Показує ВСІ події
|
||
- ✅ Генерує повідомлення через Monitor Agent API
|
||
|
||
---
|
||
|
||
## 🔄 Повний потік даних
|
||
|
||
### Приклад: Подія з НОДА1
|
||
|
||
```
|
||
1. WebSocket подія надходить
|
||
└─> useMonitorEvents отримує подію
|
||
└─> addMonitorEventToBatch(node-1, event)
|
||
└─> flushMonitorEventBatch()
|
||
└─> POST /api/memory/monitor-events/batch
|
||
└─> Memory Service зберігає:
|
||
├── monitor-node-node-1 (специфічна пам'ять)
|
||
└── monitor (загальна пам'ять)
|
||
|
||
2. Project Change надходить
|
||
└─> projectChangeTracker.addChange(change)
|
||
└─> generateMonitorMessage(change)
|
||
├── saveToMonitorMemory() → POST /api/agent/monitor/memory
|
||
│ └─> Memory Service зберігає в monitor
|
||
└── emitChangeEvent() → CustomEvent('project-change')
|
||
└─> Відображається в чатах
|
||
|
||
3. Відображення в чатах
|
||
├── MonitorChat (глобальний)
|
||
│ └─> Показує подію (не фільтрує)
|
||
│
|
||
├── NodeMonitorChat (кабінет НОДА1)
|
||
│ └─> Фільтрує по node_id === 'node-1'
|
||
│ └─> Показує подію
|
||
│
|
||
├── NodeMonitorChat (кабінет НОДА2)
|
||
│ └─> Фільтрує по node_id === 'node-2'
|
||
│ └─> НЕ показує подію (інша нода)
|
||
│
|
||
└── MicroDaoMonitorChat (кабінети мікроДАО)
|
||
└─> Фільтрує по microdao_id
|
||
└─> Показує тільки події для конкретного мікроДАО
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ Підсумок
|
||
|
||
### 1. Загальний Monitor Agent (DAARION)
|
||
|
||
✅ **Отримує:**
|
||
- ВСІ WebSocket події з усіх НОД та мікроДАО
|
||
- ВСІ Project Changes з проєкту
|
||
- Контекст з загальної пам'яті `monitor`
|
||
|
||
✅ **Генерує:**
|
||
- Повідомлення через `mistral-nemo:12b`
|
||
- Зберігає в загальну пам'ять `monitor`
|
||
|
||
### 2. Загальна пам'ять агентів Monitor сервісу
|
||
|
||
✅ **Зберігає:**
|
||
- Подвійне збереження:
|
||
- Специфічна пам'ять: `monitor-node-{node_id}` або `monitor-microdao-{microdao_id}`
|
||
- Загальна пам'ять: `monitor` (ВСІ події)
|
||
|
||
✅ **Оптимізація:**
|
||
- Батчинг: 10 подій або 5 секунд
|
||
- Автоматичне збереження
|
||
|
||
### 3. Власні чати кожного агента
|
||
|
||
✅ **MonitorChat (Глобальний):**
|
||
- Показує ВСІ події
|
||
- Розташований на всіх сторінках
|
||
|
||
✅ **NodeMonitorChat (Кабінети НОД):**
|
||
- Фільтрує по `node_id`
|
||
- Показує тільки події для конкретної НОДИ
|
||
|
||
✅ **MicroDaoMonitorChat (Кабінети мікроДАО):**
|
||
- Фільтрує по `microdao_id` або `team_id`
|
||
- Показує тільки події для конкретного мікроДАО
|
||
|
||
✅ **DaarionMonitorChat (Кабінет DAARION):**
|
||
- Показує ВСІ події (агрегація)
|
||
|
||
✅ **DagiMonitorPage (Головна сторінка):**
|
||
- Показує ВСІ події
|
||
- Генерує повідомлення через Monitor Agent API
|
||
|
||
---
|
||
|
||
**Статус:** ✅ Всі потоки даних працюють правильно
|
||
**Збереження:** ✅ Подвійне збереження працює
|
||
**Відображення:** ✅ Всі чати показують відповідні події
|
||
|