Vision Client
O VisionClient extrai dados estruturados de documentos (PDFs, imagens, etc.) utilizando layouts configurados na plataforma Tech4.ai. Use quando a action precisa processar documentos e obter campos tipados a partir deles.
Inicialização
from actions_sdk.core.types.action_context import ActionContext
from actions_sdk.features.vision import VisionClient, VisionClientError
ctx: ActionContext
vision = VisionClient(ctx)
O VisionClient extrai ctx.vision_base_url automaticamente no construtor.
Exceções no construtor:
ValueError— sectx.vision_base_urlnão estiver definido
Métodos
extract(layout_id, file_url=None, file_id=None, timeout=45)
Extrai dados estruturados de um documento com base em um layout configurado.
vision = VisionClient(ctx)
resultado = vision.extract(
layout_id="ff43427f-817c-44cc-96ee-5f85638d6ff7",
file_id="9acbfe76-9b5e-4ed2-936a-aafee99a5315",
)
print(resultado.status) # "ok", "partial" ou "nodata"
print(resultado.extracted_data) # dict com os campos extraídos
Parâmetros:
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
layout_id | str | — | Obrigatório. ID do layout Vision que define os campos a serem extraídos |
file_url | str | None | None | URL pública do arquivo a ser analisado. Mutuamente exclusivo com file_id |
file_id | str | None | None | ID de um arquivo já enviado para a plataforma. Mutuamente exclusivo com file_url |
timeout | int | 45 | Timeout em segundos (máximo: 45) |
Exatamente um entre file_url e file_id deve ser fornecido. Passar ambos ou nenhum levanta ValueError.
Retorna: VisionExtractResponse
VisionExtractResponse
Objeto com o resultado da extração:
resultado = vision.extract(layout_id="...", file_id="...")
resultado.status # Literal["ok", "partial", "nodata"]
resultado.extracted_data # dict — campos extraídos conforme o layout
Status de Extração
| Status | Significado |
|---|---|
"ok" | Todos os campos do layout foram extraídos com sucesso |
"partial" | Parte dos campos foi extraída; campos ausentes ficam com valor None ou são omitidos |
"nodata" | Nenhum campo pôde ser extraído do documento |
Sempre verifique o status antes de usar extracted_data. Com "partial" ou "nodata", trate a ausência de campos antes de prosseguir com a lógica de negócio.
Exemplos de retorno
Extração bem-sucedida ("ok"):
resultado.status # "ok"
resultado.extracted_data # {
# "numero_nota": "3524",
# "data_emissao": "2024-03-15",
# "valor_total": 1250.0,
# "cnpj_emitente": "12.345.678/0001-99",
# "razao_social": "Empresa Exemplo LTDA"
# }
Extração parcial ("partial"):
resultado.status # "partial"
resultado.extracted_data # {
# "numero_nota": "3524",
# "data_emissao": None,
# "valor_total": 1250.0,
# "cnpj_emitente": None,
# "razao_social": "Empresa Exemplo LTDA"
# }
Sem dados ("nodata"):
resultado.status # "nodata"
resultado.extracted_data # {}
Erros e Exceções
| Exceção | Quando ocorre | Como tratar |
|---|---|---|
VisionClientError | Falha na chamada à API de extração (4xx/5xx, erros de rede, respostas malformadas) | Capture e retorne ResponseToAgent com ERROR |
ValueError | ctx.vision_base_url não definido no construtor, ou ambos/nenhum de file_url/file_id fornecidos | Indica problema de configuração do motor ou uso incorreto do método |
from actions_sdk.features.vision import VisionClient, VisionClientError
try:
resultado = vision.extract(layout_id=layout_id, file_id=file_id)
except VisionClientError as erro:
return ResponseToAgent(
status=ResponseStatus.ERROR,
instruction=f"Falha na extração do documento: {erro}",
)
except ValueError as erro:
return ResponseToAgent(
status=ResponseStatus.ERROR,
instruction=f"VisionClient não disponível neste ambiente: {erro}",
)
Exemplo completo
Baseado na action vision_extract do agente de homologação:
import traceback
from actions_sdk.core.types.action_context import ActionContext
from actions_sdk.core.types.responses import ResponseStatus, ResponseToAgent
from actions_sdk.features.conversational.state import StateManager
from actions_sdk.features.tracing import ActionSpan
from actions_sdk.features.vision import VisionClient, VisionClientError
ctx: ActionContext
CHAVE_PROGRESSO = "action.vision.progresso"
def vision_extract(file_id: str, layout_id: str):
span = ActionSpan(ctx)
state = StateManager(ctx)
span.event("action.vision.inicio", {"file_id": file_id, "layout_id": layout_id})
try:
vision = VisionClient(ctx)
resultado = vision.extract(
layout_id=layout_id,
file_id=file_id,
)
state.update(CHAVE_PROGRESSO, "SUCESSO ✓")
span.event(
"action.vision.sucesso",
{"status": resultado.status, "campos": len(resultado.extracted_data)},
)
return ResponseToAgent(
status=ResponseStatus.SUCCESS,
instruction=(
f"VisionClient.extract() concluído.\n"
f"File ID: {file_id}\n"
f"Layout ID: {layout_id}\n"
f"Status da extração: {resultado.status}\n"
f"Campos extraídos ({len(resultado.extracted_data)}): {resultado.extracted_data}"
),
state_updates=state.get_full_state(),
)
except VisionClientError as erro:
state.update(CHAVE_PROGRESSO, "FALHA ✗")
span.event("action.vision.erro", {"erro": str(erro)})
return ResponseToAgent(
status=ResponseStatus.ERROR,
instruction=f"Falha na extração do documento: {erro}",
state_updates=state.get_full_state(),
)
except ValueError as erro:
# VisionClient levanta ValueError quando ctx.vision_base_url não está configurado
state.update(CHAVE_PROGRESSO, "FALHA ✗")
span.event("action.vision.nao_configurado", {"erro": str(erro)})
return ResponseToAgent(
status=ResponseStatus.ERROR,
instruction=f"VisionClient não disponível neste ambiente: {erro}",
state_updates=state.get_full_state(),
)
except Exception as erro:
state.update(CHAVE_PROGRESSO, "FALHA ✗")
span.event("action.vision.erro_inesperado", {"erro": str(erro)})
return ResponseToAgent(
status=ResponseStatus.ERROR,
instruction=f"Erro inesperado: {traceback.format_exc()}",
state_updates=state.get_full_state(),
)