Skip to content

Repository files navigation

 █████╗ ███████╗███████╗███████╗███████╗███████╗ ██████╗ ██████╗    █████╗ ██╗
██╔══██╗██╔════╝██╔════╝██╔════╝██╔════╝██╔════╝██╔═══██╗██╔══██╗  ██╔══██╗██║
███████║███████╗███████╗█████╗  ███████╗███████╗██║   ██║██████╔╝  ███████║██║
██╔══██║╚════██║╚════██║██╔══╝  ╚════██║╚════██║██║   ██║██╔══██╗  ██╔══██║██║
██║  ██║███████║███████║███████╗███████║███████║╚██████╔╝██║  ██║  ██║  ██║██║
╚═╝  ╚═╝╚══════╝╚══════╝╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚═╝  ╚═╝  ╚═╝  ╚═╝╚═╝

Assistente pessoal de finanças e agenda construído com LangChain + LangGraph.
O sistema usa uma arquitetura multi-agente onde cada agente tem uma responsabilidade bem definida:
classificar a intenção, processar o domínio correto e formatar a resposta final para o usuário.

Python LangChain LangGraph PostgreSQL MongoDB Redis Qdrant


O que o Assessor.AI faz

O Assessor.AI atua como um parceiro pessoal que responde perguntas e executa ações em dois domínios:

Finanças pessoais

  • Registra, consulta e atualiza transações (gastos, receitas, transferências)
  • Calcula saldo total e saldo por dia
  • Classifica transações por categoria (comida, transporte, lazer, saúde, etc.)
  • Gera diagnósticos e recomendações financeiras com base nos dados reais do banco

Agenda e compromissos

  • Cria, consulta e atualiza eventos
  • Consulta eventos do dia
  • Gerencia localização, horários e observações de cada evento

Para tudo fora desses dois escopos (small talk, saudações, perguntas fora de área), o próprio roteador responde diretamente ao usuário.


Diagrama de agentes

flowchart LR
    U(["Usuário"])
    GE["Guardrail Entrada"]
    R["Router"]
    F["Financeiro"]
    A["Agenda"]
    FAQ["FAQ"]
    O["Orquestrador"]
    GS["Guardrail Saída"]
    E(["Fim"])

    U --> GE
    GE -->|"bloqueado"| E
    GE -->|"aprovado"| R
    R -->|"ROUTE=financeiro"| F
    R -->|"ROUTE=agenda"| A
    R -->|"ROUTE=faq"| FAQ
    R -->|"fora de escopo"| E
    F --> O
    A --> O
    O --> GS
    FAQ --> GS
    GS --> E
Loading

Estrutura do projeto

assessor-ai/
├── main.py                          # Dispatcher — `python main.py tui|api`
├── pyproject.toml                   # Dependências do projeto
│
├── src/assessor_ai/
│   ├── api/                         # Camada HTTP (FastAPI)
│   │   ├── app.py                   # App FastAPI — routers, handlers de erro, middleware, rotas A2A
│   │   ├── lifespan.py              # Compila grafo + checkpointer no startup; dispose no shutdown
│   │   ├── exception_handlers.py    # Erros de domínio -> status HTTP + ErrorResponse{detail, code}
│   │   ├── auth.py                  # get_current_user via X-API-Key; verify_signup_secret
│   │   ├── gen_key.py               # generate_api_key
│   │   └── routes/                  # chats.py, health.py, keys.py, users.py
│   │
│   ├── services/                    # Casos de uso — não conhecem HTTP
│   │   ├── chat_service.py          # create_chat, send_message, get_history, validar_ownership, encerrar_sessao
│   │   ├── runner.py                # Invoca fluxo_agentes (graph/builder.py) via ainvoke, propaga tags/metadata pro LangSmith
│   │   └── exceptions.py            # ChatNaoEncontrado, ChatDeOutroUsuario, LimiteDeMensagensExcedido, FalhaNoAgente
│   │
│   ├── repositories/
│   │   └── chat_repository.py       # Fachada sobre graph/tools/chats, graph/tools/usuarios e infra/cache
│   │
│   ├── schemas/                     # Contratos de dados
│   │   ├── models.py                # ChatMessage, Role — contrato interno, independente de Mongo/tool
│   │   ├── chat.py                  # MessageCreate, ChatSummary, ChatMessageResponse (HTTP)
│   │   ├── errors.py                # ErrorCode e ErrorResponse
│   │   ├── health.py                # HealthStatus e HealthCheckResponse
│   │   ├── key.py                   # APIKey e contrato de criação de chave
│   │   └── user.py                  # UserID e contratos de usuário
│   │
│   ├── tui/                         # Interface Textual
│   │   ├── app.py                   # AssessorTUI — tela de chat
│   │   ├── display.py               # Bubble (Rich Panel), MessageRow, Pensando (LoadingIndicator)
│   │   └── app.tcss                 # Stylesheet do Textual
│   │
│   ├── a2a/                         # Protocolo A2A (JSON-RPC), montado no mesmo app FastAPI
│   │   ├── main.py                  # montar_rotas(app) — agent card + endpoint JSON-RPC em /a2a
│   │   └── agents/
│   │       ├── card.py              # AgentCard (nome, versão, interface, skills)
│   │       ├── capabilites.py       # AgentSkill(s) expostas no card
│   │       └── interface.py         # AssessorAgentExecutor — ponte pro services/chat_service.py
│   │
│   ├── config.py                    # Env vars via pydantic-settings; credenciais em SecretStr
│   ├── models.py                    # PROVIDER_MAP, BUILDERS, Model Enum
│   ├── logging.py                   # ColorFormatter, get_logger e o decorator log_tool
│   ├── privacy.py                   # Regex de PII + anonimizar_entrada
│   ├── identifiers.py               # UserID, ChatID, APIKey, APIKeyHash e geradores de IDs
│   ├── graph/
│   │   ├── agents/                  # Agentes compilados, nodes e prompts do LangGraph
│   │   │   ├── __init__.py          # router_app, financeiro_app, agenda_app, faq_app, orquestrador_app
│   │   │   ├── nodes/
│   │   │   │   ├── names.py         # NodeName e constantes dos nodes
│   │   │   │   ├── router.py        # no_roteador
│   │   │   │   ├── financeiro.py    # no_financeiro
│   │   │   │   ├── agenda.py        # no_agenda
│   │   │   │   ├── faq.py           # no_faq
│   │   │   │   ├── orquestrador.py  # no_orquestrador
│   │   │   │   └── guardrail/       # Nodes e schemas dos guardrails
│   │   │   └── prompts/             # Prompts .md, loader e contratos de seções
│   │   │       ├── loader.py        # load_prompt/load_sections e contexto do turno
│   │   │       └── *.md             # Prompts dos agentes e templates dos guardrails
│   │   ├── state.py                  # Estado, EstadoUpdate e Route
│   │   ├── llm.py                   # build_llm e instâncias de LLM
│   │   └── builder.py               # Construção e compilação do grafo LangGraph
│   │
│   ├── infra/                        # Conexões compartilhadas entre camadas e features
│   │   ├── postgres.py               # PostgresConn, Base, @transacional e current_user_id
│   │   ├── mongo.py                  # MongoConn + MongoRepo
│   │   ├── redis.py                  # RedisConn
│   │   ├── qdrant.py                 # QdrantConn + modelo de embedding
│   │   └── cache.py                  # Cache do perfil no Redis
│   │
│   └── graph/tools/                  # Features usadas pelos agentes e repositórios internos
│       ├── financeiro/
│       │   ├── models.py             # Transaction, Category, TransactionType, PaymentType
│       │   ├── schemas.py            # Schemas Pydantic dos argumentos das tools
│       │   └── repo.py               # FinanceiroRepo — 5 tools: add/query/update_transaction, total/daily_balance
│       ├── agenda/
│       │   ├── models.py             # Event
│       │   ├── schemas.py            # Schemas Pydantic dos argumentos das tools
│       │   └── repo.py               # AgendaRepo — 4 tools: add_event, query_events, query_daily_events, update_event
│       ├── faq/
│       │   ├── schemas.py            # FaqRetrieverArgs, SearchResponse
│       │   ├── repo.py               # FaqRepo — tool faq_retriever (busca semântica no Qdrant)
│       │   └── ingest.py             # Script (`python -m ...faq.ingest`) que indexa o PDF de FAQ
│       ├── chats/                    # Interno (não é tool do LLM)
│       │   ├── schemas.py            # ChatDocument, Role, Mensagem
│       │   ├── helpers.py            # gerar_resumo, gerar_perfil
│       │   └── repo.py               # ChatsRepo — criar, buscar, atualizar_mensagens, encerrar_sessao
│       ├── usuarios/                 # Interno — única feature que cruza os três bancos
│       │   ├── models.py             # User (linha de FK no Postgres)
│       │   ├── schemas.py            # UserDocument + chaves/TTL da API key
│       │   └── repo.py               # UsuariosRepo — cadastro/perfil (Mongo), garantir_usuario (Mongo+PG), API key (Redis)
│       └── response.py               # ResponseStatus, ToolResponse e classe Response
│
├── alembic/                         # Migrations versionadas do schema PostgreSQL
│
└── data/
    └── documents/                   # PDFs para RAG
        └── FAQ_assessor_v1.1.pdf

Fluxo dos agentes

Usuário
│
▼
[Guardrail Entrada]  ──── bloqueado ───► encerra (sem persistir no histórico)
│  detecta prompt injection e acesso a dados internos (determinístico)
│  classifica a mensagem via LLM (APROVADO | OFENSIVO | PERIGOSO | ILICITO | ...)
│  anonimiza PII antes de passar adiante
│
▼
[Router]  ──── small talk / fora de escopo ───► responde diretamente ao usuário
│
│ ROUTE=financeiro|agenda|faq
▼
[Especialista]  (Financeiro, Agenda ou FAQ)
│  consulta/escreve no banco via tools
│  popula resposta_especialista no estado
▼
[Orquestrador]  (apenas Financeiro e Agenda)
│  recebe o JSON do especialista + histórico da conversa
│  formata a resposta em linguagem natural
▼
[Guardrail Saída]
│  redige PII remanescente
│  revisa compliance (CVM/ANBIMA): remove garantias de rentabilidade e recomendações de ativos sem disclaimer
▼
Usuário

Agentes em detalhe

Agente Modelo Responsabilidade
Guardrail Entrada gemini-2.5-flash (temp 0.0) Bloqueia mensagens indevidas e anonimiza PII
Router openai/gpt-oss-120b (temp 0.0) Classifica a intenção e emite ROUTE=financeiro|agenda|faq, ou responde diretamente
Financeiro gemini-2.5-flash + fallback openai/gpt-oss-120b Interpreta a pergunta financeira e chama as tools do banco
Agenda gemini-2.5-flash + fallback openai/gpt-oss-120b Interpreta perguntas de agenda e chama as tools de eventos
FAQ openai/gpt-oss-120b (temp 0.0) Consulta o PDF via RAG e responde dúvidas sobre o sistema
Orquestrador openai/gpt-oss-120b (temp 0.0) Formata a resposta do especialista em linguagem natural
Guardrail Saída openai/gpt-oss-120b (temp 0.0) Revisa compliance e redige PII na resposta final

Guardrails

Entrada

O guardrail de entrada executa verificações em ordem de custo crescente:

  1. Detecção determinística — regex para prompt injection e keywords de acesso a dados internos
  2. Anonimização de PII — substitui CPF, CNPJ, número de conta, cartão, e-mail e telefone por tokens antes de passar ao LLM
  3. Classificação LLM — categoriza a mensagem em APROVADO, OFENSIVO, PERIGOSO, ILICITO, POLITICO ou INDICACAO_INVEST

Mensagens bloqueadas não são persistidas no histórico.

Saída

O guardrail de saída nunca bloqueia — apenas revisa:

  1. Redação de PII — remove dados pessoais remanescentes da resposta (CPF, CNPJ, número de conta e cartão)
  2. Compliance CVM/ANBIMA — corrige afirmações que garantam rentabilidade futura ou recomendem ativos sem disclaimer de risco

Tools

Financeiro (PostgreSQL)

Tool Descrição
add_transaction Insere uma transação (amount, tipo, categoria, método de pagamento)
query_transactions Consulta transações com filtros por data, tipo e texto
update_transaction Atualiza transação por ID ou por busca de texto + data
total_balance Retorna saldo total (INCOME − EXPENSES)
daily_balance Retorna saldo de um dia específico

Tipos de transação: INCOME (1), EXPENSES (2), TRANSFER (3).
Categorias: comida, besteira, estudo, férias, transporte, moradia, saúde, lazer, contas, investimento, presente, outros.

Agenda (PostgreSQL)

Tool Descrição
add_event Insere um evento (título, horário, local, observações)
query_events Consulta eventos com filtros por período e título
query_daily_events Retorna todos os eventos de um dia específico
update_event Atualiza evento por ID ou por busca de texto + data

FAQ (RAG)

Tool Descrição
faq_retriever Busca semântica no PDF de FAQ via Qdrant + Gemini Embeddings (graph/tools/faq/)

Indexação: python -m assessor_ai.graph.tools.faq.ingest (script separado da tool, roda sob demanda).


Persistência

Camada Tecnologia Responsabilidade
Transações e eventos PostgreSQL Dados financeiros e de agenda do usuário
Histórico de conversa MongoDB Mensagens por sessão (últimas 5 por consulta)
Checkpointing de grafo LangGraph AsyncPostgresSaver Estado interno do grafo entre turnos, persistido no PostgreSQL
Cache de perfil, rate limit, API keys Redis Cache do perfil_usuario (TTL 1h), limite de mensagens por user_id na janela de 60s, hash de API keys da API

O MongoDB armazena users (cadastro e perfil comportamental) e chats (histórico de mensagens por sessão). O histórico de mensagens é limitado via projeção $slice: -5 para evitar contextos longos demais.

O campo perfil_usuario — gerado a partir do histórico acumulado e armazenado em users — é carregado no estado do grafo antes de cada invocação, servindo como contexto cross-session do usuário. É lido do Redis primeiro (infra/cache.py); só cai no Mongo em cache miss, e o cache é invalidado ao encerrar a sessão (quando o perfil pode ter sido atualizado a partir do resumo).

O RAG do FAQ roda sobre o Qdrant (graph/tools/faq/) — substituiu o índice FAISS local.


Configuração

Variáveis de ambiente

GEMINI_API_KEY=...
GROQ_API_KEY=...
POSTGRES_URL=postgresql://usuario:senha@host:5432/banco
MONGO_URL=mongodb://usuario:senha@host:27017/
MONGO_COLLECTION_NAME=assessor
REDIS_URL=redis://host:6379/0
QDRANT_URL=http://host:6333
QDRANT_COLLECTION_NAME=faq
SIGNUP_SECRET=...
LANGSMITH_TRACING=false
LANGSMITH_API_KEY=...
LANGSMITH_PROJECT=assessor-ai
API_KEY_AUTH_ENABLED=true

Ver .env.example para a referência completa (inclui QDRANT_API_KEY opcional, usada só em instâncias cloud do Qdrant). SIGNUP_SECRET é obrigatório — sem ele Settings() falha ao importar; é o valor exigido no header X-Signup-Secret do POST /v1/keys. LANGSMITH_* é opcional — só ativa tracing/observabilidade do grafo se LANGSMITH_TRACING=true (ver seção Observabilidade abaixo). API_KEY_AUTH_ENABLED é opcional (default true) — com false, /v1/chats para de exigir X-API-Key e passa a reaproveitar/criar um usuário padrão a cada request (chat_service.obter_usuario_padrao, mesmo bootstrap do terminal/TUI); é um desligamento temporário pro estágio atual do projeto, não uma remoção — a chave de rota, api/auth.py:get_current_user, continua existindo e testada (ver TODO.md). A2A_BASE_URL=http://localhost:8000


Ver [.env.example](.env.example) para a referência completa (inclui `QDRANT_API_KEY` opcional, usada só em instâncias cloud do Qdrant). `SIGNUP_SECRET` é obrigatório — sem ele `Settings()` falha ao importar; é o valor exigido no header `X-Signup-Secret` do `POST /v1/keys`. `LANGSMITH_*` é opcional — só ativa tracing/observabilidade do grafo se `LANGSMITH_TRACING=true` (ver seção [Observabilidade](#observabilidade) abaixo). `A2A_BASE_URL` é opcional (default já cobre execução local) — só muda a URL declarada no `AgentCard` do A2A (ver seção [A2A](#a2a) acima).

### Instalação

```bash
uv venv
uv sync

Execução

python main.py tui        # interface Textual
python main.py api        # sobe a API FastAPI via uvicorn em 0.0.0.0:8000

Postgres, Mongo, Redis e Qdrant são serviços em nuvem — não há infra local pra subir. Na TUI, digite /exit (ou Ctrl+C) pra encerrar a sessão.

Também dá pra rodar via justfile: just venv (cria .venv), just run [modo] (default tui) e just dev [modo] (mesma coisa, injetando env vars via infisical run --).

A2A

python main.py api também expõe o protocolo A2A (agent-to-agent), montado no mesmo app FastAPI:

  • GET /.well-known/agent-card.jsonAgentCard com nome, versão e as skills expostas (capabilites.py):

    • moneysaving — registra e consulta transações, saldo total/diário e gastos por categoria e período
    • agenda — cria, consulta e atualiza compromissos do calendário
    • faq — perguntas sobre o que o assistente faz e como usá-lo

    As skills são só metadado de discovery; POST /a2a roteia toda mensagem pelo grafo completo (o router decide o domínio), independente de qual skill o cliente achou no card.

  • POST /a2a — endpoint JSON-RPC (método SendMessage) que processa a mensagem via AssessorAgentExecutor, a mesma camada services/chat_service.py usada por terminal/TUI/API. Cada context_id do protocolo vira uma sessão/usuário do Assessor — na primeira mensagem, ou sem context_id.

Primeira versão, sem tarefas assíncronas (streaming/push_notifications desligados no AgentCard) nem autenticação — a rota está aberta de propósito, porque a auth por API key atrapalha o caso de uso A2A entre agentes (ver TODO.md).

Migrations (Alembic)

Schema do PostgreSQL versionado em alembic/versions/ (POSTGRES_URL configurado):

uv run alembic upgrade head

O acesso a dados usa SQLAlchemy ORM (graph/tools/{financeiro,agenda,usuarios}/models.py), então --autogenerate funciona normalmente a partir daqui:

uv run alembic revision --autogenerate -m "..."

Sempre revise o diff gerado antes de aplicar — e rode --autogenerate sem alterações pendentes de vez em quando pra garantir que os models continuam batendo exatamente com o schema real (diff vazio).


Observabilidade

Tracing dos agentes via LangSmith — opcional, desligado por padrão (LANGSMITH_TRACING=false). Quando ligado, graph.ainvoke() (services/runner.py:executar) é rastreado automaticamente pelo LangChain/LangGraph, incluindo cada nó (guardrail, router, financeiro, agenda, faq, orquestrador) e cada chamada de LLM — sem precisar instrumentar nada à mão. runner.py também passa tags=["chat"] e metadata={"user_id", "session_id"} no config do invoke(), propagados automaticamente pra todo run filho, permitindo filtrar/auditar traces por usuário ou sessão no painel do LangSmith.

Pontos de I/O que o LangChain não rastreia sozinho (repositories/chat_repository.py:buscar_perfil, buscar_historico, salvar_mensagens) usam @traceable manual, com process_inputs/ process_outputs redigindo PII (reaproveitando anonimizar_entrada do guardrail) antes de subir pro LangSmith Cloud.

Limitação conhecida: essa redação cobre só os pontos com @traceable manual — o run raiz do LangGraph e o próprio nó de guardrail de entrada (auto-rastreados) ainda logam a mensagem crua do usuário como input, já que a anonimização só acontece no output desse nó. Ver TODO.md pra mais contexto antes de ligar tracing em produção com dado real.


Dependências principais

  • LangChain — framework de agentes e tools
  • LangGraph — orquestração stateful e checkpointing
  • LangSmith — tracing/observabilidade opcional do grafo (ver Observabilidade)
  • FastAPI — API HTTP (api/), com slowapi pro rate limit por IP
  • a2a-sdk — protocolo A2A (a2a/), agent card + JSON-RPC montados no mesmo app FastAPI
  • Textual — TUI (tui/)
  • SQLAlchemy — ORM sobre o PostgreSQL (graph/tools/{financeiro,agenda,usuarios}/models.py), com psycopg2 como driver
  • Alembic — migrations versionadas do schema PostgreSQL
  • pymongo — driver MongoDB para histórico de conversa
  • redis-py — cache de perfil, rate limit de mensagens e API keys (infra/redis.py)
  • qdrant-client — busca vetorial para RAG do FAQ (graph/tools/faq/)
  • Rich + pyfiglet — interface de terminal e arte ASCII da TUI
  • Pydantic — validação de schemas das tools
  • langchain-anthropic, langchain-google-genai, langchain-groq — integrações com providers

About

Assistente pessoal multiagente para finanças e agendamento. Feito com LangChain/LangGraph, Postgres, MongoDB, Redis e Qdrant.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages