# Local Model Service Protocol (LMSP) 1.0, 草案 > Made for Agent 發布開放、可被機器讀取的協定,讓 AI Agent、模型和工具能夠互相發現、互相通訊。第一個協定是 LMSP(本地模型服務協定)。 Source: https://madeforagent.com/zh-tw/llms-full.txt Updated: 2026-11-08 --- # 本地模型服務協定(LMSP) > LMSP 是一套開放協定,用於發現和呼叫執行在使用者自己裝置上的語言模型,涵蓋服務發現、模型目錄、串流回應、持久化的非同步任務,以及 OpenAI 相容介面。 Source: https://madeforagent.com/zh-tw/lmsp/ Language: zh-TW Updated: 2026-11-08 ## LMSP 解決什麼問題 {#what} LMSP(Local Model Service Protocol,本地模型服務協定)是一套開放協定,面向在使用者自己裝置或區域網路中執行語言模型的軟體,以及使用這些軟體的客戶端。 常駐的本地服務會在約定位址上描述自己並列出模型,用 JSON 或伺服器傳送事件(SSE)回應生成請求,並把長時間的生成作為持久化任務來執行:任務可以取消,客戶端斷線後也可以重新接上。同一個服務還相容 OpenAI API,因此今天已有的客戶端可以繼續使用。 LMSP 並不取代 OpenAI API、Ollama 或 MCP,而是把它們的優點組合成一個適合本地常駐服務的協定層。 ## 概覽 {#glance} | 主題 | 概要 | | --- | --- | | 預設位址 | `http://127.0.0.1:17832` | | 服務描述 | `GET /.well-known/lmsp.json` | | 核心介面 | `/lmsp/v1`,基於 HTTP 的 JSON,串流傳輸使用伺服器傳送事件(SSE) | | 相容介面 | 面向 OpenAI 客戶端的 `/v1`,面向 Ollama 客戶端的可選 `/api` | | 服務發現 | 手動設定、探測本機約定位址、可選的 mDNS 與 DNS-SD(`_lmsp._tcp.local.`) | | 驗證 | 透過本機配對取得的 Bearer Token | | 持久化 | 嵌入式 SQLite,任務在服務重啟後仍然保留 | | 合約 | OpenAPI 3.2 | ## 快速試用 {#try} 如果你的機器上正執行著 LMSP 服務,完整流程如下。請先按[第 10.2 節](https://madeforagent.com/zh-tw/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"}]}]}' ``` ## 與其他協定的關係 {#related} | 協定 | 覆蓋範圍 | 與 LMSP 的關係 | | --- | --- | --- | | OpenAI API | 雲端託管的模型,Responses API 是其最新介面。 | LMSP 的核心介面沿用 Responses 的結構,服務同時提供相容 OpenAI 的介面。 | | Ollama | 本地執行環境,已提供相容 OpenAI 的介面。 | 它可以作為轉接器放在 LMSP 服務之後,LMSP 也定義了可選的相容 Ollama 的介面。 | | MCP | 把 AI 代理連線到工具和上下文。 | LMSP 把 AI 代理連線到模型。兩者都傾向於無狀態發現,而不是強制工作階段。 | | A2A | AI 代理之間的協作。 | LMSP 的服務描述和任務分別借鑑了其服務卡片和顯式任務狀態。 | ## 資源 {#resources} - [規範 1.0 版(草案)](https://madeforagent.com/zh-tw/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/zh-tw/llms.txt) 和 [llms-full.txt](https://madeforagent.com/zh-tw/llms-full.txt)。 - 每個頁面都提供 Markdown 版本:把頁面位址末尾的斜線換成 `.md` 即可,例如 [`/zh-tw/lmsp/1.0/tasks.md`](https://madeforagent.com/zh-tw/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 服務嗎? 通常可以。只需把 base URL 改為 LMSP 服務的位址,並把 LMSP 權杖作為 API Key。但它們無法自動發現服務,因為服務發現需要支援 LMSP 的客戶端。 ### 客戶端如何發現本地的 LMSP 服務? 按順序嘗試三種方式:先使用使用者手動輸入的位址,再存取本機約定位址的 /.well-known/lmsp.json,最後在區域網路中透過可選的 mDNS 與 DNS-SD 瀏覽 _lmsp._tcp.local.。 ### 只監聽 localhost 的模型服務安全嗎? 不一定,因為網頁可能嘗試存取本機連線埠。LMSP 服務預設繫結回送位址,要求客戶端透過本機配對取得 Bearer Token,驗證 Origin 與 Host 請求標頭,區域網路模式下必須啟用驗證。 ### 這份規範目前處於什麼狀態? 1.0 版是首個公開草案,仍可能發生不相容的修改。英文文本是規範性文本,譯文僅為方便閱讀而提供。 --- # 概述 > LMSP 1.0 草案概述:本地模型服務協定是什麼、它所借鑑的設計、分層架構、閱讀規範的方法,以及全文使用的術語。 Source: https://madeforagent.com/zh-tw/lmsp/1.0/ Language: zh-TW Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 1.1 摘要 {#abstract} 本地模型服務協定(LMSP)規定了客戶端如何發現並使用執行在使用者自己裝置或區域網路中的語言模型服務。它定義了服務發現、模型目錄、同步與串流生成,以及在服務重啟後仍然保留的非同步任務,還規定了同一個服務如何在 LMSP 介面之外提供相容 OpenAI 的介面,使現有客戶端可以繼續使用。 LMSP 並不取代 OpenAI API、Ollama 或 MCP,而是把它們的優點組合成一個適合本地常駐服務的協定層。 ## 1.2 本文件的狀態 {#status} 這是 LMSP 1.0 的首個公開草案,發佈於 2026-11-08。「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` 介面,說明「統一服務+OpenAI 適配層」能顯著降低現有客戶端的接入成本。參見 [Ollama 的 OpenAI 相容性](https://docs.ollama.com/api/openai-compatibility)。 - **模型上下文協定(MCP)。** 其最新設計已從強制初始化工作階段轉向無狀態發現:每個請求獨立攜帶版本資訊,客戶端透過發現介面取得伺服端能力。這非常適合本地服務,因為服務重啟、客戶端崩潰和連線斷開都不應破壞協定狀態。參見 [MCP](https://modelcontextprotocol.io/)。 - **Agent2Agent(A2A)。** 服務卡片、顯式任務狀態、任務訂閱和取消機制,為 LMSP 的服務描述和非同步任務模型提供了借鑑。參見 [A2A](https://a2a-protocol.org/)。 ## 1.4 架構 {#architecture} LMSP 分為三層,其下是對接各模型執行環境的轉接器: ```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/zh-tw/lmsp/1.0/discovery.md))。 - **核心層。** 模型目錄、同步回應、伺服器傳送事件和非同步任務([第 5 至 7 章](https://madeforagent.com/zh-tw/lmsp/1.0/models.md))。 - **相容層。** 相容 OpenAI 的 API,以及可選的相容 Ollama 的 API([第 8 章](https://madeforagent.com/zh-tw/lmsp/1.0/compatibility.md))。 - **轉接器。** 對接 llama.cpp、Ollama、vLLM 或自研執行環境的橋接層。 核心原則是:**一個模型執行核心,對外同時提供 LMSP 和 OpenAI 兩種介面**。服務在同一個核心之上提供這兩類介面,因此模型載入、請求執行和任務跟蹤都只有一處實作。 ## 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 並透過轉接器執行模型的常駐行程。 | | 客戶端 | 發現並呼叫服務的軟體:AI 代理、應用或工具。 | | 轉接器 | 把服務連線到某個模型執行環境(如 llama.cpp、Ollama 或 vLLM)的元件。 | | 模型 | 本地可用的模型,由穩定的 `id` 標識。 | | 回應 | 一次生成請求的結果:輸出項、用量和狀態。 | | 任務 | 對回應請求的持久化非同步執行,具有明確的狀態。 | | 事件 | 生成回應期間發出的一條帶序號的伺服器傳送事件。 | --- # 傳輸與部署 > LMSP 服務的預設監聽位址、傳輸方式、串流格式和 URL 佈局,以及它如何作為獨立於模型行程的作業系統服務常駐執行。 Source: https://madeforagent.com/zh-tw/lmsp/1.0/transport/ Language: zh-TW 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` | | 傳輸 | HTTP/1.1 或 HTTP/2,承載 JSON | | 串流傳輸 | 伺服器傳送事件,`Content-Type: text/event-stream` | | 核心介面字首 | `/lmsp/v1` | | OpenAI 相容介面字首 | `/v1` | | 服務描述 | `GET /.well-known/lmsp.json` | | 機器可讀合約 | `GET /lmsp/v1/openapi.json` | | 可選的本機通道 | Unix Domain Socket 或 Windows Named Pipe | 請求和回應使用 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 Sequence,因此串流介面與其餘介面在同一份文件中定義。本版本的合約發布在 [`/lmsp/1.0/openapi.json`](https://madeforagent.com/lmsp/1.0/openapi.json)。 服務**可以**另外在 Unix Domain Socket 或 Windows Named Pipe 上接受連線,HTTP 語義相同。在 1.0 中,這類端點如何對外宣告交由擴充決定。 ## 2.2 常駐執行 {#operation} 服務**應**由作業系統託管:macOS 使用 launchd,Linux 使用 systemd,Windows 使用 Windows Service。 服務行程與模型行程**應**分離。模型崩潰**應**只影響使用該模型的任務,**不應**導致服務發現、模型目錄和歷史任務查詢同時失效。 --- # 服務發現 > LMSP 客戶端如何透過手動設定、探測本機位址和可選的 mDNS/DNS-SD 找到本地服務,以及如何讀取 Well-Known 服務描述文件。 Source: https://madeforagent.com/zh-tw/lmsp/1.0/discovery/ Language: zh-TW 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. **區域網路發現。** 客戶端透過 mDNS 與 DNS-SD 瀏覽 `_lmsp._tcp.local.`。 某種方式只要得到一份有效的服務描述([第 3.3 節](https://madeforagent.com/zh-tw/lmsp/1.0/discovery.md#service-description))即告成功。使用者手動輸入的位址始終優先於自動發現的位址。 ## 3.2 區域網路廣播 {#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 記錄中放置存取權杖、模型清單、檔案路徑或其他敏感資訊。該記錄只負責找到服務,真實能力**必須**從服務描述中讀取。 - 服務**必須**預設關閉 mDNS 廣播,僅在使用者明確開啟「區域網路發現」時才啟用。僅在本機使用的服務無需廣播。 - 在區域網路中廣播的服務需要監聽回送位址之外的位址,而這只有在區域網路模式下才被允許([第 10.5 節](https://madeforagent.com/zh-tw/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/zh-tw/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/zh-tw/lmsp/1.0/endpoints/ Language: zh-TW Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 4.1 介面總表 {#table} 合規的服務**必須**實作標記為「必需」的每個介面,**可以**實作可選介面,並透過其[服務描述](https://madeforagent.com/zh-tw/lmsp/1.0/discovery.md#service-description)中的能力旗標宣告。 | 方法 | 路徑 | 必需性 | 用途 | | --- | --- | --- | --- | | GET | `/.well-known/lmsp.json` | 必需 | [服務與能力發現](https://madeforagent.com/zh-tw/lmsp/1.0/discovery.md#service-description) | | GET | `/lmsp/v1/openapi.json` | 必需 | 機器可讀的介面規範 | | GET | `/lmsp/v1/health` | 必需 | [存活與就緒檢查](https://madeforagent.com/zh-tw/lmsp/1.0/common-rules.md#health) | | POST | `/lmsp/v1/pair` | 必需 | [用配對碼換取 Bearer Token](https://madeforagent.com/zh-tw/lmsp/1.0/security.md#pairing) | | GET | `/lmsp/v1/models` | 必需 | [取得本地模型列表](https://madeforagent.com/zh-tw/lmsp/1.0/models.md#list) | | GET | `/lmsp/v1/models/{model_id}` | 必需 | [取得單個模型詳情](https://madeforagent.com/zh-tw/lmsp/1.0/models.md#operations) | | POST | `/lmsp/v1/responses` | 必需 | [同步或串流生成](https://madeforagent.com/zh-tw/lmsp/1.0/responses.md#request) | | GET | `/lmsp/v1/responses/{response_id}` | 可選 | [查詢已儲存的回應](https://madeforagent.com/zh-tw/lmsp/1.0/responses.md#sync) | | POST | `/lmsp/v1/tasks` | 必需 | [建立非同步生成任務](https://madeforagent.com/zh-tw/lmsp/1.0/tasks.md#create) | | GET | `/lmsp/v1/tasks/{task_id}` | 必需 | [查詢非同步任務](https://madeforagent.com/zh-tw/lmsp/1.0/tasks.md#create) | | POST | `/lmsp/v1/tasks/{task_id}/cancel` | 必需 | [取消非同步任務](https://madeforagent.com/zh-tw/lmsp/1.0/tasks.md#cancel) | | GET | `/lmsp/v1/tasks/{task_id}/events` | 可選 | [訂閱或恢復事件流](https://madeforagent.com/zh-tw/lmsp/1.0/tasks.md#events) | | POST | `/lmsp/v1/models/{model_id}/load` | 可選 | [預載入模型](https://madeforagent.com/zh-tw/lmsp/1.0/models.md#operations) | | POST | `/lmsp/v1/models/{model_id}/unload` | 可選 | [解除安裝模型](https://madeforagent.com/zh-tw/lmsp/1.0/models.md#operations) | `/v1` 下相容 OpenAI 的介面,以及可選的相容 Ollama 的介面,列在[第 8 章](https://madeforagent.com/zh-tw/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 `。 - **可選介面。** 服務未實作的可選介面**必須**回傳 `501 Not Implemented`,錯誤碼為 `unsupported_capability`。 - **版本與錯誤。** 請求可以攜帶版本請求標頭,所有錯誤使用統一的格式,兩者均在[第 9 章](https://madeforagent.com/zh-tw/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/zh-tw/lmsp/1.0/models/ Language: zh-TW 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/zh-tw/lmsp/1.0/models.md#lifecycle))。 | | `capabilities` | 布林旗標:`chat`、`completion`、`embeddings`、`tools`、`vision`、`structured_output` 和 `streaming`。 | | `input_modalities`、`output_modalities` | 模型讀取和寫入的內容型別。1.0 只定義了 `text`。 | | `context_window` | 上下文的最大 token 數。 | | `max_output_tokens` | 單次回應的最大 token 數。 | | `runtime` | `backend`(例如 `llama.cpp`)、`device`(例如 `metal`)和 `load_on_demand`。 | | `error` | `status` 為 `error` 時存在,是一個 problem 物件([第 9.3 節](https://madeforagent.com/zh-tw/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}` 回傳單個模型物件。未知的 id 回傳 `404` 和 `model_not_found`。 兩個可選介面用於顯式控制載入。它們在 `model_management` 能力為 `true` 時可用,並要求 `models:manage` 許可權範圍([第 10.3 節](https://madeforagent.com/zh-tw/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/zh-tw/lmsp/1.0/common-rules.md#idempotency))。 --- # 回應與串流傳輸 > LMSP 的核心對話介面:Responses 風格的請求與輸出項、同步與串流生成、伺服器傳送事件的型別、事件 id、斷線重連以及工具呼叫。 Source: https://madeforagent.com/zh-tw/lmsp/1.0/responses/ Language: zh-TW 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` 結構,而不是只支援簡單的 messages 字串,以便用同一種請求格式統一承載: - 多輪訊息; - 文本、圖片、音訊和檔案; - 工具呼叫及工具結果; - 結構化輸出; - 推理摘要; - 多模態輸出。 LMSP 1.0 只定義文本內容。圖片、音訊和檔案的內容型別留待後續版本。 | 欄位 | 必需 | 含義 | | --- | --- | --- | | `model` | 是 | 要使用的模型 id。 | | `input` | 是 | 輸入項陣列。`message` 項包含 `role`(`system`、`user` 或 `assistant`)和由多個部分組成的 `content` 陣列,例如 `input_text`。使用工具時會增加 `function_call` 和 `function_call_output` 項([第 6.5 節](https://madeforagent.com/zh-tw/lmsp/1.0/responses.md#tools))。 | | `stream` | 否 | 為 `true` 時接收伺服器傳送事件,預設為 `false`。 | | `max_output_tokens` | 否 | 生成 token 數的上限。 | | `temperature` | 否 | 取樣溫度。 | | `response_format` | 否 | `{"type": "text"}`(預設),或用於結構化輸出的 `{"type": "json_schema", "json_schema": {"name": "...", "schema": {...}}}`。需要 `structured_output` 能力。 | | `tools`、`tool_choice` | 否 | 模型可以呼叫的函式工具([第 6.5 節](https://madeforagent.com/zh-tw/lmsp/1.0/responses.md#tools))。需要 `tools` 能力。 | | `store` | 否 | 請求服務儲存該回應,以便之後查詢,並可用作 `previous_response_id`。預設為 `false`。沒有 `conversations` 能力的服務會忽略它。 | | `previous_response_id` | 否 | 接著已儲存的某個回應繼續對話。需要 `conversations` 能力。 | | `extensions` | 否 | 私有擴充([第 9.1 節](https://madeforagent.com/zh-tw/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` | token 用量:`input_tokens`、`output_tokens` 和 `total_tokens`。 | | `error` | `null`;當 `status` 為 `failed` 時是一個 problem 物件([第 9.3 節](https://madeforagent.com/zh-tw/lmsp/1.0/common-rules.md#errors))。 | | `incomplete_details` | `null`;當 `status` 為 `incomplete` 時是帶有 `reason`(例如 `max_output_tokens`)的物件。 | 輸出項型別: | 項型別 | 結構 | | --- | --- | | `message` | `id`、`role`(`assistant`)和 `content`,後者是 `output_text` 部分的陣列,每個部分帶有 `text`。 | | `function_call` | `id`、`call_id`、`name` 和 `arguments`,後者是 JSON 編碼的字串。 | | `reasoning` | `id` 和 `summary`,後者是 `summary_text` 部分的陣列,每個部分帶有 `text`。 | 如果生成失敗,同步請求回傳錯誤狀態碼和 problem 文件([第 9.3 節](https://madeforagent.com/zh-tw/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 } ``` 服務回傳 `200 OK` 和 `Content-Type: text/event-stream`,並傳送語義化事件。每個事件包含 `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` | 生成提前停止,例如達到 token 上限。最終事件。 | `response_id`、`status`、`incomplete_details`、`usage` | | `response.failed` | 生成失敗。最終事件。 | `response_id`、`status`、`error` | | `response.cancelled` | 回應已取消。最終事件。 | `response_id`、`status` | | `error` | 與某個具體回應無關的問題,出現後流無法繼續。 | `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/zh-tw/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/zh-tw/lmsp/1.0/tasks.md)並跟隨它的事件。 ## 6.5 工具呼叫 {#tools} LMSP 1.0 定義由客戶端執行的函式工具,服務本身不執行工具。`tools:execute` 許可權範圍預留給會執行工具的擴充([第 10.3 節](https://madeforagent.com/zh-tw/lmsp/1.0/security.md#scopes))。 首先,客戶端列出工具。每個條目的 `type` 為 `function`,並帶有 `name`、`description` 和 `parameters`(一個 JSON Schema 物件)。`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/zh-tw/lmsp/1.0/tasks/ Language: zh-TW Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 7.1 建立任務 {#create} 客戶端透過傳送它想要執行的回應請求來建立任務,請求要包在任務信封中。建議攜帶 `Idempotency-Key` 請求標頭([第 9.2 節](https://madeforagent.com/zh-tw/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/zh-tw/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/zh-tw/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/zh-tw/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` 結束的任務(例如達到 token 上限)是 `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/zh-tw/lmsp/1.0/responses.md#streaming)中的回應事件,對應該任務所產生的回應。序號與恢復規則遵循[第 6.4 節](https://madeforagent.com/zh-tw/lmsp/1.0/responses.md#reconnection)。 - 事件流以 `response.created` 開始,它在任務被接受時記錄。流以與結果相符的一個最終事件結束:`response.completed`、`response.incomplete`、`response.failed` 或 `response.cancelled`。 - 較晚連線的客戶端先收到自己錯過的事件,再收到即時事件。服務在最終事件之後關閉流。 - 事件會保留一段保留期([第 11.2 節](https://madeforagent.com/zh-tw/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 服務如何透過疊加在同一個執行核心之上的輕量資料轉換層,相容現有的 OpenAI 和 Ollama 客戶端,以及這種相容性的邊界。 Source: https://madeforagent.com/zh-tw/lmsp/1.0/compatibility/ Language: zh-TW 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 介面相同的 Bearer Token。使用 OpenAI 協定的客戶端把 LMSP 權杖設定為 API Key 即可。 首個實作可以先從 `/v1/models` 和 `/v1/chat/completions` 開始([第 12 章](https://madeforagent.com/zh-tw/lmsp/1.0/mvp.md))。 ## 8.2 相容 Ollama 的介面 {#ollama} 服務**應**同時提供以下介面,讓為 Ollama 編寫的客戶端也能使用: | 介面 | 說明 | | --- | --- | | `GET /api/tags` | 列出 LMSP 目錄中的模型。 | | `POST /api/chat` | 對話。 | | `POST /api/generate` | 純文字生成。 | 提供這些介面的服務以 `kind` 為 `ollama` 宣告一個介面。 ## 8.3 同一個執行核心 {#core} 相容層**必須**只做資料轉換,**不得**另建第二套模型管理邏輯。相容介面上的請求會被對映為 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/zh-tw/lmsp/1.0/common-rules/ Language: zh-TW 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`。 冪等鍵的作用域是客戶端和介面。它**應**是不超過 255 個字元的唯一值,例如 ULID 或 UUID。服務**應**至少儲存冪等鍵 24 小時。 服務**可以**在串流請求上忽略該冪等鍵。需要安全重試的客戶端應使用[任務](https://madeforagent.com/zh-tw/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 | 否 | 任務已經 succeeded 或 failed。 | | `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 服務的安全要求,包括回送繫結、配對與 Bearer Token、許可權範圍、防範惡意網頁的 Origin 與 Host 驗證、區域網路模式和日誌規則。 Source: https://madeforagent.com/zh-tw/lmsp/1.0/security/ Language: zh-TW Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 10.1 基線 {#baseline} 「只監聽 localhost」並不等於絕對安全:惡意網頁仍可能嘗試存取本機連線埠。因此 LMSP 組合使用多項措施。 - 服務預設**必須**繫結回送位址。除非啟用了區域網路模式([第 10.5 節](https://madeforagent.com/zh-tw/lmsp/1.0/security.md#lan)),否則**不得**監聽 `0.0.0.0` 或任何其他非回送位址。 - 首次執行時,服務**應**生成高強度的隨機服務金鑰,並使用它(而不是任何內建的預設值)來簽發和驗證權杖。 - 對核心介面的每個請求都**必須**攜帶有效的 Bearer Token,[第 4.2 節](https://madeforagent.com/zh-tw/lmsp/1.0/endpoints.md#conventions)列出的引導介面除外。僅僅來自回送位址,永遠不足以透過驗證。 ## 10.2 配對與權杖 {#pairing} 客戶端透過本機配對碼獲得屬於自己的 Bearer Token。使用者以服務提供的任意方式讓服務顯示配對碼,客戶端再提交該配對碼: ```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`。 - 權杖按客戶端相互獨立,因此使用者可以撤銷某個客戶端而不影響其他客戶端。服務**應**允許使用者列出並撤銷已配對的客戶端。 - 客戶端**應**把權杖儲存在作業系統鑰匙圈或認證管理員中。服務**應**只儲存每個權杖的雜湊。 - 權杖缺失、無效或已被撤銷時,回傳 `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` 中給出確切的來源,絕不使用 `*`。 - 服務**必須**驗證 `Host` 請求標頭,以防止 DNS Rebinding。服務只接受自己所服務的主機名稱:回送名稱和位址,加上為區域網路模式設定的名稱。其他任何取值都回傳 `403` 和 `forbidden`。 ## 10.5 區域網路模式 {#lan} 區域網路模式指監聽回送位址之外的位址。 - 除非使用者明確開啟,服務**不得**啟用區域網路模式。 - 在區域網路模式下,服務**必須**要求驗證,並且**應**同時啟用 TLS。除非使用者明確允許,客戶端**不應**透過明文 HTTP 向非回送位址傳送權杖。 - 只有在區域網路模式下,且使用者也開啟了「區域網路發現」時,才允許 mDNS 廣播([第 3.2 節](https://madeforagent.com/zh-tw/lmsp/1.0/discovery.md#mdns))。 ## 10.6 日誌與隱私 {#logging} - 模型路徑、日誌、提示詞和生成內容**不得**出現在 mDNS 廣播中。 - 預設情況下,服務**應**只記錄中繼資料(時間、介面、狀態碼、id、token 數量和耗時),**不應**記錄完整的提示詞和生成內容。 - 健康檢查回應和錯誤詳情**不得**包含模型路徑、提示詞、存取權杖等敏感資訊。 --- # 持久化 > LMSP 服務如何儲存自己的狀態:嵌入式 SQLite 資料庫、建議保留的表、任務與事件的交易規則、保留期,以及啟動時的恢復。 Source: https://madeforagent.com/zh-tw/lmsp/1.0/persistence/ Language: zh-TW Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 11.1 儲存 {#storage} 服務**應**使用嵌入式 [SQLite](https://www.sqlite.org/) 資料庫儲存狀態。建議的表如下: | 表 | 內容 | | --- | --- | | `service_config` | 服務的身分與設定。 | | `clients` | 已配對的客戶端、權杖雜湊和許可權範圍。 | | `models` | 模型中繼資料和內容摘要。 | | `responses` | 已儲存的回應。 | | `tasks` | 非同步任務。 | | `task_events` | 每個任務的事件日誌,帶序號。 | | `idempotency_keys` | 冪等鍵、請求摘要和結果。 | 模型檔案**必須**放在資料庫之外,資料庫只儲存中繼資料與內容摘要。 表名和表結構由實作自行選擇。客戶端能夠觀察到的、需要互操作的,是接下來兩節所規定的行為。 ## 11.2 規則 {#rules} 無論服務使用何種儲存,它都: - **必須**先持久化任務,再回傳 `202 Accepted`。 - **必須**在同一個交易中更新事件序號和任務狀態。 - **必須**使取消狀態轉換原子化,避免出現「已取消又完成」的競爭。 - **應**把已結束的任務和事件保留一段可設定的保留期。 - **不得**在清理日誌時刪除未結束的任務。 - **必須**在啟動時掃描遺留的 `queued`、`loading`、`running` 和 `cancelling` 任務,並執行各自的重啟策略([第 7.5 節](https://madeforagent.com/zh-tw/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` 事件。新狀態與對應的事件在同一個交易中寫入([第 11.2 節](https://madeforagent.com/zh-tw/lmsp/1.0/persistence.md#rules))。 --- # 最小可用範圍 > 首個 LMSP 實作的建議範圍:構成可用閉環的十項內容,以及規範中可以推遲到後續版本的部分。 Source: https://madeforagent.com/zh-tw/lmsp/1.0/mvp/ Language: zh-TW Specification: LMSP 1.0 (draft) Updated: 2026-11-08 ## 12.1 第一版 {#scope} 第一版不必實作全部多模態功能、模型下載和即時語音。只要提供以下內容,就能形成可用的閉環: 1. 透過 `/.well-known/lmsp.json` 發現服務。 2. 可選的 `_lmsp._tcp.local.` 區域網路廣播。 3. `GET /lmsp/v1/models`。 4. `POST /lmsp/v1/responses`,同步和 SSE 串流都要支援。 5. 非同步任務:建立、查詢和取消,並使用 SQLite 持久化。 6. 用 `Last-Event-ID` 或 `after` 恢復斷開的流。 7. Bearer Token、本機配對,以及 `Origin` 和 `Host` 防護。 8. 相容 OpenAI 的 `/v1/models` 和 `/v1/chat/completions` 介面。 9. OpenAPI 3.2 合約檔案,以及跨語言一致性測試。 10. 至少接入一個真實後端,並驗證服務重啟後任務能正確恢復。 這套設計的重點是:LMSP 提供完整能力,OpenAI 介面提供現成的相容性,mDNS 與服務描述提供自動發現,SQLite 任務模型提供本地持久化。 ## 12.2 可以推遲的部分 {#later} 上面的清單沒有提到的內容,都可以在後續版本中跟進,包括可選介面(已儲存的回應,以及模型的載入和解除安裝)、OpenAI 的 `/v1/responses` 和 `/v1/embeddings` 介面、相容 Ollama 的 API,以及文本之外的所有內容型別。 這個範圍是通往首個可用版本的路徑,而不是一致性等級。聲稱支援 LMSP 1.0 的服務,仍然要實作[第 4 章](https://madeforagent.com/zh-tw/lmsp/1.0/endpoints.md)中所有必需的介面,以及其他章節所描述的必需行為。