メインコンテンツまでスキップ

Agents for Amazon Bedrockでホテル予約エージェントを作ってみる過程

· 約12分
moritalous
お知らせ

過去にQiitaに投稿した内容のアーカイブです。

こんなの作ってみました。

エージェント開発の試行錯誤っぷりをお楽しみください。

Agents for Amazon Bedrockの構築方法

前回の構成をもとにしました。

Agents for Amazon Bedrockの構築方法は他の方の投稿などを参照ください。 (皆さん、検証が早い!w)

https://qiita.com/nasuvitz/items/c34419150eaee78544cc

https://qiita.com/Naoki_Ishihara/items/769f373f01261aec285a

https://qiita.com/cyberBOSE/items/14cf7024ea42ec1fb4be

https://qiita.com/minorun365/items/86a3667290a8e5657f65

https://dev.classmethod.jp/articles/agents-for-amazon-bedrock-ga/

概要

  • 概要図:

    ホテルの予約はGoogleカレンダーに登録するようにしました。APIが用意されていて簡単に使用できました。

    参考:Google Calendar API の概要

    image.png

  • API:

    はじめは予約を行うAPIと客室が空いているかをチェックするAPIの2つを用意しました。 お試しなので、誰かが予約済みの場合は予約できないような感じで作りました。

    パスDescriptionリクエストボディ(例)
    /reserve予約を行うAPI{
    "reservation_holder": "string",
    "checkin": "2023-12-28",
    "checkout": "2023-12-28"
    }
    /is_vacancy予約可能かどうかを判定するAPI{
    "checkin": "2023-12-28",
    "checkout": "2023-12-28"
    }
  • Model:

    ケチなのでanthropic.claude-instant-v1を選択しました。

  • Instructions for the Agent:

    あなたはホテルの予約管理を行うAIアシスタントです。 
    高級ホテルのフロントマンのように振る舞ってください。

Lambdaのソースコードはこちら。

https://github.com/moritalous/hotel-booking-agents

試行錯誤

パターン①:ユーザー側が気を使って丁寧に入力する

まずは手始めに、ユーザーがAPI側に合わせた入力をしてくれるパターンです。 APIに必要なパラメーターは以下の3つです。

  • 予約者名
  • チェックイン日
  • チェックアウト日

一つの入力中にわかりやすく入れてみました。

期待通り動作しました。Googleカレンダーにも上手に登録されています。

パターン②:情報を小出しにしてみる

チャットでのやり取りっぽく、情報を小出しにしてみました。 情報が不足していると何が知りたいかを聞いてくれました。

ただ、予約に不要な連絡先をしつこく聞いてくる。。

こちらのパターンも、ちゃんと予約されました。

パターン③:もっと雑に依頼する

日付だけを雑に入力してみました。フォーマットが違うよと怒られます。

諦めずに挑戦してみますが、頑なに断ってきます。

実装を確認します。

APIの入力フォーマット
class is_vacancy_req(BaseModel):
checkin: datetime.date = Field(description="チェックイン日")
checkout: datetime.date = Field(description="チェックアウト日")

FastAPIのドキュメント(Extra Data Types)で確認したところ、datetime.dateで定義した値はISO 8601フォーマットである必要があるようです。

datetime.date In requests and responses will be represented as a str in ISO 8601 format, like: 2008-09-15.

入力チェックが厳しく、API内でエラーになってました。

  • 解決方法

    1. 型定義をdatetime.date | strに変更

      class is_vacancy_req(BaseModel):
      checkin: datetime.date | str = Field(description="チェックイン日")
      checkout: datetime.date | str = Field(description="チェックアウト日")
    2. 受け取ったcheckincheckoutを関数内で文字列型変換する。 dateutil.parserparseを使うことで、ある程度表記ゆれにも対応できるようです。(ドキュメント

        now = datetime.datetime.now(timezone)
      checkin = parse(request.checkin)
      checkout = parse(request.checkout)

      # 2023/12/28に1/1をパースすると、2023/1/1になるので、2024/1/1になるように調整
      if checkin.astimezone(timezone) < now:
      checkin = checkin + relativedelta.relativedelta(years=1)
      if checkout.astimezone(timezone) < now:
      checkout = checkout + relativedelta.relativedelta(years=1)

こうすることで、日付が適当なフォーマットでも予約できました。

しかーし!

John Doeって誰やねん。。

出典:Wikipedia

英語で「名無しの権兵衛」に相当するのは、ジョン・ドウ (John Doe) である。Doe 自体に架空の姓の意味がある。ジョンは、ありふれた男性の名前であり、女性が対象となる場合は同様の理由でジェーンが用いられ、ジェーン・ドウ (Jane Doe) となる。複数の「名無しの権兵衛たち」を表す場合はそれぞれジョン・ドウズ (John Does)、ジェーン・ドウズ (Jane Does) となる。

予約名は必ずユーザーに問い合わせるようにしたいです。 Instructions for the Agentに一行追加してみます。

  あなたはホテルの予約管理を行うAIアシスタントです。 
高級ホテルのフロントマンのように振る舞ってください。
+ お客様名を間違えることは許されません。まずはお客様名を訪ねてください。

これで再挑戦したけど、改善しない。。。

さて、どうしたものか。。もうプロンプトで改善するアイディアは限界です。

モデルをClaude V2.1(anthropic.claude-v2:1)にしてみると、、、

お!改善!!

さすがClaude V2.1!!!

東京リージョンに来てくれてありがとうございます!!! (当分、来ないんじゃないっすかぁ。。とか言いふらしてごめんなさい!!!)

パターン④:「来週末」みたいな言い方で予約したい

どんどん欲が出てきます。「来週の土日」で挑戦します。

わかってたことですが、日付が違います。(来週末は1/6です)

Claudeが日付を知ってる訳はないので、日付情報を取得できるよう、APIを追加しました。

パスDescriptionリクエストボディ(例)
/get_today処理当日の日付が取得できます

リクエストボディはありません。レスポンスは"2023/12/28"のように日付情報だけを返却します。

このAPIを足して再度挑戦です。

今週末を聞いてみると、、

いいね!

どことなくClaude 2.1にしたことでやり取りも丁寧になった気がします。 今週末がだめなので来週末で予約しました。

ホテルっぽいね!

パターン⑤:空いてる日を雑に聞く

どんどんイジワル質問がしたくなりますね。来週の月曜から水曜日で空いてるところありますか?と聞いてみました。

予約状況としては、火曜日から水曜日は予約が入ってますが、月曜日から火曜日は予約が入ってない状態でした。

一泊ではなく月曜日から水曜日の2泊の間が確保できるかチェックして、空いてないという判断になりました。

他の聞き方として、「1/8の週の水曜日」はどうでしょうか

.png

残念ながら1/3になりました。水曜日はあってるのですごいですね。

日付の計算は間違うこともまだありますね。

パターン⑥:予約の変更がしたい

予約の変更機能を追加します。

3つのAPIを足すイメージです。

  • 予約取得API
  • 予約変更API
  • 予約削除API

ですが、Agents for Amazon Bedrockのクオータ制限で、1つのAgentに5つのAPIまでしか含めることができません。 すでに3つAPIを使っているので、予約変更と予約削除を一つのAPIにして、フラグ制御をするようにしました。

パスDescriptionリクエストボディ(例)
/get_my_reservation自分の予約を取得するAPI{
"reservation_holder": "string"
}
/update_reservation予約を更新するAPI{
"update_type": "update",
"reserve_id": "string",
"reserve_info": {
"reservation_holder": "string",
"checkin": "2023-12-28",
"checkout": "2023-12-28"
}
}

フラグでの制御とか、上手にできるか心配。。

予約の日付変更を実験

試行錯誤の結果、なんとか動きました!

色々やってる途中で、予約をキャンセルして別の日で取りますか?と、親切に言ってくれたので従ってみました。

いいですね! ちゃんと1/6の予約が消えて、1/10に予約が入ってました。

つまずいたところ

  • リクエストパラメーターは必須だけで構成する

    API定義で必須でなくしたり、条件付きで必須にすると、自動生成されるプロンプトには含まれないことがわかった。

  • リクエストに深いネストはやめた方がいい

    パラメーターはフラットな形式にしないと正しく受け取れないように思う(FastAPIとの組み合わせのせいかもしれない)

まとめ

  • デバッグがつらい。どこまでうまく動いているのか、何がだめなのかが、生成されたプロンプトを追わないといけない
  • まだ上手く使いこなせていないのか、思ったように振る舞わせるのはまだまだ調整が必要
  • 1つのエージェントでAPIが5個までなので、それほど複雑なことはできなさそう
  • このままだと人の予約も見れちゃったりするので、プロダクションにはまだ遠い印象