概览与架构
TensorSharp 是面向 GGUF 模型的原生 .NET 大模型推理引擎 —— 既支持自回归大模型,也支持 DiffusionGemma 式的文本扩散模型。它提供控制台应用、基于 Web 的聊天机器人,以及兼容 Ollama / OpenAI 的 HTTP API。
它到底是什么 —— 通俗讲解
大语言模型(LLM)是一种预测文本的神经网络。要使用它,你需要一个推理引擎:负责加载模型权重、运行数学运算,把你的提示词变成回复的软件。TensorSharp 就是这样一个引擎,用现代 C# / .NET 10 编写,专注于本地大模型生态广泛使用的 GGUF 模型格式。
它用同一套二进制为你提供三种使用模型的方式:
- 命令行 —— 运行一条提示词、一张图片、一批问题,或一次基准测试。→ CLI
- 服务器 —— 浏览器聊天机器人,外加模仿 Ollama 与 OpenAI 的 REST 接口。→ 服务器
- 库 —— 引用源码项目,从你自己的 .NET 代码调用引擎。→ C# 库
商业价值
对评估本地推理的决策者而言,核心权衡是用运行自己的硬件换取掌控力与成本。
数据隐私与合规
提示词与文档永不离开你的基础设施 —— 适合受监管、私有部署或离网环境。
成本可预测
没有按 token 计费的 API。容量受限于你本就已预算的硬件。
无供应商锁定
来自 Hugging Face 的开放 GGUF 模型,以及现有工具已支持的 OpenAI / Ollama 兼容接口。
.NET 原生集成
将推理嵌入现有 C# 服务,而非桥接到外部运行时。
单机即可扩展
连续批处理用单个托管模型服务大量并发用户。
轻松迁移
把现有 OpenAI SDK 代码指向 http://localhost:5000/v1,应用无需改动。
架构
TensorSharp 是一个分层系统。每一层都是可独立打包的项目,因此源码使用者只需引用自己需要的部分。发布集合共 13 个包,由 eng/verify-packages.ps1 校验。其中 8 个已在 NuGet.org 发布到 3.1.2(核验于 2026-09-08),落后于当前源码树与 Release;TensorSharp.Runtime.Logging、TensorSharp.AgentHost、TensorSharp.Chat、TensorSharp.Server.Host 与 TensorSharp.Distributed 尚未发布。
| 层 | 职责 |
|---|---|
| TensorSharp.Core | 核心 Tensor 类型、存储抽象、设备抽象,以及可扩展的运算注册表(Ops)。CPU 实现使用 System.Numerics.Vectors 做 SIMD。 |
| TensorSharp.Runtime | GGUF 解析、分词器(SentencePiece / BPE)、聊天模板渲染、采样、输出解析、分页 KV 缓存,以及连续批处理的调度器 / 引擎。提示词的框架化与回复解析现在按家族声明为 ChatProtocolRegistry 中的一份 ChatProtocol,而不再按架构名分支。 |
| TensorSharp.AgentHost | 构建在 Runtime 之上的可选智能体层:Agent Skills、模型 / 工具内部循环、沙箱化的脚本与代码执行、持久工作区、文件编辑、依赖安装与产物收集。Runtime 不反向引用它,因此普通推理宿主无需携带智能体机制。 |
| TensorSharp.Models | ModelBase 以及具体架构与多模态编码器。每个家族都是一个插件——在 BuiltInArchitectures 中注册一份 ModelArchitectureDescriptor;ModelBase.Create() 从 GGUF 元数据自动识别架构,再交给对应描述符的工厂方法。 |
| TensorSharp.Backends.GGML | 通过原生 C++ 桥接(libGgmlOps)链接 ggml 实现加速运算 —— macOS 上用 Metal,Windows/Linux 上用 CUDA 与 Vulkan,以及原生 CPU。 |
| TensorSharp.Backends.Cuda | 直接 CUDA 路径:CUDA Driver API、cuBLAS GEMM,以及热点运算的 PTX 内核,并带 CPU 回退。 |
| TensorSharp.Backends.MLX | Apple Silicon 的 MLX 路径,封装 mlx-c,提供量化、融合与编译内核。 |
| TensorSharp.Distributed | 多节点张量并行所用的点对点 TCP 协调层,与模型 / 运行时层分离。 |
| TensorSharp.Server | HTTP / 应用层:兼容 Ollama 与 OpenAI 的 REST API、浏览器聊天 UI、上传处理,以及每模型的连续批处理引擎宿主。 |
| TensorSharp.Cli | 控制台宿主,用于本地提示词、多模态实验、提示词检查、JSONL 批处理工作流、交互式 REPL 与基准测试。 |
架构是插件,而不是 switch 分支。一个家族只需声明自己一次,其余代码都不必知道它的名字:在 BuiltInArchitectures 里放一份 ModelArchitectureDescriptor(id、别名、工厂、显示名、针对无元数据 GGUF 的张量表识别、多 GPU 模式、原生调优项、投影器文件名提示),再加一份描述其文本格式的 ChatProtocol。下游一律改问能力接口,而不再比对架构字符串——IVisionCapableModel、IAudioCapableModel、IMRoPEPositionSink、IBatchedPagedModel、ISpeculativeTarget、ISpeculator。因此新增一个模型、一种模态或一种聊天格式,只需改它自己的描述符和自己的目录;ModelBase 本身也按关注点拆成了多个 partial(ModelBase.TensorParallel.cs、.CpuAttention.cs、.WeightLoading.cs、.KvCache.cs、.Warmup.cs)。具体步骤见 DEVELOPMENT.md 与模型卡片 README。
非 ggml 的执行路径共用一套原语:TensorSharp.Models/Direct/DirectOps.cs 中的 Direct{Context, Linear, Ops},Wan 视频网络与 MiniMax-H3 在 BackendType.Cuda 和 BackendType.Cpu 上都走它。在 CPU 上,DirectLinear 不再在加载时把量化权重展开成 F32,而是保留 GGUF 的存储类型:--backend cpu 下一个 256×160、5 帧、单步的 Wan 渲染从 121.4 s 降到 80.9 s,权重内存只占原来的四分之一,而且距原生 ggml_cpu 渲染反而略更近(43.51 dB,旧路径为 43.39 dB)。F16/BF16/F32 权重仍走普通 GEMM;TS_DIRECT_QUANT_WEIGHTS=0 可恢复旧行为。
纯托管的 cpu 后端现在也按 GGML 后端一贯的方式加载:量化权重直接从 GGUF 的文件映射零拷贝绑定,而不再复制进新分配的匿名内存;加载时会打印切分结果——GLM-5.3-Flash UD-Q2_K_XL 上是 Quantized: 103255 MB (103255 MB file-backed), F32: 983 MB,这个模型的加载从「永远跑不完」(常驻内存 412 GB 且还在涨)变成约 48 秒,其中大部分是页缓存预取。凡是此前需要复制权重的模型都会受益,量化后的大 checkpoint 收益最明显。ManagedQuantizedOps 还补上了 IQ2_XS 与 IQ4_XS——托管反量化实现(与 ggml 自身的 dequantize_row_* 对照验证过),并进入 CPU 量化存储矩阵,因此它们在加载时保持量化而不再展开成 F32——以及直接的 IQ2_XS × Q8_K 与 IQ3_XXS × Q8_K 点积内核(带 AVX2 路径 VecDotIq2XsQ8KAvx2、VecDotIq3XxsQ8KAvx2);此前这两种类型都只能走通用的「先把整行反量化到临时缓冲」路径。
对于任何尚未实现的运算,每个后端都会回退到 CPU,因此在所有后端上输出都保持正确 —— 你牺牲的只是速度,绝不是正确性。
一次请求如何流转
- 加载 ——
ModelBase.Create(path, backend)读取 GGUF 元数据、选择架构,并把量化权重映射到所选后端。 - 渲染 —— 提示词(以及任何系统消息、图像、音频、工具)经由架构的聊天模板和分词器转成 token。
- Prefill(预填充) —— 提示词 token 在一次批量前向中处理,填充 KV 缓存。
- Decode(解码) —— 逐 token 生成(可借助推测解码一次生成多个),按你的设置采样并流式返回。
- 服务 —— 在服务器中,连续批处理引擎将多个请求交错运行于同一模型之上,并在它们之间共享 KV 缓存前缀。
项目结构
仓库按上述分层组织。最有用的入口:
| 路径 | 内容 |
|---|---|
TensorSharp.Core/ | 张量库、运算、内存、设备抽象、CPU SIMD / 量化内核。 |
TensorSharp.Runtime/ | GGUF、分词器、模板、采样;Paged/ KV 原语与 Scheduling/ 推理引擎 + MTP 核心。 |
TensorSharp.AgentHost/ | 技能、渐进式披露、有界工具循环、OS 沙箱、会话 / 请求工作区、五个代码工具、依赖安装与生成产物。 |
TensorSharp.Models/Architecture/ | 架构插件表:ModelArchitectureDescriptor、ModelArchitectureRegistry、BuiltInArchitectures,以及多模态能力接口。 |
TensorSharp.Models/Models/<Family>/ | 每个架构一个文件夹(DeepSeek4、GlmDsa、Gemma4、Qwen35、Qwen4Exp、GptOss、Nemotron、Mistral3、MuseGlimmer、DiffusionGemma、QwenImage、MiniMaxH3、WanVideo)。自回归文本家族各含一个 legacy 与一个批量前向;DeepSeek4 与 GlmDsa 例外,它们使用整模型执行器;媒体家族(DiffusionGemma、QwenImage、MiniMaxH3、WanVideo)走的是扩散管线而非 decode 循环。 |
TensorSharp.Models/Direct/ | Direct{Context, Linear, Ops}——Wan 视频网络与 MiniMax-H3 在 cuda / cpu 后端上共用的非 ggml 执行原语。 |
TensorSharp.GGML.Native/ | 到 ggml 的原生 C++ 桥接(matmul、融合 transformer 内核、分页注意力、MoE、Mamba2、GatedDeltaNet、扩散)。 |
TensorSharp.Distributed/ | 多节点张量并行使用的 TCP 网格与集合通信。 |
TensorSharp.Server/ | ASP.NET Core 服务器:程序引导、模型服务、推理引擎宿主、聊天流水线、遥测。 |
docs/ | 各模型架构卡、分页注意力深入解析、环境变量矩阵、基准矩阵。 |
当前状态与能力
| 领域 | 状态 |
|---|---|
| 模型家族 | 共 15 个,由 ModelBase.Create() 依据 GGUF 的 general.architecture 分发:DeepSeek V4 Flash(deepseek4)、DeepSeek V4.1 Flash(deepseek41)、GLM 5.x(glm-dsa、glm5next)、Gemma 4(gemma4)、DiffusionGemma(diffusion-gemma、diffusion_gemma)、Qwen 3.5/3.6-family(qwen35、qwen35moe、qwen3next)、Qwen 3.8 Flash Next(qwen4exp)、GPT OSS(gptoss、gpt-oss)、Nemotron-H 含 Nemotron 3 Nano Omni(nemotron_h、nemotron_h_moe)、Mistral 3(mistral3)、Hunyuan Dense(hunyuan-dense)、Muse-Glimmer(muse-glimmer、muse_glimmer)、Qwen-Image-Edit 图像编辑(qwen_image、qwen-image)、MiniMax-H3 音视频联合生成(minimax-h3、minimax_h3),以及 Wan 2.1 / 2.2 视频生成(wan、wan2.1、wan2.2)。MiniMax-H3 是这条元数据规则的例外:它公开的 GGUF 完全没有元数据,因此 ModelBase.Create() 改用张量表来识别它。→ 支持的架构 |
| 推理宿主 | CLI、交互式 REPL、ASP.NET Core Web UI、Ollama 式 API、OpenAI Chat Completions 式 API,以及 iOS/iPadOS 上的 TensorAgent。 |
| 智能体工作 | TensorSharp.AgentHost 为 Agent Skills 运行有界的单助手工具循环;运维开启 --code-exec 后,还提供 read_file、edit_file、write_file、shell 与 apply_patch。TensorSharp 只执行自己的内置工具;调用方定义的工具仍回传调用方处理。工作区在 Web/CLI 聊天期间持久存在,OpenAI/Ollama 的每个请求则拥有隔离工作区;OS 隔离默认是必需的。这是智能体工具使用,并非子智能体编排或交互式审批服务。→ 智能体工作 |
| 后端 | 纯 C# CPU、直接 CUDA/cuBLAS(cuda)、MLX Metal(mlx)、GGML CPU、GGML Metal、GGML CUDA、GGML Vulkan,以及 iOS/iPadOS TensorAgent 目标(通过静态链接的 .xcframework 使用 ggml_metal)。DeepSeek V4 另有三套专属的整模型执行器——Direct CUDA、原生 ggml 与纯 C#——都会把权重按层切分到所有可见 GPU。DeepSeek V4.1 Flash 也有自己的一套:ggml_cuda 是它的服务后端,ggml_cpu 用那些融合算子回退到的标量内核跑同一套原生计算图,--backend cpu 跑纯 C# 的 DeepSeek4CpuExecutor——不用 ggml、不依赖原生库、也不需要 GPU——而 cuda 用 Direct CUDA 引擎自己的内核运行 V4.1;后两者都是正确性与可移植性路径,而非服务路径。GLM 5.x(GLM-5.2、GLM-5.3 与 GLM-5.3-Flash 相同)另有两套:ggml_cuda / ggml_vulkan / ggml_cpu / ggml_metal 上的原生 ggml 执行器(不传 --tp 时按层切分到所有卡;在 GGML GPU 后端上,传入 --tp N 则为 GLM-5.2、GLM-5.3 与 GLM-5.3-Flash 一样选择仅支持本地单进程的原生 TP),以及 cpu 与 cuda 上的托管逐算子路径——在 GGML 后端上也可以用 TS_GLM_NATIVE=0 强制走这条托管路径;MLX 不支持它。 |
| 提速手段 | 真正决定快慢的往往是选哪个权重,而不是加哪个参数。MiniMax-H3 是唯一的例外:它出厂即经 CFG 蒸馏,没有更快的文件可换——--cfg 1.0 下的 4–8 步就是工作点(默认 20 步),而 --width / --height 才是质量与成本之间的主导杠杆(文档给出的起点是 640×384)。步数蒸馏的 Wan DiT(文件名含 Turbo / Lightning / lightx2v / FastWan / …-4steps-…)在加载时被自动识别,只跑 4 次无引导 DiT 前向,而非基础配方的 100 次:同一个 1088×832×121 帧的图生视频请求,基础 Wan2.2-TI2V-5B 实测约 3 小时 30 分,Turbo 权重为 17 分 30 秒(M5 Pro、ggml_metal)。Qwen-Image-Edit 的 Lightning LoRA(--qwen-image-lora)同理,把 30 步 CFG 2.5 换成 4–8 步 CFG 1.0。文本模型这边的手段是:选对后端(NVIDIA 用 ggml_cuda,Apple Silicon 用 ggml_metal,CPU 推理用 ggml_cpu 而非纯托管的 cpu)、整模型融合 decode 计算图(GPT OSS 在 A40 上 24 → 154 tok/s)、推测解码、--tp N,以及让放不下的模型跑起来的 --n-cpu-moe。→ 模型 · 基准测试 |
| 多模态 | Gemma 4 图像/视频/音频;Qwen 3.5-family、Qwen 3.8 Flash Next、GLM-5.3-Flash、Mistral 3、Nemotron-H Omni 与 Muse-Glimmer 图像输入;DeepSeek V4.1 在用 --mmproj 挂上单独准备的视觉伴随文件后支持图像与视频(音频会被拒绝,而不是被忽略)。PDF 文档可经 Web UI 上传或 CLI --pdf 传入(提取文本并内联;扫描页渲染为图像交给视觉模型)。媒体输出:MiniMax-H3(H.264 MP4 外加一份原生 32 kHz 立体声 .wav,在同一份打包潜变量里一起去噪——文本 → 视频、图像 → 视频、首尾帧、参考 → 视频)、Qwen-Image-Edit(图像),以及 Wan 2.1 / 2.2(只有画面的 H.264 MP4,文本 → 视频与图像 → 视频)。→ 视频生成 |
| 连续批处理 | vLLM 式分页 KV 缓存、块哈希前缀共享、迭代级调度器(默认开启;可用 --no-continuous-batching 关闭)。DeepSeek V4 与 GLM 5.x 在同一引擎上通过各自原生的 per-sequence slot 提供服务——压缩后的 MLA 每 token 只有一行缓存,没有可分页的布局——GLM 默认启用批处理融合解码(4 路并发下总吞吐 1.81 倍;TS_BATCHED_FUSED_DECODE=0 可关闭)。Qwen 3.8 Flash Next 则使用 per-sequence 状态持有器:每个在飞请求拥有自己的 KV、GatedDeltaNet、PLE 与 indexer 状态,因此在请求之间切换只是换一个引用,而不需要回传状态。 |
| 推测解码 | Qwen 3.6(内嵌)、GLM-5.2 与 GLM-5.3(glm-dsa,同为内嵌——--spec 直接加载检查点自带的 NextN 块,无需额外下载,且只在默认的按层切分下生效:--tp N>1 时草稿块借用的是按列并行的主干 LM head,加载器会拒绝只凭某个 rank 手里那一段词表来起草;GLM-5.3-Flash 则完全没有实现 NextN),以及 Gemma 4(独立草稿 GGUF)的 MTP / NextN 草稿头;DeepSeek V4 的 DSpark 与 Muse-Glimmer 的 DFlash 块级起草(在 cuda / ggml_cuda 后端上通过 --draft-model 加载独立草稿 GGUF)。验证以贪心方式对齐主干,因此输出与普通贪心 decode 一致。默认关闭——服务端用 --spec(环境变量 TS_MTP_SPEC)启用,CLI 直接传 --draft-model 即可。→ DSpark |
| 多 GPU | --tp 这一个参数实际对应两种不同的机制。张量并行在每一层内部切分权重 —— Megatron-LM 列/行并行,norm、词嵌入与 LM head 复制 —— 可用于 Direct cuda 后端与 GGML CUDA / Vulkan 后端(--tp N / TENSORSHARP_TP_DEGREE,CLI 与服务端均支持),并可通过点对点 TCP 网格扩展为多节点分布式 TP(--tp-node-id / --tp-peers)。融合的按 rank 计算图与 MoE 专家并行让 Gemma 4 上 --tp 2 的 decode 快于单卡。分层切分(layer split)则是每张 GPU 拿一段连续的完整层、不切分任何权重,因此没有集合通信:DeepSeek V4 Flash、DeepSeek V4.1 Flash 与 GLM 5.x 不需任何参数即默认如此,Qwen 3.8 Flash Next 在 --tp N 下走的也是这条路。GLM 5.x(GLM-5.2、GLM-5.3 与 GLM-5.3-Flash 相同)传入 --tp N 时则在 GGML GPU 后端上选择仅支持本地单进程的原生 TP,且每个 rank 各复制一份 KV 缓存;跨节点的 --tp-node-id / --tp-peers 对整个 GLM 家族都会在构建模型之前被拒绝,而在 GLM-5.3 上这一模式虽被接受,却尚未实测。Qwen 的按层切分是容量特性而非速度特性:在 2× A100-80GB 上用 Qwen3.8-Flash-Next-UD-Q2_K_XL(73.4 GiB)实测,双卡输出与单卡逐字节相同,速度也一样(prefill 约 1520–1550 t/s,decode 约 56 t/s),变的只是权重从全部压在一张卡上变成 24.2 + 26.2 GB 分放。TS_Q4E_LAYER_SPLIT=20,28 可覆盖自动均衡;两种模式都不支持的架构会在 stderr 上明说,并仅使用一张 GPU。可选 Redis 支撑的共享 KV 缓存与 Responses API 存储。→ 多 GPU 与多节点 |
| 可观测性 | 结构化的逐轮日志、队列状态,以及跨 Web UI、Ollama 与 OpenAI 响应形态的 KV 缓存复用指标。 |