数据截至 (上游 commit 473388d36390)
地基 — 消息 / 记忆 / 模型 / 工具
这章讲什么: ChatAgent、RolePlaying、Workforce 三层都站在同样四块地基上。看懂这四块,才能改动或扩展框架。
1. BaseMessage — 统一消息格式
小问题: OpenAI、Anthropic、本地模型的消息格式各不相同,框架内部不能到处写 if-else。
思路: 内部一律用 BaseMessage(一个 dataclass:role_name / role_type / content / 可选的图片、视频、推理痕迹,messages/base.py:54-90),要发给某家模型时再转成对应格式。
构造用工厂方法,语义清晰:
# 示意,基于真实 API
from camel.messages import BaseMessage
user_msg = BaseMessage.make_user_message(role_name="User", content="你好")
asst_msg = BaseMessage.make_assistant_message(role_name="Bot", content="在")
真实符号:BaseMessage.make_user_message(messages/base.py:92)、make_assistant_message。它还能转成 OpenAI 格式(to_openai_message)供模型后端消费。
2. AgentMemory — 历史 → 上下文
小问题: 记忆要存得下全部历史,但每次只能喂给模型「token 预算内」的一段。
思路:两层分工。
写入 读取(get_context)
──── ──────────────────
MemoryRecord ──▶ 存储 存储 ──▶ retrieve() 拿记录
(一条消息) (KV/向量) ──▶ ContextCreator 排序+算 token
──▶ (OpenAIMessage 列表, token 数)
三种现成记忆(memories/agent_memories.py):
| 记忆类 | 存哪 | 特点 |
|---|---|---|
ChatHistoryMemory | 键值存储(默认内存) | 最常用,可设 window_size 只取最近 N 条 |
VectorDBMemory | 向量库 | 按当前话题语义检索相关历史 |
LongtermAgentMemory | 两者结合 | 近期聊天 + 向量召回拼在一起 |
LongtermAgentMemory.retrieve(agent_memories.py:278)的拼法很巧:chat_history[:1] + 向量召回 + chat_history[1:]——把系统消息留在最前,中间插入召回的相关记忆,再接最近对话(agent_memories.py:291)。
上下文怎么算 token: ScoreBasedContextCreator.create_context(memories/context_creators/score_based.py:112)按时间戳排序、系统消息置顶,然后数 token。注意:这版不再按 token_limit 裁剪记录(注释明说 token_limit 仅保留兼容,score_based.py:35-37),裁剪交给 ChatAgent 的摘要压缩(见下)。它还缓存上次 LLM 用量、对新消息用「约 2 字符/token」估算以省去重复计数(_estimate_message_tokens,score_based.py:76)。
3. 上下文摘要压缩(精华)
小问题: 对话越来越长,迟早撑爆上下文窗口。
思路: 不是简单丢老消息,而是让 LLM 把老对话总结成一段摘要,用摘要替换原文,腾出空间。
触发点在每圈循环取上下文时——ChatAgent._get_context_with_summarization(chat_agent.py:1022):
- 算出当前 token 数,和「下次摘要阈值」比(
_calculate_next_summary_threshold,chat_agent.py:1094)。 - 超阈值 → 调
self.summarize(...)生成摘要,再_update_memory_with_summary用摘要重写记忆(chat_agent.py:1044-1054)。 - 极端情况(摘要本身都超过
token_limit * summary_window_ratio)→ 做一次「全量压缩」(chat_agent.py:1033-1042)。
# 示意,非源码:压缩的判定逻辑
context, num_tokens = memory.get_context()
if num_tokens > next_summary_threshold(): # 太长了
summary = self.summarize() # 让 LLM 总结老对话
update_memory_with_summary(summary) # 用摘要替换原文
return memory.get_context() # 重新取,现在短了
return context, num_tokens
重点看:压缩发生在「发请求之前」,所以模型永远收到的是预算内的上下文,调用者无感。
4. BaseModelBackend — 40+ 家模型一个接口
小问题: 要支持 OpenAI、Anthropic、Gemini、本地 Ollama/vLLM…… 上层代码不该关心是哪家。
思路: 所有后端实现同一个 BaseModelBackend(models/base_model.py),核心是 run(messages, response_format, tools)。ChatAgent 只调这一个方法(回看 01 的 _get_model_response)。
怎么选后端: ModelFactory.create(model_platform, model_type)(models/model_factory.py)按平台枚举查表(_MODEL_PLATFORM_TO_CLASS_MAP,model_factory.py:78)实例化对应类。model_factory.py:18-66 那一长串 import 就是支持的全部后端清单。
# 示意,基于真实 API
from camel.models import ModelFactory
from camel.types import ModelPlatformType, ModelType
model = ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O,
)
agent = ChatAgent("You are helpful.", model=model)
多后端负载均衡: ChatAgent 可接一个后端列表,由 ModelManager 按策略(默认 round_robin)轮流选(见 ChatAgent 构造参数 scheduling_strategy,chat_agent.py:420-421)。
5. FunctionTool — Python 函数 → 工具 schema
小问题: LLM 调工具需要 JSON schema(函数名、参数类型、描述),手写很烦。
思路: FunctionTool(toolkits/function_tool.py:517)从函数签名 + docstring 自动生成 OpenAI 工具 schema(get_openai_tool_schema,function_tool.py:228 / :792)。你只要写普通带类型注解和 docstring 的函数。
# 示意,基于真实用法
from camel.toolkits import FunctionTool
def add(a: int, b: int) -> int:
r"""两数相加。
Args:
a (int): 第一个数。
b (int): 第二个数。
"""
return a + b
agent = ChatAgent("...", tools=[FunctionTool(add)]) # 直接当工具用
成套的工具放在 BaseToolkit(toolkits/base.py:61),get_tools() 返回一组 FunctionTool。仓库里有 90+ 个工具箱(搜索、代码 执行、浏览器……),camel/toolkits/ 下一个文件一个。
ChatAgent 把所有工具的 schema 汇总后随请求发出:_get_full_tool_schemas(chat_agent.py:944)= 外部工具 schema + 每个内部工具的 get_openai_tool_schema()。
6. 这四块如何拼成一次 step
回到 01 的循环,现在每一步都能对上地基:
step 的一圈
───────────
① memory.get_context() ← §2 AgentMemory(+ §3 摘要压缩)
② model_backend.run( ← §4 BaseModelBackend
context,
tools=_get_full_tool_schemas()) ← §5 FunctionTool
③ 解析出 tool_call_requests / 最终消息 ← §1 BaseMessage 承载
7. 代码地图
| 主题 | 文件 | 符号 |
|---|---|---|
| 统一消息 | camel/messages/base.py | BaseMessage / make_user_message / to_openai_message |
| 记忆基类 | camel/memories/base.py | AgentMemory / BaseContextCreator |
| 三种记忆 | camel/memories/agent_memories.py | ChatHistoryMemory / VectorDBMemory / LongtermAgentMemory |
| 上下文构造 | camel/memories/context_creators/score_based.py | ScoreBasedContextCreator.create_context |
| 摘要压缩触发 | camel/agents/chat_agent.py | ChatAgent._get_context_with_summarization |
| 模型基类 | camel/models/base_model.py | BaseModelBackend.run |
| 模型工厂 | camel/models/model_factory.py | ModelFactory.create |
| 工具封装 | camel/toolkits/function_tool.py | FunctionTool / get_openai_tool_schema |
| 工具箱基类 | camel/toolkits/base.py | BaseToolkit.get_tools |