深入 LINE Bot 與 ChatGPT Plugin
從實作到理論的探討
卡米哥
今天的內容
認識 ChatGPT Plugin
ChatGPT Plugin
試用 ChatGPT Plugin
ChatGPT 設定頁面
ChatGPT 啟用 Plugin 後
可以選擇要啟用的工具
也有 Plugin store
試用 Wolfram
ChatGPT 發給 Wolfram 的 request 以及 Wolfram 的 response
ChatGPT 跟 Plugin 是怎麼溝通的?
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
發問
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
思考
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
發問
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
回覆
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
思考
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
回覆
ChatGPT Plugin 做了什麼?
ChatGPT 怎麼思考?
ChatGPT 怎麼思考?
ChatGPT 怎麼思考?
ChatGPT 怎麼思考?
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
發問
大型語言模型
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
將 Prompt 結合工具資訊後傳入
大型語言模型
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
請求使用工具
大型語言模型
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
呼叫 API
大型語言模型
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
回覆
大型語言模型
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
大型語言模型
將先前的對話、工具資訊、呼叫工具的結果整合成 Prompt 後傳入
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
回覆結果
大型語言模型
ChatGPT Plugin 做了什麼?
用戶
ChatGPT
Wolfram
回覆
大型語言模型
ChatGPT 就是代理人
用戶
ChatGPT
Wolfram
大型語言模型
Agent
做一個
ChatGPT Plugin
做一個 ChatGPT Plugin
做一個 ChatGPT Plugin
做一個 ChatGPT Plugin
{
"schema_version": "v1",
"name_for_human": "My Bash Plugin",
"name_for_model": "bash",
"description_for_human": "Plugin for executing bash commands on my MacOS.",
"description_for_model": "Plugin for executing bash commands on MacOS.",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "https://ngrok.etrex.tw/openapi.yaml",
"is_user_authenticated": false
},
"logo_url": "https://ngrok.etrex.tw/logo.png",
"contact_email": "your-email@example.com",
"legal_info_url": "https://ngrok.etrex.tw/openapi.yaml"
}
/.well-known/ai-plugin.json
ChatGPT 發出的 request 和對應的 response
本機的 API Server Log
認識 LangChain
LangChain
LangChain Agent
用戶
LangChain
Wolfram
ChatGPT API
Agent
LangChain Agent
PREFIX = """回應人類,盡可能有用和精確。你可以使用以下工具:"""
FORMAT_INSTRUCTIONS = """使用一個json塊來通過提供一個動作鍵(工具名稱)和一個動作輸入鍵(工具輸入)來指定工具。
有效的"動作"值:"最終答案"或 {tool_names}
每個$JSON_BLOB只提供一個動作,如下所示:
===
{
"action": $TOOL_NAME,
"action_input": $INPUT
}
===
遵循此格式:
問題:輸入要回答的問題
思考:考慮先前和後續步驟
動作:
===
$JSON_BLOB
===
觀察:動作結果
... (重複 思考/動作/觀察 N 次)
思考:我知道該如何回應
動作:
===
{
"action": "Final Answer",
"action_input": "Final response to human"
}
===
"""
LangChain Agent
SUFFIX = """開始!始終用單一動作的有效json塊回應。如有必要,使用工具。如有必要,直接回應。格式是Action:===\$JSON_BLOB===然後Observation:。
思考:"""
用 LangChain 實作 LINE Bot 的範例展示
LINE Bot 結合 LangChain
App Server
Wolfram
用戶
LINE
傳送訊息
Webhook
LangChain
ChatGPT API
LINE Bot 結合 LangChain
App Server
Wolfram
用戶
LINE
function
call
LangChain
ChatGPT API
LINE Bot 結合 LangChain
App Server
Wolfram
用戶
LINE
LangChain
api call
終究是 Server
ChatGPT API
LINE Bot 結合 LangChain
Wolfram
ChatGPT API
用戶
LINE
App Server &
LangChain
Webhook
Server &
Agent
LINE Bot 結合 LangChain
Wolfram
ChatGPT API
用戶
LINE
App Server &
LangChain
生成 Prompt
LINE Bot 結合 LangChain
Wolfram
ChatGPT API
用戶
LINE
App Server &
LangChain
回覆
LINE Bot 結合 LangChain
Wolfram
ChatGPT API
用戶
LINE
App Server &
LangChain
有需要使用工具時
LINE Bot 結合 LangChain
Wolfram
ChatGPT API
用戶
LINE
App Server &
LangChain
call api
回覆訊息
回覆訊息
Evan Lin 的股價小幫手
Evan Lin 的股價小幫手
程式碼 - main.py
# 3-13 行 License
# 15-43 行 載入模組
# 45-53 行 載入環境變數
# 55-59 行 App Server Setup
# 61-68 行 LangChain Setup
# 71-97 行 Webhook
程式碼
# 45-53 行 載入環境變數
# get channel_secret and channel_access_token from your environment variable
channel_secret = os.getenv('ChannelSecret', None)
channel_access_token = os.getenv('ChannelAccessToken', None)
if channel_secret is None:
print('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
程式碼
# 55-59 行 App Server Setup
app = FastAPI() # App Server
session = aiohttp.ClientSession()
async_http_client = AiohttpAsyncHttpClient(session)
line_bot_api = AsyncLineBotApi(channel_access_token, async_http_client) # LINE SDK
parser = WebhookParser(channel_secret) # LINE SDK
程式碼
# 61-68 行 LangChain Setup
model = ChatOpenAI(model="gpt-3.5-turbo-0613") # 選擇語言模型
tools = [StockPriceTool(), StockPercentageChangeTool(), # 可以使用的工具
StockGetBestPerformingTool()]
open_ai_agent = initialize_agent(tools,
model,
agent=AgentType.OPENAI_FUNCTIONS, # 選擇 Agent Type
verbose=False)
程式碼
# 71-97 行 Webhook
@app.post("/callback")
async def handle_callback(request: Request):
# 取得 events
...
# 對每個 event 篩選出文字訊息
...
# 交給 LangChain Agent 生成回覆訊息
...
# 回覆訊息給 LINE 用戶
...
程式碼
# 71-97 行 Webhook
@app.post("/callback")
async def handle_callback(request: Request):
# 取得 events
body = await request.body()
body = body.decode()
try:
events = parser.parse(body, signature)
except InvalidSignatureError:
raise HTTPException(status_code=400, detail="Invalid signature")
程式碼
# 71-97 行 Webhook
@app.post("/callback")
async def handle_callback(request: Request):
# 取得 events
...
# 對每個 event 篩選出文字訊息
for event in events:
if not isinstance(event, MessageEvent):
continue
if not isinstance(event.message, TextMessage):
continue
程式碼
# 71-97 行 Webhook
@app.post("/callback")
async def handle_callback(request: Request):
# 取得 events
...
# 對每個 event 篩選出文字訊息
for event in events: ...
# 交給 LangChain Agent 生成回覆訊息
tool_result = open_ai_agent.run(event.message.text)
程式碼
# 71-97 行 Webhook
@app.post("/callback")
async def handle_callback(request: Request):
# 取得 events
...
# 對每個 event 篩選出文字訊息
for event in events: ...
# 交給 LangChain Agent 生成回覆訊息
...
# 回覆訊息給 LINE 用戶
await line_bot_api.reply_message(event.reply_token,
TextSendMessage(text=tool_result))
return 'OK'
我的 Fork
我的 Fork
open_ai_agent = initialize_agent(
tools,
model,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
#agent=AgentType.OPENAI_FUNCTIONS,
verbose=False
)
我的 Fork
感謝聆聽