Tratamento de erros
Os hooks rodam dentro da solicitação que observam, então como suas falhas são tratadas importa. Esta página é a política exata.
- As duas políticas
- O que roda quando algo falha
- Encadeamento de exceções
- BaseException
- Erros de configuração
- Avisos
- Escolher uma política
As duas políticas
hooks_raise controla o que acontece quando um hook lança.
hooks_raise |
comportamento |
|---|---|
True (padrão) |
a exceção do hook se propaga para fora da chamada. |
False |
o hook é ignorado com um RuntimeWarning e a chamada continua. |
Ele é definido por instância e pode ser sobrescrito por chamada (hooks_raise= em predict_batch,
system_one, Router.route, Router.predict). Um None por chamada significa “usar o valor da
instância”.
# strict: a broken audit hook fails the request
laya.load("convaiinnovations/laya", on_predict_end=audit, hooks_raise=True)
# lenient: telemetry must never take down a served request
laya.load("convaiinnovations/laya", on_predict_end=metrics, hooks_raise=False)
dispatch captura Exception. Qualquer coisa que não seja uma Exception (veja
BaseException) nunca é engolida, mesmo com hooks_raise=False.
O que roda quando algo falha
O ciclo de vida da predição é envolvido em try / except / finally, então os hooks de limpeza rodam
em caso de falha.
try:
on_predict_start
inference
except BaseException as exc:
ctx.error = exc
on_error (best effort; cannot mask exc)
raise
finally:
elapsed_ms, usage
on_predict_end (best effort; cannot mask exc on the failure path)
Matriz de falhas, por evento e runtime:
| evento | runtime | se ele lançar |
|---|---|---|
on_predict_start |
Agent / Router | hooks_raise=True: on_error e on_predict_end ainda rodam, depois a exceção se propaga. False: avisa e continua (as mutações feitas antes de lançar permanecem). |
| inferência | Agent / Router | ctx.error definido, on_error roda, on_predict_end roda, exceção se propaga. |
on_error |
Agent / Router | nunca mascara a exceção original; encadeado como __context__. |
on_predict_end (caminho de sucesso) |
Agent / Router | hooks_raise=True: se propaga (o resultado é calculado mas a chamada falha). False: avisa. |
on_predict_end (caminho de falha) |
Agent / Router | nunca mascara a exceção original; encadeado como __context__. |
on_route |
Router | se propaga diretamente; ainda não há contexto de predição. |
on_load |
Router | se propaga diretamente; o checkpoint continua construído e residente. |
on_evict |
Router | se propaga diretamente; o checkpoint já foi liberado. |
Consequências que vale conhecer:
- Um
on_loadque falha deixa o modelo em cache, então o próximoloado retorna sem dispararon_loadde novo. - Um
on_predict_endque falha no caminho de sucesso significa que o chamador recebe uma exceção em vez de um resultado, mesmo que a inferência tenha tido sucesso. Usehooks_raise=Falsepara hooks de fim que são puros efeitos colaterais.
Encadeamento de exceções
Quando um hook falha enquanto outra exceção já está se propagando, a exceção original é relançada e a
exceção do hook é anexada como __context__. A causa raiz nunca é perdida.
class BadTelemetry:
def on_error(self, ctx):
raise RuntimeError("telemetry down")
try:
agent.system_one(state, questions, hooks=[BadTelemetry()])
except RuntimeError as exc:
assert exc.__context__ is not None # the telemetry failure
A mesma regra se aplica a um on_predict_end que falha no caminho de falha.
BaseException
dispatch captura Exception, não BaseException, então KeyboardInterrupt e SystemExit sempre se
propagam. Eles ainda disparam o branch except BaseException do ciclo de vida da predição, o que
significa que on_error e on_predict_end rodam antes de o processo se desenrolar. Mantenha esses
hooks rápidos e não bloqueantes se você se importa com a latência de interrupção.
Erros de configuração
Configuração ruim falha rápido com TypeError, antes de qualquer inferência:
| caso | lançado em | exemplo |
|---|---|---|
| classe em vez de instância | construção | hooks=[MyHook] |
| nenhum método de ciclo de vida | construção | hooks=[object()] |
| evento não chamável | construção | on_predict_start = 5 |
| hook de conveniência não chamável | construção | on_predict_start=123 |
callable simples em hooks= |
construção | hooks=[lambda ctx: None] |
Os hooks por chamada são validados quando a chamada é feita, então um hook por chamada ruim lança
TypeError de predict/system_one em vez de na construção.
Avisos
Com hooks_raise=False, cada hook que falha emite um RuntimeWarning nomeando o tipo do hook e o
evento:
laya: hook Metrics.on_predict_end failed: connection reset
O aviso é emitido uma vez por falha, não uma vez por definição de hook, então um hook instável sob carga pode ser ruidoso. Agregue ou limite a taxa dentro do hook se isso importar.
Timeouts
hooks_timeout limita cada chamada de hook em segundos. Um hook ainda rodando depois do limite é
tratado como falha do hook: TimeoutError quando hooks_raise=True, um RuntimeWarning quando
False. None (o padrão) significa sem limite.
laya.load("convaiinnovations/laya", on_predict_end=metrics, hooks_timeout=2.0)
Ele pode ser definido por instância ou sobrescrito por chamada em predict_batch, system_one,
Router.route, Router.predict e ONNXAgent.system_one. O valor deve ser positivo; 0 ou um número
negativo lança ValueError no ponto em que é definido, em vez de disputar em um join de duração
zero.
Um hook cronometrado roda em uma thread de worker em uma cópia do contexto contextvars do chamador,
então um id de solicitação ou span de tracing definido pelo chamador fica visível ao hook.
Uma ressalva honesta: o Python não consegue interromper uma thread, então um hook que estourou o tempo
continua rodando em segundo plano. O timeout limita quanto tempo a solicitação espera, não quanto tempo
o hook vive. Use-o para manter uma solicitação servida responsiva, não para recuperar o trabalho. Para
um hook que pode travar, dê também à chamada subjacente seu próprio timeout (um timeout de socket ou
HTTP). Como a thread não pode ser recuperada, um hook que trava em toda chamada acumula threads uma por
chamada; dê a um hook que pode travar seu próprio limite em vez de confiar em hooks_timeout para
pará-lo.
Para um hook assíncrono, a corrotina roda no event loop; um timeout do lado que chama ainda retorna depois do limite, e a corrotina continua rodando no loop.
O timeout também libera o lock de hooks_concurrent=False: o dispatch espera pelo hook apenas até o
limite, depois segue em frente, enquanto o hook que estourou o tempo continua rodando fora do lock.
Então o lock serializa os hooks que terminam a tempo, não todo hook que já foi iniciado; um hook que
ultrapassa o limite não bloqueia mais os que estão atrás dele.
Escolher uma política
| tipo de hook | recomendado | por quê |
|---|---|---|
| política / guardrail / censura | hooks_raise=True |
uma política que falha em silêncio é um buraco de segurança. |
| auditoria / logging | hooks_raise=True em testes, muitas vezes False em produção |
perder um registro de auditoria deveria ser barulhento, mas não necessariamente fatal. |
| métricas / tracing | hooks_raise=False |
observabilidade não deve falhar a solicitação. |
| leitura/escrita de cache | hooks_raise=True |
um cache quebrado deveria aparecer, não errar em silêncio. |
Você pode misturar: instale um guardrail com o padrão da instância e dê a um hook de telemetria seu
próprio try/except, ou use um hooks_raise separado por chamada.
Veja também
- Ciclo de vida: o formato
try/except/finallyem contexto. - Padrões e antipadrões: erros comuns de tratamento de erro.