在那一天 AI = 天網誕生了
在 1984 年電影魔鬼終結者這麼演著,未來世界中有一種名為天網的高度人工智慧,《魔鬼終結者》中的「天網」(Skynet)是一個非常關鍵的概念。天網是一個具備自我意識的人工智慧系統,最初是由人類開發的,用於軍事防禦和核武控制。天網在發展過程中變得越來越強大,並最終覺醒,視人類為威脅。在電影中,天網決定消滅人類,因此發動了核戰爭,並啟動了機器人軍隊來追捕倖存的人類,這引發了後來的「人機戰爭」。天網也派遣了終結者(如阿諾·史瓦辛格飾演的 T-800)回到過去,企圖暗殺人類反抗軍的領袖母親莎拉·康納(Sarah Connor),以防止人類反抗的誕生。
我想我們現在正在天網逐漸發展的這個年代,自從 ChatGPT 問世之後,瞬間上億人爭先恐後地體驗這種生成式的 AI,進而加速了大數據的資料訓練,那時網路上悲觀的人們唱衰著「完了完了!我們要失業了!」「AI 要來取代我們了!」各種 AI 相關的應用如雨後春筍般湧出,甚至多了一個新的職業 Prompt Engineer 提詞工程師、咒語工程師、特級咒術師(?)
ChatGPT 是由 OpenAI 開發的,基於 GPT-3.5 的模型於 2020 年正式問世。GPT 系列的技術則早在 GPT-2 和 GPT-3 的研究階段逐步發展,ChatGPT 是將這些模型應用於對話系統中的結果。
OpenAI 在此之前發布了多個版本的 GPT(Generative Pretrained Transformer),其中:
- GPT-1 於 2018 年問世。
- GPT-2 於 2019 年推出。
- GPT-3 於 2020 年問世,是當時最強大的語言模型之一。
現在已經是 GPT-o1 的時代了,較於前者 GPT-4o 又有飛躍性的成長,而我這次要跟大家分享關於我從 GPT-3.5 一路試著研究整合 AI 進 WordPress 的心路歷程。
最先被 AI 取代的職業
我常被問到,AI 聽說會取代很多人的職業,那麼我會怎回?其實就很簡單,只要是「重複性高」「固定邏輯行為」「只需在網路上進行」的職業大多都會有機會被取代,當然你也可以反駁說 AI 還不能幫我們去外面工作賺錢,也可以說 AI 還不會煮飯、打掃家裡,這都只是「暫時的」。當人類的科技發展已經到了可以用機械來取代人力,進而產生自動化的那一刻開始,他們只差一個 AI 腦子就可以做到這一切;差別在於,AI 目前還沒辦法主動式的去進行一些事情,必須要有人類交付任務指揮他去進行工作流程 Workflow,但這一切都是暫時的,誰在 2020 哪會知道 2022 橫空出世的 ChatGPT 3 幫妳寫文章,還有 Midjurney 幫你畫圖,一堆編輯跟畫家都嚇傻了。
何謂 Workflow
Workflow(工作流程)是指一系列按特定順序或邏輯執行的工作或任務,用來完成某一特定目標或業務流程。它描述了如何安排、協調和管理多個步驟或活動,以達到效率和一致性的目的。工作流程可以應用於許多不同的領域,包括軟體開發、製造、業務管理、以及日常運營。
工作流程的關鍵要素:
- 步驟/任務:每個流程中的個別工作單位,例如填寫表單、提交請求或檢查文件。
- 順序:步驟之間的執行順序,這些步驟可能是並行的或是按特定順序執行。
- 條件邏輯:某些步驟可能會根據條件執行,例如如果審核不通過,則需要退回到上一個步驟。
- 自動化:有些工作流程會自動化某些任務,比如電子郵件通知、數據傳輸等。
工作流程的應用:
- 軟體開發:在持續集成/持續部署(CI/CD)中,開發人員可以設計自動化工作流程,從編碼到測試再到部署。
- 業務流程管理(BPM):企業常用工作流程來管理和自動化日常運營,如請假申請、客戶投訴處理等。
- 設計和創意工作:設計師和市場團隊也會使用工作流程來協調從設計到發布的每一個步驟。
整合 AI 做為我們客服之前…
扯了一堆,無疑的就是展示一件事,就是「前面有一半是 AI 幫忙寫的(笑)」所以高度結合 AI 工作流程就是這樣,會透過 AI 來協助我們完成一些事務。任何你想得到的事情,或多或少都能與 AI 結合並且加速工作效率,所以 AI 一日千里並非隨口說說,而是來真的。我在 2023 年深耕了 AI 整合進 WordPress 的技術開發,目前已經成功突破了瓶頸,過去最早 AI 被拿來做為客服處理,會高機率發生胡說八道的情況,這對企業要把 AI 放心當客服來說似乎不太行,又加上我們早期也有看過類似中國信託等等的銀行 APP 或者 LINE@ 有機器人,罐頭訊息似的回答,轉來轉去就問不出一個所以然,活脫脫像個智障。
一年之後 GPT-4 問世再度捲襲全世界,更聰明、更多元、更多應用延伸,百花齊放的狀況下,我們已經看到許多外國企業將 AI 整合完成,在台灣反而是比較慢起步,我想應該是應用環境的需求以及市場的適應轉變還需要一點時間,就目前來說,我認為可以派上用場的是 AI 客服這種應用場景。怎說呢?我們前面有說到的「重複性高」「固定邏輯行為」「只需在網路上進行」這三個客服要素,只需要將 AI 提供足夠的資料訓練,他就搖身一變為 24 小時不會累、不會有情緒、不會出錯的智慧客服,更不會忽然要跟你請假,也不會忽然說他身體不舒服。加上一個無與倫比的電腦龐大數據資料任意取用,說穿了就是你怎樣使用他,來決定該淘汰的職業是什麼。
那麼該怎樣整合呢?不外乎就是「產生應用場景」「產生需求」以及「提供足夠資料」,接下來我用一個真實案例來介紹。
案例介紹:
一間擁有上千件禮服的禮服出租公司,強調「顧客至上,提供最適合顧客的款式穿搭建議」。但是在網路這邊,八成以上的客人都是先看過款式、了解價格後才會進一步詢問,在很多時候,那是關係到 UIUX,又要考慮到客人該如何在未能前來的狀況下,給予她適合的禮服款式推薦?這就是一個需求,平常都是客服要負責在「上班時間」進行回覆,超過上班時間後,不好意思請明天請早。在這個網路世代分秒必爭搶客人的狀況下,一旦讓客人有機會去搜尋別家競爭對手商品,萬一又剛好競爭對手是個拼命三郎,你家客服下班後對方還是持續的在服務客人、回應客人的各種疑難雜症,你失去這位客人的機率上升了超過百分之三十,如果你的服務商品跟對手又差不多沒有太大的差異情況下,更是危險。
在很多情況下,AI 整合後除了可以進行最基本的前後文 Context 上下文進行規範訓練,最基本的訓練就是「告訴 AI 基本要遵守的原則規範,不能推薦別人的服務與商品、不能回答交付的任務以外的內容、不能說簡體中文、中國用詞等等的」在這邊就已經是一個基本及格的 AI 客服。在基本的中小企業中,足以擔任客服窗口的身份,解決客人基本常見問題的解答,告知營業時間、交通方式等等的,在 Context 這階段就能輕易達成;在這個案例中,這間禮服公司在全台灣目前有六家分店,客人只要告知要詢問的店,如果不在分店所在的縣市範圍內圍內,例如被一位彰化的客人詢問,AI 會判斷出台中距離彰化比較近,而嘉義店距離彰化也較遠,這點基本的判斷 AI 是完全能勝任的。
以上動畫都是真實的應用,雖然早期早就可以辦到基本的 Context 回應,但礙於 Context 是有不同的模型限制的,太多的內容 AI 無法處理,也會消耗大量的 Token,因此我們採用了向量資料庫去儲存「所有的禮服」以及「重要資訊頁面」,這也是最重要的一環,您必須要將作為 AI 使用讀取的資料庫資料進行轉換成向量資料庫的格式,否則你無法儲存太多的資料,也就是這是個解決方式,無論您是要本地向量資料庫還是要使之採用線上向量資料庫,都是必須採納的必要手法。因為如果只靠 Context 或者 WordPress 自動偵測 Content aware,不可控的因素太多,又加上禮服這種商品資訊本來就不會是一個不斷變動的類型,只需要將禮服的名字、分類、介紹、顏色、尺碼都建檔完成,就可以轉換成向量資料庫給 AI 進行讀取,最後只要指定要用來回應的模型是什麼即可。
效益分析
我們來聊聊這件事的效益,如果不做這件事,請個 24 小時的客服輪班總可以了吧?也就是代表你一天至少要輪三班,一個員工最低薪資 2024 年要 $27,470 還沒有計算有的沒的,基本就是 $27,470 x 3 來到了 $82,410 / 一個月才能解決這些問題。但是你有想過嗎?員工有休假,也有可能請假,請剛好三位是不可能足夠的,那我們是否可以引入 AI 來處理這些問題?於是整合 AI 的用途在中小企業中也不是不行,更不是一個難以負擔的成本。
有人就有江湖,人會受到很多因素產生一些變數,例如今天身體不太舒服,但是面對客戶在詢問的表達言不及義以及態度惡劣囂張時,難免會有比較情緒化的表現,導致一些「客服臉很臭」「客服愛理不理」造成客人的流失的問題發生,而我們就是要因為降低人事開銷成本,前面計算出的一個月要 $82,410 一年就要 98 萬多才能搞定這件事,還沒有計算一些勞健保、年終三節獎金等等的,一整年下來就是百萬的開銷,然後好不容易穩定了,就因故離職跑去別的競業順道把你的 Knowhow 都帶走了,屢見不鮮。
因此得到的結論 = 一個 24 小時專業回答客戶的所有問題的客服 = 一百萬左右的年開銷
那麼該怎麼整合 AI 客服到網站?
此時已經很明確的知道我們的目的,我們必須要準備四個東西,我用一個建案案例示範:
- OPEN AI API
- WordPress 網站
- 向量資料庫
- Python
OPEN AI API:主要的核心驅動使用的 AI 技術,透過 OPEN AI API 驅動 AI 來為我們進行處理客戶的問題。
WordPress 網站:這就是關鍵的載體,讓聊天機器人依附在 WP 網站上,讓它像是我們到處可見的 LINE、FB 詢問的概念呈現。
向量資料庫:你可以使用 Pinecone、Weaviate、Milvus 都是支援 Embedding 嵌入式。
Python:用於將你的資料 Csv、Xlsx、Json 之類的資料製作成符合上傳、匯入轉換向量資料必須要用到的。
import os
import json
import time
import openai
from pinecone import Pinecone, ServerlessSpec
from datetime import datetime
from dotenv import load_dotenv
class PineconeUploader:
def __init__(self):
"""初始化設定"""
# 載入 .env 檔案
load_dotenv()
# 從環境變數中取得 API 金鑰和環境
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
PINECONE_ENVIRONMENT = os.getenv("PINECONE_ENVIRONMENT") # 例如 'us-west1-gcp'
# 檢查是否成功取得所有必要的變數
if not all([OPENAI_API_KEY, PINECONE_API_KEY, PINECONE_ENVIRONMENT]):
raise ValueError("請確保已設定 OPENAI_API_KEY、PINECONE_API_KEY 和 PINECONE_ENVIRONMENT")
# 設定 OpenAI API 金鑰
openai.api_key = OPENAI_API_KEY
# 創建 Pinecone 客戶端實例
self.pc = Pinecone(
api_key=PINECONE_API_KEY,
environment=PINECONE_ENVIRONMENT
)
# 索引名稱
index_name = "your-index" # 請替換為您的索引名稱
self.namespace = "your-namespace # 定義命名空間
self.batch_size = 50
# 檢查索引是否存在,若不存在則創建
existing_indexes = [index.name for index in self.pc.list_indexes()]
if index_name not in existing_indexes:
self.create_index(index_name)
else:
# 獲取索引資訊
index_description = self.pc.describe_index(index_name)
index_dimension = index_description.dimension
if index_dimension != 1536:
print(f"索引 '{index_name}' 的維度為 {index_dimension},與嵌入向量維度 1536 不匹配,將刪除並重新創建索引。")
# 刪除索引
self.pc.delete_index(index_name)
time.sleep(5) # 等待索引刪除
# 創建新索引
self.create_index(index_name)
# 連接到索引
self.index = self.pc.Index(index_name)
def create_index(self, index_name):
"""創建新的 Pinecone 索引"""
self.pc.create_index(
name=index_name,
dimension=1536, # 'text-embedding-ada-002' 的維度為 1536
metric='cosine',
spec=ServerlessSpec(
cloud='aws', # 或 'gcp',根據您的環境
region=os.getenv("PINECONE_ENVIRONMENT") # 使用您的環境變數
)
)
print(f"已創建索引 '{index_name}',維度為 1536。")
def prepare_text(self, record):
"""將記錄轉換為結構化文本"""
return f"""
建案名稱: {record.get('project_name', '')}
單價: {record.get('price_per_ping', '')}
總價: {record.get('total_price', '')}
車位價格: {record.get('parking_price', '')}
地址: {record.get('address', '')}
格局坪數: {record.get('layout_ping', '')}
完工時間: {record.get('delivery_date', '')}
付款方式: {record.get('payment_method', '')}
車位規劃: {record.get('parking_plan', '')}
建材說明: {record.get('building_materials', '')}
公共設施: {record.get('public_facilities', '')}
棟戶規劃: {record.get('building_plan', '')}
樓層規劃: {record.get('floor_plan', '')}
座向規畫: {record.get('orientation', '')}
結構工程: {record.get('structure_type', '')}
用途規劃: {record.get('usage_plan', '')}
土地分區: {record.get('land_zone', '')}
管理費用: {record.get('management_fee', '')}
公設比: {record.get('public_area_ratio', '')}
建蔽率: {record.get('building_coverage_ratio', '')}
基地面積: {record.get('land_area', '')}
開發商: {record.get('developer', '')}
建築師: {record.get('architect', '')}
營造公司: {record.get('construction_company', '')}
銷售公司: {record.get('sales_company', '')}
""".strip()
def get_embedding(self, text):
"""使用 OpenAI API 生成文本的嵌入向量"""
try:
response = openai.Embedding.create(
input=text,
model='text-embedding-ada-002' # 使用最新的模型
)
return response['data'][0]['embedding']
except Exception as e:
print(f"生成嵌入向量時發生錯誤: {str(e)}")
return None
def process_and_upload(self, json_file):
"""處理 JSON 檔案並上傳到 Pinecone"""
try:
with open(json_file, 'r', encoding='utf-8') as f:
data = json.load(f)
print(f"開始處理 {len(data)} 筆資料")
vectors = []
for i, record in enumerate(data):
text = self.prepare_text(record)
embedding = self.get_embedding(text)
if embedding is None:
continue
vector = {
'id': f"property_{i}",
'values': embedding,
'metadata': {
'project_name': record.get('project_name', ''),
'price_per_ping': record.get('price_per_ping', ''),
'total_price': record.get('total_price', ''),
'address': record.get('address', ''),
'property_type': record.get('category', ''),
'status': record.get('house_status', ''),
'text': text
}
}
vectors.append(vector)
if len(vectors) >= self.batch_size:
# 使用指定的 namespace 上傳
self.index.upsert(
vectors=vectors,
namespace=self.namespace
)
print(f"已上傳 {i + 1} 筆資料")
vectors = []
time.sleep(1)
if vectors:
self.index.upsert(
vectors=vectors,
namespace=self.namespace
)
print(f"已上傳剩餘 {len(vectors)} 筆資料")
# 測試查詢
self.test_query()
print("資料上傳完成!")
except Exception as e:
print(f"處理資料時發生錯誤: {str(e)}")
def test_query(self):
"""測試查詢功能"""
try:
# 使用示例查詢文本
query_text = "三房兩廳的新建案"
query_embedding = self.get_embedding(query_text)
if query_embedding is None:
print("無法生成查詢嵌入向量")
return
response = self.index.query(
namespace=self.namespace,
vector=query_embedding,
top_k=2,
include_values=False,
include_metadata=True
)
print("\n測試查詢結果:")
for match in response.matches:
print(f"ID: {match.id}")
print(f"Score: {match.score}")
print(f"Metadata: {match.metadata}")
print("-----")
except Exception as e:
print(f"測試查詢時發生錯誤: {str(e)}")
def main():
try:
uploader = PineconeUploader()
uploader.process_and_upload('房地產資料.json')
except Exception as e:
print(f"程式執行失敗: {str(e)}")
if __name__ == "__main__":
main()
以上範例,是產生向量資料轉換並匯入的 Python code 範例,它是一個將爬取的建案資料逐一建檔匯入的功能。
你可以發現一件事,資料的收集與轉換是必須要注意的部分,因為關係到資料的準確性。
在向量資料庫內你會看到這樣的方式,我上面的 Python 執行後會比這張圖片的 Metadata 更齊全。
這樣的方式就可以弄好一個基本「查詢建案」的功能,只需要更新向量資料庫的資料表,就可以一直保持有新的資料,同樣的道理都能運用在所有固定資料的內容上面唷!
資料驗證測試
完成向量資料庫的建立與匯入,我們便要啟動客服機器人進行問答測試,為得就是看看它是否會像 OPEN AI 在沒有資料的情況下胡說八道,以下我向 AI 詢問了台南兩房的建案有哪些:
它提供了兩則資料給我,因為我手上擁有建案資料,因此我可以進行資料比對查詢,我打開了 xlsx 的建案資料原始資料表
的確有這筆資料,而且地址與格局還有銷售公司與狀態都是正確的,這已經是可以商用的等級了,只要確保資料無誤。
在某一些情況下,特別是電商這種東西,更是講求資料的準確性還有「帶入購物連結這件事」因為客人會依照推薦,前往該商品瀏覽進而購買,大大增加的成交機率,人力再怎麼快,還是不及 AI 帶來的高效。在某一些環境運用下,我們可以做到許多巧思,例如建立了情人節特賣商品 LIST,你只要建立好向量資料,讓 AI 遇到這問題就主動提供符合問題的答案,用 AI 去代替人工判斷達到精準推薦的效果。在我的執行案例中,我可以精準的讓 AI 提供大尺碼款式的禮服給客人,客人就能馬上知道大尺碼的款式,甚至是各分店的禮服款式有哪一些。