Microsoft Agent Framework Concept ( Chapter-2) Created: 20 Apr 2026 Updated: 20 Apr 2026

Vector Databases

A vector database is a specialized storage system designed to hold and search embedding vectors — the numeric representations of words, images, and audio that preserve their semantic meaning. While a traditional relational database is optimized for exact matches on columns, a vector database is optimized for similarity: finding the items whose vectors are closest to a query vector in a high-dimensional space.

Vector databases are the storage backbone behind semantic search, recommendations, classification, image retrieval, and retrieval-augmented generation (RAG). They give an LLM long-term memory over data it was never trained on.

Why a Dedicated Database?

Embedding vectors are typically 512–3,072 floats each. Comparing a query vector against millions of stored vectors with brute-force math is too slow for interactive use. Vector databases solve this with specialized approximate nearest neighbor (ANN) indexes such as HNSW, IVF, and DiskANN, which can run similarity searches in milliseconds.

On top of fast search, a modern vector database provides:

  1. CRUD operations — upsert, retrieve, delete records.
  2. Metadata storage — keep the original payload next to the vector.
  3. Filtering — combine vector similarity with structured WHERE-style filters.
  4. Hybrid search — merge vector similarity with full-text keyword search.
  5. Scalability — handle millions or billions of vectors.

The End-to-End Workflow

  1. Inputs — raw non-numeric data: words, images, audio.
  2. Vectorization — an embedding model turns each input into a numeric vector that preserves its meaning.
  3. Vector Database — the vectors (plus the original record) are stored and indexed.
  4. AI Model — at query time the model compares a user's vector against the stored vectors and pulls the most relevant records.
  5. Outputs — the retrieved records are used to summarize text, find related data, or generate new content such as images.

Common Vector Database Providers

ProviderTypeNotes
Azure AI SearchCloud serviceFull-text + vector + hybrid search, with enterprise features.
Azure Cosmos DBMulti-model databaseVector search alongside document, key-value, and graph data.
Azure SQL / SQL Server 2025Relational databaseNative vector column type.
PostgreSQL + pgvectorRelational databaseOpen-source extension that adds vector columns and indexes.
Qdrant / Milvus / Weaviate / PineconeDedicated vector DBPurpose-built for high-volume vector workloads.
InMemoryVectorStoreIn-processLightweight store, ideal for prototyping and demos.

Vector Databases in .NET

The Microsoft.Extensions.VectorData library defines a provider-agnostic API. Every provider implements the same VectorStore and VectorStoreCollection<TKey,TRecord> abstractions, so you can swap one backend for another without rewriting your application logic.

Install the packages used in this demo:

dotnet add package Microsoft.Extensions.AI.OpenAI
dotnet add package Microsoft.SemanticKernel.Connectors.InMemory

Defining a Vector Store Record

A record is a plain C# class decorated with attributes:

AttributePurpose
[VectorStoreKey]Marks the primary key property.
[VectorStoreData]Marks a payload property stored alongside the vector.
[VectorStoreData(IsIndexed = true)]Makes a property usable in filters.
[VectorStoreVector(n)]Marks the embedding property, where n is the vector dimension.

Core Collection Operations

MethodDescription
EnsureCollectionExistsAsync()Creates the collection if it doesn't already exist.
UpsertAsync(record or IEnumerable)Inserts or replaces one or many records in a single call.
GetAsync(key)Retrieves a single record by its key.
SearchAsync(vector, top: N)Returns the N most similar records.
SearchAsync(vector, top: N, new VectorSearchOptions<T> { Filter = ... })Similarity search with a LINQ metadata filter.
DeleteAsync(key)Deletes a record by its key.

Full Example

The following demo builds a small museum artifact catalog. Each artifact has an era, a title, and a curator's description. The demo creates a collection, batch-upserts all artifacts, runs a plain semantic search, runs a filtered search restricted to one era, then retrieves, updates, and deletes individual records to show every core CRUD operation.

using Microsoft.Extensions.AI;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.InMemory;
using OpenAI;

namespace MicrosoftAgentFrameworkLesson.ConsoleApp;

// ── Vector-store record for a museum artifact ──────────────

public class MuseumArtifact
{
[VectorStoreKey]
public int InventoryNumber { get; set; }

[VectorStoreData(IsIndexed = true)]
public string Era { get; set; } = string.Empty;

[VectorStoreData]
public string Title { get; set; } = string.Empty;

[VectorStoreData]
public string Description { get; set; } = string.Empty;

[VectorStoreVector(1536)]
public ReadOnlyMemory<float> DescriptionVector { get; set; }
}

// ── Demo class ────────────────────────────────────────────

public static class VectorDatabaseMuseumDemo
{
// Archive of museum artifacts — each with an era for metadata filtering
private static readonly (int InventoryNumber, string Era, string Title, string Description)[] ArtifactCatalog =
[
(1001, "Ancient Egypt",
"Alabaster Canopic Jar",
"Carved ceremonial jar used to preserve the liver of a pharaoh during mummification, decorated with a jackal-headed stopper representing the god Duamutef."),
(1002, "Ancient Egypt",
"Gilded Funerary Mask",
"Wooden funerary mask covered in gold leaf and inlaid with lapis lazuli eyes, once placed over the face of a high-priest in the Valley of the Kings."),
(1003, "Ancient Rome",
"Bronze Gladius Short Sword",
"Double-edged cast bronze military short sword used by Roman legionaries during close-quarter formation combat along the Rhine frontier."),
(1004, "Ancient Rome",
"Frescoed Wall Panel from Pompeii",
"Fragment of a villa wall showing a seated muse holding a stringed lyre, painted with mineral pigments shortly before the eruption of Vesuvius in 79 AD."),
(1005, "Medieval Europe",
"Illuminated Psalter Manuscript",
"Hand-copied book of psalms with gold-leaf initials, ornate marginal drolleries, and blue pigment ground from lapis imported along the Silk Road."),
(1006, "Medieval Europe",
"Chainmail Hauberk",
"Knee-length protective garment woven from thousands of riveted iron rings, worn by a mounted knight under a surcoat during the Crusades."),
(1007, "Edo Japan",
"Lacquered Samurai Kabuto Helmet",
"War helmet formed from iron plates and finished with red urushi lacquer, topped with a pair of forged-metal crescent horns signaling clan identity."),
(1008, "Edo Japan",
"Ukiyo-e Woodblock Print",
"Multi-colored woodblock print depicting a crashing wave over Mount Fuji, printed on mulberry paper using carved cherry-wood blocks and mineral pigments."),
];

public static async Task RunAsync()
{
var apiKey = Environment.GetEnvironmentVariable("OPEN_AI_KEY");
if (string.IsNullOrWhiteSpace(apiKey))
{
Console.WriteLine("Please set the OPEN_AI_KEY environment variable.");
return;
}

IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator =
new OpenAIClient(apiKey)
.GetEmbeddingClient("text-embedding-3-small")
.AsIEmbeddingGenerator();

// ══════════════════════════════════════════════
// PART 1 — Create the Collection
// ══════════════════════════════════════════════
Console.WriteLine("═══ Part 1: Create the Vector Store Collection ═══\n");

var vectorStore = new InMemoryVectorStore();
var collection = vectorStore.GetCollection<int, MuseumArtifact>("artifacts");
await collection.EnsureCollectionExistsAsync();

Console.WriteLine("Collection 'artifacts' is ready.\n");

// ══════════════════════════════════════════════
// PART 2 — Vectorize and Batch Upsert
// ══════════════════════════════════════════════
Console.WriteLine("═══ Part 2: Vectorize and Batch Upsert ═══\n");

var descriptions = ArtifactCatalog.Select(a => a.Description).ToArray();
var vectors = await embeddingGenerator.GenerateAsync(descriptions);

var artifacts = ArtifactCatalog
.Select((a, i) => new MuseumArtifact
{
InventoryNumber = a.InventoryNumber,
Era = a.Era,
Title = a.Title,
Description = a.Description,
DescriptionVector = vectors[i].Vector,
})
.ToList();

await collection.UpsertAsync(artifacts);
Console.WriteLine($"Indexed {artifacts.Count} artifacts across multiple eras.\n");

// ══════════════════════════════════════════════
// PART 3 — Plain Vector Search
// ══════════════════════════════════════════════
Console.WriteLine("═══ Part 3: Semantic Vector Search ═══\n");

const string curatorQuestion = "a protective piece of armor worn by a warrior in battle";
Console.WriteLine($"Question: \"{curatorQuestion}\"");

var questionVector = await embeddingGenerator.GenerateVectorAsync(curatorQuestion);
var hits = collection.SearchAsync(questionVector, top: 3);

await foreach (var hit in hits)
{
Console.WriteLine($" [{hit.Score:F3}] {hit.Record.Title} ({hit.Record.Era})");
}
Console.WriteLine();

// ══════════════════════════════════════════════
// PART 4 — Filtered Vector Search (Metadata Pre-Filter)
// ══════════════════════════════════════════════
Console.WriteLine("═══ Part 4: Filtered Vector Search ═══\n");

const string eraQuestion = "an artwork depicting a natural landscape";
Console.WriteLine($"Question: \"{eraQuestion}\" (filter: Era == \"Edo Japan\")");

var eraVector = await embeddingGenerator.GenerateVectorAsync(eraQuestion);
var eraResults = collection.SearchAsync(
eraVector,
top: 3,
new VectorSearchOptions<MuseumArtifact>
{
Filter = a => a.Era == "Edo Japan",
});

await foreach (var hit in eraResults)
{
Console.WriteLine($" [{hit.Score:F3}] {hit.Record.Title}");
}
Console.WriteLine();

// ══════════════════════════════════════════════
// PART 5 — Retrieve, Update, and Delete
// ══════════════════════════════════════════════
Console.WriteLine("═══ Part 5: Retrieve, Update, and Delete ═══\n");

// Get by key
var original = await collection.GetAsync(1008);
Console.WriteLine($"Retrieved #{original!.InventoryNumber}: {original.Title}");

// Update: extend description then re-embed and re-upsert (same key → replace)
original.Description += " The print is considered a masterpiece of ukiyo-e and has inspired generations of European impressionist painters.";
original.DescriptionVector = await embeddingGenerator.GenerateVectorAsync(original.Description);
await collection.UpsertAsync(original);
Console.WriteLine($"Updated #{original.InventoryNumber} with extended curator notes.");

// Delete
await collection.DeleteAsync(key: 1003);
Console.WriteLine("Deleted #1003 (Bronze Gladius Short Sword).");

// Confirm deletion
var shouldBeNull = await collection.GetAsync(1003);
Console.WriteLine($"Lookup of #1003 after delete returned: {(shouldBeNull is null ? "null ✓" : "still present ✗")}");
}
}

Key Takeaways

  1. A vector database stores embedding vectors and searches them by similarity.
  2. It turns raw words, images, and audio into long-term semantic memory for AI models.
  3. Modern vector DBs combine similarity search with metadata filters and hybrid keyword search.
  4. In .NET, the Microsoft.Extensions.VectorData abstractions let you swap providers (InMemory, Azure AI Search, Cosmos DB, Qdrant…) without rewriting app logic.
  5. The core operations are Upsert, Get, Search, and Delete — just like any database.

Reference

Vector databases for .NET AI apps — Microsoft Learn


Share this lesson: