WordPress 外掛整合 AI 工具請求


最近常用 AI 產生程式碼,結果一堆程式自己都看不懂,只有錯誤發生時才會回頭研究到底寫了什麼。想說乾脆寫篇文章記錄一下,當作學習筆記。本篇主題是 OpenAI API 與 WordPress 的整合,讓 AI 能直接操作 WordPress。

初探工具請求(Function calling

像 GPT 這類自然語言模型是靠訓練資料回應問題,但資料若過時,或想取得即時資訊,模型本身無法處理。這時可以透過「工具請求(Function calling)」來解決。

原理是:先告訴 AI 有哪些工具可用,當對話中出現相關需求,AI 就能自動判斷是否要使用某個工具。這些工具可以是 WordPress 的函式(如 WP_Query 查文章),也可以是外部 API(例如 Google 搜尋),執行後把結果再交給 AI 處理並產出回應,自然語言化地回答使用者。

適用場景

1. 整合客服機器人

如果你想設計聊天介面給其他人使用,像是客服機器人外掛,就很適合採用這個技術來取得網站內的資訊,像是讓客人查詢商品、訂單相關資訊、搜尋產品使用說明文件,或是讓管理員查詢營業額、網站瀏覽數據。

近期在開發的網站助理就是使用這個技術來讓管理者查詢訂單資料:

https://oberonlai.blog/dwp-site-assist/

2. 整合 LINE 聊天機器人

只要透過 Messaging API 的 webhook 呼叫指定的 API,再讓這個 API 去執行 OpenAI 的工具請求,就能把執行結果回傳到 LINE 裡面,這樣就能讓有加入官方帳號或是群組裡的好友直接使用你提供的工具。

例如查星座運勢、摘要表單內容、根據提交資料自動整理資訊等,都能透過工具組合不同資料來源,提供有脈絡的回答。像是萬一有客人問 A 產品的相關資訊,除了先讀取產品描述外,還能讀取近期的訂單資料、產品評價、相關問題,最後整理成有脈絡的回應給客戶:

「您詢問的 A 產品功能包含 XXX,最近有 X 位客人選購,其中 Y 留下好評,對於 XXX 特別讚譽,解決了原本的 X 問題。」

設定工具時需要以下要素:

  • 工具名稱:與你實作的函式名稱一致
  • 工具描述:自然語言說明用途,AI 判斷是否使用工具的依據
  • 工具屬性:呼叫時需傳入的參數(如 post_id、日期等)
  • 屬性描述:自然語言說明屬性的用途

AI 使用流程:

  1. 使用者提問
  2. 呼叫 API,附上工具清單
  3. AI 判斷是否使用工具
  4. 使用工具,取得結果
  5. 將結果回應給使用者應用案例

這邊以 OpenAI API 為範例,設計一個根據文章 ID 來取得文章內容的工具。在設計上我們先處理工具的回應結果,這邊用 get_post_content_by_id 來取得文章內容,這個函式帶有一個參數也就是文章 ID:

function get_post_content_by_id( int $post_id ): ?string {
$post = get_post( $post_id );

if ( ! $post || 'publish' !== $post->post_status ) {
return null;
}

// 回傳處理過的內文
return apply_filters( 'the_content', $post->post_content );
}

接下來定義工具的清單,需要包含上述提及的幾個基本要素:

$functions = array(
array(
'name' => 'get_post_content_by_id', // 工具名稱
'description' => 'Get the content of a post by post ID.', // 工具描述
'parameters' => array(
'type' => 'object',
'properties' => array(
'post_id' => array( // 工具屬性
'type' => 'integer',
'description' => 'The ID of the WordPress post.', // 工具屬性描述
),
),
'required' => array( 'post_id' ),
),
),
);

不管是工具描述還是屬性描述,記得要寫清楚,因為這是 AI 判斷呼叫工具的依據,然後工具名稱要跟上一步驟定義的函式名稱一樣,AI 才知道要呼叫誰,工具屬性 post_id 以及函式參數 $post_id 的也是要相同。

準備好工具後就可以開始請求 API 進行聊天:

$messages = array(
array( 'role' => 'user', 'content' => 'Please show me the content of post 42.' ),
);

$response = wp_remote_post(
'https://api.openai.com/v1/chat/completions',
array(
'headers' => array(
'Authorization' => 'Bearer ' . YOUR_OPENAI_API_KEY,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( array(
'model' => 'gpt-4.1-nano,
'messages' => $messages,
'functions' => $functions,
) ),
'method' => 'POST',
)
);

$data = json_decode( wp_remote_retrieve_body( $response ), true );
$tool_call = $data['choices'][0]['message']['tool_calls'][0];

在 body 的地方傳入 $functions 就代表這次的對話有提供我們的工具,回應的結果如果帶有 tool_calls 的話,就代表使用者的提問有觸發到我們的工具,接下來我們需要拆解 AI 從使用者提問中拿到的工具名稱以及屬性:

$function_args = json_decode( $tool_call['function']['arguments'], true );
$function_name = $tool_call['function']['name'];

判斷 $function_name 是 get_post_content_by_id 的話,就執行我們的函式取得工具執行的結果,這邊要注意的是回應給 AI 的結果有固定結構化格式,要依照每一家模型的規定組成要求的格式:

$post_content = get_post_content_by_id( $function_args['post_id'] );
$tool_response = array(
'role' => 'tool',
'tool_call_id' => $tool_call['id'],
'content' => $post_content ? $post_content : 'Post not found.',
);

然後連同工具執行結果再一次請求 API 來取得實際的回應內容,在第一次請求回傳的 tool_call 也要一併放在 message 中進行呼叫,才會讓 AI 知道第二次的請求是執行工具的結果,不然它會看成是一段新的對話請求:

$messages[] = array(
'role' => 'assistant',
'tool_calls' => array( $tool_call ), // 第一次請求回傳的工具資訊
);
$messages[] = $tool_response; // 工具的執行結果

$final_response = wp_remote_post(
'https://api.openai.com/v1/chat/completions',
array(
'headers' => array(
'Authorization' => 'Bearer ' . YOUR_OPENAI_API_KEY,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( array(
'model' => 'gpt-4.1-nano',
'messages' => $messages,
) ),
'method' => 'POST',
)
);

$final_data = json_decode( wp_remote_retrieve_body( $final_response ), true );
echo $final_data['choices'][0]['message']['content'];

注意事項

以上的基本架構就能完成最簡單的工具使用,可以進一步擴展並整合更多的工具。在使用多個工具的情況下有可能會回傳多個 $tool_call 陣列,因此要記得處理多筆資料的情境,另外如果你有實作對話紀錄讓 AI 可以根據上下文進行回答的話,記得要在提示詞裡面指定優先使用工具來取得資訊,不然不會觸發到工具的使用。

我用的提示詞如下:

You must always call a tool if one is available before attempting to answer. Do NOT guess. Do NOT use internal memory before calling tools.

另外有一些動態的資訊想要讓 AI 知道的話,也可以在提示詞裡面用變數的方式加入,我卡最久的是讓 AI 取得今天日期。由於要查詢報表需要有日期區間,每次問結果都錯誤,後來才發現它根本不知道今天是幾號就胡亂回答一通。

另外還有時區的問題,因此我先把這些資訊用 PHP 拿到後再放到提示詞裡面,確保它能正確理解這些背景知識:

$today = current_time( 'Y-m-d' );
$time = current_time( 'H:i:s' );
$timezone = get_option( 'timezone_string' );
$gmt_offset = get_option( 'gmt_offset' );
$timezone_info = $timezone ? $timezone : 'GMT' . sprintf( '%+g', $gmt_offset );

$start_of_week = (int) get_option( 'start_of_week', 1 );
$weekday_map = array(
0 => 'Sunday',
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
4 => 'Thursday',
5 => 'Friday',
6 => 'Saturday',
);

$weekday = $weekday_map[ $start_of_week ];

return "
You must always call a tool if one is available before attempting to answer. Do NOT guess. Do NOT use internal memory before calling tools.

Today is `{$today}` and time now is `{$time}`, based on the WordPress site timezone setting: `{$timezone_info}`. You must use the WordPress site timezone for all date-related logic.

When calculating a week range:
- The first day of the week is `{$weekday}` (from get_option('start_of_week')).
- If {$today} is the first day of the week, it **must be considered** the start of the current week.
- The date range must cover from this day through 6 days later (inclusive).
- All date ranges must be computed dynamically based on the current time in the WordPress site timezone.";

結語

AI 實在太方便,連範例程式碼都可以不用自己寫。這篇記錄也讓我回頭終於看懂之前 Cursor 產的程式碼到底在幹嘛 XD

果然寫文章是最好的學習方式!我們下週見~

參考資料

https://platform.openai.com/docs/guides/function-calling

WordPress 開發日常

Read more from WordPress 開發日常

有經營 LINE 官方帳號的站長對以下情境一定不陌生:顧客在 LINE 上詢問「我的訂單什麼時候到?」但打開 LINE 官方帳號後台,看到的只是陌生的好友姓名,跟官網後台的會員名稱完全對不起來、也不知道這個 LINE 會員買了什麼、訂單狀態如何。 為了解決這個問題,我們團隊歷經三個月的開發,期間訪談了許多位第一線經營 LINE 官方帳號的電商業主,根據他們的回饋,開發出這套專為 WooCommerce 所設計的客戶管理解決方案,目前該外掛已經進入穩定期,也是我們團隊每日必用的 LINE 管理工具。 為了可以協助更多不同產業的使用情景,我們將該外掛開源出來,歡迎有需要使用的朋友自由取用,以下介紹該外掛件的主要功能: 一、好友管理 截至目前撰文當下,在 LINE 官方後台是無法主動推播訊息給新加入或是尚未傳送過訊息的好友,透過本外掛可以將新加入的好友直接匯入網站會員資料庫,即可主動傳送訊息並開始進行聊天。 好友匯入有三種方式,分別是:當好友主動傳送訊息時自動匯入、從網站會員匯入以及從 LINE 官方帳號匯入,如果搭配 LINE 登入外掛如...

在思考 Bazewp 的商業模式時,第一直覺就是可以把聯盟行銷的連結放在外掛分析頁的查看價格按鈕上面,萬一有人透過該連結購買外掛就能賺得分潤,但由於外掛實在太多,要個別去申請對應的推廣連結也是一個大工程。 於是就在想會不會有平台已經整合好這件事:只要申請該平台的帳號,就能直接開通 Kinsta 或是 Elementor 等多家 WordPress 產品的聯盟行銷資格,而不用一個一個去開帳號,然後身為主機商或是外掛開發者想要找到推廣者,也能上架該平台讓人推廣。 抱持著這樣的好奇心,第一份 WordPress 市場研究報告就決定以聯盟行銷外掛為主題,以下報告結合 Bazewp 的外掛數據,並使用 AI 的深度研究功能來獲取初稿,再經由人工驗證與潤稿,希望透過這樣的分析能找出該市場的潛在機會。 以下為報告全文: 網頁好讀版:https://oberonlai.blog/wordpress-affiliate-plugin-eco/...

時常看到新聞寫 WordPress 的市佔率全球之冠,或是今年市佔又提升了多少個百分點,每次看到我都會想,全世界到底有多少個網站是用 WordPress 做的?這些網站都是什麼類型的網站?他們都用了什麼佈景主題或外掛?甚至都是哪些產業在使用? 為了更進一步挖掘 WordPress 的生態系,我決定做一個專門搜集 WordPress 網站的網站,利用爬蟲去找出使用 WordPress 架設的網站,然後搜集佈景主題以及外掛資訊,並且使用 AI 進行分類與辨識: Bazewp.com 的主要功能介紹如下: 1. WordPress 網站搜尋 首頁的部分可以直觀的看到網站列表以及篩選工具,每個網站搜集的資訊有: 網站名稱、描述、語系、截圖、配色 產業類型、網站類型 網站關鍵字 使用的佈景主題、外掛 聯絡資訊、社群連結 在電腦板上面可以用分隔視窗查看單一網站的資訊,左側顯示網站列表,方便在瀏覽時可以快速切換,在右側邊欄展開的情下重新整理頁面,則會直接進入單一網站的介紹頁,方便後續瀏覽與分享。...