Amazon Bedrock Workshop (テキスト生成ラボ編、動くようにしておいたよ！)

# はじめに
本日(2023/9/29)、Amazon Bedrock Workshopが公開されました。
コンテンツとしては、以下の内容となっており、今回はやってみて詰まったところについて修正加えながらまとめていきます。

https://catalog.us-east-1.prod.workshops.aws/workshops/a4bdb007-5600-4368-81c5-ff5b4154f518/ja-JP

* テキスト生成 [完了までの推定時間-30 mins]
* 文章要約 [完了までの推定時間-30 mins]
* 質問への回答 [完了までの推定時間-45 mins]
* チャットボット [完了までの推定時間-45 mins]
* 画像生成 [完了までの推定時間-30 mins]
* コード生成 [完了までの推定時間-30 mins]
* エージェント [完了までの推定時間-30 mins]

# テキスト生成ワークショップ
さあ、やっていきましょう。

内容としては、LLM の使用方法と、LangChainを利用してBedrockとインテグレーションする方法について学べるコンテンツとなっています。
このラボでは、あるカスタマーサポートへお客様からクレームが入ったことに対して謝罪メールを自動生成することをしていきます。

使用するプロンプトは以下を使うそうです。

```
Write an email from Bob, Customer Service Manager, to the customer "John Doe" 
who provided negative feedback on the service provided by our customer support 
engineer
```

## ワークショップのための下準備

さあ、問題は、下記のパートについて実装方法がまだ何も書いてませんw
と思ったらどうやら下記のワークショップのGitに詳細が書いてあったため、こちらをみながら進めていきます。
https://github.com/aws-samples/amazon-bedrock-workshop/tree/main

* 重要なのですが、ローカルで試す場合は、aws cliは最新版にしておきましょう。(ここで僕は1時間以上頭を悩ませました。)

* まずは、ワークショップ用に下記のリポジトリをcloneしてきます。

```
git clone https://github.com/aws-samples/amazon-bedrock-workshop.git
```

* amazon-bedrock-workshopに移動したのち、直下に入っているdownload-dependencies.shを実行します。これにより、Bedrockの使用に必要なSDKパッケージを取得できます。

```
sh download-dependencies.sh 
```

* 次を実行します。下記の手順なのですが、これは無視してください。これやると動きません。
https://github.com/aws-samples/amazon-bedrock-workshop/blob/main/01_Generation/00_generate_w_bedrock.ipynb


```
×(この手順は飛ばします。) pip install --no-build-isolation --force-reinstall \
    dependencies/awscli-*-py3-none-any.whl \
    dependencies/boto3-*-py3-none-any.whl \
    dependencies/botocore-*-py3-none-any.whl
```

代わりに下記を実行しましょう。
```
pip install --upgrade boto3
```

* 次にlangchainのバージョンを指定してインストールをしていきます。
```
pip install --quiet langchain==0.0.304
```

* ここまで準備ができたら、あとは実装をしていく感じになります。

## まずは、boto3からbedrockが呼び出せるか確認します。

下記のコードでFMの最新リストを取得してみます。

```py
import json
import os
import sys

import boto3

bedrock_init = boto3.client(service_name='bedrock')

# Extract the modelId values
output = bedrock_init.list_foundation_models()
model_ids = [model['modelId'] for model in output['modelSummaries']]

# Print the modelIds
for model_id in model_ids:
    print(model_id)

```

```
❯ python bedrock.py
Create new client
  Using region: us-east-1
boto3 Bedrock client successfully created!
bedrock(https://bedrock.us-east-1.amazonaws.com)
amazon.titan-tg1-large
amazon.titan-e1t-medium
amazon.titan-embed-g1-text-02
amazon.titan-text-express-v1
amazon.titan-embed-text-v1
stability.stable-diffusion-xl
stability.stable-diffusion-xl-v0
ai21.j2-grande-instruct
ai21.j2-jumbo-instruct
ai21.j2-mid
ai21.j2-mid-v1
ai21.j2-ultra
ai21.j2-ultra-v1
anthropic.claude-instant-v1
anthropic.claude-v1
anthropic.claude-v2
```
いい感じですね！
ただ、ここで重要なのは多分まだここで出てくるモデルは古いものだと思っており、今だと上記のモデル全ては使えないことがこの後のパート1で分かります。


## パート１
* Amazon Titanで電子メールを生成します。
* Bedrock APIを使用してAmazon Titanラージテキストモデルを呼び出し、お客様への電子メール応答を生成します。
* モデルへの指示としてはゼロショットプロンプトを使用します。
* アーキテクチャは超シンプルで、boto3 SDKからBedrock　APIを叩くような形です。

* 実装手順はこちらにあるので、上から舐めていきます。

https://github.com/aws-samples/amazon-bedrock-workshop/blob/main/01_Generation/00_generate_w_bedrock.ipynb

**と思ったのですが、これやってみるとamazon.titan-tg1-largeのモデルが使えず下記のようなエラーになってしまいました。たしかに使用できるモデルを見てもtitan-tg1-largeの表記に被るものがないので、なるほどとなりました。**

![スクリーンショット 2023-09-29 21.57.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/1d89e87d-e093-1fb4-efbf-3753e28bbefb.png)

```
AccessDeniedException: An error occurred (AccessDeniedException) when calling the InvokeModel operation: Your account is not authorized to invoke this API operation.
```

## パート2
* 次は、LangChainを活用し、Bedrockとやりとりをします。
* ユーザーはBedrock APIを使用して入力し、Anthropic Claudeモデルがリクエストを処理します。
その後、Bedrockは生成されたメール/テキストで応答する形で実装していきます。

* 最終的な成果物は下記となります。
* ただ、こちらもGitHubのワークショップに掲載されているコードだと使えず、修正し動くようになっています。

```py
import json
import os
import sys

import boto3

module_path = ".."
sys.path.append(os.path.abspath(module_path))
from utils import print_ww

bedrock_init = boto3.client(service_name='bedrock-runtime')

from langchain.llms.bedrock import Bedrock

inference_modifier = {
    "max_tokens_to_sample": 4096,
    "temperature": 0.5,
    "top_k": 250,
    "top_p": 1,
    "stop_sequences": ["\n\nHuman"],
}

textgen_llm = Bedrock(
    model_id="anthropic.claude-v2",
    client=bedrock_init,
    model_kwargs=inference_modifier,
)

response = textgen_llm("""

Human: Write an email from Bob, Customer Service Manager, 
to the customer "John Doe" that provided negative feedback on the service 
provided by our customer support engineer.

Assistant:""")

print_ww(response)
```

実行してみると、Anthropic Claudeモデルで作成されたメールの文面が返ってきました。

```
❯ python bedrock.py          
 Dear Mr. Doe,

I'm writing in regards to the negative feedback you recently provided about your experience with one
of our customer support engineers. I want to sincerely apologize for the poor service you received.

At ABC Company, we strive to provide exceptional customer service and support. It's clear that we
fell short in this instance. Your business is extremely important to us, and we want to rectify this
situation.

I have spoken with the support engineer in question to understand what went wrong. We will be taking
steps to ensure this does not happen again. This includes additional training on communication
skills and customer service best practices.

I understand that this negative experience has likely impacted your view of our company. However, I
hope you will give us another chance to prove we can deliver the excellent service you deserve.
Please let me know if there is anything I can do to regain your confidence in us.

If you have any other feedback or concerns, do not hesitate to reach out to me directly. I am
committed to making this right and ensuring you have a positive experience going forward. Thank you
for bringing this to my attention - it will help us improve.

Sincerely,

Bob
Customer Service Manager
ABC Company
```


## パート3

* 最後のラボは、下記になります。

https://catalog.us-east-1.prod.workshops.aws/workshops/a4bdb007-5600-4368-81c5-ff5b4154f518/ja-JP/30-generation/32-contextual-generation

* 動きとしては次のようになり、まずユーザーはLangChainを使用して入力リクエストとコンテキスト情報を与えます。その後、Anthropic Claudeモデルがリクエストを処理し、Bedrock は生成されたメール/テキストでレスポンスを返す形となります。

* 最終的な成果物は下記となります。
* ただ、こちらもGitHubのワークショップに掲載されているコードだと使えず、修正し動くようになっています。

```py
import json
import os
import sys

import boto3

module_path = ".."
sys.path.append(os.path.abspath(module_path))
from utils import print_ww

bedrock_init = boto3.client(service_name='bedrock-runtime')

from langchain.llms.bedrock import Bedrock

inference_modifier = {'max_tokens_to_sample':4096, 
                      "temperature":0.5,
                      "top_k":250,
                      "top_p":1,
                      "stop_sequences": ["\n\nHuman"]
                     }

textgen_llm = Bedrock(model_id = "anthropic.claude-v2",
                    client = bedrock_init, 
                    model_kwargs = inference_modifier 
                    )

from langchain import PromptTemplate

# Create a prompt template that has multiple input variables
multi_var_prompt = PromptTemplate(
    input_variables=["customerServiceManager", "customerName", "feedbackFromCustomer"], 
    template="""

Human: Create an apology email from the Service Manager {customerServiceManager} to {customerName} in response to the following feedback that was received from the customer: 
<customer_feedback>
{feedbackFromCustomer}
</customer_feedback>

Assistant:"""
)

# Pass in values to the input variables
prompt = multi_var_prompt.format(customerServiceManager="Bob", 
                                 customerName="John Doe", 
                                 feedbackFromCustomer="""Hello Bob,
     I am very disappointed with the recent experience I had when I called your customer support.
     I was expecting an immediate call back but it took three days for us to get a call back.
     The first suggestion to fix the problem was incorrect. Ultimately the problem was fixed after three days.
     We are very unhappy with the response provided and may consider taking our business elsewhere.
     """
     )

num_tokens = textgen_llm.get_num_tokens(prompt)
print(f"Our prompt has {num_tokens} tokens")

response = textgen_llm(prompt)

email = response[response.index('\n')+1:]

print_ww(email)
```



```
I want to sincerely apologize for the poor experience you had with our customer support team.
Providing excellent customer service is our top priority, and I am very disappointed that we fell
short in handling your recent issue.

I completely understand your frustration with the delayed response time and incorrect suggestions
before the problem was ultimately resolved. This is unacceptable, and you deserve much better from
us. Our typical response time is within a few hours, so I have followed up with our team to
understand what went wrong here and make sure it does not happen again.

I assure you we are taking steps to improve our processes so problems get escalated and resolved
promptly in the future. This includes additional training for our support staff on troubleshooting
your specific issue, as well as implementing new policies to ensure priority follow-up on open
cases.

I appreciate you taking the time to share your feedback. Your business is extremely important to us,
and we do not want this experience to jeopardize our relationship. Please let me know if there is
anything else I can do to restore your confidence in our customer service team. I am available
anytime to discuss this further and would welcome the opportunity to regain your trust.

Again, my sincere apologies for letting you down. We are committed to doing better, and I hope you
will give us another chance to provide the excellent service you deserve.

Sincerely,

Bob
Service Manager
```

# 公式ワークショップリンク

https://catalog.us-east-1.prod.workshops.aws/workshops/a4bdb007-5600-4368-81c5-ff5b4154f518/ja-JP

https://github.com/aws-samples/amazon-bedrock-workshop/tree/1bed952ae51fa559a7325207c8cc3c525211ea4b