🙄

Amazon Lexとは?ゼロから学ぶ会話型チャットボット構築ハンズオン(Lambda連携)

に公開

Amazon Lexとは?

Amazon Lex は AWS が提供する会話型インターフェース構築サービスです。
自然言語理解(NLU)と自動音声認識(ASR)を使い、以下のようなチャットボットを簡単に作れます。

  • カスタマーサポートボット
  • FAQ ボット
  • 音声ボット
  • 入力フォームを会話で誘導するボット
  • 予約・問い合わせ対応
  • ChatGPT 的な Q&A インターフェース

Amazon Connect(コールセンター)とも統合できるのが強みです。

Lexでできること・特徴

✔ インテント(Intent)

ユーザーが「何をしたいか」を分類する仕組み。
例:

  • 予約したい → BookingIntent
  • 計算したい → CalculateIntent
    Lex はユーザーの入力から「どのインテントか」を自動で判別する。

✔ スロット(Slot)

必要な情報を抽出する項目。
例:

  • FirstNumber
  • OperatorType
  • SecondNumber

✔ スロットタイプ(SlotType)

スロットの分類。
例:OperatorType → 足す/引く/割る など

✔ Lambda 連携

Fulfillment Code Hook を使うことで、
Lex → Lambda → Lex の応答フローが作れる。

ハンズオン

本記事では、以下のチャットボットを実際に構築します。
「1 足す 2」→「1 + 2 = 3 です!」と返す四則演算チャットボット
Amazon Lex(V2)+ AWS Lambda を使って会話で動くミニ計算機を構築します。

構成図

1. Lambdaの作成

  1. 「関数の作成」をクリック
  2. 設定を以下のようにする:
項目 設定
関数名 lex-test
ランタイム Python 3.12(推奨)
実行ロール 基本的な Lambda アクセス権限で新しいロールを作成
  1. Lambda のコードを以下に書き換える
import json
import re
from decimal import Decimal, InvalidOperation

def safe_number(x):
    try:
        return Decimal(str(x))
    except InvalidOperation:
        return None

def close(intent_name, session_state, message):
    return {
        "sessionState": {
            **session_state,
            "dialogAction": {"type": "Close"},
            "intent": {
                **session_state.get("intent", {}),
                "name": intent_name,
                "state": "Fulfilled"
            }
        },
        "messages": [
            {"contentType": "PlainText", "content": message}
        ]
    }

def lambda_handler(event, context):
    intent_name = event["sessionState"]["intent"]["name"]
    slots = event["sessionState"]["intent"].get("slots", {})
    session_state = event.get("sessionState", {})

    a_raw = slots.get("FirstNumber", {}).get("value", {}).get("interpretedValue")
    b_raw = slots.get("SecondNumber", {}).get("value", {}).get("interpretedValue")
    op_raw = slots.get("OperatorType", {}).get("value", {}).get("interpretedValue")

    # 補助:空白なし "1+2" の対応
    if not (a_raw and b_raw and op_raw):
        text = event.get("inputTranscript", "")
        m = re.search(r"(-?\d+(?:\.\d+)?)\s*([+\-*/÷×])\s*(-?\d+(?:\.\d+)?)", text)
        if m:
            a_raw, op_raw, b_raw = m.groups()

    a = safe_number(a_raw)
    b = safe_number(b_raw)

    if a is None or b is None or op_raw is None:
        return close(intent_name, session_state, "うまく数字か演算子を読み取れませんでした。例: 12 + 34 のように送ってみてください。")

    op_map = {
        "+": "+", 
        "足す": "+", "たす": "+", "足して": "+", "たして": "+", "足し": "+", "加える": "+", "プラス": "+", "+": "+",
        "-": "-", 
        "引く": "-", "ひく": "-", "引いて": "-", "ひいて": "-", "マイナス": "-", "−": "-",
        "*": "*", 
        "×": "*", "かける": "*", "掛ける": "*", "かけて": "*", "掛けて": "*",
        "/": "/", 
        "÷": "/", "割る": "/", "わる": "/", "割って": "/"
    }
    op = op_map.get(op_raw, op_raw)

    try:
        if op == "+":
            result = a + b
        elif op == "-":
            result = a - b
        elif op == "*":
            result = a * b
        elif op == "/":
            if b == 0:
                return close(intent_name, session_state, "0で割ることはできません。")
            result = a / b
        else:
            return close(intent_name, session_state, f"演算子 '{op_raw}' が分かりません。")
    except Exception as e:
        return close(intent_name, session_state, f"計算中にエラーが出ました: {str(e)}")

    if result == result.to_integral():
        result_str = str(int(result))
    else:
        result_str = str(result.normalize())

    return close(intent_name, session_state, f"{a} {op} {b} = {result_str} です!")
  1. 以下のjsonでテストする
{
  "sessionState": {
    "intent": {
      "name": "CalculateIntent",
      "slots": {
        "FirstNumber": {
          "value": {
            "interpretedValue": "12"
          }
        },
        "Operator": {
          "value": {
            "interpretedValue": "+"
          }
        },
        "SecondNumber": {
          "value": {
            "interpretedValue": "34"
          }
        }
      }
    }
  },
  "inputTranscript": "12 + 34"
}

テスト結果例

2. Amazon Lex ボットを作成

  1. Amazon Lex → Bots
  2. 「Create bot」
  3. 設定を以下のようにする:
項目 設定
作成方法 空のボットを作成します。
ボット名 CalcBot
IAM アクセス許可 基本的な Amazon Lex 権限を持つロールを作成します。
エラーログ 無効
COPPA いいえ
セッションタイムアウト 5分
言語 Japanese (ja_JP)

3. インテントを作成

  1. Intents → Add intent → Empty intent
  2. 名前:CalculateIntent
  3. サンプル発話に以下を設定
{FirstNumber} {OperatorType} {SecondNumber}

※サンプル発話とはユーザーがそのインテントで話しそうな言い方の例。

  1. インテントを保存

4. スロットタイプ(OperatorType)を作成

  1. スロットタイプ→スロットタイプを追加→空のスロットタイプを追加
  2. 設定を以下のようにする:
項目 設定
スロットタイプ名 OperatorType
スロット値の解決 スロット値に制限
スロットタイプ値 足す、たす、足して...

5. Lambda を Lex に紐付け

  1. デプロイ→エイリアス
  2. 「TestBotAlias」を選択
  3. 言語:Japanese (Japan) をクリック
  4. Lambda function にlex-testを選択
  5. 保存

6. スロットを作成

  1. インテント→CalculateIntentを選択→スロット
  2. 以下のようにFirstNumberOperatorTypeSecondNumberを設定する


7. フルフィルメントのCode Hook設定

  1. インテント→CalculateIntentを選択→フルフィルメント
  2. フルフィルメントに Lambda 関数を使用にチェック

8. テスト

  1. 画面右上より構築をクリック

  2. 右上からテストをクリック

  3. 実際にテストしてみると正しく機能してることがわかります!

まとめ

本記事では Amazon Lex の基礎である

  • インテント
  • スロット
  • スロットタイプ
  • フルフィルメント(Lambda 連携)
  • エイリアス運用
    を、実際の四則演算チャットボット構築を通して学びました。

Amazon Lex は「会話から必要な情報を構造化して取り出す」ことが得意で、
ユーザー入力を “機械が扱える形” に変換するフロントエンド として非常に優れています。

これから

近年は Amazon Bedrock や Claude、Llama、Titan など
高性能な生成AIモデルと組み合わせることで、Lex で作るボットはさらに進化します。

Lex が:

  • インテントを判定し
  • スロットで情報を集め
  • 必要事項を整理して Lambda に渡す

そのうえで Lambda 側で Bedrock を使うと、たとえば:

  • 自然言語の質問に高精度で回答できる FAQ ボット
  • 社内ドキュメントを検索して回答する RAG(検索拡張生成)ボット
  • ブッキングや問い合わせの意図を AI が柔軟に判断して処理するボット
  • 感情や文脈を理解した応答生成

といった、柔軟で“賢い”チャットボットが簡単に実現できます!

Discussion