Skip to content

# TASK: PHASE 3 — LLM PROXY + MEMORY ORCHESTRATOR + TOOLCORE

Goal: Зробити агентів DAARION по-справжньому розумними та інструментальними: - єдиний LLM Proxy з підтримкою кількох провайдерів і локальних моделей; - Memory Orchestrator, який дає єдиний API для памʼяті агентів; - Toolcore — реєстр інструментів і безпечне виконання tools для агентів.

PHASE 3 = інфраструктура мислення й дії для Agent Runtime.

Existing (assume from Phase 1–2): - messaging-service, matrix-gateway, Messenger UI. - agent-filter, DAGI Router (router.invoke.agent). - agent-runtime (використовує /internal/llm/proxy та /internal/agent-memory/*, але поки можуть бути stubs). - базова БД (users, microdaos, agents, channels, messages, etc.). - NATS JetStream, PostgreSQL, docker-compose.

Deliverables: 1. services/llm-proxy/ — LLM Router + Providers + Logging. 2. services/memory-orchestrator/ — єдиний API для памʼяті агентів. 3. services/toolcore/ — registry + execution engine для tools. 4. Інтеграція з agent-runtime. 5. Оновлення docker-compose та docs.

## 1) SERVICE: LLM PROXY (services/llm-proxy)

Create folder:

    services/llm-proxy/
      main.py
      models.py
      router.py
      providers/
        __init__.py
        openai_provider.py
        deepseek_provider.py
        local_provider.py
      config.yaml
      middlewares.py
      logging_middleware.py
      Dockerfile
      requirements.txt
      README.md

### 1.1 API

Base URL (internal-only): - `POST /internal/llm/proxy`

Request (v1):

    {
      "model": "gpt-4.1-mini",
      "messages": [
        { "role": "system", "content": "..." },
        { "role": "user", "content": "..." }
      ],
      "metadata": {
        "agent_id": "agent:sofia",
        "microdao_id": "microdao:7",
        "channel_id": "..."
      }
    }

Response:

    {
      "content": "текст відповіді",
      "usage": {
        "prompt_tokens": 123,
        "completion_tokens": 45,
        "total_tokens": 168
      },
      "provider": "openai",
      "model_resolved": "gpt-4.1-mini"
    }

Optional (stub for later): - `/internal/llm/batch` - `/internal/llm/stream` (можна залишити TODO)

### 1.2 Model routing (router.py)

Створити модуль:

    def route_model(logical_model: str) -> ProviderConfig:
        # Читає зі структури в config.yaml
        # Напр.:
        #  - gpt-4.1-mini → OpenAI (gpt-4.1-mini)
        #  - deepseek-r1 → DeepSeek API
        #  - dagi-local-8b → Local provider (Ollama / vllm)

config.yaml (приклад):

    providers:
      openai:
        base_url: "https://api.openai.com/v1"
        api_key_env: "OPENAI_API_KEY"
      deepseek:
        base_url: "https://api.deepseek.com/v1"
        api_key_env: "DEEPSEEK_API_KEY"
      local:
        base_url: "http://local-llm:8000"

    models:
      gpt-4.1-mini:
        provider: "openai"
        name: "gpt-4.1-mini"
      deepseek-r1:
        provider: "deepseek"
        name: "deepseek-r1"
      dagi-local-8b:
        provider: "local"
        name: "qwen2.5-8b-instruct"

### 1.3 Providers (providers/*.py)

Кожен провайдер:

    class BaseProvider(Protocol):
        async def chat(self, messages: list[ChatMessage], model_name: str, **kwargs) -> LLMResponse: ...

Файли: - `providers/openai_provider.py` - `providers/deepseek_provider.py` - `providers/local_provider.py`

Вони: - отримують normalized messages, - викликають відповідний API/endpoint, - повертають LLMResponse (контент + usage).

Local provider: - для Phase 3 можна зробити простий HTTP-запит до існуючого локального сервісу (Ollama/vLLM), або stub.

### 1.4 Logging & limits (middlewares.py, logging_middleware.py)

Добавити: - простий rate limit per-agent (стабово, через in-memory або Redis stub). - логування викликів: - agent_id, microdao_id, model, latency, tokens. - TODO hooks для Billing/Usage Engine (на майбутнє).

### 1.5 README.md

Описати: - підтримувані моделі та мапінг, - як додати нового провайдера, - як викликати /internal/llm/proxy з інших сервісів (особливо agent-runtime).

## 2) SERVICE: MEMORY ORCHESTRATOR (services/memory-orchestrator)

Goal: Єдиний API для роботи з памʼяттю: - short-term (канальний контекст / event log), - mid-term (agent memory / RAG embedding store), - long-term (knowledge base, docs, roadmaps).

Create folder:

    services/memory-orchestrator/
      main.py
      models.py
      router.py
      backends/
        __init__.py
        short_term_pg.py
        vector_store_pg.py
        kb_filesystem.py
      embedding_client.py
      config.yaml
      Dockerfile
      requirements.txt
      README.md

### 2.1 API

Base URL: - `POST /internal/agent-memory/query` - `POST /internal/agent-memory/store` - `POST /internal/agent-memory/summarize` (optional v1)

Request /query:

    {
      "agent_id": "agent:sofia",
      "microdao_id": "microdao:7",
      "channel_id": "optional",
      "query": "коротко, які були останні зміни в цьому microDAO?",
      "limit": 5
    }

Response:

    {
      "items": [
        {
          "id": "mem-123",
          "kind": "conversation | kb | task | dao-event",
          "score": 0.87,
          "content": "Текст фрагменту памʼяті",
          "meta": {
            "source": "channel:...",
            "timestamp": "..."
          }
        }
      ]
    }

Request /store:

    {
      "agent_id": "agent:sofia",
      "microdao_id": "microdao:7",
      "channel_id": "optional",
      "kind": "conversation | kb | task | dao-event",
      "content": {
        "user_message": "...",
        "agent_reply": "..."
      }
    }

Response:

    { "ok": true, "id": "mem-123" }

### 2.2 Backends

backends/short_term_pg.py: - зберігає сирі events (або посилання на messages) в PostgreSQL, якщо треба окремо від messenger.

backends/vector_store_pg.py: - використовує embeddings (через embedding_client.py) і зберігає в таблиці: `sql agent_memories (id, agent_id, microdao_id, embedding vector, content, meta)` - простий cosine similarity пошук.

backends/kb_filesystem.py: - stub для long-term знань (roadmaps, docs), можна залишити TODO.

### 2.3 Embedding client (embedding_client.py)

- Простий клієнт до існуючої embedding-моделі (наприклад, BGE-M3 чи інша) через /internal/embedding/proxy або безпосередньо.
- Якщо embedding-сервіс не описаний — зробити stub + TODO.

### 2.4 Integration points

agent-runtime: - замінити прямі виклики agent-memory на: - `POST /internal/agent-memory/query` - `POST /internal/agent-memory/store`

Надалі можна буде підʼєднати: - microDAO knowledge, - DAO events, - roadmaps.

## 3) SERVICE: TOOLCORE (services/toolcore)

Goal: Єдина точка для: - реєстрації tools (інструментів агентів), - перевірки дозволів, - виконання tools та повернення результату агенту.

Create folder:

    services/toolcore/
      main.py
      models.py
      router.py
      registry.py
      executors/
        __init__.py
        http_executor.py
        python_executor.py (optional)
      config.yaml
      Dockerfile
      requirements.txt
      README.md

### 3.1 Data model

models.py:

    class ToolDefinition(BaseModel):
        id: str                # "projects.list"
        name: str
        description: str
        input_schema: dict     # JSON Schema
        output_schema: dict    # JSON Schema
        executor: Literal["http", "python"]
        target: str            # HTTP URL or python path
        allowed_agents: list[str] | None

    class ToolCallRequest(BaseModel):
        tool_id: str
        agent_id: str
        microdao_id: str
        args: dict
        context: dict | None = None

    class ToolCallResult(BaseModel):
        ok: bool
        result: dict | None = None
        error: str | None = None

### 3.2 API

HTTP (internal):

- `GET /internal/tools` → список доступних tools

- `POST /internal/tools/call` Body: ToolCallRequest Behavior:

- перевірити, чи agent_id має право на tool_id (allowed_agents).
- знайти ToolDefinition.
- виконати через відповідний executor.
- повернути ToolCallResult.

### 3.3 Executors

executors/http_executor.py: - викликає target як HTTP endpoint (POST з args + context). - хендлінг помилок та таймаутів.

executors/python_executor.py (optional): - allows direct call of internal python functions (у v1 можна залишити TODO або мінімальний stub).

### 3.4 Registry (registry.py)

Початок — з config.yaml (static registry):

config.yaml:

    tools:
      - id: "projects.list"
        name: "List projects"
        description: "Повертає список проєктів microDAO."
        input_schema:
          type: "object"
          properties:
            microdao_id: { type: "string" }
          required: ["microdao_id"]
        output_schema:
          type: "array"
          items: { type: "object" }
        executor: "http"
        target: "http://projects-service:8000/internal/tools/projects.list"
        allowed_agents: ["agent:sofia", "agent:pm", "agent:cto"]

Registry API: - load from config.yaml at startup - later можна зробити DB-backed registry.

### 3.5 NATS (optional для Phase 3)

Можна додати: - subject: `tool.request` - subject: `tool.response`

Але для Phase 3 достатньо HTTP-контракту, який викликає agent-runtime.

## 4) INTEGRATION: agent-runtime + LLM Proxy + Memory + Toolcore

Update agent-runtime:

### 4.1 Використовувати LLM Proxy

Замість прямого виклику будь-яких LLM: - завжди викликати: `http POST /internal/llm/proxy { "model": "<logical model from agent blueprint>", "messages": [...], "metadata": { "agent_id": ..., "microdao_id": ..., "channel_id": ... } }`

### 4.2 Використовувати Memory Orchestrator

При channel_message: - контекст: `GET /internal/messaging/channels/{channelId}/messages?limit=50` - памʼять: `POST /internal/agent-memory/query` - запис: `POST /internal/agent-memory/store` після відповіді.

### 4.3 Виклик tools

Якщо в blueprint агента передбачені tools: - парсити вихід LLM (JSON/structured) або через simple convention (наприклад, спеціальний формат "TOOL: projects.list {...}"). - для кожного tool call: `http POST /internal/tools/call { "tool_id": "...", "agent_id": "...", "microdao_id": "...", "args": {...} }` - включати результати tools у наступний LLM виклик як контекст. - (На Phase 3 можна реалізувати один-два прості tools: projects.list, followups.create.)

## 5) DOCKER + DOCS

### 5.1 docker-compose

Додати new services: - llm-proxy - memory-orchestrator - toolcore

Переконатися, що: - вони в тій же мережі, що agent-runtime, messaging-service, NATS, DB; - є healthcheck-и (GET /health).

### 5.2 Документація

Додати/оновити: - `docs/LLM_PROXY_SPEC.md` - `docs/MEMORY_ORCHESTRATOR_SPEC.md` - `docs/TOOLCORE_SPEC.md` - оновити `docs/INDEX.md` (додати посилання на ці три сервіси). - в PHASE2_READY.md або окремому PHASE3_READY.md описати нову архітектуру agent pipeline.

## 6) ACCEPTANCE CRITERIA

### 1) LLM Proxy:

- ✅ /internal/llm/proxy приймає запити, повертає відповіді.
- ✅ Принаймні 2 логічні моделі працюють (наприклад, один remote, один local.stub).
- ✅ Логуються виклики per-agent.

### 2) Memory Orchestrator:

- ✅ /internal/agent-memory/query повертає релевантні фрагменти (можна з простим vector search або stub).
- ✅ /internal/agent-memory/store зберігає нові entries.
- ✅ agent-runtime успішно використовує його при відповіді.

### 3) Toolcore:

- ✅ /internal/tools повертає список інструментів.
- ✅ /internal/tools/call виконує хоча б один реальний tool (HTTP executor).
- ✅ agent-runtime може викликати хоча б один tool впродовж свого pipeline.

### 4) End-to-end:

- ✅ Агент у Messenger:
- читає контекст (messages + memory),
- викликає LLM через LLM Proxy,
- за потреби викликає tool через Toolcore,
- повертає відповідь у канал.
- ✅ Для одного демо-агента (наприклад, Sofia-Prime) цей ланцюг стабільно працює.

## END OF TASK

Estimated Time: 6-8 weeks
Priority: High
Dependencies: Phase 2 complete
Version: 1.0.0
Date: 2025-11-24