1. はじめに
[!TIP]この記事は2026年5月時点のAzure, Open AI, ブラウザの仕様に基づいています。
今回まとめた上流設計は、まだ人間が調査できた範囲で意思決定とトライ&エラーが必要な領域です。
最近流行りのClaude Codeではまだできないと思います。
以下のサンプルコードは、LLM APIの利用を想像できない初心者のために用意しました。

18行目まではどのAzureテナントのどのAIモデルに送信するかを定義しています。
21行目がAIへの質問です。質問するだけの最小コードです。
29行目がエラー時に表示される文字列の定義です。
2. 推論用AIが受け取るbodyの概要
Open AIに限らずほとんどの推論用AIに対して、bodyに以下の3つの役割と内容を送信するとレスポンスが発生します。
| Role(Open AIの場合) | 推論用AIの認識 |
|---|---|
| system | AIが回答時のルールとして認識する |
| user | ユーザーの発言として認識する |
| assistant | AIが自分の発言として認識する |
■Azure Open AIへPOSTするときのbody例
最低限必要なのは質問だけです。つまり[messages > user+content]のみです。
{
"messages": [
{
"role": "system",
"content": "あなたはお笑い芸人です。質問に1行で答えてください。"
},
{
"role": "user",
"content": "面白いことを3行で言って"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "handle_cost",
"parameters": {
"type": "object",
"properties": {
"cost": {
"type": "number",
"description": "最大文字数",
"const": 1000
}
},
"required": ["cost"]
}
}
}
]
}
■レスポンス例
{role: "assistant", content: "お笑い芸人とかけまして寿司屋とときます。その心は…どちらも見習いはうまいネタを提供できないでしょう。"}
[!TIP]
systemはuserより優先される設計思想なので、この例だとレスポンスは通常1行です。
AIのバージョンが古いと動作が安定せずsystemの設定を守らない場合があります。
systemは最も優先されるので、通常は開発者がAIの振る舞いとして指定します。そして、利用者に操作させないことで秩序を保ちます。
LLMに利用者に合わせた振る舞いをさせるとき、例えばChatGPTやCopilotで利用者とのやり取りの中でメモリしたものは、systemより優先度が低い別の記憶場所にあります。
ちなみに、複数のsystemの内容が矛盾する場合は、後に記載されsystemが優先される傾向にあります。> systemの競合は設計思想として今のところ定義されていないようですが、今後整備されていくかもしれません。
システムが叩くLLM APIは、過去のチャットを内部記憶しません。現在主流のすべてのLLMの共通設計です。
過去のやり取りをベースに回答させる場合は、APIリクエストに毎回含める必要があります。
そのためにJSONをどこかに保存する必要があるが、どう設計すべきかというのがこの記事の本題です。
JSON内のtoolsはネストが深いです。なので、データベースにはJSONを丸ごと格納します。
[!TIP]
assistantはレスポンスですが、書き換えてPOSTすると、AIは自分が過去に返したレスポンスと認識します。
■チャットをDBに格納するために変数宣言する例
const messages = [
{role: "system", content: "あなたはお笑い芸人です。質問に1行で答えてください。"},
{role: "user", content: "面白いことを3行で言って"},
{role: "assistant", content: "お笑い芸人とかけまして寿司屋とときます。その心は…どちらも見習いはうまいネタを提供できないでしょう。"}
]
3. Azure基盤上にあるWeb SaaS構成でチャットを格納するDBの定義
1. 非機能要件[チャットをシステムのDBに格納するか?]
機密情報が含まれている可能性前提でシステムDBに保存するかという観点と、保存コストという観点で判断します。
保存コストをコントロールするために、要約, 構造化, 古いチャットの削除機能の設計は別途考慮が必要です。
選択肢は以下のとおりです。
- ステムDBに格納する
→ 平文で保存するか, 暗号化するか - 利用者のローカル環境のブラウザの記憶領域に格納する
→ IndexedDBに保存するか, OPFSに保存するか
[!TIP]
JSONをディレクトリではなくDBで管理する理由は、データが蓄積したときの大量の読み書きを安全かつ正確に処理するためです。
ブラウザ(Web SaaS)は端末のディレクトリにアクセスすることができません。
別途インストール形式アプリのモダンな構成は、工数を抑えるためにUIをElectronやTauriで"HTML, CSS, TypeScript"で開発して、軽量なSQLite(DB)と連携する構成です。
2. システムDBに保存する場合_構成要件[どのDBを選択するか]の定義
推奨リソースは以下のとおりです。
- PostgreSQL
- CosmosDB
理由は、以下2つのLLMに適した機能が、Azureの他のRDBより成熟しているためです。
- JSONB: JSON構造を壊さずに保存できます。高度なpath検索が可能です。
- embedding vector: 保存時, 検索時に、各チャットと単語の関連性を数値化して意味類似検索可能にします。利用者の質問と意味が近いチャットを検索で結びつきやすくなります。永続保存されるので別途不要データ削除が必要です。
PostgreSQLとCosmosDBの違いは3つあります。
- PostgreSQLの方が安い。CosmosDBは(パーティションでグルーピングしていたとしても)内部で必ず複数のサーバーに分散保存する可能性があります。複数チャット横断検索では複数サーバーの稼働コストがかかるうえ、そもそも高機能なので料金が高い傾向です。
- CosmosDBの方が大量アクセスに対するレイテンシに強い傾向です。
CosmosDBは、データ量と負荷によって自動スケールする設計が他DBよりも強いためです。 - DBの使用状況で一概には言えませんが、以下の傾向があります。
CosmosDBの方が単一チャットの検索が早い。
PostgreSQLの方が複数チャットの検索が早い。
3. システムDBに保存しない場合_構成要件[どのDBを選択するか]の定義
-
ローカル端末(Windows, Mac, iPhoneなど)に保存する場合の推奨機能は以下のとおりです。
- ブラウザ標準機能のIndexedDB+言語のライブラリ
- ブラウザ標準機能のOPFS+SQLite WASM
-
実装難易度という観点では、シンプルにチャットを保存するだけならIndexedDBのみで実現可能です。簡単に実装できます。
さらに複雑な動作(local RAG, vector search, 高速全文検索, 大量チャンク保存など)を実現したい場合は、上記どちらかの実装が必要です。実装難易度はローカル環境にMySQLなどのDBサーバーを立てる程度なので、身構える必要はありません。 -
2026年現在はどちらが安定性があるかという観点は、なぜSQLite WASMを選択するのかという観点や開発者観点にも関連します。
[!TIP]
歴史を話すと、IndexedDBは枯れた技術で、代表的なブラウザでバージョンが古くなければ安定稼働します。
IndexedDBは、JSONのようなオブジェクトで、DBに必要なパラメーターをある程度持っているに過ぎません。
DBとしての利用を強化するためのライブラリ(例: Dexie, RxDB, PouchDB)が長年使われている安定版です。
2024年ぐらいからほとんどの主要ブラウザでSQLite WASMというDBエンジンが使えるようになりました。
OPFSという昔からあるブラウザ標準機能のディレクトリ管理機能と連携して使用します。
SQLite WASMでできることは既存の技術でもだいたいできますが、SQLite WASMは純粋なDBエンジンなのでコードや実装が美しくなります。
しかし現在は過渡期で古い技術の安定性も捨てがたいと言ったところです。
SQLite WASMを採用する場合は特に、実装後の各主要ブラウザで動作検証が必要です。
もちろん各OSやブラウザのバージョン別動作確認などが開発会社によって必要になります。
一般的に動作保証が気になるEdge, Chrome, Safariのうち、Safariは動作確認で問題が出るかもしれないと言われています。
- エンドユーザー観点で説明したいことが二つあります。どちらの方法を選んでも同じです。
-
利用中は技術を意識することすらありません。
また、エンドユーザー側で初期設定不要です。
理由を説明すると、まずIndexedDB, OPFSは主要ブラウザ標準機能です。
SQLite WASMはバックエンド側でライブラリと同じように呼び出せます。 -
プログラマの観点では、プログラム上でのデータの呼び出し方が異なります。
IndexedDBはAPI呼び出しを行います。純粋なDBエンジンではないためです。ライブラリを追加しても似たような感じです。store.put(...)SQLite WASMはDBのクエリ実行を行います。
SELECT * FROM messages WHERE role='user'
-
4. 同期の外部要件定義
Web SaaSとデスクトップアプリが両方存在してチャットを保存する場合、同期の選択肢は以下のとおりです。
- 同期する。競合した場合はクラウドとローカルのどちらかを正とする。または更新日時などのルールを優先する。
- 同期する。競合した場合はクラウドとローカルの両方を正として差分マージする。
- 同期しない
[!TIP]
2026年現在、どのブラウザでもIndexedDB, OPFSのデータはローカル環境のみに保存されます。
ブラウザの機能でパスワードなどはクラウド上に保存されてどの端末でも使えますが、同じ仕組みで保存されないということです。