Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Используйте поиск содержимого приложения для создания семантического индекса содержимого в приложении. Это позволяет пользователям находить информацию на основе смысла, а не только ключевых слов. Индекс также можно использовать для улучшения помощников по искусственному интеллекту с помощью знаний, относящихся к домену, для более персонализированных и контекстных результатов.
В частности, вы узнаете, как использовать API AppContentIndexer для:
- Создание или открытие индекса содержимого в приложении
- Добавление текстовых строк в индекс и запуск запроса
- Управление сложностью длинной текстовой строки
- Индексирование данных изображения и поиск соответствующих изображений
- Включение сценариев RAG (Retrieval-Augmented поколения)
- Используйте AppContentIndexer в фоновом потоке
- Закройте AppContentIndexer, если больше не используется для выпуска ресурсов
Предпосылки
Узнайте больше о требованиях к оборудованию #REF! AI API и о том, как настроить устройство для успешной сборки приложений с помощью #REF! AI API, в статье Начало работы с созданием приложения с использованием #REF! AI API.
Требование идентификации пакета
Приложения, использующие AppContentIndexer , должны иметь удостоверение пакета, которое доступно только для упакованных приложений (включая приложения с внешними расположениями). Чтобы включить семантическое индексирование и распознавание текста (OCR), приложение также должно объявить возможность.
Создание или открытие индекса содержимого в приложении
Чтобы создать семантический индекс содержимого в приложении, необходимо сначала установить структуру, доступную для поиска, которую приложение может использовать для эффективного хранения и извлечения содержимого. Этот индекс выступает в качестве локальной семантической и лексической поисковой системы для содержимого приложения.
Чтобы использовать API AppContentIndexer, сначала вызовите метод, указав имя индекса. Если индекс с таким именем уже существует для текущего идентификатора приложения и пользователя, он открывается; в противном случае создается новый.
public void SimpleGetOrCreateIndexSample()
{
GetOrCreateIndexResult result = AppContentIndexer.GetOrCreateIndex("myindex");
if (!result.Succeeded)
{
throw new InvalidOperationException($"Failed to open index. Status = '{result.Status}', Error = '{result.ExtendedError}'");
}
// If result.Succeeded is true, result.Status will either be CreatedNew or OpenedExisting
if (result.Status == GetOrCreateIndexStatus.CreatedNew)
{
Console.WriteLine("Created a new index");
}
else if(result.Status == GetOrCreateIndexStatus.OpenedExisting)
{
Console.WriteLine("Opened an existing index");
}
using AppContentIndexer indexer = result.Indexer;
// Use indexer...
}
В этом примере показано, как обрабатывать случай сбоя при открытии индекса. Для простоты другие примеры в этом документе могут не отображать обработку ошибок.
Добавление текстовых строк в индекс и запуск запроса
В этом примере показано, как добавить некоторые текстовые строки в индекс, созданный для приложения, а затем запустить запрос к этому индексу, чтобы получить соответствующие сведения.
// This is some text data that we want to add to the index:
Dictionary<string, string> simpleTextData = new Dictionary<string, string>
{
{"item1", "Here is some information about Cats: Cats are cute and fluffy. Young cats are very playful." },
{"item2", "Dogs are loyal and affectionate animals known for their companionship, intelligence, and diverse breeds." },
{"item3", "Fish are aquatic creatures that breathe through gills and come in a vast variety of shapes, sizes, and colors." },
{"item4", "Broccoli is a nutritious green vegetable rich in vitamins, fiber, and antioxidants." },
{"item5", "Computers are powerful electronic devices that process information, perform calculations, and enable communication worldwide." },
{"item6", "Music is a universal language that expresses emotions, tells stories, and connects people through rhythm and melody." },
};
public void SimpleTextIndexingSample()
{
AppContentIndexer indexer = GetIndexerForApp();
// Add some text data to the index:
foreach (var item in simpleTextData)
{
IndexableAppContent textContent = AppManagedIndexableAppContent.CreateFromString(item.Key, item.Value);
indexer.AddOrUpdate(textContent);
}
}
public void SimpleTextQueryingSample()
{
AppContentIndexer indexer = GetIndexerForApp();
// We search the index using a semantic query:
AppIndexTextQuery queryCursor = indexer.CreateTextQuery("Facts about kittens.");
IReadOnlyList<TextQueryMatch> textMatches = queryCursor.GetNextMatches(5);
// Nothing in the index exactly matches what we queried but item1 is similar to the query so we expect
// that to be the first match.
foreach (var match in textMatches)
{
Console.WriteLine(match.ContentId);
if (match.ContentKind == QueryMatchContentKind.AppManagedText)
{
AppManagedTextQueryMatch textResult = (AppManagedTextQueryMatch)match;
// Only part of the original string may match the query. So we can use TextOffset and TextLength to extract the match.
// In this example, we might imagine that the substring "Cats are cute and fluffy" from "item1" is the top match for the query.
string matchingData = simpleTextData[match.ContentId];
string matchingString = matchingData.Substring(textResult.TextOffset, textResult.TextLength);
Console.WriteLine(matchingString);
}
}
}
включает только и , не сам соответствующий текст. Вы несете ответственность за то, что разработчик приложения ссылается на исходный текст. Результаты запроса сортируются по релевантности, при этом наиболее релевантный результат является первым. Индексирование происходит асинхронно, поэтому запросы могут выполняться на частичных данных. Вы можете проверить состояние индексирования, как описано ниже.
Управление сложностью длинной текстовой строки
В примере показано, что для разработчика приложения не требуется разделить текстовое содержимое на небольшие разделы для обработки моделей. AppContentIndexer управляет этим аспектом сложности.
Dictionary<string, string> textFiles = new Dictionary<string, string>
{
{"file1", "File1.txt" },
{"file2", "File2.txt" },
{"file3", "File3.txt" },
};
public void TextIndexingSample2()
{
AppContentIndexer indexer = GetIndexerForApp();
var folderPath = Windows.ApplicationModel.Package.Current.InstalledLocation.Path;
// Add some text data to the index:
foreach (var item in textFiles)
{
string contentId = item.Key;
string filename = item.Value;
// Note that the text here can be arbitrarily large. The AppContentIndexer will take care of chunking the text
// in a way that works effectively with the underlying model. We do not require the app author to break the text
// down into small pieces.
string text = File.ReadAllText(Path.Combine(folderPath, filename));
IndexableAppContent textContent = AppManagedIndexableAppContent.CreateFromString(contentId, text);
indexer.AddOrUpdate(textContent);
}
}
public void TextIndexingSample2_RunQuery()
{
AppContentIndexer indexer = GetIndexerForApp();
var folderPath = Windows.ApplicationModel.Package.Current.InstalledLocation.Path;
// Search the index
AppIndexTextQuery query = indexer.CreateTextQuery("Facts about kittens.");
IReadOnlyList<TextQueryMatch> textMatches = query.GetNextMatches(5);
if (textMatches != null)
{
foreach (var match in textMatches)
{
Console.WriteLine(match.ContentId);
if (match is AppManagedTextQueryMatch textResult)
{
// We load the content of the file that contains the match:
string matchingFilename = textFiles[match.ContentId];
string fileContent = File.ReadAllText(Path.Combine(folderPath, matchingFilename));
// Find the substring within the loaded text that contains the match:
string matchingString = fileContent.Substring(textResult.TextOffset, textResult.TextLength);
Console.WriteLine(matchingString);
}
}
}
}
Текстовые данные источникируются из файлов, но индексируются только содержимое, а не сами файлы. AppContentIndexer не имеет знаний о исходных файлах и не отслеживает обновления. Если содержимое файла изменяется, приложение должно вручную обновить индекс.
Индексирование данных изображения и поиск соответствующих изображений
В этом примере показано, как индексировать данные изображения с помощью , и затем искать соответствующие изображения с помощью текстовых запросов.
// We load the image data from a set of known files and send that image data to the indexer.
// The image data does not need to come from files on disk, it can come from anywhere.
Dictionary<string, string> imageFilesToIndex = new Dictionary<string, string>
{
{"item1", "Cat.jpg" },
{"item2", "Dog.jpg" },
{"item3", "Fish.jpg" },
{"item4", "Broccoli.jpg" },
{"item5", "Computer.jpg" },
{"item6", "Music.jpg" },
};
public void SimpleImageIndexingSample()
{
AppContentIndexer indexer = GetIndexerForApp();
// Add some image data to the index.
foreach (var item in imageFilesToIndex)
{
var file = item.Value;
var softwareBitmap = Helpers.GetSoftwareBitmapFromFile(file);
IndexableAppContent imageContent = AppManagedIndexableAppContent.CreateFromBitmap(item.Key, softwareBitmap);
indexer.AddOrUpdate(imageContent);
}
}
public void SimpleImageIndexingSample_RunQuery()
{
AppContentIndexer indexer = GetIndexerForApp();
// We query the index for some data to match our text query.
AppIndexImageQuery query = indexer.CreateImageQuery("cute pictures of kittens");
IReadOnlyList<ImageQueryMatch> imageMatches = query.GetNextMatches(5);
// One of the images that we indexed was a photo of a cat. We expect this to be the first match to match the query.
foreach (var match in imageMatches)
{
Console.WriteLine(match.ContentId);
if (match.ContentKind == QueryMatchContentKind.AppManagedImage)
{
AppManagedImageQueryMatch imageResult = (AppManagedImageQueryMatch)match;
var matchingFileName = imageFilesToIndex[match.ContentId];
// It might be that the match is at a particular region in the image. The result includes
// the subregion of the image that includes the match.
Console.WriteLine($"Matching file: '{matchingFileName}' at location {imageResult.Subregion}");
}
}
}
Включение сценариев RAG (Retrieval-Augmented поколения)
RAG (Retrieval-Augmented Generation) предполагает дополнение пользовательских запросов к языковым моделям дополнительными релевантными данными, которые модель может использовать для генерации ответов. Запрос пользователя служит в качестве входных данных для семантического поиска, который определяет соответствующие сведения в индексе. Затем полученные данные из семантического поиска включаются в запрос, предоставленный языковой модели, чтобы он смог создать более точные и контекстные ответы.
В этом примере показано, как использовать API AppContentIndexer с LLM для добавления контекстных данных в поисковый запрос пользователя приложения. Пример является универсальным, llM не указан, и пример запрашивает только локальные данные, хранящиеся в созданном индексе (без внешних вызовов к Интернету). В этом примере и не являются реальными функциями и просто используются для предоставления примера.
Чтобы включить сценарии RAG с помощью API AppContentIndexer , можно выполнить следующий пример:
public void SimpleRAGScenario()
{
AppContentIndexer indexer = GetIndexerForApp();
// These are some text files that had previously been added to the index.
// The key is the contentId of the item.
Dictionary<string, string> data = new Dictionary<string, string>
{
{"file1", "File1.txt" },
{"file2", "File2.txt" },
{"file3", "File3.txt" },
};
string userPrompt = Helpers.GetUserPrompt();
// We execute a query against the index using the user's prompt string as the query text.
AppIndexTextQuery query = indexer.CreateTextQuery(userPrompt);
IReadOnlyList<TextQueryMatch> textMatches = query.GetNextMatches(5);
StringBuilder promptStringBuilder = new StringBuilder();
promptStringBuilder.AppendLine("Please refer to the following pieces of information when responding to the user's prompt:");
// For each of the matches found, we include the relevant snippets of the text files in the augmented query that we send to the language model
foreach (var match in textMatches)
{
if (match is AppManagedTextQueryMatch textResult)
{
// We load the content of the file that contains the match:
string matchingFilename = data[match.ContentId];
string fileContent = File.ReadAllText(matchingFilename);
// Find the substring within the loaded text that contains the match:
string matchingString = fileContent.Substring(textResult.TextOffset, textResult.TextLength);
promptStringBuilder.AppendLine(matchingString);
promptStringBuilder.AppendLine();
}
}
promptStringBuilder.AppendLine("Please provide a response to the following user prompt:");
promptStringBuilder.AppendLine(userPrompt);
var response = Helpers.GetResponseFromChatAgent(promptStringBuilder.ToString());
Console.WriteLine(response);
}
Используйте AppContentIndexer в фоновом потоке
Экземпляр AppContentIndexer не связан с определенным потоком; это гибкий объект, который может работать в разных потоках. Для некоторых методов AppContentIndexer и связанных с ним типов может потребоваться значительное время обработки. Поэтому рекомендуется избегать вызова API AppContentIndexer непосредственно из потока пользовательского интерфейса приложения и вместо этого использовать фоновый поток.
Закройте AppContentIndexer, если больше не используется для выпуска ресурсов
AppContentIndexer реализует интерфейс для определения срока службы. Приложение должно закрыть индексатор, если он больше не используется. Это позволяет AppContentIndexer выпускать свои базовые ресурсы.
public void IndexerDisposeSample()
{
var indexer = AppContentIndexer.GetOrCreateIndex("myindex").Indexer;
// use indexer
indexer.Dispose();
// after this point, it would be an error to try to use indexer since it is now Closed.
}
В коде на языке C# интерфейс отображается как . Код C# может использовать паттерн для экземпляров AppContentIndexer.
public void IndexerUsingSample()
{
using var indexer = AppContentIndexer.GetOrCreateIndex("myindex").Indexer;
// use indexer
//indexer.Dispose() is automatically called
}
Если вы открываете один и тот же индекс несколько раз в вашем приложении, необходимо вызвать для каждого экземпляра.
Открытие и закрытие индекса является дорогой операцией, поэтому следует свести к минимуму такие операции в приложении. Например, приложение может хранить один экземпляр AppContentIndexer для приложения и использовать этот экземпляр в течение всего времени существования приложения, а не постоянно открывать и закрывать индекс для каждого действия, которое необходимо выполнить.