# [LangChain] 範例-飲食建議與分類
日期:2023/09/20
## 敘述
- 主題:StructuredOutputParser()、MultiPromptChain()、LLMRouterChain()
- 使用情境:透過此範例程式將針對提供的飲食敘述進行回應,可獲得飲食方面的建議。另外也可讓其根據食物六大類進行分類、整理,輸出一個容易理解的結構,以便後續作更多運用,例如:紀錄。
- 輸入:飲食敘述。例如:我今天吃了一份炒泡麵。
- 輸出:
1.給予飲食相關建議
2.分類、整理敘述中的飲食
3.回答任意問題
備註:此範例使用到範例-飲食敘述分類的功能。
## 目錄
- [1. 主程式](#1-主程式)
- [2. 輸入輸出](#2-輸入輸出)
____________________
## 1. 主程式
導入套件
```python=
from dotenv import load_dotenv
load_dotenv()
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
from langchain.prompts import (
PromptTemplate
)
from langchain import LLMChain
llm = ChatOpenAI(temperature=0)
```
```python=
from langchain.chains.router import MultiPromptChain
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
```
程式包含幾個功能:1.對飲食敘述給予建議 2.根據食物六大類進行分類 3.回應其他非前兩種類型的輸入。
前兩個功能分別透過各自的destination prompt實現。若敘述中包含飲食,則會使用prompt1,進而實現功能1;若敘述中有要求紀錄,則使用prompt2,進而實現功能2。若輸入接不符合上述兩個prompt,則將使用默認的prompt,進而實現功能3。
定義destination prompt。
```python=
#定義destination prompt
prompt1 = """對<>中提到的飲食進行評價和建議。
<{input}>"""
# prompt1 = """不論<>的內容是甚麼,只需要輸出『此為prompt1的輸出』即可。
# <{input}>"""
# prompt2 = "確認<>中的句子是否包含任何飲食種類。若沒有提及,輸入Empty;若有提及,請將提到的飲食種類列舉出來: <{input}>.\n{format_instructions}"
prompt2 = "請判斷<>中提及的飲食或料理,根據營養學六大類食物進行分類。\n{format_instructions}\n<{input}>"
# prompt2 = """不論<>的內容是甚麼,只需要輸出『此為prompt2的輸出』即可。
# <{input}>"""
#使用以下格式整理所有promtpt
prompt_infos = [
{
"name": "dietary recommendations",
"description": "如果有提到飲食相關的敘述,請使用這個prompt。",
"prompt_template": prompt1,
},
{
"name": "request record",
"description": "若輸入中要求'紀錄',請使用這個prompt。",
"prompt_template": prompt2,
},
]
```
將不同的prompt用於定義各自的chain-chain1、chain2。
destinations_str將在之後提供給router_chain。
```python=
#使用所有prompt定義各自的chain
destination_chains = {}
#chain1(prompt1)
p_info = prompt_infos[0]
name = p_info["name"]
prompt_template = p_info["prompt_template"]
prompt = PromptTemplate(template=prompt_template, input_variables=["input"])
llm = ChatOpenAI(temperature=0)
chain = LLMChain(llm=llm, prompt=prompt)
destination_chains[name] = chain
#chain2(prompt2)
p_info = prompt_infos[1]
name = p_info["name"]
prompt_template = p_info["prompt_template"]
#定義output parser
response_schemas = [
ResponseSchema(name='CER', description='請將<五穀類>的食物放到此處。'),
ResponseSchema(name='VEG', description='請將<蔬菜類>的食物放到此處。'),
ResponseSchema(name='FRU', description='請將<水果類>的食物放到此處。'),
ResponseSchema(name='M&L', description='請將<肉類和豆類>的食物放到此處。'),
ResponseSchema(name='DAIRY', description='請將<奶製品類>的食物放到此處。'),
ResponseSchema(name='F&N', description='請將<油脂和堅果類>的食物放到此處。'),
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
template=prompt_template,
input_variables=["input"],
partial_variables={"format_instructions": format_instructions},
output_parser=output_parser
)
chain = LLMChain(prompt=prompt, llm=llm)
# chain = LLMChain(prompt=prompt, llm=OpenAI())
destination_chains[name] = chain
#destinations_str包含各個不同prompt的名稱與敘述,router_chain將根據destinations_str選擇使用甚麼prompt
destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
destinations_str = "\n".join(destinations)
```
定義router_chain以及default_chain,並將所有chain都加入到MultiPromptChain()中。
```python=
#定義router_chain
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)
router_prompt = PromptTemplate(
template=router_template,
input_variables=["input"],
output_parser=RouterOutputParser(),
)
llm = ChatOpenAI(temperature=0)
# router_chain = LLMRouterChain.from_llm(llm, router_prompt)
router_chain = LLMRouterChain.from_llm(OpenAI(temperature=0), router_prompt)
#定義default_chain
default_prompt = '''請根據<>中的敘述給予回應。
<{input}>'''
default_prompt = ChatPromptTemplate.from_template(default_prompt)
default_chain = LLMChain(llm=llm, prompt=default_prompt)
#將chain都加入到MultiPromptChain()中
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=default_chain,
verbose=True,
)
```
## 2. 輸入輸出
將輸入提供給程式。
測試prompt1-飲食建議。
```python=
print(chain.run("我今天吃了十顆哈密瓜、兩個蘋果派、一片巧克力蛋糕、一個素食便當。"))
```
:::success
\> Entering new MultiPromptChain chain...
dietary recommendations: {'input': '我今天吃了十顆哈密瓜、兩個蘋果派、一片巧克力蛋糕、一個素食便當。'}
\> Finished chain.
A: 對於你提到的飲食,我可以給出以下評價和建議:
1\. 十顆哈密瓜:哈密瓜是一種水分豐富、低熱量的水果,對於保持身體水分平衡和提供維生素C有益。然而,過量攝取哈密瓜可能會導致消化不良或腹瀉。建議適量食用,每天約一到兩顆。
2\. 兩個蘋果派:蘋果派含有大量的糖分和脂肪,並且烹調過程中可能添加了其他高熱量成分。過量攝取這樣的甜點可能導致體重增加和血糖波動。建議適量享用,可以考慮選擇低糖或無糖的版本。
3\. 一片巧克力蛋糕:巧克力蛋糕是高熱量、高糖分的甜點,過量攝取可能導致體重增加和血糖波動。建議限制甜點的攝取量,選擇健康的替代品,如水果或低糖的甜點。
4\. 一個素食便當:素食便當通常包含豆類、蔬菜和全穀物,提供豐富的蛋白質、纖維和營養素。這是一個健康的選擇,有助於維持身體的平衡和健康。建議繼續選擇素食便當,並確保攝取足夠的蛋白質和其他必需營養素。
總體而言,飲食的平衡和多樣性是關鍵。建議適量攝取水果和蔬菜,限制高糖和高脂肪的甜點,並確保攝取足夠的蛋白質和其他營養素。
:::
測試prompt2-飲食分類。
```python=
print(chain.run("幫我記錄,今天吃了一份炒泡麵"))
```
:::success
\> Entering new MultiPromptChain chain...
request record: {'input': '幫我記錄,今天吃了一份炒泡麵'}
\> Finished chain.
\```json
{
"CER": "炒泡麵",
"VEG": "",
"FRU": "",
"M&L": "",
"DAIRY": "",
"F&N": ""
}
\```
:::
其他輸入測試結果如下:
:::success
Q: 記錄,我今天早上吃了十顆哈密瓜、兩個蘋果派;而下午時,吃了一片巧克力蛋糕、一個素食便當。
\> Entering new MultiPromptChain chain...
request record: {'input': '請紀錄我今天早上吃了十顆哈密瓜、兩個蘋果派;而下午時,吃了一片巧克力蛋糕、一個素食便當。'}
\> Finished chain.
A: \```json
{
"CER": "",
"VEG": "一個素食便當",
"FRU": "十顆哈密瓜、兩個蘋果派",
"M&L": "",
"DAIRY": "",
"F&N": "一片巧克力蛋糕"
}
\```
\------------------------------
Q: 我今天想去打球
\> Entering new MultiPromptChain chain...
None: {'input': '我今天想去打球'}
\> Finished chain.
A: 那聽起來很棒!你打什麼球呢?