Como fazer: OpenAIAssistantAgent
pesquisa de arquivos
Importante
Este recurso está na fase de candidato a lançamento. Os recursos neste estágio estão quase completos e geralmente estáveis, embora possam passar por pequenos refinamentos ou otimizações antes de atingir a disponibilidade geral total.
Descrição geral
Neste exemplo, exploraremos como usar a ferramenta de pesquisa de arquivos de um OpenAIAssistantAgent
para concluir tarefas de compreensão. A abordagem será passo a passo, garantindo clareza e precisão ao longo de todo o processo. Como parte da tarefa, o agente fornecerá citações de documentos na resposta.
O streaming será usado para entregar as respostas do agente. Isso fornecerá atualizações em tempo real à medida que a tarefa progride.
Introdução
Antes de prosseguir com a codificação de funcionalidades, verifique se o ambiente de desenvolvimento está totalmente configurado.
Para adicionar dependências de pacote a partir da linha de comando, use o dotnet
comando:
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
Se estiver gerenciando pacotes NuGet no Visual Studio, verifique se
Include prerelease
está marcado.
O ficheiro de projeto (.csproj
) deve conter as seguintes PackageReference
definições:
<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>
O Agent Framework
é experimental e requer supressão de aviso. Isso pode ser abordado como uma propriedade no arquivo de projeto (.csproj
):
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
Além disso, copie o conteúdo de domínio público dos
<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>
Comece criando uma pasta que armazenará seu script (.py
arquivo) e os recursos de exemplo. Inclua as seguintes importações na parte superior do arquivo .py
:
import asyncio
import os
from semantic_kernel.agents.open_ai import AzureAssistantAgent
from semantic_kernel.contents import StreamingAnnotationContent
Além disso, copie o conteúdo de domínio público Grimms-The-King-of-the-Golden-Mountain.txt
, Grimms-The-Water-of-Life.txt
e Grimms-The-White-Snake.txt
do Projeto Semantic KernelLearnResources
. Adicione esses arquivos na pasta do projeto.
Os agentes estão atualmente indisponíveis em Java.
Configuração
Este exemplo requer definição de configuração para se conectar a serviços remotos. Você precisará definir configurações para OpenAI ou Azure OpenAI.
# OpenAI
dotnet user-secrets set "OpenAISettings:ApiKey" "<api-key>"
dotnet user-secrets set "OpenAISettings:ChatModel" "gpt-4o"
# Azure OpenAI
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"
A classe a seguir é usada em todos os exemplos de agente. Certifique-se de incluí-lo em seu projeto para garantir a funcionalidade adequada. Esta classe serve como um componente fundamental para os exemplos que se seguem.
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();
}
}
A maneira mais rápida de começar com a configuração adequada para executar o código de exemplo é criar um .env
arquivo na raiz do seu projeto (onde o script é executado).
Configure as configurações seguintes no seu ficheiro .env
para Azure OpenAI ou o OpenAI.
AZURE_OPENAI_API_KEY="..."
AZURE_OPENAI_ENDPOINT="https://<resource-name>.openai.azure.com/"
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="..."
AZURE_OPENAI_API_VERSION="..."
OPENAI_API_KEY="sk-..."
OPENAI_ORG_ID=""
OPENAI_CHAT_MODEL_ID=""
")
Os Assistentes do Azure exigem uma versão de API de pelo menos 2024-05-01-preview. À medida que novos recursos são introduzidos, as versões da API são atualizadas de acordo. No momento em que este artigo foi escrito, a versão mais recente é 2025-01-01-preview. Para obter os detalhes de controle de versão mais atualizados, consulte o ciclo de vida de pré-visualização da API Azure OpenAI.
Uma vez configuradas, as respetivas classes de serviço de IA pegarão as variáveis necessárias e as usarão durante a instanciação.
Os agentes estão atualmente indisponíveis em Java.
Codificação
O processo de codificação para este exemplo envolve:
- Configuração - Inicializando as configurações e o plug-in.
-
Definição de Agente - Crie o _Chat_Completion
Agent
com instruções padronizadas e plug-in. - The Chat Loop - Escreva o loop que impulsiona a interação usuário/agente.
O código de exemplo completo é fornecido na seção Final . Consulte essa seção para obter a implementação completa.
Configuração
Antes de criar um OpenAIAssistantAgent
, verifique se as definições de configuração estão disponíveis e prepare os recursos do arquivo.
Instancie a Settings
classe referenciada na seção Configuração anterior. Use as configurações para também criar um AzureOpenAIClient
que será usado para a definição do agente, bem como para carregar arquivos e criar um VectorStore
.
Settings settings = new();
AzureOpenAIClient client = OpenAIAssistantAgent.CreateAzureOpenAIClient(new AzureCliCredential(), new Uri(settings.AzureOpenAI.Endpoint));
O método de classe setup_resources()
no Agente Assistente trata de criar o cliente e retorná-lo e o modelo a ser usado com base na configuração desejada. As configurações Pydantic são usadas para carregar variáveis de ambiente, primeiro a partir destas ou do arquivo .env
. Pode-se optar por api_key
, api_version
, deployment_name
ou endpoint
, que terá prioridade sobre quaisquer variáveis de ambiente configuradas.
# Create the client using Azure OpenAI resources and configuration
client, model = AzureAssistantAgent.setup_resources()
Os agentes estão atualmente indisponíveis em Java.
Agora crie um _Vetor Store vazio para usar com a ferramenta de pesquisa de arquivos:
Use o AzureOpenAIClient
para aceder a um VectorStoreClient
e criar um VectorStore
.
Console.WriteLine("Creating store...");
VectorStoreClient storeClient = 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.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
"resources",
)
return os.path.join(base_directory, filename)
# Upload the files to the client
file_ids: list[str] = []
for path in [get_filepath_for_filename(filename) for filename in filenames]:
with open(path, "rb") as file:
file = await client.files.create(file=file, purpose="assistants")
file_ids.append(file.id)
# Get the file search tool and resources
file_search_tools, file_search_tool_resources = AzureAssistantAgent.configure_file_search_tool(file_ids=file_ids)
Os agentes estão atualmente indisponíveis em Java.
Vamos declarar os três arquivos de conteúdo descritos na seção Configuração anterior:
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",
]
Os agentes estão atualmente indisponíveis em Java.
Agora, carregue esses arquivos e adicione-os ao Vector Store usando os clientes criados anteriormente VectorStoreClient
para carregar cada arquivo com um OpenAIFileClient
e adicioná-lo ao Vector Store, preservando as Referências de Arquivo resultantes.
Dictionary<string, OpenAIFile> fileReferences = [];
Console.WriteLine("Uploading files...");
OpenAIFileClient fileClient = 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);
}
Os agentes estão atualmente indisponíveis em Java.
Definição do agente
Estamos agora prontos para instanciar um OpenAIAssistantAgent
. O agente é configurado com seu modelo de destino, Instruções e a ferramenta de Pesquisa de Arquivos habilitada. Além disso, associamos explicitamente o Vetor Store à ferramenta de Pesquisa deFicheiros.
Utilizaremos o AzureOpenAIClient
novamente como parte da criação do OpenAIAssistantAgent
:
Console.WriteLine("Defining assistant...");
Assistant assistant =
await assistantClient.CreateAssistantAsync(
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);
// Create agent
OpenAIAssistantAgent agent = new(assistant, assistantClient);
# Create the assistant definition
definition = await client.beta.assistants.create(
model=model,
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.
""",
name="SampleAssistantAgent",
tools=file_search_tools,
tool_resources=file_search_tool_resources,
)
# Create the agent using the client and the assistant definition
agent = AzureAssistantAgent(
client=client,
definition=definition,
)
Os agentes estão atualmente indisponíveis em Java.
O Loop do Chat
Finalmente, somos capazes de coordenar a interação entre o usuário e o Agent
. Comece criando um Thread Assistente para manter o estado da conversa e criando um loop vazio.
Vamos também garantir que os recursos sejam removidos no final da execução para minimizar cobranças desnecessárias.
Console.WriteLine("Creating thread...");
AssistantThread thread = await assistantClient.CreateThreadAsync();
Console.WriteLine("Ready!");
try
{
bool isComplete = false;
do
{
// Processing occurs here
} while (!isComplete);
}
finally
{
Console.WriteLine();
Console.WriteLine("Cleaning-up...");
await Task.WhenAll(
[
assistantClient.DeleteThreadAsync(thread.Id),
assistantClient.DeleteAssistantAsync(assistant.Id),
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()
Os agentes estão atualmente indisponíveis em Java.
Agora vamos capturar a entrada do usuário dentro do loop anterior. Neste caso, a entrada vazia será ignorada e o termo EXIT
sinalizará que a conversa está concluída. Uma entrada válida será adicionada ao fio do Assistente como uma mensagem de utilizador .
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(thread.Id, new ChatMessageContent(AuthorRole.User, input));
Console.WriteLine();
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)
)
Os agentes estão atualmente indisponíveis em Java.
Antes de invocar a resposta Agent
, vamos adicionar um método auxiliar para reformatar os colchetes de anotação unicode para colchetes ANSI.
private static string ReplaceUnicodeBrackets(this string content) =>
content?.Replace('【', '[').Replace('】', ']');
# No special handling required.
Os agentes estão atualmente indisponíveis em Java.
Para gerar uma resposta Agent
à entrada do usuário, invoque o agente especificando o Assistant Thread. Neste exemplo, escolhemos uma resposta transmitida em fluxo e capturamos todas as Anotações de Citação associadas para exibição no final do ciclo de resposta. Observe que cada parte transmitida está sendo reformatada usando o método auxiliar anterior.
List<StreamingAnnotationContent> footnotes = [];
await foreach (StreamingChatMessageContent chunk in agent.InvokeStreamingAsync(thread.Id))
{
// 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})"
)
Os agentes estão atualmente indisponíveis em Java.
Final
Juntando todas as etapas, temos o código final para este exemplo. A implementação completa é fornecida abaixo.
Tente usar estas entradas sugeridas.
- Qual é a contagem de parágrafos para cada uma das histórias?
- Crie uma tabela que identifique o protagonista e antagonista de cada história.
- Qual é a moral em A Serpente Branca?
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using OpenAI.Assistants;
using OpenAI.Files;
using OpenAI.VectorStores;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
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();
// Initialize the clients
AzureOpenAIClient client = OpenAIAssistantAgent.CreateAzureOpenAIClient(new AzureCliCredential(), new Uri(settings.AzureOpenAI.Endpoint));
//OpenAIClient client = OpenAIAssistantAgent.CreateOpenAIClient(new ApiKeyCredential(settings.OpenAI.ApiKey)));
AssistantClient assistantClient = client.GetAssistantClient();
OpenAIFileClient fileClient = client.GetOpenAIFileClient();
VectorStoreClient storeClient = client.GetVectorStoreClient();
// Create the vector store
Console.WriteLine("Creating store...");
CreateVectorStoreOperation operation = await storeClient.CreateVectorStoreAsync(waitUntilCompleted: true);
string storeId = operation.VectorStoreId;
// Upload files and retain file references.
Console.WriteLine("Uploading files...");
Dictionary<string, OpenAIFile> fileReferences = [];
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);
}
// Define assistant
Console.WriteLine("Defining assistant...");
Assistant assistant =
await assistantClient.CreateAssistantAsync(
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);
// Create agent
OpenAIAssistantAgent agent = new(assistant, assistantClient);
// Create the conversation thread
Console.WriteLine("Creating thread...");
AssistantThread thread = await assistantClient.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(thread.Id, new ChatMessageContent(AuthorRole.User, input));
Console.WriteLine();
List<StreamingAnnotationContent> footnotes = [];
await foreach (StreamingChatMessageContent chunk in agent.InvokeStreamingAsync(thread.Id))
{
// 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(
[
assistantClient.DeleteThreadAsync(thread.Id),
assistantClient.DeleteAssistantAsync(assistant.Id),
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 import AzureAssistantAgent
from semantic_kernel.contents import StreamingAnnotationContent
"""
The following sample demonstrates how to create a simple,
OpenAI assistant agent that utilizes the vector store
to answer questions based on the uploaded documents.
"""
def get_filepath_for_filename(filename: str) -> str:
base_directory = os.path.join(
os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
"resources",
)
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():
# Create the client using Azure OpenAI resources and configuration
client, model = AzureAssistantAgent.setup_resources()
# Upload the files to the client
file_ids: list[str] = []
for path in [get_filepath_for_filename(filename) for filename in filenames]:
with open(path, "rb") as file:
file = await client.files.create(file=file, purpose="assistants")
file_ids.append(file.id)
vector_store = await client.beta.vector_stores.create(
name="assistant_search",
file_ids=file_ids,
)
# Get the file search tool and resources
file_search_tools, file_search_tool_resources = AzureAssistantAgent.configure_file_search_tool(
vector_store_ids=vector_store.id
)
# Create the assistant definition
definition = await client.beta.assistants.create(
model=model,
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.
""",
name="SampleAssistantAgent",
tools=file_search_tools,
tool_resources=file_search_tool_resources,
)
# Create the agent using the client and the assistant definition
agent = AzureAssistantAgent(
client=client,
definition=definition,
)
print("Creating thread...")
thread = await client.beta.threads.create()
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=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("\nCleaning up resources...")
[await client.files.delete(file_id) for file_id in file_ids]
await client.beta.threads.delete(thread.id)
await client.beta.assistants.delete(agent.id)
if __name__ == "__main__":
asyncio.run(main())
Você pode encontrar o código de completo, como mostrado acima, em nosso repositório.
Os agentes estão atualmente indisponíveis em Java.