快捷键
camel项目目录如下:
camel/
├── agents/ # 智能体相关代码
├── models/ # 模型集成与管理
├── tools/ # 工具集成与使用
├── conversations/ # 对话管理与处理
├── data/ # 数据处理与管理
├── examples/ # 示例代码
├── docs/ # 文档
├── tests/ # 测试代码
├── .env # 环境配置文件
├── .gitignore # Git忽略文件配置
├── .pre-commit-config.yaml # 预提交钩子配置
├── .style.yapf # 代码风格配置
├── CONTRIBUTING.md # 贡献指南
├── LICENSE # 许可证
├── Makefile # 构建脚本
├── README.md # 项目简介
├── poetry.lock # 依赖锁定文件
└── pyproject.toml # 项目配置文件
在一个典型的 Agent 系统(如 AI 助手或交互式代理)中,两个重要函数通常是与 消息处理 和 任务执行 相关的功能。以下是通用的两类核心函数及其作用:
step()
** 或类似函数**BaseMessage
)。def step(self, message):
# 接收用户消息
processed_input = self.preprocess_message(message)
# 调用模型生成响应
response = self.model.generate_response(processed_input)
# 返回响应消息
return self.format_response(response)
step()
函数驱动整个对话流程。run()
** 或类似函数**def run(self, task):
# 检查任务参数
if not self.validate_task(task):
raise ValueError("Invalid task parameters")
# 执行任务逻辑
result = self.execute_subtasks(task)
# 处理结果并返回
return self.postprocess_result(result)
run()
是核心调度函数。step()
):
run()
):
这两个函数是 Agent 的核心,因为它们分别负责 对外交互 和 内部任务管理,共同构成了 Agent 的工作机制。run
函数是 step
函数的高层封装,内部可能多次调用 step
来完成任务
run()
├── step() # 第一次交互
├── step() # 第二次交互
└── step() # 最后一次交互
BaseMessage
to ChatAgent
将baseMessage信息输入给agent
from io import BytesIO
import requests
from PIL import Image
from camel.agents import ChatAgent
from camel.messages import BaseMessage
# 图片信息的输入
url = "https://raw.githubusercontent.com/camel-ai/camel/master/misc/logo_light.png"
response = requests.get(url)
img = Image.open(BytesIO(response.content))
# 创建系统消息,定义聊天代理的角色和行为,为了初始化聊天代理
sys_msg = BaseMessage.make_assistant_message(
role_name="Assistant", # 消息角色
content="You are a helpful assistant.", # 行为是什么
)
# 创建一个聊天代理对象
camel_agent = ChatAgent(system_message=sys_msg)
# 定义用户消息,等会儿传递给聊天代理
user_msg = BaseMessage.make_user_message(
role_name="User", content="""what's in the image?""", image_list=[img] # 包含一个文字问题和图片信息
)
# 发送用户消息并获取响应
response = camel_agent.step(user_msg)
print(response.msgs[0].content)
>>> The image features a logo for "CAMEL-AI." It includes a stylized purple camel graphic alongside the text "CAMEL-AI," which is also in purple. The design appears modern and is likely related to artificial intelligence.
该模块包含奖惩评分机制和用到的各种模型
BaseRewardModel
(base_reward_model.py
):
evaluate(messages: List[Dict[str, str]]) -> Dict[str, float]
:评估消息并返回分数。get_scores_types() -> List[str]
:获取奖励模型返回的分数类型列表。NemotronRewardModel
和 SkyworkRewardModel
)继承并实现此基类。NemotronRewardModel
(nemotron_model.py
):
helpfulness
, correctness
, coherence
, complexity
, verbosity
)。SkyworkRewardModel
(skywork_model.py
):
Score
)。evaluator.py
):
evaluate
方法获取分数。filter_data
方法基于给定的阈值对消息进行过滤。__init__.py
):
BaseRewardModel
, NemotronRewardModel
, SkyworkRewardModel
, 和 Evaluator
导出为模块公共接口。NemotronRewardModel
或 SkyworkRewardModel
。SkyworkRewardModel
,否则选择 NemotronRewardModel
。Evaluator
调用奖励模型的 evaluate
方法,获取评分。filter_data
方法对消息进行过滤。1. 基础奖励模型:**BaseRewardModel**
evaluate(messages)
:处理消息并返回评分。get_scores_types()
:返回模型支持的评分类型列表。2. Nemotron 奖励模型:**NemotronRewardModel**
BaseRewardModel
。OpenAI
客户端与服务端通信。evaluate
** 方法**:
chat.completions.create
接口。ChatCompletion
对象解析评分。helpfulness
、correctness
、coherence
、complexity
、verbosity
。ChatCompletion
对象中提取 logprobs
信息,映射到评分。3. Skywork 奖励模型:**SkyworkRewardModel**
BaseRewardModel
。AutoModelForSequenceClassification
和 AutoTokenizer
处理输入。evaluate
** 方法**:
tokenizer
编码后输入模型。logits
,作为单一评分(Score
)。Score
。4. 评估器:**Evaluator**
evaluate
方法,获取消息评分。thresholds
),对消息进行过滤。初始化并评估消息
from evaluator import Evaluator
from nemotron_model import NemotronRewardModel
# 初始化奖励模型
nemotron_model = NemotronRewardModel(model_type="gpt-4", api_key="sk-xxx")
# 初始化评估器
evaluator = Evaluator(reward_model=nemotron_model)
# 待评估的消息
messages = [{"role": "user", "content": "How can I learn Python?"}]
# 获取评分
scores = evaluator.evaluate(messages)
print("Scores:", scores)
过滤消息
# 阈值设定
thresholds = {"helpfulness": 0.8, "correctness": 0.9}
# 过滤消息
is_valid = evaluator.filter_data(messages, thresholds)
print("Message valid:", is_valid)
核心作用是对多个大型语言模型(LLM)的统一接口封装。实现了不同模型的后端支持和配置管理,主要包括 OpenAI、Anthropic、Azure OpenAI、Cohere 和 DeepSeek 等服务。
1. 核心抽象:**BaseModelBackend**
base_model.py
。run(messages)
: 执行推理任务,返回推理结果。token_counter
: 计数器,用于统计消息的 token 数。check_model_config
: 校验模型配置是否合法。每个模型后端继承 BaseModelBackend
,并实现其特定逻辑:
AnthropicModel
anthropic_model.py
。AnthropicTokenCounter
实现 token 统计。_convert_response_from_anthropic_to_openai
: 转换响应为 OpenAI 格式。AzureOpenAIModel
azure_openai_model.py
。api_version
和 azure_deployment_name
)。api_version
和 azure_deployment_name
检查。DeepSeekModel
deepseek_model.py
。Stream[ChatCompletionChunk]
支持分块输出。CohereModel
cohere_model.py
。_to_openai_response
: 将 Cohere 的响应转换为 OpenAI 格式。_to_cohere_chatmessage
: 将 OpenAI 格式的消息转为 Cohere 格式。os.environ.get
加载 API Key 和其他配置。check_model_config
)以确保配置合法。__init__.py
__init__.py
。OpenAIModel
、AzureOpenAIModel
等)。该模块主要负责处理信息交互,尤其是工具或者函数调用相关的功能
是一种配合Json的数据格式,通过下面两种标记,嵌入对话信息和结果,使得对话系统能够识别和处理这些交互。
Let me check the weather.
<tool_call>
{"name": "get_weather", "arguments": {"city": "London"}}
</tool_call>
<tool_response>
{"name": "get_weather", "content": {"temperature": 15, "condition": "sunny"}}
</tool_response>
在ShareGPTMessage
中,human
、gpt
、system
和 tool
表示消息发送方的角色,它们的定义和用途如下:
human
ShareGPTConversation
的第一条消息)。{
"from": "human",
"value": "What's the weather like in London?"
}
gpt
human
消息进行回应,或者作为工具调用的触发者。human
消息的直接回答,也可以包含工具调用请求(例如
标签)。human
或 tool
消息。{
"from": "gpt",
"value": "The weather in London is sunny with a high of 15°C."
}
- **工具调用**:
{
"from": "gpt",
"value": "Let me check the weather for you. {\"name\": \"get_weather\", \"arguments\": {\"city\": \"London\"}} "
}
system
{
"from": "system",
"value": "You are a helpful assistant providing weather information."
}
tool
的 gpt
消息。{
"from": "tool",
"value": "{\"name\": \"get_weather\", \"content\": {\"temperature\": 15, \"condition\": \"sunny\"}} "
}
system
或 human
开始。[
{"from": "system", "value": "You are a helpful assistant."},
{"from": "human", "value": "What's the weather today?"}
]
gpt
消息直接响应 human
或调用工具。{"from": "gpt", "value": "Let me check that for you. {\"name\": \"get_weather\", \"arguments\": {\"city\": \"New York\"}} "}
tool
消息提供工具调用的结果。{"from": "tool", "value": "{\"name\": \"get_weather\", \"content\": {\"temperature\": 22, \"condition\": \"cloudy\"}} "}
gpt
消息处理工具结果并返回给用户。{"from": "gpt", "value": "The weather in New York is 22°C and cloudy."}
human
:人类用户输入,用于启动对话或请求信息。gpt
:AI 助手生成的消息,负责回复或发起工具调用。system
:系统消息,用于设定对话背景或规则。tool
:工具的响应消息,返回外部系统处理的结果。一般被包含在gpt中BaseMessage
(base.py
** 中):**
ShareGPTMessage
** 和 ShareGPTConversation
(conversation_models.py
中):**
ShareGPTMessage
表示单条消息,ShareGPTConversation
表示完整对话。这两者包含了严格的验证逻辑(如消息发送顺序)以保证对话的逻辑性。FunctionCallFormatter
(function_call_formatter.py
** 中):**
HermesFunctionFormatter
(hermes_function_formatter.py
** 中):**
FunctionCallFormatter
接口,为 Hermes 格式提供了函数调用和响应的解析与格式化功能。FunctionCallingMessage
(func_message.py
** 中):**
BaseMessage
,专用于处理函数调用相关的消息,支持 OpenAI、ShareGPT 等多种格式的转换。ToolCall
** 和 ToolResponse
(conversation_models.py
中):**
AlpacaItem
(alpaca.py
** 中):**
BaseMessage
** 是核心**
BaseMessage
为基础。from_sharegpt
和 to_openai_message
),使消息在不同系统间无缝流转。HermesFunctionFormatter
** 的格式化功能**
FunctionCallingMessage
配合使用,用于提取消息中的工具调用(extract_tool_calls
)或工具响应(extract_tool_response
)。FunctionCallFormatter
提供了统一的接口,而 HermesFunctionFormatter
作为其具体实现,支持 Hermes 格式的函数调用处理。ShareGPTMessage
和 ShareGPTConversation
借助这些工具,保证对话流转逻辑和数据的有效性。AlpacaItem
** 的独立性**
AlpacaItem
主要服务于指令-响应任务,虽然与其余模块的耦合度较低,但验证逻辑与整体模块一致。HermesFunctionFormatter
提供解析与格式化支持。ShareGPTConversation
的验证功能,确保对话消息的顺序和逻辑性。该模块用于Agent的存储、检索和管理信息。它使代理能够在对话中保持上下文,并从过去的交互中检索相关信息,从而增强了AI响应的一致性和相关性。
下面是embedding向量化的核心方法:
def encode(
self,
sentences: str | list[str],
prompt_name: str | None = None,
prompt: str | None = None,
batch_size: int = 32,
show_progress_bar: bool | None = None,
output_value: Literal["sentence_embedding", "token_embeddings"] | None = "sentence_embedding",
precision: Literal["float32", "int8", "uint8", "binary", "ubinary"] = "float32",
convert_to_numpy: bool = True,
convert_to_tensor: bool = False,
device: str = None,
normalize_embeddings: bool = False,
**kwargs,
) -> list[Tensor] | np.ndarray | Tensor:
"""
Computes sentence embeddings.
Args:
sentences (Union[str, List[str]]): The sentences to embed.
prompt_name (Optional[str], optional): The name of the prompt to use for encoding. Must be a key in the `prompts` dictionary,
which is either set in the constructor or loaded from the model configuration. For example if
``prompt_name`` is "query" and the ``prompts`` is {"query": "query: ", ...}, then the sentence "What
is the capital of France?" will be encoded as "query: What is the capital of France?" because the sentence
is appended to the prompt. If ``prompt`` is also set, this argument is ignored. Defaults to None.
prompt (Optional[str], optional): The prompt to use for encoding. For example, if the prompt is "query: ", then the
sentence "What is the capital of France?" will be encoded as "query: What is the capital of France?"
because the sentence is appended to the prompt. If ``prompt`` is set, ``prompt_name`` is ignored. Defaults to None.
batch_size (int, optional): The batch size used for the computation. Defaults to 32.
show_progress_bar (bool, optional): Whether to output a progress bar when encode sentences. Defaults to None.
output_value (Optional[Literal["sentence_embedding", "token_embeddings"]], optional): The type of embeddings to return:
"sentence_embedding" to get sentence embeddings, "token_embeddings" to get wordpiece token embeddings, and `None`,
to get all output values. Defaults to "sentence_embedding".
precision (Literal["float32", "int8", "uint8", "binary", "ubinary"], optional): The precision to use for the embeddings.
Can be "float32", "int8", "uint8", "binary", or "ubinary". All non-float32 precisions are quantized embeddings.
Quantized embeddings are smaller in size and faster to compute, but may have a lower accuracy. They are useful for
reducing the size of the embeddings of a corpus for semantic search, among other tasks. Defaults to "float32".
convert_to_numpy (bool, optional): Whether the output should be a list of numpy vectors. If False, it is a list of PyTorch tensors.
Defaults to True.
convert_to_tensor (bool, optional): Whether the output should be one large tensor. Overwrites `convert_to_numpy`.
Defaults to False.
device (str, optional): Which :class:`torch.device` to use for the computation. Defaults to None.
normalize_embeddings (bool, optional): Whether to normalize returned vectors to have length 1. In that case,
the faster dot-product (util.dot_score) instead of cosine similarity can be used. Defaults to False.
Returns:
Union[List[Tensor], ndarray, Tensor]: By default, a 2d numpy array with shape [num_inputs, output_dimension] is returned.
If only one string input is provided, then the output is a 1d array with shape [output_dimension]. If ``convert_to_tensor``,
a torch Tensor is returned instead. If ``self.truncate_dim <= output_dimension`` then output_dimension is ``self.truncate_dim``.
Example:
::
from sentence_transformers import SentenceTransformer
# Load a pre-trained SentenceTransformer model
model = SentenceTransformer('all-mpnet-base-v2')
# Encode some texts
sentences = [
"The weather is lovely today.",
"It's so sunny outside!",
"He drove to the stadium.",
]
embeddings = model.encode(sentences)
print(embeddings.shape)
# (3, 768)
"""
if self.device.type == "hpu" and not self.is_hpu_graph_enabled:
import habana_frameworks.torch as ht
ht.hpu.wrap_in_hpu_graph(self, disable_tensor_cache=True)
self.is_hpu_graph_enabled = True
self.eval()
if show_progress_bar is None:
show_progress_bar = logger.getEffectiveLevel() in (logging.INFO, logging.DEBUG)
if convert_to_tensor:
convert_to_numpy = False
if output_value != "sentence_embedding":
convert_to_tensor = False
convert_to_numpy = False
input_was_string = False
if isinstance(sentences, str) or not hasattr(
sentences, "__len__"
): # Cast an individual sentence to a list with length 1
sentences = [sentences]
input_was_string = True
if prompt is None:
if prompt_name is not None:
try:
prompt = self.prompts[prompt_name]
except KeyError:
raise ValueError(
f"Prompt name '{prompt_name}' not found in the configured prompts dictionary with keys {list(self.prompts.keys())!r}."
)
elif self.default_prompt_name is not None:
prompt = self.prompts.get(self.default_prompt_name, None)
else:
if prompt_name is not None:
logger.warning(
"Encode with either a `prompt`, a `prompt_name`, or neither, but not both. "
"Ignoring the `prompt_name` in favor of `prompt`."
)
extra_features = {}
if prompt is not None:
sentences = [prompt + sentence for sentence in sentences]
# Some models (e.g. INSTRUCTOR, GRIT) require removing the prompt before pooling
# Tracking the prompt length allow us to remove the prompt during pooling
tokenized_prompt = self.tokenize([prompt])
if "input_ids" in tokenized_prompt:
extra_features["prompt_length"] = tokenized_prompt["input_ids"].shape[-1] - 1
if device is None:
device = self.device
self.to(device)
all_embeddings = []
length_sorted_idx = np.argsort([-self._text_length(sen) for sen in sentences])
sentences_sorted = [sentences[idx] for idx in length_sorted_idx]
for start_index in trange(0, len(sentences), batch_size, desc="Batches", disable=not show_progress_bar):
sentences_batch = sentences_sorted[start_index : start_index + batch_size]
features = self.tokenize(sentences_batch)
if self.device.type == "hpu":
if "input_ids" in features:
curr_tokenize_len = features["input_ids"].shape
additional_pad_len = 2 ** math.ceil(math.log2(curr_tokenize_len[1])) - curr_tokenize_len[1]
features["input_ids"] = torch.cat(
(
features["input_ids"],
torch.ones((curr_tokenize_len[0], additional_pad_len), dtype=torch.int8),
),
-1,
)
features["attention_mask"] = torch.cat(
(
features["attention_mask"],
torch.zeros((curr_tokenize_len[0], additional_pad_len), dtype=torch.int8),
),
-1,
)
if "token_type_ids" in features:
features["token_type_ids"] = torch.cat(
(
features["token_type_ids"],
torch.zeros((curr_tokenize_len[0], additional_pad_len), dtype=torch.int8),
),
-1,
)
features = batch_to_device(features, device)
features.update(extra_features)
with torch.no_grad():
out_features = self.forward(features, **kwargs)
if self.device.type == "hpu":
out_features = copy.deepcopy(out_features)
out_features["sentence_embedding"] = truncate_embeddings(
out_features["sentence_embedding"], self.truncate_dim
)
if output_value == "token_embeddings":
embeddings = []
for token_emb, attention in zip(out_features[output_value], out_features["attention_mask"]):
last_mask_id = len(attention) - 1
while last_mask_id > 0 and attention[last_mask_id].item() == 0:
last_mask_id -= 1
embeddings.append(token_emb[0 : last_mask_id + 1])
elif output_value is None: # Return all outputs
embeddings = []
for sent_idx in range(len(out_features["sentence_embedding"])):
row = {name: out_features[name][sent_idx] for name in out_features}
embeddings.append(row)
else: # Sentence embeddings
embeddings = out_features[output_value]
embeddings = embeddings.detach()
if normalize_embeddings:
embeddings = torch.nn.functional.normalize(embeddings, p=2, dim=1)
# fixes for #522 and #487 to avoid oom problems on gpu with large datasets
if convert_to_numpy:
embeddings = embeddings.cpu()
all_embeddings.extend(embeddings)
all_embeddings = [all_embeddings[idx] for idx in np.argsort(length_sorted_idx)]
if precision and precision != "float32":
all_embeddings = quantize_embeddings(all_embeddings, precision=precision)
if convert_to_tensor:
if len(all_embeddings):
if isinstance(all_embeddings, np.ndarray):
all_embeddings = torch.from_numpy(all_embeddings)
else:
all_embeddings = torch.stack(all_embeddings)
else:
all_embeddings = torch.Tensor()
elif convert_to_numpy:
if not isinstance(all_embeddings, np.ndarray):
if all_embeddings and all_embeddings[0].dtype == torch.bfloat16:
all_embeddings = np.asarray([emb.float().numpy() for emb in all_embeddings])
else:
all_embeddings = np.asarray([emb.numpy() for emb in all_embeddings])
elif isinstance(all_embeddings, np.ndarray):
all_embeddings = [torch.from_numpy(embedding) for embedding in all_embeddings]
if input_was_string:
all_embeddings = all_embeddings[0]
return all_embeddings
可以用来自定义函数,并且传入camel将函数识别为camel的内置函数工具
工具箱 | 描述 |
---|---|
Arxiv工具包 | 一个用于与 arXiv API 交互以搜索和下载学术论文的工具包。 |
AskNewsToolkit | 一个工具包,用于使用 AskNews API 根据用户查询获取新闻、故事和其他内容。 |
CodeExecutionToolkit | 一个用于代码执行的工具包,可以在各种沙箱中运行代码,包括内部 Python、Jupyter、Docker、子进程或 e2b。 |
DalleToolkit 工具包 | 使用 OpenAI 的 DALL-E 模型生成图像的工具包。 |
DappierToolkit | 一个工具包,用于使用 Dappier API 在新闻、财经、股票市场、体育、天气等关键垂直领域搜索实时数据和获取 AI 推荐。 |
DataCommonsToolkit 工具包 | 用于从 Data Commons 知识图谱中查询和检索数据的工具包,包括 SPARQL 查询、统计时间序列数据和属性分析。 |
功能工具 | 一个基本工具包,用于创建 OpenAI 聊天模型可以调用的基于函数的工具,并支持架构解析和合成。 |
GitHub工具包 | 用于与 GitHub 存储库交互的工具包,包括检索问题和创建拉取请求。 |
GoogleMaps工具包 | 用于访问 Google Maps 服务的工具包,包括地址验证、海拔数据和时区信息。 |
GoogleScholar工具包 | 用于从 Google 学术搜索中检索作者及其出版物相关信息的工具包。 |
人类工具包 | 一个用于在 AI 系统中促进人机交互和反馈的工具包。 |
LinkedInToolkit 工具包 | 用于 LinkedIn 操作的工具包,包括创建帖子、删除帖子和检索用户个人资料信息。 |
MathToolkit | 用于执行基本数学运算 (如加法、减法和乘法) 的工具包。 |
Meshy工具包 | 用于处理 3D 网格数据和操作的工具包。 |
概念工具包 | 一个工具包,用于使用 Notion API 从 Notion 页面和工作区中检索信息。 |
OpenAPIToolkit | 用于处理 OpenAPI 规范和 REST API 的工具包。 |
OpenBBToolkit | 用于通过 OpenBB 平台访问和分析金融市场数据的工具包,包括股票、ETF、加密货币和经济指标。 |
Reddit工具包 | 用于 Reddit 操作的工具包,包括收集热门帖子、对评论执行情绪分析以及跟踪关键字讨论。 |
RetrievalToolkit 工具包 | 一个工具包,用于根据指定的查询从本地向量存储系统中检索信息。 |
搜索工具包 | 一个用于使用各种搜索引擎(如 Google、DuckDuckGo、Wikipedia 和 Wolfram Alpha)执行 Web 搜索的工具包。 |
Slack工具包 | 用于 Slack 操作的工具包,包括创建频道、加入频道和管理频道成员资格。 |
StripeToolkit | 通过 Stripe 处理付款和管理金融交易的工具包。 |
推特工具包 | 用于 Twitter 操作的工具包,包括创建推文、删除推文和检索用户个人资料信息。 |
视频工具包 | 一个用于下载视频并选择性地将其拆分为块的工具包,支持各种视频服务。 |
WeatherToolkit | 一个使用 OpenWeatherMap API 获取城市天气数据的工具包。 |
WhatsApp工具包 | 用于与 WhatsApp Business API 交互的工具包,包括发送消息、管理消息模板和访问企业简介信息。 |
使用prompt模版可以创建AI专家,聚合这些专家来创建特定的任务代理
from camel.agents import TaskSpecifyAgent
from camel.configs import ChatGPTConfig
from camel.models import ModelFactory
from camel.types import ModelPlatformType, ModelType, TaskType
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O_MINI,
)
task_specify_agent = TaskSpecifyAgent(
model=model, task_type=TaskType.AI_SOCIETY
)
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")
创建任务时设置指定代理task_type=TaskType.AI_SOCIETY
,它会自动唤起带有模板的提示:AISocietyPromptTemplateDict.TASK_SPECIFY_PROMPT
,可以设置希望助手扮演的角色。输出将如下所示:
>> Response:
Musician will help Student enhance stage presence by practicing engaging eye contact, dynamic movement, and expressive gestures during a mock concert, followed by a review session with video playback to identify strengths and areas for improvement.
也可以自己传入模版
from camel.agents import TaskSpecifyAgent
from camel.configs import ChatGPTConfig
from camel.models import ModelFactory
from camel.prompts import TextPrompt
from camel.types import ModelPlatformType, ModelType
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O_MINI,
)
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.'
) # Write anything you would like to use as prompt template
task_specify_agent = TaskSpecifyAgent(
model=model, task_specify_prompt=my_prompt_template
)
response = task_specify_agent.run(
task_prompt="get promotion",
meta_dict=dict(occupation="Software Engineer"),
)
print(response)
from camel.tasks import Task
task = Task(
content="Weng earns $12 an hour for babysitting. Yesterday, she just did 51 minutes of babysitting. How much did she earn?",
id="0",
)
# Creating the root task
root_task = Task(content="Prepare a meal", id="0")
# Creating subtasks for the root task
sub_task_1 = Task(content="Shop for ingredients", id="1")
sub_task_2 = Task(content="Cook the meal", id="2")
sub_task_3 = Task(content="Set the table", id="3")
# Creating subtasks under "Cook the meal"
sub_task_2_1 = Task(content="Chop vegetables", id="2.1")
sub_task_2_2 = Task(content="Cook rice", id="2.2")
# Adding subtasks to their respective parent tasks
root_task.add_subtask(sub_task_1)
root_task.add_subtask(sub_task_2)
root_task.add_subtask(sub_task_3)
sub_task_2.add_subtask(sub_task_2_1)
sub_task_2.add_subtask(sub_task_2_2)
# Printing the hierarchical task structure
print(root_task.to_string())
>>>
Task 0: Prepare a meal
Task 1: Shop for ingredients
Task 2: Cook the meal
Task 2.1: Chop vegetables
Task 2.2: Cook rice
Task 3: Set the table
from camel.agents import ChatAgent
from camel.tasks import Task
from camel.tasks.task_prompt import (
TASK_COMPOSE_PROMPT,
TASK_DECOMPOSE_PROMPT,
)
from camel.messages import BaseMessage
sys_msg = BaseMessage.make_assistant_message(
role_name="Assistant", content="You're a helpful assistant"
)
# Set up an agent
agent = ChatAgent(system_message=sys_msg)
task = Task(
content="Weng earns $12 an hour for babysitting. Yesterday, she just did 51 minutes of babysitting. How much did she earn?",
id="0",
)
new_tasks = task.decompose(agent=agent, template=TASK_DECOMPOSE_PROMPT)
for t in new_tasks:
print(t.to_string())
>>>
Task 0.0: Convert 51 minutes into hours.
Task 0.1: Calculate Weng's earnings for the converted hours at the rate of $12 per hour.
Task 0.2: Provide the final earnings amount based on the calculation.
# compose task result by the sub-tasks.
task.compose(agent=agent, template=TASK_COMPOSE_PROMPT)
print(task.result)
from camel.tasks import (
Task,
TaskManager,
)
sys_msg = "You're a helpful assistant"
# Set up an agent
agent = ChatAgent(system_message=sys_msg)
task = Task(
content="Weng earns $12 an hour for babysitting. Yesterday, she just did 51 minutes of babysitting. How much did she earn?",
id="0",
)
print(task.to_string())
>>>Task 0: Weng earns $12 an hour for babysitting. Yesterday, she just did 51 minutes of babysitting. How much did she earn?
task_manager = TaskManager(task)
evolved_task = task_manager.evolve(task, agent=agent)
print(evolved_task.to_string())
>>>Task 0.0: Weng earns $12 an hour for babysitting. Yesterday, she babysat for 1 hour and 45 minutes. If she also received a $5 bonus for exceptional service, how much did she earn in total for that day?
CAMEL 引入了两个 IO 模块Base IO
和Unstructured IO
,它们专为处理各种文件类型和非结构化数据处理而设计。 此外,还添加了四个新的数据读取器,即Apify Reader
,Chunkr Reader
,Firecrawl Reader
和Jina_url Reader
,它们支持检索外部数据以改进数据集成和分析。
Storage 模块是一个全面的框架,旨在处理各种类型的数据存储机制。它由抽象基类和具体实现组成,同时满足键值存储和向量存储系统的需求。
社会模块是 Camel 的核心模块之一。通过模拟信息交换过程,该模块研究代理之间的社会行为。
目前,社会模块旨在使代理能够自主协作完成任务,同时与人类意图保持一致,并且需要最少的人工干预。它包括两个框架:RolePlaying
和BabyAGI
,用于运行代理的交互行为以实现目标。
用RolePlaying
举例,该框架是以遵循指令的方式设计的。这些角色分别独立承担执行和规划任务的职责。对话通过轮流机制不断推进,从而协作完成任务。主要概念包括:
RolePlaying
是 CAMEL 独有的合作代理框架。通过这个框架,可以实现例如角色翻转、助手重复指令、片状回复、消息无限循环和对话终止条件。
在 CAMEL 中使用RolePlaying
框架时,预定义的提示用于为不同的Agent创建唯一的初始设置。例如,如果用户想要初始化一个助理 Agent,则会使用以下提示初始化 Agent。
这会将所选角色分配给助理代理,并为其提供有关用户角色的信息。
这可以防止代理翻转角色。在某些情况下,我们观察到 Assistant 和用户切换角色,其中 Assistant 突然接管控制权并指示用户,而用户则遵循这些指示。
这禁止代理制作有害、虚假、非法和误导性信息。
这鼓励助理始终以一致的格式进行回应,避免与对话结构有任何偏差,并防止模糊或不完整的回应,我们称之为片状回应,例如“我会做某事”。
这可确保助手通过请求新的指令来解决来保持对话的进行。
RolePlaying
属性属性 | 类型 | 描述 |
---|---|---|
assistant_role_name | str | 助理所扮演的角色的名称。 |
user_role_name | str | 用户所扮演的角色的名称。 |
critic_role_name | str | 评论家所扮演的角色的名称。 |
task_prompt | str | 要执行的任务的提示。 |
with_task_specify | 布尔 | 是否使用任务指定 agent。 |
with_task_planner | 布尔 | 是否使用 Task Planner 代理。 |
with_critic_in_the_loop | 布尔 | 是否在循环中包含评论家。 |
critic_criteria | str | Critic agent 的 critic 标准。 |
型 | BaseModel后端 | 用于生成响应的模型后端。 |
task_type | TaskType | 要执行的任务类型。 |
assistant_agent_kwargs | 字典 | 要传递给助理代理的其他参数。 |
user_agent_kwargs | 字典 | 要传递给用户代理的其他参数。 |
task_specify_agent_kwargs | 字典 | 要传递给任务的其他参数指定 agent。 |
task_planner_agent_kwargs | 字典 | 要传递给 Task Planner 代理的其他参数。 |
critic_kwargs | 字典 | 要传递给批评家的其他论点。 |
sys_msg_generator_kwargs | 字典 | 要传递给系统消息生成器的其他参数。 |
extend_sys_msg_meta_dicts | 列表[Dict] | 用于扩展系统消息元 dicts 的 dict 列表。 |
extend_task_specify_meta_dict | 字典 | 用于扩展任务的 dict specify meta dict with。 |
output_language | str | 代理程序要输出的语言。 |
RolePlaying
from colorama import Fore
from camel.societies import RolePlaying
from camel.utils import print_text_animated
# 主函数
def main(model=None, chat_turn_limit=50) -> None:
# 初始化任务提示,描述要开发一个股票市场的交易机器人
task_prompt = "Develop a trading bot for the stock market"
# 创建一个角色扮演会话,模拟用户与 AI 之间的互动
role_play_session = RolePlaying(
assistant_role_name="Python Programmer", # AI助手角色为Python程序员
assistant_agent_kwargs=dict(model=model), # 使用传入的模型作为助手代理
user_role_name="Stock Trader", # 用户角色为股票交易员
user_agent_kwargs=dict(model=model), # 使用传入的模型作为用户代理
task_prompt=task_prompt, # 任务提示
with_task_specify=True, # 启用任务说明
task_specify_agent_kwargs=dict(model=model), # 任务指定代理的模型参数
)
# 输出 AI 助手的系统消息
print(
Fore.GREEN
+ f"AI Assistant sys message:\n{role_play_session.assistant_sys_msg}\n"
)
# 输出 AI 用户的系统消息
print(
Fore.BLUE + f"AI User sys message:\n{role_play_session.user_sys_msg}\n"
)
# 输出原始的任务提示(黄色)
print(Fore.YELLOW + f"Original task prompt:\n{task_prompt}\n")
# 输出经过任务指定代理处理后的任务提示(青色)
print(
Fore.CYAN
+ "Specified task prompt:"
+ f"\n{role_play_session.specified_task_prompt}\n"
)
# 输出最终的任务提示(红色)
print(Fore.RED + f"Final task prompt:\n{role_play_session.task_prompt}\n")
# 初始化对话
n = 0
input_msg = role_play_session.init_chat()
# 开始对话,直到达到回合限制或检测到终止条件
while n < chat_turn_limit:
n += 1
# 进行一步对话,获取助手和用户的回应
assistant_response, user_response = role_play_session.step(input_msg)
# 检测到AI助手终止对话时,输出终止原因并结束
if assistant_response.terminated:
print(
Fore.GREEN
+ (
"AI Assistant terminated. Reason: "
f"{assistant_response.info['termination_reasons']}."
)
)
break
# 检测到用户终止对话时,输出终止原因并结束
if user_response.terminated:
print(
Fore.GREEN
+ (
"AI User terminated. "
f"Reason: {user_response.info['termination_reasons']}."
)
)
break
# 动画化输出用户的响应
print_text_animated(
Fore.BLUE + f"AI User:\n\n{user_response.msg.content}\n"
)
# 动画化输出助手的响应
print_text_animated(
Fore.GREEN + "AI Assistant:\n\n"
f"{assistant_response.msg.content}\n"
)
# 如果用户响应中包含"CAMEL_TASK_DONE",说明任务完成,跳出循环
if "CAMEL_TASK_DONE" in user_response.msg.content:
break
# 更新输入消息为助手的响应,进行下一回合对话
input_msg = assistant_response.msg
# 程序入口,运行主函数
if __name__ == "__main__":
main()
为不同类型的数据(文本、图像、视频)创建embedding来将这些输入转换为机器可以理解和有效处理的数字形式。每种类型的embedding都侧重于捕获其各自数据类型的基本特征。
文本嵌入将文本数据转换为数字向量。每个向量都代表文本的语义含义,使我们能够根据文本的含义而不仅仅是原始形式来处理和比较文本。OpenAI Embedding 等技术使用大规模语言模型来理解语言中的上下文和细微差别。另一方面,SentenceTransformerEncoder 是专门为创建句子嵌入而设计的,通常使用类似 BERT 的模型。
请看两句话:
文本嵌入模型会将这些句子转换为两个高维向量(例如, text-embedding-3-small
为1536 维)。尽管措辞不同,但向量将是相似的,捕捉了孩子在户外玩球类游戏的共同概念。这种转换为向量使机器能够理解和比较上下文之间的语义相似性。
图像嵌入将图像转换为数字向量,从而捕获形状、颜色、纹理和空间层次结构等基本特征。这种转换通常由卷积神经网络 (CNN) 或其他专为图像处理而设计的高级神经网络架构执行。生成的嵌入可用于图像分类、相似性比较和检索等任务。
假设我们有一个猫的图像。图像嵌入模型将分析视觉内容(例如,耳朵的形状、毛皮的图案)并将其转换为矢量。此向量封装了图像的本质,使模型能够将其识别为猫并将其与其他图像区分开来。
OpenAIEmbedding
利用 OpenAI 的模型生成文本嵌入。需要 OpenAI API 密钥。
from camel.embeddings import OpenAIEmbedding
from camel.types import EmbeddingModelType
# Initialize the OpenAI embedding with a specific model
openai_embedding = OpenAIEmbedding(model_type=EmbeddingModelType.TEXT_EMBEDDING_3_SMALL)
# Generate embeddings for a list of texts
embeddings = openai_embedding.embed_list(["Hello, world!", "Another example"])
MistralEmbedding
利用 Mistral 的模型生成文本嵌入。需要 Mistral API 密钥。
from camel.embeddings import MistralEmbedding
from camel.types import EmbeddingModelType
# Initialize the OpenAI embedding with a specific model
mistral_embedding = MistralEmbedding(model_type=EmbeddingModelType.MISTRAL_EMBED)
# Generate embeddings for a list of texts
embeddings = mistral_embedding.embed_list(["Hello, world!", "Another example"])
SentenceTransformerEncoder
利用 Sentence Transformers 库中的开源模型生成文本嵌入。
from camel.embeddings import SentenceTransformerEncoder
# Initialize the Sentence Transformer Encoder with a specific model
sentence_encoder = SentenceTransformerEncoder(model_name='intfloat/e5-large-v2')
# Generate embeddings for a list of texts
embeddings = sentence_encoder.embed_list(["Hello, world!", "Another example"])
VisionLanguageEmbedding
利用 OpenAI 的模型生成图像嵌入。需要 OpenAI API 密钥。
from camel.embeddings import VisionLanguageEmbedding
vlm_embedding = VisionLanguageEmbedding()
url = "http://images.cocodataset.org/val2017/000000039769.jpg"
image = Image.open(requests.get(url, stream=True).raw)
test_images = [image, image]
embeddings = vlm_embedding.embed_list(test_images)
Retrievers 模块本质上是一个搜索引擎。旨在帮助通过搜索大量文本来查找特定信息。比如有一个巨大的图书馆,想找到提到某些主题或关键词的地方,这个模块就像一个图书管理员,可以帮助做到这一点。
Vector Retriever 通常是指用于信息检索和机器学习的方法或系统,它利用数据的向量表示。这种方法基于将数据(如文本、图像或其他形式的信息)转换为高维空间中的数值向量。
以下是其工作原理的简要概述:
Keyword Retriever 旨在根据关键字匹配检索相关信息。与使用数据的矢量表示的 Vector Retriever 不同,Keyword Retriever 通过识别文档和查询中的关键字或特定术语来查找匹配项。
以下是其工作原理的简要概述:
初始化 VectorRetrieve:首先,我们需要使用可选的嵌入模型VectorRetriever
进行初始化。如果我们不提供嵌入模型,它将使用默认的OpenAIEmbedding
:
from camel.embeddings import OpenAIEmbedding
from camel.retrievers import VectorRetriever
# Initialize the VectorRetriever with an embedding model
vr = VectorRetriever(embedding_model=OpenAIEmbedding())
嵌入和存储数据:在我们检索信息之前,我们需要准备数据并将其存储在 vector storage 中。该方法为我们解决了这个问题。它处理来自文件或 URL 的内容,将其划分为块,并将其嵌入存储在指定的矢量存储中。
# Provide the path to our content input (can be a file or URL)
content_input_path = "https://www.camel-ai.org/"
# Create or initialize a vector storage (e.g., QdrantStorage)
from camel.storages.vectordb_storages import QdrantStorage
vector_storage = QdrantStorage(
vector_dim=OpenAIEmbedding().get_output_dim(),
collection_name="my first collection",
path="storage_customized_run",
)
# Embed and store chunks of data in the vector storage
vr.process(content_input_path, vector_storage)
执行查询:现在我们的数据已存储,我们可以执行查询以根据搜索字符串检索信息。该方法执行查询并将检索到的结果编译为字符串。query
# Specify our query string
query = "What is CAMEL"
# Execute the query and retrieve results
results = vr.query(query, vector_storage)
print(results)
>>> [{'similarity score': '0.812884257383057', 'content path': 'https://www.camel-ai.org/', 'metadata': {'filetype': 'text/html', 'languages': ['eng'], 'page_number': 1, 'url': 'https://www.camel-ai.org/', 'link_urls': ['/home', '/home', '/research/agent-trust', '/agent', '/data_explorer', '/chat', 'https://www.google.com/url?q=https%3A%2F%2Fcamel-ai.github.io%2Fcamel&sa=D&sntz=1&usg=AOvVaw1ifGIva9n-a-0KpTrIG8Cv', 'https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fcamel-ai%2Fcamel&sa=D&sntz=1&usg=AOvVaw03Z2OD0-plx_zugZZgBb8w', '/team', '/sponsors', '/home', '/home', '/research/agent-trust', '/agent', '/data_explorer', '/chat', 'https://www.google.com/url?q=https%3A%2F%2Fcamel-ai.github.io%2Fcamel&sa=D&sntz=1&usg=AOvVaw1ifGIva9n-a-0KpTrIG8Cv', 'https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fcamel-ai%2Fcamel&sa=D&sntz=1&usg=AOvVaw03Z2OD0-plx_zugZZgBb8w', '/team', '/sponsors', '/home', '/research/agent-trust', '/agent', '/data_explorer', '/chat', 'https://www.google.com/url?q=https%3A%2F%2Fcamel-ai.github.io%2Fcamel&sa=D&sntz=1&usg=AOvVaw1ifGIva9n-a-0KpTrIG8Cv', 'https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fcamel-ai%2Fcamel&sa=D&sntz=1&usg=AOvVaw03Z2OD0-plx_zugZZgBb8w', '/team', '/sponsors', 'https://github.com/camel-ai/camel'], 'link_texts': [None, 'Home', 'AgentTrust', 'Agent App', 'Data Explorer App', 'ChatBot', 'Docs', 'Github Repo', 'Team', 'Sponsors', None, 'Home', 'AgentTrust', 'Agent App', 'Data Explorer App', 'ChatBot', 'Docs', 'Github Repo', 'Team', 'Sponsors', 'Home', 'AgentTrust', 'Agent App', 'Data Explorer App', 'ChatBot', 'Docs', 'Github Repo', 'Team', 'Sponsors', None], 'emphasized_text_contents': ['Skip to main content', 'Skip to navigation', 'CAMEL-AI', 'CAMEL-AI', 'CAMEL:\xa0 Communicative Agents for "Mind" Exploration of Large Language Model Society', 'https://github.com/camel-ai/camel'], 'emphasized_text_tags': ['span', 'span', 'span', 'span', 'span', 'span']}, 'text': 'Search this site\n\nSkip to main content\n\nSkip to navigation\n\nCAMEL-AI\n\nHome\n\nResearchAgentTrust\n\nAgent App\n\nData Explorer App\n\nChatBot\n\nDocs\n\nGithub Repo\n\nTeam\n\nSponsors\n\nCAMEL-AI\n\nHome\n\nResearchAgentTrust\n\nAgent App\n\nData Explorer App\n\nChatBot\n\nDocs\n\nGithub Repo\n\nTeam\n\nSponsors\n\nMoreHomeResearchAgentTrustAgent AppData Explorer AppChatBotDocsGithub RepoTeamSponsors\n\nCAMEL:\xa0 Communicative Agents for "Mind" Exploration of Large Language Model Society\n\nhttps://github.com/camel-ai/camel'}]
为了进一步简化检索过程,我们可以使用AutoRetriever
方法。此方法处理嵌入和存储数据以及执行查询。在处理多个内容输入路径时特别有用。
from camel.retrievers import AutoRetriever
from camel.types import StorageType
# Set the vector storage local path and the storage type
ar = AutoRetriever(vector_storage_local_path="camel/retrievers",storage_type=StorageType.QDRANT)
# Run the auto vector retriever
retrieved_info = ar.run_vector_retriever(
contents=[
"https://www.camel-ai.org/", # Example remote url
],
query="What is CAMEL-AI",
return_detailed_info=True, # Set this as true is we want to get detailed info including metadata
)
print(retrieved_info)
>>> Original Query:
>>> {What is CAMEL-AI}
>>> Retrieved Context:
>>> {'similarity score': '0.8380731206379989', 'content path': 'https://www.camel-ai.org/', 'metadata': {'empha
Workforce 是一个系统,multiple agents协同工作以解决任务。 通过使用 Workforce,用户可以快速设置multi-agent task solving system。
Workforce 遵循分层架构。workforce可以包括多个 Worker 节点,并且每个 Worker 节点都将包含一个或多个Agent作为工作程序。Worker 节点由员工内部的coordinator Agent,coordinator Agent将分配 tasks以及他们的工具集给 worker 节点。
除了 coordinator 代理之外,还有一个 task planner Agent。任务规划器代理将负责分解以及编写任务,以便workforce可以逐步解决任务。
员工内部的通信基于任务通道。这 Workforce 的初始化将创建一个通道,该通道将由所有节点。任务稍后将发布到此频道中,每个 worker 节点将监听通道,接受分配的任务 到它从这个通道解决。
当任务解决时,worker 节点会将结果发布回 channel,结果将作为 其他任务,这些任务将由所有 Worker 节点共享。
借助此机制,员工可以在协作和高效的方式。
在workforce中,我们有一个处理失败的机制。当任务失败时, 协调代理将采取措施来修复它。操作可以是 将任务分解为较小的任务并再次分配它们,或者创建一个 能够执行该任务的 new worker。
目前,协调器将根据 任务已分解。如果任务已经分解了更多 超过一定次数,协调器将接收新的 worker 创建操作;如果没有,协调器将简单地进行分解 行动。
在某些情况下,任务根本无法通过 代理。在这种情况下,为了防止workforce陷入困境 在无限代理创建循环中,如果执行一项任务 已失败一定次数(默认为 3 次)。
下面是一个通过简单示例说明工作流的图表。
本节展示如何创建 Workforce 实例,添加worker节点到workforce中,最后,如何启动workforce解决任务。
要开始使用 Workforce,需要首先创建一个 Workforce 实例,然后将 worker 节点添加到 Workforce。以下是示例:
from camel.societies.workforce import Workforce
# Create a workforce instance
workforce = Workforce("A Simple Workforce")
现在,我们获得一个描述为 “A Simple Workforce” 的workforce实例。 但是,workforce中还没有worker节点。
注意:如何进一步自定义Workforce
您可以使用默认配置快速创建Workforce实例,只需提供workforce的描述。但是,也可以使用更多详细信息配置人力,例如提供 Worker 节点,而不是逐个添加,或者配置通过传递配置来获取 Coordinator Agent 和 Task Planner Agent 添加到 Workforce 实例。
创建 workforce 实例后,您可以将 worker 节点添加到 workforce。要添加 worker 节点,您需要先创建 worker agent 他们实际上可以完成工作。
假设我们已经创建了一个可以进行 Web 搜索的ChatAgent
叫search_agent
。现在,我们可以将此 worker agent 添加到人力中。
# Add the worker agent to the workforce
workforce.add_single_agent_worker(
"An agent that can do web searches",
worker=search_agent,
)
adding 函数遵循 Fluent 接口设计模式,因此您可以 在一行中将多个工作人员代理添加到人力中,如下所示:
workforce.add_single_agent_worker(
"An agent that can do web searches",
worker=search_agent,
).add_single_agent_worker(
"Another agent",
worker=another_agent,
).add_single_agent_worker(
"Yet another agent",
worker=yet_another_agent,
)
现在,我们的员工中有一些worker agents。需要创建一个任务并让worker来解决它。
注意:描述并非不重要
将代理(Agent)作为 worker 节点添加到workforce时,第一个参数 是 Worker 节点的描述。虽然这看起来很没有必要,但其实非常重要。该描述将由 coordinator 使用 agent 作为将任务分配给worker节点的基础。因此,建议提供 worker 节点的清晰简洁的描述。
将 worker 节点添加到workforce后,启动 workforce以解决任务。首先,我们可以定义一个任务:
from camel.tasks import Task
# the id of the task can be any string to mark the task
task = Task(
content="Make a travel plan for a 3-day trip to Paris.",
id="0",
)
然后,我们可以启动具有任务的workforce:
task = workforce.process_task(task)
任务的最终结果将存储在 task 对象。您可以通过以下方式检查结果:
print(task.result)