Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Agent Platform

Projeto de portfólio construído para cobrir, ponto a ponto, os requisitos da vaga de Engenheiro de IA Sênior (RD Station / plataforma MentorIA, via BossaBox) e da vaga de Engenheira(o) de Software Backend Sênior — Foco em IA (RD Station, via Greenhouse).

Não é um tutorial de "hello world com LLM" — é um serviço mínimo, mas real, de orquestração de agentes: API HTTP, memória de sessão, execução de ferramentas (tool calling), um servidor MCP exposto, testes automatizados e observabilidade básica (logs estruturados + métricas).

Caso de uso de referência: um agente de agendamento de serviços que consulta horários disponíveis e confirma agendamentos via ferramentas — um domínio comum em plataformas de atendimento via WhatsApp/chat.

Como cada requisito da vaga é coberto

Requisito da vaga Onde está no projeto
Desenvolver/evoluir plataforma de criação e operação de agentes de IA app/agents/orchestrator.py
Integrações com LLMs, ferramentas, APIs, MCPs e serviços externos app/tools/registry.py, app/tools/builtin.py, app/mcp/server.py
Componentes de memória, contexto, orquestração e execução de ferramentas app/core/context.py, app/core/memory.py, app/core/runtime.py
Escalabilidade, observabilidade, segurança e confiabilidade em produção app/observability/logging.py, endpoint /metrics, auth por API key (app/api/auth.py), tratamento de erro por ferramenta
Sistemas distribuídos, integrações via API, backend app/api/main.py (FastAPI), Dockerfile
Diferencial: MCP Servers e soluções integradas com IA app/mcp/server.py (SDK oficial mcp)
Boas práticas de engenharia, qualidade de código, documentação tests/, ruff, este README, docs/ARCHITECTURE.md

Rodando localmente

python -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
cp .env.example .env  # preencher ANTHROPIC_API_KEY e API_KEY

# Testes
pytest

# API
uvicorn app.api.main:app --reload

# Servidor MCP (stdio) — para plugar no Claude Desktop/Code
python -m app.mcp.server

# Demonstração autocontida (sem rede) — gera as evidências abaixo
python scripts/demo.py

Exemplo de chamada

curl -X POST http://localhost:8000/agent/chat \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -d '{"message": "Quero agendar um corte de cabelo dia 05/09"}'

/agent/chat e /metrics exigem o header X-API-Key (valor em API_KEY no .env); /health é aberto.

Evidências (saída real dos comandos)

Testes e lint

$ pytest -q
............................                                             [100%]
28 passed

$ ruff check .
All checks passed!

Autenticação por API key

$ curl -s -o /dev/null -w '%{http_code}\n' localhost:8000/health
200
$ curl -s -o /dev/null -w '%{http_code}\n' -XPOST localhost:8000/agent/chat -d '{"message":"oi"}'
401
$ curl -s -o /dev/null -w '%{http_code}\n' -XPOST localhost:8000/agent/chat -H 'X-API-Key: errado' -d '{"message":"oi"}'
401
$ curl -s -o /dev/null -w '%{http_code}\n' localhost:8000/metrics
401
$ curl -s -o /dev/null -w '%{http_code}\n' localhost:8000/metrics -H "X-API-Key: $API_KEY"
200

Loop de tool calling + observabilidade

python scripts/demo.py — uma execução do agente ("Quero agendar um corte de cabelo dia 05/09, meu nome é Ana"): o modelo chama check_availability, depois remember_fact, e responde. Cada passo emite um log estruturado JSON:

{"event": "llm_response", "session_id": "demo-1", "iteration": 0, "stop_reason": "tool_use"}
{"event": "tool_call", "session_id": "demo-1", "tool": "check_availability", "arguments": {"date": "2026-09-05", "service": "corte de cabelo"}, "is_error": false}
{"event": "llm_response", "session_id": "demo-1", "iteration": 1, "stop_reason": "tool_use"}
{"event": "tool_call", "session_id": "demo-1", "tool": "remember_fact", "arguments": {"key": "cliente_nome", "value": "Ana"}, "is_error": false}
{"event": "llm_response", "session_id": "demo-1", "iteration": 2, "stop_reason": "end_turn"}

GET /metrics ao final (contadores + duração média por chamada) — nesta execução de demonstração o cliente da Claude API é um stub, então agent.llm_call_ms não reflete latência de rede real:

{
  "counters": {
    "agent.runs": 1,
    "tool_calls.check_availability": 1,
    "tool_calls.remember_fact": 1
  },
  "avg_duration_ms": {
    "agent.llm_call_ms": 0.01,
    "tool.check_availability_ms": 0.01,
    "tool.remember_fact_ms": 0.01
  }
}

Servidor MCP — mesmas ferramentas expostas via Model Context Protocol

$ python scripts/demo.py   # trecho: list_tools + call_tool do servidor MCP
• check_availability(date, service) — Consulta horários disponíveis para agendamento em uma data.
• book_appointment(date, time, service, client_name) — Confirma um agendamento em um horário específico.
• remember_fact(key, value) — Guarda um fato sobre o cliente/sessão na memória de longo prazo.

call_tool check_availability {"date": "2026-09-05", "service": "corte de cabelo"}
-> {"date": "2026-09-05", "service": "corte de cabelo", "available_slots": ["09:00", "10:30", "14:00", "16:00"]}

Decisões de arquitetura (resumo)

  • Ferramentas desacopladas do orquestrador: ToolRegistry não sabe nada sobre o domínio específico — as ferramentas em builtin.py são o único lugar com regra de negócio. Isso permite trocar o domínio (ex.: suporte, vendas) sem tocar no orquestrador.
  • Ferramentas com estado sem acoplar o registro: remember_fact precisa do MemoryStore/session_id da execução atual, mas o ToolRegistry é agnóstico de domínio e passa só os argumentos. A ponte é um ContextVar (app/core/runtime.py): o orquestrador ativa um AgentRuntime em volta do loop e a ferramenta lê current_runtime(). Fora de uma execução de agente (ex.: a mesma ferramenta chamada por um cliente MCP) ela vira no-op.
  • Loop de tool calling fiel à API: o orquestrador devolve ao modelo os content blocks reais (tool_use do assistente emparelhado com o tool_result do usuário), não str(response.content) — é o que faz o loop funcionar contra a Claude API de verdade, não só num mock.
  • Memória com interface abstrata: InMemoryStore é só a implementação de desenvolvimento. Trocar por Redis/Postgres em produção é implementar a mesma interface, sem alterar AgentContext nem o orquestrador.
  • MCP como camada separada da API HTTP: o mesmo ToolRegistry alimenta tanto o endpoint REST quanto o servidor MCP — ferramentas escritas uma vez, expostas de duas formas.
  • Observabilidade desde o primeiro commit: cada chamada ao LLM e cada chamada de ferramenta gera um log estruturado e uma métrica de duração — não foi adicionado depois, faz parte do fluxo principal.

Mais detalhes em docs/ARCHITECTURE.md.

Limitações conscientes (sendo transparente, como no processo seletivo)

  • InMemoryStore não persiste entre restarts — proposital para o escopo de portfólio; a interface já está pronta para Redis/Postgres.
  • Sem rate limiting na API — a autenticação é por API key única (X-API-Key); rate limiting e auth por usuário/JWT ficam como próximo passo antes de multi-tenant.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages