共用方式為


聊天記錄

聊天記錄對像是用來維護聊天會話中訊息的記錄。 它用來儲存來自不同作者的訊息,例如使用者、助理、工具或系統。 作為傳送和接收訊息的主要機制,聊天記錄對象對於維護交談中的內容和持續性至關重要。

建立聊天記錄物件

聊天記錄對像是幕後清單,可讓您輕鬆地建立和新增訊息。

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

將更豐富的訊息新增至聊天記錄

將訊息新增至聊天記錄物件最簡單的方式是使用上述方法。 不過,您也可以建立新的 ChatMessage 物件,手動新增訊息。 這可讓您提供其他資訊,例如名稱和影像內容。

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

模擬函式呼叫

除了使用者、助理和系統角色之外,您也可以從工具角色新增訊息來模擬函式呼叫。 這適用於教導 AI 如何使用外掛程式,並提供其他內容給交談。

例如,若要在聊天記錄中插入目前使用者的相關信息,而不需要使用者提供資訊,或要求 LLM 浪費時間,您可以使用工具角色直接提供資訊。

以下範例說明如何藉由模擬外掛程式的函式呼叫 User ,為小幫手提供用戶過敏。

提示

模擬函數調用特別有助於提供目前使用者的詳細數據。 現今的 LLM 已經過訓練,對使用者資訊特別敏感。 即使您在系統訊息中提供使用者詳細數據,LLM 仍可能會選擇忽略它。 如果您透過使用者訊息或工具訊息提供它,則 LLM 更有可能使用它。

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

重要

模擬工具結果時,您必須一律提供 id 結果所對應之函式呼叫的 。 這讓 AI 了解結果的內容非常重要。 某些 LLM,例如 OpenAI,會在遺失 時 id 擲回錯誤,如果 id 未對應至函式呼叫,則會擲回錯誤。

檢查聊天記錄物件

每當您將聊天記錄對象傳遞至已啟用自動函數呼叫的聊天完成服務時,就會操作聊天記錄物件,使其包含函式呼叫和結果。 這可讓您避免手動將這些訊息新增至聊天記錄物件,也可讓您檢查聊天記錄物件以查看函式呼叫和結果。

不過,您仍必須將最終訊息新增至聊天記錄物件。 以下是如何檢查聊天記錄物件的範例,以查看函式呼叫和結果。

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

減少聊天記錄

管理聊天歷程記錄對於維護內容感知對話至關重要,同時確保有效率的效能。 在交談進行時,歷程記錄物件可能會成長超過模型內容視窗的限制,影響回應品質並減緩處理速度。 減少聊天記錄的結構化方法可確保最相關的資訊仍可供使用,而不會造成不必要的額外負荷。

為什麼要減少聊天記錄?

  • 效能優化:大型聊天記錄會增加處理時間。 減少其大小有助於維護快速且有效率的互動。
  • 內容視窗管理:語言模型具有固定的內容視窗。 當歷程記錄超過此限制時,較舊的訊息會遺失。 管理聊天記錄可確保最重要的內容仍可存取。
  • 記憶體效率:在資源限制的環境中,例如行動應用程式或內嵌系統,未系結的聊天記錄可能會導致記憶體使用量過多,效能變慢。
  • 隱私權和安全性:保留不必要的交談歷程記錄會增加公開敏感性信息的風險。 結構化縮減程式可將數據保留降至最低,同時維護相關內容。

減少聊天記錄的策略

數種方法可用來讓聊天記錄保持可管理,同時保留基本資訊:

  • 截斷:當歷程記錄超過預先定義的限制時,會移除最舊的訊息,以確保只會保留最近的互動。
  • 摘要:較舊的訊息會壓縮成摘要,同時保留重要詳細數據,同時減少儲存的訊息數目。
  • 令牌型:令牌型縮減可測量令牌總數,並在超過限制時移除或摘要較舊的訊息,確保聊天記錄保持在模型的令牌限制內。

聊天記錄歸納器會藉由評估歷程記錄的大小,並根據目標計數等可設定參數來減少這些策略,例如目標計數(需要保留的訊息數目)和閾值計數(觸發縮減點)。 藉由整合這些縮減技術,聊天應用程式可以保持回應且高效能,而不會危及交談內容。

在語意核心的 .NET 版本中,聊天記錄歸納器抽象概念是由 IChatHistoryReducer 介面所定義:

namespace Microsoft.SemanticKernel.ChatCompletion;

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

此介面允許自定義實作減少聊天記錄。

此外,語意核心提供內建歸納器:

  • ChatHistoryTruncationReducer - 把聊天記錄截斷至指定大小,並捨棄被移除的訊息。 當聊天記錄長度超過限制時,就會觸發縮減。
  • ChatHistorySummarizationReducer - 截斷聊天記錄、摘要移除的郵件,並將摘要新增回聊天記錄作為單一訊息。

這兩個歸納器一律會保留系統訊息,以保留模型的基本內容。

下列範例示範如何在維護對話流程時只保留最後兩個使用者訊息:

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

如需更多範例,請參閱語意核心 存放庫

在本節中,我們將討論 Python 中聊天記錄縮減的實作詳細數據。 此方法涉及建立一個 ChatHistoryReducer,使其能夠無縫地與 ChatHistory 物件整合,從而能夠在需要聊天記錄的地方輕鬆使用和傳遞。

  • 整合:在 Python 中,ChatHistoryReducer 是設計成 ChatHistory 物件的子類別。 此繼承可讓歸納器與標準聊天記錄實例交換。
  • 縮減邏輯:用戶可以在聊天記錄物件上叫用 reduce 方法。 歸納器會評估當前的訊息計數是否大於 target_count 加上 threshold_count(如果有設定)。 如果這樣做,記錄會藉由截斷或摘要來縮減為 target_count
  • 組態:縮減行為可透過 target_count 等參數來設定(需要保留的訊息數目)和threshold_count(觸發縮減程式的訊息計數)。

支持的語意核心 Python 歷程記錄歸納器是 ChatHistorySummarizationReducerChatHistoryTruncationReducer。 作為歸納器設定的一部分,auto_reduce 可以啟用,以在搭配 add_message_async使用時自動套用歷程記錄縮減,確保聊天記錄保持在已設定的限制內。

下列範例示範如何使用 ChatHistoryTruncationReducer 只保留最後兩則訊息,同時維護交談流程。

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

聊天記錄縮減目前在Java中無法使用。

下一步

既然您已瞭解如何建立及管理聊天記錄物件,您可以在函式呼叫主題中 深入瞭解函式呼叫