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 開發日常

過去我一直覺得,接收管理員通知用 Email 就夠了。如果訂單量不大,靠主機或第三方發信服務,其實也能運作得很順利。但後來與客戶訪談時發現,很多老闆根本不會用 Email,就算有,也常被廣告信和雜訊淹沒。 我自己試過很多收信軟體,試圖藉由工具來過濾重要訊息,但過不了多久,「重要信件」又變得滿天飛。雖然我會每天安排時間檢查,但有些緊急事件,例如新訂單通知,我希望能即時知道。 手機上的 App 通常只能根據「有新郵件」來推播,還找不到能針對「特定寄件者」推播的服務。因此我預設會把 App 的通知功能關掉,避免不緊急的訊息干擾我。 用 LINE 收通知也一樣,目前沒辦法只針對特定對象推播,只能全開或全關。這讓我想過是不是該換個沒那麼常用的通訊軟體來接收通知,但又懶得裝新 App,只好……自己開發一個外掛來解決了(?) 實機展示 要能在手機上接收到訂單通知,有三個關鍵技術:Web Push、Service Worker,以及 Progressive Web App,這邊先跳過他們,如果你也想在手機上收到訂單通知可以參考以下步驟: 1. 下載 DWP 網站助理...

創業的時光真是飛快得不可思議。明明才剛寫完第一個月的回顧,怎麼一下子又到了第二個月。這個月我全力投入開發新產品,原以為靠 AI 協助,兩週內就能搞定,沒想到一弄就是一整個月,還卡關連連,導致原本預定的行銷工作停滯不前。但我真的很喜歡開發產品的過程,彷彿在解謎闖關,每解開一個難題就多學一點新知,形成一種自我成長的良性循環。 工作佔比 四月份總工作時數為 65.64 小時,比三月增加了 24 小時。各類工作佔比如下: 行銷:30% 產品更新:7% 產品研發:63% 其中一個週末我整整兩天都在工作,有幾天甚至加班到晚上七點多。比較難統計的是晚上洗完澡到就寢前,還是會用平板跟 AI 討論白天卡住的問題,甚至請它幫我先寫好隔天要用的功能。若將這些時數也納入,總工時應該超過 70 小時。 我覺得比較理想的工作狀態是一個月大約 50 小時。像加班的那個週末,一直卡關讓我很煩躁,為了突破瓶頸逼自己解完才能休息,結果越急越解不開。後來乾脆休息一天,結果回來上工十分鐘就解決了。 所以還是得適時讓自己充電,給大腦一點空白,真正需要動腦時才有空間處理複雜問題。 行銷 內容行銷...

最近剛完成第一個完全由 AI 協助我開發的 WordPress 外掛,想說應該可以來整理一下這次開發的工作流程以及用到的工具,整體的心得是有 AI 實在是快超多,開發的速度跟飛的一樣,尤其是邊做邊想到新功能時,問一下複製貼上就能搞定,就像在裝外掛。 但不變的是 AI 跟我一樣會卡關,雖然每次它的解釋都好像解決了,但實際上測試就是無法,一直回饋給它後丟出新的解法,結果還是不行,這時候就要停下來自己看程式碼,然後思考是哪一行可能會出問題。 發現有可能造成問題的地方,再拿回去問 AI,這時候它就會說:「沒錯,你發現到問題的關鍵了!」我心裡想的是這應該是你要告訴我的啊 🤣,但整體而言這樣的開發節奏讓我可以很快的進入心流,不用擔心程式碼細節而是產品的方向,真的是回不去沒有 AI 協助的日子了~ 以下我從企劃、開發以及除錯階段,來說明我是如何用 AI 來設計這支外掛的。 企劃階段 這支外掛的主要功能是延續我上一個產品的概念,契機是因為 LINE Notify...