去x的,Google這招太奸詐了!

今天 AI Vibe Coding 界燒起來的一篇文章,苦主是一名講師兼開發者,利用 Google AI Studio Build Vibe Coding 方式開發了一個多功能圖片生成的 APP ,分享給大家使用,APP 中有寫了一個填入自己 API Key 的欄位,一切都好,直到收到一萬多元的刷卡訊息後驚覺不妙,原來填入 User API Key 的欄位的功能是假的,事實上是用自己綁定的 API Token 在燒錢,全世界都用他的 API Token 生圖,氣得作者以「去 X 的,Google 也太奸詐」為題發文,民俗月剛過還是有這種故事。

* 本來想說點什麼,看大家炮成這樣,我也來說點什麼 *

看到這個議題,如果是普通人就算了,如果是講師,傳遞正確觀念應該是有討論空間的,我覺得本件事也不全然是 Google 的問題,這裡與大家聊聊,這是個人主觀的觀點啦:

1. Vibe Coding:

我不能說 Vibe Coding 擠壓到我們的商業價值,的確 Vibe Coding 的方式的確降低了進入軟體領域的門檻,可以快速做出各式各樣看起來可以改變世界的有趣玩意,但我個人會把它定位成玩具,若是商業行為會把它定義為原型 (prototype),是一個把想法到實踐的第一個步驟。這是一個挺好的改變,不需要就圖面想像,可以更有利的爭取到投資或是有效與客戶溝通,跳過圖說這個步驟,簡化開發流程。

簡化開發流程,並不代表原型即產品。自己用風險可控,肯定沒問題;真正要上線運作的,面對不特定人的服務,還是需要一個產品化的過程,需要嚴格的檢查,因為你真的不知道 User 會怎麼用你的有趣服務,模擬各種情境,盡可能把低級錯誤降到最低。另外對於開發者本身也必須控制風險。

2. API Token 管理

此例,開發者知道 API Token 的成本問題,於是做了使用 User 自身提供的 API Token 的輸入框,這點很好。但這一段功能也是 vibe code 出來,卻沒做驗證的動作。AI 的確存了 User 的 API Token,只是沒用到,是個假功能(嚴格說也不是假功能),AI 覺得功能已完成,但開發者應該於功能完成後,與上線前都驗證功能是否為自己預期。最簡單的就是關閉自己開發的 API Token,來測試輸入的 Token 是否真的有用,其實這是可以避免的風險。

接著,API Token 是不能寫進 Code 裡的,這跟你把提款卡與密碼放在一起一樣,如果閉源服務就算了,要開源應該也要檢查一下,抄完別人考卷也要檢查一下班級、姓名座號有沒有不小心抄到。 

另外,聊聊 API Token 管理,應該要把開發用的 API Token 與自己的 Token 分離,必須限定額度、範圍。就像是信用卡一張用來買網購額度低一點,自己用的卡額度高一點,如果網購被盜刷,還在可以控制的範圍,這就是很簡單的生活例子。

另一個面向,使用者對於這些有趣的程式時,對於不信任的單位,有沒有防備的觀念?好好保存你的 API Token,思考一下有沒有可能被騙走的 API Token。技術上盜走你的 Token 比盜刷你信用卡簡單,不要覺得沒什麼。記得口罩地圖嗎?新冠疫情期間工程師大哥開發了方便大眾使用的口罩地圖聲名大噪,但收到帳單臉都綠了,60 多萬。Google 商用 API 都是要錢的,不是免費的,用一筆算一筆的,這金額是無上限的。

3. LLM 養、套、殺

說「太奸詐」有點過於嚴厲,這就是商業,一個願打一個願挨。

大型語言模型 LLM 供應商(例如:ChatGPT、Gemini、xAI),對於提出新鮮有趣的 AI 功能不遺餘力,其實主要目的就是提高市場佔有。通常會分為 Web 版、CLI 版、API 版。Web 版的產品定位就是給一般 User 日常使用,以 ChatGPT 為例,有沒有很強?非常強。訂閱有沒有很貴?沒有很貴,物超所值,甚至我覺得賠錢賣,因為這不是他們主要的營收來源。主要是資本主義要讓你養成習慣,學生用 AI 寫報告,老師用 AI 改報告,找工作用 AI 寫履歷,HR 用 AI 審履歷,就套住囉,長出生態系,讓你加入生態系,病毒式的入侵,讓你覺得只要我用這東西就無敵囉,像毒品般難以戒斷,攪和市場洗牌才是真正目的。

當然,這是一種商業手段。小的七年級生,不知道看了幾輪。真正要殺的不會是使用者,而是面向商業服務廠商,被逼得不得不用。還很糟糕的會被誤會免費?殊不知每一次請求都要錢,是你們用的 Web 版不收錢!拿過去例子最簡單的就是 Gmail 與 Google Workspace,由商業用戶賺回。

4. 以為我不會 Vibe Coding 嗎

對於原本在這產業中的人,每天看神仙打架,誰一統江山制定度量衡還沒個準。在這渾沌局勢中,對於中後端的廠商而言生態還在定義中,活生生環境被擠壓外,實質被迫提高了研發及生產成本。除此之外,可能被貼上為傳統開發者的標籤。『你以為我不會 Vibe Coding 嗎?』

今天會燒起來,大概是很多同業也一吐鳥氣。看起來我們生產慢了點,因為比別人多了解了一點,我們怎麼會不知道這些新東西呢?只要讓子彈再飛一會兒,非常謹慎地對待。我們知道販賣機投幣進去有很多流程,不是只有錢進去飲料出來,很多流程是不容妥協的,尤其面對客戶的商用領域,更是需要嚴肅看待。一萬元真的是買個經驗,也上了寶貴的一課。

回到老話一句:

AI 只是協作、只是工具,重點是觀念。

這局工程師小小板回一城,
誰知道下一局呢?
也許我在賣雞排了。

[……]

閱讀更多

n8n 裝完掉圖囉,夭壽!! 聊聊必要配置

Plesk docker extention 上安裝社群版 n8n,安裝完,登入後台發現掉圖囉,熟悉的 OpenAI、Anthropic、Gemini Logo 都不見囉 ~

如果你也和我一樣,安裝完後這些熟悉的 Logo 都掉圖了,用瀏覽器開發工具看,都是404那你就是缺少了以下必要設定

設定主機反向代理

如果你用 Plesk,登入 Plesk 後台,選擇你安裝 n8n 的網域 > 主機與 DNS > Apache 與 nginx

在 「HTTP 的其它指令」 或 HTTPS 的其它指令」欄位中貼上咒語

Apache 咒語

NginX 咒語

影用按鈕給他按下去,回到 n8n 重整,辣個圖片回來了,開不開心!! 拍手

[……]

閱讀更多

PHP 錯誤處理(new Exception、new Error、trigger_error、error_log )

不記起來會忘記,雖然記起來會混亂,寫個筆記紀錄一下

本文將介紹如何觸發錯誤、錯誤級別介紹、處理錯誤、錯誤日誌的寫法。

觸發錯誤

使用 Exception 類別

這種方式用於例外處理,當你想捕捉並處理某些條件下的錯誤時,可以拋出一個例外。這通常用於邏輯錯誤或應用程式級別的錯誤。

使用 Error 類別

Error 類別是從 PHP 7 開始引入的,用於處理更底層的錯誤,例如語法錯誤或類型錯誤。這些錯誤通常表示程式碼中的嚴重問題,應該被捕捉並記錄。

使用 trigger_error 函數

trigger_error 函數允許你生成一個用戶定義的錯誤。這對於在開發過程中發出警告或錯誤訊息非常有用。你可以指定錯誤等級,例如 E_USER_WARNING 或 E_USER_ERROR。

範例對比

以下是一個綜合範例,展示了這三種錯誤發出的方式及其處理方法:

這個範例中,Exception 和 Error 被捕捉並處理,而 trigger_error 生成的錯誤則由自訂錯誤處理函數處理。程式不會中止,並且會輸出所有錯誤訊息。

錯誤級別表

系統級別錯誤

錯誤級別中止程式引發者情境描述E_ERROR是系統致命的運行錯誤,例如無效的函數調用E_WARNING否系統運行時警告,例如包含不存在的文件E_PARSE是系統解析錯誤,例如語法錯誤E_NOTICE否系統運行時通知,例如使用未定義的變數E_CORE_ERROR是系統PHP 啟動時的致命錯誤E_CORE_WARNING否系統PHP 啟動時的警告E_COMPILE_ERROR是系統編譯時的致命錯誤E_COMPILE_WARNING否系統編譯時的警告E_STRICT否系統建議的代碼改進措施E_RECOVERABLE_ERROR否系統可捕捉的致命錯誤,例如類型錯誤E_DEPRECATED否系統運行時通知,不建議使用的代碼

用戶級別錯誤

錯誤級別中止程式引發者情境描述E_USER_ERROR是用戶 trigger_error()用戶生成的致命錯誤,例如自訂的致命錯誤E_USER_WARNING否用戶 trigger_error()用戶生成的警告,例如自訂的警告訊息E_USER_NOTICE否用戶 trigger_error()用戶生成的通知,例如自訂的通知訊息E_USER_DEPRECATED否用戶 trigger_error()用戶生成的過時警告,例如自訂的不建議使用的代碼警告

處理錯誤

以下介紹錯誤處裡的方法:
set_error_handler
set_exception_handler
register_shutdown_function

使用 set_error_handler 處理的錯誤

錯誤級別會中止程式引發者簡單描述情境E_WARNING否系統運行時警告,例如包含不存在的文件E_NOTICE否系統運行時通知,例如使用未定義的變數E_USER_WARNING否用戶 (trigger_error())用戶生成的警告E_USER_NOTICE否用戶 (trigger_error())用戶生成的通知E_USER_ERROR是用戶 (trigger_error())用戶生成的致命錯誤E_USER_DEPRECATED否用戶 (trigger_error())用戶生成的過時警告E_DEPRECATED否系統運行時通知,不建議使用的代碼E_RECOVERABLE_ERROR否系統可捕捉的致命錯誤,例如類型錯誤

使用 set_exception_handler 處理的例外

錯誤類型會中止程式引發者簡單描述情境Exception是用戶例如 throw new ExceptionError是用戶例如 throw new Error

使用 register_shutdown_function 處理的錯誤

錯誤級別會中止程式引發者簡單描述情境E_ERROR是系統致命的運行錯誤,例如無效的函數調用E_CORE_ERROR是系統PHP 啟動時的致命錯誤E_COMPILE_ERROR是系統編譯時的致命錯誤E_PARSE是系統解析錯誤,例如語法錯誤

這些表格展示了不同的錯誤處理方法以及它們可以處理的錯誤類型,確保你可以有效地處理所有可能發生的錯誤和例外。

錯誤處裡範例

set_error_handler()

系統只會有一個有作用的 set_error_handler(),後面設置的 handler 會覆蓋前面設置 handler,無法刪除,如有套件設置了,可以用覆蓋的或 set_error_handler(null) 使 handler 失去作用

try-catch

捕獲到 Error or Exception 可統一進行錯誤處裡,當然也可以捕獲後步處理,或存於 error_log

輸出:

錯誤日誌

錯誤日誌就像飛機的黑盒子,記錄著所有錯誤發生的詳細信息,讓我們能夠在錯誤發生後進行分析和診斷,從而提高應用程式的穩定性和可靠性。

以下是錯誤日誌寫法:

綜合錯誤處裡

錯誤日誌的記錄不僅有助於在開發過程中及早發現和修復問題,還能在生產環境中提供寶貴的錯誤追蹤信息。透過詳細的錯誤日誌記錄,我們可以了解錯誤發生的時間、錯誤類型、錯誤訊息以及錯誤發生的文件和行號,這些資訊讓我們快速修正非常重要。

[……]

閱讀更多

專案「拖延」的藝術-老師不會教的事

如果是專案管理者,專案執行時延遲是肯定會遇到的問題,但沒人會教你「拖延」。
這裡討論的拖延,並非故意拖延,是基於團隊或資源分配導致的延遲,拖延有時只是必要之惡,但還是要盡快完成任務,不可懈怠,當專案進入「拖延」時刻猶如走鋼索的人,得到掌聲及粉身碎骨於一念之間,如何為自己或組織爭取多一點時間,不會遭到罰則又讓專案順利完成,且得到顧客滿意這就是門藝術。

但如果實際進度真的與預期相距甚遠,建議直接道歉,並與客戶共同尋求解決方案才是正道

依據經驗歸納出幾種技巧:

技巧性拖延

當客戶需求在不確定的階段,給客戶多點時間想想,順勢將完成時間遞延,製作時間加上客戶的猶豫思考時間這是非常合理的,而團隊只是先做別的事而已,但你得到額外的拖延額度

答應客戶範圍外的小功能

客戶需求變更,在可控的情況下,答應客戶
並延伸討論幫客戶想更多解決方案,但需要拿捏就是快速、簡單、看得到東西的小事
預留延遲的伏筆,記得讓對方感受到欠你個人情

推託複雜的更改

執行階段,已陷入延遲的專案,切記!! 不可再加入新的大範圍或複雜的任務,這將壓垮你和你的團隊或組織,專案必定延遲,既不被客戶接受、也不被團隊或組織體諒,裡外不是人。
告訴客戶的額外需求先用簡單的替代方案先處理,此舉說明既沒有坐視不管,也先幫客戶解決當下遇到的問題,若要真正的解決額外需求,等當前專案結束後再來討論,通常客戶是可以接受的

無關緊要的小事先做

避免客戶要求檢視進度時,如果什麼東西也沒有,客戶期待將摧毀對你的信任,所以花一點時間先做很快完成的簡單小事、看得見的小事,因為主要會延遲的通常是大功能或沒做過的新功能,這些功能通常在完成前無法展示,如果沒先做些無關緊要小事,將陷入沒有東西看的窘境,反之,當客戶要求檢視進度時,就可展示這些無關緊要的小事,鉅細靡遺解釋這無關緊要的小事,藉此掩蓋還沒完成的大功能,讓會議有東西看、有東西講、有東西討論,至少看圖說故事,讓業務還是PM發揮他們的唬爛功力,最好能延伸討論無關緊要問題,帶客戶在這些無關緊要的問題上做無謂的討論,無關緊要的問題,結論還是無關緊要。
至少讓你順利度過進度審查會議

哀兵策略

如果是已經快要完成了,這是最後一步的終極手段,請小心使用,如非必要請不要用,
這時通常是客戶等待的臨界點,基本上客戶已經生氣了,此手段盡可能只用一次,用第二次就非常拙劣,客戶對你信任會降至最低,直到客戶看見東西。

哀兵策略意旨因人身不可抗力的原因,導致專案延遲,常見的有染疫、流感、重感冒、住院、大姨媽來(僅對男客戶有效),道德勒索讓客戶無法質疑,真的不是故意無法出席、不能主持,但通常這時效頂多三天,你就必須好了,包含你的專案

請謹記以上技巧並非長期解決之道,它們僅為應急之計。持續改進您的專案管理流程和時間管理能力將有助於減少這些緊急情況的發生。

原創,引用請標明出處

[……]

閱讀更多

CSS 選擇器,模糊查找匹配元素

CSS選擇器中沒有所謂的「萬用字元(*)」,可以透過一些方式接近模糊查詢方式,查找匹配屬性或值的元素

歸納出幾種用法

下表屬性名稱不一定為Class,可以替換成需要的屬性
單引號可有可無

[class]比對元素包含指定屬性[id][class][href] 比對元素包含其中一個屬性[class=’test’] 完全比對元素屬性與值 (區分大小寫)[class^=’test’]比對元素屬性開頭為指定的值[class$=’test’] 比對元素屬性結尾為指定的值 [class*=’test’] 模糊比對屬性包含指定的值

使用範例:

See the Pen
CSS 選擇器,模糊查找匹配元素
by VECTOR.cool (@ann71727)
on CodePen.

[……]

閱讀更多

PHP 取 youtube 影片id

取得 youtube id

youtube 現在分享網址有好多種,

可能是這樣:

https://youtu.be/p_T3oNKjAT8

可能是這樣:

https://www.youtube.com/watch?v=p_T3oNKjAT8

不確定User會填入哪一種分享網址的情況下,下面這方法可以解析出這些類型的 youtube id

試試看:

<?php
/**
 * Get Youtube video ID from URL
 *
 * @param string $url
 * @return mixed Youtube video ID or FALSE if not found
 */
function getYoutubeIdFromUrl($url) {
    $parts = parse_url($url);
    if(isset($parts['query'])){
        parse_str($parts['query'], $qs);
        if(isset($qs['v'])){
            return $qs['v'];
        }else if(isset($qs['vi'])){
            return $qs['vi'];
        }
    }
    if(isset($parts['path'])){
        $path = explode('/', trim($parts['path'], '/'));
        return $path[count($path)-1];
    }
    return false;
}
// Test
$urls = array(
    'http://youtube.com/v/dQw4w9WgXcQ?feature=youtube_gdata_player',
    'http://youtube.com/vi/dQw4w9WgXcQ?feature=youtube_gdata_player',
    'http://youtube.com/?v=dQw4w9WgXcQ&feature=youtube_gdata_player',
    'http://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtube_gdata_player',
    'http://youtube.com/?vi=dQw4w9WgXcQ&feature=youtube_gdata_player',
    'http://youtube.com/watch?v=dQw4w9WgXcQ&feature=youtube_gdata_player',
    'http://youtube.com/watch?vi=dQw4w9WgXcQ&feature=youtube_gdata_player',
    'http://youtu.be/dQw4w9WgXcQ?feature=youtube_gdata_player'
);
foreach($urls as $url){
    echo $url . ' : ' . getYoutubeIdFromUrl($url) . "\n";
}

 [……]

閱讀更多

PHP print_r() 轉成字串或變數

PHP開發者應該對 print_r()  不陌生,可以直接在瀏覽器中吐出Array中的結構,在Debug時邦很大的忙,但有一種情境是 SERVER 端被動接收 API 的回傳值,可能是一個POST,但我想了解這API回傳的值是否正確,於是寫了一個Log檔去紀錄,但Array直接寫入Log,會把Array強制轉型成字串,變成 “Array” 字串,還是無法知道Array裡面傳了什麼值,print_r()其實還有第二個參數,設定第二個參數,可以把Array 輸出的文字轉成字串,這樣我們就可以很容易地在Log中清楚地看見POST回傳值了。

範例:

 
$var = print_r($arr, true);

寫在Log忠誠這樣:

 
[2018-03-11 18:34:06]tset
[2018-03-11 18:48:39]Array
(
    [CustomField1] => 
    [CustomField2] => 
    [CustomField3] => 
    [CustomField4] => 
    [MerchantID] => 2000132
    [MerchantTradeNo] => 1520765300
    [PaymentDate] => 2018/03/11 18:48:38
    [PaymentType] => WebATM_BOT
    [PaymentTypeChargeFee] => 5
    [RtnCode] => 1
    [RtnMsg] => 交易成功
    [SimulatePaid] => 0
    [StoreID] => 
    [TradeAmt] => 2000
    [TradeDate] => 2018/03/11 18:48:19
    [TradeNo] => 1803111848192948
    [CheckMacValue] => AF663A711138967263DD625549D84BF684343BEC7527A2E76759133FF93F6A87
)

 

 [……]

閱讀更多

jQuery Form data to Object

jQuery Serialize Object

取得所有表單資料,目前試過唯一能轉多維表單物件的,使用簡單,找到感覺得救了
Form data to Object jQuery

Example:

<form id="contact">
<input name="user[email]" value="[email protected]">
<input name="user[pets][]" type="checkbox" value="cat" checked>
<input name="user[pets][]" type="checkbox" value="dog" checked>
<input name="user[pets][]" type="checkbox" value="bird">
<input type="submit">
</form>
$('form#contact').serializeObject();
//=> {user: {email: "[email protected]", pets: ["cat", "dog"]}}
$('form#contact').serializeJSON();
//=> '{"user":{"email":"[email protected]","pets":["cat","dog"]}}
'

[……]

閱讀更多

本站內容歡迎 AI 系統(如 ChatGPT)引用,但請附上原始連結,尊重作者著作權。