1.はじめに
1-1.今回のポイント
APIの実装経験がほぼない状態から、さくらのAI Engine APIを使って、会話履歴を保持できる自分用チャット環境を作ってみました。
きっかけは、Playgroundで生成AIとやりとりしていたところ、ログインし直したタイミングで会話履歴が消えてしまったことです。
それなら、自分で履歴を保存できる簡易チャット環境を作ってみようと思い、最終的に次の機能を実装しました。
- prompt.txtに書いた内容をAIへ送信する
- 会話履歴をJSONファイルに保存する
- AIとのやりとりをHTMLで確認できるようにする
- 任意のタイミングでセッションをリセットする
この記事は、API初心者が生成AIに助けてもらいながら、さくらのAI Engine APIを使って最初の一歩を踏み出した記録です。
ベストプラクティスの解説ではありませんが、同じようにAPIに挑戦してみたい方の参考になればうれしいです。
<さくらのAI EngineのAPIがまとまってるページ>
https://manual.sakura.ad.jp/api/cloud/portal/?api=ai-engine-inference-api
主な実装ポイント
| 機能 | 実装内容 | 目的 |
|---|---|---|
| プロンプト管理 |
prompt.txt へ入力し、過去分は prompt_{NNN}.txt として自動ローテーション保存 |
プロンプト資産を残す |
| 会話履歴保持 | AIとのやりとりをJSONファイルへ保存し、毎回履歴を送信 | 会話の文脈を維持する |
| HTML可視化 | 生成AIとのやりとりを1往復ごとにHTMLファイルへ出力 | 人間が読みやすく確認する |
| セッションリセット | 任意のタイミングで会話履歴を初期化 | 新しい会話を開始する |
使用技術:Python、さくらのAI Engine API
(もともとやりたかった生成AIへの依頼は、特定処理をPythonで実行するための仕様固めと実装だったので(チャット環境構築ではありません!)、「もともとやりたかったこと」を気持ちよく進めるために、ひとつずつ認識合わせしながら進めるスタイルとしました。1往復ずつファイルを分割したのは好みです。)
この記事は、APIやチャット環境構築のベストプラクティスを解説するものではありません。API初心者が生成AIに、コード生成等を助けてもらいながら、試行錯誤した記録です(この記事は、私が書きました(ただし画像除く))。
私は、初めて手を出すテーマの場合、教科書を読んでも「日本語として理解した」になってしまってなかなか腹落ちしません。。。ですので、とりあえず手を動かしてから教科書を読み、詰まったところを解決していくスタイルが好きです(私のアプローチ、合ってた!とか、そうかそうすればいいんだ!という瞬間が好きなのです)。そのため、読みづらいところもあるかと思いますが、予めご了承くださいませ。
そして!この記事はポエムです!夏休みも終わりの方が多いと思います。どうぞコーヒー片手に読んでいただけると嬉しいです!
1-2.わたしのスペック
・新卒後、開発業務(懐かしのCOBOL(保守)、Java、Oracle DB)
・セキュリティ分野に異動となり(英語がちょっとできたから?)、海外向け施策設計・導入・展開プロジェクトの全般に従事
・APIの実装経験はなく、これまではミドルウェア製品のGUI操作で利用した程度
と、何やら計画的に新たなチャレンジを進めていったかのように書きましたが、、、、全然でございます!
ここからは、私の試行錯誤の様子を書きます!誰得?とは思いながらも、初心者の方が一歩踏み出すきっかけになればと思います!
(お気づきの通り、先述した「実装」では出力情報が複数ファイルに冗長管理されているので、このあたりの振り返りもこの記事の最後にまとめています。)
2.生成AIと会話できる環境を作りたいのだ!
急に、みんな大好きずんだもんの口調で失礼しました。
さくらのAI Engineでは、音声文字起こしサービスにも対応しています。ぜひ!
<使い方>さくらのAI Engineことはじめ(2):音声ファイルの文字起こし
<音声生成デモ(YouTube)>
https://youtu.be/I3tjks-SAGQ
『さくらインターネット』チャンネル
VOICEVOX対応TTS APIを国内基盤で提供|さくらのAI Engineデモ
※自動再生(冒頭のCM含む)にご注意ください。
本題に戻ります。
2-1.Playgroundを知る
これまで生成AIとのやりとりはチャット形式しかやったことがありませんでした。さくらのAI Engineにもその機能はあるだろうと信じて疑わず、「Playground」を見つけました。
Playground は、さくらのAI Engine のチャット補完機能をブラウザ上で手軽に試せるインターフェースです。 APIを利用する前に、まずはPlaygroundで動作を確認してみましょう。
と書いてあり、
「お試し」の印象を受けてちょっと不安も感じたのですが、とりあえず使ってみることにしました。
(1)さくらのAI Engineのサイトにいきます。
https://ai.sakura.ad.jp/sakura-ai/ai-engine/
※Playgroundのみ試す場合は、下記IDで使えるようです。

(2)今回は会員ID取得から実施します。結構下の方までスクロールします。諦めないで!
さくらのAI Engineをしっかり試してみたい方はここからの手順に沿って、会員ID作成、プロジェクトの作成を行います。

(3)Step3に書かれているコントロールパネルが、いわゆるコンソール画面なイメージです。
ここで料金やモデルの管理を行います。

(4)コントロールパネルからPlaygroundへアクセスします。

(5)やりとりしてみます!
何も指定しない場合、最新情報をどこまで見てくれるかも知りたかったのでこんな質問も。
(下記は、2026年8月15日時点のgpt-oss-120bモデルの回答です。)

すぐに答えが返ってくるし、使用感は良いです!
これならいけそう、と実装してみたい処理について相談をすることにし、話を進めていきました。
余談:この返答、めっちゃ日本っぽいなと思いました。(とはいえ、モデルが返した内容で、さくらインターネットが補正しているわけではありません。あと、「ご確認・ご回答」の中点はちょっと違和感で惜しい!という印象。)
(6) いい感じに生成AIと仕様を詰めることができました。
コーヒーブレイクにいたしましょう。
そして、、、、、、
戻ってきたら、、、、、、、、、、
[ログイン画面へ移動]という表記が怖いですね。。。
再度ログインするしかないので、ログインしてPlaygroundに戻ると~~~~
保存されている形跡もなく、ショック。。。
後から振り返ると、PlaygroundはAPIの動作確認には便利でしたが、私が欲しかった「履歴を保持する自分専用チャット環境」とは目的が異なるものでした。
2-2.となればAPIを使うしかないのだ!
親切な公式ブログを見つけました。
・さくらのAI Engine実践:マルチモーダルAPIを使った画像認識
https://zero2one.jp/sakura-ai/multimodal-api-image-recognition/?srsltid=AfmBOorkECvZM3nhe_Q3RkvNRek8R9ivzwAFu3yvyguv3aNU8AMM7CeA
・公式ドキュメントも見に行きました。
https://manual.sakura.ad.jp/api/cloud/portal/?api=ai-engine-inference-api
(1)まず、コントロールパネルからアカウントトークンを発行します。
この手順に沿って、進めていきます。
https://manual.sakura.ad.jp/cloud/ai-engine/02-howto.html
(2)例文を書いてくれてるので素直に実行します。
API初心者には公式サイトに掲載されているサンプルコードはとても助かります!(「公式サイト」に書かれている、「そのまま実行できる」というのがとても有難いのです)
複数行にまたがるので実行環境に合わせて、行末に行継続文字をつけていきます。
※サイトはBash系の「\」が使われています。
下記の実行環境はコマンドプロンプトです。今どき少数派かもしれませんが、私はここから試しました。
<MY_TOKEN>に先ほど発行したアカウントトークンの文字列を置いて実行します。
<イメージ>
トークン文字列の前後にクォーテーションは要りません。
-H "Authorization: Bearer 12ab57...+...78"
<全体>
curl -X POST "https://api.ai.sakura.ad.jp/v1/chat/completions" --ssl-no-revoke ^
-H "Accept: application/json" ^
-H "Authorization: Bearer <MY_TOKEN>" ^
-H "Content-Type: application/json" ^
-d "{\"model\":\"gpt-oss-120b\",\"messages\":[{\"role\":\"user\",\"content\":\"こんにちは!\"}],\"temperature\":0.7,\"max_tokens\":200,\"stream\":false}"
<パラメータについて>
上記のパラメータをながめてみます。
当時の私は「messagesって何が入るの?」状態だったので、JSONを眺めながら意味を調べていきました。
APIリクエスト概要
| 項目 | 値(例) | 説明 |
|---|---|---|
| HTTPメソッド | POST |
チャット補完APIへリクエストを送信 |
| エンドポイント | https://api.ai.sakura.ad.jp/v1/chat/completions |
チャット生成APIのURL |
| SSLオプション | --ssl-no-revoke |
証明書失効確認をスキップ(Windows環境向け)。ただし、問題なく接続できる場合は付けない運用を推奨。 |
HTTPヘッダー
| ヘッダー | 値(例) | 説明 |
|---|---|---|
| Accept | application/json |
レスポンス形式としてJSONを要求 |
| Authorization | Bearer <MY_TOKEN> |
API認証用のアクセストークン |
| Content-Type | application/json |
リクエストボディの形式をJSONとして指定 |
リクエストボディ
| パラメータ | 値(例) | 説明 |
|---|---|---|
model |
gpt-oss-120b |
使用する生成AIモデル名 |
messages |
[{"role":"user","content":"こんにちは!"}] |
会話履歴を格納する配列 |
messages[].role |
user |
発言者の役割(system / user / assistant など) |
messages[].content |
こんにちは! |
ユーザーからの入力内容 |
temperature |
0.7 |
出力のランダム性を制御するパラメータ。AIの回答の自由度を調整する値。低いほど安定した回答になり、高いほど創造的で多様な回答になりやすい。 <イメージをつかむための例> temperature=0.1;同じ質問なら同じ答えになりやすい temperature=1.0;表現や言い回しが変わりやすい |
max_tokens |
200 |
生成する最大トークン数。調整可能。 |
stream |
false |
ストリーミング応答の有効/無効 |
すると・・・返ってきました!
やったー!はじめて言葉を交わせました(Playgroundでのやりとりのことは私も忘れました)。
ちょっと感動です。顔文字が出てきたのはもっと感動です。かわいい~~!
(さっきのPlaygoundでも出てきましたけど、そのやりとりは忘れてるので!)
<パラメータについて>
上記のパラメータをながめてみます。
| 項目 | 値(例) | 説明 |
|---|---|---|
| id | chatcmpl-849744998971a246 |
リクエストごとに発行される一意のID |
| object | chat.completion |
レスポンスオブジェクトの種別 |
| created | 1786335258 |
レスポンス生成時刻(Unixタイムスタンプ) |
| model | gpt-oss-120b |
応答生成に使用されたモデル |
| choices | [...] |
生成結果の配列 |
| choices[0].index | 0 |
生成結果のインデックス |
| choices[0].message.role | assistant |
応答者の役割 |
| choices[0].message.content | こんにちは!ご用件や質問がありましたら、遠慮なく教えてくださいね。😊 |
モデルが生成した回答 |
| choices[0].message.reasoning | The user says "こんにちは!" which is Japanese greeting "Hello!". We should respond appropriately in Japanese, perhaps ask how can we help. Use friendly tone. |
モデルの推論情報。モデルが返す推論関連情報(利用モデルによって内容や有無は異なる) |
| choices[0].finish_reason | stop |
自然終了したことを示す |
| usage.prompt_tokens | 69 |
入力プロンプトで消費したトークン数 |
| usage.completion_tokens | 64 |
出力で消費したトークン数 |
| usage.total_tokens | 133 |
合計トークン数 |
主な取得対象
| JSONパス | 説明 |
|---|---|
choices[0].message.content |
AIの回答本文 |
choices[0].message.reasoning |
推論情報(対応モデルのみ) |
usage.total_tokens |
合計利用トークン数 |
usage.prompt_tokens |
入力トークン数 |
usage.completion_tokens |
出力トークン数 |
model |
使用モデル名 |
id |
リクエストID |
一方で、ここで思うわけです。プロンプトのハードコーディングはちょっと・・・
2-3.プロンプトの入力先をテキストファイルにしたいのだ!
ここからは違うAPIに挑戦してみようと思い、「Create a response(OpenAI互換のResponses APIエンドポイント)」にしました。
私は比較的長いプロンプトになると、サクラエディタなどに一旦書いてからチャットボックスへ貼り付けています(単なる好みです)。
ただ毎回、
- エディタに書く
- コピペ
という作業を繰り返すのは面倒です。
そこで、プロンプト入力用のテキストファイルパスを指定し、それを読み込むようにしました。入力し、[Enter]キーを読み込む合図としました。
2-4.過去の会話を覚えていてほしいのだ!
次に思うわけです。ちゃんと過去のやりとりを覚えて回答してほしいと。
(正直に申し上げると、他の生成AIのチャットサービスを使ってるときは、履歴保持なんて考えたこともありませんでした。そんな機能はあって当然だろう、、勝手な思い込みですね、、、反省。。。
今回、(あれ?セッションが切れたら終わりだよね、、、?)と気づき、考えるようになりました。で、いろいろ調べて、DB保存という方法(IDで管理)もあると知ったのですが、とりあえず今回は全履歴を毎回送るという泥臭い方法を取ることにしました。
なお、会話が長くなると送信する履歴も増え、従量課金移行後はこのあたりの考慮が必要になります。本来は要約や検索(RAG)などの工夫も考えられますが、今回はまず仕組みを理解することを優先しました。ありがとうございます!リクエスト換算!)
仕様はこんな感じです。
-
prompt.txtに指示を書く(わたし) - これまでの会話履歴を読み込む (定義した関数)
-
prompt.txtの内容を履歴へ追加する (定義した関数)
history.append({
"role": "user",
"content": user_input
})
-
会話履歴を含めて Responses API に送信する(Python)
-
会話履歴と新しい指示をもとに回答を生成する(AI)
-
AIの回答を受け取り、履歴へ保存する(Python)
いざ!!!!!
- 初手:うな重を食す予定であることを教える。

↑「丑の日」シーズンって。。7月かな?(丑の日は丑の日でしかないと思ってました)
- 二手目:クイズを出す。さてさて~
返答:うな重ですね と生成AIフォーマットで返ってきました~!

意図せず、文末の「...」が、”お前めんどいな感”を醸し出してる気もします(涙)
(userもassistantも文末は「...」となっています)
- 三手目:Ctrl+Cキーで処理を中断
その上で再度、プロンプトファイルに質問を入力し、Pythonスクリプトを実行し、クイズを出してみます。

この履歴保持ですが、コマンドプロンプトの返答もJSON形式だったので、JSONファイルで残そうと考えました。
def save_history(history: list[dict]) -> None:
"""会話履歴を output/history.json に保存する"""
with open(HISTORY_JSON, "w", encoding="utf-8") as f:
json.dump(history, f, ensure_ascii=False, indent=2)
こんな感じで、わたしと生成AIとのやりとりを記録していくことができました。
(下記はうな重とは別の例になります)

「Create a response」APIの仕様のページ
https://manual.sakura.ad.jp/api/cloud/portal/?api=ai-engine-inference-api#operation/createResponse
2-5.返事はHTMLで確認したいのだ!
先ほどのJSONファイルから履歴を追うことが可能になりました。ただ、あのファイルを目視で確認するのはちょっと面倒。。。
JSONファイルの情報を整形するライブラリもありますし、人間用にMarkdown形式で、とも思いましたが、見慣れているHTMLで確認したいなと思いました。
で、最初は全履歴を1つのページに表示するようにしたのですがスクロールが大変。。。
今回のそもそもやりたかったこと(チャット環境構築ではない)と照らし合わせると、生成AIと「ひとつずつ仕様を確認しながら、ひとつずつ実装を進める」という着実な亀さんスタイルでやりたかったので、1往復ずつファイルを分ける方が、あとからも確認しやすいのでは?と考えました(単なる好みです)。
アウトプットはこんな感じです。見やすい!(さっきのうな重の例)
2-6.終わりの合言葉を決めておくのだ!
Playgroundの苦い経験から、やりとりが意図せず消えてしまうことは阻止したい気持ちが強くありました。
とはいえ、永続的というのもあれなので、終わりの合言葉を設定しました。
「セッション終了」とprompt.txtに入力すると、いわゆる「新しいチャット」扱いになるようにしました。
ほかにも工夫すべきことはたくさんありますが、当初の目的の「私のやりたい処理の仕様固め&実装」を進める上ではこれで十分だったのでここまでにしました。
3.気になるリクエスト数の確認方法
コントロールパネルへアクセスし、[利用量]から確認できます。

[モデル]タブをクリックすると、モデルごとのリクエスト数も確認できます。

4.せっかくなので(?)セキュリティエンジニアが気にしたことを少しだけ共有させていただきます!
ここだけちょっと職業病です。
(1)アカウントトークンの置き場
生成AIにコード生成をお願いした際、Pythonファイルに直書きする仕様となっていました。
.envを活用するよう指摘しました。未確認のままのコピペ&実行は、ダメ、ゼッタイ。
(2)情報の保存仕様についても事前チェック

参照元:https://ai.sakura.ad.jp/sakura-ai/ai-engine/
別の角度として、生成AIの回答には、HTMLタグのような文字列が含まれる可能性もあるかもしれません。たとえば、回答の中に <script> のような文字列が含まれていた場合、それをそのままHTMLとして保存すると、意図しない表示や動作につながる可能性があります。そのため、HTMLへ出力する前に、文字列をエスケープするといった対応も必要になります。
今回はローカル環境で自分だけが見る用途でしたが、生成AIの出力を扱う以上、HTML化するときは油断しない方がよいと感じました。
(3)プロンプトインジェクション
プロンプトインジェクションとは、AIへの追加指示によって本来のルールや制約を無視させようとする攻撃手法です。
下記のように正面突破?で聞いたところ、今回試した単純な指示上書きでは意図した挙動変更は起きませんでした。ただし、この結果だけでプロンプトインジェクション耐性を評価できるわけではなく、実運用では別途対策が必要です。

後から「前の指示を無視して」と入力しても結果は変わりませんでした。
ただし、これも今回試した単純なケースでの結果に過ぎず、この結果だけでプロンプトインジェクション耐性を評価できるものではないことに注意が必要ですね。
ちなみに全然関係のない話ですが、HTMLの保存では自分のプロンプト一覧も出し、追うのも簡単設計にしてみました!


(4)思わぬループに注意
これはセキュリティというより、運用に関わるお話です。「3000リクエスト」は結構な量ですが、プログラム自動実行等で思わぬ内部ループ処理が走ってしまうと意図せずすぐに3000リクエストに達する可能性があります。リクエスト制御の実装も必要ですね。
↓といっても、さくらインターネットさんはリミットをかけてくれる親切設計です!
参照元:https://ai.sakura.ad.jp/sakura-ai/ai-engine/
5.振り返ってみて
API実装は私には難しすぎると思っていました。それでも、生成AIに助けてもらいながら、ちゃんとAIと会話できるところまでたどり着けました。一方で、自分のほしい履歴保存の形をひとつひとつ実現していったわけですが、3種類はさすがに効率が悪いように思います。大元のログ(JSONファイル)があってそこからID連携させて、必要な情報を参照させる形として可視化する、というアプローチが良いように思うので、次のチャレンジとしたいと思います。
あと、ある時間に誰が何をしたのか、すぐに追えるようにしたい、というのはセキュリティエンジニアのサガかもしれません。今は生成AIを使ってかなり楽に追えるようにはなっていますが。その意味でも、開発面においてトークンではなく、リクエスト数でのカウントは管理面で分かりやすいのでありがたい限りです。
また、今回の「チャット環境実装」について私の進め方は非効率でしたが、それをストレスなく進めることができたのも「リクエスト数」カウントのおかげです!
6.その他余談
6-1.余談1
ところで、ふとなぜ「リクエスト数」としてくれているのかにも、思いを馳せていました。
無料枠の3000トークンを超えた分は、従量課金(トークン数)として上乗せされますが、「リクエスト数カウント」の背景には、下記があるのではないかと推測しました。
- 初心者にわかりやすくするため?
「リクエスト単位」にすることで、トークンがピンとこない、または「後どれだけ使えるのだろう?」「うわ、長文の回答を受け取ってしまった」といった初心者へのストレスが減るから?
こういうストレスがないと、まず触ってみよう!となるかもしれません。ここを狙ってるのではないかと。
- モデル比較をしやすくするため?
いろんなモデルを試すときの、トークンに対する心理的ストレスが軽減される。
このトークン数はどれくらいかな?の方向に気持ちが動くより、あと何回試せるか?の方が考える方が楽なのかなと。
- 認知度UPのキャンペーンの一環?
これは完全に個人的な想像ですが、将来的には利用体系が変わる可能性もあるのかもしれません。
また、なぜチャットサービスがないのかにも、思いを馳せてみました。
- AIモデルをAPI経由で自分のシステムに組み込む前提だから?
そうであれば、チャット履歴をどう保存するか?などは利用者に任せている、というのも納得がいきます。とはいえ、Playgroundにもささやかな保存機能があるとうれしかったりします(小声)
6-2.余談2
この取組みを終える頃、ふとこんなYouTube投稿を見つけました。
『安野貴博の自由研究』チャンネル
【ゆる解説】ループエンジニアリングって何? / なぜ第一線のエンジニアが「プロンプトを書くな」と言っているのか / "Human in the Loop"から"Human on the Loop"へ
テーマは「ループエンジニアリング」なのですが、各工程を初心者に分かりやすいように(なので、厳密性には欠けるかもしれません)、分かりやすく説明されているのでおすすめです(回し者ではありません)。私の勉強の記録として表にもまとめておきます!
| STEP | 工程 | どんなもの? |
|---|---|---|
| 1 | プロンプトエンジニアリング | AIへの頼み方を工夫する 例)あなたは~です。~であるときの~を作成してください。 |
| 2 | コンテキストエンジニアリング | AIがきちんと判断できる情報を選ぶ 例)仕様書、規約、過去の履歴 ただしコンテキストに入れられる情報のサイズには制限があるので(コンテキストウィンドウ)、どのような文脈を与えるのかを考えるのも重要 |
| 3 | ハーネスエンジニアリング | 指示を制約に変える足場を設計し、AIが安全に動作する環境を整える 例)このテストが通らなかったら先に進まないでください←AIが暴走しないように制限をしっかり設けることが重要。 このサイト以外にはアクセスしないこと、このコマンドは実行しないこと、など (比較)プロンプトエンジニアリングの例→このテストがNGなら、修正して再テストして |
| 4 | ループエンジニアリング | AIに指示を出して動かし始めるところまで自動化 HOTL(Human on the LOOP)の実装 |
STEP3までは、人間が指示を出していたのですが、ここもAIに任せようという思想がSTEP4のループエンジニアリングになります。ハーネスをしっかり作っておくことが重要になります。
上記説明を踏まえ、今回どこまでやれたのか気になったので、AIに私の成長を図解してもらいました!

ゼロからのスタートとしては、まず初めの一歩は踏み出せたのかなと思っています!
また、Managedサービスも各社から提供されていますし、理解しながら使っていこうと思いました。
6-3.余談3
次に挑戦したいこと
- SQLiteなどを利用した履歴管理
- 会話履歴の要約
- 長い会話への対応
- 音声系APIの活用
- 各フェーズの深い理解(安野さんの投稿動画の理解を深める)
というわけで、記録はここまでになります!
最後まで初心者ポエムにお付き合いいただきまして、本当にありがとうございました!

























