# GAS : Google App Script與生程式AI工作 基礎篇 >[!Note]基本目標 : 整理投稿者履歷並生成面試問答題目 >許多面試者根據Google表單回答了基本問題,而這些資料放置在google sheet當中 >而我們現在並沒有這麼多時間安排每位面試者的面試題目,所以我們決定透過Gemini來輔助,自動化整理履歷資料與生成個人化的面試題目 ## 完整程式碼 [連線到Gemini](https://github.com/bsbacon0966/Google-App-Script-30-days-example/blob/main/day15/Initial_Gemini.js) [Gemini x Google試算表 基礎程式碼](https://github.com/bsbacon0966/Google-App-Script-30-days-example/blob/main/day15/basic.js) [Gemini x Google試算表 進階程式碼](https://github.com/bsbacon0966/Google-App-Script-30-days-example/blob/main/day15/advance.js) ## Gemini 是什麼 Gemini是Google推出的多模態人工智慧模型,具備強大的能力,能同時識別文本、圖像、音頻、影片和程式碼等五種不同類型的資訊,這使得它在處理複雜任務時非常靈活,不僅能理解和生成高品質的文字內容,還能生成多種主流編程語言(如Python、Java、C++)的代碼。 ## Gemini API Key 取得與使用 ### 第一步 : 到[Google AI Studio](https://aistudio.google.com/)取得API Key <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/Bk9daIx5ke.png" style="width: 70%; border: 2px solid black; padding: 5px;" /> </div> 點擊Sign in Google AI Studio,登入自己的Google帳號 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/B1IC6Lg5Je.png" style="width: 75%; border: 2px solid black; padding: 5px;" /> </div> 進入到Studio中,點擊`API Keys -> Create API Key`,如果你有出現`Search Google Cloud project`此欄位的話,請選擇**Generative Language Client**或**Gemini AI**帳號,並且Create API <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/BknGkwg5Jg.png" style="width: 70%; border: 2px solid black; padding: 5px;" /> </div> 而你將會得到一串API Key,請複製後好好保存,並且不要洩漏出去,以免出現安全疑慮 >[!Important] Generative Language Client帳號 或 Gemini AI帳號 > 這個帳號主要是一個專門建立API Key的一個GCP帳號,此帳號有免費與付費專案(預設為免費,不用填寫信用卡資訊)。 > <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HyDAkDg5kg.png" style="width: 60%; border: 2px solid black; padding: 5px;" /> </div> ### 第二步: 到新建的Google App Script檔案中,切到左側`目錄->專案設定` <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/HyW2QPrcye.png" style="width: 60%; border: 2px solid black; padding: 5px;" /> </div> 我們將在這裡將剛剛格到的API Key存放到Properties Service <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/BJ4VVPrq1g.png" style="width: 70%; border: 2px solid black; padding: 5px;" /> </div> 到`指令碼屬性->屬性與值`填寫,屬性名稱命名為你喜歡的名字(範例設定為`GEMINI_API_KEY`),並且將API Key填到`值`的欄位當中 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/Sk1trwHqJl.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 回到編譯器中,設定變數GEMINI_API為"儲存在Properties Service中,Property屬性名稱為`GEMINI_API_KEY`的值" 並且,我們可以寫if判斷是否抓取成功,如果沒有跳出錯誤訊息,代表到現在你的設定都是順利的 >[!Important] 為何要這樣設定,儲存到Property後要又要用getProperty()去撈 >如果直接貼上API Key代碼,就像把家裡的鑰匙放在保險箱裡,直接寫在程式碼中就像把鑰匙貼在門上,誰都能輕易取得。 >Property 就像保險箱,安全地儲存敏感資訊(API 金鑰),只有授權的程式才能存取。 ### 第三步: 構建 API 端點與配置請求參數 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/rkZHVKr9yl.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 接下來,是一連串url(API端點)、payload(請求體)、option(配置請求)建立: #### 1. 建立URL作為API端點 ```json var url = https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=GEMINI_API_KEY ``` 此統一資源定位符(URL)作為 Gemini Pro 模型之應用程式介面(API)端點,其構成明確指示: - 調用模型為「gemini-pro」。 - 執行操作為「generateContent」,即請求模型生成文本內容。 - key=GEMINI_API_KEY,即代表我們的"身份驗證金鑰"也需一併傳上 #### 2. payload設定 ```json var payload = { "contents": [ // contents 陣列,包含一個或多個內容物件 { "parts": [ { "text": "Explain how AI works" // 要發送給 AI 模型的文字提示 } ] } ] }; ``` 將所有要上傳的資訊包裝到`contents 內容物件`中: contents 陣列包含一個或多個對話內容,每個內容物件代表一輪詢問。在每個內容物件 (contents 內的元素) 中,parts 陣列存放該輪對話的具體內容 - "parts" : 包含一個部分物件,可以想像成一輪詢問下我們要提出的問題 - 在parts中 : "text"用於指定該部分物件的內容為文字形式的提示或回應。 ```json { "contents": [ { "parts": [ { "text": "這是什麼圖片?請描述它。" }, { "inlineData": { "mimeType": "image/jpeg", "data": "BASE64_ENCODED_IMAGE_DATA" } } ] }, { "parts": [ { "text": "根據剛才的描述,這張圖片的應用場景有哪些?" } ] } ] } ``` 以上述結構而言, - 第一輪對話 - 提問:「這是什麼圖片?請描述它。」 - 附加一張 image/jpeg 格式的圖片(Base64 編碼)。 - 第二輪對話 - 提問:「根據你對這張圖片的描述,它的應用場景有哪些?」(此問題依賴第一輪對話的輸出,即對圖片的描述)。 #### 3. option請求參數配置 ```json var options = { 'method': 'post', // 使用 POST 方法發送請求 'contentType': 'application/json', // 設定內容類型為 JSON 'payload': JSON.stringify(payload) // 將 payload 物件轉換為 JSON 字串 }; ``` 我們要配置請求參數,以明確指示對指定 URL 所執行的操作 - 'method': 'post' : 說明要提交請求 - 'contentType': 'application/json' : 設定請求體的內容類型為 application/json,表明數據格式為 JSON - 'payload': JSON.stringify(payload) : 將 payload 物件序列化為 JSON 字串,以符合 Gemini API 對請求體數據格式的要求 #### 4. 發送請求並處理回應 ```json var response = UrlFetchApp.fetch(url, options); // 使用 UrlFetchApp.fetch() 方法發送 HTTP 請求,並將回應存儲在 response 變數中 var json = JSON.parse(response.getContentText()); // 將 API 回應的 JSON 字串解析為 JavaScript 物件 var text = json.candidates[0].content.parts[0].text; // 從 JSON 物件中提取 AI 模型生成的回應文本 Logger.log(text); // 將回應文本記錄到 Google Apps Script 的日誌中 ``` 使用 UrlFetchApp.fetch() 方法向指定的 URL(API 端點)發送 HTTP 請求,其中包含剛剛的請求參數一併上傳,並利用response接住所回傳的資訊,並將其物件中提取回應體的內容,提取回應結構中text文字的部分。 最終利用`Logger.log(text)`查看結果。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/BkOuNYSqke.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 如果你看到他所介紹AI工作的方法,那恭喜你正式體驗過與Gemini互動! ### 第四步: 讓Gemini與Google sheet互動 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/Hyny_Yr5ye.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 現在我們想要完成今天的基礎目標,那我們就需要連線到Google試算表,抓取相關資訊後並將其包裝到payload(請求體)上。 現在這張試算表,sheet1中有"姓名"、"聯絡信箱"、"投稿參與組別"、"對於科技知識與工具,你有什麼專長、學習歷程或應用經歷?",那我們需要抓取最後的面試者敘述來做面試題目的生成。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/H1go2trckg.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 跟Day3一樣,先將指定Google 試算表ID填入,讓Google app script找到,並且對欄位index做宣告,以得到更好的閱讀體驗。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/r1-3jYBcyg.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 上述的步驟都跟day3的範例很像,重點是**如果Gemini要看到面試者的資訊,那就需要把資訊放在options中的payload** 我們設定變數Prompt,將面試者"對於科技知識與工具,你有什麼專長、學習歷程或應用經歷?"文字內容儲存到變數Prompt中,並且將Prompt變數包裝到payload。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/ryHlrgn5Jx.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 如果我們要發送請求,那一樣需要把url與option準備好,將剛剛的Prompt轉成JSON格式後,將其包裝到options當中,並利用`UrlFetchApp.fetch(url,options)`發送。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/SkVX2FB5kg.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 最終詢問的結果拆包後查看text的部分 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/rJuMAKB91g.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> Gemini就可以讀取到在Google 試算表上,面試者的資訊並且完成我們所想要的目標了! <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/rk3-y5rqkg.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 當然我們可以將Gemini回應存放到Sheet中。 這樣我們就有AI的輔助下,能夠做到快速生成需要面試面試者的個人化題目了! Day 15收工! ## 進階挑戰 >[!Warning] 進階目標 : 根據投稿組別,生成更準確的面試題目 >現在我們的公司有許多職位,每個職位所需負責的職責與人員需求皆有一定區別 >那我們如何讓Gemini,理解各組組別的差異,並且依照各組職責生成更好的面試題目? ><div style="text-align: center;"> <img src="https://hackmd.io/_uploads/BkZakqHqkx.png" style="width: 20%; border: 2px solid black; padding: 5px;" /> </div> >> 組別職責說明: >> 活動組:需要負責統籌規劃與科技或社團招生的活動,需要一定活動規劃能力 >> 技術組:需要能夠教學與編寫與現今相關的科技工具教材,需要一定教學經歷 >> 行銷組:需要負責社團社群經營,並且與校外尋求合作機會 >> 美宣組:需要輔佐社群經營,設計出符合當年主視覺設計圖 >> 財政組:需要能夠管理財務分配與審查金流與預算規劃 在使用生成式AI時,有一個觀念挺重要,那就是Prompt Engineering >[!Important]什麼是Prompt Engineering?其實你在點咖啡的時候就體驗過Prompt Engineering了! > 想像一下,你走進咖啡店,**對店員說:「我要咖啡」** > 你可能會得到任何一種咖啡如美式、摩卡,甚至甜度、冷熱、杯數都完全取決於店員的猜測,結果往往與你的預期不符,導致需要花費大量時間重新敘述要求。 > > 但如果你清楚地說: **「您好,我要點兩杯咖啡。一杯大杯冰美式,加一包糖,希望使用深度烘焙的咖啡豆;一杯熱拿鐵,改用燕麥奶,不加糖,拉花圖案希望是貓咪」** >這樣的指令更具體,店員能夠準確地提供你想要的兩杯咖啡。 > > 這就像在使用 AI 時的 Prompt Engineering——你的Prompt越清晰、細緻,AI 生成的回應就越符合你的期待。 為了使Gemini能夠理解面試者投稿組別間的職責差異,我們需透過Prompt工程,將各組的職責內容明確地提供給Gemini。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/rJftjcH5kx.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 首先,我們可以建立一份「組別職責對照表」,詳細列出每個組別的職責說明,作為 Gemini 理解各組差異的依據。 <div style="text-align: center;"> <img src="https://hackmd.io/_uploads/ryE3j5Bq1l.png" style="width: 100%; border: 2px solid black; padding: 5px;" /> </div> 在構建 Prompt 時,我們將依據當前面試者所投稿的組別,從「組別職責對照表」中擷取對應的職責說明,並將其納入 Prompt 中 如此一來,Gemini 就能夠根據面試者所屬組別的職責,生成更具針對性的面試題目 這樣進階任務就完成了!