Automatizando o Planejamento de Metas com IA e Google Apps Script
No post anterior, você aprendeu como a IA pode ajudar a definir metas financeiras realistas e recebeu uma planilha no Google Sheets para colocar esse planejamento em prática. Hoje vamos um passo além: vamos fazer a própria planilha chamar a IA automaticamente e escrever a análise para você — tudo com um clique, usando Google Apps Script e a API da OpenAI.
![]() |
| Fonte: https://br.freepik.com/ |
O que vamos construir
Ao final deste tutorial, sua planilha terá um menu customizado 🤖 Análise IA que, ao ser acionado, vai:
- Ler seus dados financeiros do mês (receitas, despesas, objetivos e metas)
- Montar um prompt contextualizado com essas informações
- Enviar esse prompt para o modelo gpt-4o (ou outro modelo de sua preferência) via API
- Escrever os resultados diretamente na aba IA_Analises da planilha, nas linhas de Resumo mensal, Alertas e Sugestões
Pré-requisitos
- A planilha do post anterior (se ainda não tem, baixe aqui)
- Uma conta na OpenAI Platform com créditos disponíveis
- Conhecimento básico de JavaScript — o Apps Script usa uma variante dele
Obtendo sua chave da API
Acesse platform.openai.com/api-keys, crie uma nova chave e guarde-a em um lugar seguro. Você vai usá-la em instantes.
Neste post mostramos em detalhes como obter sua chave da API OpenAI.
Atenção: nunca exponha sua API key em código compartilhado ou versionado. Mais abaixo mostramos como armazená-la de forma segura usando o PropertiesService do próprio Apps Script.
Acessando o Apps Script
Na sua planilha, vá em Extensões → Apps Script. O editor será aberto em uma nova aba. Apague qualquer código padrão que estiver lá e cole o código abaixo.
O Código Completo
// CONFIG — use PropertiesService em produção (veja abaixo) const OPENAI_API_KEY = PropertiesService.getScriptProperties().getProperty("OPENAI_API_KEY"); const OPENAI_MODEL = "gpt-4o"; // MENU CUSTOMIZADO function onOpen() { SpreadsheetApp.getUi() .createMenu("🤖 Análise IA") .addItem("Analisar minhas metas", "analisarMetas") .addToUi(); } // FUNÇÃO PRINCIPAL function analisarMetas() { const ss = SpreadsheetApp.getActiveSpreadsheet(); const config = lerAbaComoObjeto(ss, "Configurações"); const resumo = lerAbaComoObjeto(ss, "Resumo_Mensal"); const objetivos = lerAbaComoTabela(ss, "Objetivos"); const metas = lerAbaComoTabela(ss, "Metas_Mensais"); const prompt = montarPrompt(config, resumo, objetivos, metas); const resposta = chamarOpenAI(prompt); if (!resposta) { SpreadsheetApp.getUi().alert("Erro ao chamar a API da OpenAI. Verifique os logs."); return; } escreverAnalises(ss, resposta); SpreadsheetApp.getUi().alert("✅ Análise concluída! Veja a aba IA_Analises."); } // HELPERS DE LEITURA function lerAbaComoObjeto(ss, nomeAba) { const aba = ss.getSheetByName(nomeAba); if (!aba) return {}; const dados = aba.getDataRange().getValues(); const obj = {}; dados.forEach(([campo, valor]) => { if (campo) obj[campo] = valor; }); return obj; } function lerAbaComoTabela(ss, nomeAba) { const aba = ss.getSheetByName(nomeAba); if (!aba) return []; const dados = aba.getDataRange().getValues(); const cabecalho = dados[0]; return dados.slice(1) .filter(linha => linha.some(cel => cel !== "" && cel !== 0)) .map(linha => { const obj = {}; cabecalho.forEach((col, i) => { obj[col] = linha[i]; }); return obj; }); } // MONTAGEM DO PROMPT function montarPrompt(config, resumo, objetivos, metas) { const objetivosTexto = objetivos.length ? objetivos.map(o => `- ${o["Objetivo"]} | Tipo: ${o["Tipo"]} | Prioridade: ${o["Prioridade"]} | ` + `Valor: R$ ${o["Valor total"]} | Prazo: ${o["Prazo (meses)"]} meses | Status: ${o["Status"]}` ).join("\n") : "Nenhum objetivo cadastrado."; const metasTexto = metas.length ? metas.map(m => `- ${m["Objetivo"]}: Ideal R$ ${m["Valor ideal"]} | Mínimo R$ ${m["Valor mínimo"]} | ` + `Sugerido R$ ${m["Valor sugerido"]} | Realizado R$ ${m["Valor realizado"]} | ` + `Diferença R$ ${m["Diferença"]}` ).join("\n") : "Nenhuma meta mensal cadastrada."; return ` Você é um consultor financeiro pessoal especializado em planejamento de metas. Analise os dados financeiros abaixo e responda EXCLUSIVAMENTE no seguinte formato JSON: { "resumo_mensal": "texto corrido com análise geral do mês", "alertas": "texto corrido com alertas sobre riscos ou desvios identificados", "sugestoes": "texto corrido com sugestões práticas e priorizadas" } === DADOS DO USUÁRIO === Perfil: ${config["Perfil financeiro"] || "Não informado"} Período: ${config["Mês/Ano"] || "Não informado"} Renda total: R$ ${config["Renda total"] || 0} Limite de comprometimento: ${config["Limite comprometimento (%)"] || 0}% Meta mínima de poupança: ${config["Meta mínima (%)"] || 0}% Resumo do mês: - Total de Receitas: R$ ${resumo["Total de Receitas"] || 0} - Total de Despesas: R$ ${resumo["Total de Despesas"] || 0} - Saldo do Mês: R$ ${resumo["Saldo do Mês"] || 0} - % da Renda Comprometida: ${resumo["% da Renda Comprometida"] || 0} - Capacidade para Metas: R$ ${resumo["Capacidade para Metas"] || 0} Objetivos cadastrados: ${objetivosTexto} Metas mensais: ${metasTexto} Responda apenas o JSON, sem markdown, sem explicações fora do JSON. `.trim(); } // CHAMADA À API DA OPENAI function chamarOpenAI(prompt) { const url = "https://api.openai.com/v1/chat/completions"; const payload = { model: OPENAI_MODEL, messages: [{ role: "user", content: prompt }], temperature: 0.7, response_format: { type: "json_object" } }; const options = { method: "post", contentType: "application/json", headers: { Authorization: `Bearer ${OPENAI_API_KEY}` }, payload: JSON.stringify(payload), muteHttpExceptions: true }; try { const response = UrlFetchApp.fetch(url, options); const json = JSON.parse(response.getContentText()); if (json.error) { console.error("Erro OpenAI:", json.error.message); return null; } return JSON.parse(json.choices[0].message.content); } catch (e) { console.error("Exceção:", e.message); return null; } } // ESCRITA NA ABA IA_Analises function escreverAnalises(ss, resposta) { const aba = ss.getSheetByName("IA_Analises"); if (!aba) return; const dados = aba.getDataRange().getValues(); dados.forEach((linha, i) => { const tipo = linha[0]; if (tipo === "Resumo mensal") aba.getRange(i + 1, 2).setValue(resposta.resumo_mensal || ""); if (tipo === "Alertas") aba.getRange(i + 1, 2).setValue(resposta.alertas || ""); if (tipo === "Sugestões") aba.getRange(i + 1, 2).setValue(resposta.sugestoes || ""); }); }
Armazenando a chave com segurança
function salvarChave() { PropertiesService.getScriptProperties().setProperty("OPENAI_API_KEY", "sk-SUA_CHAVE_AQUI"); }
Entendendo o fluxo
- Leitura dos dados — as funções lerAbaComoObjeto e lerAbaComoTabela transformam os dados das abas em objetos JavaScript simples, filtrando linhas vazias automaticamente.
- Montagem do prompt — a função montarPrompt injeta esses dados em um prompt estruturado que instrui o modelo a responder apenas em JSON, evitando texto livre que quebraria o parse, o processo de converter os dados recebidos.
- Chamada à API — UrlFetchApp.fetch é o equivalente do fetch nativo no ambiente do Apps Script. O parâmetro response_format: { type: "json_object" } garante que o gpt-4o sempre retorne um JSON válido.
- Escrita dos resultados — a função escreverAnalises percorre a aba IA_Analises procurando as linhas pelo valor da coluna A ("Resumo mensal", "Alertas", "Sugestões") e escreve o conteúdo correspondente na coluna B.
Resultado esperado
Próximos passos possíveis
- Histórico de análises — salvar cada análise na aba Historico com data e timestamp
- Trigger automático — configurar um gatilho para a análise rodar todo dia 1º do mês sem intervenção manual
- Análise do Simulador — incluir os dados da aba Simulador no prompt para que a IA avalie cenários de variação de renda e despesas

Comentários