Uso de ferramentas

Uso de ferramentas (frequentemente chamado de function calling) é como um modelo de linguagem alcança além de sua própria caixa de texto: você lhe passa uma lista de funções que ele pode pedir para você executar, e ele decide quando usá-las. O modelo nunca executa nada por si próprio, apenas solicita uma chamada, e seu código faz o trabalho.
Um modelo por si só está selado em uma caixa. Ele não consegue verificar o clima de hoje, procurar um usuário em seu banco de dados ou enviar um e-mail. Ele só prediz texto baseado no que aprendeu, congelado no momento do treinamento, a mesma imagem de como funcionam os LLMs. O uso de ferramentas é como você deixa ele alcançar para fora da caixa, e é a base sobre a qual agentes são construídos.
A palavra para manter em mente é "pedir". O modelo lhe diz qual função ele quer e com quais argumentos. Seu código a executa e devolve o resultado. Você permanece no controle o tempo todo.
O loop
O uso de ferramentas é um vai e volta, não uma única chamada:
- Você envia a mensagem do usuário mais uma lista de ferramentas que o modelo pode usar.
- O modelo responde de uma de duas formas: uma resposta normal ou uma solicitação para chamar uma ferramenta, com os argumentos que ele quer.
- Se ele pediu uma ferramenta, seu código executa essa função e envia o resultado de volta para o modelo.
- O modelo usa o resultado para escrever sua resposta final.
Esse é todo o padrão. O modelo pede, seu código executa. Os passos 2 e 3 podem se repetir se o modelo precisar de várias ferramentas, o que é a semente de agentes alguns capítulos à frente.
O que está realmente acontecendo
O uso de ferramentas pode parecer que o modelo ganhou novos poderes, e não ganhou. Sob o capô, o modelo está fazendo a única coisa que sempre faz: predizer texto. Nada mudou sobre a caixa em que ele vive.
Quando você inclui definições de ferramentas na solicitação, você as está adicionando ao contexto a partir do qual o modelo prediz. O modelo foi treinado em exemplos onde, dadas ferramentas como estas, a continuação correta é às vezes uma mensagem especialmente formatada que significa "chame get_weather com cidade Rio de Janeiro" em vez de uma sentença normal. Então quando sua pergunta faz uma ferramenta parecer útil, a saída mais provável é essa mensagem de chamada de ferramenta estruturada, e a API a expõe para você como tool_calls.
O modelo não alcançou e executou nada. Ele predisse que uma chamada de ferramenta é o movimento certo e escreveu uma solicitação para uma, em um formato que seu código sabe como ler.
Então você, não o modelo, executa a função. Você pega o resultado real e o coloca de volta nas mensagens como mais contexto, e o modelo prediz sua resposta final a partir desse contexto ampliado. A característica inteira são duas predições ordinárias com seu código fazendo o trabalho real no meio.
Você é a ponte entre a caixa de texto do modelo e o mundo real. Mantenha essa imagem e o resto do capítulo, agentes incluídos, para de parecer misterioso: toda ação que uma IA toma é uma solicitação predita que seu código escolhe executar.
tool_calls. O modelo nunca executa nada, ele prediz uma solicitação. Seu código executa a função e alimenta o resultado de volta como mais contexto para a próxima predição, então você é a ponte entre o modelo e o mundo real. Definindo uma ferramenta
Você descreve cada ferramenta para o modelo: seu nome, o que ela faz e os argumentos que ela recebe. A descrição não é documentação para você, é uma instrução para o modelo sobre quando usar a ferramenta, então escreva como o prompt que é.
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city. Use when the user asks about weather.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "The city name, e.g. 'Rio de Janeiro'"},
},
"required": ["city"],
},
},
},
]O bloco parameters é um schema, a mesma ideia de saída estruturada: ele define os argumentos que o modelo deve produzir. Quando o modelo decide chamar get_weather, ele envia de volta um argumento city correspondendo a esta forma. Uma descrição vaga ("obtém clima") leva o modelo a usar a ferramenta nos momentos errados. Uma clara ("Use quando o usuário pergunta sobre clima") o guia bem.
parameters para seus argumentos. A descrição é realmente um prompt: ela diz ao modelo quando usar a ferramenta, então escreva com cuidado. O schema de parâmetros é o mesmo mecanismo que saída estruturada, constrangendo os argumentos que o modelo envia. Tratando a chamada
Quando o modelo quer uma ferramenta, a resposta contém tool_calls em vez de uma resposta final. Você lê a função solicitada e argumentos, executa a função real e envia o resultado de volta como uma mensagem tool. Então você chama o modelo novamente para que ele possa terminar.
import json
# implementação real da ferramenta
def get_weather(city):
# em uma app real isso chamaria uma API de clima; aqui fingimos
return {"city": city, "tempC": 18, "condition": "cloudy"}
messages = [{"role": "user", "content": "What's the weather in Rio de Janeiro?"}]
# 1. primeira chamada: ofereça as ferramentas
response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
tool_calls = response.choices[0].message.tool_calls
tool_call = tool_calls[0] if tool_calls else None
if tool_call:
# 2. execute a função solicitada com os argumentos do modelo
args = json.loads(tool_call.function.arguments)
result = get_weather(args["city"])
# 3. envie a solicitação do modelo e seu resultado de volta
messages.append(response.choices[0].message) # solicitação de ferramenta do assistente
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result),
})
# 4. chame novamente para que o modelo possa responder usando o resultado
response = client.chat.completions.create(model=MODEL, messages=messages)
print(response.choices[0].message.content)
# "It's currently 18 degrees and cloudy in Rio de Janeiro."Os argumentos chegam como uma string JSON, então você json.loads eles em um objeto real, depois verifica antes de usar. Você coloca duas mensagens no histórico: a solicitação de ferramenta do modelo e seu resultado tool, vinculados por tool_call_id. A chamada final transforma os dados brutos de clima em uma sentença natural.
tool_calls em vez de texto. Você analisa os argumentos de uma string JSON, executa a função real e coloca de volta duas mensagens: a solicitação do modelo e seu resultado, vinculadas por tool_call_id. Uma chamada de modelo final transforma seu resultado bruto em uma resposta natural. Na prática
O mesmo fluxo que uma função reutilizável. Perceba que a única coisa entre o modelo e seus sistemas é código que você escreveu e controla:
import json
tool_impls = {"get_weather": lambda args: get_weather(args["city"])}
def answer_with_tools(user_text):
messages = [{"role": "user", "content": user_text}]
response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
calls = response.choices[0].message.tool_calls
if not calls:
return response.choices[0].message.content # modelo respondeu diretamente
call = calls[0]
args = json.loads(call.function.arguments)
result = tool_impls[call.function.name](args)
messages.append(response.choices[0].message)
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
response = client.chat.completions.create(model=MODEL, messages=messages)
return response.choices[0].message.contentIsso trata uma chamada de ferramenta. Um modelo também pode solicitar várias ao mesmo tempo, chamadas paralelas de ferramenta, que você trataria fazendo um loop sobre cada entrada em tool_calls. E se você deixar o modelo continuar chamando ferramentas em um loop até que seja feito, decidindo seu próprio próximo passo a cada vez, você tem um agente, que é exatamente para onde o próximo capítulo vai.

