提示格式化工具

AgentScope中的格式化模块主要负责

  • 将消息转换为不同LLM API所期望的格式,

  • (可选) 截断消息以适应令牌限制,

  • (可选) 提示工程,例如总结长对话。

最后两项是可选的,也可以由开发者在内存中或在智能体级别处理。

在AgentScope中,有两种格式化器,“ChatFormatter”和“MultiAgentFormatter”,根据其输入消息中的智能体身份进行区分。

  • ChatFormatter: 专为标准用户-助手场景(聊天机器人)设计,使用 role 字段来区分用户和助手。

  • MultiAgentFormatter: 专为多智能体场景设计,使用 name 字段识别不同智能体,该组件将对话历史合并为单个用户消息字典。

内置格式化工具如下所列
AgentScope中的内置格式化工具

API 提供商

用户-助手场景

多智能体场景

OpenAI

OpenAIChatFormatter

OpenAIMultiAgentFormatter

Anthropic

AnthropicChatFormatter

AnthropicMultiAgentFormatter

DashScope

DashScopeChatFormatter

DashScopeMultiAgentFormatter

双子座

GeminiChatFormatter

Gemini聊天格式化器

Ollama

OllamaChatFormatter

OllamaMultiAgentFormatter

契约搜寻

DeepSeekChatFormatter

DeepSeekMultiAgentFormatter

vLLM

OpenAIChatFormatter

OpenAIMultiAgentFormatter

提示

OpenAI API支持name字段,因此OpenAIChatFormatter也可用于多智能体场景。您亦可使用OpenAIMultiAgentFormatter替代,它将对话历史合并为单一用户消息。

此外,内置格式化工具支持将不同的消息块转换为目标API所期望的格式,具体如下:

内置格式化器中支持的消息块

格式化程序

工具使用/结果

图像

音频

视频

思考

OpenAIChatFormatter

(注意: 根据内容,此HTML片段仅有一个错误符号"❌",没有需要翻译的文本内容。根据指令第7条,如果不需要翻译则输出原文)

DashScopeChatFormatter

错误

DashScopeMultiAgentFormatter

AnthropicChatFormatter

AnthropicMultiAgentFormatter

GeminiChatFormatter

GeminiMultiAgentFormatter

OllamaChatFormatter

错误

OllamaMultiAgentFormatter

错误

错误

DeepSeekChatFormatter

错误

DeepSeekMultiAgentFormatter

注意

官方文档所述,只有Anthropic建议在提示格式化中保留思考块。对于其他情况,我们直接忽略输入消息中的思考块。

基于ReAct的格式化

内置格式化器均设计用于支持ReAct风格智能体,其中输入消息包含交替的对话历史和工具调用序列

在用户-助手场景中,对话历史包含用户和助手的消息,我们只需将其直接转换为预期格式。 然而,在多智能体场景中,对话历史是来自不同智能体的消息列表,如下所示:

example of multiagent messages

多智能体消息示例

因此,我们需要将会话历史合并为一个带有“”和“”标签的用户消息。 以DashScope为例,格式化后的消息将如下所示:

from agentscope.token import HuggingFaceTokenCounter
from agentscope.formatter import DashScopeMultiAgentFormatter
from agentscope.message import Msg, ToolResultBlock, ToolUseBlock, TextBlock
import asyncio, json


input_msgs = [
    # System prompt
    Msg("system", "You're a helpful assistant named Friday", "system"),
    # Conversation history
    Msg("Bob", "Hi, Alice, do you know the nearest library?", "assistant"),
    Msg(
        "Alice",
        "Sorry, I don't know. Do you have any idea, Charlie?",
        "assistant",
    ),
    Msg(
        "Charlie",
        "No, let's ask Friday. Friday, get me the nearest library.",
        "assistant",
    ),
    # Tool sequence
    Msg(
        "Friday",
        [
            ToolUseBlock(
                type="tool_use",
                name="get_current_location",
                id="1",
                input={},
            ),
        ],
        "assistant",
    ),
    Msg(
        "system",
        [
            ToolResultBlock(
                type="tool_result",
                name="get_current_location",
                id="1",
                output=[TextBlock(type="text", text="104.48, 36.30")],
            ),
        ],
        "system",
    ),
    Msg(
        "Friday",
        [
            ToolUseBlock(
                type="tool_use",
                name="search_around",
                id="2",
                input={"location": [104.48, 36.30], "keyword": "library"},
            ),
        ],
        "assistant",
    ),
    Msg(
        "system",
        [
            ToolResultBlock(
                type="tool_result",
                name="search_around",
                id="2",
                output=[TextBlock(type="text", text="[...]")],
            ),
        ],
        "system",
    ),
    # Conversation history continues
    Msg("Friday", "The nearest library is ...", "assistant"),
    Msg("Bob", "Thanks, Friday!", "user"),
    Msg("Alice", "Let's go together.", "user"),
]


async def run_formatter_example() -> list[dict]:
    """Example of how to format multi-agent messages."""
    formatter = DashScopeMultiAgentFormatter()
    formatted_message = await formatter.format(input_msgs)
    print("The formatted message:")
    print(json.dumps(formatted_message, indent=4))
    return formatted_message


formatted_message = asyncio.run(run_formatter_example())
The formatted message:
[
    {
        "role": "system",
        "content": "You're a helpful assistant named Friday"
    },
    {
        "role": "user",
        "content": "# Conversation History\nThe content between <history></history> tags contains your conversation history\n<history>\nBob: Hi, Alice, do you know the nearest library?\nAlice: Sorry, I don't know. Do you have any idea, Charlie?\nCharlie: No, let's ask Friday. Friday, get me the nearest library.\n</history>"
    },
    {
        "role": "assistant",
        "content": [
            {
                "text": null
            }
        ],
        "tool_calls": [
            {
                "id": "1",
                "type": "function",
                "function": {
                    "name": "get_current_location",
                    "arguments": "{}"
                }
            }
        ]
    },
    {
        "role": "tool",
        "tool_call_id": "1",
        "content": "104.48, 36.30",
        "name": "get_current_location"
    },
    {
        "role": "assistant",
        "content": [
            {
                "text": null
            }
        ],
        "tool_calls": [
            {
                "id": "2",
                "type": "function",
                "function": {
                    "name": "search_around",
                    "arguments": "{\"location\": [104.48, 36.3], \"keyword\": \"library\"}"
                }
            }
        ]
    },
    {
        "role": "tool",
        "tool_call_id": "2",
        "content": "[...]",
        "name": "search_around"
    },
    {
        "role": "user",
        "content": "<history>\nFriday: The nearest library is ...\nBob: Thanks, Friday!\nAlice: Let's go together.\n</history>"
    }
]

具体来说,对话历史被格式化为:

print("The first conversation history:")
print(formatted_message[1]["content"])

print("\nThe second conversation history:")
print(formatted_message[-1]["content"])
The first conversation history:
# Conversation History
The content between <history></history> tags contains your conversation history
<history>
Bob: Hi, Alice, do you know the nearest library?
Alice: Sorry, I don't know. Do you have any idea, Charlie?
Charlie: No, let's ask Friday. Friday, get me the nearest library.
</history>

The second conversation history:
<history>
Friday: The nearest library is ...
Bob: Thanks, Friday!
Alice: Let's go together.
</history>

基于截断的格式化

通过AgentScope中的token模块,内置格式化器支持在token超出限制时通过删除最旧的消息(系统提示消息除外)来截断输入消息。

以OpenAIFormatter为例,我们首先计算输入消息的总令牌数。

async def run_token_counter() -> int:
    """Compute the token number of the input messages."""
    # We use huggingface token counter for dashscope models.
    token_counter = HuggingFaceTokenCounter(
        "Qwen/Qwen2.5-VL-3B-Instruct",
        use_mirror=True,
    )

    return await token_counter.count(formatted_message)


n_tokens = asyncio.run(run_token_counter())
print("The tokens in the formatted messages are: ", n_tokens)
None of PyTorch, TensorFlow >= 2.0, or Flax have been found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.
The tokens in the formatted messages are:  156

然后我们将最大令牌限制设置为比令牌总数少20个,并运行格式化器。

async def run_truncated_formatter() -> None:
    """Example of how to format messages with truncation."""
    token_counter = HuggingFaceTokenCounter(
        pretrained_model_name_or_path="Qwen/Qwen2.5-VL-3B-Instruct",
        use_mirror=True,
    )
    formatter = DashScopeMultiAgentFormatter(
        token_counter=token_counter,
        max_tokens=n_tokens - 20,
    )
    truncated_formatted_message = await formatter.format(input_msgs)
    n_truncated_tokens = await token_counter.count(truncated_formatted_message)
    print("The tokens after truncation: ", n_truncated_tokens)

    print("\nThe conversation history after truncation:")
    print(truncated_formatted_message[1]["content"])


asyncio.run(run_truncated_formatter())
The tokens after truncation:  126

The conversation history after truncation:
# Conversation History
The content between <history></history> tags contains your conversation history
<history>
Charlie: No, let's ask Friday. Friday, get me the nearest library.
</history>

我们可以看到,为了避免超出上下文长度限制,来自Bob和Alice的前两条消息已被移除。

自定义格式化工具

AgentScope 提供了两个基础类 FormatterBase 和它的子类 TruncatedFormatterBaseTruncatedFormatterBase 类提供了 FIFO 截断策略,并且所有内置格式化器都继承自它。

AgentScope格式化器的基类

抽象方法

描述

FormatterBase

格式

将输入的Msg对象格式化为目标API期望的格式

TruncatedFormatterBase

_format_agent_message

格式化智能体消息,这些消息在多智能体场景中可能包含多个身份

_format_tool_sequence

将工具使用和结果序列格式化为预期格式

_format (可选)

将输入的 Msg 对象格式化为目标API所期望的格式

提示

  • _formatTruncatedFormatterBase 中将输入消息分组为智能体消息和工具序列,然后分别通过调用 _format_agent_message_format_tool_sequence 进行格式化。你可以重写它来实现自己的格式化策略。

  • 可选地,您可以在 _truncate 方法中覆盖 TruncatedFormatterBase 来实现自己的截断策略。

扩展阅读

脚本总运行时间:(0分钟7.058秒)

Gallery generated by Sphinx-Gallery