🤖

Amazon Lex と Kendra をつなぐ

2023/03/23に公開

はじめに

ChatGPTが盛り上がっているので、AI Chatbotなるものを触ってみようと。

やったこと

弊社では、AWSのサービスを使用したSaaS開発を行っているので、AWSのサービスを使ったAI Chatbotだとどんな感じになるのかやってみました。

Chatbotを作るのはAmazon Lexで、Amazon Lexが上手に回答するため、エンタープライズ検索エンジンのAmazon Kendraをつないだらいい感じになるんじゃない?

と思って検索したら、ありました。

https://aws.amazon.com/jp/blogs/news/integrate-amazon-kendra-and-amazon-lex-using-a-search-intent/

Amazon KendraとAmazon Lexが統合できない?

Amazon Kendraは、2023年2月から東京リージョンでも利用可能になっていたので、当然ながら東京リージョンで日本語で設定していきます。

KendraのIndexを作るところまでは問題なかったのですが、LexのBotに組み込みインテントのAMAZON.KendraSearchIntentを設定する箇所で候補にでてこないのです。

実は、AMAZON.KendraSearchIntentは、この記事を書いている時点(2023/3/23)では、en-USロケール、および米国東部(バージニア北部)、米国西部(オレゴン)、欧州(アイルランド)のリージョンのみ使用できます。ということでした。

ダメじゃん。ということで、もう一度検索したら、ありました。(ありがとう。中の人)

https://github.com/aws-samples/simple-lex-kendra-jp

これをそのまま動かすと、うちでは、Invalid PhysicalResourceIdエラーになってしまったので、以下のように変更して、無事動かすことができました。

data-source.ts
@@ -42,7 +42,7 @@ export class DataSource extends Construct {
 
     this.resource = new cdk.CustomResource(
       this,
-      `CustomResourceDataSource${id}`,
+      'CustomResourceDataSource',
       {
         serviceToken: customResourceHandler.functionArn,
         resourceType: 'Custom::DataSource',

golangでLexのLambda書けない?

AMAZON.KendraSearchIntentが使えなくても、Lambdaでやればいいんだね。

弊社はLambda開発の標準言語はgolangなので、いろいろいじるならgolangで書いてみようということで、まずは、ここから。

https://github.com/aws/aws-lambda-go/blob/main/events/README_Lex.md

こんな感じのを動かしてみたら、

runtime error: invalid memory address or nil pointer dereference: errorString

これって、Lex V1かな?

https://github.com/aws/aws-lambda-go/blob/main/events/lex.go

...ということで、Lex V2 の構造体から書かないといけないのでした。

ちょっと長いので見たい方だけどうぞ
lex-lambda-types.go
package main

// LexV2Event は、Lambda 関数の入力パラメータ
// https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html
// @types/aws-lambda/triggrt/lex-v2.ts
type LexV2Event struct {
	MessageVersion      string                `json:"messageVersion"`
	InvocationSource    string                `json:"invocationSource"`
	InputMode           string                `json:"inputMode"`
	ResponseContentType string                `json:"responseContentType"`
	SessionID           string                `json:"sessionId"`
	InputTranscript     string                `json:"inputTranscript"`
	Bot                 LexV2Bot              `json:"bot"`
	Interpretations     []LexV2Interpretation `json:"interpretations"`
	ProposedNextState   struct {
		DialogAction LexV2DialogAction `json:"dialogAction"`
		Intent       LexV2Intent       `json:"intent"`
	} `json:"proposedNextState"`
	RequestAttributes map[string]string    `json:"requestAttributes"`
	SessionState      *LexV2SessionState   `json:"sessionState"`
	Transcriptions    []LexV2Transcription `json:"transcriptions"`
}

// LexV2Bot は、Lamabda を起動したボット
type LexV2Bot struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	AliasID   string `json:"aliasId"`
	AliasName string `json:"aliasName"`
	LocaleID  string `json:"localeId"`
	Version   string `json:"version"`
}

// LexV2Interpretation は、Lex が行った会話の解釈
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_Interpretation.html
type LexV2Interpretation struct {
	Intent            LexV2Intent            `json:"intent"`
	NluConfidence     float64                `json:"nluConfidence"`
	SentimentResponse LexV2SentimentResponse `json:"sentimentResponse"`
}

// LexV2Intent は、Lex が解釈した意図
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_Intent.html
type LexV2Intent struct {
	ConfirmationState string               `json:"confirmationState"`
	Name              string               `json:"name"`
	Slots             map[string]LexV2Slot `json:"slots"`
	State             string               `json:"state"`
	KendraResponse    interface{}          `json:"kendraResponse"`
}

// LexV2SentimentResponse は、Comprehend が解釈した応答の感情
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_SentimentResponse.html
type LexV2SentimentResponse struct {
	Sentiment      string              `json:"sentiment"`
	SentimentScore LexV2SentimentScore `json:"sentimentScore"`
}

// LexV2SentimentScore は、4つの感情を示す
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_SentimentScore.html
type LexV2SentimentScore struct {
	Mixed    float64 `json:"mixed"`
	Negative float64 `json:"negative"`
	Neutral  float64 `json:"neutral"`
	Positive float64 `json:"positive"`
}

// LexV2SessionState は、Lex のユーザーセッション情報
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_SessionState.html
type LexV2SessionState struct {
	ActiveContexts       []LexV2ActiveContext `json:"activeContexts,omitempty"`
	SessionAttributes    map[string]string    `json:"sessionAttributes,omitempty"`
	DialogAction         LexV2DialogAction    `json:"dialogAction,omitempty"`
	Intent               LexV2Intent          `json:"intent"`
	OriginatingRequestID string               `json:"originatingRequestId"`
}

// LexV2ActiveContext は、セッションの文脈
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_ActiveContext.html
type LexV2ActiveContext struct {
	Name              string            `json:"name"`
	ContextAttributes map[string]string `json:"contextAttributes"`
	TimeToLive        struct {
		TimeToLiveInSeconds int `json:"timeToLiveInSeconds"`
		TurnsToLive         int `json:"turnsToLive"`
	} `json:"timeToLive"`
}

// LexV2DialogAction は、会話の次のアクション
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_DialogAction.html
type LexV2DialogAction struct {
	Type                 string `json:"type"`
	SlotToElicit         string `json:"slotToElicit"`
	SlotElicitationStyle string `json:"slotElicitationStyle,omitempty"`
}

// LexV2Result は、Lambda 関数のレスポンス
// https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html#lambda-response-format
type LexV2Result struct {
	SessionState *LexV2SessionState `json:"sessionState"`
	Messages     []*LexV2Message    `json:"messages"`
}

// LexV2Message は、ユーザーに返される回答文の入れ物
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_Message.html
type LexV2Message struct {
	ContentType       string                  `json:"contentType"`
	Content           string                  `json:"content,omitempty"`
	ImageResponseCard *LexV2ImageResponseCard `json:"imageResponseCard,omitempty"`
}

// LexV2ImageResponseCard は、メッセージプラットホームでユーザーに表示されるカード
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_ImageResponseCard.html
type LexV2ImageResponseCard struct {
	Title    string         `json:"title"`
	Subtitle string         `json:"subtitle,omitempty"`
	ImageURL string         `json:"imageUrl,omitempty"`
	Buttons  []*LexV2Button `json:"buttons,omitempty"`
}

// LexV2Button は、ユーザーに表示されるカード上のボタン
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_Button.html
type LexV2Button struct {
	Text  string `json:"text"`
	Value string `json:"value"`
}

// LexV2Slot は、Lex がインテントを満たすために使用する値
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_Slot.html
type LexV2Slot struct {
	Shape  string         `json:"shape"`
	Value  LexV2SlotValue `json:"value"`
	Values []LexV2Slot    `json:"values,omitempty"`
}

// LexV2SlotValue は、スロットの値
// https://docs.aws.amazon.com/lexv2/latest/dg/API_runtime_Value.html
type LexV2SlotValue struct {
	InterpretedValue string   `json:"interpretedValue,omitempty"`
	OriginalValue    string   `json:"originalValue"`
	ResolvedValues   []string `json:"resolvedValues"`
}

// LexV2Transcription は、ユーザーの音声発話に対応する書き起こし
// https://docs.aws.amazon.com/lexv2/latest/dg/using-transcript-confidence-scores.html
type LexV2Transcription struct {
	Transcription           string  `json:"transcription"`
	TranscriptionConfidence float64 `json:"transcriptionConfidence"`
	ResolvedContext         struct {
		Intent string `json:"intent"`
	} `json:"resolvedContext"`
	ResolvedSlots map[string]LexV2Slot `json:"resolvedSlots"`
}

まとめ

  • ということで、現時点(2023/3/23)でも東京リージョンでAmazon LexとKendraを組み合わせた日本語のチャットボットは動作可能でした。
  • AMAZON.KendraSearchIntentが日本語で東京リージョンで使えるなら、会社中のドキュメントの内容を知っている優秀なチャットボットが簡単にできそうなので、期待したいです。
GitHubで編集を提案
株式会社ROBONの技術ブログ

Discussion