次の方法で共有


チャット履歴

チャット履歴オブジェクトは、チャット セッション内のメッセージのレコードを保持するために使用されます。 ユーザー、アシスタント、ツール、システムなど、さまざまな作成者からのメッセージを格納するために使用されます。 メッセージを送受信するための主要なメカニズムとして、チャット履歴オブジェクトは、会話のコンテキストと継続性を維持するために不可欠です。

チャット履歴オブジェクトの作成

チャット履歴オブジェクトは内部のリストであり、メッセージの作成と追加が簡単になります。

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 が結果のコンテキストを理解するために重要です。 OpenAI などの一部の LLM は、 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 - チャット履歴を切り捨て、削除されたメッセージを要約し、概要を 1 つのメッセージとしてチャット履歴に追加します。

どちらのリジューサーも、モデルに不可欠なコンテキストを保持するために、常にシステム メッセージを保持します。

次の例では、会話フローを維持しながら、最後の 2 つのユーザー メッセージのみを保持する方法を示します。

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 でのチャット履歴の削減の実装の詳細について説明します。 このアプローチでは、ChatHistory オブジェクトとシームレスに統合される ChatHistoryReducer を作成し、チャット履歴が必要な場所で使用して渡すことができます。

  • 統合: Python では、ChatHistoryReducerChatHistory オブジェクトのサブクラスとして設計されています。 この継承により、レジューサーは標準のチャット履歴インスタンスと互換性を持ちます。
  • 削減ロジック: ユーザーはチャット履歴オブジェクトに対して reduce メソッドを呼び出すことができます。 レジューサーは、現在のメッセージ数が target_count プラス threshold_count (設定されている場合) を超えているかどうかを評価します。 その場合、履歴は切り捨てまたは要約によって target_count に減らされます。
  • 構成: 削減動作は、target_count (保持するメッセージの必要な数) やthreshold_count (削減プロセスをトリガーするメッセージ数) などのパラメーターを使用して構成できます。

サポートされているセマンティック カーネル Python 履歴リジューサーは、ChatHistorySummarizationReducerChatHistoryTruncationReducerです。 レジューサーの構成の一環として、auto_reduce を有効にして、add_message_asyncで使用した場合に履歴の縮小を自動的に適用し、チャット履歴が構成された制限内に収まるようにすることができます。

次の例では、ChatHistoryTruncationReducer を使用して、会話フローを維持しながら最後の 2 つのメッセージのみを保持する方法を示します。

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 では使用できません。

次のステップ

チャット履歴オブジェクトを作成および管理する方法がわから、関数呼び出しの詳細については、「 Function 呼び出し 」トピックを参照してください。