# 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`.