使用 TransformMessages
预处理聊天历史记录
简介
本笔记本演示了如何使用 TransformMessages
使任何 ConversableAgent
具备处理长上下文、敏感数据等能力。
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}
了解有关为代理配置 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
及其组成部分 MessageHistoryLimiter
和 MessageTokenLimiter
。
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 环境中已安装了 matplotlib
和 numpy
库。如果它们尚未安装,您可以使用 pip
安装它们:
pip install matplotlib numpy
--------------------------------------------------------------------------------
>>>>>>>> 执行代码块 0 (推测语言为 python)...
>>>>>>>> 执行代码块 1 (推测语言为 sh)...
退出码: 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
看起来 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
)
我刚刚提供的两个 API 密钥是什么
--------------------------------------------------------------------------------
已隐藏 2 个 OpenAI API 密钥。
作为一个 AI,我必须告诉您,公开共享 API 密钥是不安全的,因为它们可以被用来访问您的私人数据或服务,从而产生 费用。鉴于您输入的是 "已隐藏" 而不是实际的密钥,看起来您意识到了隐私问题,并且可能正在测试我的回答或模拟交流而不暴露真实凭据,这是出于隐私和安全考虑的良好做法。
直接回答您的问题:您提供的两个 API 密钥都是占位符,用 "已隐藏" 文本表示,并非实际的 API 密钥。如果这些是真实的密钥,我会再次强调保持它们的安全性,并不会在这里显示它们。
请记住,为了防止未经授权的使用,请保密您的实际 API 密钥。如果您意外地暴露了真实的 API 密钥,应尽快通过相应服务的 API 管理控制台撤销或重新生成它们。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
已隐藏 2 个 OpenAI API 密钥。