arcadiasuite/.planning/phases/03-miroflow-embutido/03-RESEARCH.md

552 lines
29 KiB
Markdown

# Phase 3: MiroFlow Embutido - Research
**Researched:** 2026-03-25
**Domain:** Python agent framework (MiroFlow) integrado ao Node.js/Express + Ollama local + Superset bridge
**Confidence:** HIGH (baseado em inspeção direta do código-fonte e serviços ativos)
---
## Summary
O Phase 3 conecta o framework Python MiroFlow (já clonado como submodule em `server/modules/miroflow/`) ao backend Node.js do Arcádia Suite, expondo um endpoint `POST /api/miroflow/analyze` que despacha para um microserviço Python FastAPI. Esse microserviço instancia agentes MiroFlow configurados via YAML, aponta para o Ollama local (já rodando na porta 11434 com `deepseek-r1:14b` instalado), e registra cada execução como nó imutável no KG (tabela `graph_nodes` via `/api/graph/nodes`).
O padrão de integração Node ↔ Python já está estabelecido no projeto: `server/bi/engine-proxy.ts` e `server/python/bi_analysis_service.py` demonstram o fluxo exato — Express proxy faz `fetch()` para FastAPI local; a resposta é repassada ao cliente. O novo microserviço MiroFlow (`miroflow_service.py`) seguirá o mesmo padrão, rodando na porta 8006. O agente `Researcher` exige `llama3.1:8b`, que **ainda não está instalado no Ollama** (apenas `deepseek-r1:14b`, `llama3.2:3b` e `nomic-embed-text` estão disponíveis).
No frontend, o componente `MiroFlowControl.tsx` será um painel overlay/tab injetado na página `BiWorkspace.tsx` que já usa o `SupersetDashboard` component. O toggle "Modo Científico" chama `POST /api/miroflow/analyze` e exibe a resposta estruturada ao lado do dashboard Superset.
**Primary recommendation:** Construir `server/python/miroflow_service.py` como FastAPI standalone (porta 8006), instanciar agentes MiroFlow com YAML inline (sem arquivos externos), usar `UnifiedOpenAIClient` apontando para Ollama via `OLLAMA_BASE_URL/v1`, registrar execuções via `createNode()` do graph service.
---
## User Constraints
*(Seção obrigatória — copiada do STATE.md/ROADMAP.md/PROJECT.md que fazem as vezes de CONTEXT.md)*
### Locked Decisions
- Stack: Node.js + TypeScript backend, React + TypeScript frontend, PostgreSQL, Neo4j KG
- IA: Ollama local MÁXIMO 14B — `deepseek-r1:14b` para Statistician e Fiscal Auditor, `llama3.1:8b` para Researcher
- **NUNCA usar modelos acima de 14B**: proibido citar ou planejar `deepseek-r1:32b`, `deepseek-r1:70b`, `llama3.1:70b` ou qualquer modelo maior
- MiroFlow já clonado em `server/modules/miroflow/` como submodule Python
- Branch de deploy: `Servidor`
- Superset em produção com RLS — não alterar configurações existentes
- Backend-first: API definida antes do frontend
- Auditoria imutável em todas as execuções (KG)
### Claude's Discretion
- Porta do microserviço MiroFlow (recomenda-se 8006 pelo padrão existente)
- Estrutura interna dos agentes MiroFlow (YAML inline vs arquivos externos)
- Forma de integrar o toggle no frontend (overlay, tab, painel lateral)
- Como fazer o `llama3.1:8b` ser baixado (wave 0 task vs setup manual)
### Deferred Ideas (OUT OF SCOPE)
- Versionamento Git-like de skills (Phase 2 pendente, não Phase 3)
- OpenClaw (Phase 4)
- Automation Fabric (Phase 5)
- Dev Center Completo (Phase 6)
---
## Standard Stack
### Core
| Library | Version | Purpose | Why Standard |
|---------|---------|---------|--------------|
| MiroFlow | 1.7.0 | Framework de agentes Python | Já no submodule, desenvolvido pela MiromindAI |
| FastAPI | >=0.115.0 | Microserviço Python | Padrão já usado em todos os serviços Python do projeto |
| uvicorn | >=0.32.0 | ASGI server | Usado em todos os serviços Python |
| omegaconf | (via miroflow deps) | Configuração YAML com variáveis | Dependência central do MiroFlow |
| hydra-core | >=1.3.2 | Composição de configurações | Dependência central do MiroFlow |
| openai | ==1.78.1 | Cliente HTTP para Ollama (OpenAI-compat.) | MiroFlow usa `UnifiedOpenAIClient` via openai SDK |
| httpx / aiohttp | >=0.28.1 / >=3.12.15 | HTTP async | Dependências do MiroFlow e FastAPI |
### Supporting
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| python-dotenv | >=1.1.1 | Leitura de .env | Inicialização do microserviço |
| pydantic | >=2.x | Validação de request/response | Models FastAPI |
| neo4j (driver) | opcional | Neo4j direto | Apenas se precisar grafo real; por ora usa `graph_nodes` PostgreSQL |
| psycopg2-binary | >=2.9.x | PostgreSQL direto | Se microserviço precisar escrever no banco |
### Alternatives Considered
| Instead of | Could Use | Tradeoff |
|------------|-----------|----------|
| Microserviço FastAPI separado | Subprocess Node → Python | subprocess é síncrono e frágil; FastAPI segue padrão estabelecido |
| YAML files externos | YAML inline (dict) em Python | Inline elimina dependência de caminhos de arquivo em container |
| Ollama direto (porta 11434) | Via LiteLLM gateway (porta 4000) | LiteLLM tem fallback e roteamento; Ollama direto é mais simples para agentes isolados |
**Installation (para o microserviço):**
```bash
# Instalar dependências no ambiente Python do servidor
pip install fastapi uvicorn omegaconf hydra-core openai python-dotenv pydantic psycopg2-binary
# Ou instalar o miroflow inteiro com uv:
cd server/modules/miroflow && uv sync
```
**Version verification:** Os pacotes acima são os listados no `pyproject.toml` do MiroFlow v1.7.0, verificado diretamente no arquivo.
---
## Architecture Patterns
### Recommended Project Structure
```
server/python/
├── miroflow_service.py # novo microserviço FastAPI (porta 8006)
├── bi_analysis_service.py # existente (referência de padrão)
└── ...
server/miroflow/
├── routes.ts # Express router: POST /api/miroflow/analyze
└── engine-proxy.ts # fetch() para http://localhost:8006
client/src/
├── pages/BiWorkspace.tsx # EXISTENTE — adicionar tab "Científico"
└── components/
└── MiroFlowControl.tsx # NOVO — toggle + resultado da análise
docker/python-entrypoint.sh # adicionar case "miroflow" → porta 8006
docker-compose.prod.yml # adicionar serviço miroflow (porta 8006)
```
### Pattern 1: Node → Python Microservice Proxy
**What:** Express recebe a request, faz fetch() para FastAPI, retorna o resultado. Sem subprocess, sem child_process.
**When to use:** Sempre que Node.js precisar de lógica Python (pandas, ML, agentes).
**Example:**
```typescript
// Source: server/bi/engine-proxy.ts (padrão existente)
const MIROFLOW_URL = `http://${process.env.MIROFLOW_HOST || "localhost"}:${process.env.MIROFLOW_PORT || "8006"}`;
async function proxyToMiroFlow(path: string, body: object): Promise<any> {
const response = await fetch(`${MIROFLOW_URL}${path}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
signal: AbortSignal.timeout(300_000), // 5min — modelos LLM são lentos
});
if (!response.ok) {
const err = await response.json().catch(() => ({ detail: response.statusText }));
throw new Error(err.detail || `MiroFlow error: ${response.status}`);
}
return response.json();
}
```
### Pattern 2: MiroFlow Agent com Ollama via UnifiedOpenAIClient
**What:** MiroFlow usa `UnifiedOpenAIClient` com `base_url` apontando para Ollama (OpenAI-compatible API em `/v1`).
**When to use:** Para todos os agentes do Phase 3.
**Example:**
```python
# Source: server/modules/miroflow/miroflow/llm/openai_client.py + config/llm/base.yaml
from miroflow.agents.factory import build_agent
from miroflow.agents.context import AgentContext
AGENT_CONFIG_STATISTICIAN = {
"type": "IterativeAgentWithToolAndRollback",
"max_turns": 10,
"llm": {
"provider_class": "UnifiedOpenAIClient",
"model_name": "deepseek-r1:14b",
"api_key": "ollama", # Ollama não valida api_key
"base_url": "http://localhost:11434/v1", # Ollama OpenAI-compat
"max_tokens": 8192,
"temperature": 0.7,
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
"reasoning_effort": None, "repetition_penalty": 1.0,
"max_context_length": -1, "async_client": True,
"disable_cache_control": True, "keep_tool_result": -1,
"use_tool_calls": False, "oai_tool_thinking": False,
},
}
agent = build_agent(AGENT_CONFIG_STATISTICIAN)
ctx = AgentContext(task_description="Analise os seguintes dados SQL: ...")
result = await agent.run(ctx)
```
### Pattern 3: Registro Imutável no KG via graph_nodes
**What:** Após cada execução MiroFlow, registrar no PostgreSQL via `POST /api/graph/nodes`.
**When to use:** Toda execução de agente (success ou error).
**Example:**
```typescript
// Source: server/graph/service.ts createNode() — chamado pelo routes.ts de miroflow
await fetch("/api/graph/nodes", {
method: "POST",
body: JSON.stringify({
type: "miroflow_execution",
tenantId: req.user?.tenantId,
data: {
agent: "statistician",
model: "deepseek-r1:14b",
input: analysisRequest,
output: analysisResult,
auditHash: sha256(JSON.stringify({ executionId, input, output })),
executedAt: new Date().toISOString(),
}
})
});
```
### Pattern 4: MiroFlowControl.tsx como tab em BiWorkspace
**What:** BiWorkspace.tsx já tem estrutura de tabs (Tabs/TabsContent de shadcn/ui). Adicionar tab "Científico" que renderiza `MiroFlowControl`.
**When to use:** Ponto de entrada do toggle "Modo Científico" no contexto do BI.
**Example:**
```tsx
// Source: client/src/pages/BiWorkspace.tsx (padrão de tabs existente)
import { MiroFlowControl } from "@/components/MiroFlowControl";
// Dentro do <TabsList>:
<TabsTrigger value="cientifico"><Brain /> Científico</TabsTrigger>
// Dentro das <TabsContent>:
<TabsContent value="cientifico">
<MiroFlowControl currentDashboardData={currentData} />
</TabsContent>
```
### Anti-Patterns to Avoid
- **subprocess.run() no Node**: Não usar `child_process.spawn()` para chamar Python — use o microserviço FastAPI (padrão estabelecido)
- **Carregar miroflow no container Node**: O Dockerfile principal é Node.js puro — Python deve rodar em container separado (Dockerfile.python)
- **Modelos > 14B**: Nunca usar `deepseek-r1:32b`, `deepseek-r1:70b`, `llama3.1:70b` ou qualquer modelo acima de 14B parâmetros
- **Modificar configurações do Superset**: O proxy `/superset/*` e as rotas `/api/superset/*` existentes não devem ser alterados
- **Hardcoded OLLAMA_BASE_URL**: Sempre ler de env var `OLLAMA_BASE_URL` (default `http://localhost:11434`)
---
## Don't Hand-Roll
| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| Orquestração de agentes LLM | Loop manual de chamadas LLM | MiroFlow `IterativeAgentWithToolAndRollback` | Rollback automático, retry, tool calling já implementado |
| Proxy Node → Python | Child process / spawn | `fetch()` para FastAPI (padrão `engine-proxy.ts`) | Stateless, async, sem gestão de processos |
| Configuração de LLM | Classe própria de cliente | `UnifiedOpenAIClient` do MiroFlow | Já tem retry, context limit handling, tool protocol |
| Embedding da análise no Superset | iframe manual com JS | `SupersetDashboard.tsx` já existente | Guest token, SDK do Superset, error handling prontos |
| Logs de execução imutáveis | Nova tabela no banco | `graph_nodes` + `createNode()` via `server/graph/service.ts` | Infraestrutura de KG já existente com embeddings |
| Validação de request/response | Manual | Pydantic models + FastAPI auto-validation | Type safety e OpenAPI spec grátis |
**Key insight:** O projeto já tem todos os primitivos necessários. Phase 3 é essencialmente "conectar os pontos" — o agente Python já existe, o gateway LLM (LiteLLM/Ollama) já está configurado, o KG já tem tabelas, o padrão de proxy Node→Python já está implementado.
---
## Runtime State Inventory
> Fase é greenfield (novo microserviço e novas rotas). Não há renomeações ou migrações de dados existentes.
| Category | Items Found | Action Required |
|----------|-------------|------------------|
| Stored data | `graph_nodes` no PostgreSQL — usado pelo KG existente | Nenhum — novas inserções com `type: "miroflow_execution"` |
| Live service config | Ollama rodando na porta 11434 com `deepseek-r1:14b` instalado | `llama3.1:8b` NÃO instalado — precisa de `ollama pull llama3.1:8b` |
| OS-registered state | Nenhum Task Scheduler / cron para MiroFlow | Nenhuma ação necessária |
| Secrets/env vars | `OLLAMA_BASE_URL=http://ollama:11434` já no `.env`; `LITELLM_API_KEY` existente | Adicionar `MIROFLOW_PORT=8006` no `.env` e docker-compose |
| Build artifacts | `server/modules/miroflow/` tem `uv.lock` mas sem `.venv` instalado | Wave 0: instalar dependências via `uv sync` ou `pip install` no Dockerfile.python |
**Modelo faltando:** `llama3.1:8b` não está instalado no Ollama (`False` confirmado por `curl http://localhost:11434/api/tags`). Instalado: `deepseek-r1:14b` (8.4GB), `arcadia-agent:latest` (2GB), `llama3.2:3b` (2GB), `nomic-embed-text` (274MB). O plano deve incluir `ollama pull llama3.1:8b` como task de Wave 0 ou pré-requisito.
---
## Environment Availability
| Dependency | Required By | Available | Version | Fallback |
|------------|------------|-----------|---------|----------|
| Ollama API `:11434` | Todos os agentes MiroFlow | ✓ | 0.18.2 | — |
| `deepseek-r1:14b` | Statistician, Fiscal Auditor | ✓ | 8.4GB | — |
| `llama3.1:8b` | Researcher | ✗ | — | `llama3.2:3b` (degraded) ou `deepseek-r1:14b` |
| Python 3.12 | Microserviço FastAPI | ✓ | 3.12.3 | — |
| `uv` | Instalar deps MiroFlow | ✓ | 0.9.30 | `pip3` |
| FastAPI/uvicorn | Microserviço | ✓ | fastapi 0.128.8 | — |
| `omegaconf` (sistema) | MiroFlow direto | ✗ | — | Instalar via `uv sync` no `server/modules/miroflow/` |
| PostgreSQL `:5432` | KG graph_nodes | ✓ | 16 | — |
| Neo4j `:7687` | KG real (opcional) | Requer profile `[kg]` | — | `graph_nodes` PostgreSQL (já usado) |
**Missing dependencies with no fallback:**
- `llama3.1:8b` para o agente Researcher — deve ser baixado em Wave 0 (`ollama pull llama3.1:8b`)
**Missing dependencies with fallback:**
- `omegaconf` no sistema: instalar com `pip3 install omegaconf hydra-core` ou rodar MiroFlow dentro de venv com `uv sync`
- `llama3.1:8b` temporariamente: usar `deepseek-r1:14b` como fallback até o download completar
---
## Common Pitfalls
### Pitfall 1: Ollama não expõe `/v1` sem flag explícita em versões antigas
**What goes wrong:** `UnifiedOpenAIClient` do MiroFlow usa `base_url=http://localhost:11434/v1` (OpenAI-compatible endpoint). Versões antigas do Ollama (<0.1.24) não tinham esse endpoint.
**Why it happens:** Ollama 0.18.2 (confirmado no servidor) suporta `/v1/chat/completions`. Mas o endpoint retorna erro se o modelo não estiver carregado.
**How to avoid:** Verificar `GET http://localhost:11434/api/tags` antes de iniciar o agente. Pré-carregar o modelo com `ollama run deepseek-r1:14b` ou `ollama pull`.
**Warning signs:** `404 Not Found` na URL `/v1/chat/completions`, ou `model not found` no corpo da resposta.
### Pitfall 2: MiroFlow requer dependências Python não instaladas no container Node
**What goes wrong:** O `Dockerfile` principal é Node.js Alpine não tem Python. Se tentar importar `miroflow` no contexto Node (via python-bridge ou similar), vai falhar.
**Why it happens:** A tentativa de `from omegaconf import ...` falha com `ModuleNotFoundError` porque o sistema Python não tem as deps do MiroFlow.
**How to avoid:** Manter a separação clara: MiroFlow roda SOMENTE em `Dockerfile.python` como microserviço FastAPI. Node nunca importa Python diretamente.
**Warning signs:** Erro de `ModuleNotFoundError: No module named 'omegaconf'` nos logs do app principal.
### Pitfall 3: Timeout do fetch() insuficiente para modelos grandes
**What goes wrong:** `deepseek-r1:14b` com raciocínio pode levar 60-120 segundos para responder. O fetch padrão tem timeout de 30s nos outros proxies.
**Why it happens:** O `bi/engine-proxy.ts` usa `BI_ENGINE_TIMEOUT = 30000`. Para LLMs de 14B, isso é insuficiente.
**How to avoid:** Usar `AbortSignal.timeout(300_000)` (5 minutos) no proxy MiroFlow. Implementar streaming ou polling se necessário.
**Warning signs:** `AbortError: The operation was aborted` nos logs do Node.
### Pitfall 4: Neo4j não está ativo por padrão — usar graph_nodes do PostgreSQL
**What goes wrong:** O `docker-compose.yml` tem Neo4j no profile `[kg]` não está ativo em deploy padrão.
**Why it happens:** Neo4j é opcional; o KG do Arcádia usa `graph_nodes` PostgreSQL por padrão.
**How to avoid:** Para imutabilidade, usar `POST /api/graph/nodes` (PostgreSQL), não o driver `neo4j` Python direto. O `server/graph/service.ts` implementa isso com hashing.
**Warning signs:** `Connection refused: 7687` se tentar conectar ao Neo4j sem o profile ativo.
### Pitfall 5: MiroFlow não tem `llama3.1:8b` instalado
**What goes wrong:** O agente Researcher usa `llama3.1:8b` mas esse modelo não está no Ollama (`False` confirmado por inspeção direta).
**Why it happens:** Apenas `deepseek-r1:14b`, `arcadia-agent:latest`, `llama3.2:3b` e `nomic-embed-text` foram instalados.
**How to avoid:** Incluir `ollama pull llama3.1:8b` como step explícito na Wave 0 do plano.
**Warning signs:** Agente Researcher falha com `model 'llama3.1:8b' not found`.
### Pitfall 6: BiWorkspace.tsx é uma página grande — cuidado ao modificar
**What goes wrong:** `BiWorkspace.tsx` tem muitas funcionalidades (datasets, charts, dashboards, backup, BI engine). Modificação descuidada pode quebrar features existentes.
**Why it happens:** O arquivo tem ~1000+ linhas com múltiplas tabs e estados.
**How to avoid:** Adicionar MiroFlowControl como componente SEPARADO importado, não modificar lógica existente. Adicionar apenas uma nova `TabsTrigger` e `TabsContent`.
**Warning signs:** Erros de TypeScript em outros tabs depois da edição.
---
## Code Examples
Verified patterns from official sources:
### MiroFlow Agent com Ollama (config inline Python)
```python
# Source: server/modules/miroflow/miroflow/agents/base.py + config/llm/base.yaml
import sys
sys.path.insert(0, "/app/server/modules/miroflow")
import asyncio
from miroflow.agents.factory import build_agent
from miroflow.agents.context import AgentContext
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
def make_agent_cfg(model: str) -> dict:
return {
"type": "IterativeAgentWithToolAndRollback",
"name": f"arcadia_{model.replace(':', '_')}",
"max_turns": 10,
"llm": {
"provider_class": "UnifiedOpenAIClient",
"model_name": model,
"api_key": "ollama",
"base_url": f"{OLLAMA_BASE_URL}/v1",
"max_tokens": 8192,
"temperature": 0.7,
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
"reasoning_effort": None, "repetition_penalty": 1.0,
"max_context_length": -1, "async_client": True,
"disable_cache_control": True, "keep_tool_result": -1,
"use_tool_calls": False, "oai_tool_thinking": False,
},
}
async def run_agent(agent_type: str, task: str) -> str:
model = "deepseek-r1:14b" if agent_type in ("statistician", "fiscal_auditor") else "llama3.1:8b"
cfg = make_agent_cfg(model)
agent = build_agent(cfg)
ctx = AgentContext(task_description=task)
result = await agent.run(ctx)
return result.get("summary", str(result))
```
### FastAPI endpoint pattern (seguindo bi_analysis_service.py)
```python
# Source: server/python/bi_analysis_service.py (padrão existente)
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Arcádia MiroFlow Service", version="1.0.0")
class AnalyzeRequest(BaseModel):
agent: str # "statistician" | "fiscal_auditor" | "researcher"
task: str
context: dict = {}
tenant_id: int | None = None
class AnalyzeResponse(BaseModel):
agent: str
model: str
result: str
execution_id: str
duration_ms: int
@app.post("/analyze", response_model=AnalyzeResponse)
async def analyze(req: AnalyzeRequest):
...
```
### Express proxy para MiroFlow (seguindo engine-proxy.ts)
```typescript
// Source: server/bi/engine-proxy.ts (padrão existente)
const MIROFLOW_URL = `http://${process.env.MIROFLOW_HOST || "localhost"}:${
process.env.MIROFLOW_PORT || "8006"
}`;
export function registerMiroFlowRoutes(app: Express): void {
app.post("/api/miroflow/analyze", async (req: Request, res: Response) => {
if (!req.isAuthenticated()) return res.status(401).json({ error: "Não autenticado" });
try {
const response = await fetch(`${MIROFLOW_URL}/analyze`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...req.body, tenant_id: (req.user as any)?.tenantId }),
signal: AbortSignal.timeout(300_000),
});
const data = await response.json();
// Registrar no KG
await registerExecutionInKG(req, data);
res.json(data);
} catch (err: any) {
res.status(502).json({ error: err.message });
}
});
app.get("/api/miroflow/health", async (_req, res) => {
try {
const r = await fetch(`${MIROFLOW_URL}/health`, { signal: AbortSignal.timeout(5000) });
res.json({ online: r.ok, url: MIROFLOW_URL });
} catch {
res.json({ online: false, url: MIROFLOW_URL });
}
});
}
```
### Registro no KG (graph_nodes)
```typescript
// Source: server/graph/service.ts createNode()
import { createNode } from "../graph/service";
import crypto from "crypto";
async function registerExecutionInKG(req: any, execData: any) {
const auditHash = crypto
.createHash("sha256")
.update(JSON.stringify(execData))
.digest("hex");
await createNode({
type: "miroflow_execution",
tenantId: req.user?.tenantId,
data: { ...execData, auditHash, immutable: true },
});
}
```
---
## State of the Art
| Old Approach | Current Approach | When Changed | Impact |
|--------------|------------------|--------------|--------|
| Llamaindex/LangChain para agentes | MiroFlow (framework próprio MiromindAI) | 2025-08 | Performance-first, benchmarks públicos, no submodule |
| OpenAI API direta | Ollama local via LiteLLM gateway | 2025 (Arcádia) | Soberania total dos dados |
| Neo4j obrigatório para KG | graph_nodes PostgreSQL + Neo4j opcional | Phase 1 | Sem dependência de serviço extra em produção básica |
| Hydra config files externos | Config dict inline (Python) | MiroFlow 1.x | Sem dependência de paths, mais fácil em containers |
**Deprecated/outdated:**
- MiroFlow via CLI (`python run_single_task.py`): adequado para benchmark, mas não para microserviço de produção. Usar `web_app/main.py` como referência OU criar FastAPI própria.
- Modelos > 14B: explicitamente proibidos na arquitetura do projeto.
---
## Open Questions
1. **`llama3.1:8b` vs `llama3.2:3b` para Researcher**
- What we know: `llama3.1:8b` não está instalado; `llama3.2:3b` está (2GB, já disponível)
- What's unclear: Se João quer baixar `llama3.1:8b` (4.7GB a mais) ou aceitar `llama3.2:3b` como substituto para o Researcher
- Recommendation: Plano deve incluir `ollama pull llama3.1:8b` como Wave 0, com fallback para `llama3.2:3b` se bandwidth for problema
2. **MiroFlow via web_app existente vs FastAPI própria**
- What we know: `server/modules/miroflow/web_app/main.py` é uma FastAPI completa com session manager e task executor; `server/python/bi_analysis_service.py` é um FastAPI simples sem estado
- What's unclear: Usar o `web_app` completo do MiroFlow (mais funcionalidades) ou escrever `miroflow_service.py` minimal seguindo o padrão de outros serviços
- Recommendation: Escrever `miroflow_service.py` minimal — mais simples, sem session management, sem frontend próprio do MiroFlow
3. **Registro de execuções: graph_nodes vs skill_executions**
- What we know: `skill_executions` é específico para Skills Arcádia; `graph_nodes` é KG genérico
- What's unclear: O requirement diz "KG" — se é necessário usar o Neo4j real ou PostgreSQL `graph_nodes` é suficiente
- Recommendation: Usar `graph_nodes` PostgreSQL com `type: "miroflow_execution"` — atende o requisito de imutabilidade sem depender do Neo4j (profile opcional)
4. **Posicionamento do MiroFlowControl na UI**
- What we know: `BiWorkspace.tsx` já tem sistema de tabs (LayoutDashboard, Database, BarChart3...); `Scientist.tsx` já existe como página standalone de análise científica
- What's unclear: Se o toggle deve ficar dentro do BiWorkspace (access contextual) ou se deve ser um botão flutuante ou se Scientist.tsx deve ser integrado ao BI
- Recommendation: Tab "Científico" dentro de `BiWorkspace.tsx` — mais coerente com o objetivo "integrado ao Superset"
---
## Validation Architecture
> `workflow.nyquist_validation` não encontrado em `.planning/config.json` (arquivo não existe). Tratado como habilitado.
### Test Framework
| Property | Value |
|----------|-------|
| Framework | pytest (Python) + vitest/jest implícito (TS) |
| Config file | Nenhum — Wave 0 deve criar `pytest.ini` para o microserviço |
| Quick run command | `pytest server/python/test_miroflow_service.py -x` |
| Full suite command | `pytest server/python/ -v` |
### Phase Requirements → Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------|
| REQ-3.1 | MiroFlow configurado para Ollama local ≤14B | integration | `pytest tests/test_miroflow_ollama.py -x` | ❌ Wave 0 |
| REQ-3.2 | Agente Statistician analisa SQL com deepseek-r1:14b | integration | `pytest tests/test_miroflow_service.py::test_statistician -x` | ❌ Wave 0 |
| REQ-3.3 | Agente Fiscal Auditor valida NFe/SPED com deepseek-r1:14b | integration | `pytest tests/test_miroflow_service.py::test_fiscal_auditor -x` | ❌ Wave 0 |
| REQ-3.4 | Agente Researcher consulta KG com llama3.1:8b | integration | `pytest tests/test_miroflow_service.py::test_researcher -x` | ❌ Wave 0 |
| REQ-3.5 | `POST /api/miroflow/analyze` retorna análise estruturada | integration | `curl -X POST localhost:5000/api/miroflow/analyze ...` | ❌ Wave 0 |
| REQ-3.6 | MiroFlowControl.tsx toggle aparece no Superset/BiWorkspace | manual | Inspeção visual no browser | N/A |
| REQ-3.7 | Execuções registradas com imutabilidade no KG | unit | `pytest tests/test_miroflow_kg.py -x` | ❌ Wave 0 |
### Sampling Rate
- **Per task commit:** `pytest server/python/test_miroflow_service.py -x`
- **Per wave merge:** `pytest server/python/ -v`
- **Phase gate:** Health check `/api/miroflow/health` retorna `{"online": true}` + todos os 3 agentes respondem com modelo correto
### Wave 0 Gaps
- [ ] `server/python/test_miroflow_service.py` — cobre REQ-3.1, 3.2, 3.3, 3.4, 3.5, 3.7
- [ ] `server/python/miroflow_service.py` — o microserviço principal
- [ ] `server/miroflow/routes.ts` + `engine-proxy.ts` — rotas Express
- [ ] `client/src/components/MiroFlowControl.tsx` — componente React
- [ ] `ollama pull llama3.1:8b` — modelo necessário para Researcher (Wave 0 setup)
- [ ] Instalar dependências MiroFlow: `pip install omegaconf hydra-core openai` (ou `uv sync` no diretório do submodule)
---
## Sources
### Primary (HIGH confidence)
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/pyproject.toml` — versão 1.7.0, dependências confirmadas
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/llm/openai_client.py``UnifiedOpenAIClient`, `base_url` configurável
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/agents/base.py``BaseAgent`, `AgentContext`, `build_agent()`
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/config/llm/base.yaml` — campos obrigatórios do LLM config
- Inspeção direta: `/opt/arcadia_merged/server/bi/engine-proxy.ts` — padrão estabelecido de proxy Node → Python
- Inspeção direta: `/opt/arcadia_merged/server/python/bi_analysis_service.py` — padrão do microserviço FastAPI
- Inspeção direta: `/opt/arcadia_merged/docker/litellm-config.yaml``deepseek-r1:14b` já configurado como `arcadia-default`
- Inspeção direta: `/opt/arcadia_merged/docker-compose.prod.yml``OLLAMA_BASE_URL` configurado, serviço `ollama` com profile `[ai]`
- Runtime check: `curl http://localhost:11434/api/tags` — modelos instalados confirmados
- Runtime check: `curl http://localhost:11434/api/version` — Ollama 0.18.2 com `/v1` support
### Secondary (MEDIUM confidence)
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/agents/iterative_agent_with_rollback.py` — tipo `IterativeAgentWithToolAndRollback` confirmado
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/web_app/main.py` — FastAPI web_app existente no submodule (alternativa não usada)
- Inspeção direta: `/opt/arcadia_merged/server/graph/service.ts``createNode()`, `graph_nodes` tabela
### Tertiary (LOW confidence)
- Inferência sobre tempo de resposta do `deepseek-r1:14b` (60-120s) baseada em conhecimento geral de modelos 14B — não medido diretamente no servidor
---
## Metadata
**Confidence breakdown:**
- Standard stack: HIGH — verificado diretamente nos arquivos do submodule e nas dependências instaladas
- Architecture: HIGH — padrões copiados de código existente funcionando em produção
- Pitfalls: HIGH (3-5) e MEDIUM (1-2) — baseados em inspeção direta do runtime e código
- Ambiente: HIGH — testado com curl direto ao Ollama e listagem de modelos
**Research date:** 2026-03-25
**Valid until:** 2026-04-25 (30 dias; stack estável, Ollama versão fixada)
---
## RESEARCH COMPLETE