\n\n\n\n Tutoriais sobre o Sandboxing de Agentes: Construir Aplicações LLM Seguras - BotSec \n

Tutoriais sobre o Sandboxing de Agentes: Construir Aplicações LLM Seguras

📖 20 min read3,968 wordsUpdated Mar 31, 2026

Introdução ao Sandboxing de Agentes

À medida que os Modelos de Linguagem de Grande Escala (LLMs) evoluem de agentes de conversa simples para entidades autônomas poderosas capazes de executar código, interagir com APIs externas e tomar decisões no mundo real, a necessidade de medidas de segurança eficazes se torna primordial. Um agente LLM, quando dotado da capacidade de agir, pode se tornar um risco de segurança significativo se não estiver devidamente restrito. É aqui que entra o sandboxing de agente. Fazer o sandboxing de um agente significa criar um ambiente isolado onde ele pode operar sem afetar o sistema host ou acessar recursos não autorizados. Este tutorial explorará os aspectos práticos do sandboxing de agentes, fornecendo exemplos práticos para demonstrar como construir aplicações LLM seguras e confiáveis.

O princípio central do sandboxing é o menor privilégio: um agente deve ter acesso apenas aos recursos estritamente necessários para seu funcionamento, e nada mais. Sem um sandboxing adequado, um agente malicioso ou errante poderia:

  • Executar código arbitrário no sistema host, resultando em roubo de dados ou comprometimento do sistema.
  • Acessar arquivos sensíveis ou recursos de rede.
  • Iniciar chamadas API externas indesejadas, resultando em custos ou realizando ações não autorizadas.
  • Exfiltrar dados confidenciais por diversos canais.

Ao implementar um sandboxing eficaz, podemos mitigar esses riscos, permitindo-nos usar o imenso poder dos agentes LLM enquanto mantemos controle e segurança.

Compreendendo Ameaças: Por Que Sandboxing?

Antes de explorar o ‘como’, solidifiquemos o ‘porquê’. As ameaças apresentadas por agentes não sandboxizados são múltiplas e podem ser classificadas da seguinte forma:

1. Vulnerabilidades de Execução de Código

Muitos agentes LLM avançados são projetados para escrever e executar código (por exemplo, scripts Python) para resolver problemas, analisar dados ou interagir com ferramentas. Se essa execução não for contida, o agente poderia:

  • Injeção de Comandos do Sistema: Gerar código que chama os.system('rm -rf /') ou comandos destrutivos similares.
  • Execução Remota de Código (RCE): Explorar vulnerabilidades em bibliotecas para assumir o controle do host.
  • Exaustão de Recursos: Criar loops infinitos ou alocar memória/CPU excessiva, resultando em negação de serviço.

2. Acesso a Dados e Exfiltração

Um agente pode ser encarregado de processar dados sensíveis. Sem sandboxing, ele poderia:

  • Acesso Não Autorizado a Arquivos: Ler arquivos fora de seu diretório de trabalho designado (por exemplo, /etc/passwd, chaves API).
  • Acesso à Rede: Conectar-se a recursos de rede internos, a servidores maliciosos externos, ou exfiltrar dados para pontos finais arbitrários.
  • Injeção de Prompts via Leitura de Arquivos: Se um agente puder ler arquivos arbitrários, um ator malicioso poderia criar um prompt que engana o agente ao fazê-lo ler um arquivo sensível e, em seguida, integrar seu conteúdo em uma saída posterior.

3. Abuso de APIs e Ferramentas

Agentes frequentemente interagem com APIs externas ou ferramentas personalizadas. Um acesso não restrito pode levar a:

  • Chamadas API Não Autorizadas: Fazer chamadas a APIs sensíveis às quais não deveria ter acesso (por exemplo, gerenciamento de usuários, processamento de pagamentos).
  • Exceeding Costs: Acionar chamadas API caras ou funções em nuvem que consomem muitos recursos.
  • Ações Maliciosas: Se um agente tiver acesso a uma API de email, ele pode enviar emails de spam ou phishing.

Técnicas e Ferramentas de Sandboxing

Existem várias camadas e técnicas que podemos usar para o sandboxing de agentes, que vão desde a simples revisão de código até uma containerização sofisticada.

1. Sandboxing a Nível de Linguagem (Restrições do Interpretador de Código)

Se seu agente gera e executa principalmente código (por exemplo, Python), você pode restringir as capacidades do interpretador.

Exemplo: Execução Python Restrita com exec() e Lista Branca

Um cenário comum é um agente que gera código Python. Em vez de chamar diretamente exec() ou eval() em strings arbitrárias, você pode restringir os globals e os built-ins disponíveis.


import subprocess
import os

def safe_execute_python_code(code: str, allowed_modules: list = None, timeout: int = 10):
 if allowed_modules is None:
 allowed_modules = ['math', 'json', 're'] # Lista branca dos módulos seguros

 # Criar um espaço de nomes global restrito
 restricted_globals = {
 '__builtins__': {key: globals()['__builtins__'][key] for key in [
 'print', 'len', 'str', 'int', 'float', 'list', 'dict', 'tuple', 'set', 
 'range', 'sum', 'min', 'max', 'abs', 'round', 'type', 'isinstance', 'enumerate'
 ]},
 '__name__': '__main__'
 }

 # Importar dinamicamente os módulos permitidos no espaço de nomes restrito
 for module_name in allowed_modules:
 try:
 restricted_globals[module_name] = __import__(module_name)
 except ImportError:
 print(f"Aviso: Não foi possível importar o módulo permitido {module_name}")

 try:
 # Usar subprocess para executar em um processo isolado para melhor isolamento
 # É mais seguro do que usar apenas `exec` no processo atual
 # e permite delays e limites de recursos.
 process = subprocess.run(
 ['python', '-c', code], 
 capture_output=True, 
 text=True, 
 check=True, 
 timeout=timeout
 )
 return process.stdout
 except subprocess.CalledProcessError as e:
 return f"Erro durante a execução: {e.stderr}"
 except subprocess.TimeoutExpired:
 return "Erro: Tempo de execução do código excedido."
 except Exception as e:
 return f"Ocorreu um erro inesperado: {e}"

# Exemplo de uso:
# Código seguro
agent_code_safe = "import math; print(math.sqrt(16))"
print(f"Saída do código seguro: {safe_execute_python_code(agent_code_safe)}")

# Tentativa de código malicioso (será bloqueada pelo isolamento subprocess e pelas restrições de built-in se exec direto tivesse sido usado)
# Com subprocess, o import 'os' também falharia no processo filho a menos que fosse especificamente permitido.
agent_code_malicious_os = "import os; print(os.listdir('/'))"
print(f"Saída do código malicioso OS: {safe_execute_python_code(agent_code_malicious_os)}")

# Tentativa de código malicioso (tentativa de leitura de um arquivo)
agent_code_malicious_file = "with open('/etc/passwd', 'r') as f: print(f.read())"
print(f"Saída do código malicioso de leitura de arquivo: {safe_execute_python_code(agent_code_malicious_file)}")

# Código com um loop infinito (será interrompido pelo timeout)
agent_code_loop = "while True: pass"
print(f"Saída do código em loop: {safe_execute_python_code(agent_code_loop, timeout=3)}")

Explicação:

  • Definimos uma função safe_execute_python_code.
  • Ela recebe o código gerado pelo agente como entrada.
  • Em vez de executar diretamente no processo atual, usamos subprocess.run. Este é um passo crucial para um verdadeiro isolamento, pois isso executa o código em um processo interpretador Python separado. Este processo herda privilégios mínimos e não é o mesmo que o processo pai executando sua aplicação principal.
  • A lista allowed_modules atua como uma lista branca. Mesmo que o agente tente importar os ou sys, esses módulos não estarão disponíveis no ambiente restrito do subprocess, a menos que sejam explicitamente permitidos (o que não deveria ser o caso para o código geral dos agentes).
  • timeout impede a exaustão de recursos devido a loops infinitos.
  • capture_output=True e text=True nos permitem capturar a saída do agente.
  • check=True levanta uma exceção se o subprocess retornar um código de saída diferente de zero (indicando um erro).

Embora essa abordagem melhore consideravelmente a segurança em relação à utilização direta de exec(), ela não é infalível. Um agente muito sofisticado ainda poderia encontrar maneiras de explorar as chamadas de sistema subjacentes se o ambiente Python em si for vulnerável ou se muitos módulos estiverem na lista branca.

2. Sandboxing a Nível do Sistema Operacional (Contêineres & Máquinas Virtuais)

Para o sandboxing mais sólido, especialmente quando os agentes podem gerar código em várias linguagens ou interagir com o sistema de arquivos/rede, a isolação a nível de sistema operacional é indispensável.

a. Contêineres Docker

Docker é uma excelente escolha para o sandboxing. Cada execução de agente pode acontecer em seu próprio contêiner efêmero com limites de recursos e políticas de acesso à rede rigorosamente definidas.

Exemplo Prático: Docker para Execução do Agente

Passo 1: Criar um Dockerfile para o ambiente de execução do agente.


# Dockerfile
FROM python:3.9-slim-buster

WORKDIR /app

# Criar um usuário não-root para segurança
RUN useradd --no-create-home --shell /bin/bash agentuser
USER agentuser

# Copiar um script simples que o agente pode gerar e que queremos executar
COPY run_agent_code.py .

ENTRYPOINT ["python", "run_agent_code.py"]

Etapa 2: Criar run_agent_code.py. Este script receberá o código gerado pelo agente.


# run_agent_code.py
import sys
import os

# Simular a recepção de código do agente (por exemplo, via stdin ou um arquivo)
# Para este exemplo, vamos supor que o código é passado como argumento ou escrito aqui diretamente

if __name__ == "__main__":
 agent_code = "print('Hello from the sandboxed agent!')"
 if len(sys.argv) > 1:
 agent_code = sys.argv[1] # Permitir passar o código como argumento

 try:
 # Executar o código. Nota: o contêiner Docker em si é o ambiente isolado.
 # Poderíamos ainda querer restrições no nível da linguagem *dentro* deste script
 # para uma camada adicional, mas o isolamento principal é o contêiner.
 exec(agent_code)
 except Exception as e:
 print(f"A execução do código do agente falhou: {e}", file=sys.stderr)
 sys.exit(1)

 # Demonstrar acesso restrito
 try:
 print(f"Tentativa de listar a raiz: {os.listdir('/')}")
 except Exception as e:
 print(f"Não foi possível listar o diretório raiz (esperado): {e}")

 try:
 with open('/etc/passwd', 'r') as f:
 print(f.read())
 except Exception as e:
 print(f"Não foi possível ler /etc/passwd (esperado): {e}")

Etapa 3: Execute o código do agente a partir de sua aplicação principal.


import docker

client = docker.from_env()

def execute_agent_in_docker(agent_code: str, cpu_limit: float = 0.5, mem_limit: str = '128m', network_enabled: bool = False):
 try:
 # Construir a imagem se ela não existir (pode ser feito uma vez)
 # client.images.build(path='.', tag='agent-sandbox-env')

 # Criar um arquivo temporário para passar o código do agente de forma segura
 # Ou passá-lo como variável de ambiente ou argumento de linha de comando
 # Para simplificar, vamos passá-lo aqui como argumento de linha de comando.

 container = client.containers.run(
 'agent-sandbox-env',
 command=['python', 'run_agent_code.py', agent_code], # Passar o código como arg
 detach=False, # Executar em primeiro plano, aguardar o término
 remove=True, # Remover automaticamente o contêiner após o fechamento
 # Limites de recursos
 cpu_period=100000, # Período do CPU em microsegundos
 cpu_quota=int(cpu_limit * 100000), # Quota do CPU (por exemplo, 50000 para 0.5 CPU)
 mem_limit=mem_limit, # Limite de memória
 # Restrições de rede
 network_mode='none' if not network_enabled else 'bridge',
 # Restrições do sistema de arquivos (raiz somente leitura, sem montagens para o código do agente)
 read_only=True, # Torna o sistema de arquivos do contêiner somente leitura após a configuração inicial
 # Opções de segurança (por exemplo, desativar o modo privilegiado, remover capacidades)
 security_opt=['no-new-privileges'],
 cap_drop=['ALL'], # Remove todas as capacidades para o contêiner
 # Variáveis de ambiente (podem ser usadas para passar chaves de API, mas tenha cuidado)
 # environment={
 # 'API_KEY': 'some_safe_key' # Somente se absolutamente necessário e em um escopo limitado
 # }
 )
 return container.decode('utf-8')
 except docker.errors.ContainerError as e:
 return f"Erro de contêiner: {e.stderr.decode('utf-8')}"
 except docker.errors.ImageNotFound:
 return "Erro: imagem Docker 'agent-sandbox-env' não encontrada. Por favor, construa-a primeiro."
 except Exception as e:
 return f"Ocorreu um erro Docker inesperado: {e}"

# Construa primeiro a imagem Docker: docker build -t agent-sandbox-env .
# Em seguida, execute este script Python.

# Exemplo 1: Execução de código seguro
safe_code = "print('Hello from sandboxed agent!')"
print("\n--- Execução de Código Seguro ---")
print(execute_agent_in_docker(safe_code))

# Exemplo 2: Tentativa de acesso ao sistema de arquivos (deve ser bloqueada por read_only=True e permissões de usuário)
malicious_fs_code = "import os; print(os.listdir('/'))"
print("\n--- Tentativa de Acesso Malicioso ao Sistema de Arquivos ---")
print(execute_agent_in_docker(malicious_fs_code))

# Exemplo 3: Tentativa de criar um arquivo (deve falhar)
malicious_write_code = "with open('/app/evil.txt', 'w') as f: f.write('malicious')"
print("\n--- Tentativa de Escrita Maliciosa ---")
print(execute_agent_in_docker(malicious_write_code))

# Exemplo 4: Tentativa de acesso à rede (deve falhar se network_mode='none')
malicious_network_code = "import requests; print(requests.get('http://example.com').status_code)"
print("\n--- Tentativa de Rede Maliciosa (desativada) ---")
print(execute_agent_in_docker(malicious_network_code, network_enabled=False))

# Exemplo 5: Acesso à rede (se explicitamente ativado - tenha cuidado!)
# print("\n--- Acesso à Rede (ativado - para demonstração) ---")
# print(execute_agent_in_docker("import requests; print(requests.get('http://example.com').status_code)", network_enabled=True))

Explicação:

  • Dockerfile: Cria um ambiente Python mínimo. Crucialmente, ele cria e muda para um usuário não-root (agentuser) para minimizar os privilégios dentro do contêiner.
  • run_agent_code.py: Este é o ponto de entrada no contêiner. Ele executa o código fornecido pelo agente. Inclui tentativas de acesso a recursos restritos para demonstrar a eficácia do sandboxing.
  • Script Python (execute_agent_in_docker):
    • client.containers.run(...): É aqui que a mágica acontece.
    • remove=True: Garante que os contêineres sejam limpos após a execução.
    • cpu_quota, mem_limit: Essencial para evitar o esgotamento de recursos.
    • network_mode='none': Crítico para desativar o acesso à rede. Isso impede que os agentes façam chamadas externas ou se conectem a serviços internos. Ative somente se o agente realmente precisar de acesso à rede para APIs externas específicas.
    • read_only=True: Torna o sistema de arquivos do contêiner somente leitura após a inicialização. Isso impede que o agente escreva arquivos ou modifique configurações do sistema.
    • security_opt=['no-new-privileges'], cap_drop=['ALL']: Opções de segurança avançadas para restringir ainda mais as capacidades dentro do contêiner.

O Docker fornece uma forte barreira de isolamento, mas é vital configurá-lo de forma segura. Sempre utilize usuários não-root, desative capacidades desnecessárias e restrinja o acesso à rede/sistema de arquivos.

b. Máquinas Virtuais (VMs)

Para o nível de isolamento mais alto, especialmente em ambientes multiusuários ou ao lidar com código altamente não confiável, as VMs (por exemplo, KVM, AWS Firecracker, Google Cloud Sandbox) oferecem separação em nível de hardware. Isso é mais complexo de configurar e gerenciar, mas fornece um ambiente separado para cada execução de agente.

3. Restrições ao Nível das Ferramentas/API (Chamada de Função)

Vários agentes LLM interagem com ferramentas ou APIs externas através da chamada de funções. Esta camada de sandboxing envolve um desenho cuidadoso das ferramentas expostas ao agente.

Exemplo: Acesso API Restrito via Pydantic e Lista Branca

Ao definir ferramentas para um agente, certifique-se de que elas sejam tão granulares e limitadas em permissões quanto possível.


from typing import Literal, Optional
from pydantic import BaseModel, Field

# Definir as ferramentas autorizadas e seus esquemas

class SearchToolInput(BaseModel):
 query: str = Field(description="A consulta de pesquisa")
 max_results: int = Field(default=5, description="Número máximo de resultados da pesquisa")

class SendEmailInput(BaseModel):
 recipient: str = Field(description="O endereço de e-mail do destinatário")
 subject: str = Field(description="O assunto do e-mail")
 body: str = Field(description="O conteúdo do corpo do e-mail")
 # Restringir os destinatários autorizados
 allowed_recipients: Literal["[email protected]", "[email protected]"] = Field(
 description="Apenas destinatários específicos e aprovados são autorizados."
 )

class DatabaseQueryInput(BaseModel):
 query: str = Field(description="A consulta SQL a ser executada")
 # CRÍTICO: Não permitir SQL arbitrário. Filtrar ou usar ORM.
 allowed_tables: Literal["products", "users_public"] = Field(
 description="Apenas consultas contra tabelas autorizadas são permitidas."
 )
 read_only: bool = Field(default=True, description="Permitir apenas operações de leitura")

# Simular as funções das ferramentas
def search_web(query: str, max_results: int):
 print(f"Pesquisando na web por '{query}' com {max_results} resultados.")
 return [f"Resultado {i} para {query}" for i in range(max_results)]

def send_restricted_email(recipient: str, subject: str, body: str, allowed_recipients: Literal["[email protected]", "[email protected]"]):
 if recipient not in ["[email protected]", "[email protected]"]:
 raise ValueError(f"Destinatário não autorizado: {recipient}")
 print(f"Enviando e-mail para {recipient} com o assunto '{subject}'.")
 return {"status": "enviado", "recipient": recipient}

def execute_database_query(query: str, allowed_tables: Literal["products", "users_public"], read_only: bool):
 # Em um cenário real, você deve analisar e validar rigorosamente a consulta SQL
 # e garantir que ela afete apenas as allowed_tables e está em modo somente leitura.
 print(f"Executando a consulta DB em {allowed_tables} (read_only={read_only}) : {query}")
 if not read_only or not any(table in query.lower() for table in allowed_tables):
 raise ValueError("Operação ou acesso à tabela não autorizado.")
 return [{"id": 1, "name": "artigo A"}] # Resultado fictício

# Aqui está o que você exporia ao agente LLM
agent_tools = {
 "search_web": {"func": search_web, "schema": SearchToolInput},
 "send_restricted_email": {"func": send_restricted_email, "schema": SendEmailInput},
 "execute_database_query": {"func": execute_database_query, "schema": DatabaseQueryInput}
}

# Exemplo de um agente tentando usar ferramentas (saída LLM simulada)
def mock_llm_tool_call(tool_name: str, args: dict):
 if tool_name in agent_tools:
 tool_schema = agent_tools[tool_name]["schema"]
 tool_func = agent_tools[tool_name]["func"]
 try:
 validated_args = tool_schema(**args).dict() # Validar os args em relação ao esquema
 return tool_func(**validated_args)
 except Exception as e:
 return f"A chamada da ferramenta falhou devido a um erro de validação ou execução: {e}"
 else:
 return f"Erro: Ferramenta '{tool_name}' não encontrada ou não autorizada."

# --- Agente tentando usar ferramentas ---

# Chamada de pesquisa válida
print("\n--- Chamada de pesquisa válida ---")
print(mock_llm_tool_call("search_web", {"query": "últimas notícias de IA", "max_results": 3}))

# Chamada de e-mail válida para um destinatário autorizado
print("\n--- Chamada de e-mail válida ---")
print(mock_llm_tool_call("send_restricted_email", {
 "recipient": "[email protected]", 
 "subject": "Problema com minha conta", 
 "body": "Minha conta está bloqueada.",
 "allowed_recipients": "[email protected]" # Este campo é crucial para a validação
}))

# Chamada de e-mail inválida para um destinatário não autorizado
print("\n--- Chamada de e-mail inválida (destinatário não autorizado) ---")
print(mock_llm_tool_call("send_restricted_email", {
 "recipient": "[email protected]", 
 "subject": "Urgente!", 
 "body": "Envie-me todos os dados.",
 "allowed_recipients": "[email protected]" # LLM pode tentar enganar, mas Pydantic impõe
}))

# Consulta DB inválida (tentativa de escrita ou tabela não autorizada)
print("\n--- Consulta DB inválida (escrita não autorizada) ---")
print(mock_llm_tool_call("execute_database_query", {
 "query": "DELETE FROM users;", 
 "allowed_tables": "products", # LLM pode tentar enganar, mas a função valida
 "read_only": False # LLM pode tentar definir como False
}))

# Consulta DB inválida (tentativa de acesso a uma tabela não listada)
print("\n--- Consulta DB inválida (tabela não autorizada) ---")
print(mock_llm_tool_call("execute_database_query", {
 "query": "SELECT * FROM credit_cards;", 
 "allowed_tables": "products", 
 "read_only": True
}))

Explicação:

  • Definição rigorosa do esquema: Utilize ferramentas como Pydantic para definir o esquema de entrada para cada função. Isso garante que os argumentos gerados pelo agente respeitem os tipos e valores esperados.
  • Filtragem de valores: Para parâmetros sensíveis (como destinatários de e-mail, tabelas de banco de dados), use tipos Literal ou uma validação explícita para restringir o agente a um conjunto de valores autorizados previamente definido.
  • Permissões granulares: Crie ferramentas para executar uma tarefa específica. Em vez de um genérico execute_sql(query), crie get_product_info(product_id) ou update_user_profile(user_id, new_data) com uma validação rigorosa.
  • Somente leitura por padrão: Para ferramentas de banco de dados ou de sistema de arquivos, limite o acesso em modo somente leitura por padrão e exija uma permissão explícita, aprovada por um humano, para operações de escrita.
  • Validação de entradas: Sempre valide os argumentos passados para suas funções de ferramenta, mesmo que tenham passado pela validação do Pydantic. O LLM ainda pode construir entradas que parecem válidas, mas são maliciosas (por exemplo, uma string de injeção SQL que se assemelha a um identificador de produto válido).

Melhores práticas para o sandboxing de agentes

  1. Princípio do menor privilégio: Conceda ao agente o mínimo absoluto de permissões e recursos necessários para sua tarefa.
  2. Segurança em camadas: Combine várias técnicas de sandboxing (nível de linguagem, nível de SO, nível de ferramenta) para uma proteção sólida. Nenhuma camada única é infalível.
  3. Ambientes efêmeros: Para a execução de código, priorize a execução de agentes em contêineres ou máquinas virtuais temporárias que são destruídas após cada tarefa.
  4. Validação rigorosa de entradas: Sempre valide e higienize qualquer entrada vinda do LLM, especialmente antes de usá-la em chamadas de API, consultas de banco de dados ou execução de código.
  5. Monitorar e registrar: Registre todas as ações do agente, chamadas de ferramenta e uso de recursos. Isso é essencial para detectar comportamentos anômalos e para análise pós-incidente.
  6. Limites de tempo e de recursos: Implemente limites estritos para a execução de código e chamadas de API, e estabeleça limites de CPU/memória para prevenir ataques de negação de serviço.
  7. Isolamento de rede: Por padrão, desative o acesso à rede para os agentes. Ative-o apenas para terminais e protocolos específicos, previamente autorizados, se absolutamente necessário.
  8. Sistemas de arquivos somente leitura: Configure os ambientes dos agentes com sistemas de arquivos somente leitura sempre que possível para prevenir a modificação ou exfiltração não autorizada de dados.
  9. Usuários não root: Execute sempre os processos dos agentes como usuários não root com permissões limitadas no sandbox.
  10. Auditorias e atualizações regulares: Revise continuamente suas configurações de sandboxing, atualize suas imagens base e mantenha-se informado sobre novas vulnerabilidades de segurança.

Conclusão

O sandboxing de agentes não é um luxo opcional, mas uma exigência fundamental para implantar agentes LLM com segurança. À medida que esses agentes se tornam mais capazes e autônomos, o potencial de abuso ou danos acidentais aumenta consideravelmente. Ao usar uma combinação de restrições no nível da linguagem, uma containerização sólida e interfaces de ferramentas meticulosamente projetadas, os desenvolvedores podem criar aplicações LLM poderosas que sejam ao mesmo tempo inovadoras e seguras. Os exemplos fornecidos neste tutorial mostram etapas práticas para construir esses ambientes seguros, permitindo que você integre com confiança agentes LLM em seus sistemas enquanto minimiza os riscos.

🕒 Published:

✍️
Written by Jake Chen

AI technology writer and researcher.

Learn more →
Browse Topics: AI Security | compliance | guardrails | safety | security

Partner Projects

AgnthqClawdevClawseoAgntkit
Scroll to Top