Ollama: Integração com SDK Python
O SDK Python é a chave que abre a porta para IA local — três linhas de código, transição perfeita de script para aplicação.
ollama) é essencialmente um wrapper da API REST do Ollama — o método chat() encapsula /api/chat, generate() encapsula /api/generate, e list() encapsula /api/tags. Entender essa relação ajuda na solução de problemas: quando o SDK lança um erro, você pode usar curl para chamar a API diretamente e determinar se é um problema do SDK ou do serviço Ollama.
📋 Pré-requisitos: Você precisa dominar o seguinte primeiro
- Aula 5: Fundamentos da API REST
1. O Que Você Vai Aprender
- Instalação da biblioteca ollama do Python e APIs síncronas/assíncronas
- Uso de
chat()/generate()/list() - Implementação de resposta streaming assíncrona para saída em tempo real
- Anotações de tipo Python e tratamento de erros
- Wrapper Python do SupportBot V1 da Alice
2. Uma História Real de Uma Fundadora de SaaS
0.0.0.0, informações de conexão do SDK (por exemplo, http://your-server:11434) podem ser exploradas por outros. Em produção, sempre adicione uma camada de autenticação.
(1) O Problema: Scripts Shell Não São Suficientes
Alice construiu um protótipo SupportBot com curl, mas scripts Shell são difíceis de manter para estado de conversa, tratamento de erros e integração com serviços web. Ela precisa de uma linguagem de programação adequada para construir aplicações de nível de produção.
(2) A Solução: Integração em Três Linhas com SDK Python
import ollama
response = ollama.chat(model='qwen2.5', messages=[
{'role': 'user', 'content': 'Hello'}
])
print(response['message']['content'])
3. Instalação e Visão Geral da API
ollama se conecta por padrão a http://localhost:11434, sem necessidade de configuração adicional. Se o Ollama estiver rodando em um endereço diferente, você pode sobrescrever definindo a variável de ambiente OLLAMA_HOST ou especificando Client(host='http://...') no código.
chat() do SDK Python é recomendado sobre generate() — chat suporta diálogo de múltiplas rodadas (array messages), enquanto generate suporta apenas rodada única. Mesmo para perguntas de uso único, a distinção de papel do chat (system/user/assistant) produz melhor qualidade de saída.
(1) Instalação e Verificação de Conexão
# Install the ollama Python package
pip install ollama
# Verify connection to Ollama server
python3 -c "import ollama; print(ollama.list())"
(2) Comparação API Síncrona vs Assíncrona
AsyncClient), todas as chamadas devem ser prefixadas com await, por exemplo, await client.chat(...). Esquecer await retorna um objeto coroutine em vez do resultado real — o programa não erro mas não produz saída correta. Em frameworks async como FastAPI, você deve usar a API assíncrona; caso contrário, bloqueia o loop de eventos e prejudica o desempenho concorrente.
| Dimensão | API Síncrona | API Assíncrona |
|---|---|---|
| Módulo | ollama |
ollama (AsyncClient) |
| Estilo de chamada | ollama.chat() |
await client.chat() |
| Bloqueio | Bloqueia thread atual | Não bloqueante, concorrente |
| Caso de uso | Scripts, ferramentas simples | Serviços web, processamento concorrente |
| Suporte streaming | for chunk in stream |
async for chunk in stream |
▶ Exemplo 1: Chamadas Básicas Síncronas e Assíncronas
import ollama
import asyncio
# Synchronous call
def sync_chat():
response = ollama.chat(
model='qwen2.5',
messages=[{'role': 'user', 'content': 'Hello!'}]
)
print(response['message']['content'])
# Asynchronous call
async def async_chat():
client = ollama.AsyncClient()
response = await client.chat(
model='qwen2.5',
messages=[{'role': 'user', 'content': 'Hello!'}]
)
print(response['message']['content'])
sync_chat()
asyncio.run(async_chat())
Saída:
# Function defined successfully
4. Aprofundamento nos Métodos Centrais
(1) Método chat()
| Parâmetro | Tipo | Descrição |
|---|---|---|
model |
str | Nome do Modelo |
messages |
list[dict] | Lista de mensagens, cada uma contendo role/content |
stream |
bool | Se a saída é streaming |
format |
str | Formato de saída: json |
options |
dict | Parâmetros de Inferência (temperature, etc.) |
keep_alive |
str | Tempo de retenção do Modelo na memória |
(2) Método generate()
| Parâmetro | Tipo | Descrição |
|---|---|---|
model |
str | Nome do Modelo |
prompt |
str | Texto do Prompt |
system |
str | System Prompt |
stream |
bool | Se é streaming |
options |
dict | Parâmetros de Inferência |
▶ Exemplo 2: Comparação chat vs generate
import ollama
# chat(): multi-turn with message history
response = ollama.chat(
model='qwen2.5',
messages=[
{'role': 'system', 'content': 'You are a SQL expert.'},
{'role': 'user', 'content': 'Write a query for top 5 customers'}
],
stream=False,
options={'temperature': 0.3}
)
print('chat:', response['message']['content'])
# generate(): single-shot text generation
response = ollama.generate(
model='qwen2.5',
prompt='Write a haiku about debugging',
system='You are a poet.',
stream=False
)
print('generate:', response['response'])
Saída:
chat:
generate:
5. Implementação de Resposta Streaming
(1) Princípio da Saída Streaming
sequenceDiagram
participant P as App Python
participant O as Servidor Ollama
P->>O: chat(stream=True)
loop Cada chunk de token
O-->>P: chunk {"content": "word"}
P->>P: print(word, end="")
end
O-->>P: chunk {"done": true}
▶ Exemplo 3: Saída Streaming Síncrona
import ollama
# Stream chat response in real-time
stream = ollama.chat(
model='qwen2.5',
messages=[{'role': 'user', 'content': 'Explain RAG in 3 sentences'}],
stream=True
)
for chunk in stream:
content = chunk['message']['content']
print(content, end='', flush=True)
print() # newline at end
Saída:
# Execution successful
▶ Exemplo 4: Saída Streaming Assíncrona
import ollama
import asyncio
async def stream_chat():
client = ollama.AsyncClient()
stream = await client.chat(
model='qwen2.5',
messages=[{'role': 'user', 'content': 'Tell me about Ollama'}],
stream=True
)
async for chunk in stream:
content = chunk['message']['content']
print(content, end='', flush=True)
print()
asyncio.run(stream_chat())
Saída:
# Function defined successfully
6. Tratamento de Erros e Anotações de Tipo
(1) Tipos Comuns de Erro
| Erro | Condição de Disparo | Tratamento |
|---|---|---|
ConnectionError |
Serviço Ollama não está rodando | Iniciar serviço ou retry |
ResponseError |
Modelo não encontrado / parâmetros inválidos | Verificar nome do Modelo e parâmetros |
TimeoutError |
Timeout de Inferência | Reduzir num_ctx ou aumentar timeout |
JSONDecodeError |
Anomalia na saída format=json | Adicionar validação JSON e retry |
▶ Exemplo 5: Tratamento de Erros Robusto
import ollama
import json
from typing import Optional
def safe_chat(
model: str,
messages: list[dict],
temperature: float = 0.3,
max_retries: int = 3
) -> Optional[str]:
"""Chat with error handling and retries."""
for attempt in range(max_retries):
try:
response = ollama.chat(
model=model,
messages=messages,
stream=False,
options={'temperature': temperature}
)
return response['message']['content']
except ConnectionError:
print(f"Connection failed (attempt {attempt + 1})")
if attempt == max_retries - 1:
return None
except ollama.ResponseError as e:
print(f"API error: {e.error}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
if attempt == max_retries - 1:
return None
return None
# Usage
result = safe_chat('qwen2.5', [
{'role': 'user', 'content': 'What is your return policy?'}
])
if result:
print(result)
else:
print("Failed to get response")
Saída:
Failed to get response
7. Exemplo Abrangente: Wrapper Python do SupportBot V1
# ============================================
# Comprehensive: SupportBot V1
# Python wrapper for e-commerce customer service
# ============================================
import ollama
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class SupportBot:
model: str = "qwen2.5"
temperature: float = 0.4
max_history: int = 10
system_prompt: str = (
"You are SupportBot, an e-commerce customer service agent. "
"Be polite, concise, and helpful. "
"If unsure, say 'Let me connect you with a human agent.'"
)
messages: list[dict] = field(default_factory=list)
def __post_init__(self):
self.messages = [
{"role": "system", "content": self.system_prompt}
]
def chat(self, user_input: str) -> str:
self.messages.append({"role": "user", "content": user_input})
try:
response = ollama.chat(
model=self.model,
messages=self.messages[-self.max_history:],
stream=False,
options={"temperature": self.temperature}
)
assistant_msg = response["message"]["content"]
self.messages.append({"role": "assistant", "content": assistant_msg})
return assistant_msg
except Exception as e:
self.messages.pop() # Remove failed user message
return f"Error: {str(e)}"
def stream_chat(self, user_input: str):
self.messages.append({"role": "user", "content": user_input})
full_response = []
try:
stream = ollama.chat(
model=self.model,
messages=self.messages[-self.max_history:],
stream=True,
options={"temperature": self.temperature}
)
for chunk in stream:
content = chunk["message"]["content"]
full_response.append(content)
print(content, end="", flush=True)
print()
self.messages.append({"role": "assistant", "content": "".join(full_response)})
except Exception as e:
print(f"\nError: {e}")
def reset(self):
self.messages = [{"role": "system", "content": self.system_prompt}]
# Usage
if __name__ == "__main__":
bot = SupportBot(model="qwen2.5", temperature=0.4)
print("=== SupportBot V1 ===")
print(bot.chat("Where is my order #12345?"))
print()
print(bot.chat("It has been 7 days since I ordered."))
print()
print(bot.chat("Can I get a refund instead?"))
=== SupportBot V1 ===
I'd be happy to check on your order #12345. Based on our records, your order is currently in transit and expected to arrive within 2-3 business days. You can track it at our website.
I understand your concern. If you'd prefer a refund instead of waiting, I can initiate that for you. Our refund policy covers orders that haven't been delivered within the estimated timeframe.
Yes, I can process a full refund for order #12345. The refund will be credited to your original payment method within 3-5 business days. Would you like me to proceed?
❓ Perguntas Frequentes
P: pip install ollama falha, o que devo fazer? R: Garanta Python >= 3.8 e pip atualizado:
pip install --upgrade pip. Para problemas de rede, use um espelho:pip install ollama -i https://pypi.tuna.tsinghua.edu.cn/simple.
P: Como escolher entre APIs síncronas e assíncronas? R: Use síncrona para scripts e ferramentas simples (mais simples). Use assíncrona para serviços web (FastAPI/Django) para evitar bloquear o loop de eventos. A diferença é mínima para cenários de usuário único.
P: Saída streaming não exibe no Jupyter Notebook, o que devo fazer? R: Jupyter tem suporte limitado para
flush=True. UseIPython.display.clear_outputcom atualizações em loop, ou mude para modo não-streaming.
P: Como especificar o endereço do servidor Ollama? R: Conexão padrão é localhost:11434. Altere via variável de ambiente
OLLAMA_HOST, ou instancieClient(host='http://...')no código.
P: O que acontece quando a lista messages fica muito longa? R: Conteúdo que excede a janela de contexto do Modelo (num_ctx) será truncado. Recomendamos implementar uma janela deslizante, mantendo apenas as últimas N rodadas, ou resumindo conversas anteriores.
P: Como obtenho velocidade de Inferência e outras estatísticas? R: Respostas não-streaming contêm campos
total_duration,eval_count,prompt_eval_count. O último chunk de respostas streaming contém essas estatísticas.
📖 Resumo
- Pacote
ollamado Python — instalação em uma linha, APIs síncronas e assíncronas disponíveis chat()é adequado para diálogo de múltiplas rodadas;generate()é adequado para geração single-shot- Saída streaming é implementada via
stream=True+ iteração sobre chunks para exibição em tempo real - Streaming assíncrono usa iteração
async for, adequado para concorrência em serviços web - Tratamento de erros deve cobrir ConnectionError, ResponseError e TimeoutError
- SupportBot V1 usa um dataclass para encapsular histórico de conversa e parâmetros de Inferência
📝 Exercícios
- Básico (Dificuldade ⭐): Use o SDK Python para implementar
chat()egenerate()uma vez cada, comparando diferenças na saída. - Intermediário (Dificuldade ⭐⭐): Implemente uma função de chat streaming com janela deslizante (mantendo as últimas 5 rodadas), descartando automaticamente as mensagens mais antigas quando a conversa excede 5 rodadas.
- Avançado (Dificuldade ⭐⭐⭐): Construa um SupportBot V1+ com retry de erros, controle de timeout e saída em formato JSON que salva conversas de atendimento em um arquivo e registra o tempo de Inferência de cada chamada.