Aula 5: Deploy - API + Frontend Simples
Video da aula
Video em breve. O conteudo completo em texto esta disponivel abaixo.
O que voce vai aprender
- Criar uma API REST com FastAPI para o seu agente
- Construir um frontend de chat minimalista com HTML + JS
- Adicionar streaming (SSE) para respostas em tempo real
- Dockerizar a aplicacao
- Fazer deploy na Railway ou Render (free tier)
1. Estrutura do projeto
ia-pratica/
app/
__init__.py
main.py # API FastAPI
agent.py # Agente com tools (Aulas 1-4)
static/
index.html # Frontend
Dockerfile
requirements.txt
.env
2. API com FastAPI
Instale as dependencias:
pip install fastapi uvicorn sse-starlette
Crie app/agent.py (reutilizando o que construimos):
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langchain_core.tools import tool
load_dotenv()
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
@tool
def calcular(expressao: str) -> str:
"""Calcula uma expressao matematica."""
try:
allowed = set("0123456789+-*/.() ,powsqrtabs")
if not all(c in allowed for c in expressao.replace(" ", "")):
return "Expressao invalida."
result = eval(expressao, {"__builtins__": {}}, {
"pow": pow, "sqrt": __import__("math").sqrt, "abs": abs
})
return str(result)
except Exception as e:
return f"Erro: {e}"
SYSTEM = SystemMessage(content="""Voce e um assistente inteligente.
Responda de forma clara e util. Sempre em portugues.""")
# Sessoes em memoria (em producao, use Redis)
sessions: dict[str, list] = {}
def get_response(session_id: str, message: str) -> str:
"""Processa uma mensagem e retorna a resposta."""
if session_id not in sessions:
sessions[session_id] = []
history = sessions[session_id]
history.append(HumanMessage(content=message))
# Manter apenas ultimas 20 mensagens
if len(history) > 20:
history = history[-20:]
sessions[session_id] = history
messages = [SYSTEM] + history
response = llm.invoke(messages)
history.append(AIMessage(content=response.content))
return response.content
Crie app/main.py:
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse, FileResponse
from pydantic import BaseModel
from app.agent import get_response
import uuid
app = FastAPI(title="IA Pratica API")
class ChatRequest(BaseModel):
message: str
session_id: str | None = None
class ChatResponse(BaseModel):
response: str
session_id: str
@app.post("/api/chat", response_model=ChatResponse)
async def chat(req: ChatRequest):
session_id = req.session_id or str(uuid.uuid4())
response = get_response(session_id, req.message)
return ChatResponse(response=response, session_id=session_id)
@app.get("/api/health")
async def health():
return {"status": "ok"}
# Servir frontend
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/")
async def index():
return FileResponse("static/index.html")
3. Frontend de chat
Crie static/index.html:
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IA Pratica - Chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #0f172a; color: #e2e8f0;
display: flex; flex-direction: column; height: 100vh;
}
header {
padding: 1rem; text-align: center;
border-bottom: 1px solid #1e293b;
}
header h1 { font-size: 1.25rem; color: #38bdf8; }
#chat {
flex: 1; overflow-y: auto; padding: 1rem;
display: flex; flex-direction: column; gap: 0.75rem;
}
.msg { max-width: 80%; padding: 0.75rem 1rem; border-radius: 1rem; line-height: 1.5; }
.msg.user {
align-self: flex-end; background: #2563eb; color: white;
border-bottom-right-radius: 0.25rem;
}
.msg.ai {
align-self: flex-start; background: #1e293b;
border-bottom-left-radius: 0.25rem;
}
.msg.ai pre { background: #0f172a; padding: 0.5rem; border-radius: 0.5rem; overflow-x: auto; margin: 0.5rem 0; }
.msg.ai code { font-size: 0.85rem; }
form {
display: flex; gap: 0.5rem; padding: 1rem;
border-top: 1px solid #1e293b;
}
input {
flex: 1; padding: 0.75rem 1rem; border: 1px solid #334155;
border-radius: 0.75rem; background: #1e293b; color: #e2e8f0;
font-size: 1rem; outline: none;
}
input:focus { border-color: #2563eb; }
button {
padding: 0.75rem 1.5rem; background: #2563eb; color: white;
border: none; border-radius: 0.75rem; font-size: 1rem; cursor: pointer;
}
button:hover { background: #1d4ed8; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
</head>
<body>
<header><h1>IA Pratica - Chat</h1></header>
<div id="chat"></div>
<form id="form">
<input id="input" type="text" placeholder="Digite sua mensagem..." autocomplete="off" />
<button type="submit" id="btn">Enviar</button>
</form>
<script>
var chat = document.getElementById('chat');
var form = document.getElementById('form');
var input = document.getElementById('input');
var btn = document.getElementById('btn');
var sessionId = null;
function addMsg(text, role) {
var div = document.createElement('div');
div.className = 'msg ' + role;
div.textContent = text;
chat.appendChild(div);
chat.scrollTop = chat.scrollHeight;
}
form.addEventListener('submit', function(e) {
e.preventDefault();
var msg = input.value.trim();
if (!msg) return;
addMsg(msg, 'user');
input.value = '';
btn.disabled = true;
fetch('/api/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: msg, session_id: sessionId})
})
.then(function(r) { return r.json(); })
.then(function(data) {
sessionId = data.session_id;
addMsg(data.response, 'ai');
btn.disabled = false;
input.focus();
})
.catch(function() {
addMsg('Erro de conexao.', 'ai');
btn.disabled = false;
});
});
input.focus();
</script>
</body>
</html>
4. Testando local
uvicorn app.main:app --reload --port 8000
Acesse http://localhost:8000 e converse com seu agente.
5. Dockerizando
Crie requirements.txt:
fastapi==0.115.0
uvicorn[standard]==0.30.0
langchain==0.3.0
langchain-openai==0.2.0
langchain-core==0.3.0
python-dotenv==1.0.1
Crie Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Teste local com Docker:
docker build -t ia-pratica .
docker run -p 8000:8000 --env-file .env ia-pratica
6. Deploy na Railway
Railway oferece free tier generoso e deploy simples:
# Instalar CLI
npm install -g @railway/cli
# Login e deploy
railway login
railway init
railway up
Configure as variaveis de ambiente no dashboard da Railway:
OPENAI_API_KEY=sk-...
A URL publica sera algo como https://ia-pratica-production.up.railway.app.
7. Deploy alternativo: Render
Crie um arquivo render.yaml na raiz:
services:
- type: web
name: ia-pratica
runtime: docker
envVars:
- key: OPENAI_API_KEY
sync: false
Conecte o repositorio GitHub no Render e faca deploy automatico.
8. Melhorias para producao
# Adicione rate limiting
from fastapi import HTTPException
from collections import defaultdict
import time
request_counts = defaultdict(list)
@app.middleware("http")
async def rate_limit(request: Request, call_next):
ip = request.client.host
now = time.time()
# Limpar requests antigos (janela de 60s)
request_counts[ip] = [t for t in request_counts[ip] if now - t < 60]
if len(request_counts[ip]) >= 20: # 20 requests/min
raise HTTPException(status_code=429, detail="Too many requests")
request_counts[ip].append(now)
return await call_next(request)
# Adicione CORS para seu dominio
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://seudominio.com"],
allow_methods=["POST", "GET"],
allow_headers=["Content-Type"],
)
Checklist de producao
- Variaveis de ambiente (nunca hardcode API keys)
- Rate limiting (evitar abuso)
- CORS configurado para seu dominio
- HTTPS (Railway/Render fazem automatico)
- Logs estruturados
- Health check endpoint
- Limite de tamanho de mensagem
- Monitoramento de custos da API
Exercicio final
- Faca deploy do seu agente usando Railway ou Render
- Adicione as tools das aulas anteriores (clima, calculadora, GitHub)
- Compartilhe a URL com alguem e peca feedback
- Monitore os logs e custos por 24h
Parabens!
Voce completou o mini-curso “IA Pratica com Python”. Agora voce sabe:
- Criar agentes conversacionais com LangChain
- Implementar RAG para consultar seus documentos
- Adicionar memoria para conversas naturais
- Usar tools para dar superpoderes ao agente
- Fazer deploy em producao
O proximo passo e adaptar esses conceitos para resolver problemas reais do seu trabalho ou projeto.