開發 WordPress AI 外掛:一次與 Cloudflare Worker 和 WordPress 的左右互搏 Building a WordPress AI Plugin: A Battle with Cloudflare Workers and WordPress
開發 WordPress AI 外掛:一次與 Cloudflare Worker 和 WordPress 的左右互搏
Building a WordPress AI Plugin: A Battle with Cloudflare Workers and WordPress
由於我還有朋友在新聞採訪的領域奮鬥,想要用AI幫點什麼忙,所以陸續開發迭代幾次的輔助寫作功能。這次完成的 WordPress 外掛主要功能如下:
Since I have friends still working hard in the field of news reporting, and I wanted to use AI to help them out, I’ve been iteratively developing writing assistance features. The WordPress plugin completed this time has the following main features:
- 文字改寫: 支援可自訂的提示模板 (如:新聞編輯、專業風格)。
- 圖片替代文字: 自動為圖片生成無障礙描述 (alt text)。
- 文章重寫: 保留原始格式重塑全文,需圖文穿插。
- Text Rewriting: Supports customizable prompt templates (e.g., news editing, professional style).
- Image Alt Text: Automatically generates accessible descriptions (alt text) for images.
- Article Rewriting: Reshapes the entire article while preserving the original format, requiring text-image interspersion.
然後這段文字紀錄的由來是,我一開始先請 Claude 運用我的 commit log 產生。然而產出的結果實在太過機器化,我看得受不了,便開始想該怎麼改善。
The origin of this text record is that I initially asked Claude to generate it using my commit log. However, the output was too mechanical, and I couldn’t stand reading it, so I started thinking about how to improve it.
我試著找了 Twitter 上的發文,想用 AI 去分析我的寫作風格,但從推文分析出來的文章卻又太過尖銳,導致寫作風格變成一種「奇怪、機械、尖銳又做作」的文字,實在吞不下去。
I tried finding my tweets on Twitter, wanting to use AI to analyze my writing style, but the article analyzed from tweets was too sharp, resulting in a writing style that was “strange, mechanical, sharp, and contrived” - I really couldn’t swallow it.
所以,我用語音輸入,對著 AI 產出的文稿碎碎念了一堆改善事項,才終於產出以下這篇部落格文字。我認為這個版本比較好接受,讀起來也比較平易近人。
So, I used voice input to ramble on about a bunch of improvements to the AI-generated draft, and finally produced this blog post. I think this version is more acceptable and reads more approachably.
最後還是經歷了一半內容的手工修改。直接讓 AI 產文實在不是個好點子,以這次的經驗來說,我想產出一個架構用來改寫應該會比較好。
In the end, I still went through manual editing of half the content. Letting AI generate text directly is really not a good idea. Based on this experience, I think it would be better to generate a framework for rewriting.
開發故事是這樣開始的
How the Development Story Began
這是一次大約 30 個工時的開發紀錄,目標是打造一個能串接多個 AI 供應商(Gemini、OpenAI、Anthropic)的 WordPress 外掛。這次的核心需求是,利用 Cloudflare Workers 作為一個安全代理,來避免任何 API 金鑰暴露在前端。
This is a development record of approximately 30 hours, with the goal of creating a WordPress plugin that can integrate with multiple AI providers (Gemini, OpenAI, Anthropic). The core requirement was to use Cloudflare Workers as a secure proxy to prevent any API keys from being exposed on the frontend.
最終的成品,希望能做到改寫文字、為圖片生成替代描述,以及重塑整篇文章的結構。
The final product was expected to rewrite text, generate alternative descriptions for images, and reshape the structure of entire articles.
聽起來是個不錯的計畫。然而,在開發過程中,我發現真正的挑戰並不是程式碼的複雜度,而是我對這些工具的特性不熟悉,以及因此踩到的幾個大坑。
It sounds like a good plan. However, during development, I found that the real challenge wasn’t the complexity of the code, but rather my unfamiliarity with the characteristics of these tools, and the major pitfalls I stepped into as a result.
最初的構想
The Initial Concept
我主要使用Cloudflare Worker作為API呼叫的入口,之前幾次使用我對他已經有一點熟悉度了。
I mainly used Cloudflare Worker as the entry point for API calls, having gained some familiarity with it from previous uses.
利用worker的功能加上一道白名單構成的防線,確保只有授權使用者才能存取。
Using the Worker’s functionality combined with a whitelist as a defense layer, ensuring only authorized users could access it.
流程很清楚:WordPress 的請求被送往 Cloudflare Worker,由它作為代理,與後端的 AI API 溝通,再將結果送回。
The flow was clear: WordPress requests were sent to Cloudflare Worker, which acted as a proxy to communicate with the backend AI API, and then sent the results back.
專案啟動與基礎功能
Project Launch and Basic Features
萬事起頭,總是最順利的。開發從最基礎的結構開始:建立專案,並試圖與 WordPress 的生態系串接。
The beginning is always the smoothest. Development started from the most basic structure: setting up the project and attempting to integrate with the WordPress ecosystem.
總之開了兩個資料夾,worker/ 和 plugin/ 兩個目錄分別容納了 Cloudflare 和 WordPress 的程式碼。
In short, I created two folders: worker/ and plugin/ directories to house Cloudflare and WordPress code respectively.
WordPress 將圖片轉為 base64 編碼,再由 Worker 交給 Gemini 分析產出描述。然後為 plugin 介面增加 i18n,這點對於 claude 來說都是小菜一碟。由於我之前就已經迭代過使用 gemini 分析圖文的幾個版本,因此這一段執行很快。
WordPress converted images to base64 encoding, which Worker then passed to Gemini for analysis and description generation. Then I added i18n to the plugin interface, which was a piece of cake for Claude. Since I had already iterated through several versions of using Gemini to analyze images and text, this part went very quickly.
疊加進階功能與風暴前夕
Adding Advanced Features and the Calm Before the Storm
隨著進階功能的加入,一些麻煩也開始醞釀。
As advanced features were added, some troubles began to brew.
API 用量追蹤是個必要功能。為了避免用量失控,使用 Cloudflare KV 來打造一個簡易的用量儀表板。不過它一開始沒辦法正常運作,讀得到但是寫不進去。
API usage tracking was a necessary feature. To prevent usage from getting out of control, I used Cloudflare KV to build a simple usage dashboard. However, it couldn’t work properly at first - it could read but couldn’t write.
再進一步加入 可自訂的提示模板功能,讓使用者可以自訂提示詞。再來是另外有一個奇怪的問題,我不知道是Gemini還是其他AI也有類似的方法,就是產生文章的時候,也會隨機產生圖片的網址,說是為了保護內容啦…?所以不能用純文字的方式丟給AI,必須要使用 markdown 格式,AI 才不會更動網址。當然還需要使用 canvas 縮小圖片,才能加速產生的流程。
Further adding the customizable prompt template feature, allowing users to customize prompts. Then there was another strange problem - I don’t know if it’s Gemini or other AIs that have similar behavior - when generating articles, it would also randomly generate image URLs, supposedly to protect content…? So I couldn’t pass it as plain text to the AI; I had to use markdown format so the AI wouldn’t modify URLs. Of course, I also needed to use canvas to shrink images to speed up the generation process.
開發後期的兩場硬仗
Two Hard Battles in Late Development
在開發後期,所有累積的技術債和我不熟悉的地方,一起爆發了。
In the late development stage, all accumulated technical debt and my unfamiliarity with certain aspects exploded together.
問題一:Canvas 的「無聲失敗」
Issue 1: Canvas’s “Silent Failure”
測試中一則錯誤訊息不斷跳出:「無法建立縮放後的圖片 blob」,在看了各種 log 之後,終於抓到 canvas.toBlob() 的「無聲失敗」。元兇就是:CORS 與畫布污染(canvas tainting),如果沒有解除 CORS,canvas 就沒辦法正確的render,當然我也沒辦法用圖片轉述文字。
During testing, an error message kept appearing: “Unable to create scaled image blob”. After looking at various logs, I finally caught the “silent failure” of canvas.toBlob(). The culprit was: CORS and canvas tainting. If CORS isn’t resolved, canvas can’t render correctly, and of course I couldn’t convert images to text.
問題二:Cloudflare KV 的「失憶症」
Issue 2: Cloudflare KV’s “Amnesia”
第二個問題在於,我明明使用了 API,但卻沒有成功地轉換為 Cloudflare KV 的紀錄數據。用 curl 測試 API 時,一切看似順利,但 Cloudflare KV 儲存區裡的值始終是 0。更離奇的是,單獨測試時全都運作正常,KV 跟程式本身沒有問題;但是在真實情境下就會失敗。
The second problem was that even though I was using the API, it wasn’t successfully converting to Cloudflare KV record data. When testing the API with curl, everything seemed smooth, but the values in the Cloudflare KV storage were always 0. Even more bizarre, when tested separately, everything worked normally - KV and the program itself had no issues; but in real scenarios, it would fail.
在反覆檢視後,發現問題來自於 Cloudflare Workers 的特性:它是一個臨時工,不是長工。 在向客戶端送出回應(response)後,它的 lifecycle就停了,紀錄還來不及寫入。所以要使用 ctx.waitUntil() 將這個非同步任務包裹起來。
After repeated examination, I discovered the problem came from a characteristic of Cloudflare Workers: it’s a temporary worker, not a permanent one. After sending a response to the client, its lifecycle stops, and the record doesn’t have time to be written. So I needed to use ctx.waitUntil() to wrap this asynchronous task.
最終的樣貌
The Final Form
這大約 30 個工時奮鬥的終點,是一個由超過 1,200 行 TypeScript(Cloudflare Worker)和近 2,000 行 PHP/JavaScript(WordPress 外掛)構成。它支援多家 AI、能處理圖像、追蹤用量,並在 WordPress 區塊編輯器中實現了整合。
The culmination of approximately 30 hours of effort was a system consisting of over 1,200 lines of TypeScript (Cloudflare Worker) and nearly 2,000 lines of PHP/JavaScript (WordPress plugin). It supports multiple AI providers, can process images, track usage, and is integrated into the WordPress block editor.
結語
Conclusion
第一次在 serverless 上打造一個可用的程式,的確是蠻有趣的。我學到了很多東西,覺得這是透過 AI 增強自己的開發能力(以及學習過程中不要那麼容易玻璃心)的最佳範例。
Building a working program on serverless for the first time was indeed quite interesting. I learned a lot and feel this is the best example of enhancing one’s development capabilities through AI (and not being so easily discouraged during the learning process).
對使用的工具要抱持著健康的懷疑、徹底測試、留下詳盡的 log,未來的你將會無比感激;然後學會擁抱限制,利用限制刺激創意,檢視自以為的知識。
Maintain healthy skepticism toward the tools you use, test thoroughly, and leave detailed logs - your future self will be extremely grateful. Then learn to embrace limitations, use limitations to stimulate creativity, and examine knowledge you thought you had.