🚀 feat(runtime): alinhar operacao Telegram-first para bootstrap, deploy e documentacao

- atualiza docker-compose, Dockerfile e service do systemd para subir o bootstrap de banco e o Telegram satellite como runtime principal do projeto\n- revisa .env.example, README, TEST_CASES e guia de deploy para refletir a arquitetura atual com MySQL, Redis, Vertex AI e canal Telegram\n- endurece o parsing de configuracao com aliases controlados para DEBUG e normalizacao de ENVIRONMENT e CONVERSATION_STATE_BACKEND\n- centraliza a inicializacao legada do app HTTP em app.db.init_db e faz o bootstrap respeitar flags de seed e falhar explicitamente quando algum backend nao sobe\n- adiciona cobertura dedicada para parsing de settings e para o bootstrap de banco do runtime
main
parent af30a81cef
commit 0e019824e6

@ -1,51 +1,52 @@
# ============================================
# CONFIGURACOES DO GOOGLE CLOUD
# VERTEX AI
# ============================================
GOOGLE_PROJECT_ID=id_do_seu_projeto
GOOGLE_LOCATION=loc_do_seu_projeto
GOOGLE_PROJECT_ID=seu_projeto
GOOGLE_LOCATION=us-central1
VERTEX_MODEL_NAME=gemini-2.5-pro
# ============================================
# CONFIGURACOES DO BANCO DE DADOS (MYSQL - TOOLS)
# AMBIENTE
# ============================================
# Banco de metadados de tools (pode ser o mesmo banco do mock)
DB_HOST=localhost
# Valores comuns: development, staging, production
ENVIRONMENT=development
# Use apenas true ou false.
# Alias como "release" agora sao aceitos, mas evite depender disso.
DEBUG=false
# ============================================
# BANCO DE TOOLS (MYSQL)
# ============================================
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=SUA_SENHA
DB_PASSWORD=sua_senha
DB_NAME=orquestrador_mock
DB_CLOUD_SQL_CONNECTION_NAME=
# ============================================
# CONFIGURACOES DO BANCO DE DADOS MOCK (MYSQL - DADOS FICTICIOS)
# BANCO MOCK DE NEGOCIO (MYSQL)
# ============================================
MOCK_DB_HOST=127.0.0.1
MOCK_DB_PORT=3306
MOCK_DB_USER=root
MOCK_DB_PASSWORD=SUA_SENHA
MOCK_DB_PASSWORD=sua_senha
MOCK_DB_NAME=orquestrador_mock
MOCK_DB_CLOUD_SQL_CONNECTION_NAME=
MOCK_SEED_ENABLED=true
AUTO_SEED_TOOLS=true
AUTO_SEED_MOCK=true
# ============================================
# CONFIGURACOES DE API - GOOGLE GENERATIVE AI (Gemini)
# TELEGRAM
# ============================================
# Descomente e informe a chave apenas se usar Gemini
# GOOGLE_API_KEY=sua-chave-api-aqui
# ============================================
# AMBIENTE E DEBUG
# ============================================
# Valores: development, staging, production
ENVIRONMENT=development
# DEBUG deve ser false em producao
DEBUG=true
# ============================================
# TELEGRAM (SERVICO SATELITE)
# ============================================
TELEGRAM_BOT_TOKEN=
TELEGRAM_POLLING_TIMEOUT=30
TELEGRAM_REQUEST_TIMEOUT=45
@ -53,11 +54,11 @@ TELEGRAM_REQUEST_TIMEOUT=45
# ============================================
# ESTADO CONVERSACIONAL
# ============================================
# Valores: memory, redis
CONVERSATION_STATE_BACKEND=memory
# Em producao com Telegram, prefira redis.
CONVERSATION_STATE_BACKEND=redis
CONVERSATION_STATE_TTL_MINUTES=60
# Redis usado quando CONVERSATION_STATE_BACKEND=redis
REDIS_URL=redis://127.0.0.1:6379/0
REDIS_KEY_PREFIX=orquestrador
REDIS_SOCKET_TIMEOUT_SECONDS=5

@ -1,15 +1,17 @@
# Deploy no Servidor da Empresa (Debian/Ubuntu + systemd)
# Deploy do Telegram Satellite no Servidor
## 1) Fluxo correto com Git
Este guia considera o modelo operacional atual do projeto:
- MySQL para tools e base mock
- Redis para estado conversacional
- Vertex AI para o modelo
- Telegram como canal principal
Sim: primeiro voce faz commit e push da sua maquina local, depois no servidor faz pull.
Exemplo:
## 1) Fluxo com Git
```bash
# local
# maquina local
git add .
git commit -m "ajusta mysql/tools + telegram webhook"
git commit -m "ajustes no orquestrador"
git push origin main
# servidor
@ -17,7 +19,7 @@ cd /opt/orquestrador
git pull origin main
```
## 2) Preparacao no servidor
## 2) Preparacao do ambiente
```bash
cd /opt/orquestrador
@ -27,14 +29,25 @@ pip install -U pip
pip install -r requirements.txt
```
## 3) Configurar .env.prod
## 3) Dependencias externas
Garanta antes da subida:
- MySQL acessivel pelo host configurado
- Redis acessivel pelo host configurado
- credencial valida do Vertex AI
- token do bot do Telegram
## 4) Configurar `.env.prod`
Crie/atualize o arquivo `.env.prod` no servidor:
Exemplo minimo:
```env
GOOGLE_PROJECT_ID=seu-projeto
GOOGLE_LOCATION=us-central1
VERTEX_MODEL_NAME=gemini-2.5-flash
VERTEX_MODEL_NAME=gemini-2.5-pro
ENVIRONMENT=production
DEBUG=false
DB_HOST=127.0.0.1
DB_PORT=3306
@ -52,30 +65,50 @@ AUTO_SEED_TOOLS=true
AUTO_SEED_MOCK=true
MOCK_SEED_ENABLED=true
# telegram (opcional)
# TELEGRAM_BOT_TOKEN=...
# TELEGRAM_WEBHOOK_SECRET=...
CONVERSATION_STATE_BACKEND=redis
CONVERSATION_STATE_TTL_MINUTES=60
REDIS_URL=redis://127.0.0.1:6379/0
REDIS_KEY_PREFIX=orquestrador
REDIS_SOCKET_TIMEOUT_SECONDS=5
TELEGRAM_BOT_TOKEN=seu_token_aqui
TELEGRAM_POLLING_TIMEOUT=30
TELEGRAM_REQUEST_TIMEOUT=45
```
## 4) Credencial para Vertex AI
Observacoes:
- em producao, o satelite exige `CONVERSATION_STATE_BACKEND=redis`;
- use `DEBUG=false` ou `DEBUG=true`, sem valores livres como `release`.
## 5) Credencial do Vertex AI
Defina um dos formatos abaixo no servidor:
Defina um dos formatos abaixo:
- `GOOGLE_APPLICATION_CREDENTIALS=/opt/orquestrador/sa.json`
- ou Application Default Credentials (`gcloud auth application-default login`)
- ou Application Default Credentials
A service account deve ter permissao no Vertex AI (ex.: `roles/aiplatform.user`).
A service account precisa de permissao de uso do Vertex AI, como `roles/aiplatform.user`.
## 5) Configurar service do systemd
## 6) Bootstrap manual inicial
1. Copie o template e ajuste usuario/path:
Antes de ativar o servico, rode uma inicializacao manual para validar banco e seeds:
```bash
cd /opt/orquestrador
source venv/bin/activate
python -m app.db.init_db
```
## 7) Configurar `systemd`
Copie o template:
```bash
sudo cp deploy/systemd/orquestrador.service.example /etc/systemd/system/orquestrador.service
sudo nano /etc/systemd/system/orquestrador.service
```
2. Recarregue e inicie:
Depois recarregue e inicie:
```bash
sudo systemctl daemon-reload
@ -83,33 +116,27 @@ sudo systemctl enable --now orquestrador
sudo systemctl status orquestrador
```
3. Logs:
Logs:
```bash
journalctl -u orquestrador -f
```
## 6) Testes rapidos
```bash
curl -s http://127.0.0.1:8080/openapi.json | head
```
```bash
curl -s -X POST http://127.0.0.1:8080/chat \
-H "Content-Type: application/json" \
-d '{"message":"Quero um sedan ate 50000"}'
```
## 8) Validacao rapida
```bash
curl -s -X POST http://127.0.0.1:8080/mock/consultar-estoque \
-H "Content-Type: application/json" \
-d '{"preco_max":50000,"categoria":"sedan"}'
```
Checklist minimo:
- o servico sobe sem excecao
- o Redis responde
- o bot aparece online no Telegram
- uma mensagem simples recebe resposta
## 7) Atualizacao em producao
Teste funcional:
1. Abra o bot no Telegram.
2. Envie uma mensagem como `Ola`.
3. Envie uma mensagem operacional como `Quero ver carros ate 50000 reais`.
4. Verifique os logs do servico se necessario.
Sempre que subir novas mudancas:
## 9) Atualizacao em producao
```bash
cd /opt/orquestrador

@ -8,7 +8,6 @@ WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
@ -19,15 +18,9 @@ FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PORT=8080
WORKDIR /app
# Install runtime dependencies only
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
# Copy Python dependencies from builder
COPY --from=builder /root/.local /root/.local
@ -36,9 +29,5 @@ COPY app /app/app
ENV PATH=/root/.local/bin:$PATH
EXPOSE 8080
#HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
# CMD python -c "import httpx; httpx.get('http://localhost:8080/docs', timeout=5)" || exit 1
CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8080}"]
# Sobe o bootstrap de banco e inicia o satelite do Telegram.
CMD ["sh", "-c", "python -m app.db.init_db && python -m app.integrations.telegram_satellite_service"]

@ -1,69 +1,76 @@
# AI Orquestrador
Backend conversacional para concessionária, focado em orquestrar ferramentas de negócio com apoio de LLM.
Orquestrador conversacional para concessionaria, focado em combinar:
- interpretacao semantica via Vertex AI
- execucao deterministica de tools de negocio
- normalizacao tecnica na aplicacao
- atendimento pelo Telegram como canal principal
A proposta do sistema é:
- receber uma mensagem em linguagem natural
- deixar o modelo interpretar a intenção do usuário
- usar ferramentas do sistema quando precisar consultar dados ou executar ações
- devolver uma resposta objetiva ao usuário
## Proposta
Hoje o projeto já implementa esse fluxo, mas ainda inclui camadas de suporte ao modelo, como extração estruturada, memória temporária por usuário, coleta incremental de dados e fallback determinístico de resposta.
A ideia central do sistema e:
- o usuario conversa em linguagem natural
- o modelo entende intencao, contexto e proximo passo
- a aplicacao normaliza dados, protege regras e executa tools
- a resposta final volta para o usuario pelo canal de atendimento
## Visão Geral
Hoje o projeto esta mais proximo de um orquestrador conversacional do que de uma API tradicional. O FastAPI continua no repositorio como apoio legado/bootstrap, mas o fluxo operacional principal e o satelite do Telegram.
O domínio atual do projeto cobre dois fluxos principais:
## Estado Atual
O dominio atual cobre dois grupos principais:
- vendas
- revisão/pós-venda
- revisao e pos-venda
O sistema consegue:
- consultar estoque de veículos
Capacidades ja implementadas:
- consultar estoque de veiculos
- validar cliente para compra
- avaliar veículo para troca
- criar pedido de compra
- avaliar veiculo para troca
- criar pedido
- listar pedidos
- cancelar pedido
- agendar revisão
- listar revisões agendadas
- cancelar revisão
- remarcar revisão
- agendar revisao
- listar revisoes
- cancelar revisao
- remarcar revisao
Além do roteamento por tool, o orquestrador também trata cenários conversacionais como:
- mensagens com mais de um pedido na mesma entrada
- coleta de parâmetros em múltiplas mensagens
- reaproveitamento de contexto temporário
- confirmação antes de trocar de assunto
- sugestão de próximo horário quando uma revisão entra em conflito
Capacidades conversacionais ja tratadas:
- multiplos pedidos na mesma mensagem
- fila de pedidos
- troca de contexto entre dominios
- coleta incremental de campos
- reaproveitamento de contexto temporario
- confirmacao antes de mudar de assunto
- sugestao de horario alternativo em conflito de agenda
## Stack Atual
## Arquitetura Atual
| Componente | Tecnologia | Observação |
| Componente | Tecnologia | Papel atual |
| --- | --- | --- |
| Backend | FastAPI | API principal |
| LLM | Google Vertex AI | Modelos Gemini |
| Banco de tools | MySQL | Metadados das ferramentas |
| Banco mock de negócio | MySQL | Veículos, clientes, usuários, pedidos e revisões |
| ORM | SQLAlchemy | Acesso aos dois bancos |
| Runtime | Python 3.11+ | Aplicação principal |
| Integração de canal | Telegram Bot API | Long polling via serviço satélite |
| Containerização | Docker | Ambiente local e deploy |
## Como o Sistema Funciona
Fluxo principal do `/chat`:
1. O usuário envia uma mensagem.
2. O `OrquestradorService` inicializa ou atualiza o contexto temporário do usuário.
3. O sistema tenta separar múltiplos pedidos contidos na mesma mensagem.
4. O LLM ajuda a extrair intenções e entidades estruturadas.
5. Se houver fluxo em aberto, o orquestrador continua a coleta dos dados faltantes.
6. Quando necessário, o modelo escolhe uma tool.
7. A tool é executada pelos handlers do sistema.
8. O resultado é devolvido ao usuário, usando resposta do modelo ou fallback determinístico.
| LLM | Vertex AI / Gemini | interpretacao e apoio a decisao |
| Orquestracao | Python | coordena contexto, fluxo e tools |
| Metadados de tools | MySQL | catalogo das tools disponiveis |
| Dados mock de negocio | MySQL | veiculos, clientes, usuarios, pedidos e revisoes |
| Estado conversacional | Redis ou memoria | rascunhos, fila e contexto temporario |
| Canal | Telegram Bot API | atendimento principal via long polling |
| Containerizacao | Docker Compose | ambiente local e deploy simples |
## Fluxo Principal
Fluxo principal do atendimento pelo Telegram:
1. O usuario envia uma mensagem ao bot.
2. O `TelegramSatelliteService` identifica ou cria o usuario interno.
3. O `OrquestradorService` recupera o contexto conversacional.
4. O `MessagePlanner` e o Vertex ajudam a estruturar a intencao do turno.
5. A aplicacao continua fluxos abertos ou decide a proxima tool.
6. A tool e executada com validacoes e normalizacoes deterministicas.
7. O sistema devolve a resposta ao usuario no Telegram.
Importante:
- a memória de conversa atual é volátil e fica em memória do processo
- o sistema ainda não persiste histórico conversacional entre reinícios
- o `response_formatter` responde direto ao usuário; ele não devolve o resultado para o modelo
- o historico completo da conversa ainda nao e persistido como trilha audivel;
- o estado de trabalho pode ficar em memoria ou Redis;
- em producao, Redis e o backend recomendado para continuidade real.
## Estrutura do Projeto
@ -71,11 +78,6 @@ Importante:
app/
main.py
api/
routes/
chat.py
mock.py
tools.py
dependencies.py
schemas.py
core/
settings.py
@ -87,170 +89,117 @@ app/
mock_seed.py
models/
tool.py
integrations/
telegram_satellite_service.py
models/
tool_model.py
repositories/
tool_repository.py
user_repository.py
integrations/
telegram_satellite_service.py
services/
ai/
llm_service.py
domain/
credit_service.py
inventory_service.py
order_service.py
review_service.py
flows/
order_flow.py
review_flow.py
orchestration/
orquestrador_service.py
orchestrator_config.py
conversation_policy.py
conversation_state_repository.py
conversation_state_store.py
entity_normalizer.py
message_planner.py
orquestrador_service.py
prompt_builders.py
redis_state_repository.py
response_formatter.py
tool_executor.py
turn_decision.py
tools/
handlers.py
tool_registry.py
user/
mock_customer_service.py
user_service.py
tests/
...
```
## Organização das Camadas
### `services/orchestration`
Núcleo do orquestrador:
- coordenação do fluxo conversacional
- controle de contexto ativo
- fila de pedidos
- construção de prompts
- fallback de resposta
### `services/flows`
Fluxos incrementais de negócio:
- criação e cancelamento de pedido
- agendamento, listagem, cancelamento e remarcação de revisão
### `services/ai`
Integração com Vertex AI e Gemini.
## Tools Disponiveis
### `services/tools`
Registro das tools e implementação dos handlers que executam as ações reais.
### `services/user`
Serviços ligados à identificação e persistência de usuários de canal, como Telegram.
## Ferramentas Disponíveis
As tools padrão são semeadas a partir de [app/db/tool_seed.py](app/db/tool_seed.py):
As definicoes padrao ficam em [app/db/tool_seed.py](/d:/vitor/Pessoal/PJ/Orquestrador/app/db/tool_seed.py):
| Tool | Finalidade |
| --- | --- |
| `consultar_estoque` | Consulta veículos por preço, categoria e ordenação |
| `validar_cliente_venda` | Valida elegibilidade de compra por CPF e valor |
| `avaliar_veiculo_troca` | Estima valor de troca do veículo do cliente |
| `agendar_revisao` | Agenda revisão com cálculo de valor |
| `listar_agendamentos_revisao` | Lista revisões do usuário |
| `cancelar_agendamento_revisao` | Cancela uma revisão por protocolo |
| `editar_data_revisao` | Remarca revisão existente |
| `realizar_pedido` | Cria pedido de compra |
| `cancelar_pedido` | Cancela pedido existente |
## Bancos e Seeds
O projeto usa duas conexões distintas:
### Banco de tools
Responsável por armazenar:
- nome da tool
- descrição
- schema de parâmetros
Arquivos principais:
- `app/db/database.py`
- `app/db/models/tool.py`
- `app/db/tool_seed.py`
### Banco mock de negócio
Responsável por armazenar dados fictícios de:
- veículos
- clientes
- usuários
- pedidos
- agendamentos de revisão
Arquivos principais:
- `app/db/mock_database.py`
- `app/db/mock_models.py`
- `app/db/mock_seed.py`
No startup, a aplicação tenta:
- criar as tabelas dos dois bancos
- popular tools
- popular dados mock
- fazer warmup do LLM
| `consultar_estoque` | consulta veiculos por preco, categoria e ordenacao |
| `validar_cliente_venda` | valida elegibilidade de compra por CPF e valor |
| `avaliar_veiculo_troca` | estima valor de troca do veiculo do cliente |
| `agendar_revisao` | agenda revisao com calculo de valor |
| `listar_agendamentos_revisao` | lista revisoes do usuario |
| `cancelar_agendamento_revisao` | cancela revisao por protocolo |
| `editar_data_revisao` | remarca revisao existente |
| `realizar_pedido` | cria pedido de compra |
| `listar_pedidos` | lista pedidos do usuario |
| `cancelar_pedido` | cancela pedido existente |
| `limpar_contexto_conversa` | zera contexto e fila |
| `continuar_proximo_pedido` | retoma o proximo item da fila |
| `descartar_pedidos_pendentes` | limpa apenas a fila pendente |
| `cancelar_fluxo_atual` | encerra apenas o fluxo atual |
## Banco e Bootstrap
O projeto usa duas conexoes MySQL:
- banco de tools
- banco mock de negocio
O bootstrap atual cria tabelas e executa seed por meio de:
- [app/db/init_db.py](/d:/vitor/Pessoal/PJ/Orquestrador/app/db/init_db.py)
Esse bootstrap e usado no container e pode ser executado manualmente antes do servico principal.
## Execucao Local
## Endpoints
### Chat
- `POST /chat`
- entrada principal do orquestrador
### Tools
- `GET /tools/`
- `POST /tools/`
- `GET /tools/{tool_id}`
- `DELETE /tools/{tool_id}`
### Mock
Endpoints diretos para depuração e testes manuais dos handlers:
- `POST /mock/consultar-estoque`
- `POST /mock/validar-cliente-venda`
- `POST /mock/avaliar-veiculo-troca`
- `POST /mock/agendar-revisao`
- `POST /mock/listar-agendamentos-revisao`
- `POST /mock/cancelar-agendamento-revisao`
- `POST /mock/editar-data-revisao`
- `POST /mock/realizar-pedido`
- `POST /mock/cancelar-pedido`
### Sem Docker
Documentação interativa:
- `GET /docs`
1. Configure as variaveis de ambiente com base em `.env.example`.
2. Inicialize banco e seed:
## Execução Local
```bash
python -m app.db.init_db
```
### Sem Docker
3. Inicie o satelite do Telegram:
```bash
uvicorn app.main:app --reload
python -m app.integrations.telegram_satellite_service
```
### Com Docker Compose
O compose atual sobe:
- `mysql`
- `redis`
- `telegram`
Subida completa:
```bash
docker-compose up
docker compose up --build
```
Para testar estado conversacional em Redis com Docker Compose:
Somente infraestrutura:
```bash
docker-compose up redis app
docker compose up mysql redis
```
Arquivos úteis:
- `TEST_CASES.md`
- `DEPLOY_SERVIDOR.md`
- `.env.example`
## Variáveis de Ambiente
## Variaveis de Ambiente
Principais variáveis:
Principais variaveis:
### Vertex AI
@ -285,7 +234,7 @@ Principais variáveis:
- `TELEGRAM_POLLING_TIMEOUT`
- `TELEGRAM_REQUEST_TIMEOUT`
### Estado Conversacional
### Estado conversacional
- `CONVERSATION_STATE_BACKEND` (`memory` ou `redis`)
- `CONVERSATION_STATE_TTL_MINUTES`
@ -293,36 +242,50 @@ Principais variáveis:
- `REDIS_KEY_PREFIX`
- `REDIS_SOCKET_TIMEOUT_SECONDS`
## Telegram Satellite Service
### Ambiente
Existe um serviço satélite para atendimento via Telegram em long polling.
- `ENVIRONMENT`
- `DEBUG`
Arquivo principal:
- `app/integrations/telegram_satellite_service.py`
Observacao:
- para producao com Telegram, prefira `CONVERSATION_STATE_BACKEND=redis`;
- evite valores nao booleanos em `DEBUG`.
Execução:
## Docker
O [Dockerfile](/d:/vitor/Pessoal/PJ/Orquestrador/Dockerfile) hoje sobe o servico principal do projeto:
```bash
python -m app.integrations.telegram_satellite_service
python -m app.db.init_db && python -m app.integrations.telegram_satellite_service
```
Isso deixa o container alinhado com o uso atual do sistema, sem assumir FastAPI como interface principal.
## Testes
Suite automatizada atual:
```bash
python -m unittest discover -s tests -v
```
Esse serviço:
- consome mensagens do Telegram
- cria ou recupera o usuário interno
- encaminha a mensagem para o `OrquestradorService`
- envia a resposta de volta ao chat
## Estado Atual do Projeto
O sistema já está além de um MVP simples de tool calling. Hoje ele combina:
- decisão assistida por modelo
- execução determinística de tools
- memória volátil por usuário
- fluxos multi-etapas
- múltiplos pedidos na mesma conversa
Pontos que ainda permanecem em aberto:
- persistência real de histórico conversacional
- suíte automatizada de testes
- evolução da autonomia do modelo
- alinhamento contínuo entre prompts, fluxo e regras de negócio
Se o ambiente local tiver `DEBUG` com valor invalido, force antes da execucao:
```bash
DEBUG=false python -m unittest discover -s tests -v
```
## Arquivos Uteis
- [TEST_CASES.md](/d:/vitor/Pessoal/PJ/Orquestrador/TEST_CASES.md)
- [DEPLOY_SERVIDOR.md](/d:/vitor/Pessoal/PJ/Orquestrador/DEPLOY_SERVIDOR.md)
- [.env.example](/d:/vitor/Pessoal/PJ/Orquestrador/.env.example)
## Proximos Passos Naturais
Os proximos ganhos mais valiosos para o projeto sao:
- persistir trilha de conversa e decisoes
- desacoplar bootstrap de banco do startup da aplicacao
- aumentar observabilidade por turno e por tool
- reduzir o tamanho do `OrquestradorService`
- consolidar documentacao operacional Telegram-first

@ -6,34 +6,27 @@ Este documento foi reorganizado para validar a etapa atual do projeto: orquestra
### Setup
Opcao sem Docker:
```bash
# Terminal 1
uvicorn app.main:app --reload --log-level debug
python -m app.db.init_db
python -m app.integrations.telegram_satellite_service
```
Opcional com Docker:
Opcao com Docker:
```bash
docker-compose up
docker compose up --build
```
### Endpoint principal
### Canal principal
Todos os cenarios abaixo usam:
```bash
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{
"message": "SUA_MENSAGEM_AQUI",
"user_id": "qa-user-001"
}'
```
Todos os cenarios abaixo devem ser executados enviando as mensagens diretamente ao bot no Telegram.
Observacoes:
- Reaproveite o mesmo `user_id` dentro de cada cenario multi-turno.
- Troque o `user_id` entre cenarios para evitar contaminacao de contexto.
- Quando precisar inspecionar handlers diretamente, use os endpoints em `/mock/*`.
- Reaproveite a mesma conta do Telegram dentro de cada cenario multi-turno.
- Entre cenarios, limpe o contexto ou use outra conta para evitar contaminacao.
- O foco deste roteiro e validar comportamento conversacional, nao endpoints HTTP.
## Convencoes de Validacao
@ -490,23 +483,6 @@ Esperado:
## Apoio de Debug
### Swagger UI
Acesse:
```text
http://localhost:8000/docs
```
### Endpoints mock uteis
- `POST /mock/agendar-revisao`
- `POST /mock/listar-agendamentos-revisao`
- `POST /mock/cancelar-agendamento-revisao`
- `POST /mock/editar-data-revisao`
- `POST /mock/realizar-pedido`
- `POST /mock/cancelar-pedido`
### O que observar nos logs
Com `--log-level debug`, acompanhe:

@ -1,7 +1,13 @@
from pydantic_settings import BaseSettings
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
extra="ignore",
)
google_project_id: str
google_location: str = "us-central1"
vertex_model_name: str = "gemini-2.5-pro"
@ -49,9 +55,23 @@ class Settings(BaseSettings):
redis_key_prefix: str = "orquestrador"
redis_socket_timeout_seconds: int = 5
class Config:
env_file = ".env"
extra = "ignore"
@field_validator("debug", mode="before")
@classmethod
def parse_debug_aliases(cls, value):
if isinstance(value, str):
normalized = value.strip().lower()
if normalized in {"debug", "development", "dev"}:
return True
if normalized in {"release", "production", "prod"}:
return False
return value
@field_validator("environment", "conversation_state_backend", mode="before")
@classmethod
def normalize_text_settings(cls, value):
if isinstance(value, str):
return value.strip().lower()
return value
settings = Settings()

@ -3,6 +3,7 @@ Inicializacao de banco de dados.
Cria tabelas e executa seed inicial em ambos os bancos.
"""
from app.core.settings import settings
from app.db.database import Base, engine
from app.db.mock_database import MockBase, mock_engine
from app.db.models import Tool
@ -14,24 +15,38 @@ from app.db.tool_seed import seed_tools
def init_db():
"""Cria tabelas e executa seed inicial em ambos os bancos."""
print("Inicializando bancos...")
failures: list[str] = []
try:
print("Criando tabelas MySQL (tools)...")
Base.metadata.create_all(bind=engine)
if settings.auto_seed_tools:
print("Populando tools iniciais...")
seed_tools()
else:
print("Seed de tools desabilitada por configuracao.")
print("MySQL tools OK.")
except Exception as exc:
print(f"Aviso: falha no MySQL (tools): {exc}")
failures.append(f"tools={exc}")
try:
print("Criando tabelas MySQL (dados ficticios)...")
MockBase.metadata.create_all(bind=mock_engine)
if settings.auto_seed_mock and settings.mock_seed_enabled:
print("Populando dados ficticios iniciais...")
seed_mock_data()
else:
print("Seed mock desabilitada por configuracao.")
print("MySQL mock OK.")
except Exception as exc:
print(f"Aviso: falha no MySQL mock: {exc}")
failures.append(f"mock={exc}")
if failures:
raise RuntimeError(
"Falha ao inicializar bancos do orquestrador: " + " | ".join(failures)
)
print("Bancos inicializados com sucesso!")

@ -1,10 +1,6 @@
from fastapi import FastAPI
from app.core.settings import settings
from app.db.database import Base, engine
from app.db.mock_database import MockBase, mock_engine
from app.db.models import Tool
from app.db.mock_models import Customer, Order, ReviewSchedule, Vehicle
from app.db.init_db import init_db
from app.services.ai.llm_service import LLMService
app = FastAPI(title="AI Orquestrador")
@ -15,26 +11,8 @@ async def startup_event():
"""
Inicializa o banco de dados e executa seeds automaticamente.
"""
print("[Auto-Seed] Iniciando configuracao do banco...")
# Tools (MySQL) e mock (MySQL) sobem de forma independente.
try:
Base.metadata.create_all(bind=engine)
if settings.auto_seed_tools:
from app.db.tool_seed import seed_tools
seed_tools()
print("[Auto-Seed] MySQL de tools inicializado.")
except Exception as e:
print(f"[Auto-Seed] Aviso: falha ao inicializar MySQL (tools): {e}")
try:
MockBase.metadata.create_all(bind=mock_engine)
if settings.auto_seed_mock and settings.mock_seed_enabled:
from app.db.mock_seed import seed_mock_data
seed_mock_data()
print("[Auto-Seed] MySQL de mock inicializado.")
except Exception as e:
print(f"[Auto-Seed] Aviso: falha ao inicializar MySQL (mock): {e}")
print("[Startup] Iniciando bootstrap legado do app HTTP...")
init_db()
try:
await LLMService().warmup()
@ -42,4 +20,4 @@ async def startup_event():
except Exception as e:
print(f"[Startup] Aviso: falha no warmup do LLM: {e}")
print("[Auto-Seed] Startup finalizado.")
print("[Startup] App HTTP legado inicializado.")

@ -1,5 +1,5 @@
[Unit]
Description=AI Orquestrador API (FastAPI/Uvicorn)
Description=AI Orquestrador Telegram Satellite
After=network.target
[Service]
@ -9,7 +9,7 @@ Group=vitor
WorkingDirectory=/opt/orquestrador
EnvironmentFile=/opt/orquestrador/.env.prod
Environment=PATH=/opt/orquestrador/venv/bin
ExecStart=/opt/orquestrador/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8080
ExecStart=/bin/sh -c '/opt/orquestrador/venv/bin/python -m app.db.init_db && /opt/orquestrador/venv/bin/python -m app.integrations.telegram_satellite_service'
Restart=always
RestartSec=5

@ -1,22 +1,20 @@
version: "3.8"
services:
postgres:
image: postgres:15-alpine
container_name: orquestrador_postgres
mysql:
image: mysql:8.4
container_name: orquestrador_mysql
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: orquestrador
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: orquestrador_mock
ports:
- "5432:5432"
- "3306:3306"
volumes:
- postgres_data:/var/lib/postgresql/data
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p$$MYSQL_ROOT_PASSWORD"]
interval: 10s
timeout: 5s
retries: 5
retries: 10
start_period: 20s
redis:
image: redis:7-alpine
@ -29,35 +27,42 @@ services:
timeout: 5s
retries: 5
app:
telegram:
build: .
container_name: orquestrador_app
ports:
- "8000:8080"
container_name: orquestrador_telegram
environment:
GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID}
GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID:-local-dev}
GOOGLE_LOCATION: ${GOOGLE_LOCATION:-us-central1}
DB_HOST: postgres
DB_PORT: 5432
DB_USER: postgres
DB_PASSWORD: postgres
DB_NAME: orquestrador
MOCKAROO_API_KEY: ${MOCKAROO_API_KEY}
MOCKAROO_BASE_URL: ${MOCKAROO_BASE_URL:-https://my.api.mockaroo.com}
USE_MOCKAROO_WRITES: ${USE_MOCKAROO_WRITES:-false}
VERTEX_MODEL_NAME: ${VERTEX_MODEL_NAME:-gemini-2.5-pro}
ENVIRONMENT: ${ENVIRONMENT:-development}
DEBUG: ${ORQUESTRADOR_DEBUG:-false}
DB_HOST: mysql
DB_PORT: 3306
DB_USER: root
DB_PASSWORD: root
DB_NAME: orquestrador_mock
MOCK_DB_HOST: mysql
MOCK_DB_PORT: 3306
MOCK_DB_USER: root
MOCK_DB_PASSWORD: root
MOCK_DB_NAME: orquestrador_mock
AUTO_SEED_TOOLS: ${AUTO_SEED_TOOLS:-true}
AUTO_SEED_MOCK: ${AUTO_SEED_MOCK:-true}
MOCK_SEED_ENABLED: ${MOCK_SEED_ENABLED:-true}
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:-}
TELEGRAM_POLLING_TIMEOUT: ${TELEGRAM_POLLING_TIMEOUT:-30}
TELEGRAM_REQUEST_TIMEOUT: ${TELEGRAM_REQUEST_TIMEOUT:-45}
CONVERSATION_STATE_BACKEND: ${CONVERSATION_STATE_BACKEND:-redis}
CONVERSATION_STATE_TTL_MINUTES: ${CONVERSATION_STATE_TTL_MINUTES:-60}
REDIS_URL: ${REDIS_URL:-redis://redis:6379/0}
REDIS_KEY_PREFIX: ${REDIS_KEY_PREFIX:-orquestrador}
REDIS_SOCKET_TIMEOUT_SECONDS: ${REDIS_SOCKET_TIMEOUT_SECONDS:-5}
depends_on:
postgres:
mysql:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- .:/app
command: uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
restart: unless-stopped
volumes:
postgres_data:
mysql_data:

@ -0,0 +1,73 @@
import unittest
from unittest.mock import patch
from app.core.settings import Settings
from app.db import init_db as init_db_module
class SettingsParsingTests(unittest.TestCase):
def test_debug_accepts_release_alias(self):
settings = Settings(
google_project_id="test-project",
debug="release",
)
self.assertFalse(settings.debug)
def test_normalizes_environment_and_backend_values(self):
settings = Settings(
google_project_id="test-project",
environment=" Production ",
conversation_state_backend=" Redis ",
)
self.assertEqual(settings.environment, "production")
self.assertEqual(settings.conversation_state_backend, "redis")
class InitDbBootstrapTests(unittest.TestCase):
@patch.object(init_db_module, "seed_tools")
@patch.object(init_db_module, "seed_mock_data")
@patch.object(init_db_module.MockBase.metadata, "create_all")
@patch.object(init_db_module.Base.metadata, "create_all")
def test_init_db_respects_seed_flags(
self,
tools_create_all,
mock_create_all,
seed_mock_data,
seed_tools,
):
with patch.object(init_db_module.settings, "auto_seed_tools", False), patch.object(
init_db_module.settings,
"auto_seed_mock",
False,
), patch.object(init_db_module.settings, "mock_seed_enabled", True):
init_db_module.init_db()
tools_create_all.assert_called_once()
mock_create_all.assert_called_once()
seed_tools.assert_not_called()
seed_mock_data.assert_not_called()
@patch.object(init_db_module, "seed_tools")
@patch.object(init_db_module, "seed_mock_data")
@patch.object(init_db_module.MockBase.metadata, "create_all")
@patch.object(init_db_module.Base.metadata, "create_all", side_effect=RuntimeError("tools db down"))
def test_init_db_raises_when_any_backend_fails(
self,
tools_create_all,
mock_create_all,
seed_mock_data,
seed_tools,
):
with self.assertRaisesRegex(RuntimeError, "tools=tools db down"):
init_db_module.init_db()
tools_create_all.assert_called_once()
mock_create_all.assert_called_once()
seed_mock_data.assert_called_once()
seed_tools.assert_not_called()
if __name__ == "__main__":
unittest.main()
Loading…
Cancel
Save