PartialContextQuestionProcessor
The PartialContextQuestionProcessor class enables you to ask questions about a PDF document and receive answers based on the most relevant parts of the document content. This processor uses embeddings to identify and send only the relevant portions of the document to the AI model, making it more efficient for token usage and more suitable for large documents. This class inherits from the abstract AIProcessorBase class, which provides common functionality for all AI processors.
The PartialContextQuestionProcessor is ideal for the following scenarios:
- Large Documents: When the document exceeds the token limit of the AI model and cannot be processed in a single call.
- Efficient Token Usage: When you want to minimize token consumption and optimize costs.
- Specific Questions: When questions are targeted at specific information within the document rather than requiring complete document understanding.
Public API and Configuration
Constructor | Platform | Description |
---|---|---|
PartialContextQuestionProcessor(IChatClient chatClient, int modelMaxInputTokenLimit, ISimpleTextDocument document) | .NET 8 | Creates an instance with built-in embeddings storage |
PartialContextQuestionProcessor(IChatClient chatClient, IEmbeddingsStorage embeddingsStorage, int modelMaxInputTokenLimit, ISimpleTextDocument document) | Any | Creates an instance with custom embeddings storage |
The .NET 8 constructor uses DefaultEmbeddingsStorage internally, while the cross-platform constructor requires a custom implementation of IEmbeddingsStorage as shown in the Prerequisites.
Properties and Methods
Member | Type | Description |
---|---|---|
Settings | Property | Gets the PartialContextProcessorSettings for configuring the AI process |
AnswerQuestion(string question) | Method | Returns an answer to the question using relevant document context |
PartialContextProcessorSettings
The settings class provides configuration options for the question-answering process:
- ModelMaxInputTokenLimit: Maximum input token limit the model allows
- TokenizationEncoding: Tokenization encoding used
- ModelId: ID of the AI model
- MaxNumberOfEmbeddingsSent: Maximum number of context chunks sent (default: 30)
- EmbeddingTokenSize: Size in tokens of each context chunk (default: 300)
Usage Examples
Example 1: Using PartialContextQuestionProcessor in .NET 8 and Later
This example demonstrates how to use the PartialContextQuestionProcessor with the built-in embeddings storage on .NET 8 and later. For setting up the AI client, see the AI Provider Setup section:
public async void AskQuestionsUsingPartialContext()
{
// Load the PDF document
string filePath = @"path\to\your\document.pdf";
PdfFormatProvider formatProvider = new PdfFormatProvider();
RadFixedDocument fixedDocument;
using (FileStream fs = File.OpenRead(filePath))
{
fixedDocument = formatProvider.Import(fs, TimeSpan.FromSeconds(10));
}
// Convert the document to a simple text representation
ISimpleTextDocument plainDoc = fixedDocument.ToSimpleTextDocument();
// Set up the AI client (Azure OpenAI in this example)
string key = "AZUREOPENAI_KEY";
string endpoint = "AZUREOPENAI_ENDPOINT";
string model = "gpt-4o-mini";
Azure.AI.OpenAI.AzureOpenAIClient azureClient = new AzureOpenAIClient(
new Uri(endpoint),
new Azure.AzureKeyCredential(key),
new Azure.AI.OpenAI.AzureOpenAIClientOptions());
ChatClient chatClient = azureClient.GetChatClient(model);
IChatClient iChatClient = new OpenAIChatClient(chatClient);
int maxTokenCount = 128000;
// Create the processor with built-in DefaultEmbeddingsStorage (.NET 8+ only)
using (PartialContextQuestionProcessor processor =
new PartialContextQuestionProcessor(iChatClient, maxTokenCount, plainDoc))
{
// Customize settings if needed
processor.Settings.MaxNumberOfEmbeddingsSent = 20;
processor.Settings.EmbeddingTokenSize = 500;
// Ask a question
string question = "What are the key findings in the document?";
string answer = await processor.AnswerQuestion(question);
Console.WriteLine($"Question: {question}");
Console.WriteLine($"Answer: {answer}");
// Ask additional questions using the same processor
string question2 = "What methodology was used in the research?";
string answer2 = await processor.AnswerQuestion(question2);
Console.WriteLine($"\nQuestion: {question2}");
Console.WriteLine($"Answer: {answer2}");
}
}
Example 2: Using PartialContextQuestionProcessor with Custom Embeddings (.NET Standard/.NET Framework)
This example demonstrates how to use the PartialContextQuestionProcessor with a custom embeddings storage implementation as described in the IEmbeddingsStorage Setup section:
public async void AskQuestionsUsingPartialContextIEmbeddingsStorage()
{
// Load the PDF document and convert to simple text (same as Example 1)
string filePath = @"path\to\your\document.pdf";
PdfFormatProvider formatProvider = new PdfFormatProvider();
RadFixedDocument fixedDocument;
using (FileStream fs = File.OpenRead(filePath))
{
fixedDocument = formatProvider.Import(fs, TimeSpan.FromSeconds(10));
}
ISimpleTextDocument plainDoc = fixedDocument.ToSimpleTextDocument();
// Set up Ollama client
IChatClient iChatClient = new OllamaChatClient(new Uri("http://localhost:11434/"), "llama3");
int maxTokenCount = 4096; // Adjust based on your model
// Create an embeddings storage implementation
// Uses Ollama for embeddings and SQLite for storage
IEmbeddingsStorage embeddingsStorage = new OllamaEmbeddingsStorage();
// Create the processor with custom embeddings storage
using (PartialContextQuestionProcessor processor =
new PartialContextQuestionProcessor(iChatClient, embeddingsStorage, maxTokenCount, plainDoc))
{
// Ask a question
string question = "What are the main conclusions of the document?";
string answer = await processor.AnswerQuestion(question);
Console.WriteLine($"Question: {question}");
Console.WriteLine($"Answer: {answer}");
}
}
Implementing custom IEmbeddingsStorage
A sample custom implementation for the OllamaEmbeddingsStorage is shown in the below code snippet:
Requires installing the following NuGet packages:
- LangChain
- LangChain.Databases.Sqlite
- Microsoft.Extensions.AI.Ollama
- Telerik.Windows.Documents.AIConnector
- Telerik.Windows.Documents.Fixed
- Install Ollama from ollama.com.
- Pull the model you want to use.
- Start the Ollama server.
// Install and run Ollama:
// 1. Install Ollama: https://ollama.com/
// 2. Pull the model: ollama pull llama3
// 3. Ensure Ollama is running: ollama serve
// Example implementation of IEmbeddingsStorage using Ollama and SQLite
internal class OllamaEmbeddingsStorage : IEmbeddingsStorage
{
private const string AllMinilmEmbeddingModelName = "all-minilm";
private const string DBName = "vectors.db";
private const int DimensionsForAllMinilm = 384; // Should be 384 for all-minilm
private static readonly string defaultCollectionName = "defaultName";
private readonly SqLiteVectorDatabase vectorDatabase;
private readonly OllamaEmbeddingModel embeddingModel;
IVectorCollection vectorCollection;
public OllamaEmbeddingsStorage()
{
OllamaProvider provider = new OllamaProvider();
this.embeddingModel = new OllamaEmbeddingModel(provider, id: AllMinilmEmbeddingModelName);
this.vectorDatabase = new SqLiteVectorDatabase(dataSource: DBName);
}
public async Task<string> GetQuestionContext(string question)
{
IReadOnlyCollection<LangChain.DocumentLoaders.Document> similarDocuments = await this.vectorCollection.GetSimilarDocuments(this.embeddingModel, question, amount: 5);
return similarDocuments.AsString();
}
public void SetText(string text, PartialContextProcessorSettings settings)
{
MemoryStream memoryStream = new MemoryStream();
StreamWriter writer = new StreamWriter(memoryStream);
writer.Write(text);
if (this.vectorDatabase.IsCollectionExistsAsync(defaultCollectionName).Result)
{
this.vectorDatabase.DeleteCollectionAsync(defaultCollectionName).Wait();
}
this.vectorCollection = this.vectorDatabase.AddDocumentsFromAsync<TextLoader>(
this.embeddingModel,
dimensions: DimensionsForAllMinilm,
dataSource: DataSource.FromBytes(memoryStream.ToArray()),
textSplitter: null,
collectionName: defaultCollectionName,
behavior: AddDocumentsToDatabaseBehavior.JustReturnCollectionIfCollectionIsAlreadyExists).Result;
}
public void Dispose()
{
this.vectorDatabase.Dispose();
}
}
internal class TextLoader : IDocumentLoader
{
public async Task<IReadOnlyCollection<LangChain.DocumentLoaders.Document>> LoadAsync(DataSource dataSource, DocumentLoaderSettings settings = null, CancellationToken cancellationToken = default)
{
using (Stream inputStream = await dataSource.GetStreamAsync(cancellationToken))
{
StreamReader reader = new StreamReader(inputStream);
string content = reader.ReadToEnd();
string[] pages = content.Split(new string[]{ "----------"}, System.StringSplitOptions.RemoveEmptyEntries);
return pages.Select(x => new LangChain.DocumentLoaders.Document(x)).ToList();
}
}
}
Example 3: Processing Specific Pages
// Convert only pages 5-10 to a simple text document (0-based index)
ISimpleTextDocument partialDoc = fixedDocument.ToSimpleTextDocument(4, 9);
using (PartialContextQuestionProcessor processor =
new PartialContextQuestionProcessor(iChatClient, maxTokenCount, partialDoc))
{
// Ask a question about the specific pages
string question = "What information is presented on pages 5-10 of the document?";
string answer = await processor.AnswerQuestion(question);
Console.WriteLine($"Question: {question}");
Console.WriteLine($"Answer: {answer}");
}
Example 4: Optimizing Embeddings Settings
using (PartialContextQuestionProcessor processor =
new PartialContextQuestionProcessor(iChatClient, maxTokenCount, plainDoc))
{
// For more comprehensive answers at the cost of more tokens
processor.Settings.MaxNumberOfEmbeddingsSent = 50; // Increase the number of context chunks
processor.Settings.EmbeddingTokenSize = 400; // Increase the size of each context chunk
// For more precise answers and fewer tokens
// processor.Settings.MaxNumberOfEmbeddingsSent = 15; // Decrease for fewer chunks
// processor.Settings.EmbeddingTokenSize = 250; // Decrease for smaller chunks
string question = "What are the key recommendations in the document?";
string answer = await processor.AnswerQuestion(question);
Console.WriteLine($"Question: {question}");
Console.WriteLine($"Answer: {answer}");
}