---
title: Hooks de Chamada de Ferramenta
description: Aprenda a usar hooks de chamada de ferramenta para interceptar, modificar e controlar execução de ferramentas no CrewAI
mode: "wide"
---

Os Hooks de Chamada de Ferramenta fornecem controle fino sobre a execução de ferramentas durante operações do agente. Esses hooks permitem interceptar chamadas de ferramenta, modificar entradas, transformar saídas, implementar verificações de segurança e adicionar logging ou monitoramento abrangente.

## Visão Geral

Os hooks de ferramenta são executados em dois pontos críticos:
- **Antes da Chamada de Ferramenta**: Modificar entradas, validar parâmetros ou bloquear execução
- **Depois da Chamada de Ferramenta**: Transformar resultados, sanitizar saídas ou registrar detalhes de execução

## Tipos de Hook

### Hooks Antes da Chamada de Ferramenta

Executados antes de cada execução de ferramenta, esses hooks podem:
- Inspecionar e modificar entradas de ferramenta
- Bloquear execução de ferramenta com base em condições
- Implementar gates de aprovação para operações perigosas
- Validar parâmetros
- Registrar invocações de ferramenta

**Assinatura:**
```python
def before_hook(context: ToolCallHookContext) -> bool | None:
    # Retorne False para bloquear execução
    # Retorne True ou None para permitir execução
    ...
```

### Hooks Depois da Chamada de Ferramenta

Executados depois de cada execução de ferramenta, esses hooks podem:
- Modificar ou sanitizar resultados de ferramenta
- Adicionar metadados ou formatação
- Registrar resultados de execução
- Implementar validação de resultado
- Transformar formatos de saída

**Assinatura:**
```python
def after_hook(context: ToolCallHookContext) -> str | None:
    # Retorne string de resultado modificado
    # Retorne None para manter resultado original
    ...
```

## Contexto do Hook de Ferramenta

O objeto `ToolCallHookContext` fornece acesso abrangente ao estado de execução da ferramenta:

```python
class ToolCallHookContext:
    tool_name: str                    # Nome da ferramenta sendo chamada
    tool_input: dict[str, Any]        # Parâmetros de entrada mutáveis da ferramenta
    tool: CrewStructuredTool          # Referência da instância da ferramenta
    agent: Agent | BaseAgent | None   # Agente executando a ferramenta
    task: Task | None                 # Tarefa atual
    crew: Crew | None                 # Instância da crew
    tool_result: str | None           # Resultado da ferramenta (apenas hooks posteriores)
```

### Modificando Entradas de Ferramenta

**Importante:** Sempre modifique entradas de ferramenta in-place:

```python
# ✅ Correto - modificar in-place
def sanitize_input(context: ToolCallHookContext) -> None:
    context.tool_input['query'] = context.tool_input['query'].lower()

# ❌ Errado - substitui referência do dict
def wrong_approach(context: ToolCallHookContext) -> None:
    context.tool_input = {'query': 'nova consulta'}
```

## Métodos de Registro

### 1. Registro Baseado em Decoradores (Recomendado)

Use decoradores para sintaxe mais limpa:

```python
from crewai.hooks import before_tool_call, after_tool_call

@before_tool_call
def block_dangerous_tools(context):
    """Bloqueia ferramentas perigosas."""
    dangerous_tools = ['delete_database', 'drop_table', 'rm_rf']
    if context.tool_name in dangerous_tools:
        print(f"⛔ Ferramenta perigosa bloqueada: {context.tool_name}")
        return False  # Bloquear execução
    return None

@after_tool_call
def sanitize_results(context):
    """Sanitiza resultados."""
    if context.tool_result and "password" in context.tool_result.lower():
        return context.tool_result.replace("password", "[CENSURADO]")
    return None
```

### 2. Hooks com Escopo de Crew

Registre hooks para uma instância específica de crew:

```python
from crewai import CrewBase
from crewai.project import crew
from crewai.hooks import before_tool_call_crew, after_tool_call_crew

@CrewBase
class MyProjCrew:
    @before_tool_call_crew
    def validate_tool_inputs(self, context):
        # Aplica-se apenas a esta crew
        if context.tool_name == "web_search":
            if not context.tool_input.get('query'):
                print("❌ Consulta de busca inválida")
                return False
        return None
    
    @after_tool_call_crew
    def log_tool_results(self, context):
        # Logging de ferramenta específico da crew
        print(f"✅ {context.tool_name} concluída")
        return None
    
    @crew
    def crew(self) -> Crew:
        return Crew(
            agents=self.agents,
            tasks=self.tasks,
            process=Process.sequential,
            verbose=True
        )
```

## Casos de Uso Comuns

### 1. Guardrails de Segurança

```python
@before_tool_call
def safety_check(context: ToolCallHookContext) -> bool | None:
    """Bloqueia ferramentas que podem causar danos."""
    destructive_tools = [
        'delete_file',
        'drop_table',
        'remove_user',
        'system_shutdown'
    ]
    
    if context.tool_name in destructive_tools:
        print(f"🛑 Ferramenta destrutiva bloqueada: {context.tool_name}")
        return False
    
    # Avisar em operações sensíveis
    sensitive_tools = ['send_email', 'post_to_social_media', 'charge_payment']
    if context.tool_name in sensitive_tools:
        print(f"⚠️  Executando ferramenta sensível: {context.tool_name}")
    
    return None
```

### 2. Gate de Aprovação Humana

```python
@before_tool_call
def require_approval_for_actions(context: ToolCallHookContext) -> bool | None:
    """Requer aprovação para ações específicas."""
    approval_required = [
        'send_email',
        'make_purchase',
        'delete_file',
        'post_message'
    ]
    
    if context.tool_name in approval_required:
        response = context.request_human_input(
            prompt=f"Aprovar {context.tool_name}?",
            default_message=f"Entrada: {context.tool_input}\nDigite 'sim' para aprovar:"
        )
        
        if response.lower() != 'sim':
            print(f"❌ Execução de ferramenta negada: {context.tool_name}")
            return False
    
    return None
```

### 3. Validação e Sanitização de Entrada

```python
@before_tool_call
def validate_and_sanitize_inputs(context: ToolCallHookContext) -> bool | None:
    """Valida e sanitiza entradas."""
    # Validar consultas de busca
    if context.tool_name == 'web_search':
        query = context.tool_input.get('query', '')
        if len(query) < 3:
            print("❌ Consulta de busca muito curta")
            return False
        
        # Sanitizar consulta
        context.tool_input['query'] = query.strip().lower()
    
    # Validar caminhos de arquivo
    if context.tool_name == 'read_file':
        path = context.tool_input.get('path', '')
        if '..' in path or path.startswith('/'):
            print("❌ Caminho de arquivo inválido")
            return False
    
    return None
```

### 4. Sanitização de Resultado

```python
@after_tool_call
def sanitize_sensitive_data(context: ToolCallHookContext) -> str | None:
    """Sanitiza dados sensíveis."""
    if not context.tool_result:
        return None
    
    import re
    result = context.tool_result
    
    # Remover chaves de API
    result = re.sub(
        r'(api[_-]?key|token)["\']?\s*[:=]\s*["\']?[\w-]+',
        r'\1: [CENSURADO]',
        result,
        flags=re.IGNORECASE
    )
    
    # Remover endereços de email
    result = re.sub(
        r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
        '[EMAIL-CENSURADO]',
        result
    )
    
    # Remover números de cartão de crédito
    result = re.sub(
        r'\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b',
        '[CARTÃO-CENSURADO]',
        result
    )
    
    return result
```

### 5. Análise de Uso de Ferramenta

```python
import time
from collections import defaultdict

tool_stats = defaultdict(lambda: {'count': 0, 'total_time': 0, 'failures': 0})

@before_tool_call
def start_timer(context: ToolCallHookContext) -> None:
    context.tool_input['_start_time'] = time.time()
    return None

@after_tool_call
def track_tool_usage(context: ToolCallHookContext) -> None:
    start_time = context.tool_input.get('_start_time', time.time())
    duration = time.time() - start_time
    
    tool_stats[context.tool_name]['count'] += 1
    tool_stats[context.tool_name]['total_time'] += duration
    
    if not context.tool_result or 'error' in context.tool_result.lower():
        tool_stats[context.tool_name]['failures'] += 1
    
    print(f"""
    📊 Estatísticas da Ferramenta {context.tool_name}:
    - Execuções: {tool_stats[context.tool_name]['count']}
    - Tempo Médio: {tool_stats[context.tool_name]['total_time'] / tool_stats[context.tool_name]['count']:.2f}s
    - Falhas: {tool_stats[context.tool_name]['failures']}
    """)
    
    return None
```

### 6. Limitação de Taxa

```python
from collections import defaultdict
from datetime import datetime, timedelta

tool_call_history = defaultdict(list)

@before_tool_call
def rate_limit_tools(context: ToolCallHookContext) -> bool | None:
    """Limita taxa de chamadas de ferramenta."""
    tool_name = context.tool_name
    now = datetime.now()
    
    # Limpar entradas antigas (mais antigas que 1 minuto)
    tool_call_history[tool_name] = [
        call_time for call_time in tool_call_history[tool_name]
        if now - call_time < timedelta(minutes=1)
    ]
    
    # Verificar limite de taxa (máximo 10 chamadas por minuto)
    if len(tool_call_history[tool_name]) >= 10:
        print(f"🚫 Limite de taxa excedido para {tool_name}")
        return False
    
    # Registrar esta chamada
    tool_call_history[tool_name].append(now)
    return None
```

### 7. Logging de Debug

```python
@before_tool_call
def debug_tool_call(context: ToolCallHookContext) -> None:
    """Debug de chamada de ferramenta."""
    print(f"""
    🔍 Debug de Chamada de Ferramenta:
    - Ferramenta: {context.tool_name}
    - Agente: {context.agent.role if context.agent else 'Desconhecido'}
    - Tarefa: {context.task.description[:50] if context.task else 'Desconhecida'}...
    - Entrada: {context.tool_input}
    """)
    return None

@after_tool_call
def debug_tool_result(context: ToolCallHookContext) -> None:
    """Debug de resultado de ferramenta."""
    if context.tool_result:
        result_preview = context.tool_result[:200]
        print(f"✅ Preview do Resultado: {result_preview}...")
    else:
        print("⚠️  Nenhum resultado retornado")
    return None
```

## Gerenciamento de Hooks

### Desregistrando Hooks

```python
from crewai.hooks import (
    unregister_before_tool_call_hook,
    unregister_after_tool_call_hook
)

# Desregistrar hook específico
def my_hook(context):
    ...

register_before_tool_call_hook(my_hook)
# Mais tarde...
success = unregister_before_tool_call_hook(my_hook)
print(f"Desregistrado: {success}")
```

### Limpando Hooks

```python
from crewai.hooks import (
    clear_before_tool_call_hooks,
    clear_after_tool_call_hooks,
    clear_all_tool_call_hooks
)

# Limpar tipo específico de hook
count = clear_before_tool_call_hooks()
print(f"Limpou {count} hooks antes")

# Limpar todos os hooks de ferramenta
before_count, after_count = clear_all_tool_call_hooks()
print(f"Limpou {before_count} hooks antes e {after_count} hooks depois")
```

## Padrões Avançados

### Execução Condicional de Hook

```python
@before_tool_call
def conditional_blocking(context: ToolCallHookContext) -> bool | None:
    """Bloqueia apenas em condições específicas."""
    # Bloquear apenas para agentes específicos
    if context.agent and context.agent.role == "junior_agent":
        if context.tool_name in ['delete_file', 'send_email']:
            print(f"❌ Agentes júnior não podem usar {context.tool_name}")
            return False
    
    # Bloquear apenas durante tarefas específicas
    if context.task and "sensível" in context.task.description.lower():
        if context.tool_name == 'web_search':
            print("❌ Busca na web bloqueada para tarefas sensíveis")
            return False
    
    return None
```

### Modificação de Entrada com Consciência de Contexto

```python
@before_tool_call
def enhance_tool_inputs(context: ToolCallHookContext) -> None:
    """Adiciona contexto baseado no papel do agente."""
    # Adicionar contexto baseado no papel do agente
    if context.agent and context.agent.role == "researcher":
        if context.tool_name == 'web_search':
            # Adicionar restrições de domínio para pesquisadores
            context.tool_input['domains'] = ['edu', 'gov', 'org']
    
    # Adicionar contexto baseado na tarefa
    if context.task and "urgente" in context.task.description.lower():
        if context.tool_name == 'send_email':
            context.tool_input['priority'] = 'high'
    
    return None
```

## Melhores Práticas

1. **Mantenha Hooks Focados**: Cada hook deve ter uma responsabilidade única
2. **Evite Computação Pesada**: Hooks executam em cada chamada de ferramenta
3. **Trate Erros Graciosamente**: Use try-except para prevenir falhas de hooks
4. **Use Type Hints**: Aproveite `ToolCallHookContext` para melhor suporte IDE
5. **Documente Condições de Bloqueio**: Deixe claro quando/por que ferramentas são bloqueadas
6. **Teste Hooks Independentemente**: Teste unitário de hooks antes de usar em produção
7. **Limpe Hooks em Testes**: Use `clear_all_tool_call_hooks()` entre execuções de teste
8. **Modifique In-Place**: Sempre modifique `context.tool_input` in-place, nunca substitua
9. **Registre Decisões Importantes**: Especialmente ao bloquear execução de ferramenta
10. **Considere Performance**: Cache validações caras quando possível

## Tratamento de Erros

```python
@before_tool_call
def safe_validation(context: ToolCallHookContext) -> bool | None:
    try:
        # Sua lógica de validação
        if not validate_input(context.tool_input):
            return False
    except Exception as e:
        print(f"⚠️ Erro no hook: {e}")
        # Decida: permitir ou bloquear em erro
        return None  # Permitir execução apesar do erro
```

## Segurança de Tipos

```python
from crewai.hooks import ToolCallHookContext, BeforeToolCallHookType, AfterToolCallHookType

# Anotações de tipo explícitas
def my_before_hook(context: ToolCallHookContext) -> bool | None:
    return None

def my_after_hook(context: ToolCallHookContext) -> str | None:
    return None

# Registro type-safe
register_before_tool_call_hook(my_before_hook)
register_after_tool_call_hook(my_after_hook)
```

## Solução de Problemas

### Hook Não Está Executando
- Verifique se hook está registrado antes da execução da crew
- Verifique se hook anterior retornou `False` (bloqueia execução e hooks subsequentes)
- Garanta que assinatura do hook corresponda ao tipo esperado

### Modificações de Entrada Não Funcionam
- Use modificações in-place: `context.tool_input['key'] = value`
- Não substitua o dict: `context.tool_input = {}`

### Modificações de Resultado Não Funcionam
- Retorne a string modificada dos hooks posteriores
- Retornar `None` mantém o resultado original
- Garanta que a ferramenta realmente retornou um resultado

### Ferramenta Bloqueada Inesperadamente
- Verifique todos os hooks antes por condições de bloqueio
- Verifique ordem de execução do hook
- Adicione logging de debug para identificar qual hook está bloqueando

## Conclusão

Os Hooks de Chamada de Ferramenta fornecem capacidades poderosas para controlar e monitorar execução de ferramentas no CrewAI. Use-os para implementar guardrails de segurança, gates de aprovação, validação de entrada, sanitização de resultado, logging e análise. Combinados com tratamento adequado de erros e segurança de tipos, os hooks permitem sistemas de agentes seguros e prontos para produção com observabilidade abrangente.

