🎯 O que vamos construir
Neste tutorial, você criará um assistente que recebe perguntas pelo terminal, consulta um modelo de IA e salva as conversas em SQLite. Ao reiniciar o programa, ele recuperará as mensagens recentes para continuar a conversa.
Você também adicionará comandos para limpar a conversa e encerrar o programa.
Tempo estimado de leitura: 8 minutos. A execução das etapas pode levar mais tempo.
🧠 A teoria mínima necessária
Nosso projeto terá três peças:
- Python: coordena a entrada do usuário, a consulta à IA e o armazenamento.
- API de IA: recebe mensagens e devolve uma resposta. Pense nela como um serviço acessado por um balcão de atendimento.
- SQLite: guarda o histórico em um arquivo local, sem exigir um servidor de banco de dados.
Para a IA acompanhar a conversa, enviaremos algumas mensagens anteriores junto com cada pergunta. É como entregar um resumo do atendimento antes de fazer uma nova solicitação.
Isso não treina o modelo: apenas fornece contexto naquela chamada.
📌 Nota / Dica: Este assistente responde perguntas. Ele não executa comandos do sistema nem modifica arquivos por solicitação da IA.
🧱 Passo 1: prepare o ambiente
Você precisará de Python 3.10 ou superior e de uma chave de API da OpenAI com acesso a um modelo compatível.
Crie a pasta e o ambiente virtual:
mkdir assistente-terminal
cd assistente-terminal
python -m venv .venv
-
mkdircria a pasta do projeto. -
cdentra nessa pasta. -
python -m venvcria um ambiente isolado para instalar as dependências.
Ative o ambiente no Linux ou macOS:
source .venv/bin/activate
-
sourceaplica a configuração do ambiente ao terminal atual.
No Windows, usando PowerShell:
.\.venv\Scripts\Activate.ps1
- O script
Activate.ps1ativa o mesmo ambiente no PowerShell.
Instale o SDK, a biblioteca oficial usada para conversar com a API:
python -m pip install openai
-
python -m pipinstala o pacote no Python do ambiente ativo. -
sqlite3já acompanha as distribuições usuais do Python.
Configure a chave no Linux ou macOS:
export OPENAI_API_KEY="sua-chave"
-
exportdisponibiliza a chave para os programas iniciados nesse terminal.
No PowerShell:
$env:OPENAI_API_KEY="sua-chave"
-
$env:define uma variável de ambiente na sessão atual.
⚠️ Atenção / Erro Comum: A API pode gerar cobranças por uso. Uma assinatura do ChatGPT não substitui o acesso à API. Não coloque sua chave no código nem publique o histórico de comandos que a contenha.
🧱 Passo 2: crie a memória em SQLite
Crie um arquivo chamado assistente.py. Adicione os blocos seguintes, na ordem apresentada.
Comece pelas importações e configurações:
import os
import sqlite3
from contextlib import closing
from openai import OpenAI, OpenAIError
BANCO = "conversas.db"
MODELO = os.getenv("OPENAI_MODEL", "gpt-4.1-mini")
LIMITE_HISTORICO = 12
-
oslê configurações do ambiente. -
sqlite3acessa o banco. -
closinggarante o fechamento da conexão ao sair de um bloco. -
OpenAIé o cliente da API;OpenAIErrorrepresenta erros dessa biblioteca. -
BANCOdefine o arquivo local. -
MODELOaceita uma configuração externa e usa um valor padrão. -
LIMITE_HISTORICOlimita quantas mensagens anteriores serão enviadas.
Se o modelo padrão não estiver disponível na sua conta, configure OPENAI_MODEL com outro modelo compatível com Chat Completions.
Agora crie a estrutura do banco:
def preparar_banco():
with closing(sqlite3.connect(BANCO)) as conexao:
with conexao:
conexao.execute("""
CREATE TABLE IF NOT EXISTS mensagens (
id INTEGER PRIMARY KEY,
papel TEXT NOT NULL,
conteudo TEXT NOT NULL
)
""")
-
connectabre o arquivo ou o cria, caso ainda não exista. -
with conexaoconfirma a operação quando ela termina com sucesso. -
CREATE TABLE IF NOT EXISTScria a tabela somente quando necessário. -
ididentifica e ordena as mensagens. -
papelregistra quem escreveu;conteudoguarda o texto.
SQLite funciona como um caderno organizado: cada linha representa uma mensagem.
🧱 Passo 3: salve e recupere conversas
Vamos salvar a pergunta e a resposta juntas:
def salvar_turno(pergunta, resposta):
with closing(sqlite3.connect(BANCO)) as conexao:
with conexao:
conexao.executemany(
"INSERT INTO mensagens (papel, conteudo) VALUES (?, ?)",
[("user", pergunta), ("assistant", resposta)],
)
-
salvar_turnorecebe os dois lados de uma interação. -
executemanyexecuta a inserção para cada par de valores. -
useridentifica o usuário;assistant, a IA. - Os marcadores
?passam os valores separadamente do SQL. - A transação mantém as duas mensagens juntas: se a gravação falhar, nenhuma delas é confirmada.
⚠️ Atenção / Erro Comum: Não monte consultas SQL concatenando texto digitado pelo usuário. Use parâmetros como
?, inclusive quando a entrada parecer inofensiva.
Em seguida, recupere as mensagens recentes:
def carregar_historico():
with closing(sqlite3.connect(BANCO)) as conexao:
linhas = conexao.execute(
"""
SELECT papel, conteudo
FROM mensagens
ORDER BY id DESC
LIMIT ?
""",
(LIMITE_HISTORICO,),
).fetchall()
return [
{"role": papel, "content": conteudo}
for papel, conteudo in reversed(linhas)
]
-
ORDER BY id DESCbusca primeiro as mensagens mais novas. -
LIMIT ?restringe a quantidade recuperada. - A vírgula em
(LIMITE_HISTORICO,)cria uma tupla com um elemento. -
fetchallreúne os resultados. -
reversedrestaura a ordem cronológica. -
roleecontentsão os campos esperados pela API.
O banco manterá todo o histórico, mas enviaremos apenas as últimas 12 mensagens. Esse limite reduz o contexto e ajuda a controlar custos, embora não limite o tamanho de cada mensagem.
🧱 Passo 4: conecte o assistente à IA
Adicione a função responsável pela resposta:
def perguntar_ia(cliente, pergunta):
mensagens = [
{
"role": "system",
"content": (
"Você é um assistente de terminal. "
"Responda em português, com clareza e objetividade."
),
},
*carregar_historico(),
{"role": "user", "content": pergunta},
]
resultado = cliente.chat.completions.create(
model=MODELO,
messages=mensagens,
)
resposta = resultado.choices[0].message.content
if not resposta:
raise ValueError("A API não retornou uma resposta de texto.")
return resposta
- A mensagem
systemorienta o comportamento do assistente. -
*carregar_historico()inclui as mensagens anteriores na lista. - A última mensagem contém a pergunta atual.
-
createenvia a solicitação ao modelo. -
choices[0]acessa a primeira alternativa retornada. - A verificação evita salvar uma resposta vazia.
📌 Nota / Dica: As mensagens recuperadas do SQLite serão enviadas ao provedor da API. Evite inserir senhas, chaves ou informações confidenciais.
🧱 Passo 5: adicione os comandos do terminal
Primeiro, implemente a limpeza do histórico:
def limpar_historico():
with closing(sqlite3.connect(BANCO)) as conexao:
with conexao:
conexao.execute("DELETE FROM mensagens")
-
DELETE FROM mensagensremove todas as linhas da tabela. - A tabela continua disponível para novas conversas.
- Esse comando limpa o histórico usado pelo aplicativo; não representa apagamento seguro do arquivo em disco.
Agora adicione o fluxo principal:
def main():
if not os.getenv("OPENAI_API_KEY"):
print("Configure a variável OPENAI_API_KEY antes de iniciar.")
return
preparar_banco()
cliente = OpenAI(timeout=30.0, max_retries=1)
print("Assistente pronto! Comandos: /limpar e /sair")
while True:
pergunta = input("\nVocê: ").strip()
if not pergunta:
continue
if pergunta == "/sair":
break
if pergunta == "/limpar":
limpar_historico()
print("Histórico limpo.")
continue
try:
resposta = perguntar_ia(cliente, pergunta)
salvar_turno(pergunta, resposta)
except (OpenAIError, ValueError) as erro:
print(f"Não foi possível obter a resposta: {erro}")
continue
print(f"\nAssistente: {resposta}")
- A verificação inicial detecta a ausência da chave.
-
timeoutlimita a espera de cada tentativa;max_retriespermite uma nova tentativa em falhas elegíveis. -
while Truemantém a conversa funcionando. -
stripremove espaços nas extremidades. -
continueinicia a próxima interação;breakencerra o laço. -
tryeexcepttratam falhas da API e respostas vazias. - O histórico só é salvo após uma resposta válida.
Finalize o arquivo:
if __name__ == "__main__":
try:
main()
except (KeyboardInterrupt, EOFError):
print("\nAté mais!")
except sqlite3.Error as erro:
print(f"Erro no banco de dados: {erro}")
- A condição executa
mainquando o arquivo é iniciado diretamente. -
KeyboardInterruptpermite sair comCtrl+C. -
EOFErrortrata o encerramento da entrada. -
sqlite3.Errorapresenta falhas de armazenamento.
🔍 Passo 6: teste a memória
Execute na pasta do projeto:
python assistente.py
- O comando inicia o programa e cria
conversas.dbnessa pasta.
Faça este teste:
- Digite: “Estou aprendendo Python e quero criar uma lista de tarefas”.
- Encerre com
/sair. - Execute novamente e pergunte: “Qual projeto eu quero criar?”.
- Use
/limpare repita a pergunta.
Antes da limpeza, o assistente deve conseguir usar a conversa anterior. Depois, aquela informação deixa de integrar o contexto enviado.
⚠️ Atenção / Erro Comum: O caminho do banco é relativo à pasta de execução. Iniciar o programa em outra pasta pode criar outro arquivo e dar a impressão de que a memória desapareceu.
🛠️ Tabela de Sintaxe
| Comando/Propriedade | O que faz | Exemplo |
|---|---|---|
sqlite3.connect |
Abre o banco | sqlite3.connect(BANCO) |
execute |
Executa SQL | conexao.execute(sql, valores) |
LIMIT |
Restringe resultados | LIMIT 12 |
/limpar |
Apaga a conversa salva | /limpar |
/sair |
Encerra o programa | /sair |
💪 Faça você mesmo
-
Personalize o assistente: altere a mensagem
systempara criar um tutor de Python. Teste como a orientação muda as respostas. -
Crie o comando
/historico: mostre as mensagens retornadas porcarregar_historico()sem consultar a API. - Separe conversas: adicione um identificador de sessão à tabela e filtre as consultas por ele. Assim, cada projeto poderá ter seu próprio histórico.
🚀 Novidades, Desafios e Dicas
Quer sair da teoria e construir um portfólio real com validação automática e apoio da comunidade?
Acesse nossos canais oficiais:
Comunidade HDEV | Desenvolvimento de Casa + DevsFree
- 🌐 Site e Trilha de Desafios: https://hdev.com.br/hub
- 💻 Repositórios no GitHub: https://github.com/HDEV-Desenvolvimento-de-Casa
- 💬 Grupo no WhatsApp: https://chat.whatsapp.com/GwcomlxHICV1R74eXruWZX
- 📢 Canal no WhatsApp: https://whatsapp.com/channel/0029VaBSZw48Pgs8MGYdsR3e
Notas do Editor
- A construção foi dividida em etapas pequenas, com explicação das instruções-chave após cada bloco.
- Foram explicitados os limites da memória, os cuidados com credenciais e um roteiro para verificar a persistência entre execuções.

Top comments (0)