Um agente de IA que só responde em texto tem utilidade limitada. O que torna um agente realmente útil é a capacidade de agir como por exemplo consultar uma agenda, registrar um pedido ou modificar um banco de dados. O ToolCallingAgent da biblioteca smolagents faz exatamente isso: permite que um modelo de linguagem chame funções Python como parte do seu raciocínio.

Neste artigo vamos construir um assistente de agendamento médico completo usando o ToolCallingAgent.

Clique aqui para abrir o código deste artigo no Google Colab

O que é o ToolCallingAgent

O ToolCallingAgent é um agente que age chamando ferramentas, que são meras funções Python que você define e expõe para ele. A cada turno da conversa, o agente decide qual ferramenta chamar, monta os argumentos e executa. O resultado da execução volta como observação, e o agente decide o próximo passo.

O fluxo interno é este:

Mensagem do usuário
    → Agente decide qual ferramenta chamar e com quais argumentos
    → Ferramenta é executada
    → Resultado volta como observação
    → Agente formula a resposta (ou chama mais uma ferramenta)
    → Resposta ao usuário

O ponto central é que o agente só pode agir pelas ferramentas que você definiu. Ele não tem acesso direto às suas variáveis, ao seu banco de dados, nem a nenhum sistema externo, a menos que você crie uma ferramenta para isso. Esse controle é intencional.

Configuração


!pip install -q "smolagents[litellm]"

from smolagents import ToolCallingAgent, LiteLLMModel
from google.colab import userdata

model = LiteLLMModel(
    model_id="gemini/gemini-2.5-flash",
    api_key=userdata.get("GOOGLE_API_KEY"),
    timeout=60
)

O Problema: Assistente de Agendamento Médico

Vamos construir um assistente para uma clínica fictícia. O assistente precisa ser capaz de:

  • Listar especialidades e médicos disponíveis
  • Verificar horários livres de um médico
  • Agendar consultas para pacientes
  • Mostrar um resumo dos agendamentos feitos

Primeiro, definimos os dados que o assistente vai manipular:


MEDICOS = {
    "Dr. Carlos Mendes": {"especialidade": "Cardiologia", "crm": "PE-12345"},
    "Dra. Ana Lima":     {"especialidade": "Dermatologia", "crm": "PE-67890"},
    "Dr. Pedro Souza":   {"especialidade": "Ortopedia",   "crm": "PE-11223"},
    "Dra. Julia Costa":  {"especialidade": "Pediatria",   "crm": "PE-44556"},
}

AGENDA = {
    "Dr. Carlos Mendes": ["08:00", "09:00", "14:00", "15:00"],
    "Dra. Ana Lima":     ["10:00", "11:00", "16:00"],
    "Dr. Pedro Souza":   ["08:00", "13:00", "14:00"],
    "Dra. Julia Costa":  ["09:00", "10:00", "11:00", "15:00"],
}

CONSULTAS_AGENDADAS = []

MEDICOS e AGENDA simulam um banco de dados. CONSULTAS_AGENDADAS acumula os agendamentos feitos e vai ser modificado diretamente pelas ferramentas quando o agente chamar agendar_consulta().

Definindo as Ferramentas com @tool

O decorator @tool transforma uma função Python comum em uma ferramenta que o agente pode chamar. Dois elementos são essenciais em cada ferramenta:

  • A docstring: o agente lê isso para decidir quando e como usar cada ferramenta
  • As type annotations: o agente usa isso para passar os argumentos no tipo correto

from smolagents import tool

@tool
def listar_especialidades() -> str:
    """
    Lista todas as especialidades médicas disponíveis na clínica
    e os respectivos médicos de cada especialidade.
    Use esta ferramenta quando o paciente perguntar quais médicos
    ou especialidades estão disponíveis.
    """
    resultado = "Especialidades disponíveis:\n"
    for medico, dados in MEDICOS.items():
        resultado += f"- {dados['especialidade']}: {medico}\n"
    return resultado


@tool
def verificar_horarios(nome_medico: str) -> str:
    """
    Retorna os horários disponíveis de um médico específico.
    Use esta ferramenta antes de agendar, para verificar
    se há horários livres.

    Args:
        nome_medico: Nome completo do médico conforme listado
                     em listar_especialidades().
    """
    if nome_medico not in AGENDA:
        return f"Médico '{nome_medico}' não encontrado. Use listar_especialidades() para ver os nomes corretos."
    horarios = AGENDA[nome_medico]
    if not horarios:
        return f"{nome_medico} não possui horários disponíveis no momento."
    return f"Horários disponíveis de {nome_medico}: {', '.join(horarios)}"


@tool
def agendar_consulta(nome_paciente: str, nome_medico: str, horario: str) -> str:
    """
    Agenda uma consulta para o paciente com o médico no horário indicado.
    Só chame esta ferramenta após confirmar com verificar_horarios()
    que o horário está disponível.

    Args:
        nome_paciente: Nome completo do paciente.
        nome_medico: Nome completo do médico.
        horario: Horário no formato HH:MM (ex: '09:00').
    """
    if nome_medico not in AGENDA:
        return f"Médico '{nome_medico}' não encontrado."
    if horario not in AGENDA[nome_medico]:
        return f"Horário {horario} não está disponível para {nome_medico}."

    AGENDA[nome_medico].remove(horario)
    especialidade = MEDICOS[nome_medico]["especialidade"]
    CONSULTAS_AGENDADAS.append({
        "paciente": nome_paciente,
        "medico": nome_medico,
        "especialidade": especialidade,
        "horario": horario
    })
    return (f"Consulta agendada com sucesso!\n"
            f"Paciente: {nome_paciente}\n"
            f"Médico: {nome_medico} ({especialidade})\n"
            f"Horário: {horario}")


@tool
def ver_agendamentos() -> str:
    """
    Mostra todas as consultas agendadas na sessão atual.
    Use no final do atendimento ou quando o paciente pedir
    um resumo do que foi marcado.
    """
    if not CONSULTAS_AGENDADAS:
        return "Nenhuma consulta agendada ainda."
    resultado = "Consultas agendadas:\n"
    for c in CONSULTAS_AGENDADAS:
        resultado += (f"- {c['paciente']} com {c['medico']} "
                      f"({c['especialidade']}) às {c['horario']}\n")
    return resultado

Note que agendar_consulta() remove o horário da AGENDA e adiciona um registro em CONSULTAS_AGENDADAS. Isso significa que o estado real do sistema muda quando o agente chama essa ferramenta.

Criando o Agente

Para criar o agente, basta instanciar a classe ToolCallingAgent. As funções que você definiu são passadas como uma lista para o parâmetro tools do construtor, e o modelo de linguagem que vai operar o agente é passado em model:


agente_clinica = ToolCallingAgent(
    tools=[listar_especialidades, verificar_horarios,
           agendar_consulta, ver_agendamentos],
    model=model
)

O smolagents monta automaticamente um system prompt a partir das ferramentas. Para inspecionar:


print(agente_clinica.system_prompt)

Cada ferramenta vira um bloco no system prompt com nome, descrição e parâmetros. É exatamente por isso que a docstring importa: ela é lida pelo modelo antes de qualquer interação. Esse ciclo de raciocínio ao observar, pensar e agir, segue de perto o padrão ReAct (Reasoning + Acting), que formaliza exatamente essa alternância entre pensamento e chamada de ferramenta.

Conversa em Múltiplos Turnos

O ToolCallingAgent suporta conversas de múltiplos turnos com reset=False. A partir da segunda mensagem, o agente mantém memória do que foi dito e feito anteriormente.

Turno 1 — O que está disponível?


from IPython.display import Markdown

resposta1 = agente_clinica.run(
    "Olá! Preciso marcar uma consulta. "
    "Quais especialidades vocês têm disponíveis?"
)
Markdown(resposta1)
Temos as seguintes especialidades disponíveis:

Cardiologia: Dr. Carlos Mendes
Dermatologia: Dra. Ana Lima
Ortopedia: Dr. Pedro Souza
Pediatria: Dra. Julia Costa
Qual especialidade ou médico você gostaria de agendar?

O agente chamou listar_especialidades() internamente e formatou a resposta para o paciente.

Turno 2 — Verificando horários


resposta2 = agente_clinica.run(
    "Quero Cardiologia. Quais horários o Dr. Carlos Mendes tem disponível?",
    reset=False
)
Markdown(resposta2)
Dr. Carlos Mendes tem os seguintes horários disponíveis: 08:00, 09:00, 14:00, 15:00. Qual horário você gostaria de agendar?

Turno 3 — Agendando


resposta3 = agente_clinica.run(
    "Pode marcar para Maria Silva às 14h.",
    reset=False
)
Markdown(resposta3)
Consulta agendada com sucesso para Maria Silva com Dr. Carlos Mendes (Cardiologia) às 14:00. Precisa de mais alguma coisa?

Turno 4 — Agendando mais uma e pedindo resumo

Neste turno, o agente recebe uma instrução que exige três ferramentas em sequência: verificar horários, agendar e mostrar o resumo.


resposta4 = agente_clinica.run(
    "Sim! Quero também marcar Pediatria para o João Silva. "
    "Verifique os horários disponíveis e marque às 10h. "
    "Depois me mostre o resumo de tudo que foi agendado.",
    reset=False
)
Markdown(resposta4)
Certo! Aqui está o resumo de tudo que foi agendado:

Maria Silva com Dr. Carlos Mendes (Cardiologia) às 14:00
João Silva com Dra. Julia Costa (Pediatria) às 10:00

Verificando o Estado Real

Para confirmar que as ferramentas realmente modificaram o sistema:

print("CONSULTAS AGENDADAS:", CONSULTAS_AGENDADAS)
print("AGENDA Dr. Carlos Mendes:", AGENDA["Dr. Carlos Mendes"])
print("AGENDA Dra. Julia Costa:", AGENDA["Dra. Julia Costa"])
CONSULTAS AGENDADAS: [{'paciente': 'Maria Silva', 'medico': 'Dr. Carlos Mendes', 'especialidade': 'Cardiologia', 'horario': '14:00'}, {'paciente': 'João Silva', 'medico': 'Dra. Julia Costa', 'especialidade': 'Pediatria', 'horario': '10:00'}]

AGENDA Dr. Carlos Mendes: ['08:00', '09:00', '15:00']
AGENDA Dra. Julia Costa:  ['09:00', '11:00', '15:00']

Os horários foram removidos da agenda. O estado do sistema mudou de verdade.

O Papel da Docstring

Escrever a docstring de uma ferramenta é, na prática, escrever um prompt porque é exatamente isso que vai acontecer: o smolagents usa o conteúdo das docstrings para montar o system prompt do agente. Cada ferramenta vira um bloco de instrução que o modelo lê antes de qualquer interação. A qualidade do comportamento do agente depende diretamente da qualidade do que está escrito ali.

Por isso, vale aplicar os mesmos princípios de quem escreve prompts: seja claro sobre o que a ferramenta faz, diga explicitamente quando ela deve ser usada, descreva os argumentos com precisão, e use exemplos quando o formato importa. Se a descrição for vaga ou ambígua, o agente pode chamar a ferramenta errada, na ordem errada, ou deixar de chamá-la quando deveria.

Compare as duas versões abaixo:

# Versão ruim — o agente não sabe quando nem como usar
@tool
def verificar_horarios(nome_medico: str) -> str:
    """Retorna horários."""
    ...

# Versão boa — o agente sabe quando usar e como passar o argumento
@tool
def verificar_horarios(nome_medico: str) -> str:
    """
    Retorna os horários disponíveis de um médico específico.
    Use esta ferramenta antes de agendar, para verificar
    se há horários livres.

    Args:
        nome_medico: Nome completo do médico conforme listado
                     em listar_especialidades().
    """
    ...

A versão boa faz três coisas além de descrever o que a função retorna: diz para qual finalidade ela serve (“antes de agendar”), diz quando acionar (“para verificar se há horários livres”), e orienta como passar o argumento corretamente (“conforme listado em listar_especialidades()”). Essa última referência é especialmente importante: ela ensina o agente a encadear as ferramentas na ordem certa, primeiro listar, depois verificar, depois agendar.

Erros nas ferramentas também voltam como observação. Se agendar_consulta() retornar uma mensagem de erro (horário indisponível, médico não encontrado), o agente lê esse retorno e pode tentar uma alternativa. O tratamento de erros dentro da ferramenta é responsabilidade sua, e mensagens de erro descritivas ajudam o agente a se recuperar com mais precisão.

Conclusão

O ToolCallingAgent permite que um modelo de linguagem interaja com sistemas reais de forma controlada. Você define quais ações existem, o agente decide quando e como usá-las.

O assistente de agendamento ilustra bem o padrão: o agente nunca acessa AGENDA diretamente, ele só age pelas ferramentas que você expôs. Esse controle é o que torna o ToolCallingAgent adequado para fluxos onde as ações têm consequências reais.

Código completo no Google Colab

Recursos Adicionais