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

先講結論:錢燒完了、產品賣不動,明年要先接案子過活了XD 雖然產品都沒有成功,但卻累積了一些不錯的失敗,知道哪些路不能走,以及投入實戰 AI 開發後看見全新的風景與機會,這些經驗讓我對於創業不再只是停留在書本的知識,而是自己親身走過的一段路,同時也明白自己的不足,現在看到能夠在創業路上存活下來的人都打從內心敬佩萬分。 今年的收穫 今年靠賣外掛賺了 28 萬,平均一個月 23,000 元,而這全是單一外掛所達成的營收,很自然的就會想如果有第二支、第三支的話,照這節奏累積下去想要月入十萬不是夢,於是年初時便開始著手研發第二款產品。 由於第一款是做客戶訂單推播功能就有如此的成績,因此第二款延續這主軸,改以管理員為主的推播,並整合 AI 讓管理員可以用自然語言做訂單查詢,商業模式是打算先釋出免費的版本,再搭配不同的 AI 查詢項目來做收費:https://oberonlai.blog/dwp-site-assist/ 開發期間請了很多客戶幫我測試,很慶幸有先找他們,如果沒有他們幫我測試就推出絕對會是災難一場,主要發現的問題是打算拿來收費的 AI 查詢訂單功能不穩定,會因為...

很開心今年又有 WordCamp 可以參加,每次參加都很珍惜,每年都靠著志工們的無私付出,才能有這麼多精彩的活動可以參與。今年是第一次舉辦在高雄,高雄展覽館超大氣,整個會場超舒服,真希望新北也能有這麼棒的場地~ 今年我的攻略路線就是專心聽演講,以往都覺得在現場跟人聊天交流比較重要,演講的內容事後還可以去看 WordPress.tv 回顧,結果最後都沒去看XD,自己當過講者也知道,每次上台前要花好多時間準備、練習再練習才有辦法呈現一場演講,所以與其只是在哪邊喇賽,還不如好好享受每位講者精心準備的內容。 為了達成此目標,我六點起床,搭了 6:54 的高鐵直達高雄,再坐小黃 30 分鐘拼第一場演講,然後趁著每場中間的休息時間去贊助商攤位認識一下,午餐過後找不到咖啡可以喝,只能撐著睡意,努力聽到最後一場,最後為了趕高鐵放棄了閉幕抽獎,度過非常充實的一天。 為了更精準的吸收每場的內容,我聽的場次我都有用語音備忘錄錄下來,然後回到家再用 Gemini 整理出逐字稿與重點整理,這邊我把有聽的七個場次整理出來並補上自己的心得,分享給當天無法參與的朋友們。 高效率人才管理,讓 WordPress...

OrderChatz 全新推播功能公開封測中!現在加入 LINE 官方帳號 @dwpev 輸入「真買家」,就會收到外掛下載連結以及免費的封測序號,讓你親身體驗分眾行銷的強大威力! 在廣告成效越來越差的情況下,再行銷是所有電商業主的必修課題,尤其是如何篩選出正確的 LINE 好友進而投放正確的行銷資訊更是一門大學問,常使用 LINE 官方帳號進行再行銷的你可能會遇到以下問題: 人力成本與工作量爆表:人工逐筆查訂單、比對商品並貼標,遇到一位好友多張訂單就會產生大量重複作業,整體工時與人事成本快速攀升。 標籤維護難度高、錯誤率大:退款、追加購買或不同客服的命名習慣會讓標籤狀態不一致,導致分眾邏輯失準、名單品質下降。 推播額度與行銷成本浪費:把訊息傳給不相關或已流失的好友會降低開信/點擊率,浪費 LINE 發送額度與廣告預算,讓 ROI 看不清楚。 時效性不足、錯失最佳接觸時機:人工貼標與核查耗時,導致無法在限時促銷或新品上市的黃金時段快速觸達目標客群。 測量與優化受限:錯誤或雜亂的分眾會讓 A/B 測試與成效分析失真,行銷團隊難以找出真正有效的訊息與受眾組合。...