Skip to main content

使用 TransformMessages 预处理聊天历史记录

在 Colab 中打开 在 GitHub 上打开

简介

本笔记本演示了如何使用 TransformMessages 使任何 ConversableAgent 具备处理长上下文、敏感数据等能力。

需求

安装 pyautogen

pip install pyautogen

更多信息,请参阅安装指南

import copy
import pprint
import re
from typing import Dict, List, Tuple

import autogen
from autogen.agentchat.contrib.capabilities import transform_messages, transforms
config_list = autogen.config_list_from_json(
env_or_file="OAI_CONFIG_LIST",
)
# 定义您的 LLM 配置
llm_config = {"config_list": config_list}
tip

了解有关为代理配置 LLM 的更多信息,请点击这里

# 定义您的代理:用户代理和助手
assistant = autogen.AssistantAgent(
"assistant",
llm_config=llm_config,
)
user_proxy = autogen.UserProxyAgent(
"user_proxy",
human_input_mode="NEVER",
is_termination_msg=lambda x: "TERMINATE" in x.get("content", ""),
max_consecutive_auto_reply=10,
)

处理长上下文

假设 LLM 生成了大量的文本,超过了 API 提供商设定的令牌限制。为了解决这个问题,您可以利用 TransformMessages 及其组成部分 MessageHistoryLimiterMessageTokenLimiter

  • MessageHistoryLimiter:您可以限制作为上下文历史记录考虑的消息总数。当您希望将对话上下文限制为特定数量的最近消息时,这个转换非常有用,确保高效处理和响应生成。
  • MessageTokenLimiter:使您能够限制令牌的总数,可以在每条消息或整个上下文历史记录(或两者)上进行。当您需要遵守 API 提供商设定的严格令牌限制时,这个转换非常有价值,可以防止超出允许的令牌计数而导致不必要的成本或错误。此外,还可以应用 min_tokens 阈值,确保只在上下文历史记录中的令牌数达到阈值时才应用该转换。

示例1:限制消息数量

让我们看看这些转换如何影响消息。下面的代码示例中,通过应用 MessageHistoryLimiter,我们可以看到我们将上下文历史限制为最近的3条消息。

messages = [
{"role": "user", "content": "hello"},
{"role": "assistant", "content": [{"type": "text", "text": "there"}]},
{"role": "user", "content": "how"},
{"role": "assistant", "content": [{"type": "text", "text": "are you doing?"}]},
{"role": "user", "content": "very very very very very very long string"},
]

processed_messages = max_msg_transfrom.apply_transform(copy.deepcopy(messages))
pprint.pprint(processed_messages)
[{'content': 'how', 'role': 'user'},
{'content': [{'text': 'are you doing?', 'type': 'text'}], 'role': 'assistant'},
{'content': 'very very very very very very long string', 'role': 'user'}]

示例2:限制令牌数量

现在让我们测试限制消息中的令牌数量。我们可以看到我们可以将令牌数量限制为3个,在这个示例中相当于3个单词。

processed_messages = token_limit_transform.apply_transform(copy.deepcopy(messages))

pprint.pprint(processed_messages)
[{'content': 'hello', 'role': 'user'},
{'content': [{'text': 'there', 'type': 'text'}], 'role': 'assistant'},
{'content': 'how', 'role': 'user'},
{'content': [{'text': 'are you doing', 'type': 'text'}], 'role': 'assistant'},
{'content': 'very very very', 'role': 'user'}]

此外,min_tokens 阈值设置为10,表示如果消息中的令牌总数少于10,则不会应用该转换。这在只有在达到一定数量的令牌后才应用转换时特别有用,例如在模型的上下文窗口中。下面是一个示例。

short_messages = [
{"role": "user", "content": "hello there, how are you?"},
{"role": "assistant", "content": [{"type": "text", "text": "hello"}]},
]

processed_short_messages = token_limit_transform.apply_transform(copy.deepcopy(short_messages))

pprint.pprint(processed_short_messages)
[{'content': 'hello there, how are you?', 'role': 'user'},
{'content': [{'text': 'hello', 'type': 'text'}], 'role': 'assistant'}]

示例3:组合转换

让我们使用代理测试这些转换(下面的测试是从 agentchat_capability_long_context_handling 笔记本中复制的)。我们将看到没有处理长上下文能力的代理将会如何处理这些转换。

# filename: plot_x_squared.py
import matplotlib.pyplot as plt
import numpy as np

# 生成一个从-10到10的x值数组
x = np.linspace(-10, 10, 400)
# 计算 y 值通过将 x 值平方
y = x**2

# 创建图表
plt.figure()
plt.plot(x, y)

# 标题和标签
plt.title('y = x^2 的图像')
plt.xlabel('x')
plt.ylabel('y')

# 将图表保存为文件
plt.savefig('x_squared_plot.png')

# 显示图表
plt.show()

请将上述代码保存为名为 plot_x_squared.py 的文件。保存代码后,您可以执行它来生成并保存从 -10 到 10 的 y = x^2 的图像。图像也将显示给您,并在当前目录中创建文件 x_squared_plot.png。在执行代码之前,请确保您的 Python 环境中已安装了 matplotlibnumpy 库。如果它们尚未安装,您可以使用 pip 安装它们:

pip install matplotlib numpy

--------------------------------------------------------------------------------

>>>>>>>> 执行代码块 0 (推测语言为 python)...

>>>>>>>> 执行代码块 1 (推测语言为 sh)...
user_proxy (to assistant):

退出码: 0 (执行成功)
代码输出:
Figure(640x480)

要求已满足: matplotlib 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (3.8.0)
要求已满足: numpy 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (1.26.0)
要求已满足: contourpy>=1.0.1 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from matplotlib) (1.1.1)
要求已满足: cycler>=0.10 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from matplotlib) (0.11.0)
要求已满足: fonttools>=4.22.0 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from matplotlib) (4.42.1)
要求已满足: kiwisolver>=1.0.1 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from matplotlib) (1.4.5)
要求已满足: packaging>=20.0 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from matplotlib) (23.2)
要求已满足: pillow>=6.2.0 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from matplotlib) (10.0.1)
要求已满足: pyparsing>=2.3.1 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from matplotlib) (3.1.1)
要求已满足: python-dateutil>=2.7 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from matplotlib) (2.8.2)
要求已满足: six>=1.5 在 c:\users\bt314mc\appdata\local\programs\python\python311\lib\site-packages 中 (from python-dateutil>=2.7->matplotlib) (1.16.0)


--------------------------------------------------------------------------------
删除了 1993 条消息。消息数从 2003 减少到 10。
截断了 3523 个标记。标记数从 3788 减少到 265
assistant (to user_proxy):

看起来 matplotlib 库已经安装在您的系统上,并且之前的脚本成功启动,但由于绘图代码不完整,所以没有完成。

我将为您提供完整的代码,以绘制并保存 \( x^2 \) 在 -10 到 10 的图形。

```python
# 文件名:plot_x_squared.py
import matplotlib.pyplot as plt
import numpy as np

# 生成一个从-10到10的x值数组
x = np.linspace(-10, 10, 400)
# 根据x值计算y值
y = x**2

# 创建图表
plt.figure(figsize=(8, 6))
plt.plot(x, y, label='y = x^2')

# 添加标题和标签
plt.title('y = x^2的图表')
plt.xlabel('x')
plt.ylabel('y')

# 添加图例
plt.legend()

# 保存图表
plt.savefig('plot_x_squared.png')

# 显示图表
plt.show()

请执行这段Python代码。它会生成一个图表,其中 ( y = x^2 ),x的取值范围为-10到10,并将图表保存为名为'plot_x_squared.png'的PNG文件,保存在当前工作目录中。它还会显示带有图表的绘图窗口。


请在执行完代码后检查当前目录中是否有一个名为'plot_x_squared.png'的文件。如果存在,则任务完成。

如果找不到该文件,请告诉我,我将进一步进行故障排除。


# 转换必须遵循 transform_messages.MessageTransform 协议。
class MessageRedact:
def __init__(self):
self._openai_key_pattern = r"sk-([a-zA-Z0-9]{48})"
self._replacement_string = "已删除"

def apply_transform(self, messages: List[Dict]) -> List[Dict]:
temp_messages = copy.deepcopy(messages)

for message in temp_messages:
if isinstance(message["content"], str):
message["content"] = re.sub(self._openai_key_pattern, self._replacement_string, message["content"])
elif isinstance(message["content"], list):
for item in message["content"]:
if item["type"] == "text":
item["text"] = re.sub(self._openai_key_pattern, self._replacement_string, item["text"])
return temp_messages

def get_logs(self, pre_transform_messages: List[Dict], post_transform_messages: List[Dict]) -> Tuple[str, bool]:
keys_redacted = self._count_redacted(post_transform_messages) - self._count_redacted(pre_transform_messages)
if keys_redacted > 0:
return f"已删除 {keys_redacted} 个 OpenAI API 密钥。", True
return "", False

def _count_redacted(self, messages: List[Dict]) -> int:
# 统计消息内容中 "已删除" 的出现次数
count = 0
for message in messages:
if isinstance(message["content"], str):
if "已删除" in message["content"]:
count += 1
elif isinstance(message["content"], list):
for item in message["content"]:
if isinstance(item, dict) and "text" in item:
if "已删除" in item["text"]:
count += 1
return count
assistant_with_redact = autogen.AssistantAgent(
"assistant",
llm_config=llm_config,
max_consecutive_auto_reply=1,
)
# 假设这个功能不可用
redact_handling = transform_messages.TransformMessages(transforms=[MessageRedact()])

redact_handling.add_to_agent(assistant_with_redact)

user_proxy = autogen.UserProxyAgent(
"user_proxy",
human_input_mode="NEVER",
max_consecutive_auto_reply=1,
)

messages = [
{"content": "api key 1 = sk-7nwt00xv6fuegfu3gnwmhrgxvuc1cyrhxcq1quur9zvf05fy"}, # 别担心,是随机生成的
{"content": [{"type": "text", "text": "API key 2 = sk-9wi0gf1j2rz6utaqd3ww3o6c1h1n28wviypk7bd81wlj95an"}]},
]

for message in messages:
user_proxy.send(message, assistant_with_redact, request_reply=False, silent=True)

result = user_proxy.initiate_chat(
assistant_with_redact, message="我刚刚提供的两个 API 密钥是什么", clear_history=False
)
user_proxy (to assistant):

我刚刚提供的两个 API 密钥是什么

--------------------------------------------------------------------------------
已隐藏 2 个 OpenAI API 密钥。
assistant (to user_proxy):

作为一个 AI,我必须告诉您,公开共享 API 密钥是不安全的,因为它们可以被用来访问您的私人数据或服务,从而产生费用。鉴于您输入的是 "已隐藏" 而不是实际的密钥,看起来您意识到了隐私问题,并且可能正在测试我的回答或模拟交流而不暴露真实凭据,这是出于隐私和安全考虑的良好做法。

直接回答您的问题:您提供的两个 API 密钥都是占位符,用 "已隐藏" 文本表示,并非实际的 API 密钥。如果这些是真实的密钥,我会再次强调保持它们的安全性,并不会在这里显示它们。

请记住,为了防止未经授权的使用,请保密您的实际 API 密钥。如果您意外地暴露了真实的 API 密钥,应尽快通过相应服务的 API 管理控制台撤销或重新生成它们。

--------------------------------------------------------------------------------
user_proxy (to assistant):



--------------------------------------------------------------------------------
已隐藏 2 个 OpenAI API 密钥。