---
# System prepended metadata

title: '[LangChain] 範例－飲食建議與分類'
tags: [LangChain_samples]

---

# [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:  那聽起來很棒！你打什麼球呢？






