---
# System prepended metadata

title: Chapter12. 用 LangChain 實作新書宣傳自動小編
tags: [ChatGPT 開發手冊]

---

# 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)
```

> 但這個部分我執行出現錯誤
> 
> ![Screenshot 2024-03-18 at 12.24.56 AM](https://hackmd.io/_uploads/SkQDF94AT.png)
>
> 目前無解（囧）

### 建立 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')
```

![Screenshot 2024-03-18 at 12.42.28 AM](https://hackmd.io/_uploads/By-taqNR6.png)

## 串接 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)
```

輸出結果：

![Screenshot 2024-03-18 at 1.02.17 AM](https://hackmd.io/_uploads/H1mmMsERa.png)