Historie chatu
Objekt historie chatu slouží k udržování záznamu zpráv v relaci chatu. Slouží k ukládání zpráv od různých autorů, jako jsou uživatelé, asistenti, nástroje nebo systém. Jako primární mechanismus pro odesílání a přijímání zpráv je objekt historie chatu nezbytný pro udržování kontextu a kontinuity v konverzaci.
Vytvoření objektu historie chatu
Objekt historie chatu je seznam pod kapotou, což usnadňuje vytváření a přidávání zpráv.
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.");
Přidání bohatších zpráv do historie chatu
Nejjednodušší způsob, jak přidat zprávy do objektu historie chatu, je použít výše uvedené metody. Zprávy ale můžete přidávat také ručně vytvořením nového ChatMessage
objektu. To vám umožní zadat další informace, jako jsou názvy a obrázky obsahu.
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."
);
Simulace volání funkcí
Kromě rolí uživatelů, asistentů a systémových rolí můžete také přidat zprávy z role nástroje pro simulaci volání funkcí. To je užitečné pro výuku umělé inteligence, jak používat moduly plug-in a poskytnout další kontext konverzace.
Pokud chcete například vložit informace o aktuálním uživateli v historii chatu, aniž by uživatel musel poskytnout informace nebo když o ni požádal LLM, můžete použít roli nástroje k přímému poskytnutí informací.
Níže je příklad toho, jak můžeme asistentovi poskytnout alergii uživatelů simulací volání funkce do modulu User
plug-in.
Tip
Volání simulovaných funkcí jsou užitečná zejména pro poskytování podrobností o aktuálních uživatelích. Dnešní LLM byly natrénovány tak, aby byly obzvláště citlivé na informace o uživatelích. I když do systémové zprávy zadáte podrobnosti o uživateli, může se LLM i nadále rozhodnout, že je bude ignorovat. Pokud ji zadáte prostřednictvím zprávy uživatele nebo zprávy nástroje, bude ho llM pravděpodobně používat.
// 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.
Důležité
Při simulaci výsledků nástroje je nutné vždy zadat id
volání funkce, které výsledek odpovídá. To je důležité pro AI, aby porozuměla kontextu výsledku. Některé LLM, jako je OpenAI, vyvolá chybu, pokud id
chybí nebo pokud id
neodpovídá volání funkce.
Kontrola objektu historie chatu
Při každém předání objektu historie chatu službě pro dokončování chatu s povoleným automatickým voláním funkce bude objekt historie chatu manipulován tak, aby zahrnoval volání a výsledky funkce. To vám umožní vyhnout se ručnímu přidání těchto zpráv do objektu historie chatu a také můžete zkontrolovat objekt historie chatu, abyste viděli volání a výsledky funkce.
Přesto ale musíte do objektu historie chatu přidat konečné zprávy. Níže je příklad, jak můžete zkontrolovat objekt historie chatu a zobrazit volání a výsledky funkce.
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);
Snížení historie chatu
Správa historie chatu je nezbytná pro udržování konverzací pracujících s kontextem a zajištění efektivního výkonu. Během konverzace může objekt historie růst nad rámec limitů kontextového okna modelu, což má vliv na kvalitu odezvy a zpomalení zpracování. Strukturovaný přístup ke snížení historie chatu zajišťuje, že nejrelevantnější informace zůstanou dostupné bez zbytečné režie.
Proč omezit historii chatu?
- Optimalizace výkonu: Velké historie chatů zvyšují dobu zpracování. Zmenšení jejich velikosti pomáhá udržovat rychlé a efektivní interakce.
- Správa kontextových oken: Jazykové modely mají pevné kontextové okno. Pokud historie tento limit překročí, dojde ke ztrátě starších zpráv. Správa historie chatu zajišťuje, že nejdůležitější kontext zůstane přístupný.
- Efektivita paměti: V prostředích s omezenými prostředky, jako jsou mobilní aplikace nebo vložené systémy, může historie nevázaných chatů vést k nadměrnému využití paměti a pomalému výkonu.
- Ochrana osobních údajů a zabezpečení: Zachování nepotřebné historie konverzací zvyšuje riziko zveřejnění citlivých informací. Strukturovaný proces redukce minimalizuje uchovávání dat při zachování relevantního kontextu.
Strategie pro snížení historie chatu
Při zachování základních informací je možné spravovat historii chatu několika přístupy:
- Zkrácení: Nejstarší zprávy se odstraňují, když historie překročí předdefinovaný limit, aby se zachovaly jenom nedávné interakce.
- Shrnutí: Starší zprávy jsou zhuštěné do souhrnu, zachování klíčových podrobností a snížení počtu uložených zpráv.
- Na základě tokenů: Snížení počtu tokenů zajišťuje, že historie chatu zůstane v rámci limitu tokenu modelu tím, že měří celkový počet tokenů a odebírá nebo shrnuje starší zprávy při překročení limitu.
Redukce historie chatu automatizuje tyto strategie vyhodnocením velikosti historie a jejím snížením na základě konfigurovatelných parametrů, jako je cílový počet (požadovaný počet zpráv, které se mají zachovat) a počet prahových hodnot (bod, kdy se aktivuje snížení). Díky integraci těchto technik redukce můžou chatovací aplikace zůstat responzivní a výkonné bez ohrožení konverzačního kontextu.
Ve verzi sémantického jádra .NET je abstraktní koncept pro redukci historie chatu definován rozhraním IChatHistoryReducer
.
namespace Microsoft.SemanticKernel.ChatCompletion;
[Experimental("SKEXP0001")]
public interface IChatHistoryReducer
{
Task<IEnumerable<ChatMessageContent>?> ReduceAsync(IReadOnlyList<ChatMessageContent> chatHistory, CancellationToken cancellationToken = default);
}
Toto rozhraní umožňuje vlastní implementace pro snížení historie chatu.
Sémantické jádro navíc poskytuje integrované reduktory:
-
ChatHistoryTruncationReducer
– zkrátí historii chatu na zadanou velikost a zahodí odebrané zprávy. Snížení se aktivuje, když délka historie chatu překročí limit. -
ChatHistorySummarizationReducer
– zkracuje historii chatu, shrnuje odebrané zprávy a přidá souhrn zpět do historie chatu jako jednu zprávu.
Oba redukční moduly vždy zachovávají systémové zprávy, aby zachovaly základní kontext modelu.
Následující příklad ukazuje, jak zachovat pouze poslední dvě uživatelské zprávy při zachování toku konverzace:
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}");
V této části se podíváme na podrobnosti implementace redukce historie chatu v Pythonu. Tento přístup zahrnuje vytvoření ChatHistoryReducer, který se bezproblémově integruje s objektem ChatHistory, což umožňuje jeho použití a předání bez ohledu na to, kde je potřeba historie chatu.
- Integrace: V Pythonu je
ChatHistoryReducer
navržená tak, aby byla podtřídou objektuChatHistory
. Díky této dědičnosti může být reduktor zaměnitelný se standardními instancemi historie chatu. - Logika redukce: Uživatelé mohou vyvolat metodu
reduce
objektu historie chatu. Redukční funkce vyhodnotí, jestli aktuální počet zpráv překračujetarget_count
plusthreshold_count
(pokud je nastavený). Pokud ano, historie se zmenší natarget_count
buď zkrácením, nebo souhrnem. - Konfigurace: Chování redukce je možné konfigurovat prostřednictvím parametrů, jako jsou
target_count
(požadovaný počet zpráv, které se mají zachovat) a threshold_count (počet zpráv, který aktivuje proces redukce).
Podporované reduktory historie sémantického jádra Pythonu jsou ChatHistorySummarizationReducer
a ChatHistoryTruncationReducer
. V rámci konfigurace redukčního nástroje je možné povolit, aby auto_reduce
automaticky používala redukci historie při použití s add_message_async
, což zajišťuje, že historie chatu zůstane v rámci nakonfigurovaných limitů.
Následující příklad ukazuje, jak pomocí ChatHistoryTruncationReducer zachovat pouze poslední dvě zprávy při zachování toku konverzace.
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())
Snížení historie chatu je momentálně v Javě nedostupné.
Další kroky
Teď, když víte, jak vytvořit a spravovat objekt historie chatu, můžete se dozvědět více o volání funkcí v tématu volání funkce.