从 C# 使用 TensorSharp

TensorSharp 是一个真正的 .NET 库,而不只是可执行文件。从源码 checkout 引用其项目,即可在不经过 HTTP 的情况下从自己的代码驱动推理。

句向量编码器:Snowflake Arctic Embed L v2.0 / all-MiniLM-L6-v2,支持纯 C# CPU 与原生 GGML CPU/Metal/CUDA,通过 --embeddings 提供兼容 OpenAI/Ollama 的 API。 嵌入模型、支持的后端与验证 →

项目与包边界

仓库按可发布包边界拆分。源码树的发布集合是下表的全部 14 个包,由 eng/verify-packages.ps1 校验,并作为发布流水线的门禁。

包版本与内容对应选定的发布标签。个人知识库与回答引导在 v2026.10.10 后合入,需要包含它们的新构建;使用未发布 API 时请引用当前源码项目。发布流程可打包下表项目,不代表已检查某次发行的全部 NuGet 包或安装资产。

项目 / 包命名空间职责
TensorSharp.Core / TensorSharp.TensorsTensorSharp张量原语、运算、分配器、存储、设备抽象;NuGet ID 与项目名不同。
TensorSharp.Runtime.LoggingTensorSharp.Runtime.Logging各宿主共用的日志抽象与输出目标,不再压在引擎各层上。
TensorSharp.RuntimeTensorSharp.RuntimeGGUF 解析、分词器、提示词渲染、采样、分页 KV 缓存、连续批处理调度器。
TensorSharp.ModelsTensorSharp.ModelsModelBase、架构实现、多模态编码器、批量/分页前向。
TensorSharp.Backends.GGMLTensorSharp.GGMLGGML 支撑的执行与原生互操作。
TensorSharp.Backends.CudaTensorSharp.Cuda直接 CUDA 分配器、存储、cuBLAS GEMM、PTX 内核、量化 CUDA 运算。
TensorSharp.Backends.MLXTensorSharp.MLXApple Silicon MLX 后端(mlx-c / Metal)。
TensorSharp.KnowledgeTensorSharp.Knowledge独立个人存储、原文件、文档提取、版本化片段、BM25 与可选嵌入检索。知识库指南。
TensorSharp.ChatTensorSharp.Chat与宿主无关的聊天流水线——ModelService、会话、生成、技能循环、Web UI 请求/流式契约。不依赖 ASP.NET Core;服务器与 TensorAgent 使用其流水线。CLI 引用共享类型,但通过自己的循环直接驱动 Runtime。
TensorSharp.ServerTensorSharp.ServerASP.NET Core 库:OpenAI/Ollama 适配器、基于 TensorSharp.Chat 的 HTTP 传输、Web UI 资源。它是库——单独构建不产生可执行文件。
TensorSharp.Server.HostTensorSharp.Server.Host基于该库的可运行 Web 应用:Program.cs、宿主装配、wwwroot/ 与命令行。实际运行的是它。
TensorSharp.CliTensorSharp.Cli控制台宿主与调试 / 批处理工具。
TensorSharp.AgentHostTensorSharp.AgentHost.*技能、有界智能体循环、子智能体协调、四个代码工具、沙箱、工作区与产物。
TensorSharp.DistributedTensorSharp.Distributed点对点 TCP 分布式张量并行。

典型嵌入场景只需引用 TensorSharp.Models.csproj;它已引用 Core、Runtime 与各后端项目。若你的应用与 TensorSharp checkout 同级:

dotnet add reference ../TensorSharp/TensorSharp.Models/TensorSharp.Models.csproj
📦

使用所选版本的发布说明核对包内功能;需要标签之后的实现时使用当前项目引用。源码构建默认编译原生 GGML/MLX;开发托管 CPU 路径时传入 -p:TensorSharpSkipGgmlNative=true -p:TensorSharpSkipMlxNative=true,或按后端页面构建原生库。

知识库 SDK:KnowledgeStore、KnowledgeOptions、IKnowledgeExtractor 与 IKnowledgeEmbeddingProvider 位于独立项目;Chat 层的 KnowledgeRuntime.CreateStore 接入加载模型、GGUF 编码器与 OCR。应用须显式提供选择范围与认证边界。宿主配置 →

最小示例 —— 加载、生成、解码

每个模型都以相同方式加载:ModelBase.Create() 读取 GGUF 元数据并实例化正确的架构。从这里开始你做分词、运行前向、采样并解码。

using System;
using System.Collections.Generic;
using System.Linq;
using TensorSharp.Models;
using TensorSharp.Runtime;

// 1. 加载任意受支持的 GGUF —— 架构从元数据自动识别。
//    按你的构建选择 BackendType:GgmlCuda、GgmlMetal、GgmlVulkan 或 GgmlCpu。
using var model = ModelBase.Create("gemma-4-E4B-it-Q8_0.gguf", BackendType.GgmlCuda);

// 2. 配置采样(默认值与 Ollama 一致:temp 0.8、top_k 40、top_p 0.9)。
var sampling = new SamplingConfig { Temperature = 0.7f, TopP = 0.9f, TopK = 40 };

// 3. 对提示词分词。
var tokens = model.Tokenizer
    .Encode("Explain mixture-of-experts in one sentence.", addSpecial: true)
    .ToList();
var generated = new List<int>();

// 4. 完整提示词只 prefill 一次。
float[] logits = model.Forward(tokens.ToArray());

// 5. 每次只 decode 一个新 token。Forward() 自己维护 KV-cache 位置,
// prefill 后只传刚采样出的 token,不要重复传完整历史。
for (int step = 0; step < 200; step++)
{
    int next = model.Sample(logits, sampling, generated); // 应用惩罚 + 采样
    if (model.Tokenizer.IsEos(next)) break;
    generated.Add(next);
    logits = model.Forward(new[] { next });
}

// 6. 反分词得到结果。
Console.WriteLine(model.Tokenizer.Decode(generated));

要做贪心/确定性解码,请调用 model.SampleGreedy(logits) 而非 Sample。

冒烟测试变体

一次性健全性检查:加载模型、运行一次前向,并打印 top token:

using var model = ModelBase.Create(modelPath, backend);
var tokenIds = model.Tokenizer.Encode("Hello", addSpecial: true);
float[] logits = model.Forward(tokenIds.ToArray());

int topToken = model.SampleGreedy(logits);
Console.WriteLine($"vocab={model.Config.VocabSize}, tokens={tokenIds.Count}, topToken={topToken}");

DiffusionGemma — 文本扩散

DiffusionGemma 是块文本扩散模型,而非自回归模型。ModelBase.Create() 仍可加载它(GGUF 架构为 diffusion-gemma / diffusion_gemma)并返回 DiffusionGemmaModel,但其 Forward(int[]) 会故意抛出异常——生成需通过 DiffusionGemmaSampler,它在 [prompt | canvas] 序列上对定长的 canvas 块迭代去噪。架构详见 DiffusionGemma 模型卡。

using System;
using System.Collections.Generic;
using System.Linq;
using TensorSharp.Models;   // DiffusionGemmaModel、DiffusionGemmaSampler、DiffusionEbParams
using TensorSharp.Runtime;

// 加载 diffusion-gemma GGUF——架构会自动检测。
using var model = (DiffusionGemmaModel)ModelBase.Create("diffusion-gemma.gguf", BackendType.GgmlCuda);

// 用模型的聊天模板渲染 prompt,然后分词。
var messages = new List<ChatMessage> { new() { Role = "user", Content = "Write a haiku about winter." } };
var renderer = new GgufPromptRenderer();
string rendered = renderer.Render(model.Config.ChatTemplate, messages,
    addGenerationPrompt: true, architecture: model.Config.Architecture);
int[] promptTokens = model.Tokenizer.Encode(rendered, addSpecial: true).ToArray();

// 配置 EntropyBound 去噪采样器。
var p = new DiffusionEbParams
{
    MaxDenoisingSteps = 48,   // 每个 canvas 块的精化步数
    Seed = 0,                 // 确定性
    MaxBlocks = 1,            // 块自回归的 canvas 块数
};

var sampler = new DiffusionGemmaSampler(model);

// 生成。可选回调在每个去噪步后触发,参数为
// (blockIndex, step, totalSteps, previewTokens)——适合做实时 UI。
List<int> generated = sampler.Generate(promptTokens, p,
    (block, step, total, preview) => Console.Write($"\rblock {block + 1} step {step + 1}/{total}   "));

Console.WriteLine();
Console.WriteLine(model.Tokenizer.Decode(generated));

DiffusionEbParams 的关键参数:MaxDenoisingSteps(48)、TMin/TMax 温度调度(0.4 / 0.8)、EntropyBound(0.1)、StabilityThreshold / ConfidenceThreshold 早停、Seed 与 MaxBlocks。model.CanvasLength 给出每块的 canvas 大小。

图像输入与类型化决策。model.LoadVisionEncoder(path) 从 mmproj GGUF,或直接从 Hugging Face 分片 model-00011-of-00011.safetensors 加载 Gemma 4 视觉塔;音频与视频会被拒绝。加载视觉塔后,在用户消息上设置 ImagePaths,并在 Generate 之前让分词后的提示词依次经过 model.MultimodalInjector.ProcessPromptTokens(messages, tokens) 与 QueuePromptEmbeddings(0),做法与 CLI 相同。要做 Jev 风格的类型化决策,请引用 TensorSharp.Chat 并调用 ModelService.JevAsync(JevRequest.Parse(json.RootElement))(命名空间 TensorSharp.Server 与 TensorSharp.Server.Jev)——这正是 POST /v1/systemone 背后经过校验的路径。底层读取是 DiffusionGemmaModel.ReadStructured(promptTokens, seedCanvas, positions, tokenIds),调用期间需持有 GpuComputeLock。见 docs/models/jev_zh-cn.md。

🌫️

在 GPU 后端与纯 C# 的 cpu 后端上,prompt 的 K/V 每块缓存一次并在各去噪步间复用;GGML 后端默认使用融合的整模型解码加融合 lm-head 尾。调优开关(DIFFUSION_STEPS、DIFFUSION_NO_SC 等)见高级页。

Qwen-Image-2.1 — 图像生成与编辑

Qwen-Image-2.1 可以把提示词变成图像,也可以把提示词 + 一张或多张参考图变成编辑后的图像。所加载的 qwen_image GGUF 是扩散 Transformer;模型还会在 DiT 文件同目录解析伴随文件(或通过 TS_QWEN_IMAGE_VAE / TS_QWEN_IMAGE_TE / TS_QWEN_IMAGE_MMPROJ 环境变量指定):专用 2.1 VAE、Qwen3-VL-8B 文本编码器,以及编辑时必需的 mmproj 视觉编码器。与 DiffusionGemma 一样,它不是自回归文本模型——自回归入口会抛异常,图像由 GenerateImage() 与 EditImage() 产生。

using TensorSharp.Models;
using TensorSharp.Models.QwenImage;
using TensorSharp.Runtime;

// 加载 Qwen-Image-2.1 DiT GGUF(架构 = qwen_image)。2.1 VAE 与 Qwen3-VL-8B
// 文本编码器 + mmproj 从同一目录解析(或用 TS_QWEN_IMAGE_* 环境变量)。
// 任意 GGML 后端都可以,BackendType.Cpu(纯 C#)也可以。
using var model = (QwenImageModel)ModelBase.Create("qwen_image_2.1_Q4_K_M.gguf", BackendType.GgmlMetal);

var p = new QwenImageParams
{
    Steps = 0,          // 0 = 模型默认:40 步 FlowMatch-Euler
    CfgScale = 0f,      // 0 = 模型默认:1.0,每步一次 Transformer 预测
    Seed = 42,
    // Width = 1024, Height = 1024, // 显式尺寸,须为 32 的倍数(更快的草图)
};

// 文生图(默认 2048x2048)
RgbImage generated = model.GenerateImage("A small orange cat beside a blue ceramic vase, soft daylight", p);
ImageIO.SavePng("generated.png", generated);

// 编辑:这里只有一张参考图;多张参考图按顺序传入 IReadOnlyList<RgbImage>
RgbImage input = ImageIO.Load("generated.png", preserveAlpha: true);
RgbImage edited = model.EditImage("Change the blue vase to a red vase.", input, p);
ImageIO.SavePng("edited.png", edited);
Console.WriteLine($"Saved {edited.Width}x{edited.Height} edited image.");

若要做实时 UI,设置 p.OnStep = (step, total, preview) => { … } 与 p.PreviewCount,即可在节流的步上收到部分去噪潜变量解码出的 RGB 预览。RgbImage 暴露 Width、Height 与平面/交错的 Pixels 缓冲;ImageIO 提供 Load、Decode(byte[])、EncodePng、SavePng 及缩放辅助。PNG 输出保留透明度。

LoRA 插件。model.SetLoras(new[] { new LoraSpec("config/lora/qwen-image-2.1-viggle-turbo.json") }) 替换之后每个请求所用的插件组;LoraSpec(Path, Scale, ConfigPath) 接受与 --lora / --lora-scale / --lora-config 相同的三个值,空列表表示移除插件组。适配器会立即针对 Transformer 校验,失败时保留原来的插件组。当 Steps / CfgScale 为 0 时,由插件的采样配方提供步数与 CFG。见 LoRA 插件。

🖼️

图像生成计算量大:默认是 2048×2048、40 步。省略 Width/Height 时,生成为 2048×2048,编辑则取与第一张参考图宽高比一致、面积大致相同的尺寸;Width = 1024, Height = 1024 可把潜在图像 token 数降为四分之一,用于出草图。原生路径在 GgmlMetal、GgmlCuda、GgmlVulkan 或 GgmlCpu 上运行 GGML 图;BackendType.Cpu 用纯 C# 运行完整流水线,默认面积 1024×1024,并在推理前检查预计内存。伴随文件见图像生成。

局部编辑:在 EditImage() 之前设置 p.Mask = ImageIO.Load("selection.png", preserveAlpha: true),并按需设置 MaskMode、MaskInvert、MaskFeather、MaskCrop 与 MaskCropPadding。QwenImageMaskMode.Grayscale(默认)中白色表示编辑;Alpha 使用透明度。蒙版匹配第一张输入图,结果保留其画布与保护像素。蒙版语义 →

保持源图尺寸:设置 p.KeepSourceSize = true,编辑结果即为第一张输入图的精确宽高,采样面积约为图片自身面积,至少 1 百万像素、至多 TargetArea(或自动面积)。它不能与 Width/Height 同时使用,GenerateImage() 也不接受它。

音视频与嵌入模型

LTX 2.5 通过注册的 Ltx25Model 实现 IVideoGenerationModel,MiniMax-H3 与 Wan 使用同一视频接口;参数按模型契约校验。LTX 默认完整两阶段配方与 48 kHz 立体声。嵌入文本使用 EmbeddingModel.Load,EmbeddingGemma 2 媒体使用 EmbeddingGemma2MultimodalEncoder 与有序输入部分。 LTX 2.5 · 嵌入集成

SamplingConfig

采样旋钮与 CLI 参数及 API 选项一一对应。默认值与 Ollama 一致。

属性类型默认含义
Temperaturefloat0.8随机性;0 = 贪心/确定性。
TopKint40限制为概率最高的 K 个 token;0 = 禁用。
TopPfloat0.9核采样;1.0 = 禁用。
MinPfloat0相对最大值的最小概率阈值。
RepetitionPenaltyfloat1.1乘性惩罚;>1 抑制重复。
PenaltyLastNint64重复 / 存在 / 频率惩罚考虑的最近 token 数(两个宿主都用 --repeat-last-n;请求字段为 repeat_last_n)。
PresencePenaltyfloat0对已出现 token 的加性惩罚。
FrequencyPenaltyfloat0与 token 频率成比例的加性惩罚。
Seedint-1可复现采样;-1 = 基于时间。
StopSequencesList<string>null产生其中任一字符串时停止。
MaxTokensint0最大生成 token 数;0 = 使用调用方默认。

Agent Skills —— SkillsChatClient

Agent Skill(智能体技能)是一个面向模型的说明文件夹——一份 SKILL.md,外加它引用的脚本、参考文档与素材——模型只在任务需要时才加载它。SkillsChatClient(命名空间 TensorSharp.AgentHost.Skills,位于 TensorSharp.AgentHost 项目)就是 .NET 应用取用技能的入口。它通过 HTTP 调用兼容 OpenAI 的聊天端点,而不是在进程内加载 GGUF,因此与本页其余内容不同:它既不需要后端,也不需要引用 TensorSharp.Models。

在 TensorSharp 支持工具的聊天格式上,已选择和发现到的技能最初都只披露元数据;模型按需读取说明与随附文件。若模型没有可用的工具解析器,TensorSharp 会改为内联已选择技能的正文并隐藏内置技能/代码工具,使请求仍然有用,而不会假装工具往返能工作。

它覆盖实际会遇到的两种情形,由 SkillsChatClientOptions.Delivery 选择。

面向 TensorSharp 服务端(TensorSharp.Server.Host)—— SkillDelivery.Server

只需点名技能,其余全部由服务端在模型旁边完成,渐进式披露的循环也在那里跑。每次请求不上传任何东西,技能文件也从不离开服务端。

using System;
using System.Threading.Tasks;
using TensorSharp.AgentHost.Skills;

using var client = new SkillsChatClient(new SkillsChatClientOptions
{
    Endpoint = "http://localhost:5000",           // 带不带结尾的 /v1 都可以
    DefaultModel = "gemma-4-E4B-it-Q8_0.gguf",
    Delivery = SkillDelivery.Server,
});

// 单轮场景的便捷写法:提示词 + 要使用的技能。
SkillsChatResponse reply = await client.CompleteAsync(
    SkillsChatRequest.User("Extract the tables from report.pdf", "pdf"));

Console.WriteLine(reply.Content);
Console.WriteLine($"{reply.Rounds} 轮,prompt {reply.PromptTokens} + completion {reply.CompletionTokens} token");

服务端交付模式下,渐进式披露的循环跑在服务端内部,因此 reply.SkillInvocations 返回为空,reply.Rounds 为 1 —— 本客户端恰好只发出了一次请求。

面向任意其他兼容 OpenAI 的端点 —— SkillDelivery.Local

把客户端指向一个本地 SkillRegistry,它就会在本进程内构造提示词块、声明技能工具并运行整个循环,于是一个从没听说过技能的端点表现得就像原生支持一样。代价是模型每读一个文件多一次网络往返。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using TensorSharp.Runtime;          // ChatMessage
using TensorSharp.AgentHost.Skills;

var registry = new SkillRegistry(new SkillRegistryOptions
{
    Roots = new[] { "/srv/skills" },   // 最深扫描 MaxDepth(3)层
});

using var client = new SkillsChatClient(new SkillsChatClientOptions
{
    Endpoint = "https://api.example.com/v1",
    ApiKey = Environment.GetEnvironmentVariable("EXAMPLE_API_KEY"),
    DefaultModel = "some-hosted-model",
    Delivery = SkillDelivery.Local,
    Registry = registry,
    Discovery = true,                  // 同时展示本次请求没有点名的技能
});

SkillsChatResponse reply = await client.CompleteAsync(new SkillsChatRequest
{
    Messages = { new ChatMessage { Role = "user", Content = "Fill in this AcroForm and tell me what you set." } },
    Skills = { "pdf" },
    MaxTokens = 800,
});

Console.WriteLine(reply.Content);

// 模型读过的每一个技能文件,按顺序排列。
foreach (SkillToolInvocation call in reply.SkillInvocations)
    Console.WriteLine($"round {call.Round}: {call.Tool} {call.SkillId}/{call.ResourcePath} ok={call.Ok}");

默认值 SkillDelivery.Auto 会探测一次 /v1/skills,并把结果缓存到该客户端的整个生命周期:端点有响应就走服务端交付,没有响应且本客户端带了注册表就走本地交付。await client.ResolveDeliveryAsync() 会告诉你最终选了哪一种;await client.ListServerSkillsAsync() 则向端点询问它有哪些技能,端点没有实现技能 API 时返回空列表而不是抛异常。

🔁

SkillsChatResponse.ToolCalls 装的是对调用方自己的工具(即传入 SkillsChatRequest.Tools 的那些)的调用,客户端从不执行它们——只有调用方知道这些工具做什么。列表非空就意味着你需要自己执行它们,并在后续请求中把结果送回;技能工具此时已经应答完毕,reply.Messages 就是可以直接续写的完整对话记录。请求失败会抛出 SkillsChatException。

SkillsChatClientOptions

属性类型默认含义
Endpointstring必填API 根地址,带不带结尾的 /v1 都可以。
ApiKeystring?null端点需要时使用的 Bearer token。TensorSharp.Server.Host 不需要。
DefaultModelstring?null未按请求单独指定时,随每次请求发送的模型名。
DeliverySkillDeliveryAuto由谁解析技能:Auto、Server 或 Local。
RegistrySkillRegistry?null本地交付时使用的技能集合。服务端交付时忽略——那时技能归端点所有。
PromptOptionsSkillPromptOptions.Default本地交付的提示词预算。
LoopOptionsSkillAgentLoopOptions.Default本地交付的循环上限 —— MaxRounds 8、MaxCallsPerRound 8,以及每次工具调用执行后触发的 OnInvocation 回调。
MultiAgentMultiAgentOptionsnew()(开启)本地交付下的子智能体委派:模型可以派生子智能体,每个子智能体与同一端点进行各自独立的对话。设置了 LoopOptions.MultiAgent 时以它为准。端点被识别为 TensorSharp 或带技能 API 时,本地交付会发送 "multi_agent": false,免得服务端也去委派。
Discoverybooltrue把本次请求未点名的技能也展示给模型,好让它自己发现。SkillsChatRequest.Discovery 可按请求覆盖。
TimeoutTimeSpan10 分钟单次请求超时。仅在客户端自行创建 HttpClient 时生效——若从 IHttpClientFactory 取一个传进构造函数,则不会改动它。

SkillsChatRequest 与 SkillsChatResponse

成员类型含义
SkillsChatRequest.MessagesList<ChatMessage>对话内容。首条 system 消息会与技能文本块合并,而不是被顶掉。
SkillsChatRequest.SkillsList<string>要使用的技能名,取自注册表中的名称。
SkillsChatRequest.ToolsList<ToolFunction>?调用方自己的工具。客户端从不执行,会原样回传给调用方处理。
SkillsChatRequest.Model · MaxTokens · Temperature · TopP · Think · Discoverystring? · int? · double? · double? · bool · bool?按请求覆盖的设置;为 null 时交由端点决定(Discovery 则回落到客户端自身的选项)。
SkillsChatRequest.MultiAgentbool?false 为本次请求关闭委派——本地交付时在本进程内关闭,服务端交付时发送 "multi_agent": false。为 null 时遵循客户端(或服务端)的策略。
SkillsChatRequest.User(prompt, params skills)静态方法单轮常见场景的便捷写法。
SkillsChatResponse.Content · Thinkingstring · string?助手的回答,以及模型给出推理内容时的推理文本。
SkillsChatResponse.ToolCallsIReadOnlyList<ToolCall>对调用方自己的工具的调用,客户端从不执行。
SkillsChatResponse.MessagesList<ChatMessage>完整对话记录,含披露循环追加的全部内容。
SkillsChatResponse.SkillInvocationsIReadOnlyList<SkillToolInvocation>父智能体与各子智能体的每一次宿主工具调用,按完成顺序排列 —— Round、Tool、SkillId、ResourcePath、Ok、ResultBytes,以及 AgentId(父智能体为 /root)。服务端交付时为空。
SkillsChatResponse.FinishReason · PromptTokens · CompletionTokens · Roundsstring? · int · int · int生成停止的原因、逐轮累加的 token 数,以及实际跑了几次生成。Rounds 为 1 表示模型没读任何东西就作答了。

SkillRegistry 与 SkillRegistryOptions

注册表负责发现(在配置的目录中查找 SKILL.md)、安装与查找;它是唯一接触技能存储的组件,因此边界约束规则集中在一处。CLI 与服务端也正是用 --skills-dir 构造出它。

成员类型 / 默认含义
SkillRegistryOptions.RootsIReadOnlyList<string>,空要扫描的目录,按优先级顺序排列。一个根目录可以是单个技能(直接含 SKILL.md),也可以是装着若干技能的目录。
SkillRegistryOptions.InstallDirectorystring?,null运行时安装的技能写入的位置;同样会被扫描,并且优先级永远排在最前。为 null 时注册表只读。
SkillRegistryOptions.MaxDepthint,3在一个根目录下查找 SKILL.md 的最大深度。
SkillRegistryOptions.MaxSkills · MaxManifestBytes · MaxSkillBytes · MaxSkillFiles512 · 4 MB · 256 MB · 4096各项上限,好让指错目录的根目录显式失败,而不是耗尽内存。
Skills · Errors · RootsIReadOnlyList<Skill> · IReadOnlyList<SkillLoadError> · IReadOnlyList<string>成功加载的技能、加载失败的项(含 Path 与 Message),以及实际扫描过的根目录。
CanInstall · InstallDirectorybool · string?是否配置了运行时安装,以及安装到哪里。
TryGet(id, out Skill) · Resolve(ids, out unknown)bool · IReadOnlyList<Skill>按名称查找单个技能,或解析一组选择并得知哪些名称没找到。
Refresh()SkillScanResult重新扫描全部根目录 —— POST /api/skills/rescan 调用的就是它。
InstallFromDirectory(path, overwrite) · InstallFromZip(stream, overwrite, limits) · Remove(id)Skill · Skill · bool从文件夹或上传的压缩包安装技能,或删除一个已安装的技能。压缩包的每个条目都要经过与模型读取相同的那道路径关卡;被拒绝的压缩包会抛出 SkillInstallException。

完整参考(frontmatter 字段、提示词预算与安全模型)见 docs/agent_skills.md。可直接取用的开源技能:github.com/anthropics/skills。

AgentHost 运行时层

TensorSharp.AgentHost 是构建在 TensorSharp.Runtime 之上的可选智能体层;Runtime 不反向依赖它。CodeExecOptions、ShellRunner 与 CodeRunnerAdapter 实现宿主拥有的四个工具:read_file、write_file、shell、apply_patch。只有宿主提供持久工作区时才声明文件工具;没有工作区的直接调用方只获得 shell。

SkillAgentLoop 运行有界循环,只执行 TensorSharp 自有的技能/代码调用,并把应用自身工具的调用放入 SkillLoopResult.PendingClientToolCalls 返回。同时设置 SkillAgentLoopOptions.MultiAgent 与 SubagentGeneratorFactory(一个为每个子智能体返回独立生成器的 Func<string, SkillTurnGenerator>)后,循环会通过 spawn_agent、wait_agent、send_input、close_agent 与 list_agents 协调一棵请求级的子智能体树;设置了已启用的 MultiAgent(其 Enabled 默认为 true)却没有工厂时会抛出 ArgumentException。MultiAgentOptions(位于 TensorSharp.AgentHost.Agents)限定这棵树:MaxConcurrentAgents 3、MaxAgents 8、MaxDepth 2、MaxRoundsPerAgent 8、MaxTotalChildGenerations 48、AgentTimeoutSeconds 180、MaxResultCharacters 8000、MaxTaskCharacters 16000,AllowWorkerTools 默认关闭。回调携带 SkillToolInvocation.AgentId。宿主默认最多生成 8 轮;提供代码工具时提升为 24,除非运维方显式设定。SessionWorkspaceManager 为每个服务器会话提供私有工作区;仅请求调用方则在一次请求内共享。工作文件、允许导出的环境状态与已安装包会在该作用域内保留,而 PATH 每次 shell 调用都会重建。

🔒

代码工具默认关闭,沙箱模式默认 required。macOS Seatbelt 无法保证清理刻意脱离的子进程;Linux 需要 bubblewrap 0.12+;Windows 作业对象无法约束文件或套接字,因此执行必须显式选择无隔离模式。这些是启动权限,不是逐命令审批。子智能体默认只读,除非设置了 AllowWorkerTools(且只有 worker 子智能体能获得父智能体的可写工具);它们永远拿不到应用自己的工具,并与父智能体共用一道宿主工具闸门,因此一棵树内的工具调用逐个执行。结构化输出请求会内联已选择技能并隐藏内置工具。完整边界见智能体工作。

关键类型与接口

类型角色
ModelBase每个架构的抽象基类。Create(path, backend)、Forward(int[])、Sample(...)、SampleGreedy(...),外加 Config 与 Tokenizer。
BackendType枚举:Cpu、GgmlCpu、GgmlMetal、GgmlCuda、GgmlVulkan、Cuda、Mlx。
SamplingConfig采样配置(见上表)。
ITokenizerEncode(text, addSpecial)、Decode(ids)、IsEos(id)、EosTokenIds(BPE 与 SentencePiece 实现)。
ModelConfig架构元数据:VocabSize、上下文长度等。
IBatchedPagedModel可选的批量/分页前向(ForwardBatch),多数架构为连续批处理而实现。
DiffusionGemmaModel + DiffusionGemmaSampler文本扩散模型及其 EntropyBound 去噪采样器(Generate(promptTokens, DiffusionEbParams, …)),通过 LoadVisionEncoder 接收图像输入,通过 ReadStructured 做一步式 Jev 读取。Forward() 不受支持。
QwenImageModel + QwenImageParamsQwen-Image-2.1 图像生成与编辑器。GenerateImage(prompt, QwenImageParams) 与 EditImage(prompt, RgbImage 或 IReadOnlyList<RgbImage>, QwenImageParams) 返回 RgbImage;ImageIO 负责 PNG 加载/保存。
SkillsChatClient + SkillsChatClientOptions面向兼容 OpenAI 的端点使用 Agent Skills。CompleteAsync(SkillsChatRequest) 返回 SkillsChatResponse;SkillDelivery 决定披露循环由服务端还是本进程运行(位于 TensorSharp.AgentHost.Skills)。
MultiAgentOptions + MultiAgentSession子智能体上限,以及 spawn_agent / wait_agent / send_input / close_agent / list_agents 背后那棵请求级智能体树(位于 TensorSharp.AgentHost.Agents)。由 SkillAgentLoop、SkillsChatClient 与服务端聊天路径使用。
SkillRegistry + SkillRegistryOptions宿主已知的技能集合:在配置的根目录中扫描 SKILL.md、从文件夹或 .zip 安装,以及查找。它是唯一接触技能存储的组件,因此边界约束集中在一处生效。
InferenceEngine支撑服务器连续批处理的 worker 线程调度器 + 分页块池(在 TensorSharp.Runtime.Scheduling 中)。

其他值得了解的运行时契约:IModelArchitecture、IPromptRenderer(由 GgufPromptRenderer 实现)、IOutputProtocolParser 以及 IMultimodalInjector。

💡

对多数应用而言,最简单的集成是运行 TensorSharp.Server.Host 并通过兼容 OpenAI 的 API 调用它 —— 你的应用进程保持干净,并免费获得连续批处理。当你需要进程内控制或自定义解码时,再使用库 API。