Utilitários
Funções e recursos auxiliares para o desenvolvimento de actions.
format_response_message
Formata uma mensagem de resposta com contexto e timestamp opcionais. Útil para padronizar textos de instrução em ResponseToAgent.
from actions_sdk.features.utils.formatters import format_response_message
Uso
# Mensagem simples
texto = format_response_message("Operação concluída")
# → "Operação concluída"
# Com contexto
texto = format_response_message(
"Consulta realizada",
context={"registros": 5, "tempo_ms": 120},
)
# → "Consulta realizada (registros: 5 | tempo_ms: 120)"
# Com timestamp
texto = format_response_message("Log de execução", include_timestamp=True)
# → "[2025-06-07 14:30:00] Log de execução"
# Combinando contexto e timestamp
texto = format_response_message(
"Resultado",
context={"status": "ok"},
include_timestamp=True,
)
# → "[2025-06-07 14:30:00] Resultado (status: ok)"
Parâmetros
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
content | str | — | Conteúdo base da mensagem |
context | dict | None | None | Dicionário de contexto a anexar à mensagem |
include_timestamp | bool | False | Se True, adiciona timestamp [YYYY-MM-DD HH:MM:SS] no início |
Retorna: str
Code Secrets
Code Secrets são variáveis de ambiente injetadas pelo motor no namespace de execução da action. Use para armazenar credenciais, chaves de API e outros dados sensíveis sem colocá-los no código.
Como definir um Code Secret
Os Code Secrets são configurados na plataforma Tech4AI — na configuração do agente ou da action, na seção de variáveis de ambiente.
Como acessar na action
Code Secrets são disponibilizados como variáveis de módulo — declare a variável no escopo global do arquivo:
# No topo do arquivo da action (escopo global)
MINHA_API_KEY: str # Declaração — o motor injeta o valor
def minha_action(parametro: str):
# Usar a variável como string normal
headers = {"Authorization": f"Bearer {MINHA_API_KEY}"}
# ...
- Nunca imprima Code Secrets em logs ou inclua em payloads de tracing
- Nunca retorne Code Secrets em
instruction,detailoustate_updates - Use o parâmetro
sensitive_json_keysdoHttpClientpara mascarar chaves em requests HTTP
Boas práticas
# Boa prática — declarar no topo com type hint
API_KEY: str
API_URL: str
def consultar_api(query: str):
from actions_sdk.features.http import HttpClient
client = HttpClient(ctx)
resposta = client.get(
f"{API_URL}/endpoint",
headers={"X-API-Key": API_KEY},
sensitive_json_keys=["X-API-Key"], # Mascara nos logs de tracing
)
# ...
Verificar se a variável foi injetada
API_KEY: str
def minha_action():
try:
chave = API_KEY
except NameError:
return ResponseToAgent(
status=ResponseStatus.ERROR,
instruction="Variável API_KEY não foi configurada como Code Secret.",
)
Criptografia simétrica
O namespace de execução expõe as primitivas de baixo nível da lib cryptography para cifra simétrica (AES, TripleDES etc.), disponíveis sem import adicional em actions e tasks. Use quando a integração externa (parceiro, banco, gateway de pagamento) exige que você mesmo cifre/decifre o payload com uma chave compartilhada — por exemplo, campos de um webhook ou de um arquivo trocado com um sistema legado.
| Atributo | Origem |
|---|---|
Cipher | cryptography.hazmat.primitives.ciphers.Cipher |
crypto_algorithms | cryptography.hazmat.primitives.ciphers.algorithms (AES, TripleDES etc.) |
crypto_modes | cryptography.hazmat.primitives.ciphers.modes (CBC, GCM, CTR etc.) |
crypto_padding | cryptography.hazmat.primitives.padding (PKCS7) |
Qual algoritmo usar
- AES — algoritmo padrão para cifra simétrica hoje. Use
AES-128ouAES-256(tamanho da chave em bytes: 16 ou 32) para qualquer caso novo. - TripleDES — considerado obsoleto e mais lento que AES. Só use se a integração externa exigir explicitamente (ex.: sistema legado que só aceita TripleDES). Não escolha para casos novos.
Qual modo usar
O modo define como blocos de dados são combinados durante a cifra. A escolha certa depende de o parceiro precisar (ou não) de autenticação da mensagem:
| Modo | Precisa de padding? | Autentica a mensagem (detecta adulteração)? | Quando usar |
|---|---|---|---|
GCM | Não | Sim (gera uma tag de autenticação) | Padrão recomendado para qualquer integração nova — cifra e garante integridade em uma única operação |
CBC | Sim (crypto_padding.PKCS7) | Não | Somente quando a integração externa exige CBC (protocolo legado). Sem autenticação própria — se precisar detectar adulteração, combine com HMAC (veja hmac, disponível no namespace) |
CTR | Não | Não | Casos específicos de streaming/paralelização. Assim como CBC, não autentica a mensagem por conta própria |
Regra prática: use GCM a menos que o sistema do outro lado exija outro modo.
Cuidado com IV/nonce
- Nunca reutilize o mesmo par (chave, IV/nonce) para cifrar mensagens diferentes — isso quebra a segurança do algoritmo (em CTR e GCM, reutilizar o nonce pode até expor a chave). Gere um IV/nonce novo e aleatório a cada operação de cifra (
os.urandom(...)). - O IV/nonce não é segredo: é comum e seguro transmiti-lo junto com o texto cifrado (ex.: concatenado no início do payload) para que quem for decifrar consiga reconstruir a operação.
- Em GCM, guarde também a tag de autenticação gerada pelo
encryptor()— ela é necessária para decifrar e validar a integridade.
Uso — cifrar e decifrar com GCM (recomendado)
def cifrar_aes_gcm(dado: bytes, chave: bytes, iv: bytes) -> tuple[bytes, bytes]:
# iv: nonce de 12 bytes (96 bits), gerado por uma fonte segura de
# aleatoriedade a cada chamada — nunca reaproveite o mesmo valor
cifrador = Cipher(crypto_algorithms.AES(chave), crypto_modes.GCM(iv)).encryptor()
dado_cifrado = cifrador.update(dado) + cifrador.finalize()
return dado_cifrado, cifrador.tag
def decifrar_aes_gcm(dado_cifrado: bytes, chave: bytes, iv: bytes, tag: bytes) -> bytes:
decifrador = Cipher(crypto_algorithms.AES(chave), crypto_modes.GCM(iv, tag)).decryptor()
return decifrador.update(dado_cifrado) + decifrador.finalize()
# Levanta InvalidTag se o dado ou a tag foram adulterados
Uso — cifrar e decifrar com CBC (protocolos legados)
def cifrar_aes_cbc(dado: bytes, chave: bytes, iv: bytes) -> bytes:
padder = crypto_padding.PKCS7(crypto_algorithms.AES.block_size).padder()
dado_com_padding = padder.update(dado) + padder.finalize()
cifrador = Cipher(crypto_algorithms.AES(chave), crypto_modes.CBC(iv)).encryptor()
return cifrador.update(dado_com_padding) + cifrador.finalize()
def decifrar_aes_cbc(dado_cifrado: bytes, chave: bytes, iv: bytes) -> bytes:
decifrador = Cipher(crypto_algorithms.AES(chave), crypto_modes.CBC(iv)).decryptor()
dado_com_padding = decifrador.update(dado_cifrado) + decifrador.finalize()
unpadder = crypto_padding.PKCS7(crypto_algorithms.AES.block_size).unpadder()
return unpadder.update(dado_com_padding) + unpadder.finalize()
Cuidado com BOM ao decodificar texto
Se o texto original foi salvo como "UTF-8 with BOM" (comum em arquivos exportados por Excel ou algumas ferramentas Windows) antes de ser cifrado, o resultado decifrado vem com um caractere BOM (U+FEFF, invisível) no início. bytes.decode("utf-8") não remove esse caractere — ele quebra comparações de string e faz json.loads falhar com JSONDecodeError mesmo que o conteúdo pareça correto. Use bytes.decode("utf-8-sig") em vez de "utf-8" quando houver risco de BOM na origem, ou remova explicitamente com texto.lstrip("\ufeff") após decodificar.
Fora de escopo: criptografia assimétrica (RSA, ECDH) e utilitários de alto nível (Fernet, X.509).