[學習心得][Python] 透過 LangChain 的 Functions Agent 達成用中文來操控資料夾

image-20230619204550551

前提

以前 Linux 課程上,大家總是對於 ls , mvcp 的指令無法背起來。都忘記有多少次聽到朋友抱怨,難道不能用中文來下指令嗎? 比如說:

  • 幫我移動1.pdf
  • 刪除 2.cpp 這個檔案
  • 列出檔案

現在,其實你可以透過 LangChain 很快來達成。本篇文章將給你範例程式碼,跟相關的原理 LangChain Functions Agent 的用法。

這裡也列出一系列,我有撰寫關於 LangChain 的學習文章:

開源套件參考這個 https://github.com/kkdai/langchain_tools/tree/master/func_filemgr

什麼是 LangChain

透過 LLM (大型語言模型)的開發上,有許多很方便的工具可以幫助你快速地打造出 POC 。 這裡最知名的莫過於 LangChain ,除了支援眾多的大型語言模型之外,更支援許多小工具(類似: Flowise) 。

What is LangChain and how to use it

什麼是 Functions Tool

就像是這篇文章提到的一樣, Functions Tools 是根據 最新的 06/13 公佈的 OpenAI Function Calling 的功能 所打造的功能。也就是你先給予 LLM 一連串可以執行的「工具列表」後。他會根據你的語意,來回答給你說「可能」是屬於哪一個 Function Tool 可以呼叫。 並且讓你來決定,究竟是要呼叫呢? 還是要繼續處理它。

就像是幫你的 LLM 告訴他可以做哪些判斷,由他來幫你判斷使用者的語意「可能」做哪一件事情。

image-20230620112225023

(讓機器人來幫你決定,應該要準備執行哪個 Function Tools)

在 Python LangChain 裡面也相當的簡單,前一篇文章已經有大部分的內容。 這裡做相關的說明:

from stock_tool import StockPriceTool
from stock_tool import get_stock_price

model = ChatOpenAI(model="gpt-3.5-turbo-0613")

# 將工具轉換成可以被解釋的 JSON 格式給 LLM 來處理。(目前只有 OpenAI)
tools = [StockPriceTool()]
functions = [format_tool_to_openai_function(t) for t in tools]

.....

# 透過 OpenAI 的最新模型,來判斷這個使用者的文字應該去執行哪個 Function
# 回傳可能是 get_stock_price 或者甚至可能是空的。
hm = HumanMessage(content=event.message.text)
ai_message = model.predict_messages([hm], functions=functions)

# 處理 OpenAI 幫你抓出的「參數」(arguments)
_args = json.loads(
    ai_message.additional_kwargs['function_call'].get('arguments'))

# (optional) 直接去執行該 function tool
tool_result = tools[0](_args)

這邊如果直接把結果回傳,可能只會達到股價的「數字」而已。甚至那個數字的單位都不會加上去。其實這只是讓 LLM 來告訴你該使用哪個 function 的判斷跟解釋。就很像我們之前使用 NLU 的 Intent detection 一樣。

如何完成 LangChain Functions Agent

image-20230621132804983

如果是 Function Agent 的話,就像是幫你的 LLM 大腦裝上了手臂,開始讓他不只是「說」,更能夠開使「動手做」。

image-20230620110007097

這邊「更加的抽象一點」,除了讓 LLM 知道有哪些工具外。 並且透過將他們定義成 Agent 去抓取資料後再來解釋。也就是會有以下流程:

  • 輸入使用者語詞,判斷呼叫哪個 Agent。並且取出相關的 Arguments 。
  • 呼叫該 Agent ,並且取得結果。
  • 透過結果,再一次詢問 LLM 總結回覆的答案。
# 以下 impot 工具類別中的 file management 工具組
# 這些是真的可以操控檔案的相關 tools
# 分別是: 讀取檔案,複製檔案,刪除檔案,移動檔案,寫入檔案。
from langchain.tools.file_management import (
    ReadFileTool,
    CopyFileTool,
    DeleteFileTool,
    MoveFileTool,
    WriteFileTool,
    ListDirectoryTool,
)

....

# 以下方式透過 OPENAI_FUNCTIONS 的 Agent 格式將剛才所有的工具都加入進去。
# 也就是說我們的 LLM 現在多了檔案操控的能力。

    model = ChatOpenAI(model="gpt-3.5-turbo-0613")
    tools = FileManagementToolkit().get_tools()
    open_ai_agent = initialize_agent(tools,
                                     model,
                                     agent=AgentType.OPENAI_FUNCTIONS,
                                     verbose=True)



# 真的要執行 Agent 的功能只要呼叫這一行就好。
        tool_result = open_ai_agent.run(question)

Langchain Functions Agent 跟 Functions Tool 的比照表

  OpenAI Function Tools OpenAI Function Agent
定義方式 Tools 透過 LangChain Tools 包裝成 Agents (OPENAI_FUNCTIONS)
執行流程 (1)輸入使用者語詞,判斷呼叫哪個 Agent。並且取出相關的 Arguments 。 (2) 開發者自己決定要不要去執行他,或是剩下的動作。 (1)輸入使用者語詞,判斷呼叫哪個 Agent。並且取出相關的 Arguments 。 (2) 呼叫該 Agent ,並且取得結果。 (3) 透過結果,再一次詢問 LLM 總結回覆的答案。
不要回覆定意外問題? 可以,自己決定哪些要回覆 不行!不論什麼都會回覆。

成果

以下的影片可以看到,這樣才幾行的程式碼,卻可以開始操控檔案。

並且,你還不需要完整把指令打對,甚至可以使用中文的指令才「列表」「讀取」「寫入」「刪除」,

最令人不可相信的是,你甚至可以「寫一首詩,然後把內容寫進 1.txt」這種比較複雜的指令都可以完整地被執行。

asciicast

結語

近期完了不少的 LangChain 的相關範例,並且感受了透過 Flowise 來打造相關應用的方式。 覺得 LINE Bot 的開發將會幫助各位更容易貼近使用者。打造出一個「專一」「好用」的聊天機器人,並且讓 LINE 官方帳號來幫助你的生意邁向生成式 AI 的時代吧!!

[學習心得][Python] 透過 LangChain 打造一個股價查詢 LINEBot - 股價小幫手

img

前提

透過 LLM (大型語言模型)的開發上,有許多很方便的工具可以幫助你快速地打造出 POC 。 這裡最知名的莫過於 LangChain ,除了支援眾多的大型語言模型之外,更支援許多小工具(類似: Flowise) 。

本篇文章將介紹如何透過 LangChain 打造一個查詢股價 (stock price) 的小工具,並且提供一個可以快速打造的開源套件讓各位一起學習。 對了,本篇文章使用的範例也是最新的 06/13 公佈的 OpenAI Function Calling 的功能。除了可以快速判斷使用者 Intent 之外,更可以呼叫外部的 API 來查詢本來 OpenAI 無法了解的資訊(比如說現在的股價)。

What is LangChain and how to use it

透過使用 LLM 的 LINE Bot: 你可以使用任何敘述公司的文字,來找尋公司股價。 比如說:

  • 蘋果公司,甲骨文… 等中文公司名稱。
  • 甚至從英文完全找不出股票代碼的公司,比如說: CloudFlare –> NET

開源套件參考這個 https://github.com/kkdai/linebot-langchain

這裡也列出一系列,我有撰寫關於 LangChain 的學習文章:

為何要挑選 LangChain 作為開發 LINE Bot 的架構

這題目很大,很難一句話回答。

  • LangChain 是一個很方便打造 POC 概念的東西。搭配 Flowise 甚至可以讓 prompt 人員跟開發人員完全分開。
  • 但是他畢竟還是類似黑箱子,有太多需要注意的地方。真的要上線,建議還是要透過 OpenAI 自己來開發。
  • 不過 LangChain 開發出來的架構,可以無痛轉換到其他 LLM 就是了。

  • 也有視覺化的工具類似: Flowise 可以讓拉框架的人,跟 Prompt 的人分開。修改 Prompt 甚至不需要重新 deploy

img

Flowise 提供一個 LangChain 視覺畫前端,可以透過拉框架快速測試架構跟 Prompt ,開發 LINE Bot 可以直接串 API 。 甚至修改 Prompt 可以做到不需要改 API Call ,也不需要重新 Deploy 。

先來準備一個快速導入 Heroku 的 Python LINE Bot 套件

這邊有一些選擇方式:

如果是從頭開啟的專案,幾件事情需要注意:

  • app.json

    • 主要敘述設定的 build packs ,這裡主要注意兩個項目:
    • “repository”: 記得填寫正確,如果你有 Clone 過去,記得改成你的。
    • 裡面有三個參數,請參考 README 即可,有一個比較雷的地方是。
      • LangChain 只支援系統參數 OPENAI_API_KEY 你設定成其他文字都會讀不到爆掉!
      • LangChain 只支援系統參數 OPENAI_API_KEY 你設定成其他文字都會讀不到爆掉!
      • LangChain 只支援系統參數 OPENAI_API_KEY 你設定成其他文字都會讀不到爆掉!
  • Profile:
    • 這個不用改,記得使用 web: uvicorn main:app --host=0.0.0.0 --port=${PORT:-5000}
    • 如果你主程式 main.py 有改名字的話,記得跟著改。
  • requirements.txt
    • 這個不用改,照抄過去就好。
  • runtime.txt
    • 設定 Heroku 運行 Python 的版本,這邊建議使用 3.10.11 比較穩定。 不建議使用最新版本 3.11 (聽社群說很雷)

以上,雖然 Heroku 是一個需要付費的公司。但是要改到 Render 應該也是沒問題。 不過能快速協助 deploy,又有穩定的伺服器很重要。

如果只是要接 OpenAI 在 LINE Bot 上的話

其實相對程式碼是比較簡單的,不過只有最簡單的串接功能。這邊稍微列出一下:

import os
import openai

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())  # read local .env file
openai.api_key = os.environ['OPENAI_API_KEY']


# A demo code how to call OpenAI completion
def get_completion(prompt, model="gpt-3.5-turbo"):
    messages = [{"role": "user", "content": prompt}]
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=0,
    )
    return response.choices[0].message["content"]

之後再來把 get_completion(event.message.text) 就可以。

來打造第一個 LangChain + LINE Bot 的範例: 記憶力

常聽到有人詢問, LangChain 到底有什麼方便的地方。我們先透過以下的範例來說明吧,以下透過相關程式碼。可以快速打造一個具有「記憶力」的聊天機器人。並且可以把你之前問題記住。

在 OpenAI API 該怎麼打造?

最常需要開發的,就是類似功能。但是原本 OpenAI 狀況下,這樣開發有點複雜。

  • User: Hi, I am John.
    • AI: Hello, John.
  • User: what is 1+1?
    • AI: 2.
  • User: What is my name?
    • AI: I am sorry, you need to provide more detail.

由於 OpenAI 每次呼叫 API 給他,其實都是嶄新的聊天視窗。 想要做成像是 ChatGPT 這種方式可能就需要有「記憶力」,在 OpenAI 的 API 中其實需要使用 Chat Completion,而且需要,每一筆慢慢填進去:

{
  "model": "gpt-3.5-turbo",
  "messages": [
  {"role": "system", "content": "You are a helpful assistant."},
  {"role": "user", "content": "Hello!"}]
}

是不是很麻煩?

使用 LangChain 的 ConversationBufferWindowMemory

這裡有完整程式碼,我只列出重要的部分:

# Langchain 串接 OpenAI ,這裡 model 可以先選 gpt-3.5-turbo
llm = ChatOpenAI(temperature=0.9, model='gpt-3.5-turbo')

# 透過 ConversationBufferWindowMemory 快速打造一個具有「記憶力」的聊天機器人,可以記住至少五回。
# 通常來說 5 回還蠻夠的
memory = ConversationBufferWindowMemory(k=5)
conversation = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=False
)

@app.post("/callback")
async def handle_callback(request: Request):
    signature = request.headers['X-Line-Signature']

   ......
   
    for event in events:
        if not isinstance(event, MessageEvent):
            continue
        if not isinstance(event.message, TextMessage):
            continue

        # 將使用者傳來的訊息 event.message.text 當成輸入,等 LangChain 傳回結果。
        ret = conversation.predict(input=event.message.text)

LangChain + OpenAI Functions 的範例: 股價查詢

快速介紹什麼 OpenAI Functions Calling

可以參考“Function calling OpenAI 說明文件“ ,也可以參考我畫出的流程圖。

這裡給一張流程圖: PlantUML

image-20230615152527422

更多細節可以參考我的另外一篇文章「關於 OpenAI 新功能: Function Calling

先來打造 Functions Tools

LangChain 裡面把 OpenAI Function calling OpenAI“ 當成是一個 Tools (Agent) 來呼叫,可以參考 LangChain 官方的範例程式 。 快速來看一下:

# 很重要!!! 一定要使用這個 model ,不然會雷的超怪的!
# 很重要!!! 一定要使用這個 model ,不然會雷的超怪的!
# 很重要!!! 一定要使用這個 model ,不然會雷的超怪的!
model = ChatOpenAI(model="gpt-3.5-turbo-0613")

# MoveFileTool 是一個基本的 tool Agent,負責移動檔案的。
from langchain.tools import MoveFileTool, format_tool_to_openai_function

tools = [MoveFileTool()]

# 取得 MoveFileTool 所有的 functions 接口
functions = [format_tool_to_openai_function(t) for t in tools]

# 透過 OpenAI 提供的 Predict Message 來呼叫 functions
message = model.predict_messages(
    [HumanMessage(content="move file foo to bar")], functions=functions
)

那我們來打造抓取股票資訊的工具 stock_tools.py

import yfinance as yf

def get_stock_price(symbol):
    ticker = yf.Ticker(symbol)
    todays_data = ticker.history(period='1d')
    return round(todays_data['Close'][0], 2)

以上是一個簡單的抓取股價的 functions ,接下來是定義這個 BaseTools

class StockPriceCheckInput(BaseModel):
    """Input for Stock price check."""

    stockticker: str = Field(...,
                             description="Ticker symbol for stock or index")


class StockPriceTool(BaseTool):
    name = "get_stock_ticker_price"
    description = "Useful for when you need to find out the price of stock. You should input the stock ticker used on the yfinance API"

    def _run(self, stockticker: str):
        # print("i'm running")
        price_response = get_stock_price(stockticker)

        return price_response

    def _arun(self, stockticker: str):
        raise NotImplementedError("This tool does not support async")

    args_schema: Optional[Type[BaseModel]] = StockPriceCheckInput

其中兩個重要的就是:

  • name = “get_stock_ticker_price”:
    • 定義相關 func call 名稱。
  • description = “Useful for when you need to find out the price of stock. You should input the stock ticker used on the yfinance API”
    • 這個敘述其實很重要,因為 OpenAI 會真的去讀取你裡面的敘述。來學習該如何抓取使用者輸入的文字。所以這邊需要講的清楚一點,不能打錯也不能隨便打。

跟 LINE Bot 主要程式碼串接起來

可以參考 main.py ,這裡節錄重要的部分:

from stock_tool import StockPriceTool
from stock_tool import get_stock_price

# Langchain (you must use 0613 model to use OpenAI functions.)
# Langchain (you must use 0613 model to use OpenAI functions.)
# Langchain (you must use 0613 model to use OpenAI functions.)
model = ChatOpenAI(model="gpt-3.5-turbo-0613")

# Prepare openai.functions format.
tools = [StockPriceTool()]
functions = [format_tool_to_openai_function(t) for t in tools]

.....

    for event in events:
        if not isinstance(event, MessageEvent):
            continue
        if not isinstance(event.message, TextMessage):
            continue

        # Use OpenAI functions to parse user intent of his message text.
        hm = HumanMessage(content=event.message.text)
        ai_message = model.predict_messages([hm], functions=functions)

        # parse args parsing result from OpenAI.
        _args = json.loads(
            ai_message.additional_kwargs['function_call'].get('arguments'))

        # Call the 3rd party API get_stock_price to get stock price.
        tool_result = tools[0](_args)

首先先讓 LangChain 知道怎麼跟你的 tool 串接

tools = [StockPriceTool()]
functions = [format_tool_to_openai_function(t) for t in tools]

這裡就是讓 OpenAI 幫你把問題變成 API 的資訊 (假設問 Google 股價)

AIMessage(content='', additional_kwargs={'function_call': {'name': 'get_stock_ticker_price', 'arguments': '{\n "stockticker": "GOOGL"\n}'}}, example=False)

就會直接轉換成 GOOGL 這個股票代號。也因為知道是 get_stock_ticker_price 會對應到 tools[0]這邊其實可以支援多個 Functions,但是支援多個代表你要判斷要呼叫哪一個。

tool_result = tools[0](_args) # 就跟 get_stock_price(`GOOGL`)

這樣就可以完成了,如果多個 Tools 判斷會比較困難。 但是往往這才是 LangChain 可以帶給你方便的地方。

結語

近期完了不少的 LangChain 的相關範例,並且感受了透過 Flowise 來打造相關應用的方式。 覺得 LINE Bot 的開發將會幫助各位更容易貼近使用者。打造出一個「專一」「好用」的聊天機器人,並且讓 LINE 官方帳號來幫助你的生意邁向生成式 AI 的時代吧!!

[學習心得][Golang] 透過 Flex Simulator 有效率開發與除錯 FLEX Message 以 Go SDK 為範例

image-20230616182027588

前提

LINE Bot 的開發上, Flex Message 是一個強大又美觀的訊息顯示方式。並且可以發出多種類型訊息格式。本篇文章分享了,如何透過快速版本設計來寫出你想要的形式。 本篇文章將透過一個流程,循序漸進告訴你如何完成上面的範例訊息,並且如果開發 FLEX Message 上的問題時,也可以透過 FLEX Simulator 來幫你除錯。

開始了,設計苦手

任何的前端苦手(像是我),都對於如何找到一個好 Flex 的格式內容感覺很辛苦。這時候可以考慮使用 FLEX Simulator 先做你的起手式。

image-20230616173437478

(FLEX Message Simulator 提供給你所見即所得的編輯方式)

這裡也推驗”藉由 Flex Message Simulator 實現並發送測試用 Flex Message” 讓你對於 FLEX Message Simulator 有更深刻的了解。

透過 Showcase 挑選喜歡的樣板

image-20230616173711658

這裡可以挑選喜歡的格式,我們先挑選出我們喜歡的 “Local Search” 版面。

透過 FLEX Simulator 修改

image-20230616174006884

依照本次範例,我們不需要評比的星星。可以透過 FLEX Message Simulator 來直接移除(用剪刀) 。

image-20230616174145606

修改好之後,按下右上角的 </> View as JSON 來查看相關資訊。這些可以讓你知道如何開始編譯你的程式碼。

開始弄到 Golang 程式碼

如果沒有要把 FLEX 套著資料,整包 JSON 直接丟下去用

如果要開始把 FLEX Message 弄到你開發的聊天機器人的話,可以先建議以下方式: 其中 jsonString

contents, err := linebot.UnmarshalFlexMessageJSON([]byte(jsonString))
		if err != nil {
			return err
		}
		if _, err := app.bot.ReplyMessage(
			replyToken,
			linebot.NewFlexMessage("Flex message alt text", contents),
		).Do(); err != nil {
			return err
		}

其中 UnmarshalFlexMessageJSON 可以直接把 JSON 直接轉換成 Golang code 裡面的元件,你可以再去修改,或是直接丟到訊息就好。

直接一個個的刻出來

這裡貼上一段 code 可以直接看到,這是一個慢慢用內部定義的格式來打造 FLEX Message 。

  • 優點:
    • 很有彈性,甚至可以套用外部資料打造數個 FLEX Bubble Carousel 。
  • 缺點:
    • 就是需要一個個把格式填寫上去,不過使用 VS Code 的體驗,應該都可以快速選到才是。

發送 FLEX Message 失敗怎麼解決?

大概也是許多開發者的痛,就是當 FLEX Message 發送失敗的時候。到底要去哪裡查錯誤訊息? 經常會發現 Log 也沒那麼清楚怎麼辦?

flexMsg := linebot.NewFlexMessage(ALT_TRAVEL_FLEX, flexContainerObj)
if _, err := bot.ReplyMessage(event.ReplyToken, flexMsg, linebot.NewTextMessage(sumMsg)).Do(); err != nil {
				log.Print(err)

				if out, err := json.Marshal(flexContainerObj); err != nil {
					log.Println("Marshal error:", err)
				} else {
					log.Println("---\nflex\n---\n", string(out))
				}
			}

這段程式碼給一個範例,當你發送訊息錯誤的時候。這時候,就把你的錯誤的 FLEX Message JSOM 印出來。然後一樣複製下來後,到 FLEX Simulator 一樣打開 “View as JSON” 然後放上 JSON 並且套用。

image-20230616182235119

image-20230616182247356

這時候可以知道你的 JSON 哪裡有問題,也可以查到。 這時候可以透過發生錯誤的元件,來回頭查看你的程式碼。

結語

FLEX Message 真的是很強大,不論在桌面上,還是在手機端都可以顯示出漂亮的資料呈現。 但是透過程式來打造的話,又會因為格式過於自由變得有些迷惘。 希望這一篇文章可以讓每一個跟我一樣的前端(視覺)苦手,知道如何開發外,遇到問題也能夠快速解決。

[學習心得][OpenAI] 關於 OpenAI 新功能: Function Calling

image-20230615115340699

前提

OpenAI 在 06/13 發表了新的功能 “Function calling“,其實對 LLM 的開發上算是一個完整的新發展。 本篇文章將快速解釋一下這個更新將會帶來哪一些變革,並且也透過將 LINE 官方帳號的相關整合為案例,幫你打造一個旅遊小幫手。

關於 OpenAI 新的功能Function Calling 的細節

關於 “Function calling” 主要是拿來處理 Intent 的判斷,並且針對使用者的意圖給予相關的 JSON 輸出給開發者作為處理之用。 換成白話文來說,如果你今天想要做一個「天氣服務的 ChatGPT LINE Bot 」的話,那麼你該如何弄呢?

相關資料:

在 OpenAI Functions Calling 之前,要怎麼做?

根據 “DeepLearning 提供一堂很好的 Prompt Engineering for Developers” 有提到,你可能要這樣做:

你現在是一個協助抓取使用者資訊的小幫手,如果使用者詢問了某個地區的天氣。你就幫我把地區抓出來透過以下格式呈現。 
---
{
   "location": "xxxx"
}

image-20230615123019584

很多地方是聰明的,但是還是沒有處理非相關天氣詢問要拒絕。這裡可以透過 ChatGPT Share 查看結果。如果是一個 NLU 的小幫手,在這個情況下可能要回覆 { "location" : ""} 或是 NULL ,但是這樣會讓你的 Prompt 非常的冗長,而且通常很長的防禦Prompt (咒語)只能防禦一個階段。 那該怎麼辦呢?

那 OpenAI Functions Calling 怎麼幫助你呢?

這裡給一張流程圖: PlantUML

image-20230615152527422

第一步: 呼叫 Chat / Complete Function Calling

從文章內 “Function calling” 可以提供一個很簡單的範例,你可以發現以下得呼叫方式跟原本使用 chat/ completion 沒有差別,但是回傳資訊差很多了。

curl https://api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
  "model": "gpt-3.5-turbo-0613",
  "messages": [
    {"role": "user", "content": "What is the weather like in Boston?"}
  ],
  "functions": [
    {
      "name": "get_current_weather",
      "description": "Get the current weather in a given location",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "The city and state, e.g. San Francisco, CA"
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"]
          }
        },
        "required": ["location"]
      }
    }
  ]
}'

也就是你可以透過詢問 "What is the weather like in Boston?" 讓 chatGPT 透過 gpt-3.5-turbo-0613 (這邊要強調是 GPT3.5) 的模型來跑分析,直接回答給你 JSON 檔案。

{
  "id": "chatcmpl-123",
  ...
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": null,
      "function_call": {
        "name": "get_current_weather",
        "arguments": "{ \"location\": \"Boston, MA\"}"
      }
    },
    "finish_reason": "function_call"
  }]
}

這時候, OpenAI 的 API 絕對 會回覆給你 JSON ,不需要透過各種黑魔防禦術(特殊 Prompt) 就可以達成。 如果他找得到相關資料,他就會幫你把資料抽取出來,並且放在 arguments 傳給你。

更多細節可以參考 API 文件的部分:

Function_call / string or object / Optional

Controls how the model responds to function calls. "none" means the model does not call a function, and responds to the end-user. "auto" means the model can pick between an end-user or calling a function. Specifying a particular function via {"name":\ "my_function"} forces the model to call that function. "none" is the default when no functions are present. "auto" is the default if functions are present.

跟原本 Prompt 的差別

那你會想說,這樣跟原本透過特殊的 Prompt 有什麼差別?

不會有 JSON 以外的回傳:

如果你這時候,忽然又插嘴一句說:「給我一首詩」,他還是會回傳給你 JSON 檔案。可能會告訴你沒有相關可以使用的 arguments。

多個 Functions 的處理:

這也是最強大的,最好用的地方。請查看

curl https: //api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
"model": "gpt-3.5-turbo-0613",
"messages": [
    {
        "role": "user",
        "content": "What is the weather like in Boston?"
    }
],
"functions": [
    {
        "name": "get_current_weather",
        "description": "Get the current weather in a given location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA"
                },
                "unit": {
                    "type": "string",
                    "enum": [
                        "celsius",
                        "fahrenheit"
                    ]
                }
            },
            "required": [
                "location"
            ]
        }
    },
    {
        "name": "get_current_date",
        "description": "Get the current time in a given location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA"
                },
                "unit": {
                    "type": "string",
                    "enum": [
                        "celsius",
                        "fahrenheit"
                    ]
                }
            },
            "required": [
                "location"
            ]
        }
    }
]
}'

你會發現,上面的程式碼有兩個 function 等著判別。 get_current_dateget_current_weather

  • 如果你的問句是: What is the weather like in Boston? ,那他就會回傳給你 get_current_weather,然後給你 location = Boston
  • 反之,如果你問句是: What time is it in Boston? ,那他就會回傳給你 get_current_date,然後給你 location = Boston

第二步 : 呼叫 3rd Party API

  • 這邊就跳過,可以是各種 Open API 去找資料。
  • 但是,可以透過 ChatGPT Plugin 的資料去找,將自己模擬成 ChatGPT Plugin 。

第三步: Send the summary to OpenAI

原本 https://openai.com/blog/function-calling-and-other-api-updates 資料有錯誒

image-20230615161410297

這裡要注意,如果你去找 Call 3rd API 故意回傳空的,代表 OpenAI 的 Summary 會沒有「參考資料」 參考以下: ` {“role”: “function”, “name”: “get_poi”, “content”: “{}”}` 。

也就是説,他會開始使用自己知道的相關資訊。(但是可能不是你期待的)。

curl https://api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
  "model": "gpt-3.5-turbo-0613",
  "messages": [
    {"role": "user", "content": "台北好玩地方在哪裡?"},
    {"role": "assistant", "content": null, "function_call": {"name": "get_current_poi", "arguments": "{ \"location\": \"台北\"}"}},
    {"role": "function", "name": "get_poi", "content": "{}"}
  ],
  "functions": [
    {
      "name": "get_current_poi",
      "description": "Get the current point interesting in a given location",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "The city and state, e.g. San Francisco, CA"
          }
        },
        "required": ["location"]
      }
    }
  ]
}'

這裡會收到回覆(Response):

{
  "id": "xxx",
  "object": "chat.completion",
  "created": 1686816314,
  "model": "gpt-3.5-turbo-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "台北有很多好玩的地方,以下是一些推薦的地點:\n\n1. 101大樓:台北最著名的地標建築之一,是世界上最高的摩天大樓之一,可以在觀景台欣賞城市全景。\n\n2. 象山:台北的一座山峰,可以從山頂俯瞰整個城市,是非常適合登山和賞景的地方。\n\n3. 士林夜市:台北最著名的夜市之一,可以品嚐到各種美食和購物。\n\n4. 故宮博物院:收藏中國文化藝術品的博物館,展示了大量的歷史文物。\n\n5. 國立自然科學博物館:一個專門展示自然科學知識和展品的博物館,非常適合家庭和學生參觀。\n\n6. 北投溫泉:台北最著名的溫泉區之一,可以享受溫泉浸浴和放鬆身心。\n\n7. 龍山寺:台北最古老的寺廟之一,是信仰佛教的重要地方,也是一個參觀的旅遊景點。\n\n希望這些地方能讓您在台北有一個愉快的旅行!"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 104,
    "completion_tokens": 419,
    "total_tokens": 523
  }
}

本來以為 "finish_reason": "stop" 在回覆後檢查。並且設定, for loop 檢查,但是發現就算把資料傳空的給 OpenAI ,他也會根據他有的資訊開始回覆給你。

使用 Function Calling 需要注意的事情

1. Langchain 也可以用了嗎?

2. 使用 Func Calling 的語意理解能力沒有原來的 Chat 聰明

經過我自己觀察,透過 Func Calling 擷取出來相關 Argument 沒有以前直接在 ChatGPT 測試還聰明。 可能也因為 API GPT3.5 Turbo 其實也是不等於 ChatGPT 的。 所以在使用上,會需要常常去多問一次。來解釋相關內容,再來擷取出相關的 Arguments 。

3. Func Calling Summarized 如果沒資料, ChatGPT 會以他知道的來回覆。

Function Calling 可以帶來的應用

這裡快速摘要幾個可以帶來的新應用:

  • 自幹自己的 ChatGPT Plugin:
    • ChatGPT App在台灣上架!Plugins開放、一次搞懂怎麼用 上架後就受到很多人歡迎,但是他還是相對「少數」,因為需要額外付費 $20 。所以使用的人還是相對少。
    • 透過 Function Calling 就可以打造出自己的 ChatGPT Plugin 在你喜歡的平台(LINE 官方帳號, slack …)
  • 更聰明的 Intent 識別機制 :
    • 因為這個 API 的速度還蠻快的,回覆的 token 也都不多。
    • 作為 NLU 的識別機制會相當的實用。並且可以確保回覆的 JSON format 相當的精確。

[學習心得][Golang] 透過 ChatGPT 來做 REGEX 的事情

image-20230611001729736

前提

大家應該都跟我一樣,經常使用 ChatGPT 來協助一些 coding 上的問題。尤其是對於 Regular Expression 的問題。最近在處理上發現,需要透過「正向表列」,而非跟他講哪些不可以的方向比較容易成功。也應該說, Regular Expression 本來就要透過正向表來才對。

原本:

// AddLineBreaksAroundURLs takes a string as input, finds URLs,
// and inserts a newline character before and after each URL.
// It returns the modified string.
func AddLineBreaksAroundURLs(input string) string {
	re := regexp.MustCompile(`(https?:\/\/[^\s\p{Han}]+)`)
	return re.ReplaceAllString(input, "\n$1\n")
}

發現無法處理:

 https://voyager.minedojo.org/。

當我發現我一直跟 ChatGPT 講要排除 ` https://voyager.minedojo.org/。` 這個案例的時候,往往會一直卡住無法向前。 但是寫多個測試案例倒是真的蠻好的。

Unit Testing code

func TestAddLineBreaksAroundURLs(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{
			input:    "Check out this website https://example.com and this one http://another-example.com",
			expected: "Check out this website \nhttps://example.com\n and this one \nhttp://another-example.com\n",
		},
		{
			input:    "Here is an URL with dot at the end https://voyager.minedojo.org/。",
			expected: "Here is an URL with dot at the end \nhttps://voyager.minedojo.org/\n。",
		},
		{
			input:    "This is another test 可在https://voyager.minedojo.org/上訪問。",
			expected: "This is another test 可在\nhttps://voyager.minedojo.org/\n上訪問。",
		},
		// Add more test cases here as needed
	}

	for _, tt := range tests {
		res := AddLineBreaksAroundURLs(tt.input)
		assert.Equal(t, tt.expected, res, "Should correctly insert line breaks around URLs")
	}
}

正面表列

後來決定,透過正面表列來解決這個問題。

// AddLineBreaksAroundURLs takes a string as input, finds URLs,
// and inserts a newline character before and after each URL.
// It returns the modified string.
func AddLineBreaksAroundURLs(input string) string {
    re := regexp.MustCompile(`(https?:\/\/[\w\/\?\&\.\-]+)`)
    matches := re.FindAllString(input, -1)
    for _, match := range matches {
        input = strings.Replace(input, match, "\n" + match + "\n", 1)
    }
    return input
}

大家可以透過以下方式分享 ChatGPT link

image-20230611002225533

[TIL] 關於 WWDC 2023 身為開發者你應該知道的事情

image-20230606091609243

我如何看待 WWDC23 ?

身為技術研討會主辦人,我看的是:

  • 整體錄影方式與收音技巧
  • 每一個講者的職稱(可以看出內部分工與架構)
  • 平台想要規劃出的生態圈

職稱相關

image-20230606172229878image-20230606172232338

應該跟許多公司一樣:

  • Engineering Progam Manager - 技術專案經理
  • Engineering Manage - 技術開發的經理

職稱其實也代表部門的多寡,對外部而言很嶄新的 visionOS 部門卻有那麼多清楚分工,而且能演講的人還蠻多的。不愧是 Apple 。

硬體相關

  • New Mac Studio 可以有六顆螢幕(原來之前的不行啊?!)

iOS 17 帶來軟體開發新變化

Name Drop 近場溝通新應用

image-20230606094035126

  • 取代掉所有名片應用 (LINE OA 其實有類似做法)
  • 不知道會用到哪個 SDK ,應該會開放給其他 App 使用。

更智能的鍵盤輸入

Image

可能是使用 local 「In-line predictive text API」

Image

TV OS 相關變革

透過 iPhone 直接在 Apple TV 開會

image-20230606114817277

image-20230606114820316image-20230606114822745

走 Continuity Camera API

Vision Pro 帶來的軟體開發變化

img

(要接線~~~~~ Orz)

image-20230606120805201

果然要另外接電池

image-20230606121309156

Apple Vision Pro 帶來新的生物識別系統「Optic ID」

image-20230606151548290

可能帶來影響:

  • 虹膜辨識會變成是基本 OAuth 的認證設備(搭配 Passkey (a.k.a. FiDO2)
  • 比起 WorldCoin 可能因為 AirDrop 產生盜用 ,但是 Vision Pro 更可以取得更多人的虹膜資訊。
  • 雖然依照 Apple 的個性,不可能有任何取得方式。但是代表 Optic ID 可以更加的準確, Passkey 的未來更令人期待。

iOS SDK

Accessibility SDK

image-20230606153541003

Privacy API

image-20230606153526487