O que CounterCall faz?
Pak Yanto tirou a manhã de folga do trabalho, pedalou quarenta minutos até o balcão do Samsat e foi dispensado porque sua fotocópia era folha tamanho ofício em vez de A4. Ele perdeu um dia de salário, e ainda não sabia o que mais estava faltando para amanhã.
Os balcões de serviços públicos publicam listas de requisitos que são incompletas, desatualizadas ou silenciosamente contraditadas no guichê. A fonte confiável é a linha telefônica do escritório, e essa linha está ocupada, protegida por URA, e atendida na quarta tentativa. Então as pessoas não ligam. Elas viajam, enfrentam filas, e são dispensadas por um único documento.
Esta skill faz a ligação no lugar delas e retorna uma lista de verificação que a pessoa pode capturar de tela e levar consigo.
Ela se encaixa bem no design do CALL-E: trabalho telefônico de baixa frequência, pessoal, de alto risco, contra um número que um humano teria que rediscar a manhã toda.
Antes de começar
Esta skill faz uma chamada telefônica real para um escritório público real. Confirme com o usuário:
- qual procedimento ele está consultando, nas próprias palavras do escritório
- qual escritório, e o número de telefone no formato E.164, a partir da página publicada do escritório
- que ele deseja que uma chamada seja feita agora
Nunca inferir um número de telefone. Nem de um diretório, nem de um escritório similar, nem adivinhando um código de país. Um número entra no fluxo apenas quando um humano o leu da página publicada do próprio escritório. Ligar para o número errado significa que um chamador automatizado alcança um desconhecido, que é a pior coisa que esta skill pode fazer.
Limites de segurança
Esta skill coleta apenas requisitos de balcão.
- Ela não submete uma solicitação, agenda uma consulta, paga uma taxa ou compromete o usuário a nada. Ela pergunta e relata.
- Ela não fornece aconselhamento jurídico ou de imigração. Se um atendente sugerir uma rota alternativa, reporte-a textualmente como algo que o atendente disse, nunca como uma recomendação.
- A resposta verbal de um atendente é informativa, não juridicamente vinculante, e todo resultado renderizado diz isso. Requisitos mudam e balcões individuais aplicam discricionariedade.
- Ela declara claramente no início da chamada que é um assistente automatizado e por que está ligando. Se a pessoa se recusar a responder, agradece e encerra a chamada. Ela não insiste, pergunta novamente ou retorna a ligação no mesmo dia.
- Uma chamada por escritório por procedimento por dia. Essas são linhas de serviço público atendidas por pessoas com uma fila na frente delas. A chave de idempotência impõe isso; não a remova.
- Quando o atendente não tem certeza, o resultado diz incerto. Nunca preenche uma lacuna com o que é típico e nunca apresenta uma hesitação como fato. Uma pessoa pode viajar por uma cidade com base nesta resposta.
- Liga para escritórios apenas durante seu horário de funcionamento publicado. Uma linha pública tocando às 22:00 não é um dado, é um incômodo.
Leia references/safety.md antes de adaptar esta skill para uma nova instituição. Alguns destinatários
não são alvos apropriados para um chamador automatizado.
O que torna isso diferente de uma transcrição
A maioria das skills de chamada retorna prosa e deixa o leitor interpretá-la. Esta retorna um objeto fixo, validado e se recusa a retornar qualquer outra coisa.
O result_schema do Goal define additionalProperties: false e restringe quatro campos a
enumerações — payment_method, appointment_required, originals_or_copies e
clerk_certainty, cada um incluindo um valor para "o atendente não sabia". Se a chamada retornar com formato diferente, o resultado é colocado em quarentena em vez de
renderizado. Uma lista de verificação meio interpretada é pior do que nenhuma lista, porque o usuário age
com base nela.
O contrato completo está em references/result-contract.md.
Fluxo de trabalho
1. Coletar a solicitação
Obrigatório: o nome do procedimento, o nome do escritório, o número de telefone do escritório em E.164 e a URL da fonte publicada de onde o número veio.
Validar o número antes de qualquer outra coisa acontecer. E.164 é um sinal de mais, um dígito de código de país
não zero, depois 7 a 14 dígitos adicionais: ^\+[1-9]\d{7,14}$. Rejeitar qualquer outra coisa e pedir ao
usuário. Nunca adivinhar um código de país. Um número em formato local chegando ao discador é como a
pessoa errada é chamada.
Execute scripts/preflight.mjs para verificar o número e o contrato sem discar.
2. Verificar se o contrato publicado não mudou
Antes de cada execução, leia a interface do Goal ao vivo e compare-a com o contrato para o qual esta skill foi escrita.
import { diffContract, publishedRunSpec } from './scripts/_lib.mjs';
const goal = await client.goals.get(GOAL_ID);
const drift = diffContract(PINNED_CONTRACT, publishedRunSpec(goal));
if (drift.length) throw new ContractDrift(drift); // recusar discar
Use o helper publishedRunSpec(goal), não goal.published_run_spec diretamente. O formato
do fio e a documentação escrevem published_run_spec.result_schema; o SDK TypeScript converte para camelCase
como publishedRunSpec.resultSchema. Ler apenas o nome documentado contra o cliente TS
retorna undefined em vez de lançar um erro — o guarda então conclui "nenhuma especificação publicada" e
recusa cada discagem enquanto parece estar funcionando. O helper lê ambas as grafias.
Isso nos custou um dia; está documentado em FEEDBACK.md na raiz do repositório.
A documentação do CALL-E recomenda essa comparação antes de implantar uma alteração de variável. Fazer isso em toda execução custa uma chamada de API e converte uma falha silenciosa em uma recusa sonora. Discar para uma pessoa real com um esquema desatualizado produz um resultado que parece bom e está silenciosamente errado.
3. Fazer uma chamada
goals.run com uma Idempotency-Key com escopo para escritório, procedimento e data, para que uma repetição
nunca disque duas vezes uma linha pública:
countercall:{office}:{procedure}:{yyyy-mm-dd}:v1
4. Aguardar até obter um resultado ou um erro
goals.waitForResult tem uma regra de conclusão: parar quando result ou error for
não nulo. Uma chamada concluída pode brevemente retornar ambos nulos enquanto o CALL-E analisa o resultado
estruturado, portanto não trate essa janela como uma falha.
5. Renderizar, ou falhar honestamente
Todos os oito códigos publicados de GoalRunError direcionam para um resultado distinto. O conjunto abaixo é aquele
que scripts/render.mjs implementa — mantenha-os sincronizados se o CALL-E adicionar um código.
| Resultado | O que o usuário vê |
|---|---|
| resultado válido | a lista de verificação, com a linha textual do atendente e a URL da fonte |
no_answer | "a linha não atendeu". Sem lista de verificação |
declined | "o escritório recusou-se a atender um chamador automatizado". Sem lista de verificação |
result_invalid | a chamada foi concluída mas a resposta não correspondeu ao contrato. Colocado em quarentena |
result_unavailable | a chamada foi concluída mas não produziu resposta estruturada. Sem lista de verificação |
result_failed | a resposta não pôde ser processada em uma lista de verificação. Sem lista de verificação |
timed_out | "nenhuma resposta utilizável" antes do prazo. Sem lista de verificação |
call_failed | a chamada não pôde ser concluída. Sem lista de verificação |
canceled | cancelada antes de produzir uma resposta. Sem lista de verificação |
Nenhum ramo renderiza uma lista de verificação parcial. Exemplos trabalhados de cada um estão em
references/examples.md.
Executando
scripts/call.mjs é dry-run por padrão. Ele imprime a solicitação exata que enviaria e
não faz nenhuma chamada. Discar requer um --live explícito:
# dry run — imprime a solicitação que enviaria, não disca nada, não precisa de credenciais
node scripts/call.mjs --office <id> --procedure "<procedure>"
# realmente toca um telefone
node scripts/call.mjs --office <id> --procedure "<procedure>" --live
Uma skill que disca por padrão é uma skill que disca por acidente.
data/offices.json vem com seu número de telefone mascarado (+62XXXXXXXXXX), então a entrada
seminal não pode discar e todo comando contra ela sai com 3 e REFUSING TO DIAL. Isso é
intencional: um número entra no arquivo apenas quando um humano o leu da página publicada do
próprio escritório e registrou source_url e source_checked. Adicione sua própria entrada antes de executar
qualquer coisa.
Para ver a saída do dry-run imediatamente, o fixture de teste carrega um número fictício válido:
node scripts/call.mjs --offices ../../test/fixtures/offices.test.json \
--office fixture-sourced --procedure "perpanjangan paspor"
Quando não usar isso
- Emergências. Esta é uma consulta de requisitos com latência de vários minutos. Não é um meio de contatar alguém urgentemente.
- Escritórios com uma página de requisitos publicada, confiável e atualizada. Se o site estiver correto, leia o site. A chamada só se justifica onde a resposta publicada é conhecida por ser incompleta.
- Qualquer instituição que tenha solicitado não ser contatada por sistemas automatizados, ou onde a lei local restrinja chamadas automatizadas. Verifique antes de adicionar um escritório.
- Consultas de alto volume. Um escritório por procedimento por dia é o teto por design. Se você precisa de volume, você precisa de um acordo de compartilhamento de dados, não de um telefone.
- Qualquer coisa onde estar errado é barato. O valor aqui vem do custo de uma viagem desperdiçada. Se o usuário pode simplesmente ir e descobrir, deixe-o fazer isso.
Limitações honestas
- A cobertura é limitada a escritórios no arquivo seminal, cada um com uma URL de fonte publicada.
- As taxas de resposta em linhas de serviço público variam por escritório e horário do dia, e uma grande parte das chamadas não será atendida. Essa falta de confiabilidade é a razão pela qual esta skill existe, e é relatada em vez de oculta.
- O resultado é a resposta de um único atendente em um único dia. É evidência, não uma garantia.