Freigeben über


Chatverlauf

Das Chatverlaufsobjekt wird verwendet, um eine Aufzeichnung von Nachrichten in einer Chatsitzung zu verwalten. Es wird verwendet, um Nachrichten von verschiedenen Autoren zu speichern, z. B. Benutzer, Assistenten, Tools oder das System. Als primärer Mechanismus zum Senden und Empfangen von Nachrichten ist das Chatverlaufsobjekt für die Aufrechterhaltung von Kontext und Kontinuität in einer Unterhaltung unerlässlich.

Erstellen eines Chatverlaufsobjekts

Ein Chatverlaufsobjekt ist eine Liste unter der Haube und erleichtert das Erstellen und Hinzufügen von Nachrichten.

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.");

Hinzufügen umfangreicherer Nachrichten zu einem Chatverlauf

Die einfachste Möglichkeit zum Hinzufügen von Nachrichten zu einem Chatverlaufsobjekt besteht darin, die oben genannten Methoden zu verwenden. Sie können nachrichten jedoch auch manuell hinzufügen, indem Sie ein neues ChatMessage Objekt erstellen. Auf diese Weise können Sie zusätzliche Informationen bereitstellen, z. B. Namen und Bildinhalte.

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."
);

Simulieren von Funktionsaufrufen

Zusätzlich zu Benutzer-, Assistenten- und Systemrollen können Sie auch Nachrichten aus der Toolrolle hinzufügen, um Funktionsaufrufe zu simulieren. Dies ist nützlich, um die KI zu unterrichten, wie Plug-Ins verwendet werden, und um zusätzlichen Kontext für die Unterhaltung bereitzustellen.

Wenn Sie z. B. Informationen über den aktuellen Benutzer in den Chatverlauf einfügen möchten, ohne dass der Benutzer die Informationen bereitstellen muss oder der LLM zeitverwendet, um ihn zu fragen, können Sie die Toolrolle verwenden, um die Informationen direkt bereitzustellen.

Nachfolgend finden Sie ein Beispiel dafür, wie wir dem Assistenten Allergien durch Simulieren eines Funktionsaufrufs an das User Plug-In bereitstellen können.

Tipp

Simulierte Funktionsaufrufe sind besonders hilfreich, um Details zu den aktuellen Benutzern bereitzustellen. Die heutigen LLMs wurden geschult, um besonders vertraulich für Benutzerinformationen zu sein. Selbst wenn Sie Benutzerdetails in einer Systemmeldung angeben, kann die LLM dies weiterhin ignorieren. Wenn Sie sie über eine Benutzernachricht oder eine Toolnachricht bereitstellen, ist die LLM wahrscheinlicher, dass sie verwendet wird.

// 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. 

Wichtig

Beim Simulieren von Toolergebnissen müssen Sie immer den id Funktionsaufruf angeben, dem das Ergebnis entspricht. Dies ist wichtig für die KI, den Kontext des Ergebnisses zu verstehen. Einige LLMs, z. B. OpenAI, lösen einen Fehler aus, wenn der id Fehler fehlt oder wenn der id Funktionsaufruf nicht entspricht.

Überprüfen eines Chatverlaufsobjekts

Wenn Sie ein Chatverlaufsobjekt an einen Chatabschlussdienst übergeben, bei dem automatische Funktionsaufrufe aktiviert sind, wird das Chatverlaufsobjekt so bearbeitet, dass es die Funktionsaufrufe und Ergebnisse enthält. Auf diese Weise können Sie vermeiden, dass Sie diese Nachrichten dem Chatverlaufsobjekt manuell hinzufügen müssen. Außerdem können Sie das Chatverlaufsobjekt überprüfen, um die Funktionsaufrufe und -ergebnisse anzuzeigen.

Sie müssen jedoch weiterhin die endgültigen Nachrichten zum Chatverlaufsobjekt hinzufügen. Nachfolgend finden Sie ein Beispiel dafür, wie Sie das Chatverlaufsobjekt überprüfen können, um die Funktionsaufrufe und -ergebnisse anzuzeigen.

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);

Chatverlaufsreduzierung

Das Verwalten des Chatverlaufs ist für die Aufrechterhaltung kontextbezogener Unterhaltungen unerlässlich und sorgt gleichzeitig für eine effiziente Leistung. Wenn eine Unterhaltung voranschreitet, kann das Verlaufsobjekt über die Grenzen des Kontextfensters eines Modells hinaus wachsen, was sich auf die Antwortqualität auswirkt und die Verarbeitung verlangsamt. Ein strukturierter Ansatz zur Reduzierung des Chatverlaufs stellt sicher, dass die relevantesten Informationen ohne unnötigen Aufwand verfügbar bleiben.

Warum den Chatverlauf reduzieren?

  • Leistungsoptimierung: Große Chathistorien erhöhen die Verarbeitungszeit. Durch die Reduzierung ihrer Größe können Sie schnelle und effiziente Interaktionen aufrecht erhalten.
  • Kontextfensterverwaltung: Sprachmodelle weisen ein festes Kontextfenster auf. Wenn der Verlauf diesen Grenzwert überschreitet, gehen ältere Nachrichten verloren. Durch die Verwaltung des Chatverlaufs wird sichergestellt, dass der wichtigste Kontext weiterhin zugänglich bleibt.
  • Speichereffizienz: In ressourcengeschränkten Umgebungen wie mobilen Anwendungen oder eingebetteten Systemen kann der ungebundene Chatverlauf zu übermäßiger Speicherauslastung und langsamer Leistung führen.
  • Datenschutz und Sicherheit: Durch die Beibehaltung des unnötigen Unterhaltungsverlaufs wird das Risiko erhöht, vertrauliche Informationen offen zu legen. Durch einen strukturierten Reduktionsprozess wird die Datenaufbewahrung minimiert, während der relevante Kontext beibehalten wird.

Strategien zum Reduzieren des Chatverlaufs

Mehrere Ansätze können verwendet werden, um den Chatverlauf überschaubar zu halten und dabei wichtige Informationen beizubehalten:

  • Abschneiden: Die ältesten Nachrichten werden entfernt, wenn der Verlauf ein vordefiniertes Limit überschreitet. Dies stellt sicher, dass nur die letzten Interaktionen beibehalten werden.
  • Zusammenfassung: Ältere Nachrichten werden in einer Zusammenfassung zusammengefasst, wobei wichtige Details erhalten bleiben und gleichzeitig die Anzahl der gespeicherten Nachrichten reduziert wird.
  • Tokenbasiert: Die tokenbasierte Reduzierung stellt sicher, dass der Chatverlauf innerhalb des Tokenlimits eines Modells bleibt, indem die Gesamtzahl der Token gemessen und ältere Nachrichten entfernt oder zusammengefasst werden, wenn der Grenzwert überschritten wird.

Ein Chatverlaufsminderer automatisiert diese Strategien, indem die Größe des Verlaufs ausgewertet und basierend auf konfigurierbaren Parametern wie der Zielanzahl (die gewünschte Anzahl von Nachrichten, die aufbewahrt werden sollen) und die Schwellenwertanzahl (der Punkt, an dem die Reduzierung ausgelöst wird) reduziert wird. Durch die Integration dieser Reduktionstechniken können Chatanwendungen reaktionsfähig und leistungsfähig bleiben, ohne den Unterhaltungskontext zu beeinträchtigen.

In der .NET-Version des Semantic Kernel wird die Abstraktion des Chatverlaufs durch die Schnittstelle IChatHistoryReducer definiert.

namespace Microsoft.SemanticKernel.ChatCompletion;

[Experimental("SKEXP0001")]
public interface IChatHistoryReducer
{
    Task<IEnumerable<ChatMessageContent>?> ReduceAsync(IReadOnlyList<ChatMessageContent> chatHistory, CancellationToken cancellationToken = default);
}

Diese Schnittstelle ermöglicht benutzerdefinierte Implementierungen für die Reduzierung des Chatverlaufs.

Darüber hinaus bietet der semantische Kernel integrierte Reduzierungen:

  • ChatHistoryTruncationReducer – schneidet den Chatverlauf auf eine angegebene Größe ab und verwirft die entfernten Nachrichten. Die Reduzierung wird ausgelöst, wenn die Länge des Chatverlaufs den Grenzwert überschreitet.
  • ChatHistorySummarizationReducer – schneidet den Chatverlauf ab, fasst die entfernten Nachrichten zusammen und fügt die Zusammenfassung als einzelne Nachricht wieder in den Chatverlauf ein.

Beide Reduzierer bewahren stets Systemnachrichten auf, um den wesentlichen Kontext für das Modell zu erhalten.

Im folgenden Beispiel wird veranschaulicht, wie nur die letzten beiden Benutzernachrichten beibehalten werden, während der Unterhaltungsfluss beibehalten wird:

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}");

Weitere Beispiele finden Sie im Semantischen Kernel Repository.

In diesem Abschnitt behandeln wir die Implementierungsdetails der Reduzierung des Chatverlaufs in Python. Der Ansatz umfasst das Erstellen eines ChatHistoryReducer, der nahtlos in das ChatHistory-Objekt integriert wird, sodass es verwendet und übergeben werden kann, wo ein Chatverlauf erforderlich ist.

  • Integration: In Python ist die ChatHistoryReducer als Unterklasse des ChatHistory-Objekts konzipiert. Diese Vererbung ermöglicht es dem Reducer, mit Standard-Chatverlauf-Instanzen austauschbar zu sein.
  • Reduzierungslogik: Benutzer können die reduce-Methode für das Chatverlaufsobjekt aufrufen. Der Reducer bewertet, ob die aktuelle Nachrichtenanzahl target_count plus threshold_count (falls festgelegt) übersteigt. Wenn dies der Fall ist, wird der Verlauf entweder durch Kürzung oder Zusammenfassung auf target_count reduziert.
  • Konfiguration: Das Reduzierungsverhalten kann über Parameter wie target_count (die gewünschte Anzahl der zu speichernden Nachrichten) und threshold_count (die Nachrichtenanzahl, die den Reduktionsprozess auslöst) konfiguriert werden.

Die unterstützten Semantic Kernel Python Historienreduzierer sind ChatHistorySummarizationReducer und ChatHistoryTruncationReducer. Im Rahmen der Reduzierungskonfiguration kann auto_reduce aktiviert werden, um die Verlaufsreduzierung automatisch anzuwenden, wenn sie mit add_message_asyncverwendet wird, um sicherzustellen, dass der Chatverlauf innerhalb der konfigurierten Grenzwerte bleibt.

Im folgenden Beispiel wird veranschaulicht, wie Sie chatHistoryTruncationReducer verwenden, um nur die letzten beiden Nachrichten beizubehalten, während der Unterhaltungsfluss beibehalten wird.

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())

Die Reduzierung des Chatverlaufs ist derzeit in Java nicht verfügbar.

Nächste Schritte

Nachdem Sie nun wissen, wie Sie ein Chatverlaufsobjekt erstellen und verwalten, erfahren Sie mehr über Funktionsaufrufe im Thema "Funktionsaufrufe ".