Практическое руководство. Открытие поиска файлов агента помощника по искусственному интеллекту (экспериментальный)
Предупреждение
Платформа агента семантического ядра является экспериментальной, по-прежнему в разработке и подлежит изменению.
Обзор
В этом примере мы рассмотрим, как использовать средство поиска файлов агента Open AI Assistant для выполнения задач понимания. Подход будет пошаговая, обеспечивая четкость и точность во всем процессе. В рамках задачи агент предоставит ссылки на документы в ответе.
Потоковая передача будет использоваться для доставки ответов агента. Это обеспечит обновления в режиме реального времени по мере выполнения задачи.
Начало работы
Прежде чем продолжить программирование компонентов, убедитесь, что среда разработки полностью настроена и настроена.
Чтобы добавить зависимости пакета из командной строки, используйте dotnet
команду:
dotnet add package Azure.Identity
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Binder
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
dotnet add package Microsoft.SemanticKernel
dotnet add package Microsoft.SemanticKernel.Agents.OpenAI --prerelease
При управлении пакетами NuGet в Visual Studio убедитесь, что
Include prerelease
установлен флажок.
Файл проекта (.csproj
) должен содержать следующие PackageReference
определения:
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="<stable>" />
<PackageReference Include="Microsoft.SemanticKernel" Version="<latest>" />
<PackageReference Include="Microsoft.SemanticKernel.Agents.OpenAI" Version="<latest>" />
</ItemGroup>
Платформа агента является экспериментальной и требует подавления предупреждений. Это может быть устранено в виде свойства в файле проекта (.csproj
):
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
Кроме того, скопируйте содержимое общедоступного Grimms-The-King-of-the-Golden-Mountain.txt
Grimms-The-White-Snake.txt
Grimms-The-Water-of-Life.txt
домена из проекта семантического ядра.LearnResources
Добавьте эти файлы в папку проекта и настройте их копирование в выходной каталог:
<ItemGroup>
<None Include="Grimms-The-King-of-the-Golden-Mountain.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Grimms-The-Water-of-Life.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Grimms-The-White-Snake.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
Начните с создания папки, в которую будут храниться скрипт (.py
файл) и примеры ресурсов. Включите следующие импорты в верхней части .py
файла:
import asyncio
import os
from semantic_kernel.agents.open_ai.azure_assistant_agent import AzureAssistantAgent
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.streaming_annotation_content import StreamingAnnotationContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.kernel import Kernel
Кроме того, скопируйте содержимое общедоступного Grimms-The-King-of-the-Golden-Mountain.txt
Grimms-The-White-Snake.txt
Grimms-The-Water-of-Life.txt
домена из проекта семантического ядра.LearnResources
Добавьте эти файлы в папку проекта.
Агенты в настоящее время недоступны в Java.
Настройка
Этот пример требует настройки для подключения к удаленным службам. Вам потребуется определить параметры для Open AI или Azure Open AI.
# Open AI
dotnet user-secrets set "OpenAISettings:ApiKey" "<api-key>"
dotnet user-secrets set "OpenAISettings:ChatModel" "gpt-4o"
# Azure Open AI
dotnet user-secrets set "AzureOpenAISettings:ApiKey" "<api-key>" # Not required if using token-credential
dotnet user-secrets set "AzureOpenAISettings:Endpoint" "https://lightspeed-team-shared-openai-eastus.openai.azure.com/"
dotnet user-secrets set "AzureOpenAISettings:ChatModelDeployment" "gpt-4o"
Следующий класс используется во всех примерах агента. Не забудьте включить его в проект, чтобы обеспечить правильную функциональность. Этот класс служит базовым компонентом для приведенных ниже примеров.
using System.Reflection;
using Microsoft.Extensions.Configuration;
namespace AgentsSample;
public class Settings
{
private readonly IConfigurationRoot configRoot;
private AzureOpenAISettings azureOpenAI;
private OpenAISettings openAI;
public AzureOpenAISettings AzureOpenAI => this.azureOpenAI ??= this.GetSettings<Settings.AzureOpenAISettings>();
public OpenAISettings OpenAI => this.openAI ??= this.GetSettings<Settings.OpenAISettings>();
public class OpenAISettings
{
public string ChatModel { get; set; } = string.Empty;
public string ApiKey { get; set; } = string.Empty;
}
public class AzureOpenAISettings
{
public string ChatModelDeployment { get; set; } = string.Empty;
public string Endpoint { get; set; } = string.Empty;
public string ApiKey { get; set; } = string.Empty;
}
public TSettings GetSettings<TSettings>() =>
this.configRoot.GetRequiredSection(typeof(TSettings).Name).Get<TSettings>()!;
public Settings()
{
this.configRoot =
new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets(Assembly.GetExecutingAssembly(), optional: true)
.Build();
}
}
Самый быстрый способ начать работу с правильной конфигурацией для запуска примера кода — создать .env
файл в корне проекта (где выполняется скрипт).
Настройте следующие параметры в .env
файле для Azure OpenAI или OpenAI:
AZURE_OPENAI_API_KEY="..."
AZURE_OPENAI_ENDPOINT="https://..."
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="..."
AZURE_OPENAI_API_VERSION="..."
OPENAI_API_KEY="sk-..."
OPENAI_ORG_ID=""
OPENAI_CHAT_MODEL_ID=""
После настройки соответствующие классы служб ИИ будут собирать необходимые переменные и использовать их во время создания экземпляра.
Агенты в настоящее время недоступны в Java.
Написание кода
Процесс написания кода для этого примера включает в себя:
- Настройка — инициализация параметров и подключаемый модуль.
- Определение агента— создание Chat_Completion_Agent с шаблонными инструкциями и подключаемым модулем.
- Цикл чата — запись цикла , который управляет взаимодействием пользователя или агента.
Полный пример кода представлен в заключительном разделе. См. этот раздел для полной реализации.
Настройка
Перед созданием агента Open AI Assistant убедитесь, что параметры конфигурации доступны и подготовьте файловые ресурсы.
Создайте экземпляр класса, на который ссылается предыдущий Settings
раздел конфигурации . Используйте параметры, чтобы также создать OpenAIClientProvider
объект, который будет использоваться для определения агента, а также отправки VectorStore
файлов и создания объекта.
Settings settings = new();
OpenAIClientProvider clientProvider =
OpenAIClientProvider.ForAzureOpenAI(
new AzureCliCredential(),
new Uri(settings.AzureOpenAI.Endpoint));
Агенты в настоящее время недоступны в Java.
Теперь создайте пустой _Vector Store для использования с средством поиска файлов:
OpenAIClientProvider
Используйте для доступа к объекту VectorStoreClient
и созданию VectorStore
.
Console.WriteLine("Creating store...");
VectorStoreClient storeClient = clientProvider.Client.GetVectorStoreClient();
CreateVectorStoreOperation operation = await storeClient.CreateVectorStoreAsync(waitUntilCompleted: true);
string storeId = operation.VectorStoreId;
def get_filepath_for_filename(filename: str) -> str:
base_directory = os.path.dirname(os.path.realpath(__file__))
return os.path.join(base_directory, filename)
Агенты в настоящее время недоступны в Java.
Давайте объявим три файла содержимого, описанные в предыдущем разделе конфигурации :
private static readonly string[] _fileNames =
[
"Grimms-The-King-of-the-Golden-Mountain.txt",
"Grimms-The-Water-of-Life.txt",
"Grimms-The-White-Snake.txt",
];
filenames = [
"Grimms-The-King-of-the-Golden-Mountain.txt",
"Grimms-The-Water-of-Life.txt",
"Grimms-The-White-Snake.txt",
]
Агенты в настоящее время недоступны в Java.
Теперь отправьте эти файлы и добавьте их в векторное хранилище с помощью ранее созданных VectorStoreClient
клиентов для отправки каждого файла с OpenAIFileClient
помощью и добавления его в векторное хранилище, сохраняя полученные ссылки на файлы.
Dictionary<string, OpenAIFile> fileReferences = [];
Console.WriteLine("Uploading files...");
OpenAIFileClient fileClient = clientProvider.Client.GetOpenAIFileClient();
foreach (string fileName in _fileNames)
{
OpenAIFile fileInfo = await fileClient.UploadFileAsync(fileName, FileUploadPurpose.Assistants);
await storeClient.AddFileToVectorStoreAsync(storeId, fileInfo.Id, waitUntilCompleted: true);
fileReferences.Add(fileInfo.Id, fileInfo);
}
Агенты в настоящее время недоступны в Java.
Определение агента
Теперь мы готовы создать экземпляр агента Помощника OpenAI. Агент настроен с помощью целевой модели, инструкций и средства поиска файлов. Кроме того, мы явно связываем векторное хранилище с средством поиска файлов.
Мы будем использовать OpenAIClientProvider
еще раз в рамках создания OpenAIAssistantAgent
:
Console.WriteLine("Defining agent...");
OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.CreateAsync(
clientProvider,
new OpenAIAssistantDefinition(settings.AzureOpenAI.ChatModelDeployment)
{
Name = "SampleAssistantAgent",
Instructions =
"""
The document store contains the text of fictional stories.
Always analyze the document store to provide an answer to the user's question.
Never rely on your knowledge of stories not included in the document store.
Always format response using markdown.
""",
EnableFileSearch = true,
VectorStoreId = storeId,
},
new Kernel());
agent = await AzureAssistantAgent.create(
kernel=Kernel(),
service_id="agent",
name="SampleAssistantAgent",
instructions="""
The document store contains the text of fictional stories.
Always analyze the document store to provide an answer to the user's question.
Never rely on your knowledge of stories not included in the document store.
Always format response using markdown.
""",
enable_file_search=True,
vector_store_filenames=[get_filepath_for_filename(filename) for filename in filenames],
)
Агенты в настоящее время недоступны в Java.
Цикл чата
Наконец, мы можем координировать взаимодействие между пользователем и агентом. Начните с создания потока помощника для поддержания состояния беседы и создания пустого цикла.
Давайте также убедитесь, что ресурсы удаляются в конце выполнения, чтобы свести к минимуму ненужные расходы.
Console.WriteLine("Creating thread...");
string threadId = await agent.CreateThreadAsync();
Console.WriteLine("Ready!");
try
{
bool isComplete = false;
do
{
// Processing occurrs here
} while (!isComplete);
}
finally
{
Console.WriteLine();
Console.WriteLine("Cleaning-up...");
await Task.WhenAll(
[
agent.DeleteThreadAsync(threadId),
agent.DeleteAsync(),
storeClient.DeleteVectorStoreAsync(storeId),
..fileReferences.Select(fileReference => fileClient.DeleteFileAsync(fileReference.Key))
]);
}
print("Creating thread...")
thread_id = await agent.create_thread()
try:
is_complete: bool = False
while not is_complete:
# Processing occurs here
finally:
print("Cleaning up resources...")
if agent is not None:
[await agent.delete_file(file_id) for file_id in agent.file_search_file_ids]
await agent.delete_thread(thread_id)
await agent.delete()
Агенты в настоящее время недоступны в Java.
Теперь давайте зафиксируем входные данные пользователя в предыдущем цикле. В этом случае пустые входные данные будут игнорироваться, и термин EXIT
будет сигнализировать о завершении беседы. Допустимый nput будет добавлен в поток помощника в качестве сообщения пользователя.
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
await agent.AddChatMessageAsync(threadId, new ChatMessageContent(AuthorRole.User, input));
Console.WriteLine();
user_input = input("User:> ")
if not user_input:
continue
if user_input.lower() == "exit":
is_complete = True
await agent.add_chat_message(
thread_id=thread_id, message=ChatMessageContent(role=AuthorRole.USER, content=user_input)
)
Агенты в настоящее время недоступны в Java.
Перед вызовом ответа агента добавим вспомогательный метод для переформатирования квадратных скобок заметки Юникода в квадратные скобки ANSI.
private static string ReplaceUnicodeBrackets(this string content) =>
content?.Replace('【', '[').Replace('】', ']');
# No special handling required.
Агенты в настоящее время недоступны в Java.
Чтобы создать ответ агента на входные данные пользователя, вызовите агент, указав поток Помощника. В этом примере мы выбираем потокованный ответ и фиксируем все связанные заметки ссылок для отображения в конце цикла отклика. Обратите внимание, что каждый потоковые блоки переформатируются с помощью предыдущего вспомогательного метода.
List<StreamingAnnotationContent> footnotes = [];
await foreach (StreamingChatMessageContent chunk in agent.InvokeStreamingAsync(threadId))
{
// Capture annotations for footnotes
footnotes.AddRange(chunk.Items.OfType<StreamingAnnotationContent>());
// Render chunk with replacements for unicode brackets.
Console.Write(chunk.Content.ReplaceUnicodeBrackets());
}
Console.WriteLine();
// Render footnotes for captured annotations.
if (footnotes.Count > 0)
{
Console.WriteLine();
foreach (StreamingAnnotationContent footnote in footnotes)
{
Console.WriteLine($"#{footnote.Quote.ReplaceUnicodeBrackets()} - {fileReferences[footnote.FileId!].Filename} (Index: {footnote.StartIndex} - {footnote.EndIndex})");
}
}
footnotes: list[StreamingAnnotationContent] = []
async for response in agent.invoke_stream(thread_id=thread_id):
footnotes.extend([item for item in response.items if isinstance(item, StreamingAnnotationContent)])
print(f"{response.content}", end="", flush=True)
print()
if len(footnotes) > 0:
for footnote in footnotes:
print(
f"\n`{footnote.quote}` => {footnote.file_id} "
f"(Index: {footnote.start_index} - {footnote.end_index})"
)
Агенты в настоящее время недоступны в Java.
Завершение
В этом примере приведен окончательный код для всех шагов. Полная реализация представлена ниже.
Попробуйте использовать эти предлагаемые входные данные:
- Что такое количество абзацей для каждой истории?
- Создайте таблицу, которая идентифицирует главного героя и антагониста для каждой истории.
- Что такое мораль в Белой змеи?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using OpenAI.Files;
using OpenAI.VectorStores;
namespace AgentsSample;
public static class Program
{
private static readonly string[] _fileNames =
[
"Grimms-The-King-of-the-Golden-Mountain.txt",
"Grimms-The-Water-of-Life.txt",
"Grimms-The-White-Snake.txt",
];
/// <summary>
/// The main entry point for the application.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static async Task Main()
{
// Load configuration from environment variables or user secrets.
Settings settings = new();
OpenAIClientProvider clientProvider =
OpenAIClientProvider.ForAzureOpenAI(
new AzureCliCredential(),
new Uri(settings.AzureOpenAI.Endpoint));
Console.WriteLine("Creating store...");
VectorStoreClient storeClient = clientProvider.Client.GetVectorStoreClient();
CreateVectorStoreOperation operation = await storeClient.CreateVectorStoreAsync(waitUntilCompleted: true);
string storeId = operation.VectorStoreId;
// Retain file references.
Dictionary<string, OpenAIFile> fileReferences = [];
Console.WriteLine("Uploading files...");
OpenAIFileClient fileClient = clientProvider.Client.GetOpenAIFileClient();
foreach (string fileName in _fileNames)
{
OpenAIFile fileInfo = await fileClient.UploadFileAsync(fileName, FileUploadPurpose.Assistants);
await storeClient.AddFileToVectorStoreAsync(storeId, fileInfo.Id, waitUntilCompleted: true);
fileReferences.Add(fileInfo.Id, fileInfo);
}
Console.WriteLine("Defining agent...");
OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.CreateAsync(
clientProvider,
new OpenAIAssistantDefinition(settings.AzureOpenAI.ChatModelDeployment)
{
Name = "SampleAssistantAgent",
Instructions =
"""
The document store contains the text of fictional stories.
Always analyze the document store to provide an answer to the user's question.
Never rely on your knowledge of stories not included in the document store.
Always format response using markdown.
""",
EnableFileSearch = true,
VectorStoreId = storeId,
},
new Kernel());
Console.WriteLine("Creating thread...");
string threadId = await agent.CreateThreadAsync();
Console.WriteLine("Ready!");
try
{
bool isComplete = false;
do
{
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
await agent.AddChatMessageAsync(threadId, new ChatMessageContent(AuthorRole.User, input));
Console.WriteLine();
List<StreamingAnnotationContent> footnotes = [];
await foreach (StreamingChatMessageContent chunk in agent.InvokeStreamingAsync(threadId))
{
// Capture annotations for footnotes
footnotes.AddRange(chunk.Items.OfType<StreamingAnnotationContent>());
// Render chunk with replacements for unicode brackets.
Console.Write(chunk.Content.ReplaceUnicodeBrackets());
}
Console.WriteLine();
// Render footnotes for captured annotations.
if (footnotes.Count > 0)
{
Console.WriteLine();
foreach (StreamingAnnotationContent footnote in footnotes)
{
Console.WriteLine($"#{footnote.Quote.ReplaceUnicodeBrackets()} - {fileReferences[footnote.FileId!].Filename} (Index: {footnote.StartIndex} - {footnote.EndIndex})");
}
}
} while (!isComplete);
}
finally
{
Console.WriteLine();
Console.WriteLine("Cleaning-up...");
await Task.WhenAll(
[
agent.DeleteThreadAsync(threadId),
agent.DeleteAsync(),
storeClient.DeleteVectorStoreAsync(storeId),
..fileReferences.Select(fileReference => fileClient.DeleteFileAsync(fileReference.Key))
]);
}
}
private static string ReplaceUnicodeBrackets(this string content) =>
content?.Replace('【', '[').Replace('】', ']');
}
import asyncio
import os
from semantic_kernel.agents.open_ai.azure_assistant_agent import AzureAssistantAgent
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.streaming_annotation_content import StreamingAnnotationContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.kernel import Kernel
def get_filepath_for_filename(filename: str) -> str:
base_directory = os.path.dirname(os.path.realpath(__file__))
return os.path.join(base_directory, filename)
filenames = [
"Grimms-The-King-of-the-Golden-Mountain.txt",
"Grimms-The-Water-of-Life.txt",
"Grimms-The-White-Snake.txt",
]
async def main():
agent = await AzureAssistantAgent.create(
kernel=Kernel(),
service_id="agent",
name="SampleAssistantAgent",
instructions="""
The document store contains the text of fictional stories.
Always analyze the document store to provide an answer to the user's question.
Never rely on your knowledge of stories not included in the document store.
Always format response using markdown.
""",
enable_file_search=True,
vector_store_filenames=[get_filepath_for_filename(filename) for filename in filenames],
)
print("Creating thread...")
thread_id = await agent.create_thread()
try:
is_complete: bool = False
while not is_complete:
user_input = input("User:> ")
if not user_input:
continue
if user_input.lower() == "exit":
is_complete = True
break
await agent.add_chat_message(
thread_id=thread_id, message=ChatMessageContent(role=AuthorRole.USER, content=user_input)
)
footnotes: list[StreamingAnnotationContent] = []
async for response in agent.invoke_stream(thread_id=thread_id):
footnotes.extend([item for item in response.items if isinstance(item, StreamingAnnotationContent)])
print(f"{response.content}", end="", flush=True)
print()
if len(footnotes) > 0:
for footnote in footnotes:
print(
f"\n`{footnote.quote}` => {footnote.file_id} "
f"(Index: {footnote.start_index} - {footnote.end_index})"
)
finally:
print("Cleaning up resources...")
if agent is not None:
[await agent.delete_file(file_id) for file_id in agent.file_search_file_ids]
await agent.delete_thread(thread_id)
await agent.delete()
if __name__ == "__main__":
asyncio.run(main())
Агенты в настоящее время недоступны в Java.