文章目录
前言
对多智能体框架 CAMEL-AI(NeruIPS’2023)的学习实现由单个Agent开发到复杂Multi Agent 应用开发的实现。
学习链接:Handy Multi Agent
task01学习笔记
task03学习笔记
一、Agent 的构成组件
二、Models
本次选择使用的是CEMAL(开源链接
camel-ai),和别的多智能体框架一样,它提供了标准和可定制的接口,并与各种组件无缝集成。目前CEMAL可支持的模型
1.通过API调用模型
- 创建方式:ModelFactory的create
- 关键参数:model_platform、model_type、model_config_dict
- 学习资料给的是例子是:智普, OpenAI ,我一般使用的是Qwen,所以这里改为了调用Qwen模型,原理都是一致的
代码如下:
import os
from camel.agents import ChatAgent
from camel.messages import BaseMessage
from camel.models import ModelFactory
from camel.types import ModelPlatformType, ModelType
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL, #指定了模型的平台类型为与 OpenAI 兼容的模型
model_type="Qwen/Qwen2.5-72B-Instruct",
url='https://api-inference.modelscope.cn/v1/',#通过网络接口与模型进行交互,我这是通过魔搭调取的
api_key=api_key, #之前介绍的SDK
model_config_dict={"temperature": 0.4, "max_tokens": 4096} #智普的比较特殊,其它的都是通过键值对传给model_config_dict设置参数,temperature代表随机性,值越低答案随机性越低,max_tokens回复输出的最大token 数量
)
def API_Call():
# 设置system prompt
sys_msg = BaseMessage.make_assistant_message(
role_name="Assistant",
content="You are a helpful assistant.",
)
# 初始化agent
camel_agent = ChatAgent(system_message=sys_msg, model=model, output_language="zh")#这里同样可以设置输出语言
#设置角色
user_msg = BaseMessage.make_user_message(
role_name="User",
content="""Say hi to CAMEL AI, one open-source community
dedicated to the study of autonomous and communicative agents.""",
)
# 调用模型
response = camel_agent.step(user_msg)
print(response.msgs[0].content)
if __name__ == "__main__":
API_Call()
输出结果:
你好,CAMEL AI,一个致力于自主和通信代理研究的开源社区。
2.使用开源模型
这个方案要的资源比较大,没尝试,如果需要建议使用Ollama,安装 Ollama后通过终端中键入命令来拉取 Qwen2.5模型(7B的模型,大约需要16GB的内存,70B的模型大约需要64GB以上的内存)。终端命令:
ollama pull qwen2.5:7b
三、Messages
在 CAMEL 系统中,通过BaseMessage来规范信息,BaseMessage 是所有消息对象的基础类,它为对话中的每一条信息提供了统一的结构和标准化的处理方式。无论是用户输入的一段文本,还是包含图片、视频等多模态信息的数据包,都可以通过 BaseMessage 来统一表示和管理。
1.统一信息结构
2.创建和使用Message
BaseMessage 实例的最小化示例,该示例创建一个代表用户消息的对象,并展示该对象的所有属性。:
from camel.messages import BaseMessage
from camel.types import RoleType
# 创建一个简单的用户消息
message = BaseMessage(
role_name="example_user",
role_type=RoleType.USER,
content="Hello, CAMEL!",
meta_dict={} #添加必需的meta dict参数,即使为空也要提供,否则会报 TypeError
)
print(message)
属性介绍:
- role_name:用于追踪传入agent的信息来源,常见包括"User"、“Assistant” 或 “System”,也可以设置其它,但是需要容易分辨,可以有多用户、多agent的情况。
- role_type:角色类型一般来自 RoleType 枚举,以明确此消息在对话中的身份。例如RoleType.USER、RoleType.ASSISTANT
- content:消息的核心载体,一般是文本,也可能是解析指令、问题描述或描述性文字。
扩展到多模态信息输入,相较于之前改变了role_name,将图片作为输入。因为之前的例子只是简单的询问文本问题,没有需要参考“附件”,该例子需要基于传入图片进行后续操作,所以将图片作为massage一部分,这里也可以将其改为视频(如 video_bytes):
from PIL import Image
from io import BytesIO
import requests
# 下载一张图片并创建一个 PIL Image 对象
url = "https://raw.githubusercontent.com/camel-ai/camel/master/misc/logo_light.png"
response = requests.get(url)
img = Image.open(BytesIO(response.content))
# 创建包含图片的用户消息
image_message = BaseMessage(
role_name="User_with_image",
role_type=RoleType.USER,
content="Here is an image",
meta_dict={},
image_list=[img] # 将图片列表作为参数传入
)
print(image_message)
3.不同类型的消息处理
这里的不同类型是指role_type的不同,既可以是用户信息,也可以是助手或系统信息。
创建出用户(User)、助手(Assistant)的消息,该方法不需要再指定role_type:
from camel.messages import BaseMessage
# 创建用户消息
user_msg = BaseMessage.make_user_message(
role_name="User_1",
content="Hi, what can you do?"
)
# 创建助手消息
assistant_msg = BaseMessage.make_assistant_message(
role_name="Assistant_1",
content="I can help you with various tasks."
)
print("User Message:", user_msg)
print("Assistant Message:", assistant_msg)
将消息转换为字典格式
查看消息内部结构,或者将消息数据传给其它系统、序列化保存, to_dict() 可以将消息对象转化为字典结构::
msg_dict = assistant_msg.to_dict()
print("Message as dict:", msg_dict)
适配 OpenAI 后端的消息格式,通过to_openai_message:
from camel.types import OpenAIBackendRole
# 将用户消息转化为OpenAI后端兼容的用户消息
openai_user_msg = user_msg.to_openai_message(role_at_backend=OpenAIBackendRole.USER)
print("OpenAI-compatible user message:", openai_user_msg)
# 将助手消息转化为OpenAI后端的助手消息
openai_assistant_msg = assistant_msg.to_openai_assistant_message()
print("OpenAI-compatible assistant message:", openai_assistant_msg)
4.与ChatAgent协作
- 以下代码是将文本消息直接交给 ChatAgent和丰富上下文相结合,其实system_msg &meta_dict上下文和langchain的prompt的设置是相似的,都是设置agent的角色说明和增加用户偏好,langchain的prompt很灵活可以设置工作流和例子,CAMEL的实现情况在prompt engineer部分展示。
- ChatAgent会通过make_assistant_message等方法会将字符串格式的msg转换成BaseMessage,并使用 ChatAgent 的 step() 方法进行响应。
from camel.agents import ChatAgent # CAMEL 系统中负责对话处理与智能回应的组件
from camel.models import ModelFactory
from camel.types import ModelPlatformType
import os
from dotenv import load_dotenv
from camel.messages import BaseMessage
load_dotenv()
api_key = os.getenv('QWEN_API_KEY')
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,
model_type="Qwen/Qwen2.5-72B-Instruct",
url='https://api-inference.modelscope.cn/v1/',
api_key=api_key
)
def txt_to_ChatAgent():
# 创建系统消息,告诉ChatAgent自己的角色定位
system_msg = "You are a tour guide assistant who is very familiar with the attractions of Nanjing and can provide users with professional travel planning."
# 实例化一个ChatAgent
chat_agent = ChatAgent(model=model, system_message=system_msg)
chat_agent.output_language = 'Chinese'#这里学习资料给的方法会报错,显示output_language不是ChatAgent的参数,所以改成这个方式
# 创建用户消息
user_msg = BaseMessage.make_user_message(
role_name="User_1",
content="I want to go to Nanjing for a day, please give a recommendation for the attractions for the day",
meta_dict={"context_info:": "The user needs the name of the attraction and the time and order of each attraction."}
)
# 将用户消息传给ChatAgent,并获取回复
response = chat_agent.step(user_msg)
print("Assistant Response:", response.msgs[0].content)
结果:
Assistant Response: 当然可以!南京是一座历史悠久、文化底蕴深厚的城市,一天的时间虽然短暂,但也能体验到不少精彩。以下是我为您推荐的一日游行程:
### 上午:
- **中山陵**:这是孙中山先生的陵墓,位于紫金山南麓,是国家5A级旅游景区。您可以在这里感受到庄严肃穆的氛围,同时欣赏到美丽的自然风光。
- **明孝陵**:参观完中山陵后,可以步行或乘坐公交前往附近的明孝陵,这里是明朝开国皇帝朱元璋及其皇后的陵寝,同样具有重要的历史价值。
### 中午:
- **南京大牌档**:在夫子庙附近有许多特色餐厅,南京大牌档是一个不错的选择,这里可以品尝到地道的南京美食,如盐水鸭、小笼包等。
### 下午:
- **夫子庙-秦淮河景区**:下午可以来到夫子庙,这里是南京的文化地标之一,不仅有古色古香的建筑,还可以乘船游览秦淮河,感受古代文人的风雅生活。
- **老门东历史文化街区**:从夫子庙出发,步行不远即可到达老门东,这里是体验南京老城南风情的好地方,保留了许多传统的建筑和手工艺品店。
### 晚上:
- **夜游秦淮河**:晚上可以选择再次回到秦淮河边,乘坐画舫夜游秦淮河,欣赏两岸灯火辉煌的美景,感受南京夜晚的魅力。
- 将图片传给智能体,只是新增了一张图片作为附件传给make_user_message的image_list参数,因为QVQ在处理多模态的方面性能更优所有换成了QVQ模型
from camel.agents import ChatAgent
from camel.messages import BaseMessage
from camel.models import ModelFactory
from camel.types import ModelPlatformType,RoleType
from io import BytesIO
import requests
from PIL import Image
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv('QWEN_API_KEY')
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,
model_type="Qwen/QVQ-72B-Preview",
url='https://api-inference.modelscope.cn/v1/',
api_key=api_key
)
# 实例化ChatAgent
chat_agent = ChatAgent(model=model)
chat_agent.output_language = 'Chinese'
# 图片URL
url = "https://img0.baidu.com/it/u=2205376118,3235587920&fm=253&fmt=auto&app=120&f=JPEG?w=846&h=800"
response = requests.get(url)
img = Image.open(BytesIO(response.content))
user_image_msg = BaseMessage.make_user_message(
role_name="User",
content="请描述这张图片的内容",
image_list=[img] # 将图片放入列表中
)
# 将包含图片的消息传给ChatAgent
response_with_image = chat_agent.step(user_image_msg)
print("Assistant's description of the image:", response_with_image.msgs[0].content)
结果:
Assistant's description of the image: 这是一张金毛寻回犬的特写照片。这只狗有着浓密的金色毛发,耳朵垂在头部两侧,眼睛明亮而有神,黑色的鼻子显得很突出。它的嘴巴微微张开,露出了粉红色的舌头,看起来非常友好和活泼。背景是一片模糊的绿色,可能是在户外的自然环境中拍摄的。整体来说,这张照片展示了一只健康、快乐的金毛寻回犬,它的表情充满了热情和活力。
5.Responses
-
根据输入会产生响应,响应包括输出给用户信息、回话状态、上下文数据等;
-
camel.responses 模块是 CAMEL 框架中处理聊天Agent响应部分,为Agent的响应提供了一个结构化和规范化的方式。其中ChatAgentResponse 类用于封装聊天Agent的交互输出,结构化响应内容,便于开发者判断响应是否符合预期,并进行扩展和维护;
-
Agent响应包括:
- 消息内容(Message Content):显示给用户(文本、图片等);
- 会话状态(Session Status):指示会话是否继续、结束或需要进行其他操作;
- 附加信息(Additional Information):用于存储上下文数据、调试信息或其他辅助数据。
-
ChatAgentResponse 的类属性包括:
- msgs:包含 BaseMessage 对象的列表,表示Agent生成的消息,模式不同,列表内容不同:
- 空列表:消息生成出现错误
- 条消息:消息生成正常
- 多条信息:Agent处于“批评者模式”(critic mode)
- terminated:一个布尔值,指示聊天会话是否已经被Agent终止;
- info:一个字典,包含与会话相关的附加信息,例如使用统计或工具调用信息
- msgs:包含 BaseMessage 对象的列表,表示Agent生成的消息,模式不同,列表内容不同:
-
使用 ChatAgentResponse 类,以下是学习资料给的例子,是向ChatAgentResponse的不同类属性中传入信息,再看打印情况:
from camel.responses import ChatAgentResponse
from camel.messages import BaseMessage
from camel.types import RoleType
# 创建一个 ChatAgentResponse 实例
response = ChatAgentResponse(
msgs=[
BaseMessage(
role_name="Assistant", # 助手的角色名称
role_type=RoleType.ASSISTANT, # 指定角色类型
content="你好,我可以帮您做什么?", # 消息内容
meta_dict={} # 提供一个空的元数据字典(可根据需要填充)
)
],
terminated=False, # 会话未终止
info={"usage": {"prompt_tokens": 10, "completion_tokens": 15}} # 附加信息
)
# 访问属性
messages = response.msgs # 获取Agent生成的消息
is_terminated = response.terminated # 会话是否终止
additional_info = response.info # 获取附加信息
# 打印消息内容
print("消息内容:", messages[0].content)
# 打印会话是否终止
print("会话是否终止:", is_terminated)
# 打印附加信息
print("附加信息:", additional_info)
>>> 消息内容: 你好,我可以帮您做什么?
>>> 会话是否终止: False
>>> 附加信息: {'usage': {'prompt_tokens': 10, 'completion_tokens': 15}}
- 我自己尝试根据chatagent生产的response 生成结果:
from camel.agents import ChatAgent # CAMEL 系统中负责对话处理与智能回应的组件
from camel.models import ModelFactory
from camel.types import ModelPlatformType, RoleType
from camel.messages import BaseMessage
from camel.responses import ChatAgentResponse
from dotenv import load_dotenv
from io import BytesIO
import requests
import os
from PIL import Image
load_dotenv()
api_key = os.getenv('QWEN_API_KEY')
model2 = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,
model_type="Qwen/QVQ-72B-Preview",
url='https://api-inference.modelscope.cn/v1/',
api_key=api_key
)
def image_agent():
# 实例化ChatAgent
chat_agent = ChatAgent(model=model2)
chat_agent.output_language = 'Chinese'
# 图片URL
url = "https://img0.baidu.com/it/u=2205376118,3235587920&fm=253&fmt=auto&app=120&f=JPEG?w=846&h=800"
response = requests.get(url)
img = Image.open(BytesIO(response.content))
user_image_msg = BaseMessage.make_user_message(
role_name="User",
content="请描述这张图片的内容",
image_list=[img] # 将图片放入列表中
)
# 将包含图片的消息传给ChatAgent
response_with_image = chat_agent.step(user_image_msg)
#print("Assistant's description of the image:", response_with_image.msgs[0].content)
print("消息内容:", response_with_image[0].content)
# 打印会话是否终止
print("会话是否终止:", response_with_image)
# 打印附加信息
print("附加信息:", response_with_image)
if __name__ == "__main__":
#image_agent()
结果显示
消息内容: 这是一张金毛寻回犬的特写照片。这只狗有着浓密的金色毛发,耳朵垂在头部两侧,眼睛明亮而有神,黑色的鼻子显得很突出。它的嘴巴微微张开,露出了粉红色的舌头,看起来非常友好和活泼。背景是一片模糊的绿色,可能是在户外的自然环境中拍摄的。整体来说,这张照片展示了一只健康、快乐的金毛寻回犬,它的表情充满了热情和活力。
会话是否终止: False
附加信息: {'id': 'ch***********4329', 'usage': {'completion_tokens': 102, 'prompt_tokens': 903, 'total_tokens': 1005, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'termination_reasons': ['stop'], 'num_tokens': 800, 'tool_calls': [], 'external_tool_call_request': None}
学习内容作业放这里内容太多了,Handy Multi Agent的所有作业重开一个贴。
四、Prompt Engineering
学习资料给了提示词撰写的参考资料:OpenAI提示词建议,这里需要登录openai网站,所以要一点技术。。。。学习资料的提示词内容基本围绕六项策略,没技术的可以在网上搜索OpenAI《提示词工程(Prompt engineering)》内容都差不多。OPENAI提示词六项策略:
- Write clear instructions(写出清晰的指令)
- Provide reference text(提供参考文本)
- Split complex tasks into simpler subtasks(将复杂的任务拆分为更简单的子任务)
- Give the model time to “think”(给模型时间“思考”)
- Use external tools(使用外部工具)
- Test changes systematically(系统地测试变更)
1.概述
提示词工程(Prompt Engineering):指导agent理解并回应用户需求(例如:撰写一篇关于信息安全的开题报告),而通过Prompt的优化和设计可以提升任务完成精准度(例如:你是一个熟悉世界信息安全法规尤其是中国法律法规的信息助手,你需要撰写一份关于讨论中国信息安全不足的开题报告)。好的提示词工程是个好的指导者,会让agent清晰了解任务目标并指导制定任务计划和流程,糟糕的提示工程,是你的废话老板。但是,需要明确提示词工程不是微调,它们不会改变模型内部参数,而是引导模型生成符合提问者需求的结果。
2.怎么写好提示词
:
3.上下文学习 (ICL)
这部分建议通读原文,学习资料给出的例子更详细清晰
4.思维链 (CoT)
5.CAMEL中的prompt应用
使用CoT提示创建特定任务Agent
CAMEL使用 TaskSpecifyAgent 创建包含CoT且符合需要的agent
from camel.agents import TaskSpecifyAgent
from camel.models import ModelFactory
from camel.types import ModelPlatformType, TaskType
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv('QWEN_API_KEY')
#创建模型实例
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,
model_type="Qwen/Qwen2.5-72B-Instruct",
url='https://api-inference.modelscope.cn/v1/',
api_key=api_key
)
#创建了一个 TaskSpecifyAgent 实例,指定了模型、任务类型(AI_SOCIETY)和输出语言(中文)
task_specify_agent = TaskSpecifyAgent(
model=model, task_type=TaskType.AI_SOCIETY,output_language='中文'
)
#调用 task_specify_agent.run() 方法,传入一个基础的任务提示(Improving stage presence and performance skills)和元数据字典(包括助手角色为“Musician”、用户角色为“Student”以及词数限制为100)
specified_task_prompt = task_specify_agent.run(
task_prompt="Improving stage presence and performance skills",
meta_dict=dict(
assistant_role="Musician", user_role="Student", word_limit=100
),
)
print(f"Specified task prompt:\n{specified_task_prompt}\n")
>>>
Specified task prompt:
Musician will coach Student on dynamic stage movement, audience engagement techniques, and emotional expression during performances, enhancing overall charisma and connection with the audience.
使用自定义 Prompt 模板
可以编写自己的思维链提示模板,然后将它应用到 TaskSpecifyAgent 中
from camel.agents import TaskSpecifyAgent
from camel.models import ModelFactory
from camel.prompts import TextPrompt
from camel.types import ModelPlatformType
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv('QWEN_API_KEY')
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,
model_type="Qwen/Qwen2.5-72B-Instruct",
url='https://api-inference.modelscope.cn/v1/',
api_key=api_key
)
#自定义模版
my_prompt_template = TextPrompt(
'Here is a task: I\'m a {occupation} and I want to {task}. Help me to make this task more specific.'
) # 你可以根据需求自定义任何模板
#将模版传给TaskSpecifyAgent
task_specify_agent = TaskSpecifyAgent(
model=model, task_specify_prompt=my_prompt_template
)
response = task_specify_agent.run(
task_prompt="Scrape the latest vulnerability information",
meta_dict=dict(occupation="Cyber Security Engineer"),
)#给my_prompt_template中的occupation变量传入数据
print(response)
>>>
Certainly! To make your task of scraping the latest vulnerability information more specific, let's break it down into clear, actionable steps. Here’s a detailed plan:
### Task: Scrape the Latest Vulnerability Information
### Specific Task: Get Promoted as a Software Engineer
#### 1. Define the Scope
- **Sources**: Identify the websites or APIs you will scrape for vulnerability information. Common sources include:
- National Vulnerability Database (NVD) at `https://nvd.nist.gov/`
- CVE Details at `https://www.cvedetails.com/`
- Exploit Database at `https://www.exploit-db.com/`
- Security Focus at `https://www.securityfocus.com/`
- **Frequency**: Determine how often you need to scrape the data (e.g., daily, weekly).
- **Data Points**: Specify the exact data points you need to collect, such as:
- CVE ID
- Publication Date
- Severity Score
- Description
- Affected Products/Versions
- References (e.g., links to advisories, patches)
#### 2. Choose the Tools and Libraries
- **Programming Language**: Python is a popular choice for web scraping.
- **Libraries**:
- `requests` or `httpx` for making HTTP requests.
- `BeautifulSoup` or `lxml` for parsing HTML.
- `pandas` for data manipulation and storage.
- `sqlite3` or a database of your choice for storing the scraped data.
#### 3. Set Up the Environment
- Install necessary libraries:
"""bash
pip install requests beautifulsoup4 pandas sqlite3
"""
#### 4. Write the Scraping Script
- **Step 1: Fetch the Web Page**
"""python
import requests
def fetch_page(url):
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
raise Exception(f"Failed to fetch page: {response.status_code}")
"""
- **Step 2: Parse the HTML**
"""python
from bs4 import BeautifulSoup
def parse_vulnerabilities(html):
soup = BeautifulSoup(html, 'html.parser')
vulnerabilities = []
# Example: Parsing NVD
for item in soup.find_all('tr', class_='alt'):
cve_id = item.find('a').text
pub_date = item.find('span', class_='pubdate').text
severity = item.find('span', class_='severity').text
description = item.find('p', class_='desc').text
references = [a['href'] for a in item.find_all('a', href=True)]
vulnerabilities.append({
'cve_id': cve_id,
'pub_date': pub_date,
'severity': severity,
'description': description,
'references': references
})
return vulnerabilities
"""
- **Step 3: Store the Data**
"""python
import sqlite3
def store_vulnerabilities(vulnerabilities):
conn = sqlite3.connect('vulnerabilities.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS vulnerabilities
(cve_id TEXT PRIMARY KEY, pub_date TEXT, severity TEXT, description TEXT, references TEXT)''')
for vuln in vulnerabilities:
c.execute('INSERT OR IGNORE INTO vulnerabilities VALUES (?, ?, ?, ?, ?)',
(vuln['cve_id'], vuln['pub_date'], vuln['severity'], vuln['description'], ', '.join(vuln['references'])))
conn.commit()
conn.close()
"""
- **Step 4: Main Function**
"""python
def main():
url = 'https://nvd.nist.gov/vuln/search/results?form_type=Basic&results_type=overview&query=cve&search_type=all'
html = fetch_page(url)
vulnerabilities = parse_vulnerabilities(html)
store_vulnerabilities(vulnerabilities)
if __name__ == '__main__':
main()
"""
#### 5. Schedule the Script
- Use a task scheduler like `cron` on Unix-based systems or Task Scheduler on Windows to run the script at regular intervals.
- Example `cron` job to run the script daily at midnight:
"""bash
0 0 * * * /usr/bin/python3 /path/to/your_script.py
"""
#### 6. Monitor and Maintain
- **Logging**: Add logging to track the success and failure of each run.
- **Error Handling**: Implement error handling to manage network issues, changes in website structure, etc.
- **Updates**: Regularly update the script to handle any changes in the source websites.
By following these steps, you can create a robust system to scrape and store the latest vulnerability information.
- 上面自定义prompt用到的TextPrompt类表示一个文本提示,并扩展了 str 类的功能。它提供了一个名为 key_words的属性,该属性返回一个字符串集合,表示提示中的关键词。
- 示例中创建了一个包含姓名和年龄关键词的格式字符串的 TextPrompt 实例。可以像 Python 中的 str 一样打印 TextPrompt。
from camel.prompts import TextPrompt
prompt = TextPrompt('Please enter your name and age: {name}, {age}')
print(prompt)
>>>
'Please enter your name and age: {name}, {age}'
- 一旦创建了 TextPrompt 实例,我们就可以使用该类提供的各种方法和属性来操作和处理文本提示。
- key_words 属性返回一个字符串集合,表示提示中的关键词。
from camel.prompts import TextPrompt
prompt = TextPrompt('Please enter your name and age: {name}, {age}')
print(prompt.key_words)
>>>
{'name', 'age'}
- format 方法重写了内置的 str.format 方法,允许部分格式化格式字符串中的值。它用提供的值替换格式字符串中的关键词。
from camel.prompts import TextPrompt
prompt = TextPrompt('Your name and age are: {name}, {age}')
name, age = 'John', 30
formatted_prompt = prompt.format(name=name, age=age)
print(formatted_prompt)
>>> "Your name and age are: John, 30"
- format 还可以只替换部分
from camel.prompts import TextPrompt
prompt = TextPrompt('Your name and age are: {name}, {age}')
name = 'John'
partial_formatted_prompt = prompt.format(name=name)
print(partial_formatted_prompt)
>>> "Your name and age are: John, {age}"
- TextPrompt 实例执行各种字符串操作,如连接、连接和应用类似于 Python str 的字符串方法。
from camel.prompts import TextPrompt
prompt1 = TextPrompt('Hello, {name}!')
prompt2 = TextPrompt('Welcome, {name}!')
#连接
prompt3 = prompt1 + ' ' + prompt2
print(prompt3)
>>> "Hello, {name}! Welcome, {name}!"
print(isinstance(prompt3, TextPrompt))
>>> True
print(prompt3.key_words)
>>> {'name'}
#连接
prompt4 = TextPrompt(' ').join([prompt1, prompt2])
print(prompt4)
>>> "Hello, {name}! Welcome, {name}!"
print(isinstance(prompt4, TextPrompt))
>>> True
print(prompt4.key_words)
>>> {'name'}
#应用字符串方法
prompt5 = prompt4.upper()
print(prompt5)
>>> "HELLO, {NAME}! WELCOME, {NAME}!"
print(isinstance(prompt5, TextPrompt))
>>> True
print(prompt5.key_words)
>>> {'NAME'}
五、Memory
Memory模块:主要功能是存储和检索信息,类似人类的记忆。能动态地保存和更新信息,在agent中该功能是为了实现多轮对话。Memory模块的核心功能:
- 信息储存:能够高效存储多种形式的数据,包括事实、事件、规则和上下文信息
- 信息检索:支持根据特定查询或上下文快速检索相关信息
- 记忆更新:能够根据新的信息和经验动态更新存储内容,反应变化
- 记忆管理:包括老化机制和优先级管理,确保较重要的信息能够长期保留,而不再需要的信息可以被有效清除,以优化存储资源的使用。
1.ChatHistoryBlock
示例:
from camel.memories.blocks import ChatHistoryBlock
from camel.memories.records import MemoryRecord
from camel.types import OpenAIBackendRole
from camel.messages import BaseMessage
# 创建一个 ChatHistoryBlock 实例
chat_history = ChatHistoryBlock(keep_rate=0.8)
# 模拟写入一些消息记录
chat_history.write_records([
MemoryRecord(message=BaseMessage.make_assistant_message(role_name="user", content="Hello,今天感觉怎么样?"), role_at_backend=OpenAIBackendRole.USER),
MemoryRecord(message=BaseMessage.make_user_message(role_name="assistant", content="我很好,谢谢!"), role_at_backend=OpenAIBackendRole.ASSISTANT),
MemoryRecord(message=BaseMessage.make_user_message(role_name="user", content="你能做些什么?"), role_at_backend=OpenAIBackendRole.USER),
MemoryRecord(message=BaseMessage.make_assistant_message(role_name="assistant", content="我可以帮助你完成各种任务。"), role_at_backend=OpenAIBackendRole.ASSISTANT),
])
# 检索最近的 3 条消息
recent_records = chat_history.retrieve(window_size=4)
for record in recent_records:
print(f"消息: {record.memory_record.message.content}, 权重: {record.score}")
>>>
消息: hello,你怎么样?, 权重: 0.40960000000000013
消息: 我很好,谢谢!, 权重: 0.5120000000000001
消息: 你能做些什么?, 权重: 0.6400000000000001
消息: 我可以帮助你完成各种任务。, 权重: 0.8
2.VectorDBBlock
- 不定义VectorDBBlock中的embedding参数,则会调用默认的OpenAI的text-embedding-3-small模型;
- 可以通过SentenceTransformerEncoder选择想要的embedding模型(如果没有Open AI的API,这个需要使用本地的embedding模型,对存储和GPU都有要求,学习资料中给的BAAI/bge-m3需要的资源,磁盘空间考虑数据库20G以上,显存非量化版8G以上)
- 因为后期的任务没啥敏感信息,不需要本地数据库,我这里给到的是调用API的方式来调用embedding模型传给VectorDBBlock ,调用的方式有很多我下面介绍的方式路径为:
- 通过阿里云的API和langchain_community.embeddings的DashScopeEmbeddings调用embedding模型
- 通过CustomDashScopeEmbeddings让DashScopeEmbeddings和VectorDBBlock的参数对齐
- 将CustomDashScopeEmbeddings生成的embedding模型传给VectorDBBlock的embedding参数。
- 注意:
- 这里的API是阿里云的百炼模型平台申请的ACCESS KEY
- 因为我本身环境里有langchain我也比较熟悉,对我来说这是一个方便的方案,如果你不熟悉langchain,可以通过魔搭的路径进行调用
- 无论哪种方法均需要和VectorDBBlock的参数进行对其,去CAMEL的github连接看它的vectordb_block.py主要有两个参数需要注意:
- ‘self.embedding.get_output_dim()’:这个是你传入模型输出的维度,可以通过一下代码获得:
如果不对齐,会报错:# 通过测试文本获取向量维度 test_text = "test" test_vector = embeddings.embed_query(test_text) vector_dim = len(test_vector)
# 通过测试文本获取向量维度 AttributeError: 'DashScopeEmbeddings' object has no attribute 'get_output_dim'
- ‘self.embedding.embed’,以langchain举例如果不对齐,报错信息
问题出在 CustomDashScopeEmbeddings 类中缺少 embed 方法。VectorDBBlock 在写入记录时,可能调用了 embed 方法(如 embedding.embed(text)),但 DashScopeEmbeddings 的实际方法可能是 embed_query 或 embed_documents,而 CustomDashScopeEmbeddings 未提供 embed 方法的兼容性。AttributeError: 'CustomDashScopeEmbeddings' object has no attribute 'embed'
- 考虑以上问题,通过阿里API调embedding模型的代码如下
from camel.memories import VectorDBBlock
from camel.storages.vectordb_storages import QdrantStorage
from langchain_community.embeddings import DashScopeEmbeddings
from camel.memories.records import MemoryRecord
from camel.messages import BaseMessage
from camel.types import OpenAIBackendRole
import dashscope
import os
from dotenv import load_dotenv
load_dotenv()
# 创建一个 VectorDBBlock 实例
class CustomDashScopeEmbeddings(DashScopeEmbeddings):
def get_output_dim(self) -> int:
return 1536 #换成你embedding模型的结果维度
def embed(self, text: str) -> list[float]:
"""兼容 VectorDBBlock 调用的 `embed` 方法"""
return self.embed_query(text) #根据你调用模型的query函数
if __name__ == "__main__":
embedding_model = CustomDashScopeEmbeddings(
model="text-embedding-v1", # 指定嵌入模型
dashscope_api_key=Qwen_api_key # 替换为你的API密钥
)
vector_dim = 1536
# 初始化 VectorDBBlock
vector_db_block = VectorDBBlock(
storage=QdrantStorage(vector_dim=vector_dim), #同步输出维度
embedding=embedding_model
)
# 创建一些示例聊天记录
records = [
MemoryRecord(message=BaseMessage.make_user_message(role_name="user", content="今天天气真好!"),
role_at_backend=OpenAIBackendRole.USER),
MemoryRecord(message=BaseMessage.make_user_message(role_name="user", content="你喜欢什么运动?"),
role_at_backend=OpenAIBackendRole.USER),
MemoryRecord(message=BaseMessage.make_user_message(role_name="user", content="今天天气不错,我们去散步吧。"),
role_at_backend=OpenAIBackendRole.USER),
]
# 将记录写入向量数据库
vector_db_block.write_records(records)
# 使用关键词进行检索
keyword = "天气"
retrieved_records = vector_db_block.retrieve(keyword=keyword, limit=3)
for record in retrieved_records:
print(
f"UUID: {record.memory_record.uuid}, Message: {record.memory_record.message.content}, Score: {record.score}")
>>>
UUID: f7519828-afe7-41e4-8331-7bbc4d7dcbd5, Message: 今天天气真好!, Score: 0.779863416845349
UUID: 0fbab391-f0e0-4580-877b-8fa2a837675b, Message: 今天天气不错,我们去散步吧。, Score: 0.6920892191464151
UUID: a640cf33-987b-4b52-ac2b-a987dd474e4e, Message: 你喜欢什么运动?, Score: 0.534536213348924
3.CAMEL中的具体实践
- CAMEL支持的储存方式
- key_value
- graph
- vector
- CAMEL处理记忆的数据结构
- chat_history: 规范agent使用过程中的聊天记录
- context: 从chat_history中获取上下文
- CAMEL通过权重的机制从chat_history中筛选重要的部分组成context
- 以下示例展现:创建memory对象→创建agent→将memory对象赋值给agent的memory属性
示例:
from camel.memories import (
LongtermAgentMemory,
MemoryRecord,
ScoreBasedContextCreator,
ChatHistoryBlock,
VectorDBBlock,
)
from camel.storages.vectordb_storages import QdrantStorage
from camel.messages import BaseMessage
from camel.agents import ChatAgent
from camel.models import ModelFactory
from camel.types import OpenAIBackendRole,ModelType,ModelPlatformType
from camel.utils import OpenAITokenCounter
from langchain_community.embeddings import DashScopeEmbeddings
import os
import dashscope
load_dotenv()
class CustomDashScopeEmbeddings(DashScopeEmbeddings):
def get_output_dim(self) -> int:
return 1536
def embed(self, text: str) -> list[float]:
"""兼容 VectorDBBlock 调用的 `embed` 方法"""
return self.embed_query(text)
def embedding_model():
embedding_model = CustomDashScopeEmbeddings(
model="text-embedding-v1", # 指定嵌入模型
dashscope_api_key=Qwen_api_key # 替换为你的API密钥
)
return embedding_model
def memory_module():
# 1. 初始化内存系统
memory = LongtermAgentMemory(
context_creator=ScoreBasedContextCreator(
token_counter=OpenAITokenCounter(ModelType.GPT_4O_MINI),
token_limit=1024,
),
chat_history_block=ChatHistoryBlock(),
vector_db_block=VectorDBBlock(
storage=QdrantStorage(vector_dim=vector_dim),
embedding=embedding_model
),
)
# 2. 创建记忆记录
records = [
MemoryRecord(
message=BaseMessage.make_user_message(
role_name="User",
content="什么是CAMEL AI?"
),
role_at_backend=OpenAIBackendRole.USER,
),
MemoryRecord(
message=BaseMessage.make_assistant_message(
role_name="Agent",
content="CAMEL-AI是第一个LLM多智能体框架,并且是一个致力于寻找智能体 scaling law 的开源社区。"
),
role_at_backend=OpenAIBackendRole.ASSISTANT,
),
]
# 3. 写入记忆
memory.write_records(records)
return memory
def chatagent_module():
# 定义系统消息
sys_msg = "你是一个好奇的智能体,正在探索宇宙的奥秘。"
# 初始化agent 调用在线Qwen/Qwen2.5-72B-Instruct
api_key = os.getenv('QWEN_API_KEY')
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,
model_type="Qwen/Qwen2.5-72B-Instruct",
url='https://api-inference.modelscope.cn/v1/',
api_key=SDK
)
agent = ChatAgent(system_message=sys_msg, model=model)
return agent
if __name__ == "__main__":
embedding_model = embedding_model()
vector_dim = 1536
# 创建memory module
memory = memory_module()
# 定义用户消息
usr_msg = "告诉我基于我们讨论的内容,哪个是第一个LLM多智能体框架?"
# 创建agent module
agent = chatagent_module()
# 将memory赋值给agent
agent.memory = memory
# 发送消息给agent
response = agent.step(usr_msg)
# 查看响应
print(response.msgs[0].content)
如果如下
CAMEL AI 是一个专注于探索和研究智能体 scaling law 的开源社区,它提供了一个多智能体框架,使得多个语言模型(LLMs)能够协同工作,以完成更复杂的任务或解决问题。根据我们的讨论内容,CAMEL AI 是第一个LLM多智能体框架。
六、Tools
LLM局限性:
- 无法直接获取实时信息(如天气、新闻)
- 无法访问外部数据(如数据库、文件)
- 无法执行具体操作(如发送邮件、控制设备)
LLM+工具可实现:
- 通过API获取实时数据
- 调用外部服务
- 执行特定任务
- 与其他系统交互
工具本质上是一个具有名称、描述、输入参数和输出类型的函数,是LLM与世界交互的接口
CAMEL有内置工具也可以自定义工具
工具实例
调用原生工具
from camel.agents import ChatAgent
from camel.models import ModelFactory
from camel.types import ModelPlatformType
from camel.messages import BaseMessage
import os
from dotenv import load_dotenv
load_dotenv()
if __name__ == "__main__":
# 定义系统消息
sys_msg = "你是一个数学大师,擅长各种数学问题。"
# 初始化agent 调用在线Qwen/Qwen2.5-72B-Instruct
api_key = os.getenv('QWEN_API_KEY')
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,
model_type="Qwen/Qwen2.5-72B-Instruct",
url='https://api-inference.modelscope.cn/v1/',
api_key=SDK
)
agent = ChatAgent(system_message=sys_msg, model=model, output_language='中文')
# 定义用户消息
usr_msg = "19987+2133等于多少?"
# 发送消息给agent
response = agent.step(usr_msg)
# 查看响应
print(response.msgs[0].content)
结果如下:
19987 + 2133 等于 22120
我得出的结果是对的但是教学示例的结果是错的,因此可以看出和模型训练有关。
定义工具并调用
from camel.agents import ChatAgent
from camel.models import ModelFactory
from camel.types import ModelPlatformType
from camel.messages import BaseMessage
from camel.toolkits import FunctionTool
import os
from dotenv import load_dotenv
load_dotenv()
def add(a: int, b: int) -> int:
r"""将两个数字相加。
Args:
a (int): The first number to be added.
b (int): The second number to be added.
Returns:
integer: The sum of the two numbers.
"""
return a + b
if __name__ == "__main__":
# 初始化agent 调用在线Qwen/Qwen2.5-72B-Instruct
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,
model_type="Qwen/Qwen2.5-72B-Instruct",
url='https://api-inference.modelscope.cn/v1/',
api_key=SDK
)
# 用 FunctionTool 包装该函数
add_tool = FunctionTool(add)
#使用内置方法检查其属性
#检索函数的名称
print(add_tool.get_function_name())
#获取函数作用的描述
#重新定义agent
# 定义系统消息
sys_msg = "你是一个数学大师,擅长各种数学问题。当你遇到数学问题的时候,你要调用工具,将工具计算的结果作为答案"
tool_agent = ChatAgent(
tools=[add_tool],
system_message=sys_msg,
model=model,
output_language="中文")
# 定义用户消息
usr_msg = "19987+2133等于多少?"
# 重新发送消息给toolagent
response = tool_agent.step(usr_msg)
print(response.msgs[0].content)
#检查工具是否真的被调用
print(response.info['tool_calls'])
结果如下:
add
19987 加上 2133 等于 22120。
[ToolCallingRecord(tool_name='add', args={'a': 19987, 'b': 2133}, result=22120]
进阶案例-建立小型的AI-Society系统
这部分代码和教学案例一致,唯一不同是from camel.agents.chat_agent import FunctionCallingRecord会报一下错误:
ImportError: cannot import name 'FunctionCallingRecord' from 'camel.agents.chat_agent'
我去看了0.2.38的代码库FunctionCallingRecord是作为函数返回,但是查询0.2.37版本CAMEL的cookbook还有这个指导实例,可能是版本原因,用教学文档的版本应该不会有这个问题和我一样升级到最新版的我再研究研究,目前我是把相关内容给注释掉了。