\n\n\n\n Minha Jornada Construindo IA Conversacional com SDKs - AgntBox Minha Jornada Construindo IA Conversacional com SDKs - AgntBox \n

Minha Jornada Construindo IA Conversacional com SDKs

📖 12 min read2,328 wordsUpdated Apr 3, 2026

Olá a todos, Nina aqui, de volta do meu laboratório digital (que, sejamos honestos, é principalmente minha mesa de cozinha cheia de café morno). Hoje, quero falar sobre algo que tem repercutido nos meus canais do Slack e assombrado minhas sessões de codificação noturnas: o mundo, por vezes frustrante, frequentemente brilhante, dos SDKs de IA, especificamente quando você está tentando construir um agente de conversação que realmente se sinta como uma conversa, e não apenas um bot de FAQ glorificado.

Minha obsessão particular ultimamente tem sido a integração da Assistants API da OpenAI. Agora, eu sei o que você está pensando, “Nina, Assistants API? Isso é notícia velha!” E sim, você não está errado. Já está disponível há um tempo. Mas escute-me: não estou aqui para lhe dar um tutorial básico de “como chamar client.beta.assistants.create()”. Todos nós já vimos isso. Meu foco hoje é em um problema específico e espinhoso que encontrei e como lutei com o SDK para fazer com que ele fizesse o que eu queria: gerenciar conversas com estado e múltiplas interações com ferramentas personalizadas sem perder a cabeça (ou o contexto do meu usuário). Pense além de simples perguntas e respostas. Pense em fluxos de trabalho complexos, perguntas de acompanhamento e chamadas de ferramentas dinâmicas baseadas na intenção em evolução do usuário.

A Estado da Minha Sanidade: Por Que a Assistants API (Ainda) Importa

Antes da Assistants API, construir qualquer coisa que mantivesse estado com os modelos da OpenAI muitas vezes se sentia como ser um malabarista com muitas bolas no ar. Você era responsável por gerenciar o histórico de mensagens, engenharia de prompts para contexto e orquestração de chamadas de ferramentas tudo sozinho. Era… exaustivo. A Assistants API prometeu tirar grande parte desse fardo de nossos ombros gerenciando threads, mensagens e até mesmo a orquestração de ferramentas. E para casos simples, ela cumpre muito bem.

Meu projeto atual envolve construir um assistente de finanças pessoais “inteligente”. Não apenas um que te diga seu saldo, mas um que possa ajudar você a planejar orçamentos, sugerir estratégias de investimento com base na sua tolerância ao risco e até simular cenários financeiros futuros. Isso requer muito vai e vem, lembrando declarações anteriores (“Quero economizar para uma casa”, “Meu rendimento é X”, “E se eu investir em Y?”) e, crucialmente, chamando funções específicas (como buscar dados de ações em tempo real ou rodar um algoritmo de projeção de orçamento) no momento certo.

Minha primeira tentativa foi uma bela bagunça. Eu estava passando todo o histórico de mensagens para frente e para trás, tentando enfiar instruções complexas no prompt do sistema e verificando manualmente se era necessária uma chamada de ferramenta. Funcionou, mas era frágil, lento e uma dor de cabeça para depurar. Foi então que decidi realmente mergulhar na Assistants API e seu SDK.

O Abraço do SDK: Threads, Mensagens e o Elusivo `run`

O núcleo da Assistants API é o conceito de `Threads` e `Messages`. Você cria uma `Thread` para uma conversa, adiciona `Messages` a ela e então “executa” o `Assistant` nessa `Thread`. O `Assistant` então processa as mensagens, decide se precisa chamar uma ferramenta, e gera uma resposta. Simples assim, certo?

Aqui é onde fica interessante. Meu assistente financeiro precisa fazer coisas como:

  • Rastrear metas: “Quero economizar $50.000 para uma entrada de casa.”
  • Coletar informações: “Qual é sua renda mensal atual?”
  • Executar cálculos: “Com base nisso, quanto tempo levará se eu economizar $1.000 por mês?”
  • Fornecer conselhos: “Considere diversificar seu portfólio com fundos de índice de baixo custo.”

Cada uma dessas frequentemente requer uma chamada de ferramenta personalizada. Por exemplo, `calculate_savings_timeline(goal_amount, monthly_income, monthly_savings)`. O desafio não é apenas definir a ferramenta; é fazer o Assistant:

  1. Reconhecer que precisa da ferramenta.
  2. Identificar todos os parâmetros necessários (mesmo que estejam espalhados por várias mensagens do usuário).
  3. Pedir ao usuário os parâmetros que estão faltando de maneira amigável.
  4. Executar a ferramenta e incorporar os resultados.

O Problema dos Parâmetros: Quando o Assistant Pede Mais

Digamos que um usuário diga, “Quanto tempo levará para eu economizar para uma casa?” Minha ferramenta `calculate_savings_timeline` precisa de `goal_amount`, `monthly_income`, e `monthly_savings`. O Assistant, sendo inteligente, muitas vezes percebe que faltam informações. O SDK, através do objeto `run`, vai te avisar que está `requires_action`.

Minha abordagem inicial era apenas esperar pelo status `requires_action` e então apresentar uma mensagem genérica “Que informações você precisa?” para o usuário. Isso era clunky. O usuário frequentemente não sabia quais parâmetros a ferramenta precisava. Uma melhor abordagem, que eventualmente adotei, envolveu inspecionar as `tool_calls` dentro do status `requires_action`.


def submit_tool_outputs(client, thread_id, run_id, tool_outputs):
 return client.beta.threads.runs.submit_tool_outputs(
 thread_id=thread_id,
 run_id=run_id,
 tool_outputs=tool_outputs
 )

# ... dentro do seu loop de conversa ...

if run.status == 'requires_action':
 tool_outputs = []
 missing_params = {}
 for tool_call in run.required_action.submit_tool_outputs.tool_calls:
 function_name = tool_call.function.name
 arguments = json.loads(tool_call.function.arguments)

 # Aqui está o truque: verifique se um parâmetro necessário está faltando
 # Isso assume que suas ferramentas são definidas com um esquema
 # Para simplificar, vamos dizer que sabemos que 'monthly_savings' é sempre necessário
 if function_name == "calculate_savings_timeline" and "monthly_savings" not in arguments:
 missing_params['monthly_savings'] = 'Quanto você planeja economizar por mês?'
 
 # ... checagem de parâmetros mais sofisticada aqui ...

 if missing_params:
 # Informe ao usuário o que está faltando
 user_prompt = "Preciso de um pouco mais de informação para ajudá-lo com isso. "
 for param, question in missing_params.items():
 user_prompt += f"{question} "
 return {"status": "waiting_for_user_input", "prompt": user_prompt}
 else:
 # Todos os parâmetros estão presentes, execute a ferramenta
 output = execute_tool_function(function_name, arguments) # Sua função para rodar a lógica da ferramenta real
 tool_outputs.append({
 "tool_call_id": tool_call.id,
 "output": json.dumps(output)
 })
 
 if tool_outputs:
 run = submit_tool_outputs(client, thread.id, run.id, tool_outputs)
 # Continue verificando o run até que seja concluído ou exija nova ação
 else:
 # Este caso trata de se detectamos parâmetros faltando e solicitamos ao usuário
 pass

Este trecho é uma versão simplificada, mas a ideia central é interceptar `requires_action`, observar o que o Assistant quer fazer e, se estiver faltando argumentos, pedir ao usuário especificamente por eles. Isso faz a conversa parecer muito mais natural, como um humano fazendo perguntas de esclarecimento, em vez de um genérico “erro.”

O Elusivo Estado: Mantendo o Controle entre Interações

Uma das maiores dores de cabeça era garantir que, se um usuário fornecesse uma informação (“Minha renda é de $5.000 por mês”), o Assistant se lembrasse disso para chamadas de ferramentas subsequentes dentro da mesma conversa, mesmo que não fosse usado imediatamente. A Assistants API lida bem com isso dentro de uma `Thread` mantendo o histórico de mensagens. No entanto, às vezes você quer guiá-lo explicitamente ou dar um contexto que não faz parte de uma mensagem direta do usuário.

Descobri que estava usando `metadata` nos objetos `Thread` e `Message` mais do que inicialmente pensei. Por exemplo, se o usuário declarar explicitamente sua tolerância ao risco, eu poderia armazenar isso na metadata da thread. Embora o Assistant não “leia” diretamente essa metadata em seu processo de raciocínio, ela é inestimável para a lógica da minha aplicação quando preciso pré-preencher argumentos para chamadas de ferramentas ou tomar decisões sobre quais ferramentas são relevantes.

Outro padrão que adotei foi injetar mensagens “semelhantes a sistemas” na thread se eu precisasse lembrar explicitamente o Assistant de certos fatos que foram derivados de uma chamada de ferramenta ou um processo interno. Por exemplo, depois que `calculate_savings_timeline` retorna que levará 10 anos, eu poderia adicionar uma mensagem como: `{“role”: “user”, “content”: “O plano de economia do usuário indica um prazo de 10 anos para alcançar sua meta.”}`. Isso não vem diretamente do usuário, mas ajuda a reforçar o contexto para as respostas subsequentes do Assistant.

Tratando Saídas de Ferramenta e Ações Subsequentemente

Quando uma ferramenta é executada, você recebe uma saída. O Assistant então pega essa saída e gera uma resposta. Mas e se a saída da ferramenta em si gerar um novo conjunto de perguntas ou uma nova chamada de ferramenta? Por exemplo, a ferramenta `calculate_savings_timeline` pode retornar que a meta é inatingível com as economias atuais. O Assistant deveria idealmente sugerir, “Talvez devêssemos olhar para formas de aumentar suas economias mensais ou reduzir o valor da sua meta.” Isso não se trata apenas de gerar texto; trata-se de encadear passos lógicos.

A Assistants API SDK lida com isso lindamente mantendo o objeto `run` ativo. Depois que você `submit_tool_outputs`, o `run` geralmente transita de volta para `in_progress` e depois pode gerar um novo `requires_action` (para outra chamada de ferramenta) ou `completed`. Meu loop principal para gerenciar o fluxo da conversa parece algo assim:


def get_assistant_response(client, thread_id, assistant_id, user_message):
 client.beta.threads.messages.create(
 thread_id=thread_id,
 role="user",
 content=user_message
 )

 run = client.beta.threads.runs.create(
 thread_id=thread_id,
 assistant_id=assistant_id
 )

 while run.status in ['queued', 'in_progress', 'cancelling']:
 time.sleep(1) # Seja um bom cidadão, não sobrecarregue a API
 run = client.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run.id)

 if run.status == 'completed':
 messages = client.beta.threads.messages.list(thread_id=thread_id, order="desc")
 for msg in messages.data:
 if msg.role == "assistant":
 # Encontre a mensagem mais recente do assistente, normalmente a primeira
 return msg.content[0].text.value # Assumindo conteúdo de texto
 return "Nenhuma resposta do assistente."

 elif run.status == 'requires_action':
 tool_outputs = []
 for tool_call in run.required_action.submit_tool_outputs.tool_calls:
 function_name = tool_call.function.name
 arguments = json.loads(tool_call.function.arguments)
 
 # Aqui está onde sua lógica personalizada para lidar com parâmetros faltantes ou executar ferramentas vai
 # Para este exemplo simplificado, vamos assumir que todos os parâmetros estão presentes e vamos executar
 print(f"Assistente deseja chamar {function_name} com args: {arguments}")
 
 output = execute_tool_function(function_name, arguments) # Sua lógica de execução da ferramenta
 tool_outputs.append({
 "tool_call_id": tool_call.id,
 "output": json.dumps(output)
 })
 
 # Enviar as saídas da ferramenta e continuar a execução
 run = client.beta.threads.runs.submit_tool_outputs(
 thread_id=thread_id,
 run_id=run.id,
 tool_outputs=tool_outputs
 )
 # Chamar recursivamente esta função para processar a execução atualizada
 # Cuidado com a profundidade da recursão em produção, pode precisar de um loop
 return get_assistant_response(client, thread_id, assistant_id, "") # Passar mensagem vazia
 
 elif run.status == 'failed':
 return f"A execução do assistente falhou: {run.last_error.message}"
 
 else:
 return f"Status de execução inesperado: {run.status}"

Esta função `get_assistant_response`, embora simplificada, mostra o loop central. O importante é que `requires_action` não é um beco sem saída. É uma oportunidade para seu aplicativo intervir, executar a ferramenta solicitada e depois passar os resultados de volta para o Assistente continuar seu raciocínio. Este feedback em loop fechado é o que torna possíveis conversas verdadeiramente dinâmicas e com estado.

Aprendizados Ação para Seu Próximo Projeto de Assistente de IA

  1. Abrace o ciclo de vida do `run.status`: Não verifique apenas se está `completed`. `requires_action` é seu amigo, não um erro. Crie manipuladores sólidos para cada status relevante.
  2. Inspecione `tool_calls` em busca de parâmetros faltantes: Em vez de solicitações genéricas, mergulhe em `run.required_action.submit_tool_outputs.tool_calls` para entender *exatamente* o que o Assistente precisa. Isso eleva sua experiência do usuário.
  3. Uso estratégico de Mensagens de Sistema: Se sua lógica interna do aplicativo derivar novos fatos ou precisar enfatizar certos contextos, considere injetar mensagens de “usuário” que representem esses fatos. O Assistente as processará como parte do histórico da conversa.
  4. Metadados para o Estado do Aplicativo: Embora o Assistente possa não “ler” diretamente metadados de threads ou mensagens para seu raciocínio, é um ótimo lugar para seu aplicativo armazenar e recuperar estado relevante para a conversa. Pense em preferências do usuário, variáveis da sessão atual, etc.
  5. Teste Casos Limite para Orquestração de Ferramentas: O que acontece se uma ferramenta falhar? E se o usuário mudar de ideia durante a invocação da ferramenta? Projete sua `execute_tool_function` e o tratamento de erros dentro do loop `requires_action` com cuidado.

Construir agentes de IA conversacionais verdadeiramente inteligentes não é apenas sobre escolher o modelo mais recente. É sobre manejar habilidosamente os SDKs que eles fornecem para gerenciar as complexidades da interação humana. A API de Assistentes da OpenAI, com um pouco de engenharia cuidadosa em torno de sua gestão de estado e orquestração de ferramentas, pode realmente elevar sua experiência conversacional além de simples turnos. Ainda é uma jornada e ainda estou aprendendo novas técnicas, mas espero que essas insights economizem algumas noites mal dormidas e cafés frios!

Artigos Relacionados

🕒 Published:

🧰
Written by Jake Chen

Software reviewer and AI tool expert. Independently tests and benchmarks AI products. No sponsored reviews — ever.

Learn more →
Browse Topics: AI & Automation | Comparisons | Dev Tools | Infrastructure | Security & Monitoring
Scroll to Top