Como Usar Ollama para Streaming de Respostas e Chamada de Ferramentas

Mark Ponomarev

Mark Ponomarev

16 agosto 2025

Como Usar Ollama para Streaming de Respostas e Chamada de Ferramentas

Apidog para empresas

Implantação local

SSO & RBAC

Conforme SOC 2

Explorar Apidog Enterprise

Este guia irá levá-lo através de como usar uma das novas e poderosas funcionalidades do Ollama: a capacidade de transmitir respostas (streaming) e chamar ferramentas (como funções ou APIs) em tempo real. Isso muda o jogo para construir aplicações de chat que parecem vivas e podem interagir com o mundo ao seu redor.

O que você aprenderá neste tutorial:

💡
Quer uma ótima ferramenta de Teste de API que gera documentação de API bonita?

Quer uma plataforma integrada e completa para sua equipe de desenvolvedores trabalhar em conjunto com máxima produtividade?

Apidog entrega todas as suas demandas e substitui o Postman por um preço muito mais acessível!
button

Primeiros Passos: O Que Você Vai Precisar

Para acompanhar, você precisará de algumas coisas:

Compreendendo Ideias Chave: Streaming e Chamadas de Ferramentas

Vamos detalhar o que queremos dizer com "streaming de respostas" e "chamada de ferramentas".

O Que É Streaming de Respostas?

Imagine que você está conversando com uma IA. Em vez de esperar que ela pense e digite a resposta inteira antes de você ver qualquer coisa, streaming significa que a IA envia a resposta para você pedaço por pedaço, palavra por palavra, conforme a gera. Isso torna a interação muito mais rápida e natural, como uma conversa real.

Com o Ollama, quando você habilita o streaming ("stream": true), você recebe essas atualizações incrementais.

Como Funciona a Chamada de Ferramentas?

A chamada de ferramentas permite que seus modelos de IA façam mais do que apenas gerar texto. Você pode definir "ferramentas" – que são essencialmente funções ou APIs externas – que a IA pode decidir usar para obter informações ou realizar ações.

Por exemplo, uma ferramenta poderia ser:

Você descreve essas ferramentas para o Ollama, e quando a IA determina que usar uma ferramenta ajudaria a responder à consulta do usuário, ela sinaliza sua intenção de chamar essa ferramenta com argumentos específicos. Sua aplicação então executa a ferramenta e pode enviar os resultados de volta para a IA para continuar a conversa.

Por Que Combinar Streaming com Chamada de Ferramentas?

A grande atualização do Ollama é que ele agora pode lidar com a chamada de ferramentas enquanto transmite respostas. Isso significa que sua aplicação pode:

  1. Receber texto inicial do modelo (transmitido).
  2. De repente, o stream pode indicar que uma chamada de ferramenta é necessária.
  3. Sua aplicação processa a chamada de ferramenta.
  4. Enquanto isso, o modelo pode até transmitir mais texto (por exemplo, "Ok, vou buscar o clima para você...").
  5. Assim que sua aplicação obtém o resultado da ferramenta, você pode enviá-lo de volta para o modelo, e ele continuará transmitindo sua resposta, agora informada pela saída da ferramenta.

Isso cria aplicações de IA altamente responsivas e capazes.

Quais Modelos Suportam Essas Funcionalidades?

Ollama habilitou isso para vários modelos populares, incluindo:

Como Fazer Sua Primeira Chamada de Ferramenta com Streaming Usando cURL

cURL é uma ótima maneira de testar rapidamente a API do Ollama. Vamos pedir o clima em Toronto.

Passo 1: Conceituando Sua Ferramenta

Nossa ferramenta será get_current_weather. Ela precisa de:

Passo 2: Construindo o Comando cURL

Abra seu terminal e prepare o seguinte comando. Vamos detalhá-lo:

curl <http://localhost:11434/api/chat> -d '{
  "model": "qwen3",
  "messages": [
    {
      "role": "user",
      "content": "What is the weather today in Toronto?"
    }
  ],
  "stream": true,
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_current_weather",
        "description": "Get the current weather for a location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The location to get the weather for, e.g. San Francisco, CA"
            },
            "format": {
              "type": "string",
              "description": "The format to return the weather in, e.g. \\\\\\\\'celsius\\\\\\\\' or \\\\\\\\'fahrenheit\\\\\\\\'",
              "enum": ["celsius", "fahrenheit"]
            }
          },
          "required": ["location", "format"]
        }
      }
    }
  ]
}'

Detalhes:

Passo 3: Executar e Observar a Saída

Pressione Enter. Você verá uma série de objetos JSON aparecerem um após o outro. Este é o stream!

Exemplos de trechos do stream:

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "Okay, " }, "done": false
}

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "I will " }, "done": false
}

{
  "model": "qwen3", "created_at": "...",
  "message": { "role": "assistant", "content": "try to get that for you." }, "done": false
}

(O modelo pode gerar alguns tokens de "pensamento" como <think>...celsius...</think> dependendo do seu processo interno, estes também fazem parte do stream)

Então, crucialmente, você pode ver algo assim:

{
  "model": "qwen3",
  "created_at": "2025-05-27T22:54:58.100509Z",
  "message": {
    "role": "assistant",
    "content": "", // O conteúdo pode estar vazio quando uma chamada de ferramenta é feita
    "tool_calls": [
      {
        "function": {
          "name": "get_current_weather",
          "arguments": { // Os argumentos que o modelo decidiu!
            "format": "celsius",
            "location": "Toronto"
          }
        }
      }
    ]
  },
  "done": false // Ainda não terminou, aguardando resultado da ferramenta
}

O Que Observar:

Em uma aplicação real, quando você vê um chunk tool_calls, seu código faria o seguinte:

  1. Pausar o processamento do stream (ou lidar com ele de forma assíncrona).
  2. Executar a função/API get_current_weather real com "Toronto" e "celsius".
  3. Obter o resultado (por exemplo, "20 graus Celsius").
  4. Enviar este resultado de volta para o Ollama em uma nova mensagem com role: "tool".
  5. O modelo então usará esta informação para continuar gerando sua resposta, também transmitida.

Como Transmitir Chamadas de Ferramentas Usando Python

Vamos implementar uma ideia similar em Python usando a biblioteca oficial do Ollama.

Passo 1: Instalando a Biblioteca Python do Ollama

Se você ainda não o fez, instale ou atualize a biblioteca:

pip install -U ollama

Passo 2: Definindo Sua Ferramenta e Codificando em Python

O SDK Python do Ollama permite inteligentemente que você passe funções Python diretamente como ferramentas. Ele inspeciona a assinatura da função e a docstring para criar o esquema para a IA.

Vamos criar um exemplo simples de ferramenta de matemática (a entrada usa add_two_numbers, mas o exemplo de saída mostra subtract_two_numbers sendo chamado pelo modelo. Manteremos o add_two_numbers fornecido para definição e deixaremos o modelo decidir o que fazer com base no prompt).

import ollama

# Define the python function that can be used as a tool
def add_two_numbers(a: int, b: int) -> int:
  """
  Add two numbers.

  Args:
    a (int): The first number as an int.
    b (int): The second number as an int.

  Returns:
    int: The sum of the two numbers.
  """
  print(f"--- Tool 'add_two_numbers' called with a={a}, b={b} ---")
  return a + b

# --- Main conversation logic ---
messages = [{'role': 'user', 'content': 'What is three plus one?'}]
# Or, for the subtraction example in the original output:
# messages = [{'role': 'user', 'content': 'what is three minus one?'}]

print(f"User: {messages[0]['content']}")

# Make the chat request with streaming and the tool
# Note: ChatResponse type hint might be ollama.ChatResponse or similar depending on library version
response_stream = ollama.chat(
  model='qwen3', # Or another capable model
  messages=messages,
  tools=[
      { # You can also define the tool explicitly if needed, or pass the function directly
          'type': 'function',
          'function': {
              'name': 'add_two_numbers', # Must match the Python function name if you want it to be called directly by your code later
              'description': 'Add two integer numbers together.',
              'parameters': {
                  'type': 'object',
                  'properties': {
                      'a': {'type': 'integer', 'description': 'The first number'},
                      'b': {'type': 'integer', 'description': 'The second number'}
                  },
                  'required': ['a', 'b']
              }
          }
      }
      # Simpler way for Python: pass the function directly if the library supports easy schema generation from it
      # tools=[add_two_numbers] # The SDK can often create the schema from this
  ],
  stream=True
)

print("Assistant (streaming):")
full_response_content = ""
tool_call_info = None

for chunk in response_stream:
  # Print the streamed content part
  if chunk['message']['content']:
    print(chunk['message']['content'], end='', flush=True)
    full_response_content += chunk['message']['content']

  # Check for tool calls in the chunk
  if 'tool_calls' in chunk['message'] and chunk['message']['tool_calls']:
    tool_call_info = chunk['message']['tool_calls'][0] # Assuming one tool call for simplicity
    print(f"\\\\n--- Detected Tool Call: {tool_call_info['function']['name']} ---")
    break # Stop processing stream for now, handle tool call

  if chunk.get('done'):
      print("\\\\n--- Stream finished ---")
      if not tool_call_info:
          print("No tool call was made.")

# --- If a tool call was detected, handle it ---
if tool_call_info:
  tool_name = tool_call_info['function']['name']
  tool_args = tool_call_info['function']['arguments']

  print(f"Arguments for the tool: {tool_args}")

  # Here, you'd actually call your Python tool function
  if tool_name == "add_two_numbers":
    # For safety, ensure arguments are of correct type if necessary
    try:
        arg_a = int(tool_args.get('a'))
        arg_b = int(tool_args.get('b'))
        tool_result = add_two_numbers(a=arg_a, b=arg_b)
        print(f"--- Tool execution result: {tool_result} ---")

        # Now, send this result back to Ollama to continue the conversation
        messages.append({'role': 'assistant', 'content': full_response_content, 'tool_calls': [tool_call_info]})
        messages.append({
            'role': 'tool',
            'content': str(tool_result), # Result must be a string
            'tool_call_id': tool_call_info.get('id', '') # If your library/model provides a tool_call_id
        })

        print("\\\\n--- Sending tool result back to model ---")

        follow_up_response_stream = ollama.chat(
            model='qwen3',
            messages=messages,
            stream=True
            # No tools needed here unless you expect another tool call
        )

        print("Assistant (after tool call):")
        for follow_up_chunk in follow_up_response_stream:
            if follow_up_chunk['message']['content']:
                print(follow_up_chunk['message']['content'], end='', flush=True)
            if follow_up_chunk.get('done'):
                print("\\\\n--- Follow-up stream finished ---")
                break
    except ValueError:
        print("Error: Could not parse tool arguments as integers.")
    except Exception as e:
        print(f"An error occurred during tool execution or follow-up: {e}")
  else:
    print(f"Error: Unknown tool '{tool_name}' requested by the model.")

Explicação do Código Python:

  1. Importa ollama.
  2. Função add_two_numbers: Esta é nossa ferramenta. A docstring e as type hints ajudam o Ollama a entender seu propósito e parâmetros.
  3. messages: Começamos a conversa com a consulta do usuário.
  4. ollama.chat(...):
  1. Iterando sobre response_stream:
  1. Lidando com a Chamada de Ferramenta:

Fluxo de Saída Esperado:Você verá a pergunta inicial do usuário, então a resposta do assistente sendo transmitida. Se ele decidir chamar add_two_numbers (ou subtract_two_numbers como no exemplo de saída do material original, se o prompt fosse para subtração), você verá a mensagem "Detected Tool Call", os argumentos, o resultado da sua função Python, e então o assistente continuando sua resposta usando esse resultado.

(A saída de exemplo original mostrava:

<think>
Okay, the user is asking ...
</think>

[ToolCall(function=Function(name='subtract_two_numbers', arguments={'a': 3, 'b': 1}))]

Isso indica o processo de "pensamento" interno da IA e então o objeto de chamada de ferramenta estruturado que o SDK Python fornece.)

Como Transmitir Chamadas de Ferramentas Usando JavaScript (Node.js)

Agora, vamos fazer o mesmo com JavaScript, tipicamente para um backend Node.js ou aplicação web.

Passo 1: Instalando a Biblioteca JavaScript do Ollama

No diretório do seu projeto, execute:

npm i ollama

Passo 2: Definindo o Esquema da Ferramenta e Codificando em JavaScript

Em JavaScript, você geralmente define o esquema da ferramenta como um objeto JSON.

import ollama from 'ollama';

// Describe the tool schema (e.g., for adding two numbers)
const addTool = {
    type: 'function',
    function: {
        name: 'addTwoNumbers',
        description: 'Add two numbers together',
        parameters: {
            type: 'object',
            required: ['a', 'b'],
            properties: {
                a: { type: 'number', description: 'The first number' },
                b: { type: 'number', description: 'The second number' }
            }
        }
    }
};

// Your actual JavaScript function that implements the tool
function executeAddTwoNumbers(a, b) {
    console.log(`--- Tool 'addTwoNumbers' called with a=${a}, b=${b} ---`);
    return a + b;
}

async function main() {
    const messages = [{ role: 'user', content: 'What is 2 plus 3?' }];
    console.log('User:', messages[0].content);

    console.log('Assistant (streaming):');
    let assistantResponseContent = "";
    let toolToCallInfo = null;

    try {
        const responseStream = await ollama.chat({
            model: 'qwen3', // Or another capable model
            messages: messages,
            tools: [addTool],
            stream: true
        });

        for await (const chunk of responseStream) {
            if (chunk.message.content) {
                process.stdout.write(chunk.message.content);
                assistantResponseContent += chunk.message.content;
            }
            if (chunk.message.tool_calls && chunk.message.tool_calls.length > 0) {
                toolToCallInfo = chunk.message.tool_calls[0]; // Assuming one tool call
                process.stdout.write(`\\\\n--- Detected Tool Call: ${toolToCallInfo.function.name} ---\\\\n`);
                break; // Stop processing stream to handle tool call
            }
            if (chunk.done) {
                process.stdout.write('\\\\n--- Stream finished ---\\\\n');
                if (!toolToCallInfo) {
                    console.log("No tool call was made.");
                }
                break;
            }
        }

        // --- If a tool call was detected, handle it ---
        if (toolToCallInfo) {
            const toolName = toolToCallInfo.function.name;
            const toolArgs = toolToCallInfo.function.arguments;

            console.log(`Arguments for the tool:`, toolArgs);

            let toolResult;
            if (toolName === 'addTwoNumbers') {
                toolResult = executeAddTwoNumbers(toolArgs.a, toolArgs.b);
                console.log(`--- Tool execution result: ${toolResult} ---`);

                // Append assistant's partial message and the tool message
                messages.push({
                    role: 'assistant',
                    content: assistantResponseContent, // Include content leading up to tool call
                    tool_calls: [toolToCallInfo]
                });
                messages.push({
                    role: 'tool',
                    content: toolResult.toString(), // Result must be a string
                    // tool_call_id: toolToCallInfo.id // If available and needed
                });

                console.log("\\\\n--- Sending tool result back to model ---");
                const followUpStream = await ollama.chat({
                    model: 'qwen3',
                    messages: messages,
                    stream: true
                });

                console.log("Assistant (after tool call):");
                for await (const followUpChunk of followUpStream) {
                    if (followUpChunk.message.content) {
                        process.stdout.write(followUpChunk.message.content);
                    }
                     if (followUpChunk.done) {
                        process.stdout.write('\\\\n--- Follow-up stream finished ---\\\\n');
                        break;
                    }
                }
            } else {
                console.error(`Error: Unknown tool '${toolName}' requested.`);
            }
        }

    } catch (error) {
        console.error('Error during Ollama chat:', error);
    }
}

main().catch(console.error);

Explicação do Código JavaScript:

  1. Importa ollama.
  2. Objeto addTool: Este é o esquema JSON que descreve nossa ferramenta para o Ollama.
  3. Função executeAddTwoNumbers: Nossa função JavaScript real que implementa a ferramenta.
  4. Função assíncrona main:
  1. Lidando com a Chamada de Ferramenta: Similar ao Python, se toolToCallInfo estiver definido:

Fluxo de Saída Esperado (similar aos exemplos de cURL e Python):Você verá a pergunta do usuário, então a resposta do assistente sendo transmitida. Quando ele decidir chamar addTwoNumbers, ele imprimirá as informações da chamada da ferramenta, o resultado da sua função JavaScript, e então continuará transmitindo a resposta da IA com base nesse resultado.

A saída de exemplo original para JS parecia com:

Question: What is 2 plus 3?
<think>
Okay, the user is asking...
</think>
Tool call: {
  function: {
    name: "addTwoNumbers",
    arguments: { a: 2, b: 3 },
  },
}

Como Ollama Lida com a Análise de Ferramentas Durante o Streaming

Você pode se perguntar como o Ollama consegue transmitir texto e identificar chamadas de ferramentas de forma tão suave. Ele usa um novo e inteligente parser incremental.

Por Que Isso É Melhor?

Dica: Melhorando o Desempenho com a Janela de Contexto

Para interações mais complexas, especialmente com chamada de ferramentas, o tamanho da "janela de contexto" que o modelo usa pode ser importante. Uma janela de contexto maior significa que o modelo lembra mais da conversa atual.

Exemplo: Definindo a Janela de Contexto com cURL(Use um modelo que suporte contextos maiores, como llama3.1 ou llama4, como sugerido no material original - embora o exemplo use llama3.2)

curl -X POST "<http://localhost:11434/api/chat>" -d '{
  "model": "llama3.1",
  "messages": [
    {
      "role": "user",
      "content": "why is the sky blue?"
    }
  ],
  "options": {
    "num_ctx": 32000
  }
}'

Experimente esta configuração se você achar que a chamada de ferramentas não é tão confiável quanto gostaria.

Para Onde Ir a Partir Daqui?

Você agora tem os fundamentos para construir aplicações de IA sofisticadas e em tempo real com o Ollama usando streaming de respostas e chamada de ferramentas!

Ideias para explorar:

Consulte a documentação oficial do Ollama e seu repositório GitHub (incluindo o "Tool Streaming Pull Request" mencionado na fonte original para mergulhos técnicos mais profundos) para as últimas atualizações e exemplos mais avançados.

💡
Quer uma ótima ferramenta de Teste de API que gera documentação de API bonita?

Quer uma plataforma integrada e completa para sua equipe de desenvolvedores trabalhar em conjunto com máxima produtividade?

Apidog entrega todas as suas demandas e substitui o Postman por um preço muito mais acessível!
button

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs

Como Usar Ollama para Streaming de Respostas e Chamada de Ferramentas