Skip to content

RAG Types

The Strategos.Rag package provides vector store adapters for Retrieval-Augmented Generation (RAG) patterns.

Interface for vector similarity search operations.

MethodParametersReturnsDescription
SearchAsyncstring query, int topK, double minRelevance, CancellationToken ctTask<IReadOnlyList<SearchResult>>Searches for similar documents
SearchAsyncfloat[] embedding, int topK, double minRelevance, CancellationToken ctTask<IReadOnlyList<SearchResult>>Searches with pre-computed embedding
UpsertAsyncDocument document, CancellationToken ctTaskInserts or updates document
DeleteAsyncstring documentId, CancellationToken ctTaskRemoves document from index
public class RetrieveContextStep : IWorkflowStep<QueryState>
{
private readonly IVectorSearchAdapter _vectorSearch;
public RetrieveContextStep(IVectorSearchAdapter vectorSearch)
{
_vectorSearch = vectorSearch;
}
public async Task<StepResult<QueryState>> ExecuteAsync(
QueryState state,
StepContext context,
CancellationToken ct)
{
var results = await _vectorSearch.SearchAsync(
query: state.Query,
topK: 10,
minRelevance: 0.7,
ct);
return state
.With(s => s.RetrievedContext, results)
.AsResult();
}
}

Result from a vector similarity search.

PropertyTypeDescription
DocumentIdstringUnique identifier of matched document
ContentstringDocument text content
ScoredoubleSimilarity score (0.0 to 1.0)
MetadataDictionary<string, object>Additional document metadata
var results = await _vectorSearch.SearchAsync(query, topK: 5, minRelevance: 0.7, ct);
foreach (var result in results)
{
Console.WriteLine($"ID: {result.DocumentId}");
Console.WriteLine($"Score: {result.Score:F3}");
Console.WriteLine($"Content: {result.Content.Substring(0, 100)}...");
if (result.Metadata.TryGetValue("source", out var source))
{
Console.WriteLine($"Source: {source}");
}
}

Represents a document for indexing.

PropertyTypeDescription
IdstringUnique document identifier
ContentstringText content to embed
Embeddingfloat[]?Pre-computed embedding (optional)
MetadataDictionary<string, object>Additional metadata
var document = new Document
{
Id = "doc-123",
Content = "Agentic workflows combine deterministic state machines with AI agents...",
Metadata = new Dictionary<string, object>
{
["source"] = "documentation",
["chapter"] = "introduction",
["created"] = DateTimeOffset.UtcNow
}
};
await _vectorSearch.UpsertAsync(document, ct);

In-memory adapter for development and testing.

AspectValue
StatusAvailable
Use CaseDevelopment, unit testing, prototyping
PersistenceNone (data lost on restart)
services.AddScoped<IVectorSearchAdapter, InMemoryVectorSearchAdapter>();
  • No persistence between restarts
  • Not suitable for production
  • Limited scalability

PostgreSQL with pgvector extension.

AspectValue
StatusPlanned
Use CaseProduction with existing PostgreSQL
DependenciesPostgreSQL with pgvector extension
  • Leverages existing PostgreSQL infrastructure
  • HNSW and IVFFlat index support
  • Transactional consistency with workflow state
services.AddVectorSearch(options => options
.UsePgVector(connectionString)
.WithDimension(1536)
.WithIndexType(IndexType.HNSW));

Azure AI Search for enterprise-scale deployments.

AspectValue
StatusPlanned
Use CaseEnterprise production, Azure ecosystem
DependenciesAzure AI Search service
  • Managed infrastructure
  • Hybrid search (vector + keyword)
  • Semantic ranking
services.AddVectorSearch(options => options
.UseAzureAISearch(
endpoint: "https://your-search.search.windows.net",
apiKey: "your-api-key",
indexName: "documents"));

Workflow<QueryState>.Create("simple-rag")
.StartWith<RetrieveContextStep>()
.Then<GenerateResponseStep>()
.Finally<FormatOutputStep>();
Workflow<QueryState>.Create("reranked-rag")
.StartWith<RetrieveContextStep>()
.Then<RerankResultsStep>()
.Then<GenerateResponseStep>()
.Finally<FormatOutputStep>();
Workflow<QueryState>.Create("multi-query-rag")
.StartWith<GenerateQueriesStep>()
.Fork(
path => path.Then<RetrieveForQuery1Step>(),
path => path.Then<RetrieveForQuery2Step>(),
path => path.Then<RetrieveForQuery3Step>())
.Join<MergeResultsStep>()
.Then<DeduplicateStep>()
.Then<GenerateResponseStep>()
.Finally<FormatOutputStep>();

public class RagQueryStep : IAgentStep<QueryState>
{
private readonly IVectorSearchAdapter _vectorSearch;
private readonly IChatClient _chatClient;
public RagQueryStep(
IVectorSearchAdapter vectorSearch,
IChatClient chatClient)
{
_vectorSearch = vectorSearch;
_chatClient = chatClient;
}
public async Task<StepResult<QueryState>> ExecuteAsync(
QueryState state,
AgentStepContext context,
CancellationToken ct)
{
// Retrieve relevant context
var searchResults = await _vectorSearch.SearchAsync(
state.Query,
topK: 5,
minRelevance: 0.7,
ct);
// Build context string
var contextText = string.Join("\n\n",
searchResults.Select(r => r.Content));
// Generate response with context
var prompt = $"""
Answer the following question using only the provided context.
Context:
{contextText}
Question: {state.Query}
Answer:
""";
var response = await _chatClient.GetResponseAsync(prompt, ct);
return state
.With(s => s.RetrievedContext, searchResults.ToList())
.With(s => s.Response, response)
.AsResult();
}
}

// Development
services.AddScoped<IVectorSearchAdapter, InMemoryVectorSearchAdapter>();
// Production (when available)
services.AddVectorSearch(options => options
.UsePgVector(connectionString)
.WithEmbeddingClient<OpenAIEmbeddingClient>());