Projeto de estudos sobre padrões de sistemas multi-agente com LLMs: prompt chaining, roteamento dinâmico, function calling e structured output — sem frameworks de agentes, tudo na mão com FastAPI + Groq.
Boilerplate de sistema multi-agente de suporte ao cliente. Clone, aponte pro seu produto e coloque em produção.
Uma mensagem entra → Router Agent classifica intenção e sentimento → roteia para o Agente Especialista correto → especialista usa function calling para consultar sistemas internos → resposta contextualizada com metadados completos do pipeline.
English version below ↓
| Padrão | Onde |
|---|---|
| Prompt Chaining | Router → lógica de roteamento → Especialista; cada etapa alimenta a próxima |
| Dynamic Routing | Decisão de roteamento em Python puro, sem LLM |
| Function Calling | Agentes especialistas chamam tools via Groq tool-use API |
| Structured Output | Router usa response_format: json_object + validação Pydantic |
Mensagem do Cliente
│
▼
┌─────────────────────┐
│ ROUTER AGENT │ Etapa 1 — 1 chamada LLM, JSON mode
│ │
│ intent │ billing | technical | sales | cancellation | general
│ sentiment │ positive | neutral | negative | frustrated | urgent
│ language │ pt-BR | en | es | …
│ confidence │ 0.0 — 1.0
│ summary │ resumo em uma frase
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ ROUTING LOGIC │ Etapa 2 — Python puro, sem LLM
│ │
│ confidence < 0.6? │ → resposta genérica (fallback)
│ intent = general? │ → resposta genérica (fallback)
│ else │ → roteia para especialista
└────────┬────────────┘
│
▼
┌──────────────────────────────────────┐
│ SPECIALIST AGENT (loop de tools) │ Etapa 3 — N chamadas LLM + execução de tools
│ │
│ BillingAgent → billing tools │
│ TechnicalAgent → technical tools │
│ SalesAgent → sales tools │
│ CancellationAgent → cancel tools │
└──────────────────────────────────────┘
# 1. Clone
git clone https://github.com/your-username/agentic-support-router.git
cd agentic-support-router
# 2. Instale as dependências
pip install -r requirements.txt
# 3. Configure
cp .env.example .env
# Edite o .env (veja a seção "Adaptando para seu produto" abaixo)
# 4. Rode
make run
# ou: uvicorn app.main:app --reload --port 8000Docs interativos em http://localhost:8000/docs.
Tudo que é específico do produto vive no .env. Você não precisa tocar em nenhum arquivo Python para trocar o produto:
# Identidade do produto
PRODUCT_NAME=MinhaEmpresa
PRODUCT_DESCRIPTION=uma plataforma de e-commerce B2B
SUPPORT_EMAIL=suporte@minhaempresa.com
SALES_EMAIL=vendas@minhaempresa.com
PRODUCT_URL=https://minhaempresa.com
# Chave da Groq (obrigatório)
GROQ_API_KEY=gsk_...| O que | Arquivo | Por quê |
|---|---|---|
| Dados mock dos clientes | app/tools/billing_tools.py |
Substituir por chamadas reais (Stripe, Paddle…) |
| Status dos serviços | app/tools/technical_tools.py |
Apontar para seu status page ou Datadog |
| Planos e preços | app/tools/sales_tools.py |
Buscar do seu banco ou billing provider |
| Ofertas de retenção | app/tools/cancellation_tools.py |
Conectar ao seu CRM |
| Novos agentes | app/agents/specialist/ |
Criar um BaseSpecialistAgent e registrar no orchestrator |
curl -X POST http://localhost:8000/support/chat \
-H "Content-Type: application/json" \
-d '{
"message": "Oi, tô tentando pagar minha fatura mas o cartão tá sendo recusado. Meu ID é C001.",
"session_id": "sess-001"
}'{
"reply": "Olá! Verifiquei sua conta (C001) — você está no plano Pro, R$49,90/mês. Vi que seu cartão Visa terminando em 4242 está ativo. Recomendo verificar com o banco se há algum bloqueio temporário. Deseja prosseguir?",
"routing_info": {
"intent": "billing",
"sentiment": "frustrated",
"language": "pt-BR",
"confidence": 0.95,
"summary": "Customer cannot pay invoice, card being declined"
},
"agent_used": "billing",
"tools_called": [
{"name": "get_customer_plan", "arguments": {"customer_id": "C001"}},
{"name": "get_payment_methods", "arguments": {"customer_id": "C001"}}
],
"processing_time_ms": 1823,
"session_id": "sess-001"
}curl -X POST http://localhost:8000/support/chat \
-H "Content-Type: application/json" \
-d '{
"message": "I keep getting ERR_SYNC_001 when trying to sync my GitHub integration."
}'curl -X POST http://localhost:8000/support/chat \
-H "Content-Type: application/json" \
-d '{
"message": "Qual a diferença entre o plano Pro e o Enterprise?"
}'GET /support/agents retorna a lista completa.
| Agente | Tools |
|---|---|
billing |
get_customer_plan, get_invoice, get_payment_methods |
technical |
check_service_status, get_known_issues, get_error_details |
sales |
get_available_plans, get_promotions, compare_plans |
cancellation |
get_retention_offers, get_cancellation_policy, process_cancellation |
O output do router (Classification) é passado para o especialista antes de gerar qualquer token — dando contexto sobre intenção, sentimento e idioma sem nenhuma chamada extra ao LLM.
BaseSpecialistAgent implementa o loop:
- Envia a mensagem + schemas das tools para o Groq.
- Se o modelo retornar
tool_calls, executa cada uma viaToolRegistry. - Adiciona o resultado como
role: "tool"na conversa. - Repete até resposta em texto ou
max_tool_iterations(padrão 3).
Cada chamada é registrada em tools_called na resposta.
O endpoint POST /support/chat aceita texto e retorna JSON estruturado. Para conectar ao WhatsApp:
- Receba a mensagem via webhook (Meta Cloud API, Twilio, Z-API, etc.).
- Faça POST para
/support/chatcom{"message": "...", "session_id": "<numero>"}. - Envie o campo
replyde volta ao usuário.
O session_id é retornado sem alteração — use o número do WhatsApp como identificador de sessão.
MODEL_NAME=llama-3.1-8b-instant # mais rápido e barato
MODEL_NAME=llama-3.3-70b-versatile # padrão, maior qualidadeQualquer modelo Groq com suporte a tool use funciona.
make testNenhuma chave da Groq necessária — todas as chamadas LLM são mockadas.
Study project exploring multi-agent LLM patterns: prompt chaining, dynamic routing, function calling and structured output — no agent frameworks, everything hand-rolled with FastAPI + Groq.
Boilerplate for a multi-agent customer support system. Clone, point it at your product, ship it.
A message comes in → Router Agent classifies intent and sentiment → routes to the right Specialist Agent → specialist uses function calling to query internal systems → contextualised reply with full pipeline metadata.
| Pattern | Where |
|---|---|
| Prompt Chaining | Router → routing logic → Specialist; each step feeds the next |
| Dynamic Routing | Pure Python routing decision — no extra LLM call |
| Function Calling | Specialist agents call tools via the Groq tool-use API |
| Structured Output | Router uses response_format: json_object + Pydantic validation |
Everything product-specific lives in .env — no Python files to edit just to change the product name:
PRODUCT_NAME=MyCompany
PRODUCT_DESCRIPTION=a B2B e-commerce platform
SUPPORT_EMAIL=support@mycompany.com
SALES_EMAIL=sales@mycompany.com
PRODUCT_URL=https://mycompany.com
GROQ_API_KEY=gsk_...| What | File | Why |
|---|---|---|
| Mock customer data | app/tools/billing_tools.py |
Replace with real calls (Stripe, Paddle…) |
| Service status | app/tools/technical_tools.py |
Point to your status page or Datadog |
| Plans & pricing | app/tools/sales_tools.py |
Fetch from your DB or billing provider |
| Retention offers | app/tools/cancellation_tools.py |
Connect to your CRM |
| New agents | app/agents/specialist/ |
Extend BaseSpecialistAgent, register in orchestrator |
git clone https://github.com/your-username/agentic-support-router.git
cd agentic-support-router
pip install -r requirements.txt
cp .env.example .env # fill in GROQ_API_KEY and product fields
make runThe BaseSpecialistAgent implements a tool-use loop: send message + tool schemas → execute any tool_calls → append results → repeat until plain text reply or max_tool_iterations. Every call is recorded in tools_called.
POST /support/chat accepts plain text and returns structured JSON. Receive the WhatsApp webhook, POST the message body, send the reply field back. Use session_id to track conversation threads.
make test # no Groq API key requiredMIT