Files
AI-inovyo-assistende-db/docs/architecture.md
2026-05-14 15:29:03 -03:00

165 lines
9.0 KiB
Markdown

# Arquitetura
## Visão Geral
O Assistente Analítico é composto por três camadas principais: **interface** (Streamlit e FastAPI), **agente de IA** (LangGraph com Bedrock) e **dados** (DynamoDB).
```
┌─────────────────────────────────────────────────────────┐
│ Interfaces │
│ ┌────────────────────┐ ┌────────────────────────┐ │
│ │ Streamlit (8501) │ │ FastAPI (8000) │ │
│ │ front.py │ │ api.py │ │
│ └────────┬───────────┘ └──────────┬─────────────┘ │
│ │ │ │
│ └──────────┬──────────────┘ │
│ ▼ │
│ orquestrador.main(query, history, model, base) │
│ │ │
│ ┌───────────────────▼──────────────────────────┐ │
│ │ agent_bedrock — LangGraph │ │
│ │ ┌────────┐ ┌───────┐ ┌────────────┐ │ │
│ │ │ Model │───▶│Router │───▶│ Tools │ │ │
│ │ │ Node │◀───│ │ │ Node │ │ │
│ │ └────────┘ └───────┘ └────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Serviços AWS │ │
│ │ ┌──────────┐ ┌────────────────────────────┐ │ │
│ │ │ Bedrock │ │ DynamoDB │ │ │
│ │ │ (LLMs) │ │ (contexto + dados pré- │ │ │
│ │ └──────────┘ │ processados) │ │ │
│ │ └────────────────────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Observabilidade │ │
│ │ ┌──────────┐ ┌──────────────────────────┐ │ │
│ │ │ Langfuse │ │ CloudWatch Logs │ │ │
│ │ └──────────┘ └──────────────────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
## Módulos do Backend
O pacote `backend` é organizado por responsabilidade:
```
backend/
├── config.py ← variáveis de ambiente
├── dynamo.py ← depende de config
├── tools.py ← depende de config, dynamo
├── agent_bedrock.py ← depende de config, tools
└── orquestrador.py ← depende de config, dynamo, agent_bedrock
```
Não há dependências circulares entre os módulos.
### `config.py`
Lê todas as variáveis de ambiente na inicialização e exporta como constantes:
| Variável | Descrição |
|----------|-----------|
| `TABLE` | Tabela DynamoDB |
| `REGION` | Região AWS |
| `AWS_ACCOUNT` | ID da conta AWS |
| `SECRET_NAME` | Nome do secret no Secrets Manager |
### `dynamo.py`
- Instancia o cliente `dynamodb` (boto3) usando `REGION`
- `get_secret()` — busca credenciais do Langfuse no Secrets Manager
- Inicializa o objeto `langfuse` na carga do módulo
- `get_contexto(dashboard: str) -> dict` — carrega contexto, filtro e itens disponíveis do DynamoDB para o dashboard informado
### `tools.py`
Define a classe `ReportTools`, instanciada por requisição com o mapeamento de IDs local → real.
```
ReportTools(id_mapping: dict[str, str])
├── get_variable_value(id, variable) — busca uma variável no DynamoDB
├── get_variables_list(id) — lista as variáveis disponíveis para um ID
└── as_tools() -> list[StructuredTool] — retorna as tools prontas para o agente
```
| Tool (nome exposto ao LLM) | Método | Descrição |
|---------------------------|--------|-----------|
| `get_variable_value` | `get_variable_value(id, variable)` | Busca o valor de uma variável no DynamoDB para o ID informado |
| `get_variable_list` | `get_variables_list(id)` | Lista as variáveis disponíveis para o ID informado |
Os IDs expostos ao LLM são locais (`id_1`, `id_2`, …). Internamente cada método converte o ID local para o ID real do DynamoDB via `self.id_mapping`. Por usar estado de instância em vez de variável global, múltiplas requisições simultâneas ficam completamente isoladas.
### `agent_bedrock.py`
- `AgentState` — TypedDict com `messages` e `current_step`
- `create_bedrock_llm(model_id, region, tools)` — instancia `ChatBedrockConverse` e vincula as tools via `bind_tools`
- `call_model(state, llm)` — nó do grafo: invoca o LLM
- `call_tools(state, tools_map)` — nó do grafo: executa as tool calls usando o `tools_map` da requisição
- `should_continue(state)` — roteador: `"tools"` se há tool_calls, `"end"` se não
- `create_agent(inference_profile_arn, region, tools)` — constrói `tools_map = {t.name: t for t in tools}`, monta e compila o `StateGraph`
**Fluxo do Grafo:**
```
SystemMessage + HumanMessage
┌─────────┐
│ model │ ◄── LLM via Bedrock
└────┬────┘
should_continue?
├── tool_calls → ┌───────┐
│ │ tools │ → executa tools → volta ao model
│ └───────┘
└── (fim) → [END]
```
### `orquestrador.py`
Ponto de entrada da lógica de negócio. A função `main(user_query, history, model, base)`:
1. Chama `get_contexto(base)` para carregar o contexto do dashboard
2. Constrói `id_mapping` (`id_1`, `id_2`, … → IDs reais do DynamoDB) e `local_items` (IDs locais → descrições)
3. Instancia `ReportTools(id_mapping)` com o mapeamento isolado da requisição
4. Monta o `SYSTEM_PROMPT` dinamicamente com contexto, regras de filtro, `local_items` e histórico
5. Cria o agente via `create_agent(model, REGION, tools=report_tools.as_tools())`
6. Invoca o agente com o estado inicial
7. Agrega tokens de todos os `AIMessage` do estado final
8. Retorna `response`, `input_tokens`, `output_tokens`, `total_tokens`
## Interfaces
### `front.py` — Streamlit (porta 8501)
- Importa `from backend import orquestrador`
- Lista bases disponíveis consultando DynamoDB (`item_type_index`, `item_type = "contexto"`)
- Seleção de modelo LLM e base via `st.selectbox`
- Histórico de conversas em `session_state`
- Efeito de digitação caractere a caractere
- Exibe consumo de tokens por resposta
### `api.py` — FastAPI (porta 8000)
- Importa `from .backend import orquestrador`
- `GET /` — health check
- `POST /agent` — recebe `QueryRequest`, chama `orquestrador.main()`, retorna `QueryResponse`
## Modelos LLM Suportados
| Modelo | Provider | Prefixo de Rota |
|--------|----------|-----------------|
| `anthropic.claude-haiku-4-5-20251001-v1:0` | Anthropic | `us` |
| `anthropic.claude-sonnet-4-5-20250929-v1:0` | Anthropic | `global` |
| `meta.llama4-maverick-17b-instruct-v1:0` | Meta | `us` |
| `meta.llama4-scout-17b-instruct-v1:0` | Meta | `us` |
| `amazon.nova-lite-v1:0` | Amazon | `us` |
| `amazon.nova-pro-v1:0` | Amazon | `us` |
| `amazon.nova-2-lite-v1:0` | Amazon | `global` |
Todos acessados via AWS Bedrock inference profiles cross-region. O ARN é construído dinamicamente com `REGION` e `AWS_ACCOUNT`.