数据截至 (上游 commit 473388d36390)
RolePlaying — 对称双人对话
这章讲什么:
RolePlaying是 CAMEL 这个名字的由来。它让两个角色对称地结对:一个扮「用户」不停下指令,一个扮「助手」不停执行,框架在两者间转发消息推进任务。理解它能看清 CAMEL 「communicative agents」的初心。
1. 它要解决的小问题
人想让 AI 完成一个复杂任务("开发 一个股票交易机器人"),但人懒得一步步下指令。RolePlaying 的点子:再造一个 AI 来扮演那个「不停下指令的人」。于是变成两个 AI 对话——一个负责想下一步该干嘛,一个负责真的干。
2. 思路 / 直觉
两个角色不对等但对称地轮流:
- user agent(扮指令方):看当前进展,产出「下一条指令」。
- assistant agent(扮执行方):收到指令,产出「执行结果」。
一来一回就是一个 step()。注意箭头:assistant 的输出喂给 user,user 产出新指令再喂给 assistant。
开始前还可能有两个辅助 agent 先加工任务:
| 辅助 agent | 作用 | 默认开? |
|---|---|---|
TaskSpecifyAgent | 把模糊任务变具体("做个游戏" → 详细规格) | 是(with_task_specify=True) |
TaskPlannerAgent | 给任务追加一个执行计划 | 否 |
3. 图示:一个 step 的消息流
上一轮 assistant_msg
│
▼
┌──────────────┐ 产出「下一条指令」 user_msg
│ user_agent │ ───────────────────────────┐
│ (扮指令方) │ │
└──────────────┘ ▼
┌────────────────┐
新的 assistant_msg ◀────────────── │ assistant_agent │
(下一轮的输入) 产出「执行结果」 │ (扮执行方) │
└────────────────┘
怎么读:一个
step()= 先让 user 出指令,再让 assistant 执行。两个箭头首尾相接,所以连续step()就是不断的一问一答。
4. 原理演示
# 示意,非源码:RolePlaying 的主循环用法
from camel.societies import RolePlaying
session = RolePlaying(
assistant_role_name="Python 程序员",
user_role_name="股票交易员",
task_prompt="开发一个股票交易机器人",
)
assistant_msg = session.init_chat() # 拿到开场白
for _ in range(10): # 跑 10 轮
assistant_resp, user_resp = session.step(assistant_msg)
print("用户说:", user_resp.msg.content) # 这一轮的指令
print("助手做:", assistant_resp.msg.content) # 这一轮的执行
assistant_msg = assistant_resp.msg # 助手输出 → 下一轮输入
重点看最后一行:assistant 的输出回灌成下一轮的起点,对话就这样自我推进。
5. 真实实现
RolePlaying.step()(role_playing.py:631)的真实顺序与上图一致:
role_playing.py:661user_response = self.user_agent.step(assistant_msg)——先让 user agent 基于上一轮 assistant 的话,产出新指令。role_playing.py:671user_msg = self._reduce_message_options(user_response.msgs)——若开了 critic 或多候选,在这里收敛成一条。role_playing.py:681assistant_response = self.assistant_agent.step(user_msg)——再把这条指令喂给 assistant 执行。- 最后(
role_playing.py:698-708)把两边结果打包成(assistant_response, user_response)返回。
开场消息由 init_chat(role_playing.py:548)产生,默认内 容是 "Now start to give me instructions one by one..."(role_playing.py:565-568)——即让 assistant 先发话,邀请 user 开始下指令。
任务预处理在构造函数里就跑完:_init_specified_task_prompt(role_playing.py:214)调 TaskSpecifyAgent 把任务具体化;_init_planned_task_prompt(role_playing.py:264)可选地追加计划。
6. 关键细节 / 坑
① 系统提示决定「入戏」程度。 两个 agent 的系统消息由 SystemMessageGenerator.from_dicts(role_playing.py:345-353)按角色名 + 任务生成,这就是论文里的「inception prompting」:用提示把 LLM 锁进角色,让它持续以该角色说话。
② 可以塞 critic 做「三选一」。 with_critic_in_the_loop=True 时,某一方可产出多个候选消息,CriticAgent 来挑一个(_reduce_message_options,role_playing.py:540-542)。
③ 终止靠任一方 terminated。 若 user 或 assistant 的 step 返回 terminated=True(或 msgs 为空),step() 立即返回空消息(role_playing.py:662-670、:682-692),对话结束。
④ 与 Workforce 的关系: RolePlaying 也能作为 Workforce 里的一个工人节点(RolePlayingWorker),即「一个工位上坐着结对的两个 agent」。详见 03-workforce.md。
7. 横向对比
| RolePlaying | Workforce | |
|---|---|---|
| 结构 | 对称(两个固定角色轮流) | 非对称(协调者 + 多工人) |
| 任务拆分 | 不拆,靠对话逐步推进 | 显式拆成子任务并行派发 |
| 失败处理 | 无内建恢复 | 多种恢复策略 |
| 适合 | 探索性、数据生成、两角色协作 | 工程化、多专长并行流水线 |
8. 代码地图
| 主题 | 文件 | 符号 |
|---|---|---|
| 一轮对话 | camel/societies/role_playing.py | RolePlaying.step / astep |
| 开场消息 | camel/societies/role_playing.py | RolePlaying.init_chat |
| 任务具体化 | camel/societies/role_playing.py | RolePlaying._init_specified_task_prompt |
| 多候选收敛 | camel/societies/role_playing.py | RolePlaying._reduce_message_options |
| 系统提示生成 | camel/generators.py | SystemMessageGenerator |