# Chapter12. 用 LangChain 實作新書宣傳自動小編
:+1: 完整程式碼在 https://github.com/iamalex33329/chatgpt-develop-guide-zhtw
## 其他章節
[Chapter1. OpenAI API 入門](https://hackmd.io/@U3f2IzHERbymAst2-lDdjA/S1cNMYi6T)
[Chapter2. 使用 Python 呼叫 API](https://hackmd.io/@U3f2IzHERbymAst2-lDdjA/HyZBg5ia6)
[Chapter3. API 參數解析與錯誤處理](https://hackmd.io/@U3f2IzHERbymAst2-lDdjA/BJWNtsh6p)
[Chapter4. 打造自己的 ChatGPT](https://hackmd.io/@112356044/Hk81U96Tp)
[Chapter5. 突破時空限制 - 整合搜尋功能](https://hackmd.io/@112356044/HkbVM-ApT)
[Chapter6. 讓 AI 幫 AI - 自動串接流程](https://hackmd.io/@112356044/r1Ke-GR6T)
[Chapter7. 網頁版聊天程式與文字生圖 Image API](https://hackmd.io/@112356044/Hyf-AvgAT)
[Chapter8. 設計 LINE AI 聊天機器人](https://hackmd.io/@112356044/r1d6HsgAa)
[Chapter9. 自媒體業者必看!使用 AI 自動生成高品質字幕](https://hackmd.io/@112356044/rJ2T37V0T)
[Chapter10. 把 AI 帶到 Discord](https://hackmd.io/@112356044/Sy_L-B40T)
[Chapter11. AI 客製化投資理財應用實戰](https://hackmd.io/@112356044/HkUE0rER6)
## 目錄結構
[TOC]
LangChain 是一個框架,可以協助開發者使用語言模型建構應用程式,藉此來簡化開發流程。
## 認識 LangChain
LangChain 的主要功能是把語言模型與外部工具**整合**成應用程式,讓應用程式自動選擇適合的工具完成要求。就像 ChatGPT Plus 能夠自行選擇合適的外掛一樣。
上述功能主要由以下元件組合:
1. Model:串接語言模型,例如使用 OpenAI 與模型互動。
2. Chain:把原本要自己依序完成的工作,依照固定的順序串接起來自動完成。
3. Memory:紀錄對話內容,像是 ChatGPT 能夠記得前幾次的對話紀錄。
4. Agent:可根據內容在必要時拆解成各個子步驟,再針對子步驟從指定工具中挑選適當的工具來完成任務。例如要回答「台灣最高的山的高度的兩倍是多少?」,Agent 能夠拆解成以下兩個步驟
1. 台灣最高的山的高度 :arrow_right: 透過搜尋工具得知最高的山為**玉山**,其高度為 3952 公尺
2. 高度的兩倍是多少 :arrow_right: 以運送工具將 3952 公尺乘以 2 倍,得出 7904 公尺
LangChain 的主要核心為 **Model**,其他元件則是依照需求結合使用。
若是需要固定順序完成的任務,可以使用 **Chain**;若是需要邏輯判斷獲釋根據前項輸出才能決定接下來的任務,則使用 **Agent** 較為恰當。
## 熟悉 LangChain 基本功能
在 Python 中使用 LangChain 需要特別安裝套件
``` python=
!pip install langchain
from langchain.chat_models import ChatOpenAI
```
### 使用 ChatOpenAI
可以把 ChatOpenAI 當作 ChatCompletion 的抽象化版本,使用時要先建立 ChatOpenAI 物件,搭配對應到 user、system、assistant 角色訊息的 HumenMessage、SystemMessage、AIMessage 物件。
``` python=
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
AIMessage,
SystemMessage,
HumanMessage,
)
import apikey
model = ChatOpenAI(
temperature=0,
openai_api_key=apikey.OPENAI_API_KEY
)
messages = ([HumanMessage(content='回答問題使用繁體中文和台灣詞語解釋, 問題:NBA 的英文全名是?')])
response = model(messages)
print(response.content)
```
### 提示模板(PromptTemplate)
前一個範例如果想要**提問別的問題**或是**使用其他語言回答**,就需要修改整個 Prompt,這邊可以使用**提示模板**的概念來解決。
提示模板可以將需要變化的部分轉為參數,等到執行時再以實際內容替換參數代入,以取得模型的回覆。
``` python=
from langchain.prompts.chat import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
```
並將剛剛的問題改為
``` python=+
system_message = '你是一位助手,回答時答案使用 {input_language}'
human_message = '問題:{question}'
system_template = SystemMessagePromptTemplate.from_template(system_message)
human_template = HumanMessagePromptTemplate.from_template(human_message)
```
ChatPromptTemplate 是由訊息提示模板**串列**組成的對話提示模板
``` python=+
chat_prompt = ChatPromptTemplate.from_messages([system_template, human_template])
```
#### 輸入參數傳回給模型
``` python=+
message = chat_prompt.from_messages(
input_language='繁體中文和台灣詞語',
text='台灣最高的建築物是?'
)
response = model(message)
print(response.content)
```
> 但這個部分我執行出現錯誤
>
> 
>
> 目前無解(囧)
### 建立 LLMChain
若是使用 LLMChain 是正常可以執行的,而且也能正確得到輸出:
``` python=
from langchain import LLMChain
from langchain_openai import ChatOpenAI
from langchain.prompts.chat import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
import apikey
model = ChatOpenAI(
temperature=0,
openai_api_key=apikey.OPENAI_API_KEY
)
system_message = '你是一位助手,回答答案時使用 {input_language}'
human_message = '問題:{text}'
system_template = SystemMessagePromptTemplate.from_template(system_message)
human_template = HumanMessagePromptTemplate.from_template(human_message)
chat_prompt = ChatPromptTemplate.from_messages([system_template, human_template])
chain = LLMChain(llm=model, prompt=chat_prompt)
answer = chain.run(input_language='繁體中文和台灣詞語', text='台灣最高的建築物是?')
print(answer)
```
```
台灣最高的建築物是台北101大樓,位於台北市信義區。截至目前,台北101大樓是台灣最高的建築物,也是世界第十高的摩天大樓。
```
### 對話記憶 Memory
如果將上述的程式碼放到迴圈中持續問問題,沒辦法判斷上下文的脈絡或是前一個問題的答案,因此我們可以加上 `Memory` 元件幫助程式記憶問題和答案,就能實現對話記憶。
``` python=
import time
from langchain_openai import ChatOpenAI
from langchain.prompts import MessagesPlaceholder
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain.prompts.chat import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
import apikey
model = ChatOpenAI(
temperature=0,
openai_api_key=apikey.OPENAI_API_KEY
)
ai_prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template('以下是使用者與AI之間的對話,AI很健談而且能根據上下文提供具體細節,如果AI不知道問題會如實說不知道。'),
MessagesPlaceholder(variable_name='history'),
HumanMessagePromptTemplate.from_template('{input}')
])
memory = ConversationBufferMemory(return_messages=True)
conversation = ConversationChain(
memory=memory,
prompt=ai_prompt,
llm=model,
verbose=True
)
while True:
question = input('Q: ')
if not question.strip(): break
response = conversation.predict(input=question)
print(response + '\n')
```

## 串接 Google search 及 Agent 代理運用
### 建立 LangChain 代理(Agent)
上一節的機器人雖然可以記錄歷史對話,但還是無法得知最新的資訊。在 LangChain 中,提供兩種機制可以協助語言模型:
1. 外部工具:可以提供語言模型額外功能
2. 代理元件(Agent):依照使用者的輸入去判斷要使用哪種工具
這節會使用 `GoogleSearchAPIWrapper` 和 `LLMMathChain`,`GoogleSearchAPIWrapper` 會將 Google Search API 串接到 LangChain,利用搜尋功能讓程式找尋未知資料;`LLMMathChain` 則是逕行計算的數學工具。
``` python=
!pip install google-api-python-client
!pip install numexpr
from langchain_openai import ChatOpenAI
from langchain import GoogleSearchAPIWrapper
from langchain import LLMMathChain
import ApiKey
model = ChatOpenAI(
temperature=0,
openai_api_key=ApiKey.OPENAI_API_KEY
)
search = GoogleSearchAPIWrapper()
llm_math_chain = LLMMathChain.from_llm(llm=model, verbose=True)
```
### 建立工具組
``` python=+
from langchain.agents import Tool
tools = [
Tool(
name="Search",
func=search.run,
description='如果不曉得答案,用此工具搜尋資料'
),
Tool(
name="Calculator",
func=llm_math_chain.run,
description='如果需要數學計算,用此工具計算'
),
]
```
### 建立 Agent
``` python=+
from langchain.agents import initialize_agent, AgentType
agent = initialize_agent(
llm=model,
tools=tools,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
```
> 這裡要注意,ZERO_SHOT_REACT_DESCRIPTION 沒辦法記憶歷史對話
``` python=+
response = agent.run('台灣最高的建築物相當於世界上最高的建築物的幾倍?請注意計算單位需一致!')
print(response)
```
輸出結果:
