Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Guía:
Importante
Esta característica está en la fase candidata para lanzamiento. Las características de esta fase son casi completas y, por lo general, estables, aunque pueden someterse a pequeños refinamientos o optimizaciones antes de alcanzar la disponibilidad general completa.
Información general
En este ejemplo, exploraremos cómo usar la herramienta de búsqueda de archivos de un OpenAIAssistantAgent
para completar las tareas de comprensión. El enfoque será paso a paso, lo que garantizará la claridad y la precisión en todo el proceso. Como parte de la tarea, el agente proporcionará citas de documentos dentro de la respuesta.
El streaming se usará para entregar las respuestas del agente. Esto proporcionará actualizaciones en tiempo real a medida que avanza la tarea.
Introducción
Antes de continuar con la programación de funcionalidades, asegúrese de que su entorno de desarrollo esté completamente configurado.
Para agregar dependencias de paquete desde la línea de comandos, use el 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
Importante
Si administra paquetes NuGet en Visual Studio, asegúrese de que Include prerelease
está activado.
El archivo de proyecto (.csproj
) debe contener las definiciones siguientes 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>
El Agent Framework
es experimental y requiere la supresión de advertencias. Esto puede abordarse en como una propiedad en el archivo del proyecto (.csproj
):
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
Además, copie el contenido de dominio público Grimms-The-King-of-the-Golden-Mountain.txt
, Grimms-The-Water-of-Life.txt
y Grimms-The-White-Snake.txt
del Semantic Kernel LearnResources
Project. Agregue estos archivos en la carpeta del proyecto y configúrelos para que se copien en el directorio de salida:
<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>
Empiece por crear una carpeta que contenga el script (.py
archivo) y los recursos de ejemplo. Incluya las siguientes importaciones en la parte superior del .py
archivo:
import asyncio
import os
from semantic_kernel.agents import AssistantAgentThread, AzureAssistantAgent
from semantic_kernel.contents import StreamingAnnotationContent
Además, copie el contenido de dominio público Grimms-The-King-of-the-Golden-Mountain.txt
, Grimms-The-Water-of-Life.txt
y Grimms-The-White-Snake.txt
del Semantic Kernel LearnResources
Project. Agregue estos archivos en la carpeta del proyecto.
Característica actualmente no disponible en Java.
Configuración
Este ejemplo requiere la configuración para conectarse a servicios remotos. Deberá definir la configuración de OpenAI o 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"
La siguiente clase se usa en todos los ejemplos del agente. Asegúrese de incluirlo en el proyecto para garantizar una funcionalidad adecuada. Esta clase actúa como un componente fundamental para los ejemplos siguientes.
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();
}
}
La forma más rápida de empezar a trabajar con la configuración adecuada para ejecutar el código de ejemplo es crear un .env
archivo en la raíz del proyecto (donde se ejecuta el script).
Configure los siguientes ajustes en su archivo .env
para Azure OpenAI 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=""
Propina
Azure Assistants requiere una versión de API de al menos 2024-05-01-preview. A medida que se introducen nuevas características, las versiones de API se actualizan en consecuencia. A partir de este artículo, la versión más reciente es 2025-01-01-preview. Para obtener los detalles de versiones más actualizados, consulte el ciclo de vida de la versión preliminar de la API de Azure OpenAI.
Una vez configuradas, las clases de servicio de IA correspondientes recogerán las variables necesarias y las usarán durante la creación de instancias.
Característica actualmente no disponible en Java.
Codificar
El proceso de codificación de este ejemplo implica:
- Configuración : inicialización de la configuración y el complemento.
-
Definición del Agente - Crear el _Chat_Completion
Agent
con instrucciones y complementos basados en plantillas. - Bucle de chat: escriba el bucle que impulsa la interacción del usuario o agente.
El código de ejemplo completo se proporciona en la sección Final . Consulte esa sección para obtener la implementación completa.
Configuración
Antes de crear un OpenAIAssistantAgent
, asegúrese de que las opciones de configuración están disponibles y preparen los recursos de archivo.
Cree una instancia de la clase Settings
referenciada en la sección Configuración anterior. Use la configuración para crear también un AzureOpenAIClient
que se usará para la definición del agente, así como para la carga de archivos y la creación de un VectorStore
.
Settings settings = new();
AzureOpenAIClient client = OpenAIAssistantAgent.CreateAzureOpenAIClient(new AzureCliCredential(), new Uri(settings.AzureOpenAI.Endpoint));
El método create_client()
estático del Agente asistente controla la creación del cliente y la devuelve en función de la configuración deseada. La configuración de Pydantic se usa para cargar las variables de entorno, ya sea desde las variables de entorno directamente o desde el archivo .env
. Es posible utilizar el api_key
, api_version
, deployment_name
o endpoint
, que tendrá prioridad sobre las variables de entorno configuradas.
# Create the client using Azure OpenAI resources and configuration
client = AzureAssistantAgent.create_client()
Característica actualmente no disponible en Java.
Ahora cree un almacén de _Vector vacío para usarlo con la herramienta búsqueda de archivos:
Utilice el AzureOpenAIClient
para acceder a un VectorStoreClient
y crear un 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
)
Característica actualmente no disponible en Java.
Vamos a declarar los tres archivos de contenido descritos en la sección Configuración 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",
]
Característica actualmente no disponible en Java.
Ahora cargue esos archivos y agréguelos al almacén de vectores mediante el uso de los clientes creados VectorStoreClient
anteriormente para cargar cada archivo con OpenAIFileClient
y agregarlo al almacén de vectores, conservando las referencias de archivo 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);
}
Característica actualmente no disponible en Java.
Definición del agente
Ahora estamos listos para instanciar un OpenAIAssistantAgent
. El agente está configurado con su modelo de destino, Instrucciones y la herramienta búsqueda de archivos habilitada. Además, asociamos explícitamente el almacén de vectores a la herramienta búsqueda de archivos .
Usaremos AzureOpenAIClient
de nuevo como parte de la creación de 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,
)
Característica actualmente no disponible en Java.
Bucle de chat
Por último, podemos coordinar la interacción entre el usuario y el Agent
. Empiece por crear un AgentThread
para mantener el estado de la conversación y crear un bucle vacío.
Asegurémonos también de que los recursos se eliminen al final de la ejecución para minimizar cargos innecesarios.
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)
Característica actualmente no disponible en Java.
Ahora vamos a capturar la entrada del usuario dentro del bucle anterior. En este caso, se omitirá la entrada vacía y el término EXIT
indicará que la conversación se ha completado.
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
Característica actualmente no disponible en Java.
Antes de invocar la respuesta Agent
, vamos a agregar un método auxiliar para volver a formatear los corchetes de anotación unicode a corchetes ANSI.
private static string ReplaceUnicodeBrackets(this string content) =>
content?.Replace('【', '[').Replace('】', ']');
# No special handling required.
Característica actualmente no disponible en Java.
Para generar una respuesta Agent
a la entrada del usuario, invoque al agente especificando el mensaje y el subproceso del agente. En este ejemplo, se elige una respuesta transmitida y se capturan las anotaciones de cita asociadas para mostrarse al final del ciclo de respuesta. Tenga en cuenta que cada fragmento transmitido se vuelve a formatear mediante el método auxiliar anterior.
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})"
)
Característica actualmente no disponible en Java.
Final
Al reunir todos los pasos, tenemos el código final de este ejemplo. A continuación se proporciona la implementación completa.
Pruebe a usar estas entradas sugeridas:
- ¿Cuál es el recuento de párrafos para cada una de las historias?
- Crea una tabla que identifica al protagonista y al adversario de cada historia.
- ¿Cuál es la moral en La Serpiente Blanca?
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())
Puede encontrar el código completo , como se muestra anteriormente, en nuestro repositorio.
Característica actualmente no disponible en Java.