背景や目的
1.生成AIの活用をテーマに開発研究をしています。AIへの注目度は引き続き高いのですが、企業が具体的な効果を得るのはまだこれから、と理解しています。
2.その効果を得るために「企業独自のデータから新たな知恵を得る」という点に着目していますが、技術の方法としては検索で補う(RAG)かLLMを追加学習するか、の2通りありそうです。
3.今回は後者の独自モデル利用に注目し、その時に必要となりそうなOSSモデルのAPI作成を実施しました。実用レベルで試したく、LLMはDeepSeek-R1(32B版)、API環境はAmazon SageMakerをそれぞれ使いました。追加学習して質が高そうなLLM、自由度が高く現実的な費用のAWS環境、という観点でそれぞれ選び、とにかく構築しました。
既出の情報が少なかった印象でした。一つの動いた例としてお役に立てれば幸いです。
構築方針
以前の実験では以下の課題がありました。
【課題1】タイムアウト制限の回避 (AWS API Gateway利用時の問題)
その手の制限にひっかからないようにする。
【課題2】回答文の逐次表示 (ChatGPT等でよくみかける少しづつ表示させる機能)
UXを考慮して、このレベルも考慮しておく。
【課題3】優れたモデルを利用
追加学習する場合、ベースモデルの質の高さも重要と考えています。
これらを考慮し、構築では以下を採用しました。
1.SageMakerAIのリアルタイム推論
タイムアウト制限が無く、推論コードを自由に書けて遂次処理ができそうなこの方法を採用しました。カスタムコンテナの使い方で試行錯誤がありましたが、最終的に動きました。
以下の記事を参考にさせて頂きました。
Amazon SageMaker におけるカスタムコンテナ実装パターン詳説
(記事中「4 つの実装パターン」のパターン3に相当する使い方となります。ご参考まで。)
2.cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japaneseの量子化版
高性能と言われるDeepSeek-R1の蒸留版をcyberagentさんが日本語強化されたものを使用させて頂きました。GPU費用を考慮し、それを量子化して使いました。
構築手順のサマリー
試行錯誤もありましたが、最終的には以下の手順で動作をしています。
1.コンテナイメージの作成
ローカルPC(windows)のWSL2/ubuntuコンソールで作成。
2.ECR(Amazon Elastic Container Registry)のリポジトリ作成
AWS上で処理するコンテナイメージの設置場所に相当するリポジトリを作成します。
3.コンテナイメージをECRへプッシュ
ローカルに入れたAWS CLIにて、ECR認証&コンテナプッシュをします。
4.モデル(量子化済)をS3へアップ
モデルを調達し、それをS3へアップしておきます。あとでSageMakerが参照します。
5.SageMakerAIメニューでの操作
モデル作成、エンドポイント設定等、必要な操作をしエンドポイントを作成します。
6.エンドポイント自動作成&削除の構築
”現実的な価格”を考慮し、営業時間内のみ起動するようにします。LambdaとEventBridgeで実現します。
構築1:コンテナイメージの作成
推論処理をコンテナイメージにします。量子化版だという点と、コンテナがSageMakerで起動可能だという事に注意して作成しました。手順は以下の通りです。
1.docker環境の準備
ローカルで使えるようにします。ここは必要に応じて以前の記事をご参照下さい。
→性能の良さそうなローカルLLM3つを動作確認しました
(この記事内の「インストール:WSL2」の欄に記載してあります)
・Ubuntuコンソールを開きdockerをインストールします。
y-nishihara@PC999:~$ sudo apt update
(成功!メッセージ省略)
y-nishihara@PC999:~$ sudo apt install docker.io
(成功!メッセージ省略)
y-nishihara@PC999:~$ docker --version
Docker version 24.0.7, build 24.0.7-0ubuntu2~22.04.1とにかく入ればOKです。私の場合は上のバージョンでした。
2.Dockerfileとrequirements.txtを作成
コンテナに必要なファイルを作成しますが、まずDockerfileを作成します。
・適当な作業用ディレクトリを作成しておきます。
y-nishihara@GSPC999:~$ mkdir reform_api_container_work_awq
y-nishihara@GSPC999:~$ cd reform_api_container_work_awq/
y-nishihara@GSPC999:~/reform_api_container_work_awq$・作業用ディレクトリで、vi等でコードを作成します。以下はDockerfileです。
# ベースイメージを指定(Python 3.11)
FROM python:3.11
# SageMaker のワークディレクトリを指定
WORKDIR /opt/ml/code
# 必要なファイルをコピー
COPY requirements.txt .
COPY requirements2.txt .
COPY app.py .
# 必要なパッケージをインストール
RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install --no-cache-dir -r requirements2.txt
# ポート8080を開放(SageMaker の仕様)
EXPOSE 8080
# エントリーポイントを指定
ENTRYPOINT ["python", "app.py"] #SageMakerの docker run <image> serve に対応
21行目はSageMaker仕様に合わせた箇所で、これで起動します。他も色々とありましたが、最終的に上記コードで動作しています。
・追加ライブラリはautoawqが上手く入らず、以下の2ファイル(requirements.txtとrequirements2.txt)に分けて収めました。内容は以下の通りです。
https://download.pytorch.org/whl/cu124/torch-2.6.0%2Bcu124-cp311-cp311-linux_x86_64.whl
https://download.pytorch.org/whl/cu124/torchvision-0.21.0%2Bcu124-cp311-cp311-linux_x86_64.whl
transformers
sentencepiece
accelerate
Flask
multi-model-server
sagemaker-inference
numpy<2autoawq3.app.pyを作成
推論処理のapp.pyを用意します。
・同様にvi等でコードを書きます。
y-nishihara@GSPC999:~/elyza_container_work$ vi app.py最終的なコードは以下の通りです。
from flask import Flask, request, jsonify, Response
import torch
from awq import AutoAWQForCausalLM #AWQ版
from transformers import AutoTokenizer,TextIteratorStreamer
import os
import time
import threading
import sys
app = Flask(__name__)
IS_SAGEMAKER = True
if IS_SAGEMAKER:
MODEL_DIR = "/opt/ml/model" # SageMaker環境では S3 から展開されたモデルを使用
else:
MODEL_DIR = "" # ローカル試験する場合の予備部分
print(f"Loading model from {MODEL_DIR} ...")
tokenizer = AutoTokenizer.from_pretrained(MODEL_DIR)
model = AutoAWQForCausalLM.from_quantized(MODEL_DIR,device="cuda",fuse_layers=True,trust_remote_code=True) #AWQ版
@app.route("/ping", methods=["GET"])
def ping():
"""ヘルスチェック用エンドポイント"""
return jsonify({"status": "healthy"}), 200
def generate_text(system_prompt,question):
if system_prompt == "":
system_prompt = f"あなたは誠実で優秀な日本人のアシスタントです。以下の質問に対して、1問1答で答えてください。\n\n質問:{question}\n回答:" #R1を考慮したプロンプト
inputs = tokenizer(system_prompt, return_tensors="pt").to("cuda")
streamer = TextIteratorStreamer(tokenizer, skip_special_tokens=True,skip_prompt=True) #回答文だけ返却する
generation_kwargs = {
"input_ids": inputs["input_ids"],
"attention_mask": inputs["attention_mask"],
"max_new_tokens": 5000,
"do_sample": False,
"pad_token_id": tokenizer.eos_token_id,
"streamer": streamer
}
thread = threading.Thread(target=model.generate, kwargs=generation_kwargs) #別スレッドで生成を実行
thread.start()
for new_text in streamer: #逐次出力
yield new_text
yield "\n"
@app.route("/invocations", methods=["POST"])
def invocations():
data = request.json
system_prompt = data.get("system_prompt", "")
question = data.get("question", "")
return Response(generate_text(system_prompt,question), content_type="text/plain", status=200)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "serve": #SageMakerの docker run <image> serve を受け、その引数が入るはず
app.run(host="0.0.0.0", port=8080, threaded=True)
else:
print("This container is for SageMaker! (atd)")
「#AWQ版」 をつけた箇所は、量子化モデルを扱うために考慮した部分です。
遂次出力は44行目~48行目で実行しています。その他ここも色々とありましたが、この内容で動作確認済です。
これでコードの準備は完了です。
4.コンテナイメージを作成
最後にコンテナイメージを作成します。
・4ファイルの存在確認をし、よければビルドします。
y-nishihara@GSPC999:~/reform_api_container_work_awq$ ls
Dockerfile requirements2.txt app.py requirements.txt
y-nishihara@GSPC999:~/reform_api_container_work_awq$ docker build -t reform-api-deepseek310-10 .
(・・途中省略・・)
Successfully tagged reform-api-deepseek310-10:latest
y-nishihara@GSPC999:~/reform_api_container_work_awq$・念のため作成後の状況を確認しておきます。以下のようにimageが増えたはずです。
y-nishihara@GSPC999:~/reform_api_container_work_awq$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
reform-api-deepseek310-10 latest ************ ************ 7.32GBimageが作成できました。次に進みます。
構築2:ECRのリポジトリ作成
ローカルで作成したコンテナイメージをクラウドで使うための置き場所として、ECR(Elastic Container Registry)のリポジトリを作成します。
・AWSコンソールでECRメニューからプライベートリポジトリの画面を開き、リポジトリの作成を押します。

・表示された作成用の画面では、下表の値で登録をします。

| 設定項目 | 値 |
|---|---|
| リポジトリ名 | reform-api-deepseek ※お好きな名称で |
| イメージタグのミュータビリティ | Mutable |
| 暗号化設定 | AES-256 |
| その他 | 全てデフォルト |
・作成ボタンを押すと、そのリポジトリが追加されます。
これでECRのリポジトリ作成は完了です。
構築3:コンテナイメージをECRへプッシュ
作成したリポジトリの画面内で「プッシュコマンドを表示」を押すと、その手順が表示されます。これを参考にして、自分の環境に合わせて順番に実行します。

・ローカルPCに戻り、AWS CLIをインストールします。(入っていれば不要です。)
y-nishihara@PC999:~$ sudo apt update
(・・途中省略・・)
56 packages can be upgraded. Run 'apt list --upgradable' to see them.
y-nishihara@PC999:~$ sudo apt install unzip curl -y
(・・途中省略・・)
0 upgraded, 0 newly installed, 0 to remove and 56 not upgraded.
y-nishihara@PC999:~$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 63.4M 100 63.4M 0 0 10.3M 0 0:00:06 0:00:06 --:--:-- 10.6M
y-nishihara@PC999:~$unzip awscliv2.zip
(・・途中省略・・)
y-nishihara@PC999:~$ sudo ./aws/install
[sudo] password for y-nishihara:
You can now run: /usr/local/bin/aws --version
y-nishihara@PC999:~$ aws --version
aws-cli/2.18.15 Python/3.12.6 Linux/5.15.153.1-microsoft-standard-WSL2 exe/x86_64.ubuntu.22・ECRアクセスの権限を持つIAMユーザとそのアクセスキーを用意します。許可ポリシーAmazonEC2ContainerRegistryFullAccessがアタッチされたユーザであればOKです。作成の手順は、以下記事をご参照下さい(「ユーザーの作成」で検索できます)。
→OSS版EmbeddingモデルをAWSで試用した例
・許可されたIAMユーザのアクセスキーをローカル環境へ登録します。aws configureコマンドで、用意したアクセスキーを以下のように対話式で入力すればOKです。
y-nishihara@GSPC999:~/reform_api_container_work_awq$ aws configure
AWS Access Key ID [None]: ***上で得たアクセスキー
AWS Secret Access Key [None]: ***上で得たシークレットアクセスキー
Default region name [None]: ap-northeast-1
Default output format [None]: ***未入力
y-nishihara@GSPC999:~/elyza_container_work$・IAMユーザが登録されたか以下コマンド確認しておきます。
y-nishihara@GSPC999:~/reform_api_container_work_awq$ aws sts get-caller-identity
{
"UserId": "***",
"Account": "****",
"Arn": "arn:aws:iam::***:user/***ここにユーザ名が表示されます"
}
y-nishihara@GSPC999:~/reform_api_container_work_awq$・ユーザの準備を終えたので、ECRの認証に進みます。コンソール上で実行します。
y-nishihara@GSPC999:~/reform_api_container_work_awq$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ***以降省略 AMC画面で表示された認証コマンドのコピーでOK
(・・途中省略・・)
Login Succeeded
y-nishihara@GSPC999:~/reform_api_container_work_awq$・次は、前の章でビルドしたコンテナにタグを付けます。
y-nishihara@GSPC999:~/reform_api_container_work_awq$ docker tag reform-api-deepseek310-10:latest **(これ以降はAWS画面で表示されたタグ付けコマンドの内容と同じ)
y-nishihara@GSPC999:~/reform_api_container_work_awq$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
************.dkr.ecr.ap-northeast-1.amazonaws.com/reform-api-deepseek latest ************ ************ 7.32GB
reform-api-deepseek310-10 latest ************ ************ 7.32GBdocker imagesコマンドで上の1行目(***~)があればOKです。タグ付けで1個増えました。
・準備が全て完了したので、プッシュします。これもコンソール上で実行します。
y-nishihara@GSPC999:~/reform_api_container_work_awq$ docker push ..(以降省略 AWS画面で表示されたpushコマンドのコピーでOK)
The push refers to repository [***.dkr.ecr.ap-northeast-1.amazonaws.com/reform-api-deepseek]
************: Pushed
(・・途中省略・・)
************: Pushed
latest: digest: sha256:*** size: ***上のように latest: digest~ が表示されたらOKです。プッシュできています。
構築4:モデル(量子化済)をS3へアップ
推論モデルのファイルをS3にアップしておきます。後でSageMakerから参照させます。
1.量子化されたモデルを用意
試したいAWQ版のモデルファイルをご用意下さい。私が作成した量子化版は以下にあります。
https://huggingface.co/nishihara-ATD/DeepSeek-R1-Distill-Qwen-32B-Japanese-AWQ
トライアルで必要な場合はここからダウンロードしてお使い下さい。(量子化自体のプロセスは今回は割愛させて頂きます。)
2.AWS S3の準備
とにかくバケットを用意します。既存で使えるバケットがあればそれでも大丈夫です。
・AWSコンソールでS3メニューを開き、「バケットの作成」を押します。

・作成画面では、下表の値を入力し「バケットを作成」を押します。

| 設定項目 | 値 |
|---|---|
| S3バケット名 | abc-reform-api ※お好きな名称で |
| (その他) | (全てデフォルト) |
・そして、そのバケット内にモデルのフォルダを作成しておきます。私は以下を作成しました。
| 値 | |
|---|---|
| S3バケット名 | DeepSeek-R1-Distill-Qwen-32B-Japanese-AWQ-ATD ※お好きな名称で |
これで、S3の準備は完了です。
3.S3へモデルファイルをアップロード
私がアップロードしたのは以下のファイルでした。
| No. | 値 |
|---|---|
| 1 | config.json |
| 2 | generation_config.json |
| 3 | model-00001-of-00004.safetensors |
| 4 | model-00002-of-00004.safetensors |
| 5 | model-00003-of-00004.safetensors |
| 6 | model-00004-of-00004.safetensors |
| 7 | model.safetensors.index.json |
| 8 | special_tokens_map.json |
| 9 | tokenizer_config.json |
| 10 | tokenizer.json |
・試したいAWQ版モデルのファイルを手元に用意したら、それを作成したバケットにアップロードします。以下はGoogle colabでアップしたコードです。(手動でも良いのですが遅いです。)
/content# curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
/content# unzip awscliv2.zip
/content# ./aws/install
/content# aws --version
/content# aws configure
AWS Access Key ID [None]: ***アクセスするIAMユーザのアクセスキー
AWS Secret Access Key [None]: ***アクセスするIAMユーザのシークレットアクセスキー
Default region name [None]: ap-northeast-1
Default output format [None]:
/content#
aws s3 cp --recursive \
/content/drive/MyDrive/model/***手元の用意したモデルフォルダ \
s3://abc-reform-api/DeepSeek-R1-Distill-Qwen-32B-Japanese-AWQ-ATD/アクセス可能なユーザでcopyされるので、適当なIAMユーザで実行しています。
また、手元ファイルはgoogle driveに事前にセットしておきました。
・完了したらS3メニューで確認しておきます。

これでS3へのアップロードは完了です。
構築5:SageMakerAIメニューでの操作
モデルと推論処理が参照できる状態になったので、ここでまとめ上げるイメージです。
1.モデルを作成
「SageMakerのモデル」が必要なので、それを作成します。
・AWSコンソールでSageMaker AIメニューを開き、推論→モデルを選択します。

・そこで「モデルの作成」を押し、作成画面では下表の値を入力し、最後に「モデルの作成」を押します。


| 設定項目 | 値 |
|---|---|
| モデル名 | deepseek-awq ※お好きな名称で |
| IAMロール | AmazonSage*** ※自動作成をお勧めします |
| コンテナ入力オプション | モデルアーティファクトと推論イメージの場所を指定します。 |
| モデル圧縮タイプ | UncompressedModel ※S3側と合わせる |
| S3 データタイプ | S3Prefix ※S3側と合わせる |
| 推論コードイメージの場所 | ***.dkr.ecr.ap-northeast-1.amazonaws.com/reform-api-deepseek:latest *ECRにセットしたイメージのURIです。 |
| アーティファクトの場所 | s3://abc-reform-api/DeepSeek-R1-Distill-Qwen-32B-Japanese-AWQ-ATD/ *作成したモデルファイルのS3 URIです。 |
| (その他) | (全てデフォルト) |
・作成後は、一覧画面で確認しておきます。(直ぐ作成されるはずです。)

これで、SageMakerのモデル作成は完了です。
2.エンドポイント設定の作成
エンドポイントを作るための情報を登録する箇所となります。
・同様にSageMaker AIメニューで、推論→エンドポイント設定を選択します。

・そこで「エンドポイント設定の作成」を押し、作成画面では下表の値を入力し、最後に一番下の「エンドポイント設定の作成」を押します。


| 設定項目 | 値 |
|---|---|
| エンドポイント設定名 | deepseek-awq-endpoint1 ※お好きな名称で |
| エンドポイントのタイプ | プロビジョン済み |
| 暗号化キー – オプション | カスタム暗号無し |
| 非同期呼び出し設定 | オフ (デフォルト) |
| データキャプチャを有効にする | オフ (デフォルト) |
| 本番稼働用バリアントを作成/モデルの追加 | deepseek-awq ※上で作成したモデル |
| [バリアント編集モーダル/バリアント名] | deepseek-awq-variant1 ※お好きな名称で |
| [バリアント編集モーダル/インスタンスタイプ] | ml.g6.xlarge ※NVIDIA L4/GPUメモリ24GB/メモリ16GB |
| [バリアント編集モーダル/初期インスタンス数] | 1 ※複数起動が可能そうだが、まずは1 |
| [バリアント編集モーダル/初期重量] | 1 ※複数起動の振り分けらしい。自動的に1 |
| [バリアント編集モーダル/モデルデータのダウンロードタイムアウト] | (空欄) ※Default 60分のまま |
| [バリアント編集モーダル/コンテナの起動タイムアウト] | 1200 ※20分にした(Default 300秒) |
※インスタンスタイプml.g6シリーズは、私の場合はそのままでは使えませんでした。使えない場合はAWSコンソールのService Quotasメニューでリクエストをして下さい。サービス=Amazon SageMaker、クオータ名称=ml.g6.xlarge for endpoint usageを指定したリクエストをして、直ぐに使えるようになりました。
・一覧画面を表示させると、作成されたか確認できます。

3.エンドポイントの作成
準備は終わったので、あとは起動するだけです。
・同様にメニューから、推論→エンドポイントを選択します。

・そこで「エンドポイントの作成」を押し、作成画面では下表の値を入力し、下にある「エンドポイントの作成」を押します。


| 設定項目 | 値 |
|---|---|
| エンドポイント名 | deepseek-awq-endpoint1-exec1 ※お好きな名称で |
| エンドポイント設定のアタッチ | 既存のエンドポイント設定の使用 |
| エンドポイント設定 | deepseek-awq-endpoint1 ※上で作成したものを選択し「エンドポイント設定の選択」を押す |
・作成後は確認をします。まず左の画面のようにステータス=Creatingで表示され、モデルのロードが終了したらInserviceに変わります。(7,8分かかります)


右の画面が表示されたら、全て完了です。アクセス可能な状態になりました。
構築6:エンドポイント自動作成&削除の構築
APIが使える状態になったので試してみますが、その前に構築の最後のところを、さらっとご紹介します。営業時間外には削除して費用削減しておきたく、その設定です。
1.エンドポイント作成削除処理の作成 (Lambdaの作成)
まず処理部分が必要です。今回はAWS Lambdaで作成します。
・AWSコンソールでLambdaメニューを開き関数を一つ作成します。設定値は下表の通りです。
(Lambda関数の作成方法は、必要に応じてこちらをご参照下さい→AWS公式サイト)
| 設定項目 | 値 |
|---|---|
| 関数の作成 | 一から作成 |
| 関数名 | reform_api_endpoint_make_and_delete ※お好きな名称 |
| ランタイム | Python3.11 |
| アーキテクチャ | x86_64 |
| 実行ロール | 既存のロールを使用する |
| 既存のロール | atd_role_for_lambda *お手元の適当なロールで |
| タイムアウト(作成後に設定) | 30 秒 *少し増やしておきました。 |
| (その他) | (全てデフォルト) |
・作成後は以下の様に関数一覧に表示されます。ご確認下さい。

・その処理でSageMakerにアクセスできるようにロールに下表の権限を追加します。
(IAMロールの画面操作は、必要に応じてこちらをご参照下さい→AWS公式サイト)
| 設定項目 | 値 |
|---|---|
| 許可ポリシー | AmazonSageMakerFullAccess |
・コードは以下の通りです。Lambda関数内コードとしてセットすればOKです。
import json
import boto3
def lambda_handler(event, context):
print("----START ENDPOINT ACTION----\n")
sagemaker = boto3.client("sagemaker")
endpoint_name = "deepseek-awq-endpoint1-exec1"
config_name = "deepseek-awq-endpoint1"#作成したエンドポイント設定の名前を指定
action = event.get("action", "")
# 存在チェック
try:
sagemaker.describe_endpoint(EndpointName=endpoint_name)
exists = True
except sagemaker.exceptions.ClientError as e:
if "Could not find" in str(e):
exists = False
else:
raise e
if action == "delete":
if exists:
print(f"エンドポイント {endpoint_name} を削除します")
sagemaker.delete_endpoint(EndpointName=endpoint_name)
else:
print(f"エンドポイント {endpoint_name} は既に存在しません")
elif action == "create":
if exists:
print(f"エンドポイント {endpoint_name} は既に存在しています")
else:
print(f"エンドポイント {endpoint_name} を作成します(設定: {config_name})")
sagemaker.create_endpoint(
EndpointName=endpoint_name,
EndpointConfigName=config_name
)
else:
print(f"無効なアクションです: {action}")
return {
"statusCode": 200,
"body": json.dumps(f"{action} 処理が完了しました")
}
これでLambdaの部分は完了です。
2.EventBridgeの設定
時間が来たら上の処理を実行するように設定します。
・コンソールでAmazon EventBridgeメニューを開き、下表の値でスケジュールを作成します。
(EventBridgeの画面操作は、必要に応じてこちらをご参照下さい→AWS公式サイト)
まず起動用です。
| スケジュール1:起動用 | 値 |
|---|---|
| 名前 | invoke_reform_api_endpoint_make ※お好きな名称で |
| 説明 | APIサービス開始時刻にエンドポイントを作成する。 |
| スケジュールグループ | Default |
| [スケジュールのパターン/頻度] | 定期的なスケジュール |
| [スケジュールのパターン/タイムゾーン] | (UTC+09:00) Asia/Tokyo |
| [スケジュールのパターン/スケジュールの種類] | cron ベースのスケジュール |
| [スケジュールのパターン/cron 式] | 30 8 * * ? * ※朝に発動 |
| [スケジュールのパターン/フレックスタイムウィンドウ] | オフ |
| [ターゲットの詳細/ターゲット API] | テンプレート化されたターゲット→AWS Lambda Invoke |
| [Invoke/Lambda 関数] | reform_api_endpoint_make_and_delete ※上で作成したLambda関数を選択 |
| [Invoke/ペイロード] | {“action”: “create”} ※関数に渡すパラメータです。 |
| [アクセス許可/実行ロール] | このスケジュールの新しいロールを作成 *お手元の環境でお選びください |
| [アクセス許可/ロール名] | Amazon_EventBridge_Scheduler_LAMBDA_82be** *私は自動作成させ適当な名前を付けました。 |
| (その他) | (全てデフォルト) |
・続いて、同様に削除用も作成します。設定値は下表の通りです(起動用と変わる部分のみ記載)。
| スケジュール2:削除用 | 値 |
|---|---|
| 名前 | invoke_reform_api_endpoint_delete ※お好きな名称で |
| 説明 | APIサービス終了時刻にエンドポイントを削除する。 |
| [スケジュールのパターン/cron 式] | 30 18 * * ? * ※営業終了時に発動 |
| [Invoke/ペイロード] | {“action”: “delete”} ※関数に渡すパラメータです。 |
| [アクセス許可/実行ロール] | 既存のロールを使用 |
| [アクセス許可/ロール名] | Amazon_EventBridge_Scheduler_LAMBDA_82be** *起動用で自動作成したもの。 |
・2件作成後は以下の様にスケジュール一覧に表示されます。ご確認下さい。

これで、自動作成と削除の設定は完了です。
そして、全ての構築が完了しました。
最終構成
AWSで色々と作成しましたが、全体構成としては下図の通りです。

動作確認(準備)
クライアント側の環境はGoogle Colabのセルを使用し、そこそこのスピードで推論結果のテキストが遂次表示されか、試してみました。
・Colabを準備したら、必要なライブラリをまずインストールします。スクリプトは以下の通りです。
###Deepseek-AWQ版Sagemakerエンドポイント作成後の表示確認 インストール
!pip install boto3
!pip install requests-aws4authaws認証と遂次表示にこれらを使います。
・次に推論のコードを用意します。以下がスクリプトです。
###Deepseek-AWQ版Sagemakerエンドポイント作成後の表示確認 推論コード
import boto3
import json
# IAMユーザーの認証情報
aws_access_key = "***" # 適当なIAMユーザのアクセスキー
aws_secret_key = "***" # 適当なIAMユーザのシークレットアクセスキー
region = "ap-northeast-1"
# boto3 セッション作成
session = boto3.Session(aws_access_key_id=aws_access_key,aws_secret_access_key=aws_secret_key,region_name=region)
# SageMaker Runtime クライアント作成
client = session.client("sagemaker-runtime")
payload = {
"system_prompt": "",
"question": "世界で一番長い川は?"
}
response = client.invoke_endpoint_with_response_stream(
EndpointName="deepseek-awq-endpoint1-exec1",#作成したエンドポイント名を指定
ContentType="application/json",
Body=json.dumps(payload).encode("utf-8")
)
##表示処理----------------------------------------------
import time
import threading
#スピナー表示用(点滅アニメーション)
stop_blinking = False
def blink_confirming():
dots = ["・", "・・", "・・・", "・・・・"]
i = 0
while not stop_blinking:
print(f"\r受信中{dots[i % len(dots)]}", end="", flush=True)
i += 1
time.sleep(0.5)
blink_thread = threading.Thread(target=blink_confirming)
blink_thread.start()
#回答文表示
has_think_ended = False
buffer = ""
start_time = time.time()
timeout_sec = 0 #</think>受信まで待ってからそれ以降を表示させたい時に指定
for event in response['Body']:
if 'PayloadPart' in event:
chunk = event['PayloadPart']['Bytes'].decode("utf-8")
if not has_think_ended:
buffer += chunk
if "</think>" in buffer:
has_think_ended = True
stop_blinking = True
blink_thread.join()
after_think = buffer.split("</think>")[-1]
print(after_think, end="", flush=True)
buffer = ""
elif time.time() - start_time > timeout_sec:
has_think_ended = True
stop_blinking = True
blink_thread.join()
print("\n",buffer, end="", flush=True)
else:
print(chunk, end="", flush=True)
・コード中「適当なIAMユーザ」には、Sagemakerの以下の権限をAWSで追記しておいてください。推論実行時にエラーが出てしまいますので。
| IAMユーザへの追加許可(JSON) |
|---|
{ "Sid": "Statement1", "Effect": "Allow", "Action": "sagemaker:InvokeEndpoint", "Resource": "arn:aws:sagemaker:ap-northeast-1:*****:endpoint/deepseek-awq-endpoint1-exec1" } |
| Actionのところに、作成したエンドポイントのARNを指定します。Sidのところはお手元の設定状況に合わせて指定して下さい。 |
これで、動作確認の準備は完了です。
動作確認結果
上で用意したセルをShift+Enterで実行しました。

大丈夫です。表示されました。
・遂次表示もされています。
・表示速度は0.62秒/1文字でした。少し遅めです。
まとめ
自由度が高く現実的な費用のAWS環境にて、質が高そうなOSSのLLMのAPIを、とにかく作成できました。
速度はそこそこで、費用は月額$440前後(ml.g6.xlarge単価:$1.64/h、月の稼働時間270h(1日9時間x30)として)となり、OpenAIのAPIを従量制で使うより有利な場合がありそうです。
追加学習をしてそのLLMを使いたい時に、この方法が選択肢の一つになるでしょう。
一方で、よりハイスペックなGPUを使いたい場合にAWS以外の選択肢がありそうだ、とも感じました。次はLambda.aiやRunpod等のGPU環境特化?サービスも試してみたいと思います。
最近は「AIエージェント」というキーワードで、色んなツールがAI的機能を組み込んでいますね。私も各案件でAI利用機能を積極的に提案し、色々と確認したいと思います。参考になりそうな情報はまたご紹介させて頂きます。お読み頂きましてありがとうございました。
お気づきの点がありましたら、以下の「ご連絡フォーム」から、コメントを頂けますと幸いです。
