API: Agents¶
Visão Geral¶
Gerenciamento de agentes IA que processam conversas. Cada agente possui um prompt, mensagens customizáveis e base de conhecimento.
Endpoints¶
GET /api/agents¶
Lista todos os agentes do tenant.
Query Parameters:
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| includeArchived | bool | Sim | Incluir agentes arquivados na listagem |
Response (200 OK):
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Suporte TI",
"description": "Agente para suporte técnico interno",
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"todayConversations": 12,
"isArchived": false
}
]
GET /api/agents/{id}¶
Retorna detalhes de um agente.
Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Suporte TI",
"description": "Agente para suporte técnico interno",
"prompt": "Você é um assistente de TI...",
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-20T14:00:00Z",
"totalConversations": 150,
"todayConversations": 12,
"activeKnowledgeBlocksCount": 5,
"totalKnowledgeBlocksCount": 8,
"totalFaqCount": 15,
"instancesCount": 2
}
POST /api/agents¶
Cria um novo agente.
Autorização: Admin
Request:
{
"name": "Suporte TI",
"description": "Agente para suporte técnico interno",
"prompt": "Você é um assistente de TI especializado...",
"isActive": true
}
Response (201 Created):
PUT /api/agents/{id}¶
Atualiza um agente.
Autorização: Admin
Request:
{
"name": "Suporte TI v2",
"description": "Agente atualizado",
"prompt": "Você é um assistente de TI...",
"isActive": true
}
Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Suporte TI v2",
"updatedAt": "2024-01-20T14:00:00Z"
}
DELETE /api/agents/{id}¶
Arquiva um agente (soft delete). O agente fica inativo e não processa mensagens, mas pode ser restaurado.
Autorização: Admin
Response: 204 No Content
POST /api/agents/{id}/restore¶
Restaura um agente arquivado.
Autorização: Admin
Response: 204 No Content
Comportamento:
- Remove flag de arquivamento (IsArchived = false, ArchivedAt = null)
- Agente volta ao estado inativo (IsActive = false) — o admin deve ativá-lo manualmente
DELETE /api/agents/{id}/permanent?name={confirmationName}¶
Exclui permanentemente um agente arquivado e todos os dados associados. Requer confirmação por nome.
Autorização: Admin
Query Parameters:
| Parâmetro | Tipo | Descrição |
|---|---|---|
| name | string | Nome do agente (deve coincidir para confirmar) |
Response: 204 No Content
Comportamento:
- Somente agentes arquivados podem ser excluídos permanentemente
- Remove em cascata: steps, knowledge, FAQs, prompt versions, escalation rules, attachments
- Remove arquivos do R2 (storage) em paralelo (Parallel.ForEachAsync, MaxDegreeOfParallelism=10)
- Ação irreversível — não pode ser desfeita
Erros:
| Status | Código | Descrição |
|---|---|---|
| 404 | AGENT_NOT_FOUND | Agente não encontrado |
| 400 | VALIDATION_ERROR | Nome de confirmação não confere ou agente não está arquivado |
Sub-recursos¶
Mensagens Customizadas¶
GET /api/agents/{agentId}/messages¶
Retorna as mensagens customizadas do agente.
Response (200 OK):
{
"errorMessage": "Desculpe, estou com dificuldades técnicas.",
"welcomeMessage": "Olá! Como posso ajudar?",
"rateLimitMessage": "Você está enviando muitas mensagens.",
"outOfHoursMessage": "Nosso atendimento funciona das 8h às 18h."
}
PUT /api/agents/{agentId}/messages¶
Atualiza as mensagens customizadas.
Request:
{
"errorMessage": "Desculpe, tente novamente.",
"welcomeMessage": "Bem-vindo!",
"rateLimitMessage": null,
"outOfHoursMessage": null
}
Base de Conhecimento¶
GET /api/agents/{agentId}/knowledge¶
Lista blocos de conhecimento do agente.
Response (200 OK):
[
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"title": "Impressoras",
"content": "## Problemas comuns\n\n- Papel atolado...",
"displayOrder": 0,
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
]
GET /api/agents/{agentId}/knowledge/{id}¶
Retorna um bloco específico.
POST /api/agents/{agentId}/knowledge¶
Cria um bloco de conhecimento.
Request:
{
"title": "Impressoras",
"content": "## Problemas comuns\n\n- Papel atolado...",
"displayOrder": 0,
"isActive": true
}
PUT /api/agents/{agentId}/knowledge/{id}¶
Atualiza um bloco.
DELETE /api/agents/{agentId}/knowledge/{id}¶
Remove um bloco.
POST /api/agents/{agentId}/knowledge/reorder¶
Reordena os blocos.
Request:
{
"orderedIds": [
"660e8400-e29b-41d4-a716-446655440000",
"770e8400-e29b-41d4-a716-446655440001"
]
}
FAQ (Perguntas Frequentes)¶
FAQs são pares pergunta/resposta que participam da busca semântica junto com a base de conhecimento.
GET /api/agents/{agentId}/faq¶
Lista FAQs do agente.
Response (200 OK):
[
{
"id": "990e8400-e29b-41d4-a716-446655440000",
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"question": "Qual o horário de funcionamento?",
"answer": "Funcionamos de segunda a sexta, das 9h às 18h.",
"displayOrder": 0,
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
]
GET /api/agents/{agentId}/faq/{id}¶
Retorna uma FAQ específica.
POST /api/agents/{agentId}/faq¶
Cria uma FAQ. O embedding é gerado automaticamente.
Request:
{
"question": "Qual o horário de funcionamento?",
"answer": "Funcionamos de segunda a sexta, das 9h às 18h.",
"isActive": true
}
Response (201 Created):
{
"id": "990e8400-e29b-41d4-a716-446655440000",
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"question": "Qual o horário de funcionamento?",
"answer": "Funcionamos de segunda a sexta, das 9h às 18h.",
"displayOrder": 0,
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
PUT /api/agents/{agentId}/faq/{id}¶
Atualiza uma FAQ. Se pergunta ou resposta mudarem, o embedding é regenerado.
Request:
{
"question": "Qual o horário de atendimento?",
"answer": "Funcionamos de segunda a sexta, das 9h às 18h, e sábados das 9h às 13h.",
"isActive": true
}
DELETE /api/agents/{agentId}/faq/{id}¶
Remove uma FAQ.
Response: 204 No Content
Follow-Up¶
Configuração de follow-up automático para agentes e etapas. O follow-up envia mensagens automáticas ao contato após um período de inatividade na conversa.
Existem dois níveis de follow-up: - Agent-level: configuração global do agente (aplica-se quando não há follow-up específico na etapa) - Step-level: configuração específica por etapa (sobrepõe o follow-up do agente)
GET /api/agents/{agentId}/follow-up¶
Retorna a configuração de follow-up do agente.
Response (200 OK):
{
"id": "aa0e8400-e29b-41d4-a716-446655440000",
"message": "Olá! Ainda precisa de ajuda?",
"delayMinutes": 30,
"maxSends": 2,
"allowedHours": [
{ "dayOfWeek": 1, "startTime": "09:00", "endTime": "18:00" }
],
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
Retorna null se não houver configuração de follow-up.
PUT /api/agents/{agentId}/follow-up¶
Cria ou atualiza (upsert) a configuração de follow-up do agente.
Autorização: Admin
Request:
{
"message": "Olá! Ainda precisa de ajuda?",
"delayMinutes": 30,
"maxSends": 2,
"allowedHours": [
{ "dayOfWeek": 1, "startTime": "09:00", "endTime": "18:00" }
]
}
Response (200 OK): FollowUpConfigResponse (mesmo schema do GET)
DELETE /api/agents/{agentId}/follow-up¶
Remove a configuração de follow-up do agente.
Autorização: Admin
Response: 204 No Content
GET /api/agents/{agentId}/steps/{stepId}/follow-up¶
Retorna a configuração de follow-up de uma etapa específica.
Response (200 OK): FollowUpConfigResponse (mesmo schema do GET agent-level) ou null.
PUT /api/agents/{agentId}/steps/{stepId}/follow-up¶
Cria ou atualiza (upsert) a configuração de follow-up de uma etapa.
Autorização: Admin
Request:
{
"message": "Você ainda está aí? Posso ajudar com mais alguma coisa?",
"delayMinutes": 15,
"maxSends": 1,
"allowedHours": null
}
Response (200 OK): FollowUpConfigResponse
DELETE /api/agents/{agentId}/steps/{stepId}/follow-up¶
Remove a configuração de follow-up de uma etapa.
Autorização: Admin
Response: 204 No Content
Anexos (Attachments)¶
Arquivos anexados ao agente que podem ser enviados automaticamente pela IA durante conversas.
GET /api/agents/{agentId}/attachments¶
Lista todos os anexos do agente.
Response (200 OK):
[
{
"id": "bb0e8400-e29b-41d4-a716-446655440000",
"agentId": "550e8400-e29b-41d4-a716-446655440000",
"fileName": "cardapio.pdf",
"mimeType": "application/pdf",
"fileSizeBytes": 204800,
"storageUrl": "https://r2.example.com/tenants/.../attachments/cardapio.pdf",
"displayName": "Cardápio Atualizado",
"description": "Cardápio completo com preços",
"tags": "cardapio,precos",
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
]
POST /api/agents/{agentId}/attachments¶
Faz upload de um novo anexo.
Autorização: Admin
Request:
{
"fileName": "cardapio.pdf",
"mimeType": "application/pdf",
"base64Content": "JVBERi0xLjQK...",
"displayName": "Cardápio Atualizado",
"description": "Cardápio completo com preços",
"tags": "cardapio,precos"
}
Response (201 Created): AttachmentResponse (mesmo schema do GET)
PUT /api/agents/{agentId}/attachments/{id}¶
Atualiza metadados de um anexo (display name, descrição, tags).
Autorização: Admin
Request:
{
"displayName": "Cardápio 2024",
"description": "Cardápio atualizado com novos itens",
"tags": "cardapio,precos,2024"
}
Response (200 OK): AttachmentResponse
DELETE /api/agents/{agentId}/attachments/{id}¶
Remove um anexo.
Autorização: Admin
Response: 204 No Content
PUT /api/agents/{agentId}/attachments/{id}/toggle¶
Ativa/desativa um anexo.
Autorização: Admin
Response (200 OK): AttachmentResponse
Etapas de Atendimento¶
Etapas guiam a IA por um fluxo estruturado de atendimento. Sem etapas, o agente responde livremente.
GET /api/agents/{agentId}/steps¶
Lista etapas do agente ordenadas por posição.
Response (200 OK):
[
{
"id": "aa0e8400-e29b-41d4-a716-446655440000",
"name": "Saudação",
"instructions": "Cumprimente o cliente e pergunte como pode ajudar...",
"optimizedInstructions": null,
"order": 1,
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
]
POST /api/agents/{agentId}/steps¶
Cria uma nova etapa. Instruções são otimizadas automaticamente.
Request:
Response (201 Created):
PUT /api/agents/{agentId}/steps/{id}¶
Atualiza uma etapa. Re-otimiza se as instruções mudaram.
Request:
{
"name": "Coleta do Pedido",
"instructions": "Pergunte ao cliente o que deseja pedir. Confirme cada item."
}
DELETE /api/agents/{agentId}/steps/{id}¶
Remove uma etapa. Conversas ativas nesta etapa terão CurrentStepId limpo.
Response: 204 No Content
PUT /api/agents/{agentId}/steps/reorder¶
Reordena as etapas.
Request:
PUT /api/agents/{agentId}/steps/{id}/toggle¶
Ativa/desativa uma etapa.
Validações de Etapas¶
| Campo | Regra |
|---|---|
| Name | Obrigatório, máx 100 caracteres |
| Instructions | Obrigatório, máx 20000 caracteres |
AI Assistant¶
Assistente de IA integrado ao portal para ajudar na criação e refinamento de conteúdo do agente (prompt, base de conhecimento, etapas).
- Requer autorização Admin
- Rate limiting LLM
- Processado via pipeline: docs/flows/ai-assistant-pipeline.md
POST /api/agents/{agentId}/ai-assistant/chat¶
Envia uma mensagem ao assistente de IA contextualizado pela seção do agente sendo editada.
Request:
{
"sectionKey": "freeform",
"userMessage": "Melhore este prompt para atendimento de pizzaria",
"history": [
{ "role": "user", "content": "Mensagem anterior" },
{ "role": "assistant", "content": "Resposta anterior" }
],
"currentContent": "Você é um assistente de atendimento...",
"contentTitle": "Prompt Principal",
"stepsContext": "1. Saudação\n2. Coleta do Pedido"
}
Campos:
| Campo | Tipo | Descrição |
|---|---|---|
| sectionKey | string | Seção sendo editada: freeform, knowledge, step, step_planner |
| userMessage | string | Mensagem do usuário (máx 4000 chars) |
| history | array | Histórico de mensagens da conversa atual |
| currentContent | string? | Conteúdo atual da seção (prompt, instrução, etc.) |
| contentTitle | string? | Título do conteúdo (ex: nome do bloco de conhecimento) |
| stepsContext | string? | Contexto das etapas do agente (apenas para step e step_planner) |
Response (200 OK):
{
"sectionKey": "freeform",
"suggestedContent": "Você é um assistente virtual especializado em pizzaria...",
"assistantMessage": "Aqui está uma versão melhorada do seu prompt...",
"quickReplies": ["Mais formal", "Mais curto", "Adicionar exemplos"]
}
| Campo | Tipo | Descrição |
|---|---|---|
| sectionKey | string? | Seção do conteúdo sugerido |
| suggestedContent | string? | Conteúdo sugerido para aplicar na seção |
| assistantMessage | string | Resposta textual do assistente |
| quickReplies | array? | Sugestões de respostas rápidas |
Playground¶
Endpoints para testar o agente diretamente pelo portal administrativo. Todos os endpoints do playground:
- Requerem autorização Admin (RequireAuthorization(AuthConstants.Roles.Admin))
- Estão sob rate limiting LLM (RequireRateLimiting(RateLimitPolicies.Llm))
- Upload de mídia para R2 é feito em paralelo com o processamento via PlaygroundMediaHelper
POST /api/agents/{agentId}/playground/chat¶
Envia mensagens de teste para o agente. Suporta texto e imagens (Claude Vision).
Request:
{
"messages": [
{
"role": "user",
"content": "Olá",
"type": "text"
},
{
"role": "assistant",
"content": "Olá! Como posso ajudar?",
"type": "text"
},
{
"role": "user",
"content": "O que tem nesta imagem?",
"type": "image",
"mediaBase64": "iVBORw0KGgo...",
"mediaMimeType": "image/jpeg"
}
]
}
Campos da mensagem (PlaygroundMessageDto):
| Campo | Tipo | Descrição |
|---|---|---|
| role | string | "user" ou "assistant" |
| content | string | Texto da mensagem (ou legenda da imagem) |
| type | string | "text" (padrão) ou "image" |
| mediaBase64 | string? | Dados da imagem em base64 (apenas type="image") |
| mediaMimeType | string? | MIME type da mídia (ex: "image/jpeg") |
Response (200 OK):
Comportamento:
- Mensagens de texto são enviadas normalmente ao LLM
- Mensagens de imagem são convertidas para ChatMessage.WithImage() (Claude Vision multimodal)
- Limitado às últimas 20 mensagens do histórico
POST /api/agents/{agentId}/playground/transcribe¶
Transcreve áudio em texto usando Whisper (OpenAI).
Request:
| Campo | Tipo | Descrição |
|---|---|---|
| audioBase64 | string | Áudio gravado em base64 |
| mimeType | string | MIME type do áudio (ex: "audio/webm", "audio/ogg") |
Response (200 OK):
Comportamento: - Converte base64 para bytes e envia ao Whisper API - Retorna texto transcrito que o frontend usa como mensagem de texto - Validação: audioBase64 e mimeType são obrigatórios
POST /api/agents/{agentId}/playground/describe-image¶
Realiza OCR/descrição de imagem via Claude Vision. O frontend usa o texto retornado como contexto antes de enviar ao LLM no chat.
Request:
| Campo | Tipo | Descrição |
|---|---|---|
| imageBase64 | string | Dados da imagem em base64 |
| mimeType | string | MIME type da imagem (ex: "image/jpeg", "image/png") |
| caption | string? | Legenda opcional da imagem |
Response (200 OK):
{
"description": "Uma foto de uma pizza margherita em uma caixa de papelão branca...",
"mediaUrl": "https://r2.example.com/tenants/.../playground/image.jpeg"
}
| Campo | Tipo | Descrição |
|---|---|---|
| description | string | Texto descritivo gerado pelo Claude Vision |
| mediaUrl | string? | URL da imagem salva no R2 (pode ser null se upload falhar) |
Comportamento:
- OCR via Claude Vision e upload para R2 executam em paralelo (Task.WhenAll)
- Se o upload falhar, o processamento continua e mediaUrl retorna null
- Se a descrição falhar, retorna erro 400 com mensagem de validação
- Handler: PlaygroundDescribeImageHandler
Rate Limiting¶
Endpoints de escrita (POST, PUT, DELETE) de agentes e sub-recursos (KB, steps, FAQ, attachments, prompt versions, agent CRUD) usam a política AgentConfig: 30 requisições por 60 segundos por usuário.
Endpoints de leitura (GET) continuam na política Admin: 30 requisições por minuto.
Veja rate-limiting.md para detalhes.
Validações¶
CreateAgentRequest / UpdateAgentRequest¶
| Campo | Regra |
|---|---|
| Name | Obrigatório, máx 255 caracteres |
| Description | Máx 1000 caracteres |
| Prompt | Máx 20000 caracteres |
AgentAiAssistantRequest¶
| Campo | Regra |
|---|---|
| AgentId | Obrigatório |
| SectionKey | Obrigatório (freeform, knowledge, step, step_planner) |
| UserMessage | Obrigatório, máx 4000 caracteres |
CreateKnowledgeBlockRequest¶
| Campo | Regra |
|---|---|
| Title | Obrigatório, máx 200 caracteres |
| Content | Obrigatório |
CreateFaqItemRequest / UpdateFaqItemRequest¶
| Campo | Regra |
|---|---|
| Question | Obrigatório, máx 500 caracteres |
| Answer | Obrigatório |
Erros Comuns¶
| Status | Código | Descrição |
|---|---|---|
| 404 | AGENT_NOT_FOUND | Agente não encontrado |
| 409 | AGENT_NAME_EXISTS | Já existe agente com este nome |
| 400 | VALIDATION_ERROR | Campos inválidos |