14 KiB
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:
- services/llm-proxy/ — LLM Router + Providers + Logging.
- services/memory-orchestrator/ — єдиний API для памʼяті агентів.
- services/toolcore/ — registry + execution engine для tools.
- Інтеграція з agent-runtime.
- Оновлення 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.pyproviders/deepseek_provider.pyproviders/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/queryPOST /internal/agent-memory/storePOST /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) і зберігає в таблиці:
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/queryPOST /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/callBody: 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:
- завжди викликати:
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:
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.mddocs/MEMORY_ORCHESTRATOR_SPEC.mddocs/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