Skip to content

Instantly share code, notes, and snippets.

@horacioibrahim
Created March 6, 2026 11:06
Show Gist options
  • Select an option

  • Save horacioibrahim/7aade01cb6b7366461e8681bd8de5ada to your computer and use it in GitHub Desktop.

Select an option

Save horacioibrahim/7aade01cb6b7366461e8681bd8de5ada to your computer and use it in GitHub Desktop.
Classificar itens app-script-ai.js
const ANTHROPIC_API_KEY = "ANTHROPIC_API_KEY"
const MODEL = "claude-haiku-4-5-20251001"; // Modelo de alta performance para 2026
const BATCH_SIZE = 10; // Quantidade de linhas por chamada
/**
* Cria o menu superior na planilha
*/
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('▶️ Classificar w/ Claude')
.addItem('Executar Classificação em Lote', 'solicitarIntervalo')
.addToUi();
}
/**
* Função principal que o botão/menu chama
*/
function solicitarIntervalo() {
const ui = SpreadsheetApp.getUi();
const respostaInicio = ui.prompt('🍋 Início', 'Digite a linha INICIAL:', ui.ButtonSet.OK_CANCEL);
if (respostaInicio.getSelectedButton() !== ui.Button.OK) return;
const respostaFim = ui.prompt('🍋 Fim', 'Digite a linha FINAL:', ui.ButtonSet.OK_CANCEL);
if (respostaFim.getSelectedButton() !== ui.Button.OK) return;
const linhaInicio = parseInt(respostaInicio.getResponseText());
const linhaFim = parseInt(respostaFim.getResponseText());
if (isNaN(linhaInicio) || isNaN(linhaFim) || linhaInicio > linhaFim) {
ui.alert('Erro', 'Insira números de linha válidos.', ui.ButtonSet.OK);
return;
}
processarEmLote(linhaInicio, linhaFim);
}
/**
* Agrupa as linhas e envia para o Claude
*/
function processarEmLote(inicio, fim) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const ui = SpreadsheetApp.getUi();
for (let i = inicio; i <= fim; i += BATCH_SIZE) {
let batchEnd = Math.min(i + BATCH_SIZE - 1, fim);
let itensParaProcessar = [];
// Coleta o conteúdo das linhas do lote atual
for (let j = i; j <= batchEnd; j++) {
let texto = sheet.getRange(j, 3).getValue(); // Coluna C
if (texto) {
itensParaProcessar.push({ id: j, conteudo: texto });
}
}
if (itensParaProcessar.length === 0) continue;
const systemPrompt = `Você é um analista de portais de compras públicas, conhecedor das 14.133/2021, aléms das
antigas leis a antiga Lei Geral (Lei 8.666/93), a Lei do Pregão (Lei 10.520/02). Classifique cada item no JSON recebido.
Categorias: LEGAL-REQUERIDO, LEGAL-PREFERIVEL, USABILIDADE, PLATAFORMA (performance, custo, etc), CONVENIENCIA, Segurança, Outros.
REGRA: Retorne APENAS um array JSON de objetos com "id", "classe" e "comentario" (máx 120 carac).
Não use markdown, não use backticks.`;
try {
const promptUsuario = "Itens: " + JSON.stringify(itensParaProcessar);
const respostaBruta = chamarClaude(systemPrompt, promptUsuario);
// Limpeza de segurança contra Markdown indesejado
const respostaLimpa = respostaBruta.replace(/```json/g, "").replace(/```/g, "").trim();
const resultados = JSON.parse(respostaLimpa);
// Distribui os resultados nas colunas G e H
resultados.forEach(res => {
sheet.getRange(res.id, 7).setValue(res.classe.trim()); // Coluna G
sheet.getRange(res.id, 8).setValue(res.comentario); // Coluna H
});
SpreadsheetApp.flush(); // Atualiza a planilha em tempo real
Utilities.sleep(800); // Evita sobrecarga na API
} catch (e) {
console.error("Erro no lote da linha " + i + ": " + e.message);
}
}
ui.alert('🍋 Concluído', 'O backlog foi processado com sucesso.', ui.ButtonSet.OK);
}
/**
* Comunicação direta com a Anthropic API
*/
function chamarClaude(system, prompt) {
const url = "https://api.anthropic.com/v1/messages";
const payload = {
"model": MODEL,
"max_tokens": 2048,
"system": system,
"messages": [{ "role": "user", "content": prompt }],
"temperature": 0
};
const options = {
"method": "post",
"headers": {
"x-api-key": ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json"
},
"payload": JSON.stringify(payload),
"muteHttpExceptions": true
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
if (json.content && json.content[0]) {
return json.content[0].text;
}
throw new Error("Falha na API: " + JSON.stringify(json));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment