Created
March 6, 2026 11:06
-
-
Save horacioibrahim/7aade01cb6b7366461e8681bd8de5ada to your computer and use it in GitHub Desktop.
Classificar itens app-script-ai.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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