Not
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Instruktioner:
Viktig
Den här funktionen är i releasekandidatstadiet. Funktionerna i det här skedet är nästan fullständiga och i allmänhet stabila, även om de kan genomgå mindre förbättringar eller optimeringar innan de når full allmän tillgänglighet.
Översikt
I det här exemplet utforskar vi hur du använder filsökningsverktyget i en OpenAIAssistantAgent för att slutföra förståelseuppgifter. Metoden kommer att vara stegvis, vilket säkerställer tydlighet och precision under hela processen. Som en del av uppgiften tillhandahåller agenten dokumentciteringar i svaret.
Direktuppspelning används för att leverera agentens svar. Detta ger realtidsuppdateringar under aktivitetens gång.
Komma igång
Innan du fortsätter med kodning av funktioner, kontrollerar du att utvecklingsmiljön är fullt inställd och konfigurerad.
Om du vill lägga till paketberoenden från kommandoraden dotnet använder du kommandot:
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
Viktig
Om du hanterar NuGet-paket i Visual Studio kontrollerar du att Include prerelease det är markerat.
Projektfilen (.csproj) bör innehålla följande PackageReference definitioner:
<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>
Agent Framework är experimentell och kräver varningsundertryckning. Detta kan adresseras som en egenskap i projektfilen (.csproj):
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
Kopiera dessutom innehåll från den offentliga domänen för Grimms-The-King-of-the-Golden-Mountain.txt, Grimms-The-Water-of-Life.txt och Grimms-The-White-Snake.txt från Semantic Kernel LearnResources-projektet. Lägg till dessa filer i projektmappen och konfigurera så att de kopieras till utdatakatalogen:
<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>
Börja med att skapa en mapp som innehåller skriptet (.py filen) och exempelresurserna. Inkludera följande importer överst i .py-filen:
import asyncio
import os
from semantic_kernel.agents import AssistantAgentThread, AzureAssistantAgent
from semantic_kernel.contents import StreamingAnnotationContent
Kopiera dessutom innehåll från den offentliga domänen för Grimms-The-King-of-the-Golden-Mountain.txt, Grimms-The-Water-of-Life.txt och Grimms-The-White-Snake.txt från Semantic Kernel LearnResources-projektet. Lägg till de här filerna i projektmappen.
Funktionen är för närvarande inte tillgänglig i Java.
Konfiguration
Det här exemplet kräver konfigurationsinställning för att ansluta till fjärrtjänster. Du måste definiera inställningar för antingen OpenAI eller 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"
Följande klass används i alla agentexempel. Se till att inkludera den i projektet för att säkerställa rätt funktioner. Den här klassen fungerar som en grundläggande komponent för de exempel som följer.
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();
}
}
Det snabbaste sättet att komma igång med rätt konfiguration för att köra exempelkoden är att skapa en .env fil i roten av projektet (där skriptet körs).
Konfigurera följande inställningar i .env filen för Antingen Azure OpenAI eller 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=""
Tips
Azure Assistants kräver en API-version av minst 2024-05-01-preview. När nya funktioner introduceras uppdateras API-versioner i enlighet med detta. När detta skrivs är den senaste versionen 2025-01-01-preview. För de senaste detaljerna om versionshantering, se Azure OpenAI API-förhandsgranskningslivscykel.
När de har konfigurerats hämtar respektive AI-tjänstklasser de variabler som krävs och använder dem under instansieringen.
Funktionen är för närvarande inte tillgänglig i Java.
Kodning
Kodningsprocessen för det här exemplet omfattar:
- Installation – Initiera inställningar och plugin-programmet.
-
Agentdefinition – Skapa _Chat_Completion
Agentmed mallbaserade instruktioner och plugin. - Chattslingan – Skriv loopen som driver interaktion mellan användare och agent.
Den fullständiga exempelkoden finns i avsnittet Slutlig . Se det avsnittet för den fullständiga implementeringen.
Inställning
Innan du skapar en OpenAIAssistantAgentkontrollerar du att konfigurationsinställningarna är tillgängliga och förbereder filresurserna.
Instansiera klassen Settings som refereras i föregående konfigurationsavsnitt . Använd inställningarna för att även skapa en AzureOpenAIClient som ska användas för agentdefinitionen samt filuppladdning och skapandet av en VectorStore.
Settings settings = new();
AzureOpenAIClient client = OpenAIAssistantAgent.CreateAzureOpenAIClient(new AzureCliCredential(), new Uri(settings.AzureOpenAI.Endpoint));
Den statiska metoden create_client() på assistentagenten hanterar skapandet av klienten och returnerar den baserat på önskad konfiguration. Pydantiska inställningar används för att läsa in miljövariabler först från miljövariabler eller från filen .env. Man kan skicka in api_key, api_version, deployment_name eller endpoint, som har företräde framför alla konfigurerade miljövariabler.
# Create the client using Azure OpenAI resources and configuration
client = AzureAssistantAgent.create_client()
Funktionen är för närvarande inte tillgänglig i Java.
Skapa nu en tom _Vector Store för användning med filsökningsverktyget:
Använd AzureOpenAIClient för att komma åt en VectorStoreClient och skapa en VectorStore.
Console.WriteLine("Creating store...");
VectorStoreClient storeClient = client.GetVectorStoreClient();
CreateVectorStoreOperation operation = await storeClient.CreateVectorStoreAsync(waitUntilCompleted: true);
string storeId = operation.VectorStoreId;
# 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.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
)
Funktionen är för närvarande inte tillgänglig i Java.
Nu ska vi deklarera de tre innehållsfilerna som beskrivs i föregående konfigurationsavsnitt :
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",
]
Funktionen är för närvarande inte tillgänglig i Java.
Ladda nu upp dessa filer och lägg till dem i Vector Store med hjälp av tidigare skapade VectorStoreClient klienter för att ladda upp varje fil med en OpenAIFileClient och lägga till den i Vector Store, vilket bevarar de resulterande filreferenserna.
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);
}
Funktionen är för närvarande inte tillgänglig i Java.
Definition av agent
Nu är vi redo att instansiera en OpenAIAssistantAgent. Agenten är konfigurerad med sin målmodell, instruktioner och filsökningsverktygetaktiverat. Dessutom associerar vi explicit Vector Store med filsökningsverktyget.
Vi kommer att använda AzureOpenAIClient igen som en del av att skapa 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=AzureOpenAISettings().chat_deployment_name,
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,
)
Funktionen är för närvarande inte tillgänglig i Java.
Chattloopen
Äntligen kan vi samordna interaktionen mellan användaren och Agent. Börja med att skapa en AgentThread för att behålla konversationstillståndet och skapa en tom loop.
Vi ska också se till att resurserna tas bort vid körningens slut för att minimera onödiga avgifter.
Console.WriteLine("Creating thread...");
OpenAIAssistantAgent agentThread = new();
Console.WriteLine("Ready!");
try
{
bool isComplete = false;
do
{
// Processing occurs here
} while (!isComplete);
}
finally
{
Console.WriteLine();
Console.WriteLine("Cleaning-up...");
await Task.WhenAll(
[
agentThread.DeleteAsync();
assistantClient.DeleteAssistantAsync(assistant.Id),
storeClient.DeleteVectorStoreAsync(storeId),
..fileReferences.Select(fileReference => fileClient.DeleteFileAsync(fileReference.Key))
]);
}
# If no thread is provided, a new thread will be
# created and returned with the initial response
thread: AssistantAgentThread = None
try:
is_complete: bool = False
while not is_complete:
# Processing occurs here
finally:
print("\nCleaning up resources...")
[await client.files.delete(file_id) for file_id in file_ids]
await client.vector_stores.delete(vector_store.id)
await thread.delete() if thread else None
await client.beta.assistants.delete(agent.id)
Funktionen är för närvarande inte tillgänglig i Java.
Nu ska vi samla in användarindata i föregående loop. I det här fallet ignoreras tomma indata och termen EXIT signalerar att konversationen har slutförts.
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
continue;
}
if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
isComplete = true;
break;
}
var message = 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
Funktionen är för närvarande inte tillgänglig i Java.
Innan vi anropar svaret för Agent, låt oss lägga till en hjälpmetod för att reformatera unicode-anteckningsklamrarna till ANSI-klamrar.
private static string ReplaceUnicodeBrackets(this string content) =>
content?.Replace('【', '[').Replace('】', ']');
# No special handling required.
Funktionen är för närvarande inte tillgänglig i Java.
För att generera ett Agent svar på användarens indata aktiverar du agenten genom att specificera agenttråden och meddelandet. I det här exemplet väljer vi ett strömmat svar och samlar in eventuella associerade citatanteckningar för visning i slutet av svarscykeln. Observera att varje strömmat segment formateras om med hjälp av den tidigare hjälpmetoden.
List<StreamingAnnotationContent> footnotes = [];
await foreach (StreamingChatMessageContent chunk in agent.InvokeStreamingAsync(message, agentThread))
{
// 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(messages=user_input, thread=thread):
thread = response.thread
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})"
)
Funktionen är för närvarande inte tillgänglig i Java.
Slutlig
För att samla alla steg har vi den slutliga koden för det här exemplet. Den fullständiga implementeringen tillhandahålls nedan.
Prova att använda följande föreslagna indata:
- Vad är styckeantalet för var och en av berättelserna?
- Skapa en tabell som identifierar huvudpersonen och antagonisten för varje berättelse.
- Vad är moralen i Den vita ormen?
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...");
AssistantAgentThread agentThread = new();
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;
}
var message = new ChatMessageContent(AuthorRole.User, input);
Console.WriteLine();
List<StreamingAnnotationContent> footnotes = [];
await foreach (StreamingChatMessageContent chunk in agent.InvokeStreamingAsync(message, agentThread))
{
// 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(
[
agentThread.DeleteAsync(),
assistantClient.DeleteAssistantAsync(assistant.Id),
storeClient.DeleteVectorStoreAsync(storeId),
..fileReferences.Select(fileReference => fileClient.DeleteFileAsync(fileReference.Key))
]);
}
}
private static string ReplaceUnicodeBrackets(this string content) =>
content?.Replace('【', '[').Replace('】', ']');
}
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import os
from semantic_kernel.agents import AssistantAgentThread, AzureAssistantAgent
from semantic_kernel.connectors.ai.open_ai import AzureOpenAISettings
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.
This is the full code sample for the Semantic Kernel Learn Site: How-To: Open AI Assistant Agent File Search
https://learn.microsoft.com/semantic-kernel/frameworks/agent/examples/example-assistant-search?pivots=programming-language-python
"""
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 = AzureAssistantAgent.create_client()
# 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.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=AzureOpenAISettings().chat_deployment_name,
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,
)
thread: AssistantAgentThread | None = None
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
footnotes: list[StreamingAnnotationContent] = []
async for response in agent.invoke_stream(messages=user_input, thread=thread):
footnotes.extend([item for item in response.items if isinstance(item, StreamingAnnotationContent)])
print(f"{response.content}", end="", flush=True)
thread = response.thread
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.vector_stores.delete(vector_store.id)
await thread.delete() if thread else None
await client.beta.assistants.delete(agent.id)
if __name__ == "__main__":
asyncio.run(main())
Du kan hitta den fullständiga koden, som du ser ovan, på vår lagringsplats.
Funktionen är för närvarande inte tillgänglig i Java.