A funcionalidade de File Search do Azure OpenAI permite que você carregue arquivos para o serviço e faça buscas sofisticadas dentro deles, utilizando a tecnologia de vetores para armazenar e consultar esses documentos. Isso é particularmente útil para criar assistentes personalizados que podem responder a perguntas específicas com base em informações presentes nos arquivos carregados. Para mais detalhes, você pode conferir a documentação oficial aqui.
Já escrevi um post sobre os assistentes do Azure OpenAI! Se você ainda não leu, recomendo dar uma olhada para entender como integrar essas funcionalidades em seus projetos. Você pode acessar o post aqui!
Agora, vamos continuar com o nosso projeto. Primeiro, crie o arquivo .gitignore
:
dotnet new gitignore
Em seguida, adicionaremos um projeto do tipo worker. Para mais detalhes, consulte a documentação oficial:
dotnet new worker
Instale o pacote NuGet necessário para trabalhar com o Azure OpenAI:
dotnet add package Azure.AI.OpenAI --prerelease
Agora, vamos configurar o deployment no Azure OpenAI seguindo este guia. Após criar o deployment, adicione as chaves de configuração no appsettings.json
:
"AzureOpenAi": {
"Endpoint": "<your-endpoint>",
"Key": "<your-key>",
"Deployment": "<your-deployment-name>"
}
Agora é hora de começar a implementação do código. No método execute do Worker, vamos criar os objetos necessários para o nosso assistente:
var client = GetClient();
var assistantClient = client.GetAssistantClient();
var vectorStoreClient = client.GetVectorStoreClient();
var fileClient = client.GetFileClient();
VectorStore vectorStore = await CreateVectorStore(vectorStoreClient, stoppingToken);
OpenAIFileInfo file = await UploadFileAsync(client, vectorStore, fileClient);
AssistantThread thread = await assistantClient.CreateThreadAsync(cancellationToken: stoppingToken);
Assistant assistant = await CreateAssistantAsync(assistantClient, vectorStore, stoppingToken);
Temos os métodos para criar a vector store, upload de arquivo, e o assistente:
private async Task<VectorStore> CreateVectorStore(VectorStoreClient vectorStoreClient, CancellationToken stoppingToken)
{
// Create vector store
return await vectorStoreClient.CreateVectorStoreAsync(new VectorStoreCreationOptions
{
Name = "Docs"
}, stoppingToken);
}
private async Task<OpenAIFileInfo> UploadFileAsync(AzureOpenAIClient client, VectorStore vectorStore, FileClient fileClient)
{
// Upload file
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "History.md");
OpenAIFileInfo file =await fileClient.UploadFileAsync(filePath, FileUploadPurpose.Assistants);
// Link file to the vector store
var vectorStoreClient = client.GetVectorStoreClient();
await vectorStoreClient.AddFileToVectorStoreAsync(vectorStore, file);
return file;
}
private async Task<Assistant> CreateAssistantAsync(AssistantClient assistantClient, VectorStore vectorStore, CancellationToken stoppingToken)
{
// Create assistant
var assistantOptions = new AssistantCreationOptions
{
Name = "my-assistant",
Instructions = "You are a helpful assistant that can help fetch data from files you know about",
Tools = { new FileSearchToolDefinition() },
ToolResources = new ToolResources()
{
FileSearch = new FileSearchToolResources
{
VectorStoreIds = [vectorStore.Id]
}
}
};
return await assistantClient.CreateAssistantAsync(configuration["AzureOpenAi:Deployment"]!, assistantOptions, stoppingToken);
}
As etapas realizadas acima foram:
- Criamos um vector store para armazenar as informações.
- Fizemos o upload de um arquivo via SDK do Azure OpenAI, conforme a referência.
- Associamos o arquivo ao vector store criado.
- Criamos um assistente com acesso ao vector store.
No loop abaixo, o assistente lê a mensagem digitada pelo usuário, envia a mensagem para o assistente, pede para o assistente processar a mensagem (pode ou não utilizar o file search), busca a última mensagem do histórico e exibe o resultado, as referências e os tokens utilizados.
while (true)
{
var request = ReadConsole();
if (request.ToLower() == "exit")
{
break;
}
await assistantClient.CreateMessageAsync(thread.Id, [MessageContent.FromText(request)], cancellationToken: stoppingToken);
ThreadRun threadRun = await assistantClient.CreateRunAsync(thread.Id, assistant.Id, cancellationToken: stoppingToken);
do
{
await Task.Delay(TimeSpan.FromMilliseconds(100));
threadRun = await assistantClient.GetRunAsync(thread.Id, threadRun.Id);
}
while (threadRun.Status == RunStatus.Queued || threadRun.Status == RunStatus.InProgress);
AsyncPageableCollection<ThreadMessage> messagePage = assistantClient.GetMessagesAsync(thread.Id, ListOrder.NewestFirst);
await using var enumerator = messagePage.GetAsyncEnumerator(stoppingToken);
var messageItem = await enumerator.MoveNextAsync() ? enumerator.Current : null;
foreach (MessageContent contentItem in messageItem!.Content)
{
if (!string.IsNullOrEmpty(contentItem.Text))
{
Console.WriteLine($"Assistants -> {contentItem.Text}");
if (contentItem.TextAnnotations.Count > 0)
{
Console.WriteLine();
}
foreach (TextAnnotation annotation in contentItem.TextAnnotations)
{
if (!string.IsNullOrEmpty(annotation.InputFileId))
{
Console.WriteLine($"-> File citation, file ID: {annotation.InputFileId}");
}
}
}
}
Console.WriteLine($"-> Completions tokens: {threadRun.Usage.CompletionTokens}");
Console.WriteLine($"-> PromptTokens tokens: {threadRun.Usage.PromptTokens}");
Console.WriteLine($"-> TotalTokens tokens: {threadRun.Usage.TotalTokens}");
Console.WriteLine();
}
Para fechar o desenvolvimento, iremos deletar os itens criados via SDK, caso o usuário digite "exit".
await fileClient.DeleteFileAsync(file);
await vectorStoreClient.DeleteVectorStoreAsync(vectorStore);
await assistantClient.DeleteThreadAsync(thread);
await assistantClient.DeleteAssistantAsync(assistant);
Vamos adicionar um novo arquivo chamado History.md
na raiz do projeto, que contém uma história sobre três irmãos em Hogwarts!
### The Adventures of Mike and His Three Sisters: Maria, Jane, and Jessica at Hogwarts
The sky above Hogwarts was a kaleidoscope of color as the sun set over the ancient castle. The shadows of its turrets stretched long across the grounds, hinting at the secrets and mysteries hidden within. It was in this magical world that Mike, along with his three sisters—Maria, Jane, and Jessica—found themselves embarking on the adventure of a lifetime.
#### The Sorting Hat’s Decision
Mike was the youngest in the family, a shy but clever boy who had always looked up to his older sisters. Maria, the eldest, was the epitome of wisdom and grace. She had a natural affinity for Charms and was known for her calm demeanor. Jane, the middle sister, was the artistic one, with a deep love for all things creative. Her wandwork was often described as elegant, and she had a special talent for Transfiguration. Jessica, the youngest of the three sisters, was a firecracker—bold, daring, and always ready for an adventure. She excelled in Defense Against the Dark Arts and was never one to back down from a challenge.
The four siblings had grown up hearing stories about Hogwarts from their parents, both accomplished wizards themselves. When the time came, each sister had been sorted into a different house: Maria into Ravenclaw, Jane into Hufflepuff, and Jessica into Gryffindor. Now it was Mike’s turn, and as he stood nervously before the Sorting Hat, he couldn’t help but wonder where he would be placed.
The Sorting Hat pondered for a moment before finally announcing, “Hufflepuff!” Mike was both relieved and excited. He would be in the same house as Jane, which meant they could support each other as they navigated the challenges of Hogwarts together.
#### The First Adventure: The Forbidden Forest
It didn’t take long for the siblings to find themselves in the middle of an adventure. On a crisp autumn evening, Jessica burst into the Hufflepuff common room, her eyes gleaming with excitement.
“Mike, Jane, you’ve got to come with me!” she whispered, barely able to contain herself. “I overheard Professor McGonagall talking about something strange happening in the Forbidden Forest. I think we should check it out!”
Maria, who had been reading in the Ravenclaw common room, was soon pulled into the plan as well. Though she was more cautious by nature, Maria couldn’t resist the thrill of solving a mystery with her siblings. The four of them sneaked out of the castle under the cover of darkness, their wands at the ready.
The Forbidden Forest loomed before them, dark and foreboding. As they ventured deeper into the woods, the air grew thick with tension. Strange sounds echoed through the trees, and the shadows seemed to move of their own accord. But the siblings pressed on, determined to uncover the secret hidden within.
Suddenly, a low growl rumbled through the forest, and they found themselves face-to-face with a massive, three-headed dog. It was a creature of legend, known as Fluffy, who had once guarded the Philosopher’s Stone.
“Stay calm,” Maria instructed, her voice steady. She raised her wand and began to hum a soft, soothing melody—a charm that she had mastered over the years. Fluffy’s growls softened as it was lulled to sleep by Maria’s spell, allowing the siblings to slip past the beast unharmed.
They continued deeper into the forest until they reached a clearing where an ancient stone stood. Strange runes were carved into its surface, glowing faintly in the moonlight. Jane, with her keen eye for detail, recognized the symbols as part of an ancient puzzle.
“It’s a test,” she murmured, running her fingers over the stone. “We need to figure out the right combination to unlock whatever it’s hiding.”
The siblings worked together, each contributing their unique skills. Maria deciphered the runes’ meanings, while Jane carefully arranged them in the correct order. Jessica kept watch, her wand ready to fend off any danger, while Mike provided a fresh perspective, noticing patterns that the others had missed.
Finally, the stone rumbled, and the ground beneath them shifted, revealing a hidden chamber. Inside, they found a glowing amulet, pulsating with powerful magic. Maria picked it up carefully, recognizing it as an ancient artifact that had been lost to time.
“This is incredible,” Maria whispered. “We need to take this to Dumbledore. He’ll know what to do.”
The siblings hurried back to the castle, their hearts pounding with excitement. When they presented the amulet to Professor Dumbledore, he praised them for their bravery and quick thinking.
“This amulet holds great power,” Dumbledore explained, “and it’s fortunate that it’s now in safe hands. I’m very proud of all of you.”
#### The Challenges of Hogwarts
As the school year progressed, the siblings faced many challenges. Mike, though initially shy, began to come into his own. He excelled in Herbology and Potions, quickly becoming one of Professor Sprout’s favorite students. With Jane’s encouragement, he even joined the Hufflepuff Quidditch team as a Seeker, proving to be a natural on a broomstick.
Maria continued to impress with her intellect, often earning top marks in her classes. She became a prefect and took her responsibilities seriously, guiding younger students and helping maintain order in the Ravenclaw common room.
Jane’s artistic talents blossomed as she explored new forms of magical art, such as spellbinding tapestries and enchanted paintings that moved and changed with the seasons. She even collaborated with Professor Flitwick to create a stunning mural in the Great Hall, depicting the four founders of Hogwarts.
Jessica, ever the thrill-seeker, joined the Duelling Club and quickly rose through the ranks. Her courage and quick reflexes made her a formidable opponent, and she earned the respect of her peers, even those in rival houses.
Despite their individual successes, the siblings always made time for each other. They would often meet in the Room of Requirement, a magical space that provided whatever they needed, whether it was a quiet place to study or a cozy spot to relax and share stories.
#### The Final Showdown
As the end of the school year approached, rumors began to circulate about a dark force rising within the castle. The siblings were determined to get to the bottom of it. With Dumbledore’s permission, they began investigating, uncovering clues that pointed to a hidden chamber deep beneath the castle.
The night of the final showdown arrived. The four siblings, armed with their wands and a plan, descended into the depths of Hogwarts. They encountered numerous obstacles—enchanted traps, magical beasts, and dark curses—but their bond as siblings saw them through.
At last, they reached the heart of the chamber, where they found an ancient relic pulsing with dark magic. A shadowy figure emerged, revealing itself as the spirit of an old dark wizard who had been trapped in the castle for centuries, waiting for the right moment to seize power.
With no time to lose, the siblings sprang into action. Maria’s precise spellcasting protected them from the dark wizard’s attacks, while Jane used her knowledge of ancient magic to weaken the relic’s power. Jessica engaged the dark wizard in a fierce duel, her bravery and skill pushing him back. Meanwhile, Mike, who had discovered a hidden passage leading to the chamber’s core, managed to destroy the relic, severing the dark wizard’s connection to the world.
As the relic shattered, the chamber began to collapse. The siblings raced to escape, narrowly making it out before the chamber was buried under tons of stone. Back in the safety of the castle, they were hailed as heroes. Their adventure had not only saved Hogwarts but had also brought them closer together.
#### Epilogue
The summer holidays arrived, and the siblings returned home, eager to share their tales of adventure with their parents. They had grown in ways they hadn’t imagined, each discovering their own strengths while learning the value of teamwork and family.
For Mike, Maria, Jane, and Jessica, Hogwarts had been more than just a school—it had been the place where they truly found themselves. Their adventures had shown them that while they each had their own unique talents, it was their bond as siblings that made them unstoppable.
As they sat around the fireplace at home, reminiscing about their year at Hogwarts, they knew that no matter what challenges lay ahead, they would always face them together, united by the magic of family and the adventures that awaited them at every turn.
Adicione a seguinte configuração para que o arquivo seja incluído no output directory durante o processo de build:
<ItemGroup>
<None Update="History.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
Execute o projeto com o seguinte comando:
dotnet run
Vamos fazer algumas perguntas para o assistente sobre o texto acima!.
Primeira Pergunta:
What happened in the epilogue? Give me a brief answer
Resposta:
Outra pergunta:
What are Maria's most striking characteristics?
Reposta:
Esse foi mais um post sobre as features do Azure OpenAI! Você já pode baixar o projeto por esse link, e não esquece de me seguir no LinkedIn!
Até a próxima, abraços!