Historial de chat
El objeto de historial de chat se usa para mantener un registro de mensajes en una sesión de chat. Se usa para almacenar mensajes de diferentes autores, como usuarios, asistentes, herramientas o el sistema. Como mecanismo principal para enviar y recibir mensajes, el objeto de historial de chat es esencial para mantener el contexto y la continuidad en una conversación.
Creación de un objeto de historial de chat
Un objeto de historial de chat es una lista en segundo plano, lo que facilita la creación y adición de mensajes.
using Microsoft.SemanticKernel.ChatCompletion;
// Create a chat history object
ChatHistory chatHistory = [];
chatHistory.AddSystemMessage("You are a helpful assistant.");
chatHistory.AddUserMessage("What's available to order?");
chatHistory.AddAssistantMessage("We have pizza, pasta, and salad available to order. What would you like to order?");
chatHistory.AddUserMessage("I'd like to have the first option, please.");
# Create a chat history object
chat_history = ChatHistory()
chat_history.add_system_message("You are a helpful assistant.")
chat_history.add_user_message("What's available to order?")
chat_history.add_assistant_message("We have pizza, pasta, and salad available to order. What would you like to order?")
chat_history.add_user_message("I'd like to have the first option, please.")
import com.microsoft.semantickernel.services.chatcompletion.ChatHistory;
// Create a chat history object
ChatHistory chatHistory = new ChatHistory();
chatHistory.addSystemMessage("You are a helpful assistant.");
chatHistory.addUserMessage("What's available to order?");
chatHistory.addAssistantMessage("We have pizza, pasta, and salad available to order. What would you like to order?");
chatHistory.addUserMessage("I'd like to have the first option, please.");
Adición de mensajes más enriquecidos a un historial de chat
La manera más fácil de agregar mensajes a un objeto de historial de chat es usar los métodos anteriores. Sin embargo, también puede agregar mensajes manualmente mediante la creación de un nuevo ChatMessage
objeto. Esto le permite proporcionar información adicional, como nombres e imágenes de contenido.
using Microsoft.SemanticKernel.ChatCompletion;
// Add system message
chatHistory.Add(
new() {
Role = AuthorRole.System,
Content = "You are a helpful assistant"
}
);
// Add user message with an image
chatHistory.Add(
new() {
Role = AuthorRole.User,
AuthorName = "Laimonis Dumins",
Items = [
new TextContent { Text = "What available on this menu" },
new ImageContent { Uri = new Uri("https://example.com/menu.jpg") }
]
}
);
// Add assistant message
chatHistory.Add(
new() {
Role = AuthorRole.Assistant,
AuthorName = "Restaurant Assistant",
Content = "We have pizza, pasta, and salad available to order. What would you like to order?"
}
);
// Add additional message from a different user
chatHistory.Add(
new() {
Role = AuthorRole.User,
AuthorName = "Ema Vargova",
Content = "I'd like to have the first option, please."
}
);
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents import ChatMessageContent, TextContent, ImageContent
from semantic_kernel.contents.utils.author_role import AuthorRole
# Add system message
chat_history.add_message(
ChatMessage(
role=AuthorRole.System,
content="You are a helpful assistant"
)
)
# Add user message with an image
chat_history.add_message(
ChatMessageContent(
role=AuthorRole.USER,
name="Laimonis Dumins",
items=[
TextContent(text="What available on this menu"),
ImageContent(uri="https://example.com/menu.jpg")
]
)
)
# Add assistant message
chat_history.add_message(
ChatMessageContent(
role=AuthorRole.ASSISTANT,
name="Restaurant Assistant",
content="We have pizza, pasta, and salad available to order. What would you like to order?"
)
)
# Add additional message from a different user
chat_history.add_message(
ChatMessageContent(
role=AuthorRole.USER,
name="Ema Vargova",
content="I'd like to have the first option, please."
)
)
import com.microsoft.semantickernel.services.chatcompletion.message.ChatMessageImageContent;
import com.microsoft.semantickernel.services.chatcompletion.message.ChatMessageTextContent;
// Add system message
chatHistory.addSystemMessage(
"You are a helpful assistant"
);
// Add user message with an image
chatHistory.addUserMessage(
"What available on this menu"
);
chatHistory.addMessage(
ChatMessageImageContent.builder()
.withImageUrl("https://example.com/menu.jpg")
.build()
);
// Add assistant message
chatHistory.addAssistantMessage(
"We have pizza, pasta, and salad available to order. What would you like to order?"
);
// Add additional message from a different user
chatHistory.addUserMessage(
"I'd like to have the first option, please."
);
Simulación de llamadas de función
Además de los roles de usuario, asistente y sistema, también puede agregar mensajes desde el rol de herramienta para simular llamadas de función. Esto es útil para enseñar a la inteligencia artificial cómo usar complementos y proporcionar contexto adicional a la conversación.
Por ejemplo, para insertar información sobre el usuario actual en el historial de chat sin necesidad de que el usuario proporcione la información o que el LLM pierda tiempo pidiéndole, puede usar el rol de herramienta para proporcionar la información directamente.
A continuación se muestra un ejemplo de cómo podemos proporcionar alergias al usuario al asistente mediante la simulación de una llamada de función al User
complemento.
Sugerencia
Las llamadas de función simuladas son especialmente útiles para proporcionar detalles sobre los usuarios actuales. Los LLM de hoy se han entrenado para ser especialmente sensibles a la información del usuario. Incluso si proporciona detalles del usuario en un mensaje del sistema, llm puede optar por omitirlo. Si la proporciona a través de un mensaje de usuario o mensaje de herramienta, es más probable que llm la use.
// Add a simulated function call from the assistant
chatHistory.Add(
new() {
Role = AuthorRole.Assistant,
Items = [
new FunctionCallContent(
functionName: "get_user_allergies",
pluginName: "User",
id: "0001",
arguments: new () { {"username", "laimonisdumins"} }
),
new FunctionCallContent(
functionName: "get_user_allergies",
pluginName: "User",
id: "0002",
arguments: new () { {"username", "emavargova"} }
)
]
}
);
// Add a simulated function results from the tool role
chatHistory.Add(
new() {
Role = AuthorRole.Tool,
Items = [
new FunctionResultContent(
functionName: "get_user_allergies",
pluginName: "User",
id: "0001",
result: "{ \"allergies\": [\"peanuts\", \"gluten\"] }"
)
]
}
);
chatHistory.Add(
new() {
Role = AuthorRole.Tool,
Items = [
new FunctionResultContent(
functionName: "get_user_allergies",
pluginName: "User",
id: "0002",
result: "{ \"allergies\": [\"dairy\", \"soy\"] }"
)
]
}
);
from semantic_kernel.contents import ChatMessageContent, FunctionCallContent, FunctionResultContent
# Add a simulated function call from the assistant
chat_history.add_message(
ChatMessageContent(
role=AuthorRole.ASSISTANT,
items=[
FunctionCallContent(
name="get_user_allergies-User",
id="0001",
arguments=str({"username": "laimonisdumins"})
),
FunctionCallContent(
name="get_user_allergies-User",
id="0002",
arguments=str({"username": "emavargova"})
)
]
)
)
# Add a simulated function results from the tool role
chat_history.add_message(
ChatMessageContent(
role=AuthorRole.TOOL,
items=[
FunctionResultContent(
name="get_user_allergies-User",
id="0001",
result="{ \"allergies\": [\"peanuts\", \"gluten\"] }"
)
]
)
)
chat_history.add_message(
ChatMessageContent(
role=AuthorRole.TOOL,
items=[
FunctionResultContent(
name="get_user_allergies-User",
id="0002",
result="{ \"allergies\": [\"dairy\", \"gluten\"] }"
)
]
)
)
This functionality is not supported in the current version of Semantic Kernel for Java.
Importante
Al simular los resultados de la herramienta, siempre debe proporcionar la id
de la llamada de función a la que corresponde el resultado. Esto es importante para que la inteligencia artificial comprenda el contexto del resultado. Algunas VM, como OpenAI, producirán un error si falta o id
si no id
corresponde a una llamada de función.
Inspección de un objeto de historial de chat
Siempre que pase un objeto de historial de chat a un servicio de finalización de chat con la llamada automática a funciones habilitadas, el objeto de historial de chat se manipulará para que incluya las llamadas y los resultados de la función. Esto le permite evitar tener que agregar manualmente estos mensajes al objeto de historial de chat y también le permite inspeccionar el objeto de historial de chat para ver las llamadas de función y los resultados.
Sin embargo, debe agregar los mensajes finales al objeto de historial de chat. A continuación se muestra un ejemplo de cómo puede inspeccionar el objeto de historial de chat para ver las llamadas de función y los resultados.
using Microsoft.SemanticKernel.ChatCompletion;
ChatHistory chatHistory = [
new() {
Role = AuthorRole.User,
Content = "Please order me a pizza"
}
];
// Get the current length of the chat history object
int currentChatHistoryLength = chatHistory.Count;
// Get the chat message content
ChatMessageContent results = await chatCompletionService.GetChatMessageContentAsync(
chatHistory,
kernel: kernel
);
// Get the new messages added to the chat history object
for (int i = currentChatHistoryLength; i < chatHistory.Count; i++)
{
Console.WriteLine(chatHistory[i]);
}
// Print the final message
Console.WriteLine(results);
// Add the final message to the chat history object
chatHistory.Add(results);
from semantic_kernel.contents import ChatMessageContent
chat_history = ChatHistory([
ChatMessageContent(
role=AuthorRole.USER,
content="Please order me a pizza"
)
])
# Get the current length of the chat history object
current_chat_history_length = len(chat_history)
# Get the chat message content
results = await chat_completion.get_chat_message_content(
chat_history=history,
settings=execution_settings,
kernel=kernel,
)
# Get the new messages added to the chat history object
for i in range(current_chat_history_length, len(chat_history)):
print(chat_history[i])
# Print the final message
print(results)
# Add the final message to the chat history object
chat_history.add_message(results)
import com.microsoft.semantickernel.services.chatcompletion.ChatHistory;
ChatHistory chatHistory = new ChatHistory();
chatHistory.addUserMessage("Please order me a pizza");
// Get the chat message content
List<ChatMessageContent> results = chatCompletionService.getChatMessageContentsAsync(
chatHistory,
kernel,
null
).block();
results.forEach(result -> System.out.println(result.getContent());
// Get the new messages added to the chat history object. By default,
// the ChatCompletionService returns new messages only.
chatHistory.addAll(results);
Reducción del historial de chat
La administración del historial de chat es esencial para mantener conversaciones con reconocimiento del contexto, a la vez que garantiza un rendimiento eficaz. A medida que avanza una conversación, el objeto de historial puede crecer más allá de los límites de la ventana de contexto de un modelo, lo que afecta a la calidad de la respuesta y ralentiza el procesamiento. Un enfoque estructurado para reducir el historial de chat garantiza que la información más relevante permanezca disponible sin sobrecarga innecesaria.
¿Por qué reducir el historial de chats?
- Optimización del rendimiento: los historiales de chat grandes aumentan el tiempo de procesamiento. Reducir su tamaño ayuda a mantener interacciones rápidas y eficaces.
- Administración de ventanas de contexto: los modelos de lenguaje tienen una ventana de contexto fija. Cuando el historial supera este límite, se pierden los mensajes más antiguos. La administración del historial de chat garantiza que el contexto más importante siga siendo accesible.
- Eficiencia de la memoria: en entornos con restricciones de recursos, como aplicaciones móviles o sistemas incrustados, el historial de chat sin enlazar puede provocar un uso excesivo de memoria y un rendimiento lento.
- Privacidad y seguridad: conservar el historial de conversaciones innecesario aumenta el riesgo de exponer información confidencial. Un proceso de reducción estructurada minimiza la retención de datos al tiempo que mantiene el contexto pertinente.
Estrategias para reducir el historial de chats
Se pueden usar varios enfoques para mantener el historial de chat administrable, a la vez que se conserva la información esencial:
- Truncamiento: los mensajes más antiguos se quitan cuando el historial supera un límite predefinido, lo que garantiza que solo se conservan las interacciones recientes.
- Resumen: los mensajes más antiguos se condensan en un resumen, conservando los detalles clave a la vez que se reduce el número de mensajes almacenados.
- Basado en tokens: la reducción basada en tokens garantiza que el historial de chat permanezca dentro del límite de tokens de un modelo mediante la medición del recuento total de tokens y la eliminación o resumen de mensajes anteriores cuando se supere el límite.
Un reductor del historial de chat automatiza estas estrategias mediante la evaluación del tamaño del historial y la reducción en función de parámetros configurables, como el recuento de destinos (el número deseado de mensajes que se van a conservar) y el recuento de umbrales (el punto en el que se desencadena la reducción). Al integrar estas técnicas de reducción, las aplicaciones de chat pueden seguir respondiendo y funcionando sin poner en peligro el contexto conversacional.
En la versión de .NET del Kernel Semántico, la abstracción del Chat History Reducer se define por la interfaz IChatHistoryReducer
.
namespace Microsoft.SemanticKernel.ChatCompletion;
[Experimental("SKEXP0001")]
public interface IChatHistoryReducer
{
Task<IEnumerable<ChatMessageContent>?> ReduceAsync(IReadOnlyList<ChatMessageContent> chatHistory, CancellationToken cancellationToken = default);
}
Esta interfaz permite implementaciones personalizadas para la reducción del historial de chat.
Además, el kernel semántico proporciona reductores integrados:
-
ChatHistoryTruncationReducer
: trunca el historial de chat en un tamaño especificado y descarta los mensajes eliminados. La reducción se desencadena cuando la longitud del historial de chat supera el límite. -
ChatHistorySummarizationReducer
: trunca el historial de chats, resume los mensajes eliminados y vuelve a agregar el resumen al historial de chat como un solo mensaje.
Ambos reductores siempre conservan los mensajes del sistema para conservar el contexto esencial del modelo.
En el ejemplo siguiente se muestra cómo conservar solo los dos últimos mensajes de usuario mientras se mantiene el flujo de conversación:
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
var chatService = new OpenAIChatCompletionService(
modelId: "<model-id>",
apiKey: "<api-key>");
var reducer = new ChatHistoryTruncationReducer(targetCount: 2); // Keep system message and last user message
var chatHistory = new ChatHistory("You are a librarian and expert on books about cities");
string[] userMessages = [
"Recommend a list of books about Seattle",
"Recommend a list of books about Dublin",
"Recommend a list of books about Amsterdam",
"Recommend a list of books about Paris",
"Recommend a list of books about London"
];
int totalTokenCount = 0;
foreach (var userMessage in userMessages)
{
chatHistory.AddUserMessage(userMessage);
Console.WriteLine($"\n>>> User:\n{userMessage}");
var reducedMessages = await reducer.ReduceAsync(chatHistory);
if (reducedMessages is not null)
{
chatHistory = new ChatHistory(reducedMessages);
}
var response = await chatService.GetChatMessageContentAsync(chatHistory);
chatHistory.AddAssistantMessage(response.Content!);
Console.WriteLine($"\n>>> Assistant:\n{response.Content!}");
if (response.InnerContent is OpenAI.Chat.ChatCompletion chatCompletion)
{
totalTokenCount += chatCompletion.Usage?.TotalTokenCount ?? 0;
}
}
Console.WriteLine($"Total Token Count: {totalTokenCount}");
Puede encontrar más ejemplos en el repositorio Semantic Kernel .
En esta sección, tratamos los detalles de implementación de la reducción del historial de chat en Python. El enfoque implica la creación de un chatHistoryReducer que se integra sin problemas con el objeto ChatHistory, lo que permite su uso y su paso siempre que se requiera un historial de chat.
- Integración: en Python, el
ChatHistoryReducer
está diseñado para ser una subclase del objetoChatHistory
. Esta herencia permite que el reductor sea intercambiable con instancias de historial de chat estándar. - Lógica de reducción: los usuarios pueden invocar el método
reduce
en el objeto de historial de chat. El reductor evalúa si el recuento de mensajes actual superatarget_count
másthreshold_count
(si se establece). Si es así, el historial se reduce atarget_count
por truncamiento o resumen. - Configuración: el comportamiento de reducción se puede configurar a través de parámetros como
target_count
(el número deseado de mensajes que se van a conservar) y threshold_count (el recuento de mensajes que desencadena el proceso de reducción).
Los reductores de historial de Python del kernel semántico admitidos son ChatHistorySummarizationReducer
y ChatHistoryTruncationReducer
. Como parte de la configuración del reductor, auto_reduce
se puede habilitar para aplicar automáticamente la reducción del historial cuando se usa con add_message_async
, lo que garantiza que el historial de chat permanezca dentro de los límites configurados.
En el ejemplo siguiente se muestra cómo usar ChatHistoryTruncationReducer para conservar solo los dos últimos mensajes mientras se mantiene el flujo de conversación.
import asyncio
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatHistoryTruncationReducer
from semantic_kernel.kernel import Kernel
async def main():
kernel = Kernel()
kernel.add_service(AzureChatCompletion())
# Keep the last two messages
truncation_reducer = ChatHistoryTruncationReducer(
target_count=2,
)
truncation_reducer.add_system_message("You are a helpful chatbot.")
is_reduced = False
while True:
user_input = input("User:> ")
if user_input.lower() == "exit":
print("\n\nExiting chat...")
break
is_reduced = await truncation_reducer.reduce()
if is_reduced:
print(f"@ History reduced to {len(truncation_reducer.messages)} messages.")
response = await kernel.invoke_prompt(
prompt="{{$chat_history}}{{$user_input}}", user_input=user_input, chat_history=truncation_reducer
)
if response:
print(f"Assistant:> {response}")
truncation_reducer.add_user_message(str(user_input))
truncation_reducer.add_message(response.value[0])
if is_reduced:
for msg in truncation_reducer.messages:
print(f"{msg.role} - {msg.content}\n")
print("\n")
if __name__ == "__main__":
asyncio.run(main())
La reducción del historial de chat no está disponible actualmente en Java.
Pasos siguientes
Ahora que sabe cómo crear y administrar un objeto de historial de chat, puede obtener más información sobre las llamadas a funciones en el tema Llamada a funciones.