31 KiB
API Overview
Status
Documento vivo. Descreve o estado atual da API e sera atualizado ao longo do refactor.
O que esta API faz
Esta API implementa um fluxo de voz-to-voz para TIA:
- recebe conexao websocket do cliente
- recebe audio PCM do cliente
- encaminha audio para o agent via LiveKit
- transcreve, processa a pipeline de negocio e sintetiza resposta
- devolve audio para o cliente
- finaliza a chamada e exporta resultado
Componentes principais
Bridge
Arquivo principal:
app/ws_gateway/main.pyapp/ws_gateway/voice_client.html
Responsabilidades:
- aceitar conexao em
/ws/agent - receber
start - montar contexto da chamada
- despachar agent para uma room LiveKit
- mandar audio do cliente para LiveKit
- devolver audio do agent para o cliente
- encerrar websocket ao receber
DONE - servir um cliente web de teste em
/voice-client
Agent
Arquivos principais:
app/livekit/main.pyapp/livekit/runtime/call_runtime.pyapp/livekit/runtime/state.pyapp/livekit/runtime/commands.pyapp/livekit/runtime/command_executor.pyapp/livekit/runtime/scheduler.pyapp/livekit/policies/app/livekit/adapters/
Responsabilidades:
- entrar na room do LiveKit
- configurar STT, VAD e TTS
- receber transcricao final do usuario
- chamar o backend de IA configurado
- vocalizar resposta
- tratar interrupcao, idle nudge e finalizacao
Organizacao atual:
main.pyfaz o wiring do agent e das dependenciasCallRuntimecoordena o ciclo de vida da chamadaCallStateconcentra o estado mutavel da sessaocommands.pydefine os comandos internos para side effectsRuntimeCommandExecutorexecuta bridge, export, speech, pipeline e start da sessaoTimerSchedulerconcentra os timers nomeados do runtimeInterruptPolicy,IdlePolicyeFinalizationPolicyconcentram regras operacionais da chamada- adapters encapsulam acesso ao backend de IA, speech, bridge e export
Pipeline de negocio
Backends disponiveis no runtime websocket:
remote_ws:app/livekit/adapters/remote_agent_ws_adapter.py
remote_sse:app/livekit/adapters/remote_agent_sse_adapter.py
Responsabilidades:
- preparar dados de atendimento
- controlar os estagios da chamada ou delegar esse controle ao agent remoto
- gerar a resposta textual por etapa
- registrar metadados e finalizar a conversa
Selecao atual:
AGENT_BACKEND=remote_wsenvia cada turno transcrito paraREMOTE_AGENT_WS_URLAGENT_BACKEND=remote_sseusa contratos especificos por agente:contausaGET /agent/ssepara inicializar a sessao ePOST /agent/ssepara executar cada acaoofertausaPOST /agent/executepor turno e consome os eventos SSEschedule_message,messageedone
AGENT_BACKEND=remote_ws_fakeusa um fake interno no proprio processo, sem abrir websocket local- o fluxo do runtime local continua o mesmo: STT produz texto, o backend de IA devolve texto e o TTS vocaliza
- o roteamento entre agentes acontece pelo campo
data.agent - quando
AGENT_BACKEND=remote_ws, o adapter escolhe a URL poragentse existirem:REMOTE_AGENT_WS_URL_CONTAREMOTE_AGENT_WS_URL_OFERTAREMOTE_AGENT_WS_URL_COBRANCAouREMOTE_AGENT_WS_URL_COBRA
- quando
AGENT_BACKEND=remote_sse, o adapter exige a URL especifica doagent, sem fallback generico:REMOTE_AGENT_SSE_URL_CONTAREMOTE_AGENT_SSE_URL_OFERTAREMOTE_AGENT_SSE_URL_COBRANCAouREMOTE_AGENT_SSE_URL_COBRA
Contrato remoto atual por turno:
timestampagentRouterCallKeyDayRouterCallKeyANIGSMcallIdGedID_FATURAsomente paraagent=contatextprotocolstage
Contrato especifico atual de conta:
- request:
- na abertura da sessao, o transporte envia query string com
ani,channelIdeuraCallId action: "chat"payload.messagepayload.message_idno transporte SSE deconta, com o mesmo UUID enviado na query stringpayload.channelno transporte websocketpayload.interruptionepayload.eventsquando existirem
- na abertura da sessao, o transporte envia query string com
- response:
type: "ready"para a primeira fala ou fala que abre uma janela de resposta do cliente- a fala de
readynao e interrompivel; se o cliente falar enquanto o audio doreadyainda estiver tocando, a transcricao e descartada e registrada em log - apos o fim do audio de
ready, o runtime mantem uma janela protegida de 750ms para absorver atraso de playback do cliente; fala iniciada nessa janela tambem e descartada - depois dessa janela protegida, o runtime passa a esperar resposta do cliente
type: "result"action: "chat"result.type: "final"para respostas intermediariasresult.contentcomo texto para TTStype: "feedback"ouresult.type: "feedback"para mensagens de acompanhamento enquanto o backend continua processando o turno atual- a fala de
feedbacknao e interrompivel, nao abre novo turno, nao espera resposta do cliente e nao arma timeout de silencio do cliente - se o cliente falar durante
feedback, o runtime descarta a transcricao, registra a tentativa em log e continua aguardando a resposta final do backend feedbacknao disparametadata.wait_retry_messages, como mensagens do tipo "Voce esta ai?"- para finalizacoes esperadas, o runtime aguarda uma janela curta de silencio estavel do cliente, fala o
result.contente depois enviastop; se uma nova transcricao final chegar durante a espera, a finalizacao anterior e descartada em favor do novo turno - falas de finalizacao esperada (
resolvido,nao_resolvido,erro_no_matchetc.) nao sao interrompiveis; qualquer fala do cliente durante a finalizacao e descartada - nesses casos o runtime nao chama finalizacao remota adicional (
end/end_service_once) - quando o
readyvier commetadata.wait_timeout_seconds, o runtime aguarda esse tempo apos falar a mensagem deready - se tambem vier
metadata.wait_retry_messages, o runtime fala cada item do array a cada novo estouro dewait_timeout_seconds; depois do ultimo item, aguarda mais um intervalo igual e enviastop_silencio_longocomreason: "no_user_response" - sem
metadata.wait_retry_messages, o comportamento continua sendo encerrar direto no primeiro estouro dewait_timeout_seconds - enquanto aguarda processamento depois do fim de fala do usuario, o runtime toca somente o audio local longo de conforto; no padrao atual, usa intervalo de 12s e no maximo 6 vezes
- se a resposta do backend remoto nao chegar em 300s, o runtime encerra com
stop_agent_backend_unavailable
- se o participante do agent LiveKit desconectar sem
DONE, o bridge tenta redispatch na mesma room; se o agent nao voltar, encerra comstop_agent_runtime_unavailable
Mapeamento de finalizacoes esperadas de conta:
result.type: "resolvido"->stop_resolvido_e_finalizadoresult.type: "nao_resolvido"->stop_nao_resolvidoresult.type: "resolvido_outros_assuntos"->stop_outro_assuntoresult.type: "outros_assuntos"->stop_outro_assuntoresult.type: "erro_falha_sistema"->stop_falha_sistemaresult.type: "erro_no_match"->stop_no_match
Contrato SSE atual de conta:
- o runtime guarda o
session_idretornado pelo backend nos eventosready - abre
GET /agent/sse?msisdn=...&invoice_id=...&ani=...&protocol_id=...&session_id=...&message_id=...&channelId=ura&uraCallId=...noprepare - consome o stream de inicializacao ate
readye ate o termino do prefetch (prefetch_done,prefetch_skippedouprefetch_failed) ou fechamento da resposta - envia cada turno com
POST /agent/sse?session_id=...&ani=...&protocol_id=...&message_id=...&channelId=ura&uraCallId=... message_ide um UUID gerado por turno e independente desession_id; nos turnos de acao,session_idcontinua sendo o identificador de sessao retornado pelo backend de contas- body do
POST:action: "chat"payload.messagepayload.message_idcom o mesmo UUID do parametromessage_idda query stringpayload.interruptionepayload.eventsquando existirem
- a resposta do proprio
POSTetext/event-stream; o runtime consomeready,progress,resulteerrorate receber o resultado da acao
Contrato SSE atual de oferta:
preparenao abre stream remoto; o primeiro turno do agente chamaPOST /agent/execute- o turno inicial envia
message: "inicio_atendimento"para o backend remoto produzir a primeira fala - cada turno usa body:
messageIdgerado por turno como UUIDmessagecominicio_atendimentono primeiro turno ou a transcricao do cliente nos demaiscontext.protocolNumberecontext.protocolovindos dedata.protocolocontext.gsmvindo dedata.gsmcontext.uraIdvindo dedata.callIdGedcontext.callIdGed,context.ani,context.routerCallKey,context.routerCallKeyDay,context.agentecontext.assetIdquando existirem
context.sessionIde enviado a partir dedata.session_id/data.sessionIdrecebido nostart; protocolo,callIdGederouterCallKeynao sao usados como fallback desessionId- headers enviados:
Accept: text/event-stream,Content-Type: application/jsoneChannel-idvindo dechannelIdouura - eventos recebidos:
schedule_message: fala imediatamentescheduledMessage.messagemessage: falaresponsedone: encerra o stream do turno; a chamada so e finalizada quando o status recebido indicar encerramento terminal
- de/para do
done.additionalInformations.service_status:RESOLVED->stop_resolvido_e_finalizadoUNRESOLVEDou ausente ->stop_nao_resolvidoRESOLVED_WITH_NEW_REQUEST-> nao enviastop; a conversa permanece aberta para o novo assunto
done.status=transferrede reconhecido, mas ainda nao orquestra transferencia; nesta versao encerra como nao resolvido e preserva metadados de handover para evolucao futura
Endpoint dev fornecido para oferta:
- host:
https://agt-ai-atendimento-ofertas-dev.internal.timbrasil.com.br - execute:
https://agt-ai-atendimento-ofertas-dev.internal.timbrasil.com.br/agent/execute - health: nao configurado por enquanto; a readiness nao deve derivar nem chamar
/healthpara oferta ate essa rota ser fornecida - enquanto o certificado interno nao estiver confiavel no ambiente local,
REMOTE_AGENT_SSE_TLS_VERIFY_OFERTA=0permite testar o fluxo ignorando a validacao TLS dohttpx - pod de referencia:
tim-ai-atend-agnt-sales-65764fcf8d-xpzmb - IP de referencia:
http://10.153.35.23 - portas:
80:31332/TCP,443:30635/TCP
Origem desses campos:
- o bridge extrai esses valores do
datarecebido emWS /ws/agent - o agent local apenas reaproveita esse contexto para chamar o websocket remoto
protocol/protocoloe identificador de negocio;session_ide identificador explicito de sessao recebido nostart. O runtime nao preenchesession_idcom protocolo,callIdGedourouterCallKey.
Configuracao opcional por chamada:
- o cliente pode enviar
callConfignostart, mas esse bloco e opcional - objetivo atual: testes, homologacao e overrides tecnicos por sessao
callConfig.agentBackendfaz override do backend websocket para aquela chamadacallConfig.agentBackendtambem pode selecionar o transporte SSE para a sessaocallConfig.sttpode ajustarprovider,initialPrompt,configOverrideeminProbSingleWordcallConfig.ttspode ajustarprovider,voiceIdemodelId- valores aceitos hoje em
callConfig.agentBackend:remote_wsremote_sseremote_ws_fake
- hoje os providers suportados no agent local sao:
- STT:
internal_http(Sofya Batchno cliente de teste),fake - TTS:
elevenlabs,azure,xai,fake
- STT:
Endpoints atuais
GET /health
Retorna status simples de saude do bridge.
GET /health/resources
Retorna o deep health do bridge com validacao dos recursos usados pelo WS /ws/agent.
Comportamento:
200quando os checks obrigatorios estao saudaveis503quando algum recurso obrigatorio falha- inclui
active_connections,max_connections,cached,failed_resourcesechecks - os checks atuais cobrem:
agent_runtimeagent_backendstttts
GET /health/services
Retorna o health consolidado dos servicos do TIA no formato consumido pela esteira de operacao.
Comportamento:
200quando todos os servicos monitorados estao saudaveis503quando algum servico monitorado falhastatusno corpo retornaokoufailhtp_cod_statuspreserva o status HTTP retornado pelo health do servico quando houver resposta HTTPhhtp_cod_descpreserva a descricao retornada pelo health quando houver; se nao houver descricao no corpo, usa a reason phrase HTTP- ignora
AGENT_BACKEND=remote_ws_fakee usa as rotas reais configuradas nos envs dos servicos checkscobre apenas os servicos atualmente monitorados:agent_runtimeagent_backend.contas:REMOTE_AGENT_HEALTH_URL_CONTA,REMOTE_AGENT_HEALTH_URL_CONTASouREMOTE_AGENT_HEALTH_URLagent_backend.oferta:REMOTE_AGENT_HEALTH_URL_OFERTA,REMOTE_AGENT_HEALTH_URL_OFERTASou health derivado deREMOTE_AGENT_SSE_URL_OFERTAstt.sofya:STT_HEALTH_URLou health derivado deSTT_URLtts.xAI: provider xAI comXAI_WEBSOCKET_URL/XAI_API_KEY
Para checks sem resposta HTTP, como falha de conexao ou probe por WebSocket, a rota usa fallback 200/500 e a mensagem interna do erro ou sucesso.
Formato:
{
"status": "ok",
"checks": {
"agent_runtime": {
"htp_cod_status": 200,
"hhtp_cod_desc": "SUCCESS"
},
"agent_backend": {
"contas": {
"htp_cod_status": 200,
"hhtp_cod_desc": "SUCCESS"
},
"oferta": {
"htp_cod_status": 200,
"hhtp_cod_desc": "SUCCESS"
}
},
"stt": {
"sofya": {
"htp_cod_status": 200,
"hhtp_cod_desc": "SUCCESS"
}
},
"tts": {
"xAI": {
"htp_cod_status": 200,
"hhtp_cod_desc": "SUCCESS"
}
}
}
}
GET /voice-client
Cliente web de teste para capturar microfone, enviar audio para WS /ws/agent,
reproduzir o audio do agent e configurar STT, TTS e AGENT por chamada.
WS /fake-agent/ws
Websocket fake para homologacao local do backend remoto.
Uso esperado:
- usar apenas para homologacao manual do contrato websocket fake
- manter
agent=conta|oferta|cobranca - deixar um cliente websocket externo chamar o fake diretamente quando precisar testar esse endpoint
Comportamento:
- suporta o contrato
contacomaction/payload - suporta o contrato generico com
text/stage - responde com progressao simples de stages
- encerra quando recebe textos como
encerrar,obrigadooutchau
WS /ws/agent
Fluxo principal de voz:
- cliente conecta
- envia mensagem
start - bridge valida capacidade e readiness dos recursos obrigatorios
- recebe
readyoustop - se receber
ready, envia audio binario - recebe audio binario de resposta
- recebe
stopao fim da chamada ou em falha terminal
Contrato de inicio da chamada type=start:
- a primeira mensagem de inicio deve ser um JSON textual com
type: "start"; opcionalmente,transferencia_session_idpode chegar antes dela para informar somente osession_id - os campos de negocio devem ser enviados em
data - chaves canonicas obrigatorias em
data:agentanigsmsession_idrouterCallKeyrouterCallKeyDaycallIdGed
- chaves obrigatorias por agente:
agentData.idFaturaquandoagent=contaprotocoloquandoagent=oferta
- valores canonicos recomendados para
agent:contaofertacobranca
Exemplo recomendado de start para conta:
{
"type": "start",
"data": {
"agent": "conta",
"ani": "5511999990000",
"gsm": "5511999990000",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"routerCallKeyDay": "20260409",
"routerCallKey": "RCK-001",
"callIdGed": "GED-123456",
"agentData": {
"idFatura": "FAT-123"
}
},
"audioFormat": {
"encoding": "linear16",
"sampleRateHz": 16000,
"channels": 1
},
"callConfig": {
"agentBackend": "remote_ws"
}
}
Exemplo recomendado de start para oferta:
{
"type": "start",
"data": {
"agent": "oferta",
"ani": "5511999990000",
"gsm": "5511999990000",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"routerCallKeyDay": "20260409",
"routerCallKey": "RCK-001",
"callIdGed": "GED-123456",
"protocolo": "PRT-20260409-0001"
},
"audioFormat": {
"encoding": "linear16",
"sampleRateHz": 16000,
"channels": 1
},
"callConfig": {
"agentBackend": "remote_sse"
}
}
Mensagem opcional aceita antes do start em cenarios de transferencia:
{
"type": "transferencia_session_id",
"data": {
"session_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
Se essa mensagem chegar antes do start, o bridge usa esse valor apenas para preencher data.session_id quando o start ainda nao trouxer o campo. Em producao, o caminho recomendado e enviar session_id diretamente dentro de data no start.
Contrato de audio:
- apos o
ready, o cliente deve enviar audio binario bruto emPCM16/LINEAR16,16000 Hz,1 canal - o
readyinforma os parametros operacionais atuais do bridge:session_id: eco dodata.session_idaceito para a chamadasample_rate: 16000channels: 1frame_ms: 20bytes_per_frame: 640
- no contrato atual,
readysignifica que o bridge esta pronto para receber os bytes de audio do cliente - quando
agent_starts_conversationesta habilitado, o agent mantem a entrada do usuario desativada noRoomIOdesde antes doStartSessionate o fim da primeira mensagem; esse gate impede que fala ou backlog vindo da URA alcance VAD/STT durante o setup e a saudacao - o gate e liberado no fim do primeiro turno do agent e tambem em falha do pipeline, finalizacao ou timeout de setup; chamadas em que o usuario inicia a conversa nao usam esse bloqueio
audioFormatnostarte opcional e hoje funciona como campo informativo/reservado- enviar outro codec, sample rate ou numero de canais nesse campo nao reconfigura o bridge atualmente
- se houver necessidade de outro formato, homologar com a equipe de desenvolvimento antes da integracao
Mensagens devolvidas pelo servidor:
readyquando a sessao foi aceita e o bridge esta pronto para receber audio- audio binario PCM16 durante a resposta do agent
stopcomo mensagem terminal em qualquer encerramento doWS /ws/agent
Recuperacao do participante LiveKit do agent:
- quando o participant identificado como agent desconecta e a chamada ainda nao terminou, o bridge faz redispatch do mesmo
AGENT_NAMEna mesma room - quando o novo participant entra, o bridge reenvia o controle
client_audio_enablede passa a consumir o audio desse novo participant - se o redispatch nao trouxer um novo agent dentro do timeout configurado, o bridge envia
stop_agent_runtime_unavailablecomreason: "agent_disconnected"
Contrato de stop:
- toda mensagem terminal em
WS /ws/agentusa:
{
"type": "stop",
"data": {}
}
- bloqueio antes do
ready:
{
"type": "stop",
"data": {
"status": "stop_stt_unavailable",
"reason": "resource_unhealthy",
"resource": "stt",
"failed_resources": ["stt"],
"phase": "pre_ready"
}
}
- falha de recurso durante a sessao:
{
"type": "stop",
"data": {
"status": "stop_agent_backend_unavailable",
"reason": "resource_unhealthy",
"resource": "agent_backend",
"failed_resources": ["agent_backend"],
"phase": "in_session"
}
}
- fim normal da chamada:
{
"type": "stop",
"data": {
"status": "stop_resolvido_e_finalizado",
"reason": "stage_done",
"phase": "in_session"
}
}
- falha terminal durante a sessao:
{
"type": "stop",
"data": {
"status": "stop_bridge_failed",
"reason": "bridge_failed",
"resource": "bridge",
"phase": "in_session"
}
}
Status terminais atualmente usados em WS /ws/agent:
stop_capacity_tiastop_agent_runtime_unavailablestop_agent_backend_unavailablestop_stt_unavailablestop_tts_unavailablestop_resolvido_e_finalizadostop_nao_resolvidostop_falha_sistemastop_no_matchstop_outro_assuntostop_silencio_longostop_bridge_failed
Regra de interpretacao:
phase: "pre_ready"indica bloqueio antes de a sessao aceitar audiophase: "in_session"indica falha terminal ou encerramento depois doready- os status de recurso podem aparecer nas duas fases, dependendo de quando a indisponibilidade foi detectada
Campos opcionais de testes e homologacao:
audioFormatpode ser enviado nostart, mas hoje nao altera o pipeline de audiocallConfige opcional e existe para testes, smoke test local e homologacao tecnica- para integracao produtiva com cliente externo, o contrato pode omitir
callConfig
Configuracao de chamada para teste de carga com STT Sofya, agente fake e TTS xAI:
{
"debugEvents": true,
"callConfig": {
"agentBackend": "remote_ws_fake",
"agentFake": {
"delayMs": 2500,
"responses": "Primeira resposta simulada com tamanho intermediario;Segunda resposta simulada com tamanho intermediario;Resposta final encerrando o atendimento simulado"
},
"stt": {
"provider": "internal_http",
"disableVosk": true
},
"tts": {
"provider": "xai"
}
}
}
Regras desse modo:
agentFake.responsescontem de 2 a 10 frases separadas por;; espacos laterais sao removidos e cada frase deve ter de 40 a 180 caracteresagentFake.delayMse aplicado antes de cada resposta, usa2500por padrao e aceita valores de0a180000- a saudacao inicial continua sendo o
intronormal; cada fala posterior reconhecida pelo STT consome uma resposta fake, sem usar o texto transcrito para escolher a resposta - respostas anteriores as duas ultimas usam
ARGUMENTATION, a penultima usaFORMALIZATIONe a ultima usaDONE; chamadas posteriores ao fim recebem novamente o mesmo resultado terminal - a sequencia e isolada por sessao
debugEvents=truepublica pelo websocket somente eventos tecnicos e valores agregados de fala, STT Sofya, TTS xAI e sheds do Bridge; o conteudo integral da conversa nao e incluido nas metricas- o endpoint nao possui uma autorizacao adicional especifica para o fake: qualquer cliente ja autorizado a abrir
/ws/agentpode selecionarremote_ws_fakeporcallConfig
WS /ws/text
Fluxo de texto sem audio para testes e integracao basica.
WS /ws/text_stream
Fluxo textual com resposta em streaming.
Fluxo ponta a ponta atual
- Cliente abre websocket em
/ws/agent - Bridge recebe
starte faz parse do contexto da chamada - Bridge valida capacidade e readiness dos recursos
- Bridge cria room/token e envia
ready - Cliente envia audio para o bridge
- Bridge publica audio no LiveKit
- Agent recebe audio, STT produz texto
- Agent chama o backend de IA configurado
- Agent usa TTS para vocalizar resposta
- Bridge devolve audio ao cliente
- Agent sinaliza
DONEou ocorre erro terminal - Bridge envia
stope fecha a chamada
Dependencias externas relevantes
- LiveKit
- STT interno HTTP
- Vosk
- ElevenLabs
- modelo LLM da pipeline
- API de fidelizacao
Timeline de chamada
O projeto agora gera uma timeline estruturada por chamada em formato jsonl.
Configuracao:
CALL_TIMELINE_ENABLED=1ativa a escrita da timelineCALL_TIMELINE_CONSOLE=1replica os eventos tambem no stdoutCALL_TIMELINE_DIR=./timelinedefine o diretorio dos arquivosCALL_TIMELINE_QUEUE_MAX=10000limita eventos pendentes para escrita assincronaCALL_LOG_QUEUE_MAX=20000limita registros pendentes dos arquivos por chamadaASYNC_IO_WARNING_INTERVAL_S=60limita a frequencia dos avisos de descarte/erro
A timeline e o arquivo de log por chamada sao gravados por threads de fundo para
nao executar I/O de disco no event loop de audio. Quando uma fila atinge o limite,
o evento e descartado em vez de bloquear o audio e um warning rate-limited registra
o total acumulado. Os limites aceitos ficam entre 1 e 1000000.
Os spans estruturados usam BatchSpanProcessor: span.end() apenas enfileira o
span, enquanto o envio OTLP acontece em lote fora da thread chamadora. O provider
faz flush e shutdown no encerramento normal do processo.
Fake remoto:
remote_ws_fakeusa um fake interno em memoria e nao depende deREMOTE_AGENT_WS_FAKE_URL- o endpoint
ws://127.0.0.1:8000/fake-agent/wscontinua disponivel apenas para testes manuais do contrato websocket
Logs de websocket remoto:
- o adapter
remote_wsagora registra no stdout eventosREMOTE_AGENT_WS_CONNECT_OPEN,REMOTE_AGENT_WS_CONNECT_OK,REMOTE_AGENT_WS_REQUEST,REMOTE_AGENT_WS_RESPONSEe falhas*_FAIL - os logs incluem
instance(hostname/pod),agent,url,host,stage,protocole metadados do payload para facilitar comparar pods com erro de DNS/host
Mock de encerramento apos primeiro audio:
MOCK_STOP_AFTER_FIRST_AUDIO_ENABLED=1faz o bridge enviar ostopterminal configurado para oreasondepois que o primeiro audio real do agente for entregue ao cliente e a saida ficar em silencio pelo intervalo configuradoMOCK_STOP_AFTER_FIRST_AUDIO_SILENCE_S=0.35controla quanto tempo de silencio o bridge espera antes de disparar ostopMOCK_STOP_AFTER_FIRST_AUDIO_REASON=stage_donedefine oreasonexato enviado nostop
Status terminais configuraveis por env:
FINAL_STOP_STATUS_RESOLVED=stop_resolvido_e_finalizadoFINAL_STOP_STATUS_UNRESOLVED=stop_nao_resolvidoFINAL_STOP_STATUS_OTHER_SUBJECT=stop_outro_assuntoFINAL_STOP_STATUS_LONG_SILENCE=stop_silencio_longoFINAL_STOP_DEFAULT_KIND=resolveddefine o fallback quando oreasonnao bater em nenhum valor configuradoFINAL_STOP_REASON_RESOLVED=stage_doneFINAL_STOP_REASON_UNRESOLVED=nao_resolvidoFINAL_STOP_REASON_OTHER_SUBJECT=outro_assuntoFINAL_STOP_REASON_LONG_SILENCE=no_user_response- o mapeamento agora usa comparacao exata do
reason, sem aliases
Espera de resposta do backend remoto:
REMOTE_AGENT_INFLIGHT_WAIT_INTERVAL_S=12REMOTE_AGENT_INFLIGHT_WAIT_SHORT_AUDIO_DIR=src/app/livekit/assets/comfort/shortREMOTE_AGENT_INFLIGHT_WAIT_LONG_AUDIO_DIR=src/app/livekit/assets/comfort/long- o primeiro audio de conforto usa um WAV aleatorio da pasta
short; a partir do segundo, usa WAVs aleatorios da pastalong - o intervalo dos audios de conforto conta a partir do fim do audio anterior
REMOTE_AGENT_INFLIGHT_WAIT_TIMEOUT_S=180REMOTE_AGENT_INFLIGHT_WAIT_MAX_NOTICES=0(0mantém os confortos sem limite de quantidade até o timeout)REMOTE_AGENT_INFLIGHT_WAIT_TEXT=Um momento, ainda estou consultando para te ajudar.- este timeout e tecnico: limita quanto tempo o runtime aguarda o backend remoto processar um turno
- ele e diferente do timeout de resposta do cliente configurado por
metadata.wait_timeout_seconds - mensagens
feedback, tanto top-level quantoresult.type: "feedback", usam a espera tecnica do backend, mas nao contam silencio do cliente nem disparammetadata.wait_retry_messages - em um turno normal, depois que o agent termina de falar e o cliente responde, o fim de fala detectado pelo VAD pode antecipar o primeiro conforto curto enquanto o STT conclui a transcricao
- o conforto antecipado do VAD nao e agendado enquanto o agent esta falando; se outra fala do agent ocupar o
say_lockdepois do agendamento, o conforto tambem e abortado para nao sair colado ao fim da mensagem - se o cliente interromper uma fala interrompivel do agent, o VAD nao enfileira conforto atras dessa fala; o novo pipeline ainda pode usar os confortos normais caso o processamento demore
- se o cliente falar por mais de
800msenquanto o backend ja processa outro turno e o agent esta em silencio, o conforto curto especulativo do VAD e suprimido e a transcricao abre uma interrupcao diferida - cada interrupcao diferida toca
src/app/livekit/assets/comfort/interruption/01.wav, correspondente a "Ouvi o que voce falou, um instante", e invalida a resposta anterior quando ela chegar - no pipeline substituto, o conforto dedicado conta como o primeiro aviso ja consumido: o audio
shortnormal nao toca logo depois; se o processamento continuar, o proximo aviso permitido elonge respeita o intervalo configurado - o estado de interrupcao e rearmado antes de iniciar o pipeline substituto: cada nova fala valida durante o novo processamento repete o conforto dedicado e substitui novamente a resposta em voo, sem descartar a nova transcricao nem deixar silencio na chamada
- falas de ate
800msdurante processamento sao tratadas como ruido ou backchannel curto e nao abrem interrupcao diferida - os logs principais desse fluxo sao
pre_backend_wait_notice_skipped(agent_speakingoubackend_processing),deferred_interruption_accumulated,deferred_interruption_dispatchede os estagios TTSAGENT_BACKEND_WAIT/INTERRUPTION_COMFORT
Recuperacao do participant LiveKit do agent:
AGENT_RECONNECT_ENABLED=1AGENT_RECONNECT_MAX_ATTEMPTS=1AGENT_RECONNECT_TIMEOUT_S=10
Protecao de reenvio TTS:
TTS_EMPTY_FRAME_RETRY_TIMEOUT_S=3interrompe uma tentativa de TTS sem primeiro frame de audio ou com gap entre frames no meio da fala apos o timeout e reenvia o mesmo texto uma vez- antes do reenvio, toca
src/app/livekit/assets/comfort/fails/tts_fail_recovery.wav - quando o reenvio tem sucesso, registra um unico
envio msgcomhttp_cod_status=200eerro_msg=TTS_regerado
STT fake:
STT_PROVIDER=fakedispensaSTT_URL- usa
FAKE_STT_TRANSCRIPTScomo fila de falas por turno FAKE_STT_MODE=repeat_last|cyclecontrola o comportamento ao consumir a fila
TTS fake:
TTS_PROVIDER=fakedispensa credenciais externas- gera audio PCM sintetico local para smoke tests do pipeline
Formato:
- um arquivo por chamada
- nome do arquivo baseado no
room - eventos do
bridgee doagententram no mesmo arquivo - cada linha contem:
tst_rel_mscomponenteventprotocolroom- campos especificos do evento
Eventos relevantes:
bridge:call_startready_sentdispatch_startedlivekit_room_connectedagent_joinclient_audio_first_frame_receivedclient_audio_first_frame_publishedclient_audio_enableddone_packet_receivedstop_sentcall_end
agent:call_startroom_entersession_start_requestedstt_recognize_startedstt_http_completeduser_transcript_finalpipeline_run_startedpipeline_run_completedremote_agent_requestremote_agent_responsetts_stage_startedtts_stage_resultinterrupt_markedfinalize_startedfinalize_completed
Uso pratico:
- iniciar a chamada normalmente
- identificar no terminal o
roomou oprotocol - abrir o arquivo correspondente em
./timeline - ler os eventos em ordem de
t_rel_ms
Ponto de atencao
Durante o refactor, este documento deve continuar descrevendo:
- comportamento publico
- contrato do websocket
- responsabilidades de cada camada
Na versao final, ele deve evoluir para a documentacao oficial da API.