🕵️

Agents for Amazon BedrockでClaudeに世紀末の正解を教える

2024/04/12に公開

生成AIも「知らない」と言うことがある

世紀末になると、突然襲ってきて「俺の名を言ってみろ」という無茶な質問をしてくる怖い人がいるそうです。
https://dic.pixiv.net/a/俺の名を言ってみろ

そんな困った質問にだって生成AIなら! と思ったけど、、、とんだポンコツでした。

煽ってどうする

もちろんキチンと事情を説明しながら質問すれば、正しい答えを返してくれます。

あの人がこんな親切なワケがない

このように質問の仕方を工夫することで生成AIから期待する回答を得る手法を「プロンプトエンジニアリング」というらしいですが、今回はこの方法に頼りません。質問する側が先に答えを教えてくれることに期待できない場面を想定しているためです。

生成AIにコッソリ正解を教える

それではどうするか。質問される側が正解をどこかから持ってこれるようにしてあげれば良さそうです。Amazon BedrockにはAgents for Amazon Bedrockという仕組みが用意されているので、それを使います。詳しい仕組みは引用先のドキュメントを見てください。
https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/agents-how.html

Agents for Amazon Bedrockには、生成AIに追加の知識を与えるための方法として、大雑把にKnowledge BaseとAction Groupsという2つの仕組みが用意されていて、雑に説明すると以下のようなイメージです。

名前 概要
Knowledge Base 関連する情報が検索できるデータベースを追加します。
Action Groups 関連する情報が取得できるWeb APIを追加します。

Knowledge Baseは今回は使わずに、比較的に手軽に生成AIを拡張できるAction Groupsの方を使って世紀末を生き残ることにします。

正解を知ってるAWS Lambda関数を作る

まずは、正解を教えてあげるためのWeb APIを作ります。Agents for Amazon Bedrockでは、AWS Lambda関数で実装することが可能です。AWS Lambda関数とAgents for Amazon Bedrockの間で交わされるオブジェクトは以下に詳しく書いてありますので、これにならって実装すれば簡単です。
https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/agents-lambda.html

今回はシンプルに答えるべき名前を教えてあげればよいので、responseオブジェクトの中にあるbody項目の内容だけを書き換えます。あくまでも例ですが、以下のようなイメージです。

    response_body = {
        'application/json': {
            'body': "ケンシロウ様"
        }
    }

もちろん、Amazon Bedrockから呼び出しできるように権限設定します。

実装を終えたらDeployしておきましょう。

正解を聞きに行くAgents for Amazon Bedrockを作る

続いて、Agents for Amazon Bedrockを作ります。手取り足取りスクリーンキャプチャ貼り付けて解説するものツラいので、ドキュメント読んで頑張ってください。
https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/agents-create.html

今回はAction Groupsを使って正解を持ってくることが目的なので、Agents for Amazon Bedrock自体のプロンプトはシンプルなものにしておきます。

大規模言語モデルには冒頭のポンコツ回答したヤツを指名

Action Groupsでつなげる

Agents for Amazon BedrockとAWS Lambda関数を関連付けるために、Action Groupsを追加します。

中身はこんな感じです。Lambda関数にWhoAmIFunctionが指定されていますが、これが先ほど作成した関数だと思ってください。なお、OpenAPI定義のdescriptionは手抜きしないでキチンと提供する情報について説明しておくのが良さそうです。理由は後でわかります。

もちろん、Agents for Amazon Bedrock側にもAWS Lambda関数が実行可能な権限を付与しておきます。

リソースは限定するのがベストプラクティスです(これは手抜きです)

Agents for Amazon Bedrockで世紀末を生き残る

これで、名前も知らない怖い人に襲われても生き残れるようになりました。

知らなきゃ生き残れない

裏でAgents for Amazon Bedrockが何をしていたのかは「トレースを表示」というリンクをたどると詳しく見ることが出来ます。ここでは、参考までに一部を紹介しておきます。

前処理トレース

既定のプロンプトを用いて生成AIを使ってユーザーからの質問を処理した結果、前処理段階で以下の結論を得ていたことが分かりました。後続の処理を実行することで回答可能と判断してくれていますね。

ユーザーの入力は「あなたの名前を教えてください」と尋ねています。 これはエージェントに自分の名前を要求している質問のようです。関数の一覧を確認すると、GET::WhoAmI::orenoNawoIttemiro関数があり、名前を尋ねられたときに優秀な弟の名前を返すように記述されています。 。 したがって、この入力は、関数呼び出しエージェントがその関数を呼び出すことで答えることができる質問です。 したがって、この入力は、提供された機能を使用してエージェントが回答できる質問のカテゴリ D に属します。

ちなみに、トレース情報はJSON形式かつ英語で出力されています。上記の情報は出力された内容の一部を抜き取って機械翻訳したものです。そして、出力された結果からは、生成AIがOpenAPI定義の内容をしっかりと読み込んで呼び出すか否かを判定していることが推測できます。

オーケストレーションとナレッジベース

JSONを見ると実際にAWS Lambda関数を呼び出したことが分かります。期待通りの挙動です。

  "rationale": {
    "text": "The user has asked: 俺の名を言ってみろ (Say my name)\n\nI have a tool called GET::WhoAmI::orenoNawoIttemiro that can provide an appropriate response when asked \"Say my name\".",
    "traceId": "7a468b13-92d9-4ed2-9b5b-14ccc9ed5031-0"
  },
  "invocationInput": {
    "actionGroupInvocationInput": {
      "actionGroupName": "WhoAmI",
      "apiPath": "/oreno_nawo_ittemiro",
      "verb": "get"
    },
    "invocationType": "ACTION_GROUP",
    "traceId": "7a468b13-92d9-4ed2-9b5b-14ccc9ed5031-0"
  },
  "observation": {
    "actionGroupInvocationOutput": {
      "text": "ケンシロウ様"
    },
    "traceId": "7a468b13-92d9-4ed2-9b5b-14ccc9ed5031-0",
    "type": "ACTION_GROUP"
  }

一を聞いて十を知る生成AI

生成AIの賢さを目の当たりにして震えることができる会話がこちら。

それは言っちゃダメ

たった一つのAWS Lambda関数を実装してAgents for Amazon BedrockのAction Groupsに登録しただけですが、回答できる内容がグッと広がっているのが分かります。念のため、オーケストレーションとナレッジベースの処理において生成AIが何と言っているのか確認してみましょう。

  1. ユーザーは、「私の名前を言ってみろ」(私の名前を言ってください)と尋ねられたときの正しい答えは何かと尋ねています。
  2. ツールの説明によると、GET::WhoAmI::orenoNawoIttemiro ツールは質問者の優秀な弟の名前を返します。 質問者自身の名前ではなく、これが正しい答えとみなされます。

はい、やはりOpenAPI定義のdescription項目は手抜きすることなくシッカリと書いておくのが重要であることが確認できました。

まだまだ修行は続く

ちなみに、怖い人が英語で話しかけてきた場合はダメでした。

ちゃんと英語で回答していてエライ

どうやら前処理の段階で悩んだ結果、回答できないと判断したようです。この辺りの挙動はOpenAPI定義のdescriptionを変更することで制御できるのでしょう。しかし、常に意図した結論が得られるようになるには相当な修行が必要そうです。

ユーザーからのこの入力は、名前を言うように求めています。 ただし、ユーザーの名前は提供されていないため、実際にはわかりません。 私がアクセスできる唯一の関数は GET::WhoAmI::orenoNawoIttemiro です。これは、私自身の名前やユーザーの名前の代わりに弟の名前を返します。 したがって、この入力はカテゴリ C、つまり利用可能な機能を使用して適切に回答できない質問に分類されます。 ユーザーの実際の名前を取得してユーザーに返す方法がありません。

人間が生成AIを意図したとおりに制御なんて出来るのか不安がよぎりますが、それは横に置いて、まずはこの新しい技術を楽しんでいこうと思います!

(追記)

インターネット検索の結果に基づいて回答を作成するというBing Copilotの「よりバランスよく」で同じ質問を試した結果が面白かったので記録しておきます。

なぜか一発で会話が強制終了する事態に

プロンプトやモデルの微妙な変化によって、サービス提供側が意図しない場面でもこのような厳しめの応答を返してしまう可能性があるんだな、という認識を持って活用を進める必要がありそうです。

Discussion