OllamaSharp:把 Ollama 的每個端點包成 awaitable 方法之後,你得先決定要不要讓它管你的對話歷史
The easiest way to use Ollama in .NET
秒懂
- 它是什麼?
- OllamaSharp 是 Ollama HTTP API 的 .NET 綁定,覆蓋全部端點並實作 Microsoft.Extensions.AI 的 IChatClient 與 IEmbeddingGenerator,採用 MIT 授權。它的核心取捨在於 Chat 類別替你保存訊息歷史,換來方便,也換來一份你不再完全掌握的狀態。
- 適合誰用?
- 如果你已經在用 Microsoft.Extensions.AI 或 Semantic Kernel,而且模型跑在自架的 Ollama 上,OllamaSharp 是最短的接入路徑:`new OllamaApiClient(uri)` 之後直接當 `IChatClient` 用,不必自己寫 HTTP 與串流解析。反過來說,如果你只需要打一兩個端點、或你的對話狀態必須由自己的儲存層掌握,這個套件替你保管的 `Chat.Messages` 會變成要繞過的東西,直接寫 HttpClient 反而乾淨。
- 可以商用嗎?
- 可以。MIT 是寬鬆授權:你可以使用、修改並販售以它為基礎的軟體,只需保留著作權與授權聲明。
- 還在維護嗎?
- 有在維護。儲存庫最近一次提交在 53 天前。
- 用什麼語言寫的?
- 主要是 C#(依據 GitHub 的語言統計)。
以上回答依據專案的 GitHub 資料(最近同步於 2026年9月15日)與我們的分析,不構成法律意見。
開源專案深度解析
它解決的是重複造輪子的問題,不是模型能力的問題
Ollama 本身提供 HTTP API,任何語言都能呼叫。所以 OllamaSharp 處理的不是「能不能用」,而是「每次都要重寫一遍」。串流回應要自己處理 chunk 邊界,拉模型要自己解析進度事件,工具呼叫要把 JSON schema 轉成模型看得懂的格式再解析回來。這些工作在每個 .NET 專案裡長得差不多,卻沒人想維護第三份。
目標讀者是已經選定 Ollama 作為推論後端、而且應用層是 C# 的團隊。README 的定位寫得直白:「The easiest way to use Ollama in .NET」。若你的模型跑在 OpenAI 或 Anthropic 的託管服務上,這個套件幫不上忙,它只講 Ollama 的 API 方言。
還有一類讀者值得注意:正在用 Microsoft.Extensions.AI 抽象層、想同時支援雲端與本機模型的人。OllamaSharp 是該抽象層的完整實作之一,README 明確寫出它實作 `IChatClient` 與 `IEmbeddingGenerator<string, Embedding<float>>`。對這群人來說,採用它的理由不是方便,是介面一致性。
每個端點一個 awaitable 方法,串流走 IAsyncEnumerable
OllamaSharp 的架構沒有魔法。它把 Ollama 的每一個 API 端點包成可 await 的方法,回傳型別對應端點的回應結構。`GenerateAsync` 對應 `/api/generate`,README 說明它適合單輪、無上下文的補全;`ListLocalModelsAsync` 對應列出本機模型;`PullModelAsync` 對應拉取模型。
串流是主要設計。`GenerateAsync` 與 `PullModelAsync` 都回傳 `IAsyncEnumerable`,前者逐 token 吐出 `Response`,後者逐筆吐出帶有 `Percent` 與 `Status` 的進度物件。這代表背壓由消費端的 `await foreach` 決定,你不必自己接 `HttpCompletionOption.ResponseHeadersRead`,也不必處理半個 JSON 物件跨 chunk 的情況。
真正改變資料流的是 `Chat` 類別。README 說它「automatically tracks the full message history (including tool calls and their results) across turns」。也就是說,訊息陣列的所有權從你的程式碼移到 `Chat` 實例,可透過 `Messages` 屬性讀取。這是便利與控制權之間的交換,後面會再談。
工具支援走的是 source generator 路線,README 稱之為「Sophisticated tool support with source generators」。這意味著工具定義在編譯期產生,而不是執行期反射。對 AOT 情境是必要條件,對啟動速度也有影響。
從 NuGet 到第一個 token 的實際步驟
安裝套件之後,初始化只有兩行。README 給的範例是先建立指向本機 11434 埠的 `Uri`,再 `new OllamaApiClient(uri)`,然後指定 `ollama.SelectedModel`。模型名稱必須是本機已存在的標籤,否則後續請求會直接失敗,這是第一個會踩到的坑。
單輪補全用 `GenerateAsync`,逐段寫進 Console。多輪對話改用 `Chat`:`var chat = new Chat(ollama);` 之後在迴圈裡 `await foreach (var answerToken in chat.SendAsync(message))`。歷史訊息由 `chat` 物件自行累積,你不需手動 append。
拉模型時想顯示進度,用 `PullModelAsync` 並讀取每個狀態物件的 `Percent` 與 `Status`。這是這個套件少數直接處理長時間操作回饋的地方,其餘端點多半是一次性回應。
Native AOT 需要額外一步。README 要求在自訂型別上標註 `JsonSerializable`,宣告一個 `partial class` 繼承 `JsonSerializerContext`,再用帶三個參數的建構子 `new OllamaApiClient(uri, model, MyJsonContext.Default)`。沒有這一步,序列化會走反射路徑,AOT 編譯後在執行期出問題。
要接 Ollama 的雲端模型,README 的做法是改用吃 `HttpClient` 的建構子,設定 `BaseAddress`,並在 `DefaultRequestHeaders` 加上 API key。注意 README 的範例裡 `BaseAddress` 仍寫著 localhost,實際指向哪個位址取決於你的部署,程式碼片段在此處被截斷,無法從素材確認完整寫法。
Chat 替你保管歷史,這件事有代價
`Chat` 類別是整個套件最需要想清楚的部分。它自動追蹤訊息、工具呼叫與工具結果,讓對話迴圈縮到五行。方便是真的,代價也是真的。
第一個代價是狀態位置。對話歷史活在 `Chat` 實例裡,不在你的資料庫、不在你的快取。多使用者情境下,你得自己決定這個實例的生命週期與隔離方式,套件不提供。如果你的產品需要對話持久化、稽核或跨節點遷移,`Messages` 屬性會是你匯出狀態的出口,但匯出與重建的邏輯得自己寫。
第二個代價是上下文長度。歷史自動累積,不會自動修剪。長對話最終會超過模型的 context window,而 README 沒有描述任何截斷或摘要機制。這件事必須由應用層處理,且處理時你得直接操作 `Messages`,等於繞過了 `Chat` 的抽象。
第三個代價是版本節奏。素材顯示 5.4.28、5.4.29、5.4.30 三個版本集中在 2026 年 7 月 24 日當天發布,patch 號連續遞增。這不代表品質問題,但代表這個套件跟著 Ollama 上游的變動走,你的升級視窗會比一般函式庫窄。綁定層的宿命就是如此。
什麼情況下不該用它
如果你的 .NET 服務只需要呼叫一個端點,例如固定打 `/api/generate` 做文字摘要,引入整個套件換來的是一層抽象與一份升級負擔。直接寫 `HttpClient` 加上 `System.Text.Json` 的程式碼量大約是幾十行,而且完全在你的掌控下。
如果你的對話狀態必須由自有儲存層掌握,例如要寫進關聯式資料庫並支援稽核軌跡,`Chat` 的自動累積會變成阻礙。你可以只用 `IOllamaApiClient` 的底層方法,自己組訊息陣列,但這樣一來 `Chat` 的價值就消失了,採用它的理由也跟著消失。
如果你的模型不是跑在 Ollama 上,這個套件沒有意義。它綁的是 Ollama 的 API 形狀,不是通用的推論協定。
還有一種情況:團隊對第三方相依極度敏感,要求每一個引入的套件都有內部維護能力。OllamaSharp 是 MIT 授權的單一維護者專案,README 提到它被 Microsoft Semantic Kernel、.NET Aspire 與 Microsoft.Extensions.AI 採用,這說明生態位階,但不改變維護人力的事實。這不是缺點,是需要被納入考量的條件。
與直接使用 Microsoft.Extensions.AI 抽象層的差別
最直接的替代方案是自己寫一個 `IChatClient` 實作,內部呼叫 Ollama 的 HTTP API。差異在於:OllamaSharp 已經把端點覆蓋、串流解析、工具 schema 產生與 AOT 序列化都做完了,你自己寫則是把這些工作重新做一遍,換來對每一行的完全理解與控制。
第二個替代是走 OpenAI 相容介面。Ollama 與不少推論伺服器都提供 OpenAI 格式的端點,用官方的 OpenAI .NET 客戶端指向本機位址即可。這條路的差別在於你只能用到 OpenAI 協定涵蓋的功能,Ollama 特有的模型管理端點(列出、拉取、複製、刪除、顯示)不在其中。README 明確說 OllamaSharp「Covers every single Ollama API endpoint」,這是它與 OpenAI 相容路線最實質的分野。
第三個替代是 Semantic Kernel 內建的 Ollama connector。README 提到 OllamaSharp 支撐了 Semantic Kernel 的相關實作,也就是說在某些版本裡,你透過 Semantic Kernel 用 Ollama,底層跑的可能就是這個套件。這種情況下「選不選 OllamaSharp」其實不是你能決定的問題,而是版本相依的問題。
選擇的判準很簡單:需要模型管理與完整端點覆蓋,用 OllamaSharp;只需要推論且想保持協定中立,走 OpenAI 相容;需要對每個 HTTP 細節有話語權,自己寫。
授權、維護成本與升級前該確認的事
授權是 MIT,條款寬鬆,商業使用、修改與再散布的限制都少。這裡不提供法律意見,實際條文與其對你產品的影響請自行或請法務確認。實務上要留意的是套件本身與其相依項目的授權是否一致,這需要看你的還原圖。
維護成本主要落在兩處。一是版本跟隨:素材顯示版本號在一天內連續推進三次,這種節奏表示上游 Ollama API 有變動時,綁定層會很快跟上,你的升級頻率也連帶提高。二是 API 表面積:套件覆蓋全部端點,代表你能用的東西很多,也代表每次大版本都可能碰到你沒用到但被改動的方法簽章。
升級前值得確認的具體項目:`OllamaApiClient` 的建構子多載是否變動,特別是 Native AOT 用的三參數版本;`IChatClient` 與 `IEmbeddingGenerator` 的實作是否仍與當前 Microsoft.Extensions.AI 版本對齊,因為這是抽象層,兩邊版本脫鉤時症狀通常出現在執行期而非編譯期;以及 `Chat.Messages` 的結構是否改變,如果你有從它匯出狀態的程式碼,這是最容易斷的地方。
編輯結論
如果你已經在用 Microsoft.Extensions.AI 或 Semantic Kernel,而且模型跑在自架的 Ollama 上,OllamaSharp 是最短的接入路徑:`new OllamaApiClient(uri)` 之後直接當 `IChatClient` 用,不必自己寫 HTTP 與串流解析。反過來說,如果你只需要打一兩個端點、或你的對話狀態必須由自己的儲存層掌握,這個套件替你保管的 `Chat.Messages` 會變成要繞過的東西,直接寫 HttpClient 反而乾淨。動手前先確認三件事:目標模型名稱在 `ollama.SelectedModel` 設定後確實存在於本機,否則第一個請求就會失敗;需要 Native AOT 時先確認你的自訂型別都進了 `JsonSerializerContext`;最後把 `Chat` 物件當成可丟棄的執行期狀態,不要當成資料庫。
社群筆記