意外と（？）いろいろできるAmazon Bedrock Prompt ManagementがGAしましたよ！

Amazon BedrockのPrompt ManagementがGAしました🎉🎉🎉

https://aws.amazon.com/jp/blogs/machine-learning/amazon-bedrock-prompt-management-is-now-available-in-ga/



プレビュー時との違いなどはこちらに情報がありました！（ありがとうございます！）

https://dev.classmethod.jp/articles/amazon-bedrock-prompt-management-ga/



## Prompt Managementとは？

プロンプトをテンプレート化して登録しておく機能です。（超ざっくり！）

### テンプレート化？

以下のプロンプトを例にします。

```text:プロンプト
<text>の内容を英語に翻訳してください。

<text>こんにちは</text>
```

このプロンプトの一部分を変数（Variables)にして、再利用しやすい形にします。
変数は`{{` `}}`で囲って表現します。

```text:プロンプトテンプレート
<text>の内容を{{language}}に翻訳してください。

<text>{{message}}</text>
```
```text:変数（Variables)
language
message
```

## Prompt Managementやってみた

では、Prompt Managementを使ってみましょう

まずは、Boto3を「1.35.56」以降にアップデートします。

```shell:ライブラリーのインストール
pip install -U boto3
```

:::note warn
「create_prompt」APIを使用するので、作成するものは「プロンプト」となるのですが、一般用語としての「プロンプト」と見分けがつかないので、本記事では「"プロンプト"」とダブルクオートで囲って表現することにします。
:::


"プロンプト"の作成は「bedrock-agent」を使います。
"プロンプト"の利用は従来通り「bedrock-runtime」を使います。


```python:クライアントの定義
import boto3

bedrock_agent = boto3.Session().client("bedrock-agent")
bedrock_runtime = boto3.Session().client("bedrock-runtime")
```

プロンプトテンプレートのタイプとして、2024/11/09時点では「TEXT」と「CHAT」の２種類あります。

### シンプルなテキスト（templateType: TEXT）

"プロンプト"の作成はAPIで一発です。

```python: "プロンプト"の作成
response = bedrock_agent.create_prompt(
    name="prompt1",
    variants=[
        {
            "name": "variant-1",
            "modelId": "anthropic.claude-3-haiku-20240307-v1:0",
            "templateType": "TEXT",
            "templateConfiguration": {
                "text": {
                    "text": "<text>の内容を{{language}}に翻訳してください。<text>{{message}}</text>",
                    "inputVariables": [{"name": "language"}, {"name": "message"}],
                }
            },
        }
    ],
)

prompt1_arn = response["arn"]
```

他に推論のパラメーター（inferenceConfiguration）やメタデータ（metadata）の指定も可能です。

作成した"プロンプト"を使用するにはInvokeModel APIやConverse APIを使用します。

通常の呼び出しと異なる部分は以下のとおりです。

* modelIdに"プロンプト"のARNを指定する
* promptVariablesパラメーターで、変数を指定する
* messagesパラメーターがない

```python: "プロンプト"の呼び出し
respnose = bedrock_runtime.converse(
    modelId=prompt1_arn,
    promptVariables={"language": {"text": "英語"}, "message": {"text": "こんにちは"}},
)
```

呼び出し結果のフォーマットは通常のConverse APIと同様です。

```text: 呼び出し結果
{'ResponseMetadata': {'RequestId': 'd204bb30-a46c-4319-ae47-824858fe12fc',
  'HTTPStatusCode': 200,
  'HTTPHeaders': {'date': 'Sat, 09 Nov 2024 01:31:43 GMT',
   'content-type': 'application/json',
   'content-length': '251',
   'connection': 'keep-alive',
   'x-amzn-requestid': 'd204bb30-a46c-4319-ae47-824858fe12fc'},
  'RetryAttempts': 0},
 'output': {'message': {'role': 'assistant',
   'content': [{'text': 'Here is the translation of the text in English:\n\n<text>Hello</text>'}]}},
 'stopReason': 'end_turn',
 'usage': {'inputTokens': 35, 'outputTokens': 21, 'totalTokens': 56},
 'metrics': {'latencyMs': 1010}}
```

:::note
呼び出し部分がとてもシンプルになりますね！
:::

### 複雑なこともできるチャット（templateType: CHAT）

templateTypeがTEXTの場合は、シンプルな一つのテキストのみでしたが、CHATを使うと複雑なことができるようになります。

具体的には以下のような違いがあります。

* システムプロンプトの指定ができる
* messagesとして、ユーザーとアシスタントのやり取りを複数指定できる（5つまで？）
* ツールの指定ができる（後述）

```python: "プロンプト"の作成
response = bedrock_agent.create_prompt(
    name="prompt2",
    variants=[
        {
            "name": "variant-1",
            "modelId": "anthropic.claude-3-haiku-20240307-v1:0",
            "templateType": "CHAT",
            "templateConfiguration": {
                "chat": {
                    "system": [{"text": "あなたは優秀なAIアシスタントです"}],
                    "messages": [
                        {
                            "role": "user",
                            "content": [
                                {
                                    "text": "<text>の内容を{{language}}に翻訳してください。<text>{{message}}</text>"
                                }
                            ],
                        }
                    ],
                    "inputVariables": [{"name": "language"}, {"name": "message"}],
                }
            },
        }
    ],
)

prompt2_arn = response["arn"]
```

:::note
contentで指定できるタイプはtextのみのようです。（Imageなどはできなさそう）
:::

呼び出し部分は変わりません。

```python: "プロンプト"の呼び出し
respnose = bedrock_runtime.converse(
    modelId=prompt2_arn,
    promptVariables={"language": {"text": "英語"}, "message": {"text": "こんにちは"}},
)
```

せっかくチャットなので、複数回のやり取りをしたい場合はどうするのでしょう？

ドキュメント上明確な記載は見つけられませんでしたが、初回のレスポンス以降をmessasgesにセットするとうまくいきました。

```python: "プロンプト"の呼び出し（会話継続）
messages = []
messages.append(respnose["output"]["message"])
messages.append(
    {
        "role": "user",
        "content": [{"text": "ありがとう。私が依頼した内容を復唱してください。"}],
    }
)

respnose = bedrock_runtime.converse(
    modelId=prompt2_arn,
    promptVariables={"language": {"text": "英語"}, "message": {"text": "こんにちは"}},
    messages=messages,
)
```
```text: 呼び出し結果
{'ResponseMetadata': {'RequestId': '166daa25-6feb-4da4-a53a-3778e7a60734',
  'HTTPStatusCode': 200,
  'HTTPHeaders': {'date': 'Sat, 09 Nov 2024 01:49:55 GMT',
   'content-type': 'application/json',
   'content-length': '331',
   'connection': 'keep-alive',
   'x-amzn-requestid': '166daa25-6feb-4da4-a53a-3778e7a60734'},
  'RetryAttempts': 0},
 'output': {'message': {'role': 'assistant',
   'content': [{'text': '依頼された内容は以下の通りです:\n\n<text>こんにちは</text>\n\nを英語に翻訳してください、というものでした。'}]}},
 'stopReason': 'end_turn',
 'usage': {'inputTokens': 93, 'outputTokens': 47, 'totalTokens': 140},
 'metrics': {'latencyMs': 1060}}
 ```

 ## Tool呼び出し（templateType: CHAT）

 templateTypeがCHATの場合はツール呼び出しも"プロンプト"に含めることができます。
 toolConfigurationブロックでツールを定義します。

 ```python: "プロンプト"の作成
response = bedrock_agent.create_prompt(
    name="prompt3",
    variants=[
        {
            "name": "variant-1",
            "modelId": "anthropic.claude-3-haiku-20240307-v1:0",
            "templateType": "CHAT",
            "templateConfiguration": {
                "chat": {
                    "messages": [
                        {
                            "role": "user",
                            "content": [
                                {"text": "{{location}}の天気は？"},
                            ],
                        }
                    ],
                    "inputVariables": [{"name": "location"}],
                    "toolConfiguration": {
                        "toolChoice": {"auto": {}},
                        "tools": [
                            {
                                "toolSpec": {
                                    "name": "get_weather",
                                    "description": "Get the current weather in a given location",
                                    "inputSchema": {
                                        "json": {
                                            "type": "object",
                                            "properties": {
                                                "location": {
                                                    "type": "string",
                                                    "description": "The city and state, e.g. San Francisco, CA",
                                                }
                                            },
                                            "required": ["location"],
                                        }
                                    },
                                }
                            }
                        ],
                    },
                }
            },
        }
    ],
)

prompt3_arn = response["arn"]
```

```python:"プロンプト"の呼び出し（会話継続）
respnose = bedrock_runtime.converse(
    modelId=prompt3_arn,
    promptVariables={"location": {"text": "東京"}},
)
```

```text: 呼び出し結果
{'ResponseMetadata': {'RequestId': '3f61250f-f524-4ad3-bbcc-4f329095915b',
  'HTTPStatusCode': 200,
  'HTTPHeaders': {'date': 'Sat, 09 Nov 2024 01:56:04 GMT',
   'content-type': 'application/json',
   'content-length': '281',
   'connection': 'keep-alive',
   'x-amzn-requestid': '3f61250f-f524-4ad3-bbcc-4f329095915b'},
  'RetryAttempts': 0},
 'output': {'message': {'role': 'assistant',
   'content': [{'toolUse': {'toolUseId': 'tooluse_RXZdMEfySyGT5ZZIS9RpnQ',
      'name': 'get_weather',
      'input': {'location': '東京'}}}]}},
 'stopReason': 'tool_use',
 'usage': {'inputTokens': 353, 'outputTokens': 54, 'totalTokens': 407},
 'metrics': {'latencyMs': 724}}
```

期待通り、呼び出し結果にtoolUseが含まれます。
ツール実行結果をtoolResultとしてセットし、再度呼び出します。

```python:"プロンプト"の呼び出し（ツール実行結果）
messages = []
messages.append(respnose["output"]["message"])
messages.append(
    {
        "role": "user",
        "content": [
            {
                "toolResult": {
                    "toolUseId": respnose["output"]["message"]["content"][0]["toolUse"][
                        "toolUseId"
                    ],
                    "content": [{"text": "晴れ"}],
                }
            }
        ],
    }
)

respnose = bedrock_runtime.converse(
    modelId=prompt3_arn,
    promptVariables={"location": {"text": "東京"}},
    messages=messages,
)
```
```text:呼び出し結果
{'ResponseMetadata': {'RequestId': '5de772cd-f4cb-4b6a-8bff-0c1de97316ee',
  'HTTPStatusCode': 200,
  'HTTPHeaders': {'date': 'Sat, 09 Nov 2024 01:56:08 GMT',
   'content-type': 'application/json',
   'content-length': '217',
   'connection': 'keep-alive',
   'x-amzn-requestid': '5de772cd-f4cb-4b6a-8bff-0c1de97316ee'},
  'RetryAttempts': 0},
 'output': {'message': {'role': 'assistant',
   'content': [{'text': '東京の天気は晴れです。'}]}},
 'stopReason': 'end_turn',
 'usage': {'inputTokens': 423, 'outputTokens': 15, 'totalTokens': 438},
 'metrics': {'latencyMs': 1035}}
```

## エージェント呼び出し

GAのタイミングでBedrockエージェントにも対応したようです。

:::note warn
"プロンプト"の作成まではできるものの、呼び出し方がわからないです。。
Converse APIではエラーになり、Invoke Agentだと"プロンプト"を指定する方法がわからないです。
:::

TBD.