Skip to content

Latest commit

 

History

History
1462 lines (1210 loc) · 51.9 KB

File metadata and controls

1462 lines (1210 loc) · 51.9 KB

Section02 : Chamadas de Funções em Modelos de Linguagem Pequenos (SLMs)

Índice

  1. O que são Chamadas de Funções?
  2. Como Funcionam as Chamadas de Funções
  3. Cenários de Aplicação
  4. Configuração de Chamadas de Funções com Phi-4-mini e Ollama
  5. Trabalhando com Chamadas de Funções do Qwen3
  6. Integração Local com Foundry
  7. Melhores Práticas e Solução de Problemas
  8. Exemplos Avançados

O que são Chamadas de Funções?

Chamadas de funções são uma capacidade poderosa que permite que Modelos de Linguagem Pequenos (SLMs) interajam com ferramentas externas, APIs e serviços. Em vez de serem limitados aos seus dados de treinamento, os SLMs podem agora:

  • Conectar-se a APIs externas (serviços meteorológicos, bases de dados, motores de busca)
  • Executar funções específicas com base em solicitações dos utilizadores
  • Obter informações em tempo real de várias fontes
  • Realizar tarefas computacionais através de ferramentas especializadas
  • Encadear múltiplas operações para fluxos de trabalho complexos

Essa capacidade transforma os SLMs de geradores de texto estáticos em agentes de IA dinâmicos que podem realizar tarefas do mundo real.

Como Funcionam as Chamadas de Funções

O processo de chamadas de funções segue um fluxo de trabalho sistemático:

1. Integração de Ferramentas

  • Ferramentas Externas: Os SLMs podem conectar-se a APIs meteorológicas, bases de dados, serviços web e outros sistemas externos
  • Definições de Funções: Cada ferramenta é definida com parâmetros específicos, formatos de entrada/saída e descrições
  • Compatibilidade com APIs: As ferramentas são integradas através de interfaces padronizadas (APIs REST, SDKs, etc.)

2. Definição de Funções

As funções são definidas com três componentes principais:

{
  "name": "function_name",
  "description": "Clear description of what the function does",
  "parameters": {
    "parameter_name": {
      "description": "What this parameter represents",
      "type": "data_type",
      "default": "default_value"
    }
  }
}

3. Detecção de Intenção

  • Processamento de Linguagem Natural: O SLM analisa a entrada do utilizador para entender a intenção
  • Correspondência de Funções: Determina quais funções são necessárias para atender à solicitação
  • Extração de Parâmetros: Identifica e extrai os parâmetros necessários da mensagem do utilizador

4. Geração de Saída JSON

O SLM gera um JSON estruturado contendo:

  • Nome da função a ser chamada
  • Parâmetros necessários com valores apropriados
  • Contexto de execução e metadados

5. Execução Externa

  • Validação de Parâmetros: Garante que todos os parâmetros necessários estão presentes e formatados corretamente
  • Execução da Função: A aplicação executa a função especificada com os parâmetros fornecidos
  • Gestão de Erros: Lida com falhas, tempos de espera e respostas inválidas

6. Integração de Respostas

  • Processamento de Resultados: A saída da função é retornada ao SLM
  • Integração de Contexto: O SLM incorpora os resultados na sua resposta
  • Comunicação com o Utilizador: Apresenta as informações de forma natural e conversacional

Cenários de Aplicação

Recuperação de Dados

Converter consultas em linguagem natural em chamadas de API estruturadas:

  • "Mostra os meus pedidos recentes" → Consulta à base de dados com ID do utilizador e filtros de data
  • "Qual é o tempo em Tóquio?" → Chamada à API meteorológica com parâmetro de localização
  • "Encontra emails do João na semana passada" → Consulta ao serviço de email com remetente e filtros de data

Execução de Operações

Transformar solicitações dos utilizadores em chamadas de funções específicas:

  • "Agendar uma reunião para amanhã às 14h" → Integração com API de calendário
  • "Enviar uma mensagem para a equipa" → API de plataforma de comunicação
  • "Criar um backup dos meus ficheiros" → Operação no sistema de ficheiros

Tarefas Computacionais

Lidar com operações matemáticas ou lógicas complexas:

  • "Calcular juros compostos sobre $10.000 a 5% por 10 anos" → Função de cálculo financeiro
  • "Analisar este conjunto de dados para tendências" → Ferramentas de análise estatística
  • "Otimizar esta rota para entrega" → Algoritmos de otimização de rotas

Fluxos de Trabalho de Processamento de Dados

Encadear múltiplas chamadas de funções para operações complexas:

  1. Recuperar dados de várias fontes
  2. Analisar e validar as informações
  3. Transformar os dados no formato necessário
  4. Armazenar resultados em sistemas apropriados
  5. Gerar relatórios ou visualizações

Integração com UI/UX

Permitir atualizações dinâmicas na interface:

  • "Mostrar dados de vendas no painel" → Geração e exibição de gráficos
  • "Atualizar o mapa com novas localizações" → Integração de dados geoespaciais
  • "Atualizar a exibição de inventário" → Sincronização de dados em tempo real

Configuração de Chamadas de Funções com Phi-4-mini e Ollama

O Phi-4-mini da Microsoft suporta chamadas de funções únicas e paralelas através do Ollama. Veja como configurá-lo:

Pré-requisitos

  • Versão 0.5.13 ou superior do Ollama
  • Modelo Phi-4-mini (recomendado: phi4-mini:3.8b-fp16)

Passos de Instalação

1. Instalar e Executar Phi-4-mini

# Download the model (if not already present)
ollama run phi4-mini:3.8b-fp16

# Verify the model is available
ollama list

2. Criar Template de ModelFile Personalizado

Devido a limitações atuais nos templates padrão do Ollama, é necessário criar um ModelFile personalizado com o seguinte template:

TEMPLATE """
{{- if .Messages }}
{{- if or .System .Tools }}<|system|>
{{ if .System }}{{ .System }}
{{- end }}
In addition to plain text responses, you can chose to call one or more of the provided functions.
Use the following rule to decide when to call a function:
* if the response can be generated from your internal knowledge (e.g., as in the case of queries like "What is the capital of Poland?"), do so
* if you need external information that can be obtained by calling one or more of the provided functions, generate a function calls
If you decide to call functions:
* prefix function calls with functools marker (no closing marker required)
* all function calls should be generated in a single JSON list formatted as functools[{"name": [function name], "arguments": [function arguments as JSON]}, ...]
* follow the provided JSON schema. Do not hallucinate arguments or values. Do to blindly copy values from the provided samples
* respect the argument type formatting. E.g., if the type if number and format is float, write value 7 as 7.0
* make sure you pick the right functions that match the user intent
Available functions as JSON spec:
{{- if .Tools }}
{{ .Tools }}
{{- end }}<|end|>
{{- end }}
{{- range .Messages }}
{{- if ne .Role "system" }}<|{{ .Role }}|>
{{- if and .Content (eq .Role "tools") }}
{"result": {{ .Content }}}
{{- else if .Content }}
{{ .Content }}
{{- else if .ToolCalls }}
functools[
{{- range .ToolCalls }}{{ "{" }}"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}{{ "}" }}
{{- end }}]
{{- end }}<|end|>
{{- end }}
{{- end }}<|assistant|>
{{ else }}
{{- if .System }}<|system|>
{{ .System }}<|end|>{{ end }}{{ if .Prompt }}<|user|>
{{ .Prompt }}<|end|>{{ end }}<|assistant|>
{{ end }}{{ .Response }}{{ if .Response }}<|user|>{{ end }}
"""

3. Criar o Modelo Personalizado

# Save the template above as 'Modelfile' and run:
ollama create phi4-mini-fc:3.8b-fp16 -f ./Modelfile

Exemplo de Chamada de Função Única

import json
import requests

# Define the tool/function
tools = [
    {
        "name": "get_weather",
        "description": "Get current weather information for a location",
        "parameters": {
            "location": {
                "description": "The city or location name",
                "type": "str",
                "default": "New York"
            },
            "units": {
                "description": "Temperature units (celsius or fahrenheit)",
                "type": "str",
                "default": "celsius"
            }
        }
    }
]

# Create the message with system prompt including tools
messages = [
    {
        "role": "system",
        "content": "You are a helpful weather assistant",
        "tools": json.dumps(tools)
    },
    {
        "role": "user",
        "content": "What's the weather like in London today?"
    }
]

# Make request to Ollama API
response = requests.post(
    "http://localhost:11434/api/chat",
    json={
        "model": "phi4-mini-fc:3.8b-fp16",
        "messages": messages,
        "stream": False
    }
)

print(response.json())

Exemplo de Chamada de Funções Paralelas

import json
import requests

# Define multiple tools for parallel execution
AGENT_TOOLS = {
    "booking_flight": {
        "name": "booking_flight",
        "description": "Book a flight ticket",
        "parameters": {
            "departure": {
                "description": "Departure airport code",
                "type": "str"
            },
            "destination": {
                "description": "Destination airport code", 
                "type": "str"
            },
            "outbound_date": {
                "description": "Departure date (YYYY-MM-DD)",
                "type": "str"
            },
            "return_date": {
                "description": "Return date (YYYY-MM-DD)",
                "type": "str"
            }
        }
    },
    "booking_hotel": {
        "name": "booking_hotel",
        "description": "Book a hotel room",
        "parameters": {
            "city": {
                "description": "City name for hotel booking",
                "type": "str"
            },
            "check_in_date": {
                "description": "Check-in date (YYYY-MM-DD)",
                "type": "str"
            },
            "check_out_date": {
                "description": "Check-out date (YYYY-MM-DD)",
                "type": "str"
            }
        }
    }
}

SYSTEM_PROMPT = """
You are my travel agent with some tools available.
"""

messages = [
    {
        "role": "system",
        "content": SYSTEM_PROMPT,
        "tools": json.dumps(AGENT_TOOLS)
    },
    {
        "role": "user", 
        "content": "I need to travel from London to New York from March 21 2025 to March 27 2025. Please book both flight and hotel."
    }
]

# The model will generate parallel function calls
response = requests.post(
    "http://localhost:11434/api/chat",
    json={
        "model": "phi4-mini-fc:3.8b-fp16",
        "messages": messages,
        "stream": False
    }
)

print(response.json())

Trabalhando com Chamadas de Funções do Qwen3

O Qwen3 oferece capacidades avançadas de chamadas de funções com excelente desempenho e flexibilidade. Veja como implementá-lo:

Usando o Framework Qwen-Agent

O Qwen-Agent fornece um framework de alto nível que simplifica a implementação de chamadas de funções:

Instalação

pip install -U "qwen-agent[gui,rag,code_interpreter,mcp]"

Configuração Básica

import os
from qwen_agent.agents import Assistant

# Configure the LLM
llm_cfg = {
    'model': 'Qwen3-8B',
    # Option 1: Use Alibaba Model Studio
    'model_type': 'qwen_dashscope',
    'api_key': os.getenv('DASHSCOPE_API_KEY'),
    
    # Option 2: Use local deployment
    # 'model_server': 'http://localhost:8000/v1',
    # 'api_key': 'EMPTY',
    
    # Optional configuration for thinking mode
    'generate_cfg': {
        'thought_in_content': True,  # Include reasoning in response
    }
}

# Define tools using MCP (Model Context Protocol)
tools = [
    {
        'mcpServers': {
            'time': {
                'command': 'uvx',
                'args': ['mcp-server-time', '--local-timezone=Asia/Shanghai']
            },
            'fetch': {
                'command': 'uvx', 
                'args': ['mcp-server-fetch']
            }
        }
    },
    'code_interpreter',  # Built-in code execution tool
]

# Create the assistant
bot = Assistant(llm=llm_cfg, function_list=tools)

# Example usage
messages = [
    {
        'role': 'user', 
        'content': 'What time is it now? Also, fetch the latest news from https://example.com/news'
    }
]

# Generate response with function calling
for response in bot.run(messages=messages):
    print(response)

Implementação de Funções Personalizadas

Também é possível definir funções personalizadas para o Qwen3:

import json
from qwen_agent.tools.base import BaseTool

class WeatherTool(BaseTool):
    description = 'Get weather information for a specific location'
    parameters = [
        {
            'name': 'location',
            'type': 'string', 
            'description': 'City or location name',
            'required': True
        },
        {
            'name': 'units',
            'type': 'string',
            'description': 'Temperature units (celsius or fahrenheit)',
            'required': False,
            'default': 'celsius'
        }
    ]
    
    def call(self, params: str, **kwargs) -> str:
        """Execute the weather lookup"""
        params_dict = json.loads(params)
        location = params_dict.get('location')
        units = params_dict.get('units', 'celsius')
        
        # Simulate weather API call
        weather_data = {
            'location': location,
            'temperature': '22°C' if units == 'celsius' else '72°F',
            'condition': 'Partly cloudy',
            'humidity': '65%'
        }
        
        return json.dumps(weather_data)

# Use the custom tool
tools = [WeatherTool()]
bot = Assistant(llm=llm_cfg, function_list=tools)

messages = [{'role': 'user', 'content': 'What\'s the weather in Tokyo?'}]
response = bot.run(messages=messages)
print(list(response)[-1])

Recursos Avançados do Qwen3

Controle de Modo de Pensamento

O Qwen3 suporta alternância dinâmica entre modos de pensamento e não-pensamento:

# Enable thinking mode for complex reasoning
messages = [
    {
        'role': 'user',
        'content': '/think Solve this complex math problem: If a train travels 120 km in 1.5 hours, and another train travels 200 km in 2.5 hours, which train is faster and by how much?'
    }
]

# Disable thinking mode for simple queries
messages = [
    {
        'role': 'user', 
        'content': '/no_think What is the capital of France?'
    }
]

Chamadas de Funções em Múltiplos Passos

O Qwen3 é excelente para encadear múltiplas chamadas de funções:

# Complex workflow example
messages = [
    {
        'role': 'user',
        'content': '''
        I need to prepare for a business meeting:
        1. Check my calendar for conflicts tomorrow
        2. Get weather forecast for the meeting location (San Francisco)
        3. Find recent news about the client company (TechCorp)
        4. Calculate travel time from my office to their headquarters
        '''
    }
]

# Qwen3 will automatically determine the sequence of function calls needed

Integração Local com Foundry

O Foundry Local da Microsoft fornece uma API compatível com OpenAI para executar modelos localmente com maior privacidade e desempenho.

Configuração e Instalação

Windows

Faça o download do instalador na página de lançamentos do Foundry Local e siga as instruções de instalação.

macOS

brew tap microsoft/foundrylocal
brew install foundrylocal

Uso Básico

import openai
from foundry_local import FoundryLocalManager

# Initialize with model alias
alias = "phi-3.5-mini"  # Or any supported model
manager = FoundryLocalManager(alias)

# Create OpenAI client pointing to local endpoint
client = openai.OpenAI(
    base_url=manager.endpoint,
    api_key=manager.api_key
)

# Define functions for the model
functions = [
    {
        "name": "calculate_tax",
        "description": "Calculate tax amount based on income and rate",
        "parameters": {
            "type": "object",
            "properties": {
                "income": {
                    "type": "number",
                    "description": "Annual income amount"
                },
                "tax_rate": {
                    "type": "number", 
                    "description": "Tax rate as decimal (e.g., 0.25 for 25%)"
                }
            },
            "required": ["income", "tax_rate"]
        }
    }
]

# Make function calling request
response = client.chat.completions.create(
    model=manager.model_info.id,
    messages=[
        {
            "role": "user",
            "content": "Calculate the tax for someone earning $75,000 with a 22% tax rate"
        }
    ],
    functions=functions,
    function_call="auto"
)

print(response.choices[0].message.content)

Recursos Avançados do Foundry Local

Gestão de Modelos

# List available models
foundry model list

# Download specific model
foundry model download phi-3.5-mini

# Run model interactively
foundry model run phi-3.5-mini

# Remove model from cache
foundry model remove phi-3.5-mini

# Delete all cached models
foundry model remove "*"

Otimização de Desempenho

O Foundry Local seleciona automaticamente a melhor variante de modelo para o seu hardware:

  • CUDA GPU: Faz download de modelos otimizados para GPU
  • Qualcomm NPU: Utiliza variantes aceleradas por NPU
  • Apenas CPU: Seleciona modelos otimizados para CPU

Melhores Práticas e Solução de Problemas

Melhores Práticas para Definição de Funções

1. Nomes Claros e Descritivos

# Good
{
    "name": "get_stock_price",
    "description": "Retrieve current stock price for a given symbol"
}

# Avoid
{
    "name": "get_data", 
    "description": "Gets data"
}

2. Definições Abrangentes de Parâmetros

{
    "name": "send_email",
    "description": "Send an email message to specified recipients",
    "parameters": {
        "to": {
            "type": "array",
            "items": {"type": "string"},
            "description": "List of recipient email addresses",
            "required": True
        },
        "subject": {
            "type": "string",
            "description": "Email subject line",
            "required": True
        },
        "body": {
            "type": "string", 
            "description": "Email message content",
            "required": True
        },
        "priority": {
            "type": "string",
            "enum": ["low", "normal", "high"],
            "description": "Email priority level",
            "default": "normal",
            "required": False
        }
    }
}

3. Validação de Entrada e Gestão de Erros

def execute_function(function_name, parameters):
    try:
        # Validate required parameters
        if function_name == "send_email":
            if not parameters.get("to") or not parameters.get("subject"):
                return {"error": "Missing required parameters: to, subject"}
            
            # Validate email format
            email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
            for email in parameters["to"]:
                if not re.match(email_pattern, email):
                    return {"error": f"Invalid email format: {email}"}
        
        # Execute function logic
        result = perform_actual_function(function_name, parameters)
        return {"success": True, "data": result}
        
    except Exception as e:
        return {"error": str(e)}

Problemas Comuns e Soluções

Problema 1: Função Não Está Sendo Chamada

Sintomas: O modelo responde com texto em vez de chamar a função

Soluções:

  1. Verifique a descrição da função: Certifique-se de que corresponde claramente à intenção do utilizador
  2. Confirme as definições de parâmetros: Certifique-se de que todos os parâmetros necessários estão devidamente definidos
  3. Revise o prompt do sistema: Inclua instruções claras sobre quando usar funções
  4. Teste com solicitações explícitas: Experimente "Por favor, use a função meteorológica para obter dados de Londres"

Problema 2: Parâmetros Incorretos

Sintomas: Função chamada com parâmetros errados ou ausentes

Soluções:

  1. Adicione exemplos de parâmetros: Inclua valores de exemplo nas descrições dos parâmetros
  2. Use restrições de enumeração: Limite os valores dos parâmetros a opções específicas quando possível
  3. Implemente valores padrão: Forneça valores padrão sensatos para parâmetros opcionais
{
    "name": "book_restaurant",
    "parameters": {
        "cuisine": {
            "type": "string",
            "enum": ["italian", "chinese", "mexican", "american", "french"],
            "description": "Type of cuisine (example: 'italian' for Italian food)"
        },
        "party_size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "description": "Number of people (example: 4 for a family of four)"
        }
    }
}

Problema 3: Falhas em Chamadas de Funções Paralelas

Sintomas: Apenas uma função é executada quando várias deveriam ser

Soluções:

  1. Verifique o suporte do modelo: Certifique-se de que o modelo suporta chamadas de funções paralelas
  2. Atualize o prompt do sistema: Inclua "algumas ferramentas" ou "múltiplas ferramentas" na mensagem do sistema
  3. Use versões apropriadas do modelo: Phi-4-mini:3.8b-fp16 recomendado para Ollama

Problema 4: Problemas de Template com Ollama

Sintomas: Chamadas de funções não funcionam com a configuração padrão do Ollama

Soluções:

  1. Use ModelFile personalizado: Aplique o template corrigido fornecido neste tutorial
  2. Atualize o Ollama: Certifique-se de que está usando a versão 0.5.13 ou superior
  3. Verifique a quantização do modelo: Níveis de quantização mais altos (Q8_0, fp16) funcionam melhor do que versões altamente quantizadas

Otimização de Desempenho

1. Design Eficiente de Funções

  • Mantenha as funções focadas: Cada função deve ter um único propósito claro
  • Minimize dependências externas: Reduza chamadas de API e solicitações de rede sempre que possível
  • Cache de resultados: Armazene dados solicitados com frequência para melhorar os tempos de resposta

2. Operações em Lote e Assíncronas

import asyncio
import aiohttp

async def batch_function_calls(function_calls):
    """Execute multiple function calls concurrently"""
    async with aiohttp.ClientSession() as session:
        tasks = []
        for call in function_calls:
            if call["name"] == "fetch_url":
                task = fetch_url_async(session, call["parameters"]["url"])
                tasks.append(task)
        
        results = await asyncio.gather(*tasks)
        return results

async def fetch_url_async(session, url):
    async with session.get(url) as response:
        return await response.text()

3. Gestão de Recursos

  • Pooling de conexões: Reutilize conexões de bases de dados e APIs
  • Limitação de taxa: Implemente limitação de taxa adequada para APIs externas
  • Gestão de tempos de espera: Defina tempos de espera razoáveis para todas as chamadas externas

Exemplos Avançados

Sistema de Colaboração Multi-Agente

import json
from typing import List, Dict
from qwen_agent.agents import Assistant

class MultiAgentSystem:
    def __init__(self):
        # Research Agent
        self.research_agent = Assistant(
            llm={'model': 'Qwen3-8B', 'model_server': 'http://localhost:8000/v1'},
            function_list=[
                {'mcpServers': {'search': {'command': 'uvx', 'args': ['mcp-server-search']}}},
                {'mcpServers': {'fetch': {'command': 'uvx', 'args': ['mcp-server-fetch']}}}
            ]
        )
        
        # Analysis Agent
        self.analysis_agent = Assistant(
            llm={'model': 'Qwen3-8B', 'model_server': 'http://localhost:8000/v1'},
            function_list=['code_interpreter']
        )
        
        # Communication Agent
        self.comm_agent = Assistant(
            llm={'model': 'Qwen3-8B', 'model_server': 'http://localhost:8000/v1'},
            function_list=[self.create_email_tool(), self.create_slack_tool()]
        )
    
    def create_email_tool(self):
        """Custom email sending tool"""
        class EmailTool:
            name = "send_email"
            description = "Send email to specified recipients"
            parameters = {
                "to": {"type": "string", "description": "Recipient email"},
                "subject": {"type": "string", "description": "Email subject"},
                "body": {"type": "string", "description": "Email content"}
            }
            
            def call(self, params):
                # Implement actual email sending logic
                return f"Email sent successfully to {params['to']}"
        
        return EmailTool()
    
    def create_slack_tool(self):
        """Custom Slack messaging tool"""  
        class SlackTool:
            name = "send_slack"
            description = "Send message to Slack channel"
            parameters = {
                "channel": {"type": "string", "description": "Slack channel"},
                "message": {"type": "string", "description": "Message content"}
            }
            
            def call(self, params):
                # Implement actual Slack API call
                return f"Message sent to {params['channel']}"
        
        return SlackTool()
    
    async def process_complex_request(self, user_request: str):
        """Process complex multi-step requests using multiple agents"""
        
        # Step 1: Research phase
        research_prompt = f"Research the following topic and gather relevant information: {user_request}"
        research_results = []
        for response in self.research_agent.run([{'role': 'user', 'content': research_prompt}]):
            research_results.append(response)
        
        # Step 2: Analysis phase
        analysis_prompt = f"Analyze the following research data and provide insights: {research_results[-1]}"
        analysis_results = []
        for response in self.analysis_agent.run([{'role': 'user', 'content': analysis_prompt}]):
            analysis_results.append(response)
        
        # Step 3: Communication phase
        comm_prompt = f"Create a summary report and send it via email: {analysis_results[-1]}"
        comm_results = []
        for response in self.comm_agent.run([{'role': 'user', 'content': comm_prompt}]):
            comm_results.append(response)
        
        return {
            'research': research_results[-1],
            'analysis': analysis_results[-1], 
            'communication': comm_results[-1]
        }

# Usage example
async def main():
    system = MultiAgentSystem()
    
    request = """
    Analyze the impact of remote work on productivity in tech companies. 
    Research recent studies, analyze the data, and send a summary to our team.
    """
    
    results = await system.process_complex_request(request)
    print("Multi-agent processing complete:", results)

# Run the example
# asyncio.run(main())

Sistema de Seleção Dinâmica de Ferramentas

class DynamicToolSelector:
    def __init__(self):
        self.available_tools = {
            'weather': {
                'description': 'Get weather information',
                'domains': ['weather', 'temperature', 'forecast', 'climate'],
                'function': self.get_weather
            },
            'calculator': {
                'description': 'Perform mathematical calculations',
                'domains': ['math', 'calculate', 'compute', 'arithmetic'],
                'function': self.calculate
            },
            'web_search': {
                'description': 'Search the internet for information',
                'domains': ['search', 'find', 'lookup', 'research'],
                'function': self.web_search
            },
            'file_manager': {
                'description': 'Manage files and directories',
                'domains': ['file', 'directory', 'save', 'load', 'delete'],
                'function': self.manage_files
            }
        }
    
    def analyze_intent(self, user_input: str) -> List[str]:
        """Analyze user input to determine which tools might be needed"""
        user_words = user_input.lower().split()
        relevant_tools = []
        
        for tool_name, tool_info in self.available_tools.items():
            for domain in tool_info['domains']:
                if domain in user_words:
                    relevant_tools.append(tool_name)
                    break
        
        return relevant_tools
    
    def get_tool_definitions(self, tool_names: List[str]) -> List[Dict]:
        """Generate function definitions for selected tools"""
        definitions = []
        
        for tool_name in tool_names:
            if tool_name == 'weather':
                definitions.append({
                    'name': 'get_weather',
                    'description': 'Get current weather information',
                    'parameters': {
                        'location': {'type': 'string', 'description': 'City or location name'},
                        'units': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'default': 'celsius'}
                    }
                })
            elif tool_name == 'calculator':
                definitions.append({
                    'name': 'calculate',
                    'description': 'Perform mathematical calculations',
                    'parameters': {
                        'expression': {'type': 'string', 'description': 'Mathematical expression to evaluate'},
                        'precision': {'type': 'integer', 'default': 2, 'description': 'Decimal places for result'}
                    }
                })
            # Add more tool definitions as needed
        
        return definitions
    
    def get_weather(self, location: str, units: str = 'celsius') -> Dict:
        """Mock weather function"""
        return {
            'location': location,
            'temperature': '22°C' if units == 'celsius' else '72°F',
            'condition': 'Sunny',
            'humidity': '60%'
        }
    
    def calculate(self, expression: str, precision: int = 2) -> Dict:
        """Safe mathematical calculation"""
        try:
            # Simple evaluation for demo - in production, use a proper math parser
            import math
            allowed_names = {
                k: v for k, v in math.__dict__.items() if not k.startswith("__")
            }
            allowed_names.update({"abs": abs, "round": round})
            
            result = eval(expression, {"__builtins__": {}}, allowed_names)
            return {
                'expression': expression,
                'result': round(float(result), precision),
                'success': True
            }
        except Exception as e:
            return {
                'expression': expression,
                'error': str(e),
                'success': False
            }
    
    def web_search(self, query: str, max_results: int = 5) -> Dict:
        """Mock web search function"""
        return {
            'query': query,
            'results': [
                {'title': f'Result {i+1} for {query}', 'url': f'https://example{i+1}.com'}
                for i in range(max_results)
            ]
        }
    
    def manage_files(self, action: str, file_path: str, content: str = None) -> Dict:
        """Mock file management function"""
        return {
            'action': action,
            'file_path': file_path,
            'success': True,
            'message': f'Successfully {action}ed file: {file_path}'
        }

# Usage example
def smart_assistant_with_dynamic_tools():
    selector = DynamicToolSelector()
    
    user_requests = [
        "What's the weather like in New York and calculate 15% tip on $50?",
        "Search for recent AI developments and save the results to a file",
        "Calculate the area of a circle with radius 10 and check weather in Tokyo"
    ]
    
    for request in user_requests:
        print(f"\nUser Request: {request}")
        
        # Analyze which tools might be needed
        relevant_tools = selector.analyze_intent(request)
        print(f"Relevant Tools: {relevant_tools}")
        
        # Get function definitions for the LLM
        tool_definitions = selector.get_tool_definitions(relevant_tools)
        print(f"Tool Definitions: {len(tool_definitions)} functions available")
        
        # In a real implementation, you would pass these to your LLM
        # The LLM would then decide which functions to call and with what parameters

### Enterprise Integration Example

```python
import asyncio
import json
from typing import Dict, List, Any
from dataclasses import dataclass
from datetime import datetime

@dataclass
class FunctionResult:
    """Formato padrão de resultado para todas as chamadas de funções"""
    success: bool
    data: Any = None
    error: str = None
    execution_time: float = 0.0
    timestamp: datetime = None

class EnterpriseAIAgent:
    """Agente de IA pronto para produção com capacidades abrangentes de chamadas de funções"""
    
    def __init__(self, config: Dict):
        self.config = config
        self.functions = {}
        self.audit_log = []
        self.rate_limiters = {}
        
        # Inicializar funções principais de negócios
        self._register_core_functions()
    
    def _register_core_functions(self):
        """Registrar todas as funções de negócios disponíveis"""
        
        # Funções de CRM
        self.register_function(
            name="get_customer_info",
            description="Recuperar informações do cliente do CRM",
            parameters={
                "customer_id": {"type": "string", "required": True},
                "include_history": {"type": "boolean", "default": False}
            },
            handler=self._get_customer_info,
            rate_limit=100  # chamadas por minuto
        )
        
        # Funções de Vendas
        self.register_function(
            name="create_sales_opportunity",
            description="Criar uma nova oportunidade de vendas",
            parameters={
                "customer_id": {"type": "string", "required": True},
                "product_id": {"type": "string", "required": True},
                "estimated_value": {"type": "number", "required": True},
                "expected_close_date": {"type": "string", "required": True}
            },
            handler=self._create_sales_opportunity,
            rate_limit=50
        )
        
        # Funções de Análise
        self.register_function(
            name="generate_sales_report",
            description="Gerar relatório de desempenho de vendas",
            parameters={
                "period": {"type": "string", "enum": ["daily", "weekly", "monthly", "quarterly"]},
                "region": {"type": "string", "required": False},
                "product_category": {"type": "string", "required": False}
            },
            handler=self._generate_sales_report,
            rate_limit=10
        )
        
        # Funções de Notificação
        self.register_function(
            name="send_notification",
            description="Enviar notificação para membros da equipa",
            parameters={
                "recipients": {"type": "array", "items": {"type": "string"}},
                "message": {"type": "string", "required": True},
                "priority": {"type": "string", "enum": ["low", "medium", "high"], "default": "medium"},
                "channel": {"type": "string", "enum": ["email", "slack", "teams"], "default": "email"}
            },
            handler=self._send_notification,
            rate_limit=200
        )
    
    def register_function(self, name: str, description: str, parameters: Dict, 
                         handler: callable, rate_limit: int = 60):
        """Registrar uma nova função com o agente"""
        self.functions[name] = {
            'description': description,
            'parameters': parameters,
            'handler': handler,
            'rate_limit': rate_limit,
            'call_count': 0,
            'last_reset': datetime.now()
        }
    
    async def execute_function(self, function_name: str, parameters: Dict) -
Desculpe, mas não vejo o conteúdo do ficheiro Markdown que precisa de ser traduzido. Por favor, forneça o texto que deseja traduzir e seguirei as regras indicadas.
"""Executar uma função com tratamento abrangente de erros e registo"""

start_time = datetime.now()

try:
    # Validar se a função existe
    if function_name not in self.functions:
        return FunctionResult(
            success=False,
            error=f"Função '{function_name}' não encontrada",
            timestamp=start_time
        )
    
    # Verificar limites de taxa
    if not self._check_rate_limit(function_name):
        return FunctionResult(
            success=False,
            error=f"Limite de taxa excedido para a função '{function_name}'",
            timestamp=start_time
        )
    
    # Validar parâmetros
    validation_result = self._validate_parameters(function_name, parameters)
    if not validation_result.success:
        return validation_result
    
    # Executar a função
    func_info = self.functions[function_name]
    handler = func_info['handler']
    
    if asyncio.iscoroutinefunction(handler):
        result_data = await handler(**parameters)
    else:
        result_data = handler(**parameters)
    
    execution_time = (datetime.now() - start_time).total_seconds()
    
    result = FunctionResult(
        success=True,
        data=result_data,
        execution_time=execution_time,
        timestamp=start_time
    )
    
    # Registar execução bem-sucedida
    self._log_function_call(function_name, parameters, result)
    
    return result
    
except Exception as e:
    execution_time = (datetime.now() - start_time).total_seconds()
    result = FunctionResult(
        success=False,
        error=str(e),
        execution_time=execution_time,
        timestamp=start_time
    )
    
    # Registar execução falhada
    self._log_function_call(function_name, parameters, result)
    
    return result

def _check_rate_limit(self, function_name: str) -> bool:
    """Verificar se a chamada da função está dentro dos limites de taxa"""
    func_info = self.functions[function_name]
    now = datetime.now()
    
    # Reiniciar contador se tiver passado um minuto
    if (now - func_info['last_reset']).seconds >= 60:
        func_info['call_count'] = 0
        func_info['last_reset'] = now
    
    # Verificar se está abaixo do limite
    if func_info['call_count'] >= func_info['rate_limit']:
        return False
    
    func_info['call_count'] += 1
    return True

def _validate_parameters(self, function_name: str, parameters: Dict) -> FunctionResult:
    """Validar os parâmetros da função"""
    func_params = self.functions[function_name]['parameters']
    
    # Verificar parâmetros obrigatórios
    for param_name, param_info in func_params.items():
        if param_info.get('required', False) and param_name not in parameters:
            return FunctionResult(
                success=False,
                error=f"Parâmetro obrigatório em falta: {param_name}"
            )
    
    # Validar tipos e restrições dos parâmetros
    for param_name, value in parameters.items():
        if param_name in func_params:
            param_info = func_params[param_name]
            
            # Validação de tipo
            expected_type = param_info.get('type')
            if expected_type == 'string' and not isinstance(value, str):
                return FunctionResult(
                    success=False,
                    error=f"O parâmetro '{param_name}' deve ser uma string"
                )
            elif expected_type == 'number' and not isinstance(value, (int, float)):
                return FunctionResult(
                    success=False,
                    error=f"O parâmetro '{param_name}' deve ser um número"
                )
            elif expected_type == 'boolean' and not isinstance(value, bool):
                return FunctionResult(
                    success=False,
                    error=f"O parâmetro '{param_name}' deve ser um booleano"
                )
            
            # Validação de enumeração
            if 'enum' in param_info and value not in param_info['enum']:
                return FunctionResult(
                    success=False,
                    error=f"O parâmetro '{param_name}' deve ser um dos seguintes: {param_info['enum']}"
                )
    
    return FunctionResult(success=True)

def _log_function_call(self, function_name: str, parameters: Dict, result: FunctionResult):
    """Registar chamada de função para fins de auditoria"""
    log_entry = {
        'timestamp': result.timestamp.isoformat(),
        'function_name': function_name,
        'parameters': parameters,
        'success': result.success,
        'execution_time': result.execution_time,
        'error': result.error if not result.success else None
    }
    
    self.audit_log.append(log_entry)
    
    # Opcionalmente escrever para sistema de registo externo
    if self.config.get('enable_external_logging', False):
        self._write_to_external_log(log_entry)

def _write_to_external_log(self, log_entry: Dict):
    """Escrever entrada de registo para sistema de registo externo"""
    # A implementação dependeria da infraestrutura de registo
    # Exemplo: enviar para ELK stack, CloudWatch, etc.
    pass

# Implementações de Funções de Negócio
async def _get_customer_info(self, customer_id: str, include_history: bool = False) -> Dict:
    """Obter informações do cliente a partir do sistema CRM"""
    # Simular chamada a base de dados/API
    await asyncio.sleep(0.1)  # Simular atraso de rede
    
    customer_data = {
        'customer_id': customer_id,
        'name': 'John Doe',
        'email': 'john.doe@example.com',
        'phone': '+1-555-0123',
        'status': 'active',
        'tier': 'premium'
    }
    
    if include_history:
        customer_data['purchase_history'] = [
            {'date': '2024-01-15', 'product': 'Produto A', 'amount': 1500},
            {'date': '2024-03-22', 'product': 'Produto B', 'amount': 2300}
        ]
    
    return customer_data

async def _create_sales_opportunity(self, customer_id: str, product_id: str, 
                                    estimated_value: float, expected_close_date: str) -> Dict:
    """Criar uma nova oportunidade de vendas"""
    # Simular chamada à API do CRM
    await asyncio.sleep(0.2)
    
    opportunity_id = f"OPP-{datetime.now().strftime('%Y%m%d%H%M%S')}"
    
    return {
        'opportunity_id': opportunity_id,
        'customer_id': customer_id,
        'product_id': product_id,
        'estimated_value': estimated_value,
        'expected_close_date': expected_close_date,
        'status': 'open',
        'created_date': datetime.now().isoformat()
    }

async def _generate_sales_report(self, period: str, region: str = None, 
                                 product_category: str = None) -> Dict:
    """Gerar relatório abrangente de vendas"""
    # Simular agregação de dados
    await asyncio.sleep(0.5)
    
    return {
        'report_id': f"RPT-{datetime.now().strftime('%Y%m%d%H%M%S')}",
        'period': period,
        'region': region,
        'product_category': product_category,
        'total_sales': 125000.00,
        'total_opportunities': 45,
        'conversion_rate': 0.67,
        'top_products': [
            {'product_id': 'PROD-001', 'sales': 45000},
            {'product_id': 'PROD-002', 'sales': 32000}
        ],
        'generated_at': datetime.now().isoformat()
    }

async def _send_notification(self, recipients: List[str], message: str, 
                             priority: str = 'medium', channel: str = 'email') -> Dict:
    """Enviar notificação através do canal especificado"""
    # Simular chamada ao serviço de notificações
    await asyncio.sleep(0.1)
    
    notification_id = f"NOTIF-{datetime.now().strftime('%Y%m%d%H%M%S')}"
    
    return {
        'notification_id': notification_id,
        'recipients': recipients,
        'channel': channel,
        'priority': priority,
        'status': 'sent',
        'sent_at': datetime.now().isoformat()
    }

def get_function_definitions(self) -> List[Dict]:
    """Obter definições de funções compatíveis com OpenAI para todas as funções registadas"""
    definitions = []
    
    for func_name, func_info in self.functions.items():
        definition = {
            'name': func_name,
            'description': func_info['description'],
            'parameters': {
                'type': 'object',
                'properties': {},
                'required': []
            }
        }
        
        for param_name, param_info in func_info['parameters'].items():
            definition['parameters']['properties'][param_name] = {
                'type': param_info['type'],
                'description': param_info.get('description', '')
            }
            
            if 'enum' in param_info:
                definition['parameters']['properties'][param_name]['enum'] = param_info['enum']
            
            if 'default' in param_info:
                definition['parameters']['properties'][param_name]['default'] = param_info['default']
            
            if param_info.get('required', False):
                definition['parameters']['required'].append(param_name)
        
        definitions.append(definition)
    
    return definitions

# Exemplo de Utilização para Integração Empresarial
async def enterprise_demo():
    """Demonstrar capacidades de agentes de IA empresariais"""
    
    config = {
        'enable_external_logging': True,
        'max_concurrent_functions': 10,
        'default_timeout': 30
    }
    
    agent = EnterpriseAIAgent(config)
    
    # Exemplo 1: Processamento de consulta de cliente
    print("=== Processamento de Consulta de Cliente ===")
    
    # Obter informações do cliente
    result = await agent.execute_function(
        'get_customer_info',
        {'customer_id': 'CUST-12345', 'include_history': True}
    )
    
    if result.success:
        print(f"Informações do Cliente Obtidas: {result.data['name']}")
        print(f"Tempo de Execução: {result.execution_time:.3f}s")
    
    # Exemplo 2: Criação de oportunidade de vendas
    print("\n=== Criação de Oportunidade de Vendas ===")
    
    result = await agent.execute_function(
        'create_sales_opportunity',
        {
            'customer_id': 'CUST-12345',
            'product_id': 'PROD-001',
            'estimated_value': 15000.0,
            'expected_close_date': '2025-09-30'
        }
    )
    
    if result.success:
        print(f"Oportunidade Criada: {result.data['opportunity_id']}")
    
    # Exemplo 3: Operações em lote
    print("\n=== Operações em Lote ===")
    
    tasks = [
        agent.execute_function('generate_sales_report', {'period': 'monthly'}),
        agent.execute_function('send_notification', {
            'recipients': ['manager@company.com'],
            'message': 'Nova oportunidade criada',
            'priority': 'high',
            'channel': 'email'
        })
    ]
    
    results = await asyncio.gather(*tasks)
    
    for i, result in enumerate(results):
        if result.success:
            print(f"Tarefa {i+1} concluída com sucesso")
        else:
            print(f"Tarefa {i+1} falhou: {result.error}")
    
    # Exibir registo de auditoria
    print(f"\n=== Registo de Auditoria ({len(agent.audit_log)} entradas) ===")
    for entry in agent.audit_log[-3:]:  # Mostrar as últimas 3 entradas
        print(f"{entry['timestamp']}: {entry['function_name']} - {'SUCESSO' if entry['success'] else 'FALHA'}")

# Executar a demonstração empresarial
# asyncio.run(enterprise_demo())

## Conclusão

A chamada de funções em Small Language Models representa uma mudança de paradigma, transformando assistentes de IA estáticos em agentes dinâmicos e capazes de interagir com o mundo real. Este tutorial abordou:

### Principais Pontos

1. **Compreensão Fundamental**: A chamada de funções permite que os SLMs se expandam além dos seus dados de treino, conectando-se a ferramentas e serviços externos.

2. **Flexibilidade de Implementação**: Existem várias abordagens, desde implementações de baixo nível com templates personalizados até frameworks avançados como Qwen-Agent e Foundry Local.

3. **Considerações de Produção**: Implementações empresariais exigem atenção ao tratamento de erros, limites de taxa, segurança e registo de auditoria.

4. **Otimização de Desempenho**: Um design adequado de funções, execução eficiente e caching inteligente podem melhorar significativamente os tempos de resposta.

### Direções Futuras

À medida que a tecnologia SLM continua a evoluir, podemos esperar:

- **Melhoria na Precisão de Chamada de Funções**: Melhor deteção de intenções e extração de parâmetros
- **Processamento Paralelo Avançado**: Orquestração mais sofisticada de múltiplas funções
- **Padrões de Integração Melhorados**: Protocolos padronizados para integração de ferramentas
- **Recursos de Segurança Avançados**: Mecanismos aprimorados de autenticação e autorização
- **Expansão do Ecossistema**: Biblioteca crescente de funções e integrações pré-construídas

### Como Começar

Para começar a implementar chamadas de funções nos seus projetos:

1. **Comece Simples**: Inicie com cenários básicos de função única
2. **Escolha o Seu Framework**: Decida entre implementação direta (Ollama/Phi-4) ou assistida por framework (Qwen-Agent)
3. **Desenhe Funções com Cuidado**: Foque em definições de funções claras e bem documentadas
4. **Implemente Tratamento de Erros**: Construa um tratamento robusto de erros desde o início
5. **Escale Gradualmente**: Progrida de cenários simples para complexos à medida que ganha experiência

A chamada de funções transforma os SLMs de geradores de texto impressionantes em agentes de IA práticos, capazes de resolver problemas reais. Seguindo os padrões e práticas descritos neste tutorial, pode construir sistemas de IA poderosos e confiáveis que vão muito além das interfaces de chat tradicionais.

### Recursos e Referências
- **Modelos Phi-4**: [Coleção Hugging Face](https://huggingface.co/collections/microsoft/phi-4-677e9380e514feb5577a40e4)
- **Documentação Qwen3**: [Documentação Oficial Qwen](https://qwen.readthedocs.io/)
- **Ollama**: [Website Oficial](https://ollama.com/)
- **Foundry Local**: [Repositório GitHub](https://github.com/microsoft/Foundry-Local)
- **Melhores Práticas para Chamadas de Função**: [Guia Hugging Face](https://huggingface.co/docs/hugs/en/guides/function-calling)

Lembre-se de que chamadas de função são um campo em constante evolução, e manter-se atualizado com os últimos desenvolvimentos nos frameworks e modelos escolhidos ajudará a criar agentes de IA mais eficazes.


## ➡️ O que vem a seguir

- [03: Integração do Protocolo de Contexto de Modelo (MCP)](./03.IntroduceMCP.md)

---

**Aviso Legal**:  
Este documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, é importante notar que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas decorrentes da utilização desta tradução.