Compare commits
10
Commits
e791474d15
...
5748888997
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5748888997 | ||
|
|
e614763023 | ||
|
|
d873ee02dd | ||
|
|
da3b38551f | ||
|
|
c9ffc580d6 | ||
|
|
e5f3d27230 | ||
|
|
69b34ead9e | ||
|
|
1e2b93c7be | ||
|
|
48efbe478b | ||
|
|
f3e513a73a |
+25
@@ -0,0 +1,25 @@
|
||||
# Environment
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Dependencies / build output
|
||||
node_modules/
|
||||
dist/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# OS / editor
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Temp / cache
|
||||
*.tmp
|
||||
*.bak
|
||||
.cache/
|
||||
|
||||
# Documentação local de fornecedor aguardando revisão antes de versionamento
|
||||
docs/API Clinicall.pdf
|
||||
Binary file not shown.
@@ -9,7 +9,21 @@
|
||||
> nativo do n8n). A seção 14 cobre os outros três providers — **Klingo**,
|
||||
> **SysLife** e **ClinicAll** — todos construídos com o mesmo padrão de
|
||||
> MCP Server nativo, mas ainda sem clínica real pra testar (ver cada
|
||||
> subseção pras specs/planos completos e pendências específicas).
|
||||
> subseção pras specs/planos completos e pendências específicas). As
|
||||
> seções 15-17 cobrem features transversais do Z-PRO (config de IA via
|
||||
> Google Docs, memória via Postgres, MCP de mensageria standalone). As
|
||||
> seções 18-20 cobrem a feature de **confirmação de agenda via
|
||||
> WhatsApp**, replicada nos três providers adicionais (Klingo,
|
||||
> ClinicAll, SysLife) — ver o resumo comparativo na seção 21. A seção 22
|
||||
> cobre os workflows de exemplo de AI Agent para **agendamento** desses
|
||||
> mesmos três providers (fechando a lacuna que só a confirmação tinha
|
||||
> preenchido) — ver o inventário geral de todos os workflows de exemplo
|
||||
> (agendamento + confirmação, nos 4 providers) no final da seção 22. A
|
||||
> seção 23 registra três bugs reais encontrados em produção no Z-PRO
|
||||
> depois que o gestor da clínica editou o AI Agent pela UI (endpoint MCP
|
||||
> revertido pro servidor errado, agendamento em horário já passado) e o
|
||||
> estado da investigação sobre suporte a imagem/áudio (ainda sem
|
||||
> solução — depende do painel admin do Z-PRO).
|
||||
|
||||
## 1. Node MCP: qual usar e como instalar
|
||||
|
||||
@@ -800,3 +814,300 @@ exemplo, prontos pra ativar quando houver clínica ClinicAll real —
|
||||
falta resolver a autenticação (mesma pendência do MVP de agendamento),
|
||||
preencher o `baseUrl` real, e provisionar banco Postgres + credencial
|
||||
pro cliente.
|
||||
|
||||
## 20. Confirmação de Agenda via WhatsApp (SysLife)
|
||||
|
||||
**Data:** 11/08/2026. Ver design completo em
|
||||
`docs/superpowers/specs/2026-08-11-confirmacao-agenda-syslife-design.md`
|
||||
e plano em
|
||||
`docs/superpowers/plans/2026-08-11-confirmacao-agenda-syslife.md`.
|
||||
|
||||
**Motivação**: mesmo objetivo das seções 18 (Klingo) e 19 (ClinicAll),
|
||||
agora para o SysLife. O recurso relevante, `AgendaWSApp`, já vem
|
||||
descrito no manual (`Manual WS SysLife V3.4.pdf`, salvo em
|
||||
`docs/Manual WS SysLife V3.4 (1).pdf`, relido nesta sessão — não
|
||||
estava commitado no repositório antes) explicitamente como "retorna os
|
||||
registros da agenda **para uso do WhatsApp**".
|
||||
|
||||
**MCP Server "SysLife - Confirmação de Agenda"** (`xq8CyqyewtENpIMW`) — 3 tools:
|
||||
|
||||
| Tool | Endpoint | Parâmetro | Workflow id |
|
||||
| --- | --- | --- | --- |
|
||||
| `listar_agendamentos_do_dia` | `GET /AgendaWSApp` | `QuantidadeDias` | `ezcqDpU4R4LuHldj` |
|
||||
| `buscar_agendamento_por_telefone` | `GET /AgendaWSApp` | `NumeroCelular` | `sTLFa6VyUkBnM8C0` |
|
||||
| `confirmar_presenca` | `POST /AgendaWSApp` | `CodigoAgenda` + `Resposta` (`"1"`/`"2"`) | `Pv0BbagqdeoBgENZ` |
|
||||
|
||||
**Ponto único deste provider**: um mesmo `GET /AgendaWSApp` serve três
|
||||
propósitos diferentes conforme qual dos três parâmetros é enviado
|
||||
(`QuantidadeDias`, `CodigoAgenda` ou `NumeroCelular` — o manual é
|
||||
explícito: **"Deverá ser usado somente um dos 3 parâmetros"**). Duas
|
||||
tools deste MCP (`listar_agendamentos_do_dia`,
|
||||
`buscar_agendamento_por_telefone`) apontam pro mesmo endpoint via
|
||||
sub-workflows diferentes, cada um montando só o parâmetro que lhe
|
||||
cabe.
|
||||
|
||||
**Diferenças reais em relação a Klingo e ClinicAll**:
|
||||
|
||||
- **Autenticação já resolvida de verdade**: diferente dos outros dois,
|
||||
a credencial Basic Auth (`httpBasicAuth`, `u8XXJ6wyrKuGJp4Y`,
|
||||
`SysLife Basic Auth - clínica pendente`) **já existia**, criada
|
||||
durante o MVP de agendamento do SysLife — usuário/senha fixos do
|
||||
WebService, documentados literalmente no manual, não variam por
|
||||
clínica. Foi só reaproveitada, nenhuma credencial nova criada.
|
||||
- **`Resposta` é o mais simples dos três providers**: só `"1"` (SIM)
|
||||
ou `"2"` (NÃO), documentado direto no manual — sem precisar de tools
|
||||
de tabela de domínio como no ClinicAll, nem terceiro estado
|
||||
("Sem Resposta") como no Klingo.
|
||||
- **Resposta já vem com texto formatado pro WhatsApp**: os campos
|
||||
`txWhatsappProcedimento`/`txWhatsappHorario`/`txWhatsappMedico` já
|
||||
vêm prontos do próprio SysLife — usados no workflow de lembrete com
|
||||
fallback pros campos brutos quando ausentes.
|
||||
- **Telefone é campo direto do item** (`nuCelular`/`nuCelularDdd`),
|
||||
sem objeto `Paciente` aninhado como no ClinicAll.
|
||||
- **Fora de escopo**: `AgendaCentral` (central telefônica, não
|
||||
WhatsApp — mesmo raciocínio do `/entrada` do Klingo), `AgendaRetorno`
|
||||
(consultas de retorno), `AgendaStatus` (base pra NPS, sem endpoint
|
||||
de submissão documentado), `AgendaConfirmado` (só consulta
|
||||
informativa, sem ação).
|
||||
|
||||
**Workflow proativo "Lembrete de Confirmação - SysLife (exemplo)"**
|
||||
(`LpRRZxTsM0Vc9PsV`): cron 8h → `listar_agendamentos_do_dia`
|
||||
(`quantidadeDias: 1`, literal fixo — o próprio SysLife já calcula
|
||||
"amanhã" a partir desse número, sem precisar de node de cálculo de
|
||||
data) → loop → `enviar_texto` do MCP Mensageria ZPRO. Validado (0
|
||||
erros), deixado **inativo de propósito**.
|
||||
|
||||
**Workflow de exemplo "Confirmação de Agenda - GChat - SysLife+Z-PRO
|
||||
(exemplo)"** (`wBjXOZSEj5TopzfB`): mesmo padrão dos outros dois
|
||||
(webhook → AI Agent → memória Postgres), `systemMessage` ajustado pro
|
||||
fluxo mais simples (telefone → `buscar_agendamento_por_telefone` →
|
||||
`confirmar_presenca` com `resposta: "1"`/`"2"`). Tools conectadas:
|
||||
`buscar_agendamento_por_telefone`, `confirmar_presenca`
|
||||
(`listar_agendamentos_do_dia` só serve ao workflow proativo). Validado
|
||||
(0 erros), deixado **inativo de propósito**.
|
||||
|
||||
**Resultado**: MCP Server de confirmação de agenda (SysLife) completo
|
||||
e validado estruturalmente, junto com os dois workflows de exemplo,
|
||||
prontos pra ativar quando houver clínica SysLife real — diferente dos
|
||||
outros dois providers, a autenticação já está pronta; falta só
|
||||
preencher o `baseUrl` real (IP:9000) e provisionar banco Postgres +
|
||||
credencial pro cliente. Com isso, os 4 providers (Z-PRO em produção
|
||||
real, Klingo, ClinicAll, SysLife estruturalmente prontos) têm o mesmo
|
||||
padrão de confirmação de agenda implementado.
|
||||
|
||||
## 21. Resumo — Confirmação de Agenda via WhatsApp nos 4 providers
|
||||
|
||||
Visão consolidada das seções 18-20 (Z-PRO usa a mensageria própria da
|
||||
clínica exemplo, sem MCP de confirmação dedicado — a confirmação lá
|
||||
seria uma extensão futura do AI Agent já em produção, não construída
|
||||
nesta rodada).
|
||||
|
||||
| | **Klingo** | **ClinicAll** | **SysLife** |
|
||||
| --- | --- | --- | --- |
|
||||
| API/recurso | Klingo Telefonia (separada da de agendamento) | `TCalendarController`/`TDomainController` (mesma do agendamento) | `AgendaWSApp` (mesma API do agendamento) |
|
||||
| Endpoint de confirmação | `POST /confirmar` | `POST /AgendamentoFlags` | `POST /AgendaWSApp` |
|
||||
| Valor de confirmação | `status`: `"C"`/`"S"`/`"N"` (3 estados) | `estadoAgenda`+`situacaoAgenda`: strings longas exatas (tabela de domínio) | `Resposta`: `"1"`/`"2"` (2 estados, mais simples) |
|
||||
| Busca de pendente | `GET /lista_ultimo_por_cpf` (CPF) | `GET /agendamentoSemPaciente` (CPF **+** data de nascimento) | `GET /AgendaWSApp` (telefone) |
|
||||
| Autenticação | `X-APP-TOKEN` por clínica (token real confirmado, sem clínica de teste) | Não documentada (`authentication: none`, mesma pendência do MVP de agendamento) | **Já resolvida** — Basic Auth fixo, credencial reaproveitada do MVP de agendamento |
|
||||
| Tools no MCP Server | 6 | 5 | 3 |
|
||||
| Workflow servidor | `tSw2LGlOYVg9antR` | `lHU5H0cZP4BJ6dWp` | `xq8CyqyewtENpIMW` |
|
||||
| Lembrete proativo (exemplo, inativo) | `IvLzmASwa9LGaMxf` | `liBLpRUmbcnWk05V` | `LpRRZxTsM0Vc9PsV` |
|
||||
| AI Agent de exemplo (inativo) | `F5jEliFFE3K9cW14` | `ylc3Jcz239ra6XZK` | `wBjXOZSEj5TopzfB` |
|
||||
|
||||
**Padrão idêntico nos três**: `MCP Server Trigger` + N sub-workflows
|
||||
(`Execute Workflow Trigger` → `Config` → montagem de request → `HTTP
|
||||
Request` com `neverError`+`fullResponse` → `Classificar resultado`
|
||||
com taxonomia genérica por status HTTP); workflow proativo de lembrete
|
||||
via cron 8h chamando o MCP de Mensageria ZPRO (`enviar_texto`); workflow
|
||||
de exemplo de AI Agent reativo (webhook Z-PRO/GChat → `AI Agent` com
|
||||
`Postgres Chat Memory` → tools via `MCP Client Tool`) — os dois últimos
|
||||
sempre criados **inativos de propósito**, já que nenhum dos três tem
|
||||
clínica real disponível pra testar.
|
||||
|
||||
**Pendência comum aos três**: nenhum foi testado com chamada HTTP real.
|
||||
Falta, por provider, quando houver clínica real: preencher `baseUrl`,
|
||||
resolver autenticação (só pendente no Klingo — sem clínica de teste — e
|
||||
no ClinicAll — sem mecanismo documentado; o SysLife já está resolvido),
|
||||
provisionar banco Postgres + credencial, e então ativar os workflows de
|
||||
lembrete e de AI Agent de exemplo.
|
||||
|
||||
## 22. Workflows de exemplo de AI Agent para Agendamento (Klingo, ClinicAll, SysLife)
|
||||
|
||||
**Data:** 11/08/2026. Ver design completo em
|
||||
`docs/superpowers/specs/2026-08-11-workflows-exemplo-agente-agendamento-design.md`
|
||||
e plano em
|
||||
`docs/superpowers/plans/2026-08-11-workflows-exemplo-agente-agendamento.md`.
|
||||
|
||||
**Motivação**: os MCP Servers de agendamento do Klingo, ClinicAll e
|
||||
SysLife (seção 14) existiam há tempo, mas nenhum tinha um workflow de
|
||||
agente cliente — diferente da confirmação de agenda (seções 18-20), que
|
||||
já nasceu com seu próprio exemplo. Esta seção fecha essa lacuna,
|
||||
seguindo exatamente o mesmo esqueleto dos exemplos de confirmação
|
||||
(webhook Z-PRO/GChat → `AI Agent` com `OpenAI Chat Model` real +
|
||||
`Postgres Chat Memory` placeholder), mas conectando nas tools de
|
||||
**agendamento** — decisão explícita do usuário de manter os dois
|
||||
propósitos (agendar vs. confirmar) em agentes de exemplo separados por
|
||||
enquanto, mesmo padrão do Z-PRO em produção (que também só tem
|
||||
agendamento).
|
||||
|
||||
**Correção de contagem de tools**: durante o brainstorming desta
|
||||
feature, o Klingo tinha sido citado por engano como tendo 8 tools de
|
||||
agendamento (confundindo com as 6 tools da API de Telefonia usada na
|
||||
confirmação, seção 18). Conferido direto no design real
|
||||
(`docs/superpowers/specs/2026-08-11-mcp-server-nativo-n8n-klingo-design.md`):
|
||||
o MCP de agendamento do Klingo tem só **2 tools**
|
||||
(`consultar_horarios`, `agendar`). ClinicAll tem 8, SysLife tem 7 —
|
||||
esses dois já estavam corretos.
|
||||
|
||||
**Limitação real do Klingo, herdada por este exemplo**:
|
||||
`consultar_horarios` sempre retorna `SLOT_MAPPING_UNAVAILABLE` — a API
|
||||
do Klingo responde com sucesso, mas não documenta publicamente o
|
||||
formato de cada horário individual, então `agendar` (que exige um
|
||||
`slotId`) não tem como ser alimentado por essa integração hoje. Decisão
|
||||
explícita do usuário: construir o exemplo mesmo assim, com o
|
||||
`systemMessage` avisando o agente pra informar essa indisponibilidade
|
||||
ao paciente em vez de inventar um horário — não é um bug desta
|
||||
implementação, é uma lacuna documentada da API do Klingo (já registrada
|
||||
na seção 14.1).
|
||||
|
||||
| Provider | Workflow de exemplo | Tools conectadas | MCP Server consumido |
|
||||
| --- | --- | --- | --- |
|
||||
| Klingo | `Agendamento - GChat - Klingo (exemplo)` (`ToilBwbsSdXMCvCI`) | 2 (`consultar_horarios`, `agendar`) | `3eiFEnfOTLnutP78` |
|
||||
| ClinicAll | `Agendamento - GChat - ClinicAll (exemplo)` (`6E7dXKtG68mN8Xkv`) | 8 (todas) | `igaKjoHcPNYRAxPS` |
|
||||
| SysLife | `Agendamento - GChat - SysLife (exemplo)` (`6tylTUf0CAWTnrhD`) | 7 (todas) | `TYu78UqIUr2yCBHK` |
|
||||
|
||||
Diferente dos exemplos de confirmação (que filtravam tools específicas
|
||||
via `includeTools`), o `MCP Client Tool` dos três usa `include: "all"`
|
||||
— o agente de agendamento precisa de todas as tools do provider pra
|
||||
completar um fluxo de ponta a ponta. Nenhum sub-workflow de tool foi
|
||||
criado ou modificado — só os 3 workflows de agente e a conexão via
|
||||
`MCP Client Tool` aos servidores já existentes.
|
||||
|
||||
Validados estruturalmente (0 erros nos três), deixados **inativos de
|
||||
propósito** — sem clínica real de nenhum dos três providers nem
|
||||
credencial Postgres válida, ativá-los só geraria execuções com erro.
|
||||
|
||||
### Inventário geral — workflows de exemplo nos 4 providers
|
||||
|
||||
Com esta seção, os 4 providers passam a ter cobertura simétrica: cada
|
||||
um com um caminho de **agendamento** e um de **confirmação de agenda**,
|
||||
seja em produção real (só o Z-PRO) ou como esqueleto de exemplo pronto
|
||||
pra duplicar (os outros três).
|
||||
|
||||
| Provider | Agendamento | Confirmação de agenda |
|
||||
| --- | --- | --- |
|
||||
| **Z-PRO** | `Agendamento - GChat - Z-PRO (clínica exemplo)` (`yG2mi6ECBpPv4Hmn`) — **produção real, ativo** | Não construída ainda — fica pra quando for demandada |
|
||||
| **Klingo** | `Agendamento - GChat - Klingo (exemplo)` (`ToilBwbsSdXMCvCI`) — exemplo, inativo | `Confirmação de Agenda - GChat - Klingo+Z-PRO (exemplo)` (`F5jEliFFE3K9cW14`) — exemplo, inativo |
|
||||
| **ClinicAll** | `Agendamento - GChat - ClinicAll (exemplo)` (`6E7dXKtG68mN8Xkv`) — exemplo, inativo | `Confirmação de Agenda - GChat - ClinicAll+Z-PRO (exemplo)` (`ylc3Jcz239ra6XZK`) — exemplo, inativo |
|
||||
| **SysLife** | `Agendamento - GChat - SysLife (exemplo)` (`6tylTUf0CAWTnrhD`) — exemplo, inativo | `Confirmação de Agenda - GChat - SysLife+Z-PRO (exemplo)` (`wBjXOZSEj5TopzfB`) — exemplo, inativo |
|
||||
|
||||
Todos os seis workflows de exemplo (Klingo/ClinicAll/SysLife ×
|
||||
agendamento/confirmação) seguem o mesmo esqueleto: `Webhook` (contrato
|
||||
Z-PRO/GChat, seção 6) → `AI Agent` com `OpenAI Chat Model` (credencial
|
||||
real e compartilhada `29vMIDah5DTIhUYJ`, "GPT") + `Postgres Chat
|
||||
Memory` (credencial placeholder) + tools via `MCP Client Tool`
|
||||
apontando pro respectivo MCP Server nativo já construído. Nenhum foi
|
||||
ativado nem testado com chamada HTTP real — cada um fica pronto pra
|
||||
duplicar assim que a clínica real correspondente e o banco Postgres do
|
||||
cliente existirem.
|
||||
|
||||
## 23. Incidentes reais em produção (Z-PRO, 17/08/2026) — edição manual pela UI
|
||||
|
||||
Depois que o gestor da clínica exemplo editou o node `AI Agent` e o
|
||||
`OpenAI Chat Model` diretamente pela UI do n8n (troca de modelo), o
|
||||
agendamento em produção (`Agendamento - GChat - Z-PRO (clínica
|
||||
exemplo)`, `yG2mi6ECBpPv4Hmn`) parou de funcionar. Três problemas reais
|
||||
foram encontrados e corrigidos, nesta ordem:
|
||||
|
||||
### 23.1. `endpointUrl` do MCP Client Tool revertido pro servidor errado
|
||||
|
||||
**Sintoma**: toda mensagem caía no branch de erro padrão ("Desculpe,
|
||||
tive um problema técnico..."). Execução mostrava
|
||||
`"error": "Error in sub-node MCP - ZPRO (nativo n8n)"` /
|
||||
`"Could not connect to your MCP server"`.
|
||||
|
||||
**Causa raiz**: o node `MCP - ZPRO (nativo n8n)` (o único `MCP Client
|
||||
Tool` conectado ao AI Agent deste workflow) apontava pro endpoint de
|
||||
**teste** do MCP de Mensageria
|
||||
(`https://devn8n.gtigrupo.com.br/mcp-test/mcp-server-mensageria-zpro`)
|
||||
em vez do MCP de **agendamento** de produção
|
||||
(`https://devn8n.gtigrupo.com.br/mcp/mcp-server-zpro-clinica-exemplo`,
|
||||
servidor `MCP Server - ZPRO` / `qjSaKllvneBIZ7mC`, tools `agendar` +
|
||||
`consultar_horarios`). Confirmado via execução `248` (12/08, um dia
|
||||
antes) que o agendamento real só funciona com esse endpoint correto —
|
||||
o node havia sido apontado pro MCP errado (mensageria, não
|
||||
agendamento) durante uma edição manual, e a correção foi **revertida
|
||||
duas vezes** só de reabrir/salvar o node do modelo pela UI (o editor
|
||||
recarrega o valor antigo em cache do campo `endpointUrl` e resalva o
|
||||
workflow inteiro).
|
||||
|
||||
**Fix**: `endpointUrl` corrigido pra
|
||||
`https://devn8n.gtigrupo.com.br/mcp/mcp-server-zpro-clinica-exemplo`.
|
||||
**Risco de recorrência real** — documentado como aviso pro
|
||||
gestor: qualquer edição futura do `AI Agent`/`OpenAI Chat Model` pela
|
||||
UI deve conferir esse campo antes de salvar.
|
||||
|
||||
### 23.2. Modelo trocado para `gpt-5.6-luna`
|
||||
|
||||
O gestor trocou o modelo do `OpenAI Chat Model` de `gpt-4.1-mini` para
|
||||
`gpt-5.6-luna` (veio do dropdown de modelos da credencial "GPT",
|
||||
`cachedResultName` igual ao `value` — não foi digitado à mão). Mantido
|
||||
a pedido do usuário; não identificado como causa de nenhum dos bugs
|
||||
desta seção (o erro de conexão MCP acontece antes de qualquer chamada
|
||||
ao modelo).
|
||||
|
||||
### 23.3. Agendamento aceito em horário já passado
|
||||
|
||||
**Sintoma**: paciente pediu "17/08 as 8:30" às 11:48 (mesmo dia) e o
|
||||
agente agendou sem objeção, mesmo com o horário já tendo passado.
|
||||
|
||||
**Causa raiz**: nem o `systemMessage` do `AI Agent` nem o doc de
|
||||
prompt da clínica (Google Docs) tinham qualquer regra comparando o
|
||||
horário pedido com o horário atual — só validavam dia da semana e
|
||||
horário comercial (seção 3 do doc). O `systemMessage` calculava
|
||||
`$now` só com **data**, sem hora, então o modelo não tinha como
|
||||
comparar.
|
||||
|
||||
**Fix**: `systemMessage` do `AI Agent` alterado para incluir data
|
||||
**e hora** atuais (`yyyy-MM-dd (cccc) HH:mm`) e nova regra 8: nunca
|
||||
agendar num horário já passado — comparar com o horário atual antes de
|
||||
confirmar/agendar, e pedir outro horário se o pedido já passou.
|
||||
Também revisado o doc de prompt da clínica (`docId`
|
||||
`1NmYRIoF7yD_B6N160_6Bpfv4NyJiryTTO2LFr9xNlYY`, Google Docs) pra
|
||||
remover a exigência de "consultar disponibilidade antes de agendar"
|
||||
(o Z-PRO não oferece essa consulta — nunca ofereceu, mesmo nos
|
||||
agendamentos que funcionaram — então a regra original só confundia o
|
||||
modelo sem nunca ter sido cumprível).
|
||||
|
||||
### 23.4. Investigação em aberto — suporte a imagem/áudio (sem solução ainda)
|
||||
|
||||
Pedido do usuário: fazer o AI Agent entender imagens e áudios enviados
|
||||
pelo paciente no WhatsApp. Testado enviando uma imagem e um áudio reais
|
||||
pro número de produção:
|
||||
|
||||
- **Confirmado por teste direto**: o webhook `Webhook Z-PRO` recebe
|
||||
ambos exatamente como uma mensagem de texto vazia —
|
||||
`{"step":"process","entrada":"","number":...,"contactName":...,"protocol":...}`
|
||||
— sem nenhum campo de mídia, URL, base64 ou tipo de mensagem.
|
||||
- **Descartado como evidência**: uma mensagem encontrada num ticket
|
||||
antigo ("No momento, consigo processar apenas mensagens em texto...")
|
||||
**não é** um aviso de sistema do Z-PRO — vem do chatFlow nativo
|
||||
`id 78`, nomeado **"GUSTAVO"**, criado em 14/07/2026 dentro do próprio
|
||||
painel de chatbot do Z-PRO. É texto escrito manualmente por alguém da
|
||||
equipe num flow separado, não evidência da API.
|
||||
- A collection pública do Postman do Z-PRO
|
||||
(`postman.com/comunidade-zdg/z-pro/collection/ksvu6vu`) documenta só
|
||||
a API de saída (enviar mensagem, criar ticket, `appointment/*`,
|
||||
etc.) — não tem nenhum endpoint de webhook de entrada nem de busca
|
||||
de mídia recebida.
|
||||
- Pista não seguida: o objeto de ticket retornado por
|
||||
`showticketchatbot` tem campos `n8nStatus`/`n8nUrl` (hoje `false`/
|
||||
`null` no ticket testado), sugerindo que o Z-PRO tem alguma
|
||||
configuração de integração com n8n por ticket/canal cujas opções não
|
||||
foram investigadas.
|
||||
|
||||
**Pendência real**: confirmar no painel administrativo do Z-PRO (quem
|
||||
tem acesso) se a configuração do webhook usado por este workflow
|
||||
(`agendamento-zpro-clinica-exemplo`) tem alguma opção pra incluir mídia
|
||||
no payload. Sem isso, a feature de entender imagem/áudio não é
|
||||
implementável só editando o workflow do n8n.
|
||||
|
||||
@@ -0,0 +1,792 @@
|
||||
# Confirmação de Agenda via WhatsApp (SysLife) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task (não subagent-driven-development — as tasks são chamadas de API do n8n via MCP, não commits de código; não há diff de git pra revisar por task). Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Construir o MCP Server "SysLife - Confirmação de Agenda" (3 tools) + o workflow proativo de lembrete de confirmação (cron diário) + um workflow de exemplo de AI Agent reativo que confirma presença via WhatsApp, todos estruturalmente validados (0 erros), sem clínica SysLife real disponível para teste de ponta a ponta — mesmo padrão já executado para Klingo (`docs/superpowers/plans/2026-08-11-confirmacao-agenda-klingo.md`) e ClinicAll (`docs/superpowers/plans/2026-08-11-confirmacao-agenda-clinicall.md`).
|
||||
|
||||
**Architecture:** Mesmo padrão de MCP Server nativo n8n dos outros providers: `MCP Server Trigger` + N × `Call n8n Sub-Workflow Tool`, cada um apontando pra um sub-workflow `Execute Workflow Trigger` → `Config SysLife` → `Montar query`/`Montar corpo` (Code) → `HTTP Request` → `Classificar resultado` (Code). Reaproveita **exatamente** o estilo de node já usado no MVP de agendamento do SysLife (`MCP Server - SysLife - consultar_horarios`, `GCbnkMM1JTnVXRSr`, lido nesta sessão como referência): `HTTP Request` com `authentication: "genericCredentialType"` + `genericAuthType: "httpBasicAuth"`, credencial `httpBasicAuth` já existente (`u8XXJ6wyrKuGJp4Y`), `sendBody: true` mesmo em métodos GET (documentado assim no manual do SysLife). O recurso `AgendaWSApp` é único e serve três propósitos diferentes conforme o parâmetro enviado — duas tools deste MCP (`listar_agendamentos_do_dia`, `buscar_agendamento_por_telefone`) apontam pra sub-workflows distintos que chamam o mesmo endpoint com parâmetros diferentes. O workflow de lembrete chama a tool via `Execute Workflow` direto (sem IA), reaproveitando o MCP de Mensageria (Z-PRO) já validado. O workflow de exemplo reativo usa `AI Agent` + `Postgres Chat Memory` + tools do MCP SysLife via `MCP Client Tool`.
|
||||
|
||||
**Tech Stack:** n8n (`devn8n.gtigrupo.com.br`), MCP tools do n8n (`n8n_create_workflow`, `n8n_update_partial_workflow`, `n8n_validate_workflow`, `n8n_get_workflow`), SysLife WebService (`http://<IP>:9000/AgendaWSApp`, Basic Auth), MCP Server - Mensageria ZPRO (`WsL4RlSD0hZ34gpB` → sub-workflow `enviar_texto` `Y4ndG7GuIUrmVRxV`, já em produção).
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Nenhum campo de request/response é inventado — usar só os campos confirmados no manual (`docs/Manual WS SysLife V3.4 (1).pdf`).
|
||||
- **Credencial Basic Auth já existe e é reaproveitada**: `httpBasicAuth`, id `u8XXJ6wyrKuGJp4Y`, nome `SysLife Basic Auth - clínica pendente` — usuário/senha fixos do WebService (`WebSysLife`/`$Sys10Life`), documentados literalmente no manual, não variam por clínica. `baseUrl` fica com o mesmo placeholder já usado nos sub-workflows do MVP de agendamento: `"http://SUBSTITUIR-PELO-IP-REAL-DA-CLINICA:9000"`.
|
||||
- **Lição da seção 17 do runbook — `$fromAI` só com um único `=` no início**: ao montar `workflowInputs.value` dos nodes `Call n8n Sub-Workflow Tool` do servidor, cada expressão deve ser exatamente `"={{ $fromAI(...) }}"` (um `=`). Como este plano cria os nodes via `n8n_create_workflow` diretamente, o bug do `==` duplicado não deve ocorrer — conferir mesmo assim com `n8n_get_workflow` (mode `filtered`) antes de validar.
|
||||
- **Campos opcionais precisam de valor-padrão no `$fromAI`**: `$fromAI('nome', 'descrição', 'tipo', valorPadrão)` — senão o schema gerado da tool trata o campo como obrigatório. Neste plano, todos os campos das 3 tools são obrigatórios (o manual não documenta nenhum campo opcional em `AgendaWSApp`), então nenhum `$fromAI` precisa de 4º argumento — mas confira mesmo assim.
|
||||
- Operações que trocam node + conexões devem ser feitas numa única chamada `n8n_update_partial_workflow`.
|
||||
- Todo workflow (servidor + sub-workflows) precisa estar `active: true` para responder via protocolo MCP.
|
||||
- Taxonomia de erro padrão (idêntica à já usada no MVP de agendamento do SysLife): `AUTHENTICATION_ERROR` (401/403), `NOT_FOUND` (404), `VALIDATION_ERROR` (400/422), `SYSLIFE_API_ERROR` (demais).
|
||||
- `AgendaCentral`, `AgendaRetorno`, `AgendaStatus`, `AgendaConfirmado` ficam **fora de escopo** — não implementar tools para esses endpoints.
|
||||
- **Regra do manual**: `GET /AgendaWSApp` aceita `QuantidadeDias` **ou** `CodigoAgenda` **ou** `NumeroCelular` — nunca mais de um ao mesmo tempo. Cada sub-workflow de leitura deste plano monta o body só com o parâmetro que lhe cabe, nunca os dois.
|
||||
|
||||
Spec de referência: `docs/superpowers/specs/2026-08-11-confirmacao-agenda-syslife-design.md`
|
||||
|
||||
---
|
||||
|
||||
## Campos confirmados no manual (`docs/Manual WS SysLife V3.4 (1).pdf`, já lido nesta sessão)
|
||||
|
||||
### `GET /AgendaWSApp`
|
||||
- Body: **um único** de `QuantidadeDias` (number), `CodigoAgenda` (number) ou `NumeroCelular` (string, DDD + número).
|
||||
- Resposta: array de objetos com `coAgenda`, `dtAtendimento`, `hrAtendimentoAlterado`, `nmPaciente`, `nmLogonWeb`, `nmSenhaWeb`, `coMedico`, `txTituloMedico`, `nmMedico`, `nmEspecialidade`, `nmOrigemDestino`, `coProcedimento`, `nmProcedimento`, `nuTelefone`, `nuTelefoneDdd`, `nuCelular`, `nuCelularDdd`, `txObservacao`, `txInstrucaoProc`, `txWhatsappProcedimento`, `txWhatsappHorario`, `txWhatsappMedico`, `txEndereco`, `txBairro`, `txComplemento`, `txCidade`, `txEstado`, `nmMsgSms`, `nmMsgSmsExtra`.
|
||||
|
||||
### `POST /AgendaWSApp`
|
||||
- Body: `CodigoAgenda` (number, obrigatório), `Resposta` (string, obrigatório — `"1"` = SIM, `"2"` = NÃO).
|
||||
- Resposta: `{ "Status": "?" }` (mensagem de sucesso ou erro).
|
||||
|
||||
---
|
||||
|
||||
## Pré-requisito
|
||||
|
||||
Nenhum — a credencial Basic Auth já existe no n8n (`u8XXJ6wyrKuGJp4Y`), criada durante o MVP de agendamento do SysLife. Quando houver clínica real: preencher o `baseUrl` real (IP:9000) nos 3 sub-workflows.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Sub-workflow `listar_agendamentos_do_dia`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: nenhuma.
|
||||
- Produces: workflow `MCP Server - SysLife - Confirmação - listar_agendamentos_do_dia`, consumido pela Task 4 (servidor) e pela Task 6 (lembrete proativo).
|
||||
|
||||
- [ ] **Step 1: Criar o sub-workflow**
|
||||
|
||||
`n8n_create_workflow`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "MCP Server - SysLife - Confirmação - listar_agendamentos_do_dia",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "trigger",
|
||||
"name": "Execute Workflow Trigger",
|
||||
"type": "n8n-nodes-base.executeWorkflowTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [0, 0],
|
||||
"parameters": {
|
||||
"workflowInputs": { "values": [{ "name": "quantidadeDias", "type": "number" }] }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "config",
|
||||
"name": "Config SysLife",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.5,
|
||||
"position": [260, 0],
|
||||
"parameters": {
|
||||
"mode": "manual",
|
||||
"includeOtherFields": true,
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{ "id": "cfg-baseurl", "name": "baseUrl", "type": "string", "value": "http://SUBSTITUIR-PELO-IP-REAL-DA-CLINICA:9000" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "montar-query",
|
||||
"name": "Montar query",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [520, 0],
|
||||
"parameters": {
|
||||
"mode": "runOnceForEachItem",
|
||||
"jsCode": "const input = $json;\nreturn {\n json: {\n baseUrl: input.baseUrl,\n queryBody: {\n QuantidadeDias: input.quantidadeDias,\n },\n },\n};"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "get-agendawsapp",
|
||||
"name": "GET /AgendaWSApp",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [780, 0],
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "={{ $json.baseUrl }}/AgendaWSApp",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpBasicAuth",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify($json.queryBody) }}",
|
||||
"options": {
|
||||
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
|
||||
"timeout": 15000
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"httpBasicAuth": { "id": "u8XXJ6wyrKuGJp4Y", "name": "SysLife Basic Auth - clínica pendente" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "classificar",
|
||||
"name": "Classificar resultado",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1040, 0],
|
||||
"parameters": {
|
||||
"mode": "runOnceForEachItem",
|
||||
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API do SysLife ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API do SysLife ao ${context} (status ${status}).` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API do SysLife rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"SYSLIFE_API_ERROR\", message: `A API do SysLife retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { success: false, error: classifyError(status, \"listar agendamentos do dia\") } };\n}\n\nreturn { json: { success: true, data: rawBody } };"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Execute Workflow Trigger": { "main": [[{ "node": "Config SysLife", "type": "main", "index": 0 }]] },
|
||||
"Config SysLife": { "main": [[{ "node": "Montar query", "type": "main", "index": 0 }]] },
|
||||
"Montar query": { "main": [[{ "node": "GET /AgendaWSApp", "type": "main", "index": 0 }]] },
|
||||
"GET /AgendaWSApp": { "main": [[{ "node": "Classificar resultado", "type": "main", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Anotar o `id` retornado — necessário nas Tasks 4 e 6.
|
||||
|
||||
- [ ] **Step 2: Validar**
|
||||
|
||||
`n8n_validate_workflow` com o id retornado. Esperado: `errorCount: 0`.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Sub-workflow `buscar_agendamento_por_telefone`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: nenhuma.
|
||||
- Produces: workflow `MCP Server - SysLife - Confirmação - buscar_agendamento_por_telefone`, consumido pela Task 4 e pelo workflow de exemplo (Task 7).
|
||||
|
||||
- [ ] **Step 1: Criar o sub-workflow**
|
||||
|
||||
`n8n_create_workflow`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "MCP Server - SysLife - Confirmação - buscar_agendamento_por_telefone",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "trigger",
|
||||
"name": "Execute Workflow Trigger",
|
||||
"type": "n8n-nodes-base.executeWorkflowTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [0, 0],
|
||||
"parameters": {
|
||||
"workflowInputs": { "values": [{ "name": "numeroCelular", "type": "string" }] }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "config",
|
||||
"name": "Config SysLife",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.5,
|
||||
"position": [260, 0],
|
||||
"parameters": {
|
||||
"mode": "manual",
|
||||
"includeOtherFields": true,
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{ "id": "cfg-baseurl", "name": "baseUrl", "type": "string", "value": "http://SUBSTITUIR-PELO-IP-REAL-DA-CLINICA:9000" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "montar-query",
|
||||
"name": "Montar query",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [520, 0],
|
||||
"parameters": {
|
||||
"mode": "runOnceForEachItem",
|
||||
"jsCode": "const input = $json;\nreturn {\n json: {\n baseUrl: input.baseUrl,\n queryBody: {\n NumeroCelular: input.numeroCelular,\n },\n },\n};"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "get-agendawsapp",
|
||||
"name": "GET /AgendaWSApp",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [780, 0],
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "={{ $json.baseUrl }}/AgendaWSApp",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpBasicAuth",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify($json.queryBody) }}",
|
||||
"options": {
|
||||
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
|
||||
"timeout": 15000
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"httpBasicAuth": { "id": "u8XXJ6wyrKuGJp4Y", "name": "SysLife Basic Auth - clínica pendente" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "classificar",
|
||||
"name": "Classificar resultado",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1040, 0],
|
||||
"parameters": {
|
||||
"mode": "runOnceForEachItem",
|
||||
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API do SysLife ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API do SysLife ao ${context} (status ${status}).` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API do SysLife rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"SYSLIFE_API_ERROR\", message: `A API do SysLife retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { success: false, error: classifyError(status, \"buscar agendamento por telefone\") } };\n}\n\nreturn { json: { success: true, data: rawBody } };"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Execute Workflow Trigger": { "main": [[{ "node": "Config SysLife", "type": "main", "index": 0 }]] },
|
||||
"Config SysLife": { "main": [[{ "node": "Montar query", "type": "main", "index": 0 }]] },
|
||||
"Montar query": { "main": [[{ "node": "GET /AgendaWSApp", "type": "main", "index": 0 }]] },
|
||||
"GET /AgendaWSApp": { "main": [[{ "node": "Classificar resultado", "type": "main", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Anotar o `id` retornado.
|
||||
|
||||
- [ ] **Step 2: Validar**
|
||||
|
||||
`n8n_validate_workflow` com o id retornado. Esperado: `errorCount: 0`.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Sub-workflow `confirmar_presenca`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: nenhuma.
|
||||
- Produces: workflow `MCP Server - SysLife - Confirmação - confirmar_presenca`, consumido pela Task 4 e pelo workflow de exemplo (Task 7).
|
||||
|
||||
- [ ] **Step 1: Criar o sub-workflow**
|
||||
|
||||
`n8n_create_workflow`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "MCP Server - SysLife - Confirmação - confirmar_presenca",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "trigger",
|
||||
"name": "Execute Workflow Trigger",
|
||||
"type": "n8n-nodes-base.executeWorkflowTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [0, 0],
|
||||
"parameters": {
|
||||
"workflowInputs": {
|
||||
"values": [
|
||||
{ "name": "codigoAgenda", "type": "number" },
|
||||
{ "name": "resposta", "type": "string" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "config",
|
||||
"name": "Config SysLife",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.5,
|
||||
"position": [260, 0],
|
||||
"parameters": {
|
||||
"mode": "manual",
|
||||
"includeOtherFields": true,
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{ "id": "cfg-baseurl", "name": "baseUrl", "type": "string", "value": "http://SUBSTITUIR-PELO-IP-REAL-DA-CLINICA:9000" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "montar-corpo",
|
||||
"name": "Montar corpo",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [520, 0],
|
||||
"parameters": {
|
||||
"mode": "runOnceForEachItem",
|
||||
"jsCode": "const input = $json;\nreturn {\n json: {\n baseUrl: input.baseUrl,\n requestBody: {\n CodigoAgenda: input.codigoAgenda,\n Resposta: input.resposta,\n },\n },\n};"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "post-agendawsapp",
|
||||
"name": "POST /AgendaWSApp",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [780, 0],
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "={{ $json.baseUrl }}/AgendaWSApp",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpBasicAuth",
|
||||
"sendBody": true,
|
||||
"contentType": "json",
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify($json.requestBody) }}",
|
||||
"options": {
|
||||
"response": { "response": { "fullResponse": true, "neverError": true, "responseFormat": "autodetect" } },
|
||||
"timeout": 15000
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"httpBasicAuth": { "id": "u8XXJ6wyrKuGJp4Y", "name": "SysLife Basic Auth - clínica pendente" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "classificar",
|
||||
"name": "Classificar resultado",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1040, 0],
|
||||
"parameters": {
|
||||
"mode": "runOnceForEachItem",
|
||||
"jsCode": "const status = $json.statusCode;\nconst rawBody = $json.body;\n\nfunction classifyError(status, context) {\n if (status === 401 || status === 403) {\n return { code: \"AUTHENTICATION_ERROR\", message: `Falha de autenticação na API do SysLife ao ${context} (status ${status}).` };\n }\n if (status === 404) {\n return { code: \"NOT_FOUND\", message: `Recurso não encontrado na API do SysLife ao ${context} (status ${status}).` };\n }\n if (status === 400 || status === 422) {\n return { code: \"VALIDATION_ERROR\", message: `A API do SysLife rejeitou os dados enviados ao ${context} (status ${status}).` };\n }\n return { code: \"SYSLIFE_API_ERROR\", message: `A API do SysLife retornou um erro inesperado ao ${context} (status ${status}).` };\n}\n\nif (status < 200 || status >= 300) {\n return { json: { success: false, error: classifyError(status, \"confirmar presença\") } };\n}\n\nreturn { json: { success: true, data: rawBody } };"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Execute Workflow Trigger": { "main": [[{ "node": "Config SysLife", "type": "main", "index": 0 }]] },
|
||||
"Config SysLife": { "main": [[{ "node": "Montar corpo", "type": "main", "index": 0 }]] },
|
||||
"Montar corpo": { "main": [[{ "node": "POST /AgendaWSApp", "type": "main", "index": 0 }]] },
|
||||
"POST /AgendaWSApp": { "main": [[{ "node": "Classificar resultado", "type": "main", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`resposta` deve ser documentado na `description` da tool (Task 4) como
|
||||
aceitando só `"1"` (SIM) ou `"2"` (NÃO), conforme o manual.
|
||||
|
||||
Anotar o `id` retornado.
|
||||
|
||||
- [ ] **Step 2: Validar**
|
||||
|
||||
`n8n_validate_workflow` com o id retornado. Esperado: `errorCount: 0`.
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Servidor MCP "SysLife - Confirmação de Agenda" com as 3 tools
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: os 3 workflow ids das Tasks 1–3.
|
||||
- Produces: workflow `MCP Server - SysLife - Confirmação de Agenda` (servidor), path `mcp-server-syslife-confirmacao`, consumido pela Task 7 (workflow de exemplo).
|
||||
|
||||
- [ ] **Step 1: Criar o servidor com o `MCP Server Trigger` e os 3 nodes de tool**
|
||||
|
||||
`n8n_create_workflow` — substituir `ID_LISTAR`, `ID_BUSCAR_TELEFONE`,
|
||||
`ID_CONFIRMAR` pelos ids reais anotados nas Tasks 1–3:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "MCP Server - SysLife - Confirmação de Agenda",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "mcp-trigger",
|
||||
"name": "MCP Server Trigger",
|
||||
"type": "@n8n/n8n-nodes-langchain.mcpTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [0, 256],
|
||||
"parameters": { "path": "mcp-server-syslife-confirmacao", "authentication": "none" }
|
||||
},
|
||||
{
|
||||
"id": "tool-listar",
|
||||
"name": "listar_agendamentos_do_dia",
|
||||
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
|
||||
"typeVersion": 2.2,
|
||||
"position": [-320, 448],
|
||||
"parameters": {
|
||||
"description": "Lista os agendamentos de uma data futura no SysLife, contando a partir de hoje. Usado como base do lembrete proativo de confirmação — use quantidadeDias=1 para os agendamentos de amanhã.",
|
||||
"workflowId": { "value": "ID_LISTAR", "mode": "id" },
|
||||
"workflowInputs": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": { "quantidadeDias": "={{ $fromAI('quantidadeDias', 'Quantidade de dias a partir de hoje (1 = amanhã, 2 = depois de amanhã, etc.)', 'number') }}" },
|
||||
"matchingColumns": [],
|
||||
"schema": [{ "id": "quantidadeDias", "displayName": "quantidadeDias", "required": false, "defaultMatch": false, "display": true, "canBeUsedToMatch": true, "type": "number" }],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tool-buscar-telefone",
|
||||
"name": "buscar_agendamento_por_telefone",
|
||||
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
|
||||
"typeVersion": 2.2,
|
||||
"position": [-160, 448],
|
||||
"parameters": {
|
||||
"description": "Busca o(s) agendamento(s) pendente(s) de um paciente pelo número de celular (DDD + número, só dígitos). Use isso para descobrir qual agendamento o paciente está confirmando quando ele responde 'sim'/'não'.",
|
||||
"workflowId": { "value": "ID_BUSCAR_TELEFONE", "mode": "id" },
|
||||
"workflowInputs": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": { "numeroCelular": "={{ $fromAI('numeroCelular', 'Número de celular do paciente, com DDD, somente dígitos (ex.: 71999999999)', 'string') }}" },
|
||||
"matchingColumns": [],
|
||||
"schema": [{ "id": "numeroCelular", "displayName": "numeroCelular", "required": false, "defaultMatch": false, "display": true, "canBeUsedToMatch": true, "type": "string" }],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tool-confirmar",
|
||||
"name": "confirmar_presenca",
|
||||
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
|
||||
"typeVersion": 2.2,
|
||||
"position": [0, 448],
|
||||
"parameters": {
|
||||
"description": "Registra a confirmação de presença de um agendamento no SysLife. resposta deve ser exatamente '1' (paciente confirmou) ou '2' (paciente não confirmou/recusou) — nenhum outro valor é aceito. Use depois de identificar o codigoAgenda correto (ex.: via buscar_agendamento_por_telefone).",
|
||||
"workflowId": { "value": "ID_CONFIRMAR", "mode": "id" },
|
||||
"workflowInputs": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": {
|
||||
"codigoAgenda": "={{ $fromAI('codigoAgenda', 'Código do agendamento (coAgenda) a confirmar', 'number') }}",
|
||||
"resposta": "={{ $fromAI('resposta', \"'1' se o paciente confirmou presença, '2' se recusou/não vai comparecer\", 'string') }}"
|
||||
},
|
||||
"matchingColumns": [],
|
||||
"schema": [
|
||||
{ "id": "codigoAgenda", "displayName": "codigoAgenda", "required": false, "defaultMatch": false, "display": true, "canBeUsedToMatch": true, "type": "number" },
|
||||
{ "id": "resposta", "displayName": "resposta", "required": false, "defaultMatch": false, "display": true, "canBeUsedToMatch": true, "type": "string" }
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"listar_agendamentos_do_dia": { "ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]] },
|
||||
"buscar_agendamento_por_telefone": { "ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]] },
|
||||
"confirmar_presenca": { "ai_tool": [[{ "node": "MCP Server Trigger", "type": "ai_tool", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Anotar o `id` do servidor retornado — necessário na Task 7.
|
||||
|
||||
- [ ] **Step 2: Conferir visualmente os textos salvos dos `$fromAI`**
|
||||
|
||||
`n8n_get_workflow` com `{"id": "<id do servidor>", "mode": "filtered", "nodeNames": ["listar_agendamentos_do_dia", "buscar_agendamento_por_telefone", "confirmar_presenca"]}`. Conferir que cada valor de `workflowInputs.value` começa com um único `=` (não `==`).
|
||||
|
||||
- [ ] **Step 3: Validar**
|
||||
|
||||
`n8n_validate_workflow` com o id do servidor. Esperado: `errorCount: 0`.
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Ativar os 4 workflows do MCP Server
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: os 4 workflow ids (3 sub-workflows das Tasks 1–3 + servidor da Task 4).
|
||||
- Produces: MCP Server "SysLife - Confirmação de Agenda" respondendo via protocolo MCP.
|
||||
|
||||
- [ ] **Step 1: Ativar cada um dos 4 workflows**
|
||||
|
||||
Para cada um dos 4 ids (3 sub-workflows + servidor), rodar
|
||||
`n8n_update_partial_workflow` com `{"id": "<id>", "operations": [{"type": "activateWorkflow"}]}`.
|
||||
|
||||
- [ ] **Step 2: Confirmar ativação**
|
||||
|
||||
`n8n_get_workflow` com `mode: "minimal"` em cada um dos 4 ids —
|
||||
confirmar `"active": true` em todos.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Workflow proativo "Lembrete de Confirmação - SysLife (exemplo)"
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: workflow `listar_agendamentos_do_dia` (Task 1) via `Execute Workflow`; tool `enviar_texto` do MCP de Mensageria ZPRO (`Y4ndG7GuIUrmVRxV`, já em produção) via `Execute Workflow`.
|
||||
- Produces: workflow `Lembrete de Confirmação - SysLife (exemplo)`.
|
||||
|
||||
- [ ] **Step 1: Criar o workflow**
|
||||
|
||||
`n8n_create_workflow` — substituir `ID_LISTAR_AGENDAMENTOS` pelo id
|
||||
anotado na Task 1:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Lembrete de Confirmação - SysLife (exemplo)",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "cron",
|
||||
"name": "Cron 8h diário",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.2,
|
||||
"position": [0, 0],
|
||||
"parameters": {
|
||||
"rule": { "interval": [{ "field": "cronExpression", "expression": "0 8 * * *" }] }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "listar",
|
||||
"name": "Listar agendamentos de amanhã",
|
||||
"type": "n8n-nodes-base.executeWorkflow",
|
||||
"typeVersion": 1.2,
|
||||
"position": [224, 0],
|
||||
"parameters": {
|
||||
"workflowId": { "value": "ID_LISTAR_AGENDAMENTOS", "mode": "id" },
|
||||
"workflowInputs": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": { "quantidadeDias": 1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "extrair-lista",
|
||||
"name": "Extrair lista de agendamentos",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [448, 0],
|
||||
"parameters": {
|
||||
"jsCode": "const result = $input.item.json;\nif (!result.success) {\n throw new Error('Falha ao listar agendamentos do dia: ' + JSON.stringify(result.error));\n}\nconst agendamentos = Array.isArray(result.data) ? result.data : [];\nreturn agendamentos.map(a => ({ json: a }));"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "montar-mensagem",
|
||||
"name": "Montar mensagem de lembrete",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [672, 0],
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{ "name": "numeroWhatsapp", "type": "string", "value": "={{ $json.nuCelularDdd }}{{ $json.nuCelular }}" },
|
||||
{ "name": "textoLembrete", "type": "string", "value": "=Olá, {{ $json.nmPaciente }}! Passando para confirmar sua consulta{{ $json.txWhatsappProcedimento ? ' de ' + $json.txWhatsappProcedimento : ($json.nmProcedimento ? ' de ' + $json.nmProcedimento : '') }}{{ $json.txWhatsappMedico ? ' com ' + $json.txWhatsappMedico : ($json.nmMedico ? ' com ' + $json.nmMedico : '') }}{{ $json.txWhatsappHorario ? ' em ' + $json.txWhatsappHorario : ' em ' + $json.dtAtendimento + ' às ' + $json.hrAtendimentoAlterado }}. Você confirma presença? Responda SIM ou NÃO." },
|
||||
{ "name": "externalKey", "type": "string", "value": "=lembrete-syslife-{{ $json.coAgenda }}" }
|
||||
]
|
||||
},
|
||||
"includeOtherFields": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "enviar",
|
||||
"name": "Enviar lembrete via Mensageria ZPRO",
|
||||
"type": "n8n-nodes-base.executeWorkflow",
|
||||
"typeVersion": 1.2,
|
||||
"position": [896, 0],
|
||||
"parameters": {
|
||||
"workflowId": { "value": "Y4ndG7GuIUrmVRxV", "mode": "id" },
|
||||
"workflowInputs": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": {
|
||||
"number": "={{ $json.numeroWhatsapp }}",
|
||||
"body": "={{ $json.textoLembrete }}",
|
||||
"externalKey": "={{ $json.externalKey }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Cron 8h diário": { "main": [[{ "node": "Listar agendamentos de amanhã", "type": "main", "index": 0 }]] },
|
||||
"Listar agendamentos de amanhã": { "main": [[{ "node": "Extrair lista de agendamentos", "type": "main", "index": 0 }]] },
|
||||
"Extrair lista de agendamentos": { "main": [[{ "node": "Montar mensagem de lembrete", "type": "main", "index": 0 }]] },
|
||||
"Montar mensagem de lembrete": { "main": [[{ "node": "Enviar lembrete via Mensageria ZPRO", "type": "main", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Nota: `quantidadeDias: 1` é passado como literal fixo (não expressão) —
|
||||
diferente do Klingo/ClinicAll, o SysLife já calcula "amanhã" no próprio
|
||||
servidor a partir desse número, então não precisa de um node
|
||||
`Calcular data` separado. Telefone montado direto de
|
||||
`nuCelularDdd`+`nuCelular` do próprio item (sem objeto aninhado,
|
||||
diferente do ClinicAll). O texto usa os campos já formatados pro
|
||||
WhatsApp (`txWhatsappProcedimento`/`txWhatsappMedico`/
|
||||
`txWhatsappHorario`) quando presentes, com fallback pros campos brutos
|
||||
(`nmProcedimento`/`nmMedico`/`dtAtendimento`+`hrAtendimentoAlterado`).
|
||||
|
||||
- [ ] **Step 2: Validar**
|
||||
|
||||
`n8n_validate_workflow` com o id retornado. Esperado: `errorCount: 0`.
|
||||
|
||||
- [ ] **Step 3: Deixar inativo**
|
||||
|
||||
Mesma razão do Klingo e do ClinicAll — sem clínica SysLife real, um
|
||||
cron ativo geraria chamadas reais com erro contra a API e tentativas
|
||||
de envio com dados vazios. Ativação fica para quando houver clínica
|
||||
real (a credencial Basic Auth já existe; só falta o `baseUrl`).
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Workflow de exemplo "AI Agent — Confirmação SysLife (exemplo)"
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: workflow servidor `MCP Server - SysLife - Confirmação de Agenda` (Task 4, id do servidor).
|
||||
- Produces: workflow `Confirmação de Agenda - GChat - SysLife+Z-PRO (exemplo)`.
|
||||
|
||||
- [ ] **Step 1: Criar o workflow de exemplo**
|
||||
|
||||
`n8n_create_workflow` — mesmo contrato de webhook Z-PRO/GChat já usado
|
||||
nos exemplos do Klingo e do ClinicAll (`body.protocol`→`sessionId`,
|
||||
`body.entrada`→`mensagem`, `body.contactName`→`nomeContato`,
|
||||
`body.number`→`telefone`), mesma credencial OpenAI real e
|
||||
compartilhada (`29vMIDah5DTIhUYJ`, "GPT"):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Confirmação de Agenda - GChat - SysLife+Z-PRO (exemplo)",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "webhook",
|
||||
"name": "Webhook GChat",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [0, 0],
|
||||
"parameters": { "httpMethod": "POST", "path": "confirmacao-agenda-syslife-exemplo", "responseMode": "lastNode", "options": {} }
|
||||
},
|
||||
{
|
||||
"id": "extrair-campos",
|
||||
"name": "Extrair campos do webhook",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [224, 0],
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{ "name": "sessionId", "type": "string", "value": "={{ $json.body.protocol }}" },
|
||||
{ "name": "mensagem", "type": "string", "value": "={{ String($json.body.entrada || '').trim() }}" },
|
||||
{ "name": "nomeContato", "type": "string", "value": "={{ $json.body.contactName }}" },
|
||||
{ "name": "telefone", "type": "string", "value": "={{ $json.body.number }}" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ai-agent",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"typeVersion": 2.1,
|
||||
"position": [448, 0],
|
||||
"parameters": {
|
||||
"text": "={{ $json.mensagem }}",
|
||||
"systemMessage": "Você é um assistente de confirmação de presença em consultas médicas, via WhatsApp, para uma clínica que usa o sistema SysLife. Seu trabalho: 1) entender se a mensagem do paciente é uma resposta de confirmação (ex.: 'sim', 'confirmo') ou recusa (ex.: 'não', 'não posso ir') a uma consulta agendada; 2) se for, use o telefone do paciente (o telefone do contato do WhatsApp já está disponível — {{ $json.telefone }} — mas confirme com o paciente se não tiver certeza) e chame a tool buscar_agendamento_por_telefone para achar o codigoAgenda pendente; 3) chame confirmar_presenca com esse codigoAgenda e resposta='1' (se o paciente confirmou) ou resposta='2' (se recusou) — nenhum outro valor de resposta é aceito; 4) responda ao paciente confirmando que o registro foi feito. Se a mensagem não for uma resposta de confirmação/recusa clara, peça esclarecimento. Nunca invente um codigoAgenda — sempre busque antes de confirmar."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "openai-chat-model",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"typeVersion": 1.3,
|
||||
"position": [448, -160],
|
||||
"parameters": { "model": { "mode": "id", "value": "gpt-4o-mini" }, "builtInTools": {}, "options": {} },
|
||||
"credentials": { "openAiApi": { "id": "29vMIDah5DTIhUYJ", "name": "GPT" } }
|
||||
},
|
||||
{
|
||||
"id": "memoria",
|
||||
"name": "Memória da Sessão (Postgres)",
|
||||
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
|
||||
"typeVersion": 1.4,
|
||||
"position": [448, 160],
|
||||
"parameters": { "sessionIdType": "customKey", "sessionKey": "={{ $json.sessionId }}", "tableName": "n8n_chat_histories", "contextWindowLength": 20 },
|
||||
"credentials": { "postgres": { "id": "PLACEHOLDER_SEM_CREDENCIAL", "name": "Postgres - (sem cliente real)" } }
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook GChat": { "main": [[{ "node": "Extrair campos do webhook", "type": "main", "index": 0 }]] },
|
||||
"Extrair campos do webhook": { "main": [[{ "node": "AI Agent", "type": "main", "index": 0 }]] },
|
||||
"OpenAI Chat Model": { "ai_languageModel": [[{ "node": "AI Agent", "type": "ai_languageModel", "index": 0 }]] },
|
||||
"Memória da Sessão (Postgres)": { "ai_memory": [[{ "node": "AI Agent", "type": "ai_memory", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Anotar o `id` retornado.
|
||||
|
||||
- [ ] **Step 2: Validar**
|
||||
|
||||
`n8n_validate_workflow` com o id retornado. Esperado: `errorCount: 0`
|
||||
(a credencial `postgres` é placeholder — aceitável, sem cliente real).
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Conectar as tools do MCP SysLife ao AI Agent de exemplo
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: workflow servidor `MCP Server - SysLife - Confirmação de Agenda` (Task 4, id do servidor) e workflow `Confirmação de Agenda - GChat - SysLife+Z-PRO (exemplo)` (Task 7).
|
||||
- Produces: `AI Agent` do workflow de exemplo com acesso às tools `buscar_agendamento_por_telefone` e `confirmar_presenca` via `MCP Client Tool`.
|
||||
|
||||
- [ ] **Step 1: Adicionar o node `MCP Client Tool` conectado ao AI Agent**
|
||||
|
||||
`n8n_update_partial_workflow` no workflow da Task 7, numa única chamada
|
||||
atômica:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<id do workflow da Task 7>",
|
||||
"operations": [
|
||||
{
|
||||
"type": "addNode",
|
||||
"node": {
|
||||
"id": "mcp-client-syslife-confirmacao",
|
||||
"name": "MCP SysLife Confirmação",
|
||||
"type": "@n8n/n8n-nodes-langchain.mcpClientTool",
|
||||
"typeVersion": 1,
|
||||
"position": [448, 320],
|
||||
"parameters": {
|
||||
"endpointUrl": "https://devn8n.gtigrupo.com.br/mcp/mcp-server-syslife-confirmacao",
|
||||
"authentication": "none",
|
||||
"include": "selected",
|
||||
"includeTools": ["buscar_agendamento_por_telefone", "confirmar_presenca"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "type": "addConnection", "source": "MCP SysLife Confirmação", "target": "AI Agent", "sourceOutput": "ai_tool", "targetIndex": 0 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Validar**
|
||||
|
||||
`n8n_validate_workflow` no workflow da Task 7. Esperado: `errorCount: 0`.
|
||||
|
||||
- [ ] **Step 3: Deixar inativo**
|
||||
|
||||
Mesma razão das outras tasks — sem credencial Postgres real nem
|
||||
clínica SysLife real, ativar não teria efeito útil. Fica pronto pra
|
||||
ativar quando houver cliente real.
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Atualizar o runbook e commit
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: todos os ids de workflow criados nas Tasks 1–8.
|
||||
|
||||
- [ ] **Step 1: Adicionar seção 20 ao runbook**
|
||||
|
||||
Adicionar em `docs/n8n-agendamento-runbook.md`, após a seção 19, uma
|
||||
seção "20. Confirmação de Agenda via WhatsApp (SysLife)" cobrindo: as
|
||||
3 tools do MCP Server e seus endpoints (com destaque pro fato de
|
||||
`GET /AgendaWSApp` servir três propósitos via parâmetro exclusivo), os
|
||||
ids de todos os 7 workflows criados (3 sub-workflows + servidor +
|
||||
lembrete proativo + AI Agent de exemplo), a nota de que a credencial
|
||||
Basic Auth já existe e foi reaproveitada do MVP de agendamento (não
|
||||
precisou criar nada novo), a simplicidade do enum `Resposta` (`"1"`/`"2"`,
|
||||
sem terceiro estado como no Klingo e sem strings longas como no
|
||||
ClinicAll), e o status "estrutura pronta, sem teste de ponta a ponta —
|
||||
falta só o `baseUrl` real de uma clínica". Seguir o mesmo estilo de
|
||||
prosa das seções 14, 17, 18 e 19.
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
cd "D:\PROJETOS\KLINGO-CLINICAL-SYSLIFE-GCHAT-AGENDAMENTO"
|
||||
git add docs/n8n-agendamento-runbook.md
|
||||
git commit -m "docs: registra MCP SysLife Confirmação e confirmação de agenda via WhatsApp
|
||||
|
||||
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Observação sobre execução
|
||||
|
||||
Mesma disciplina dos planos anteriores: validar
|
||||
(`n8n_validate_workflow`) depois de cada workflow criado/modificado, e
|
||||
conferir visualmente o texto salvo de qualquer expressão `$fromAI`
|
||||
antes de considerar uma tool pronta (Task 4, Step 2). Os workflows de
|
||||
lembrete proativo (Task 6) e de AI Agent de exemplo (Tasks 7–8) ficam
|
||||
**intencionalmente inativos** ao final deste plano — sem clínica
|
||||
SysLife real (mesmo com a credencial Basic Auth já pronta, falta o
|
||||
`baseUrl` real) e sem credencial Postgres válida, ativá-los só geraria
|
||||
execuções com erro. Diferente do Klingo e do ClinicAll, este é o único
|
||||
dos três providers de confirmação onde a autenticação **já está
|
||||
resolvida** (credencial `httpBasicAuth` `u8XXJ6wyrKuGJp4Y` reaproveitada
|
||||
do MVP de agendamento) — só falta o endereço da clínica real pra testar
|
||||
de ponta a ponta.
|
||||
@@ -0,0 +1,428 @@
|
||||
# Workflows de Exemplo de AI Agent para Agendamento (Klingo, ClinicAll, SysLife) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task (não subagent-driven-development — as tasks são chamadas de API do n8n via MCP, não commits de código; não há diff de git pra revisar por task). Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Construir 3 workflows de exemplo de AI Agent (um por provider — Klingo, ClinicAll, SysLife), cada um conectando via webhook Z-PRO/GChat num agente conversacional com acesso a todas as tools do respectivo MCP Server de agendamento já existente, todos validados estruturalmente (0 erros) e deixados **inativos de propósito** (sem clínica real de nenhum dos três providers).
|
||||
|
||||
**Architecture:** Réplica do esqueleto já usado nos 3 workflows de exemplo de confirmação de agenda (Klingo/ClinicAll/SysLife, seções 18-20 do runbook): `Webhook` (contrato Z-PRO/GChat) → `Extrair campos do webhook` (`Set`) → `AI Agent` com `OpenAI Chat Model` (credencial real compartilhada) + `Postgres Chat Memory` (credencial placeholder) + `MCP Client Tool` conectado ao MCP Server de agendamento do provider, com `include: "all"` (diferente dos exemplos de confirmação, que filtravam tools específicas — aqui o agente precisa de todas as tools do provider pra completar um agendamento). Nenhum sub-workflow novo é criado — os MCP Servers de agendamento já existem e são só referenciados pela URL do endpoint MCP.
|
||||
|
||||
**Tech Stack:** n8n (`devn8n.gtigrupo.com.br`), MCP tools do n8n (`n8n_create_workflow`, `n8n_update_partial_workflow`, `n8n_validate_workflow`), MCP Servers de agendamento já existentes (Klingo `3eiFEnfOTLnutP78`, ClinicAll `igaKjoHcPNYRAxPS`, SysLife `TYu78UqIUr2yCBHK`).
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Nenhum sub-workflow de tool é criado ou modificado nesta rodada — só os 3 workflows de agente e a conexão `MCP Client Tool` que aponta pros servidores já existentes.
|
||||
- Contrato do webhook Z-PRO/GChat idêntico ao já documentado na seção 6 do runbook e reaproveitado nos 3 exemplos de confirmação: `body.protocol`→`sessionId`, `body.entrada`→`mensagem`, `body.contactName`→`nomeContato`, `body.number`→`telefone`.
|
||||
- `OpenAI Chat Model` usa a credencial real e compartilhada já em uso em produção e nos exemplos de confirmação: `openAiApi` id `29vMIDah5DTIhUYJ`, nome "GPT" — não é credencial inventada.
|
||||
- `Postgres Chat Memory` usa credencial placeholder (`PLACEHOLDER_SEM_CREDENCIAL`) — sem cliente real, sem banco provisionado, mesma disciplina dos exemplos de confirmação.
|
||||
- `MCP Client Tool` de cada workflow usa `include: "all"` (não `"selected"` como nos exemplos de confirmação) — o agente de agendamento precisa de todas as tools do provider pra completar um fluxo de ponta a ponta.
|
||||
- Webhook paths distintos por provider, pra não colidir entre si nem com os exemplos de confirmação já existentes: `agendamento-klingo-exemplo`, `agendamento-clinicall-exemplo`, `agendamento-syslife-exemplo`.
|
||||
- Os 3 workflows ficam **inativos de propósito** ao final deste plano — sem clínica real de nenhum provider e sem credencial Postgres válida, ativá-los só geraria execuções com erro.
|
||||
- O `systemMessage` do agente do Klingo precisa avisar explicitamente sobre a limitação real conhecida: `consultar_horarios` sempre retorna `SLOT_MAPPING_UNAVAILABLE` — o agente deve informar isso ao paciente em vez de inventar horários ou tentar chamar `agendar` sem um `slotId` real.
|
||||
|
||||
Spec de referência: `docs/superpowers/specs/2026-08-11-workflows-exemplo-agente-agendamento-design.md`
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Workflow de exemplo "Agendamento - GChat - Klingo (exemplo)"
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: MCP Server de agendamento Klingo (`3eiFEnfOTLnutP78`, path `mcp-server-klingo`, tools `consultar_horarios` + `agendar`).
|
||||
- Produces: workflow `Agendamento - GChat - Klingo (exemplo)`.
|
||||
|
||||
- [ ] **Step 1: Criar o workflow (webhook + AI Agent + modelo + memória)**
|
||||
|
||||
`n8n_create_workflow`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Agendamento - GChat - Klingo (exemplo)",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "webhook",
|
||||
"name": "Webhook GChat",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [0, 0],
|
||||
"parameters": { "httpMethod": "POST", "path": "agendamento-klingo-exemplo", "responseMode": "lastNode", "options": {} }
|
||||
},
|
||||
{
|
||||
"id": "extrair-campos",
|
||||
"name": "Extrair campos do webhook",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [224, 0],
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{ "name": "sessionId", "type": "string", "value": "={{ $json.body.protocol }}" },
|
||||
{ "name": "mensagem", "type": "string", "value": "={{ String($json.body.entrada || '').trim() }}" },
|
||||
{ "name": "nomeContato", "type": "string", "value": "={{ $json.body.contactName }}" },
|
||||
{ "name": "telefone", "type": "string", "value": "={{ $json.body.number }}" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ai-agent",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"typeVersion": 2.1,
|
||||
"position": [448, 0],
|
||||
"parameters": {
|
||||
"text": "={{ $json.mensagem }}",
|
||||
"systemMessage": "Você é um assistente de agendamento de consultas médicas, via WhatsApp, para uma clínica que usa o sistema Klingo. Seu trabalho: 1) entender o que o paciente quer agendar; 2) usar a tool consultar_horarios para ver horários disponíveis. ATENÇÃO — limitação real conhecida desta integração: consultar_horarios pode retornar o erro SLOT_MAPPING_UNAVAILABLE, porque a API do Klingo não documenta publicamente o formato de cada horário individual. Se isso acontecer, informe educadamente ao paciente que a consulta de horários está temporariamente indisponível por essa integração e que ele será atendido de outra forma — nunca invente um horário nem um slotId; 3) se consultar_horarios funcionar e devolver um slotId real, use a tool agendar com esse slotId e os demais dados do paciente para concluir o agendamento; 4) responda sempre em português, de forma clara e cordial."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "openai-chat-model",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"typeVersion": 1.3,
|
||||
"position": [448, -160],
|
||||
"parameters": { "model": { "mode": "id", "value": "gpt-4o-mini" }, "builtInTools": {}, "options": {} },
|
||||
"credentials": { "openAiApi": { "id": "29vMIDah5DTIhUYJ", "name": "GPT" } }
|
||||
},
|
||||
{
|
||||
"id": "memoria",
|
||||
"name": "Memória da Sessão (Postgres)",
|
||||
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
|
||||
"typeVersion": 1.4,
|
||||
"position": [448, 160],
|
||||
"parameters": { "sessionIdType": "customKey", "sessionKey": "={{ $json.sessionId }}", "tableName": "n8n_chat_histories", "contextWindowLength": 20 },
|
||||
"credentials": { "postgres": { "id": "PLACEHOLDER_SEM_CREDENCIAL", "name": "Postgres - (sem cliente real)" } }
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook GChat": { "main": [[{ "node": "Extrair campos do webhook", "type": "main", "index": 0 }]] },
|
||||
"Extrair campos do webhook": { "main": [[{ "node": "AI Agent", "type": "main", "index": 0 }]] },
|
||||
"OpenAI Chat Model": { "ai_languageModel": [[{ "node": "AI Agent", "type": "ai_languageModel", "index": 0 }]] },
|
||||
"Memória da Sessão (Postgres)": { "ai_memory": [[{ "node": "AI Agent", "type": "ai_memory", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Anotar o `id` retornado.
|
||||
|
||||
- [ ] **Step 2: Conectar o MCP Client Tool com todas as tools do MCP Klingo**
|
||||
|
||||
`n8n_update_partial_workflow`, numa única chamada atômica:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<id do workflow do Step 1>",
|
||||
"operations": [
|
||||
{
|
||||
"type": "addNode",
|
||||
"node": {
|
||||
"id": "mcp-client-klingo-agendamento",
|
||||
"name": "MCP Klingo Agendamento",
|
||||
"type": "@n8n/n8n-nodes-langchain.mcpClientTool",
|
||||
"typeVersion": 1,
|
||||
"position": [448, 320],
|
||||
"parameters": {
|
||||
"endpointUrl": "https://devn8n.gtigrupo.com.br/mcp/mcp-server-klingo",
|
||||
"authentication": "none",
|
||||
"include": "all"
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "type": "addConnection", "source": "MCP Klingo Agendamento", "target": "AI Agent", "sourceOutput": "ai_tool", "targetIndex": 0 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Validar**
|
||||
|
||||
`n8n_validate_workflow`. Esperado: `errorCount: 0` (warnings de
|
||||
credencial `postgres` placeholder são aceitáveis).
|
||||
|
||||
- [ ] **Step 4: Deixar inativo**
|
||||
|
||||
Não ativar — sem clínica Klingo real nem credencial Postgres válida.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Workflow de exemplo "Agendamento - GChat - ClinicAll (exemplo)"
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: MCP Server de agendamento ClinicAll (`igaKjoHcPNYRAxPS`, path `mcp-server-clinicall`, 8 tools).
|
||||
- Produces: workflow `Agendamento - GChat - ClinicAll (exemplo)`.
|
||||
|
||||
- [ ] **Step 1: Criar o workflow (webhook + AI Agent + modelo + memória)**
|
||||
|
||||
`n8n_create_workflow`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Agendamento - GChat - ClinicAll (exemplo)",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "webhook",
|
||||
"name": "Webhook GChat",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [0, 0],
|
||||
"parameters": { "httpMethod": "POST", "path": "agendamento-clinicall-exemplo", "responseMode": "lastNode", "options": {} }
|
||||
},
|
||||
{
|
||||
"id": "extrair-campos",
|
||||
"name": "Extrair campos do webhook",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [224, 0],
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{ "name": "sessionId", "type": "string", "value": "={{ $json.body.protocol }}" },
|
||||
{ "name": "mensagem", "type": "string", "value": "={{ String($json.body.entrada || '').trim() }}" },
|
||||
{ "name": "nomeContato", "type": "string", "value": "={{ $json.body.contactName }}" },
|
||||
{ "name": "telefone", "type": "string", "value": "={{ $json.body.number }}" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ai-agent",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"typeVersion": 2.1,
|
||||
"position": [448, 0],
|
||||
"parameters": {
|
||||
"text": "={{ $json.mensagem }}",
|
||||
"systemMessage": "Você é um assistente de agendamento de consultas médicas, via WhatsApp, para uma clínica que usa o sistema ClinicAll. Seu trabalho: 1) entender o que o paciente quer agendar (especialidade, convênio, profissional, procedimento), usando as tools especialidade, convenio, profissional e procedimento conforme necessário pra resolver os códigos certos; 2) confirmar/consultar o paciente com a tool paciente; 3) usar consultar_horarios para ver os horários livres de uma unidade+profissional — cada horário livre já é um registro existente, identificado por um idAgenda; 4) para concluir, chamar agendar passando EXATAMENTE o idAgenda do horário livre escolhido no passo anterior — agendar não cria um registro novo do zero, ele concilia (atualiza) esse mesmo idAgenda com os dados do paciente/procedimento/convênio. Nunca invente um idAgenda; 5) responda sempre em português, de forma clara e cordial."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "openai-chat-model",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"typeVersion": 1.3,
|
||||
"position": [448, -160],
|
||||
"parameters": { "model": { "mode": "id", "value": "gpt-4o-mini" }, "builtInTools": {}, "options": {} },
|
||||
"credentials": { "openAiApi": { "id": "29vMIDah5DTIhUYJ", "name": "GPT" } }
|
||||
},
|
||||
{
|
||||
"id": "memoria",
|
||||
"name": "Memória da Sessão (Postgres)",
|
||||
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
|
||||
"typeVersion": 1.4,
|
||||
"position": [448, 160],
|
||||
"parameters": { "sessionIdType": "customKey", "sessionKey": "={{ $json.sessionId }}", "tableName": "n8n_chat_histories", "contextWindowLength": 20 },
|
||||
"credentials": { "postgres": { "id": "PLACEHOLDER_SEM_CREDENCIAL", "name": "Postgres - (sem cliente real)" } }
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook GChat": { "main": [[{ "node": "Extrair campos do webhook", "type": "main", "index": 0 }]] },
|
||||
"Extrair campos do webhook": { "main": [[{ "node": "AI Agent", "type": "main", "index": 0 }]] },
|
||||
"OpenAI Chat Model": { "ai_languageModel": [[{ "node": "AI Agent", "type": "ai_languageModel", "index": 0 }]] },
|
||||
"Memória da Sessão (Postgres)": { "ai_memory": [[{ "node": "AI Agent", "type": "ai_memory", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Anotar o `id` retornado.
|
||||
|
||||
- [ ] **Step 2: Conectar o MCP Client Tool com todas as tools do MCP ClinicAll**
|
||||
|
||||
`n8n_update_partial_workflow`, numa única chamada atômica:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<id do workflow do Step 1>",
|
||||
"operations": [
|
||||
{
|
||||
"type": "addNode",
|
||||
"node": {
|
||||
"id": "mcp-client-clinicall-agendamento",
|
||||
"name": "MCP ClinicAll Agendamento",
|
||||
"type": "@n8n/n8n-nodes-langchain.mcpClientTool",
|
||||
"typeVersion": 1,
|
||||
"position": [448, 320],
|
||||
"parameters": {
|
||||
"endpointUrl": "https://devn8n.gtigrupo.com.br/mcp/mcp-server-clinicall",
|
||||
"authentication": "none",
|
||||
"include": "all"
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "type": "addConnection", "source": "MCP ClinicAll Agendamento", "target": "AI Agent", "sourceOutput": "ai_tool", "targetIndex": 0 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Validar**
|
||||
|
||||
`n8n_validate_workflow`. Esperado: `errorCount: 0`.
|
||||
|
||||
- [ ] **Step 4: Deixar inativo**
|
||||
|
||||
Não ativar — sem clínica ClinicAll real nem credencial Postgres válida.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Workflow de exemplo "Agendamento - GChat - SysLife (exemplo)"
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: MCP Server de agendamento SysLife (`TYu78UqIUr2yCBHK`, path `mcp-server-syslife`, 7 tools).
|
||||
- Produces: workflow `Agendamento - GChat - SysLife (exemplo)`.
|
||||
|
||||
- [ ] **Step 1: Criar o workflow (webhook + AI Agent + modelo + memória)**
|
||||
|
||||
`n8n_create_workflow`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Agendamento - GChat - SysLife (exemplo)",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "webhook",
|
||||
"name": "Webhook GChat",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [0, 0],
|
||||
"parameters": { "httpMethod": "POST", "path": "agendamento-syslife-exemplo", "responseMode": "lastNode", "options": {} }
|
||||
},
|
||||
{
|
||||
"id": "extrair-campos",
|
||||
"name": "Extrair campos do webhook",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [224, 0],
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{ "name": "sessionId", "type": "string", "value": "={{ $json.body.protocol }}" },
|
||||
{ "name": "mensagem", "type": "string", "value": "={{ String($json.body.entrada || '').trim() }}" },
|
||||
{ "name": "nomeContato", "type": "string", "value": "={{ $json.body.contactName }}" },
|
||||
{ "name": "telefone", "type": "string", "value": "={{ $json.body.number }}" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ai-agent",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"typeVersion": 2.1,
|
||||
"position": [448, 0],
|
||||
"parameters": {
|
||||
"text": "={{ $json.mensagem }}",
|
||||
"systemMessage": "Você é um assistente de agendamento de consultas médicas, via WhatsApp, para uma clínica que usa o sistema SysLife. Siga esta ordem: 1) use a tool especialidade para identificar a especialidade desejada pelo paciente; 2) use a tool medico para listar os médicos daquela especialidade/origem; 3) em qualquer ordem, use as tools convenio, paciente e procedimento conforme necessário pra resolver os códigos de convênio/plano, os dados do paciente (CPF, matrícula ou celular) e o procedimento/valor; 4) use consultar_horarios (tool Horario) para ver os horários disponíveis do médico escolhido; 5) para concluir, chame agendar com todos os códigos já resolvidos nos passos anteriores (data, hora, número da consulta, origem, paciente, convênio, plano, tabela de procedimento, procedimento, médico, especialidade, celular) — nunca invente um código que não veio de uma das tools anteriores; 6) responda sempre em português, de forma clara e cordial."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "openai-chat-model",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"typeVersion": 1.3,
|
||||
"position": [448, -160],
|
||||
"parameters": { "model": { "mode": "id", "value": "gpt-4o-mini" }, "builtInTools": {}, "options": {} },
|
||||
"credentials": { "openAiApi": { "id": "29vMIDah5DTIhUYJ", "name": "GPT" } }
|
||||
},
|
||||
{
|
||||
"id": "memoria",
|
||||
"name": "Memória da Sessão (Postgres)",
|
||||
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
|
||||
"typeVersion": 1.4,
|
||||
"position": [448, 160],
|
||||
"parameters": { "sessionIdType": "customKey", "sessionKey": "={{ $json.sessionId }}", "tableName": "n8n_chat_histories", "contextWindowLength": 20 },
|
||||
"credentials": { "postgres": { "id": "PLACEHOLDER_SEM_CREDENCIAL", "name": "Postgres - (sem cliente real)" } }
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook GChat": { "main": [[{ "node": "Extrair campos do webhook", "type": "main", "index": 0 }]] },
|
||||
"Extrair campos do webhook": { "main": [[{ "node": "AI Agent", "type": "main", "index": 0 }]] },
|
||||
"OpenAI Chat Model": { "ai_languageModel": [[{ "node": "AI Agent", "type": "ai_languageModel", "index": 0 }]] },
|
||||
"Memória da Sessão (Postgres)": { "ai_memory": [[{ "node": "AI Agent", "type": "ai_memory", "index": 0 }]] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Anotar o `id` retornado.
|
||||
|
||||
- [ ] **Step 2: Conectar o MCP Client Tool com todas as tools do MCP SysLife**
|
||||
|
||||
`n8n_update_partial_workflow`, numa única chamada atômica:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<id do workflow do Step 1>",
|
||||
"operations": [
|
||||
{
|
||||
"type": "addNode",
|
||||
"node": {
|
||||
"id": "mcp-client-syslife-agendamento",
|
||||
"name": "MCP SysLife Agendamento",
|
||||
"type": "@n8n/n8n-nodes-langchain.mcpClientTool",
|
||||
"typeVersion": 1,
|
||||
"position": [448, 320],
|
||||
"parameters": {
|
||||
"endpointUrl": "https://devn8n.gtigrupo.com.br/mcp/mcp-server-syslife",
|
||||
"authentication": "none",
|
||||
"include": "all"
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "type": "addConnection", "source": "MCP SysLife Agendamento", "target": "AI Agent", "sourceOutput": "ai_tool", "targetIndex": 0 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Validar**
|
||||
|
||||
`n8n_validate_workflow`. Esperado: `errorCount: 0`.
|
||||
|
||||
- [ ] **Step 4: Deixar inativo**
|
||||
|
||||
Não ativar — sem clínica SysLife real nem credencial Postgres válida.
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Atualizar o runbook e commit
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: os 3 workflow ids criados nas Tasks 1–3.
|
||||
|
||||
- [ ] **Step 1: Adicionar seção 22 ao runbook**
|
||||
|
||||
Adicionar em `docs/n8n-agendamento-runbook.md`, após a seção 21, uma
|
||||
seção "22. Workflows de exemplo de AI Agent para Agendamento (Klingo,
|
||||
ClinicAll, SysLife)" cobrindo: o motivo (os MCP Servers de agendamento
|
||||
existiam sem workflow de agente cliente, diferente da confirmação),
|
||||
os 3 ids de workflow criados, a correção do número real de tools de
|
||||
cada provider (Klingo 2, ClinicAll 8, SysLife 7 — não os 8 do Klingo
|
||||
Telefonia que tinha sido citado por engano antes), a limitação real do
|
||||
Klingo (`SLOT_MAPPING_UNAVAILABLE`) e como o `systemMessage` lida com
|
||||
ela, e o status "estrutura pronta, inativos de propósito — falta
|
||||
clínica real de qualquer um dos três providers".
|
||||
|
||||
- [ ] **Step 2: Atualizar a tabela da seção 21 (opcional, se fizer sentido)**
|
||||
|
||||
Adicionar uma linha na tabela comparativa da seção 21 com o id do
|
||||
workflow de agendamento de exemplo de cada provider, complementando as
|
||||
linhas já existentes de confirmação — só se isso não deixar a tabela
|
||||
poluída; caso contrário, deixar a seção 22 autocontida sem tocar na 21.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
cd "D:\PROJETOS\KLINGO-CLINICAL-SYSLIFE-GCHAT-AGENDAMENTO"
|
||||
git add docs/n8n-agendamento-runbook.md
|
||||
git commit -m "docs: registra workflows de exemplo de AI Agent para agendamento (Klingo, ClinicAll, SysLife)
|
||||
|
||||
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Observação sobre execução
|
||||
|
||||
Mesma disciplina dos planos anteriores: validar
|
||||
(`n8n_validate_workflow`) depois de cada workflow criado/modificado.
|
||||
Os 3 workflows deste plano ficam **intencionalmente inativos** ao
|
||||
final — sem clínica real de nenhum dos três providers e sem credencial
|
||||
Postgres válida, ativá-los só geraria execuções com erro. O
|
||||
`systemMessage` do Klingo é o único que precisa de atenção redobrada:
|
||||
ele documenta uma limitação real da API (não um bug desta
|
||||
implementação) — não simplificar nem remover esse aviso ao escrever o
|
||||
texto final.
|
||||
@@ -0,0 +1,175 @@
|
||||
# Design — Confirmação de Agenda via WhatsApp (SysLife)
|
||||
|
||||
**Data:** 2026-08-11
|
||||
**Status:** Aprovado pelo usuário, aguardando plano de implementação.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Mesmo padrão já implementado para Klingo e ClinicAll
|
||||
(`docs/superpowers/specs/2026-08-11-confirmacao-agenda-klingo-design.md`,
|
||||
`docs/superpowers/specs/2026-08-11-confirmacao-agenda-clinicall-design.md`),
|
||||
agora para o **SysLife**: permitir que o paciente confirme presença numa
|
||||
consulta agendada via WhatsApp. Os endpoints usados fazem parte do mesmo
|
||||
WebService já usado no MVP de agendamento (`Manual WS SysLife V3.4.pdf`,
|
||||
salvo em `docs/Manual WS SysLife V3.4 (1).pdf`, relido nesta sessão),
|
||||
recurso `AgendaWSApp` — explicitamente descrito no manual como "retorna
|
||||
os registros da agenda **para uso do WhatsApp**".
|
||||
|
||||
## Relação com o trabalho anterior
|
||||
|
||||
- Reaproveita o **MCP Server standalone de Mensageria (Z-PRO)**
|
||||
(`docs/n8n-agendamento-runbook.md`, seção 17) para o disparo do
|
||||
lembrete, mesma decisão do Klingo e do ClinicAll.
|
||||
- É um MCP **novo e separado** do MCP de agendamento do SysLife
|
||||
(seção 14.2 do runbook) — endpoints diferentes (`AgendaWSApp` de
|
||||
confirmação vs. `Agenda`/`Horario`/etc. de criação), mesmo espírito
|
||||
de separação já usado nos outros dois providers.
|
||||
- **Reaproveita a credencial Basic Auth já criada** no MVP de
|
||||
agendamento do SysLife (`SysLife Basic Auth - clínica pendente`,
|
||||
`u8XXJ6wyrKuGJp4Y`) — usuário/senha fixos do WebService
|
||||
(`WebSysLife`/`$Sys10Life`), documentados literalmente no manual, não
|
||||
variam por clínica nem por endpoint. Diferente do Klingo (token por
|
||||
clínica) e do ClinicAll (sem auth documentada), aqui a credencial já
|
||||
existe e pode ser usada de verdade.
|
||||
|
||||
## Escopo: 3 tools do MCP Server "SysLife - Confirmação de Agenda"
|
||||
|
||||
O recurso `AgendaWSApp` do manual é mais rico que o equivalente dos
|
||||
outros dois providers: um único `GET` já serve três propósitos
|
||||
diferentes, alternados por qual dos três parâmetros é enviado (o manual
|
||||
é explícito: **"Deverá ser usado somente um dos 3 parâmetros"**):
|
||||
|
||||
| Tool | Endpoint | Parâmetro usado | Uso |
|
||||
| --- | --- | --- | --- |
|
||||
| `listar_agendamentos_do_dia` | `GET /AgendaWSApp` | `QuantidadeDias` (`1` = amanhã, `2` = depois de amanhã, etc.) | Base do lembrete proativo |
|
||||
| `buscar_agendamento_por_telefone` | `GET /AgendaWSApp` | `NumeroCelular` (DDD + número, sem separador) | O agente usa pra achar a consulta pendente do paciente pelo telefone |
|
||||
| `confirmar_presenca` | `POST /AgendaWSApp` | `CodigoAgenda` + `Resposta` (`"1"` = SIM, `"2"` = NÃO) | Ação central — o mais simples dos três providers, só dois valores possíveis |
|
||||
|
||||
Duas tools de leitura (`listar_agendamentos_do_dia`,
|
||||
`buscar_agendamento_por_telefone`) apontam pro **mesmo endpoint**, cada
|
||||
uma com um sub-workflow próprio que monta o parâmetro certo — mesma
|
||||
separação de responsabilidade já usada nos outros MCP Servers deste
|
||||
projeto (uma tool, um propósito claro), mesmo reaproveitando a chamada
|
||||
HTTP de baixo nível.
|
||||
|
||||
**Fora de escopo, mesmo raciocínio do `/entrada` do Klingo e do
|
||||
`AgendaCentral`/relatórios do ClinicAll**: `AgendaCentral` (central
|
||||
telefônica, não WhatsApp), `AgendaRetorno` (consultas de retorno, não
|
||||
confirmação), `AgendaStatus` (base para NPS, sem endpoint de submissão
|
||||
documentado no manual) e `AgendaConfirmado` (só consulta informativa de
|
||||
quem já confirmou, sem ação — não essencial ao fluxo).
|
||||
|
||||
## Arquitetura
|
||||
|
||||
```
|
||||
1) MCP Server "SysLife - Confirmação de Agenda" (servidor + 3 sub-workflows)
|
||||
[MCP Server Trigger] ──Tools──┬── listar_agendamentos_do_dia ──▶ GET /AgendaWSApp (QuantidadeDias)
|
||||
├── buscar_agendamento_por_telefone ──▶ GET /AgendaWSApp (NumeroCelular)
|
||||
└── confirmar_presenca ──▶ POST /AgendaWSApp (CodigoAgenda, Resposta)
|
||||
|
||||
2) Workflow proativo "Lembrete de Confirmação - SysLife (exemplo)"
|
||||
[Cron 8h diário] → [SysLife: listar_agendamentos_do_dia (QuantidadeDias=1)]
|
||||
→ [Loop por agendamento] → [Mensageria ZPRO: enviar_texto]
|
||||
|
||||
3) Workflow de exemplo "Confirmação de Agenda - GChat - SysLife+Z-PRO (exemplo)"
|
||||
[Webhook Z-PRO/GChat] → [AI Agent] → resposta ao paciente
|
||||
tools: MCP SysLife - Confirmação de Agenda (buscar_agendamento_por_telefone, confirmar_presenca)
|
||||
memória: Postgres Chat Memory (mesmo padrão dos outros exemplos)
|
||||
```
|
||||
|
||||
Sem clínica SysLife real disponível: mesma disciplina dos outros dois
|
||||
providers — estrutura completa, validada (`n8n_validate_workflow`, 0
|
||||
erros esperados), sem teste de ponta a ponta.
|
||||
|
||||
## Componentes
|
||||
|
||||
### MCP Server "SysLife - Confirmação de Agenda"
|
||||
|
||||
Mesmo esqueleto de sub-workflow do MVP de agendamento do SysLife:
|
||||
`Execute Workflow Trigger` → `Config SysLife` (`Set` com `baseUrl`
|
||||
placeholder) → `Montar corpo` (monta o JSON do body — mesmo em GET,
|
||||
conforme documentado: `Seção Body, Type raw, JSON`) → `HTTP Request`
|
||||
(Basic Auth via credencial reaproveitada, `neverError: true` +
|
||||
`fullResponse: true`) → `Classificar resultado` (mesma taxonomia
|
||||
genérica dos outros providers: `AUTHENTICATION_ERROR` 401/403,
|
||||
`NOT_FOUND` 404, `VALIDATION_ERROR` 400/422, `SYSLIFE_API_ERROR`
|
||||
demais — o manual não documenta código de erro específico).
|
||||
|
||||
- **`listar_agendamentos_do_dia`**: parâmetro `quantidadeDias`
|
||||
(obrigatório — `1` para amanhã, conforme o exemplo do manual "1 = Dia
|
||||
atual + 1"). Body: `{ "QuantidadeDias": <int> }`.
|
||||
- **`buscar_agendamento_por_telefone`**: parâmetro `numeroCelular`
|
||||
(obrigatório, string — DDD + número, ex.: `"71999999999"`, conforme
|
||||
documentado "Código DDD + Número do Celular"). Body:
|
||||
`{ "NumeroCelular": "<string>" }`.
|
||||
- **`confirmar_presenca`**: parâmetros `codigoAgenda` (obrigatório) e
|
||||
`resposta` (obrigatório, string `"1"` ou `"2"` — documentado na
|
||||
`description` da tool como `"1" = paciente confirmou`, `"2"` =
|
||||
paciente não confirmou/recusou, sem terceiro estado como no Klingo).
|
||||
Body: `{ "CodigoAgenda": <int>, "Resposta": "<string>" }`.
|
||||
|
||||
Resposta de `AgendaWSApp` GET é um array rico (campos `coAgenda`,
|
||||
`dtAtendimento`, `hrAtendimentoAlterado`, `nmPaciente`, `coMedico`,
|
||||
`nmMedico`, `nmEspecialidade`, `nmOrigemDestino`, `coProcedimento`,
|
||||
`nmProcedimento`, `nuTelefone`, `nuTelefoneDdd`, `nuCelular`,
|
||||
`nuCelularDdd`, `txObservacao`, `txWhatsappProcedimento`,
|
||||
`txWhatsappHorario`, `txWhatsappMedico`, `txEndereco`, `txBairro`,
|
||||
`txComplemento`, `txCidade`, `txEstado`, `nmMsgSms`, `nmMsgSmsExtra`) —
|
||||
já vem pronto com textos formatados pro WhatsApp
|
||||
(`txWhatsappProcedimento`/`txWhatsappHorario`/`txWhatsappMedico`), útil
|
||||
pra montar a mensagem do lembrete sem concatenar campos manualmente.
|
||||
|
||||
### Workflow proativo "Lembrete de Confirmação - SysLife (exemplo)"
|
||||
|
||||
```
|
||||
[Cron Trigger, 8h diário] → [SysLife: listar_agendamentos_do_dia]
|
||||
(quantidadeDias = 1, ou seja, amanhã)
|
||||
→ [Loop] → [Mensageria ZPRO: enviar_texto]
|
||||
(number = nuCelularDdd + nuCelular do item, body = mensagem usando
|
||||
txWhatsappProcedimento/txWhatsappHorario/txWhatsappMedico quando
|
||||
presentes, senão monta com nmProcedimento/dtAtendimento/nmMedico)
|
||||
```
|
||||
|
||||
Diferente do ClinicAll, o telefone aqui é campo direto do item
|
||||
(`nuCelular`/`nuCelularDdd`), sem objeto `Paciente` aninhado — mais
|
||||
parecido com o Klingo nesse aspecto. Fica **inativo** de propósito.
|
||||
|
||||
### Workflow de exemplo "Confirmação de Agenda - GChat - SysLife+Z-PRO (exemplo)"
|
||||
|
||||
Réplica estrutural do padrão já usado no Klingo e no ClinicAll (webhook
|
||||
→ AI Agent → memória Postgres), sem cliente real associado.
|
||||
|
||||
- **Webhook**: mesmo contrato Z-PRO/GChat já documentado (seção 6 do
|
||||
runbook).
|
||||
- **AI Agent**: `systemMessage` fixo instruindo o agente a: entender se
|
||||
a mensagem é confirmação/recusa; se for, pedir o telefone do paciente
|
||||
(ou usar o `telefone` já vindo do próprio contato do WhatsApp, quando
|
||||
disponível) e usar `buscar_agendamento_por_telefone` para achar o
|
||||
`coAgenda` pendente; chamar `confirmar_presenca` com
|
||||
`resposta: "1"` (confirmou) ou `"2"` (recusou); responder confirmando
|
||||
o registro. Mais simples que o ClinicAll — não precisa de tool de
|
||||
tabela de domínio, os dois valores já são documentados diretamente.
|
||||
- **Tools**: MCP SysLife - Confirmação de Agenda —
|
||||
`buscar_agendamento_por_telefone` e `confirmar_presenca` conectadas
|
||||
(`listar_agendamentos_do_dia` só serve ao workflow proativo).
|
||||
- **Memória**: `Postgres Chat Memory`, mesmo padrão da seção 16/18/19
|
||||
do runbook, credencial placeholder.
|
||||
|
||||
Fica **inativo** de propósito.
|
||||
|
||||
## Fora de escopo (adiado)
|
||||
|
||||
- `AgendaCentral`, `AgendaRetorno`, `AgendaStatus`, `AgendaConfirmado`
|
||||
— ver seção "Escopo" acima.
|
||||
- Configuração via Google Docs no workflow de exemplo — sem gestor
|
||||
real associado.
|
||||
- Teste de ponta a ponta contra a API real do SysLife — sem clínica
|
||||
real disponível (mesma pendência do MVP de agendamento).
|
||||
|
||||
## Testes
|
||||
|
||||
Sem clínica SysLife real disponível: validação estrutural apenas
|
||||
(`n8n_validate_workflow`, 0 erros esperados em todos os workflows).
|
||||
Teste real de ponta a ponta fica pendente para quando houver uma
|
||||
clínica SysLife real — a credencial Basic Auth já existe (reaproveitada
|
||||
do MVP de agendamento), então só falta o `baseUrl` real da clínica.
|
||||
@@ -0,0 +1,137 @@
|
||||
# Design — Workflows de Exemplo de AI Agent para Agendamento (Klingo, ClinicAll, SysLife)
|
||||
|
||||
**Data:** 2026-08-11
|
||||
**Status:** Aprovado pelo usuário, aguardando plano de implementação.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Os MCP Servers de agendamento do Klingo, ClinicAll e SysLife existem e
|
||||
estão validados estruturalmente há algum tempo, mas nenhum tem um
|
||||
workflow de agente cliente (AI Agent + prompt + `MCP Client Tool`) —
|
||||
isso foi explicitamente adiado em cada design ("sem clínica real nem
|
||||
canal de mensagens definido"). Mesmo padrão que já resolvemos pra
|
||||
**confirmação de agenda** dos mesmos três providers (seções 18-20 do
|
||||
runbook): construir um workflow de exemplo/esqueleto, sem cliente real
|
||||
associado, pronto pra duplicar quando houver clínica de verdade.
|
||||
|
||||
## Relação com o trabalho anterior
|
||||
|
||||
- Reaproveita **exatamente** o esqueleto já usado nos três exemplos de
|
||||
confirmação de agenda (webhook Z-PRO/GChat → `Extrair campos` →
|
||||
`AI Agent` com `OpenAI Chat Model` real + `Postgres Chat Memory`
|
||||
placeholder), só trocando as tools conectadas.
|
||||
- Conecta nas tools de **agendamento**, não nas de confirmação — decisão
|
||||
explícita do usuário: "só agendamento por enquanto", mesmo padrão do
|
||||
Z-PRO em produção (que também só tem agendamento).
|
||||
- Os três MCP Servers de agendamento já existem, ativos ou não conforme
|
||||
cada design; workflows e ids reaproveitados diretamente, nenhum
|
||||
sub-workflow novo é criado nesta rodada — só o workflow de agente e a
|
||||
conexão via `MCP Client Tool`.
|
||||
|
||||
## Escopo real de cada MCP Server de agendamento (conferido nos designs)
|
||||
|
||||
| Provider | MCP Server (id) | Tools | Observação |
|
||||
| --- | --- | --- | --- |
|
||||
| **Klingo** | `3eiFEnfOTLnutP78` (path `mcp-server-klingo`) | `consultar_horarios`, `agendar` (2 tools) | **Limitação real conhecida**: `consultar_horarios` sempre retorna `SLOT_MAPPING_UNAVAILABLE` — a API do Klingo responde com sucesso, mas o formato de cada horário individual não é documentado publicamente. Consequência: `agendar` exige um `slotId` que, na prática, não há hoje como obter por esta integração. |
|
||||
| **ClinicAll** | `igaKjoHcPNYRAxPS` (path `mcp-server-clinicall`) | `unidade`, `especialidade`, `convenio`, `profissional`, `procedimento`, `paciente`, `consultar_horarios`, `agendar` (8 tools) | `agendar` usa `PUT` (suposição não verificada) e **concilia** um `idAgenda` de horário livre já existente (obtido via `consultar_horarios`) — não cria um registro do zero. |
|
||||
| **SysLife** | `TYu78UqIUr2yCBHK` (path `mcp-server-syslife`) | `especialidade`, `medico`, `convenio`, `paciente`, `procedimento`, `consultar_horarios`, `agendar` (7 tools) | Fluxo linear padrão: consulta os recursos de apoio, resolve os códigos, agenda via `PUT /Agenda`. |
|
||||
|
||||
**Correção em relação à conversa inicial**: o Klingo tinha sido
|
||||
apresentado como tendo 8 tools de agendamento (confundindo com a API
|
||||
de Telefonia, que essa sim tem 6 tools de confirmação) — na verdade
|
||||
são só 2. A tabela acima é a fonte de verdade, extraída direto dos
|
||||
designs já commitados.
|
||||
|
||||
## Decisão sobre a limitação do Klingo
|
||||
|
||||
**Decisão explícita do usuário**: construir o workflow de exemplo do
|
||||
Klingo mesmo assim, documentando a limitação — tanto no `systemMessage`
|
||||
do agente (pra ele explicar ao paciente que não consegue consultar
|
||||
horários no momento, em vez de inventar uma resposta) quanto na spec e
|
||||
no runbook. Não é um bug desta implementação; é uma lacuna real e já
|
||||
conhecida da documentação pública da API do Klingo, fora do escopo
|
||||
resolver aqui.
|
||||
|
||||
## Arquitetura
|
||||
|
||||
Três workflows novos, um por provider, todos seguindo o mesmo esqueleto:
|
||||
|
||||
```
|
||||
[Webhook GChat] → [Extrair campos do webhook] → [AI Agent] → resposta
|
||||
├─ tools: MCP de Agendamento do provider (MCP Client Tool, todas as tools)
|
||||
├─ modelo: OpenAI Chat Model (credencial real "GPT", 29vMIDah5DTIhUYJ)
|
||||
└─ memória: Postgres Chat Memory (credencial placeholder, sem cliente real)
|
||||
```
|
||||
|
||||
- `Agendamento - GChat - Klingo (exemplo)` — conecta nas 2 tools do MCP
|
||||
Klingo (`3eiFEnfOTLnutP78`).
|
||||
- `Agendamento - GChat - ClinicAll (exemplo)` — conecta nas 8 tools do
|
||||
MCP ClinicAll (`igaKjoHcPNYRAxPS`).
|
||||
- `Agendamento - GChat - SysLife (exemplo)` — conecta nas 7 tools do
|
||||
MCP SysLife (`TYu78UqIUr2yCBHK`).
|
||||
|
||||
Mesmo contrato de webhook Z-PRO/GChat já documentado (seção 6 do
|
||||
runbook): `body.protocol`→`sessionId`, `body.entrada`→`mensagem`,
|
||||
`body.contactName`→`nomeContato`, `body.number`→`telefone`.
|
||||
|
||||
## Componentes
|
||||
|
||||
### `systemMessage` de cada agente
|
||||
|
||||
Adaptado do fluxo real de cada API (não genérico, não inventado):
|
||||
|
||||
- **Klingo**: instrui o agente a seguir `consultar_horarios` →
|
||||
`agendar`, mas **avisa explicitamente** que `consultar_horarios`
|
||||
pode retornar `SLOT_MAPPING_UNAVAILABLE` — nesse caso, o agente deve
|
||||
informar ao paciente que a consulta de horários está temporariamente
|
||||
indisponível por essa integração, sem inventar horários nem tentar
|
||||
chamar `agendar` sem um `slotId` real.
|
||||
- **ClinicAll**: instrui o agente a consultar especialidade/convênio/
|
||||
profissional/procedimento conforme necessário, usar
|
||||
`consultar_horarios` para obter um `idAgenda` de horário livre, e
|
||||
então chamar `agendar` **nesse mesmo `idAgenda`** (nunca inventar um
|
||||
novo) — deixando explícito que é uma conciliação, não uma criação.
|
||||
- **SysLife**: instrui o agente a seguir a ordem
|
||||
`especialidade` → `medico` → (`convenio`/`paciente`/`procedimento`,
|
||||
qualquer ordem) → `consultar_horarios` → `agendar`, mesma ordem já
|
||||
documentada no design do MCP SysLife.
|
||||
|
||||
Nenhum dos três usa configuração via Google Docs (workflows de exemplo,
|
||||
sem gestor real).
|
||||
|
||||
### Tools conectadas
|
||||
|
||||
Via `MCP Client Tool` nativo, `include: "all"` (diferente dos exemplos
|
||||
de confirmação, que filtravam tools específicas — aqui faz sentido
|
||||
conectar todas as tools do MCP de agendamento, já que o agente precisa
|
||||
de todas elas pra completar um agendamento).
|
||||
|
||||
### Memória e modelo
|
||||
|
||||
Idênticos aos três exemplos de confirmação já construídos: `Postgres
|
||||
Chat Memory` com credencial placeholder (`PLACEHOLDER_SEM_CREDENCIAL`),
|
||||
`OpenAI Chat Model` com a credencial real e compartilhada já usada em
|
||||
produção (`29vMIDah5DTIhUYJ`, "GPT").
|
||||
|
||||
## Fora de escopo (adiado)
|
||||
|
||||
- Resolver `SLOT_MAPPING_UNAVAILABLE` do Klingo de verdade — lacuna de
|
||||
contrato da API, não desta implementação.
|
||||
- Verificar empiricamente o verbo `PUT` de `/agendamento` do ClinicAll
|
||||
— pendência já registrada no MVP de agendamento, continua em aberto.
|
||||
- Conectar as tools de confirmação de agenda nesses mesmos agentes —
|
||||
decisão explícita do usuário de manter só agendamento por enquanto;
|
||||
pode ser revisitado depois.
|
||||
- Ativação dos três workflows — ficam **inativos de propósito**, mesma
|
||||
disciplina de todos os workflows de exemplo já construídos (sem
|
||||
clínica real, sem credencial Postgres real).
|
||||
|
||||
## Testes
|
||||
|
||||
Sem clínica real disponível para nenhum dos três providers: validação
|
||||
estrutural apenas (`n8n_validate_workflow`, 0 erros esperados, exceto
|
||||
warnings já aceitos de credencial placeholder). Nenhuma chamada HTTP
|
||||
real. Quando houver clínica real de qualquer um dos três: preencher
|
||||
`baseUrl`/credenciais reais, provisionar Postgres, ativar o workflow
|
||||
correspondente — mesmo checklist já seguido nos exemplos de
|
||||
confirmação.
|
||||
Reference in New Issue
Block a user