Ugrás a fő tartalomra

AI Hírek Automata Küldése (AI agenttel)

 AI Hírek Automata Küldése (AI agenttel)





Íme a javasolt architektúra az MVP (Minimum Viable Product) verzióhoz:

1. A rendszer felépítése

  • Futatókörnyezet: Google Apps Script (GAS).

    • Miért: Szervermentes, ingyenes, közvetlen hozzáférése van a Gmailhez, Drive-hoz, és van beépített időzítője (Trigger).

  • Intelligencia: Gemini API (Google AI Studio).

    • Miért: A Flash modellek (pl. 1.5 Flash, vagy az érkező 2.0) olcsók/ingyenesek a free tier-ben, és nagy a kontextus ablakuk.

  • Keresés: Google Custom Search JSON API vagy RSS Aggregátor.

    • Kritika: A Search API ingyenes kvótája napi 100 kérés. Ez elég, de szűkös. Alternatíva: TechCrunch, The Verge, Wired RSS feedjeinek begyűjtése és feldolgozása a Geminivel (ez stabilabb).

  • Memória & Tanulás: Google Sheets vagy Google Drive JSON file.

    • Megoldás: A script nem a kódot írja át, hanem egy JSON fájlt frissít a Drive-on (pl. memory.json). Ebben tárolja: utolsó témák hash-ét (duplikáció szűrés), felhasználói preferencia súlyozást (hossz, stílus).

2. A megvalósítás lépései

1. Lépés: API Kulcsok beszerzése

  • Gemini API: Lépj be a Google AI Studio-ba, kérj egy API kulcsot.

  • Google Search (Opcionális): Ha ragaszkodsz a valós kereséshez, aktiváld a Custom Search API-t a Google Cloud Console-ban.

2. Lépés: A „Szerver” létrehozása (Apps Script)

  • Nyiss egy új projektet a script.google.com oldalon.

  • Ez lesz a main.gs, ami a logikát futtatja.

3. Lépés: A Logika megírása (Vázlat)


const MAX_MEMORY_ITEMS = 50; // Ennyi régi cikkre emlékezzen vissza

function mainAgentJob() {
  // 1. Memória betöltése
  const memory = loadMemory();

  // 2. Információgyűjtés (RSS vagy Search)
  const rawNews = fetchTechNews();

  // 3. Gondolkodás (Gemini hívás)
  const prompt = `
    Te egy technikai AI asszisztens vagy.
    User preferenciák (tanult): ${JSON.stringify(memory.preferences)}
   
    Feladat: Elemzed az alábbi híreket, szűrd ki a zajt, és készíts összefoglalót.
    Hírek: ${rawNews}
   
    Formátum: HTML, tömör, bullet points.
  `;
  const analysis = callGemini(prompt);

  // 4. Jelentés (Email)
  GmailApp.sendEmail("mierdekel@gmail.com", "Napi AI Jelentés", "", {
    htmlBody: analysis + generateFeedbackLinks()
  });

  // 5. Memória frissítése (pl. mikor futott le utoljára)
  updateMemory(memory);
}

function fetchTechNews() {
  // Egyszerűsített RSS példa
  const url = 'https://techcrunch.com/category/artificial-intelligence/feed/';
  const xml = UrlFetchApp.fetch(url).getContentText();
  // Itt kellene parse-olni az XML-t (a Gemini is meg tudja csinálni, ha beküldöd a nyerset)
  return xml.substring(0, 10000); // Token limit miatt vágva teszteléshez
}

function callGemini(prompt) {
  const url = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL_NAME}:generateContent?key=${GEMINI_API_KEY}`;
  const payload = {
    contents: [{ parts: [{ text: prompt }] }]
  };
 
  const response = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload)
  });
 
  return JSON.parse(response.getContentText()).candidates[0].content.parts[0].text;
}

// ... Segédfüggvények (loadMemory, updateMemory) a DriveApp használatával

/**
 * Betölti a JSON adatokat a Google Drive-ról.
 * Ha a fájl üres vagy nem olvasható, alapértelmezett memóriát ad vissza.
 */
function loadMemory() {
  try {
    // Fájl keresése ID alapján
    var file = DriveApp.getFileById(MEMORY_FILE_ID);
   
    // Tartalom beolvasása stringként
    var content = file.getBlob().getDataAsString();
   
    // Ha üres a fájl, visszaadjuk az alapértelmezett struktúrát
    if (!content || content.trim() === "") {
      return initializeMemory();
    }
   
    return JSON.parse(content);
  } catch (e) {
    Logger.log("Hiba a memória betöltésekor (vagy első futás): " + e.toString());
    return initializeMemory();
  }
}

/**
 * Alapértelmezett memória szerkezet, ha még nincs adat.
 */
function initializeMemory() {
  return {
    preferences: {
      length: "közepes", // rövid, közepes, hosszú
      style: "tárgyilagos", // humoros, formális
      topics: ["AI", "Tech", "Space"]
    },
    lastRun: new Date().toString(),
    history: [] // Itt tároljuk az elmúlt napok témáit
  };
}

/**
 * Visszamenti a frissített állapotot a JSON fájlba.
 */
function updateMemory(memory) {
  try {
    // Frissítjük az utolsó futás idejét
    memory.lastRun = new Date().toString();
   
    var file = DriveApp.getFileById(MEMORY_FILE_ID);
    file.setContent(JSON.stringify(memory, null, 2)); // Formázott JSON mentése
    Logger.log("Memória sikeresen frissítve.");
  } catch (e) {
    Logger.log("Hiba a mentéskor: " + e.toString());
  }
}


/**
 * Ez generálja az e-mail aljára a HTML láblécet.
 */
function generateFeedbackLinks() {
  var html =  `
    <br><br>
    <hr style="border: 0; border-top: 1px solid #eee;">
    <div style="font-family: Arial, sans-serif; font-size: 11px; color: #888;">
      <p>🤖 <strong>AI Agent Status:</strong> Active | Model: Gemini Flash</p>
      <p>Ez egy automatikusan generált jelentés. A válaszadás egyelőre nem módosítja a memóriát (fejlesztés alatt).</p>
    </div>
  `;
  return html;
}




4. Lépés: A visszacsatolás (Learning Loop) – A kritikus pont Az e-mail elolvasását technikailag nehéz mérni („pixel tracking” megbízhatatlan). Helyette interakciót kell kérni.

  • Megoldás: Az e-mail aljára generálj két linket (Apps Script Web App URL paraméterekkel):

    • [Túl hosszú volt] -> Kattintás -> Script csökkenti a length_preference értéket a JSON-ban.

    • [Releváns volt] -> Kattintás -> Script megerősíti a témaköröket.

5. Lépés: Automatizálás

  • Az Apps Script felületén kattints a Triggers (Óra ikon) menüre.

  • Adj hozzá új triggert: mainAgentJob futtatása -> Time-driven -> Day timer -> 8am to 9am.

Kritika és Kockázatok

  1. Search vs. Context Window: A Google Search API találatai csak címek és snippetek. A teljes cikkek tartalmának elemzéséhez a találatokra rá kellene kattintani (crawling), amit a GAS UrlFetchApp korlátozottan tud (időtúllépés, JavaScriptes oldalak).

    • Javaslat: Kezdd RSS feedekkel. Azok tartalmazzák a lead-et vagy a teljes szöveget, amit a Gemini sokkal jobban tud feldolgozni hiba nélkül.

  2. Öntanulás korlátai: A rendszer nem fogja "érteni", hogy elolvastad-e, hacsak nem kattintasz. A passzív tanulás e-mailben nem működik megbízhatóan.

  3. Token költség: Ha túl sok hírt küldesz be a Gemininek minden reggel, gyorsan elérheted a Rate Limit-et (RPM - Request Per Minute). Kezeld a hibákat (try-catch block).

Hogyan kezdj neki?

Ne a teljes autonóm rendszert építsd meg egyszerre.

  1. Írd meg a scriptet, ami csak egy statikus szöveget (pl. "Hello World") elküld emailben, amikor lefuttatod.

  2. Kösd be a Gemini API-t, hogy a statikus szöveg helyett egy viccet generáljon.

  3. Cseréld le a viccet RSS feed tartalomra.

  4. Implementáld a memória JSON-t Drive-on.


Módosítsuk a kódot úgy, hogy soha ne küldjön kétszer ugyanarról a hírről?
(Ez teszi majd emberivé, mert úgy tűnik, mintha követné az eseményeket).


// --- KONFIGURÁCIÓ ---
// A kulcsot a Project Settings -> Script Properties menüben tárold 'GEMINI_API_KEY' néven!
const MODEL_NAME = 'gemini-1.5-flash'; 
const MEMORY_FILE_ID = 'IDE_ÍRD_A_DRIVE_FILE_ID-T'; // A te JSON fájl ID-d
const MAX_MEMORY_ITEMS = 50; // Ennyi régi cikkre emlékezzen vissza
// --- FŐ PROGRAM ---
function mainAgentJob() {
  try {
    // 1. Memória betöltése
    var memory = loadMemory();
    Logger.log("Memória betöltve. Eddig ismert cikkek száma: " + memory.history.length);
    // 2. Hírek lekérése és PARSOLÁSA
    var allNews = fetchAndParseRSS();
    Logger.log("Összes hír a feedben: " + allNews.length);
    // 3. Szűrés: Csak azokat tartsuk meg, amik nincsenek a memóriában
    var newArticles = allNews.filter(function(article) {
      return memory.history.indexOf(article.link) === -1;
    });
    Logger.log("Ebből ténylegesen új hír: " + newArticles.length);
    // HA NINCS ÚJ HÍR, LEÁLLUNK
    if (newArticles.length === 0) {
      Logger.log("Nincs új tartalom mára. Pihenés.");
      return; 
    }
    // 4. Gondolkodás (Gemini hívás) - Csak az újakat küldjük!
    // JSON stringgé alakítjuk az új cikkeket, hogy a Gemini könnyen értse
    var promptData = newArticles.map(function(a) {
      return "- Cím: " + a.title + "\n  Leírás: " + a.description;
    }).join("\n\n");
    var prompt = `
      Te egy AI hírügynök vagy. Ma ${newArticles.length} új cikket találtam, amiről még nem beszéltünk.
      
      Feladat: Válaszd ki a legfontosabbakat, és készíts belőlük egy összefoglalót.
      Ne csak fordíts, hanem elemezz: miért fontos ez?
      
      Formátum: HTML (használj <h2>, <ul>, <li>, <b>).
      Stílus: Tömör, lényegretörő.
      
      Hírek:
      ${promptData}
    `;
    
    var analysis = callGemini(prompt);
    Logger.log("Gemini elemzés kész.");
    // 5. E-mail küldése
    var emailAddress = Session.getActiveUser().getEmail();
    GmailApp.sendEmail(emailAddress, "🤖 Napi AI Frissítés (" + newArticles.length + " új)", "", {
      htmlBody: analysis + generateFeedbackLinks()
    });
    Logger.log("E-mail elküldve.");
    // 6. Memória frissítése (Hozzáadjuk az újakat)
    newArticles.forEach(function(a) {
      memory.history.push(a.link);
    });
    // Karbantartás: Ne hízzon a végtelenségig a fájl, tartsuk meg csak az utolsó 50-et
    if (memory.history.length > MAX_MEMORY_ITEMS) {
      memory.history = memory.history.slice(memory.history.length - MAX_MEMORY_ITEMS);
    }
    updateMemory(memory);
  } catch (e) {
    Logger.log("KRITIKUS HIBA: " + e.toString());
  }
}
// --- ÚJ HÍRLEKÉRŐ (XML PARSER) ---
function fetchAndParseRSS() {
  var url = 'https://techcrunch.com/category/artificial-intelligence/feed/';
  var xml = UrlFetchApp.fetch(url).getContentText();
  var document = XmlService.parse(xml);
  var root = document.getRootElement();
  
  // RSS struktúra bejárása: channel -> item
  var channel = root.getChild('channel');
  var items = channel.getChildren('item');
  
  var newsList = [];
  
  // Csak az első 10-et nézzük meg, felesleges 50-et feldolgozni
  for (var i = 0; i < Math.min(items.length, 10); i++) {
    var item = items[i];
    var title = item.getChildText('title');
    var link = item.getChildText('link');
    var description = item.getChildText('description');
    
    // HTML tagek eltávolítása a leírásból (clean up)
    if (description) {
      description = description.replace(/<[^>]*>/g, '').substring(0, 300) + "...";
    }
    
    newsList.push({
      title: title,
      link: link,
      description: description
    });
  }
  
  return newsList;
}
// --- EGYÉB SEGÉDFÜGGVÉNYEK ---
function callGemini(prompt) {
  var key = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
  if (!key) throw new Error("Nincs beállítva a GEMINI_API_KEY a Script Properties-ben!");
  var url = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL_NAME}:generateContent?key=${key}`;
  var payload = { contents: [{ parts: [{ text: prompt }] }] };
  
  var options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };
  var response = UrlFetchApp.fetch(url, options);
  var json = JSON.parse(response.getContentText());
  
  if (json.error) throw new Error(json.error.message);
  return json.candidates[0].content.parts[0].text;
}
function generateFeedbackLinks() {
  return `<br><hr><small>🤖 AI Memory System v2.0 | Auto-learning enabled</small>`;
}
// --- MEMÓRIA ---
function loadMemory() {
  try {
    var file = DriveApp.getFileById(MEMORY_FILE_ID);
    var content = file.getBlob().getDataAsString();
    if (!content || content.trim() === "") return initializeMemory();
    var mem = JSON.parse(content);
    // Biztosítjuk, hogy a history tömb létezzen (régi json esetén)
    if (!mem.history) mem.history = [];
    return mem;
  } catch (e) {
    return initializeMemory();
  }
}
function initializeMemory() {
  return { lastRun: null, history: [] };
}
function updateMemory(memory) {
  try {
    memory.lastRun = new Date().toString();
    DriveApp.getFileById(MEMORY_FILE_ID).setContent(JSON.stringify(memory, null, 2));
  } catch (e) {
    Logger.log("Mentési hiba: " + e.toString());
  }
}





Mit csinál ez a kód másképp?

Szűrés: Az XML Parse segítségével kinyeri a linkeket. Ha a link már benne van a memory.json-ban, akkor azt a cikket nem küldi el a Gemininek.


Költséghatékonyság: Mivel a Gemini csak az új cikkeket kapja meg, sokkal kevesebb tokent használsz, és a válasz is relevánsabb lesz.


Memória tisztítás: A MAX_MEMORY_ITEMS = 50 sor biztosítja, hogy a JSON fájl ne nőjön túl nagyra (a legrégebbi híreket elfelejti).


Tipp az első futtatáshoz:

Amikor először futtatod ezt az új kódot, minden hír újnak fog számítani, ezért kapsz majd egy e-mailt. Ha rögtön utána futtatod újra, a Logban azt kell látnod: "Nincs új tartalom mára. Pihenés.", és nem kapsz e-mailt. Ez jelzi, hogy az ügynököd "emlékszik".





Megjegyzések