構成イメージ
WindowsでPowershellを起動して、そこからcurlコマンドでAPI Gateway経由でLambdaをキックします。Lambdaの処理で受け取ったデータをComprehendに渡して感情分析をしてもらいます。実行結果はDynamoDBに格納します。またLambdaの実行ログとしてCloudwatchとも連携します。
用語
Comprehend
AWS Comprehendは、文章から意味や傾向を自動で抽出するための自然言語処理(NLP)サービスです。
感情分析(ポジ/ネガなど)、キーフレーズ抽出、固有表現抽出、トピック分類などをAPIで実行できます。
機械学習の専門知識がなくても、アプリやログ分析にテキスト解析を組み込めます。
DynamoDB
Amazon DynamoDBは、AWSが提供するフルマネージドのNoSQLデータベースで、ミリ秒単位の低レイテンシで大規模トラフィックをさばけます。
サーバ管理が不要で、自動スケーリングやバックアップ、暗号化などが組み込まれています。
キー・バリュー/ドキュメント型のデータを扱え、セッション管理やIoT、ゲーム、イベントログなどに使われます。
構築
DynamoDB
DynamoDBの管理画面を開き、テーブルを作成していきます。

テーブル名をSentimentResultsとし、パーティションキーをidとします。
他はデフォルトの設定で作成していきます。

Lambda
1から作成を選択して、関数名をSentimentLambdaとします。
ランタイムはPython3.13にします。他はデフォルトの設定で作成していきます。

Lambdaがデプロイされたことを確認します。
設定タブの環境変数を押下します。編集ボタンを押下します。

キー:TABLE_NAME
値:SentimentResults
と設定して保存を押下します。

次にこのLambdaに紐づけるIAMRoleを作成していきます。
IAMのRoleを開いて、作成ボタンを押下します。

AWSのサービスを選択して、ユースケースはLambdaを選択。
次に進みます。

Policyをアタッチします。
ComprehendFullAccessとAmazonDynamoDBFullAccess_v2とCloudWatchLogsFullAccessにチェックを入れて次に進みます。

Roleの名前はSentimentLambdaRoleとしました。
アタッチされているPolicyが正しいことを確認して作成していきます。

Lambdaの設定画面に戻ります。
設定タブのアクセス権限を押下します。デフォルトだと自動生成されたIAMRoleがアタッチされていますので、これを先ほど作成したRoleに差し替えます。
編集ボタンを押下します。

既存のロール部分を作成したSentimentLambdaRoleに変更して保存を押下します。

ロール名のところがSentimentLambdaRoleとなっていることを確認します。

コードタブを押下するとPythonでコーディングできる部分が表示されますので、以下のコードを入力します。
import os
import json
import uuid
from datetime import datetime, timezone
import boto3
dynamodb = boto3.resource("dynamodb")
comprehend = boto3.client("comprehend")
TABLE_NAME = os.environ["TABLE_NAME"]
table = dynamodb.Table(TABLE_NAME)
def response(status, body):
return {
"statusCode": status,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(body, ensure_ascii=False)
}
def lambda_handler(event, context):
# 1) 入力取り出し
try:
body = event.get("body")
if body is None:
return response(400, {"message": "body is required"})
if isinstance(body, str):
body = json.loads(body)
text = body.get("text", "")
except Exception:
return response(400, {"message": "Invalid JSON body"})
# 2) バリデーション
text = text.strip()
if not text:
return response(400, {"message": "text is required"})
if len(text) > 1000: # 授業用の安全上限(任意)
return response(400, {"message": "text is too long (max 1000 chars for this demo)"})
# 3) Comprehendで感情分析
lang = "ja"
result = comprehend.detect_sentiment(Text=text, LanguageCode=lang)
sentiment = result["Sentiment"]
scores = result["SentimentScore"]
# 4) DynamoDBに保存
item_id = str(uuid.uuid4())
created_at = datetime.now(timezone.utc).isoformat()
table.put_item(
Item={
"id": item_id,
"createdAt": created_at,
"language": lang,
"text": text,
"sentiment": sentiment,
"scores": {k: str(v) for k, v in scores.items()}, # Decimal対策の簡易版
}
)
# 5) 応答
return response(200, {
"id": item_id,
"sentiment": sentiment,
"scores": scores
})
入力しましたらDeployを押下して、編集内容を保存します。

API Gateway
API Gatewayの管理画面を開きます。
APIを作成を押下します。

名前をSentimentAPIとします。
後はデフォルトのまま次に進みます。

以下のような画面が表示されます。
SentimentAPIのルートの右に作成ボタンがあるので、これを押下します。

以下のような画面が表示されるようになると思いまうs。
統合の部分でアタッチを押下します。

統合タイプ:Lambda関数としSentimentLambdaを選択します。
アクセス許可を呼び出すをアクティブにして作成を押下します。

Lambda側の管理画面においてもAPI Gatewayがトリガーに表示されていることが確認できると思います。

動作確認
API Gatewayの画面からAPIのURLを確認します。

自分のPC(今回はWindows11)のPowershellを起動して以下のコマンドをコピペして実行します。
★で囲まれた部分は上記で控えたAPI GatewayのURLに置き換えてください。
今回は「このサービスはとても便利で助かります」という文章の感情分析をしてもらおうと思います。
$payload = '{"text":"このサービスはとても便利で助かります"}'
[System.IO.File]::WriteAllText("payload.json", $payload, (New-Object System.Text.UTF8Encoding($false)))
curl.exe -i -X POST "★APIのURL★/analyze" -H "Content-Type: application/json; charset=utf-8" --data-binary "@payload.json"
実行結果は以下となります。
HTTP/1.1 200 OKとなっていることと、{"id": ~}の出力があることを確認します。このidの部分が感情分析の結果となっています。
PS C:\Users\ohtsu> $payload = '{"text":"このサービスはとても便利で助かります"}'
PS C:\Users\ohtsu> [System.IO.File]::WriteAllText("payload.json", $payload, (New-Object System.Text.UTF8Encoding($false)))
PS C:\Users\ohtsu> curl.exe -i -X POST "★APIのURL★/analyze" -H "Content-Type: application/json; charset=utf-8" --data-binary "@payload.json"
HTTP/1.1 200 OK
Date: Fri, 23 Jan 2026 03:00:11 GMT
Content-Type: application/json
Content-Length: 217
Connection: keep-alive
Apigw-Requestid: XniZFhl6NjMEMRg=
{"id": "b3905be6-66d8-437f-bc41-5b83d2c71356", "sentiment": "POSITIVE", "scores": {"Positive": 0.9995601773262024, "Negative": 0.0002331074356334284, "Neutral": 0.0002035265351878479, "Mixed": 3.2255238693323918e-06}}
Comprehendの項目と意味は以下になります。
| 項目 | 値 | 意味 |
|---|---|---|
| id | b3905be6-66d8-437f-bc41-5b83d2c71356 | この解析結果(1リクエスト分)を一意に識別するID(UUID)。DynamoDBなどで保存・検索するためのキー。 |
| sentiment | POSITIVE | 総合判定の感情ラベル。文章全体として「肯定的(ポジティブ)」と判定されたことを示す。 |
| scores.Positive | 0.9995601773262024 | 「肯定的」である確率(信頼度)。1に近いほど強くポジティブと判断。 |
| scores.Negative | 0.0002331074356334284 | 「否定的」である確率(信頼度)。0に近いほどネガティブではない。 |
| scores.Neutral | 0.0002035265351878479 | 「中立的」である確率(信頼度)。0に近いほど中立ではない。 |
| scores.Mixed | 3.2255238693323918e-06 | 「肯定・否定が混在している」確率(信頼度)。0に近いほど混在していない。 |
| ラベル | 日本語 | どういうときに高くなるか |
|---|---|---|
| Positive | 肯定的 | 褒める、満足、好意、嬉しい、便利、助かる等の表現が中心のとき |
| Negative | 否定的 | 不満、批判、怒り、困る、最悪、使えない等の表現が中心のとき |
| Neutral | 中立 | 感情が薄い事実説明・報告・手順の文章が中心のとき |
| Mixed | 混在 | 褒めと不満が同時に書かれているなど、感情が混ざっているとき |
Cloudwatch LogsでLambdaの実行ログが吐き出されていることを確認します。


またDynamoDBにもデータが溜まっていることを確認します。
今回作成したテーブルの管理画面を開いて、右上にあるテーブルアイテムの探索を押下します。

例えばComprehendに分析させる文言を以下にしてみます。
「便利で普段はとても助かっているのですが、時間帯によっては読み込みがかなり遅くなることがあって、そのたびに少しイライラしてしまいます。特に急いで作業しているときに待たされるとストレスが大きいので、動作が安定してもっとサクサク使えるようになると嬉しいです。」
$payload = '{"text":"便利で普段はとても助かっているのですが、時間帯によっては読み込みがかなり遅くなることがあって、そのたびに少しイライラしてしまいます。特に急いで作業しているときに待たされるとストレスが大きいので、動作が安定してもっとサクサク使えるようになると嬉しいです。"}'
[System.IO.File]::WriteAllText("payload.json", $payload, (New-Object System.Text.UTF8Encoding($false)))
curl.exe -i -X POST "★APIのURL★/analyze" -H "Content-Type: application/json; charset=utf-8" --data-binary "@payload.json"
結果としては以下のようになっていました。分析としては妥当のように思いますね。
{
"id": "f70aed3a-4ac3-4c44-941d-8440cf0d2b0d",
"sentiment": "MIXED",
"scores":
{
"Positive": 0.0004430233675520867,
"Negative": 0.0035658443812280893,
"Neutral": 1.5062880265759304e-05,
"Mixed": 0.9959760308265686
}
}












