# Local Model Service Protocol (LMSP) 1.0, ドラフト > Made for Agent は、AI エージェント、モデル、ツールが互いを発見し、通信するための、機械可読なオープンプロトコルを公開しています。最初のプロトコルは、ローカルモデルサービスプロトコル(LMSP)です。 Source: https://madeforagent.com/ja/llms-full.txt Updated: 2026-11-08 --- # ローカルモデルサービスプロトコル(LMSP) > LMSP は、自分のデバイス上で動作する言語モデルを発見して呼び出すためのオープンなプロトコルです。サービス発見、モデルカタログ、ストリーミング、永続タスク、OpenAI 互換を定義します。 Source: https://madeforagent.com/ja/lmsp/ Language: ja Updated: 2026-11-08 ## LMSP が解決すること {#what} LMSP(Local Model Service Protocol)は、ユーザー自身のデバイスやローカルネットワーク上で言語モデルを動かすソフトウェアと、それを利用するクライアントのためのオープンなプロトコルです。 常駐するローカルサービスは、既知のアドレスで自身を記述し、モデルを一覧します。生成リクエストには JSON またはサーバー送信イベントで応答し、長時間の生成は永続化されたタスクとして実行します。タスクはキャンセルでき、接続が切れた後でも再び追跡できます。同じサービスが OpenAI API も話すため、今あるクライアントはそのまま動作します。 LMSP は OpenAI API、Ollama、MCP を置き換えるものではありません。それぞれの長所を、常駐するローカルサービス向けのプロトコル層としてまとめたものです。 ## 概要 {#glance} | 項目 | 概要 | | --- | --- | | 既定のアドレス | `http://127.0.0.1:17832` | | サービス記述 | `GET /.well-known/lmsp.json` | | コアインターフェース | `/lmsp/v1`。HTTP 上の JSON で、ストリーミングはサーバー送信イベント | | 互換インターフェース | OpenAI クライアント向けの `/v1`、Ollama クライアント向けの任意の `/api` | | 発見 | 手動アドレス、ローカルアドレスのプローブ、任意の mDNS と DNS-SD(`_lmsp._tcp.local.`) | | 認証 | ローカルでのペアリングで得るベアラートークン | | 永続化 | 組み込みの SQLite。タスクは再起動後も残る | | 契約 | OpenAPI 3.2 | ## 試してみる {#try} お使いのマシンで LMSP サービスが動いているなら、流れはこれだけです。まず [10.2 節](https://madeforagent.com/ja/lmsp/1.0/security.md#pairing)の手順でペアリングしてトークンを取得し、`LMSP_TOKEN` に入れておいてください。 ```sh # 1. サービスを発見する curl http://127.0.0.1:17832/.well-known/lmsp.json # 2. ローカルモデルを一覧する curl http://127.0.0.1:17832/lmsp/v1/models \ -H "Authorization: Bearer $LMSP_TOKEN" # 3. レスポンスを生成する curl http://127.0.0.1:17832/lmsp/v1/responses \ -H "Authorization: Bearer $LMSP_TOKEN" \ -H "Content-Type: application/json" \ -d '{"model":"qwen3:14b-q4_k_m","input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"Hello"}]}]}' ``` ## LMSP と他のプロトコルの関係 {#related} | プロトコル | 対象範囲 | LMSP との関係 | | --- | --- | --- | | OpenAI API | ホスト型のモデル。Responses API が最新のインターフェース。 | LMSP はコアインターフェースで Responses の構造に従い、サービスはその隣に OpenAI 互換インターフェースを提供する。 | | Ollama | すでに OpenAI 互換エンドポイントを提供しているローカルランタイム。 | LMSP サービスの背後にアダプターとして置ける。LMSP は任意の Ollama 互換エンドポイントも定義している。 | | MCP | エージェントをツールとコンテキストに接続する。 | LMSP はエージェントをモデルに接続する。どちらも、必須のセッションよりステートレスな発見を好む。 | | A2A | エージェント間の協調。 | LMSP はサービス記述とタスクに、サービスカードと明示的なタスク状態を取り入れている。 | ## リソース {#resources} - [仕様 1.0 版(ドラフト)](https://madeforagent.com/ja/lmsp/1.0.md)。 - OpenAPI 契約。[JSON](https://madeforagent.com/lmsp/1.0/openapi.json) と [YAML](https://madeforagent.com/lmsp/1.0/openapi.yaml) があります。 - 言語モデル向けの [llms.txt](https://madeforagent.com/ja/llms.txt) と [llms-full.txt](https://madeforagent.com/ja/llms-full.txt)。 - すべてのページは Markdown でも入手できます。ページのアドレス末尾のスラッシュを `.md` に置き換えてください。例: [`/ja/lmsp/1.0/tasks.md`](https://madeforagent.com/ja/lmsp/1.0/tasks.md)。 ## よくある質問 ### LMSP とは何ですか? LMSP(Local Model Service Protocol)は、ユーザー自身のデバイスやローカルネットワーク上で動作する言語モデルサービスのためのオープンなプロトコルです。クライアントがサービスを発見し、モデルを一覧し、レスポンスを要求し、出力をストリーミングし、長時間のタスクを実行する方法を定義します。 ### LMSP は OpenAI API、Ollama、MCP を置き換えるものですか? いいえ。LMSP はそれらの上に成り立っています。コアインターフェースは OpenAI Responses の構造に従い、サービスは OpenAI 互換と Ollama 互換のエンドポイントを併せて提供でき、MCP のステートレスな発見と A2A のタスク状態を取り入れています。 ### 既存の OpenAI クライアントは LMSP サービスを使えますか? 通常は使えます。ベース URL を変更し、LMSP のトークンを API キーとして設定するだけです。ただし、サービスの発見には LMSP に対応したクライアントが必要なため、自動では見つかりません。 ### クライアントはローカルの LMSP サービスをどうやって見つけますか? 次の 3 つを順に試します。まず手動で入力されたアドレス、次に慣例のローカルアドレスで /.well-known/lmsp.json を要求する方法、最後にローカルネットワーク上で mDNS と DNS-SD により _lmsp._tcp.local. を探す方法(任意)です。 ### localhost で動くモデルサービスは安全ですか? 自動的に安全になるわけではありません。Web ページがローカルのポートへの接続を試みることがあるためです。LMSP サービスは既定でループバックにバインドし、ローカルでのペアリングで得たベアラートークンを必須とし、Origin ヘッダーと Host ヘッダーを検査し、LAN モードでは認証を要求します。 ### 仕様の現在の状態はどうなっていますか? バージョン 1.0 は最初の公開ドラフトで、今後も互換性のない変更が入る可能性があります。英語版が規範であり、翻訳は利便性のために提供されています。 --- # 概要 > LMSP 1.0 ドラフトの概要。ローカルモデルサービスプロトコルとは何か、土台とした設計、階層型アーキテクチャ、仕様の読み方、全体で使う用語を説明します。 Source: https://madeforagent.com/ja/lmsp/1.0/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 1.1 要旨 {#abstract} ローカルモデルサービスプロトコル(LMSP)は、クライアントが、ユーザー自身のデバイスやローカルネットワーク上で動作する言語モデルサービスを見つけて利用する方法を定義する。サービス発見、モデルカタログ、同期生成とストリーミング生成、再起動後も残る非同期タスクを規定する。また、1 つのサービスが LMSP インターフェースと並べて OpenAI 互換インターフェースを提供する方法も定義し、既存のクライアントがそのまま動作できるようにする。 LMSP は OpenAI API、Ollama、MCP を置き換えるものではない。それぞれの長所を、常駐するローカルサービス向けのプロトコル層にまとめたものである。 ## 1.2 本文書の状態 {#status} 本文書は 2026-11-08 に公開された LMSP 1.0 の最初の公開ドラフトである。「LMSP」は仮称である。内容はフィードバックを受け付けており、1.0 が確定するまでは互換性のない変更が入る可能性がある。 英語版が規範である。翻訳は利便性のために提供されており、両者が異なる場合は英語版が優先される。 ## 1.3 設計の基礎 {#design-basis} LMSP は新しい設計を発明せず、実績のある設計を再利用する。 - **OpenAI Responses API。** 構造化された出力アイテム、意味を持つサーバー送信イベント、長時間処理のための照会とキャンセルを備えたバックグラウンド実行。LMSP のコア会話インターフェースの手本である。[ストリーミングレスポンス](https://developers.openai.com/api/docs/guides/streaming-responses)と[バックグラウンドモード](https://developers.openai.com/api/docs/guides/background)を参照。 - **Ollama。** OpenAI 互換の `/v1/models`、`/v1/chat/completions`、`/v1/responses` エンドポイントを提供しており、1 つのサービスに OpenAI 適応層を加えることで導入コストが大きく下がることを示している。[Ollama の OpenAI 互換性](https://docs.ollama.com/api/openai-compatibility)を参照。 - **Model Context Protocol(MCP)。** 最新の設計は、必須のセッション初期化からステートレスな発見へ移っている。すべてのリクエストが自分のバージョン情報を携え、クライアントは発見用インターフェースを通じて機能を知る。再起動、クライアントのクラッシュ、接続断があってもプロトコルの状態が壊れてはならないローカルサービスに適している。[MCP](https://modelcontextprotocol.io/) を参照。 - **Agent2Agent(A2A)。** サービスカード、明示的なタスク状態、タスクの購読とキャンセルは、LMSP のサービス記述と非同期タスクモデルの参考になっている。[A2A](https://a2a-protocol.org/) を参照。 ## 1.4 アーキテクチャ {#architecture} LMSP は、モデルランタイムと通信するアダプターの上に、3 つの層で構成される。 ```text +---------------------------------------------------------------+ | Discovery mDNS / DNS-SD + well-known service document | +---------------------------------------------------------------+ | Core model catalog, responses, SSE, async tasks | +---------------------------------------------------------------+ | Compatibility OpenAI API, optional Ollama API | +---------------------------------------------------------------+ | Adapters llama.cpp, Ollama, vLLM, custom runtimes | +---------------------------------------------------------------+ ``` - **発見。** mDNS/DNS-SD と、well-known のサービス記述ドキュメント([第 3 章](https://madeforagent.com/ja/lmsp/1.0/discovery.md))。 - **コア。** モデルカタログ、同期レスポンス、サーバー送信イベント、非同期タスク([第 5〜7 章](https://madeforagent.com/ja/lmsp/1.0/models.md))。 - **互換。** OpenAI 互換 API と、任意の Ollama 互換 API([第 8 章](https://madeforagent.com/ja/lmsp/1.0/compatibility.md))。 - **アダプター。** llama.cpp、Ollama、vLLM、またはカスタムランタイムへのブリッジ。 指針は「**1 つのモデル実行コア、2 つのインターフェースファミリー**」である。サービスは同じコアの上に LMSP インターフェースと OpenAI インターフェースを提供するため、モデルのロード、リクエストの実行、タスクの追跡を行う場所が 1 か所に定まる。 ## 1.5 適合性の表現 {#conformance} 本文書で太字で表記された **なければならない**(MUST)、**てはならない**(MUST NOT)、**必須**(REQUIRED)、**べきである**(SHOULD)、**べきではない**(SHOULD NOT)、**推奨**(RECOMMENDED)、**てもよい**(MAY)、**任意**(OPTIONAL)は、[BCP 14](https://www.rfc-editor.org/info/bcp14)([RFC 2119](https://www.rfc-editor.org/rfc/rfc2119)、[RFC 8174](https://www.rfc-editor.org/rfc/rfc8174))に記述されているとおりに解釈する。太字で表記されていない場合は、通常の語として扱う。 各ページには、規範的か参考情報かが示されている。規範的なページは要件を定め、参考情報のページは手引きを与える。例はすべて参考情報である。JSON は UTF-8 とし、フィールド名には `snake_case` を使う。 ## 1.6 用語 {#terminology} | 用語 | 意味 | | --- | --- | | サービス | LMSP を実装し、アダプターを通じてモデルを実行する常駐プロセス。 | | クライアント | サービスを発見して呼び出すソフトウェア。エージェント、アプリケーション、ツールなど。 | | アダプター | サービスを 1 つのモデルランタイム(llama.cpp、Ollama、vLLM など)に接続するコンポーネント。 | | モデル | ローカルで利用できるモデル。安定した `id` で識別される。 | | レスポンス | 1 回の生成リクエストの結果。出力アイテム、使用量、ステータスからなる。 | | タスク | レスポンスリクエストを永続化して非同期に実行するもの。明示的な状態を持つ。 | | イベント | レスポンスの生成中に発行される、シーケンス番号付きのサーバー送信イベント 1 件。 | --- # トランスポートとデプロイ > LMSP サービスの既定の待ち受けアドレス、トランスポート、ストリーミング形式、URL の構成と、モデルプロセスとは別の OS サービスとして常時稼働させる方法を定めます。 Source: https://madeforagent.com/ja/lmsp/1.0/transport/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 2.1 既定のデプロイ {#deployment} サービスは既定でループバックアドレス `127.0.0.1` と `::1` で待ち受け**なければならない**。慣例のポートは `17832` で、既定のベースアドレスは `http://127.0.0.1:17832` となる。 | 項目 | 値 | | --- | --- | | 待ち受けアドレス | `127.0.0.1` と `::1` | | トランスポート | JSON を運ぶ HTTP/1.1 または HTTP/2 | | ストリーミング | サーバー送信イベント。`Content-Type: text/event-stream` | | コアインターフェースのプレフィックス | `/lmsp/v1` | | OpenAI 互換のプレフィックス | `/v1` | | サービス記述 | `GET /.well-known/lmsp.json` | | 機械可読な契約 | `GET /lmsp/v1/openapi.json` | | 任意のローカルチャネル | Unix ドメインソケットまたは Windows 名前付きパイプ | リクエストとレスポンスは UTF-8 の JSON(`Content-Type: application/json`)を使う。ただし、ストリーム(`text/event-stream`)とエラー(`application/problem+json`)は除く。タイムスタンプは整数で、Unix エポックからの秒数とする。識別子は不透明な文字列であり、`resp_` や `task_` のような型プレフィックスを付けることを**推奨**する。 インターフェース契約は OpenAPI 3.2 ドキュメントである。OpenAPI 3.2 はサーバー送信イベント、JSON Lines、JSON Sequences を記述できるため、ストリーミングエンドポイントも他のエンドポイントと同じドキュメントで規定される。このバージョンの契約は [`/lmsp/1.0/openapi.json`](https://madeforagent.com/lmsp/1.0/openapi.json) で公開されている。 サービスは、同じ HTTP セマンティクスで Unix ドメインソケットや Windows 名前付きパイプによる接続を受け付け**てもよい**。1.0 では、そのようなエンドポイントをどう通知するかは拡張に委ねられる。 ## 2.2 常時稼働 {#operation} サービスは OS によってホストされる**べきである**。macOS では launchd、Linux では systemd、Windows では Windows サービスマネージャーを使う。 サービスのプロセスとモデルのプロセスは分離される**べきである**。モデルのクラッシュは、そのモデルを使うタスクだけに影響する**べきである**。サービス発見、モデルカタログ、過去のタスクへの問い合わせを利用不能にする**べきではない**。 --- # サービス発見 > LMSP クライアントが、手動設定、ループバックのプローブ、任意の mDNS/DNS-SD によってローカルサービスを見つける方法と、well-known のサービス記述ドキュメントの読み方を説明します。 Source: https://madeforagent.com/ja/lmsp/1.0/discovery/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 3.1 発見方法 {#methods} クライアントは、次の方法を順に試してサービスを発見する。 1. **手動設定。** ユーザーがサービスのアドレスを直接入力する。 2. **慣例のローカルアドレスのプローブ。** クライアントは候補アドレスに `/.well-known/lmsp.json` を要求する。慣例の候補は `http://127.0.0.1:17832` と `http://[::1]:17832` である。 3. **LAN での発見。** クライアントは mDNS と DNS-SD で `_lmsp._tcp.local.` を探索する。 有効なサービス記述([3.3 節](https://madeforagent.com/ja/lmsp/1.0/discovery.md#service-description))が得られた時点で、その方法は成功となる。ユーザーが手動で入力したアドレスは、発見されたアドレスより常に優先される。 ## 3.2 LAN でのアドバタイズ {#mdns} DNS-SD は、PTR レコードでサービスインスタンスを見つけ、SRV レコードでホストとポートを得て、TXT レコードで少量の追加情報を運ぶ。mDNS と組み合わせると、ローカルネットワークでゼロ構成の発見が可能になる([RFC 6763](https://www.rfc-editor.org/rfc/rfc6763)、[RFC 6762](https://www.rfc-editor.org/rfc/rfc6762))。 アドバタイズの例: ```text Service type: _lmsp._tcp.local. Instance name: Workstation LMSP._lmsp._tcp.local. TXT record: v=1 id=svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2 path=/ auth=pairing ``` | TXT キー | 意味 | | --- | --- | | `v` | アドバタイズ形式のバージョン。LMSP 1.0 では `1`。 | | `id` | サービス ID。サービス記述の `service.id` に等しい。 | | `path` | サービスルートの URL パス。クライアントは `{path}.well-known/lmsp.json` を要求する。 | | `auth` | 認証のヒント。`pairing` は、利用前にクライアントがペアリングする必要があることを示す。 | 規則: - サービスは、アクセストークン、モデル一覧、ファイルパス、その他の機微な情報を TXT レコードに入れ**てはならない**。TXT レコードはサービスの場所を示すだけである。実際の機能は、サービス記述から読み取ら**なければならない**。 - サービスは、mDNS アドバタイズを既定で無効にしてお**かなければならない**。ユーザーが LAN 発見を明示的に有効にした場合にのみ有効になる。ローカルマシンだけで使うサービスはアドバタイズしない。 - LAN でアドバタイズするサービスは、ループバック以外でも待ち受ける必要があり、これは LAN モードでのみ許される([10.5 節](https://madeforagent.com/ja/lmsp/1.0/security.md#lan))。 - クライアントは、TXT レコードを検証されていないヒントとして扱い、サービス記述を読んでサービスを確認し**なければならない**。 ## 3.3 サービス記述 {#service-description} ```http GET /.well-known/lmsp.json ``` サービスは、このドキュメントを認証を要求せずに `application/json` として提供し**なければならない**。 ```json { "protocol": "lmsp", "versions": ["1.0"], "service": { "id": "svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2", "name": "Workstation Local Models", "version": "1.0.0" }, "interfaces": [ { "kind": "lmsp", "version": "1.0", "url": "http://127.0.0.1:17832/lmsp/v1", "transport": "http+json" }, { "kind": "openai", "version": "responses+chat", "url": "http://127.0.0.1:17832/v1", "transport": "http+json" } ], "capabilities": { "streaming": true, "tasks": true, "event_resume": true, "conversations": true, "model_management": true }, "authentication": { "type": "bearer", "pairing_url": "http://127.0.0.1:17832/lmsp/v1/pair" }, "openapi": "http://127.0.0.1:17832/lmsp/v1/openapi.json" } ``` | フィールド | 意味 | | --- | --- | | `protocol` | 常に `"lmsp"`。 | | `versions` | サービスが対応する LMSP のバージョン。`major.minor` 形式の文字列。 | | `service` | `id` は安定したサービス識別子、`name` は表示名、`version` はサービスソフトウェアのバージョン。 | | `interfaces` | 提供しているインターフェース。それぞれ `kind`(`lmsp`、`openai`、`ollama`)、`version`、ベース `url`、`transport` を持つ。`openai` の場合、`version` は提供している API ファミリーを示す(例: `responses+chat`)。 | | `capabilities` | 真偽値の機能フラグ。下に示す。 | | `authentication` | `type` は `bearer`。`pairing_url` は、クライアントがペアリングコードをトークンと交換する場所である([10.2 節](https://madeforagent.com/ja/lmsp/1.0/security.md#pairing))。 | | `openapi` | LMSP インターフェースを記述する OpenAPI ドキュメントの URL。 | 機能フラグ: | フラグ | 意味 | | --- | --- | | `streaming` | レスポンスをサーバー送信イベントとしてストリーミングできる。1.0 では `true` で**なければならない**。 | | `tasks` | 非同期タスクが利用できる。1.0 では `true` で**なければならない**。 | | `event_resume` | タスクのイベントストリームを購読・再開できる(`GET /lmsp/v1/tasks/{task_id}/events`)。 | | `conversations` | サービスがレスポンスを保存するため、`store` と `previous_response_id` が機能し、`GET /lmsp/v1/responses/{response_id}` が利用できる。 | | `model_management` | モデルを明示的にロード・アンロードできる。 | ## 3.4 クライアントの動作 {#client-behavior} クライアントは次のようにする。 - `versions` のうち、自分も対応している最も高いバージョンを選択する**べきである**。 - 自分が話せるものに応じて、`interfaces` から LMSP インターフェースまたは OpenAI インターフェースを選択する**べきである**。 - サービス記述が宣言している機能だけを使わ**なければならない**。フラグがなければ `false` を意味する。 - 認識できない拡張フィールドは無視し**なければならない**。 - サービス記述を永久にキャッシュせず、定期的に、またリクエストが失敗したときに再読み込みする**べきである**。 - 選択したサービスは、アドレスではなく `service.id` で記憶する**べきである**。アドレスは変わり得るためである。 --- # エンドポイント > LMSP 1.0 のエンドポイント一覧。メソッド、パス、要件レベル、用途に加え、パス、認証、任意エンドポイントに関する規約を示します。 Source: https://madeforagent.com/ja/lmsp/1.0/endpoints/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 4.1 エンドポイント一覧 {#table} 適合するサービスは、必須とされたすべてのエンドポイントを実装し**なければならない**。任意のエンドポイントは実装し**てもよく**、その場合は[サービス記述](https://madeforagent.com/ja/lmsp/1.0/discovery.md#service-description)の機能フラグで示す。 | メソッド | パス | 要件 | 用途 | | --- | --- | --- | --- | | GET | `/.well-known/lmsp.json` | 必須 | [サービスと機能の発見](https://madeforagent.com/ja/lmsp/1.0/discovery.md#service-description) | | GET | `/lmsp/v1/openapi.json` | 必須 | 機械可読なインターフェース仕様 | | GET | `/lmsp/v1/health` | 必須 | [稼働状態と準備状態の確認](https://madeforagent.com/ja/lmsp/1.0/common-rules.md#health) | | POST | `/lmsp/v1/pair` | 必須 | [ペアリングコードをベアラートークンと交換する](https://madeforagent.com/ja/lmsp/1.0/security.md#pairing) | | GET | `/lmsp/v1/models` | 必須 | [ローカルモデルを一覧する](https://madeforagent.com/ja/lmsp/1.0/models.md#list) | | GET | `/lmsp/v1/models/{model_id}` | 必須 | [モデルを 1 つ取得する](https://madeforagent.com/ja/lmsp/1.0/models.md#operations) | | POST | `/lmsp/v1/responses` | 必須 | [同期生成またはストリーミング生成](https://madeforagent.com/ja/lmsp/1.0/responses.md#request) | | GET | `/lmsp/v1/responses/{response_id}` | 任意 | [保存済みのレスポンスを取得する](https://madeforagent.com/ja/lmsp/1.0/responses.md#sync) | | POST | `/lmsp/v1/tasks` | 必須 | [非同期の生成タスクを作成する](https://madeforagent.com/ja/lmsp/1.0/tasks.md#create) | | GET | `/lmsp/v1/tasks/{task_id}` | 必須 | [非同期タスクを取得する](https://madeforagent.com/ja/lmsp/1.0/tasks.md#create) | | POST | `/lmsp/v1/tasks/{task_id}/cancel` | 必須 | [非同期タスクをキャンセルする](https://madeforagent.com/ja/lmsp/1.0/tasks.md#cancel) | | GET | `/lmsp/v1/tasks/{task_id}/events` | 任意 | [イベントストリームを購読または再開する](https://madeforagent.com/ja/lmsp/1.0/tasks.md#events) | | POST | `/lmsp/v1/models/{model_id}/load` | 任意 | [モデルを事前ロードする](https://madeforagent.com/ja/lmsp/1.0/models.md#operations) | | POST | `/lmsp/v1/models/{model_id}/unload` | 任意 | [モデルをアンロードする](https://madeforagent.com/ja/lmsp/1.0/models.md#operations) | `/v1` 配下の OpenAI 互換エンドポイントと、任意の Ollama 互換エンドポイントは[第 8 章](https://madeforagent.com/ja/lmsp/1.0/compatibility.md)に挙げている。 ## 4.2 規約 {#conventions} - **本文。** リクエストとレスポンスの本文は JSON(`application/json`)である。ただし、イベントストリーム(`text/event-stream`)とエラー(`application/problem+json`)は除く。 - **パスパラメーター。** `{model_id}` のように波括弧で囲んだ名前はプレースホルダーである。値はパーセントエンコードし**なければならない**。`qwen3:14b-q4_k_m` のようなモデル ID は `qwen3%3A14b-q4_k_m` として送る。 - **認証。** `/.well-known/lmsp.json`、`/lmsp/v1/openapi.json`、`/lmsp/v1/health`、`/lmsp/v1/pair` を除くすべてのエンドポイントは `Authorization: Bearer ` を必要とする。 - **任意のエンドポイント。** サービスが実装していない任意のエンドポイントは、エラーコード `unsupported_capability` を付けて `501 Not Implemented` で応答し**なければならない**。 - **バージョンとエラー。** リクエストはバージョンヘッダーを携えることができ、すべてのエラーは 1 つの形式を使う。どちらも[第 9 章](https://madeforagent.com/ja/lmsp/1.0/common-rules.md)で定義している。 - **契約。** [`/lmsp/1.0/openapi.json`](https://madeforagent.com/lmsp/1.0/openapi.json) で公開されている OpenAPI ドキュメントは、このページの機械可読な形式である。両者が異なる場合は、本文が規範である。 --- # モデルカタログ > LMSP サービスがローカルモデルを一覧する方法、モデルオブジェクトとその機能フラグ、モデルのライフサイクル状態、安定したモデル ID、カタログのリビジョンを説明します。 Source: https://madeforagent.com/ja/lmsp/1.0/models/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 5.1 モデルの一覧 {#list} ```http GET /lmsp/v1/models?capability=chat&ready_only=false Authorization: Bearer ``` | クエリパラメーター | 意味 | | --- | --- | | `capability` | 指定した機能を持つモデルだけを返す。例: `chat`、`embeddings`、`tools`。 | | `ready_only` | `true` のとき、`status` が `ready` のモデルだけを返す。既定は `false`。 | ```json { "object": "list", "catalog_revision": "42", "data": [ { "id": "qwen3:14b-q4_k_m", "object": "model", "display_name": "Qwen3 14B Q4_K_M", "owned_by": "local", "family": "qwen3", "revision": "sha256:8f27c1a4e9b05d36f2a7c8e1b4d90a63c5e2f7b8a1d4c9e60b3f5a7d2c8e1b04", "status": "ready", "capabilities": { "chat": true, "completion": true, "embeddings": false, "tools": true, "vision": false, "structured_output": true, "streaming": true }, "input_modalities": ["text"], "output_modalities": ["text"], "context_window": 32768, "max_output_tokens": 8192, "runtime": { "backend": "llama.cpp", "device": "metal", "load_on_demand": true } } ] } ``` クライアントがカタログ全体を何度も取得し直さずに済むよう、サービスは `ETag` ヘッダー、`catalog_revision` フィールド、またはその両方を返す**べきである**。 - `catalog_revision` は、カタログが変わるたびに変わる不透明な文字列である。 - `ETag` を送るサービスは、一致する `If-None-Match` に対して `304 Not Modified` で応答する**べきである**。 ## 5.2 モデルオブジェクト {#object} | フィールド | 意味 | | --- | --- | | `id` | 安定したモデル識別子。モデルを指定するすべてのリクエストで使う。 | | `object` | 常に `"model"`。 | | `display_name` | 人間が読める名前。ユーザーが変更できる。 | | `aliases` | 別名の任意のリスト。参考情報にすぎない。 | | `owned_by` | モデルの出どころ。このデバイスにインストールされたモデルでは `local`。 | | `family` | モデルファミリー。例: `qwen3`。 | | `revision` | モデル内容のダイジェスト。例: `sha256:8f27...`。 | | `status` | ライフサイクル状態([5.3 節](https://madeforagent.com/ja/lmsp/1.0/models.md#lifecycle))。 | | `capabilities` | 真偽値のフラグ: `chat`、`completion`、`embeddings`、`tools`、`vision`、`structured_output`、`streaming`。 | | `input_modalities`, `output_modalities` | モデルが読み書きするコンテンツの種類。1.0 で定義されているモダリティは `text` だけである。 | | `context_window` | コンテキストの最大トークン数。 | | `max_output_tokens` | 1 回のレスポンスの最大トークン数。 | | `runtime` | `backend`(例: `llama.cpp`)、`device`(例: `metal`)、`load_on_demand`。 | | `error` | `status` が `error` のときに存在する。problem オブジェクト([9.3 節](https://madeforagent.com/ja/lmsp/1.0/common-rules.md#errors))。 | モデルの `id` は安定してい**なければならない**。表示名と別名はユーザーが変更できるため、主キーとして使っ**てはならない**。 ## 5.3 モデルのライフサイクル {#lifecycle} ```text installed --> loading --> ready --> unloading --> installed | +-----> error ``` | 状態 | 意味 | | --- | --- | | `installed` | モデルは存在するが、ロードされていない。 | | `loading` | メモリの確保中、またはランタイムの初期化中。 | | `ready` | モデルがすぐにリクエストを処理できる。 | | `unloading` | リソースを解放中。 | | `error` | ロードに失敗した。モデルオブジェクトは構造化されたエラーを持つ**べきである**。 | `error` 状態のモデルは、新しいロードリクエストによって `loading` に戻る。 リクエストが `installed` 状態のモデルを指定し、その `runtime.load_on_demand` が `true` の場合、サービスはリクエストの一部としてそのモデルをロードする。モデルがすぐに使えず、オンデマンドでもロードされない場合、サービスは `409` と `model_not_ready` で応答する。ロードに失敗した場合のエラーは `model_load_failed` である。 ## 5.4 モデルの操作 {#operations} `GET /lmsp/v1/models/{model_id}` は 1 つのモデルオブジェクトを返す。未知の ID では `404` と `model_not_found` を返す。 2 つの任意エンドポイントで、ロードを明示的に制御できる。これらは `model_management` 機能が `true` の場合に利用でき、`models:manage` スコープを必要とする([10.3 節](https://madeforagent.com/ja/lmsp/1.0/security.md#scopes))。 | エンドポイント | 動作 | | --- | --- | | `POST /lmsp/v1/models/{model_id}/load` | モデルを事前ロードする。ロードが開始された場合はモデルオブジェクトを `202 Accepted` で返し、すでに `ready` の場合は `200` で返す。 | | `POST /lmsp/v1/models/{model_id}/unload` | モデルをアンロードする。モデルオブジェクトを返す。同じリクエストを繰り返しても害はない。 | どちらも `Idempotency-Key`([9.2 節](https://madeforagent.com/ja/lmsp/1.0/common-rules.md#idempotency))を受け付ける。 --- # レスポンスとストリーミング > LMSP のコア会話インターフェース。Responses 形式のリクエストと出力アイテム、同期生成とストリーミング生成、サーバー送信イベントの種類、イベント ID、再接続、ツール呼び出しを定めます。 Source: https://madeforagent.com/ja/lmsp/1.0/responses/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 6.1 リクエスト {#request} ```http POST /lmsp/v1/responses Authorization: Bearer Content-Type: application/json Idempotency-Key: 01J8M5T7Z2K4N6Q8S0V3X5B9DG LMSP-Version: 1.0 ``` ```json { "model": "qwen3:14b-q4_k_m", "input": [ { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "Describe a local model service in one sentence." } ] } ], "stream": false, "max_output_tokens": 512, "temperature": 0.7, "response_format": { "type": "text" }, "store": true } ``` コアプロトコルは、メッセージ文字列の単純なリストではなく、Responses 形式の `input` と `output` の構造を使う。これにより、1 つのリクエスト形式で次のものを運べる。 - 複数ターンのメッセージ - テキスト、画像、音声、ファイル - ツール呼び出しとツール結果 - 構造化出力 - 推論の要約 - マルチモーダル出力 LMSP 1.0 が定義するのはテキストコンテンツだけである。画像、音声、ファイルのコンテンツタイプは、将来のバージョンのために予約されている。 | フィールド | 必須 | 意味 | | --- | --- | --- | | `model` | はい | 使用するモデルの ID。 | | `input` | はい | 入力アイテムの配列。`message` アイテムは `role`(`system`、`user`、`assistant`)と、`input_text` などのパートからなる `content` 配列を持つ。ツールを使う場合は `function_call` アイテムと `function_call_output` アイテムが加わる([6.5 節](https://madeforagent.com/ja/lmsp/1.0/responses.md#tools))。 | | `stream` | いいえ | `true` にするとサーバー送信イベントを受け取る。既定は `false`。 | | `max_output_tokens` | いいえ | 生成するトークン数の上限。 | | `temperature` | いいえ | サンプリング温度。 | | `response_format` | いいえ | `{"type": "text"}`(既定)、または構造化出力のための `{"type": "json_schema", "json_schema": {"name": "...", "schema": {...}}}`。`structured_output` 機能が必要。 | | `tools`, `tool_choice` | いいえ | モデルが呼び出せる関数ツール([6.5 節](https://madeforagent.com/ja/lmsp/1.0/responses.md#tools))。`tools` 機能が必要。 | | `store` | いいえ | サービスにレスポンスを保存するよう求める。これにより、後で取得したり、`previous_response_id` として使ったりできる。既定は `false`。`conversations` 機能を持たないサービスは無視する。 | | `previous_response_id` | いいえ | 保存済みのレスポンスから続ける。`conversations` 機能が必要。 | | `extensions` | いいえ | 非公開の拡張([9.1 節](https://madeforagent.com/ja/lmsp/1.0/common-rules.md#versioning))。 | モデルが持たない機能を必要とするリクエストは、`400` と `unsupported_capability` で拒否される。コンテキストウィンドウに収まらないリクエストは、`400` と `context_length_exceeded` で拒否される。 会話を保存できるサービスは、`previous_response_id` にも対応し**てもよい**。そうでない場合、すべてのリクエストが完全なコンテキストを携え**なければならない**。サービスは、会話の状態を保持するために HTTP 接続に依存し**てはならない**。 ## 6.2 同期レスポンス {#sync} `stream` が `false` のとき、サービスは生成の完了後に、レスポンスオブジェクトを付けて `200 OK` を返す。 ```json { "id": "resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF", "object": "response", "created_at": 1794200000, "status": "completed", "model": "qwen3:14b-q4_k_m", "output": [ { "id": "msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE", "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "A local model service is a resident interface layer that discovers, manages and calls models on the user's own device." } ] } ], "usage": { "input_tokens": 28, "output_tokens": 31, "total_tokens": 59 }, "error": null, "incomplete_details": null } ``` | フィールド | 意味 | | --- | --- | | `id` | レスポンス識別子。例: `resp_...`。 | | `object` | 常に `"response"`。 | | `created_at` | 作成時刻。Unix エポックからの秒数。 | | `status` | `in_progress`、`completed`、`incomplete`、`failed`、`cancelled`。同期レスポンスは最終ステータスで返される。 | | `model` | レスポンスを生成したモデルの ID。 | | `output` | 出力アイテムの配列。 | | `usage` | トークン数: `input_tokens`、`output_tokens`、`total_tokens`。 | | `error` | `null`。`status` が `failed` の場合は problem オブジェクト([9.3 節](https://madeforagent.com/ja/lmsp/1.0/common-rules.md#errors))。 | | `incomplete_details` | `null`。`status` が `incomplete` の場合は、`max_output_tokens` などの `reason` を持つオブジェクト。 | 出力アイテムの種類: | アイテムの種類 | 形 | | --- | --- | | `message` | `id`、`role`(`assistant`)、`content`。`content` は、それぞれ `text` を持つ `output_text` パートの配列。 | | `function_call` | `id`、`call_id`、`name`、`arguments`。`arguments` は JSON エンコードされた文字列。 | | `reasoning` | `id` と `summary`。`summary` は、それぞれ `text` を持つ `summary_text` パートの配列。 | 生成に失敗した場合、同期リクエストへの応答は、レスポンスオブジェクトではなく、エラーステータスと problem ドキュメント([9.3 節](https://madeforagent.com/ja/lmsp/1.0/common-rules.md#errors))になる。`failed` ステータスが現れるのは、ストリーム、タスク、保存済みレスポンスである。 `GET /lmsp/v1/responses/{response_id}` は、`store: true` で保存されたレスポンスを返す。これは任意であり、`conversations` 機能が必要である。未知の ID では `404` と `response_not_found` を返す。 ## 6.3 ストリーミング {#streaming} ストリーミングするには、リクエストで `stream` を設定する。 ```json { "stream": true } ``` サービスは `Content-Type: text/event-stream` を付けて `200 OK` で応答し、意味を持つイベントを送る。各イベントは `id`、`event` 名、JSON の `data` オブジェクトを持つ。 ```text id: 1 event: response.created data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","status":"in_progress"} id: 2 event: response.output_item.added data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","output_index":0,"item":{"id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","type":"message","role":"assistant","content":[]}} id: 3 event: response.output_text.delta data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","item_id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","delta":"A local model "} id: 4 event: response.output_text.delta data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","item_id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","delta":"service is a resident interface layer."} id: 5 event: response.output_item.done data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","output_index":0,"item":{"id":"msg_01J8M6Q4W5C8H2K9P3S7Y1A6ZE","type":"message","role":"assistant","content":[{"type":"output_text","text":"A local model service is a resident interface layer."}]}} id: 6 event: response.completed data: {"response_id":"resp_01J8M6Q4W2T7N5R3V9X1Z8B0DF","status":"completed","usage":{"input_tokens":28,"output_tokens":12,"total_tokens":40}} ``` 少なくとも次のイベントが定義されている。 | イベント | 意味 | データフィールド | | --- | --- | --- | | `response.created` | レスポンスが作成された。 | `response_id`、`status`(`in_progress`) | | `response.output_item.added` | 新しい出力アイテムが始まった。 | `response_id`、`output_index`、`item` | | `response.output_text.delta` | message アイテムにテキストが追加された。 | `response_id`、`item_id`、`delta` | | `response.output_item.done` | 出力アイテムが完了した。 | `response_id`、`output_index`、`item` | | `response.completed` | レスポンスが完了した。最終イベント。 | `response_id`、`status`、`usage` | | `response.incomplete` | 生成が早めに止まった。たとえばトークン上限による。最終イベント。 | `response_id`、`status`、`incomplete_details`、`usage` | | `response.failed` | 生成に失敗した。最終イベント。 | `response_id`、`status`、`error` | | `response.cancelled` | レスポンスがキャンセルされた。最終イベント。 | `response_id`、`status` | | `error` | 1 つのレスポンスに結び付かない問題。この後ストリームは継続できない。 | `error`(problem オブジェクト) | サービスは、最終イベントまたは `error` イベントの後にストリームを閉じる。 サービスは、たとえばツール呼び出しの引数、画像、音声、構造化 JSON の差分などのために、追加のイベントを送っ**てもよい**。クライアントは、知らないイベントタイプを無視し**なければならない**。 ストリーミングリクエストの接続をクライアントが閉じた場合、サービスは生成を停止する**べきである**。 ## 6.4 イベント ID と再接続 {#reconnection} イベントの `id` は、レスポンス内で厳密に増加する、1 から始まる整数のシーケンス番号で**なければならない**。 接続が切れた後、クライアントは、受信した最後の ID を送ることで再開する。ヘッダーで送る方法: ```http Last-Event-ID: 2 ``` クエリパラメーターで送る方法: ```http GET /lmsp/v1/tasks/{task_id}/events?after=2 ``` どちらも、タスクのイベントエンドポイント([7.4 節](https://madeforagent.com/ja/lmsp/1.0/tasks.md#events))に送る。サービスは、シーケンス番号が 2 より大きいイベントだけを再送する。両方が指定された場合は `after` が優先される。 要求されたイベントが保持期間より古い場合、サービスは `410 Gone` とエラー `event_history_expired` で応答する。クライアントは代わりに、`GET /lmsp/v1/tasks/{task_id}` で最終結果を取得する。 `POST /lmsp/v1/responses` から直接ストリーミングされたレスポンスは、その HTTP 接続に属しており、再開できない。再開可能な出力が必要なクライアントは、[タスク](https://madeforagent.com/ja/lmsp/1.0/tasks.md)を作成してそのイベントを追う**べきである**。 ## 6.5 ツール呼び出し {#tools} LMSP 1.0 は、クライアントが実行する関数ツールを定義する。サービス自身はツールを実行しない。`tools:execute` スコープは、ツールを実行する拡張のために予約されている([10.3 節](https://madeforagent.com/ja/lmsp/1.0/security.md#scopes))。 まず、クライアントがツールを列挙する。各エントリーは、`function` に設定された `type`、`name`、`description`、そして JSON Schema オブジェクトである `parameters` を持つ。`tool_choice` は `auto`(既定)、`none`、`required`、または `{"type": "function", "name": "get_weather"}` である。 ```json { "model": "qwen3:14b-q4_k_m", "input": [ { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "What is the weather in Hangzhou?" } ] } ], "tools": [ { "type": "function", "name": "get_weather", "description": "Get the current weather for a city.", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] } } ], "tool_choice": "auto" } ``` モデルがツールを呼び出すと決めると、レスポンスには `function_call` アイテムが含まれる。その `arguments` は JSON エンコードされた文字列である。ストリームでは、アイテムは `response.output_item.done` で完全な形で届く。 ```json { "id": "resp_01J8M7B3N6Q9T2V5X8Z1C4D7FH", "object": "response", "created_at": 1794200050, "status": "completed", "model": "qwen3:14b-q4_k_m", "output": [ { "id": "fc_01J8M7B3N8R1S4W7Y0A3E6G9JK", "type": "function_call", "call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM", "name": "get_weather", "arguments": "{\"city\":\"Hangzhou\"}" } ], "usage": { "input_tokens": 74, "output_tokens": 18, "total_tokens": 92 }, "error": null, "incomplete_details": null } ``` クライアントはツールを実行し、完全なコンテキストを携えた新しいリクエストを送る。そこには、以前の入力アイテム、受け取ったままの `function_call` アイテム、同じ `call_id` を持つ `function_call_output` アイテムが含まれる。その `output` は文字列で、通常は JSON エンコードされる。 ```json { "type": "function_call_output", "call_id": "call_01J8M7B3N9T2V5X8Z1C4D7F0HM", "output": "{\"temperature_c\":17,\"condition\":\"light rain\"}" } ``` --- # 非同期タスク > LMSP が長時間の生成を永続化されたタスクとして実行する方法。タスクの作成、状態遷移、冪等なキャンセル、イベントストリーム、未完了タスクの再起動ポリシーを定めます。 Source: https://madeforagent.com/ja/lmsp/1.0/tasks/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 7.1 タスクの作成 {#create} クライアントは、実行したいレスポンスリクエストをタスクエンベロープで包んで POST することでタスクを作成する。`Idempotency-Key` ヘッダーを付けることを推奨する([9.2 節](https://madeforagent.com/ja/lmsp/1.0/common-rules.md#idempotency))。 ```http POST /lmsp/v1/tasks Authorization: Bearer Idempotency-Key: 01J8M7C4Y3N5P8R2T6V9X1Z4BH ``` ```json { "type": "response", "input": { "model": "qwen3:14b-q4_k_m", "input": [ { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "Write a long report." } ] } ] }, "on_restart": "fail" } ``` | フィールド | 必須 | 意味 | | --- | --- | --- | | `type` | はい | `response`。1.0 で唯一のタスクタイプ。 | | `input` | はい | `POST /lmsp/v1/responses` とまったく同じレスポンスリクエスト。その `stream` フィールドは無視される。 | | `on_restart` | いいえ | `fail`(既定)、`retry`、`resume`。[7.5 節](https://madeforagent.com/ja/lmsp/1.0/tasks.md#restart)を参照。 | サービスは、応答する前にタスクを永続化し**なければならない**。その後、次を返す。 ```http HTTP/1.1 202 Accepted ``` ```json { "id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC", "object": "task", "type": "response", "status": "queued", "created_at": 1794200100, "updated_at": 1794200100, "response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW", "events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events", "on_restart": "fail", "response": null, "error": null } ``` | タスクのフィールド | 意味 | | --- | --- | | `id` | タスク識別子。例: `task_...`。 | | `object` | 常に `"task"`。 | | `type` | `response`。 | | `status` | 現在の状態([7.2 節](https://madeforagent.com/ja/lmsp/1.0/tasks.md#states))。 | | `created_at`, `updated_at` | Unix エポックからの秒数で表した時刻。 | | `response_id` | タスクが生成するレスポンスの ID。タスクの作成時に割り当てられる。 | | `events_url` | イベントストリームのパス。`event_resume` 機能が `true` のときに存在する。 | | `on_restart` | タスクの再起動ポリシー。 | | `response` | `status` が `succeeded` になるとレスポンスオブジェクト、それ以外は `null`。 | | `error` | `status` が `failed` のときは problem オブジェクト([9.3 節](https://madeforagent.com/ja/lmsp/1.0/common-rules.md#errors))、それ以外は `null`。 | `GET /lmsp/v1/tasks/{task_id}` はタスクオブジェクトを返す。イベントストリームを使わないクライアントは、このエンドポイントをポーリングする。未知の ID では `404` と `task_not_found` を返す。完了したタスクは次のようになる。 ```json { "id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC", "object": "task", "type": "response", "status": "succeeded", "created_at": 1794200100, "updated_at": 1794200162, "response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW", "events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events", "on_restart": "fail", "response": { "id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW", "object": "response", "created_at": 1794200100, "status": "completed", "model": "qwen3:14b-q4_k_m", "output": [ { "id": "msg_01J8M8R6A2C5E8G1J4M7P0S3VY", "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "Local model services make on-device inference a shared, discoverable utility..." } ] } ], "usage": { "input_tokens": 12, "output_tokens": 1480, "total_tokens": 1492 }, "error": null, "incomplete_details": null }, "error": null } ``` ## 7.2 状態遷移 {#states} ```text queued --> loading --> running --> succeeded | | | | | +--> failed | | +--> cancelling --> cancelled | +--------------> failed +--------------------------> cancelled ``` | 状態 | 意味 | 次の状態 | | --- | --- | --- | | `queued` | 受理され永続化済みで、開始を待っている。 | `loading`、`cancelled`、`failed` | | `loading` | モデルをロード中。 | `running`、`cancelling`、`failed` | | `running` | モデルが生成中。 | `succeeded`、`failed`、`cancelling` | | `cancelling` | キャンセルが要求されたが、バックエンドはまだ停止していない。 | `cancelled` | | `succeeded` | 終端。レスポンスは完了している。 | なし | | `failed` | 終端。 | なし | | `cancelled` | 終端。 | なし | 終端状態は `succeeded`、`failed`、`cancelled` である。終端状態のタスクは、再び変化し**てはならない**。レスポンスが `incomplete` で終わるタスク(たとえばトークン上限による)は `succeeded` である。 すべての遷移はアトミックであるため、タスクがキャンセルと完了の両方になることはない。先にコミットされた遷移が有効になる。 ## 7.3 キャンセル {#cancel} ```http POST /lmsp/v1/tasks/{task_id}/cancel ``` サービスは `200 OK` と現在のタスクオブジェクトを返す。`queued` のタスクは直ちに `cancelled` になる。`loading` または `running` のタスクは、バックエンドが停止するまで `cancelling` となり、その後 `cancelled` になる。 キャンセルは冪等で**なければならない**。すでに `cancelling` または `cancelled` のタスクをキャンセルしても、現在のタスクが返され、新しいエラーは生じない。 すでに `succeeded` または `failed` のタスクをキャンセルすると、`409` と `task_not_cancelable` で応答する。未知の ID では `404` と `task_not_found` を返す。 ## 7.4 イベントストリーム {#events} ```http GET /lmsp/v1/tasks/{task_id}/events Accept: text/event-stream ``` このエンドポイントは任意である。`event_resume` 機能が `true` のときに利用できる。 - イベントは、タスクが生成するレスポンスについての、[6.3 節](https://madeforagent.com/ja/lmsp/1.0/responses.md#streaming)のレスポンスイベントである。番号付けと再開は[6.4 節](https://madeforagent.com/ja/lmsp/1.0/responses.md#reconnection)に従う。 - ストリームは、タスクの受理時に記録される `response.created` で始まる。結果に対応する最終イベントを 1 つ送って終わる。`response.completed`、`response.incomplete`、`response.failed`、`response.cancelled` のいずれかである。 - 遅れて接続したクライアントは、まず取り逃したイベントを受け取り、その後でライブのイベントを受け取る。サービスは最終イベントの後にストリームを閉じる。 - イベントは保持期間の間、保存される([11.2 節](https://madeforagent.com/ja/lmsp/1.0/persistence.md#rules))。それより古いイベントを要求すると、`410 Gone` と `event_history_expired` が返る。 ## 7.5 再起動時の動作 {#restart} `on_restart` は、サービスの再起動時に未完了のタスクをどう扱うかを示す。 | 値 | 動作 | | --- | --- | | `fail` | 既定。サービスは未完了のタスクを `failed` とし、エラーコードは `server_restarted` とする。 | | `retry` | サービスはタスクを最初からもう一度実行する。 | | `resume` | サービスはスナップショットから続行する。これが許されるのは、バックエンドが再開可能なスナップショットに明示的に対応している場合だけである。そうでない場合、作成リクエストは `400` と `unsupported_capability` で拒否される。 | サービスの再起動時に `cancelling` だったタスクは、ポリシーにかかわらず、キャンセルを完了して `cancelled` になる。 `retry` を既定にすることは推奨しない。タスクがすでにツールを実行していたり、外部への副作用を生じていたりする可能性があるためである。`retry` を選ぶクライアントは、`Idempotency-Key` と組み合わせ、かつツール呼び出しを冪等にし**なければならない**。 --- # OpenAI と Ollama の互換性 > LMSP サービスが、1 つの実行コアの上に置いた薄いデータ変換層によって、既存の OpenAI クライアントと Ollama クライアントとの互換性を保つ方法と、その互換性の限界を説明します。 Source: https://madeforagent.com/ja/lmsp/1.0/compatibility/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 8.1 OpenAI 互換エンドポイント {#openai} サービスは、LMSP インターフェースと並べて OpenAI 互換インターフェースを提供し、`interfaces` に `kind` を `openai` として宣言する**べきである**。既存のクライアントをカバーするため、そうするサービスは、`/v1` プレフィックスの下に少なくとも次のエンドポイントを実装し**なければならない**。 | エンドポイント | 備考 | | --- | --- | | `GET /v1/models` | LMSP カタログのモデルを一覧する。各 `id` は LMSP のモデル ID である。 | | `POST /v1/chat/completions` | Chat Completions。ストリーミングあり・なしの両方。 | | `POST /v1/responses` | OpenAI Responses の形式。 | | `POST /v1/embeddings` | LMSP 1.0 のコアインターフェースには embeddings エンドポイントがない。アダプターが、`capabilities.embeddings` が `true` のモデル向けに、このエンドポイントを直接提供する。 | これらのエンドポイントは、LMSP インターフェースと同じベアラートークンを受け付ける。OpenAI プロトコルを話すクライアントは、LMSP トークンを API キーとして設定する。 最初の実装は、`/v1/models` と `/v1/chat/completions` から始めてよい([第 12 章](https://madeforagent.com/ja/lmsp/1.0/mvp.md))。 ## 8.2 Ollama 互換エンドポイント {#ollama} サービスは、Ollama 向けに書かれたクライアントも動作するよう、次のエンドポイントも提供する**べきである**。 | エンドポイント | 備考 | | --- | --- | | `GET /api/tags` | LMSP カタログのモデルを一覧する。 | | `POST /api/chat` | チャット。 | | `POST /api/generate` | プレーンテキスト生成。 | これらを提供するサービスは、`kind` を `ollama` としたインターフェースを宣言する。 ## 8.3 1 つの実行コア {#core} 互換層は、データの変換だけを行わ**なければならない**。モデル管理のロジックを第 2 の系統として作っ**てはならない**。互換インターフェース上のリクエストは LMSP レスポンスにマッピングされ、ネイティブのリクエストと同じコアとアダプターの上で実行され、同じモデルカタログと認証を使う。 ```text OpenAI Chat Completions | リクエストのマッピング v LMSP Response | バックエンドへの適応 v llama.cpp / Ollama / vLLM / その他のランタイム ``` `/v1` と `/api` のエンドポイントでは、サービスは、変更されていないクライアントが解析できるよう、対応する API が定義する形式でエラーを報告する**べきである**。LMSP インターフェースは常に `application/problem+json` を使う。 ## 8.4 境界 {#boundaries} 互換性には限界がある。それをはっきり述べておく価値がある。 - 既存の OpenAI 互換クライアントに必要なのは、通常 `base_url` の変更だけである。LMSP の発見が自動的に使えるようになるわけではない。 - サービスを自動的に発見し、詳細な機能を読み取り、イベントストリームを再開し、非同期タスクを管理できるのは、LMSP に対応したクライアントだけである。 - 設定できず、公式のクラウドアドレスだけを受け付けるクライアントを動作させられるサーバー側プロトコルは存在しない。 --- # 共通ルール > すべての LMSP エンドポイントに共通するルール。バージョンヘッダーと拡張、冪等キー、エラーコードを伴う problem+json のエラー形式、ヘルスチェックを定めます。 Source: https://madeforagent.com/ja/lmsp/1.0/common-rules/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 9.1 バージョンと拡張 {#versioning} LMSP のリクエストは、次のヘッダーを携え**てもよい**。 ```http LMSP-Version: 1.0 LMSP-Client: example-client/1.2.0 ``` `LMSP-Version` は、クライアントが対象とするプロトコルのバージョンである。`LMSP-Client` は、診断用にクライアントソフトウェアを `name/version` の形式で識別する。サービスは、これに基づいてプロトコルの動作を変更し**てはならない**。 規則: - パス中の `v1` はメジャーバージョンを表す。 - 未知のメジャーバージョンを指定するリクエストには、`426 Upgrade Required` とエラーコード `unsupported_protocol_version` で応答する。 - 新しい任意フィールドは、1.x の互換性を壊し**てはならない**。 - サービスとクライアントは、知らないフィールドを無視し**なければならない**。 - 非公開の拡張は `extensions` オブジェクトに入れ、逆ドメイン表記で名前を付ける。 ```json { "extensions": { "com.example.cache": { "enabled": true } } } ``` ## 9.2 冪等性 {#idempotency} レスポンスの作成、タスクの作成、モデルのロードのように、何かを作成するリクエストは、冪等キーを受け付ける。 ```http Idempotency-Key: ``` サービスは、キーを、リクエスト本文のダイジェストと結果と併せて保存する。 - 同じキーで同じ本文なら、元の結果を返す。 - 同じキーで異なる本文なら、`409 Conflict` と `idempotency_conflict` で応答する。 キーのスコープは、クライアントとエンドポイントである。キーは、ULID や UUID のような、最大 255 文字の一意な値である**べきである**。サービスは、キーを少なくとも 24 時間保持する**べきである**。 サービスは、ストリーミングリクエストではキーを無視し**てもよい**。安全な再試行が必要なクライアントは、[タスク](https://madeforagent.com/ja/lmsp/1.0/tasks.md)を使う。 ## 9.3 エラー {#errors} LMSP インターフェース上のすべてのエラーは `application/problem+json`([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457))を使う。 ```json { "type": "urn:lmsp:error:model-not-found", "title": "Model not found", "status": 404, "code": "model_not_found", "detail": "No local model with id 'qwen3:14b-q4_k_m'", "request_id": "req_01J8M9S6Y4W8C3P5R1V7Z2A0DH", "retryable": false } ``` | フィールド | 必須 | 意味 | | --- | --- | --- | | `type` | はい | エラーの種類を識別する URI。LMSP は `urn:lmsp:error:` に、アンダースコアをハイフンに置き換えたコードを続けたものを使う。 | | `title` | はい | 人間が読める短い要約。 | | `status` | はい | HTTP ステータスコード。 | | `code` | はい | 下の一覧にある、機械可読なエラーコード。 | | `detail` | いいえ | このエラーの発生についての、人間が読める説明。シークレット、プロンプト、生成内容を含ん**ではならない**。 | | `request_id` | いいえ | リクエストの識別子。サービスのログとの突き合わせに使う。 | | `retryable` | はい | 後でリクエストを繰り返せば成功し得るかどうか。 | 一般的なエラーコード: | コード | HTTP ステータス | 再試行可 | 意味 | | --- | --- | --- | --- | | `invalid_request` | 400 | いいえ | リクエストの形式が正しくないか、パラメーターが無効である。 | | `unauthorized` | 401 | いいえ | トークンがない、無効、期限切れ、または失効している。あるいはペアリングコードが誤っている。 | | `forbidden` | 403 | いいえ | トークンに必要なスコープがない、または `Origin` か `Host` の検査に失敗した。 | | `model_not_found` | 404 | いいえ | この ID のローカルモデルがない。 | | `model_not_ready` | 409 | はい | モデルがロードされておらず、オンデマンドでもロードされない。 | | `model_load_failed` | 500 | はい | モデルのロードに失敗した。 | | `context_length_exceeded` | 400 | いいえ | 入力がモデルのコンテキストウィンドウに収まらない。 | | `unsupported_capability` | 400 または 501 | いいえ | モデルまたはサービスが、リクエストに必要なものに対応していない。未実装の任意エンドポイントは 501 で応答する。 | | `unsupported_protocol_version` | 426 | いいえ | サービスが、要求されたメジャーバージョンに対応していない。 | | `response_not_found` | 404 | いいえ | この ID の保存済みレスポンスがない。 | | `task_not_found` | 404 | いいえ | この ID のタスクがない。 | | `task_not_cancelable` | 409 | いいえ | タスクはすでに成功または失敗している。 | | `idempotency_conflict` | 409 | いいえ | その冪等キーは、すでに別のリクエスト本文で使われている。 | | `event_history_expired` | 410 | いいえ | 要求されたイベントが保持期間より古い。 | | `rate_limited` | 429 | はい | リクエストが多すぎる。 | | `resource_exhausted` | 503 | はい | デバイスのメモリまたはその他のリソースが不足している。 | | `backend_unavailable` | 503 | はい | モデルランタイムに到達できない。 | | `server_restarted` | 503 | はい | タスクが未完了の間にサービスが再起動した。 | | `internal_error` | 500 | 場合による | サービス内部での予期しない障害。 | `retryable` フィールドが正である。上の列は通常の値を示している。サービスは、さらにコードを定義し**てもよい**。クライアントは、知らないコードを、その HTTP ステータスと `retryable` フィールドによって処理し**なければならない**。 ## 9.4 ヘルスチェック {#health} ```http GET /lmsp/v1/health ``` ```json { "status": "ready", "service_id": "svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2", "uptime_seconds": 86420, "loaded_models": 1, "active_tasks": 2, "storage": "ok" } ``` | フィールド | 意味 | | --- | --- | | `status` | `starting`、`ready`、`degraded`、`stopping`。 | | `service_id` | サービス記述にあるサービス ID。 | | `uptime_seconds` | サービスの起動からの秒数。 | | `loaded_models` | `ready` 状態のモデルの数。 | | `active_tasks` | `queued`、`loading`、`running`、`cancelling` のタスクの数。 | | `storage` | `ok` または `error`。 | `status` が `ready` または `degraded` のときは `200 OK`、それ以外は `503 Service Unavailable` を返す。 ヘルスエンドポイントが返すのは、運用上のステータスだけである。モデルのパス、プロンプト、アクセストークン、その他の機微な情報を返し**てはならない**。 --- # ローカルセキュリティ > LMSP サービスのセキュリティ要件。ループバックへのバインド、ペアリングとベアラートークン、スコープ、悪意ある Web ページに対する Origin と Host の検査、LAN モード、ログの規則を定めます。 Source: https://madeforagent.com/ja/lmsp/1.0/security/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 10.1 基本方針 {#baseline} localhost だけで待ち受ければ絶対に安全というわけではない。悪意ある Web ページが、ローカルのポートへの到達を試みる可能性があるためである。そこで LMSP は、複数の対策を組み合わせる。 - サービスは、既定でループバックアドレスにバインドし**なければならない**。LAN モードが有効でない限り([10.5 節](https://madeforagent.com/ja/lmsp/1.0/security.md#lan))、`0.0.0.0` やその他のループバック以外のアドレスで待ち受け**てはならない**。 - サービスは、初回の実行時に、エントロピーの高いランダムなサービスシークレットを生成し、組み込みの既定値ではなくそれを使ってトークンを発行・検証する**べきである**。 - コアインターフェースへのすべてのリクエストは、[4.2 節](https://madeforagent.com/ja/lmsp/1.0/endpoints.md#conventions)に挙げたブートストラップ用エンドポイントを除き、有効なベアラートークンを携え**なければならない**。ループバックから来たというだけでは、決して十分ではない。 ## 10.2 ペアリングとトークン {#pairing} クライアントは、ローカルのペアリングコードによって、自分専用のベアラートークンを得る。ユーザーは、サービスが提供する任意の方法でペアリングコードを表示させ、クライアントがそれを送信する。 ```http POST /lmsp/v1/pair Content-Type: application/json ``` ```json { "client_name": "example-client", "pairing_code": "K7QD-4M2X", "scopes": ["inference"] } ``` | リクエストフィールド | 意味 | | --- | --- | | `client_name` | クライアントの表示名。ユーザーに表示される。 | | `pairing_code` | ユーザーがサービスから取得したコード。 | | `scopes` | 要求するスコープの任意のリスト。既定は `["inference"]`。 | サービスはトークンで応答する。`token_type` は常に `Bearer` である。 ```json { "client_id": "cli_01J8MAT3W6X9B2D5F8H1K4N7QZ", "access_token": "lmsp_example_3f9a1c52e7d84b60", "token_type": "Bearer", "scopes": ["inference"] } ``` その後、クライアントはすべてのリクエストで `Authorization: Bearer ` を送る。 - ペアリングコードは、使い捨てで**なければならず**、5 分以内に期限切れとなる**べきである**。少なくとも 40 ビットのエントロピーを持つ**べきである**。サービスは、失敗した試行にレート制限をかけ**なければならない**。誤った、または期限切れのコードには、`401` と `unauthorized` で応答する。 - トークンはクライアントごとに独立しているため、ユーザーは、他のクライアントに影響を与えずに 1 つのクライアントを失効させられる。サービスは、ユーザーがペアリング済みクライアントを一覧し、失効させられるようにする**べきである**。 - クライアントは、トークンを OS のキーチェーンや資格情報マネージャーに保存する**べきである**。サービスは、各トークンのハッシュだけを保存する**べきである**。 - トークンがない、無効、または失効している場合は、`401`、`unauthorized`、`WWW-Authenticate: Bearer` ヘッダーで応答する。有効なトークンであっても、エンドポイントが必要とするスコープを持たない場合は、`403` と `forbidden` で応答する。 ## 10.3 スコープ {#scopes} 権限は、別々のスコープに分けられている。モデルのダウンロード、モデルの削除、ツールの実行が、inference スコープに便乗することは決してない。 | スコープ | 許可すること | | --- | --- | | `inference` | モデルカタログの読み取り、レスポンスとタスクの作成、クライアント自身のタスクの読み取りとキャンセル。 | | `models:manage` | モデルのロードとアンロード。 | | `models:install` | 予約済み。モデルのダウンロードと削除。 | | `tools:execute` | 予約済み。サービス側でのツールの実行。 | タスクと保存済みレスポンスは、それを作成したクライアントに属する。サービスは、他のクライアントからのリクエストに対し、それらが存在しないかのように応答する**べきである**(`task_not_found` または `response_not_found`)。 ## 10.4 ブラウザーとネットワークの保護 {#browser} - サービスは、既定でクロスオリジンのブラウザーリクエストを拒否し、明示的に設定された `Origin` の値だけを許可し**なければならない**。それ以外の `Origin` を持つリクエストには、`403` と `forbidden` で応答する。 - オリジンが許可されている場合、サービスはその CORS プリフライトリクエストに応答し、`Access-Control-Allow-Origin` に、`*` ではなく、そのオリジンそのものを入れて送る。 - サービスは、DNS リバインディングを防ぐために、`Host` ヘッダーを検証し**なければならない**。受け付けるのは、サービスが提供するホスト名、すなわちループバックの名前とアドレス、および LAN モード用に設定された名前だけである。それ以外の値には、`403` と `forbidden` で応答する。 ## 10.5 LAN モード {#lan} LAN モードとは、ループバックアドレスの外でも待ち受けることを指す。 - サービスは、ユーザーが明示的にオンにしない限り、LAN モードを有効にし**てはならない**。 - LAN モードでは、サービスは認証を要求し**なければならず**、TLS も有効にする**べきである**。クライアントは、ユーザーが明示的に許可しない限り、ループバック以外のアドレスにプレーンな HTTP でトークンを送る**べきではない**。 - mDNS アドバタイズが許されるのは、LAN モードで、かつユーザーが LAN 発見もオンにしている場合だけである([3.2 節](https://madeforagent.com/ja/lmsp/1.0/discovery.md#mdns))。 ## 10.6 ログとプライバシー {#logging} - モデルのパス、ログ、プロンプト、生成内容は、mDNS アドバタイズに含まれ**てはならない**。 - 既定では、サービスはメタデータ(時刻、エンドポイント、ステータスコード、ID、トークン数、所要時間)だけを記録する**べきである**。完全なプロンプトや生成内容は記録する**べきではない**。 - ヘルスレスポンスとエラーの詳細は、モデルのパス、プロンプト、アクセストークン、その他の機微な情報を含ん**ではならない**。 --- # 永続化 > LMSP サービスが状態を保存する方法。組み込みの SQLite データベース、保持すべきテーブル、タスクとイベントのトランザクション規則、保持期間、起動時の復旧を定めます。 Source: https://madeforagent.com/ja/lmsp/1.0/persistence/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 11.1 ストレージ {#storage} サービスは、状態を組み込みの [SQLite](https://www.sqlite.org/) データベースに保存する**べきである**。推奨するテーブルの構成は次のとおりである。 | テーブル | 内容 | | --- | --- | | `service_config` | サービスの ID と設定。 | | `clients` | ペアリング済みクライアント、トークンのハッシュ、スコープ。 | | `models` | モデルのメタデータと内容のダイジェスト。 | | `responses` | 保存済みのレスポンス。 | | `tasks` | 非同期タスク。 | | `task_events` | 各タスクのイベントログ。シーケンス番号付き。 | | `idempotency_keys` | 冪等キー、リクエストのダイジェスト、結果。 | モデルファイルはデータベースの外に置か**なければならない**。データベースが保持するのは、メタデータと内容のダイジェストだけである。 テーブル名とレイアウトは実装の選択である。クライアントが観察でき、相互運用のために必要なのは、次の 2 つの節にある動作である。 ## 11.2 規則 {#rules} サービスは、どのようなストレージを使うにしても、次のことを行う。 - `202 Accepted` を返す前に、タスクを永続化し**なければならない**。 - イベントのシーケンス番号とタスクの状態を、同じトランザクションで更新し**なければならない**。 - キャンセルの遷移をアトミックにし、タスクがキャンセルされた後に完了するという競合を避け**なければならない**。 - 完了したタスクとイベントを、設定可能な保持期間の間、保持する**べきである**。 - ログの整理の際に、未完了のタスクを削除し**てはならない**。 - 起動時に、残っている `queued`、`loading`、`running`、`cancelling` のタスクを走査し、それぞれの再起動ポリシーを適用し**なければならない**([7.5 節](https://madeforagent.com/ja/lmsp/1.0/tasks.md#restart))。 ## 11.3 起動時の復旧 {#recovery} 次の表は、残っていたタスクが、`on_restart` ポリシーに応じて移される状態を示す。 | 起動時に見つかった状態 | `fail` | `retry` | `resume` | | --- | --- | --- | --- | | `queued` | `failed` | `queued` | `queued` | | `loading` | `failed` | `queued` | `queued` | | `running` | `failed` | `queued` | `running`(スナップショットから続行) | | `cancelling` | `cancelled` | `cancelled` | `cancelled` | `failed` に移されたタスクには、エラーコード `server_restarted` と `response.failed` イベントが付く。`cancelled` に移されたタスクには、`response.cancelled` イベントが付く。新しい状態とそのイベントは、1 つのトランザクションで書き込まれる([11.2 節](https://madeforagent.com/ja/lmsp/1.0/persistence.md#rules))。 --- # 最小実用範囲 > 最初の LMSP 実装に向けて推奨する範囲。実用的な閉ループを成す 10 項目と、仕様のうち後のリリースに回せる部分を示します。 Source: https://madeforagent.com/ja/lmsp/1.0/mvp/ Language: ja Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 12.1 最初のバージョン {#scope} 最初のバージョンで、すべてのマルチモーダル機能、モデルのダウンロード、リアルタイム音声を実装する必要はない。次のものを提供すれば、実用的な閉ループになる。 1. `/.well-known/lmsp.json` によるサービス発見。 2. `_lmsp._tcp.local.` の LAN アドバタイズ(任意)。 3. `GET /lmsp/v1/models`。 4. `POST /lmsp/v1/responses`。同期と SSE ストリームの両方。 5. 非同期タスク。作成、照会、キャンセルを、SQLite による永続化とともに。 6. `Last-Event-ID` または `after` による、切断されたストリームの再開。 7. ベアラートークン、ローカルペアリング、`Origin` と `Host` の保護。 8. OpenAI 互換の `/v1/models` と `/v1/chat/completions` エンドポイント。 9. OpenAPI 3.2 の契約ファイルと、言語横断の適合性テスト。 10. 少なくとも 1 つの実際のバックエンドと、サービスの再起動後にタスクが正しく復旧することの確認。 この設計の要点は、LMSP が機能一式を提供し、OpenAI インターフェースが既製の互換性を提供し、mDNS とサービス記述が自動発見を提供し、SQLite のタスクモデルがローカルでの永続化を提供することである。 ## 12.2 後回しにできること {#later} 上のリストにないものは、後のリリースで追加できる。これには、任意のエンドポイント(保存済みレスポンス、モデルのロードとアンロード)、OpenAI の `/v1/responses` と `/v1/embeddings` エンドポイント、Ollama 互換 API、テキスト以外のすべてのコンテンツタイプが含まれる。 この範囲は、使える最初のリリースに至る道筋であり、適合性プロファイルではない。LMSP 1.0 への対応を謳うサービスは、やはり[第 4 章](https://madeforagent.com/ja/lmsp/1.0/endpoints.md)の必須エンドポイントをすべて実装し、他のページに記述された必須の動作も実装する。