注意
Go to the end 下载完整示例代码。
提示格式化工具¶
AgentScope中的格式化模块主要负责
将消息转换为不同LLM API所期望的格式,
(可选) 截断消息以适应令牌限制,
(可选) 提示工程,例如总结长对话。
最后两项是可选的,也可以由开发者在内存中或在智能体级别处理。
在AgentScope中,有两种格式化器,“ChatFormatter”和“MultiAgentFormatter”,根据其输入消息中的智能体身份进行区分。
ChatFormatter: 专为标准用户-助手场景(聊天机器人)设计,使用
role字段来区分用户和助手。MultiAgentFormatter: 专为多智能体场景设计,使用
name字段识别不同智能体,该组件将对话历史合并为单个用户消息字典。
API 提供商 |
用户-助手场景 |
多智能体场景 |
|---|---|---|
OpenAI |
|
|
Anthropic |
|
|
DashScope |
|
|
双子座 |
|
|
Ollama |
|
|
契约搜寻 |
|
|
vLLM |
|
|
提示
OpenAI API支持name字段,因此OpenAIChatFormatter也可用于多智能体场景。您亦可使用OpenAIMultiAgentFormatter替代,它将对话历史合并为单一用户消息。
此外,内置格式化工具支持将不同的消息块转换为目标API所期望的格式,具体如下:
格式化程序 |
工具使用/结果 |
图像 |
音频 |
视频 |
思考 |
|---|---|---|---|---|---|
|
✅ |
✅ |
✅ |
(注意: 根据内容,此HTML片段仅有一个错误符号"❌",没有需要翻译的文本内容。根据指令第7条,如果不需要翻译则输出原文)
❌ |
|
|
✅ |
✅ |
✅ |
错误 |
|
|
✅ |
✅ |
✅ |
❌ |
|
|
✅ |
✅ |
❌ |
❌ |
✅ |
|
✅ |
✅ |
❌ |
❌ |
✅ |
|
✅ |
✅ |
✅ |
✅ |
|
|
✅ |
✅ |
✅ |
✅ |
|
|
✅ |
✅ |
❌ |
错误 |
|
|
✅ |
✅ |
错误 |
错误 |
|
|
✅ |
❌ |
错误 |
❌ |
|
|
✅ |
❌ |
❌ |
❌ |
注意
如官方文档所述,只有Anthropic建议在提示格式化中保留思考块。对于其他情况,我们直接忽略输入消息中的思考块。
基于ReAct的格式化¶
内置格式化器均设计用于支持ReAct风格智能体,其中输入消息包含交替的对话历史和工具调用序列。
在用户-助手场景中,对话历史包含用户和助手的消息,我们只需将其直接转换为预期格式。 然而,在多智能体场景中,对话历史是来自不同智能体的消息列表,如下所示:
多智能体消息示例¶
因此,我们需要将会话历史合并为一个带有“
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 和它的子类 TruncatedFormatterBase。
TruncatedFormatterBase 类提供了 FIFO 截断策略,并且所有内置格式化器都继承自它。
类 |
抽象方法 |
描述 |
|---|---|---|
|
|
将输入的 |
|
|
格式化智能体消息,这些消息在多智能体场景中可能包含多个身份 |
|
将工具使用和结果序列格式化为预期格式 |
|
|
将输入的 |
提示
_format在TruncatedFormatterBase中将输入消息分组为智能体消息和工具序列,然后分别通过调用_format_agent_message和_format_tool_sequence进行格式化。你可以重写它来实现自己的格式化策略。可选地,您可以在
_truncate方法中覆盖TruncatedFormatterBase来实现自己的截断策略。
扩展阅读¶
脚本总运行时间:(0分钟7.058秒)