Sistema de análise automática de diagramas de arquitetura de software utilizando IA generativa (Gemini 2.5 Flash Lite via LangChain), desenvolvido como projeto integrado do Hackathon FIAP — trilhas SOAT e IADT.
Aluna: Andressa Silva — RM 359660
Vídeo Apresentação: > Abrir no YouTube
Repositórios:
| Serviço | Repositório |
|---|---|
| Upload Service | Hackaton-FIAP-Andressa-Silva/upload-service |
| Report Service | Hackaton-FIAP-Andressa-Silva/report-service |
| AI Processing Service | Hackaton-FIAP-Andressa-Silva/ai-processing-service |
| API Gateway | Hackaton-FIAP-Andressa-Silva/api-gateway |
| Infrastructure (Terraform) | Hackaton-FIAP-Andressa-Silva/infrastructure |
Equipes de engenharia perdem horas revisando manualmente diagramas de arquitetura para identificar riscos e gargalos. Este sistema automatiza essa análise: o usuário faz upload de um diagrama (PNG, JPEG ou PDF) e recebe em segundos um relatório técnico estruturado com componentes identificados, riscos classificados por severidade e recomendações priorizadas, produzidos por um modelo de IA com guardrails de validação de saída.
Ver código-fonte Mermaid do diagrama de sequência
sequenceDiagram
autonumber
actor Cliente
participant GW as API Gateway<br/>(Nginx)
participant US as Upload Service<br/>(FastAPI + PostgreSQL)
participant S3 as AWS S3
participant SQS as AWS SQS
participant AI as AI Processing Worker<br/>(LangChain + Gemini)
participant Gemini as Gemini 2.5 Flash Lite<br/>(Google API)
participant RS as Report Service<br/>(FastAPI + MongoDB)
rect rgb(220, 235, 255)
Note over Cliente,SQS: Fase 1 — Upload e Enfileiramento
Cliente->>GW: POST /api/v1/uploads<br/>X-API-Key: ***<br/>body: multipart/form-data (arquivo)
GW->>GW: Valida X-API-Key<br/>Rate limit (10 req/s, burst 20)
GW->>US: POST /api/v1/uploads (proxy)
US->>US: Valida MIME type (whitelist PNG/JPEG/PDF)
US->>US: Valida magic bytes (assinatura binária)
US->>US: Valida tamanho ≤ 20 MB
US->>S3: PUT s3://bucket/{uuid}/{filename}
S3-->>US: 200 OK
US->>US: INSERT upload (id, filename, s3_key,<br/>status=RECEIVED) → PostgreSQL
US->>SQS: SendMessage<br/>{upload_id, s3_key, content_type}
SQS-->>US: MessageId
US-->>GW: 202 {upload_id, status: "RECEIVED"}
GW-->>Cliente: 202 {upload_id, status: "RECEIVED"}
end
rect rgb(240, 240, 240)
Note over Cliente,US: Fase 2 — Polling de Status (assíncrono)
loop Até status = ANALYZED ou ERROR
Cliente->>GW: GET /api/v1/uploads/{upload_id}/status<br/>X-API-Key: ***
GW->>US: GET /uploads/{upload_id}/status (proxy)
US->>US: SELECT upload WHERE id = upload_id → PostgreSQL
US-->>GW: 200 {upload_id, status, updated_at}
GW-->>Cliente: 200 {upload_id, status, updated_at}
end
end
rect rgb(255, 240, 220)
Note over SQS,RS: Fase 3 — Processamento Assíncrono (Worker)
AI->>SQS: ReceiveMessage (long polling)
SQS-->>AI: {upload_id, s3_key, content_type}
AI->>US: PATCH /uploads/{upload_id}/status<br/>X-Internal-Token: ***<br/>body: {status: "PROCESSING"}
US->>US: UPDATE upload SET status=PROCESSING → PostgreSQL
US-->>AI: 200 OK
AI->>S3: GetObject s3://bucket/{s3_key}
S3-->>AI: bytes do arquivo
alt content_type = application/pdf
AI->>AI: pdf2image → converte PDF para PNG
end
AI->>AI: base64.encode(image_bytes)
AI->>Gemini: ChatMessage [SystemPrompt + UserPrompt + image_base64]<br/>response_mime_type: application/json
Gemini-->>AI: JSON {summary, components[], risks[], recommendations[]}
AI->>AI: Guardrail 1: Pydantic schema validation<br/>(campos obrigatórios, enums HIGH/MEDIUM/LOW)
AI->>AI: Guardrail 2: sanitize strings<br/>(strip + truncate 2000 chars)
alt Guardrails OK
AI->>RS: POST /reports<br/>X-Internal-Token: ***<br/>body: {upload_id, summary, components,<br/>risks, recommendations, ai_model,<br/>processing_time_seconds}
RS->>RS: INSERT report → MongoDB
RS-->>AI: 201 {report_id, upload_id}
AI->>US: PATCH /uploads/{upload_id}/status<br/>X-Internal-Token: ***<br/>body: {status: "ANALYZED"}
US->>US: UPDATE upload SET status=ANALYZED → PostgreSQL
US-->>AI: 200 OK
else Guardrails FALHOU
AI->>US: PATCH /uploads/{upload_id}/status<br/>X-Internal-Token: ***<br/>body: {status: "ERROR", error_message: "..."}
US->>US: UPDATE upload SET status=ERROR → PostgreSQL
US-->>AI: 200 OK
end
AI->>SQS: DeleteMessage (remove da fila)
end
rect rgb(220, 255, 230)
Note over Cliente,RS: Fase 4 — Consulta do Relatório
Cliente->>GW: GET /api/v1/reports/{upload_id}<br/>X-API-Key: ***
GW->>GW: Valida X-API-Key<br/>Rate limit
GW->>RS: GET /reports/{upload_id} (proxy)
RS->>RS: db.reports.findOne({upload_id}) → MongoDB
RS-->>GW: 200 {report_id, summary, components[],<br/>risks[], recommendations[],<br/>ai_model, processing_time_seconds}
GW-->>Cliente: 200 {relatório completo}
end
1. Cliente → API Gateway (valida X-API-Key, rate limit)
2. Gateway → Upload Service (valida MIME, magic bytes, tamanho ≤ 20 MB)
3. Upload Service → S3 (persiste arquivo) + PostgreSQL (metadados: status RECEIVED)
4. Upload Service → SQS (mensagem com upload_id, s3_key, content_type)
5. AI Worker ← SQS (long polling)
6. AI Worker → S3 (download do arquivo) [converte PDF→PNG se necessário]
7. AI Worker → Gemini 2.5 Flash Lite (análise multimodal via LangChain)
8. AI Worker → Guardrails Pydantic (valida + sanitiza output do LLM)
9. AI Worker → Upload Service PATCH status → ANALYZED (ou ERROR)
10. AI Worker → Report Service POST /reports (persiste relatório no MongoDB)
11. Cliente → GET /reports/{upload_id} (resultado disponível)
| Serviço | Porta | Stack | Responsabilidade |
|---|---|---|---|
api-gateway |
8080 | Nginx | Reverse proxy, autenticação X-API-Key, rate limiting (10 req/s, burst 20), headers de segurança, CORS |
upload-service |
8001 | FastAPI · Python 3.12 · PostgreSQL | Recebe uploads, valida tipo/tamanho/magic bytes, persiste no S3, publica em SQS, expõe status |
ai-processing-service |
— | Python 3.12 · LangChain · Gemini 2.5 Flash Lite | Worker SQS: baixa arquivo, analisa com IA, valida output com guardrails, salva relatório |
report-service |
8003 | FastAPI · Python 3.12 · MongoDB Atlas | Armazena e serve relatórios gerados pela IA |
| Campo | Tipo | Descrição |
|---|---|---|
id |
UUID v4 | Identificador único |
filename |
string | Nome original do arquivo |
content_type |
string | image/png, image/jpeg, application/pdf |
s3_key |
string | Chave do objeto no S3 |
file_size |
int | Tamanho em bytes |
status |
enum | RECEIVED → PROCESSING → ANALYZED / ERROR |
error_message |
string | Mensagem de erro se status = ERROR |
created_at / updated_at |
datetime | Timestamps |
{
"id": "uuid",
"upload_id": "uuid",
"summary": "Descrição geral da arquitetura...",
"components": [
{ "name": "API Gateway", "type": "Gateway", "description": "...", "technology": "Nginx" }
],
"risks": [
{ "title": "Single point of failure", "severity": "HIGH", "description": "...", "impact": "...", "affected_components": ["API Gateway"] }
],
"recommendations": [
{ "title": "Adicionar redundância", "priority": "HIGH", "description": "...", "rationale": "..." }
],
"ai_model": "gemini-2.5-flash-lite",
"processing_time_seconds": 4.32,
"created_at": "2026-05-14T23:00:00Z"
}Gemini 2.5 Flash Lite via langchain-google-genai, com:
- Temperatura
0.1(determinístico) response_mime_type: application/json(JSON mode nativo)- Timeout de 30 s por requisição
- Até 2 retentativas em caso de falha
O output do LLM passa por validação obrigatória antes de ser persistido:
- Schema Pydantic — verifica estrutura e tipos (campos obrigatórios, enums
HIGH/MEDIUM/LOW) - Sanitização de strings — strip de whitespace + truncamento em 2.000 caracteres por campo
- Em caso de violação → status
ERRORno upload, nenhum dado inválido é salvo
- System prompt: define o papel de arquiteto sênior e exige resposta exclusivamente em JSON
- User prompt: estrutura exata do JSON esperado com regras (identificar todos os componentes visíveis, severidade em maiúsculas, mínimo 1 risco e 1 recomendação)
- PDFs são convertidos para PNG antes de serem enviados ao modelo (multimodal)
| Controle | Implementação |
|---|---|
| Autenticação externa | X-API-Key header validado no Nginx (comparação com variável de ambiente injetada) |
| Autenticação interna | X-Internal-Token header validado nos endpoints /uploads/{id}/status e /reports |
| Rate limiting | 10 req/s por IP, burst 20 — resposta 429 (Nginx limit_req) |
| Validação de tipo de arquivo | Whitelist de MIME types + verificação de magic bytes (assinatura binária real do arquivo) |
| Tamanho máximo | 20 MB por upload (validado no Upload Service e no Nginx client_max_body_size 25M) |
| Validação de saída LLM | Guardrails Pydantic + sanitização — nenhum output do modelo é salvo sem validação |
| Princípio do menor privilégio | IAM roles separados por serviço no ECS (upload-service acessa S3+SQS; ai-processing acessa S3+SQS) |
| Segredos | AWS Secrets Manager (produção) / variáveis de ambiente (desenvolvimento local) |
| Criptografia em repouso | S3 (KMS), RDS PostgreSQL (AES-256) |
| Imagens Docker | Usuário não-root, multi-stage builds |
| Headers de segurança | X-Content-Type-Options, X-Frame-Options: DENY, X-XSS-Protection em todas as respostas |
Todos os recursos são provisionados via Terraform (infrastructure/terraform/) com estado remoto no S3.
terraform/
├── main.tf # Orchestração dos módulos + secrets globais
├── variables.tf # environment, aws_region, api_key, google_api_key, internal_token, mongodb_url
├── outputs.tf # ALB DNS, S3 bucket, SQS URL, ECR repos
└── modules/
├── networking/ # VPC, subnets públicas/privadas, Internet Gateway, NAT
├── s3/ # Bucket de diagramas (criptografia KMS)
├── sqs/ # Fila principal + Dead Letter Queue
├── rds/ # PostgreSQL (RDS) para upload-service
├── documentdb/ # MongoDB URL secret (MongoDB Atlas via Secrets Manager)
├── ecr/ # Repositórios de imagens Docker (4 serviços)
├── iam/ # Roles ECS com least privilege por serviço
└── ecs/ # Cluster ECS Fargate + ALB + Task Definitions (4 serviços)
| Recurso | Descrição |
|---|---|
| VPC | Subnets públicas (ALB) e privadas (ECS tasks, RDS) |
| ECS Fargate | Cluster com tasks para os 4 serviços |
| ALB | Application Load Balancer na frente do api-gateway |
| RDS PostgreSQL | Banco do upload-service (subnet privada) |
| S3 | Bucket para armazenar diagramas enviados |
| SQS | Fila de processamento + DLQ |
| ECR | 4 repositórios Docker privados |
| Secrets Manager | api-key, google-api-key, internal-token, mongodb-url |
| IAM Roles | Roles separados para upload-service e ai-processing-service |
Cada serviço tem seu próprio pipeline em .github/workflows/ci-cd.yml:
push to main
├── test → pytest + coverage ≥ 80% (threshold obrigatório)
└── build-push → docker build → ECR push → ECS deploy (rolling update)
O repositório infrastructure tem pipeline separado:
push to main (paths: terraform/**)
├── plan → terraform plan (sem environment)
└── apply → terraform apply (environment: production, requer aprovação)
| Secret | upload | report | ai-proc | api-gw | infra |
|---|---|---|---|---|---|
AWS_ACCESS_KEY_ID |
✓ | ✓ | ✓ | ✓ | ✓ |
AWS_SECRET_ACCESS_KEY |
✓ | ✓ | ✓ | ✓ | ✓ |
AWS_SESSION_TOKEN |
✓ | ✓ | ✓ | ✓ | ✓ |
GOOGLE_API_KEY |
— | — | ✓ | — | ✓ |
INTERNAL_SERVICE_TOKEN |
✓ | ✓ | ✓ | — | ✓ |
API_KEY |
— | — | — | ✓ | ✓ |
Nota AWS Academy: as credenciais são temporárias (STS) e expiram a cada 4 horas. Os três campos (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN) precisam ser atualizados conjuntamente.
- Docker 24+ e Docker Compose v2
- Python 3.12
- Chave de API Google Gemini — obtenha gratuitamente em https://aistudio.google.com
# 1. Clone e configure variáveis
git clone <repo-url> && cd hackaton-fiap
cp infrastructure/.env.local infrastructure/.env
# Edite infrastructure/.env e preencha GOOGLE_API_KEY
# 2. Suba toda a stack (LocalStack + todos os serviços)
cd infrastructure
docker compose --env-file .env up --build
# 3. Verifique os health checks
curl http://localhost:8080/health
# → {"status":"healthy","service":"api-gateway"}A stack local usa LocalStack para simular S3 e SQS sem custos de nuvem. As filas e buckets são criados automaticamente pelo script scripts/init-localstack.sh.
curl -X POST http://localhost:8080/api/v1/uploads \
-H "X-API-Key: dev-api-key-change-me" \
-F "file=@meu-diagrama.png"{
"upload_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "RECEIVED",
"filename": "meu-diagrama.png",
"created_at": "2026-05-14T23:00:00Z"
}curl http://localhost:8080/api/v1/uploads/550e8400-e29b-41d4-a716-446655440000/status \
-H "X-API-Key: dev-api-key-change-me"{ "upload_id": "...", "status": "ANALYZED", "updated_at": "..." }Ciclo de status: RECEIVED → PROCESSING → ANALYZED (ou ERROR)
curl http://localhost:8080/api/v1/reports/550e8400-e29b-41d4-a716-446655440000 \
-H "X-API-Key: dev-api-key-change-me"{
"report_id": "uuid",
"upload_id": "uuid",
"summary": "Arquitetura de microserviços com 4 serviços...",
"components": [
{ "name": "API Gateway", "type": "Gateway", "description": "...", "technology": "Nginx" }
],
"risks": [
{ "title": "Ausência de circuit breaker", "severity": "HIGH", "description": "...", "impact": "...", "affected_components": ["Upload Service"] }
],
"recommendations": [
{ "title": "Implementar Resilience4j", "priority": "HIGH", "description": "...", "rationale": "..." }
],
"ai_model": "gemini-2.5-flash-lite",
"processing_time_seconds": 4.32
}| Formato | MIME Type | Observação |
|---|---|---|
| PNG | image/png |
Recomendado |
| JPEG / JPG | image/jpeg |
|
application/pdf |
Convertido para PNG antes da análise |
Tamanho máximo: 20 MB
cd infrastructure/terraform
terraform init
terraform plan \
-var="environment=prod" \
-var="google_api_key=AIza..." \
-var="internal_service_token=$(openssl rand -hex 32)" \
-var="api_key=$(openssl rand -hex 16)" \
-var="mongodb_url=mongodb+srv://..."
terraform applyApós o apply, as imagens ECR são publicadas automaticamente pelo CI/CD ao fazer push para main em cada repositório de serviço.
Outputs do Terraform:
alb_dns_name— endpoint público da aplicaçãoecr_repositories— URLs dos repositórios Dockers3_bucket_name— bucket de diagramassqs_queue_url— URL da fila de processamento
hackaton-fiap/
├── api-gateway/ # Nginx — proxy, auth, rate limit
│ ├── nginx.conf
│ └── Dockerfile
├── upload-service/ # FastAPI + PostgreSQL
│ ├── src/
│ │ ├── domain/ # Entidades (Upload, UploadStatus) e repositórios
│ │ ├── application/ # Use cases: CreateUpload, GetStatus, UpdateStatus
│ │ ├── api/ # Routers FastAPI + schemas Pydantic
│ │ └── infrastructure/ # PostgreSQL, S3, SQS, config
│ └── tests/
├── ai-processing-service/ # Worker SQS + LangChain + Gemini
│ ├── src/
│ │ ├── domain/ # Entidades (AnalysisResult, Component, Risk, Recommendation)
│ │ ├── application/ # Use case: ProcessDiagram
│ │ └── infrastructure/ # SQS consumer, S3 client, OpenAI/Gemini analyzer, guardrails, prompts
│ └── tests/
├── report-service/ # FastAPI + MongoDB
│ ├── src/
│ │ ├── domain/ # Entidade Report
│ │ ├── application/ # Use cases: CreateReport, GetReport
│ │ ├── api/ # Routers + schemas
│ │ └── infrastructure/ # MongoDB (Motor async), config
│ └── tests/
├── infrastructure/
│ ├── docker-compose.yml # Stack local completa (LocalStack, PostgreSQL, MongoDB, todos os serviços)
│ ├── .env.local # Template de variáveis de ambiente
│ ├── scripts/
│ │ └── init-localstack.sh # Cria bucket S3 e fila SQS no LocalStack
│ └── terraform/ # Infraestrutura AWS completa (módulos: VPC, ECS, RDS, S3, SQS, ECR, IAM)
└── docs/
├── refinamento-tecnico-negocial.md
└── backlog-epicos.md
- Arquitetura Hexagonal (Ports & Adapters) em todos os serviços Python: domínio isolado de infraestrutura via interfaces abstratas
- Clean Architecture: separação em camadas
domain→application→infrastructure - Event-driven: desacoplamento entre upload e processamento via SQS (produtor/consumidor assíncrono)
- CQRS simplificado: separação de escrita (Upload Service + AI Worker) e leitura (Report Service)

