From e2c98a86127cf62bfd388b1255fcdec18e803669 Mon Sep 17 00:00:00 2001 From: luisquintanilla Date: Thu, 5 Mar 2026 16:41:43 -0500 Subject: [PATCH 1/2] Add meai-embeddings skill Adds the MEAI embeddings skill covering IEmbeddingGenerator setup, batch generation, dimension selection, and caching. Fixes https://github.com/dotnet/skills/issues/228 Part of https://github.com/dotnet/skills/issues/225 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../dotnet-ai/skills/meai-embeddings/SKILL.md | 144 ++++++++++++++++++ tests/dotnet-ai/meai-embeddings/eval.yaml | 28 ++++ 2 files changed, 172 insertions(+) create mode 100644 plugins/dotnet-ai/skills/meai-embeddings/SKILL.md create mode 100644 tests/dotnet-ai/meai-embeddings/eval.yaml diff --git a/plugins/dotnet-ai/skills/meai-embeddings/SKILL.md b/plugins/dotnet-ai/skills/meai-embeddings/SKILL.md new file mode 100644 index 0000000000..7f13775f9e --- /dev/null +++ b/plugins/dotnet-ai/skills/meai-embeddings/SKILL.md @@ -0,0 +1,144 @@ +--- +name: meai-embeddings +description: Generate text embeddings in .NET using Microsoft.Extensions.AI IEmbeddingGenerator for semantic similarity, vector search, RAG, clustering by meaning, and document deduplication. Do not use for tabular data features (use mlnet), storing or searching vectors (use vector-data-search), or end-to-end RAG pipelines (use rag-pipeline). +--- + +# MEAI Embeddings + +Generate and use text embeddings in .NET through the `IEmbeddingGenerator>` abstraction from Microsoft.Extensions.AI, with support for OpenAI, Azure OpenAI, Azure AI Inference, and Ollama providers. + +## When to Use + +- Generating text embeddings for semantic similarity comparisons +- Producing vectors for storage in a vector database +- Building the embedding step of a RAG pipeline +- Clustering documents by meaning +- Deduplicating documents based on semantic content + +## When Not to Use + +- Extracting features from tabular data (use the `mlnet` skill) +- Storing and searching vectors in a database (use the `vector-data-search` skill) +- Building an end-to-end RAG pipeline (use the `rag-pipeline` skill) + +## Inputs + +| Input | Required | Description | +|-------|----------|-------------| +| Text to embed | Yes | One or more strings to generate embeddings for | +| Embedding provider | Yes | The provider SDK: OpenAI, Azure.AI.OpenAI, Azure.AI.Inference, or OllamaSharp | +| Embedding model | Yes | Model name (e.g., `text-embedding-3-small`, `text-embedding-3-large`) | +| API key / endpoint | Yes | Provider credentials, loaded from configuration | + +## Workflow + +### Step 1: Install packages + +Add the abstraction package and one provider package: + +```bash +dotnet add package Microsoft.Extensions.AI +``` + +Then add the provider package for your backend: + +```bash +# OpenAI +dotnet add package OpenAI + +# Azure OpenAI +dotnet add package Azure.AI.OpenAI + +# Azure AI Inference +dotnet add package Azure.AI.Inference + +# Ollama +dotnet add package OllamaSharp +``` + +`Microsoft.Extensions.AI` includes middleware such as `UseRateLimiting()` — no extra package is needed for rate limiting. + +### Step 2: Register IEmbeddingGenerator in DI + +Use `AddEmbeddingGenerator` to register the generator with optional middleware: + +```csharp +builder.Services.AddEmbeddingGenerator(b => b + .UseRateLimiting() + .UseOpenTelemetry() + .UseLogging() + .Use(new OpenAIClient(apiKey) + .GetEmbeddingClient(model) + .AsIEmbeddingGenerator())); +``` + +Replace the inner provider call for other backends: + +```csharp +// Azure OpenAI +new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential()) + .GetEmbeddingClient(model) + .AsIEmbeddingGenerator() + +// Azure AI Inference +new EmbeddingsClient(new Uri(endpoint), new AzureKeyCredential(key)) + .AsIEmbeddingGenerator() + +// Ollama +new OllamaApiClient(new Uri("http://localhost:11434"), model) + .AsIEmbeddingGenerator() +``` + +### Step 3: Generate embeddings + +Inject `IEmbeddingGenerator>` and generate embeddings: + +```csharp +// Single text +var embedding = await generator.GenerateAsync("text to embed"); +ReadOnlyMemory vector = embedding[0].Vector; + +// Batch of texts +var embeddings = await generator.GenerateAsync(["first text", "second text", "third text"]); +for (int i = 0; i < embeddings.Count; i++) +{ + ReadOnlyMemory v = embeddings[i].Vector; +} +``` + +Always prefer batch generation over calling `GenerateAsync` once per string — it reduces HTTP round-trips and is more efficient. + +### Step 4: Choose embedding model dimensions + +| Model | Dimensions | Notes | +|-------|-----------|-------| +| `text-embedding-3-small` | 1536 | Lower cost, good for most use cases | +| `text-embedding-3-large` | 3072 | Higher accuracy, larger storage footprint | + +Some providers allow reducing dimensions via an API parameter to save storage. Ensure the dimensionality you choose matches your vector store index configuration. + +### Step 5: Verify + +- [ ] Embeddings have the expected dimensionality for the chosen model +- [ ] Semantically similar texts produce high cosine similarity scores +- [ ] Rate limiting middleware prevents HTTP 429 errors under load + +## Validation + +- [ ] `IEmbeddingGenerator>` is registered via DI and can be resolved +- [ ] API keys and endpoints are loaded from configuration (not hard-coded) +- [ ] Batch generation is used when embedding multiple texts (not one-at-a-time calls) +- [ ] Rate limiting is configured for production workloads + +## Common Pitfalls + +| Pitfall | Solution | +|---------|----------| +| Generating embeddings one at a time instead of batching | Pass a list to `GenerateAsync` to embed multiple texts in a single call | +| Embedding model dimensions don't match the vector store index | Verify the model's output dimensions match your vector store's configured dimensions | +| Mixing embeddings from different models in the same collection | Always use a single model per collection; re-embed all documents when switching models | +| Not configuring rate limiting and hitting provider quotas | Add `.UseRateLimiting()` in the middleware pipeline to throttle requests | + +## More Info + +See https://learn.microsoft.com/dotnet/ai/microsoft-extensions-ai for the full Microsoft.Extensions.AI reference. diff --git a/tests/dotnet-ai/meai-embeddings/eval.yaml b/tests/dotnet-ai/meai-embeddings/eval.yaml new file mode 100644 index 0000000000..3657108c96 --- /dev/null +++ b/tests/dotnet-ai/meai-embeddings/eval.yaml @@ -0,0 +1,28 @@ +scenarios: + - name: "Generate embeddings for documents" + prompt: "Add embedding generation to this .NET 8 console app. It should generate embeddings for a list of text documents using OpenAI's text-embedding-3-small model." + setup: + files: + - path: "EmbeddingApp/EmbeddingApp.csproj" + content: | + + + Exe + net8.0 + + + - path: "EmbeddingApp/Program.cs" + content: | + Console.WriteLine("Hello, World!"); + assertions: + - type: "output_contains" + value: "IEmbeddingGenerator" + - type: "output_contains" + value: "GenerateAsync" + - type: "exit_success" + rubric: + - "Uses IEmbeddingGenerator from Microsoft.Extensions.AI" + - "Configures OpenAI as the embedding provider" + - "Uses batch generation for multiple documents (not one at a time)" + - "API key loaded from configuration, not hardcoded" + timeout: 360 From 8b217585a08cc81223dec12f1916d6560f4ce7ea Mon Sep 17 00:00:00 2001 From: luisquintanilla Date: Fri, 6 Mar 2026 11:38:11 -0500 Subject: [PATCH 2/2] Update .NET 8 references to .NET 10 (current LTS) .NET 10 is the current LTS (released Nov 2025). .NET 8 reaches end-of-support Nov 2026. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/dotnet-ai/meai-embeddings/eval.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/dotnet-ai/meai-embeddings/eval.yaml b/tests/dotnet-ai/meai-embeddings/eval.yaml index 3657108c96..bac6666731 100644 --- a/tests/dotnet-ai/meai-embeddings/eval.yaml +++ b/tests/dotnet-ai/meai-embeddings/eval.yaml @@ -1,6 +1,6 @@ scenarios: - name: "Generate embeddings for documents" - prompt: "Add embedding generation to this .NET 8 console app. It should generate embeddings for a list of text documents using OpenAI's text-embedding-3-small model." + prompt: "Add embedding generation to this .NET 10 console app. It should generate embeddings for a list of text documents using OpenAI's text-embedding-3-small model." setup: files: - path: "EmbeddingApp/EmbeddingApp.csproj" @@ -8,7 +8,7 @@ scenarios: Exe - net8.0 + net10.0 - path: "EmbeddingApp/Program.cs"